diff options
-rw-r--r-- | tools/iphonebackup.c | 504 |
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 @@ #include <errno.h> #include <stdlib.h> #include <signal.h> +#include <glib.h> #include <libiphone/libiphone.h> #include <libiphone/lockdown.h> @@ -31,6 +32,10 @@ #define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup" +static mobilebackup_client_t mobilebackup = NULL; +static lockdownd_client_t client = NULL; +static iphone_device_t phone = NULL; + static int quit_flag = 0; enum cmd_mode { @@ -38,43 +43,68 @@ enum cmd_mode { CMD_RESTORE }; -/* -Backup Process Communication: --------------------------------------------------------------------------------- -* Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt -* Verify battery on AC enough battery remaining -* ValidatePair with device for TrustedHost ability -> DLMessageVersionExchange -< DLMessageVersionExchange - DLVersionsOk -> DLMessageDeviceReady -< DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupRequest -> DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupReplyOK -... -> DLSendFile -< DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFileReceived -... -> DLMessageProcessMessage: BackupMessageTypeKey: BackupMessageBackupFinished +static plist_t mobilebackup_factory_info_plist() +{ + /* gather data from lockdown */ + GTimeVal tv = {0, 0}; + plist_t value_node = NULL; + plist_t root_node = NULL; + char *uuid = NULL; + char *uuid_uppercase = NULL; + plist_t ret = plist_new_dict(); -*/ + /* get basic device information in one go */ + lockdownd_get_value(client, NULL, NULL, &root_node); -/* -Restore Process Communication: --------------------------------------------------------------------------------- -* Verify battery on AC enough battery remaining -* ValidatePair with device for TrustedHost ability -> DLMessageVersionExchange -< DLMessageVersionExchange - DLVersionsOk -> DLMessageDeviceReady -< DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreMigrate -... -DLSendFile -... -< DLMessageProcessMessage - BackupMessageTypeKey: BackupMessageRestoreReplyOK -*/ + /* set fields we understand */ + value_node = plist_dict_get_item(root_node, "BuildVersion"); + plist_dict_insert_item(ret, "Build Version", plist_copy(value_node)); + + value_node = plist_dict_get_item(root_node, "DeviceName"); + plist_dict_insert_item(ret, "Device Name", plist_copy(value_node)); + plist_dict_insert_item(ret, "Display Name", plist_copy(value_node)); + + /* FIXME: How is the GUID generated? */ + plist_dict_insert_item(ret, "GUID", plist_new_string("---")); + + value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity"); + if (value_node) + plist_dict_insert_item(ret, "IMEI", plist_copy(value_node)); + + g_get_current_time(&tv); + plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, tv.tv_usec)); -static plist_t mobilebackup_factory_metadata() + value_node = plist_dict_get_item(root_node, "ProductType"); + plist_dict_insert_item(ret, "Product Type", plist_copy(value_node)); + + value_node = plist_dict_get_item(root_node, "ProductVersion"); + plist_dict_insert_item(ret, "Product Version", plist_copy(value_node)); + + value_node = plist_dict_get_item(root_node, "SerialNumber"); + plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node)); + + value_node = plist_dict_get_item(root_node, "UniqueDeviceID"); + iphone_device_get_uuid(phone, &uuid); + plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid)); + + /* uppercase */ + uuid_uppercase = g_ascii_strup(uuid, -1); + plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase)); + free(uuid_uppercase); + free(uuid); + + plist_t files = plist_new_dict(); + /* FIXME: Embed files as <data> nodes */ + plist_dict_insert_item(ret, "iTunes Files", files); + plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2")); + + return ret; +} + +static plist_t mobilebackup_factory_metadata_plist() { + plist_t ret = NULL; /* Metadata key is: <dict> @@ -106,6 +136,7 @@ Metadata key is: <false/> </dict> */ + return ret; } /** @@ -113,92 +144,165 @@ Metadata key is: */ static plist_t mobilebackup_factory_manifest_data_plist() { - plist_t manifest_data = plist_dict_new(); - /* - File hash is: - sha1(<Domain>-<Relative File Path>) - */ - /* - Data hash is: - sha1(<file>) - */ + plist_t ret = NULL; + plist_t value_node = NULL; + char *uuid = NULL; + GTimeVal tv = {0, 0}; -/* -<dict> - <key>DeviceId</key> - <string>7a7b570ee169f02c43d3893f0d661cf7a32e1cf5</string> - <key>Version</key> - <string>6.2</string> - <key>Files</key> - <dict> - <key>3d0d7e5fb2ce288813306e4d4636395e047a3d28</key> - <dict> - <key>ModificationTime</key> - <date>2009-12-29T02:12:17Z</date> - <key>FileLength</key> - <integer>131072</integer> - <key>Domain</key> - <string>HomeDomain</string> - <key>DataHash</key> - <data> - MfpSk+qw+RAJqLNTJI81tntvrwc= - </data> - <key>Group ID</key> - <integer>501</integer> - <key>User ID</key> - <integer>501</integer> - <key>Mode</key> - <integer>420</integer> - </dict> - </dict> - <key>DeviceICCID</key> - <string>89492060399209300736</string> -</dict> -*/ + ret = plist_new_dict(); - return manifest_data; + /* get basic device information in one go */ + lockdownd_get_value(client, NULL, "IntegratedCircuitCardIdentity", &value_node); + + iphone_device_get_uuid(phone, &uuid); + plist_dict_insert_item(ret, "DeviceId", plist_new_string(uuid)); + free(uuid); + + plist_dict_insert_item(ret, "Version", plist_new_string("6.2")); + + /* TODO: add all Applications */ + + /* TODO: add all Files */ + plist_t files = plist_new_dict(); + + /* single file entry */ + plist_t info_node = plist_new_dict(); + g_get_current_time(&tv); + plist_dict_insert_item(info_node, "ModificationTime", plist_new_date(tv.tv_sec, tv.tv_usec)); + plist_dict_insert_item(info_node, "FileLength", plist_new_uint(131072)); + plist_dict_insert_item(info_node, "Domain", plist_new_string("HomeDomain")); + + /* FIXME: calculate correct data hash */ + /* Data hash is: sha1(<file>) */ + plist_dict_insert_item(info_node, "DataHash", plist_new_data(NULL, 0)); + plist_dict_insert_item(info_node, "Group ID", plist_new_uint(501)); + plist_dict_insert_item(info_node, "User ID", plist_new_uint(501)); + plist_dict_insert_item(info_node, "Mode ID", plist_new_uint(420)); + + /* FIXME: calculate correct file hash */ + /* File hash is: sha1(<Domain>-<Relative File Path>) */ + plist_dict_insert_item(files, "3d0d7e5fb2ce288813306e4d4636395e047a3d28", info_node); + plist_dict_insert_item(ret, "Files", files); + + /* last node with ICCID */ + if (value_node) + plist_dict_insert_item(ret, "DeviceICCID", &value_node); + + return ret; } /** * Generates a manifest plist with all needed information and hashes */ -static plist_t mobilebackup_factory_manifest_plist() +static plist_t mobilebackup_factory_manifest_plist(plist_t manifest_data) { - plist_t manifest_data = mobilebackup_factory_manifest_data_plist(); - plist_t manifest = plist_dict_new(); + char *buffer = NULL; + char *s = NULL; + uint32_t length; + unsigned char sha1[20]; + gsize sha1_len; + GChecksum *checksum; + plist_t ret = NULL; - /* - AuthSignature Hash is: - sha1(<manifest_data>) - */ + if (!manifest_data) + return ret; -/* -<dict> - <key>BackupManifestKey</key> - <dict> - <key>AuthVersion</key> - <string>2.0</string> - <key>AuthSignature</key> - <data> - WBdfjcZWg/u/Bpn7aKDJC68UZF4= - </data> - <key>IsEncrypted</key> - <integer>0</integer> - <key>Data</key> - <data><!-- binary plist --> - ... - </data> - </dict> - <key>BackupComputerBasePathKey</key> - <string>/</string> - <key>BackupMessageTypeKey</key> - <string>BackupMessageBackupRequest</string> - <key>BackupProtocolVersion</key> - <string>1.6</string> -</dict> -*/ + ret = plist_new_dict(); + plist_dict_insert_item(ret, "AuthVersion", plist_new_string("2.0")); - return manifest; + /* AuthSignature Hash is: sha1(<manifest_data>) */ + plist_to_bin(manifest_data, &buffer, &length); + + sha1_len = g_checksum_type_get_length(G_CHECKSUM_SHA1); + checksum = g_checksum_new(G_CHECKSUM_SHA1); + g_checksum_update(checksum, (guchar *)buffer, length); + g_checksum_get_digest(checksum, sha1, &sha1_len); + s = (char *)g_checksum_get_string(checksum); + printf("SHA1 AuthSignature: %s\n", s); + plist_dict_insert_item(ret, "AuthSignature", plist_new_data((char*)sha1, sha1_len)); + g_checksum_free(checksum); + + + plist_dict_insert_item(ret, "IsEncrypted", plist_new_uint(0)); + plist_dict_insert_item(ret, "Data", plist_new_data(buffer, length)); + + free(buffer); + + return ret; +} + +enum plist_format_t { + PLIST_FORMAT_XML, + PLIST_FORMAT_BINARY +}; + +static int plist_read_from_filename(char *filename, plist_t *plist) +{ + return 1; +} + +static int plist_write_to_filename(plist_t plist, char *filename, enum plist_format_t format) +{ + char *buffer = NULL; + uint32_t length; + FILE *f; + + if (!plist || !filename) + return 0; + + if (format == PLIST_FORMAT_XML) + plist_to_xml(plist, &buffer, &length); + else if (format == PLIST_FORMAT_BINARY) + plist_to_bin(plist, &buffer, &length); + else + return 0; + + f = fopen(filename, "wb"); + fwrite(buffer, sizeof(char), length, f); + fclose(f); + + free(buffer); + + return 1; +} + +static int plist_strcmp(plist_t node, const char *str) +{ + char *buffer = NULL; + int ret = 0; + + if (plist_get_node_type(node) != PLIST_STRING) + return ret; + + plist_get_string_val(node, &buffer); + ret = strcmp(buffer, str); + free(buffer); + + return ret; +} + +static void mobilebackup_write_status(char *path, int status) +{ + plist_t status_plist = plist_new_dict(); + plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status)); + char *file_path = g_build_path(G_DIR_SEPARATOR_S, path, "Status.plist", NULL); + plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML); + g_free(file_path); + plist_free(status_plist); +} + +static void debug_plist(plist_t plist) +{ + char *buffer = NULL; + uint32_t length = 0; + + if (!plist) + return; + + plist_to_xml(plist, &buffer, &length); + + printf("Printing %i bytes plist:\n%s\n", length, buffer); + free(buffer); } /** @@ -226,10 +330,6 @@ static void print_usage(int argc, char **argv) printf("\n"); } -static mobilebackup_client_t mobilebackup = NULL; -static lockdownd_client_t client = NULL; -static iphone_device_t phone = NULL; - int main(int argc, char *argv[]) { iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; @@ -239,6 +339,8 @@ int main(int argc, char *argv[]) uuid[0] = 0; int cmd = -1; char *backup_directory = NULL; + struct stat st; + plist_t node = NULL; /* we need to exit cleanly on running backups and restores or we cause havok */ signal(SIGINT, clean_exit); @@ -293,6 +395,22 @@ int main(int argc, char *argv[]) return -1; } + /* verify if passed backup directory exists */ + if (stat(backup_directory, &st) != 0) { + printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory); + return -1; + } + + /* restore directory must contain an Info.plist */ + char *info_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Info.plist", NULL); + if (cmd == CMD_RESTORE) { + if (stat(info_path, &st) != 0) { + g_free(info_path); + printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory); + return -1; + } + } + printf("Backup directory is \"%s\"\n", backup_directory); if (uuid[0] != 0) { @@ -322,46 +440,140 @@ int main(int argc, char *argv[]) printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port); mobilebackup_client_new(phone, port, &mobilebackup); - /* TODO: Command implementations */ switch(cmd) { case CMD_BACKUP: - printf("TODO: Creating backup...\n"); -/* -Create target directory: -MobileSync/Backup/<uuid>-YYYYMMDD-HHMMSS/ -*/ - -/* -Create: Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) -Create:Manifest.plist (backup manifest (backup state)) -*/ + printf("Starting backup...\n"); + /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */ + /* TODO: verify battery on AC enough battery remaining */ + + /* ????: create target directory: MobileSync/Backup/<uuid>-YYYYMMDD-HHMMSS/ */ + + /* create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */ + printf("Creating \"%s/Info.plist\".\n", backup_directory); + plist_t info_plist = mobilebackup_factory_info_plist(); + plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); + g_free(info_path); + + /* create Manifest.plist (backup manifest (backup state)) */ + printf("Creating \"%s/Manifest.plist\".\n", backup_directory); + char *manifest_path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, "Manifest.plist", NULL); + plist_t manifest_data = mobilebackup_factory_manifest_data_plist(); + plist_t manifest_plist = mobilebackup_factory_manifest_plist(manifest_data); + plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); + g_free(manifest_path); + + /* create Status.plist with failed status for now */ + mobilebackup_write_status(backup_directory, 0); + + /* close down lockdown connection as it is no longer needed */ lockdownd_client_free(client); -/* -Receive: -... -<hash>.mddata (Raw filedata) -<hash>.mdinfo (backup file information) -... -Create: Status.plist (Info on how the backup process turned out) -*/ + client = NULL; + + /* request backup from device with manifest */ + printf("Sending manifest and requesting backup.\n"); + + node = plist_new_dict(); + plist_dict_insert_item(node, "BackupManifestKey", manifest_plist); + plist_dict_insert_item(node, "BackupComputerBasePathKey", plist_new_string("/")); + plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest")); + plist_dict_insert_item(node, "BackupProtocolVersion", plist_new_string("1.6")); + + plist_t message = plist_new_array(); + plist_array_append_item(message, plist_new_string("DLMessageProcessMessage")); + plist_array_append_item(message, node); + + mobilebackup_send(mobilebackup, message); + plist_free(message); + message = NULL; + + /* get response */ + int backup_ok = 0; + mobilebackup_receive(mobilebackup, &message); + node = plist_array_get_item(message, 0); + if (!plist_strcmp(node, "DLMessageProcessMessage")) { + node = plist_array_get_item(message, 1); + node = plist_dict_get_item(node, "BackupMessageTypeKey"); + if (node && !plist_strcmp(node, "BackupMessageBackupReplyOK")) { + printf("Device accepts manifest and will send backup data now...\n"); + backup_ok = 1; + printf("Acknowledging...\n"); + /* send it back for ACK */ + mobilebackup_send(mobilebackup, message); + } + } else { + printf("Unhandled message received!\n"); + debug_plist(message); + } + plist_free(message); + message = NULL; + + if (!backup_ok) { + printf("ERROR: Device rejected to start the backup process.\n"); + break; + } + + /* receive and save DLSendFile files and metadata, ACK each */ + int file_index = 0; + do { + mobilebackup_receive(mobilebackup, &message); + node = plist_array_get_item(message, 0); + if (plist_strcmp(node, "DLSendFile")) + break; + + printf("Receiving file %d...\n", file_index); + /* TODO: save <hash>.mdinfo */ + /* TODO: save <hash>.mddata */ + debug_plist(message); + plist_free(message); + message = NULL; + + if (quit_flag) { + /* FIXME: need to cancel the backup here */ + break; + } + + /* acknowlegdge that we received the file */ + node = plist_new_dict(); + plist_dict_insert_item(node, "BackupMessageTypeKey", plist_new_string("kBackupMessageBackupFileReceived")); + + message = plist_new_array(); + plist_array_append_item(message, plist_new_string("DLMessageProcessMessage")); + plist_array_append_item(message, node); + mobilebackup_send(mobilebackup, message); + + plist_free(message); + message = NULL; + + file_index++; + } while (!plist_strcmp(node, "DLSendFile")); + + printf("Received %d files from device.\n", file_index); + + if (!plist_strcmp(node, "DLMessageProcessMessage")) { + node = plist_array_get_item(message, 1); + node = plist_dict_get_item(node, "BackupMessageTypeKey"); + /* wait until received final backup finished message */ + if (node && !plist_strcmp(node, "BackupMessageBackupFinished")) { + /* backup finished */ + /* create: Status.plist (Info on how the backup process turned out) */ + printf("Backup Successful.\n"); + mobilebackup_write_status(backup_directory, 1); + } + } + + if (node) + plist_free(node); -/* -- Check lockdown value for domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt -- Verify battery on AC enough battery remaining -- Request backup from device with manifest (BackupMessageBackupRequest) -- Receive and save DLSendFile files and metadata, ACK each -- Wait until received final backup finished message -*/ break; case CMD_RESTORE: - printf("TODO: Restoring backup...\n"); -/* -- Verify battery on AC enough battery remaining -- Request restore from device (BackupMessageRestoreMigrate) -- Read mddata files and send to devices using DLSendFile -- Signal restore finished message to device -*/ + printf("Restoring backup...\n"); + /* verify battery on AC enough battery remaining */ + /* request restore from device (BackupMessageRestoreMigrate) */ + /* read mddata files and send to devices using DLSendFile */ + /* signal restore finished message to device */ + /* close down lockdown connection as it is no longer needed */ lockdownd_client_free(client); + client = NULL; break; default: break; @@ -369,8 +581,12 @@ Create: Status.plist (Info on how the backup process turned out) } else { printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME); lockdownd_client_free(client); + client = NULL; } + if (client) + lockdownd_client_free(client); + if (mobilebackup) mobilebackup_client_free(mobilebackup); iphone_device_free(phone); |