summaryrefslogtreecommitdiffstats
path: root/src/lockdown.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lockdown.c')
-rw-r--r--src/lockdown.c1440
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";
+}