diff options
author | Jonathan Beck | 2008-08-03 20:47:47 +0200 |
---|---|---|
committer | Matt Colyer | 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); |