~ubuntu-branches/ubuntu/lucid/libimobiledevice/lucid

« back to all changes in this revision

Viewing changes to src/lockdown.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2010-02-02 21:19:51 UTC
  • Revision ID: james.westby@ubuntu.com-20100202211951-qv87ejwmucigqezf
Tags: upstream-0.9.7
Import upstream version 0.9.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * lockdown.c
 
3
 * libimobiledevice built-in lockdownd client
 
4
 *
 
5
 * Copyright (c) 2008 Zach C. All Rights Reserved.
 
6
 *
 
7
 * This library is free software; you can redistribute it and/or
 
8
 * modify it under the terms of the GNU Lesser General Public
 
9
 * License as published by the Free Software Foundation; either
 
10
 * version 2.1 of the License, or (at your option) any later version.
 
11
 *
 
12
 * This library is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
 * Lesser General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU Lesser General Public
 
18
 * License along with this library; if not, write to the Free Software
 
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
20
 */
 
21
 
 
22
#include <arpa/inet.h>
 
23
#include <errno.h>
 
24
#include <string.h>
 
25
#include <stdlib.h>
 
26
#include <glib.h>
 
27
#include <libtasn1.h>
 
28
#include <gnutls/x509.h>
 
29
#include <plist/plist.h>
 
30
 
 
31
#include "property_list_service.h"
 
32
#include "lockdown.h"
 
33
#include "idevice.h"
 
34
#include "debug.h"
 
35
#include "userpref.h"
 
36
 
 
37
#define RESULT_SUCCESS 0
 
38
#define RESULT_FAILURE 1
 
39
 
 
40
const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
 
41
        {"PKCS1", 536872976, 0},
 
42
        {0, 1073741836, 0},
 
43
        {"RSAPublicKey", 536870917, 0},
 
44
        {"modulus", 1073741827, 0},
 
45
        {"publicExponent", 3, 0},
 
46
        {0, 0, 0}
 
47
};
 
48
 
 
49
/**
 
50
 * Internally used function for checking the result from lockdown's answer
 
51
 * plist to a previously sent request.
 
52
 *
 
53
 * @param dict The plist to evaluate.
 
54
 * @param query_match Name of the request to match.
 
55
 *
 
56
 * @return RESULT_SUCCESS when the result is 'Success',
 
57
 *         RESULT_FAILURE when the result is 'Failure',
 
58
 *         or a negative value if an error occured during evaluation.
 
59
 */
 
60
static int lockdown_check_result(plist_t dict, const char *query_match)
 
61
{
 
62
        int ret = -1;
 
63
 
 
64
        plist_t query_node = plist_dict_get_item(dict, "Request");
 
65
        if (!query_node) {
 
66
                return ret;
 
67
        }
 
68
        if (plist_get_node_type(query_node) != PLIST_STRING) {
 
69
                return ret;
 
70
        } else {
 
71
                char *query_value = NULL;
 
72
                plist_get_string_val(query_node, &query_value);
 
73
                if (!query_value) {
 
74
                        return ret;
 
75
                }
 
76
                if (strcmp(query_value, query_match) != 0) {
 
77
                        free(query_value);
 
78
                        return ret;
 
79
                }
 
80
                free(query_value);
 
81
        }
 
82
 
 
83
        plist_t result_node = plist_dict_get_item(dict, "Result");
 
84
        if (!result_node) {
 
85
                return ret;
 
86
        }
 
87
 
 
88
        plist_type result_type = plist_get_node_type(result_node);
 
89
 
 
90
        if (result_type == PLIST_STRING) {
 
91
 
 
92
                char *result_value = NULL;
 
93
 
 
94
                plist_get_string_val(result_node, &result_value);
 
95
 
 
96
                if (result_value) {
 
97
                        if (!strcmp(result_value, "Success")) {
 
98
                                ret = RESULT_SUCCESS;
 
99
                        } else if (!strcmp(result_value, "Failure")) {
 
100
                                ret = RESULT_FAILURE;
 
101
                        } else {
 
102
                                debug_info("ERROR: unknown result value '%s'", result_value);
 
103
                        }
 
104
                }
 
105
                if (result_value)
 
106
                        free(result_value);
 
107
        }
 
108
        return ret;
 
109
}
 
110
 
 
111
/**
 
112
 * Adds a label key with the passed value to a plist dict node.
 
113
 *
 
114
 * @param plist The plist to add the key to
 
115
 * @param label The value for the label key
 
116
 *
 
117
 */
 
118
static void plist_dict_add_label(plist_t plist, const char *label)
 
119
{
 
120
        if (plist && label) {
 
121
                if (plist_get_node_type(plist) == PLIST_DICT)
 
122
                        plist_dict_insert_item(plist, "Label", plist_new_string(label));
 
123
        }
 
124
}
 
125
 
 
126
/**
 
127
 * Closes the lockdownd communication session, by sending the StopSession
 
128
 * Request to the device.
 
129
 *
 
130
 * @see lockdownd_start_session
 
131
 *
 
132
 * @param control The lockdown client
 
133
 * @param session_id The id of a running session
 
134
 *
 
135
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
136
 */
 
137
lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id)
 
138
{
 
139
        if (!client)
 
140
                return LOCKDOWN_E_INVALID_ARG;
 
141
 
 
142
        if (!session_id) {
 
143
                debug_info("no session_id given, cannot stop session");
 
144
                return LOCKDOWN_E_INVALID_ARG;
 
145
        }
 
146
 
 
147
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
148
 
 
149
        plist_t dict = plist_new_dict();
 
150
        plist_dict_add_label(dict, client->label);
 
151
        plist_dict_insert_item(dict,"Request", plist_new_string("StopSession"));
 
152
        plist_dict_insert_item(dict,"SessionID", plist_new_string(session_id));
 
153
 
 
154
        debug_info("stopping session %s", session_id);
 
155
 
 
156
        ret = lockdownd_send(client, dict);
 
157
 
 
158
        plist_free(dict);
 
159
        dict = NULL;
 
160
 
 
161
        ret = lockdownd_receive(client, &dict);
 
162
 
 
163
        if (!dict) {
 
164
                debug_info("LOCKDOWN_E_PLIST_ERROR");
 
165
                return LOCKDOWN_E_PLIST_ERROR;
 
166
        }
 
167
 
 
168
        ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
169
        if (lockdown_check_result(dict, "StopSession") == RESULT_SUCCESS) {
 
170
                debug_info("success");
 
171
                ret = LOCKDOWN_E_SUCCESS;
 
172
        }
 
173
        plist_free(dict);
 
174
        dict = NULL;
 
175
        if (client->ssl_enabled) {
 
176
                property_list_service_disable_ssl(client->parent);
 
177
        }
 
178
        return ret;
 
179
}
 
180
 
 
181
/** Closes the lockdownd client and does the necessary housekeeping.
 
182
 *
 
183
 * @param client The lockdown client
 
184
 *
 
185
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
186
 */
 
