diff options
| author | 2008-08-03 20:47:47 +0200 | |
|---|---|---|
| committer | 2008-08-05 23:28:10 -0700 | |
| commit | b9f9675e1e3978693bb2e7f66a7125473b3cb30e (patch) | |
| tree | ca582a51bce704bfb764e31b8ef65403b4e6fc86 /src/lockdown.c | |
| parent | b25fea997fc798e945dd7f19f8d0be0d8d3289d1 (diff) | |
| download | libimobiledevice-b9f9675e1e3978693bb2e7f66a7125473b3cb30e.tar.gz libimobiledevice-b9f9675e1e3978693bb2e7f66a7125473b3cb30e.tar.bz2 | |
Initial pairing implementation.
Signed-off-by: Matt Colyer <matt@colyer.name>
Diffstat (limited to 'src/lockdown.c')
| -rw-r--r-- | src/lockdown.c | 290 |
1 files changed, 290 insertions, 0 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 @@ | |||
| 25 | #include "userpref.h" | 25 | #include "userpref.h" |
| 26 | #include <errno.h> | 26 | #include <errno.h> |
| 27 | #include <string.h> | 27 | #include <string.h> |
| 28 | #include <glib.h> | ||
| 29 | #include <libtasn1.h> | ||
| 28 | 30 | ||
| 29 | extern int debug; | 31 | extern int debug; |
| 30 | 32 | ||
| 33 | const ASN1_ARRAY_TYPE pkcs1_asn1_tab[]={ | ||
| 34 | {"PKCS1",536872976,0}, | ||
| 35 | {0,1073741836,0}, | ||
| 36 | {"RSAPublicKey",536870917,0}, | ||
| 37 | {"modulus",1073741827,0}, | ||
| 38 | {"publicExponent",3,0}, | ||
| 39 | {0,0,0} | ||
| 40 | }; | ||
| 41 | |||
| 42 | |||
| 43 | |||
| 31 | lockdownd_client *new_lockdownd_client(iPhone *phone) { | 44 | lockdownd_client *new_lockdownd_client(iPhone *phone) { |
| 32 | if (!phone) return NULL; | 45 | if (!phone) return NULL; |
| 33 | lockdownd_client *control = (lockdownd_client*)malloc(sizeof(lockdownd_client)); | 46 | lockdownd_client *control = (lockdownd_client*)malloc(sizeof(lockdownd_client)); |
| @@ -139,6 +152,283 @@ int lockdownd_hello(lockdownd_client *control) { | |||
| 139 | return 0; | 152 | return 0; |
| 140 | } | 153 | } |
| 141 | 154 | ||
| 155 | int lockdownd_get_device_public_key(lockdownd_client *control, char **public_key) | ||
| 156 | { | ||
| 157 | xmlDocPtr plist = new_plist(); | ||
| 158 | xmlNode *dict = NULL; | ||
| 159 | xmlNode *key = NULL;; | ||
| 160 | char **dictionary = NULL; | ||
| 161 | int bytes = 0, i = 0; | ||
| 162 | char *XML_content = NULL; | ||
| 163 | uint32 length = 0; | ||
| 164 | |||
| 165 | /* Setup DevicePublicKey request plist */ | ||
| 166 | dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); | ||
| 167 | key = add_key_str_dict_element(plist, dict, "Key", "DevicePublicKey", 1); | ||
| 168 | key = add_key_str_dict_element(plist, dict, "Request", "GetValue", 1); | ||
| 169 | xmlDocDumpMemory(plist, (xmlChar**)&XML_content, &length); | ||
| 170 | |||
| 171 | /* send to iPhone */ | ||
| 172 | bytes = lockdownd_send(control, XML_content, length); | ||
| 173 | |||
| 174 | xmlFree(XML_content); | ||
| 175 | xmlFreeDoc(plist); plist = NULL; | ||
| 176 | |||
| 177 | /* Now get iPhone's answer */ | ||
| 178 | bytes = lockdownd_recv(control, &XML_content); | ||
| 179 | |||
| 180 | plist = xmlReadMemory(XML_content, bytes, NULL, NULL, 0); | ||
| 181 | if (!plist) return 0; | ||
| 182 | dict = xmlDocGetRootElement(plist); | ||
| 183 | for (dict = dict->children; dict; dict = dict->next) { | ||
| 184 | if (!xmlStrcmp(dict->name, "dict")) break; | ||
| 185 | } | ||
| 186 | if (!dict) return 0; | ||
| 187 | |||
| 188 | /* Parse xml to check success and to find public key */ | ||
| 189 | dictionary = read_dict_element_strings(dict); | ||
| 190 | xmlFreeDoc(plist); | ||
| 191 | free(XML_content); | ||
| 192 | |||
| 193 | int success = 0; | ||
| 194 | for (i = 0; strcmp(dictionary[i], ""); i+=2) { | ||
| 195 | if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) { | ||
| 196 | success = 1; | ||
| 197 | } | ||
| 198 | if (!strcmp(dictionary[i], "Value")) { | ||
| 199 | *public_key = strdup(dictionary[i+1]); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | if (dictionary) { | ||
| 204 | free_dictionary(dictionary); | ||
| 205 | dictionary = NULL; | ||
| 206 | } | ||
| 207 | return success; | ||
| 208 | } | ||
| 209 | |||
| 210 | int lockdownd_init(iPhone *phone, lockdownd_client **control) | ||
| 211 | { | ||
| 212 | int ret = 0; | ||
| 213 | char *host_id = NULL; | ||
| 214 | |||
| 215 | if (!phone) | ||
| 216 | return 0; | ||
| 217 | |||
| 218 | *control = new_lockdownd_client(phone); | ||
| 219 | if (!lockdownd_hello(*control)){ | ||
| 220 | fprintf(stderr, "Hello failed in the lockdownd client.\n"); | ||
| 221 | } | ||
| 222 | |||
| 223 | char *public_key = NULL; | ||
| 224 | if(!lockdownd_get_device_public_key(*control, &public_key)){ | ||
| 225 | fprintf(stderr, "Device refused to send public key.\n"); | ||
| 226 | } | ||
| 227 | |||
| 228 | host_id = get_host_id(); | ||
| 229 | if (!is_device_known(public_key)){ | ||
| 230 | ret = lockdownd_pair_device(*control, public_key, host_id); | ||
| 231 | } | ||
| 232 | free(public_key); | ||
| 233 | public_key = NULL; | ||
| 234 | |||
| 235 | if (ret && host_id && !lockdownd_start_SSL_session(*control, host_id)) { | ||
| 236 | fprintf(stderr, "SSL Session opening failed.\n"); | ||
| 237 | } else { | ||
| 238 | ret = 1; | ||
| 239 | free(host_id); | ||
| 240 | host_id = NULL; | ||
| 241 | } | ||
| 242 | |||
| 243 | return ret; | ||
| 244 | } | ||
| 245 | |||
| 246 | int lockdownd_pair_device(lockdownd_client *control, char *public_key_b64, char *host_id) | ||
| 247 | { | ||
| 248 | int ret = 0; | ||
| 249 | xmlDocPtr plist = new_plist(); | ||
| 250 | xmlNode *dict = NULL; | ||
| 251 | xmlNode *dictRecord = NULL; | ||
| 252 | char **dictionary = NULL; | ||
| 253 | int bytes = 0, i = 0; | ||
| 254 | char *XML_content = NULL; | ||
| 255 | uint32 length = 0; | ||
| 256 | |||
| 257 | char* device_cert_b64 = NULL; | ||
| 258 | char* host_cert_b64 = NULL; | ||
| 259 | char* root_cert_b64 = NULL; | ||
| 260 | |||
| 261 | lockdownd_gen_pair_cert(public_key_b64, &device_cert_b64, &host_cert_b64, &root_cert_b64); | ||
| 262 | |||
| 263 | /* Setup Pair request plist */ | ||
| 264 | dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); | ||
| 265 | add_key_str_dict_element(plist, dict, "Key", "PairRecord", 1); | ||
| 266 | dictRecord = add_child_to_plist(plist, "dict", "\n", NULL, 1); | ||
| 267 | add_key_data_dict_element(plist, dictRecord, "DeviceCertificate", device_cert_b64, 2); | ||
| 268 | add_key_data_dict_element(plist, dictRecord, "HostCertificate", host_cert_b64, 2); | ||
| 269 | add_key_str_dict_element(plist, dictRecord, "HostID", host_id, 2); | ||
| 270 | add_key_data_dict_element(plist, dictRecord, "RootCertificate", root_cert_b64, 2); | ||
| 271 | add_key_str_dict_element(plist, dict, "Request", "Pair", 1); | ||
| 272 | |||
| 273 | xmlDocDumpMemory(plist, (xmlChar**)&XML_content, &length); | ||
| 274 | |||
| 275 | /* send to iPhone */ | ||
| 276 | bytes = lockdownd_send(control, XML_content, length); | ||
| 277 | |||
| 278 | xmlFree(XML_content); | ||
| 279 | xmlFreeDoc(plist); plist = NULL; | ||
| 280 | |||
| 281 | /* Now get iPhone's answer */ | ||
| 282 | bytes = lockdownd_recv(control, &XML_content); | ||
| 283 | |||
| 284 | plist = xmlReadMemory(XML_content, bytes, NULL, NULL, 0); | ||
| 285 | if (!plist) return 0; | ||
| 286 | dict = xmlDocGetRootElement(plist); | ||
| 287 | for (dict = dict->children; dict; dict = dict->next) { | ||
| 288 | if (!xmlStrcmp(dict->name, "dict")) break; | ||
| 289 | } | ||
| 290 | if (!dict) return 0; | ||
| 291 | |||
| 292 | /* Parse xml to check success and to find public key */ | ||
| 293 | dictionary = read_dict_element_strings(dict); | ||
| 294 | xmlFreeDoc(plist); | ||
| 295 | free(XML_content); | ||
| 296 | |||
| 297 | int success = 0; | ||
| 298 | for (i = 0; strcmp(dictionary[i], ""); i+=2) { | ||
| 299 | if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) { | ||
| 300 | success = 1; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | if (dictionary) { | ||
| 305 | free_dictionary(dictionary); | ||
| 306 | dictionary = NULL; | ||
| 307 | } | ||
| 308 | |||
| 309 | /* store public key in config if pairing succeeded */ | ||
| 310 | if (success) | ||
| 311 | store_device_public_key(public_key_b64); | ||
| 312 | return ret; | ||
| 313 | } | ||
| 314 | |||
| 315 | int lockdownd_gen_pair_cert(char *public_key_b64, char **device_cert_b64, char **host_cert_b64, char **root_cert_b64) | ||
| 316 | { | ||
| 317 | int ret = 0; | ||
| 318 | |||
| 319 | gnutls_datum_t modulus = {NULL, 0}; | ||
| 320 | gnutls_datum_t exponent = {NULL, 0}; | ||
| 321 | |||
| 322 | /* first decode base64 public_key */ | ||
| 323 | gnutls_datum_t pem_pub_key; | ||
| 324 | pem_pub_key.data = g_base64_decode (public_key_b64, &pem_pub_key.size); | ||
| 325 | |||
| 326 | |||
| 327 | /* now decode the PEM encoded key */ | ||
| 328 | gnutls_datum_t der_pub_key; | ||
| 329 | if( GNUTLS_E_SUCCESS == gnutls_pem_base64_decode_alloc ("RSA PUBLIC KEY", &pem_pub_key, &der_pub_key) ){ | ||
| 330 | ret = 1; | ||
| 331 | |||
| 332 | /* initalize asn.1 parser */ | ||
| 333 | ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY; | ||
| 334 | if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) { | ||
| 335 | |||
| 336 | ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY; | ||
| 337 | asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key); | ||
| 338 | |||
| 339 | if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) { | ||
| 340 | |||
| 341 | /* get size to read */ | ||
| 342 | int ret1 = asn1_read_value (asn1_pub_key, "modulus", NULL, &modulus.size); | ||
| 343 | int ret2 = asn1_read_value (asn1_pub_key, "publicExponent", NULL, &exponent.size); | ||
| 344 | |||
| 345 | modulus.data = gnutls_malloc(modulus.size); | ||
| 346 | exponent.data = gnutls_malloc(exponent.size); | ||
| 347 | |||
| 348 | ret1 = asn1_read_value (asn1_pub_key, "modulus", modulus.data, &modulus.size); | ||
| 349 | ret2 = asn1_read_value (asn1_pub_key, "publicExponent", exponent.data, &exponent.size); | ||
| 350 | if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2) | ||
| 351 | ret = 1; | ||
| 352 | } | ||
| 353 | if (asn1_pub_key) | ||
| 354 | asn1_delete_structure(&asn1_pub_key); | ||
| 355 | } | ||
| 356 | if (pkcs1) | ||
| 357 | asn1_delete_structure(&pkcs1); | ||
| 358 | } | ||
| 359 | |||
| 360 | /* now generate certifcates */ | ||
| 361 | if (1 == ret && 0 != modulus.size && 0 != exponent.size) { | ||
| 362 | |||
| 363 | gnutls_global_init(); | ||
| 364 | int effthis = 0; | ||
| 365 | gnutls_datum_t essentially_null = {strdup("abababababababab"), strlen("abababababababab")}; | ||
| 366 | |||
| 367 | gnutls_x509_privkey_t fake_privkey, root_privkey; | ||
| 368 | gnutls_x509_crt_t dev_cert, root_cert; | ||
| 369 | |||
| 370 | gnutls_x509_privkey_init(&fake_privkey); | ||
| 371 | gnutls_x509_crt_init(&dev_cert); | ||
| 372 | |||
| 373 | if ( GNUTLS_E_SUCCESS == gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null, &essentially_null, &essentially_null) ) { | ||
| 374 | |||
| 375 | gnutls_x509_privkey_init(&root_privkey); | ||
| 376 | |||
| 377 | /* get certificate stored in config */ | ||
| 378 | *host_cert_b64 = get_host_certificate(); | ||
| 379 | *root_cert_b64 = get_root_certificate(); | ||
| 380 | |||
| 381 | gnutls_datum_t pem_root_cert = {NULL, 0}; | ||
| 382 | pem_root_cert.data = g_base64_decode (*root_cert_b64, &pem_root_cert.size); | ||
| 383 | |||
| 384 | ret = gnutls_x509_crt_import (root_cert, &pem_root_cert, GNUTLS_X509_FMT_PEM); | ||
| 385 | gnutls_free(pem_root_cert.data); | ||
| 386 | |||
| 387 | |||
| 388 | /* get root private key */ | ||
| 389 | char *root_priv_b64 = get_root_private_key(); | ||
| 390 | gnutls_datum_t pem_root_priv = {NULL, 0}; | ||
| 391 | pem_root_priv.data = g_base64_decode (root_priv_b64, &pem_root_priv.size); | ||
| 392 | |||
| 393 | ret = gnutls_x509_privkey_import (root_privkey, &pem_root_priv, GNUTLS_X509_FMT_PEM); | ||
| 394 | gnutls_free(pem_root_priv.data); | ||
| 395 | |||
| 396 | /* generate device certificate */ | ||
| 397 | |||
| 398 | gnutls_x509_crt_set_key(dev_cert, fake_privkey); | ||
| 399 | gnutls_x509_crt_set_serial(dev_cert, "\x00", 1); | ||
| 400 | gnutls_x509_crt_set_version(dev_cert, 3); | ||
| 401 | gnutls_x509_crt_set_ca_status(dev_cert, 0); | ||
| 402 | gnutls_x509_crt_set_activation_time(dev_cert, time(NULL)); | ||
| 403 | gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
| 404 | gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey); | ||
| 405 | |||
| 406 | //TODO handle errors | ||
| 407 | ret = 1; | ||
| 408 | |||
| 409 | if (ret) { | ||
| 410 | /* if everything went well, export in PEM format */ | ||
| 411 | |||
| 412 | gnutls_datum_t dev_pem = {NULL, 0}; | ||
| 413 | gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &dev_pem.size); | ||
| 414 | dev_pem.data = gnutls_malloc(dev_pem.size); | ||
| 415 | gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_pem.data, &dev_pem.size); | ||
| 416 | |||
| 417 | /* now encode certificates for output */ | ||
| 418 | *device_cert_b64 = g_base64_encode(dev_pem.data, dev_pem.size); | ||
| 419 | ret = 1; | ||
| 420 | } | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | gnutls_free(modulus.data); | ||
| 425 | gnutls_free(exponent.data); | ||
| 426 | |||
| 427 | gnutls_free(der_pub_key.data); | ||
| 428 | g_free(pem_pub_key.data); | ||
| 429 | return ret; | ||
| 430 | } | ||
| 431 | |||
| 142 | int lockdownd_start_SSL_session(lockdownd_client *control, const char *HostID) { | 432 | int lockdownd_start_SSL_session(lockdownd_client *control, const char *HostID) { |
| 143 | xmlDocPtr plist = new_plist(); | 433 | xmlDocPtr plist = new_plist(); |
| 144 | xmlNode *dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); | 434 | xmlNode *dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); |
