1 /*
2 * ideviceprovision.c
3 * Simple utility to install, get, or remove provisioning profiles
4 * to/from idevices
5 *
6 * Copyright (c) 2012-2016 Nikias Bassen, All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #define TOOL_NAME "ideviceprovision"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <errno.h>
34 #ifndef WIN32
35 #include <signal.h>
36 #endif
37
38 #ifdef WIN32
39 #include <windows.h>
40 #else
41 #include <arpa/inet.h>
42 #endif
43
44 #include <libimobiledevice/libimobiledevice.h>
45 #include <libimobiledevice/lockdown.h>
46 #include <libimobiledevice/misagent.h>
47 #include "common/utils.h"
48
print_usage(int argc,char ** argv)49 static void print_usage(int argc, char **argv)
50 {
51 char *name = NULL;
52
53 name = strrchr(argv[0], '/');
54 printf("Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
55 printf("\n");
56 printf("Manage provisioning profiles on a device.\n");
57 printf("\n");
58 printf("Where COMMAND is one of:\n");
59 printf(" install FILE\tInstalls the provisioning profile specified by FILE.\n");
60 printf(" \tA valid .mobileprovision file is expected.\n");
61 printf(" list\t\tGet a list of all provisioning profiles on the device.\n");
62 printf(" copy PATH\tRetrieves all provisioning profiles from the device and\n");
63 printf(" \tstores them into the existing directory specified by PATH.\n");
64 printf(" \tThe files will be stored as UUID.mobileprovision\n");
65 printf(" copy UUID PATH Retrieves the provisioning profile identified by UUID\n");
66 printf(" \tfrom the device and stores it into the existing directory\n");
67 printf(" \tspecified by PATH. The file will be stored as UUID.mobileprovision.\n");
68 printf(" remove UUID\tRemoves the provisioning profile identified by UUID.\n");
69 printf(" remove-all\tRemoves all installed provisioning profiles.\n");
70 printf(" dump FILE\tPrints detailed information about the provisioning profile\n");
71 printf(" \tspecified by FILE.\n");
72 printf("\n");
73 printf("The following OPTIONS are accepted:\n");
74 printf(" -u, --udid UDID target specific device by UDID\n");
75 printf(" -n, --network connect to network device\n");
76 printf(" -x, --xml print XML output when using the 'dump' command\n");
77 printf(" -d, --debug enable communication debugging\n");
78 printf(" -h, --help prints usage information\n");
79 printf(" -v, --version prints version information\n");
80 printf("\n");
81 printf("Homepage: <" PACKAGE_URL ">\n");
82 printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n");
83 }
84
85 enum {
86 OP_INSTALL,
87 OP_LIST,
88 OP_COPY,
89 OP_REMOVE,
90 OP_DUMP,
91 NUM_OPS
92 };
93
94 #define ASN1_SEQUENCE 0x30
95 #define ASN1_CONTAINER 0xA0
96 #define ASN1_OBJECT_IDENTIFIER 0x06
97 #define ASN1_OCTET_STRING 0x04
98
asn1_next_item(unsigned char ** p)99 static void asn1_next_item(unsigned char** p)
100 {
101 char bsize = *(*p+1);
102 if (bsize & 0x80) {
103 *p += 2 + (bsize & 0xF);
104 } else {
105 *p += 3;
106 }
107 }
108
asn1_item_get_size(unsigned char * p)109 static size_t asn1_item_get_size(unsigned char* p)
110 {
111 size_t res = 0;
112 char bsize = *(p+1);
113 if (bsize & 0x80) {
114 uint16_t ws = 0;
115 uint32_t ds = 0;
116 switch (bsize & 0xF) {
117 case 2:
118 ws = *(uint16_t*)(p+2);
119 res = ntohs(ws);
120 break;
121 case 3:
122 ds = *(uint32_t*)(p+2);
123 res = ntohl(ds) >> 8;
124 break;
125 case 4:
126 ds = *(uint32_t*)(p+2);
127 res = ntohl(ds);
128 break;
129 default:
130 fprintf(stderr, "ERROR: Invalid or unimplemented byte size %d\n", bsize & 0xF);
131 break;
132 }
133 } else {
134 res = (int)bsize;
135 }
136 return res;
137 }
138
asn1_skip_item(unsigned char ** p)139 static void asn1_skip_item(unsigned char** p)
140 {
141 size_t sz = asn1_item_get_size(*p);
142 *p += 2;
143 *p += sz;
144 }
145
profile_get_embedded_plist(plist_t profile)146 static plist_t profile_get_embedded_plist(plist_t profile)
147 {
148 if (plist_get_node_type(profile) != PLIST_DATA) {
149 fprintf(stderr, "%s: unexpected plist node type for profile (PLIST_DATA expected)\n", __func__);
150 return NULL;
151 }
152 char* bbuf = NULL;
153 uint64_t blen = 0;
154 plist_get_data_val(profile, &bbuf, &blen);
155 if (!bbuf) {
156 fprintf(stderr, "%s: could not get data value from plist node\n", __func__);
157 return NULL;
158 }
159
160 unsigned char* pp = (unsigned char*)bbuf;
161
162 if (*pp != ASN1_SEQUENCE) {
163 free(bbuf);
164 fprintf(stderr, "%s: unexpected profile data (0)\n", __func__);
165 return NULL;
166 }
167 size_t slen = asn1_item_get_size(pp);
168 char bsize = *(pp+1);
169 if (bsize & 0x80) {
170 slen += 2 + (bsize & 0xF);
171 } else {
172 slen += 3;
173 }
174 if (slen != blen) {
175 free(bbuf);
176 fprintf(stderr, "%s: unexpected profile data (1)\n", __func__);
177 return NULL;
178 }
179 asn1_next_item(&pp);
180
181 if (*pp != ASN1_OBJECT_IDENTIFIER) {
182 free(bbuf);
183 fprintf(stderr, "%s: unexpected profile data (2)\n", __func__);
184 return NULL;
185 }
186 asn1_skip_item(&pp);
187
188 if (*pp != ASN1_CONTAINER) {
189 free(bbuf);
190 fprintf(stderr, "%s: unexpected profile data (3)\n", __func__);
191 return NULL;
192 }
193 asn1_next_item(&pp);
194
195 if (*pp != ASN1_SEQUENCE) {
196 free(bbuf);
197 fprintf(stderr, "%s: unexpected profile data (4)\n", __func__);
198 return NULL;
199 }
200 asn1_next_item(&pp);
201
202 int k = 0;
203 // go to the 3rd element (skip 2)
204 while (k < 2) {
205 asn1_skip_item(&pp);
206 k++;
207 }
208 if (*pp != ASN1_SEQUENCE) {
209 free(bbuf);
210 fprintf(stderr, "%s: unexpected profile data (5)\n", __func__);
211 return NULL;
212 }
213 asn1_next_item(&pp);
214
215 if (*pp != ASN1_OBJECT_IDENTIFIER) {
216 free(bbuf);
217 fprintf(stderr, "%s: unexpected profile data (6)\n", __func__);
218 return NULL;
219 }
220 asn1_skip_item(&pp);
221
222 if (*pp != ASN1_CONTAINER) {
223 free(bbuf);
224 fprintf(stderr, "%s: unexpected profile data (7)\n", __func__);
225 return NULL;
226 }
227 asn1_next_item(&pp);
228
229 if (*pp != ASN1_OCTET_STRING) {
230 free(bbuf);
231 fprintf(stderr, "%s: unexpected profile data (8)\n", __func__);
232 return NULL;
233 }
234 slen = asn1_item_get_size(pp);
235 asn1_next_item(&pp);
236
237 plist_t pl = NULL;
238 plist_from_xml((char*)pp, slen, &pl);
239 free(bbuf);
240
241 return pl;
242 }
243
profile_read_from_file(const char * path,unsigned char ** profile_data,unsigned int * profile_size)244 static int profile_read_from_file(const char* path, unsigned char **profile_data, unsigned int *profile_size)
245 {
246 FILE* f = fopen(path, "rb");
247 if (!f) {
248 fprintf(stderr, "Could not open file '%s'\n", path);
249 return -1;
250 }
251 fseek(f, 0, SEEK_END);
252 long int size = ftell(f);
253 fseek(f, 0, SEEK_SET);
254
255 if (size >= 0x1000000) {
256 fprintf(stderr, "The file '%s' is too large for processing.\n", path);
257 fclose(f);
258 return -1;
259 }
260
261 unsigned char* buf = malloc(size);
262 if (!buf) {
263 fprintf(stderr, "Could not allocate memory...\n");
264 fclose(f);
265 return -1;
266 }
267
268 long int cur = 0;
269 while (cur < size) {
270 ssize_t r = fread(buf+cur, 1, 512, f);
271 if (r <= 0) {
272 break;
273 }
274 cur += r;
275 }
276 fclose(f);
277
278 if (cur != size) {
279 free(buf);
280 fprintf(stderr, "Could not read in file '%s' (size %ld read %ld)\n", path, size, cur);
281 return -1;
282 }
283
284 *profile_data = buf;
285 *profile_size = (unsigned int)size;
286
287 return 0;
288 }
289
main(int argc,char * argv[])290 int main(int argc, char *argv[])
291 {
292 lockdownd_client_t client = NULL;
293 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
294 lockdownd_service_descriptor_t service = NULL;
295 idevice_t device = NULL;
296 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
297 int res = 0;
298 int i;
299 int op = -1;
300 int output_xml = 0;
301 const char* udid = NULL;
302 const char* param = NULL;
303 const char* param2 = NULL;
304 int use_network = 0;
305
306 #ifndef WIN32
307 signal(SIGPIPE, SIG_IGN);
308 #endif
309 /* parse cmdline args */
310 for (i = 1; i < argc; i++) {
311 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
312 idevice_set_debug_level(1);
313 continue;
314 }
315 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
316 i++;
317 if (!argv[i] || !*argv[i]) {
318 print_usage(argc, argv);
319 return 0;
320 }
321 udid = argv[i];
322 continue;
323 }
324 else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--network")) {
325 use_network = 1;
326 continue;
327 }
328 else if (!strcmp(argv[i], "install")) {
329 i++;
330 if (!argv[i] || (strlen(argv[i]) < 1)) {
331 print_usage(argc, argv);
332 return 0;
333 }
334 param = argv[i];
335 op = OP_INSTALL;
336 continue;
337 }
338 else if (!strcmp(argv[i], "list")) {
339 op = OP_LIST;
340 }
341 else if (!strcmp(argv[i], "copy")) {
342 i++;
343 if (!argv[i] || (strlen(argv[i]) < 1)) {
344 print_usage(argc, argv);
345 return 0;
346 }
347 param = argv[i];
348 op = OP_COPY;
349 i++;
350 if (argv[i] && (strlen(argv[i]) > 0)) {
351 param2 = argv[i];
352 }
353 continue;
354 }
355 else if (!strcmp(argv[i], "remove")) {
356 i++;
357 if (!argv[i] || (strlen(argv[i]) < 1)) {
358 print_usage(argc, argv);
359 return 0;
360 }
361 param = argv[i];
362 op = OP_REMOVE;
363 continue;
364 }
365 else if (!strcmp(argv[i], "remove-all")) {
366 i++;
367 op = OP_REMOVE;
368 continue;
369 }
370 else if (!strcmp(argv[i], "dump")) {
371 i++;
372 if (!argv[i] || (strlen(argv[i]) < 1)) {
373 print_usage(argc, argv);
374 return 0;
375 }
376 param = argv[i];
377 op = OP_DUMP;
378 continue;
379 }
380 else if (!strcmp(argv[i], "-x") || !strcmp(argv[i], "--xml")) {
381 output_xml = 1;
382 continue;
383 }
384 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
385 print_usage(argc, argv);
386 return 0;
387 }
388 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
389 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
390 return 0;
391 }
392 else {
393 print_usage(argc, argv);
394 return 0;
395 }
396 }
397
398 if ((op == -1) || (op >= NUM_OPS)) {
399 print_usage(argc, argv);
400 return 0;
401 }
402
403 if (op == OP_DUMP) {
404 unsigned char* profile_data = NULL;
405 unsigned int profile_size = 0;
406 if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
407 return -1;
408 }
409 plist_t pdata = plist_new_data((char*)profile_data, profile_size);
410 plist_t pl = profile_get_embedded_plist(pdata);
411 plist_free(pdata);
412 free(profile_data);
413
414 if (pl) {
415 if (output_xml) {
416 char* xml = NULL;
417 uint32_t xlen = 0;
418 plist_to_xml(pl, &xml, &xlen);
419 if (xml) {
420 printf("%s\n", xml);
421 free(xml);
422 }
423 } else {
424 if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
425 plist_print_to_stream(pl, stdout);
426 } else {
427 fprintf(stderr, "ERROR: unexpected node type in profile plist (not PLIST_DICT)\n");
428 res = -1;
429 }
430 }
431 } else {
432 fprintf(stderr, "ERROR: could not extract embedded plist from profile!\n");
433 }
434 plist_free(pl);
435
436 return res;
437 } else if (op == OP_COPY) {
438 struct stat st;
439 const char *checkdir = (param2) ? param2 : param;
440 if ((stat(checkdir, &st) < 0) || !S_ISDIR(st.st_mode)) {
441 fprintf(stderr, "ERROR: %s does not exist or is not a directory!\n", checkdir);
442 return -1;
443 }
444 }
445
446 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
447 if (ret != IDEVICE_E_SUCCESS) {
448 if (udid) {
449 printf("No device found with udid %s.\n", udid);
450 } else {
451 printf("No device found.\n");
452 }
453 return -1;
454 }
455
456 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
457 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
458 idevice_free(device);
459 return -1;
460 }
461
462 plist_t pver = NULL;
463 char *pver_s = NULL;
464 lockdownd_get_value(client, NULL, "ProductVersion", &pver);
465 if (pver && plist_get_node_type(pver) == PLIST_STRING) {
466 plist_get_string_val(pver, &pver_s);
467 }
468 plist_free(pver);
469 int product_version_major = 0;
470 int product_version_minor = 0;
471 int product_version_patch = 0;
472 if (pver_s) {
473 sscanf(pver_s, "%d.%d.%d", &product_version_major, &product_version_minor, &product_version_patch);
474 free(pver_s);
475 }
476 if (product_version_major == 0) {
477 fprintf(stderr, "ERROR: Could not determine the device's ProductVersion\n");
478 lockdownd_client_free(client);
479 idevice_free(device);
480 return -1;
481 }
482 int product_version = ((product_version_major & 0xFF) << 16) | ((product_version_minor & 0xFF) << 8) | (product_version_patch & 0xFF);
483
484 if (LOCKDOWN_E_SUCCESS != lockdownd_start_service(client, "com.apple.misagent", &service)) {
485 fprintf(stderr, "Could not start service \"com.apple.misagent\"\n");
486 lockdownd_client_free(client);
487 idevice_free(device);
488 return -1;
489 }
490 lockdownd_client_free(client);
491 client = NULL;
492
493 misagent_client_t mis = NULL;
494 if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) {
495 fprintf(stderr, "Could not connect to \"com.apple.misagent\" on device\n");
496 if (service)
497 lockdownd_service_descriptor_free(service);
498 lockdownd_client_free(client);
499 idevice_free(device);
500 return -1;
501 }
502
503 if (service)
504 lockdownd_service_descriptor_free(service);
505
506 switch (op) {
507 case OP_INSTALL:
508 {
509 unsigned char* profile_data = NULL;
510 unsigned int profile_size = 0;
511 if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
512 break;
513 }
514
515 uint64_t psize = profile_size;
516 plist_t pdata = plist_new_data((const char*)profile_data, psize);
517 free(profile_data);
518
519 if (misagent_install(mis, pdata) == MISAGENT_E_SUCCESS) {
520 printf("Profile '%s' installed successfully.\n", param);
521 } else {
522 int sc = misagent_get_status_code(mis);
523 fprintf(stderr, "Could not install profile '%s', status code: 0x%x\n", param, sc);
524 }
525 }
526 break;
527 case OP_LIST:
528 case OP_COPY:
529 {
530 plist_t profiles = NULL;
531 misagent_error_t merr;
532 if (product_version < 0x090300) {
533 merr = misagent_copy(mis, &profiles);
534 } else {
535 merr = misagent_copy_all(mis, &profiles);
536 }
537 if (merr == MISAGENT_E_SUCCESS) {
538 int found_match = 0;
539 uint32_t num_profiles = plist_array_get_size(profiles);
540 if (op == OP_LIST || !param2) {
541 printf("Device has %d provisioning %s installed:\n", num_profiles, (num_profiles == 1) ? "profile" : "profiles");
542 }
543 uint32_t j;
544 for (j = 0; !found_match && j < num_profiles; j++) {
545 char* p_name = NULL;
546 char* p_uuid = NULL;
547 plist_t profile = plist_array_get_item(profiles, j);
548 plist_t pl = profile_get_embedded_plist(profile);
549 if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
550 plist_t node;
551 node = plist_dict_get_item(pl, "Name");
552 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
553 plist_get_string_val(node, &p_name);
554 }
555 node = plist_dict_get_item(pl, "UUID");
556 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
557 plist_get_string_val(node, &p_uuid);
558 }
559 }
560 if (param2) {
561 if (p_uuid && !strcmp(p_uuid, param)) {
562 found_match = 1;
563 } else {
564 free(p_uuid);
565 free(p_name);
566 continue;
567 }
568 }
569 printf("%s - %s\n", (p_uuid) ? p_uuid : "(unknown id)", (p_name) ? p_name : "(no name)");
570 if (op == OP_COPY) {
571 char pfname[512];
572 if (p_uuid) {
573 sprintf(pfname, "%s/%s.mobileprovision", (param2) ? param2 : param, p_uuid);
574 } else {
575 sprintf(pfname, "%s/profile%d.mobileprovision", (param2) ? param2 : param, j);
576 }
577 FILE* f = fopen(pfname, "wb");
578 if (f) {
579 char* dt = NULL;
580 uint64_t ds = 0;
581 plist_get_data_val(profile, &dt, &ds);
582 fwrite(dt, 1, ds, f);
583 fclose(f);
584 printf(" => %s\n", pfname);
585 } else {
586 fprintf(stderr, "Could not open '%s' for writing: %s\n", pfname, strerror(errno));
587 }
588 }
589 free(p_uuid);
590 free(p_name);
591 }
592 if (param2 && !found_match) {
593 fprintf(stderr, "Profile '%s' was not found on the device.\n", param);
594 res = -1;
595 }
596 } else {
597 int sc = misagent_get_status_code(mis);
598 fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc);
599 res = -1;
600 }
601 plist_free(profiles);
602 }
603 break;
604 case OP_REMOVE:
605 if (param) {
606 /* remove specified provisioning profile */
607 if (misagent_remove(mis, param) == MISAGENT_E_SUCCESS) {
608 printf("Profile '%s' removed.\n", param);
609 } else {
610 int sc = misagent_get_status_code(mis);
611 fprintf(stderr, "Could not remove profile '%s', status code 0x%x\n", param, sc);
612 }
613 } else {
614 /* remove all provisioning profiles */
615 plist_t profiles = NULL;
616 misagent_error_t merr;
617 if (product_version < 0x090300) {
618 merr = misagent_copy(mis, &profiles);
619 } else {
620 merr = misagent_copy_all(mis, &profiles);
621 }
622 if (merr == MISAGENT_E_SUCCESS) {
623 uint32_t j;
624 uint32_t num_removed = 0;
625 for (j = 0; j < plist_array_get_size(profiles); j++) {
626 char* p_name = NULL;
627 char* p_uuid = NULL;
628 plist_t profile = plist_array_get_item(profiles, j);
629 plist_t pl = profile_get_embedded_plist(profile);
630 if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
631 plist_t node;
632 node = plist_dict_get_item(pl, "Name");
633 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
634 plist_get_string_val(node, &p_name);
635 }
636 node = plist_dict_get_item(pl, "UUID");
637 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
638 plist_get_string_val(node, &p_uuid);
639 }
640 }
641 if (p_uuid) {
642 if (misagent_remove(mis, p_uuid) == MISAGENT_E_SUCCESS) {
643 printf("OK profile removed: %s - %s\n", p_uuid, (p_name) ? p_name : "(no name)");
644 num_removed++;
645 } else {
646 int sc = misagent_get_status_code(mis);
647 printf("FAIL profile not removed: %s - %s (status code 0x%x)\n", p_uuid, (p_name) ? p_name : "(no name)", sc);
648 }
649 }
650 free(p_name);
651 free(p_uuid);
652 }
653 printf("%d profiles removed.\n", num_removed);
654 } else {
655 int sc = misagent_get_status_code(mis);
656 fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc);
657 res = -1;
658 }
659 plist_free(profiles);
660 }
661 break;
662 default:
663 break;
664 }
665
666 misagent_client_free(mis);
667
668 idevice_free(device);
669
670 return res;
671 }
672
673