summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/iphonebackup.c504
1 files changed, 360 insertions, 144 deletions
diff --git a/tools/iphonebackup.c b/tools/iphonebackup.c
index 8e29160..611c0f2 100644
--- a/tools/iphonebackup.c
+++ b/tools/iphonebackup.c
@@ -24,6 +24,7 @@
24#include <errno.h> 24#include <errno.h>
25#include <stdlib.h> 25#include <stdlib.h>
26#include <signal.h> 26#include <signal.h>
27#include <glib.h>
27 28
28#include <libiphone/libiphone.h> 29#include <libiphone/libiphone.h>
29#include <libiphone/lockdown.h> 30#include <libiphone/lockdown.h>
@@ -31,6 +32,10 @@
31 32
32#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup" 33#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup"
33 34
35static mobilebackup_client_t mobilebackup = NULL;
36static lockdownd_client_t client = NULL;
37static iphone_device_t phone = NULL;
38
34static int quit_flag = 0; 39static int quit_flag = 0;
35 40
36enum cmd_mode { 41enum cmd_mode {
@@ -38,43 +43,68 @@ enum cmd_mode {
38 CMD_RESTORE 43 CMD_RESTORE
39}; 44};
40 45
41/* 46static plist_t mobilebackup_factory_info_plist()
42Backup Process Communication: 47{
43-------------------------------------------------------------------------------- 48 /* gather data from lockdown */
44* Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt 49 GTimeVal tv = {0, 0};
45* Verify battery on AC enough battery remaining 50 plist_t value_node = NULL;
46* ValidatePair with device for TrustedHost ability 51 plist_t root_node = NULL;
47> DLMessageVersionExchange 52 char *uuid = NULL;
48< DLMessageVersionExchange - DLVersionsOk 53 char *uuid_uppercase = NULL;
49> DLMessageDeviceReady
50< DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupRequest
51> DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupReplyOK
52...
53> DLSendFile
54< DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFileReceived
55...
56> DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFinished
57 54
55 plist_t ret = plist_new_dict();
58 56
59*/ 57 /* get basic device information in one go */
58 lockdownd_get_value(client, NULL, NULL, &root_node);
60 59
61/* 60 /* set fields we understand */
62Restore Process Communication: 61 value_node = plist_dict_get_item(root_node, "BuildVersion");
63-------------------------------------------------------------------------------- 62 plist_dict_insert_item(ret, "Build Version", plist_copy(value_node));
64* Verify battery on AC enough battery remaining 63
65* ValidatePair with device for TrustedHost ability 64 value_node = plist_dict_get_item(root_node, "DeviceName");
66> DLMessageVersionExchange 65 plist_dict_insert_item(ret, "Device Name", plist_copy(value_node));
67< DLMessageVersionExchange - DLVersionsOk 66 plist_dict_insert_item(ret, "Display Name", plist_copy(value_node));
68> DLMessageDeviceReady 67
69< DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreMigrate 68 /* FIXME: How is the GUID generated? */
70... 69 plist_dict_insert_item(ret, "GUID", plist_new_string("---"));
71DLSendFile 70
72... 71 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
73< DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreReplyOK 72 if (value_node)
74*/ 73 plist_dict_insert_item(ret, "IMEI", plist_copy(value_node));
74
75 g_get_current_time(&tv);
76 plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, tv.tv_usec));
75 77
76static plist_t mobilebackup_factory_metadata() 78 value_node = plist_dict_get_item(root_node, "ProductType");
79 plist_dict_insert_item(ret, "Product Type", plist_copy(value_node));
80
81 value_node = plist_dict_get_item(root_node, "ProductVersion");
82 plist_dict_insert_item(ret, "Product Version", plist_copy(value_node));
83
84 value_node = plist_dict_get_item(root_node, "SerialNumber");
85 plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node));
86
87 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
88 iphone_device_get_uuid(phone, &uuid);
89 plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid));
90
91 /* uppercase */
92 uuid_uppercase = g_ascii_strup(uuid, -1);
93 plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase));
94 free(uuid_uppercase);
95 free(uuid);
96
97 plist_t files = plist_new_dict();
98 /* FIXME: Embed files as <data> nodes */
99 plist_dict_insert_item(ret, "iTunes Files", files);
100 plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2"));
101
102 return ret;
103}
104
105static plist_t mobilebackup_factory_metadata_plist()
77{ 106{
107 plist_t ret = NULL;
78/* 108/*
79Metadata key is: 109Metadata key is:
80<dict> 110<dict>
@@ -106,6 +136,7 @@ Metadata key is:
106 <false/> 136 <false/>
107</dict> 137</dict>
108*/ 138*/
139 return ret;
109} 140}
110 141
111/** 142/**
@@ -113,92 +144,165 @@ Metadata key is:
113 */ 144 */
114static plist_t mobilebackup_factory_manifest_data_plist() 145static plist_t mobilebackup_factory_manifest_data_plist()
115{ 146{
116 plist_t manifest_data = plist_dict_new(); 147 plist_t ret = NULL;
117 /* 148 plist_t value_node = NULL;
118 File hash is: 149 char *uuid = NULL;
119 sha1(<Domain>-<Relative File Path>) 150 GTimeVal tv = {0, 0};
120 */
121 /*
122 Data hash is:
123 sha1(<file>)
124 */
125 151
126/* 152 ret = plist_new_dict();
127<dict>
128 <key>DeviceId</key>
129 <string>7a7b570ee169f02c43d3893f0d661cf7a32e1cf5</string>
130 <key>Version</key>
131 <string>6.2</string>
132 <key>Files</key>
133 <dict>
134 <key>3d0d7e5fb2ce288813306e4d4636395e047a3d28</key>
135 <dict>
136 <key>ModificationTime</key>
137 <date>2009-12-29T02:12:17Z</date>
138 <key>FileLength</key>
139 <integer>131072</integer>
140 <key>Domain</key>
141 <string>HomeDomain</string>
142 <key>DataHash</key>
143 <data>
144 MfpSk+qw+RAJqLNTJI81tntvrwc=
145 </data>
146 <key>Group ID</key>
147 <integer>501</integer>
148 <key>User ID</key>
149 <integer>501</integer>
150 <key>Mode</key>
151 <integer>420</integer>
152 </dict>
153 </dict>
154 <key>DeviceICCID</key>
155 <string>89492060399209300736</string>
156</dict>
157*/
158 153
159 return manifest_data; 154 /* get basic device information in one go */
155 lockdownd_get_value(client, NULL, "IntegratedCircuitCardIdentity", &value_node);
156
157 iphone_device_get_uuid(phone, &uuid);
158 plist_dict_insert_item(ret, "DeviceId", plist_new_string(uuid));
159 free(uuid);
160
161 plist_dict_insert_item(ret, "Version", plist_new_string("6.2"));
162
163 /* TODO: add all Applications */
164
165 /* TODO: add all Files */
166 plist_t files = plist_new_dict();
167
168 /* single file entry */
169 plist_t info_node = plist_new_dict();
170 g_get_current_time(&tv);
171 plist_dict_insert_item(info_node, "ModificationTime", plist_new_date(tv.tv_sec, tv.tv_usec));
172 plist_dict_insert_item(info_node, "FileLength", plist_new_uint(131072));
173 plist_dict_insert_item(info_node, "Domain", plist_new_string("HomeDomain"));
174
175 /* FIXME: calculate correct data hash */
176 /* Data hash is: sha1(<file>) */
177 plist_dict_insert_item(info_node, "DataHash", plist_new_data(NULL, 0));
178 plist_dict_insert_item(info_node, "Group ID", plist_new_uint(501));
179 plist_dict_insert_item(info_node, "User ID", plist_new_uint(501));
180 plist_dict_insert_item(info_node, "Mode ID", plist_new_uint(420));
181
182 /* FIXME: calculate correct file hash */
183 /* File hash is: sha1(<Domain>-<Relative File Path>) */
184 plist_dict_insert_item(files, "3d0d7e5fb2ce288813306e4d4636395e047a3d28", info_node);
185 plist_dict_insert_item(ret, "Files", files);
186
187 /* last node with ICCID */
188 if (value_node)
189 plist_dict_insert_item(ret, "DeviceICCID", &value_node);
190
191 return ret;
160} 192}
161 193
162/** 194/**
163 * Generates a manifest plist with all needed information and hashes 195 * Generates a manifest plist with all needed information and hashes
164 */ 196 */
165static plist_t mobilebackup_factory_manifest_plist() 197static plist_t mobilebackup_factory_manifest_plist(plist_t manifest_data)
166{ 198{
167 plist_t manifest_data = mobilebackup_factory_manifest_data_plist(); 199 char *buffer = NULL;
168 plist_t manifest = plist_dict_new(); 200 char *s = NULL;
201 uint32_t length;
202 unsigned char sha1[20];
203 gsize sha1_len;
204 GChecksum *checksum;
205 plist_t ret = NULL;
169 206
170 /* 207 if (!manifest_data)
171 AuthSignature Hash is: 208 return ret;
172 sha1(<manifest_data>)
173 */
174 209
175/* 210 ret = plist_new_dict();
176<dict> 211 plist_dict_insert_item(ret, "AuthVersion", plist_new_string("2.0"));
177 <key>BackupManifestKey</key>
178 <dict>
179 <key>AuthVersion</key>
180 <string>2.0</string>
181 <key>AuthSignature</key>
182 <data>
183 WBdfjcZWg/u/Bpn7aKDJC68UZF4=
184 </data>
185 <key>IsEncrypted</key>
186 <integer>0</integer>
187 <key>Data</key>
188 <data><!-- binary plist -->
189 ...
190 </data>
191 </dict>
192 <key>BackupComputerBasePathKey</key>
193 <string>/</string>
194 <key>BackupMessageTypeKey</key>
195 <string>BackupMessageBackupRequest</string>
196 <key>BackupProtocolVersion</key>
197 <string>1.6</string>
198</dict>
199*/
200 212
201 return manifest; 213 /* AuthSignature Hash is: sha1(<manifest_data>) */
214 plist_to_bin(manifest_data, &buffer, &length);
215
216 sha1_len = g_checksum_type_get_length(G_CHECKSUM_SHA1);
217 checksum = g_checksum_new(G_CHECKSUM_SHA1);
218 g_checksum_update(checksum, (guchar *)buffer, length);
219 g_checksum_get_digest(checksum, sha1, &sha1_len);
220 s = (char *)g_checksum_get_string(checksum);
221 printf("SHA1 AuthSignature: %s\n", s);
222 plist_dict_insert_item(ret, "AuthSignature", plist_new_data((char*)sha1, sha1_len));
223 g_checksum_free(checksum);
224
225
226 plist_dict_insert_item(ret, "IsEncrypted", plist_new_uint(0));
227 plist_dict_insert_item(ret, "Data", plist_new_data(buffer, length));
228
229 free(buffer);
230
231 return ret;
232}
233
234enum plist_format_t {
235 PLIST_FORMAT_XML,
236 PLIST_FORMAT_BINARY
237};
238
239static int plist_read_from_filename(char *filename, plist_t *plist)
240{
241 return 1;
242}
243
244static int plist_write_to_filename(plist_t plist, char *filename, enum plist_format_t format)
245{
246 char *buffer = NULL;
247 uint32_t length;
248 FILE *f;
249
250 if (!plist || !filename)
251 return 0;
252
253 if (format == PLIST_FORMAT_XML)
254 plist_to_xml(plist, &buffer, &length);
255 else if (format == PLIST_FORMAT_BINARY)
256 plist_to_bin(plist, &buffer, &length);
257 else
258 return 0;
259
260 f = fopen(filename, "wb");
261 fwrite(buffer, sizeof(char), length, f);
262 fclose(f);
263
264 free(buffer);
265
266 return 1;
267}
268
269static int plist_strcmp(plist_t node, const char *str)
270{
271 char *buffer = NULL;
272 int ret = 0;
273
274 if (plist_get_node_type(node) != PLIST_STRING)
275 return ret;
276
277 plist_get_string_val(node, &buffer);
278 ret = strcmp(buffer, str);
279 free(buffer);
280
281 return ret;
282}
283
284static void mobilebackup_write_status(char *path, int status)
285{
286 plist_t status_plist = plist_new_dict();
287 plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status));
288 char *file_path = g_build_path(G_DIR_SEPARATOR_S, path, "Status.plist", NULL);
289 plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML);
290 g_free(file_path);
291 plist_free(status_plist);
292}
293
294static void debug_plist(plist_t plist)
295{
296 char *buffer = NULL;
297 uint32_t length = 0;
298
299 if (!plist)
300 return;
301
302 plist_to_xml(plist, &buffer, &length);
303
304 printf("Printing %i bytes plist:\n%s\n", length, buffer);
305 free(buffer);
202} 306}
203 307
204/** 308/**
@@ -226,10 +330,6 @@ static void print_usage(int argc, char **argv)
226 printf("\n"); 330 printf("\n");
227} 331}
228 332
229static mobilebackup_client_t mobilebackup = NULL;
230static lockdownd_client_t client = NULL;
231static iphone_device_t phone = NULL;
232
233int main(int argc, char *argv[]) 333int main(int argc, char *argv[])
234{ 334{
235 iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; 335 iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;
@@ -239,6 +339,8 @@ int main(int argc, char *argv[])
239 uuid[0] = 0; 339 uuid[0] = 0;
240 int cmd = -1; 340 int cmd = -1;
241 char *backup_directory = NULL; 341 char *backup_directory = NULL;
342 struct stat st;
343 plist_t node = NULL;
242 344
243 /* we need to exit cleanly on running backups and restores or we cause havok */ 345 /* we need to exit cleanly on running backups and restores or we cause havok */
244 signal(SIGINT, clean_exit); 346 signal(SIGINT, clean_exit);
@@ -293,6 +395,22 @@ int main(int argc, char *argv[])
293 return -1; 395 return -1;
294 } 396 }
295 397
398 /* verify if passed backup directory exists */
399 if (stat(backup_directory, &st) != 0) {
400 printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
401 return -1;
402 }
403
404 /* restore directory must contain an Info.plist */
405 char *info_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Info.plist", NULL);
406 if (cmd == CMD_RESTORE) {
407 if (stat(info_path, &st) != 0) {
408 g_free(info_path);
409 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory);
410 return -1;
411 }
412 }
413
296 printf("Backup directory is \"%s\"\n", backup_directory); 414 printf("Backup directory is \"%s\"\n", backup_directory);
297 415
298 if (uuid[0] != 0) { 416 if (uuid[0] != 0) {
@@ -322,46 +440,140 @@ int main(int argc, char *argv[])
322 printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port); 440 printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port);
323 mobilebackup_client_new(phone, port, &mobilebackup); 441 mobilebackup_client_new(phone, port, &mobilebackup);
324 442
325 /* TODO: Command implementations */
326 switch(cmd) { 443 switch(cmd) {
327 case CMD_BACKUP: 444 case CMD_BACKUP:
328 printf("TODO: Creating backup...\n"); 445 printf("Starting backup...\n");
329/* 446 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
330Create target directory: 447 /* TODO: verify battery on AC enough battery remaining */
331MobileSync/Backup/<uuid>-YYYYMMDD-HHMMSS/ 448
332*/ 449 /* ????: create target directory: MobileSync/Backup/<uuid>-YYYYMMDD-HHMMSS/ */
333 450
334/* 451 /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
335Create: Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) 452 printf("Creating \"%s/Info.plist\".\n", backup_directory);
336Create:Manifest.plist (backup manifest (backup state)) 453 plist_t info_plist = mobilebackup_factory_info_plist();
337*/ 454 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
455 g_free(info_path);
456
457 /* create Manifest.plist (backup manifest (backup state)) */
458 printf("Creating \"%s/Manifest.plist\".\n", backup_directory);
459 char *manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Manifest.plist", NULL);
460 plist_t manifest_data = mobilebackup_factory_manifest_data_plist();
461 plist_t manifest_plist = mobilebackup_factory_manifest_plist(manifest_data);
462 plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML);
463 g_free(manifest_path);
464
465 /* create Status.plist with failed status for now */
466 mobilebackup_write_status(backup_directory, 0);
467
468 /* close down lockdown connection as it is no longer needed */
338 lockdownd_client_free(client); 469 lockdownd_client_free(client);
339/* 470 client = NULL;
340Receive: 471
341... 472 /* request backup from device with manifest */
342<hash>.mddata (Raw filedata) 473 printf("Sending manifest and requesting backup.\n");
343<hash>.mdinfo (backup file information) 474
344... 475 node = plist_new_dict();
345Create: Status.plist (Info on how the backup process turned out) 476 plist_dict_insert_item(node, "BackupManifestKey", manifest_plist);
346*/ 477 plist_dict_insert_item(node, "BackupComputerBasePathKey", plist_new_string("/"));
478 plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest"));
479 plist_dict_insert_item(node, "BackupProtocolVersion", plist_new_string("1.6"));
480
481 plist_t message = plist_new_array();
482 plist_array_append_item(message, plist_new_string("DLMessageProcessMessage"));
483 plist_array_append_item(message, node);
484
485 mobilebackup_send(mobilebackup, message);
486 plist_free(message);
487 message = NULL;
488
489 /* get response */
490 int backup_ok = 0;
491 mobilebackup_receive(mobilebackup, &message);
492 node = plist_array_get_item(message, 0);
493 if (!plist_strcmp(node, "DLMessageProcessMessage")) {
494 node = plist_array_get_item(message, 1);
495 node = plist_dict_get_item(node, "BackupMessageTypeKey");
496 if (node && !plist_strcmp(node, "BackupMessageBackupReplyOK")) {
497 printf("Device accepts manifest and will send backup data now...\n");
498 backup_ok = 1;
499 printf("Acknowledging...\n");
500 /* send it back for ACK */
501 mobilebackup_send(mobilebackup, message);
502 }
503 } else {
504 printf("Unhandled message received!\n");
505 debug_plist(message);
506 }
507 plist_free(message);
508 message = NULL;
509
510 if (!backup_ok) {
511 printf("ERROR: Device rejected to start the backup process.\n");
512 break;
513 }
514
515 /* receive and save DLSendFile files and metadata, ACK each */
516 int file_index = 0;
517 do {
518 mobilebackup_receive(mobilebackup, &message);
519 node = plist_array_get_item(message, 0);
520 if (plist_strcmp(node, "DLSendFile"))
521 break;
522
523 printf("Receiving file %d...\n", file_index);
524 /* TODO: save <hash>.mdinfo */
525 /* TODO: save <hash>.mddata */
526 debug_plist(message);
527 plist_free(message);
528 message = NULL;
529
530 if (quit_flag) {
531 /* FIXME: need to cancel the backup here */
532 break;
533 }
534
535 /* acknowlegdge that we received the file */
536 node = plist_new_dict();
537 plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("kBackupMessageBackupFileReceived"));
538
539 message = plist_new_array();
540 plist_array_append_item(message, plist_new_string("DLMessageProcessMessage"));
541 plist_array_append_item(message, node);
542 mobilebackup_send(mobilebackup, message);
543
544 plist_free(message);
545 message = NULL;
546
547 file_index++;
548 } while (!plist_strcmp(node, "DLSendFile"));
549
550 printf("Received %d files from device.\n", file_index);
551
552 if (!plist_strcmp(node, "DLMessageProcessMessage")) {
553 node = plist_array_get_item(message, 1);
554 node = plist_dict_get_item(node, "BackupMessageTypeKey");
555 /* wait until received final backup finished message */
556 if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) {
557 /* backup finished */
558 /* create: Status.plist (Info on how the backup process turned out) */
559 printf("Backup Successful.\n");
560 mobilebackup_write_status(backup_directory, 1);
561 }
562 }
563
564 if (node)
565 plist_free(node);
347 566
348/*
349- Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt
350- Verify battery on AC enough battery remaining
351- Request backup from device with manifest (BackupMessageBackupRequest)
352- Receive and save DLSendFile files and metadata, ACK each
353- Wait until received final backup finished message
354*/
355 break; 567 break;
356 case CMD_RESTORE: 568 case CMD_RESTORE:
357 printf("TODO: Restoring backup...\n"); 569 printf("Restoring backup...\n");
358/* 570 /* verify battery on AC enough battery remaining */
359- Verify battery on AC enough battery remaining 571 /* request restore from device (BackupMessageRestoreMigrate) */
360- Request restore from device (BackupMessageRestoreMigrate) 572 /* read mddata files and send to devices using DLSendFile */
361- Read mddata files and send to devices using DLSendFile 573 /* signal restore finished message to device */
362- Signal restore finished message to device 574 /* close down lockdown connection as it is no longer needed */
363*/
364 lockdownd_client_free(client); 575 lockdownd_client_free(client);
576 client = NULL;
365 break; 577 break;
366 default: 578 default:
367 break; 579 break;
@@ -369,8 +581,12 @@ Create: Status.plist (Info on how the backup process turned out)
369 } else { 581 } else {
370 printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME); 582 printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME);
371 lockdownd_client_free(client); 583 lockdownd_client_free(client);
584 client = NULL;
372 } 585 }
373 586
587 if (client)
588 lockdownd_client_free(client);
589
374 if (mobilebackup) 590 if (mobilebackup)
375 mobilebackup_client_free(mobilebackup); 591 mobilebackup_client_free(mobilebackup);
376 iphone_device_free(phone); 592 iphone_device_free(phone);