From 182b08380564a4c9c7d5dd35d49cd0f3f5dd3f34 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Thu, 29 Aug 2019 10:01:13 +0200 Subject: Add support to "preboard" a device on update restore to prevent 'Attempting data recovery' --- src/idevicerestore.c | 103 +++++++++++++++++++++++ src/idevicerestore.h | 1 + src/normal.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/normal.h | 4 +- src/tss.c | 8 ++ 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 #include #include -#include #include +#include +#include #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); -- cgit v1.1-32-gdbae