diff options
| author | 2008-08-03 20:47:47 +0200 | |
|---|---|---|
| committer | 2008-08-05 23:28:10 -0700 | |
| commit | b9f9675e1e3978693bb2e7f66a7125473b3cb30e (patch) | |
| tree | ca582a51bce704bfb764e31b8ef65403b4e6fc86 | |
| parent | b25fea997fc798e945dd7f19f8d0be0d8d3289d1 (diff) | |
| download | libimobiledevice-b9f9675e1e3978693bb2e7f66a7125473b3cb30e.tar.gz libimobiledevice-b9f9675e1e3978693bb2e7f66a7125473b3cb30e.tar.bz2 | |
Initial pairing implementation.
Signed-off-by: Matt Colyer <matt@colyer.name>
| -rw-r--r-- | src/lockdown.c | 290 | ||||
| -rw-r--r-- | src/lockdown.h | 6 | ||||
| -rw-r--r-- | src/main.c | 9 | 
3 files changed, 302 insertions, 3 deletions
| diff --git a/src/lockdown.c b/src/lockdown.c index 7d5c16d..5f73a49 100644 --- a/src/lockdown.c +++ b/src/lockdown.c @@ -25,9 +25,22 @@  #include "userpref.h"  #include <errno.h>  #include <string.h> +#include <glib.h> +#include <libtasn1.h>  extern int debug; +const ASN1_ARRAY_TYPE pkcs1_asn1_tab[]={ +  {"PKCS1",536872976,0}, +  {0,1073741836,0}, +  {"RSAPublicKey",536870917,0}, +  {"modulus",1073741827,0}, +  {"publicExponent",3,0}, +  {0,0,0} +}; + + +  lockdownd_client *new_lockdownd_client(iPhone *phone) {  	if (!phone) return NULL;  	lockdownd_client *control = (lockdownd_client*)malloc(sizeof(lockdownd_client)); @@ -139,6 +152,283 @@ int lockdownd_hello(lockdownd_client *control) {  	return 0;  } +int lockdownd_get_device_public_key(lockdownd_client *control, char **public_key) +{ +	xmlDocPtr plist = new_plist(); +	xmlNode *dict = NULL; +	xmlNode *key = NULL;; +	char **dictionary = NULL; +	int bytes = 0, i = 0; +	char *XML_content = NULL; +	uint32 length = 0; +	 +	/* Setup DevicePublicKey request plist */ +	dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); +	key = add_key_str_dict_element(plist, dict, "Key", "DevicePublicKey", 1); +	key = add_key_str_dict_element(plist, dict, "Request", "GetValue", 1); +	xmlDocDumpMemory(plist, (xmlChar**)&XML_content, &length); + +	/* send to iPhone */ +	bytes = lockdownd_send(control, XML_content, length); +	 +	xmlFree(XML_content); +	xmlFreeDoc(plist); plist = NULL; + +	/* Now get iPhone's answer */ +	bytes = lockdownd_recv(control, &XML_content); + +	plist = xmlReadMemory(XML_content, bytes, NULL, NULL, 0); +	if (!plist) return 0; +	dict = xmlDocGetRootElement(plist); +	for (dict = dict->children; dict; dict = dict->next) { +		if (!xmlStrcmp(dict->name, "dict")) break; +	} +	if (!dict) return 0; +	 +	/* Parse xml to check success and to find public key */ +	dictionary = read_dict_element_strings(dict); +	xmlFreeDoc(plist); +	free(XML_content);	 +	 +	int success = 0; +	for (i = 0; strcmp(dictionary[i], ""); i+=2) { +		if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) { +			success = 1; +		} +		if (!strcmp(dictionary[i], "Value")) { +			*public_key = strdup(dictionary[i+1]); +		} +	} +	 +	if (dictionary) { +		free_dictionary(dictionary); +		dictionary = NULL; +	} +	return success; +} + +int lockdownd_init(iPhone *phone, lockdownd_client **control) +{ +	int ret = 0; +	char *host_id = NULL; + +	if (!phone) +		return 0; + +	*control = new_lockdownd_client(phone); +	if (!lockdownd_hello(*control)){ +		fprintf(stderr, "Hello failed in the lockdownd client.\n"); +	} + +	char *public_key = NULL; +	if(!lockdownd_get_device_public_key(*control, &public_key)){ +		fprintf(stderr, "Device refused to send public key.\n"); +	} + +	host_id = get_host_id(); +	if (!is_device_known(public_key)){ +		ret = lockdownd_pair_device(*control, public_key, host_id); +	} +	free(public_key); +	public_key = NULL; +	 +	if (ret && host_id && !lockdownd_start_SSL_session(*control, host_id)) { +		fprintf(stderr, "SSL Session opening failed.\n"); +	} else {  +		ret = 1; +		free(host_id); +		host_id = NULL; +	} + +	return ret; +} + +int lockdownd_pair_device(lockdownd_client *control, char *public_key_b64, char *host_id) +{ +	int ret = 0; +	xmlDocPtr plist = new_plist(); +	xmlNode *dict = NULL; +	xmlNode *dictRecord = NULL; +	char **dictionary = NULL; +	int bytes = 0, i = 0; +	char *XML_content = NULL; +	uint32 length = 0; + +	char* device_cert_b64 = NULL; +	char* host_cert_b64 = NULL; +	char* root_cert_b64 = NULL; + +	lockdownd_gen_pair_cert(public_key_b64, &device_cert_b64, &host_cert_b64, &root_cert_b64); + +	/* Setup Pair request plist */ +	dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); +	add_key_str_dict_element(plist, dict, "Key", "PairRecord", 1); +	dictRecord = add_child_to_plist(plist, "dict", "\n", NULL, 1); +	add_key_data_dict_element(plist, dictRecord, "DeviceCertificate", device_cert_b64, 2); +	add_key_data_dict_element(plist, dictRecord, "HostCertificate", host_cert_b64, 2); +	add_key_str_dict_element(plist, dictRecord, "HostID", host_id, 2); +	add_key_data_dict_element(plist, dictRecord, "RootCertificate", root_cert_b64, 2); +	add_key_str_dict_element(plist, dict, "Request", "Pair", 1); + +	xmlDocDumpMemory(plist, (xmlChar**)&XML_content, &length); + +	/* send to iPhone */ +	bytes = lockdownd_send(control, XML_content, length); +	 +	xmlFree(XML_content); +	xmlFreeDoc(plist); plist = NULL; + +	/* Now get iPhone's answer */ +	bytes = lockdownd_recv(control, &XML_content); + +	plist = xmlReadMemory(XML_content, bytes, NULL, NULL, 0); +	if (!plist) return 0; +	dict = xmlDocGetRootElement(plist); +	for (dict = dict->children; dict; dict = dict->next) { +		if (!xmlStrcmp(dict->name, "dict")) break; +	} +	if (!dict) return 0; +	 +	/* Parse xml to check success and to find public key */ +	dictionary = read_dict_element_strings(dict); +	xmlFreeDoc(plist); +	free(XML_content);	 +	 +	int success = 0; +	for (i = 0; strcmp(dictionary[i], ""); i+=2) { +		if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) { +			success = 1; +		} +	} +	 +	if (dictionary) { +		free_dictionary(dictionary); +		dictionary = NULL; +	} + +	/* store public key in config if pairing succeeded */ +	if (success)  +		store_device_public_key(public_key_b64); +	return ret; +} + +int lockdownd_gen_pair_cert(char *public_key_b64, char **device_cert_b64, char **host_cert_b64, char **root_cert_b64) +{ +	int ret = 0; + +	gnutls_datum_t modulus = {NULL, 0}; +	gnutls_datum_t exponent = {NULL, 0}; + +	/* first decode base64 public_key */ +	gnutls_datum_t pem_pub_key; +	pem_pub_key.data = g_base64_decode (public_key_b64, &pem_pub_key.size); + + +	/* now decode the PEM encoded key */ +	gnutls_datum_t der_pub_key; +	if( GNUTLS_E_SUCCESS  == gnutls_pem_base64_decode_alloc ("RSA PUBLIC KEY", &pem_pub_key, &der_pub_key) ){ +		ret = 1; +  +		/* initalize asn.1 parser */ +		ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY; +		if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) { + +			ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY; +			asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key); + +			if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) { + +				/* get size to read */ +				int ret1 = asn1_read_value (asn1_pub_key, "modulus", NULL, &modulus.size); +				int ret2 = asn1_read_value (asn1_pub_key, "publicExponent", NULL, &exponent.size); + +				modulus.data = gnutls_malloc(modulus.size); +				exponent.data = gnutls_malloc(exponent.size); + +				ret1 = asn1_read_value (asn1_pub_key, "modulus", modulus.data, &modulus.size); +				ret2 = asn1_read_value (asn1_pub_key, "publicExponent", exponent.data, &exponent.size); +				if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2) +					ret = 1; +			} +			if (asn1_pub_key) +				asn1_delete_structure(&asn1_pub_key); +		} +		if (pkcs1) +			asn1_delete_structure(&pkcs1); +	} + +	/* now generate certifcates */ +	if (1 == ret && 0 != modulus.size && 0 != exponent.size) { + +		gnutls_global_init(); +		int effthis = 0; +		gnutls_datum_t essentially_null = {strdup("abababababababab"), strlen("abababababababab")}; +	 +		gnutls_x509_privkey_t fake_privkey, root_privkey; +		gnutls_x509_crt_t dev_cert, root_cert; +	 +		gnutls_x509_privkey_init(&fake_privkey); +		gnutls_x509_crt_init(&dev_cert); +		 +		if ( GNUTLS_E_SUCCESS == gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null, &essentially_null, &essentially_null) ) { +		 +			gnutls_x509_privkey_init(&root_privkey); +			 +			/* get certificate stored in config */ +			*host_cert_b64 = get_host_certificate(); +			*root_cert_b64 = get_root_certificate(); + +			gnutls_datum_t pem_root_cert = {NULL, 0}; +			pem_root_cert.data = g_base64_decode (*root_cert_b64, &pem_root_cert.size); + +			ret = gnutls_x509_crt_import (root_cert, &pem_root_cert, GNUTLS_X509_FMT_PEM); +			gnutls_free(pem_root_cert.data); + + +			/* get root private key */ +			char *root_priv_b64 = get_root_private_key(); +			gnutls_datum_t pem_root_priv = {NULL, 0}; +			pem_root_priv.data = g_base64_decode (root_priv_b64, &pem_root_priv.size); + +			ret = gnutls_x509_privkey_import (root_privkey, &pem_root_priv, GNUTLS_X509_FMT_PEM); +			gnutls_free(pem_root_priv.data); + +			/* generate device certificate */ +			 +			gnutls_x509_crt_set_key(dev_cert, fake_privkey); +			gnutls_x509_crt_set_serial(dev_cert, "\x00", 1); +			gnutls_x509_crt_set_version(dev_cert, 3); +			gnutls_x509_crt_set_ca_status(dev_cert, 0); +			gnutls_x509_crt_set_activation_time(dev_cert, time(NULL)); +			gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); +			gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey); + +			//TODO handle errors +			ret = 1; + +			if (ret) { +				/* if everything went well, export in PEM format */ +	 +				gnutls_datum_t dev_pem = {NULL, 0}; +				gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &dev_pem.size); +				dev_pem.data = gnutls_malloc(dev_pem.size); +				gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_pem.data, &dev_pem.size); + +				/* now encode certificates for output */ +				*device_cert_b64 = g_base64_encode(dev_pem.data, dev_pem.size); +				ret = 1; +			} +		} +	} + +	gnutls_free(modulus.data); +	gnutls_free(exponent.data); + +	gnutls_free(der_pub_key.data); +	g_free(pem_pub_key.data); +	return ret; +} +  int lockdownd_start_SSL_session(lockdownd_client *control, const char *HostID) {  	xmlDocPtr plist = new_plist();  	xmlNode *dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); diff --git a/src/lockdown.h b/src/lockdown.h index 4abfe18..d24e770 100644 --- a/src/lockdown.h +++ b/src/lockdown.h @@ -35,13 +35,19 @@ typedef struct {  	int gtls_buffer_hack_len;  } lockdownd_client; +int lockdownd_init(iPhone *phone, lockdownd_client **control); +  lockdownd_client *new_lockdownd_client(iPhone *phone);  int lockdownd_hello(lockdownd_client *control); +int lockdownd_get_device_public_key(lockdownd_client *control, char **public_key); +int lockdownd_gen_pair_cert(char *public_key_b64, char **device_cert_b64, char **host_cert_b64, char **root_cert_b64); +int lockdownd_pair_device(lockdownd_client *control, char *public_key, char *host_id);  int lockdownd_recv(lockdownd_client *control, char **dump_data);  int lockdownd_send(lockdownd_client *control, char *raw_data, uint32 length);  void lockdownd_close(lockdownd_client *control);  // SSL functions +  int lockdownd_start_SSL_session(lockdownd_client *control, const char *HostID);  ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_t length);  ssize_t lockdownd_secuwrite(gnutls_transport_ptr_t transport, char *buffer, size_t length); @@ -45,7 +45,10 @@ int main(int argc, char *argv[]) {  	int bytes = 0, port = 0, i = 0;  	if (phone) printf("I got a phone.\n");  	else { printf("oops\n"); return -1; } -	 + +	lockdownd_client *control = NULL; +	lockdownd_init(phone, &control); +	/*  	lockdownd_client *control = new_lockdownd_client(phone);  	if (!lockdownd_hello(control)) {  		printf("Something went wrong in the lockdownd client, go take a look.\n"); @@ -59,10 +62,10 @@ int main(int argc, char *argv[]) {  		printf("Error happened in GnuTLS...\n");  	} else {   		free(host_id); -		host_id = NULL; +		host_id = NULL;*/  		printf("... we're in SSL with the phone... !?\n");  		port = lockdownd_start_service(control, "com.apple.afc"); -	} +	//}  	if (port) {  		printf("Start Service successful -- connect on port %i\n", port);  		AFClient *afc = afc_connect(phone, 3432, port); | 
