summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Jonathan Beck2008-08-03 20:47:47 +0200
committerGravatar Matt Colyer2008-08-05 23:28:10 -0700
commitb9f9675e1e3978693bb2e7f66a7125473b3cb30e (patch)
treeca582a51bce704bfb764e31b8ef65403b4e6fc86
parentb25fea997fc798e945dd7f19f8d0be0d8d3289d1 (diff)
downloadlibimobiledevice-b9f9675e1e3978693bb2e7f66a7125473b3cb30e.tar.gz
libimobiledevice-b9f9675e1e3978693bb2e7f66a7125473b3cb30e.tar.bz2
Initial pairing implementation.
Signed-off-by: Matt Colyer <matt@colyer.name>
-rw-r--r--src/lockdown.c290
-rw-r--r--src/lockdown.h6
-rw-r--r--src/main.c9
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);
diff --git a/src/main.c b/src/main.c
index 3ab7139..1efc228 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);