ideviceprovision.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. * ideviceprovision.c
  3. * Simple utility to install, get, or remove provisioning profiles
  4. * to/from idevices
  5. *
  6. * Copyright (c) 2012 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. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #ifdef WIN32
  26. #include <windows.h>
  27. #else
  28. #include <arpa/inet.h>
  29. #endif
  30. #include <libimobiledevice/libimobiledevice.h>
  31. #include <libimobiledevice/lockdown.h>
  32. #include <libimobiledevice/misagent.h>
  33. #include "common/utils.h"
  34. static void print_usage(int argc, char **argv)
  35. {
  36. char *name = NULL;
  37. name = strrchr(argv[0], '/');
  38. printf("Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
  39. printf("Manage provisioning profiles on a device.\n\n");
  40. printf(" Where COMMAND is one of:\n");
  41. printf(" install FILE\tInstalls the provisioning profile specified by FILE.\n");
  42. printf(" \tA valid .mobileprovision file is expected.\n");
  43. printf(" list\t\tGet a list of all provisioning profiles on the device.\n");
  44. printf(" copy PATH\tRetrieves all provisioning profiles from the device and\n");
  45. printf(" \tstores them into the existing directory specified by PATH.\n");
  46. printf(" \tThe files will be stored as UUID.mobileprovision\n");
  47. printf(" remove UUID\tRemoves the provisioning profile identified by UUID.\n");
  48. printf(" dump FILE\tPrints detailed information about the provisioning profile\n");
  49. printf(" \tspecified by FILE.\n\n");
  50. printf(" The following OPTIONS are accepted:\n");
  51. printf(" -d, --debug enable communication debugging\n");
  52. printf(" -u, --udid UDID target specific device by its 40-digit device UDID\n");
  53. printf(" -x, --xml print XML output when using the 'dump' command\n");
  54. printf(" -h, --help prints usage information\n");
  55. printf("\n");
  56. printf("Homepage: <http://libimobiledevice.org>\n");
  57. }
  58. enum {
  59. OP_INSTALL,
  60. OP_LIST,
  61. OP_COPY,
  62. OP_REMOVE,
  63. OP_DUMP,
  64. NUM_OPS
  65. };
  66. #define ASN1_SEQUENCE 0x30
  67. #define ASN1_CONTAINER 0xA0
  68. #define ASN1_OBJECT_IDENTIFIER 0x06
  69. #define ASN1_OCTET_STRING 0x04
  70. static void asn1_next_item(unsigned char** p)
  71. {
  72. if (*(*p+1) & 0x80) {
  73. *p += 4;
  74. } else {
  75. *p += 3;
  76. }
  77. }
  78. static int asn1_item_get_size(unsigned char* p)
  79. {
  80. int res = 0;
  81. if (*(p+1) & 0x80) {
  82. uint16_t ws = 0;
  83. memcpy(&ws, p+2, 2);
  84. ws = ntohs(ws);
  85. res = ws;
  86. } else {
  87. res = (int) *(p+1);
  88. }
  89. return res;
  90. }
  91. static void asn1_skip_item(unsigned char** p)
  92. {
  93. int sz = asn1_item_get_size(*p);
  94. *p += 2;
  95. *p += sz;
  96. }
  97. static plist_t profile_get_embedded_plist(plist_t profile)
  98. {
  99. if (plist_get_node_type(profile) != PLIST_DATA) {
  100. fprintf(stderr, "%s: unexpected plist node type for profile (PLIST_DATA expected)\n", __func__);
  101. return NULL;
  102. }
  103. char* bbuf = NULL;
  104. uint64_t blen = 0;
  105. plist_get_data_val(profile, &bbuf, &blen);
  106. if (!bbuf) {
  107. fprintf(stderr, "%s: could not get data value from plist node\n", __func__);
  108. return NULL;
  109. }
  110. unsigned char* pp = (unsigned char*)bbuf;
  111. if (*pp != ASN1_SEQUENCE) {
  112. free(bbuf);
  113. fprintf(stderr, "%s: unexpected profile data (0)\n", __func__);
  114. return NULL;
  115. }
  116. uint16_t slen = asn1_item_get_size(pp);
  117. if (slen+4 != (uint16_t)blen) {
  118. free(bbuf);
  119. fprintf(stderr, "%s: unexpected profile data (1)\n", __func__);
  120. return NULL;
  121. }
  122. asn1_next_item(&pp);
  123. if (*pp != ASN1_OBJECT_IDENTIFIER) {
  124. free(bbuf);
  125. fprintf(stderr, "%s: unexpected profile data (2)\n", __func__);
  126. return NULL;
  127. }
  128. asn1_skip_item(&pp);
  129. if (*pp != ASN1_CONTAINER) {
  130. free(bbuf);
  131. fprintf(stderr, "%s: unexpected profile data (3)\n", __func__);
  132. return NULL;
  133. }
  134. asn1_next_item(&pp);
  135. if (*pp != ASN1_SEQUENCE) {
  136. free(bbuf);
  137. fprintf(stderr, "%s: unexpected profile data (4)\n", __func__);
  138. return NULL;
  139. }
  140. asn1_next_item(&pp);
  141. int k = 0;
  142. // go to the 3rd element (skip 2)
  143. while (k < 2) {
  144. asn1_skip_item(&pp);
  145. k++;
  146. }
  147. if (*pp != ASN1_SEQUENCE) {
  148. free(bbuf);
  149. fprintf(stderr, "%s: unexpected profile data (5)\n", __func__);
  150. return NULL;
  151. }
  152. asn1_next_item(&pp);
  153. if (*pp != ASN1_OBJECT_IDENTIFIER) {
  154. free(bbuf);
  155. fprintf(stderr, "%s: unexpected profile data (6)\n", __func__);
  156. return NULL;
  157. }
  158. asn1_skip_item(&pp);
  159. if (*pp != ASN1_CONTAINER) {
  160. free(bbuf);
  161. fprintf(stderr, "%s: unexpected profile data (7)\n", __func__);
  162. return NULL;
  163. }
  164. asn1_next_item(&pp);
  165. if (*pp != ASN1_OCTET_STRING) {
  166. free(bbuf);
  167. fprintf(stderr, "%s: unexpected profile data (8)\n", __func__);
  168. return NULL;
  169. }
  170. slen = asn1_item_get_size(pp);
  171. asn1_next_item(&pp);
  172. plist_t pl = NULL;
  173. plist_from_xml((char*)pp, slen, &pl);
  174. free(bbuf);
  175. return pl;
  176. }
  177. static int profile_read_from_file(const char* path, unsigned char **profile_data, unsigned int *profile_size)
  178. {
  179. FILE* f = fopen(path, "rb");
  180. if (!f) {
  181. fprintf(stderr, "Could not open file '%s'\n", path);
  182. return -1;
  183. }
  184. fseek(f, 0, SEEK_END);
  185. long int size = ftell(f);
  186. fseek(f, 0, SEEK_SET);
  187. if (size >= 0x1000000) {
  188. fprintf(stderr, "The file '%s' is too large for processing.\n", path);
  189. fclose(f);
  190. return -1;
  191. }
  192. unsigned char* buf = malloc(size);
  193. if (!buf) {
  194. fprintf(stderr, "Could not allocate memory...\n");
  195. fclose(f);
  196. return -1;
  197. }
  198. long int cur = 0;
  199. while (cur < size) {
  200. ssize_t r = fread(buf+cur, 1, 512, f);
  201. if (r <= 0) {
  202. break;
  203. }
  204. cur += r;
  205. }
  206. fclose(f);
  207. if (cur != size) {
  208. free(buf);
  209. fprintf(stderr, "Could not read in file '%s' (size %ld read %ld)\n", path, size, cur);
  210. return -1;
  211. }
  212. *profile_data = buf;
  213. *profile_size = (unsigned int)size;
  214. return 0;
  215. }
  216. int main(int argc, char *argv[])
  217. {
  218. lockdownd_client_t client = NULL;
  219. lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
  220. lockdownd_service_descriptor_t service = NULL;
  221. idevice_t device = NULL;
  222. idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  223. int i;
  224. int op = -1;
  225. int output_xml = 0;
  226. const char* udid = NULL;
  227. const char* param = NULL;
  228. /* parse cmdline args */
  229. for (i = 1; i < argc; i++) {
  230. if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
  231. idevice_set_debug_level(1);
  232. continue;
  233. }
  234. else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
  235. i++;
  236. if (!argv[i] || (strlen(argv[i]) != 40)) {
  237. print_usage(argc, argv);
  238. return 0;
  239. }
  240. udid = argv[i];
  241. continue;
  242. }
  243. else if (!strcmp(argv[i], "install")) {
  244. i++;
  245. if (!argv[i] || (strlen(argv[i]) < 1)) {
  246. print_usage(argc, argv);
  247. return 0;
  248. }
  249. param = argv[i];
  250. op = OP_INSTALL;
  251. continue;
  252. }
  253. else if (!strcmp(argv[i], "list")) {
  254. op = OP_LIST;
  255. }
  256. else if (!strcmp(argv[i], "copy")) {
  257. i++;
  258. if (!argv[i] || (strlen(argv[i]) < 1)) {
  259. print_usage(argc, argv);
  260. return 0;
  261. }
  262. param = argv[i];
  263. op = OP_COPY;
  264. continue;
  265. }
  266. else if (!strcmp(argv[i], "remove")) {
  267. i++;
  268. if (!argv[i] || (strlen(argv[i]) < 1)) {
  269. print_usage(argc, argv);
  270. return 0;
  271. }
  272. param = argv[i];
  273. op = OP_REMOVE;
  274. continue;
  275. }
  276. else if (!strcmp(argv[i], "dump")) {
  277. i++;
  278. if (!argv[i] || (strlen(argv[i]) < 1)) {
  279. print_usage(argc, argv);
  280. return 0;
  281. }
  282. param = argv[i];
  283. op = OP_DUMP;
  284. continue;
  285. }
  286. else if (!strcmp(argv[i], "-x") || !strcmp(argv[i], "--xml")) {
  287. output_xml = 1;
  288. continue;
  289. }
  290. else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
  291. print_usage(argc, argv);
  292. return 0;
  293. }
  294. else {
  295. print_usage(argc, argv);
  296. return 0;
  297. }
  298. }
  299. if ((op == -1) || (op >= NUM_OPS)) {
  300. print_usage(argc, argv);
  301. return 0;
  302. }
  303. if (op == OP_DUMP) {
  304. int res = 0;
  305. unsigned char* profile_data = NULL;
  306. unsigned int profile_size = 0;
  307. if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
  308. return -1;
  309. }
  310. plist_t pdata = plist_new_data((char*)profile_data, profile_size);
  311. plist_t pl = profile_get_embedded_plist(pdata);
  312. plist_free(pdata);
  313. free(profile_data);
  314. if (pl) {
  315. if (output_xml) {
  316. char* xml = NULL;
  317. uint32_t xlen = 0;
  318. plist_to_xml(pl, &xml, &xlen);
  319. if (xml) {
  320. printf("%s\n", xml);
  321. free(xml);
  322. }
  323. } else {
  324. if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
  325. plist_print_to_stream(pl, stdout);
  326. } else {
  327. fprintf(stderr, "ERROR: unexpected node type in profile plist (not PLIST_DICT)\n");
  328. res = -1;
  329. }
  330. }
  331. } else {
  332. fprintf(stderr, "ERROR: could not extract embedded plist from profile!\n");
  333. }
  334. plist_free(pl);
  335. return res;
  336. }
  337. ret = idevice_new(&device, udid);
  338. if (ret != IDEVICE_E_SUCCESS) {
  339. if (udid) {
  340. printf("No device found with udid %s, is it plugged in?\n", udid);
  341. } else {
  342. printf("No device found, is it plugged in?\n");
  343. }
  344. return -1;
  345. }
  346. if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, "ideviceprovision"))) {
  347. fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
  348. idevice_free(device);
  349. return -1;
  350. }
  351. if (LOCKDOWN_E_SUCCESS != lockdownd_start_service(client, "com.apple.misagent", &service)) {
  352. fprintf(stderr, "Could not start service \"com.apple.misagent\"\n");
  353. lockdownd_client_free(client);
  354. idevice_free(device);
  355. return -1;
  356. }
  357. lockdownd_client_free(client);
  358. client = NULL;
  359. misagent_client_t mis = NULL;
  360. if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) {
  361. fprintf(stderr, "Could not connect to \"com.apple.misagent\" on device\n");
  362. if (service)
  363. lockdownd_service_descriptor_free(service);
  364. lockdownd_client_free(client);
  365. idevice_free(device);
  366. return -1;
  367. }
  368. if (service)
  369. lockdownd_service_descriptor_free(service);
  370. switch (op) {
  371. case OP_INSTALL:
  372. {
  373. unsigned char* profile_data = NULL;
  374. unsigned int profile_size = 0;
  375. if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
  376. break;
  377. }
  378. uint64_t psize = profile_size;
  379. plist_t pdata = plist_new_data((const char*)profile_data, psize);
  380. free(profile_data);
  381. if (misagent_install(mis, pdata) == MISAGENT_E_SUCCESS) {
  382. printf("Profile '%s' installed successfully.\n", param);
  383. } else {
  384. int sc = misagent_get_status_code(mis);
  385. fprintf(stderr, "Could not install profile '%s', status code: 0x%x\n", param, sc);
  386. }
  387. }
  388. break;
  389. case OP_LIST:
  390. case OP_COPY:
  391. {
  392. plist_t profiles = NULL;
  393. if (misagent_copy(mis, &profiles) == MISAGENT_E_SUCCESS) {
  394. uint32_t num_profiles = plist_array_get_size(profiles);
  395. printf("Device has %d provisioning %s installed:\n", num_profiles, (num_profiles == 1) ? "profile" : "profiles");
  396. uint32_t j;
  397. for (j = 0; j < num_profiles; j++) {
  398. char* p_name = NULL;
  399. char* p_uuid = NULL;
  400. plist_t profile = plist_array_get_item(profiles, j);
  401. plist_t pl = profile_get_embedded_plist(profile);
  402. if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
  403. plist_t node;
  404. node = plist_dict_get_item(pl, "Name");
  405. if (node && (plist_get_node_type(node) == PLIST_STRING)) {
  406. plist_get_string_val(node, &p_name);
  407. }
  408. node = plist_dict_get_item(pl, "UUID");
  409. if (node && (plist_get_node_type(node) == PLIST_STRING)) {
  410. plist_get_string_val(node, &p_uuid);
  411. }
  412. }
  413. printf("%s - %s\n", (p_uuid) ? p_uuid : "(unknown id)", (p_name) ? p_name : "(no name)");
  414. if (op == OP_COPY) {
  415. char pfname[512];
  416. if (p_uuid) {
  417. sprintf(pfname, "%s/%s.mobileprovision", param, p_uuid);
  418. } else {
  419. sprintf(pfname, "%s/profile%d.mobileprovision", param, j);
  420. }
  421. FILE* f = fopen(pfname, "wb");
  422. if (f) {
  423. char* dt = NULL;
  424. uint64_t ds = 0;
  425. plist_get_data_val(profile, &dt, &ds);
  426. fwrite(dt, 1, ds, f);
  427. fclose(f);
  428. printf(" => %s\n", pfname);
  429. } else {
  430. fprintf(stderr, "Could not open '%s' for writing\n", pfname);
  431. }
  432. }
  433. if (p_uuid) {
  434. free(p_uuid);
  435. }
  436. if (p_name) {
  437. free(p_name);
  438. }
  439. }
  440. } else {
  441. int sc = misagent_get_status_code(mis);
  442. fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc);
  443. }
  444. }
  445. break;
  446. case OP_REMOVE:
  447. if (misagent_remove(mis, param) == MISAGENT_E_SUCCESS) {
  448. printf("Profile '%s' removed.\n", param);
  449. } else {
  450. int sc = misagent_get_status_code(mis);
  451. fprintf(stderr, "Could not remove profile '%s', status code 0x%x\n", param, sc);
  452. }
  453. break;
  454. default:
  455. break;
  456. }
  457. misagent_client_free(mis);
  458. idevice_free(device);
  459. return 0;
  460. }