187
lockdownd_error_t lockdownd_client_free(lockdownd_client_t client)
 
188
{
 
189
        if (!client)
 
190
                return LOCKDOWN_E_INVALID_ARG;
 
191
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
192
 
 
193
        if (client->session_id)
 
194
                lockdownd_stop_session(client, client->session_id);
 
195
 
 
196
        if (client->parent) {
 
197
                lockdownd_goodbye(client);
 
198
 
 
199
                if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) {
 
200
                        ret = LOCKDOWN_E_SUCCESS;
 
201
                }
 
202
        }
 
203
 
 
204
        if (client->uuid) {
 
205
                free(client->uuid);
 
206
        }
 
207
        if (client->label) {
 
208
                free(client->label);
 
209
        }
 
210
 
 
211
        free(client);
 
212
        return ret;
 
213
}
 
214
 
 
215
/**
 
216
 * Sets the label to send for requests to lockdownd.
 
217
 *
 
218
 * @param client The lockdown client
 
219
 * @param label The label to set or NULL to disable sending a label
 
220
 *
 
221
 */
 
222
void lockdownd_client_set_label(lockdownd_client_t client, const char *label)
 
223
{
 
224
        if (client) {
 
225
                if (client->label)
 
226
                        free(client->label);
 
227
 
 
228
                client->label = (label != NULL) ? strdup(label): NULL;
 
229
        }
 
230
}
 
231
 
 
232
/** Polls the device for lockdownd data.
 
233
 *
 
234
 * @param control The lockdownd client
 
235
 * @param plist The plist to store the received data
 
236
 *
 
237
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
238
 */
 
239
lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist)
 
240
{
 
241
        if (!client || !plist || (plist && *plist))
 
242
                return LOCKDOWN_E_INVALID_ARG;
 
243
        lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
 
244
        property_list_service_error_t err;
 
245
 
 
246
        err = property_list_service_receive_plist(client->parent, plist);
 
247
        if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
 
248
                ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
249
        }
 
250
 
 
251
        if (!*plist)
 
252
                ret = LOCKDOWN_E_PLIST_ERROR;
 
253
 
 
254
        return ret;
 
255
}
 
256
 
 
257
/** Sends lockdownd data to the device
 
258
 *
 
259
 * @note This function is low-level and should only be used if you need to send
 
260
 *        a new type of message.
 
261
 *
 
262
 * @param client The lockdownd client
 
263
 * @param plist The plist to send
 
264
 *
 
265
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
266
 */
 
267
lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist)
 
268
{
 
269
        if (!client || !plist)
 
270
                return LOCKDOWN_E_INVALID_ARG;
 
271
 
 
272
        lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
 
273
        idevice_error_t err;
 
274
 
 
275
        err = property_list_service_send_xml_plist(client->parent, plist);
 
276
        if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
 
277
                ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
278
        }
 
279
        return ret;
 
280
}
 
281
 
 
282
/** Query the type of the service daemon. Depending on whether the device is
 
283
 * queried in normal mode or restore mode, different types will be returned.
 
284
 *
 
285
 * @param client The lockdownd client
 
286
 * @param type The type returned by the service daemon. Can be NULL to ignore.
 
287
 *
 
288
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
289
 */
 
290
lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
 
291
{
 
292
        if (!client)
 
293
                return LOCKDOWN_E_INVALID_ARG;
 
294
 
 
295
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
296
 
 
297
        plist_t dict = plist_new_dict();
 
298
        plist_dict_add_label(dict, client->label);
 
299
        plist_dict_insert_item(dict,"Request", plist_new_string("QueryType"));
 
300
 
 
301
        debug_info("called");
 
302
        ret = lockdownd_send(client, dict);
 
303
 
 
304
        plist_free(dict);
 
305
        dict = NULL;
 
306
 
 
307
        ret = lockdownd_receive(client, &dict);
 
308
 
 
309
        if (LOCKDOWN_E_SUCCESS != ret)
 
310
                return ret;
 
311
 
 
312
        ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
313
        if (lockdown_check_result(dict, "QueryType") == RESULT_SUCCESS) {
 
314
                /* return the type if requested */
 
315
                if (type != NULL) {
 
316
                        plist_t type_node = plist_dict_get_item(dict, "Type");
 
317
                        plist_get_string_val(type_node, type);
 
318
                }
 
319
                debug_info("success with type %s", *type);
 
320
                ret = LOCKDOWN_E_SUCCESS;
 
321
        }
 
322
        plist_free(dict);
 
323
        dict = NULL;
 
324
 
 
325
        return ret;
 
326
}
 
327
 
 
328
/** Retrieves a preferences plist using an optional domain and/or key name.
 
329
 *
 
330
 * @param client an initialized lockdownd client.
 
331
 * @param domain the domain to query on or NULL for global domain
 
332
 * @param key the key name to request or NULL to query for all keys
 
333
 * @param value a plist node representing the result value node
 
334
 *
 
335
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
336
 */
 
337
lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value)
 
338
{
 
339
        if (!client)
 
340
                return LOCKDOWN_E_INVALID_ARG;
 
341
 
 
342
        plist_t dict = NULL;
 
343
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
344
 
 
345
        /* setup request plist */
 
346
        dict = plist_new_dict();
 
347
        plist_dict_add_label(dict, client->label);
 
348
        if (domain) {
 
349
                plist_dict_insert_item(dict,"Domain", plist_new_string(domain));
 
350
        }
 
351
        if (key) {
 
352
                plist_dict_insert_item(dict,"Key", plist_new_string(key));
 
353
        }
 
354
        plist_dict_insert_item(dict,"Request", plist_new_string("GetValue"));
 
355
 
 
356
        /* send to device */
 
357
        ret = lockdownd_send(client, dict);
 
358
 
 
359
        plist_free(dict);
 
360
        dict = NULL;
 
361
 
 
362
        if (ret != LOCKDOWN_E_SUCCESS)
 
363
                return ret;
 
364
 
 
365
        /* Now get device's answer */
 
366
        ret = lockdownd_receive(client, &dict);
 
367
        if (ret != LOCKDOWN_E_SUCCESS)
 
368
                return ret;
 
369
 
 
370
        if (lockdown_check_result(dict, "GetValue") == RESULT_SUCCESS) {
 
371
                debug_info("success");
 
372
                ret = LOCKDOWN_E_SUCCESS;
 
373
        }
 
374
        if (ret != LOCKDOWN_E_SUCCESS) {
 
375
                plist_free(dict);
 
376
                return ret;
 
377
        }
 
378
 
 
379
        plist_t value_node = plist_dict_get_item(dict, "Value");
 
380
 
 
381
        if (value_node) {
 
382
                debug_info("has a value");
 
383
                *value = plist_copy(value_node);
 
384
        }
 
385
 
 
386
        plist_free(dict);
 
387
        return ret;
 
388
}
 
