summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2010-06-04 18:29:28 +0200
committerGravatar Nikias Bassen2010-06-04 18:29:28 +0200
commit7a2811b76793b262237b4b61bc777edf9ec37257 (patch)
treee3d32664a9b705bbc9109cb0087ab6f8c5ae0935
parente528494384b9a670a6bfd630524665657aae9311 (diff)
downloadlibimobiledevice-7a2811b76793b262237b4b61bc777edf9ec37257.tar.gz
libimobiledevice-7a2811b76793b262237b4b61bc777edf9ec37257.tar.bz2
idevicebackup: verify all backup files when restoring
-rw-r--r--tools/idevicebackup.c243
1 files changed, 241 insertions, 2 deletions
diff --git a/tools/idevicebackup.c b/tools/idevicebackup.c
index 15b8d05..0a7fd70 100644
--- a/tools/idevicebackup.c
+++ b/tools/idevicebackup.c
@@ -76,6 +76,62 @@ static int compare_hash(const unsigned char *hash1, const unsigned char *hash2,
76 return 1; 76 return 1;
77} 77}
78 78
79static void compute_datahash(const char *path, const char *destpath, uint8_t greylist, const char *domain, const char *appid, const char *version, unsigned char *hash_out)
80{
81 gcry_md_hd_t hd = NULL;
82 gcry_md_open(&hd, GCRY_MD_SHA1, 0);
83 if (!hd) {
84 printf("ERROR: Could not initialize libgcrypt/SHA1\n");
85 return;
86 }
87 gcry_md_reset(hd);
88
89 FILE *f = fopen(path, "rb");
90 if (f) {
91 unsigned char buf[16384];
92 size_t len;
93 while ((len = fread(buf, 1, 16384, f)) > 0) {
94 gcry_md_write(hd, buf, len);
95 }
96 fclose(f);
97 gcry_md_write(hd, destpath, strlen(destpath));
98 gcry_md_write(hd, ";", 1);
99 if (greylist == 1) {
100 gcry_md_write(hd, "true", 4);
101 } else {
102 gcry_md_write(hd, "false", 5);
103 }
104 gcry_md_write(hd, ";", 1);
105 if (domain) {
106 gcry_md_write(hd, domain, strlen(domain));
107 } else {
108 gcry_md_write(hd, "(null)", 6);
109 }
110 gcry_md_write(hd, ";", 1);
111 if (appid) {
112 gcry_md_write(hd, appid, strlen(appid));
113 } else {
114 gcry_md_write(hd, "(null)", 6);
115 }
116 gcry_md_write(hd, ";", 1);
117 if (version) {
118 gcry_md_write(hd, version, strlen(version));
119 } else {
120 gcry_md_write(hd, "(null)", 6);
121 }
122 unsigned char *newhash = gcry_md_read(hd, GCRY_MD_SHA1);
123 memcpy(hash_out, newhash, 20);
124 }
125 gcry_md_close(hd);
126}
127
128static void print_hash(const unsigned char *hash, int len)
129{
130 int i;
131 for (i = 0; i < len; i++) {
132 printf("%02x", hash[i]);
133 }
134}
79 135
80static void notify_cb(const char *notification, void *userdata) 136static void notify_cb(const char *notification, void *userdata)
81{ 137{
@@ -399,6 +455,157 @@ static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory,
399 return ret; 455 return ret;
400} 456}
401 457
458static int mobilebackup_check_file_integrity(const char *backup_directory, const char *hash, plist_t filedata)
459{
460 char *datapath;
461 char *infopath;
462 plist_t mdinfo = NULL;
463 struct stat st;
464 unsigned char file_hash[20];
465
466 datapath = mobilebackup_build_path(backup_directory, hash, ".mddata");
467 if (stat(datapath, &st) != 0) {
468 printf("\r\n");
469 printf("ERROR: '%s.mddata' is missing!\n", hash);
470 free(datapath);
471 return 0;
472 }
473
474 infopath = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
475 plist_read_from_filename(&mdinfo, infopath);
476 free(infopath);
477 if (!mdinfo) {
478 printf("\r\n");
479 printf("ERROR: '%s.mdinfo' is missing or corrupted!\n", hash);
480 free(datapath);
481 return 0;
482 }
483
484 /* sha1 hash verification */
485 plist_t node = plist_dict_get_item(filedata, "DataHash");
486 if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
487 printf("\r\n");
488 printf("ERROR: Could not get DataHash for file entry '%s'\n", hash);
489 plist_free(mdinfo);
490 free(datapath);
491 return 0;
492 }
493
494 node = plist_dict_get_item(mdinfo, "Metadata");
495 if (!node && (plist_get_node_type(node) != PLIST_DATA)) {
496 printf("\r\n");
497 printf("ERROR: Could not find Metadata in plist '%s.mdinfo'\n", hash);
498 plist_free(mdinfo);
499 free(datapath);
500 return 0;
501 }
502
503 char *meta_bin = NULL;
504 uint64_t meta_bin_size = 0;
505 plist_get_data_val(node, &meta_bin, &meta_bin_size);
506 plist_t metadata = NULL;
507 if (meta_bin) {
508 plist_from_bin(meta_bin, (uint32_t)meta_bin_size, &metadata);
509 }
510 if (!metadata) {
511 printf("\r\n");
512 printf("ERROR: Could not get Metadata from plist '%s.mdinfo'\n", hash);
513 plist_free(mdinfo);
514 free(datapath);
515 return 0;
516 }
517
518 char *version = NULL;
519 node = plist_dict_get_item(metadata, "Version");
520 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
521 plist_get_string_val(node, &version);
522 }
523
524 char *destpath = NULL;
525 node = plist_dict_get_item(metadata, "Path");
526 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
527 plist_get_string_val(node, &destpath);
528 }
529
530 uint8_t greylist = 0;
531 node = plist_dict_get_item(metadata, "Greylist");
532 if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) {
533 plist_get_bool_val(node, &greylist);
534 }
535
536 char *domain = NULL;
537 node = plist_dict_get_item(metadata, "Domain");
538 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
539 plist_get_string_val(node, &domain);
540 }
541
542 char *fnstr = malloc(strlen(domain) + 1 + strlen(destpath) + 1);
543 strcpy(fnstr, domain);
544 strcat(fnstr, "-");
545 strcat(fnstr, destpath);
546 unsigned char fnhash[20];
547 char fnamehash[41];
548 char *p = fnamehash;
549 sha1_of_data(fnstr, strlen(fnstr), fnhash);
550 free(fnstr);
551 int i;
552 for ( i = 0; i < 20; i++, p += 2 ) {
553 snprintf (p, 3, "%02x", (unsigned char)fnhash[i] );
554 }
555 if (strcmp(fnamehash, hash)) {
556 printf("\r\n");
557 printf("WARNING: filename hash does not match for entry '%s'\n", hash);
558 }
559
560 char *auth_version = NULL;
561 node = plist_dict_get_item(mdinfo, "AuthVersion");
562 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
563 plist_get_string_val(node, &auth_version);
564 }
565
566 if (strcmp(auth_version, "1.0")) {
567 printf("\r\n");
568 printf("WARNING: Unknown AuthVersion '%s', DataHash cannot be verified!\n", auth_version);
569 }
570
571 node = plist_dict_get_item(filedata, "DataHash");
572 if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
573 printf("\r\n");
574 printf("WARNING: Could not get DataHash key from file info data for entry '%s'\n", hash);
575 }
576
577 int res = 1;
578 unsigned char *data_hash = NULL;
579 uint64_t data_hash_len = 0;
580 plist_get_data_val(node, (char**)&data_hash, &data_hash_len);
581 int hash_ok = 0;
582 if (data_hash && (data_hash_len == 20)) {
583 compute_datahash(datapath, destpath, greylist, domain, NULL, version, file_hash);
584 hash_ok = compare_hash(data_hash, file_hash, 20);
585 } else if (data_hash_len == 0) {
586 /* no datahash present */
587 hash_ok = 1;
588 }
589
590 g_free(domain);
591 g_free(version);
592 g_free(destpath);
593
594 if (!hash_ok) {
595 printf("\r\n");
596 printf("ERROR: The hash for '%s.mddata' does not match DataHash entry in Manifest\n", hash);
597 printf("datahash: ");
598 print_hash(data_hash, 20);
599 printf("\nfilehash: ");
600 print_hash(file_hash, 20);
601 printf("\n");
602 res = 0;
603 }
604 g_free(data_hash);
605 plist_free(mdinfo);
606 return res;
607}
608
402static void do_post_notification(const char *notification) 609static void do_post_notification(const char *notification)
403{ 610{
404 uint16_t nport = 0; 611 uint16_t nport = 0;
@@ -983,8 +1190,40 @@ int main(int argc, char *argv[])
983 printf("Could not read plist from Manifest.plist Data key!\n"); 1190 printf("Could not read plist from Manifest.plist Data key!\n");
984 break; 1191 break;
985 } 1192 }
986 /* loop over Files entries in Manifest data plist */ 1193 plist_t files = plist_dict_get_item(backup_data, "Files");
987 /* make sure both .mddata/.mdinfo files are available for each entry */ 1194 if (files && (plist_get_node_type(files) == PLIST_DICT)) {
1195 plist_dict_iter iter = NULL;
1196 plist_dict_new_iter(files, &iter);
1197 if (iter) {
1198 /* loop over Files entries in Manifest data plist */
1199 char *hash = NULL;
1200 int file_ok = 0;
1201 int total_files = plist_dict_get_size(files);
1202 int cur_file = 1;
1203 node = NULL;
1204 plist_dict_next_item(files, iter, &hash, &node);
1205 while (node) {
1206 printf("Verifying files %d/%d (%d%%) \r", cur_file, total_files, (cur_file*100/total_files));
1207 cur_file++;
1208 /* make sure both .mddata/.mdinfo files are available for each entry */
1209 file_ok = mobilebackup_check_file_integrity(backup_directory, hash, node);
1210 node = NULL;
1211 free(hash);
1212 hash = NULL;
1213 if (!file_ok) {
1214 break;
1215 }
1216 plist_dict_next_item(files, iter, &hash, &node);
1217 }
1218 printf("\n");
1219 free(iter);
1220 if (!file_ok) {
1221 plist_free(backup_data);
1222 break;
1223 }
1224 printf("All backup files appear to be valid\n");
1225 }
1226 }
988 /* request restore from device with manifest (BackupMessageRestoreMigrate) */ 1227 /* request restore from device with manifest (BackupMessageRestoreMigrate) */
989 /* loop over Files entries in Manifest data plist */ 1228 /* loop over Files entries in Manifest data plist */
990 /* read mddata/mdinfo files and send to device using DLSendFile */ 1229 /* read mddata/mdinfo files and send to device using DLSendFile */