3
* libimobiledevice built-in lockdownd client
5
* Copyright (c) 2008 Zach C. All Rights Reserved.
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License as published by the Free Software Foundation; either
10
* version 2.1 of the License, or (at your option) any later version.
12
* This library is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this library; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
#include <arpa/inet.h>
28
#include <gnutls/x509.h>
29
#include <plist/plist.h>
31
#include "property_list_service.h"
37
#define RESULT_SUCCESS 0
38
#define RESULT_FAILURE 1
40
const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
41
{"PKCS1", 536872976, 0},
43
{"RSAPublicKey", 536870917, 0},
44
{"modulus", 1073741827, 0},
45
{"publicExponent", 3, 0},
50
* Internally used function for checking the result from lockdown's answer
51
* plist to a previously sent request.
53
* @param dict The plist to evaluate.
54
* @param query_match Name of the request to match.
56
* @return RESULT_SUCCESS when the result is 'Success',
57
* RESULT_FAILURE when the result is 'Failure',
58
* or a negative value if an error occured during evaluation.
60
static int lockdown_check_result(plist_t dict, const char *query_match)
64
plist_t query_node = plist_dict_get_item(dict, "Request");
68
if (plist_get_node_type(query_node) != PLIST_STRING) {
71
char *query_value = NULL;
72
plist_get_string_val(query_node, &query_value);
76
if (strcmp(query_value, query_match) != 0) {
83
plist_t result_node = plist_dict_get_item(dict, "Result");
88
plist_type result_type = plist_get_node_type(result_node);
90
if (result_type == PLIST_STRING) {
92
char *result_value = NULL;
94
plist_get_string_val(result_node, &result_value);
97
if (!strcmp(result_value, "Success")) {
99
} else if (!strcmp(result_value, "Failure")) {
100
ret = RESULT_FAILURE;
102
debug_info("ERROR: unknown result value '%s'", result_value);
112
* Adds a label key with the passed value to a plist dict node.
114
* @param plist The plist to add the key to
115
* @param label The value for the label key
118
static void plist_dict_add_label(plist_t plist, const char *label)
120
if (plist && label) {
121
if (plist_get_node_type(plist) == PLIST_DICT)
122
plist_dict_insert_item(plist, "Label", plist_new_string(label));
127
* Closes the lockdownd communication session, by sending the StopSession
128
* Request to the device.
130
* @see lockdownd_start_session
132
* @param control The lockdown client
133
* @param session_id The id of a running session
135
* @return an error code (LOCKDOWN_E_SUCCESS on success)
137
lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id)
140
return LOCKDOWN_E_INVALID_ARG;
143
debug_info("no session_id given, cannot stop session");
144
return LOCKDOWN_E_INVALID_ARG;
147
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
149
plist_t dict = plist_new_dict();
150
plist_dict_add_label(dict, client->label);
151
plist_dict_insert_item(dict,"Request", plist_new_string("StopSession"));
152
plist_dict_insert_item(dict,"SessionID", plist_new_string(session_id));
154
debug_info("stopping session %s", session_id);
156
ret = lockdownd_send(client, dict);
161
ret = lockdownd_receive(client, &dict);
164
debug_info("LOCKDOWN_E_PLIST_ERROR");
165
return LOCKDOWN_E_PLIST_ERROR;
168
ret = LOCKDOWN_E_UNKNOWN_ERROR;
169
if (lockdown_check_result(dict, "StopSession") == RESULT_SUCCESS) {
170
debug_info("success");
171
ret = LOCKDOWN_E_SUCCESS;
175
if (client->ssl_enabled) {
176
property_list_service_disable_ssl(client->parent);
181
/** Closes the lockdownd client and does the necessary housekeeping.
183
* @param client The lockdown client
185
* @return an error code (LOCKDOWN_E_SUCCESS on success)
187
lockdownd_error_t lockdownd_client_free(lockdownd_client_t client)
190
return LOCKDOWN_E_INVALID_ARG;
191
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
193
if (client->session_id)
194
lockdownd_stop_session(client, client->session_id);
196
if (client->parent) {
197
lockdownd_goodbye(client);
199
if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) {
200
ret = LOCKDOWN_E_SUCCESS;
216
* Sets the label to send for requests to lockdownd.
218
* @param client The lockdown client
219
* @param label The label to set or NULL to disable sending a label
222
void lockdownd_client_set_label(lockdownd_client_t client, const char *label)
228
client->label = (label != NULL) ? strdup(label): NULL;
232
/** Polls the device for lockdownd data.
234
* @param control The lockdownd client
235
* @param plist The plist to store the received data
237
* @return an error code (LOCKDOWN_E_SUCCESS on success)
239
lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist)
241
if (!client || !plist || (plist && *plist))
242
return LOCKDOWN_E_INVALID_ARG;
243
lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
244
property_list_service_error_t err;
246
err = property_list_service_receive_plist(client->parent, plist);
247
if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
248
ret = LOCKDOWN_E_UNKNOWN_ERROR;
252
ret = LOCKDOWN_E_PLIST_ERROR;
257
/** Sends lockdownd data to the device
259
* @note This function is low-level and should only be used if you need to send
260
* a new type of message.
262
* @param client The lockdownd client
263
* @param plist The plist to send
265
* @return an error code (LOCKDOWN_E_SUCCESS on success)
267
lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist)
269
if (!client || !plist)
270
return LOCKDOWN_E_INVALID_ARG;
272
lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
275
err = property_list_service_send_xml_plist(client->parent, plist);
276
if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
277
ret = LOCKDOWN_E_UNKNOWN_ERROR;
282
/** Query the type of the service daemon. Depending on whether the device is
283
* queried in normal mode or restore mode, different types will be returned.
285
* @param client The lockdownd client
286
* @param type The type returned by the service daemon. Can be NULL to ignore.
288
* @return an error code (LOCKDOWN_E_SUCCESS on success)
290
lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
293
return LOCKDOWN_E_INVALID_ARG;
295
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
297
plist_t dict = plist_new_dict();
298
plist_dict_add_label(dict, client->label);
299
plist_dict_insert_item(dict,"Request", plist_new_string("QueryType"));
301
debug_info("called");
302
ret = lockdownd_send(client, dict);
307
ret = lockdownd_receive(client, &dict);
309
if (LOCKDOWN_E_SUCCESS != ret)
312
ret = LOCKDOWN_E_UNKNOWN_ERROR;
313
if (lockdown_check_result(dict, "QueryType") == RESULT_SUCCESS) {
314
/* return the type if requested */
316
plist_t type_node = plist_dict_get_item(dict, "Type");
317
plist_get_string_val(type_node, type);
319
debug_info("success with type %s", *type);
320
ret = LOCKDOWN_E_SUCCESS;
328
/** Retrieves a preferences plist using an optional domain and/or key name.
330
* @param client an initialized lockdownd client.
331
* @param domain the domain to query on or NULL for global domain
332
* @param key the key name to request or NULL to query for all keys
333
* @param value a plist node representing the result value node
335
* @return an error code (LOCKDOWN_E_SUCCESS on success)
337
lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value)
340
return LOCKDOWN_E_INVALID_ARG;
343
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
345
/* setup request plist */
346
dict = plist_new_dict();
347
plist_dict_add_label(dict, client->label);
349
plist_dict_insert_item(dict,"Domain", plist_new_string(domain));
352
plist_dict_insert_item(dict,"Key", plist_new_string(key));
354
plist_dict_insert_item(dict,"Request", plist_new_string("GetValue"));
357
ret = lockdownd_send(client, dict);
362
if (ret != LOCKDOWN_E_SUCCESS)
365
/* Now get device's answer */
366
ret = lockdownd_receive(client, &dict);
367
if (ret != LOCKDOWN_E_SUCCESS)
370
if (lockdown_check_result(dict, "GetValue") == RESULT_SUCCESS) {
371
debug_info("success");
372
ret = LOCKDOWN_E_SUCCESS;
374
if (ret != LOCKDOWN_E_SUCCESS) {
379
plist_t value_node = plist_dict_get_item(dict, "Value");
382
debug_info("has a value");
383
*value = plist_copy(value_node);
390
/** Sets a preferences value using a plist and optional domain and/or key name.
392
* @param client an initialized lockdownd client.
393
* @param domain the domain to query on or NULL for global domain
394
* @param key the key name to set the value or NULL to set a value dict plist
395
* @param value a plist node of any node type representing the value to set
397
* @return an error code (LOCKDOWN_E_SUCCESS on success)
399
lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value)
401
if (!client || !value)
402
return LOCKDOWN_E_INVALID_ARG;
405
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
407
/* setup request plist */
408
dict = plist_new_dict();
409
plist_dict_add_label(dict, client->label);
411
plist_dict_insert_item(dict,"Domain", plist_new_string(domain));
414
plist_dict_insert_item(dict,"Key", plist_new_string(key));
416
plist_dict_insert_item(dict,"Request", plist_new_string("SetValue"));
417
plist_dict_insert_item(dict,"Value", value);
420
ret = lockdownd_send(client, dict);
425
if (ret != LOCKDOWN_E_SUCCESS)
428
/* Now get device's answer */
429
ret = lockdownd_receive(client, &dict);
430
if (ret != LOCKDOWN_E_SUCCESS)
433
if (lockdown_check_result(dict, "SetValue") == RESULT_SUCCESS) {
434
debug_info("success");
435
ret = LOCKDOWN_E_SUCCESS;
438
if (ret != LOCKDOWN_E_SUCCESS) {
447
/** Removes a preference node on the device by domain and/or key name
449
* @note: Use with caution as this could remove vital information on the device
451
* @param client an initialized lockdownd client.
452
* @param domain the domain to query on or NULL for global domain
453
* @param key the key name to remove or NULL remove all keys for the current domain
455
* @return an error code (LOCKDOWN_E_SUCCESS on success)
457
lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key)
460
return LOCKDOWN_E_INVALID_ARG;
463
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
465
/* setup request plist */
466
dict = plist_new_dict();
467
plist_dict_add_label(dict, client->label);
469
plist_dict_insert_item(dict,"Domain", plist_new_string(domain));
472
plist_dict_insert_item(dict,"Key", plist_new_string(key));
474
plist_dict_insert_item(dict,"Request", plist_new_string("RemoveValue"));
477
ret = lockdownd_send(client, dict);
482
if (ret != LOCKDOWN_E_SUCCESS)
485
/* Now get device's answer */
486
ret = lockdownd_receive(client, &dict);
487
if (ret != LOCKDOWN_E_SUCCESS)
490
if (lockdown_check_result(dict, "RemoveValue") == RESULT_SUCCESS) {
491
debug_info("success");
492
ret = LOCKDOWN_E_SUCCESS;
495
if (ret != LOCKDOWN_E_SUCCESS) {
504
/** Asks for the device's unique id. Part of the lockdownd handshake.
506
* @return an error code (LOCKDOWN_E_SUCCESS on success)
508
lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uuid)
510
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
511
plist_t value = NULL;
513
ret = lockdownd_get_value(client, NULL, "UniqueDeviceID", &value);
514
if (ret != LOCKDOWN_E_SUCCESS) {
517
plist_get_string_val(value, uuid);
524
/** Askes for the device's public key. Part of the lockdownd handshake.
526
* @return an error code (LOCKDOWN_E_SUCCESS on success)
528
lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key)
530
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
531
plist_t value = NULL;
532
char *value_value = NULL;
535
ret = lockdownd_get_value(client, NULL, "DevicePublicKey", &value);
536
if (ret != LOCKDOWN_E_SUCCESS) {
539
plist_get_data_val(value, &value_value, &size);
540
public_key->data = (unsigned char*)value_value;
541
public_key->size = size;
549
/** Askes for the device's name.
551
* @param client The pointer to the location of the new lockdownd_client
554
* @return an error code (LOCKDOWN_E_SUCCESS on success)
556
lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name)
558
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
559
plist_t value = NULL;
561
ret = lockdownd_get_value(client, NULL, "DeviceName", &value);
562
if (ret != LOCKDOWN_E_SUCCESS) {
565
plist_get_string_val(value, device_name);
573
/** Creates a lockdownd client for the device.
575
* @param phone The device to create a lockdownd client for
576
* @param client The pointer to the location of the new lockdownd_client
577
* @param label The label to use for communication. Usually the program name
579
* @return an error code (LOCKDOWN_E_SUCCESS on success)
581
lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label)
584
return LOCKDOWN_E_INVALID_ARG;
586
lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
588
property_list_service_client_t plistclient = NULL;
589
if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
590
debug_info("could not connect to lockdownd (device %s)", device->uuid);
591
return LOCKDOWN_E_MUX_ERROR;
594
lockdownd_client_t client_loc = (lockdownd_client_t) malloc(sizeof(struct lockdownd_client_int));
595
client_loc->parent = plistclient;
596
client_loc->ssl_enabled = 0;
597
client_loc->session_id = NULL;
598
client_loc->uuid = NULL;
599
client_loc->label = NULL;
603
if (LOCKDOWN_E_SUCCESS == ret) {
604
*client = client_loc;
606
lockdownd_client_free(client_loc);
612
/** Creates a lockdownd client for the device and starts initial handshake.
613
* The handshake consists of query_type, validate_pair, pair and
614
* start_session calls.
616
* @param phone The device to create a lockdownd client for
617
* @param client The pointer to the location of the new lockdownd_client
618
* @param label The label to use for communication. Usually the program name
620
* @return an error code (LOCKDOWN_E_SUCCESS on success)
622
lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label)
625
return LOCKDOWN_E_INVALID_ARG;
627
lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
628
lockdownd_client_t client_loc = NULL;
629
char *host_id = NULL;
633
ret = lockdownd_client_new(device, &client_loc, label);
635
/* perform handshake */
636
if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc, &type)) {
637
debug_info("QueryType failed in the lockdownd client.");
638
ret = LOCKDOWN_E_NOT_ENOUGH_DATA;
640
if (strcmp("com.apple.mobile.lockdown", type)) {
641
debug_info("Warning QueryType request returned \"%s\".", type);
647
ret = idevice_get_uuid(device, &client_loc->uuid);
648
if (LOCKDOWN_E_SUCCESS != ret) {
649
debug_info("failed to get device uuid.");
651
debug_info("device uuid: %s", client_loc->uuid);
653
userpref_get_host_id(&host_id);
654
if (LOCKDOWN_E_SUCCESS == ret && !host_id) {
655
ret = LOCKDOWN_E_INVALID_CONF;
658
if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->uuid))
659
ret = lockdownd_pair(client_loc, NULL);
661
/* in any case, we need to validate pairing to receive trusted host status */
662
ret = lockdownd_validate_pair(client_loc, NULL);
664
/* if not paired yet, let's do it now */
665
if (LOCKDOWN_E_INVALID_HOST_ID == ret) {
666
ret = lockdownd_pair(client_loc, NULL);
667
if (LOCKDOWN_E_SUCCESS == ret) {
668
ret = lockdownd_validate_pair(client_loc, NULL);
672
if (LOCKDOWN_E_SUCCESS == ret) {
673
ret = lockdownd_start_session(client_loc, host_id, NULL, NULL);
674
if (LOCKDOWN_E_SUCCESS != ret) {
675
debug_info("Session opening failed.");
684
if (LOCKDOWN_E_SUCCESS == ret) {
685
*client = client_loc;
687
lockdownd_client_free(client_loc);
693
static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_record)
698
char *host_id_loc = pair_record->host_id;
700
/* setup request plist */
701
plist_t dict = plist_new_dict();
702
plist_dict_insert_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate)));
703
plist_dict_insert_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate)));
704
if (!pair_record->host_id)
705
userpref_get_host_id(&host_id_loc);
706
plist_dict_insert_item(dict, "HostID", plist_new_string(host_id_loc));
707
plist_dict_insert_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate)));
709
if (!pair_record->host_id)
715
static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, char *host_id, plist_t *pair_record_plist)
717
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
719
gnutls_datum_t device_cert = { NULL, 0 };
720
gnutls_datum_t host_cert = { NULL, 0 };
721
gnutls_datum_t root_cert = { NULL, 0 };
723
ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert);
724
if (ret != LOCKDOWN_E_SUCCESS) {
728
char *host_id_loc = host_id;
731
userpref_get_host_id(&host_id_loc);
733
/* setup request plist */
734
*pair_record_plist = plist_new_dict();
735
plist_dict_insert_item(*pair_record_plist, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size));
736
plist_dict_insert_item(*pair_record_plist, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size));
737
plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id_loc));
738
plist_dict_insert_item(*pair_record_plist, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size));
746
/** Function used internally by lockdownd_pair() and lockdownd_validate_pair()
748
* @param client The lockdown client to pair with.
749
* @param pair_record The pair record to use for pairing. If NULL is passed, then
750
* the pair records from the current machine are used. New records will be
751
* generated automatically when pairing is done for the first time.
752
* @param verb This is either "Pair", "ValidatePair" or "Unpair".
754
* @return an error code (LOCKDOWN_E_SUCCESS on success)
756
static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb)
758
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
760
plist_t dict_record = NULL;
761
gnutls_datum_t public_key = { NULL, 0 };
762
int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */
764
if (pair_record && pair_record->host_id) {
765
/* valid pair_record passed? */
766
if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) {
767
return LOCKDOWN_E_PLIST_ERROR;
770
/* use passed pair_record */
771
dict_record = lockdownd_pair_record_to_plist(pair_record);
775
ret = lockdownd_get_device_public_key(client, &public_key);
776
if (ret != LOCKDOWN_E_SUCCESS) {
778
free(public_key.data);
779
debug_info("device refused to send public key.");
782
debug_info("device public key follows:\n%s", public_key.data);
783
/* get libimobiledevice pair_record */
784
ret = generate_pair_record_plist(public_key, NULL, &dict_record);
785
if (ret != LOCKDOWN_E_SUCCESS) {
787
plist_free(dict_record);
792
/* Setup Pair request plist */
793
dict = plist_new_dict();
794
plist_dict_add_label(dict, client->label);
795
plist_dict_insert_item(dict,"PairRecord", dict_record);
796
plist_dict_insert_item(dict, "Request", plist_new_string(verb));
799
ret = lockdownd_send(client, dict);
803
if (ret != LOCKDOWN_E_SUCCESS)
806
/* Now get device's answer */
807
ret = lockdownd_receive(client, &dict);
809
if (ret != LOCKDOWN_E_SUCCESS)
812
if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) {
813
ret = LOCKDOWN_E_PAIRING_FAILED;
816
/* if pairing succeeded */
817
if (ret == LOCKDOWN_E_SUCCESS) {
818
debug_info("%s success", verb);
820
if (!strcmp("Unpair", verb)) {
821
/* remove public key from config */
822
userpref_remove_device_public_key(client->uuid);
824
/* store public key in config */
825
userpref_set_device_public_key(client->uuid, public_key);
829
debug_info("%s failure", verb);
830
plist_t error_node = NULL;
831
/* verify error condition */
832
error_node = plist_dict_get_item(dict, "Error");
835
plist_get_string_val(error_node, &value);
837
/* the first pairing fails if the device is password protected */
838
if (!strcmp(value, "PasswordProtected")) {
839
ret = LOCKDOWN_E_PASSWORD_PROTECTED;
840
} else if (!strcmp(value, "InvalidHostID")) {
841
ret = LOCKDOWN_E_INVALID_HOST_ID;
846
plist_free(error_node);
853
free(public_key.data);
858
* Pairs the device with the given HostID.
859
* It's part of the lockdownd handshake.
861
* @param client The lockdown client to pair with.
862
* @param pair_record The pair record to use for pairing. If NULL is passed, then
863
* the pair records from the current machine are used. New records will be
864
* generated automatically when pairing is done for the first time.
866
* @return an error code (LOCKDOWN_E_SUCCESS on success)
868
lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
870
return lockdownd_do_pair(client, pair_record, "Pair");
874
* Pairs the device with the given HostID. The difference to lockdownd_pair()
875
* is that the specified host will become trusted host of the device.
876
* It's part of the lockdownd handshake.
878
* @param client The lockdown client to pair with.
879
* @param pair_record The pair record to validate pairing with. If NULL is
880
* passed, then the pair records from the current machine are used.
881
* New records will be generated automatically when pairing is done
882
* for the first time.
884
* @return an error code (LOCKDOWN_E_SUCCESS on success)
886
lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
888
return lockdownd_do_pair(client, pair_record, "ValidatePair");
892
* Unpairs the device with the given HostID and removes the pairing records
893
* from the device and host.
895
* @param client The lockdown client to pair with.
896
* @param pair_record The pair record to use for unpair. If NULL is passed, then
897
* the pair records from the current machine are used.
899
* @return an error code (LOCKDOWN_E_SUCCESS on success)
901
lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
903
return lockdownd_do_pair(client, pair_record, "Unpair");
907
* Tells the device to immediately enter recovery mode.
909
* @param client The lockdown client
911
* @return an error code (LOCKDOWN_E_SUCCESS on success)
913
lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
916
return LOCKDOWN_E_INVALID_ARG;
918
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
920
plist_t dict = plist_new_dict();
921
plist_dict_add_label(dict, client->label);
922
plist_dict_insert_item(dict,"Request", plist_new_string("EnterRecovery"));
924
debug_info("telling device to enter recovery mode");
926
ret = lockdownd_send(client, dict);
930
ret = lockdownd_receive(client, &dict);
932
if (lockdown_check_result(dict, "EnterRecovery") == RESULT_SUCCESS) {
933
debug_info("success");
934
ret = LOCKDOWN_E_SUCCESS;
942
* Performs the Goodbye Request to tell the device the communication
943
* session is now closed.
945
* @param client The lockdown client
947
* @return an error code (LOCKDOWN_E_SUCCESS on success)
949
lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
952
return LOCKDOWN_E_INVALID_ARG;
954
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
956
plist_t dict = plist_new_dict();
957
plist_dict_add_label(dict, client->label);
958
plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye"));
960
debug_info("called");
962
ret = lockdownd_send(client, dict);
966
ret = lockdownd_receive(client, &dict);
968
debug_info("did not get goodbye response back");
969
return LOCKDOWN_E_PLIST_ERROR;
972
if (lockdown_check_result(dict, "Goodbye") == RESULT_SUCCESS) {
973
debug_info("success");
974
ret = LOCKDOWN_E_SUCCESS;
981
/** Generates the device certificate from the public key as well as the host
982
* and root certificates.
984
* @return an error code (LOCKDOWN_E_SUCCESS on success)
986
lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * odevice_cert,
987
gnutls_datum_t * ohost_cert, gnutls_datum_t * oroot_cert)
989
if (!public_key.data || !odevice_cert || !ohost_cert || !oroot_cert)
990
return LOCKDOWN_E_INVALID_ARG;
991
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
992
userpref_error_t uret = USERPREF_E_UNKNOWN_ERROR;
994
gnutls_datum_t modulus = { NULL, 0 };
995
gnutls_datum_t exponent = { NULL, 0 };
997
/* now decode the PEM encoded key */
998
gnutls_datum_t der_pub_key;
999
if (GNUTLS_E_SUCCESS == gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key)) {
1001
/* initalize asn.1 parser */
1002
ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY;
1003
if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) {
1005
ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY;
1006
asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key);
1008
if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) {
1010
/* get size to read */
1011
int ret1 = asn1_read_value(asn1_pub_key, "modulus", NULL, (int*)&modulus.size);
1012
int ret2 = asn1_read_value(asn1_pub_key, "publicExponent", NULL, (int*)&exponent.size);
1014
modulus.data = gnutls_malloc(modulus.size);
1015
exponent.data = gnutls_malloc(exponent.size);
1017
ret1 = asn1_read_value(asn1_pub_key, "modulus", modulus.data, (int*)&modulus.size);
1018
ret2 = asn1_read_value(asn1_pub_key, "publicExponent", exponent.data, (int*)&exponent.size);
1019
if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2)
1020
ret = LOCKDOWN_E_SUCCESS;
1023
asn1_delete_structure(&asn1_pub_key);
1026
asn1_delete_structure(&pkcs1);
1029
/* now generate certificates */
1030
if (LOCKDOWN_E_SUCCESS == ret && 0 != modulus.size && 0 != exponent.size) {
1032
gnutls_global_init();
1033
gnutls_datum_t essentially_null = { (unsigned char*)strdup("abababababababab"), strlen("abababababababab") };
1035
gnutls_x509_privkey_t fake_privkey, root_privkey, host_privkey;
1036
gnutls_x509_crt_t dev_cert, root_cert, host_cert;
1038
gnutls_x509_privkey_init(&fake_privkey);
1039
gnutls_x509_crt_init(&dev_cert);
1040
gnutls_x509_crt_init(&root_cert);
1041
gnutls_x509_crt_init(&host_cert);
1043
if (GNUTLS_E_SUCCESS ==
1044
gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null,
1045
&essentially_null, &essentially_null)) {
1047
gnutls_x509_privkey_init(&root_privkey);
1048
gnutls_x509_privkey_init(&host_privkey);
1050
uret = userpref_get_keys_and_certs(root_privkey, root_cert, host_privkey, host_cert);
1052
if (USERPREF_E_SUCCESS == uret) {
1053
/* generate device certificate */
1054
gnutls_x509_crt_set_key(dev_cert, fake_privkey);
1055
gnutls_x509_crt_set_serial(dev_cert, "\x00", 1);
1056
gnutls_x509_crt_set_version(dev_cert, 3);
1057
gnutls_x509_crt_set_ca_status(dev_cert, 0);
1058
gnutls_x509_crt_set_activation_time(dev_cert, time(NULL));
1059
gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
1060
gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey);
1062
if (LOCKDOWN_E_SUCCESS == ret) {
1063
/* if everything went well, export in PEM format */
1064
size_t export_size = 0;
1065
gnutls_datum_t dev_pem = { NULL, 0 };
1066
gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &export_size);
1067
dev_pem.data = gnutls_malloc(export_size);
1068
gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_pem.data, &export_size);
1069
dev_pem.size = export_size;
1071
gnutls_datum_t pem_root_cert = { NULL, 0 };
1072
gnutls_datum_t pem_host_cert = { NULL, 0 };
1074
uret = userpref_get_certs_as_pem(&pem_root_cert, &pem_host_cert);
1076
if (USERPREF_E_SUCCESS == uret) {
1077
/* copy buffer for output */
1078
odevice_cert->data = malloc(dev_pem.size);
1079
memcpy(odevice_cert->data, dev_pem.data, dev_pem.size);
1080
odevice_cert->size = dev_pem.size;
1082
ohost_cert->data = malloc(pem_host_cert.size);
1083
memcpy(ohost_cert->data, pem_host_cert.data, pem_host_cert.size);
1084
ohost_cert->size = pem_host_cert.size;
1086
oroot_cert->data = malloc(pem_root_cert.size);
1087
memcpy(oroot_cert->data, pem_root_cert.data, pem_root_cert.size);
1088
oroot_cert->size = pem_root_cert.size;
1090
g_free(pem_root_cert.data);
1091
g_free(pem_host_cert.data);
1097
case USERPREF_E_INVALID_ARG:
1098
ret = LOCKDOWN_E_INVALID_ARG;
1100
case USERPREF_E_INVALID_CONF:
1101
ret = LOCKDOWN_E_INVALID_CONF;
1103
case USERPREF_E_SSL_ERROR:
1104
ret = LOCKDOWN_E_SSL_ERROR;
1111
gnutls_free(modulus.data);
1112
gnutls_free(exponent.data);
1114
gnutls_free(der_pub_key.data);
1119
/** Starts communication with lockdownd after the device has been paired,
1120
* and if the device requires it, switches to SSL mode.
1122
* @param client The lockdownd client
1123
* @param host_id The HostID of the computer
1124
* @param session_id The session_id of the created session
1125
* @param ssl_enabled Whether SSL communication is used in the session
1127
* @return an error code (LOCKDOWN_E_SUCCESS on success)
1129
lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled)
1131
lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
1132
plist_t dict = NULL;
1134
if (!client || !host_id)
1135
ret = LOCKDOWN_E_INVALID_ARG;
1137
/* if we have a running session, stop current one first */
1138
if (client->session_id) {
1139
lockdownd_stop_session(client, client->session_id);
1142
/* setup request plist */
1143
dict = plist_new_dict();
1144
plist_dict_add_label(dict, client->label);
1145
plist_dict_insert_item(dict,"HostID", plist_new_string(host_id));
1146
plist_dict_insert_item(dict,"Request", plist_new_string("StartSession"));
1148
ret = lockdownd_send(client, dict);
1152
if (ret != LOCKDOWN_E_SUCCESS)
1155
ret = lockdownd_receive(client, &dict);
1158
return LOCKDOWN_E_PLIST_ERROR;
1160
if (lockdown_check_result(dict, "StartSession") == RESULT_FAILURE) {
1161
plist_t error_node = plist_dict_get_item(dict, "Error");
1162
if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1164
plist_get_string_val(error_node, &error);
1165
if (!strcmp(error, "InvalidHostID")) {
1166
ret = LOCKDOWN_E_INVALID_HOST_ID;
1171
uint8_t use_ssl = 0;
1173
plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL");
1174
if (enable_ssl && (plist_get_node_type(enable_ssl) == PLIST_BOOLEAN)) {
1175
plist_get_bool_val(enable_ssl, &use_ssl);
1177
debug_info("Session startup OK");
1179
if (ssl_enabled != NULL)
1180
*ssl_enabled = use_ssl;
1182
/* store session id, we need it for StopSession */
1183
plist_t session_node = plist_dict_get_item(dict, "SessionID");
1184
if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) {
1185
plist_get_string_val(session_node, &client->session_id);
1187
if (client->session_id) {
1188
debug_info("SessionID: %s", client->session_id);
1189
if (session_id != NULL)
1190
*session_id = strdup(client->session_id);
1192
debug_info("Failed to get SessionID!");
1194
debug_info("Enable SSL Session: %s", (use_ssl?"true":"false"));
1196
ret = property_list_service_enable_ssl(client->parent);
1197
if (ret == PROPERTY_LIST_SERVICE_E_SUCCESS) {
1198
client->ssl_enabled = 1;
1200
ret = LOCKDOWN_E_SSL_ERROR;
1201
client->ssl_enabled = 0;
1204
client->ssl_enabled = 0;
1205
ret = LOCKDOWN_E_SUCCESS;
1215
/** Command to start the desired service
1217
* @param client The lockdownd client
1218
* @param service The name of the service to start
1219
* @param port The port number the service was started on
1221
* @return an error code (LOCKDOWN_E_SUCCESS on success)
1223
lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, uint16_t *port)
1225
if (!client || !service || !port)
1226
return LOCKDOWN_E_INVALID_ARG;
1228
char *host_id = NULL;
1229
userpref_get_host_id(&host_id);
1231
return LOCKDOWN_E_INVALID_CONF;
1232
if (!client->session_id)
1233
return LOCKDOWN_E_NO_RUNNING_SESSION;
1235
plist_t dict = NULL;
1236
uint16_t port_loc = 0;
1237
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1242
dict = plist_new_dict();
1243
plist_dict_add_label(dict, client->label);
1244
plist_dict_insert_item(dict,"Request", plist_new_string("StartService"));
1245
plist_dict_insert_item(dict,"Service", plist_new_string(service));
1247
/* send to device */
1248
ret = lockdownd_send(client, dict);
1252
if (LOCKDOWN_E_SUCCESS != ret)
1255
ret = lockdownd_receive(client, &dict);
1257
if (LOCKDOWN_E_SUCCESS != ret)
1261
return LOCKDOWN_E_PLIST_ERROR;
1263
ret = LOCKDOWN_E_UNKNOWN_ERROR;
1264
if (lockdown_check_result(dict, "StartService") == RESULT_SUCCESS) {
1265
plist_t port_value_node = plist_dict_get_item(dict, "Port");
1267
if (port_value_node && (plist_get_node_type(port_value_node) == PLIST_UINT)) {
1268
uint64_t port_value = 0;
1269
plist_get_uint_val(port_value_node, &port_value);
1272
port_loc = port_value;
1273
ret = LOCKDOWN_E_SUCCESS;
1275
if (port && ret == LOCKDOWN_E_SUCCESS)
1279
ret = LOCKDOWN_E_START_SERVICE_FAILED;
1280
plist_t error_node = plist_dict_get_item(dict, "Error");
1281
if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1283
plist_get_string_val(error_node, &error);
1284
if (!strcmp(error, "InvalidService")) {
1285
ret = LOCKDOWN_E_INVALID_SERVICE;
1297
* Activates the device. Only works within an open session.
1298
* The ActivationRecord plist dictionary must be obtained using the
1299
* activation protocol requesting from Apple's https webservice.
1301
* @see http://iphone-docs.org/doku.php?id=docs:protocols:activation
1303
* @param control The lockdown client
1304
* @param activation_record The activation record plist dictionary
1306
* @return an error code (LOCKDOWN_E_SUCCESS on success)
1308
lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record)
1311
return LOCKDOWN_E_INVALID_ARG;
1313
if (!client->session_id)
1314
return LOCKDOWN_E_NO_RUNNING_SESSION;
1316
if (!activation_record)
1317
return LOCKDOWN_E_INVALID_ARG;
1319
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1321
plist_t dict = plist_new_dict();
1322
plist_dict_add_label(dict, client->label);
1323
plist_dict_insert_item(dict,"Request", plist_new_string("Activate"));
1324
plist_dict_insert_item(dict,"ActivationRecord", activation_record);
1326
ret = lockdownd_send(client, dict);
1330
ret = lockdownd_receive(client, &dict);
1332
debug_info("LOCKDOWN_E_PLIST_ERROR");
1333
return LOCKDOWN_E_PLIST_ERROR;
1336
ret = LOCKDOWN_E_ACTIVATION_FAILED;
1337
if (lockdown_check_result(dict, "Activate") == RESULT_SUCCESS) {
1338
debug_info("success");
1339
ret = LOCKDOWN_E_SUCCESS;
1342
plist_t error_node = plist_dict_get_item(dict, "Error");
1343
if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1345
plist_get_string_val(error_node, &error);
1346
if (!strcmp(error, "InvalidActivationRecord")) {
1347
ret = LOCKDOWN_E_INVALID_ACTIVATION_RECORD;
1360
* Deactivates the device, returning it to the locked
1361
* “Activate with iTunes” screen.
1363
* @param control The lockdown client
1365
* @return an error code (LOCKDOWN_E_SUCCESS on success)
1367
lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1370
return LOCKDOWN_E_INVALID_ARG;
1372
if (!client->session_id)
1373
return LOCKDOWN_E_NO_RUNNING_SESSION;
1375
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1377
plist_t dict = plist_new_dict();
1378
plist_dict_add_label(dict, client->label);
1379
plist_dict_insert_item(dict,"Request", plist_new_string("Deactivate"));
1381
ret = lockdownd_send(client, dict);
1385
ret = lockdownd_receive(client, &dict);
1387
debug_info("LOCKDOWN_E_PLIST_ERROR");
1388
return LOCKDOWN_E_PLIST_ERROR;
1391
ret = LOCKDOWN_E_UNKNOWN_ERROR;
1392
if (lockdown_check_result(dict, "Deactivate") == RESULT_SUCCESS) {
1393
debug_info("success");
1394
ret = LOCKDOWN_E_SUCCESS;