diff options
Diffstat (limited to 'src/lockdown.c')
-rw-r--r-- | src/lockdown.c | 1440 |
1 files changed, 710 insertions, 730 deletions
diff --git a/src/lockdown.c b/src/lockdown.c index 935f24e..256bff0 100644 --- a/src/lockdown.c +++ b/src/lockdown.c @@ -2,6 +2,8 @@ * lockdown.c * com.apple.mobile.lockdownd service implementation. * + * Copyright (c) 2009-2015 Martin Szulecki All Rights Reserved. + * Copyright (c) 2014-2015 Nikias Bassen All Rights Reserved. * Copyright (c) 2010 Bryan Forbes All Rights Reserved. * Copyright (c) 2008 Zach C. All Rights Reserved. * @@ -20,36 +22,125 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include <string.h> #include <stdlib.h> #define _GNU_SOURCE 1 #define __USE_GNU 1 #include <stdio.h> #include <ctype.h> -#include <glib.h> -#include <libtasn1.h> -#include <gnutls/x509.h> +#include <unistd.h> #include <plist/plist.h> +#include <libimobiledevice-glue/utils.h> #include "property_list_service.h" #include "lockdown.h" #include "idevice.h" -#include "debug.h" -#include "userpref.h" - -#define RESULT_SUCCESS 0 -#define RESULT_FAILURE 1 - -const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { - {"PKCS1", 536872976, 0}, - {0, 1073741836, 0}, - {"RSAPublicKey", 536870917, 0}, - {"modulus", 1073741827, 0}, - {"publicExponent", 3, 0}, - {0, 0, 0} +#include "common/debug.h" +#include "common/userpref.h" +#include "asprintf.h" + +#ifdef WIN32 +#include <windows.h> +#define sleep(x) Sleep(x*1000) +#endif + +struct st_lockdownd_error_str_map { + const char *lockdown_errstr; + const char *errstr; + lockdownd_error_t errcode; +}; + +static struct st_lockdownd_error_str_map lockdownd_error_str_map[] = { + { "InvalidResponse", "Invalid response", LOCKDOWN_E_INVALID_RESPONSE }, + { "MissingKey", "Missing key", LOCKDOWN_E_MISSING_KEY }, + { "MissingValue", "Missing value", LOCKDOWN_E_MISSING_VALUE }, + { "GetProhibited", "Get value prohibited", LOCKDOWN_E_GET_PROHIBITED }, + { "SetProhibited", "Set value prohibited", LOCKDOWN_E_SET_PROHIBITED }, + { "RemoveProhibited", "Remove value prohibited", LOCKDOWN_E_REMOVE_PROHIBITED }, + { "ImmutableValue", "Immutable value", LOCKDOWN_E_IMMUTABLE_VALUE }, + { "PasswordProtected", "Password protected", LOCKDOWN_E_PASSWORD_PROTECTED }, + { "UserDeniedPairing", "User denied pairing", LOCKDOWN_E_USER_DENIED_PAIRING }, + { "PairingDialogResponsePending", "Pairing dialog response pending", LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING }, + { "MissingHostID", "Missing HostID", LOCKDOWN_E_MISSING_HOST_ID }, + { "InvalidHostID", "Invalid HostID", LOCKDOWN_E_INVALID_HOST_ID }, + { "SessionActive", "Session active", LOCKDOWN_E_SESSION_ACTIVE }, + { "SessionInactive", "Session inactive", LOCKDOWN_E_SESSION_INACTIVE }, + { "MissingSessionID", "Missing session ID", LOCKDOWN_E_MISSING_SESSION_ID }, + { "InvalidSessionID", "Invalid session ID", LOCKDOWN_E_INVALID_SESSION_ID }, + { "MissingService", "Missing service", LOCKDOWN_E_MISSING_SERVICE }, + { "InvalidService", "Invalid service", LOCKDOWN_E_INVALID_SERVICE }, + { "ServiceLimit", "Service limit reached", LOCKDOWN_E_SERVICE_LIMIT }, + { "MissingPairRecord", "Missing pair record", LOCKDOWN_E_MISSING_PAIR_RECORD }, + { "SavePairRecordFailed", "Saving pair record failed", LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED }, + { "InvalidPairRecord", "Invalid pair record", LOCKDOWN_E_INVALID_PAIR_RECORD }, + { "InvalidActivationRecord", "Invalid activation record", LOCKDOWN_E_INVALID_ACTIVATION_RECORD }, + { "MissingActivationRecord", "Missing activation record", LOCKDOWN_E_MISSING_ACTIVATION_RECORD }, + { "ServiceProhibited", "Service prohibited", LOCKDOWN_E_SERVICE_PROHIBITED }, + { "EscrowLocked", "Escrow lockded", LOCKDOWN_E_ESCROW_LOCKED }, + { "PairingProhibitedOverThisConnection", "Pairing prohibited over this connection", LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION }, + { "FMiPProtected", "Find My iPhone/iPod/iPad protected", LOCKDOWN_E_FMIP_PROTECTED }, + { "MCProtected", "MC protected" , LOCKDOWN_E_MC_PROTECTED }, + { "MCChallengeRequired", "MC challenge required", LOCKDOWN_E_MC_CHALLENGE_REQUIRED }, + { NULL, NULL, 0 } }; /** + * Convert an error string identifier to a lockdownd_error_t value. + * Used internally to get correct error codes from a response. + * + * @param name The error name to convert. + * + * @return A matching lockdownd_error_t error code, + * LOCKDOWN_E_UNKNOWN_ERROR otherwise. + */ +static lockdownd_error_t lockdownd_strtoerr(const char* name) +{ + lockdownd_error_t err = LOCKDOWN_E_UNKNOWN_ERROR; + int i = 0; + while (lockdownd_error_str_map[i].lockdown_errstr) { + if (strcmp(lockdownd_error_str_map[i].lockdown_errstr, name) == 0) { + return lockdownd_error_str_map[i].errcode; + } + i++; + } + return err; +} + +/** + * Convert a property_list_service_error_t value to a lockdownd_error_t + * value. Used internally to get correct error codes. + * + * @param err A property_list_service_error_t error code + * + * @return A matching lockdownd_error_t error code, + * LOCKDOWND_E_UNKNOWN_ERROR otherwise. + */ +static lockdownd_error_t lockdownd_error(property_list_service_error_t err) +{ + switch (err) { + case PROPERTY_LIST_SERVICE_E_SUCCESS: + return LOCKDOWN_E_SUCCESS; + case PROPERTY_LIST_SERVICE_E_INVALID_ARG: + return LOCKDOWN_E_INVALID_ARG; + case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: + return LOCKDOWN_E_PLIST_ERROR; + case PROPERTY_LIST_SERVICE_E_MUX_ERROR: + return LOCKDOWN_E_MUX_ERROR; + case PROPERTY_LIST_SERVICE_E_SSL_ERROR: + return LOCKDOWN_E_SSL_ERROR; + case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: + return LOCKDOWN_E_RECEIVE_TIMEOUT; + default: + break; + } + return LOCKDOWN_E_UNKNOWN_ERROR; +} + +/** * Internally used function for checking the result from lockdown's answer * plist to a previously sent request. * @@ -57,58 +148,66 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { * @param query_match Name of the request to match or NULL if no match is * required. * - * @return RESULT_SUCCESS when the result is 'Success', - * RESULT_FAILURE when the result is 'Failure', - * or a negative value if an error occured during evaluation. + * @return LOCKDOWN_E_SUCCESS when the result is 'Success', + * LOCKDOWN_E_UNKNOWN_ERROR when the result is 'Failure', + * or a specific error code if derieved from the result. */ -static int lockdown_check_result(plist_t dict, const char *query_match) +lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match) { - int ret = -1; + lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; plist_t query_node = plist_dict_get_item(dict, "Request"); if (!query_node) { return ret; } + if (plist_get_node_type(query_node) != PLIST_STRING) { return ret; - } else { - char *query_value = NULL; - plist_get_string_val(query_node, &query_value); - if (!query_value) { - return ret; - } - if (query_match && (strcmp(query_value, query_match) != 0)) { - free(query_value); - return ret; - } - free(query_value); } - plist_t result_node = plist_dict_get_item(dict, "Result"); - if (!result_node) { + const char *query_value = plist_get_string_ptr(query_node, NULL); + if (!query_value) { return ret; } - plist_type result_type = plist_get_node_type(result_node); - - if (result_type == PLIST_STRING) { - - char *result_value = NULL; + if (query_match && (strcmp(query_value, query_match) != 0)) { + return ret; + } - plist_get_string_val(result_node, &result_value); + /* Check for 'Error' in reply */ + plist_t err_node = plist_dict_get_item(dict, "Error"); + if (err_node) { + if (plist_get_node_type(err_node) == PLIST_STRING) { + const char *err_value = plist_get_string_ptr(err_node, NULL); + if (err_value) { + debug_info("ERROR: %s", err_value); + ret = lockdownd_strtoerr(err_value); + } else { + debug_info("ERROR: unknown error occurred"); + } + } + return ret; + } + plist_t result_node = plist_dict_get_item(dict, "Result"); + if (!result_node) { + /* With iOS 5+ 'Result' is not present anymore. + If there is no 'Error', we can just assume success. */ + return LOCKDOWN_E_SUCCESS; + } + if (plist_get_node_type(result_node) == PLIST_STRING) { + const char *result_value = plist_get_string_ptr(result_node, NULL); if (result_value) { if (!strcmp(result_value, "Success")) { - ret = RESULT_SUCCESS; + ret = LOCKDOWN_E_SUCCESS; } else if (!strcmp(result_value, "Failure")) { - ret = RESULT_FAILURE; + ret = LOCKDOWN_E_UNKNOWN_ERROR; } else { debug_info("ERROR: unknown result value '%s'", result_value); } } - if (result_value) - free(result_value); } + return ret; } @@ -123,20 +222,10 @@ static void plist_dict_add_label(plist_t plist, const char *label) { if (plist && label) { if (plist_get_node_type(plist) == PLIST_DICT) - plist_dict_insert_item(plist, "Label", plist_new_string(label)); + plist_dict_set_item(plist, "Label", plist_new_string(label)); } } -/** - * Closes the lockdownd session by sending the StopSession request. - * - * @see lockdownd_start_session - * - * @param client The lockdown client - * @param session_id The id of a running session - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL - */ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id) { if (!client) @@ -151,8 +240,8 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char * plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); - plist_dict_insert_item(dict,"Request", plist_new_string("StopSession")); - plist_dict_insert_item(dict,"SessionID", plist_new_string(session_id)); + plist_dict_set_item(dict,"Request", plist_new_string("StopSession")); + plist_dict_set_item(dict,"SessionID", plist_new_string(session_id)); debug_info("stopping session %s", session_id); @@ -168,64 +257,74 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char * return LOCKDOWN_E_PLIST_ERROR; } - ret = LOCKDOWN_E_UNKNOWN_ERROR; - if (lockdown_check_result(dict, "StopSession") == RESULT_SUCCESS) { + ret = lockdown_check_result(dict, "StopSession"); + if (ret == LOCKDOWN_E_SUCCESS) { debug_info("success"); - ret = LOCKDOWN_E_SUCCESS; } + plist_free(dict); dict = NULL; + + if (client->session_id) { + free(client->session_id); + client->session_id = NULL; + } + if (client->ssl_enabled) { property_list_service_disable_ssl(client->parent); + client->ssl_enabled = 0; } + 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); - } + lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; if (client->parent) { - lockdownd_goodbye(client); - if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { ret = LOCKDOWN_E_SUCCESS; } } - if (client->uuid) { - free(client->uuid); + if (client->session_id) { + free(client->session_id); + client->session_id = NULL; } if (client->label) { free(client->label); } + if (client->cu_key) { + free(client->cu_key); + client->cu_key = NULL; + } free(client); + client = NULL; + + return ret; +} + +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); + } + + ret = lockdownd_client_free_simple(client); + return ret; } -/** - * Sets the label to send for requests to lockdownd. - * - * @param client The lockdown client - * @param label The label to set or NULL to disable sending a label - * - */ void lockdownd_client_set_label(lockdownd_client_t client, const char *label) { if (client) { @@ -236,69 +335,22 @@ void lockdownd_client_set_label(lockdownd_client_t client, const char *label) } } -/** - * Receives a plist from lockdownd. - * - * @param client The lockdownd client - * @param plist The plist to store the received data - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or - * plist is NULL - */ lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist) { if (!client || !plist || (plist && *plist)) return LOCKDOWN_E_INVALID_ARG; - lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; - property_list_service_error_t err; - - err = property_list_service_receive_plist(client->parent, plist); - if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) { - ret = LOCKDOWN_E_UNKNOWN_ERROR; - } - - if (!*plist) - ret = LOCKDOWN_E_PLIST_ERROR; - return ret; + return lockdownd_error(property_list_service_receive_plist(client->parent, plist)); } -/** - * Sends a plist to lockdownd. - * - * @note This function is low-level and should only be used if you need to send - * a new type of message. - * - * @param client The lockdownd client - * @param plist The plist to send - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or - * plist is NULL - */ lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist) { if (!client || !plist) return LOCKDOWN_E_INVALID_ARG; - lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; - idevice_error_t err; - - err = property_list_service_send_xml_plist(client->parent, plist); - if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) { - ret = LOCKDOWN_E_UNKNOWN_ERROR; - } - return ret; + return lockdownd_error(property_list_service_send_xml_plist(client->parent, plist)); } -/** - * Query the type of the service daemon. Depending on whether the device is - * queried in normal mode or restore mode, different types will be returned. - * - * @param client The lockdownd client - * @param type The type returned by the service daemon. Pass NULL to ignore. - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL - */ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) { if (!client) @@ -308,7 +360,7 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); - plist_dict_insert_item(dict,"Request", plist_new_string("QueryType")); + plist_dict_set_item(dict,"Request", plist_new_string("QueryType")); debug_info("called"); ret = lockdownd_send(client, dict); @@ -322,14 +374,21 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) return ret; ret = LOCKDOWN_E_UNKNOWN_ERROR; - if (lockdown_check_result(dict, "QueryType") == RESULT_SUCCESS) { + plist_t type_node = plist_dict_get_item(dict, "Type"); + if (type_node && (plist_get_node_type(type_node) == PLIST_STRING)) { + char* typestr = NULL; + plist_get_string_val(type_node, &typestr); + debug_info("success with type %s", typestr); /* return the type if requested */ if (type != NULL) { - plist_t type_node = plist_dict_get_item(dict, "Type"); - plist_get_string_val(type_node, type); + *type = typestr; + } else { + free(typestr); } - debug_info("success with type %s", *type); ret = LOCKDOWN_E_SUCCESS; + } else { + debug_info("hmm. QueryType response does not contain a type?!"); + debug_plist(dict); } plist_free(dict); dict = NULL; @@ -337,16 +396,6 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) return ret; } -/** - * Retrieves a preferences plist using an optional domain and/or key name. - * - * @param client An initialized lockdownd client. - * @param domain The domain to query on or NULL for global domain - * @param key The key name to request or NULL to query for all keys - * @param value A plist node representing the result value node - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL - */ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value) { if (!client) @@ -359,12 +408,12 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom dict = plist_new_dict(); plist_dict_add_label(dict, client->label); if (domain) { - plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); + plist_dict_set_item(dict,"Domain", plist_new_string(domain)); } if (key) { - plist_dict_insert_item(dict,"Key", plist_new_string(key)); + plist_dict_set_item(dict,"Key", plist_new_string(key)); } - plist_dict_insert_item(dict,"Request", plist_new_string("GetValue")); + plist_dict_set_item(dict,"Request", plist_new_string("GetValue")); /* send to device */ ret = lockdownd_send(client, dict); @@ -380,10 +429,11 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom if (ret != LOCKDOWN_E_SUCCESS) return ret; - if (lockdown_check_result(dict, "GetValue") == RESULT_SUCCESS) { + ret = lockdown_check_result(dict, "GetValue"); + if (ret == LOCKDOWN_E_SUCCESS) { debug_info("success"); - ret = LOCKDOWN_E_SUCCESS; } + if (ret != LOCKDOWN_E_SUCCESS) { plist_free(dict); return ret; @@ -400,17 +450,6 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom return ret; } -/** - * Sets a preferences value using a plist and optional by domain and/or key name. - * - * @param client an initialized lockdownd client. - * @param domain the domain to query on or NULL for global domain - * @param key the key name to set the value or NULL to set a value dict plist - * @param value a plist node of any node type representing the value to set - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or - * value is NULL - */ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value) { if (!client || !value) @@ -423,13 +462,13 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom dict = plist_new_dict(); plist_dict_add_label(dict, client->label); if (domain) { - plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); + plist_dict_set_item(dict,"Domain", plist_new_string(domain)); } if (key) { - plist_dict_insert_item(dict,"Key", plist_new_string(key)); + plist_dict_set_item(dict,"Key", plist_new_string(key)); } - plist_dict_insert_item(dict,"Request", plist_new_string("SetValue")); - plist_dict_insert_item(dict,"Value", value); + plist_dict_set_item(dict,"Request", plist_new_string("SetValue")); + plist_dict_set_item(dict,"Value", value); /* send to device */ ret = lockdownd_send(client, dict); @@ -445,9 +484,9 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom if (ret != LOCKDOWN_E_SUCCESS) return ret; - if (lockdown_check_result(dict, "SetValue") == RESULT_SUCCESS) { + ret = lockdown_check_result(dict, "SetValue"); + if (ret == LOCKDOWN_E_SUCCESS) { debug_info("success"); - ret = LOCKDOWN_E_SUCCESS; } if (ret != LOCKDOWN_E_SUCCESS) { @@ -459,17 +498,6 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom return ret; } -/** - * Removes a preference node by domain and/or key name. - * - * @note: Use with caution as this could remove vital information on the device - * - * @param client An initialized lockdownd client. - * @param domain The domain to query on or NULL for global domain - * @param key The key name to remove or NULL remove all keys for the current domain - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL - */ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key) { if (!client) @@ -482,12 +510,12 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char * dict = plist_new_dict(); plist_dict_add_label(dict, client->label); if (domain) { - plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); + plist_dict_set_item(dict,"Domain", plist_new_string(domain)); } if (key) { - plist_dict_insert_item(dict,"Key", plist_new_string(key)); + plist_dict_set_item(dict,"Key", plist_new_string(key)); } - plist_dict_insert_item(dict,"Request", plist_new_string("RemoveValue")); + plist_dict_set_item(dict,"Request", plist_new_string("RemoveValue")); /* send to device */ ret = lockdownd_send(client, dict); @@ -503,9 +531,9 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char * if (ret != LOCKDOWN_E_SUCCESS) return ret; - if (lockdown_check_result(dict, "RemoveValue") == RESULT_SUCCESS) { + ret = lockdown_check_result(dict, "RemoveValue"); + if (ret == LOCKDOWN_E_SUCCESS) { debug_info("success"); - ret = LOCKDOWN_E_SUCCESS; } if (ret != LOCKDOWN_E_SUCCESS) { @@ -517,16 +545,7 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char * return ret; } -/** - * Returns the unique id of the device from lockdownd. - * - * @param client An initialized lockdownd client. - * @param uuid Holds the unique id of the device. The caller is responsible - * for freeing the memory. - * - * @return LOCKDOWN_E_SUCCESS on success - */ -lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uuid) +lockdownd_error_t lockdownd_get_device_udid(lockdownd_client_t client, char **udid) { lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; plist_t value = NULL; @@ -535,7 +554,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu if (ret != LOCKDOWN_E_SUCCESS) { return ret; } - plist_get_string_val(value, uuid); + plist_get_string_val(value, udid); plist_free(value); value = NULL; @@ -551,7 +570,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu * * @return LOCKDOWN_E_SUCCESS on success */ -lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key) +static lockdownd_error_t lockdownd_get_device_public_key_as_key_data(lockdownd_client_t client, key_data_t *public_key) { lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; plist_t value = NULL; @@ -572,15 +591,6 @@ lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnu return ret; } -/** - * Retrieves the name of the device from lockdownd set by the user. - * - * @param client An initialized lockdownd client. - * @param device_name Holds the name of the device. The caller is - * responsible for freeing the memory. - * - * @return LOCKDOWN_E_SUCCESS on success - */ lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name) { lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; @@ -598,29 +608,19 @@ lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **de return ret; } -/** - * Creates a new lockdownd client for the device. - * - * @note This function does not pair with the device or start a session. This - * has to be done manually by the caller after the client is created. - * The device disconnects automatically if the lockdown connection idles - * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon - * as the connection is no longer needed. - * - * @param device The device to create a lockdownd client for - * @param client The pointer to the location of the new lockdownd_client - * @param label The label to use for communication. Usually the program name. - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL - */ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label) { - if (!client) + if (!device || !client) return LOCKDOWN_E_INVALID_ARG; + static struct lockdownd_service_descriptor service = { + .port = 0xf27e, + .ssl_enabled = 0 + }; + property_list_service_client_t plistclient = NULL; - if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - debug_info("could not connect to lockdownd (device %s)", device->uuid); + if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { + debug_info("could not connect to lockdownd (device %s)", device->udid); return LOCKDOWN_E_MUX_ERROR; } @@ -628,7 +628,14 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli client_loc->parent = plistclient; client_loc->ssl_enabled = 0; client_loc->session_id = NULL; - client_loc->uuid = NULL; + client_loc->device = device; + client_loc->cu_key = NULL; + client_loc->cu_key_len = 0; + + if (device->udid) { + debug_info("device udid: %s", device->udid); + } + client_loc->label = label ? strdup(label) : NULL; *client = client_loc; @@ -636,23 +643,6 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli return LOCKDOWN_E_SUCCESS; } -/** - * Creates a new lockdownd client for the device and starts initial handshake. - * The handshake consists out of query_type, validate_pair, pair and - * start_session calls. It uses the internal pairing record management. - * - * @note The device disconnects automatically if the lockdown connection idles - * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon - * as the connection is no longer needed. - * - * @param device The device to create a lockdownd client for - * @param client The pointer to the location of the new lockdownd_client - * @param label The label to use for communication. Usually the program name. - * Pass NULL to disable sending the label in requests to lockdownd. - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, - * LOCKDOWN_E_INVALID_CONF if configuration data is wrong - */ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) { if (!client) @@ -660,6 +650,7 @@ 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; + plist_t pair_record = NULL; char *host_id = NULL; char *type = NULL; @@ -670,60 +661,125 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown } /* perform handshake */ - if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc, &type)) { + ret = lockdownd_query_type(client_loc, &type); + if (LOCKDOWN_E_SUCCESS != ret) { debug_info("QueryType failed in the lockdownd client."); - ret = LOCKDOWN_E_NOT_ENOUGH_DATA; - } else { - if (strcmp("com.apple.mobile.lockdown", type)) { - debug_info("Warning QueryType request returned \"%s\".", type); + } else if (strcmp("com.apple.mobile.lockdown", type) != 0) { + debug_info("Warning QueryType request returned \"%s\".", type); + } + free(type); + + if (device->version == 0) { + plist_t p_version = NULL; + if (lockdownd_get_value(client_loc, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) { + int vers[3] = {0, 0, 0}; + char *s_version = NULL; + plist_get_string_val(p_version, &s_version); + if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { + device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]); + } + free(s_version); } - if (type) - free(type); + plist_free(p_version); } - - ret = idevice_get_uuid(device, &client_loc->uuid); - if (LOCKDOWN_E_SUCCESS != ret) { - debug_info("failed to get device uuid."); + if (device->device_class == 0) { + plist_t p_device_class = NULL; + if (lockdownd_get_value(client_loc, NULL, "DeviceClass", &p_device_class) == LOCKDOWN_E_SUCCESS) { + char* s_device_class = NULL; + plist_get_string_val(p_device_class, &s_device_class); + if (s_device_class != NULL) { + if (!strcmp(s_device_class, "iPhone")) { + device->device_class = DEVICE_CLASS_IPHONE; + } else if (!strcmp(s_device_class, "iPad")) { + device->device_class = DEVICE_CLASS_IPAD; + } else if (!strcmp(s_device_class, "iPod")) { + device->device_class = DEVICE_CLASS_IPOD; + } else if (!strcmp(s_device_class, "Watch")) { + device->device_class = DEVICE_CLASS_WATCH; + } else if (!strcmp(s_device_class, "AppleTV")) { + device->device_class = DEVICE_CLASS_APPLETV; + } else { + device->device_class = DEVICE_CLASS_UNKNOWN; + } + free(s_device_class); + } + } + plist_free(p_device_class); } - debug_info("device uuid: %s", client_loc->uuid); - userpref_get_host_id(&host_id); - if (LOCKDOWN_E_SUCCESS == ret && !host_id) { + userpref_error_t uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record); + if (uerr == USERPREF_E_READ_ERROR) { + debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid); + lockdownd_client_free(client_loc); + return LOCKDOWN_E_RECEIVE_TIMEOUT; + } + if (pair_record) { + pair_record_get_host_id(pair_record, &host_id); + } + if (LOCKDOWN_E_SUCCESS == ret && pair_record && !host_id) { ret = LOCKDOWN_E_INVALID_CONF; } - if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->uuid)) + if (LOCKDOWN_E_SUCCESS == ret && !pair_record) { + /* attempt pairing */ + free(host_id); + host_id = NULL; ret = lockdownd_pair(client_loc, NULL); + } - /* in any case, we need to validate pairing to receive trusted host status */ - ret = lockdownd_validate_pair(client_loc, NULL); + plist_free(pair_record); + pair_record = NULL; - /* if not paired yet, let's do it now */ - if (LOCKDOWN_E_INVALID_HOST_ID == ret) { - ret = lockdownd_pair(client_loc, NULL); - if (LOCKDOWN_E_SUCCESS == ret) { - ret = lockdownd_validate_pair(client_loc, NULL); + if (device->version < DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) { + /* for older devices, 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) { + free(host_id); + host_id = NULL; + ret = lockdownd_pair(client_loc, NULL); + if (LOCKDOWN_E_SUCCESS == ret) { + ret = lockdownd_validate_pair(client_loc, NULL); + } } } if (LOCKDOWN_E_SUCCESS == ret) { + if (!host_id) { + uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record); + if (uerr == USERPREF_E_READ_ERROR) { + debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid); + lockdownd_client_free(client_loc); + return LOCKDOWN_E_RECEIVE_TIMEOUT; + } else if (uerr == USERPREF_E_NOENT) { + debug_info("ERROR: No pair record for %s", client_loc->device->udid); + lockdownd_client_free(client_loc); + return LOCKDOWN_E_INVALID_CONF; + } else if (uerr != USERPREF_E_SUCCESS) { + debug_info("ERROR: Failed to retrieve or parse pair record for %s", client_loc->device->udid); + lockdownd_client_free(client_loc); + return LOCKDOWN_E_INVALID_CONF; + } + if (pair_record) { + pair_record_get_host_id(pair_record, &host_id); + plist_free(pair_record); + } + } + ret = lockdownd_start_session(client_loc, host_id, NULL, NULL); if (LOCKDOWN_E_SUCCESS != ret) { debug_info("Session opening failed."); } - if (host_id) { - free(host_id); - host_id = NULL; - } } - + if (LOCKDOWN_E_SUCCESS == ret) { *client = client_loc; } else { lockdownd_client_free(client_loc); } - + free(host_id); return ret; } @@ -740,68 +796,77 @@ 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, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate))); - - if (!pair_record->host_id) - free(host_id_loc); + plist_dict_set_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate))); + plist_dict_set_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate))); + plist_dict_set_item(dict, "HostID", plist_new_string(pair_record->host_id)); + plist_dict_set_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate))); + plist_dict_set_item(dict, "SystemBUID", plist_new_string(pair_record->system_buid)); return dict; } /** - * Generates a new pairing record plist and required certificates for the - * supplied public key of the device and the host_id of the caller's host - * computer. + * Generates a pair record plist with required certificates for a specific + * device. If a pairing exists, it is loaded from the computer instead of being + * generated. * - * @param public_key The public key of the device. - * @param host_id The HostID to use for the pair record plist. - * @param pair_record_plist Holds the generated pair record. + * @param pair_record_plist Holds the pair record. * * @return LOCKDOWN_E_SUCCESS on success */ -static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, char *host_id, plist_t *pair_record_plist) +static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t *pair_record) { lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; - gnutls_datum_t device_cert = { NULL, 0 }; - gnutls_datum_t host_cert = { NULL, 0 }; - gnutls_datum_t root_cert = { NULL, 0 }; + key_data_t public_key = { NULL, 0 }; + char* host_id = NULL; + char* system_buid = NULL; - ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert); + /* retrieve device public key */ + ret = lockdownd_get_device_public_key_as_key_data(client, &public_key); if (ret != LOCKDOWN_E_SUCCESS) { - return ret; + debug_info("device refused to send public key."); + goto leave; + } + debug_info("device public key follows:\n%.*s", public_key.size, public_key.data); + + *pair_record = plist_new_dict(); + + /* generate keys and certificates into pair record */ + userpref_error_t uret = USERPREF_E_SUCCESS; + uret = pair_record_generate_keys_and_certs(*pair_record, public_key); + switch(uret) { + case USERPREF_E_INVALID_ARG: + ret = LOCKDOWN_E_INVALID_ARG; + break; + case USERPREF_E_INVALID_CONF: + ret = LOCKDOWN_E_INVALID_CONF; + break; + case USERPREF_E_SSL_ERROR: + ret = LOCKDOWN_E_SSL_ERROR; + default: + break; } - char *host_id_loc = host_id; + /* set SystemBUID */ + userpref_read_system_buid(&system_buid); + if (system_buid) { + plist_dict_set_item(*pair_record, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid)); + } - if (!host_id) - userpref_get_host_id(&host_id_loc); + /* set HostID */ + host_id = generate_uuid(); + pair_record_set_host_id(*pair_record, host_id); - /* 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, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size)); - - if (!host_id) - free(host_id_loc); - - if (device_cert.data) - free(device_cert.data); - if (host_cert.data) - free(host_cert.data); - if (root_cert.data) - free(root_cert.data); +leave: + if (host_id) + free(host_id); + if (system_buid) + free(system_buid); + if (public_key.data) + free(public_key.data); return ret; } @@ -809,11 +874,13 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c /** * Function used internally by lockdownd_pair() and lockdownd_validate_pair() * - * @param client The lockdown client to pair with. + * @param client The lockdown client * @param pair_record The pair record to use for pairing. If NULL is passed, then * the pair records from the current machine are used. New records will be * generated automatically when pairing is done for the first time. * @param verb This is either "Pair", "ValidatePair" or "Unpair". + * @param options The pairing options to pass. + * @param response If non-NULL a pointer to lockdownd's response dictionary is returned. * * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, @@ -821,73 +888,103 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id */ -static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb) +static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb, plist_t options, plist_t *result) { if (!client) return LOCKDOWN_E_INVALID_ARG; lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; plist_t dict = NULL; - plist_t dict_record = NULL; - gnutls_datum_t public_key = { NULL, 0 }; + plist_t pair_record_plist = NULL; + plist_t wifi_node = NULL; int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ - 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; } /* use passed pair_record */ - dict_record = lockdownd_pair_record_to_plist(pair_record); + pair_record_plist = lockdownd_pair_record_to_plist(pair_record); pairing_mode = 1; } else { - ret = lockdownd_get_device_public_key(client, &public_key); - if (ret != LOCKDOWN_E_SUCCESS) { - if (public_key.data) - free(public_key.data); - debug_info("device refused to send public key."); - 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); - if (ret != LOCKDOWN_E_SUCCESS) { - if (dict_record) - plist_free(dict_record); - return ret; + /* generate a new pair record if pairing */ + if (!strcmp("Pair", verb)) { + ret = pair_record_generate(client, &pair_record_plist); + + if (ret != LOCKDOWN_E_SUCCESS) { + if (pair_record_plist) + plist_free(pair_record_plist); + return ret; + } + + /* get wifi mac now, if we get it later we fail on iOS 7 which causes a reconnect */ + lockdownd_get_value(client, NULL, "WiFiAddress", &wifi_node); + } else { + /* use existing pair record */ + userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record_plist); + if (uerr == USERPREF_E_READ_ERROR) { + debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid); + return LOCKDOWN_E_RECEIVE_TIMEOUT; + } else if (uerr == USERPREF_E_NOENT) { + debug_info("ERROR: No pair record for %s", client->device->udid); + return LOCKDOWN_E_INVALID_CONF; + } else if (uerr != USERPREF_E_SUCCESS) { + debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid); + return LOCKDOWN_E_INVALID_CONF; + } } } - /* Setup Pair request plist */ + plist_t request_pair_record = plist_copy(pair_record_plist); + + /* remove stuff that is private */ + plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY); + plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY); + + /* 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, "Request", plist_new_string(verb)); + plist_dict_set_item(dict, "PairRecord", request_pair_record); + plist_dict_set_item(dict, "Request", plist_new_string(verb)); + plist_dict_set_item(dict, "ProtocolVersion", plist_new_string(LOCKDOWN_PROTOCOL_VERSION)); + + if (options) { + plist_dict_set_item(dict, "PairingOptions", plist_copy(options)); + } /* send to device */ ret = lockdownd_send(client, dict); plist_free(dict); dict = NULL; - if (ret != LOCKDOWN_E_SUCCESS) + if (ret != LOCKDOWN_E_SUCCESS) { + plist_free(pair_record_plist); + if (wifi_node) + plist_free(wifi_node); return ret; + } /* Now get device's answer */ ret = lockdownd_receive(client, &dict); - if (ret != LOCKDOWN_E_SUCCESS) + if (ret != LOCKDOWN_E_SUCCESS) { + plist_free(pair_record_plist); + if (wifi_node) + plist_free(wifi_node); return ret; + } if (strcmp(verb, "Unpair") == 0) { /* workaround for Unpair giving back ValidatePair, * seems to be a bug in the device's fw */ - if (lockdown_check_result(dict, NULL) != RESULT_SUCCESS) { + if (lockdown_check_result(dict, NULL) != LOCKDOWN_E_SUCCESS) { ret = LOCKDOWN_E_PAIRING_FAILED; } } else { - if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) { + if (lockdown_check_result(dict, verb) != LOCKDOWN_E_SUCCESS) { ret = LOCKDOWN_E_PAIRING_FAILED; } } @@ -896,13 +993,32 @@ 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->uuid); + userpref_delete_pair_record(client->device->udid); } else { - /* store public key in config */ - userpref_set_device_public_key(client->uuid, public_key); + if (!strcmp("Pair", verb)) { + /* add returned escrow bag if available */ + plist_t extra_node = plist_dict_get_item(dict, USERPREF_ESCROW_BAG_KEY); + if (extra_node && plist_get_node_type(extra_node) == PLIST_DATA) { + debug_info("Saving EscrowBag from response in pair record"); + plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(extra_node)); + } + + /* save previously retrieved wifi mac address in pair record */ + if (wifi_node) { + debug_info("Saving WiFiAddress from device in pair record"); + plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, plist_copy(wifi_node)); + plist_free(wifi_node); + wifi_node = NULL; + } + + userpref_save_pair_record(client->device->udid, client->device->mux_id, pair_record_plist); + } } + } else { + debug_info("external pairing mode"); } } else { debug_info("%s failure", verb); @@ -914,92 +1030,60 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ plist_get_string_val(error_node, &value); if (value) { /* the first pairing fails if the device is password protected */ - if (!strcmp(value, "PasswordProtected")) { - ret = LOCKDOWN_E_PASSWORD_PROTECTED; - } else if (!strcmp(value, "InvalidHostID")) { - ret = LOCKDOWN_E_INVALID_HOST_ID; - } + ret = lockdownd_strtoerr(value); free(value); } - - plist_free(error_node); - error_node = NULL; } } - plist_free(dict); - dict = NULL; - if (public_key.data) - free(public_key.data); + + if (pair_record_plist) { + plist_free(pair_record_plist); + pair_record_plist = NULL; + } + + if (wifi_node) { + plist_free(wifi_node); + wifi_node = NULL; + } + + if (result) { + *result = dict; + } else { + plist_free(dict); + dict = NULL; + } + return ret; } -/** - * Pairs the device using the supplied pair record. - * - * @param client The lockdown client to pair with. - * @param pair_record The pair record to use for pairing. If NULL is passed, then - * the pair records from the current machine are used. New records will be - * generated automatically when pairing is done for the first time. - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, - * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, - * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, - * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, - * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id - */ lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) { - return lockdownd_do_pair(client, pair_record, "Pair"); + + plist_t options = plist_new_dict(); + plist_dict_set_item(options, "ExtendedPairingErrors", plist_new_bool(1)); + + lockdownd_error_t ret = lockdownd_do_pair(client, pair_record, "Pair", options, NULL); + + plist_free(options); + + return ret; +} + +lockdownd_error_t lockdownd_pair_with_options(lockdownd_client_t client, lockdownd_pair_record_t pair_record, plist_t options, plist_t *response) +{ + return lockdownd_do_pair(client, pair_record, "Pair", options, response); } -/** - * Validates if the device is paired with the given HostID. If succeeded them - * specified host will become trusted host of the device indicated by the - * lockdownd preference named TrustedHostAttached. Otherwise the host must because - * paired using lockdownd_pair() first. - * - * @param client The lockdown client to pair with. - * @param pair_record The pair record to validate pairing with. If NULL is - * passed, then the pair record is read from the internal pairing record - * management. - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, - * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, - * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, - * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, - * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id - */ lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) { - return lockdownd_do_pair(client, pair_record, "ValidatePair"); + return lockdownd_do_pair(client, pair_record, "ValidatePair", NULL, NULL); } -/** - * Unpairs the device with the given HostID and removes the pairing records - * from the device and host if the internal pairing record management is used. - * - * @param client The lockdown client to pair with. - * @param pair_record The pair record to use for unpair. If NULL is passed, then - * the pair records from the current machine are used. - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, - * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, - * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, - * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, - * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id - */ lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) { - return lockdownd_do_pair(client, pair_record, "Unpair"); + return lockdownd_do_pair(client, pair_record, "Unpair", NULL, NULL); } -/** - * Tells the device to immediately enter recovery mode. - * - * @param client The lockdown client - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL - */ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) { if (!client) @@ -1009,7 +1093,7 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); - plist_dict_insert_item(dict,"Request", plist_new_string("EnterRecovery")); + plist_dict_set_item(dict,"Request", plist_new_string("EnterRecovery")); debug_info("telling device to enter recovery mode"); @@ -1019,23 +1103,17 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) ret = lockdownd_receive(client, &dict); - if (lockdown_check_result(dict, "EnterRecovery") == RESULT_SUCCESS) { + ret = lockdown_check_result(dict, "EnterRecovery"); + if (ret == LOCKDOWN_E_SUCCESS) { debug_info("success"); - ret = LOCKDOWN_E_SUCCESS; } + plist_free(dict); dict = NULL; + return ret; } -/** - * Sends the Goodbye request to lockdownd signaling the end of communication. - * - * @param client The lockdown client - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, - * LOCKDOWN_E_PLIST_ERROR if the device did not acknowledge the request - */ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) { if (!client) @@ -1045,7 +1123,7 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); - plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye")); + plist_dict_set_item(dict,"Request", plist_new_string("Goodbye")); debug_info("called"); @@ -1059,187 +1137,17 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) return LOCKDOWN_E_PLIST_ERROR; } - if (lockdown_check_result(dict, "Goodbye") == RESULT_SUCCESS) { + ret = lockdown_check_result(dict, "Goodbye"); + if (ret == LOCKDOWN_E_SUCCESS) { debug_info("success"); - ret = LOCKDOWN_E_SUCCESS; } + plist_free(dict); dict = NULL; - return ret; -} - -/** - * Generates the device certificate from the public key as well as the host - * and root certificates. - * - * @param public_key The public key of the device to use for generation. - * @param odevice_cert Holds the generated device certificate. - * @param ohost_cert Holds the generated host certificate. - * @param oroot_cert Holds the generated root certificate. - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a parameter is NULL, - * LOCKDOWN_E_INVALID_CONF if the internal configuration system failed, - * LOCKDOWN_E_SSL_ERROR if the certificates could not be generated - */ -lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * odevice_cert, - gnutls_datum_t * ohost_cert, gnutls_datum_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; - - gnutls_datum_t modulus = { NULL, 0 }; - gnutls_datum_t exponent = { NULL, 0 }; - - /* now decode the PEM encoded key */ - gnutls_datum_t der_pub_key; - if (GNUTLS_E_SUCCESS == gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key)) { - - /* initalize asn.1 parser */ - ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY; - if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) { - - ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY; - asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key); - - if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) { - - /* get size to read */ - int ret1 = asn1_read_value(asn1_pub_key, "modulus", NULL, (int*)&modulus.size); - int ret2 = asn1_read_value(asn1_pub_key, "publicExponent", NULL, (int*)&exponent.size); - - modulus.data = gnutls_malloc(modulus.size); - exponent.data = gnutls_malloc(exponent.size); - - ret1 = asn1_read_value(asn1_pub_key, "modulus", modulus.data, (int*)&modulus.size); - ret2 = asn1_read_value(asn1_pub_key, "publicExponent", exponent.data, (int*)&exponent.size); - if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2) - ret = LOCKDOWN_E_SUCCESS; - } - if (asn1_pub_key) - asn1_delete_structure(&asn1_pub_key); - } - if (pkcs1) - asn1_delete_structure(&pkcs1); - } - - /* now generate certificates */ - if (LOCKDOWN_E_SUCCESS == ret && 0 != modulus.size && 0 != exponent.size) { - - gnutls_global_init(); - gnutls_datum_t essentially_null = { (unsigned char*)strdup("abababababababab"), strlen("abababababababab") }; - - gnutls_x509_privkey_t fake_privkey, root_privkey, host_privkey; - gnutls_x509_crt_t dev_cert, root_cert, host_cert; - - gnutls_x509_privkey_init(&fake_privkey); - gnutls_x509_crt_init(&dev_cert); - gnutls_x509_crt_init(&root_cert); - gnutls_x509_crt_init(&host_cert); - - if (GNUTLS_E_SUCCESS == - gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null, - &essentially_null, &essentially_null)) { - - gnutls_x509_privkey_init(&root_privkey); - gnutls_x509_privkey_init(&host_privkey); - - uret = userpref_get_keys_and_certs(root_privkey, root_cert, host_privkey, host_cert); - - if (USERPREF_E_SUCCESS == uret) { - /* generate device certificate */ - gnutls_x509_crt_set_key(dev_cert, fake_privkey); - gnutls_x509_crt_set_serial(dev_cert, "\x00", 1); - gnutls_x509_crt_set_version(dev_cert, 3); - gnutls_x509_crt_set_ca_status(dev_cert, 0); - gnutls_x509_crt_set_activation_time(dev_cert, time(NULL)); - gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); - gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey); - - if (LOCKDOWN_E_SUCCESS == ret) { - /* if everything went well, export in PEM format */ - size_t export_size = 0; - gnutls_datum_t dev_pem = { NULL, 0 }; - gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &export_size); - dev_pem.data = gnutls_malloc(export_size); - gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_pem.data, &export_size); - dev_pem.size = export_size; - - gnutls_datum_t pem_root_cert = { NULL, 0 }; - gnutls_datum_t pem_host_cert = { NULL, 0 }; - - uret = userpref_get_certs_as_pem(&pem_root_cert, &pem_host_cert); - - if (USERPREF_E_SUCCESS == uret) { - /* copy buffer for output */ - odevice_cert->data = malloc(dev_pem.size); - memcpy(odevice_cert->data, dev_pem.data, dev_pem.size); - odevice_cert->size = dev_pem.size; - - ohost_cert->data = malloc(pem_host_cert.size); - memcpy(ohost_cert->data, pem_host_cert.data, pem_host_cert.size); - ohost_cert->size = pem_host_cert.size; - - oroot_cert->data = malloc(pem_root_cert.size); - memcpy(oroot_cert->data, pem_root_cert.data, pem_root_cert.size); - oroot_cert->size = pem_root_cert.size; - - g_free(pem_root_cert.data); - g_free(pem_host_cert.data); - - if (dev_pem.data) - gnutls_free(dev_pem.data); - } - } - } - - switch(uret) { - case USERPREF_E_INVALID_ARG: - ret = LOCKDOWN_E_INVALID_ARG; - break; - case USERPREF_E_INVALID_CONF: - ret = LOCKDOWN_E_INVALID_CONF; - break; - case USERPREF_E_SSL_ERROR: - ret = LOCKDOWN_E_SSL_ERROR; - default: - break; - } - } - - if (essentially_null.data) - free(essentially_null.data); - gnutls_x509_crt_deinit(dev_cert); - gnutls_x509_crt_deinit(root_cert); - gnutls_x509_crt_deinit(host_cert); - gnutls_x509_privkey_deinit(fake_privkey); - gnutls_x509_privkey_deinit(root_privkey); - gnutls_x509_privkey_deinit(host_privkey); - - } - - gnutls_free(modulus.data); - gnutls_free(exponent.data); - - gnutls_free(der_pub_key.data); return ret; } -/** - * Opens a session with lockdownd and switches to SSL mode if device wants it. - * - * @param client The lockdownd client - * @param host_id The HostID of the computer - * @param session_id The new session_id of the created session - * @param ssl_enabled Whether SSL communication is used in the session - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a client or - * host_id is NULL, LOCKDOWN_E_PLIST_ERROR if the response plist had errors, - * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the supplied HostID, - * LOCKDOWN_E_SSL_ERROR if enabling SSL communication failed - */ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled) { lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; @@ -1251,14 +1159,28 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char /* if we have a running session, stop current one first */ if (client->session_id) { lockdownd_stop_session(client, client->session_id); - free(client->session_id); } /* 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")); + plist_dict_set_item(dict,"Request", plist_new_string("StartSession")); + + /* add host id */ + if (host_id) { + plist_dict_set_item(dict, "HostID", plist_new_string(host_id)); + } + + /* add system buid */ + char *system_buid = NULL; + userpref_read_system_buid(&system_buid); + if (system_buid) { + plist_dict_set_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); @@ -1272,17 +1194,8 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char if (!dict) return LOCKDOWN_E_PLIST_ERROR; - if (lockdown_check_result(dict, "StartSession") == RESULT_FAILURE) { - plist_t error_node = plist_dict_get_item(dict, "Error"); - if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { - char *error = NULL; - plist_get_string_val(error_node, &error); - if (!strcmp(error, "InvalidHostID")) { - ret = LOCKDOWN_E_INVALID_HOST_ID; - } - free(error); - } - } else { + ret = lockdown_check_result(dict, "StartSession"); + if (ret == LOCKDOWN_E_SUCCESS) { uint8_t use_ssl = 0; plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL"); @@ -1299,6 +1212,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) @@ -1306,18 +1220,15 @@ 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")); + + 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) { - client->ssl_enabled = 1; - } else { - ret = LOCKDOWN_E_SSL_ERROR; - client->ssl_enabled = 0; - } + ret = lockdownd_error(property_list_service_enable_ssl(client->parent)); + client->ssl_enabled = (ret == LOCKDOWN_E_SUCCESS ? 1 : 0); } else { - client->ssl_enabled = 0; ret = LOCKDOWN_E_SUCCESS; + client->ssl_enabled = 0; } } @@ -1328,40 +1239,96 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char } /** - * Requests to start a service and retrieve it's port on success. + * Internal function used by lockdownd_do_start_service to create the + * StartService request's plist. * * @param client The lockdownd client - * @param service The name of the service to start - * @param port The port number the service was started on - - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter + * @param identifier The identifier of the service to start + * @param send_escrow_bag Should we send the device's escrow bag with the request + * @param request The request's plist on success, NULL on failure + * + * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_CONF on failure + * to read the escrow bag from the device's record (when used). + */ +static lockdownd_error_t lockdownd_build_start_service_request(lockdownd_client_t client, const char *identifier, int send_escrow_bag, plist_t *request) +{ + plist_t dict = plist_new_dict(); + + /* create the basic request params */ + plist_dict_add_label(dict, client->label); + plist_dict_set_item(dict, "Request", plist_new_string("StartService")); + plist_dict_set_item(dict, "Service", plist_new_string(identifier)); + + /* if needed - get the escrow bag for the device and send it with the request */ + if (send_escrow_bag) { + /* get the pairing record */ + plist_t pair_record = NULL; + userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record); + if (uerr == USERPREF_E_READ_ERROR) { + debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid); + plist_free(dict); + return LOCKDOWN_E_RECEIVE_TIMEOUT; + } else if (uerr == USERPREF_E_NOENT) { + debug_info("ERROR: No pair record for %s", client->device->udid); + plist_free(dict); + return LOCKDOWN_E_INVALID_CONF; + } else if (uerr != USERPREF_E_SUCCESS) { + debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid); + plist_free(dict); + return LOCKDOWN_E_INVALID_CONF; + } + + /* try to read the escrow bag from the record */ + plist_t escrow_bag = plist_dict_get_item(pair_record, USERPREF_ESCROW_BAG_KEY); + if (!escrow_bag || (PLIST_DATA != plist_get_node_type(escrow_bag))) { + debug_info("ERROR: Failed to retrieve the escrow bag from the device's record"); + plist_free(dict); + plist_free(pair_record); + return LOCKDOWN_E_INVALID_CONF; + } + + debug_info("Adding escrow bag to StartService for %s", identifier); + plist_dict_set_item(dict, USERPREF_ESCROW_BAG_KEY, plist_copy(escrow_bag)); + plist_free(pair_record); + } + + *request = dict; + return LOCKDOWN_E_SUCCESS; +} + +/** + * Function used internally by lockdownd_start_service and lockdownd_start_service_with_escrow_bag. + * + * @param client The lockdownd client + * @param identifier The identifier of the service to start + * @param send_escrow_bag Should we send the device's escrow bag with the request + * @param descriptor The service descriptor on success or NULL on failure + * + * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_ARG if a parameter * is NULL, LOCKDOWN_E_INVALID_SERVICE if the requested service is not known * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because - * started by the device + * started by the device, LOCKDOWN_E_INVALID_CONF if the host id or escrow bag (when + * used) are missing from the device record. */ -lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, uint16_t *port) +static lockdownd_error_t lockdownd_do_start_service(lockdownd_client_t client, const char *identifier, int send_escrow_bag, lockdownd_service_descriptor_t *service) { - if (!client || !service || !port) + if (!client || !identifier || !service) return LOCKDOWN_E_INVALID_ARG; - char *host_id = NULL; - userpref_get_host_id(&host_id); - if (!host_id) - return LOCKDOWN_E_INVALID_CONF; - if (!client->session_id) - return LOCKDOWN_E_NO_RUNNING_SESSION; + if (*service) { + // reset fields if service descriptor is reused + (*service)->port = 0; + (*service)->ssl_enabled = 0; + } plist_t dict = NULL; uint16_t port_loc = 0; lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; - free(host_id); - host_id = NULL; - - dict = plist_new_dict(); - plist_dict_add_label(dict, client->label); - plist_dict_insert_item(dict,"Request", plist_new_string("StartService")); - plist_dict_insert_item(dict,"Service", plist_new_string(service)); + /* create StartService request */ + ret = lockdownd_build_start_service_request(client, identifier, send_escrow_bag, &dict); + if (LOCKDOWN_E_SUCCESS != ret) + return ret; /* send to device */ ret = lockdownd_send(client, dict); @@ -1379,57 +1346,63 @@ lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char if (!dict) return LOCKDOWN_E_PLIST_ERROR; - ret = LOCKDOWN_E_UNKNOWN_ERROR; - if (lockdown_check_result(dict, "StartService") == RESULT_SUCCESS) { - plist_t port_value_node = plist_dict_get_item(dict, "Port"); - - if (port_value_node && (plist_get_node_type(port_value_node) == PLIST_UINT)) { + ret = lockdown_check_result(dict, "StartService"); + if (ret == LOCKDOWN_E_SUCCESS) { + if (*service == NULL) + *service = (lockdownd_service_descriptor_t)malloc(sizeof(struct lockdownd_service_descriptor)); + (*service)->port = 0; + (*service)->ssl_enabled = 0; + (*service)->identifier = strdup(identifier); + + /* read service port number */ + plist_t node = plist_dict_get_item(dict, "Port"); + if (node && (plist_get_node_type(node) == PLIST_UINT)) { uint64_t port_value = 0; - plist_get_uint_val(port_value_node, &port_value); + plist_get_uint_val(node, &port_value); if (port_value) { port_loc = port_value; ret = LOCKDOWN_E_SUCCESS; } - if (port && ret == LOCKDOWN_E_SUCCESS) - *port = port_loc; + if (port_loc && ret == LOCKDOWN_E_SUCCESS) { + (*service)->port = port_loc; + } + } + + /* check if the service requires SSL */ + node = plist_dict_get_item(dict, "EnableServiceSSL"); + if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { + uint8_t b = 0; + plist_get_bool_val(node, &b); + (*service)->ssl_enabled = b; } } else { - ret = LOCKDOWN_E_START_SERVICE_FAILED; plist_t error_node = plist_dict_get_item(dict, "Error"); if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { char *error = NULL; plist_get_string_val(error_node, &error); - if (!strcmp(error, "InvalidService")) { - ret = LOCKDOWN_E_INVALID_SERVICE; - } + ret = lockdownd_strtoerr(error); free(error); } } plist_free(dict); dict = NULL; + return ret; } -/** - * Activates the device. Only works within an open session. - * The ActivationRecord plist dictionary must be obtained using the - * activation protocol requesting from Apple's https webservice. - * - * @see http://iphone-docs.org/doku.php?id=docs:protocols:activation - * - * @param client The lockdown client - * @param activation_record The activation record plist dictionary - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or - * activation_record is NULL, LOCKDOWN_E_NO_RUNNING_SESSION if no session is - * open, LOCKDOWN_E_PLIST_ERROR if the received plist is broken, - * LOCKDOWN_E_ACTIVATION_FAILED if the activation failed, - * LOCKDOWN_E_INVALID_ACTIVATION_RECORD if the device reports that the - * activation_record is invalid - */ -lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record) +lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service) +{ + return lockdownd_do_start_service(client, identifier, 0, service); +} + +lockdownd_error_t lockdownd_start_service_with_escrow_bag(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service) +{ + return lockdownd_do_start_service(client, identifier, 1, service); +} + +lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -1444,8 +1417,8 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); - plist_dict_insert_item(dict,"Request", plist_new_string("Activate")); - plist_dict_insert_item(dict,"ActivationRecord", plist_copy(activation_record)); + plist_dict_set_item(dict,"Request", plist_new_string("Activate")); + plist_dict_set_item(dict,"ActivationRecord", plist_copy(activation_record)); ret = lockdownd_send(client, dict); plist_free(dict); @@ -1457,39 +1430,17 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati return LOCKDOWN_E_PLIST_ERROR; } - ret = LOCKDOWN_E_ACTIVATION_FAILED; - if (lockdown_check_result(dict, "Activate") == RESULT_SUCCESS) { + ret = lockdown_check_result(dict, "Activate"); + if (ret == LOCKDOWN_E_SUCCESS) { debug_info("success"); - ret = LOCKDOWN_E_SUCCESS; - - } else { - plist_t error_node = plist_dict_get_item(dict, "Error"); - if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { - char *error = NULL; - plist_get_string_val(error_node, &error); - if (!strcmp(error, "InvalidActivationRecord")) { - ret = LOCKDOWN_E_INVALID_ACTIVATION_RECORD; - } - free(error); - } } - + plist_free(dict); dict = NULL; return ret; } -/** - * Deactivates the device, returning it to the locked “Activate with iTunes” - * screen. - * - * @param client The lockdown client - * - * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, - * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open, - * LOCKDOWN_E_PLIST_ERROR if the received plist is broken - */ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) { if (!client) @@ -1502,7 +1453,7 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); - plist_dict_insert_item(dict,"Request", plist_new_string("Deactivate")); + plist_dict_set_item(dict,"Request", plist_new_string("Deactivate")); ret = lockdownd_send(client, dict); plist_free(dict); @@ -1514,11 +1465,11 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) return LOCKDOWN_E_PLIST_ERROR; } - ret = LOCKDOWN_E_UNKNOWN_ERROR; - if (lockdown_check_result(dict, "Deactivate") == RESULT_SUCCESS) { + ret = lockdown_check_result(dict, "Deactivate"); + if (ret == LOCKDOWN_E_SUCCESS) { debug_info("success"); - ret = LOCKDOWN_E_SUCCESS; } + plist_free(dict); dict = NULL; @@ -1537,19 +1488,6 @@ static void str_remove_spaces(char *source) *dest = 0; } -/** - * Calculates and returns the data classes the device supports from lockdownd. - * - * @param client An initialized lockdownd client. - * @param classes A pointer to store an array of class names. The caller is responsible - * for freeing the memory which can be done using mobilesync_data_classes_free(). - * @param count The number of items in the classes array. - * - * @return LOCKDOWN_E_SUCCESS on success, - * LOCKDOWN_E_INVALID_ARG when client is NULL, - * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open, - * LOCKDOWN_E_PLIST_ERROR if the received plist is broken - */ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count) { if (!client) @@ -1583,14 +1521,16 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha } while((value = plist_array_get_item(dict, *count)) != NULL) { - plist_get_string_val(value, &val); - newlist = realloc(*classes, sizeof(char*) * (*count+1)); - str_remove_spaces(val); - asprintf(&newlist[*count], "com.apple.%s", val); - free(val); - val = NULL; - *classes = newlist; - *count = *count+1; + plist_get_string_val(value, &val); + newlist = realloc(*classes, sizeof(char*) * (*count+1)); + str_remove_spaces(val); + if (asprintf(&newlist[*count], "com.apple.%s", val) < 0) { + debug_info("ERROR: asprintf failed"); + } + free(val); + val = NULL; + *classes = newlist; + *count = *count+1; } newlist = realloc(*classes, sizeof(char*) * (*count+1)); @@ -1603,14 +1543,6 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha return LOCKDOWN_E_SUCCESS; } - -/** - * Frees memory of an allocated array of data classes as returned by lockdownd_get_sync_data_classes() - * - * @param classes An array of class names to free. - * - * @return LOCKDOWN_E_SUCCESS on success - */ lockdownd_error_t lockdownd_data_classes_free(char **classes) { if (classes) { @@ -1622,3 +1554,51 @@ lockdownd_error_t lockdownd_data_classes_free(char **classes) } return LOCKDOWN_E_SUCCESS; } + +lockdownd_error_t lockdownd_service_descriptor_free(lockdownd_service_descriptor_t service) +{ + if (service) { + free(service->identifier); + free(service); + } + + return LOCKDOWN_E_SUCCESS; +} + +const char* lockdownd_strerror(lockdownd_error_t err) +{ + switch (err) { + case LOCKDOWN_E_SUCCESS: + return "Success"; + case LOCKDOWN_E_INVALID_ARG: + return "Invalid argument"; + case LOCKDOWN_E_INVALID_CONF: + return "Invalid configuration"; + case LOCKDOWN_E_PLIST_ERROR: + return "PropertyList error"; + case LOCKDOWN_E_PAIRING_FAILED: + return "Pairing failed"; + case LOCKDOWN_E_SSL_ERROR: + return "SSL error"; + case LOCKDOWN_E_DICT_ERROR: + return "Invalid dictionary"; + case LOCKDOWN_E_RECEIVE_TIMEOUT: + return "Receive timeout"; + case LOCKDOWN_E_MUX_ERROR: + return "Mux error"; + case LOCKDOWN_E_NO_RUNNING_SESSION: + return "No running session"; + case LOCKDOWN_E_UNKNOWN_ERROR: + return "Unknown Error"; + default: { + int i = 0; + while (lockdownd_error_str_map[i].lockdown_errstr) { + if (lockdownd_error_str_map[i].errcode == err) { + return lockdownd_error_str_map[i].errstr; + } + i++; + } + } break; + } + return "Unknown Error"; +} |