389
 
 
390
/** Sets a preferences value using a plist and optional domain and/or key name.
 
391
 *
 
392
 * @param client an initialized lockdownd client.
 
393
 * @param domain the domain to query on or NULL for global domain
 
394
 * @param key the key name to set the value or NULL to set a value dict plist
 
395
 * @param value a plist node of any node type representing the value to set
 
396
 *
 
397
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
398
 */
 
399
lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value)
 
400
{
 
401
        if (!client || !value)
 
402
                return LOCKDOWN_E_INVALID_ARG;
 
403
 
 
404
        plist_t dict = NULL;
 
405
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
406
 
 
407
        /* setup request plist */
 
408
        dict = plist_new_dict();
 
409
        plist_dict_add_label(dict, client->label);
 
410
        if (domain) {
 
411
                plist_dict_insert_item(dict,"Domain", plist_new_string(domain));
 
412
        }
 
413
        if (key) {
 
414
                plist_dict_insert_item(dict,"Key", plist_new_string(key));
 
415
        }
 
416
        plist_dict_insert_item(dict,"Request", plist_new_string("SetValue"));
 
417
        plist_dict_insert_item(dict,"Value", value);
 
418
 
 
419
        /* send to device */
 
420
        ret = lockdownd_send(client, dict);
 
421
 
 
422
        plist_free(dict);
 
423
        dict = NULL;
 
424
 
 
425
        if (ret != LOCKDOWN_E_SUCCESS)
 
426
                return ret;
 
427
 
 
428
        /* Now get device's answer */
 
429
        ret = lockdownd_receive(client, &dict);
 
430
        if (ret != LOCKDOWN_E_SUCCESS)
 
431
                return ret;
 
432
 
 
433
        if (lockdown_check_result(dict, "SetValue") == RESULT_SUCCESS) {
 
434
                debug_info("success");
 
435
                ret = LOCKDOWN_E_SUCCESS;
 
436
        }
 
437
 
 
438
        if (ret != LOCKDOWN_E_SUCCESS) {
 
439
                plist_free(dict);
 
440
                return ret;
 
441
        }
 
442
 
 
443
        plist_free(dict);
 
444
        return ret;
 
445
}
 
446
 
 
447
/** Removes a preference node on the device by domain and/or key name
 
448
 *
 
449
 * @note: Use with caution as this could remove vital information on the device
 
450
 *
 
451
 * @param client an initialized lockdownd client.
 
452
 * @param domain the domain to query on or NULL for global domain
 
453
 * @param key the key name to remove or NULL remove all keys for the current domain
 
454
 *
 
455
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
456
 */
 
457
lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key)
 
458
{
 
459
        if (!client)
 
460
                return LOCKDOWN_E_INVALID_ARG;
 
461
 
 
462
        plist_t dict = NULL;
 
463
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
464
 
 
465
        /* setup request plist */
 
466
        dict = plist_new_dict();
 
467
        plist_dict_add_label(dict, client->label);
 
468
        if (domain) {
 
469
                plist_dict_insert_item(dict,"Domain", plist_new_string(domain));
 
470
        }
 
471
        if (key) {
 
472
                plist_dict_insert_item(dict,"Key", plist_new_string(key));
 
473
        }
 
474
        plist_dict_insert_item(dict,"Request", plist_new_string("RemoveValue"));
 
475
 
 
476
        /* send to device */
 
477
        ret = lockdownd_send(client, dict);
 
478
 
 
479
        plist_free(dict);
 
480
        dict = NULL;
 
481
 
 
482
        if (ret != LOCKDOWN_E_SUCCESS)
 
483
                return ret;
 
484
 
 
485
        /* Now get device's answer */
 
486
        ret = lockdownd_receive(client, &dict);
 
487
        if (ret != LOCKDOWN_E_SUCCESS)
 
488
                return ret;
 
489
 
 
490
        if (lockdown_check_result(dict, "RemoveValue") == RESULT_SUCCESS) {
 
491
                debug_info("success");
 
492
                ret = LOCKDOWN_E_SUCCESS;
 
493
        }
 
494
 
 
495
        if (ret != LOCKDOWN_E_SUCCESS) {
 
496
                plist_free(dict);
 
497
                return ret;
 
498
        }
 
499
 
 
500
        plist_free(dict);
 
501
        return ret;
 
502
}
 
503
 
 
504
/** Asks for the device's unique id. Part of the lockdownd handshake.
 
505
 *
 
506
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
507
 */
 
508
lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uuid)
 
509
{
 
510
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
511
        plist_t value = NULL;
 
512
 
 
513
        ret = lockdownd_get_value(client, NULL, "UniqueDeviceID", &value);
 
514
        if (ret != LOCKDOWN_E_SUCCESS) {
 
515
                return ret;
 
516
        }
 
517
        plist_get_string_val(value, uuid);
 
518
 
 
519
        plist_free(value);
 
520
        value = NULL;
 
521
        return ret;
 
522
}
 
523
 
 
524
/** Askes for the device's public key. Part of the lockdownd handshake.
 
525
 *
 
526
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
527
 */
 
528
lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key)
 
529
{
 
530
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
531
        plist_t value = NULL;
 
532
        char *value_value = NULL;
 
533
        uint64_t size = 0;
 
534
 
 
535
        ret = lockdownd_get_value(client, NULL, "DevicePublicKey", &value);
 
536
        if (ret != LOCKDOWN_E_SUCCESS) {
 
537
                return ret;
 
538
        }
 
539
        plist_get_data_val(value, &value_value, &size);
 
540
        public_key->data = (unsigned char*)value_value;
 
541
        public_key->size = size;
 
542
 
 
543
        plist_free(value);
 
544
        value = NULL;
 
545
 
 
546
        return ret;
 
547
}
 
548
 
 
549
/** Askes for the device's name.
 
550
 *
 
551
 * @param client The pointer to the location of the new lockdownd_client
 
552
 *
 
553
 *
 
554
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
555
 */
 
556
lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name)
 
557
{
 
558
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
559
        plist_t value = NULL;
 
560
 
 
561
        ret = lockdownd_get_value(client, NULL, "DeviceName", &value);
 
562
        if (ret != LOCKDOWN_E_SUCCESS) {
 
563
                return ret;
 
564
        }
 
565
        plist_get_string_val(value, device_name);
 
566
 
 
567
        plist_free(value);
 
568
        value = NULL;
 
569
 
 
570
        return ret;
 
571
}
 
572
 
 
573
/** Creates a lockdownd client for the device.
 
574
 *
 
575
 * @param phone The device to create a lockdownd client for
 
576
 * @param client The pointer to the location of the new lockdownd_client
 
577
 * @param label The label to use for communication. Usually the program name
 
578
 *
 
579
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
580
 */
 
581
lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label)
 
