summaryrefslogtreecommitdiffstats
path: root/src/lockdown.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lockdown.c')
-rw-r--r--src/lockdown.c777
1 files changed, 441 insertions, 336 deletions
diff --git a/src/lockdown.c b/src/lockdown.c
index 2532999..1befb72 100644
--- a/src/lockdown.c
+++ b/src/lockdown.c
@@ -28,9 +28,10 @@
#include <gnutls/x509.h>
#include <plist/plist.h>
+#include "property_list_service.h"
#include "lockdown.h"
#include "iphone.h"
-#include "utils.h"
+#include "debug.h"
#include "userpref.h"
#define RESULT_SUCCESS 0
@@ -98,7 +99,7 @@ static int lockdown_check_result(plist_t dict, const char *query_match)
} else if (!strcmp(result_value, "Failure")) {
ret = RESULT_FAILURE;
} else {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: ERROR: unknown result value '%s'\n", __func__, result_value);
+ debug_info("ERROR: unknown result value '%s'", result_value);
}
}
if (result_value)
@@ -108,30 +109,49 @@ static int lockdown_check_result(plist_t dict, const char *query_match)
}
/**
- * Closes the lockdownd communication session, by sending
- * the StopSession Request to the device.
+ * Adds a label key with the passed value to a plist dict node.
+ *
+ * @param plist The plist to add the key to
+ * @param label The value for the label key
+ *
+ */
+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));
+ }
+}
+
+/**
+ * Closes the lockdownd communication session, by sending the StopSession
+ * Request to the device.
+ *
+ * @see lockdownd_start_session
*
* @param control The lockdown client
+ * @param session_id The id of a running session
*
* @return an error code (LOCKDOWN_E_SUCCESS on success)
*/
-lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client)
+lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id)
{
if (!client)
return LOCKDOWN_E_INVALID_ARG;
- if (!client->session_id) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: no session_id given, cannot stop session\n", __func__);
+ if (!session_id) {
+ debug_info("no session_id given, cannot stop session");
return LOCKDOWN_E_INVALID_ARG;
}
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
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(client->session_id));
+ plist_dict_insert_item(dict,"SessionID", plist_new_string(session_id));
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: stopping session %s\n", __func__, client->session_id);
+ debug_info("stopping session %s", session_id);
ret = lockdownd_send(client, dict);
@@ -141,55 +161,20 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client)
ret = lockdownd_recv(client, &dict);
if (!dict) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: LOCKDOWN_E_PLIST_ERROR\n", __func__);
+ debug_info("LOCKDOWN_E_PLIST_ERROR");
return LOCKDOWN_E_PLIST_ERROR;
}
ret = LOCKDOWN_E_UNKNOWN_ERROR;
if (lockdown_check_result(dict, "StopSession") == RESULT_SUCCESS) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: success\n", __func__);
+ debug_info("success");
ret = LOCKDOWN_E_SUCCESS;
}
plist_free(dict);
dict = NULL;
-
- free(client->session_id);
- client->session_id = NULL;
-
- return ret;
-}
-
-/**
- * Shuts down the SSL session by first calling iphone_lckd_stop_session
- * to cleanly close the lockdownd communication session, and then
- * performing a close notify, which is done by "gnutls_bye".
- *
- * @param client The lockdown client
- *
- * @return an error code (LOCKDOWN_E_SUCCESS on success)
- */
-static lockdownd_error_t lockdownd_stop_ssl_session(lockdownd_client_t client)
-{
- if (!client) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: invalid argument!\n", __func__);
- return LOCKDOWN_E_INVALID_ARG;
+ if (client->ssl_enabled) {
+ property_list_service_disable_ssl(client->parent);
}
- lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
-
- if (client->in_SSL) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: stopping SSL session\n", __func__);
- ret = lockdownd_stop_session(client);
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: sending SSL close notify\n", __func__);
- gnutls_bye(client->ssl_session, GNUTLS_SHUT_RDWR);
- }
- if (client->ssl_session) {
- gnutls_deinit(client->ssl_session);
- }
- if (client->ssl_certificate) {
- gnutls_certificate_free_credentials(client->ssl_certificate);
- }
- client->in_SSL = 0;
-
return ret;
}
@@ -205,29 +190,45 @@ lockdownd_error_t lockdownd_client_free(lockdownd_client_t client)
return LOCKDOWN_E_INVALID_ARG;
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
- lockdownd_stop_ssl_session(client);
+ if (client->session_id)
+ lockdownd_stop_session(client, client->session_id);
- if (client->connection) {
+ if (client->parent) {
lockdownd_goodbye(client);
- // IMO, read of final "sessionUpcall connection closed" packet
- // should come here instead of in iphone_free_device
- if ((ret = iphone_device_disconnect(client->connection)) != IPHONE_E_SUCCESS) {
- ret = LOCKDOWN_E_UNKNOWN_ERROR;
+ if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ ret = LOCKDOWN_E_SUCCESS;
}
}
- if (client->session_id) {
- free(client->session_id);
- }
if (client->uuid) {
free(client->uuid);
}
+ if (client->label) {
+ free(client->label);
+ }
free(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) {
+ if (client->label)
+ free(client->label);
+
+ client->label = (label != NULL) ? strdup(label): NULL;
+ }
+}
+
/** Polls the iPhone for lockdownd data.
*
* @param control The lockdownd client
@@ -240,18 +241,11 @@ lockdownd_error_t lockdownd_recv(lockdownd_client_t client, plist_t *plist)
if (!client || !plist || (plist && *plist))
return LOCKDOWN_E_INVALID_ARG;
lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
- iphone_error_t err;
+ property_list_service_error_t err;
- if (!client->in_SSL) {
- err = iphone_device_receive_plist(client->connection, plist);
- if (err != IPHONE_E_SUCCESS) {
- ret = LOCKDOWN_E_UNKNOWN_ERROR;
- }
- } else {
- err = iphone_device_receive_encrypted_plist(client->ssl_session, plist);
- if (err != IPHONE_E_SUCCESS) {
- return LOCKDOWN_E_SSL_ERROR;
- }
+ err = property_list_service_receive_plist(client->parent, plist);
+ if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ ret = LOCKDOWN_E_UNKNOWN_ERROR;
}
if (!*plist)
@@ -278,27 +272,22 @@ lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist)
lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
iphone_error_t err;
- if (!client->in_SSL) {
- err = iphone_device_send_xml_plist(client->connection, plist);
- if (err != IPHONE_E_SUCCESS) {
- ret = LOCKDOWN_E_UNKNOWN_ERROR;
- }
- } else {
- err = iphone_device_send_encrypted_xml_plist(client->ssl_session, plist);
- if (err != IPHONE_E_SUCCESS) {
- ret = LOCKDOWN_E_SSL_ERROR;
- }
+ err = property_list_service_send_xml_plist(client->parent, plist);
+ if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ ret = LOCKDOWN_E_UNKNOWN_ERROR;
}
return ret;
}
-/** Initiates the handshake for the lockdown session. Part of the lockdownd handshake.
+/** 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. Can be NULL to ignore.
*
* @return an error code (LOCKDOWN_E_SUCCESS on success)
*/
-lockdownd_error_t lockdownd_query_type(lockdownd_client_t client)
+lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
{
if (!client)
return LOCKDOWN_E_INVALID_ARG;
@@ -306,9 +295,10 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client)
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
plist_t dict = plist_new_dict();
+ plist_dict_add_label(dict, client->label);
plist_dict_insert_item(dict,"Request", plist_new_string("QueryType"));
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: called\n", __func__);
+ debug_info("called");
ret = lockdownd_send(client, dict);
plist_free(dict);
@@ -321,7 +311,12 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client)
ret = LOCKDOWN_E_UNKNOWN_ERROR;
if (lockdown_check_result(dict, "QueryType") == RESULT_SUCCESS) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: success\n", __func__);
+ /* 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);
+ }
+ debug_info("success with type %s", *type);
ret = LOCKDOWN_E_SUCCESS;
}
plist_free(dict);
@@ -349,6 +344,7 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
/* setup request plist */
dict = plist_new_dict();
+ plist_dict_add_label(dict, client->label);
if (domain) {
plist_dict_insert_item(dict,"Domain", plist_new_string(domain));
}
@@ -372,7 +368,7 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
return ret;
if (lockdown_check_result(dict, "GetValue") == RESULT_SUCCESS) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: success\n", __func__);
+ debug_info("success");
ret = LOCKDOWN_E_SUCCESS;
}
if (ret != LOCKDOWN_E_SUCCESS) {
@@ -383,7 +379,7 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
plist_t value_node = plist_dict_get_item(dict, "Value");
if (value_node) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: has a value\n", __func__);
+ debug_info("has a value");
*value = plist_copy(value_node);
}
@@ -410,6 +406,7 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
/* setup request plist */
dict = plist_new_dict();
+ plist_dict_add_label(dict, client->label);
if (domain) {
plist_dict_insert_item(dict,"Domain", plist_new_string(domain));
}
@@ -434,7 +431,7 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
return ret;
if (lockdown_check_result(dict, "SetValue") == RESULT_SUCCESS) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: success\n", __func__);
+ debug_info("success");
ret = LOCKDOWN_E_SUCCESS;
}
@@ -467,6 +464,7 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
/* setup request plist */
dict = plist_new_dict();
+ plist_dict_add_label(dict, client->label);
if (domain) {
plist_dict_insert_item(dict,"Domain", plist_new_string(domain));
}
@@ -490,7 +488,7 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
return ret;
if (lockdown_check_result(dict, "RemoveValue") == RESULT_SUCCESS) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: success\n", __func__);
+ debug_info("success");
ret = LOCKDOWN_E_SUCCESS;
}
@@ -572,44 +570,85 @@ lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **de
return ret;
}
-/** Creates a lockdownd client for the give iPhone
+/** Creates a lockdownd client for the device.
*
- * @param phone The iPhone to create a lockdownd client for
+ * @param phone 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 an error code (LOCKDOWN_E_SUCCESS on success)
*/
-lockdownd_error_t lockdownd_client_new(iphone_device_t device, lockdownd_client_t *client)
+lockdownd_error_t lockdownd_client_new(iphone_device_t device, lockdownd_client_t *client, const char *label)
{
if (!client)
return LOCKDOWN_E_INVALID_ARG;
+
lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
- char *host_id = NULL;
- iphone_connection_t connection;
- if (iphone_device_connect(device, 0xf27e, &connection) != IPHONE_E_SUCCESS) {
- log_debug_msg("%s: could not connect to lockdownd (device %s)\n", __func__, device->uuid);
+ 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);
return LOCKDOWN_E_MUX_ERROR;
}
lockdownd_client_t client_loc = (lockdownd_client_t) malloc(sizeof(struct lockdownd_client_int));
- client_loc->connection = connection;
- client_loc->ssl_session = NULL;
- client_loc->ssl_certificate = NULL;
- client_loc->in_SSL = 0;
+ client_loc->parent = plistclient;
+ client_loc->ssl_enabled = 0;
client_loc->session_id = NULL;
client_loc->uuid = NULL;
+ client_loc->label = NULL;
+ if (label != NULL)
+ strdup(label);
+
+ if (LOCKDOWN_E_SUCCESS == ret) {
+ *client = client_loc;
+ } else {
+ lockdownd_client_free(client_loc);
+ }
+
+ return ret;
+}
+
+/** Creates a lockdownd client for the device and starts initial handshake.
+ * The handshake consists of query_type, validate_pair, pair and
+ * start_session calls.
+ *
+ * @param phone 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 an error code (LOCKDOWN_E_SUCCESS on success)
+ */
+lockdownd_error_t lockdownd_client_new_with_handshake(iphone_device_t device, lockdownd_client_t *client, const char *label)
+{
+ if (!client)
+ return LOCKDOWN_E_INVALID_ARG;
+
+ lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
+ lockdownd_client_t client_loc = NULL;
+ char *host_id = NULL;
+ char *type = NULL;
- if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc)) {
- log_debug_msg("%s: QueryType failed in the lockdownd client.\n", __func__);
+
+ ret = lockdownd_client_new(device, &client_loc, label);
+
+ /* perform handshake */
+ if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc, &type)) {
+ 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);
+ }
+ if (type)
+ free(type);
}
ret = iphone_device_get_uuid(device, &client_loc->uuid);
if (LOCKDOWN_E_SUCCESS != ret) {
- log_debug_msg("%s: failed to get device uuid.\n", __func__);
+ debug_info("failed to get device uuid.");
}
- log_debug_msg("%s: device uuid: %s\n", __func__, client_loc->uuid);
+ debug_info("device uuid: %s", client_loc->uuid);
userpref_get_host_id(&host_id);
if (LOCKDOWN_E_SUCCESS == ret && !host_id) {
@@ -617,77 +656,135 @@ lockdownd_error_t lockdownd_client_new(iphone_device_t device, lockdownd_client_
}
if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->uuid))
- ret = lockdownd_pair(client_loc, host_id);
+ 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);
if (LOCKDOWN_E_SUCCESS == ret) {
- ret = lockdownd_start_ssl_session(client_loc, host_id);
+ ret = lockdownd_start_session(client_loc, host_id, NULL, NULL);
if (LOCKDOWN_E_SUCCESS != ret) {
- ret = LOCKDOWN_E_SSL_ERROR;
- log_debug_msg("%s: SSL Session opening failed.\n", __func__);
+ 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);
+ }
- if (LOCKDOWN_E_SUCCESS == ret)
- *client = client_loc;
+ return ret;
+}
+
+static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_record)
+{
+ 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);
+
+ return dict;
+}
+
+static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, char *host_id, plist_t *pair_record_plist)
+{
+ 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 };
+
+ ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert);
+ if (ret != LOCKDOWN_E_SUCCESS) {
+ return ret;
}
+ char *host_id_loc = host_id;
+
+ if (!host_id)
+ userpref_get_host_id(&host_id_loc);
+
+ /* setup request plist */
+ *pair_record_plist = plist_new_dict();
+ plist_dict_insert_item(*pair_record_plist, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size));
+ plist_dict_insert_item(*pair_record_plist, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size));
+ plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id_loc));
+ plist_dict_insert_item(*pair_record_plist, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size));
+
+ if (!host_id)
+ free(host_id_loc);
+
return ret;
}
/** Function used internally by lockdownd_pair() and lockdownd_validate_pair()
*
* @param client The lockdown client to pair with.
- * @param host_id The HostID to use for pairing. If NULL is passed, then
- * the HostID of the current machine is used. A new HostID will be
+ * @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" or "ValidatePair".
+ * @param verb This is either "Pair", "ValidatePair" or "Unpair".
*
* @return an error code (LOCKDOWN_E_SUCCESS on success)
*/
-static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, char *host_id, const char *verb)
+static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb)
{
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
plist_t dict = NULL;
plist_t dict_record = NULL;
-
- gnutls_datum_t device_cert = { NULL, 0 };
- gnutls_datum_t host_cert = { NULL, 0 };
- gnutls_datum_t root_cert = { NULL, 0 };
gnutls_datum_t public_key = { NULL, 0 };
+ int pairing_mode = 0; /* 0 = libiphone, 1 = external */
- char *host_id_loc = host_id;
-
- ret = lockdownd_get_device_public_key(client, &public_key);
- if (ret != LOCKDOWN_E_SUCCESS) {
- log_debug_msg("%s: device refused to send public key.\n", __func__);
- return ret;
- }
- log_debug_msg("%s: device public key follows:\n%s\n", __func__, public_key.data);
+ if (pair_record && 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;
+ }
- ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert);
- if (ret != LOCKDOWN_E_SUCCESS) {
- free(public_key.data);
- return ret;
- }
+ /* use passed pair_record */
+ dict_record = lockdownd_pair_record_to_plist(pair_record);
- if (!host_id) {
- userpref_get_host_id(&host_id_loc);
+ 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.data);
+ /* get libiphone 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;
+ }
}
/* Setup Pair request plist */
dict = plist_new_dict();
- dict_record = plist_new_dict();
+ plist_dict_add_label(dict, client->label);
plist_dict_insert_item(dict,"PairRecord", dict_record);
-
- plist_dict_insert_item(dict_record, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size));
- plist_dict_insert_item(dict_record, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size));
- plist_dict_insert_item(dict_record, "HostID", plist_new_string(host_id_loc));
- plist_dict_insert_item(dict_record, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size));
-
plist_dict_insert_item(dict, "Request", plist_new_string(verb));
/* send to iPhone */
@@ -695,10 +792,6 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, char *host
plist_free(dict);
dict = NULL;
- if (!host_id) {
- free(host_id_loc);
- }
-
if (ret != LOCKDOWN_E_SUCCESS)
return ret;
@@ -711,17 +804,40 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, char *host
if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) {
ret = LOCKDOWN_E_PAIRING_FAILED;
}
- plist_free(dict);
- dict = NULL;
- /* store public key in config if pairing succeeded */
+ /* if pairing succeeded */
if (ret == LOCKDOWN_E_SUCCESS) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: %s success\n", __func__, verb);
- userpref_set_device_public_key(client->uuid, public_key);
+ debug_info("%s success", verb);
+ if (!pairing_mode) {
+ if (!strcmp("Unpair", verb)) {
+ /* remove public key from config */
+ userpref_remove_device_public_key(client->uuid);
+ } else {
+ /* store public key in config */
+ userpref_set_device_public_key(client->uuid, public_key);
+ }
+ }
} else {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: %s failure\n", __func__, verb);
+ debug_info("%s failure", verb);
+ plist_t error_node = NULL;
+ /* verify error condition */
+ error_node = plist_dict_get_item(dict, "Error");
+ if (error_node) {
+ char *value = NULL;
+ plist_get_string_val(error_node, &value);
+ /* the first pairing fails if the device is password protected */
+ if (value && !strcmp(value, "PasswordProtected")) {
+ ret = LOCKDOWN_E_PASSWORD_PROTECTED;
+ free(value);
+ }
+ plist_free(error_node);
+ error_node = NULL;
+ }
}
- free(public_key.data);
+ plist_free(dict);
+ dict = NULL;
+ if (public_key.data)
+ free(public_key.data);
return ret;
}
@@ -730,15 +846,15 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, char *host
* It's part of the lockdownd handshake.
*
* @param client The lockdown client to pair with.
- * @param host_id The HostID to use for pairing. If NULL is passed, then
- * the HostID of the current machine is used. A new HostID will be
+ * @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 an error code (LOCKDOWN_E_SUCCESS on success)
*/
-lockdownd_error_t lockdownd_pair(lockdownd_client_t client, char *host_id)
+lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
{
- return lockdownd_do_pair(client, host_id, "Pair");
+ return lockdownd_do_pair(client, pair_record, "Pair");
}
/**
@@ -747,15 +863,31 @@ lockdownd_error_t lockdownd_pair(lockdownd_client_t client, char *host_id)
* It's part of the lockdownd handshake.
*
* @param client The lockdown client to pair with.
- * @param host_id The HostID to use for pairing. If NULL is passed, then
- * the HostID of the current machine is used. A new HostID will be
- * generated automatically when pairing is done for the first time.
+ * @param pair_record The pair record to validate pairing with. 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 an error code (LOCKDOWN_E_SUCCESS on success)
*/
-lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, char *host_id)
+lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
{
- return lockdownd_do_pair(client, host_id, "ValidatePair");
+ return lockdownd_do_pair(client, pair_record, "ValidatePair");
+}
+
+/**
+ * Unpairs the device with the given HostID and removes the pairing records
+ * from the device and host.
+ *
+ * @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 an error code (LOCKDOWN_E_SUCCESS on success)
+ */
+lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
+{
+ return lockdownd_do_pair(client, pair_record, "Unpair");
}
/**
@@ -773,9 +905,10 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
plist_t dict = plist_new_dict();
+ plist_dict_add_label(dict, client->label);
plist_dict_insert_item(dict,"Request", plist_new_string("EnterRecovery"));
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: telling device to enter recovery mode\n", __func__);
+ debug_info("telling device to enter recovery mode");
ret = lockdownd_send(client, dict);
plist_free(dict);
@@ -784,7 +917,7 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
ret = lockdownd_recv(client, &dict);
if (lockdown_check_result(dict, "EnterRecovery") == RESULT_SUCCESS) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: success\n", __func__);
+ debug_info("success");
ret = LOCKDOWN_E_SUCCESS;
}
plist_free(dict);
@@ -808,9 +941,10 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
plist_t dict = plist_new_dict();
+ plist_dict_add_label(dict, client->label);
plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye"));
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: called\n", __func__);
+ debug_info("called");
ret = lockdownd_send(client, dict);
plist_free(dict);
@@ -818,12 +952,12 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
ret = lockdownd_recv(client, &dict);
if (!dict) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: did not get goodbye response back\n", __func__);
+ debug_info("did not get goodbye response back");
return LOCKDOWN_E_PLIST_ERROR;
}
if (lockdown_check_result(dict, "Goodbye") == RESULT_SUCCESS) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: success\n", __func__);
+ debug_info("success");
ret = LOCKDOWN_E_SUCCESS;
}
plist_free(dict);
@@ -879,7 +1013,7 @@ lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datu
asn1_delete_structure(&pkcs1);
}
- /* now generate certifcates */
+ /* now generate certificates */
if (LOCKDOWN_E_SUCCESS == ret && 0 != modulus.size && 0 != exponent.size) {
gnutls_global_init();
@@ -973,24 +1107,29 @@ lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datu
* and if the device requires it, switches to SSL mode.
*
* @param client The lockdownd client
- * @param HostID The HostID used with this phone
+ * @param host_id The HostID of the computer
+ * @param session_id The session_id of the created session
+ * @param ssl_enabled Whether SSL communication is used in the session
*
* @return an error code (LOCKDOWN_E_SUCCESS on success)
*/
-lockdownd_error_t lockdownd_start_ssl_session(lockdownd_client_t client, const char *HostID)
+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;
plist_t dict = NULL;
- uint32_t return_me = 0;
- lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
+ if (!client || !host_id)
+ ret = LOCKDOWN_E_INVALID_ARG;
+
+ /* if we have a running session, stop current one first */
if (client->session_id) {
- free(client->session_id);
- client->session_id = NULL;
+ lockdownd_stop_session(client, client->session_id);
}
- /* Setup DevicePublicKey request plist */
+ /* setup request plist */
dict = plist_new_dict();
- plist_dict_insert_item(dict,"HostID", plist_new_string(HostID));
+ 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"));
ret = lockdownd_send(client, dict);
@@ -1010,183 +1149,54 @@ lockdownd_error_t lockdownd_start_ssl_session(lockdownd_client_t client, const c
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")) {
- /* hostid is unknown. Pair and try again */
- char *host_id = NULL;
- userpref_get_host_id(&host_id);
-
- if (LOCKDOWN_E_SUCCESS == lockdownd_pair(client, host_id) ) {
- /* start session again */
- plist_free(dict);
- dict = plist_new_dict();
- plist_dict_insert_item(dict,"HostID", plist_new_string(HostID));
- plist_dict_insert_item(dict,"Request", plist_new_string("StartSession"));
-
- ret = lockdownd_send(client, dict);
- plist_free(dict);
- dict = NULL;
-
- ret = lockdownd_recv(client, &dict);
- }
- free(host_id);
+ ret = LOCKDOWN_E_INVALID_HOST_ID;
}
free(error);
}
- }
-
- ret = LOCKDOWN_E_SSL_ERROR;
-
- int session_ok = 0;
- uint8_t UseSSL = 0;
+ } else {
+ uint8_t use_ssl = 0;
- if (lockdown_check_result(dict, "StartSession") == RESULT_SUCCESS) {
plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL");
if (enable_ssl && (plist_get_node_type(enable_ssl) == PLIST_BOOLEAN)) {
- plist_get_bool_val(enable_ssl, &UseSSL);
+ plist_get_bool_val(enable_ssl, &use_ssl);
}
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: Session startup OK\n", __func__);
- session_ok = 1;
- }
- if (session_ok && !UseSSL) {
- client->in_SSL = 0;
- ret = LOCKDOWN_E_SUCCESS;
- } else if (session_ok) {
- // Set up GnuTLS...
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: Switching to SSL mode\n", __func__);
- errno = 0;
- gnutls_global_init();
- //gnutls_anon_allocate_client_credentials(&anoncred);
- gnutls_certificate_allocate_credentials(&client->ssl_certificate);
- gnutls_certificate_set_x509_trust_file(client->ssl_certificate, "hostcert.pem", GNUTLS_X509_FMT_PEM);
- gnutls_init(&client->ssl_session, GNUTLS_CLIENT);
- {
- int protocol_priority[16] = { GNUTLS_SSL3, 0 };
- int kx_priority[16] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA, 0 };
- int cipher_priority[16] = { GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 };
- int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 };
- int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
-
- gnutls_cipher_set_priority(client->ssl_session, cipher_priority);
- gnutls_compression_set_priority(client->ssl_session, comp_priority);
- gnutls_kx_set_priority(client->ssl_session, kx_priority);
- gnutls_protocol_set_priority(client->ssl_session, protocol_priority);
- gnutls_mac_set_priority(client->ssl_session, mac_priority);
+ debug_info("Session startup OK");
+
+ if (ssl_enabled != NULL)
+ *ssl_enabled = use_ssl;
+
+ /* store session id, we need it for StopSession */
+ plist_t session_node = plist_dict_get_item(dict, "SessionID");
+ if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) {
+ plist_get_string_val(session_node, &client->session_id);
}
- gnutls_credentials_set(client->ssl_session, GNUTLS_CRD_CERTIFICATE, client->ssl_certificate); // this part is killing me.
-
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: GnuTLS step 1...\n", __func__);
- gnutls_transport_set_ptr(client->ssl_session, (gnutls_transport_ptr_t) client);
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: GnuTLS step 2...\n", __func__);
- gnutls_transport_set_push_function(client->ssl_session, (gnutls_push_func) & lockdownd_secuwrite);
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: GnuTLS step 3...\n", __func__);
- gnutls_transport_set_pull_function(client->ssl_session, (gnutls_pull_func) & lockdownd_securead);
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: GnuTLS step 4 -- now handshaking...\n", __func__);
- if (errno)
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: WARN: errno says %s before handshake!\n", __func__, strerror(errno));
- return_me = gnutls_handshake(client->ssl_session);
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: GnuTLS handshake done...\n", __func__);
-
- if (return_me != GNUTLS_E_SUCCESS) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: GnuTLS reported something wrong.\n", __func__);
- gnutls_perror(return_me);
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: oh.. errno says %s\n", __func__, strerror(errno));
- return LOCKDOWN_E_SSL_ERROR;
+ if (client->session_id) {
+ debug_info("SessionID: %s", client->session_id);
+ if (session_id != NULL)
+ *session_id = strdup(client->session_id);
} else {
- client->in_SSL = 1;
+ debug_info("Failed to get SessionID!");
+ }
+ debug_info("Enable SSL Session: %s", (use_ssl?"true":"false"));
+ if (use_ssl) {
+ ret = property_list_service_enable_ssl(client->parent);
+ if (ret == PROPERTY_LIST_SERVICE_E_SUCCESS) {
+ client->ssl_enabled = 1;
+ } else {
+ ret = LOCKDOWN_E_SSL_ERROR;
+ client->ssl_enabled = 0;
+ }
+ } else {
+ client->ssl_enabled = 0;
ret = LOCKDOWN_E_SUCCESS;
}
}
- /* store session id, we need it for StopSession */
- plist_t session_node = plist_dict_get_item(dict, "SessionID");
- if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) {
- plist_get_string_val(session_node, &client->session_id);
- }
- if (client->session_id) {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: SessionID: %s\n", __func__, client->session_id);
- } else {
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: Failed to get SessionID!\n", __func__);
- }
+
plist_free(dict);
dict = NULL;
- if (ret == LOCKDOWN_E_SUCCESS)
- return ret;
-
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: Apparently failed negotiating with lockdownd.\n", __func__);
- return LOCKDOWN_E_SSL_ERROR;
-}
-
-/** gnutls callback for writing data to the iPhone.
- *
- * @param transport It's really the lockdownd client, but the method signature has to match
- * @param buffer The data to send
- * @param length The length of data to send in bytes
- *
- * @return The number of bytes sent
- */
-ssize_t lockdownd_secuwrite(gnutls_transport_ptr_t transport, char *buffer, size_t length)
-{
- uint32_t bytes = 0;
- lockdownd_client_t client;
- client = (lockdownd_client_t) transport;
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: called\n", __func__);
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: pre-send length = %zi\n", __func__, length);
- iphone_device_send(client->connection, buffer, length, &bytes);
- log_dbg_msg(DBGMASK_LOCKDOWND, "%s: post-send sent %i bytes\n", __func__, bytes);
- return bytes;
-}
-
-/** gnutls callback for reading data from the iPhone
- *
- * @param transport It's really the lockdownd client, but the method signature has to match
- * @param buffer The buffer to store data in
- * @param length The length of data to read in bytes
- *
- * @return The number of bytes read
- */
-ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_t length)
-{
- int bytes = 0, pos_start_fill = 0;
- size_t tbytes = 0;
- int this_len = length;
- iphone_error_t res;
- lockdownd_client_t client;
- client = (lockdownd_client_t) transport;
- char *recv_buffer;
-
- log_debug_msg("%s: pre-read client wants %zi bytes\n", __func__, length);
-
- recv_buffer = (char *) malloc(sizeof(char) * this_len);
-
- // repeat until we have the full data or an error occurs.
- do {
- if ((res = iphone_device_recv(client->connection, recv_buffer, this_len, (uint32_t*)&bytes)) != LOCKDOWN_E_SUCCESS) {
- log_debug_msg("%s: ERROR: usbmux_recv returned %d\n", __func__, res);
- return res;
- }
- log_debug_msg("%s: post-read we got %i bytes\n", __func__, bytes);
-
- // increase read count
- tbytes += bytes;
-
- // fill the buffer with what we got right now
- memcpy(buffer + pos_start_fill, recv_buffer, bytes);
- pos_start_fill += bytes;
-
- if (tbytes >= length) {
- break;
- }
-
- this_len = length - tbytes;
- log_debug_msg("%s: re-read trying to read missing %i bytes\n", __func__, this_len);
- } while (tbytes < length);
-
- if (recv_buffer) {
- free(recv_buffer);
- }
-
- return tbytes;
+ return ret;
}
/** Command to start the desired service
@@ -1197,7 +1207,7 @@ ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_
* @return an error code (LOCKDOWN_E_SUCCESS on success)
*/
-lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, int *port)
+lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, uint16_t *port)
{
if (!client || !service || !port)
return LOCKDOWN_E_INVALID_ARG;
@@ -1206,17 +1216,18 @@ lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char
userpref_get_host_id(&host_id);
if (!host_id)
return LOCKDOWN_E_INVALID_CONF;
- if (!client->in_SSL && !lockdownd_start_ssl_session(client, host_id))
- return LOCKDOWN_E_SSL_ERROR;
+ if (!client->session_id)
+ return LOCKDOWN_E_NO_RUNNING_SESSION;
plist_t dict = NULL;
- uint32_t port_loc = 0;
+ 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));
@@ -1260,3 +1271,97 @@ lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char
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 control The lockdown client
+ * @param activation_record The activation record plist dictionary
+ *
+ * @return an error code (LOCKDOWN_E_SUCCESS on success)
+ */
+lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record)
+{
+ if (!client)
+ return LOCKDOWN_E_INVALID_ARG;
+
+ if (!client->session_id)
+ return LOCKDOWN_E_NO_RUNNING_SESSION;
+
+ if (!activation_record)
+ return LOCKDOWN_E_INVALID_ARG;
+
+ lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
+
+ 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", activation_record);
+
+ ret = lockdownd_send(client, dict);
+ plist_free(dict);
+ dict = NULL;
+
+ ret = lockdownd_recv(client, &dict);
+ if (!dict) {
+ debug_info("LOCKDOWN_E_PLIST_ERROR");
+ return LOCKDOWN_E_PLIST_ERROR;
+ }
+
+ ret = LOCKDOWN_E_ACTIVATION_FAILED;
+ if (lockdown_check_result(dict, "Activate") == RESULT_SUCCESS) {
+ debug_info("success");
+ ret = LOCKDOWN_E_SUCCESS;
+ }
+ plist_free(dict);
+ dict = NULL;
+
+ return ret;
+}
+
+/**
+ * Deactivates the device, returning it to the locked
+ * “Activate with iTunes” screen.
+ *
+ * @param control The lockdown client
+ *
+ * @return an error code (LOCKDOWN_E_SUCCESS on success)
+ */
+lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
+{
+ if (!client)
+ return LOCKDOWN_E_INVALID_ARG;
+
+ if (!client->session_id)
+ return LOCKDOWN_E_NO_RUNNING_SESSION;
+
+ lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
+
+ plist_t dict = plist_new_dict();
+ plist_dict_add_label(dict, client->label);
+ plist_dict_insert_item(dict,"Request", plist_new_string("Deactivate"));
+
+ ret = lockdownd_send(client, dict);
+ plist_free(dict);
+ dict = NULL;
+
+ ret = lockdownd_recv(client, &dict);
+ if (!dict) {
+ debug_info("LOCKDOWN_E_PLIST_ERROR");
+ return LOCKDOWN_E_PLIST_ERROR;
+ }
+
+ ret = LOCKDOWN_E_UNKNOWN_ERROR;
+ if (lockdown_check_result(dict, "Deactivate") == RESULT_SUCCESS) {
+ debug_info("success");
+ ret = LOCKDOWN_E_SUCCESS;
+ }
+ plist_free(dict);
+ dict = NULL;
+
+ return ret;
+}
+