summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2019-08-29 10:01:13 +0200
committerGravatar Nikias Bassen2019-08-29 10:01:13 +0200
commit182b08380564a4c9c7d5dd35d49cd0f3f5dd3f34 (patch)
treec3ee9e1a475fc83575216b9171f978bd9eb63392
parentc09c07b33038cc333daf1e15f3abbbe522b1de62 (diff)
downloadidevicerestore-182b08380564a4c9c7d5dd35d49cd0f3f5dd3f34.tar.gz
idevicerestore-182b08380564a4c9c7d5dd35d49cd0f3f5dd3f34.tar.bz2
Add support to "preboard" a device on update restore to prevent 'Attempting data recovery'
-rw-r--r--src/idevicerestore.c103
-rw-r--r--src/idevicerestore.h1
-rw-r--r--src/normal.c230
-rw-r--r--src/normal.h4
-rw-r--r--src/tss.c8
5 files changed, 343 insertions, 3 deletions
diff --git a/src/idevicerestore.c b/src/idevicerestore.c
index 1918987..10f8e25 100644
--- a/src/idevicerestore.c
+++ b/src/idevicerestore.c
@@ -813,6 +813,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client)
/* retrieve shsh blobs if required */
if (tss_enabled) {
+ int stashbag_commit_required = 0;
debug("Getting device's ECID for TSS request\n");
/* fetch the device's ECID for the TSS request */
if (get_ecid(client, &client->ecid) < 0) {
@@ -821,6 +822,35 @@ int idevicerestore_start(struct idevicerestore_client_t* client)
}
info("Found ECID " FMT_qu "\n", (long long unsigned int)client->ecid);
+ if (client->mode->index == MODE_NORMAL && !(client->flags & FLAG_ERASE) && !(client->flags & FLAG_SHSHONLY)) {
+ plist_t node = normal_get_lockdown_value(client, NULL, "HasSiDP");
+ uint8_t needs_preboard = 0;
+ if (node && plist_get_node_type(node) == PLIST_BOOLEAN) {
+ plist_get_bool_val(node, &needs_preboard);
+ }
+ if (needs_preboard) {
+ info("Checking if device requires stashbag...\n");
+ plist_t manifest;
+ if (get_preboard_manifest(client, build_identity, &manifest) < 0) {
+ error("ERROR: Unable to create preboard manifest.\n");
+ return -1;
+ }
+ debug("DEBUG: creating stashbag...\n");
+ int err = normal_handle_create_stashbag(client, manifest);
+ if (err < 0) {
+ if (err == -2) {
+ error("ERROR: Could not create stashbag (timeout).\n");
+ } else {
+ error("ERROR: An error occurred while creating the stashbag.\n");
+ }
+ return -1;
+ } else if (err == 1) {
+ stashbag_commit_required = 1;
+ }
+ plist_free(manifest);
+ }
+ }
+
if (client->build_major > 8) {
unsigned char* nonce = NULL;
int nonce_size = 0;
@@ -844,6 +874,19 @@ int idevicerestore_start(struct idevicerestore_client_t* client)
error("ERROR: Unable to get SHSH blobs for this device\n");
return -1;
}
+ if (stashbag_commit_required) {
+ plist_t ticket = plist_dict_get_item(client->tss, "ApImg4Ticket");
+ if (!ticket || plist_get_node_type(ticket) != PLIST_DATA) {
+ error("ERROR: Missing ApImg4Ticket in TSS response for stashbag commit\n");
+ return -1;
+ }
+ info("Committing stashbag...\n");
+ int err = normal_handle_commit_stashbag(client, ticket);
+ if (err < 0) {
+ error("ERROR: Could not commit stashbag (%d). Aborting.\n", err);
+ return -1;
+ }
+ }
}
if (client->flags & FLAG_SHSHONLY) {
@@ -1601,6 +1644,66 @@ plist_t build_manifest_get_build_identity_for_model(plist_t build_manifest, cons
return build_manifest_get_build_identity_for_model_with_restore_behavior(build_manifest, hardware_model, NULL);
}
+int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* manifest)
+{
+ plist_t request = NULL;
+ *manifest = NULL;
+
+ if (!client->image4supported) {
+ return -1;
+ }
+
+ /* populate parameters */
+ plist_t parameters = plist_new_dict();
+
+ plist_t overrides = plist_new_dict();
+ plist_dict_set_item(overrides, "@APTicket", plist_new_bool(1));
+ plist_dict_set_item(overrides, "ApProductionMode", plist_new_uint(0));
+ plist_dict_set_item(overrides, "ApSecurityDomain", plist_new_uint(0));
+
+ plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(0));
+ plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(0));
+
+ tss_parameters_add_from_manifest(parameters, build_identity);
+
+ /* create basic request */
+ request = tss_request_new(NULL);
+ if (request == NULL) {
+ error("ERROR: Unable to create TSS request\n");
+ plist_free(parameters);
+ return -1;
+ }
+
+ /* add common tags from manifest */
+ if (tss_request_add_common_tags(request, parameters, overrides) < 0) {
+ error("ERROR: Unable to add common tags\n");
+ plist_free(request);
+ plist_free(parameters);
+ return -1;
+ }
+
+ plist_dict_set_item(parameters, "_OnlyFWComponents", plist_new_bool(1));
+
+ /* add tags from manifest */
+ if (tss_request_add_ap_tags(request, parameters, NULL) < 0) {
+ error("ERROR: Unable to add ap tags\n");
+ plist_free(request);
+ plist_free(parameters);
+ return -1;
+ }
+
+ plist_t local_manifest = NULL;
+ int res = img4_create_local_manifest(request, &local_manifest);
+
+ *manifest = local_manifest;
+
+ plist_free(request);
+ plist_free(parameters);
+ plist_free(overrides);
+
+ return res;
+}
+
int get_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss) {
plist_t request = NULL;
plist_t response = NULL;
diff --git a/src/idevicerestore.h b/src/idevicerestore.h
index 0c638e4..7f76c91 100644
--- a/src/idevicerestore.h
+++ b/src/idevicerestore.h
@@ -95,6 +95,7 @@ int build_identity_get_component_path(plist_t build_identity, const char* compon
int ipsw_extract_filesystem(const char* ipsw, plist_t build_identity, char** filesystem);
int extract_component(const char* ipsw, const char* path, unsigned char** component_data, unsigned int* component_size);
int personalize_component(const char *component, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** personalized_component, unsigned int* personalized_component_size);
+int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* manifest);
const char* get_component_name(const char* filename);
diff --git a/src/normal.c b/src/normal.c
index e09dd28..4dd5d5c 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -2,7 +2,7 @@
* normal.h
* Functions for handling idevices in normal mode
*
- * Copyright (c) 2012-2015 Nikias Bassen. All Rights Reserved.
+ * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved.
* Copyright (c) 2012 Martin Szulecki. All Rights Reserved.
* Copyright (c) 2010 Joshua Hill. All Rights Reserved.
*
@@ -26,8 +26,9 @@
#include <string.h>
#include <unistd.h>
#include <libirecovery.h>
-#include <libimobiledevice/lockdown.h>
#include <libimobiledevice/libimobiledevice.h>
+#include <libimobiledevice/lockdown.h>
+#include <libimobiledevice/preboard.h>
#include "common.h"
#include "normal.h"
@@ -230,6 +231,16 @@ irecv_device_t normal_get_irecv_device(struct idevicerestore_client_t* client) {
}
lockdown_error = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore");
+ if (!(client->flags & FLAG_ERASE) && lockdown_error == LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING) {
+ info("*** Device is not paired with this computer. Please trust this computer on the device to continue. ***\n");
+ while (1) {
+ lockdown_error = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore");
+ if (lockdown_error != LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING) {
+ break;
+ }
+ sleep(1);
+ }
+ }
if (lockdown_error != LOCKDOWN_E_SUCCESS) {
lockdown_error = lockdownd_client_new(device, &lockdown, "idevicerestore");
}
@@ -400,3 +411,218 @@ int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *p
return 0;
}
+
+int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_t manifest)
+{
+ int result = -1;
+
+ idevice_t device = NULL;
+ idevice_error_t device_err;
+ lockdownd_client_t lockdown;
+ lockdownd_service_descriptor_t service = NULL;
+ lockdownd_error_t lerr;
+ preboard_client_t preboard = NULL;
+ preboard_error_t perr;
+
+ device_err = idevice_new(&device, client->udid);
+ if (device_err != IDEVICE_E_SUCCESS) {
+ error("ERROR: Could not connect to device (%d)\n", device_err);
+ return -1;
+ }
+
+ lerr = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore");
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ error("ERROR: Could not connect to lockdownd (%d)\n", lerr);
+ idevice_free(device);
+ return -1;
+ }
+
+ lerr = lockdownd_start_service(lockdown, PREBOARD_SERVICE_NAME, &service);
+ if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) {
+ info("*** Device is locked. Please unlock the device to continue. ***\n");
+ while (1) {
+ lerr = lockdownd_start_service(lockdown, PREBOARD_SERVICE_NAME, &service);
+ if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) {
+ break;
+ }
+ sleep(1);
+ }
+ }
+
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ error("ERROR: Could not start preboard service (%d)\n", lerr);
+ lockdownd_client_free(lockdown);
+ idevice_free(device);
+ return -1;
+ }
+
+ perr = preboard_client_new(device, service, &preboard);
+ lockdownd_service_descriptor_free(service);
+ lockdownd_client_free(lockdown);
+ if (perr != PREBOARD_E_SUCCESS) {
+ error("ERROR: Could not connect to preboard service (%d)\n", perr);
+ idevice_free(device);
+ return -1;
+ }
+
+ perr = preboard_create_stashbag(preboard, manifest, NULL, NULL);
+ if (perr != PREBOARD_E_SUCCESS) {
+ error("ERROR: Failed to trigger stashbag creation (%d)\n", perr);
+ preboard_client_free(preboard);
+ idevice_free(device);
+ return -1;
+ }
+
+ int ticks = 0;
+ while (ticks++ < 130) {
+ plist_t pl = NULL;
+ perr = preboard_receive_with_timeout(preboard, &pl, 1000);
+ if (perr == PREBOARD_E_TIMEOUT) {
+ continue;
+ } else if (perr != PREBOARD_E_SUCCESS) {
+ error("ERROR: could not receive from preboard service\n");
+ break;
+ } else {
+ plist_t node;
+
+ if (_plist_dict_get_bool(pl, "Skip")) {
+ result = 0;
+ info("Device does not require stashbag.\n");
+ break;
+ }
+
+ if (_plist_dict_get_bool(pl, "ShowDialog")) {
+ info("Device requires stashbag.\n");
+ printf("******************************************************************************\n"
+ "* Please enter your passcode on the device. The device will store a token *\n"
+ "* that will be used after restore to access the user data partition. This *\n"
+ "* prevents an 'Attempting data recovery' process occurring after reboot that *\n"
+ "* may take a long time to complete and will _also_ require the passcode. *\n"
+ "******************************************************************************\n");
+ plist_free(pl);
+ continue;
+ }
+ node = plist_dict_get_item(pl, "Error");
+ if (node) {
+ char *strval = NULL;
+ node = plist_dict_get_item(pl, "ErrorString");
+ if (node) {
+ plist_get_string_val(node, &strval);
+ }
+ error("ERROR: Could not create stashbag: %s\n", (strval) ? strval : "(Unknown error)");
+ free(strval);
+ plist_free(pl);
+ break;
+ }
+ if (_plist_dict_get_bool(pl, "Timeout")) {
+ error("ERROR: Timeout while waiting for user to enter passcode.\n");
+ result = -2;
+ plist_free(pl);
+ break;
+ }
+ if (_plist_dict_get_bool(pl, "HideDialog")) {
+ plist_free(pl);
+ /* hide dialog */
+ result = 1;
+ info("Stashbag created.\n");
+ break;
+ }
+ }
+ plist_free(pl);
+ }
+ preboard_client_free(preboard);
+ idevice_free(device);
+
+ return result;
+}
+
+int normal_handle_commit_stashbag(struct idevicerestore_client_t* client, plist_t manifest)
+{
+ int result = -1;
+
+ idevice_t device = NULL;
+ idevice_error_t device_err;
+ lockdownd_client_t lockdown;
+ lockdownd_service_descriptor_t service = NULL;
+ lockdownd_error_t lerr;
+ preboard_client_t preboard = NULL;
+ preboard_error_t perr;
+ plist_t pl = NULL;
+
+ device_err = idevice_new(&device, client->udid);
+ if (device_err != IDEVICE_E_SUCCESS) {
+ error("ERROR: Could not connect to device (%d)\n", device_err);
+ return -1;
+ }
+
+ lerr = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore");
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ error("ERROR: Could not connect to lockdownd (%d)\n", lerr);
+ idevice_free(device);
+ return -1;
+ }
+
+ lerr = lockdownd_start_service(lockdown, PREBOARD_SERVICE_NAME, &service);
+ if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) {
+ info("*** Device is locked. Please unlock the device to continue. ***\n");
+ while (1) {
+ lerr = lockdownd_start_service(lockdown, PREBOARD_SERVICE_NAME, &service);
+ if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) {
+ break;
+ }
+ sleep(1);
+ }
+ }
+
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ error("ERROR: Could not start preboard service (%d)\n", lerr);
+ lockdownd_client_free(lockdown);
+ idevice_free(device);
+ return -1;
+ }
+
+ perr = preboard_client_new(device, service, &preboard);
+ lockdownd_service_descriptor_free(service);
+ lockdownd_client_free(lockdown);
+ if (perr != PREBOARD_E_SUCCESS) {
+ error("ERROR: Could not connect to preboard service (%d)\n", perr);
+ idevice_free(device);
+ return -1;
+ }
+
+ perr = preboard_commit_stashbag(preboard, manifest, NULL, NULL);
+ if (perr != PREBOARD_E_SUCCESS) {
+ error("ERROR: Failed to trigger stashbag creation (%d)\n", perr);
+ preboard_client_free(preboard);
+ idevice_free(device);
+ return -1;
+ }
+
+ perr = preboard_receive_with_timeout(preboard, &pl, 30000);
+ if (perr != PREBOARD_E_SUCCESS) {
+ error("ERROR: could not receive from preboard service (%d)\n", perr);
+ } else {
+ int commit_complete = 0;
+ plist_t node = plist_dict_get_item(pl, "Error");
+ if (node) {
+ char *strval = NULL;
+ node = plist_dict_get_item(pl, "ErrorString");
+ if (node) {
+ plist_get_string_val(node, &strval);
+ }
+ error("ERROR: Could not commit stashbag: %s\n", (strval) ? strval : "(Unknown error)");
+ free(strval);
+ } else if (_plist_dict_get_bool(pl, "StashbagCommitComplete")) {
+ info("Stashbag committed!\n");
+ result = 0;
+ } else {
+ error("ERROR: Unexpected reply from preboard service\n");
+ debug_plist(pl);
+ }
+ plist_free(pl);
+ }
+ preboard_client_free(preboard);
+ idevice_free(device);
+
+ return result;
+}
diff --git a/src/normal.h b/src/normal.h
index 5de6127..bafafcc 100644
--- a/src/normal.h
+++ b/src/normal.h
@@ -2,8 +2,8 @@
* normal.h
* Functions for handling idevices in normal mode
*
+ * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved.
* Copyright (c) 2012 Martin Szulecki. All Rights Reserved.
- * Copyright (c) 2012-2015 Nikias Bassen. All Rights Reserved.
* Copyright (c) 2010 Joshua Hill. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
@@ -52,6 +52,8 @@ int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char**
int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size);
int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info);
plist_t normal_get_lockdown_value(struct idevicerestore_client_t* client, const char* domain, const char* key);
+int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_t manifest);
+int normal_handle_commit_stashbag(struct idevicerestore_client_t* client, plist_t manifest);
#ifdef __cplusplus
}
diff --git a/src/tss.c b/src/tss.c
index 2fae246..391d730 100644
--- a/src/tss.c
+++ b/src/tss.c
@@ -574,6 +574,14 @@ int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrid
continue;
}
+ if (_plist_dict_get_bool(parameters, "_OnlyFWComponents")) {
+ plist_t info_dict = plist_dict_get_item(manifest_entry, "Info");
+ if (!_plist_dict_get_bool(manifest_entry, "Trusted") && !_plist_dict_get_bool(info_dict, "IsFirmwarePayload") && !_plist_dict_get_bool(info_dict, "IsSecondaryFirmwarePayload") && !_plist_dict_get_bool(info_dict, "IsFUDFirmware")) {
+ debug("DEBUG: %s: Skipping '%s' as it is neither firmware nor secondary firmware payload\n", __func__, key);
+ continue;
+ }
+ }
+
/* copy this entry */
plist_t tss_entry = plist_copy(manifest_entry);