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