582
{
 
583
        if (!client)
 
584
                return LOCKDOWN_E_INVALID_ARG;
 
585
 
 
586
        lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
 
587
 
 
588
        property_list_service_client_t plistclient = NULL;
 
589
        if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
 
590
                debug_info("could not connect to lockdownd (device %s)", device->uuid);
 
591
                return LOCKDOWN_E_MUX_ERROR;
 
592
        }
 
593
 
 
594
        lockdownd_client_t client_loc = (lockdownd_client_t) malloc(sizeof(struct lockdownd_client_int));
 
595
        client_loc->parent = plistclient;
 
596
        client_loc->ssl_enabled = 0;
 
597
        client_loc->session_id = NULL;
 
598
        client_loc->uuid = NULL;
 
599
        client_loc->label = NULL;
 
600
        if (label != NULL)
 
601
                strdup(label);
 
602
 
 
603
        if (LOCKDOWN_E_SUCCESS == ret) {
 
604
                *client = client_loc;
 
605
        } else {
 
606
                lockdownd_client_free(client_loc);
 
607
        }
 
608
 
 
609
        return ret;
 
610
}
 
611
 
 
612
/** Creates a lockdownd client for the device and starts initial handshake.
 
613
 * The handshake consists of query_type, validate_pair, pair and
 
614
 * start_session calls.
 
615
 *
 
616
 * @param phone The device to create a lockdownd client for
 
617
 * @param client The pointer to the location of the new lockdownd_client
 
618
 * @param label The label to use for communication. Usually the program name
 
619
 *
 
620
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
621
 */
 
622
lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label)
 
623
{
 
624
        if (!client)
 
625
                return LOCKDOWN_E_INVALID_ARG;
 
626
 
 
627
        lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
 
628
        lockdownd_client_t client_loc = NULL;
 
629
        char *host_id = NULL;
 
630
        char *type = NULL;
 
631
 
 
632
 
 
633
        ret = lockdownd_client_new(device, &client_loc, label);
 
634
 
 
635
        /* perform handshake */
 
636
        if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc, &type)) {
 
637
                debug_info("QueryType failed in the lockdownd client.");
 
638
                ret = LOCKDOWN_E_NOT_ENOUGH_DATA;
 
639
        } else {
 
640
                if (strcmp("com.apple.mobile.lockdown", type)) {
 
641
                        debug_info("Warning QueryType request returned \"%s\".", type);
 
642
                }
 
643
                if (type)
 
644
                        free(type);
 
645
        }
 
646
 
 
647
        ret = idevice_get_uuid(device, &client_loc->uuid);
 
648
        if (LOCKDOWN_E_SUCCESS != ret) {
 
649
                debug_info("failed to get device uuid.");
 
650
        }
 
651
        debug_info("device uuid: %s", client_loc->uuid);
 
652
 
 
653
        userpref_get_host_id(&host_id);
 
654
        if (LOCKDOWN_E_SUCCESS == ret && !host_id) {
 
655
                ret = LOCKDOWN_E_INVALID_CONF;
 
656
        }
 
657
 
 
658
        if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->uuid))
 
659
                ret = lockdownd_pair(client_loc, NULL);
 
660
 
 
661
        /* in any case, we need to validate pairing to receive trusted host status */
 
662
        ret = lockdownd_validate_pair(client_loc, NULL);
 
663
 
 
664
        /* if not paired yet, let's do it now */
 
665
        if (LOCKDOWN_E_INVALID_HOST_ID == ret) {
 
666
                ret = lockdownd_pair(client_loc, NULL);
 
667
                if (LOCKDOWN_E_SUCCESS == ret) {
 
668
                        ret = lockdownd_validate_pair(client_loc, NULL);
 
669
                }
 
670
        }
 
671
 
 
672
        if (LOCKDOWN_E_SUCCESS == ret) {
 
673
                ret = lockdownd_start_session(client_loc, host_id, NULL, NULL);
 
674
                if (LOCKDOWN_E_SUCCESS != ret) {
 
675
                        debug_info("Session opening failed.");
 
676
                }
 
677
 
 
678
                if (host_id) {
 
679
                        free(host_id);
 
680
                        host_id = NULL;
 
681
                }
 
682
        }
 
683
        
 
684
        if (LOCKDOWN_E_SUCCESS == ret) {
 
685
                *client = client_loc;
 
686
        } else {
 
687
                lockdownd_client_free(client_loc);
 
688
        }
 
689
 
 
690
        return ret;
 
691
}
 
692
 
 
693
static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_record)
 
694
{
 
695
        if (!pair_record)
 
696
                return NULL;
 
697
 
 
698
        char *host_id_loc = pair_record->host_id;
 
699
 
 
700
        /* setup request plist */
 
701
        plist_t dict = plist_new_dict();
 
702
        plist_dict_insert_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate)));
 
703
        plist_dict_insert_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate)));
 
704
        if (!pair_record->host_id)
 
705
                userpref_get_host_id(&host_id_loc);
 
706
        plist_dict_insert_item(dict, "HostID", plist_new_string(host_id_loc));
 
707
        plist_dict_insert_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate)));
 
708
 
 
709
        if (!pair_record->host_id)
 
710
                free(host_id_loc);
 
711
 
 
712
        return dict;
 
713
}
 
714
 
 
715
static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, char *host_id, plist_t *pair_record_plist)
 
716
{
 
717
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
718
 
 
719
        gnutls_datum_t device_cert = { NULL, 0 };
 
720
        gnutls_datum_t host_cert = { NULL, 0 };
 
721
        gnutls_datum_t root_cert = { NULL, 0 };
 
722
 
 
723
        ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert);
 
724
        if (ret != LOCKDOWN_E_SUCCESS) {
 
725
                return ret;
 
726
        }
 
727
 
 
728
        char *host_id_loc = host_id;
 
729
 
 
730
        if (!host_id)
 
731
                userpref_get_host_id(&host_id_loc);
 
732
 
 
733
        /* setup request plist */
 
734
        *pair_record_plist = plist_new_dict();
 
735
        plist_dict_insert_item(*pair_record_plist, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size));
 
736
        plist_dict_insert_item(*pair_record_plist, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size));
 
737
        plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id_loc));
 
738
        plist_dict_insert_item(*pair_record_plist, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size));
 
739
 
 
740
        if (!host_id)
 
741
                free(host_id_loc);
 
742
 
 
743
        return ret;
 
744
}
 
745
 
 
746
/** Function used internally by lockdownd_pair() and lockdownd_validate_pair()
 
747
 *
 
748
 * @param client The lockdown client to pair with.
 
749
 * @param pair_record The pair record to use for pairing. If NULL is passed, then
 
750
 *    the pair records from the current machine are used. New records will be
 
751
 *    generated automatically when pairing is done for the first time.
 
752
 * @param verb This is either "Pair", "ValidatePair" or "Unpair".
 
753
 *
 
754
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
755
 */
 
756
static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb)
 
