summaryrefslogtreecommitdiffstats
path: root/src/lockdown.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lockdown.c')
-rw-r--r--src/lockdown.c1440
1 files changed, 710 insertions, 730 deletions
diff --git a/src/lockdown.c b/src/lockdown.c
index 935f24e..256bff0 100644
--- a/src/lockdown.c
+++ b/src/lockdown.c
@@ -2,6 +2,8 @@
2 * lockdown.c 2 * lockdown.c
3 * com.apple.mobile.lockdownd service implementation. 3 * com.apple.mobile.lockdownd service implementation.
4 * 4 *
5 * Copyright (c) 2009-2015 Martin Szulecki All Rights Reserved.
6 * Copyright (c) 2014-2015 Nikias Bassen All Rights Reserved.
5 * Copyright (c) 2010 Bryan Forbes All Rights Reserved. 7 * Copyright (c) 2010 Bryan Forbes All Rights Reserved.
6 * Copyright (c) 2008 Zach C. All Rights Reserved. 8 * Copyright (c) 2008 Zach C. All Rights Reserved.
7 * 9 *
@@ -20,36 +22,125 @@
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */ 23 */
22 24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
23#include <string.h> 29#include <string.h>
24#include <stdlib.h> 30#include <stdlib.h>
25#define _GNU_SOURCE 1 31#define _GNU_SOURCE 1
26#define __USE_GNU 1 32#define __USE_GNU 1
27#include <stdio.h> 33#include <stdio.h>
28#include <ctype.h> 34#include <ctype.h>
29#include <glib.h> 35#include <unistd.h>
30#include <libtasn1.h>
31#include <gnutls/x509.h>
32#include <plist/plist.h> 36#include <plist/plist.h>
37#include <libimobiledevice-glue/utils.h>
33 38
34#include "property_list_service.h" 39#include "property_list_service.h"
35#include "lockdown.h" 40#include "lockdown.h"
36#include "idevice.h" 41#include "idevice.h"
37#include "debug.h" 42#include "common/debug.h"
38#include "userpref.h" 43#include "common/userpref.h"
39 44#include "asprintf.h"
40#define RESULT_SUCCESS 0 45
41#define RESULT_FAILURE 1 46#ifdef WIN32
42 47#include <windows.h>
43const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { 48#define sleep(x) Sleep(x*1000)
44 {"PKCS1", 536872976, 0}, 49#endif
45 {0, 1073741836, 0}, 50
46 {"RSAPublicKey", 536870917, 0}, 51struct st_lockdownd_error_str_map {
47 {"modulus", 1073741827, 0}, 52 const char *lockdown_errstr;
48 {"publicExponent", 3, 0}, 53 const char *errstr;
49 {0, 0, 0} 54 lockdownd_error_t errcode;
55};
56
57static struct st_lockdownd_error_str_map lockdownd_error_str_map[] = {
58 { "InvalidResponse", "Invalid response", LOCKDOWN_E_INVALID_RESPONSE },
59 { "MissingKey", "Missing key", LOCKDOWN_E_MISSING_KEY },
60 { "MissingValue", "Missing value", LOCKDOWN_E_MISSING_VALUE },
61 { "GetProhibited", "Get value prohibited", LOCKDOWN_E_GET_PROHIBITED },
62 { "SetProhibited", "Set value prohibited", LOCKDOWN_E_SET_PROHIBITED },
63 { "RemoveProhibited", "Remove value prohibited", LOCKDOWN_E_REMOVE_PROHIBITED },
64 { "ImmutableValue", "Immutable value", LOCKDOWN_E_IMMUTABLE_VALUE },
65 { "PasswordProtected", "Password protected", LOCKDOWN_E_PASSWORD_PROTECTED },
66 { "UserDeniedPairing", "User denied pairing", LOCKDOWN_E_USER_DENIED_PAIRING },
67 { "PairingDialogResponsePending", "Pairing dialog response pending", LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING },
68 { "MissingHostID", "Missing HostID", LOCKDOWN_E_MISSING_HOST_ID },
69 { "InvalidHostID", "Invalid HostID", LOCKDOWN_E_INVALID_HOST_ID },
70 { "SessionActive", "Session active", LOCKDOWN_E_SESSION_ACTIVE },
71 { "SessionInactive", "Session inactive", LOCKDOWN_E_SESSION_INACTIVE },
72 { "MissingSessionID", "Missing session ID", LOCKDOWN_E_MISSING_SESSION_ID },
73 { "InvalidSessionID", "Invalid session ID", LOCKDOWN_E_INVALID_SESSION_ID },
74 { "MissingService", "Missing service", LOCKDOWN_E_MISSING_SERVICE },
75 { "InvalidService", "Invalid service", LOCKDOWN_E_INVALID_SERVICE },
76 { "ServiceLimit", "Service limit reached", LOCKDOWN_E_SERVICE_LIMIT },
77 { "MissingPairRecord", "Missing pair record", LOCKDOWN_E_MISSING_PAIR_RECORD },
78 { "SavePairRecordFailed", "Saving pair record failed", LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED },
79 { "InvalidPairRecord", "Invalid pair record", LOCKDOWN_E_INVALID_PAIR_RECORD },
80 { "InvalidActivationRecord", "Invalid activation record", LOCKDOWN_E_INVALID_ACTIVATION_RECORD },
81 { "MissingActivationRecord", "Missing activation record", LOCKDOWN_E_MISSING_ACTIVATION_RECORD },
82 { "ServiceProhibited", "Service prohibited", LOCKDOWN_E_SERVICE_PROHIBITED },
83 { "EscrowLocked", "Escrow lockded", LOCKDOWN_E_ESCROW_LOCKED },
84 { "PairingProhibitedOverThisConnection", "Pairing prohibited over this connection", LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION },
85 { "FMiPProtected", "Find My iPhone/iPod/iPad protected", LOCKDOWN_E_FMIP_PROTECTED },
86 { "MCProtected", "MC protected" , LOCKDOWN_E_MC_PROTECTED },
87 { "MCChallengeRequired", "MC challenge required", LOCKDOWN_E_MC_CHALLENGE_REQUIRED },
88 { NULL, NULL, 0 }
50}; 89};
51 90
52/** 91/**
92 * Convert an error string identifier to a lockdownd_error_t value.
93 * Used internally to get correct error codes from a response.
94 *
95 * @param name The error name to convert.
96 *
97 * @return A matching lockdownd_error_t error code,
98 * LOCKDOWN_E_UNKNOWN_ERROR otherwise.
99 */
100static lockdownd_error_t lockdownd_strtoerr(const char* name)
101{
102 lockdownd_error_t err = LOCKDOWN_E_UNKNOWN_ERROR;
103 int i = 0;
104 while (lockdownd_error_str_map[i].lockdown_errstr) {
105 if (strcmp(lockdownd_error_str_map[i].lockdown_errstr, name) == 0) {
106 return lockdownd_error_str_map[i].errcode;
107 }
108 i++;
109 }
110 return err;
111}
112
113/**
114 * Convert a property_list_service_error_t value to a lockdownd_error_t
115 * value. Used internally to get correct error codes.
116 *
117 * @param err A property_list_service_error_t error code
118 *
119 * @return A matching lockdownd_error_t error code,
120 * LOCKDOWND_E_UNKNOWN_ERROR otherwise.
121 */
122static lockdownd_error_t lockdownd_error(property_list_service_error_t err)
123{
124 switch (err) {
125 case PROPERTY_LIST_SERVICE_E_SUCCESS:
126 return LOCKDOWN_E_SUCCESS;
127 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
128 return LOCKDOWN_E_INVALID_ARG;
129 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
130 return LOCKDOWN_E_PLIST_ERROR;
131 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
132 return LOCKDOWN_E_MUX_ERROR;
133 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
134 return LOCKDOWN_E_SSL_ERROR;
135 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
136 return LOCKDOWN_E_RECEIVE_TIMEOUT;
137 default:
138 break;
139 }
140 return LOCKDOWN_E_UNKNOWN_ERROR;
141}
142
143/**
53 * Internally used function for checking the result from lockdown's answer 144 * Internally used function for checking the result from lockdown's answer
54 * plist to a previously sent request. 145 * plist to a previously sent request.
55 * 146 *
@@ -57,58 +148,66 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
57 * @param query_match Name of the request to match or NULL if no match is 148 * @param query_match Name of the request to match or NULL if no match is
58 * required. 149 * required.
59 * 150 *
60 * @return RESULT_SUCCESS when the result is 'Success', 151 * @return LOCKDOWN_E_SUCCESS when the result is 'Success',
61 * RESULT_FAILURE when the result is 'Failure', 152 * LOCKDOWN_E_UNKNOWN_ERROR when the result is 'Failure',
62 * or a negative value if an error occured during evaluation. 153 * or a specific error code if derieved from the result.
63 */ 154 */
64static int lockdown_check_result(plist_t dict, const char *query_match) 155lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match)
65{ 156{
66 int ret = -1; 157 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
67 158
68 plist_t query_node = plist_dict_get_item(dict, "Request"); 159 plist_t query_node = plist_dict_get_item(dict, "Request");
69 if (!query_node) { 160 if (!query_node) {
70 return ret; 161 return ret;
71 } 162 }
163
72 if (plist_get_node_type(query_node) != PLIST_STRING) { 164 if (plist_get_node_type(query_node) != PLIST_STRING) {
73 return ret; 165 return ret;
74 } else {
75 char *query_value = NULL;
76 plist_get_string_val(query_node, &query_value);
77 if (!query_value) {
78 return ret;
79 }
80 if (query_match && (strcmp(query_value, query_match) != 0)) {
81 free(query_value);
82 return ret;
83 }
84 free(query_value);
85 } 166 }
86 167
87 plist_t result_node = plist_dict_get_item(dict, "Result"); 168 const char *query_value = plist_get_string_ptr(query_node, NULL);
88 if (!result_node) { 169 if (!query_value) {
89 return ret; 170 return ret;
90 } 171 }
91 172
92 plist_type result_type = plist_get_node_type(result_node); 173 if (query_match && (strcmp(query_value, query_match) != 0)) {
93 174 return ret;
94 if (result_type == PLIST_STRING) { 175 }
95
96 char *result_value = NULL;
97 176
98 plist_get_string_val(result_node, &result_value); 177 /* Check for 'Error' in reply */
178 plist_t err_node = plist_dict_get_item(dict, "Error");
179 if (err_node) {
180 if (plist_get_node_type(err_node) == PLIST_STRING) {
181 const char *err_value = plist_get_string_ptr(err_node, NULL);
182 if (err_value) {
183 debug_info("ERROR: %s", err_value);
184 ret = lockdownd_strtoerr(err_value);
185 } else {
186 debug_info("ERROR: unknown error occurred");
187 }
188 }
189 return ret;
190 }
99 191
192 plist_t result_node = plist_dict_get_item(dict, "Result");
193 if (!result_node) {
194 /* With iOS 5+ 'Result' is not present anymore.
195 If there is no 'Error', we can just assume success. */
196 return LOCKDOWN_E_SUCCESS;
197 }
198 if (plist_get_node_type(result_node) == PLIST_STRING) {
199 const char *result_value = plist_get_string_ptr(result_node, NULL);
100 if (result_value) { 200 if (result_value) {
101 if (!strcmp(result_value, "Success")) { 201 if (!strcmp(result_value, "Success")) {
102 ret = RESULT_SUCCESS; 202 ret = LOCKDOWN_E_SUCCESS;
103 } else if (!strcmp(result_value, "Failure")) { 203 } else if (!strcmp(result_value, "Failure")) {
104 ret = RESULT_FAILURE; 204 ret = LOCKDOWN_E_UNKNOWN_ERROR;
105 } else { 205 } else {
106 debug_info("ERROR: unknown result value '%s'", result_value); 206 debug_info("ERROR: unknown result value '%s'", result_value);
107 } 207 }
108 } 208 }
109 if (result_value)
110 free(result_value);
111 } 209 }
210
112 return ret; 211 return ret;
113} 212}
114 213
@@ -123,20 +222,10 @@ static void plist_dict_add_label(plist_t plist, const char *label)
123{ 222{
124 if (plist && label) { 223 if (plist && label) {
125 if (plist_get_node_type(plist) == PLIST_DICT) 224 if (plist_get_node_type(plist) == PLIST_DICT)
126 plist_dict_insert_item(plist, "Label", plist_new_string(label)); 225 plist_dict_set_item(plist, "Label", plist_new_string(label));
127 } 226 }
128} 227}
129 228
130/**
131 * Closes the lockdownd session by sending the StopSession request.
132 *
133 * @see lockdownd_start_session
134 *
135 * @param client The lockdown client
136 * @param session_id The id of a running session
137 *
138 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
139 */
140lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id) 229lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id)
141{ 230{
142 if (!client) 231 if (!client)
@@ -151,8 +240,8 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *
151 240
152 plist_t dict = plist_new_dict(); 241 plist_t dict = plist_new_dict();
153 plist_dict_add_label(dict, client->label); 242 plist_dict_add_label(dict, client->label);
154 plist_dict_insert_item(dict,"Request", plist_new_string("StopSession")); 243 plist_dict_set_item(dict,"Request", plist_new_string("StopSession"));
155 plist_dict_insert_item(dict,"SessionID", plist_new_string(session_id)); 244 plist_dict_set_item(dict,"SessionID", plist_new_string(session_id));
156 245
157 debug_info("stopping session %s", session_id); 246 debug_info("stopping session %s", session_id);
158 247
@@ -168,64 +257,74 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *
168 return LOCKDOWN_E_PLIST_ERROR; 257 return LOCKDOWN_E_PLIST_ERROR;
169 } 258 }
170 259
171 ret = LOCKDOWN_E_UNKNOWN_ERROR; 260 ret = lockdown_check_result(dict, "StopSession");
172 if (lockdown_check_result(dict, "StopSession") == RESULT_SUCCESS) { 261 if (ret == LOCKDOWN_E_SUCCESS) {
173 debug_info("success"); 262 debug_info("success");
174 ret = LOCKDOWN_E_SUCCESS;
175 } 263 }
264
176 plist_free(dict); 265 plist_free(dict);
177 dict = NULL; 266 dict = NULL;
267
268 if (client->session_id) {
269 free(client->session_id);
270 client->session_id = NULL;
271 }
272
178 if (client->ssl_enabled) { 273 if (client->ssl_enabled) {
179 property_list_service_disable_ssl(client->parent); 274 property_list_service_disable_ssl(client->parent);
275 client->ssl_enabled = 0;
180 } 276 }
277
181 return ret; 278 return ret;
182} 279}
183 280
184/** 281static lockdownd_error_t lockdownd_client_free_simple(lockdownd_client_t client)
185 * Closes the lockdownd client session if one is running and frees up the
186 * lockdownd_client struct.
187 *
188 * @param client The lockdown client
189 *
190 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
191 */
192lockdownd_error_t lockdownd_client_free(lockdownd_client_t client)
193{ 282{
194 if (!client) 283 if (!client)
195 return LOCKDOWN_E_INVALID_ARG; 284 return LOCKDOWN_E_INVALID_ARG;
196 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
197 285
198 if (client->session_id) { 286 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
199 lockdownd_stop_session(client, client->session_id);
200 free(client->session_id);
201 }
202 287
203 if (client->parent) { 288 if (client->parent) {
204 lockdownd_goodbye(client);
205
206 if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { 289 if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) {
207 ret = LOCKDOWN_E_SUCCESS; 290 ret = LOCKDOWN_E_SUCCESS;
208 } 291 }
209 } 292 }
210 293
211 if (client->uuid) { 294 if (client->session_id) {
212 free(client->uuid); 295 free(client->session_id);
296 client->session_id = NULL;
213 } 297 }
214 if (client->label) { 298 if (client->label) {
215 free(client->label); 299 free(client->label);
216 } 300 }
301 if (client->cu_key) {
302 free(client->cu_key);
303 client->cu_key = NULL;
304 }
217 305
218 free(client); 306 free(client);
307 client = NULL;
308
309 return ret;
310}
311
312lockdownd_error_t lockdownd_client_free(lockdownd_client_t client)
313{
314 if (!client)
315 return LOCKDOWN_E_INVALID_ARG;
316
317 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
318
319 if (client->session_id) {
320 lockdownd_stop_session(client, client->session_id);
321 }
322
323 ret = lockdownd_client_free_simple(client);
324
219 return ret; 325 return ret;
220} 326}
221 327
222/**
223 * Sets the label to send for requests to lockdownd.
224 *
225 * @param client The lockdown client
226 * @param label The label to set or NULL to disable sending a label
227 *
228 */
229void lockdownd_client_set_label(lockdownd_client_t client, const char *label) 328void lockdownd_client_set_label(lockdownd_client_t client, const char *label)
230{ 329{
231 if (client) { 330 if (client) {
@@ -236,69 +335,22 @@ void lockdownd_client_set_label(lockdownd_client_t client, const char *label)
236 } 335 }
237} 336}
238 337
239/**
240 * Receives a plist from lockdownd.
241 *
242 * @param client The lockdownd client
243 * @param plist The plist to store the received data
244 *
245 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or
246 * plist is NULL
247 */
248lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist) 338lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist)
249{ 339{
250 if (!client || !plist || (plist && *plist)) 340 if (!client || !plist || (plist && *plist))
251 return LOCKDOWN_E_INVALID_ARG; 341 return LOCKDOWN_E_INVALID_ARG;
252 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
253 property_list_service_error_t err;
254
255 err = property_list_service_receive_plist(client->parent, plist);
256 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
257 ret = LOCKDOWN_E_UNKNOWN_ERROR;
258 }
259
260 if (!*plist)
261 ret = LOCKDOWN_E_PLIST_ERROR;
262 342
263 return ret; 343 return lockdownd_error(property_list_service_receive_plist(client->parent, plist));
264} 344}
265 345
266/**
267 * Sends a plist to lockdownd.
268 *
269 * @note This function is low-level and should only be used if you need to send
270 * a new type of message.
271 *
272 * @param client The lockdownd client
273 * @param plist The plist to send
274 *
275 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or
276 * plist is NULL
277 */
278lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist) 346lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist)
279{ 347{
280 if (!client || !plist) 348 if (!client || !plist)
281 return LOCKDOWN_E_INVALID_ARG; 349 return LOCKDOWN_E_INVALID_ARG;
282 350
283 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; 351 return lockdownd_error(property_list_service_send_xml_plist(client->parent, plist));
284 idevice_error_t err;
285
286 err = property_list_service_send_xml_plist(client->parent, plist);
287 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
288 ret = LOCKDOWN_E_UNKNOWN_ERROR;
289 }
290 return ret;
291} 352}
292 353
293/**
294 * Query the type of the service daemon. Depending on whether the device is
295 * queried in normal mode or restore mode, different types will be returned.
296 *
297 * @param client The lockdownd client
298 * @param type The type returned by the service daemon. Pass NULL to ignore.
299 *
300 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
301 */
302lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) 354lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
303{ 355{
304 if (!client) 356 if (!client)
@@ -308,7 +360,7 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
308 360
309 plist_t dict = plist_new_dict(); 361 plist_t dict = plist_new_dict();
310 plist_dict_add_label(dict, client->label); 362 plist_dict_add_label(dict, client->label);
311 plist_dict_insert_item(dict,"Request", plist_new_string("QueryType")); 363 plist_dict_set_item(dict,"Request", plist_new_string("QueryType"));
312 364
313 debug_info("called"); 365 debug_info("called");
314 ret = lockdownd_send(client, dict); 366 ret = lockdownd_send(client, dict);
@@ -322,14 +374,21 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
322 return ret; 374 return ret;
323 375
324 ret = LOCKDOWN_E_UNKNOWN_ERROR; 376 ret = LOCKDOWN_E_UNKNOWN_ERROR;
325 if (lockdown_check_result(dict, "QueryType") == RESULT_SUCCESS) { 377 plist_t type_node = plist_dict_get_item(dict, "Type");
378 if (type_node && (plist_get_node_type(type_node) == PLIST_STRING)) {
379 char* typestr = NULL;
380 plist_get_string_val(type_node, &typestr);
381 debug_info("success with type %s", typestr);
326 /* return the type if requested */ 382 /* return the type if requested */
327 if (type != NULL) { 383 if (type != NULL) {
328 plist_t type_node = plist_dict_get_item(dict, "Type"); 384 *type = typestr;
329 plist_get_string_val(type_node, type); 385 } else {
386 free(typestr);
330 } 387 }
331 debug_info("success with type %s", *type);
332 ret = LOCKDOWN_E_SUCCESS; 388 ret = LOCKDOWN_E_SUCCESS;
389 } else {
390 debug_info("hmm. QueryType response does not contain a type?!");
391 debug_plist(dict);
333 } 392 }
334 plist_free(dict); 393 plist_free(dict);
335 dict = NULL; 394 dict = NULL;
@@ -337,16 +396,6 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
337 return ret; 396 return ret;
338} 397}
339 398
340/**
341 * Retrieves a preferences plist using an optional domain and/or key name.
342 *
343 * @param client An initialized lockdownd client.
344 * @param domain The domain to query on or NULL for global domain
345 * @param key The key name to request or NULL to query for all keys
346 * @param value A plist node representing the result value node
347 *
348 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
349 */
350lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value) 399lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value)
351{ 400{
352 if (!client) 401 if (!client)
@@ -359,12 +408,12 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
359 dict = plist_new_dict(); 408 dict = plist_new_dict();
360 plist_dict_add_label(dict, client->label); 409 plist_dict_add_label(dict, client->label);
361 if (domain) { 410 if (domain) {
362 plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); 411 plist_dict_set_item(dict,"Domain", plist_new_string(domain));
363 } 412 }
364 if (key) { 413 if (key) {
365 plist_dict_insert_item(dict,"Key", plist_new_string(key)); 414 plist_dict_set_item(dict,"Key", plist_new_string(key));
366 } 415 }
367 plist_dict_insert_item(dict,"Request", plist_new_string("GetValue")); 416 plist_dict_set_item(dict,"Request", plist_new_string("GetValue"));
368 417
369 /* send to device */ 418 /* send to device */
370 ret = lockdownd_send(client, dict); 419 ret = lockdownd_send(client, dict);
@@ -380,10 +429,11 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
380 if (ret != LOCKDOWN_E_SUCCESS) 429 if (ret != LOCKDOWN_E_SUCCESS)
381 return ret; 430 return ret;
382 431
383 if (lockdown_check_result(dict, "GetValue") == RESULT_SUCCESS) { 432 ret = lockdown_check_result(dict, "GetValue");
433 if (ret == LOCKDOWN_E_SUCCESS) {
384 debug_info("success"); 434 debug_info("success");
385 ret = LOCKDOWN_E_SUCCESS;
386 } 435 }
436
387 if (ret != LOCKDOWN_E_SUCCESS) { 437 if (ret != LOCKDOWN_E_SUCCESS) {
388 plist_free(dict); 438 plist_free(dict);
389 return ret; 439 return ret;
@@ -400,17 +450,6 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
400 return ret; 450 return ret;
401} 451}
402 452
403/**
404 * Sets a preferences value using a plist and optional by domain and/or key name.
405 *
406 * @param client an initialized lockdownd client.
407 * @param domain the domain to query on or NULL for global domain
408 * @param key the key name to set the value or NULL to set a value dict plist
409 * @param value a plist node of any node type representing the value to set
410 *
411 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or
412 * value is NULL
413 */
414lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value) 453lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value)
415{ 454{
416 if (!client || !value) 455 if (!client || !value)
@@ -423,13 +462,13 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
423 dict = plist_new_dict(); 462 dict = plist_new_dict();
424 plist_dict_add_label(dict, client->label); 463 plist_dict_add_label(dict, client->label);
425 if (domain) { 464 if (domain) {
426 plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); 465 plist_dict_set_item(dict,"Domain", plist_new_string(domain));
427 } 466 }
428 if (key) { 467 if (key) {
429 plist_dict_insert_item(dict,"Key", plist_new_string(key)); 468 plist_dict_set_item(dict,"Key", plist_new_string(key));
430 } 469 }
431 plist_dict_insert_item(dict,"Request", plist_new_string("SetValue")); 470 plist_dict_set_item(dict,"Request", plist_new_string("SetValue"));
432 plist_dict_insert_item(dict,"Value", value); 471 plist_dict_set_item(dict,"Value", value);
433 472
434 /* send to device */ 473 /* send to device */
435 ret = lockdownd_send(client, dict); 474 ret = lockdownd_send(client, dict);
@@ -445,9 +484,9 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
445 if (ret != LOCKDOWN_E_SUCCESS) 484 if (ret != LOCKDOWN_E_SUCCESS)
446 return ret; 485 return ret;
447 486
448 if (lockdown_check_result(dict, "SetValue") == RESULT_SUCCESS) { 487 ret = lockdown_check_result(dict, "SetValue");
488 if (ret == LOCKDOWN_E_SUCCESS) {
449 debug_info("success"); 489 debug_info("success");
450 ret = LOCKDOWN_E_SUCCESS;
451 } 490 }
452 491
453 if (ret != LOCKDOWN_E_SUCCESS) { 492 if (ret != LOCKDOWN_E_SUCCESS) {
@@ -459,17 +498,6 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
459 return ret; 498 return ret;
460} 499}
461 500
462/**
463 * Removes a preference node by domain and/or key name.
464 *
465 * @note: Use with caution as this could remove vital information on the device
466 *
467 * @param client An initialized lockdownd client.
468 * @param domain The domain to query on or NULL for global domain
469 * @param key The key name to remove or NULL remove all keys for the current domain
470 *
471 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
472 */
473lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key) 501lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key)
474{ 502{
475 if (!client) 503 if (!client)
@@ -482,12 +510,12 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
482 dict = plist_new_dict(); 510 dict = plist_new_dict();
483 plist_dict_add_label(dict, client->label); 511 plist_dict_add_label(dict, client->label);
484 if (domain) { 512 if (domain) {
485 plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); 513 plist_dict_set_item(dict,"Domain", plist_new_string(domain));
486 } 514 }
487 if (key) { 515 if (key) {
488 plist_dict_insert_item(dict,"Key", plist_new_string(key)); 516 plist_dict_set_item(dict,"Key", plist_new_string(key));
489 } 517 }
490 plist_dict_insert_item(dict,"Request", plist_new_string("RemoveValue")); 518 plist_dict_set_item(dict,"Request", plist_new_string("RemoveValue"));
491 519
492 /* send to device */ 520 /* send to device */
493 ret = lockdownd_send(client, dict); 521 ret = lockdownd_send(client, dict);
@@ -503,9 +531,9 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
503 if (ret != LOCKDOWN_E_SUCCESS) 531 if (ret != LOCKDOWN_E_SUCCESS)
504 return ret; 532 return ret;
505 533
506 if (lockdown_check_result(dict, "RemoveValue") == RESULT_SUCCESS) { 534 ret = lockdown_check_result(dict, "RemoveValue");
535 if (ret == LOCKDOWN_E_SUCCESS) {
507 debug_info("success"); 536 debug_info("success");
508 ret = LOCKDOWN_E_SUCCESS;
509 } 537 }
510 538
511 if (ret != LOCKDOWN_E_SUCCESS) { 539 if (ret != LOCKDOWN_E_SUCCESS) {
@@ -517,16 +545,7 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
517 return ret; 545 return ret;
518} 546}
519 547
520/** 548lockdownd_error_t lockdownd_get_device_udid(lockdownd_client_t client, char **udid)
521 * Returns the unique id of the device from lockdownd.
522 *
523 * @param client An initialized lockdownd client.
524 * @param uuid Holds the unique id of the device. The caller is responsible
525 * for freeing the memory.
526 *
527 * @return LOCKDOWN_E_SUCCESS on success
528 */
529lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uuid)
530{ 549{
531 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 550 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
532 plist_t value = NULL; 551 plist_t value = NULL;
@@ -535,7 +554,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu
535 if (ret != LOCKDOWN_E_SUCCESS) { 554 if (ret != LOCKDOWN_E_SUCCESS) {
536 return ret; 555 return ret;
537 } 556 }
538 plist_get_string_val(value, uuid); 557 plist_get_string_val(value, udid);
539 558
540 plist_free(value); 559 plist_free(value);
541 value = NULL; 560 value = NULL;
@@ -551,7 +570,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu
551 * 570 *
552 * @return LOCKDOWN_E_SUCCESS on success 571 * @return LOCKDOWN_E_SUCCESS on success
553 */ 572 */
554lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key) 573static lockdownd_error_t lockdownd_get_device_public_key_as_key_data(lockdownd_client_t client, key_data_t *public_key)
555{ 574{
556 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 575 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
557 plist_t value = NULL; 576 plist_t value = NULL;
@@ -572,15 +591,6 @@ lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnu
572 return ret; 591 return ret;
573} 592}
574 593
575/**
576 * Retrieves the name of the device from lockdownd set by the user.
577 *
578 * @param client An initialized lockdownd client.
579 * @param device_name Holds the name of the device. The caller is
580 * responsible for freeing the memory.
581 *
582 * @return LOCKDOWN_E_SUCCESS on success
583 */
584lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name) 594lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name)
585{ 595{
586 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 596 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
@@ -598,29 +608,19 @@ lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **de
598 return ret; 608 return ret;
599} 609}
600 610
601/**
602 * Creates a new lockdownd client for the device.
603 *
604 * @note This function does not pair with the device or start a session. This
605 * has to be done manually by the caller after the client is created.
606 * The device disconnects automatically if the lockdown connection idles
607 * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon
608 * as the connection is no longer needed.
609 *
610 * @param device The device to create a lockdownd client for
611 * @param client The pointer to the location of the new lockdownd_client
612 * @param label The label to use for communication. Usually the program name.
613 *
614 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
615 */
616lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label) 611lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label)
617{ 612{
618 if (!client) 613 if (!device || !client)
619 return LOCKDOWN_E_INVALID_ARG; 614 return LOCKDOWN_E_INVALID_ARG;
620 615
616 static struct lockdownd_service_descriptor service = {
617 .port = 0xf27e,
618 .ssl_enabled = 0
619 };
620
621 property_list_service_client_t plistclient = NULL; 621 property_list_service_client_t plistclient = NULL;
622 if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 622 if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
623 debug_info("could not connect to lockdownd (device %s)", device->uuid); 623 debug_info("could not connect to lockdownd (device %s)", device->udid);
624 return LOCKDOWN_E_MUX_ERROR; 624 return LOCKDOWN_E_MUX_ERROR;
625 } 625 }
626 626
@@ -628,7 +628,14 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli
628 client_loc->parent = plistclient; 628 client_loc->parent = plistclient;
629 client_loc->ssl_enabled = 0; 629 client_loc->ssl_enabled = 0;
630 client_loc->session_id = NULL; 630 client_loc->session_id = NULL;
631 client_loc->uuid = NULL; 631 client_loc->device = device;
632 client_loc->cu_key = NULL;
633 client_loc->cu_key_len = 0;
634
635 if (device->udid) {
636 debug_info("device udid: %s", device->udid);
637 }
638
632 client_loc->label = label ? strdup(label) : NULL; 639 client_loc->label = label ? strdup(label) : NULL;
633 640
634 *client = client_loc; 641 *client = client_loc;
@@ -636,23 +643,6 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli
636 return LOCKDOWN_E_SUCCESS; 643 return LOCKDOWN_E_SUCCESS;
637} 644}
638 645
639/**
640 * Creates a new lockdownd client for the device and starts initial handshake.
641 * The handshake consists out of query_type, validate_pair, pair and
642 * start_session calls. It uses the internal pairing record management.
643 *
644 * @note The device disconnects automatically if the lockdown connection idles
645 * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon
646 * as the connection is no longer needed.
647 *
648 * @param device The device to create a lockdownd client for
649 * @param client The pointer to the location of the new lockdownd_client
650 * @param label The label to use for communication. Usually the program name.
651 * Pass NULL to disable sending the label in requests to lockdownd.
652 *
653 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
654 * LOCKDOWN_E_INVALID_CONF if configuration data is wrong
655 */
656lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) 646lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label)
657{ 647{
658 if (!client) 648 if (!client)
@@ -660,6 +650,7 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown
660 650
661 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; 651 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
662 lockdownd_client_t client_loc = NULL; 652 lockdownd_client_t client_loc = NULL;
653 plist_t pair_record = NULL;
663 char *host_id = NULL; 654 char *host_id = NULL;
664 char *type = NULL; 655 char *type = NULL;
665 656
@@ -670,60 +661,125 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown
670 } 661 }
671 662
672 /* perform handshake */ 663 /* perform handshake */
673 if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc, &type)) { 664 ret = lockdownd_query_type(client_loc, &type);
665 if (LOCKDOWN_E_SUCCESS != ret) {
674 debug_info("QueryType failed in the lockdownd client."); 666 debug_info("QueryType failed in the lockdownd client.");
675 ret = LOCKDOWN_E_NOT_ENOUGH_DATA; 667 } else if (strcmp("com.apple.mobile.lockdown", type) != 0) {
676 } else { 668 debug_info("Warning QueryType request returned \"%s\".", type);
677 if (strcmp("com.apple.mobile.lockdown", type)) { 669 }
678 debug_info("Warning QueryType request returned \"%s\".", type); 670 free(type);
671
672 if (device->version == 0) {
673 plist_t p_version = NULL;
674 if (lockdownd_get_value(client_loc, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) {
675 int vers[3] = {0, 0, 0};
676 char *s_version = NULL;
677 plist_get_string_val(p_version, &s_version);
678 if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
679 device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
680 }
681 free(s_version);
679 } 682 }
680 if (type) 683 plist_free(p_version);
681 free(type);
682 } 684 }
683 685 if (device->device_class == 0) {
684 ret = idevice_get_uuid(device, &client_loc->uuid); 686 plist_t p_device_class = NULL;
685 if (LOCKDOWN_E_SUCCESS != ret) { 687 if (lockdownd_get_value(client_loc, NULL, "DeviceClass", &p_device_class) == LOCKDOWN_E_SUCCESS) {
686 debug_info("failed to get device uuid."); 688 char* s_device_class = NULL;
689 plist_get_string_val(p_device_class, &s_device_class);
690 if (s_device_class != NULL) {
691 if (!strcmp(s_device_class, "iPhone")) {
692 device->device_class = DEVICE_CLASS_IPHONE;
693 } else if (!strcmp(s_device_class, "iPad")) {
694 device->device_class = DEVICE_CLASS_IPAD;
695 } else if (!strcmp(s_device_class, "iPod")) {
696 device->device_class = DEVICE_CLASS_IPOD;
697 } else if (!strcmp(s_device_class, "Watch")) {
698 device->device_class = DEVICE_CLASS_WATCH;
699 } else if (!strcmp(s_device_class, "AppleTV")) {
700 device->device_class = DEVICE_CLASS_APPLETV;
701 } else {
702 device->device_class = DEVICE_CLASS_UNKNOWN;
703 }
704 free(s_device_class);
705 }
706 }
707 plist_free(p_device_class);
687 } 708 }
688 debug_info("device uuid: %s", client_loc->uuid);
689 709
690 userpref_get_host_id(&host_id); 710 userpref_error_t uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record);
691 if (LOCKDOWN_E_SUCCESS == ret && !host_id) { 711 if (uerr == USERPREF_E_READ_ERROR) {
712 debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid);
713 lockdownd_client_free(client_loc);
714 return LOCKDOWN_E_RECEIVE_TIMEOUT;
715 }
716 if (pair_record) {
717 pair_record_get_host_id(pair_record, &host_id);
718 }
719 if (LOCKDOWN_E_SUCCESS == ret && pair_record && !host_id) {
692 ret = LOCKDOWN_E_INVALID_CONF; 720 ret = LOCKDOWN_E_INVALID_CONF;
693 } 721 }
694 722
695 if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->uuid)) 723 if (LOCKDOWN_E_SUCCESS == ret && !pair_record) {
724 /* attempt pairing */
725 free(host_id);
726 host_id = NULL;
696 ret = lockdownd_pair(client_loc, NULL); 727 ret = lockdownd_pair(client_loc, NULL);
728 }
697 729
698 /* in any case, we need to validate pairing to receive trusted host status */ 730 plist_free(pair_record);
699 ret = lockdownd_validate_pair(client_loc, NULL); 731 pair_record = NULL;
700 732
701 /* if not paired yet, let's do it now */ 733 if (device->version < DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) {
702 if (LOCKDOWN_E_INVALID_HOST_ID == ret) { 734 /* for older devices, we need to validate pairing to receive trusted host status */
703 ret = lockdownd_pair(client_loc, NULL); 735 ret = lockdownd_validate_pair(client_loc, NULL);
704 if (LOCKDOWN_E_SUCCESS == ret) { 736
705 ret = lockdownd_validate_pair(client_loc, NULL); 737 /* if not paired yet, let's do it now */
738 if (LOCKDOWN_E_INVALID_HOST_ID == ret) {
739 free(host_id);
740 host_id = NULL;
741 ret = lockdownd_pair(client_loc, NULL);
742 if (LOCKDOWN_E_SUCCESS == ret) {
743 ret = lockdownd_validate_pair(client_loc, NULL);
744 }
706 } 745 }
707 } 746 }
708 747
709 if (LOCKDOWN_E_SUCCESS == ret) { 748 if (LOCKDOWN_E_SUCCESS == ret) {
749 if (!host_id) {
750 uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record);
751 if (uerr == USERPREF_E_READ_ERROR) {
752 debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid);
753 lockdownd_client_free(client_loc);
754 return LOCKDOWN_E_RECEIVE_TIMEOUT;
755 } else if (uerr == USERPREF_E_NOENT) {
756 debug_info("ERROR: No pair record for %s", client_loc->device->udid);
757 lockdownd_client_free(client_loc);
758 return LOCKDOWN_E_INVALID_CONF;
759 } else if (uerr != USERPREF_E_SUCCESS) {
760 debug_info("ERROR: Failed to retrieve or parse pair record for %s", client_loc->device->udid);
761 lockdownd_client_free(client_loc);
762 return LOCKDOWN_E_INVALID_CONF;
763 }
764 if (pair_record) {
765 pair_record_get_host_id(pair_record, &host_id);
766 plist_free(pair_record);
767 }
768 }
769
710 ret = lockdownd_start_session(client_loc, host_id, NULL, NULL); 770 ret = lockdownd_start_session(client_loc, host_id, NULL, NULL);
711 if (LOCKDOWN_E_SUCCESS != ret) { 771 if (LOCKDOWN_E_SUCCESS != ret) {
712 debug_info("Session opening failed."); 772 debug_info("Session opening failed.");
713 } 773 }
714 774
715 if (host_id) {
716 free(host_id);
717 host_id = NULL;
718 }
719 } 775 }
720 776
721 if (LOCKDOWN_E_SUCCESS == ret) { 777 if (LOCKDOWN_E_SUCCESS == ret) {
722 *client = client_loc; 778 *client = client_loc;
723 } else { 779 } else {
724 lockdownd_client_free(client_loc); 780 lockdownd_client_free(client_loc);
725 } 781 }
726 782 free(host_id);
727 return ret; 783 return ret;
728} 784}
729 785
@@ -740,68 +796,77 @@ static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_recor
740 if (!pair_record) 796 if (!pair_record)
741 return NULL; 797 return NULL;
742 798
743 char *host_id_loc = pair_record->host_id;
744
745 /* setup request plist */ 799 /* setup request plist */
746 plist_t dict = plist_new_dict(); 800 plist_t dict = plist_new_dict();
747 plist_dict_insert_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate))); 801 plist_dict_set_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate)));
748 plist_dict_insert_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate))); 802 plist_dict_set_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate)));
749 if (!pair_record->host_id) 803 plist_dict_set_item(dict, "HostID", plist_new_string(pair_record->host_id));
750 userpref_get_host_id(&host_id_loc); 804 plist_dict_set_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate)));
751 plist_dict_insert_item(dict, "HostID", plist_new_string(host_id_loc)); 805 plist_dict_set_item(dict, "SystemBUID", plist_new_string(pair_record->system_buid));
752 plist_dict_insert_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate)));
753
754 if (!pair_record->host_id)
755 free(host_id_loc);
756 806
757 return dict; 807 return dict;
758} 808}
759 809
760/** 810/**
761 * Generates a new pairing record plist and required certificates for the 811 * Generates a pair record plist with required certificates for a specific
762 * supplied public key of the device and the host_id of the caller's host 812 * device. If a pairing exists, it is loaded from the computer instead of being
763 * computer. 813 * generated.
764 * 814 *
765 * @param public_key The public key of the device. 815 * @param pair_record_plist Holds the pair record.
766 * @param host_id The HostID to use for the pair record plist.
767 * @param pair_record_plist Holds the generated pair record.
768 * 816 *
769 * @return LOCKDOWN_E_SUCCESS on success 817 * @return LOCKDOWN_E_SUCCESS on success
770 */ 818 */
771static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, char *host_id, plist_t *pair_record_plist) 819static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t *pair_record)
772{ 820{
773 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 821 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
774 822
775 gnutls_datum_t device_cert = { NULL, 0 }; 823 key_data_t public_key = { NULL, 0 };
776 gnutls_datum_t host_cert = { NULL, 0 }; 824 char* host_id = NULL;
777 gnutls_datum_t root_cert = { NULL, 0 }; 825 char* system_buid = NULL;
778 826
779 ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert); 827 /* retrieve device public key */
828 ret = lockdownd_get_device_public_key_as_key_data(client, &public_key);
780 if (ret != LOCKDOWN_E_SUCCESS) { 829 if (ret != LOCKDOWN_E_SUCCESS) {
781 return ret; 830 debug_info("device refused to send public key.");
831 goto leave;
832 }
833 debug_info("device public key follows:\n%.*s", public_key.size, public_key.data);
834
835 *pair_record = plist_new_dict();
836
837 /* generate keys and certificates into pair record */
838 userpref_error_t uret = USERPREF_E_SUCCESS;
839 uret = pair_record_generate_keys_and_certs(*pair_record, public_key);
840 switch(uret) {
841 case USERPREF_E_INVALID_ARG:
842 ret = LOCKDOWN_E_INVALID_ARG;
843 break;
844 case USERPREF_E_INVALID_CONF:
845 ret = LOCKDOWN_E_INVALID_CONF;
846 break;
847 case USERPREF_E_SSL_ERROR:
848 ret = LOCKDOWN_E_SSL_ERROR;
849 default:
850 break;
782 } 851 }
783 852
784 char *host_id_loc = host_id; 853 /* set SystemBUID */
854 userpref_read_system_buid(&system_buid);
855 if (system_buid) {
856 plist_dict_set_item(*pair_record, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid));
857 }
785 858
786 if (!host_id) 859 /* set HostID */
787 userpref_get_host_id(&host_id_loc); 860 host_id = generate_uuid();
861 pair_record_set_host_id(*pair_record, host_id);
788 862
789 /* setup request plist */ 863leave:
790 *pair_record_plist = plist_new_dict(); 864 if (host_id)
791 plist_dict_insert_item(*pair_record_plist, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size)); 865 free(host_id);
792 plist_dict_insert_item(*pair_record_plist, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size)); 866 if (system_buid)
793 plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id_loc)); 867 free(system_buid);
794 plist_dict_insert_item(*pair_record_plist, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size)); 868 if (public_key.data)
795 869 free(public_key.data);
796 if (!host_id)
797 free(host_id_loc);
798
799 if (device_cert.data)
800 free(device_cert.data);
801 if (host_cert.data)
802 free(host_cert.data);
803 if (root_cert.data)
804 free(root_cert.data);
805 870
806 return ret; 871 return ret;
807} 872}
@@ -809,11 +874,13 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c
809/** 874/**
810 * Function used internally by lockdownd_pair() and lockdownd_validate_pair() 875 * Function used internally by lockdownd_pair() and lockdownd_validate_pair()
811 * 876 *
812 * @param client The lockdown client to pair with. 877 * @param client The lockdown client
813 * @param pair_record The pair record to use for pairing. If NULL is passed, then 878 * @param pair_record The pair record to use for pairing. If NULL is passed, then
814 * the pair records from the current machine are used. New records will be 879 * the pair records from the current machine are used. New records will be
815 * generated automatically when pairing is done for the first time. 880 * generated automatically when pairing is done for the first time.
816 * @param verb This is either "Pair", "ValidatePair" or "Unpair". 881 * @param verb This is either "Pair", "ValidatePair" or "Unpair".
882 * @param options The pairing options to pass.
883 * @param response If non-NULL a pointer to lockdownd's response dictionary is returned.
817 * 884 *
818 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, 885 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
819 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, 886 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong,
@@ -821,73 +888,103 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c
821 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, 888 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected,
822 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id 889 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id
823 */ 890 */
824static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb) 891static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb, plist_t options, plist_t *result)
825{ 892{
826 if (!client) 893 if (!client)
827 return LOCKDOWN_E_INVALID_ARG; 894 return LOCKDOWN_E_INVALID_ARG;
828 895
829 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 896 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
830 plist_t dict = NULL; 897 plist_t dict = NULL;
831 plist_t dict_record = NULL; 898 plist_t pair_record_plist = NULL;
832 gnutls_datum_t public_key = { NULL, 0 }; 899 plist_t wifi_node = NULL;
833 int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ 900 int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */
834 901
835 if (pair_record && pair_record->host_id) { 902 if (pair_record && pair_record->system_buid && pair_record->host_id) {
836 /* valid pair_record passed? */ 903 /* valid pair_record passed? */
837 if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) { 904 if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) {
838 return LOCKDOWN_E_PLIST_ERROR; 905 return LOCKDOWN_E_PLIST_ERROR;
839 } 906 }
840 907
841 /* use passed pair_record */ 908 /* use passed pair_record */
842 dict_record = lockdownd_pair_record_to_plist(pair_record); 909 pair_record_plist = lockdownd_pair_record_to_plist(pair_record);
843 910
844 pairing_mode = 1; 911 pairing_mode = 1;
845 } else { 912 } else {
846 ret = lockdownd_get_device_public_key(client, &public_key); 913 /* generate a new pair record if pairing */
847 if (ret != LOCKDOWN_E_SUCCESS) { 914 if (!strcmp("Pair", verb)) {
848 if (public_key.data) 915 ret = pair_record_generate(client, &pair_record_plist);
849 free(public_key.data); 916
850 debug_info("device refused to send public key."); 917 if (ret != LOCKDOWN_E_SUCCESS) {
851 return ret; 918 if (pair_record_plist)
852 } 919 plist_free(pair_record_plist);
853 debug_info("device public key follows:\n%.*s", public_key.size, public_key.data); 920 return ret;
854 /* get libimobiledevice pair_record */ 921 }
855 ret = generate_pair_record_plist(public_key, NULL, &dict_record); 922
856 if (ret != LOCKDOWN_E_SUCCESS) { 923 /* get wifi mac now, if we get it later we fail on iOS 7 which causes a reconnect */
857 if (dict_record) 924 lockdownd_get_value(client, NULL, "WiFiAddress", &wifi_node);
858 plist_free(dict_record); 925 } else {
859 return ret; 926 /* use existing pair record */
927 userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record_plist);
928 if (uerr == USERPREF_E_READ_ERROR) {
929 debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid);
930 return LOCKDOWN_E_RECEIVE_TIMEOUT;
931 } else if (uerr == USERPREF_E_NOENT) {
932 debug_info("ERROR: No pair record for %s", client->device->udid);
933 return LOCKDOWN_E_INVALID_CONF;
934 } else if (uerr != USERPREF_E_SUCCESS) {
935 debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid);
936 return LOCKDOWN_E_INVALID_CONF;
937 }
860 } 938 }
861 } 939 }
862 940
863 /* Setup Pair request plist */ 941 plist_t request_pair_record = plist_copy(pair_record_plist);
942
943 /* remove stuff that is private */
944 plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY);
945 plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY);
946
947 /* setup pair request plist */
864 dict = plist_new_dict(); 948 dict = plist_new_dict();
865 plist_dict_add_label(dict, client->label); 949 plist_dict_add_label(dict, client->label);
866 plist_dict_insert_item(dict,"PairRecord", dict_record); 950 plist_dict_set_item(dict, "PairRecord", request_pair_record);
867 plist_dict_insert_item(dict, "Request", plist_new_string(verb)); 951 plist_dict_set_item(dict, "Request", plist_new_string(verb));
952 plist_dict_set_item(dict, "ProtocolVersion", plist_new_string(LOCKDOWN_PROTOCOL_VERSION));
953
954 if (options) {
955 plist_dict_set_item(dict, "PairingOptions", plist_copy(options));
956 }
868 957
869 /* send to device */ 958 /* send to device */
870 ret = lockdownd_send(client, dict); 959 ret = lockdownd_send(client, dict);
871 plist_free(dict); 960 plist_free(dict);
872 dict = NULL; 961 dict = NULL;
873 962
874 if (ret != LOCKDOWN_E_SUCCESS) 963 if (ret != LOCKDOWN_E_SUCCESS) {
964 plist_free(pair_record_plist);
965 if (wifi_node)
966 plist_free(wifi_node);
875 return ret; 967 return ret;
968 }
876 969
877 /* Now get device's answer */ 970 /* Now get device's answer */
878 ret = lockdownd_receive(client, &dict); 971 ret = lockdownd_receive(client, &dict);
879 972
880 if (ret != LOCKDOWN_E_SUCCESS) 973 if (ret != LOCKDOWN_E_SUCCESS) {
974 plist_free(pair_record_plist);
975 if (wifi_node)
976 plist_free(wifi_node);
881 return ret; 977 return ret;
978 }
882 979
883 if (strcmp(verb, "Unpair") == 0) { 980 if (strcmp(verb, "Unpair") == 0) {
884 /* workaround for Unpair giving back ValidatePair, 981 /* workaround for Unpair giving back ValidatePair,
885 * seems to be a bug in the device's fw */ 982 * seems to be a bug in the device's fw */
886 if (lockdown_check_result(dict, NULL) != RESULT_SUCCESS) { 983 if (lockdown_check_result(dict, NULL) != LOCKDOWN_E_SUCCESS) {
887 ret = LOCKDOWN_E_PAIRING_FAILED; 984 ret = LOCKDOWN_E_PAIRING_FAILED;
888 } 985 }
889 } else { 986 } else {
890 if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) { 987 if (lockdown_check_result(dict, verb) != LOCKDOWN_E_SUCCESS) {
891 ret = LOCKDOWN_E_PAIRING_FAILED; 988 ret = LOCKDOWN_E_PAIRING_FAILED;
892 } 989 }
893 } 990 }
@@ -896,13 +993,32 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_
896 if (ret == LOCKDOWN_E_SUCCESS) { 993 if (ret == LOCKDOWN_E_SUCCESS) {
897 debug_info("%s success", verb); 994 debug_info("%s success", verb);
898 if (!pairing_mode) { 995 if (!pairing_mode) {
996 debug_info("internal pairing mode");
899 if (!strcmp("Unpair", verb)) { 997 if (!strcmp("Unpair", verb)) {
900 /* remove public key from config */ 998 /* remove public key from config */
901 userpref_remove_device_public_key(client->uuid); 999 userpref_delete_pair_record(client->device->udid);
902 } else { 1000 } else {
903 /* store public key in config */ 1001 if (!strcmp("Pair", verb)) {
904 userpref_set_device_public_key(client->uuid, public_key); 1002 /* add returned escrow bag if available */
1003 plist_t extra_node = plist_dict_get_item(dict, USERPREF_ESCROW_BAG_KEY);
1004 if (extra_node && plist_get_node_type(extra_node) == PLIST_DATA) {
1005 debug_info("Saving EscrowBag from response in pair record");
1006 plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(extra_node));
1007 }
1008
1009 /* save previously retrieved wifi mac address in pair record */
1010 if (wifi_node) {
1011 debug_info("Saving WiFiAddress from device in pair record");
1012 plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, plist_copy(wifi_node));
1013 plist_free(wifi_node);
1014 wifi_node = NULL;
1015 }
1016
1017 userpref_save_pair_record(client->device->udid, client->device->mux_id, pair_record_plist);
1018 }
905 } 1019 }
1020 } else {
1021 debug_info("external pairing mode");
906 } 1022 }
907 } else { 1023 } else {
908 debug_info("%s failure", verb); 1024 debug_info("%s failure", verb);
@@ -914,92 +1030,60 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_
914 plist_get_string_val(error_node, &value); 1030 plist_get_string_val(error_node, &value);
915 if (value) { 1031 if (value) {
916 /* the first pairing fails if the device is password protected */ 1032 /* the first pairing fails if the device is password protected */
917 if (!strcmp(value, "PasswordProtected")) { 1033 ret = lockdownd_strtoerr(value);
918 ret = LOCKDOWN_E_PASSWORD_PROTECTED;
919 } else if (!strcmp(value, "InvalidHostID")) {
920 ret = LOCKDOWN_E_INVALID_HOST_ID;
921 }
922 free(value); 1034 free(value);
923 } 1035 }
924
925 plist_free(error_node);
926 error_node = NULL;
927 } 1036 }
928 } 1037 }
929 plist_free(dict); 1038
930 dict = NULL; 1039 if (pair_record_plist) {
931 if (public_key.data) 1040 plist_free(pair_record_plist);
932 free(public_key.data); 1041 pair_record_plist = NULL;
1042 }
1043
1044 if (wifi_node) {
1045 plist_free(wifi_node);
1046 wifi_node = NULL;
1047 }
1048
1049 if (result) {
1050 *result = dict;
1051 } else {
1052 plist_free(dict);
1053 dict = NULL;
1054 }
1055
933 return ret; 1056 return ret;
934} 1057}
935 1058
936/**
937 * Pairs the device using the supplied pair record.
938 *
939 * @param client The lockdown client to pair with.
940 * @param pair_record The pair record to use for pairing. If NULL is passed, then
941 * the pair records from the current machine are used. New records will be
942 * generated automatically when pairing is done for the first time.
943 *
944 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
945 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong,
946 * LOCKDOWN_E_PAIRING_FAILED if the pairing failed,
947 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected,
948 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id
949 */
950lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) 1059lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
951{ 1060{
952 return lockdownd_do_pair(client, pair_record, "Pair"); 1061
1062 plist_t options = plist_new_dict();
1063 plist_dict_set_item(options, "ExtendedPairingErrors", plist_new_bool(1));
1064
1065 lockdownd_error_t ret = lockdownd_do_pair(client, pair_record, "Pair", options, NULL);
1066
1067 plist_free(options);
1068
1069 return ret;
1070}
1071
1072lockdownd_error_t lockdownd_pair_with_options(lockdownd_client_t client, lockdownd_pair_record_t pair_record, plist_t options, plist_t *response)
1073{
1074 return lockdownd_do_pair(client, pair_record, "Pair", options, response);
953} 1075}
954 1076
955/**
956 * Validates if the device is paired with the given HostID. If succeeded them
957 * specified host will become trusted host of the device indicated by the
958 * lockdownd preference named TrustedHostAttached. Otherwise the host must because
959 * paired using lockdownd_pair() first.
960 *
961 * @param client The lockdown client to pair with.
962 * @param pair_record The pair record to validate pairing with. If NULL is
963 * passed, then the pair record is read from the internal pairing record
964 * management.
965 *
966 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
967 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong,
968 * LOCKDOWN_E_PAIRING_FAILED if the pairing failed,
969 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected,
970 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id
971 */
972lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) 1077lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
973{ 1078{
974 return lockdownd_do_pair(client, pair_record, "ValidatePair"); 1079 return lockdownd_do_pair(client, pair_record, "ValidatePair", NULL, NULL);
975} 1080}
976 1081
977/**
978 * Unpairs the device with the given HostID and removes the pairing records
979 * from the device and host if the internal pairing record management is used.
980 *
981 * @param client The lockdown client to pair with.
982 * @param pair_record The pair record to use for unpair. If NULL is passed, then
983 * the pair records from the current machine are used.
984 *
985 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
986 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong,
987 * LOCKDOWN_E_PAIRING_FAILED if the pairing failed,
988 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected,
989 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id
990 */
991lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) 1082lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
992{ 1083{
993 return lockdownd_do_pair(client, pair_record, "Unpair"); 1084 return lockdownd_do_pair(client, pair_record, "Unpair", NULL, NULL);
994} 1085}
995 1086
996/**
997 * Tells the device to immediately enter recovery mode.
998 *
999 * @param client The lockdown client
1000 *
1001 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
1002 */
1003lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) 1087lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
1004{ 1088{
1005 if (!client) 1089 if (!client)
@@ -1009,7 +1093,7 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
1009 1093
1010 plist_t dict = plist_new_dict(); 1094 plist_t dict = plist_new_dict();
1011 plist_dict_add_label(dict, client->label); 1095 plist_dict_add_label(dict, client->label);
1012 plist_dict_insert_item(dict,"Request", plist_new_string("EnterRecovery")); 1096 plist_dict_set_item(dict,"Request", plist_new_string("EnterRecovery"));
1013 1097
1014 debug_info("telling device to enter recovery mode"); 1098 debug_info("telling device to enter recovery mode");
1015 1099
@@ -1019,23 +1103,17 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
1019 1103
1020 ret = lockdownd_receive(client, &dict); 1104 ret = lockdownd_receive(client, &dict);
1021 1105
1022 if (lockdown_check_result(dict, "EnterRecovery") == RESULT_SUCCESS) { 1106 ret = lockdown_check_result(dict, "EnterRecovery");
1107 if (ret == LOCKDOWN_E_SUCCESS) {
1023 debug_info("success"); 1108 debug_info("success");
1024 ret = LOCKDOWN_E_SUCCESS;
1025 } 1109 }
1110
1026 plist_free(dict); 1111 plist_free(dict);
1027 dict = NULL; 1112 dict = NULL;
1113
1028 return ret; 1114 return ret;
1029} 1115}
1030 1116
1031/**
1032 * Sends the Goodbye request to lockdownd signaling the end of communication.
1033 *
1034 * @param client The lockdown client
1035 *
1036 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
1037 * LOCKDOWN_E_PLIST_ERROR if the device did not acknowledge the request
1038 */
1039lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) 1117lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
1040{ 1118{
1041 if (!client) 1119 if (!client)
@@ -1045,7 +1123,7 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
1045 1123
1046 plist_t dict = plist_new_dict(); 1124 plist_t dict = plist_new_dict();
1047 plist_dict_add_label(dict, client->label); 1125 plist_dict_add_label(dict, client->label);
1048 plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye")); 1126 plist_dict_set_item(dict,"Request", plist_new_string("Goodbye"));
1049 1127
1050 debug_info("called"); 1128 debug_info("called");
1051 1129
@@ -1059,187 +1137,17 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
1059 return LOCKDOWN_E_PLIST_ERROR; 1137 return LOCKDOWN_E_PLIST_ERROR;
1060 } 1138 }
1061 1139
1062 if (lockdown_check_result(dict, "Goodbye") == RESULT_SUCCESS) { 1140 ret = lockdown_check_result(dict, "Goodbye");
1141 if (ret == LOCKDOWN_E_SUCCESS) {
1063 debug_info("success"); 1142 debug_info("success");
1064 ret = LOCKDOWN_E_SUCCESS;
1065 } 1143 }
1144
1066 plist_free(dict); 1145 plist_free(dict);
1067 dict = NULL; 1146 dict = NULL;
1068 return ret;
1069}
1070
1071/**
1072 * Generates the device certificate from the public key as well as the host
1073 * and root certificates.
1074 *
1075 * @param public_key The public key of the device to use for generation.
1076 * @param odevice_cert Holds the generated device certificate.
1077 * @param ohost_cert Holds the generated host certificate.
1078 * @param oroot_cert Holds the generated root certificate.
1079 *
1080 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a parameter is NULL,
1081 * LOCKDOWN_E_INVALID_CONF if the internal configuration system failed,
1082 * LOCKDOWN_E_SSL_ERROR if the certificates could not be generated
1083 */
1084lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * odevice_cert,
1085 gnutls_datum_t * ohost_cert, gnutls_datum_t * oroot_cert)
1086{
1087 if (!public_key.data || !odevice_cert || !ohost_cert || !oroot_cert)
1088 return LOCKDOWN_E_INVALID_ARG;
1089 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1090 userpref_error_t uret = USERPREF_E_UNKNOWN_ERROR;
1091
1092 gnutls_datum_t modulus = { NULL, 0 };
1093 gnutls_datum_t exponent = { NULL, 0 };
1094
1095 /* now decode the PEM encoded key */
1096 gnutls_datum_t der_pub_key;
1097 if (GNUTLS_E_SUCCESS == gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key)) {
1098
1099 /* initalize asn.1 parser */
1100 ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY;
1101 if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) {
1102
1103 ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY;
1104 asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key);
1105
1106 if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) {
1107
1108 /* get size to read */
1109 int ret1 = asn1_read_value(asn1_pub_key, "modulus", NULL, (int*)&modulus.size);
1110 int ret2 = asn1_read_value(asn1_pub_key, "publicExponent", NULL, (int*)&exponent.size);
1111
1112 modulus.data = gnutls_malloc(modulus.size);
1113 exponent.data = gnutls_malloc(exponent.size);
1114
1115 ret1 = asn1_read_value(asn1_pub_key, "modulus", modulus.data, (int*)&modulus.size);
1116 ret2 = asn1_read_value(asn1_pub_key, "publicExponent", exponent.data, (int*)&exponent.size);
1117 if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2)
1118 ret = LOCKDOWN_E_SUCCESS;
1119 }
1120 if (asn1_pub_key)
1121 asn1_delete_structure(&asn1_pub_key);
1122 }
1123 if (pkcs1)
1124 asn1_delete_structure(&pkcs1);
1125 }
1126
1127 /* now generate certificates */
1128 if (LOCKDOWN_E_SUCCESS == ret && 0 != modulus.size && 0 != exponent.size) {
1129
1130 gnutls_global_init();
1131 gnutls_datum_t essentially_null = { (unsigned char*)strdup("abababababababab"), strlen("abababababababab") };
1132
1133 gnutls_x509_privkey_t fake_privkey, root_privkey, host_privkey;
1134 gnutls_x509_crt_t dev_cert, root_cert, host_cert;
1135
1136 gnutls_x509_privkey_init(&fake_privkey);
1137 gnutls_x509_crt_init(&dev_cert);
1138 gnutls_x509_crt_init(&root_cert);
1139 gnutls_x509_crt_init(&host_cert);
1140
1141 if (GNUTLS_E_SUCCESS ==
1142 gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null,
1143 &essentially_null, &essentially_null)) {
1144
1145 gnutls_x509_privkey_init(&root_privkey);
1146 gnutls_x509_privkey_init(&host_privkey);
1147
1148 uret = userpref_get_keys_and_certs(root_privkey, root_cert, host_privkey, host_cert);
1149
1150 if (USERPREF_E_SUCCESS == uret) {
1151 /* generate device certificate */
1152 gnutls_x509_crt_set_key(dev_cert, fake_privkey);
1153 gnutls_x509_crt_set_serial(dev_cert, "\x00", 1);
1154 gnutls_x509_crt_set_version(dev_cert, 3);
1155 gnutls_x509_crt_set_ca_status(dev_cert, 0);
1156 gnutls_x509_crt_set_activation_time(dev_cert, time(NULL));
1157 gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
1158 gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey);
1159
1160 if (LOCKDOWN_E_SUCCESS == ret) {
1161 /* if everything went well, export in PEM format */
1162 size_t export_size = 0;
1163 gnutls_datum_t dev_pem = { NULL, 0 };
1164 gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &export_size);
1165 dev_pem.data = gnutls_malloc(export_size);
1166 gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_pem.data, &export_size);
1167 dev_pem.size = export_size;
1168
1169 gnutls_datum_t pem_root_cert = { NULL, 0 };
1170 gnutls_datum_t pem_host_cert = { NULL, 0 };
1171
1172 uret = userpref_get_certs_as_pem(&pem_root_cert, &pem_host_cert);
1173
1174 if (USERPREF_E_SUCCESS == uret) {
1175 /* copy buffer for output */
1176 odevice_cert->data = malloc(dev_pem.size);
1177 memcpy(odevice_cert->data, dev_pem.data, dev_pem.size);
1178 odevice_cert->size = dev_pem.size;
1179
1180 ohost_cert->data = malloc(pem_host_cert.size);
1181 memcpy(ohost_cert->data, pem_host_cert.data, pem_host_cert.size);
1182 ohost_cert->size = pem_host_cert.size;
1183
1184 oroot_cert->data = malloc(pem_root_cert.size);
1185 memcpy(oroot_cert->data, pem_root_cert.data, pem_root_cert.size);
1186 oroot_cert->size = pem_root_cert.size;
1187
1188 g_free(pem_root_cert.data);
1189 g_free(pem_host_cert.data);
1190
1191 if (dev_pem.data)
1192 gnutls_free(dev_pem.data);
1193 }
1194 }
1195 }
1196
1197 switch(uret) {
1198 case USERPREF_E_INVALID_ARG:
1199 ret = LOCKDOWN_E_INVALID_ARG;
1200 break;
1201 case USERPREF_E_INVALID_CONF:
1202 ret = LOCKDOWN_E_INVALID_CONF;
1203 break;
1204 case USERPREF_E_SSL_ERROR:
1205 ret = LOCKDOWN_E_SSL_ERROR;
1206 default:
1207 break;
1208 }
1209 }
1210
1211 if (essentially_null.data)
1212 free(essentially_null.data);
1213 gnutls_x509_crt_deinit(dev_cert);
1214 gnutls_x509_crt_deinit(root_cert);
1215 gnutls_x509_crt_deinit(host_cert);
1216 gnutls_x509_privkey_deinit(fake_privkey);
1217 gnutls_x509_privkey_deinit(root_privkey);
1218 gnutls_x509_privkey_deinit(host_privkey);
1219
1220 }
1221
1222 gnutls_free(modulus.data);
1223 gnutls_free(exponent.data);
1224
1225 gnutls_free(der_pub_key.data);
1226 1147
1227 return ret; 1148 return ret;
1228} 1149}
1229 1150
1230/**
1231 * Opens a session with lockdownd and switches to SSL mode if device wants it.
1232 *
1233 * @param client The lockdownd client
1234 * @param host_id The HostID of the computer
1235 * @param session_id The new session_id of the created session
1236 * @param ssl_enabled Whether SSL communication is used in the session
1237 *
1238 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a client or
1239 * host_id is NULL, LOCKDOWN_E_PLIST_ERROR if the response plist had errors,
1240 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the supplied HostID,
1241 * LOCKDOWN_E_SSL_ERROR if enabling SSL communication failed
1242 */
1243lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled) 1151lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled)
1244{ 1152{
1245 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; 1153 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
@@ -1251,14 +1159,28 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1251 /* if we have a running session, stop current one first */ 1159 /* if we have a running session, stop current one first */
1252 if (client->session_id) { 1160 if (client->session_id) {
1253 lockdownd_stop_session(client, client->session_id); 1161 lockdownd_stop_session(client, client->session_id);
1254 free(client->session_id);
1255 } 1162 }
1256 1163
1257 /* setup request plist */ 1164 /* setup request plist */
1258 dict = plist_new_dict(); 1165 dict = plist_new_dict();
1259 plist_dict_add_label(dict, client->label); 1166 plist_dict_add_label(dict, client->label);
1260 plist_dict_insert_item(dict,"HostID", plist_new_string(host_id)); 1167 plist_dict_set_item(dict,"Request", plist_new_string("StartSession"));
1261 plist_dict_insert_item(dict,"Request", plist_new_string("StartSession")); 1168
1169 /* add host id */
1170 if (host_id) {
1171 plist_dict_set_item(dict, "HostID", plist_new_string(host_id));
1172 }
1173
1174 /* add system buid */
1175 char *system_buid = NULL;
1176 userpref_read_system_buid(&system_buid);
1177 if (system_buid) {
1178 plist_dict_set_item(dict, "SystemBUID", plist_new_string(system_buid));
1179 if (system_buid) {
1180 free(system_buid);
1181 system_buid = NULL;
1182 }
1183 }
1262 1184
1263 ret = lockdownd_send(client, dict); 1185 ret = lockdownd_send(client, dict);
1264 plist_free(dict); 1186 plist_free(dict);
@@ -1272,17 +1194,8 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1272 if (!dict) 1194 if (!dict)
1273 return LOCKDOWN_E_PLIST_ERROR; 1195 return LOCKDOWN_E_PLIST_ERROR;
1274 1196
1275 if (lockdown_check_result(dict, "StartSession") == RESULT_FAILURE) { 1197 ret = lockdown_check_result(dict, "StartSession");
1276 plist_t error_node = plist_dict_get_item(dict, "Error"); 1198 if (ret == LOCKDOWN_E_SUCCESS) {
1277 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1278 char *error = NULL;
1279 plist_get_string_val(error_node, &error);
1280 if (!strcmp(error, "InvalidHostID")) {
1281 ret = LOCKDOWN_E_INVALID_HOST_ID;
1282 }
1283 free(error);
1284 }
1285 } else {
1286 uint8_t use_ssl = 0; 1199 uint8_t use_ssl = 0;
1287 1200
1288 plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL"); 1201 plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL");
@@ -1299,6 +1212,7 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1299 if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) { 1212 if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) {
1300 plist_get_string_val(session_node, &client->session_id); 1213 plist_get_string_val(session_node, &client->session_id);
1301 } 1214 }
1215
1302 if (client->session_id) { 1216 if (client->session_id) {
1303 debug_info("SessionID: %s", client->session_id); 1217 debug_info("SessionID: %s", client->session_id);
1304 if (session_id != NULL) 1218 if (session_id != NULL)
@@ -1306,18 +1220,15 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1306 } else { 1220 } else {
1307 debug_info("Failed to get SessionID!"); 1221 debug_info("Failed to get SessionID!");
1308 } 1222 }
1309 debug_info("Enable SSL Session: %s", (use_ssl?"true":"false")); 1223
1224 debug_info("Enable SSL Session: %s", (use_ssl ? "true" : "false"));
1225
1310 if (use_ssl) { 1226 if (use_ssl) {
1311 ret = property_list_service_enable_ssl(client->parent); 1227 ret = lockdownd_error(property_list_service_enable_ssl(client->parent));
1312 if (ret == PROPERTY_LIST_SERVICE_E_SUCCESS) { 1228 client->ssl_enabled = (ret == LOCKDOWN_E_SUCCESS ? 1 : 0);
1313 client->ssl_enabled = 1;
1314 } else {
1315 ret = LOCKDOWN_E_SSL_ERROR;
1316 client->ssl_enabled = 0;
1317 }
1318 } else { 1229 } else {
1319 client->ssl_enabled = 0;
1320 ret = LOCKDOWN_E_SUCCESS; 1230 ret = LOCKDOWN_E_SUCCESS;
1231 client->ssl_enabled = 0;
1321 } 1232 }
1322 } 1233 }
1323 1234
@@ -1328,40 +1239,96 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1328} 1239}
1329 1240
1330/** 1241/**
1331 * Requests to start a service and retrieve it's port on success. 1242 * Internal function used by lockdownd_do_start_service to create the
1243 * StartService request's plist.
1332 * 1244 *
1333 * @param client The lockdownd client 1245 * @param client The lockdownd client
1334 * @param service The name of the service to start 1246 * @param identifier The identifier of the service to start
1335 * @param port The port number the service was started on 1247 * @param send_escrow_bag Should we send the device's escrow bag with the request
1336 1248 * @param request The request's plist on success, NULL on failure
1337 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter 1249 *
1250 * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_CONF on failure
1251 * to read the escrow bag from the device's record (when used).
1252 */
1253static lockdownd_error_t lockdownd_build_start_service_request(lockdownd_client_t client, const char *identifier, int send_escrow_bag, plist_t *request)
1254{
1255 plist_t dict = plist_new_dict();
1256
1257 /* create the basic request params */
1258 plist_dict_add_label(dict, client->label);
1259 plist_dict_set_item(dict, "Request", plist_new_string("StartService"));
1260 plist_dict_set_item(dict, "Service", plist_new_string(identifier));
1261
1262 /* if needed - get the escrow bag for the device and send it with the request */
1263 if (send_escrow_bag) {
1264 /* get the pairing record */
1265 plist_t pair_record = NULL;
1266 userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record);
1267 if (uerr == USERPREF_E_READ_ERROR) {
1268 debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid);
1269 plist_free(dict);
1270 return LOCKDOWN_E_RECEIVE_TIMEOUT;
1271 } else if (uerr == USERPREF_E_NOENT) {
1272 debug_info("ERROR: No pair record for %s", client->device->udid);
1273 plist_free(dict);
1274 return LOCKDOWN_E_INVALID_CONF;
1275 } else if (uerr != USERPREF_E_SUCCESS) {
1276 debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid);
1277 plist_free(dict);
1278 return LOCKDOWN_E_INVALID_CONF;
1279 }
1280
1281 /* try to read the escrow bag from the record */
1282 plist_t escrow_bag = plist_dict_get_item(pair_record, USERPREF_ESCROW_BAG_KEY);
1283 if (!escrow_bag || (PLIST_DATA != plist_get_node_type(escrow_bag))) {
1284 debug_info("ERROR: Failed to retrieve the escrow bag from the device's record");
1285 plist_free(dict);
1286 plist_free(pair_record);
1287 return LOCKDOWN_E_INVALID_CONF;
1288 }
1289
1290 debug_info("Adding escrow bag to StartService for %s", identifier);
1291 plist_dict_set_item(dict, USERPREF_ESCROW_BAG_KEY, plist_copy(escrow_bag));
1292 plist_free(pair_record);
1293 }
1294
1295 *request = dict;
1296 return LOCKDOWN_E_SUCCESS;
1297}
1298
1299/**
1300 * Function used internally by lockdownd_start_service and lockdownd_start_service_with_escrow_bag.
1301 *
1302 * @param client The lockdownd client
1303 * @param identifier The identifier of the service to start
1304 * @param send_escrow_bag Should we send the device's escrow bag with the request
1305 * @param descriptor The service descriptor on success or NULL on failure
1306 *
1307 * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_ARG if a parameter
1338 * is NULL, LOCKDOWN_E_INVALID_SERVICE if the requested service is not known 1308 * is NULL, LOCKDOWN_E_INVALID_SERVICE if the requested service is not known
1339 * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because 1309 * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because
1340 * started by the device 1310 * started by the device, LOCKDOWN_E_INVALID_CONF if the host id or escrow bag (when
1311 * used) are missing from the device record.
1341 */ 1312 */
1342lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, uint16_t *port) 1313static lockdownd_error_t lockdownd_do_start_service(lockdownd_client_t client, const char *identifier, int send_escrow_bag, lockdownd_service_descriptor_t *service)
1343{ 1314{
1344 if (!client || !service || !port) 1315 if (!client || !identifier || !service)
1345 return LOCKDOWN_E_INVALID_ARG; 1316 return LOCKDOWN_E_INVALID_ARG;
1346 1317
1347 char *host_id = NULL; 1318 if (*service) {
1348 userpref_get_host_id(&host_id); 1319 // reset fields if service descriptor is reused
1349 if (!host_id) 1320 (*service)->port = 0;
1350 return LOCKDOWN_E_INVALID_CONF; 1321 (*service)->ssl_enabled = 0;
1351 if (!client->session_id) 1322 }
1352 return LOCKDOWN_E_NO_RUNNING_SESSION;
1353 1323
1354 plist_t dict = NULL; 1324 plist_t dict = NULL;
1355 uint16_t port_loc = 0; 1325 uint16_t port_loc = 0;
1356 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 1326 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1357 1327
1358 free(host_id); 1328 /* create StartService request */
1359 host_id = NULL; 1329 ret = lockdownd_build_start_service_request(client, identifier, send_escrow_bag, &dict);
1360 1330 if (LOCKDOWN_E_SUCCESS != ret)
1361 dict = plist_new_dict(); 1331 return ret;
1362 plist_dict_add_label(dict, client->label);
1363 plist_dict_insert_item(dict,"Request", plist_new_string("StartService"));
1364 plist_dict_insert_item(dict,"Service", plist_new_string(service));
1365 1332
1366 /* send to device */ 1333 /* send to device */
1367 ret = lockdownd_send(client, dict); 1334 ret = lockdownd_send(client, dict);
@@ -1379,57 +1346,63 @@ lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char
1379 if (!dict) 1346 if (!dict)
1380 return LOCKDOWN_E_PLIST_ERROR; 1347 return LOCKDOWN_E_PLIST_ERROR;
1381 1348
1382 ret = LOCKDOWN_E_UNKNOWN_ERROR; 1349 ret = lockdown_check_result(dict, "StartService");
1383 if (lockdown_check_result(dict, "StartService") == RESULT_SUCCESS) { 1350 if (ret == LOCKDOWN_E_SUCCESS) {
1384 plist_t port_value_node = plist_dict_get_item(dict, "Port"); 1351 if (*service == NULL)
1385 1352 *service = (lockdownd_service_descriptor_t)malloc(sizeof(struct lockdownd_service_descriptor));
1386 if (port_value_node && (plist_get_node_type(port_value_node) == PLIST_UINT)) { 1353 (*service)->port = 0;
1354 (*service)->ssl_enabled = 0;
1355 (*service)->identifier = strdup(identifier);
1356
1357 /* read service port number */
1358 plist_t node = plist_dict_get_item(dict, "Port");
1359 if (node && (plist_get_node_type(node) == PLIST_UINT)) {
1387 uint64_t port_value = 0; 1360 uint64_t port_value = 0;
1388 plist_get_uint_val(port_value_node, &port_value); 1361 plist_get_uint_val(node, &port_value);
1389 1362
1390 if (port_value) { 1363 if (port_value) {
1391 port_loc = port_value; 1364 port_loc = port_value;
1392 ret = LOCKDOWN_E_SUCCESS; 1365 ret = LOCKDOWN_E_SUCCESS;
1393 } 1366 }
1394 if (port && ret == LOCKDOWN_E_SUCCESS) 1367 if (port_loc && ret == LOCKDOWN_E_SUCCESS) {
1395 *port = port_loc; 1368 (*service)->port = port_loc;
1369 }
1370 }
1371
1372 /* check if the service requires SSL */
1373 node = plist_dict_get_item(dict, "EnableServiceSSL");
1374 if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) {
1375 uint8_t b = 0;
1376 plist_get_bool_val(node, &b);
1377 (*service)->ssl_enabled = b;
1396 } 1378 }
1397 } else { 1379 } else {
1398 ret = LOCKDOWN_E_START_SERVICE_FAILED;
1399 plist_t error_node = plist_dict_get_item(dict, "Error"); 1380 plist_t error_node = plist_dict_get_item(dict, "Error");
1400 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { 1381 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1401 char *error = NULL; 1382 char *error = NULL;
1402 plist_get_string_val(error_node, &error); 1383 plist_get_string_val(error_node, &error);
1403 if (!strcmp(error, "InvalidService")) { 1384 ret = lockdownd_strtoerr(error);
1404 ret = LOCKDOWN_E_INVALID_SERVICE;
1405 }
1406 free(error); 1385 free(error);
1407 } 1386 }
1408 } 1387 }
1409 1388
1410 plist_free(dict); 1389 plist_free(dict);
1411 dict = NULL; 1390 dict = NULL;
1391
1412 return ret; 1392 return ret;
1413} 1393}
1414 1394
1415/** 1395lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service)
1416 * Activates the device. Only works within an open session. 1396{
1417 * The ActivationRecord plist dictionary must be obtained using the 1397 return lockdownd_do_start_service(client, identifier, 0, service);
1418 * activation protocol requesting from Apple's https webservice. 1398}
1419 * 1399
1420 * @see http://iphone-docs.org/doku.php?id=docs:protocols:activation 1400lockdownd_error_t lockdownd_start_service_with_escrow_bag(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service)
1421 * 1401{
1422 * @param client The lockdown client 1402 return lockdownd_do_start_service(client, identifier, 1, service);
1423 * @param activation_record The activation record plist dictionary 1403}
1424 * 1404
1425 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or 1405lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record)
1426 * activation_record is NULL, LOCKDOWN_E_NO_RUNNING_SESSION if no session is
1427 * open, LOCKDOWN_E_PLIST_ERROR if the received plist is broken,
1428 * LOCKDOWN_E_ACTIVATION_FAILED if the activation failed,
1429 * LOCKDOWN_E_INVALID_ACTIVATION_RECORD if the device reports that the
1430 * activation_record is invalid
1431 */
1432lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record)
1433{ 1406{
1434 if (!client) 1407 if (!client)
1435 return LOCKDOWN_E_INVALID_ARG; 1408 return LOCKDOWN_E_INVALID_ARG;
@@ -1444,8 +1417,8 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati
1444 1417
1445 plist_t dict = plist_new_dict(); 1418 plist_t dict = plist_new_dict();
1446 plist_dict_add_label(dict, client->label); 1419 plist_dict_add_label(dict, client->label);
1447 plist_dict_insert_item(dict,"Request", plist_new_string("Activate")); 1420 plist_dict_set_item(dict,"Request", plist_new_string("Activate"));
1448 plist_dict_insert_item(dict,"ActivationRecord", plist_copy(activation_record)); 1421 plist_dict_set_item(dict,"ActivationRecord", plist_copy(activation_record));
1449 1422
1450 ret = lockdownd_send(client, dict); 1423 ret = lockdownd_send(client, dict);
1451 plist_free(dict); 1424 plist_free(dict);
@@ -1457,39 +1430,17 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati
1457 return LOCKDOWN_E_PLIST_ERROR; 1430 return LOCKDOWN_E_PLIST_ERROR;
1458 } 1431 }
1459 1432
1460 ret = LOCKDOWN_E_ACTIVATION_FAILED; 1433 ret = lockdown_check_result(dict, "Activate");
1461 if (lockdown_check_result(dict, "Activate") == RESULT_SUCCESS) { 1434 if (ret == LOCKDOWN_E_SUCCESS) {
1462 debug_info("success"); 1435 debug_info("success");
1463 ret = LOCKDOWN_E_SUCCESS;
1464
1465 } else {
1466 plist_t error_node = plist_dict_get_item(dict, "Error");
1467 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1468 char *error = NULL;
1469 plist_get_string_val(error_node, &error);
1470 if (!strcmp(error, "InvalidActivationRecord")) {
1471 ret = LOCKDOWN_E_INVALID_ACTIVATION_RECORD;
1472 }
1473 free(error);
1474 }
1475 } 1436 }
1476 1437
1477 plist_free(dict); 1438 plist_free(dict);
1478 dict = NULL; 1439 dict = NULL;
1479 1440
1480 return ret; 1441 return ret;
1481} 1442}
1482 1443
1483/**
1484 * Deactivates the device, returning it to the locked “Activate with iTunes”
1485 * screen.
1486 *
1487 * @param client The lockdown client
1488 *
1489 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
1490 * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open,
1491 * LOCKDOWN_E_PLIST_ERROR if the received plist is broken
1492 */
1493lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) 1444lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1494{ 1445{
1495 if (!client) 1446 if (!client)
@@ -1502,7 +1453,7 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1502 1453
1503 plist_t dict = plist_new_dict(); 1454 plist_t dict = plist_new_dict();
1504 plist_dict_add_label(dict, client->label); 1455 plist_dict_add_label(dict, client->label);
1505 plist_dict_insert_item(dict,"Request", plist_new_string("Deactivate")); 1456 plist_dict_set_item(dict,"Request", plist_new_string("Deactivate"));
1506 1457
1507 ret = lockdownd_send(client, dict); 1458 ret = lockdownd_send(client, dict);
1508 plist_free(dict); 1459 plist_free(dict);
@@ -1514,11 +1465,11 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1514 return LOCKDOWN_E_PLIST_ERROR; 1465 return LOCKDOWN_E_PLIST_ERROR;
1515 } 1466 }
1516 1467
1517 ret = LOCKDOWN_E_UNKNOWN_ERROR; 1468 ret = lockdown_check_result(dict, "Deactivate");
1518 if (lockdown_check_result(dict, "Deactivate") == RESULT_SUCCESS) { 1469 if (ret == LOCKDOWN_E_SUCCESS) {
1519 debug_info("success"); 1470 debug_info("success");
1520 ret = LOCKDOWN_E_SUCCESS;
1521 } 1471 }
1472
1522 plist_free(dict); 1473 plist_free(dict);
1523 dict = NULL; 1474 dict = NULL;
1524 1475
@@ -1537,19 +1488,6 @@ static void str_remove_spaces(char *source)
1537 *dest = 0; 1488 *dest = 0;
1538} 1489}
1539 1490
1540/**
1541 * Calculates and returns the data classes the device supports from lockdownd.
1542 *
1543 * @param client An initialized lockdownd client.
1544 * @param classes A pointer to store an array of class names. The caller is responsible
1545 * for freeing the memory which can be done using mobilesync_data_classes_free().
1546 * @param count The number of items in the classes array.
1547 *
1548 * @return LOCKDOWN_E_SUCCESS on success,
1549 * LOCKDOWN_E_INVALID_ARG when client is NULL,
1550 * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open,
1551 * LOCKDOWN_E_PLIST_ERROR if the received plist is broken
1552 */
1553lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count) 1491lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count)
1554{ 1492{
1555 if (!client) 1493 if (!client)
@@ -1583,14 +1521,16 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha
1583 } 1521 }
1584 1522
1585 while((value = plist_array_get_item(dict, *count)) != NULL) { 1523 while((value = plist_array_get_item(dict, *count)) != NULL) {
1586 plist_get_string_val(value, &val); 1524 plist_get_string_val(value, &val);
1587 newlist = realloc(*classes, sizeof(char*) * (*count+1)); 1525 newlist = realloc(*classes, sizeof(char*) * (*count+1));
1588 str_remove_spaces(val); 1526 str_remove_spaces(val);
1589 asprintf(&newlist[*count], "com.apple.%s", val); 1527 if (asprintf(&newlist[*count], "com.apple.%s", val) < 0) {
1590 free(val); 1528 debug_info("ERROR: asprintf failed");
1591 val = NULL; 1529 }
1592 *classes = newlist; 1530 free(val);
1593 *count = *count+1; 1531 val = NULL;
1532 *classes = newlist;
1533 *count = *count+1;
1594 } 1534 }
1595 1535
1596 newlist = realloc(*classes, sizeof(char*) * (*count+1)); 1536 newlist = realloc(*classes, sizeof(char*) * (*count+1));
@@ -1603,14 +1543,6 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha
1603 return LOCKDOWN_E_SUCCESS; 1543 return LOCKDOWN_E_SUCCESS;
1604} 1544}
1605 1545
1606
1607/**
1608 * Frees memory of an allocated array of data classes as returned by lockdownd_get_sync_data_classes()
1609 *
1610 * @param classes An array of class names to free.
1611 *
1612 * @return LOCKDOWN_E_SUCCESS on success
1613 */
1614lockdownd_error_t lockdownd_data_classes_free(char **classes) 1546lockdownd_error_t lockdownd_data_classes_free(char **classes)
1615{ 1547{
1616 if (classes) { 1548 if (classes) {
@@ -1622,3 +1554,51 @@ lockdownd_error_t lockdownd_data_classes_free(char **classes)
1622 } 1554 }
1623 return LOCKDOWN_E_SUCCESS; 1555 return LOCKDOWN_E_SUCCESS;
1624} 1556}
1557
1558lockdownd_error_t lockdownd_service_descriptor_free(lockdownd_service_descriptor_t service)
1559{
1560 if (service) {
1561 free(service->identifier);
1562 free(service);
1563 }
1564
1565 return LOCKDOWN_E_SUCCESS;
1566}
1567
1568const char* lockdownd_strerror(lockdownd_error_t err)
1569{
1570 switch (err) {
1571 case LOCKDOWN_E_SUCCESS:
1572 return "Success";
1573 case LOCKDOWN_E_INVALID_ARG:
1574 return "Invalid argument";
1575 case LOCKDOWN_E_INVALID_CONF:
1576 return "Invalid configuration";
1577 case LOCKDOWN_E_PLIST_ERROR:
1578 return "PropertyList error";
1579 case LOCKDOWN_E_PAIRING_FAILED:
1580 return "Pairing failed";
1581 case LOCKDOWN_E_SSL_ERROR:
1582 return "SSL error";
1583 case LOCKDOWN_E_DICT_ERROR:
1584 return "Invalid dictionary";
1585 case LOCKDOWN_E_RECEIVE_TIMEOUT:
1586 return "Receive timeout";
1587 case LOCKDOWN_E_MUX_ERROR:
1588 return "Mux error";
1589 case LOCKDOWN_E_NO_RUNNING_SESSION:
1590 return "No running session";
1591 case LOCKDOWN_E_UNKNOWN_ERROR:
1592 return "Unknown Error";
1593 default: {
1594 int i = 0;
1595 while (lockdownd_error_str_map[i].lockdown_errstr) {
1596 if (lockdownd_error_str_map[i].errcode == err) {
1597 return lockdownd_error_str_map[i].errstr;
1598 }
1599 i++;
1600 }
1601 } break;
1602 }
1603 return "Unknown Error";
1604}