From ec720cc1c30ac3f9b7996575e835565f60ce2b3e Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Sun, 18 Aug 2013 05:28:53 +0200 Subject: Refactor userpref logic to use plist format and implement trust dialog handling iOS 7 introduced a new pairing workflow which increases security by showing a trust dialog to the user before pairing with the host is allowed. The userpref system was refactored to use the native plist format, too. Configuration files of the native implementations are used on each platform. Former configuration files are no longer in use and can be deleted. --- common/userpref.c | 684 ++++++++++++++++++------------------ common/userpref.h | 36 +- include/libimobiledevice/lockdown.h | 5 +- src/idevice.c | 2 +- src/lockdown.c | 257 +++++++++++--- src/lockdown.h | 4 +- 6 files changed, 579 insertions(+), 409 deletions(-) diff --git a/common/userpref.c b/common/userpref.c index 23fe583..41ce9b9 100644 --- a/common/userpref.c +++ b/common/userpref.c @@ -58,14 +58,6 @@ #include "debug.h" #include "utils.h" -#define LIBIMOBILEDEVICE_CONF_DIR "libimobiledevice" -#define LIBIMOBILEDEVICE_CONF_FILE "libimobiledevicerc" - -#define LIBIMOBILEDEVICE_ROOT_PRIVKEY "RootPrivateKey.pem" -#define LIBIMOBILEDEVICE_HOST_PRIVKEY "HostPrivateKey.pem" -#define LIBIMOBILEDEVICE_ROOT_CERTIF "RootCertificate.pem" -#define LIBIMOBILEDEVICE_HOST_CERTIF "HostCertificate.pem" - #ifdef WIN32 #define DIR_SEP '\\' #define DIR_SEP_S "\\" @@ -74,6 +66,19 @@ #define DIR_SEP_S "/" #endif +#define USERPREF_CONFIG_EXTENSION ".plist" + +#ifndef __APPLE__ +#define USERPREF_CONFIG_DIR "libimobiledevice" +#else +#ifdef WIN32 +#define USERPREF_CONFIG_DIR "Apple"DIR_SEP_S"Lockdown" +#else +#define USERPREF_CONFIG_DIR "lockdown" +#endif +#endif +#define USERPREF_CONFIG_FILE "SystemConfiguration"USERPREF_CONFIG_EXTENSION + static char *__config_dir = NULL; #ifdef WIN32 @@ -172,7 +177,7 @@ static const char *userpref_get_config_dir() LPITEMIDLIST pidl = NULL; BOOL b = FALSE; - hr = SHGetSpecialFolderLocation (NULL, CSIDL_LOCAL_APPDATA, &pidl); + hr = SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_APPDATA, &pidl); if (hr == S_OK) { b = SHGetPathFromIDListW (pidl, path); if (b) { @@ -183,6 +188,10 @@ static const char *userpref_get_config_dir() use_dot_config = 0; #else +#ifdef __APPLE__ + base_config_dir = strdup("/var/db"); + use_dot_config = 0; +#else const char *cdir = getenv("XDG_CONFIG_HOME"); if (!cdir) { cdir = getenv("HOME"); @@ -199,12 +208,13 @@ static const char *userpref_get_config_dir() base_config_dir = strdup(cdir); use_dot_config = 0; } +#endif #endif if (use_dot_config) - __config_dir = string_concat(base_config_dir, DIR_SEP_S, ".config", DIR_SEP_S, LIBIMOBILEDEVICE_CONF_DIR, NULL); + __config_dir = string_concat(base_config_dir, DIR_SEP_S, ".config", DIR_SEP_S, USERPREF_CONFIG_DIR, NULL); else - __config_dir = string_concat(base_config_dir, DIR_SEP_S, LIBIMOBILEDEVICE_CONF_DIR, NULL); + __config_dir = string_concat(base_config_dir, DIR_SEP_S, USERPREF_CONFIG_DIR, NULL); if (__config_dir) { int i = strlen(__config_dir)-1; @@ -215,6 +225,8 @@ static const char *userpref_get_config_dir() free(base_config_dir); + debug_info("initialized config_dir to %s", __config_dir); + return __config_dir; } @@ -250,131 +262,6 @@ static int mkdir_with_parents(const char *dir, int mode) return res; } -static int config_write(const char *cfgfile, plist_t dict) -{ - if (!cfgfile || !dict || (plist_get_node_type(dict) != PLIST_DICT)) { - return -1; - } - int res = -1; - -#if 1 // old style config - plist_t hostid = plist_dict_get_item(dict, "HostID"); - if (hostid && (plist_get_node_type(hostid) == PLIST_STRING)) { - char *hostidstr = NULL; - plist_get_string_val(hostid, &hostidstr); - if (hostidstr) { - FILE *fd = fopen(cfgfile, "wb"); - if (fd) { - fprintf(fd, "\n[Global]\nHostID=%s\n", hostidstr); - fclose(fd); - res = 0; - } else { - debug_info("could not open '%s' for writing: %s", cfgfile, strerror(errno)); - } - free(hostidstr); - } - } -#endif -#if 0 - char *xml = NULL; - uint32_t length = 0; - - plist_to_xml(dict, &xml, &length); - if (!xml) { - return res; - } - - FILE *fd = fopen(cfgfile, "wb"); - if (!fd) { - free(xml); - return res; - } - - if (fwrite(xml, 1, length, fd) == length) { - res = 0; - } else { - fprintf(stderr, "%s: ERROR: failed to write configuration to '%s'\n", __func__, cfgfile); - } - fclose(fd); - - free(xml); -#endif - return res; -} - -static int config_read(const char *cfgfile, plist_t *dict) -{ - if (!cfgfile || !dict) { - return -1; - } - - int res = -1; - FILE *fd = fopen(cfgfile, "rb"); - if (!fd) { - debug_info("could not open '%s' for reading: %s", cfgfile, strerror(errno)); - return -1; - } - - fseek(fd, 0, SEEK_END); - unsigned long int size = ftell(fd); - fseek(fd, 0, SEEK_SET); - unsigned char *contents = NULL; - - contents = malloc(size); - if (fread(contents, 1, size, fd) != size) { - free(contents); - fclose(fd); - return -1; - } - plist_t plist = NULL; - - if (!memcmp(contents, "bplist00", 8)) { - plist_from_bin((const char*)contents, (uint32_t)size, &plist); - fclose(fd); - } else { - if (memchr(contents, '<', size)) { - plist_from_xml((const char*)contents, (uint32_t)size, &plist); - } - if (plist) { - fclose(fd); - } else { - // try parsing old format config file - char line[256]; - fseek(fd, 0, SEEK_SET); - while (fgets(line, 256, fd)) { - char *p = &line[0]; - size_t llen = strlen(p)-1; - while ((llen > 0) && ((p[llen] == '\n') || (p[llen] == '\r'))) { - p[llen] = '\0'; - llen--; - } - if (llen == 0) continue; - while ((p[0] == '\n') || (p[0] == '\r')) { - p++; - } - if (!strncmp(p, "HostID=", 7)) { - plist = plist_new_dict(); - plist_dict_insert_item(plist, "HostID", plist_new_string(p+7)); - break; - } - } - fclose(fd); -#if 0 - if (plist) { - // write new format config - config_write(cfgfile, plist); - } -#endif - } - } - free(contents); - if (plist) { - *dict = plist; - res = 0; - } - return res; -} - /** * Creates a freedesktop compatible configuration directory. */ @@ -398,12 +285,12 @@ static int get_rand(int min, int max) * * @return A null terminated string containing a valid HostID. */ -static char *userpref_generate_host_id() +static char *userpref_generate_host_id(int index) { /* HostID's are just UUID's, and UUID's are 36 characters long */ char *hostid = (char *) malloc(sizeof(char) * 37); const char *chars = "ABCDEF0123456789"; - srand(time(NULL)); + srand(time(NULL) - index); int i = 0; for (i = 0; i < 36; i++) { @@ -420,88 +307,193 @@ static char *userpref_generate_host_id() } /** - * Store HostID in config file. + * Generates a valid BUID for this system (which is actually a UUID). * - * @param host_id A null terminated string containing a valid HostID. + * @return A null terminated string containing a valid BUID. */ -static int userpref_set_host_id(const char *host_id) +static char *userpref_generate_system_buid() { - const char *config_path; - char *config_file; + return userpref_generate_host_id(1); +} - if (!host_id) +static int internal_set_value(const char *config_file, const char *key, plist_t value) +{ + if (!config_file) return 0; - /* Make sure config directory exists */ - userpref_create_config_dir(); - - config_path = userpref_get_config_dir(); - config_file = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_CONF_FILE)+1); - strcpy(config_file, config_path); - strcat(config_file, DIR_SEP_S); - strcat(config_file, LIBIMOBILEDEVICE_CONF_FILE); - - /* Now parse file to get the HostID */ + /* read file into plist */ plist_t config = NULL; - config_read(config_file, &config); + + plist_read_from_filename(&config, config_file); if (!config) { config = plist_new_dict(); - plist_dict_insert_item(config, "HostID", plist_new_string(host_id)); + plist_dict_insert_item(config, key, value); } else { - plist_t n = plist_dict_get_item(config, "HostID"); + plist_t n = plist_dict_get_item(config, key); if (n) { - plist_set_string_val(n, host_id); - } else { - plist_dict_insert_item(config, "HostID", plist_new_string(host_id)); + plist_dict_remove_item(config, key); } + plist_dict_insert_item(config, key, value); + remove(config_file); + } + + /* store in config file */ + char *value_string = NULL; + if (plist_get_node_type(value) == PLIST_STRING) { + plist_get_string_val(value, &value_string); + debug_info("setting key %s to %s in config_file %s", key, value_string, config_file); + } else { + debug_info("setting key %s in config_file %s", key, config_file); } - /* Store in config file */ - debug_info("setting hostID to %s", host_id); + plist_write_to_filename(config, config_file, PLIST_FORMAT_XML); - config_write(config_file, config); plist_free(config); - free(config_file); return 1; } -/** - * Reads the HostID from a previously generated configuration file. - * - * @note It is the responsibility of the calling function to free the returned host_id - * - * @return The string containing the HostID or NULL - */ -void userpref_get_host_id(char **host_id) +int userpref_set_value(const char *key, plist_t value) { - const char *config_path; - char *config_file; + const char *config_path = NULL; + char *config_file = NULL; + + /* Make sure config directory exists */ + userpref_create_config_dir(); config_path = userpref_get_config_dir(); - config_file = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_CONF_FILE)+1); - strcpy(config_file, config_path); - strcat(config_file, DIR_SEP_S); - strcat(config_file, LIBIMOBILEDEVICE_CONF_FILE); + config_file = string_concat(config_path, DIR_SEP_S, USERPREF_CONFIG_FILE, NULL); + + int result = internal_set_value(config_file, key, value); + + free(config_file); + + return result; +} + +int userpref_device_record_set_value(const char *udid, const char *key, plist_t value) +{ + const char *config_path = NULL; + char *config_file = NULL; + + /* Make sure config directory exists */ + userpref_create_config_dir(); - /* now parse file to get the HostID */ + config_path = userpref_get_config_dir(); + config_file = string_concat(config_path, DIR_SEP_S, udid, USERPREF_CONFIG_EXTENSION, NULL); + + int result = internal_set_value(config_file, key, value); + + free(config_file); + + return result; +} + +static int internal_get_value(const char* config_file, const char *key, plist_t *value) +{ + *value = NULL; + + /* now parse file to get the SystemBUID */ plist_t config = NULL; - if (config_read(config_file, &config) == 0) { - plist_t n_host_id = plist_dict_get_item(config, "HostID"); - if (n_host_id && (plist_get_node_type(n_host_id) == PLIST_STRING)) { - plist_get_string_val(n_host_id, host_id); + if (plist_read_from_filename(&config, config_file)) { + debug_info("reading key %s from config_file %s", key, config_file); + plist_t n = plist_dict_get_item(config, key); + if (n) { + *value = plist_copy(n); + plist_free(n); + n = NULL; } } plist_free(config); + + return 1; +} + +int userpref_get_value(const char *key, plist_t *value) +{ + const char *config_path = NULL; + char *config_file = NULL; + + config_path = userpref_get_config_dir(); + config_file = string_concat(config_path, DIR_SEP_S, USERPREF_CONFIG_FILE, NULL); + + int result = internal_get_value(config_file, key, value); + + free(config_file); + + return result; +} + +int userpref_device_record_get_value(const char *udid, const char *key, plist_t *value) +{ + const char *config_path = NULL; + char *config_file = NULL; + + config_path = userpref_get_config_dir(); + config_file = string_concat(config_path, DIR_SEP_S, udid, USERPREF_CONFIG_EXTENSION, NULL); + + int result = internal_get_value(config_file, key, value); + free(config_file); + return result; +} + +/** + * Store SystemBUID in config file. + * + * @param host_id A null terminated string containing a valid SystemBUID. + */ +static int userpref_set_system_buid(const char *system_buid) +{ + return userpref_set_value(USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid)); +} + +/** + * Reads the BUID from a previously generated configuration file. + * + * @note It is the responsibility of the calling function to free the returned system_buid + * + * @return The string containing the BUID or NULL + */ +void userpref_get_system_buid(char **system_buid) +{ + plist_t value = NULL; + + userpref_get_value(USERPREF_SYSTEM_BUID_KEY, &value); + + if (value && (plist_get_node_type(value) == PLIST_STRING)) { + plist_get_string_val(value, system_buid); + debug_info("got %s %s", USERPREF_SYSTEM_BUID_KEY, *system_buid); + } + + if (!*system_buid) { + /* no config, generate system_buid */ + debug_info("no previous %s found", USERPREF_SYSTEM_BUID_KEY); + *system_buid = userpref_generate_system_buid(); + userpref_set_system_buid(*system_buid); + } + + debug_info("using %s as %s", *system_buid, USERPREF_SYSTEM_BUID_KEY); +} + +void userpref_device_record_get_host_id(const char *udid, char **host_id) +{ + plist_t value = NULL; + + userpref_device_record_get_value(udid, USERPREF_HOST_ID_KEY, &value); + + if (value && (plist_get_node_type(value) == PLIST_STRING)) { + plist_get_string_val(value, host_id); + } + if (!*host_id) { /* no config, generate host_id */ - *host_id = userpref_generate_host_id(); - userpref_set_host_id(*host_id); + *host_id = userpref_generate_host_id(0); + userpref_device_record_set_value(udid, USERPREF_HOST_ID_KEY, plist_new_string(*host_id)); } - debug_info("Using %s as HostID", *host_id); + debug_info("using %s as %s", *host_id, USERPREF_HOST_ID_KEY); } /** @@ -512,26 +504,24 @@ void userpref_get_host_id(char **host_id) * @return 1 if the device has been connected previously to this configuration * or 0 otherwise. */ -int userpref_has_device_public_key(const char *udid) +int userpref_has_device_record(const char *udid) { int ret = 0; - const char *config_path; - char *config_file; + const char *config_path = NULL; + char *config_file = NULL; struct stat st; if (!udid) return 0; /* first get config file */ config_path = userpref_get_config_dir(); - config_file = (char*)malloc(strlen(config_path)+1+strlen(udid)+4+1); - strcpy(config_file, config_path); - strcat(config_file, DIR_SEP_S); - strcat(config_file, udid); - strcat(config_file, ".pem"); + config_file = string_concat(config_path, DIR_SEP_S, udid, USERPREF_CONFIG_EXTENSION, NULL); if ((stat(config_file, &st) == 0) && S_ISREG(st.st_mode)) ret = 1; + free(config_file); + return ret; } @@ -557,7 +547,7 @@ userpref_error_t userpref_get_paired_udids(char ***list, unsigned int *count) void *next; }; DIR *config_dir; - const char *config_path; + const char *config_path = NULL; struct slist_t *udids = NULL; unsigned int i; unsigned int found = 0; @@ -577,8 +567,8 @@ userpref_error_t userpref_get_paired_udids(char ***list, unsigned int *count) struct dirent *entry; struct slist_t *listp = udids; while ((entry = readdir(config_dir))) { - char *ext = strstr(entry->d_name, ".pem"); - if (ext && ((ext - entry->d_name) == 40) && (strlen(entry->d_name) == 44)) { + char *ext = strstr(entry->d_name, USERPREF_CONFIG_EXTENSION); + if (ext && ((ext - entry->d_name) == 40) && (strlen(entry->d_name) == (40 + strlen(ext)))) { struct slist_t *ne = (struct slist_t*)malloc(sizeof(struct slist_t)); ne->name = (char*)malloc(41); strncpy(ne->name, entry->d_name, 40); @@ -614,75 +604,77 @@ userpref_error_t userpref_get_paired_udids(char ***list, unsigned int *count) } /** - * Mark the device (as represented by the key) as having connected to this - * configuration. + * Mark the device as having connected to this configuration. * * @param udid The device UDID as given by the device - * @param public_key The public key given by the device + * @param device_record The device record with full configuration * - * @return 1 on success and 0 if no public key is given or if it has already + * @return 1 on success and 0 if no device record is given or if it has already * been marked as connected previously. */ -userpref_error_t userpref_set_device_public_key(const char *udid, key_data_t public_key) +userpref_error_t userpref_set_device_record(const char *udid, plist_t device_record) { - if (NULL == public_key.data) - return USERPREF_E_INVALID_ARG; - - if (userpref_has_device_public_key(udid)) - return USERPREF_E_SUCCESS; - /* ensure config directory exists */ userpref_create_config_dir(); /* build file path */ const char *config_path = userpref_get_config_dir(); - char *pem = (char*)malloc(strlen(config_path)+1+strlen(udid)+4+1); - strcpy(pem, config_path); - strcat(pem, DIR_SEP_S); - strcat(pem, udid); - strcat(pem, ".pem"); + char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, USERPREF_CONFIG_EXTENSION, NULL); + + remove(device_record_file); /* store file */ - FILE *pFile = fopen(pem, "wb"); - if (pFile) { - fwrite(public_key.data, 1, public_key.size, pFile); - fclose(pFile); - } else { - debug_info("could not open '%s' for writing: %s", pem, strerror(errno)); + if (!plist_write_to_filename(device_record, device_record_file, PLIST_FORMAT_XML)) { + debug_info("could not open '%s' for writing: %s", device_record_file, strerror(errno)); + } + free(device_record_file); + + return USERPREF_E_SUCCESS; +} + +userpref_error_t userpref_get_device_record(const char *udid, plist_t *device_record) +{ + /* ensure config directory exists */ + userpref_create_config_dir(); + + /* build file path */ + const char *config_path = userpref_get_config_dir(); + char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, USERPREF_CONFIG_EXTENSION, NULL); + + /* read file */ + if (!plist_read_from_filename(device_record, device_record_file)) { + debug_info("could not open '%s' for reading: %s", device_record_file, strerror(errno)); } - free(pem); + free(device_record_file); return USERPREF_E_SUCCESS; } /** - * Remove the public key stored for the device with udid from this host. + * Remove the pairing record stored for a device from this host. * * @param udid The udid of the device * * @return USERPREF_E_SUCCESS on success. */ -userpref_error_t userpref_remove_device_public_key(const char *udid) +userpref_error_t userpref_remove_device_record(const char *udid) { - if (!userpref_has_device_public_key(udid)) + if (!userpref_has_device_record(udid)) return USERPREF_E_SUCCESS; /* build file path */ const char *config_path = userpref_get_config_dir(); - char *pem = (char*)malloc(strlen(config_path)+1+strlen(udid)+4+1); - strcpy(pem, config_path); - strcat(pem, DIR_SEP_S); - strcat(pem, udid); - strcat(pem, ".pem"); + char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, USERPREF_CONFIG_EXTENSION, NULL); /* remove file */ - remove(pem); + remove(device_record_file); - free(pem); + free(device_record_file); return USERPREF_E_SUCCESS; } +#if 0 /** * Private function which reads the given file into a key_data_t structure. * @@ -696,7 +688,7 @@ static int userpref_get_file_contents(const char *file, key_data_t * data) int success = 0; unsigned long int size = 0; unsigned char *content = NULL; - const char *config_path; + const char *config_path = NULL; char *filepath; FILE *fd; @@ -705,10 +697,7 @@ static int userpref_get_file_contents(const char *file, key_data_t * data) /* Read file */ config_path = userpref_get_config_dir(); - filepath = (char*)malloc(strlen(config_path)+1+strlen(file)+1); - strcpy(filepath, config_path); - strcat(filepath, DIR_SEP_S); - strcat(filepath, file); + filepath = string_concat(config_path, DIR_SEP_S, file, NULL); fd = fopen(filepath, "rb"); if (fd) { @@ -746,13 +735,14 @@ static int userpref_get_file_contents(const char *file, key_data_t * data) return success; } +#endif /** * Private function which generate private keys and certificates. * * @return 1 if keys were successfully generated, 0 otherwise */ -static userpref_error_t userpref_gen_keys_and_cert(void) +static userpref_error_t userpref_device_record_gen_keys_and_cert(const char* udid) { userpref_error_t ret = USERPREF_E_SSL_ERROR; @@ -761,7 +751,7 @@ static userpref_error_t userpref_gen_keys_and_cert(void) key_data_t host_key_pem = { NULL, 0 }; key_data_t host_cert_pem = { NULL, 0 }; - debug_info("Generating keys and certificates"); + debug_info("generating keys and certificates"); #ifdef HAVE_OPENSSL RSA* root_keypair = RSA_generate_key(2048, 65537, NULL, NULL); RSA* host_keypair = RSA_generate_key(2048, 65537, NULL, NULL); @@ -880,7 +870,7 @@ static userpref_error_t userpref_gen_keys_and_cert(void) gnutls_global_deinit(); gnutls_global_init(); - //use less secure random to speed up key generation + /* use less secure random to speed up key generation */ gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM); gnutls_x509_privkey_init(&root_privkey); @@ -940,7 +930,7 @@ static userpref_error_t userpref_gen_keys_and_cert(void) gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, host_cert_pem.data, &host_cert_export_size); host_cert_pem.size = host_cert_export_size; - //restore gnutls env + /* restore gnutls env */ gnutls_global_deinit(); gnutls_global_init(); #endif @@ -949,7 +939,7 @@ static userpref_error_t userpref_gen_keys_and_cert(void) ret = USERPREF_E_SUCCESS; /* store values in config file */ - userpref_set_keys_and_certs( &root_key_pem, &root_cert_pem, &host_key_pem, &host_cert_pem); + userpref_device_record_set_keys_and_certs(udid, &root_key_pem, &root_cert_pem, &host_key_pem, &host_cert_pem); if (root_key_pem.data) free(root_key_pem.data); @@ -966,15 +956,15 @@ static userpref_error_t userpref_gen_keys_and_cert(void) /** * Private function which import the given key into a gnutls structure. * - * @param key_name The filename of the private key to import. + * @param name The name of the private key to import. * @param key the gnutls key structure. * * @return 1 if the key was successfully imported. */ #ifdef HAVE_OPENSSL -static userpref_error_t userpref_import_key(const char* key_name, key_data_t* key) +static userpref_error_t userpref_device_record_import_key(const char* udid, const char* name, key_data_t* key) #else -static userpref_error_t userpref_import_key(const char* key_name, gnutls_x509_privkey_t key) +static userpref_error_t userpref_device_record_import_key(const char* udid, const char* name, gnutls_x509_privkey_t key) #endif { #ifdef HAVE_OPENSSL @@ -982,37 +972,49 @@ static userpref_error_t userpref_import_key(const char* key_name, gnutls_x509_pr return USERPREF_E_SUCCESS; #endif userpref_error_t ret = USERPREF_E_INVALID_CONF; - key_data_t pem_key = { NULL, 0 }; - if (userpref_get_file_contents(key_name, &pem_key)) { + char* buffer = NULL; + uint64_t length = 0; + + plist_t crt = NULL; + if (userpref_device_record_get_value(udid, name, &crt)) { + if (crt && plist_get_node_type(crt) == PLIST_DATA) { + plist_get_data_val(crt, &buffer, &length); #ifdef HAVE_OPENSSL - key->data = (unsigned char*)malloc(pem_key.size); - memcpy(key->data, pem_key.data, pem_key.size); - key->size = pem_key.size; - ret = USERPREF_E_SUCCESS; -#else - if (GNUTLS_E_SUCCESS == gnutls_x509_privkey_import(key, &pem_key, GNUTLS_X509_FMT_PEM)) + key->data = (unsigned char*)malloc(length); + memcpy(key->data, buffer, length); + key->size = length; ret = USERPREF_E_SUCCESS; - else - ret = USERPREF_E_SSL_ERROR; +#else + key_data_t pem = { buffer, length }; + if (GNUTLS_E_SUCCESS == gnutls_x509_privkey_import(key, &pem, GNUTLS_X509_FMT_PEM)) + ret = USERPREF_E_SUCCESS; + else + ret = USERPREF_E_SSL_ERROR; #endif + } } - if (pem_key.data) - free(pem_key.data); + + if (crt) + plist_free(crt); + + if (buffer) + free(buffer); + return ret; } /** * Private function which import the given certificate into a gnutls structure. * - * @param crt_name The filename of the certificate to import. + * @param name The name of the certificate to import. * @param cert the gnutls certificate structure. * * @return IDEVICE_E_SUCCESS if the certificate was successfully imported. */ #ifdef HAVE_OPENSSL -static userpref_error_t userpref_import_crt(const char* crt_name, key_data_t* cert) +static userpref_error_t userpref_device_record_import_crt(const char* udid, const char* name, key_data_t* cert) #else -static userpref_error_t userpref_import_crt(const char* crt_name, gnutls_x509_crt_t cert) +static userpref_error_t userpref_device_record_import_crt(const char* udid, const char* name, gnutls_x509_crt_t cert) #endif { #ifdef HAVE_OPENSSL @@ -1020,23 +1022,34 @@ static userpref_error_t userpref_import_crt(const char* crt_name, gnutls_x509_cr return USERPREF_E_SUCCESS; #endif userpref_error_t ret = USERPREF_E_INVALID_CONF; - key_data_t pem_cert = { NULL, 0 }; + char* buffer = NULL; + uint64_t length = 0; - if (userpref_get_file_contents(crt_name, &pem_cert)) { + plist_t crt = NULL; + if (userpref_device_record_get_value(udid, name, &crt)) { + if (crt && plist_get_node_type(crt) == PLIST_DATA) { + plist_get_data_val(crt, &buffer, &length); #ifdef HAVE_OPENSSL - cert->data = (unsigned char*)malloc(pem_cert.size); - memcpy(cert->data, pem_cert.data, pem_cert.size); - cert->size = pem_cert.size; - ret = USERPREF_E_SUCCESS; -#else - if (GNUTLS_E_SUCCESS == gnutls_x509_crt_import(cert, &pem_cert, GNUTLS_X509_FMT_PEM)) + cert->data = (unsigned char*)malloc(length); + memcpy(cert->data, buffer, length); + cert->size = length; ret = USERPREF_E_SUCCESS; - else - ret = USERPREF_E_SSL_ERROR; +#else + key_data_t pem = { buffer, length }; + if (GNUTLS_E_SUCCESS == gnutls_x509_crt_import(cert, &pem, GNUTLS_X509_FMT_PEM)) + ret = USERPREF_E_SUCCESS; + else + ret = USERPREF_E_SSL_ERROR; #endif + } } - if (pem_cert.data) - free(pem_cert.data); + + if (crt) + plist_free(crt); + + if (buffer) + free(buffer); + return ret; } @@ -1054,41 +1067,40 @@ static userpref_error_t userpref_import_crt(const char* crt_name, gnutls_x509_cr * @return 1 if the keys and certificates were successfully retrieved, 0 otherwise */ #ifdef HAVE_OPENSSL -userpref_error_t userpref_get_keys_and_certs(key_data_t* root_privkey, key_data_t* root_crt, key_data_t* host_privkey, key_data_t* host_crt) +userpref_error_t userpref_device_record_get_keys_and_certs(const char *udid, key_data_t* root_privkey, key_data_t* root_crt, key_data_t* host_privkey, key_data_t* host_crt) #else -userpref_error_t userpref_get_keys_and_certs(gnutls_x509_privkey_t root_privkey, gnutls_x509_crt_t root_crt, gnutls_x509_privkey_t host_privkey, gnutls_x509_crt_t host_crt) +userpref_error_t userpref_device_record_get_keys_and_certs(const char *udid, gnutls_x509_privkey_t root_privkey, gnutls_x509_crt_t root_crt, gnutls_x509_privkey_t host_privkey, gnutls_x509_crt_t host_crt) #endif { userpref_error_t ret = USERPREF_E_SUCCESS; if (ret == USERPREF_E_SUCCESS) - ret = userpref_import_key(LIBIMOBILEDEVICE_ROOT_PRIVKEY, root_privkey); + ret = userpref_device_record_import_key(udid, USERPREF_ROOT_PRIVATE_KEY_KEY, root_privkey); if (ret == USERPREF_E_SUCCESS) - ret = userpref_import_key(LIBIMOBILEDEVICE_HOST_PRIVKEY, host_privkey); + ret = userpref_device_record_import_key(udid, USERPREF_HOST_PRIVATE_KEY_KEY, host_privkey); if (ret == USERPREF_E_SUCCESS) - ret = userpref_import_crt(LIBIMOBILEDEVICE_ROOT_CERTIF, root_crt); + ret = userpref_device_record_import_crt(udid, USERPREF_ROOT_CERTIFICATE_KEY, root_crt); if (ret == USERPREF_E_SUCCESS) - ret = userpref_import_crt(LIBIMOBILEDEVICE_HOST_CERTIF, host_crt); + ret = userpref_device_record_import_crt(udid, USERPREF_HOST_CERTIFICATE_KEY, host_crt); if (USERPREF_E_SUCCESS != ret) { - //we had problem reading or importing root cert - //try with a new ones. - ret = userpref_gen_keys_and_cert(); + /* we had problem reading or importing root cert, try with new ones */ + ret = userpref_device_record_gen_keys_and_cert(udid); if (ret == USERPREF_E_SUCCESS) - ret = userpref_import_key(LIBIMOBILEDEVICE_ROOT_PRIVKEY, root_privkey); + ret = userpref_device_record_import_key(udid, USERPREF_ROOT_PRIVATE_KEY_KEY, root_privkey); if (ret == USERPREF_E_SUCCESS) - ret = userpref_import_key(LIBIMOBILEDEVICE_HOST_PRIVKEY, host_privkey); + ret = userpref_device_record_import_key(udid, USERPREF_HOST_PRIVATE_KEY_KEY, host_privkey); if (ret == USERPREF_E_SUCCESS) - ret = userpref_import_crt(LIBIMOBILEDEVICE_ROOT_CERTIF, root_crt); + ret = userpref_device_record_import_crt(udid, USERPREF_ROOT_CERTIFICATE_KEY, root_crt); if (ret == USERPREF_E_SUCCESS) - ret = userpref_import_crt(LIBIMOBILEDEVICE_HOST_CERTIF, host_crt); + ret = userpref_device_record_import_crt(udid, USERPREF_ROOT_CERTIFICATE_KEY, host_crt); } return ret; @@ -1102,14 +1114,36 @@ userpref_error_t userpref_get_keys_and_certs(gnutls_x509_privkey_t root_privkey, * * @return 1 if the certificates were successfully retrieved, 0 otherwise */ -userpref_error_t userpref_get_certs_as_pem(key_data_t *pem_root_cert, key_data_t *pem_host_cert) +userpref_error_t userpref_device_record_get_certs_as_pem(const char *udid, key_data_t *pem_root_cert, key_data_t *pem_host_cert) { - if (!pem_root_cert || !pem_host_cert) + if (!udid || !pem_root_cert || !pem_host_cert) return USERPREF_E_INVALID_ARG; - if (userpref_get_file_contents(LIBIMOBILEDEVICE_ROOT_CERTIF, pem_root_cert) && userpref_get_file_contents(LIBIMOBILEDEVICE_HOST_CERTIF, pem_host_cert)) + char* buffer = NULL; + uint64_t length = 0; + plist_t root_cert = NULL; + plist_t host_cert = NULL; + if (userpref_device_record_get_value(udid, USERPREF_HOST_CERTIFICATE_KEY, &host_cert) && + userpref_device_record_get_value(udid, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert)) { + if (host_cert && plist_get_node_type(host_cert) == PLIST_DATA) { + plist_get_data_val(host_cert, &buffer, &length); + pem_host_cert->data = (unsigned char*)malloc(length); + memcpy(pem_host_cert->data, buffer, length); + pem_host_cert->size = length; + free(buffer); + buffer = NULL; + } + if (root_cert && plist_get_node_type(root_cert) == PLIST_DATA) { + plist_get_data_val(root_cert, &buffer, &length); + pem_root_cert->data = (unsigned char*)malloc(length); + memcpy(pem_root_cert->data, buffer, length); + pem_root_cert->size = length; + free(buffer); + buffer = NULL; + } + return USERPREF_E_SUCCESS; - else { + } else { if (pem_root_cert->data) { free(pem_root_cert->data); pem_root_cert->size = 0; @@ -1119,7 +1153,9 @@ userpref_error_t userpref_get_certs_as_pem(key_data_t *pem_root_cert, key_data_t pem_host_cert->size = 0; } } + debug_info("configuration invalid"); + return USERPREF_E_INVALID_CONF; } @@ -1135,81 +1171,27 @@ userpref_error_t userpref_get_certs_as_pem(key_data_t *pem_root_cert, key_data_t * * @return 1 on success and 0 otherwise. */ -userpref_error_t userpref_set_keys_and_certs(key_data_t * root_key, key_data_t * root_cert, key_data_t * host_key, key_data_t * host_cert) +userpref_error_t userpref_device_record_set_keys_and_certs(const char* udid, key_data_t * root_key, key_data_t * root_cert, key_data_t * host_key, key_data_t * host_cert) { - FILE *pFile; - char *pem; - const char *config_path; userpref_error_t ret = USERPREF_E_SUCCESS; - debug_info("saving keys and certs"); + debug_info("saving keys and certs for udid %s", udid); if (!root_key || !host_key || !root_cert || !host_cert) { debug_info("missing key or cert (root_key=%p, host_key=%p, root=cert=%p, host_cert=%p", root_key, host_key, root_cert, host_cert); return USERPREF_E_INVALID_ARG; } - /* Make sure config directory exists */ - userpref_create_config_dir(); - - config_path = userpref_get_config_dir(); - - /* Now write keys and certificates to disk */ - pem = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_ROOT_PRIVKEY)+1); - strcpy(pem, config_path); - strcat(pem, DIR_SEP_S); - strcat(pem, LIBIMOBILEDEVICE_ROOT_PRIVKEY); - pFile = fopen(pem, "wb"); - if (pFile) { - fwrite(root_key->data, 1, root_key->size, pFile); - fclose(pFile); - } else { - debug_info("could not open '%s' for writing: %s", pem, strerror(errno)); - ret = USERPREF_E_WRITE_ERROR; - } - free(pem); - - pem = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_HOST_PRIVKEY)+1); - strcpy(pem, config_path); - strcat(pem, DIR_SEP_S); - strcat(pem, LIBIMOBILEDEVICE_HOST_PRIVKEY); - pFile = fopen(pem, "wb"); - if (pFile) { - fwrite(host_key->data, 1, host_key->size, pFile); - fclose(pFile); - } else { - debug_info("could not open '%s' for writing: %s", pem, strerror(errno)); - ret = USERPREF_E_WRITE_ERROR; - } - free(pem); - - pem = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_ROOT_CERTIF)+1); - strcpy(pem, config_path); - strcat(pem, DIR_SEP_S); - strcat(pem, LIBIMOBILEDEVICE_ROOT_CERTIF); - pFile = fopen(pem, "wb"); - if (pFile) { - fwrite(root_cert->data, 1, root_cert->size, pFile); - fclose(pFile); - } else { - debug_info("could not open '%s' for writing: %s", pem, strerror(errno)); - ret = USERPREF_E_WRITE_ERROR; - } - free(pem); - - pem = (char*)malloc(strlen(config_path)+1+strlen(LIBIMOBILEDEVICE_HOST_CERTIF)+1); - strcpy(pem, config_path); - strcat(pem, DIR_SEP_S); - strcat(pem, LIBIMOBILEDEVICE_HOST_CERTIF); - pFile = fopen(pem, "wb"); - if (pFile) { - fwrite(host_cert->data, 1, host_cert->size, pFile); - fclose(pFile); + /* now write keys and certificates to disk */ + if (userpref_device_record_set_value(udid, USERPREF_HOST_PRIVATE_KEY_KEY, plist_new_data((char*)host_key->data, host_key->size)) && + userpref_device_record_set_value(udid, USERPREF_HOST_CERTIFICATE_KEY, plist_new_data((char*)host_cert->data, host_cert->size)) && + userpref_device_record_set_value(udid, USERPREF_ROOT_PRIVATE_KEY_KEY, plist_new_data((char*)root_key->data, root_key->size)) && + userpref_device_record_set_value(udid, USERPREF_ROOT_CERTIFICATE_KEY, plist_new_data((char*)root_cert->data, root_cert->size))) + { + ret = USERPREF_E_SUCCESS; } else { - debug_info("could not open '%s' for writing: %s", pem, strerror(errno)); - ret = USERPREF_E_WRITE_ERROR; + ret = 1; } - free(pem); return ret; } diff --git a/common/userpref.h b/common/userpref.h index 14db985..f59e5fe 100644 --- a/common/userpref.h +++ b/common/userpref.h @@ -37,6 +37,17 @@ typedef gnutls_datum_t key_data_t; #endif #include +#include + +#define USERPREF_DEVICE_CERTIFICATE_KEY "DeviceCertificate" +#define USERPREF_ESCROW_BAG_KEY "EscrowBag" +#define USERPREF_HOST_CERTIFICATE_KEY "HostCertificate" +#define USERPREF_ROOT_CERTIFICATE_KEY "RootCertificate" +#define USERPREF_HOST_PRIVATE_KEY_KEY "HostPrivateKey" +#define USERPREF_ROOT_PRIVATE_KEY_KEY "RootPrivateKey" +#define USERPREF_HOST_ID_KEY "HostID" +#define USERPREF_SYSTEM_BUID_KEY "SystemBUID" +#define USERPREF_WIFI_MAC_ADDRESS_KEY "WiFiMACAddress" #ifndef LIBIMOBILEDEVICE_INTERNAL #ifdef WIN32 @@ -58,16 +69,25 @@ typedef gnutls_datum_t key_data_t; typedef int16_t userpref_error_t; #ifdef HAVE_OPENSSL -LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_get_keys_and_certs(key_data_t* root_privkey, key_data_t* root_crt, key_data_t* host_privkey, key_data_t* host_crt); +LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_device_record_get_keys_and_certs(const char *udid, key_data_t* root_privkey, key_data_t* root_crt, key_data_t* host_privkey, key_data_t* host_crt); #else -LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_get_keys_and_certs(gnutls_x509_privkey_t root_privkey, gnutls_x509_crt_t root_crt, gnutls_x509_privkey_t host_privkey, gnutls_x509_crt_t host_crt); +LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_device_record_get_keys_and_certs(const char *udid, gnutls_x509_privkey_t root_privkey, gnutls_x509_crt_t root_crt, gnutls_x509_privkey_t host_privkey, gnutls_x509_crt_t host_crt); #endif -LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_set_keys_and_certs(key_data_t * root_key, key_data_t * root_cert, key_data_t * host_key, key_data_t * host_cert); -LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_get_certs_as_pem(key_data_t *pem_root_cert, key_data_t *pem_host_cert); -LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_set_device_public_key(const char *udid, key_data_t public_key); -userpref_error_t userpref_remove_device_public_key(const char *udid); -LIBIMOBILEDEVICE_INTERNAL int userpref_has_device_public_key(const char *udid); +LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_device_record_set_keys_and_certs(const char *udid, key_data_t * root_key, key_data_t * root_cert, key_data_t * host_key, key_data_t * host_cert); +LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_device_record_get_certs_as_pem(const char *udid, key_data_t *pem_root_cert, key_data_t *pem_host_cert); + +LIBIMOBILEDEVICE_INTERNAL userpref_error_t userpref_set_device_record(const char *udid, plist_t device_record); +userpref_error_t userpref_remove_device_record(const char *udid); +LIBIMOBILEDEVICE_INTERNAL int userpref_has_device_record(const char *udid); + userpref_error_t userpref_get_paired_udids(char ***list, unsigned int *count); -void userpref_get_host_id(char **host_id); +void userpref_device_record_get_host_id(const char *udid, char **host_id); +void userpref_get_system_buid(char **system_buid); + +userpref_error_t userpref_get_device_record(const char *udid, plist_t *device_record); +int userpref_get_value(const char *key, plist_t *value); +int userpref_set_value(const char *key, plist_t value); +int userpref_device_record_get_value(const char *udid, const char *key, plist_t *value); +int userpref_device_record_set_value(const char *udid, const char *key, plist_t value); #endif diff --git a/include/libimobiledevice/lockdown.h b/include/libimobiledevice/lockdown.h index 8d1b324..233c796 100644 --- a/include/libimobiledevice/lockdown.h +++ b/include/libimobiledevice/lockdown.h @@ -52,6 +52,8 @@ extern "C" { #define LOCKDOWN_E_INVALID_HOST_ID -16 #define LOCKDOWN_E_INVALID_SERVICE -17 #define LOCKDOWN_E_INVALID_ACTIVATION_RECORD -18 +#define LOCKDOWN_E_PAIRING_DIALOG_PENDING -20 +#define LOCKDOWN_E_USER_DENIED_PAIRING -21 #define LOCKDOWN_E_UNKNOWN_ERROR -256 /*@}*/ @@ -65,8 +67,9 @@ typedef lockdownd_client_private *lockdownd_client_t; /**< The client handle. */ struct lockdownd_pair_record { char *device_certificate; /**< The device certificate */ char *host_certificate; /**< The host certificate */ - char *host_id; /**< A unique HostID for the host computer */ char *root_certificate; /**< The root certificate */ + char *host_id; /**< A unique HostID for the host computer */ + char *system_buid; /**< A unique system id */ }; /** A pair record holding device, host and root certificates along the host_id */ typedef struct lockdownd_pair_record *lockdownd_pair_record_t; diff --git a/src/idevice.c b/src/idevice.c index c605da3..4a6f544 100644 --- a/src/idevice.c +++ b/src/idevice.c @@ -668,7 +668,7 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) key_data_t root_cert = { NULL, 0 }; key_data_t root_privkey = { NULL, 0 }; - userpref_error_t uerr = userpref_get_keys_and_certs(&root_privkey, &root_cert, NULL, NULL); + userpref_error_t uerr = userpref_device_record_get_keys_and_certs(connection->udid, &root_privkey, &root_cert, NULL, NULL); if (uerr != USERPREF_E_SUCCESS) { debug_info("Error %d when loading keys and certificates! %d", uerr); } diff --git a/src/lockdown.c b/src/lockdown.c index b07366b..7c516c1 100644 --- a/src/lockdown.c +++ b/src/lockdown.c @@ -30,6 +30,7 @@ #define __USE_GNU 1 #include #include +#include #ifdef HAVE_OPENSSL #include #include @@ -47,6 +48,11 @@ #include "common/userpref.h" #include "asprintf.h" +#ifdef WIN32 +#include +#define sleep(x) Sleep(x*1000) +#endif + #define RESULT_SUCCESS 0 #define RESULT_FAILURE 1 @@ -209,28 +215,18 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char * return ret; } -/** - * Closes the lockdownd client session if one is running and frees up the - * lockdownd_client struct. - * - * @param client The lockdown client - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL - */ -lockdownd_error_t lockdownd_client_free(lockdownd_client_t client) +static lockdownd_error_t lockdownd_client_free_simple(lockdownd_client_t client) { if (!client) return LOCKDOWN_E_INVALID_ARG; + lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; if (client->session_id) { - lockdownd_stop_session(client, client->session_id); free(client->session_id); } if (client->parent) { - lockdownd_goodbye(client); - if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { ret = LOCKDOWN_E_SUCCESS; } @@ -244,6 +240,36 @@ lockdownd_error_t lockdownd_client_free(lockdownd_client_t client) } free(client); + + return ret; +} + +/** + * Closes the lockdownd client session if one is running and frees up the + * lockdownd_client struct. + * + * @param client The lockdown client + * + * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL + */ +lockdownd_error_t lockdownd_client_free(lockdownd_client_t client) +{ + if (!client) + return LOCKDOWN_E_INVALID_ARG; + + lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; + + + if (client->session_id) { + lockdownd_stop_session(client, client->session_id); + } + + if (client->parent) { + lockdownd_goodbye(client); + } + + ret = lockdownd_client_free_simple(client); + return ret; } @@ -674,6 +700,33 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli return LOCKDOWN_E_SUCCESS; } +static lockdownd_error_t lockdownd_client_reconnect(idevice_t device, lockdownd_client_t *client, const char *label) +{ + lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; + int attempts = 3; + char *udid = NULL; + + /* free resources of lockownd_client */ + ret = lockdownd_client_free_simple(*client); + *client = NULL; + + /* try to reconnect */ + do { + debug_info("reconnecting to udid %s, %d remaining attempts", udid, attempts); + ret = lockdownd_client_new(device, client, label); + if (ret == LOCKDOWN_E_SUCCESS) { + debug_info("reconnected to lockdownd with err %d", ret); + break; + } + sleep(1); + } while(attempts--); + + free(udid); + udid = NULL; + + return ret; +} + /** * Creates a new lockdownd client for the device and starts initial handshake. * The handshake consists out of query_type, validate_pair, pair and @@ -698,8 +751,10 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; lockdownd_client_t client_loc = NULL; + char *system_buid = NULL; char *host_id = NULL; char *type = NULL; + int product_version_major = 0; ret = lockdownd_client_new(device, &client_loc, label); if (LOCKDOWN_E_SUCCESS != ret) { @@ -719,22 +774,55 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown free(type); } - userpref_get_host_id(&host_id); + /* get product version */ + plist_t product_version_node = NULL; + char* product_version = NULL; + lockdownd_get_value(client_loc, NULL, "ProductVersion", &product_version_node); + if (product_version_node && plist_get_node_type(product_version_node) == PLIST_STRING) { + plist_get_string_val(product_version_node, &product_version); + product_version_major = strtol(product_version, NULL, 10); + } + + if (product_version_major >= 7) { + userpref_get_system_buid(&system_buid); + + /* set our BUID for the trust dialog so the next pairing can succeed */ + lockdownd_set_value(client_loc, NULL, "UntrustedHostBUID", plist_new_string(system_buid)); + free(system_buid); + system_buid = NULL; + } + + userpref_device_record_get_host_id(client_loc->udid, &host_id); if (LOCKDOWN_E_SUCCESS == ret && !host_id) { ret = LOCKDOWN_E_INVALID_CONF; } - if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->udid)) + if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_record(client_loc->udid)) { + /* attempt pairing */ ret = lockdownd_pair(client_loc, NULL); + if (ret == LOCKDOWN_E_SUCCESS && product_version_major >= 7) { + /* the trust dialog was dissmissed, thus the device will reconnect after pairing */ + lockdownd_client_reconnect(device, &client_loc, label); + } + } + /* in any case, we need to validate pairing to receive trusted host status */ ret = lockdownd_validate_pair(client_loc, NULL); /* if not paired yet, let's do it now */ if (LOCKDOWN_E_INVALID_HOST_ID == ret) { ret = lockdownd_pair(client_loc, NULL); + + if (ret == LOCKDOWN_E_SUCCESS && product_version_major >= 7) { + /* the trust dialog was dissmissed, thus the device will reconnect after pairing */ + lockdownd_client_reconnect(device, &client_loc, label); + } + if (LOCKDOWN_E_SUCCESS == ret) { ret = lockdownd_validate_pair(client_loc, NULL); + } else if (LOCKDOWN_E_PAIRING_DIALOG_PENDING == ret) { + debug_info("Device shows the pairing dialog."); } } @@ -772,19 +860,13 @@ static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_recor if (!pair_record) return NULL; - char *host_id_loc = pair_record->host_id; - /* setup request plist */ plist_t dict = plist_new_dict(); plist_dict_insert_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate))); plist_dict_insert_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate))); - if (!pair_record->host_id) - userpref_get_host_id(&host_id_loc); - plist_dict_insert_item(dict, "HostID", plist_new_string(host_id_loc)); + plist_dict_insert_item(dict, "HostID", plist_new_string(pair_record->host_id)); plist_dict_insert_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate))); - - if (!pair_record->host_id) - free(host_id_loc); + plist_dict_insert_item(dict, "SystemBUID", plist_new_string(pair_record->system_buid)); return dict; } @@ -800,7 +882,7 @@ static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_recor * * @return LOCKDOWN_E_SUCCESS on success */ -static lockdownd_error_t generate_pair_record_plist(key_data_t public_key, char *host_id, plist_t *pair_record_plist) +static lockdownd_error_t generate_pair_record_plist(const char *udid, char* system_buid, char *host_id, key_data_t public_key, plist_t *pair_record_plist) { lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; @@ -808,25 +890,18 @@ static lockdownd_error_t generate_pair_record_plist(key_data_t public_key, char key_data_t host_cert = { NULL, 0 }; key_data_t root_cert = { NULL, 0 }; - ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert); + ret = lockdownd_gen_pair_cert_for_udid(udid, public_key, &device_cert, &host_cert, &root_cert); if (ret != LOCKDOWN_E_SUCCESS) { return ret; } - char *host_id_loc = host_id; - - if (!host_id) - userpref_get_host_id(&host_id_loc); - /* setup request plist */ *pair_record_plist = plist_new_dict(); plist_dict_insert_item(*pair_record_plist, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size)); plist_dict_insert_item(*pair_record_plist, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size)); - plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id_loc)); + plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id)); plist_dict_insert_item(*pair_record_plist, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size)); - - if (!host_id) - free(host_id_loc); + plist_dict_insert_item(*pair_record_plist, "SystemBUID", plist_new_string(system_buid)); if (device_cert.data) free(device_cert.data); @@ -863,8 +938,10 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ plist_t dict_record = NULL; key_data_t public_key = { NULL, 0 }; int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ + char* host_id = NULL; + char* system_buid = NULL; - if (pair_record && pair_record->host_id) { + if (pair_record && pair_record->system_buid && pair_record->host_id) { /* valid pair_record passed? */ if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) { return LOCKDOWN_E_PLIST_ERROR; @@ -883,34 +960,73 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ return ret; } debug_info("device public key follows:\n%.*s", public_key.size, public_key.data); + /* get libimobiledevice pair_record */ - ret = generate_pair_record_plist(public_key, NULL, &dict_record); + userpref_get_system_buid(&system_buid); + userpref_device_record_get_host_id(client->udid, &host_id); + userpref_device_record_set_value(client->udid, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid)); + + ret = generate_pair_record_plist(client->udid, system_buid, host_id, public_key, &dict_record); + + if (host_id) + free(host_id); + if (system_buid) + free(system_buid); + if (ret != LOCKDOWN_E_SUCCESS) { + if (public_key.data) + free(public_key.data); if (dict_record) plist_free(dict_record); return ret; } } - /* Setup Pair request plist */ + if (!strcmp("Pair", verb)) { + /* get wifi mac */ + plist_t wifi_mac_node = NULL; + lockdownd_get_value(client, NULL, "WiFiAddress", &wifi_mac_node); + + /* save wifi mac address in config */ + if (wifi_mac_node) { + userpref_device_record_set_value(client->udid, USERPREF_WIFI_MAC_ADDRESS_KEY, plist_copy(wifi_mac_node)); + plist_free(wifi_mac_node); + wifi_mac_node = NULL; + } + } + + /* setup pair request plist */ dict = plist_new_dict(); plist_dict_add_label(dict, client->label); - plist_dict_insert_item(dict,"PairRecord", dict_record); + plist_dict_insert_item(dict, "PairRecord", plist_copy(dict_record)); plist_dict_insert_item(dict, "Request", plist_new_string(verb)); + plist_dict_insert_item(dict, "ProtocolVersion", plist_new_string(LOCKDOWN_PROTOCOL_VERSION)); + + plist_t options = plist_new_dict(); + plist_dict_insert_item(options, "ExtendedPairingErrors", plist_new_bool(1)); + plist_dict_insert_item(dict, "PairingOptions", options); /* send to device */ ret = lockdownd_send(client, dict); plist_free(dict); dict = NULL; - if (ret != LOCKDOWN_E_SUCCESS) + if (ret != LOCKDOWN_E_SUCCESS) { + if (public_key.data) + free(public_key.data); + plist_free(dict_record); return ret; + } /* Now get device's answer */ ret = lockdownd_receive(client, &dict); - if (ret != LOCKDOWN_E_SUCCESS) + if (ret != LOCKDOWN_E_SUCCESS) { + if (public_key.data) + free(public_key.data); + plist_free(dict_record); return ret; + } if (strcmp(verb, "Unpair") == 0) { /* workaround for Unpair giving back ValidatePair, @@ -928,13 +1044,25 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ if (ret == LOCKDOWN_E_SUCCESS) { debug_info("%s success", verb); if (!pairing_mode) { + debug_info("internal pairing mode"); if (!strcmp("Unpair", verb)) { /* remove public key from config */ - userpref_remove_device_public_key(client->udid); + userpref_remove_device_record(client->udid); } else { - /* store public key in config */ - userpref_set_device_public_key(client->udid, public_key); + if (!strcmp("Pair", verb)) { + debug_info("getting EscrowBag from response"); + + /* add returned escrow bag if available */ + plist_t escrow_bag = plist_dict_get_item(dict, "EscrowBag"); + if (escrow_bag && plist_get_node_type(escrow_bag) == PLIST_DATA) { + userpref_device_record_set_value(client->udid, USERPREF_ESCROW_BAG_KEY, plist_copy(escrow_bag)); + plist_free(escrow_bag); + escrow_bag = NULL; + } + } } + } else { + debug_info("external pairing mode"); } } else { debug_info("%s failure", verb); @@ -950,6 +1078,10 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ ret = LOCKDOWN_E_PASSWORD_PROTECTED; } else if (!strcmp(value, "InvalidHostID")) { ret = LOCKDOWN_E_INVALID_HOST_ID; + } else if (!strcmp(value, "UserDeniedPairing")) { + ret = LOCKDOWN_E_USER_DENIED_PAIRING; + } else if (!strcmp(value, "PairingDialogResponsePending")) { + ret = LOCKDOWN_E_PAIRING_DIALOG_PENDING; } free(value); } @@ -958,10 +1090,18 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ error_node = NULL; } } + + if (dict_record) { + plist_free(dict_record); + dict_record = NULL; + } + plist_free(dict); dict = NULL; + if (public_key.data) free(public_key.data); + return ret; } @@ -1115,11 +1255,12 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) * system failed, LOCKDOWN_E_SSL_ERROR if the certificates could not be * generated */ -lockdownd_error_t lockdownd_gen_pair_cert(key_data_t public_key, key_data_t * odevice_cert, +lockdownd_error_t lockdownd_gen_pair_cert_for_udid(const char *udid, key_data_t public_key, key_data_t * odevice_cert, key_data_t * ohost_cert, key_data_t * oroot_cert) { if (!public_key.data || !odevice_cert || !ohost_cert || !oroot_cert) return LOCKDOWN_E_INVALID_ARG; + lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; userpref_error_t uret = USERPREF_E_UNKNOWN_ERROR; @@ -1148,7 +1289,7 @@ lockdownd_error_t lockdownd_gen_pair_cert(key_data_t public_key, key_data_t * od host_privkey.data = NULL; host_privkey.size = 0; - uret = userpref_get_keys_and_certs(&root_privkey, &root_cert, &host_privkey, &host_cert); + uret = userpref_device_record_get_keys_and_certs(udid, &root_privkey, &root_cert, &host_privkey, &host_cert); if (USERPREF_E_SUCCESS == uret) { /* generate device certificate */ ASN1_INTEGER* sn = ASN1_INTEGER_new(); @@ -1209,7 +1350,7 @@ lockdownd_error_t lockdownd_gen_pair_cert(key_data_t public_key, key_data_t * od key_data_t pem_root_cert = { NULL, 0 }; key_data_t pem_host_cert = { NULL, 0 }; - uret = userpref_get_certs_as_pem(&pem_root_cert, &pem_host_cert); + uret = userpref_device_record_get_certs_as_pem(udid, &pem_root_cert, &pem_host_cert); if (USERPREF_E_SUCCESS == uret) { /* copy buffer for output */ membp = BIO_new(BIO_s_mem()); @@ -1395,6 +1536,10 @@ lockdownd_error_t lockdownd_gen_pair_cert(key_data_t public_key, key_data_t * od gnutls_free(der_pub_key.data); #endif + /* save device cert in config */ + if (odevice_cert->size) { + userpref_device_record_set_value(udid, USERPREF_DEVICE_CERTIFICATE_KEY, plist_new_data((char*)odevice_cert->data, (uint64_t)odevice_cert->size)); + } return ret; } @@ -1429,9 +1574,24 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char /* setup request plist */ dict = plist_new_dict(); plist_dict_add_label(dict, client->label); - plist_dict_insert_item(dict,"HostID", plist_new_string(host_id)); plist_dict_insert_item(dict,"Request", plist_new_string("StartSession")); + /* add host id */ + if (host_id) { + plist_dict_insert_item(dict, "HostID", plist_new_string(host_id)); + } + + /* add system buid */ + char *system_buid = NULL; + userpref_get_system_buid(&system_buid); + if (system_buid) { + plist_dict_insert_item(dict, "SystemBUID", plist_new_string(system_buid)); + if (system_buid) { + free(system_buid); + system_buid = NULL; + } + } + ret = lockdownd_send(client, dict); plist_free(dict); dict = NULL; @@ -1471,6 +1631,7 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) { plist_get_string_val(session_node, &client->session_id); } + if (client->session_id) { debug_info("SessionID: %s", client->session_id); if (session_id != NULL) @@ -1478,7 +1639,9 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char } else { debug_info("Failed to get SessionID!"); } + debug_info("Enable SSL Session: %s", (use_ssl?"true":"false")); + if (use_ssl) { ret = property_list_service_enable_ssl(client->parent); if (ret == PROPERTY_LIST_SERVICE_E_SUCCESS) { @@ -1523,7 +1686,7 @@ lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char } char *host_id = NULL; - userpref_get_host_id(&host_id); + userpref_device_record_get_host_id(client->udid, &host_id); if (!host_id) return LOCKDOWN_E_INVALID_CONF; if (!client->session_id) diff --git a/src/lockdown.h b/src/lockdown.h index 30ebc15..9c2be44 100644 --- a/src/lockdown.h +++ b/src/lockdown.h @@ -27,6 +27,8 @@ #include "libimobiledevice/lockdown.h" #include "property_list_service.h" +#define LOCKDOWN_PROTOCOL_VERSION "2" + struct lockdownd_client_private { property_list_service_client_t parent; int ssl_enabled; @@ -36,6 +38,6 @@ struct lockdownd_client_private { }; lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, key_data_t * public_key); -lockdownd_error_t lockdownd_gen_pair_cert(key_data_t public_key, key_data_t * device_cert, key_data_t * host_cert, key_data_t * root_cert); +lockdownd_error_t lockdownd_gen_pair_cert_for_udid(const char *udid, key_data_t public_key, key_data_t * device_cert, key_data_t * host_cert, key_data_t * root_cert); #endif -- cgit v1.1-32-gdbae