757
{
 
758
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
759
        plist_t dict = NULL;
 
760
        plist_t dict_record = NULL;
 
761
        gnutls_datum_t public_key = { NULL, 0 };
 
762
        int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */
 
763
 
 
764
        if (pair_record && pair_record->host_id) {
 
765
                /* valid pair_record passed? */
 
766
                if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) {
 
767
                        return LOCKDOWN_E_PLIST_ERROR;
 
768
                }
 
769
 
 
770
                /* use passed pair_record */
 
771
                dict_record = lockdownd_pair_record_to_plist(pair_record);
 
772
 
 
773
                pairing_mode = 1;
 
774
        } else {
 
775
                ret = lockdownd_get_device_public_key(client, &public_key);
 
776
                if (ret != LOCKDOWN_E_SUCCESS) {
 
777
                        if (public_key.data)
 
778
                                free(public_key.data);
 
779
                        debug_info("device refused to send public key.");
 
780
                        return ret;
 
781
                }
 
782
                debug_info("device public key follows:\n%s", public_key.data);
 
783
                /* get libimobiledevice pair_record */
 
784
                ret = generate_pair_record_plist(public_key, NULL, &dict_record);
 
785
                if (ret != LOCKDOWN_E_SUCCESS) {
 
786
                        if (dict_record)
 
787
                                plist_free(dict_record);
 
788
                        return ret;
 
789
                }
 
790
        }
 
791
 
 
792
        /* Setup Pair request plist */
 
793
        dict = plist_new_dict();
 
794
        plist_dict_add_label(dict, client->label);
 
795
        plist_dict_insert_item(dict,"PairRecord", dict_record);
 
796
        plist_dict_insert_item(dict, "Request", plist_new_string(verb));
 
797
 
 
798
        /* send to device */
 
799
        ret = lockdownd_send(client, dict);
 
800
        plist_free(dict);
 
801
        dict = NULL;
 
802
 
 
803
        if (ret != LOCKDOWN_E_SUCCESS)
 
804
                return ret;
 
805
 
 
806
        /* Now get device's answer */
 
807
        ret = lockdownd_receive(client, &dict);
 
808
 
 
809
        if (ret != LOCKDOWN_E_SUCCESS)
 
810
                return ret;
 
811
 
 
812
        if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) {
 
813
                ret = LOCKDOWN_E_PAIRING_FAILED;
 
814
        }
 
815
 
 
816
        /* if pairing succeeded */
 
817
        if (ret == LOCKDOWN_E_SUCCESS) {
 
818
                debug_info("%s success", verb);
 
819
                if (!pairing_mode) {
 
820
                        if (!strcmp("Unpair", verb)) {
 
821
                                /* remove public key from config */
 
822
                                userpref_remove_device_public_key(client->uuid);
 
823
                        } else {
 
824
                                /* store public key in config */
 
825
                                userpref_set_device_public_key(client->uuid, public_key);
 
826
                        }
 
827
                }
 
828
        } else {
 
829
                debug_info("%s failure", verb);
 
830
                plist_t error_node = NULL;
 
831
                /* verify error condition */
 
832
                error_node = plist_dict_get_item(dict, "Error");
 
833
                if (error_node) {
 
834
                        char *value = NULL;
 
835
                        plist_get_string_val(error_node, &value);
 
836
                        if (value) {
 
837
                                /* the first pairing fails if the device is password protected */
 
838
                                if (!strcmp(value, "PasswordProtected")) {
 
839
                                        ret = LOCKDOWN_E_PASSWORD_PROTECTED;
 
840
                                } else if (!strcmp(value, "InvalidHostID")) {
 
841
                                        ret = LOCKDOWN_E_INVALID_HOST_ID;
 
842
                                }
 
843
                                free(value);
 
844
                        }
 
845
 
 
846
                        plist_free(error_node);
 
847
                        error_node = NULL;
 
848
                }
 
849
        }
 
850
        plist_free(dict);
 
851
        dict = NULL;
 
852
        if (public_key.data)
 
853
                free(public_key.data);
 
854
        return ret;
 
855
}
 
856
 
 
857
/** 
 
858
 * Pairs the device with the given HostID.
 
859
 * It's part of the lockdownd handshake.
 
860
 *
 
861
 * @param client The lockdown client to pair with.
 
862
 * @param pair_record The pair record to use for pairing. If NULL is passed, then
 
863
 *    the pair records from the current machine are used. New records will be
 
864
 *    generated automatically when pairing is done for the first time.
 
865
 *
 
866
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
867
 */
 
868
lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
 
869
{
 
870
        return lockdownd_do_pair(client, pair_record, "Pair");
 
871
}
 
872
 
 
873
/** 
 
874
 * Pairs the device with the given HostID. The difference to lockdownd_pair()
 
875
 * is that the specified host will become trusted host of the device.
 
876
 * It's part of the lockdownd handshake.
 
877
 *
 
878
 * @param client The lockdown client to pair with.
 
879
 * @param pair_record The pair record to validate pairing with. If NULL is
 
880
 *    passed, then the pair records from the current machine are used.
 
881
 *    New records will be generated automatically when pairing is done
 
882
 *    for the first time.
 
883
 *
 
884
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
885
 */
 
886
lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
 
887
{
 
888
        return lockdownd_do_pair(client, pair_record, "ValidatePair");
 
889
}
 
890
 
 
891
/** 
 
892
 * Unpairs the device with the given HostID and removes the pairing records
 
893
 * from the device and host.
 
894
 *
 
895
 * @param client The lockdown client to pair with.
 
896
 * @param pair_record The pair record to use for unpair. If NULL is passed, then
 
897
 *    the pair records from the current machine are used.
 
898
 *
 
899
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
900
 */
 
901
lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
 
902
{
 
903
        return lockdownd_do_pair(client, pair_record, "Unpair");
 
904
}
 
905
 
 
906
/**
 
907
 * Tells the device to immediately enter recovery mode.
 
908
 *
 
909
 * @param client The lockdown client
 
910
 *
 
911
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
912
 */
 
913
lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
 
914
{
 
915
        if (!client)
 
916
                return LOCKDOWN_E_INVALID_ARG;
 
917
 
 
918
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
919
 
 
920
        plist_t dict = plist_new_dict();
 
921
        plist_dict_add_label(dict, client->label);
 
922
        plist_dict_insert_item(dict,"Request", plist_new_string("EnterRecovery"));
 
923
 
 
924
        debug_info("telling device to enter recovery mode");
 
925
 
 
926
        ret = lockdownd_send(client, dict);
 
927
        plist_free(dict);
 
928
        dict = NULL;
 
929
 
 
930
        ret = lockdownd_receive(client, &dict);
 
931
 
 
932
        if (lockdown_check_result(dict, "EnterRecovery") == RESULT_SUCCESS) {
 
933
                debug_info("success");
 
934
                ret = LOCKDOWN_E_SUCCESS;
 
935
        }
 
936
        plist_free(dict);
 
937
        dict = NULL;
 
938
        return ret;
 
939
}
 
