diff options
Diffstat (limited to 'tools')
| -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);  | 