940
 
 
941
/**
 
942
 * Performs the Goodbye Request to tell the device the communication
 
943
 * session is now closed.
 
944
 *
 
945
 * @param client The lockdown client
 
946
 *
 
947
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
948
 */
 
949
lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
 
950
{
 
951
        if (!client)
 
952
                return LOCKDOWN_E_INVALID_ARG;
 
953
 
 
954
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
955
 
 
956
        plist_t dict = plist_new_dict();
 
957
        plist_dict_add_label(dict, client->label);
 
958
        plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye"));
 
959
 
 
960
        debug_info("called");
 
961
 
 
962
        ret = lockdownd_send(client, dict);
 
963
        plist_free(dict);
 
964
        dict = NULL;
 
965
 
 
966
        ret = lockdownd_receive(client, &dict);
 
967
        if (!dict) {
 
968
                debug_info("did not get goodbye response back");
 
969
                return LOCKDOWN_E_PLIST_ERROR;
 
970
        }
 
971
 
 
972
        if (lockdown_check_result(dict, "Goodbye") == RESULT_SUCCESS) {
 
973
                debug_info("success");
 
974
                ret = LOCKDOWN_E_SUCCESS;
 
975
        }
 
976
        plist_free(dict);
 
977
        dict = NULL;
 
978
        return ret;
 
979
}
 
980
 
 
981
/** Generates the device certificate from the public key as well as the host
 
982
 *  and root certificates.
 
983
 *
 
984
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
985
 */
 
986
lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * odevice_cert,
 
987
                                                                           gnutls_datum_t * ohost_cert, gnutls_datum_t * oroot_cert)
 
988
{
 
989
        if (!public_key.data || !odevice_cert || !ohost_cert || !oroot_cert)
 
990
                return LOCKDOWN_E_INVALID_ARG;
 
991
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
992
        userpref_error_t uret = USERPREF_E_UNKNOWN_ERROR;
 
993
 
 
994
        gnutls_datum_t modulus = { NULL, 0 };
 
995
        gnutls_datum_t exponent = { NULL, 0 };
 
996
 
 
997
        /* now decode the PEM encoded key */
 
998
        gnutls_datum_t der_pub_key;
 
999
        if (GNUTLS_E_SUCCESS == gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key)) {
 
1000
 
 
1001
                /* initalize asn.1 parser */
 
1002
                ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY;
 
1003
                if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) {
 
1004
 
 
1005
                        ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY;
 
1006
                        asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key);
 
1007
 
 
1008
                        if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) {
 
1009
 
 
1010
                                /* get size to read */
 
1011
                                int ret1 = asn1_read_value(asn1_pub_key, "modulus", NULL, (int*)&modulus.size);
 
1012
                                int ret2 = asn1_read_value(asn1_pub_key, "publicExponent", NULL, (int*)&exponent.size);
 
1013
 
 
1014
                                modulus.data = gnutls_malloc(modulus.size);
 
1015
                                exponent.data = gnutls_malloc(exponent.size);
 
1016
 
 
1017
                                ret1 = asn1_read_value(asn1_pub_key, "modulus", modulus.data, (int*)&modulus.size);
 
1018
                                ret2 = asn1_read_value(asn1_pub_key, "publicExponent", exponent.data, (int*)&exponent.size);
 
1019
                                if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2)
 
1020
                                        ret = LOCKDOWN_E_SUCCESS;
 
1021
                        }
 
1022
                        if (asn1_pub_key)
 
1023
                                asn1_delete_structure(&asn1_pub_key);
 
1024
                }
 
1025
                if (pkcs1)
 
1026
                        asn1_delete_structure(&pkcs1);
 
1027
        }
 
1028
 
 
1029
        /* now generate certificates */
 
1030
        if (LOCKDOWN_E_SUCCESS == ret && 0 != modulus.size && 0 != exponent.size) {
 
1031
 
 
1032
                gnutls_global_init();
 
1033
                gnutls_datum_t essentially_null = { (unsigned char*)strdup("abababababababab"), strlen("abababababababab") };
 
1034
 
 
1035
                gnutls_x509_privkey_t fake_privkey, root_privkey, host_privkey;
 
1036
                gnutls_x509_crt_t dev_cert, root_cert, host_cert;
 
1037
 
 
1038
                gnutls_x509_privkey_init(&fake_privkey);
 
1039
                gnutls_x509_crt_init(&dev_cert);
 
1040
                gnutls_x509_crt_init(&root_cert);
 
1041
                gnutls_x509_crt_init(&host_cert);
 
1042
 
 
1043
                if (GNUTLS_E_SUCCESS ==
 
1044
                        gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null,
 
1045
                                                                                           &essentially_null, &essentially_null)) {
 
1046
 
 
1047
                        gnutls_x509_privkey_init(&root_privkey);
 
1048
                        gnutls_x509_privkey_init(&host_privkey);
 
1049
 
 
1050
                        uret = userpref_get_keys_and_certs(root_privkey, root_cert, host_privkey, host_cert);
 
1051
 
 
1052
                        if (USERPREF_E_SUCCESS == uret) {
 
1053
                                /* generate device certificate */
 
1054
                                gnutls_x509_crt_set_key(dev_cert, fake_privkey);
 
1055
                                gnutls_x509_crt_set_serial(dev_cert, "\x00", 1);
 
1056
                                gnutls_x509_crt_set_version(dev_cert, 3);
 
1057
                                gnutls_x509_crt_set_ca_status(dev_cert, 0);
 
1058
                                gnutls_x509_crt_set_activation_time(dev_cert, time(NULL));
 
1059
                                gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
 
1060
                                gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey);
 
1061
 
 
1062
                                if (LOCKDOWN_E_SUCCESS == ret) {
 
1063
                                        /* if everything went well, export in PEM format */
 
1064
                                        size_t export_size = 0;
 
1065
                                        gnutls_datum_t dev_pem = { NULL, 0 };
 
1066
                                        gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &export_size);
 
1067
                                        dev_pem.data = gnutls_malloc(export_size);
 
1068
                                        gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_pem.data, &export_size);
 
1069
                                        dev_pem.size = export_size;
 
1070
 
 
1071
                                        gnutls_datum_t pem_root_cert = { NULL, 0 };
 
1072
                                        gnutls_datum_t pem_host_cert = { NULL, 0 };
 
1073
 
 
1074
                                        uret = userpref_get_certs_as_pem(&pem_root_cert, &pem_host_cert);
 
1075
 
 
1076
                                        if (USERPREF_E_SUCCESS == uret) {
 
1077
                                                /* copy buffer for output */
 
1078
                                                odevice_cert->data = malloc(dev_pem.size);
 
1079
                                                memcpy(odevice_cert->data, dev_pem.data, dev_pem.size);
 
1080
                                                odevice_cert->size = dev_pem.size;
 
1081
 
 
1082
                                                ohost_cert->data = malloc(pem_host_cert.size);
 
1083
                                                memcpy(ohost_cert->data, pem_host_cert.data, pem_host_cert.size);
 
1084
                                                ohost_cert->size = pem_host_cert.size;
 
1085
 
 
1086
                                                oroot_cert->data = malloc(pem_root_cert.size);
 
1087
                                                memcpy(oroot_cert->data, pem_root_cert.data, pem_root_cert.size);
 
1088
                                                oroot_cert->size = pem_root_cert.size;
 
1089
 
 
1090
                                                g_free(pem_root_cert.data);
 
1091
                                                g_free(pem_host_cert.data);
 
1092
                                        }
 
1093
                                }
 
1094
                        }
 
1095
 
 
1096
                        switch(uret) {
 
1097
                        case USERPREF_E_INVALID_ARG:
 
1098
                                ret = LOCKDOWN_E_INVALID_ARG;
 
1099
                                break;
 
1100
                        case USERPREF_E_INVALID_CONF:
 
1101
                                ret = LOCKDOWN_E_INVALID_CONF;
 
1102
                                break;
 
1103
                        case USERPREF_E_SSL_ERROR:
 
1104
                                ret = LOCKDOWN_E_SSL_ERROR;
 
1105
                        default:
 
1106
                                break;
 
1107
                        }
 
1108
                }
 
1109
        }
 
1110
 
 
1111
        gnutls_free(modulus.data);
 
1112
        gnutls_free(exponent.data);
 
1113
 
 
1114
        gnutls_free(der_pub_key.data);
 
1115
 
 
1116
        return ret;
 
1117
}
 
1118
 
 
1119
/** Starts communication with lockdownd after the device has been paired,
 
1120
 *  and if the device requires it, switches to SSL mode.
 
1121
 *
 
1122
 * @param client The lockdownd client
 
1123
 * @param host_id The HostID of the computer
 
1124
 * @param session_id The session_id of the created session
 
1125
 * @param ssl_enabled Whether SSL communication is used in the session
 
1126
 *
 
1127
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
1128
 */
 
1129
lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled)
 
1130
{
 
1131
        lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
 
1132
        plist_t dict = NULL;
 
1133
 
 
1134
        if (!client || !host_id)
 
1135
                ret = LOCKDOWN_E_INVALID_ARG;
 
1136
 
 
1137
        /* if we have a running session, stop current one first */
 
1138
        if (client->session_id) {
 
1139
                lockdownd_stop_session(client, client->session_id);
 
1140
        }
 
1141
 
 
1142
        /* setup request plist */
 
1143
        dict = plist_new_dict();
 
1144
        plist_dict_add_label(dict, client->label);
 
1145
        plist_dict_insert_item(dict,"HostID", plist_new_string(host_id));
 
1146
        plist_dict_insert_item(dict,"Request", plist_new_string("StartSession"));
 
1147
 
 
1148
        ret = lockdownd_send(client, dict);
 
1149
        plist_free(dict);
 
1150
        dict = NULL;
 
1151
 
 
1152
        if (ret != LOCKDOWN_E_SUCCESS)
 
1153
                return ret;
 
1154
 
 
1155
        ret = lockdownd_receive(client, &dict);
 
1156
 
 
1157
        if (!dict)
 
1158
                return LOCKDOWN_E_PLIST_ERROR;
 
1159
 
 
1160
        if (lockdown_check_result(dict, "StartSession") == RESULT_FAILURE) {
 
1161
                plist_t error_node = plist_dict_get_item(dict, "Error");
 
1162
                if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
 
1163
                        char *error = NULL;
 
1164
                        plist_get_string_val(error_node, &error);
 
1165
                        if (!strcmp(error, "InvalidHostID")) {
 
1166
                                ret = LOCKDOWN_E_INVALID_HOST_ID;
 
1167
                        }
 
1168
                        free(error);
 
1169
                }
 
1170
        } else {
 
1171
                uint8_t use_ssl = 0;
 
1172
 
 
1173
                plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL");
 
1174
                if (enable_ssl && (plist_get_node_type(enable_ssl) == PLIST_BOOLEAN)) {
 
1175
                        plist_get_bool_val(enable_ssl, &use_ssl);
 
1176
                }
 
1177
                debug_info("Session startup OK");
 
1178
 
 
1179
                if (ssl_enabled != NULL)
 
1180
                        *ssl_enabled = use_ssl;
 
1181
 
 
1182
                /* store session id, we need it for StopSession */
 
1183
                plist_t session_node = plist_dict_get_item(dict, "SessionID");
 
1184
                if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) {
 
1185
                        plist_get_string_val(session_node, &client->session_id);
 
1186
                }
 
1187
                if (client->session_id) {
 
1188
                        debug_info("SessionID: %s", client->session_id);
 
1189
                        if (session_id != NULL)
 
1190
                                *session_id = strdup(client->session_id);
 
1191
                } else {
 
1192
                        debug_info("Failed to get SessionID!");
 
1193
                }
 
1194
                debug_info("Enable SSL Session: %s", (use_ssl?"true":"false"));
 
1195
                if (use_ssl) {
 
1196
                        ret = property_list_service_enable_ssl(client->parent);
 
1197
                        if (ret == PROPERTY_LIST_SERVICE_E_SUCCESS) {
 
1198
                                client->ssl_enabled = 1;
 
1199
                        } else {
 
1200
                                ret = LOCKDOWN_E_SSL_ERROR;
 
1201
                                client->ssl_enabled = 0;
 
1202
                        }
 
1203
                } else {
 
1204
                        client->ssl_enabled = 0;
 
1205
                        ret = LOCKDOWN_E_SUCCESS;
 
1206
                }
 
1207
        }
 
1208
 
 
1209
        plist_free(dict);
 
1210
        dict = NULL;
 
1211
 
 
1212
        return ret;
 
1213
}
 
1214
 
 
1215
/** Command to start the desired service
 
1216
 *
 
1217
 * @param client The lockdownd client
 
1218
 * @param service The name of the service to start
 
1219
 * @param port The port number the service was started on
 
1220
 
 
1221
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
1222
 */
 
1223
lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, uint16_t *port)
 
1224
{
 
1225
        if (!client || !service || !port)
 
1226
                return LOCKDOWN_E_INVALID_ARG;
 
1227
 
 
1228
        char *host_id = NULL;
 
1229
        userpref_get_host_id(&host_id);
 
1230
        if (!host_id)
 
1231
                return LOCKDOWN_E_INVALID_CONF;
 
1232
        if (!client->session_id)
 
1233
                return LOCKDOWN_E_NO_RUNNING_SESSION;
 
1234
 
 
1235
        plist_t dict = NULL;
 
1236
        uint16_t port_loc = 0;
 
1237
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
1238
 
 
1239
        free(host_id);
 
1240
        host_id = NULL;
 
1241
 
 
1242
        dict = plist_new_dict();
 
1243
        plist_dict_add_label(dict, client->label);
 
1244
        plist_dict_insert_item(dict,"Request", plist_new_string("StartService"));
 
1245
        plist_dict_insert_item(dict,"Service", plist_new_string(service));
 
1246
 
 
1247
        /* send to device */
 
1248
        ret = lockdownd_send(client, dict);
 
1249
        plist_free(dict);
 
1250
        dict = NULL;
 
1251
 
 
1252
        if (LOCKDOWN_E_SUCCESS != ret)
 
1253
                return ret;
 
1254
 
 
1255
        ret = lockdownd_receive(client, &dict);
 
1256
 
 
1257
        if (LOCKDOWN_E_SUCCESS != ret)
 
1258
                return ret;
 
1259
 
 
1260
        if (!dict)
 
1261
                return LOCKDOWN_E_PLIST_ERROR;
 
1262
 
 
1263
        ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
1264
        if (lockdown_check_result(dict, "StartService") == RESULT_SUCCESS) {
 
1265
                plist_t port_value_node = plist_dict_get_item(dict, "Port");
 
1266
 
 
1267
                if (port_value_node && (plist_get_node_type(port_value_node) == PLIST_UINT)) {
 
1268
                        uint64_t port_value = 0;
 
1269
                        plist_get_uint_val(port_value_node, &port_value);
 
1270
 
 
1271
                        if (port_value) {
 
1272
                                port_loc = port_value;
 
1273
                                ret = LOCKDOWN_E_SUCCESS;
 
1274
                        }
 
1275
                        if (port && ret == LOCKDOWN_E_SUCCESS)
 
1276
                                *port = port_loc;
 
1277
                }
 
1278
        } else {
 
1279
                ret = LOCKDOWN_E_START_SERVICE_FAILED;
 
1280
                plist_t error_node = plist_dict_get_item(dict, "Error");
 
1281
                if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
 
1282
                        char *error = NULL;
 
1283
                        plist_get_string_val(error_node, &error);
 
1284
                        if (!strcmp(error, "InvalidService")) {
 
1285
                                ret = LOCKDOWN_E_INVALID_SERVICE;
 
1286
                        }
 
1287
                        free(error);
 
1288
                }
 
1289
        }
 
1290
 
 
1291
        plist_free(dict);
 
1292
        dict = NULL;
 
1293
        return ret;
 
1294
}
 
1295
 
 
1296
/**
 
1297
 * Activates the device. Only works within an open session.
 
1298
 * The ActivationRecord plist dictionary must be obtained using the
 
1299
 * activation protocol requesting from Apple's https webservice.
 
1300
 *
 
1301
 * @see http://iphone-docs.org/doku.php?id=docs:protocols:activation
 
1302
 *
 
1303
 * @param control The lockdown client
 
1304
 * @param activation_record The activation record plist dictionary
 
1305
 *
 
1306
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
1307
 */
 
1308
lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record) 
 
1309
{
 
1310
        if (!client)
 
1311
                return LOCKDOWN_E_INVALID_ARG;
 
1312
 
 
1313
        if (!client->session_id)
 
1314
                return LOCKDOWN_E_NO_RUNNING_SESSION;
 
1315
 
 
1316
        if (!activation_record)
 
1317
                return LOCKDOWN_E_INVALID_ARG;
 
1318
 
 
1319
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
1320
 
 
1321
        plist_t dict = plist_new_dict();
 
1322
        plist_dict_add_label(dict, client->label);
 
1323
        plist_dict_insert_item(dict,"Request", plist_new_string("Activate"));
 
1324
        plist_dict_insert_item(dict,"ActivationRecord", activation_record);
 
1325
 
 
1326
        ret = lockdownd_send(client, dict);
 
1327
        plist_free(dict);
 
1328
        dict = NULL;
 
1329
 
 
1330
        ret = lockdownd_receive(client, &dict);
 
1331
        if (!dict) {
 
1332
                debug_info("LOCKDOWN_E_PLIST_ERROR");
 
1333
                return LOCKDOWN_E_PLIST_ERROR;
 
1334
        }
 
1335
 
 
1336
        ret = LOCKDOWN_E_ACTIVATION_FAILED;
 
1337
        if (lockdown_check_result(dict, "Activate") == RESULT_SUCCESS) {
 
1338
                debug_info("success");
 
1339
                ret = LOCKDOWN_E_SUCCESS;
 
1340
                
 
1341
        } else {
 
1342
                plist_t error_node = plist_dict_get_item(dict, "Error");
 
1343
                if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
 
1344
                        char *error = NULL;
 
1345
                        plist_get_string_val(error_node, &error);
 
1346
                        if (!strcmp(error, "InvalidActivationRecord")) {
 
1347
                                ret = LOCKDOWN_E_INVALID_ACTIVATION_RECORD;
 
1348
                        }
 
1349
                        free(error);
 
1350
                }
 
1351
        }
 
1352
        
 
1353
        plist_free(dict);
 
1354
        dict = NULL;
 
1355
 
 
1356
        return ret;
 
1357
}
 
1358
 
 
1359
/**
 
1360
 * Deactivates the device, returning it to the locked
 
1361
 * “Activate with iTunes” screen.
 
1362
 *
 
1363
 * @param control The lockdown client
 
1364
 *
 
1365
 * @return an error code (LOCKDOWN_E_SUCCESS on success)
 
1366
 */
 
1367
lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
 
1368
{
 
1369
        if (!client)
 
1370
                return LOCKDOWN_E_INVALID_ARG;
 
1371
 
 
1372
        if (!client->session_id)
 
1373
                return LOCKDOWN_E_NO_RUNNING_SESSION;
 
1374
 
 
1375
        lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
1376
 
 
1377
        plist_t dict = plist_new_dict();
 
1378
        plist_dict_add_label(dict, client->label);
 
1379
        plist_dict_insert_item(dict,"Request", plist_new_string("Deactivate"));
 
1380
 
 
1381
        ret = lockdownd_send(client, dict);
 
1382
        plist_free(dict);
 
1383
        dict = NULL;
 
1384
 
 
1385
        ret = lockdownd_receive(client, &dict);
 
1386
        if (!dict) {
 
1387
                debug_info("LOCKDOWN_E_PLIST_ERROR");
 
1388
                return LOCKDOWN_E_PLIST_ERROR;
 
1389
        }
 
1390
 
 
1391
        ret = LOCKDOWN_E_UNKNOWN_ERROR;
 
1392
        if (lockdown_check_result(dict, "Deactivate") == RESULT_SUCCESS) {
 
1393
                debug_info("success");
 
1394
                ret = LOCKDOWN_E_SUCCESS;
 
1395
        }
 
1396
        plist_free(dict);
 
1397
        dict = NULL;
 
1398
 
 
1399
        return ret;
 
1400
}
 
1401