diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/ace3.c | 244 | ||||
-rw-r--r-- | src/ace3.h | 17 | ||||
-rw-r--r-- | src/asr.c | 70 | ||||
-rw-r--r-- | src/asr.h | 9 | ||||
-rw-r--r-- | src/common.c | 99 | ||||
-rw-r--r-- | src/common.h | 18 | ||||
-rw-r--r-- | src/dfu.c | 129 | ||||
-rw-r--r-- | src/dfu.h | 9 | ||||
-rw-r--r-- | src/fdr.c | 2 | ||||
-rw-r--r-- | src/idevicerestore.c | 883 | ||||
-rw-r--r-- | src/idevicerestore.h | 18 | ||||
-rw-r--r-- | src/img4.c | 238 | ||||
-rw-r--r-- | src/img4.h | 2 | ||||
-rw-r--r-- | src/ipsw.c | 427 | ||||
-rw-r--r-- | src/ipsw.h | 46 | ||||
-rw-r--r-- | src/jsmn.c | 280 | ||||
-rw-r--r-- | src/jsmn.h | 91 | ||||
-rw-r--r-- | src/json_plist.c | 229 | ||||
-rw-r--r-- | src/json_plist.h | 34 | ||||
-rw-r--r-- | src/normal.c | 69 | ||||
-rw-r--r-- | src/normal.h | 5 | ||||
-rw-r--r-- | src/recovery.c | 44 | ||||
-rw-r--r-- | src/recovery.h | 5 | ||||
-rw-r--r-- | src/restore.c | 994 | ||||
-rw-r--r-- | src/restore.h | 9 | ||||
-rw-r--r-- | src/tss.c | 1133 | ||||
-rw-r--r-- | src/tss.h | 5 |
28 files changed, 2708 insertions, 2408 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 4a99dec..722487a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,6 +3,7 @@ AM_CFLAGS = \ $(LFS_CFLAGS) \ $(libirecovery_CFLAGS) \ $(libimobiledevice_CFLAGS) \ + $(libusbmuxd_CFLAGS) \ $(libplist_CFLAGS) \ $(limd_glue_CFLAGS) \ $(libzip_CFLAGS) \ @@ -14,6 +15,7 @@ AM_LDFLAGS = \ $(AC_LDFLAGS) \ $(libirecovery_LIBS) \ $(libimobiledevice_LIBS) \ + $(libusbmuxd_LIBS) \ $(libplist_LIBS) \ $(limd_glue_LIBS) \ $(libzip_LIBS) \ @@ -42,12 +44,11 @@ idevicerestore_SOURCES = \ restore.c restore.h \ asr.c asr.h \ fdr.c fdr.h \ + ace3.c ace3.h \ limera1n_payload.h \ limera1n.c limera1n.h \ download.c download.h \ - locking.c locking.h \ - jsmn.c jsmn.h \ - json_plist.c json_plist.h + locking.c locking.h if USE_INTERNAL_SHA idevicerestore_SOURCES += sha1.c sha1.h sha512.c sha512.h fixedint.h endif diff --git a/src/ace3.c b/src/ace3.c new file mode 100644 index 0000000..b96e6b4 --- /dev/null +++ b/src/ace3.c @@ -0,0 +1,244 @@ +/* + * ace3.c + * Functions to handle Ace3/uarp firmware format + * + * Copyright (c) 2024 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <stdint.h> +#include <stdlib.h> +#include <libimobiledevice-glue/nskeyedarchive.h> + +#include "common.h" +#include "ace3.h" +#include "endianness.h" + +static uint32_t crc_buffer(const unsigned char* buffer, unsigned int bufsize, unsigned int salt) +{ + uint32_t result; + unsigned int i; + unsigned int j; + + if ( !buffer ) + return 0xFFFFFFFF; + result = salt; + for (i = 0; i < bufsize; ++i) { + for (j = 0; j != 8; ++j) { + unsigned int tmp0 = 2 * result; + unsigned int tmp1 = *(unsigned char*)(buffer + i); + unsigned int tmp2 = ((unsigned int)result >> 31) ^ ((tmp1 >> j) & 1); + result = (tmp2 + 2 * result) ^ 0x4C11DB6; + if (!tmp2) + result = tmp0; + } + } + return result; +} + +int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size) +{ + struct ace3bin_header { + uint32_t magic; // 0xACE00003 + uint32_t unk4; // 0x00203400 + uint32_t unk8; // 0x00002800 + uint32_t header_size; // 0x00000040 + uint32_t data1_size; + uint32_t data2_size; + uint32_t im4m_offset; + uint32_t im4m_dl_size; + uint32_t content_size; + uint32_t crc; + uint64_t fill1; // 0xFFFFFFFFFFFFFFFF + uint64_t fill2; // 0xFFFFFFFFFFFFFFFF + uint64_t fill3; // 0xFFFFFFFFFFFFFFFF + }; + + struct uarp_header { + uint32_t unk_00; // BE 0x00000002 + uint32_t header_size; // BE usually 0x0000002C + uint32_t plist_offset; // BE + uint32_t unk_0c; // 0 + uint32_t unk_10; // 0 + uint32_t unk_14; // 0 + uint32_t unk_18; // 0 + uint32_t c_offset; // BE + uint32_t unk_20; // 0 + uint32_t toc_offset; // BE usually 0x0000002c + uint32_t toc_size; // BE + }; + struct uarp_toc_entry { + uint32_t this_size; // BE usually 0x28 + uint32_t fourcc; // 'PT01' or similar + uint32_t index; // BE starting with 0, increment+1 for each entry + uint32_t unk_0c; // BE usually not zero + uint32_t unk_10; // BE usually 0 + uint32_t unk_14; // BE usually 0 + uint32_t unk_18; // BE other offset, not sure + uint32_t unk_1c; // BE usually 0 + uint32_t offset; // BE + uint32_t size; // + }; + + plist_t p_im4m = plist_dict_get_item(tss, "USBPortController1,Ticket"); + uint64_t im4m_size = 0; + const char* im4m = plist_get_data_ptr(p_im4m, &im4m_size); + + struct uarp_header* uarp_hdr = (struct uarp_header*)uarp_fw; + uint32_t uarp_hdr_size = be32toh(uarp_hdr->header_size); + uint32_t plist_offset = be32toh(uarp_hdr->plist_offset); + uint32_t plist_size = uarp_size - plist_offset; + nskeyedarchive_t ka = nskeyedarchive_new_from_data(uarp_fw + plist_offset, plist_size); + if (!ka) { + return -1; + } + plist_t uarp_dict = nskeyedarchive_to_plist(ka); + nskeyedarchive_free(ka); + + // find the corresponding entries for given BoardID+PREV + + char* payload_4cc = NULL; + char* data_payload_4ccs = NULL; + + plist_t sb_payloads = plist_dict_get_item(uarp_dict, "SuperBinary Payloads"); + if (PLIST_IS_ARRAY(sb_payloads)) { + plist_array_iter iter = NULL; + plist_array_new_iter(sb_payloads, &iter); + plist_t payload = NULL; + do { + plist_array_next_item(sb_payloads, iter, &payload); + if (!payload) { + break; + } + plist_t meta = plist_dict_get_item(payload, "Payload MetaData"); + if (!PLIST_IS_DICT(meta)) { + continue; + } + plist_t prefix = plist_dict_get_item(meta, "Personalization Manifest Prefix"); + if (!PLIST_IS_STRING(prefix)) { + continue; + } + if (strcmp(plist_get_string_ptr(prefix, NULL), "USBPortController") != 0) { + continue; + } + plist_t p_boardid = plist_dict_get_item(meta, "Personalization Board ID (64 bits)"); + if (!PLIST_IS_INT(p_boardid)) { + continue; + } + uint64_t boardid = 0; + plist_get_uint_val(p_boardid, &boardid); + if (boardid == bdid) { + plist_t p4cc = plist_dict_get_item(payload, "Payload 4CC"); + plist_get_string_val(p4cc, &payload_4cc); + plist_t matching = plist_dict_get_item(meta, "Personalization Matching Data"); + if (PLIST_IS_ARRAY(matching)) { + plist_array_iter iter2 = NULL; + plist_array_new_iter(matching, &iter2); + plist_t match = NULL; + do { + plist_array_next_item(matching, iter2, &match); + if (!PLIST_IS_DICT(match)) { + break; + } + uint64_t minrev = 0; + plist_t p_min = plist_dict_get_item(match, "Personalization Matching Data Product Revision Minimum"); + plist_get_uint_val(p_min, &minrev); + uint64_t maxrev = 0; + plist_t p_max = plist_dict_get_item(match, "Personalization Matching Data Product Revision Maximum"); + plist_get_uint_val(p_max, &maxrev); + if (prev >= minrev && prev <= maxrev) { + plist_t tags = plist_dict_get_item(match, "Personalization Matching Data Payload Tags"); + plist_get_string_val(tags, &data_payload_4ccs); + break; + } + } while (match); + plist_mem_free(iter2); + } + break; + } + } while (payload); + plist_mem_free(iter); + } + if (!payload_4cc) { + printf("Failed to get payload 4cc\n"); + return -1; + } + if (!data_payload_4ccs) { + printf("Failed to get data payload 4ccs\n"); + return -1; + } + + // now find the blobs in UARP data + uint32_t dl_offset = 0; + uint32_t dl_size = 0; + uint32_t data1_offset = 0; + uint32_t data1_size = 0; + uint32_t data2_offset = 0; + uint32_t data2_size = 0; + uint32_t toc_offset = be32toh(uarp_hdr->toc_offset); + uint32_t toc_size = be32toh(uarp_hdr->toc_size); + const unsigned char* p = uarp_fw + uarp_hdr_size; + while (p < uarp_fw + toc_size) { + struct uarp_toc_entry* entry = (struct uarp_toc_entry*)p; + uint32_t te_size = be32toh(entry->this_size); + if (strncmp((char*)&(entry->fourcc), payload_4cc, 4) == 0) { + dl_offset = be32toh(entry->offset); + dl_size = be32toh(entry->size); + } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs, 4) == 0) { + data1_offset = be32toh(entry->offset); + data1_size = be32toh(entry->size); + } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs+5, 4) == 0) { + data2_offset = be32toh(entry->offset); + data2_size = be32toh(entry->size); + } + p += te_size; + } + + uint32_t content_size = data1_size + data2_size + im4m_size + dl_size; + + *bin_out = (unsigned char*)malloc(0x40 + content_size); + struct ace3bin_header* hdr = (struct ace3bin_header*)(*bin_out); + hdr->magic = htole32(0xACE00003); + hdr->unk4 = htole32(0x00203400); + hdr->unk8 = htole32(0x00002800); + hdr->header_size = htole32(0x40); + hdr->data1_size = htole32(data1_size); + hdr->data2_size = htole32(data2_size);; + hdr->im4m_offset = htole32(0x40 + data1_size + data2_size); + hdr->im4m_dl_size = htole32(im4m_size + dl_size); + hdr->content_size = htole32(content_size); + hdr->crc = 0; + hdr->fill1 = 0xFFFFFFFFFFFFFFFFLL; + hdr->fill2 = 0xFFFFFFFFFFFFFFFFLL; + hdr->fill3 = 0xFFFFFFFFFFFFFFFFLL; + + // write data1 payload + memcpy(*bin_out + 0x40, uarp_fw + data1_offset, data1_size); + // write data2 payload + memcpy(*bin_out + 0x40 + data1_size, uarp_fw + data2_offset, data2_size); + // write IM4M + memcpy(*bin_out + 0x40 + data1_size + data2_size, im4m, im4m_size); + // write dl payload + memcpy(*bin_out + 0x40 + data1_size + data2_size + im4m_size, uarp_fw + dl_offset, dl_size); + + // calculate CRC and update header + hdr->crc = htole32(crc_buffer(*bin_out + 0x40, content_size, 0xFFFFFFFF)); + + *bin_size = 0x40 + content_size; + + return 0; +} diff --git a/src/ace3.h b/src/ace3.h new file mode 100644 index 0000000..2ef65a7 --- /dev/null +++ b/src/ace3.h @@ -0,0 +1,17 @@ +#ifndef IDEVICERESTORE_ACE3_H +#define IDEVICERESTORE_ACE3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <plist/plist.h> + +int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size); + +#ifdef __cplusplus +} +#endif + +#endif @@ -43,6 +43,7 @@ #include "asr.h" #include "idevicerestore.h" #include "common.h" +#include "ipsw.h" #define ASR_VERSION 1 #define ASR_STREAM_ID 1 @@ -205,9 +206,8 @@ void asr_free(asr_client_t asr) } } -int asr_perform_validation(asr_client_t asr, const char* filesystem) +int asr_perform_validation(asr_client_t asr, ipsw_file_handle_t file) { - FILE* file = NULL; uint64_t length = 0; char* command = NULL; plist_t node = NULL; @@ -216,20 +216,7 @@ int asr_perform_validation(asr_client_t asr, const char* filesystem) plist_t payload_info = NULL; int attempts = 0; - file = fopen(filesystem, "rb"); - if (file == NULL) { - return -1; - } - -#ifdef WIN32 - length = _lseeki64(fileno(file), 0, SEEK_END); - _lseeki64(fileno(file), 0, SEEK_SET); - rewind(file); -#else - fseeko(file, 0, SEEK_END); - length = ftello(file); - fseeko(file, 0, SEEK_SET); -#endif + length = ipsw_file_size(file); payload_info = plist_new_dict(); plist_dict_set_item(payload_info, "Port", plist_new_uint(1)); @@ -296,7 +283,7 @@ int asr_perform_validation(asr_client_t asr, const char* filesystem) return 0; } -int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file) +int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, ipsw_file_handle_t file) { char* oob_data = NULL; uint64_t oob_offset = 0; @@ -324,14 +311,14 @@ int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file) return -1; } -#ifdef WIN32 - rewind(file); - _lseeki64(fileno(file), oob_offset, SEEK_SET); -#else - fseeko(file, oob_offset, SEEK_SET); -#endif - if (fread(oob_data, 1, oob_length, file) != oob_length) { - error("ERROR: Unable to read OOB data from filesystem offset: %s\n", strerror(errno)); + if (ipsw_file_seek(file, oob_offset, SEEK_SET) < 0) { + error("ERROR: Unable to seek to OOB offset 0x%" PRIx64 "\n", oob_offset); + free(oob_data); + return -1; + } + int64_t ir = ipsw_file_read(file, oob_data, oob_length); + if (ir != oob_length) { + error("ERROR: Unable to read OOB data from filesystem offset 0x%" PRIx64 ", oob_length %" PRIu64 ", read returned %" PRIi64"\n", oob_offset, oob_length, ir); free(oob_data); return -1; } @@ -345,28 +332,14 @@ int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file) return 0; } -int asr_send_payload(asr_client_t asr, const char* filesystem) +int asr_send_payload(asr_client_t asr, ipsw_file_handle_t file) { char *data = NULL; - FILE* file = NULL; uint64_t i, length, bytes = 0; double progress = 0; - file = fopen(filesystem, "rb"); - if (file == NULL) { - error("ERROR: Unable to open filesystem image %s: %s\n", filesystem, strerror(errno)); - return -1; - } - -#ifdef WIN32 - length = _lseeki64(fileno(file), 0, SEEK_END); - _lseeki64(fileno(file), 0, SEEK_SET); - rewind(file); -#else - fseeko(file, 0, SEEK_END); - length = ftello(file); - fseeko(file, 0, SEEK_SET); -#endif + length = ipsw_file_size(file); + ipsw_file_seek(file, 0, SEEK_SET); data = (char*)malloc(ASR_PAYLOAD_CHUNK_SIZE + 20); @@ -376,26 +349,28 @@ int asr_send_payload(asr_client_t asr, const char* filesystem) SHA1_Init(&sha1); } - int size = 0; i = length; int retry = 3; while(i > 0 && retry >= 0) { - size = ASR_PAYLOAD_CHUNK_SIZE; + uint32_t size = ASR_PAYLOAD_CHUNK_SIZE; + uint32_t sendsize = 0; + if (i < ASR_PAYLOAD_CHUNK_SIZE) { size = i; } - if (fread(data, 1, size, file) != (size_t)size) { + if (ipsw_file_read(file, data, size) != (int64_t)size) { error("Error reading filesystem\n"); retry--; continue; } + sendsize = size; if (asr->checksum_chunks) { SHA1((unsigned char*)data, size, (unsigned char*)(data+size)); + sendsize += 20; } - - if (asr_send_buffer(asr, data, size+20) < 0) { + if (asr_send_buffer(asr, data, sendsize) < 0) { error("ERROR: Unable to send filesystem payload\n"); retry--; continue; @@ -412,6 +387,5 @@ int asr_send_payload(asr_client_t asr, const char* filesystem) } free(data); - fclose(file); return 0; } @@ -41,15 +41,18 @@ struct asr_client { }; typedef struct asr_client *asr_client_t; +struct ipsw_file_handle; +typedef struct ipsw_file_handle* ipsw_file_handle_t; + int asr_open_with_timeout(idevice_t device, asr_client_t* asr); void asr_set_progress_callback(asr_client_t asr, asr_progress_cb_t, void* userdata); int asr_send(asr_client_t asr, plist_t data); int asr_receive(asr_client_t asr, plist_t* data); int asr_send_buffer(asr_client_t asr, const char* data, uint32_t size); void asr_free(asr_client_t asr); -int asr_perform_validation(asr_client_t asr, const char* filesystem); -int asr_send_payload(asr_client_t asr, const char* filesystem); -int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file); +int asr_perform_validation(asr_client_t asr, ipsw_file_handle_t file); +int asr_send_payload(asr_client_t asr, ipsw_file_handle_t file); +int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, ipsw_file_handle_t file); #ifdef __cplusplus diff --git a/src/common.c b/src/common.c index 4dbabc0..e5ee07b 100644 --- a/src/common.c +++ b/src/common.c @@ -63,6 +63,7 @@ struct idevicerestore_mode_t idevicerestore_modes[] = { { 3, "Recovery" }, { 4, "Restore" }, { 5, "Normal" }, + { 6, "Port DFU" }, }; int idevicerestore_debug = 0; @@ -467,7 +468,7 @@ void idevicerestore_progress(struct idevicerestore_client_t* client, int step, d client->progress_cb(step, progress, client->progress_cb_data); } else { // we don't want to be too verbose in regular idevicerestore. - if ((step == RESTORE_STEP_UPLOAD_FS) || (step == RESTORE_STEP_VERIFY_FS) || (step == RESTORE_STEP_FLASH_FW)) { + if ((step == RESTORE_STEP_UPLOAD_FS) || (step == RESTORE_STEP_VERIFY_FS) || (step == RESTORE_STEP_FLASH_FW) || (step == RESTORE_STEP_UPLOAD_IMG)) { print_progress_bar(100.0 * progress); } } @@ -557,91 +558,19 @@ void get_user_input(char *buf, int maxlen, int secure) buf[len] = 0; } -uint64_t _plist_dict_get_uint(plist_t dict, const char *key) +const char* path_get_basename(const char* path) { - uint64_t uintval = 0; - char *strval = NULL; - uint64_t strsz = 0; - plist_t node = plist_dict_get_item(dict, key); - if (!node) { - return (uint64_t)-1LL; - } - switch (plist_get_node_type(node)) { - case PLIST_UINT: - plist_get_uint_val(node, &uintval); - break; - case PLIST_STRING: - plist_get_string_val(node, &strval); - if (strval) { - uintval = strtoull(strval, NULL, 0); - free(strval); - } - break; - case PLIST_DATA: - plist_get_data_val(node, &strval, &strsz); - if (strval) { - if (strsz == 8) { - uintval = le64toh(*(uint64_t*)strval); - } else if (strsz == 4) { - uintval = le32toh(*(uint32_t*)strval); - } else if (strsz == 2) { - uintval = le16toh(*(uint16_t*)strval); - } else if (strsz == 1) { - uintval = strval[0]; - } else { - error("%s: ERROR: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); - } - free(strval); - } - break; - default: - break; - } - return uintval; -} - -uint8_t _plist_dict_get_bool(plist_t dict, const char *key) -{ - uint8_t bval = 0; - uint64_t uintval = 0; - char *strval = NULL; - uint64_t strsz = 0; - plist_t node = plist_dict_get_item(dict, key); - if (!node) { - return 0; - } - switch (plist_get_node_type(node)) { - case PLIST_BOOLEAN: - plist_get_bool_val(node, &bval); - break; - case PLIST_UINT: - plist_get_uint_val(node, &uintval); - bval = (uint8_t)uintval; - break; - case PLIST_STRING: - plist_get_string_val(node, &strval); - if (strval) { - if (strcmp(strval, "true")) { - bval = 1; - } else if (strcmp(strval, "false")) { - bval = 0; - } - free(strval); - } - break; - case PLIST_DATA: - plist_get_data_val(node, &strval, &strsz); - if (strval) { - if (strsz == 1) { - bval = strval[0]; - } else { - error("%s: ERROR: invalid size %" PRIu64 " for data to boolean conversion\n", __func__, strsz); - } - free(strval); +#ifdef WIN32 + const char *p = path + strlen(path); + while (p > path) { + if ((*p == '/') || (*p == '\\')) { + return p+1; } - break; - default: - break; + p--; } - return bval; + return p; +#else + const char *p = strrchr(path, '/'); + return p ? p + 1 : path; +#endif } diff --git a/src/common.h b/src/common.h index 6c839ce..766a385 100644 --- a/src/common.h +++ b/src/common.h @@ -47,6 +47,7 @@ extern "C" { #define _MODE_RECOVERY 3 #define _MODE_RESTORE 4 #define _MODE_NORMAL 5 +#define _MODE_PORTDFU 6 #define MODE_UNKNOWN &idevicerestore_modes[_MODE_UNKNOWN] #define MODE_WTF &idevicerestore_modes[_MODE_WTF] @@ -54,6 +55,7 @@ extern "C" { #define MODE_RECOVERY &idevicerestore_modes[_MODE_RECOVERY] #define MODE_RESTORE &idevicerestore_modes[_MODE_RESTORE] #define MODE_NORMAL &idevicerestore_modes[_MODE_NORMAL] +#define MODE_PORTDFU &idevicerestore_modes[_MODE_PORTDFU] #define FLAG_QUIT 1 @@ -70,6 +72,9 @@ struct dfu_client_t; struct normal_client_t; struct restore_client_t; struct recovery_client_t; +struct ipsw_archive; + +typedef struct ipsw_archive* ipsw_archive_t; struct idevicerestore_mode_t { int index; @@ -88,6 +93,7 @@ struct idevicerestore_entry_t { struct idevicerestore_client_t { int flags; + int debug_level; plist_t tss; plist_t tss_localpolicy; plist_t tss_recoveryos_root_ticket; @@ -101,8 +107,7 @@ struct idevicerestore_client_t { plist_t preflight_info; char* udid; char* srnm; - char* ipsw; - const char* filesystem; + ipsw_archive_t ipsw; struct dfu_client_t* dfu; struct restore_client_t* restore; struct recovery_client_t* recovery; @@ -111,6 +116,8 @@ struct idevicerestore_client_t { struct idevicerestore_mode_t* mode; char* version; char* build; + char* device_version; + char* device_build; int build_major; char* restore_boot_args; char* cache_dir; @@ -123,6 +130,10 @@ struct idevicerestore_client_t { mutex_t device_event_mutex; cond_t device_event_cond; int ignore_device_add_events; + plist_t macos_variant; + char* restore_variant; + char* filesystem; + int delete_fs; }; extern struct idevicerestore_mode_t idevicerestore_modes[]; @@ -180,8 +191,7 @@ char* realpath(const char *filename, char *resolved_name); void get_user_input(char *buf, int maxlen, int secure); -uint8_t _plist_dict_get_bool(plist_t dict, const char *key); -uint64_t _plist_dict_get_uint(plist_t dict, const char *key); +const char* path_get_basename(const char* path); #ifdef __cplusplus } @@ -42,8 +42,6 @@ static int dfu_progress_callback(irecv_client_t client, const irecv_event_t* eve int dfu_client_new(struct idevicerestore_client_t* client) { - int i = 0; - int attempts = 10; irecv_client_t dfu = NULL; if (client->dfu == NULL) { @@ -55,18 +53,9 @@ int dfu_client_new(struct idevicerestore_client_t* client) } } - for (i = 1; i <= attempts; i++) { - if (irecv_open_with_ecid(&dfu, client->ecid) == IRECV_E_SUCCESS) { - break; - } - - if (i >= attempts) { - error("ERROR: Unable to connect to device in DFU mode\n"); - return -1; - } - - sleep(1); - debug("Retrying connection...\n"); + if (irecv_open_with_ecid_and_attempts(&dfu, client->ecid, 10) != IRECV_E_SUCCESS) { + error("ERROR: Unable to connect to device in DFU mode\n"); + return -1; } irecv_event_subscribe(dfu, IRECV_PROGRESS, &dfu_progress_callback, NULL); @@ -95,11 +84,17 @@ irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client) irecv_device_t device = NULL; irecv_init(); - if (irecv_open_with_ecid(&dfu, client->ecid) != IRECV_E_SUCCESS) { + if (irecv_open_with_ecid_and_attempts(&dfu, client->ecid, 10) != IRECV_E_SUCCESS) { return NULL; } dfu_error = irecv_devices_get_device_by_client(dfu, &device); + if (dfu_error == IRECV_E_SUCCESS) { + if (client->ecid == 0) { + const struct irecv_device_info *device_info = irecv_get_device_info(dfu); + client->ecid = device_info->ecid; + } + } irecv_close(dfu); if (dfu_error != IRECV_E_SUCCESS) { return NULL; @@ -108,13 +103,13 @@ irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client) return device; } -int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size) +int dfu_send_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options) { irecv_error_t err = 0; info("Sending data (%d bytes)...\n", size); - err = irecv_send_buffer(client->dfu->client, buffer, size, 1); + err = irecv_send_buffer(client->dfu->client, buffer, size, irecv_options); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send data: %s\n", irecv_strerror(err)); return -1; @@ -123,6 +118,11 @@ int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffe return 0; } +int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size) +{ + return dfu_send_buffer_with_options(client, buffer, size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH); +} + int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component) { char* path = NULL; @@ -198,7 +198,7 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide info("Sending %s (%d bytes)...\n", component, size); - irecv_error_t err = irecv_send_buffer(client->dfu->client, data, size, 1); + irecv_error_t err = irecv_send_buffer(client->dfu->client, data, size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send %s component: %s\n", component, irecv_strerror(err)); free(data); @@ -209,7 +209,7 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide return 0; } -int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid) +int dfu_get_bdid(struct idevicerestore_client_t* client, unsigned int* bdid) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { @@ -222,12 +222,12 @@ int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid) return -1; } - *cpid = device_info->cpid; + *bdid = device_info->bdid; return 0; } -int dfu_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid) +int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { @@ -240,11 +240,32 @@ int dfu_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid) return -1; } - *ecid = device_info->ecid; + *cpid = device_info->cpid; return 0; } +int dfu_get_prev(struct idevicerestore_client_t* client, unsigned int* prev) +{ + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); + if (!device_info) { + return -1; + } + char* ptr = strstr(device_info->serial_string, "PREV:"); + if (ptr) { + sscanf(ptr, "PREV:%x", prev); + return 0; + } + return -1; +} + + int dfu_is_image4_supported(struct idevicerestore_client_t* client) { if(client->dfu == NULL) { @@ -261,7 +282,36 @@ int dfu_is_image4_supported(struct idevicerestore_client_t* client) return (device_info->ibfl & IBOOT_FLAG_IMAGE4_AWARE); } -int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int dfu_get_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) +{ + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); + if (!device_info) { + return -1; + } + + if (device_info->ap_nonce && device_info->ap_nonce_size > 0) { + *nonce = (unsigned char*)malloc(device_info->ap_nonce_size); + if (!*nonce) { + return -1; + } + *nonce_size = device_info->ap_nonce_size; + // The nonce is backwards, so we have to swap the bytes + unsigned int i = 0; + for (i = 0; i < *nonce_size; i++) { + (*nonce)[(*nonce_size)-1-i] = device_info->ap_nonce[i]; + } + } + + return 0; +} + +int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { @@ -286,7 +336,7 @@ int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** non return 0; } -int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { @@ -372,11 +422,13 @@ int dfu_send_iboot_stage1_components(struct idevicerestore_client_t* client, pli uint8_t b = 0; plist_get_bool_val(iboot_node, &b); if (b) { - debug("DEBUG: %s is loaded by iBoot Stage 1.\n", key); - if (dfu_send_component_and_command(client, build_identity, key, "firmware") < 0) { - error("ERROR: Unable to send component '%s' to device.\n", key); - err++; - } + debug("DEBUG: %s is loaded by iBoot Stage 1 and iBoot.\n", key); + } else { + debug("DEBUG: %s is loaded by iBoot Stage 1 but not iBoot...\n", key); + } + if (dfu_send_component_and_command(client, build_identity, key, "firmware") < 0) { + error("ERROR: Unable to send component '%s' to device.\n", key); + err++; } } free(key); @@ -438,7 +490,7 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide /* get nonce */ unsigned char* nonce = NULL; - int nonce_size = 0; + unsigned int nonce_size = 0; int nonce_changed = 0; if (dfu_get_ap_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get ApNonce from device!\n"); @@ -484,7 +536,7 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide mutex_lock(&client->device_event_mutex); // Now, before sending iBEC, we must send necessary firmwares on new versions. - if (client->build_major >= 20) { + if (client->macos_variant) { // Without this empty policy file & its special signature, iBEC won't start. if (dfu_send_component_and_command(client, build_identity, "Ap,LocalPolicy", "lpolrestore") < 0) { mutex_unlock(&client->device_event_mutex); @@ -494,6 +546,21 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide return -1; } + char *value = NULL; + unsigned long boot_stage = 0; + irecv_getenv(client->dfu->client, "boot-stage", &value); + if (value) { + boot_stage = strtoul(value, NULL, 0); + } + if (boot_stage > 0) { + info("iBoot boot-stage=%s\n", value); + free(value); + value = NULL; + if (boot_stage != 1) { + error("ERROR: iBoot should be at boot stage 1, continuing anyway...\n"); + } + } + if (dfu_send_iboot_stage1_components(client, build_identity) < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send iBoot stage 1 components to device\n"); @@ -41,12 +41,15 @@ int dfu_client_new(struct idevicerestore_client_t* client); void dfu_client_free(struct idevicerestore_client_t* client); irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client); int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size); +int dfu_send_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options); int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component); +int dfu_get_bdid(struct idevicerestore_client_t* client, unsigned int* bdid); int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid); -int dfu_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); +int dfu_get_prev(struct idevicerestore_client_t* client, unsigned int* prev); int dfu_is_image4_supported(struct idevicerestore_client_t* client); -int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); -int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); +int dfu_get_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity); @@ -575,7 +575,7 @@ static int fdr_handle_proxy_cmd(fdr_client_t fdr) break; } if (bytes) { - debug("FDR %p got payload of %u bytes, now try to proxy it\n", fdr, bytes); + debug("FDR %p got payload of %u bytes, now trying to proxy it\n", fdr, bytes); debug("Sending %u bytes of data\n", bytes); sent = 0; while (sent < bytes) { diff --git a/src/idevicerestore.c b/src/idevicerestore.c index 017b45c..f27c357 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -45,6 +45,9 @@ #define SHA384 sha384 #endif +#include <libimobiledevice-glue/utils.h> + +#include "ace3.h" #include "dfu.h" #include "tss.h" #include "img3.h" @@ -72,7 +75,7 @@ static struct option longopts[] = { { "erase", no_argument, NULL, 'e' }, { "custom", no_argument, NULL, 'c' }, { "latest", no_argument, NULL, 'l' }, - { "cydia", no_argument, NULL, 's' }, + { "server", required_argument, NULL, 's' }, { "exclude", no_argument, NULL, 'x' }, { "shsh", no_argument, NULL, 't' }, { "keep-pers", no_argument, NULL, 'k' }, @@ -87,6 +90,7 @@ static struct option longopts[] = { { "version", no_argument, NULL, 'v' }, { "ipsw-info", no_argument, NULL, 'I' }, { "ignore-errors", no_argument, NULL, 1 }, + { "variant", required_argument, NULL, 2 }, { NULL, 0, NULL, 0 } }; @@ -131,8 +135,8 @@ static void usage(int argc, char* argv[], int err) "\n" \ "Advanced/experimental options:\n" " -c, --custom Restore with a custom firmware (requires bootrom exploit)\n" \ - " -s, --cydia Use Cydia's signature service instead of Apple's\n" \ - " -x, --exclude Exclude nor/baseband upgrade\n" \ + " -s, --server URL Override default signing server request URL\n" \ + " -x, --exclude Exclude nor/baseband upgrade (legacy devices)\n" \ " -t, --shsh Fetch TSS record and save to .shsh file, then exit\n" \ " -z, --no-restore Do not restore and end after booting to the ramdisk\n" \ " -k, --keep-pers Write personalized components to files for debugging\n" \ @@ -140,6 +144,8 @@ static void usage(int argc, char* argv[], int err) " -P, --plain-progress Print progress as plain step and progress\n" \ " -R, --restore-mode Allow restoring from Restore mode\n" \ " -T, --ticket PATH Use file at PATH to send as AP ticket\n" \ + " --variant VARIANT Use given VARIANT to match the build identity to use,\n" \ + " e.g. 'Customer Erase Install (IPSW)'\n" \ " --ignore-errors Try to continue the restore process after certain\n" \ " errors (like a failed baseband update)\n" \ " WARNING: This might render the device unable to boot\n" \ @@ -293,6 +299,9 @@ static void irecv_event_cb(const irecv_device_event_t* event, void *userdata) case IRECV_K_DFU_MODE: client->mode = MODE_DFU; break; + case IRECV_K_PORT_DFU_MODE: + client->mode = MODE_PORTDFU; + break; case IRECV_K_RECOVERY_MODE_1: case IRECV_K_RECOVERY_MODE_2: case IRECV_K_RECOVERY_MODE_3: @@ -311,12 +320,20 @@ static void irecv_event_cb(const irecv_device_event_t* event, void *userdata) mutex_lock(&client->device_event_mutex); client->mode = MODE_UNKNOWN; debug("%s: device %016" PRIx64 " (udid: %s) disconnected\n", __func__, client->ecid, (client->udid) ? client->udid : "N/A"); + if (event->mode == IRECV_K_PORT_DFU_MODE) { + // We have to reset the ECID here if a port DFU device disconnects, + // because when the device reconnects in a different mode, it will + // have the actual device ECID and wouldn't get detected. + client->ecid = 0; + } cond_signal(&client->device_event_cond); mutex_unlock(&client->device_event_mutex); } } } +int build_identity_check_components_in_ipsw(plist_t build_identity, ipsw_archive_t ipsw); + int idevicerestore_start(struct idevicerestore_client_t* client) { int tss_enabled = 0; @@ -336,10 +353,12 @@ int idevicerestore_start(struct idevicerestore_client_t* client) return -1; } - if (client->flags & FLAG_DEBUG) { - idevice_set_debug_level(1); - irecv_set_debug_level(1); + if (client->debug_level > 0) { idevicerestore_debug = 1; + if (client->debug_level > 1) { + idevice_set_debug_level(1); + irecv_set_debug_level(1); + } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.0); @@ -421,7 +440,9 @@ int idevicerestore_start(struct idevicerestore_client_t* client) download_to_file(s_wtfurl, wtfipsw, 0); } - ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize); + ipsw_archive_t wtf_ipsw = ipsw_open(wtfipsw); + ipsw_extract_to_memory(wtf_ipsw, wtfname, &wtftmp, &wtfsize); + ipsw_close(wtf_ipsw); if (!wtftmp) { error("ERROR: Could not extract WTF\n"); } @@ -453,6 +474,12 @@ int idevicerestore_start(struct idevicerestore_client_t* client) error("ERROR: Unable to discover device type\n"); return -1; } + if (client->ecid == 0) { + error("ERROR: Unable to determine ECID\n"); + return -1; + } + info("ECID: %" PRIu64 "\n", client->ecid); + idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.2); info("Identified device as %s, %s\n", client->device->hardware_model, client->device->product_type); @@ -461,6 +488,21 @@ int idevicerestore_start(struct idevicerestore_client_t* client) return -1; } + if (client->mode == MODE_NORMAL) { + plist_t pver = normal_get_lockdown_value(client, NULL, "ProductVersion"); + if (pver) { + plist_get_string_val(pver, &client->device_version); + plist_free(pver); + } + pver = normal_get_lockdown_value(client, NULL, "BuildVersion"); + if (pver) { + plist_get_string_val(pver, &client->device_build); + plist_free(pver); + } + } + info("Device Product Version: %s\n", (client->device_version) ? client->device_version : "N/A"); + info("Device Product Build: %s\n", (client->device_build) ? client->device_build : "N/A"); + if (client->flags & FLAG_PWN) { recovery_client_free(client); @@ -473,17 +515,24 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (dfu_client_new(client) < 0) { return -1; } - info("exploiting with limera1n...\n"); - // TODO: check for non-limera1n device and fail - if (limera1n_exploit(client->device, &client->dfu->client) != 0) { - error("ERROR: limera1n exploit failed\n"); + + if (limera1n_is_supported(client->device)) { + info("exploiting with limera1n...\n"); + if (limera1n_exploit(client->device, &client->dfu->client) != 0) { + error("ERROR: limera1n exploit failed\n"); + dfu_client_free(client); + return -1; + } + dfu_client_free(client); + info("Device should be in pwned DFU state now.\n"); + + return 0; + } + else { dfu_client_free(client); + error("ERROR: This device is not supported by the limera1n exploit"); return -1; } - dfu_client_free(client); - info("Device should be in pwned DFU state now.\n"); - - return 0; } if (client->flags & FLAG_LATEST) { @@ -585,12 +634,16 @@ int idevicerestore_start(struct idevicerestore_client_t* client) char* ipsw = NULL; res = ipsw_download_fw(fwurl, p_fwsha1, client->cache_dir, &ipsw); if (res != 0) { - if (ipsw) { - free(ipsw); - } + free(ipsw); return res; } else { - client->ipsw = ipsw; + client->ipsw = ipsw_open(ipsw); + if (!client->ipsw) { + error("ERROR: Failed to open ipsw '%s'\n", ipsw); + free(ipsw); + return -1; + } + free(ipsw); } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.6); @@ -599,14 +652,29 @@ int idevicerestore_start(struct idevicerestore_client_t* client) return 0; } + // extract buildmanifest + if (client->flags & FLAG_CUSTOM) { + info("Extracting Restore.plist from IPSW\n"); + if (ipsw_extract_restore_plist(client->ipsw, &client->build_manifest) < 0) { + error("ERROR: Unable to extract Restore.plist from %s. Firmware file might be corrupt.\n", client->ipsw->path); + return -1; + } + } else { + info("Extracting BuildManifest from IPSW\n"); + if (ipsw_extract_build_manifest(client->ipsw, &client->build_manifest, &tss_enabled) < 0) { + error("ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw->path); + return -1; + } + } + + if (client->flags & FLAG_CUSTOM) { + // prevent attempt to sign custom firmware + tss_enabled = 0; + info("Custom firmware requested; TSS has been disabled.\n"); + } + if (client->mode == MODE_RESTORE) { - if (client->flags & FLAG_ALLOW_RESTORE_MODE) { - tss_enabled = 0; - if (!client->root_ticket) { - client->root_ticket = (void*)strdup(""); - client->root_ticket_len = 0; - } - } else { + if (!(client->flags & FLAG_ALLOW_RESTORE_MODE)) { if (restore_reboot(client) < 0) { error("ERROR: Unable to exit restore mode\n"); return -2; @@ -625,26 +693,152 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } } - // verify if ipsw file exists - if (access(client->ipsw, F_OK) < 0) { - error("ERROR: Firmware file %s does not exist.\n", client->ipsw); - return -1; - } + if (client->mode == MODE_PORTDFU) { + unsigned int pdfu_bdid = 0; + unsigned int pdfu_cpid = 0; + unsigned int prev = 0; - // extract buildmanifest - if (client->flags & FLAG_CUSTOM) { - info("Extracting Restore.plist from IPSW\n"); - if (ipsw_extract_restore_plist(client->ipsw, &client->build_manifest) < 0) { - error("ERROR: Unable to extract Restore.plist from %s. Firmware file might be corrupt.\n", client->ipsw); + if (dfu_get_bdid(client, &pdfu_bdid) < 0) { + error("ERROR: Failed to get bdid for Port DFU device!\n"); return -1; } - } else { - info("Extracting BuildManifest from IPSW\n"); - if (ipsw_extract_build_manifest(client->ipsw, &client->build_manifest, &tss_enabled) < 0) { - error("ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw); + if (dfu_get_cpid(client, &pdfu_cpid) < 0) { + error("ERROR: Failed to get cpid for Port DFU device!\n"); return -1; } + if (dfu_get_prev(client, &prev) < 0) { + error("ERROR: Failed to get PREV for Port DFU device!\n"); + return -1; + } + + unsigned char* pdfu_nonce = NULL; + unsigned int pdfu_nsize = 0; + if (dfu_get_portdfu_nonce(client, &pdfu_nonce, &pdfu_nsize) < 0) { + error("ERROR: Failed to get nonce for Port DFU device!\n"); + return -1; + } + + plist_t build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_ERASE_INSTALL, 0); + if (!build_identity) { + error("ERORR: Failed to get build identity\n"); + return -1; + } + + unsigned int b_pdfu_cpid = (unsigned int)plist_dict_get_uint(build_identity, "USBPortController1,ChipID"); + if (b_pdfu_cpid != pdfu_cpid) { + error("ERROR: cpid 0x%02x doesn't match USBPortController1,ChipID in build identity (0x%02x)\n", pdfu_cpid, b_pdfu_cpid); + return -1; + } + unsigned int b_pdfu_bdid = (unsigned int)plist_dict_get_uint(build_identity, "USBPortController1,BoardID"); + if (b_pdfu_bdid != pdfu_bdid) { + error("ERROR: bdid 0x%x doesn't match USBPortController1,BoardID in build identity (0x%x)\n", pdfu_bdid, b_pdfu_bdid); + return -1; + } + + plist_t parameters = plist_new_dict(); + plist_dict_set_item(parameters, "@USBPortController1,Ticket", plist_new_bool(1)); + plist_dict_set_item(parameters, "USBPortController1,ECID", plist_new_int(client->ecid)); + plist_dict_copy_item(parameters, build_identity, "USBPortController1,BoardID", NULL); + plist_dict_copy_item(parameters, build_identity, "USBPortController1,ChipID", NULL); + plist_dict_copy_item(parameters, build_identity, "USBPortController1,SecurityDomain", NULL); + plist_dict_set_item(parameters, "USBPortController1,SecurityMode", plist_new_bool(1)); + plist_dict_set_item(parameters, "USBPortController1,ProductionMode", plist_new_bool(1)); + plist_t usbf = plist_access_path(build_identity, 2, "Manifest", "USBPortController1,USBFirmware"); + if (!usbf) { + plist_free(parameters); + error("ERROR: Unable to find USBPortController1,USBFirmware in build identity\n"); + return -1; + } + plist_t p_fwpath = plist_access_path(usbf, 2, "Info", "Path"); + if (!p_fwpath) { + plist_free(parameters); + error("ERROR: Unable to find path of USBPortController1,USBFirmware component\n"); + return -1; + } + const char* fwpath = plist_get_string_ptr(p_fwpath, NULL); + if (!fwpath) { + plist_free(parameters); + error("ERROR: Unable to get path of USBPortController1,USBFirmware component\n"); + return -1; + } + unsigned char* uarp_buf = NULL; + unsigned int uarp_size = 0; + if (ipsw_extract_to_memory(client->ipsw, fwpath, &uarp_buf, &uarp_size) < 0) { + plist_free(parameters); + error("ERROR: Unable to extract '%s' from IPSW\n", fwpath); + return -1; + } + usbf = plist_copy(usbf); + plist_dict_remove_item(usbf, "Info"); + plist_dict_set_item(parameters, "USBPortController1,USBFirmware", usbf); + plist_dict_set_item(parameters, "USBPortController1,Nonce", plist_new_data((const char*)pdfu_nonce, pdfu_nsize)); + + plist_t request = tss_request_new(NULL); + if (request == NULL) { + plist_free(parameters); + error("ERROR: Unable to create TSS request\n"); + return -1; + } + plist_dict_merge(&request, parameters); + plist_free(parameters); + + // send request and grab response + plist_t response = tss_request_send(request, client->tss_url); + plist_free(request); + if (response == NULL) { + error("ERROR: Unable to send TSS request\n"); + return -1; + } + info("Received USBPortController1,Ticket\n"); + + info("Creating Ace3Binary\n"); + unsigned char* ace3bin = NULL; + size_t ace3bin_size = 0; + if (ace3_create_binary(uarp_buf, uarp_size, pdfu_bdid, prev, response, &ace3bin, &ace3bin_size) < 0) { + error("ERROR: Could not create Ace3Binary\n"); + return -1; + } + plist_free(response); + free(uarp_buf); + + if (idevicerestore_keep_pers) { + write_file("Ace3Binary", (const char*)ace3bin, ace3bin_size); + } + + if (dfu_send_buffer_with_options(client, ace3bin, ace3bin_size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH | IRECV_SEND_OPT_DFU_SMALL_PKT) < 0) { + error("ERROR: Could not send Ace3Buffer to device\n"); + return -1; + } + + debug("Waiting for device to disconnect...\n"); + cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 5000); + if (client->mode != MODE_UNKNOWN || (client->flags & FLAG_QUIT)) { + mutex_unlock(&client->device_event_mutex); + + if (!(client->flags & FLAG_QUIT)) { + error("ERROR: Device did not disconnect. Port DFU failed.\n"); + } + return -2; + } + debug("Waiting for device to reconnect in DFU mode...\n"); + cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 5000); + if (client->mode != MODE_DFU || (client->flags & FLAG_QUIT)) { + mutex_unlock(&client->device_event_mutex); + if (!(client->flags & FLAG_QUIT)) { + error("ERROR: Device did not reconnect in DFU mode. Port DFU failed.\n"); + } + return -2; + } + mutex_unlock(&client->device_event_mutex); + + if (client->flags & FLAG_NOACTION) { + info("Port DFU restore successful.\n"); + return 0; + } else { + info("Port DFU restore successful. Continuing.\n"); + } } + idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.8); /* check if device type is supported by the given build manifest */ @@ -656,23 +850,19 @@ int idevicerestore_start(struct idevicerestore_client_t* client) /* print iOS information from the manifest */ build_manifest_get_version_information(client->build_manifest, client); - info("Product Version: %s\n", client->version); - info("Product Build: %s Major: %d\n", client->build, client->build_major); + info("IPSW Product Version: %s\n", client->version); + info("IPSW Product Build: %s Major: %d\n", client->build, client->build_major); client->image4supported = is_image4_supported(client); info("Device supports Image4: %s\n", (client->image4supported) ? "true" : "false"); - if (client->flags & FLAG_CUSTOM) { - /* prevent signing custom firmware */ - tss_enabled = 0; - info("Custom firmware requested. Disabled TSS request.\n"); - } - // choose whether this is an upgrade or a restore (default to upgrade) client->tss = NULL; plist_t build_identity = NULL; + int build_identity_needs_free = 0; if (client->flags & FLAG_CUSTOM) { build_identity = plist_new_dict(); + build_identity_needs_free = 1; { plist_t node; plist_t comp; @@ -699,6 +889,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) uint32_t msize = 0; if (ipsw_extract_to_memory(client->ipsw, tmpstr, (unsigned char**)&fmanifest, &msize) < 0) { error("ERROR: could not extract %s from IPSW\n", tmpstr); + free(build_identity); return -1; } @@ -816,40 +1007,42 @@ int idevicerestore_start(struct idevicerestore_client_t* client) // add info inf = plist_new_dict(); plist_dict_set_item(inf, "RestoreBehavior", plist_new_string((client->flags & FLAG_ERASE) ? "Erase" : "Update")); - plist_dict_set_item(inf, "Variant", plist_new_string((client->flags & FLAG_ERASE) ? "Customer Erase Install (IPSW)" : "Customer Upgrade Install (IPSW)")); + plist_dict_set_item(inf, "Variant", plist_new_string((client->flags & FLAG_ERASE) ? "Customer " RESTORE_VARIANT_ERASE_INSTALL : "Customer " RESTORE_VARIANT_UPGRADE_INSTALL)); plist_dict_set_item(build_identity, "Info", inf); // finally add manifest plist_dict_set_item(build_identity, "Manifest", manifest); } + } else if (client->restore_variant) { + build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, client->restore_variant, 1); } else if (client->flags & FLAG_ERASE) { - build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, "Customer Erase Install (IPSW)"); - if (build_identity == NULL) { - error("ERROR: Unable to find any build identities\n"); - return -1; - } + build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_ERASE_INSTALL, 0); } else { - build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, "Customer Upgrade Install (IPSW)"); + build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_UPGRADE_INSTALL, 0); if (!build_identity) { build_identity = build_manifest_get_build_identity_for_model(client->build_manifest, client->device->hardware_model); } } + if (build_identity == NULL) { + error("ERROR: Unable to find a matching build identity\n"); + return -1; + } + + client->macos_variant = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_MACOS_RECOVERY_OS, 1); /* print information about current build identity */ build_identity_print_information(build_identity); + if (client->macos_variant) { + info("Performing macOS restore\n"); + } + if (client->mode == MODE_NORMAL && !(client->flags & FLAG_ERASE) && !(client->flags & FLAG_SHSHONLY)) { - plist_t pver = normal_get_lockdown_value(client, NULL, "ProductVersion"); - char *device_version = NULL; - if (pver) { - plist_get_string_val(pver, &device_version); - plist_free(pver); - } - if (device_version && (compare_versions(device_version, client->version) > 0)) { + if (client->device_version && (compare_versions(client->device_version, client->version) > 0)) { if (client->flags & FLAG_INTERACTIVE) { char input[64]; char spaces[16]; - int num_spaces = 13 - strlen(client->version) - strlen(device_version); + int num_spaces = 13 - strlen(client->version) - strlen(client->device_version); memset(spaces, ' ', num_spaces); spaces[num_spaces] = '\0'; printf("################################ [ WARNING ] #################################\n" @@ -860,7 +1053,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) "# If you want to take the risk (and have a backup of your important data!) #\n" "# type YES and press ENTER to continue. You have been warned. #\n" "##############################################################################\n", - device_version, client->version, spaces); + client->device_version, client->version, spaces); while (1) { printf("> "); fflush(stdout); @@ -879,7 +1072,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } } } - free(device_version); } if (client->flags & FLAG_ERASE && client->flags & FLAG_INTERACTIVE) { @@ -914,112 +1106,74 @@ int idevicerestore_start(struct idevicerestore_client_t* client) /* check if all components we need are actually there */ info("Checking IPSW for required components...\n"); if (build_identity_check_components_in_ipsw(build_identity, client->ipsw) < 0) { - error("ERROR: Could not find all required components in IPSW %s\n", client->ipsw); + error("ERROR: Could not find all required components in IPSW %s\n", client->ipsw->path); return -1; } info("All required components found in IPSW\n"); - // Get filesystem name from build identity - char* fsname = NULL; - if (build_identity_get_component_path(build_identity, "OS", &fsname) < 0) { + /* Get OS (filesystem) name from build identity */ + char* os_path = NULL; + if (build_identity_get_component_path(build_identity, "OS", &os_path) < 0) { error("ERROR: Unable to get path for filesystem component\n"); return -1; } - // check if we already have an extracted filesystem - int delete_fs = 0; - char* filesystem = NULL; - struct stat st; - memset(&st, '\0', sizeof(struct stat)); - char tmpf[1024]; - if (client->cache_dir) { - if (stat(client->cache_dir, &st) < 0) { - mkdir_with_parents(client->cache_dir, 0755); + /* check if IPSW has OS component 'stored' in ZIP archive, otherwise we need to extract it */ + int needs_os_extraction = 0; + if (client->ipsw->zip) { + ipsw_file_handle_t zfile = ipsw_file_open(client->ipsw, os_path); + if (zfile) { + if (!zfile->seekable) { + needs_os_extraction = 1; + } + ipsw_file_close(zfile); } - strcpy(tmpf, client->cache_dir); - strcat(tmpf, "/"); - char *ipswtmp = strdup(client->ipsw); - strcat(tmpf, basename(ipswtmp)); - free(ipswtmp); - } else { - strcpy(tmpf, client->ipsw); } - if (!ipsw_is_directory(client->ipsw)) { - // strip off file extension if given ipsw is not a directory - char* s = tmpf + strlen(tmpf) - 1; - char* p = s; - while (*p != '\0' && *p != '.' && *p != '/' && *p != '\\') p--; - if (s - p < 6) { - if (*p == '.') { + if (needs_os_extraction && !(client->flags & FLAG_SHSHONLY)) { + char* tmpf = NULL; + struct stat st; + if (client->cache_dir) { + memset(&st, '\0', sizeof(struct stat)); + if (stat(client->cache_dir, &st) < 0) { + mkdir_with_parents(client->cache_dir, 0755); + } + char* ipsw_basename = strdup(path_get_basename(client->ipsw->path)); + char* p = strrchr(ipsw_basename, '.'); + if (p && isalpha(*(p+1))) { *p = '\0'; } - } - } - - if (stat(tmpf, &st) < 0) { - __mkdir(tmpf, 0755); - } - strcat(tmpf, "/"); - strcat(tmpf, fsname); - - memset(&st, '\0', sizeof(struct stat)); - if (stat(tmpf, &st) == 0) { - uint64_t fssize = 0; - ipsw_get_file_size(client->ipsw, fsname, &fssize); - if ((fssize > 0) && ((uint64_t)st.st_size == fssize)) { - info("Using cached filesystem from '%s'\n", tmpf); - filesystem = strdup(tmpf); - } - } - - if (!filesystem && !(client->flags & FLAG_SHSHONLY)) { - char extfn[1024]; - strcpy(extfn, tmpf); - strcat(extfn, ".extract"); - char lockfn[1024]; - strcpy(lockfn, tmpf); - strcat(lockfn, ".lock"); - lock_info_t li; - - lock_file(lockfn, &li); - FILE* extf = NULL; - if (access(extfn, F_OK) != 0) { - extf = fopen(extfn, "wb"); - } - unlock_file(&li); - if (!extf) { - // use temp filename - filesystem = get_temp_filename("ipsw_"); - if (!filesystem) { - error("WARNING: Could not get temporary filename, using '%s' in current directory\n", fsname); - filesystem = strdup(fsname); - } - delete_fs = 1; + tmpf = string_build_path(client->cache_dir, ipsw_basename, NULL); + mkdir_with_parents(tmpf, 0755); + free(tmpf); + tmpf = string_build_path(client->cache_dir, ipsw_basename, os_path, NULL); + free(ipsw_basename); } else { - // use <fsname>.extract as filename - filesystem = strdup(extfn); - fclose(extf); - } - remove(lockfn); - - // Extract filesystem from IPSW - info("Extracting filesystem from IPSW: %s\n", fsname); - if (ipsw_extract_to_file_with_progress(client->ipsw, fsname, filesystem, 1) < 0) { - error("ERROR: Unable to extract filesystem from IPSW\n"); - if (client->tss) - plist_free(client->tss); - info("Removing %s\n", filesystem); - unlink(filesystem); - return -1; + tmpf = get_temp_filename(NULL); + client->delete_fs = 1; } - if (strstr(filesystem, ".extract")) { - // rename <fsname>.extract to <fsname> - remove(tmpf); - rename(filesystem, tmpf); - free(filesystem); - filesystem = strdup(tmpf); + /* check if we already have it extracted */ + uint64_t fssize = 0; + ipsw_get_file_size(client->ipsw, os_path, &fssize); + memset(&st, '\0', sizeof(struct stat)); + if (stat(tmpf, &st) == 0) { + if ((fssize > 0) && ((uint64_t)st.st_size == fssize)) { + info("Using cached filesystem from '%s'\n", tmpf); + client->filesystem = tmpf; + } + } + + if (!client->filesystem) { + info("Extracting filesystem from IPSW: %s\n", os_path); + if (ipsw_extract_to_file_with_progress(client->ipsw, os_path, tmpf, 1) < 0) { + error("ERROR: Unable to extract filesystem from IPSW\n"); + info("Removing %s\n", tmpf); + unlink(tmpf); + free(tmpf); + return -1; + } + client->filesystem = tmpf; } } @@ -1028,13 +1182,6 @@ 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) { - error("ERROR: Unable to find device ECID\n"); - return -1; - } - info("Found ECID %" PRIu64 "\n", client->ecid); if (client->mode == MODE_NORMAL && !(client->flags & FLAG_ERASE) && !(client->flags & FLAG_SHSHONLY)) { plist_t node = normal_get_lockdown_value(client, NULL, "HasSiDP"); @@ -1067,7 +1214,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (client->build_major > 8) { unsigned char* nonce = NULL; - int nonce_size = 0; + unsigned int nonce_size = 0; if (get_ap_nonce(client, &nonce, &nonce_size) < 0) { /* the first nonce request with older firmware releases can fail and it's OK */ info("NOTE: Unable to get nonce from device\n"); @@ -1088,20 +1235,33 @@ int idevicerestore_start(struct idevicerestore_client_t* client) return -1; } - if (get_tss_response(client, build_identity, &client->tss) < 0) { - error("ERROR: Unable to get SHSH blobs for this device\n"); - return -1; - } - if (client->build_major >= 20) { - if (get_local_policy_tss_response(client, build_identity, &client->tss_localpolicy) < 0) { - error("ERROR: Unable to get SHSH blobs for this device (local policy)\n"); + if (client->mode == MODE_RESTORE && client->root_ticket) { + plist_t ap_ticket = plist_new_data((char*)client->root_ticket, client->root_ticket_len); + if (!ap_ticket) { + error("ERROR: Failed to create ApImg4Ticket node value.\n"); + return -1; + } + client->tss = plist_new_dict(); + if (!client->tss) { + error("ERROR: Failed to create ApImg4Ticket node.\n"); return -1; } - if (get_recoveryos_root_ticket_tss_response(client, build_identity, &client->tss_recoveryos_root_ticket) < - 0) { - error("ERROR: Unable to get SHSH blobs for this device (recovery OS Root Ticket)\n"); + plist_dict_set_item(client->tss, "ApImg4Ticket", ap_ticket); + } else { + if (get_tss_response(client, build_identity, &client->tss) < 0) { + error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } + if (client->macos_variant) { + if (get_local_policy_tss_response(client, build_identity, &client->tss_localpolicy) < 0) { + error("ERROR: Unable to get SHSH blobs for this device (local policy)\n"); + return -1; + } + if (get_recoveryos_root_ticket_tss_response(client, build_identity, &client->tss_recoveryos_root_ticket) < 0) { + error("ERROR: Unable to get SHSH blobs for this device (recovery OS Root Ticket)\n"); + return -1; + } + } } if (stashbag_commit_required) { @@ -1120,8 +1280,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } if (client->flags & FLAG_SHSHONLY) { @@ -1176,8 +1334,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.25); if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } @@ -1194,8 +1350,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.3); if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } @@ -1206,17 +1360,12 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if ((client->flags & FLAG_CUSTOM) && limera1n_is_supported(client->device)) { info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } info("exploiting with limera1n\n"); - // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, &client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); - if (delete_fs && filesystem) - unlink(filesystem); return -1; } dfu_client_free(client); @@ -1226,8 +1375,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) error("ERROR: Unable to place device into recovery mode from DFU mode\n"); if (client->tss) plist_free(client->tss); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } } else if (client->mode == MODE_RECOVERY) { @@ -1237,8 +1384,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) /* send ApTicket */ if (recovery_send_ticket(client) < 0) { error("ERROR: Unable to send APTicket\n"); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } } @@ -1250,54 +1395,44 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (recovery_send_ibec(client, build_identity) < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send iBEC\n"); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } recovery_client_free(client); debug("Waiting for device to disconnect...\n"); - cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); + cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 60000); if (client->mode != MODE_UNKNOWN || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not disconnect. Possibly invalid iBEC. Reset device and try again.\n"); } - if (delete_fs && filesystem) - unlink(filesystem); return -2; } debug("Waiting for device to reconnect in recovery mode...\n"); - cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); + cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 60000); if (client->mode != MODE_RECOVERY || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not reconnect in recovery mode. Possibly invalid iBEC. Reset device and try again.\n"); } - if (delete_fs && filesystem) - unlink(filesystem); return -2; } mutex_unlock(&client->device_event_mutex); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.5); if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } if (!client->image4supported && (client->build_major > 8)) { // we need another tss request with nonce. unsigned char* nonce = NULL; - int nonce_size = 0; + unsigned int nonce_size = 0; int nonce_changed = 0; if (get_ap_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get nonce from device!\n"); recovery_send_reset(client); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } @@ -1317,14 +1452,10 @@ int idevicerestore_start(struct idevicerestore_client_t* client) plist_free(client->tss); if (get_tss_response(client, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); - if (delete_fs && filesystem) - unlink(filesystem); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); - if (delete_fs && filesystem) - unlink(filesystem); return -1; } fixup_tss(client->tss); @@ -1332,25 +1463,15 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.7); if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } // now finally do the magic to put the device into restore mode if (client->mode == MODE_RECOVERY) { - if (client->srnm == NULL) { - error("ERROR: could not retrieve device serial number. Can't continue.\n"); - if (delete_fs && filesystem) - unlink(filesystem); - return -1; - } if (recovery_enter_restore(client, build_identity) < 0) { error("ERROR: Unable to place device into restore mode\n"); if (client->tss) plist_free(client->tss); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } recovery_client_free(client); @@ -1365,8 +1486,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) mutex_unlock(&client->device_event_mutex); error("ERROR: Device failed to enter restore mode.\n"); error("Please make sure that usbmuxd is running.\n"); - if (delete_fs && filesystem) - unlink(filesystem); return -1; } mutex_unlock(&client->device_event_mutex); @@ -1380,19 +1499,13 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } client->ignore_device_add_events = 1; info("About to restore device... \n"); - result = restore_device(client, build_identity, filesystem); + result = restore_device(client, build_identity); if (result < 0) { error("ERROR: Unable to restore device\n"); - if (delete_fs && filesystem) - unlink(filesystem); return result; } } - info("Cleaning up...\n"); - if (delete_fs && filesystem) - unlink(filesystem); - /* special handling of older AppleTVs as they enter Recovery mode on boot when plugged in to USB */ if ((strncmp(client->device->product_type, "AppleTV", 7) == 0) && (client->device->product_type[7] < '5')) { if (recovery_client_new(client) == 0) { @@ -1412,7 +1525,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) idevicerestore_progress(client, RESTORE_NUM_STEPS-1, 1.0); } - if (build_identity) + if (build_identity_needs_free) plist_free(build_identity); return result; @@ -1463,14 +1576,18 @@ void idevicerestore_client_free(struct idevicerestore_client_t* client) free(client->srnm); } if (client->ipsw) { - free(client->ipsw); - } - if (client->version) { - free(client->version); + ipsw_close(client->ipsw); } - if (client->build) { - free(client->build); + if (client->filesystem) { + if (client->delete_fs) { + unlink(client->filesystem); + } + free(client->filesystem); } + free(client->version); + free(client->build); + free(client->device_version); + free(client->device_build); if (client->restore_boot_args) { free(client->restore_boot_args); } @@ -1486,6 +1603,7 @@ void idevicerestore_client_free(struct idevicerestore_client_t* client) if (client->preflight_info) { plist_free(client->preflight_info); } + free(client->restore_variant); free(client); } @@ -1521,11 +1639,11 @@ void idevicerestore_set_ipsw(struct idevicerestore_client_t* client, const char* if (!client) return; if (client->ipsw) { - free(client->ipsw); + ipsw_close(client->ipsw); client->ipsw = NULL; } if (path) { - client->ipsw = strdup(path); + client->ipsw = ipsw_open(path); } } @@ -1603,7 +1721,7 @@ int main(int argc, char* argv[]) { client->flags |= FLAG_INTERACTIVE; } - while ((opt = getopt_long(argc, argv, "dhcesxtpli:u:nC:kyPRT:zv", longopts, &optindex)) > 0) { + while ((opt = getopt_long(argc, argv, "dhces:xtpli:u:nC:kyPRT:zv", longopts, &optindex)) > 0) { switch (opt) { case 'h': usage(argc, argv, 0); @@ -1611,6 +1729,7 @@ int main(int argc, char* argv[]) { case 'd': client->flags |= FLAG_DEBUG; + client->debug_level++; break; case 'e': @@ -1621,8 +1740,35 @@ int main(int argc, char* argv[]) { client->flags |= FLAG_CUSTOM; break; - case 's': - client->tss_url = strdup("http://cydia.saurik.com/TSS/controller?action=2"); + case 's': { + if (!*optarg) { + error("ERROR: URL argument for --server must not be empty!\n"); + usage(argc, argv, 1); + return EXIT_FAILURE; + } + char *baseurl = NULL; + if (!strncmp(optarg, "http://", 7) && (strlen(optarg) > 7) && (optarg[7] != '/')) { + baseurl = optarg+7; + } else if (!strncmp(optarg, "https://", 8) && (strlen(optarg) > 8) && (optarg[8] != '/')) { + baseurl = optarg+8; + } + if (baseurl) { + char *p = strchr(baseurl, '/'); + if (!p || *(p+1) == '\0') { + // no path component, add default path + const char default_path[] = "/TSS/controller?action=2"; + char* newurl = malloc(strlen(optarg)+sizeof(default_path)); + sprintf(newurl, "%s%s", optarg, (p) ? default_path+1 : default_path); + client->tss_url = newurl; + } else { + client->tss_url = strdup(optarg); + } + } else { + error("ERROR: URL argument for --server is invalid, must start with http:// or https://\n"); + usage(argc, argv, 1); + return EXIT_FAILURE; + } + } break; case 'x': @@ -1716,6 +1862,11 @@ int main(int argc, char* argv[]) { client->flags |= FLAG_IGNORE_ERRORS; break; + case 2: + free(client->restore_variant); + client->restore_variant = strdup(optarg); + break; + default: usage(argc, argv, 1); return EXIT_FAILURE; @@ -1746,8 +1897,15 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } + info("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); + if (ipsw) { - client->ipsw = strdup(ipsw); + // verify if ipsw file exists + client->ipsw = ipsw_open(ipsw); + if (!client->ipsw) { + error("ERROR: Firmware file %s cannot be opened.\n", ipsw); + return -1; + } } curl_global_init(CURL_GLOBAL_ALL); @@ -1778,6 +1936,7 @@ irecv_device_t get_irecv_device(struct idevicerestore_client_t *client) return normal_get_irecv_device(client); case _MODE_DFU: + case _MODE_PORTDFU: case _MODE_RECOVERY: return dfu_get_irecv_device(client); @@ -1815,46 +1974,7 @@ int is_image4_supported(struct idevicerestore_client_t* client) return res; } -int get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid) -{ - int mode = _MODE_UNKNOWN; - - if (client->mode) { - mode = client->mode->index; - } - - switch (mode) { - case _MODE_NORMAL: - if (normal_get_ecid(client, ecid) < 0) { - *ecid = 0; - return -1; - } - break; - - case _MODE_DFU: - if (dfu_get_ecid(client, ecid) < 0) { - *ecid = 0; - return -1; - } - break; - - case _MODE_RECOVERY: - if (recovery_get_ecid(client, ecid) < 0) { - *ecid = 0; - return -1; - } - break; - - default: - error("ERROR: Device is in an invalid state\n"); - *ecid = 0; - return -1; - } - - return 0; -} - -int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { int mode = _MODE_UNKNOWN; @@ -1905,7 +2025,7 @@ int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, return 0; } -int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { int mode = _MODE_UNKNOWN; @@ -1956,7 +2076,7 @@ int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, return 0; } -plist_t build_manifest_get_build_identity_for_model_with_variant(plist_t build_manifest, const char *hardware_model, const char *variant) +plist_t build_manifest_get_build_identity_for_model_with_variant(plist_t build_manifest, const char *hardware_model, const char *variant, int exact) { plist_t build_identities_array = plist_dict_get_item(build_manifest, "BuildIdentities"); if (!build_identities_array || plist_get_node_type(build_identities_array) != PLIST_ARRAY) { @@ -1978,30 +2098,27 @@ plist_t build_manifest_get_build_identity_for_model_with_variant(plist_t build_m if (!devclass || plist_get_node_type(devclass) != PLIST_STRING) { continue; } - char *str = NULL; - plist_get_string_val(devclass, &str); + const char *str = plist_get_string_ptr(devclass, NULL); if (strcasecmp(str, hardware_model) != 0) { - free(str); continue; } - free(str); - str = NULL; if (variant) { plist_t rvariant = plist_dict_get_item(info_dict, "Variant"); if (!rvariant || plist_get_node_type(rvariant) != PLIST_STRING) { continue; } - plist_get_string_val(rvariant, &str); - if (strcasecmp(str, variant) != 0) { - free(str); + str = plist_get_string_ptr(rvariant, NULL); + if (strcmp(str, variant) != 0) { + /* if it's not a full match, let's try a partial match, but ignore "*Research*" */ + if (!exact && strstr(str, variant) && !strstr(str, "Research")) { + return ident; + } continue; } else { - free(str); - return plist_copy(ident); + return ident; } - free(str); } else { - return plist_copy(ident); + return ident; } } @@ -2010,7 +2127,7 @@ plist_t build_manifest_get_build_identity_for_model_with_variant(plist_t build_m plist_t build_manifest_get_build_identity_for_model(plist_t build_manifest, const char *hardware_model) { - return build_manifest_get_build_identity_for_model_with_variant(build_manifest, hardware_model, NULL); + return build_manifest_get_build_identity_for_model_with_variant(build_manifest, hardware_model, NULL, 0); } int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* manifest) @@ -2028,13 +2145,13 @@ int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_ 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(overrides, "ApSecurityDomain", plist_new_uint(1)); plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(0)); plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(0)); plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(1)); - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); /* create basic request */ request = tss_request_new(NULL); @@ -2052,7 +2169,7 @@ int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_ return -1; } - plist_dict_set_item(parameters, "_OnlyFWComponents", plist_new_bool(1)); + plist_dict_set_item(parameters, "_OnlyFWOrTrustedComponents", plist_new_bool(1)); /* add tags from manifest */ if (tss_request_add_ap_tags(request, parameters, NULL) < 0) { @@ -2149,7 +2266,7 @@ int get_tss_response(struct idevicerestore_client_t* client, plist_t build_ident plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size)); } unsigned char* sep_nonce = NULL; - int sep_nonce_size = 0; + unsigned int sep_nonce_size = 0; get_sep_nonce(client, &sep_nonce, &sep_nonce_size); if (sep_nonce) { @@ -2165,7 +2282,7 @@ int get_tss_response(struct idevicerestore_client_t* client, plist_t build_ident plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0)); } - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); /* create basic request */ request = tss_request_new(NULL); @@ -2214,50 +2331,20 @@ int get_tss_response(struct idevicerestore_client_t* client, plist_t build_ident plist_t pinfo = NULL; normal_get_preflight_info(client, &pinfo); if (pinfo) { - plist_t node; - node = plist_dict_get_item(pinfo, "Nonce"); - if (node) { - plist_dict_set_item(parameters, "BbNonce", plist_copy(node)); - } - node = plist_dict_get_item(pinfo, "ChipID"); - if (node) { - plist_dict_set_item(parameters, "BbChipID", plist_copy(node)); - } - node = plist_dict_get_item(pinfo, "CertID"); - if (node) { - plist_dict_set_item(parameters, "BbGoldCertId", plist_copy(node)); - } - node = plist_dict_get_item(pinfo, "ChipSerialNo"); - if (node) { - plist_dict_set_item(parameters, "BbSNUM", plist_copy(node)); - } + plist_dict_copy_data(parameters, pinfo, "BbNonce", "Nonce"); + plist_dict_copy_uint(parameters, pinfo, "BbChipID", "ChipID"); + plist_dict_copy_uint(parameters, pinfo, "BbGoldCertId", "CertID"); + plist_dict_copy_data(parameters, pinfo, "BbSNUM", "ChipSerialNo"); /* add baseband parameters */ tss_request_add_baseband_tags(request, parameters, NULL); - node = plist_dict_get_item(pinfo, "EUICCChipID"); - uint64_t euiccchipid = 0; - if (node && plist_get_node_type(node) == PLIST_UINT) { - plist_get_uint_val(node, &euiccchipid); - plist_dict_set_item(parameters, "eUICC,ChipID", plist_copy(node)); - } - if (euiccchipid >= 5) { - node = plist_dict_get_item(pinfo, "EUICCCSN"); - if (node) { - plist_dict_set_item(parameters, "eUICC,EID", plist_copy(node)); - } - node = plist_dict_get_item(pinfo, "EUICCCertIdentifier"); - if (node) { - plist_dict_set_item(parameters, "eUICC,RootKeyIdentifier", plist_copy(node)); - } - node = plist_dict_get_item(pinfo, "EUICCGoldNonce"); - if (node) { - plist_dict_set_item(parameters, "EUICCGoldNonce", plist_copy(node)); - } - node = plist_dict_get_item(pinfo, "EUICCMainNonce"); - if (node) { - plist_dict_set_item(parameters, "EUICCMainNonce", plist_copy(node)); - } + plist_dict_copy_uint(parameters, pinfo, "eUICC,ChipID", "EUICCChipID"); + if (plist_dict_get_uint(parameters, "eUICC,ChipID") >= 5) { + plist_dict_copy_data(parameters, pinfo, "eUICC,EID", "EUICCCSN"); + plist_dict_copy_data(parameters, pinfo, "eUICC,RootKeyIdentifier", "EUICCCertIdentifier"); + plist_dict_copy_data(parameters, pinfo, "EUICCGoldNonce", NULL); + plist_dict_copy_data(parameters, pinfo, "EUICCMainNonce", NULL); /* add vinyl parameters */ tss_request_add_vinyl_tags(request, parameters, NULL); @@ -2303,7 +2390,7 @@ int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* clie plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size)); } unsigned char* sep_nonce = NULL; - int sep_nonce_size = 0; + unsigned int sep_nonce_size = 0; get_sep_nonce(client, &sep_nonce, &sep_nonce_size); /* ApSepNonce */ @@ -2323,10 +2410,10 @@ int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* clie plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0)); } - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); /* create basic request */ - /* Adds @BBTicket, @HostPlatformInfo, @VersionInfo, @UUID */ + /* Adds @HostPlatformInfo, @VersionInfo, @UUID */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create TSS request\n"); @@ -2335,7 +2422,7 @@ int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* clie } /* add common tags from manifest */ - /* Adds Ap,OSLongVersion, AppNonce, @ApImg4Ticket */ + /* Adds Ap,OSLongVersion, ApNonce, @ApImg4Ticket */ if (tss_request_add_ap_img4_tags(request, parameters) < 0) { error("ERROR: Unable to add AP IMG4 tags to TSS request\n"); plist_free(request); @@ -2403,7 +2490,7 @@ int get_recovery_os_local_policy_tss_response( plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0)); } - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); // Add Ap,LocalPolicy uint8_t digest[SHA384_DIGEST_LENGTH]; @@ -2413,11 +2500,8 @@ int get_recovery_os_local_policy_tss_response( plist_dict_set_item(lpol, "Trusted", plist_new_bool(1)); plist_dict_set_item(parameters, "Ap,LocalPolicy", lpol); - plist_t im4m_hash = plist_dict_get_item(args, "Ap,NextStageIM4MHash"); - plist_dict_set_item(parameters, "Ap,NextStageIM4MHash", plist_copy(im4m_hash)); - - plist_t nonce_hash = plist_dict_get_item(args, "Ap,RecoveryOSPolicyNonceHash"); - plist_dict_set_item(parameters, "Ap,RecoveryOSPolicyNonceHash", plist_copy(nonce_hash)); + plist_dict_copy_data(parameters, args, "Ap,NextStageIM4MHash", NULL); + plist_dict_copy_data(parameters, args, "Ap,RecoveryOSPolicyNonceHash", NULL); plist_t vol_uuid_node = plist_dict_get_item(args, "Ap,VolumeUUID"); char* vol_uuid_str = NULL; @@ -2485,7 +2569,7 @@ int get_local_policy_tss_response(struct idevicerestore_client_t* client, plist_ plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size)); } unsigned char* sep_nonce = NULL; - int sep_nonce_size = 0; + unsigned int sep_nonce_size = 0; get_sep_nonce(client, &sep_nonce, &sep_nonce_size); if (sep_nonce) { @@ -2501,7 +2585,7 @@ int get_local_policy_tss_response(struct idevicerestore_client_t* client, plist_ plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0)); } - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); // Add Ap,LocalPolicy uint8_t digest[SHA384_DIGEST_LENGTH]; @@ -2594,12 +2678,10 @@ int build_manifest_get_identity_count(plist_t build_manifest) error("ERROR: Unable to find build identities node\n"); return -1; } - - // check and make sure this identity exists in buildmanifest return plist_array_get_size(build_identities_array); } -int extract_component(const char* ipsw, const char* path, unsigned char** component_data, unsigned int* component_size) +int extract_component(ipsw_archive_t ipsw, const char* path, unsigned char** component_data, unsigned int* component_size) { char* component_name = NULL; if (!ipsw || !path || !component_data || !component_size) { @@ -2614,7 +2696,7 @@ int extract_component(const char* ipsw, const char* path, unsigned char** compon info("Extracting %s (%s)...\n", component_name, path); if (ipsw_extract_to_memory(ipsw, path, component_data, component_size) < 0) { - error("ERROR: Unable to extract %s from %s\n", component_name, ipsw); + error("ERROR: Unable to extract %s from %s\n", component_name, ipsw->path); return -1; } @@ -2628,9 +2710,9 @@ int personalize_component(const char *component_name, const unsigned char* compo unsigned char* stitched_component = NULL; unsigned int stitched_component_size = 0; - if (tss_response && tss_response_get_ap_img4_ticket(tss_response, &component_blob, &component_blob_size) == 0) { + if (tss_response && plist_dict_get_item(tss_response, "ApImg4Ticket")) { /* stitch ApImg4Ticket into IMG4 file */ - img4_stitch_component(component_name, component_data, component_size, component_blob, component_blob_size, &stitched_component, &stitched_component_size); + img4_stitch_component(component_name, component_data, component_size, tss_response, &stitched_component, &stitched_component_size); } else { /* try to get blob for current component from tss response */ if (tss_response && tss_response_get_blob_by_entry(tss_response, component_name, &component_blob) < 0) { @@ -2733,20 +2815,13 @@ void build_identity_print_information(plist_t build_identity) plist_get_string_val(node, &value); info("Variant: %s\n", value); - free(value); - - node = plist_dict_get_item(info_node, "RestoreBehavior"); - if (!node || plist_get_node_type(node) != PLIST_STRING) { - error("ERROR: Unable to find RestoreBehavior node\n"); - return; - } - plist_get_string_val(node, &value); - - if (!strcmp(value, "Erase")) - info("This restore will erase your device data.\n"); - if (!strcmp(value, "Update")) - info("This restore will update your device without erasing user data.\n"); + if (strstr(value, RESTORE_VARIANT_UPGRADE_INSTALL)) + info("This restore will update the device without erasing user data.\n"); + else if (strstr(value, RESTORE_VARIANT_ERASE_INSTALL)) + info("This restore will erase all device data.\n"); + else + info("Unknown Variant '%s'\n", value); free(value); @@ -2754,7 +2829,7 @@ void build_identity_print_information(plist_t build_identity) node = NULL; } -int build_identity_check_components_in_ipsw(plist_t build_identity, const char *ipsw) +int build_identity_check_components_in_ipsw(plist_t build_identity, ipsw_archive_t ipsw) { plist_t manifest_node = plist_dict_get_item(build_identity, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { @@ -2846,42 +2921,38 @@ int build_identity_get_component_path(plist_t build_identity, const char* compon const char* get_component_name(const char* filename) { - if (!strncmp(filename, "LLB", 3)) { - return "LLB"; - } else if (!strncmp(filename, "iBoot", 5)) { - return "iBoot"; - } else if (!strncmp(filename, "DeviceTree", 10)) { - return "DeviceTree"; - } else if (!strncmp(filename, "applelogo", 9)) { - return "AppleLogo"; - } else if (!strncmp(filename, "liquiddetect", 12)) { - return "Liquid"; - } else if (!strncmp(filename, "lowpowermode", 12)) { - return "LowPowerWallet0"; - } else if (!strncmp(filename, "recoverymode", 12)) { - return "RecoveryMode"; - } else if (!strncmp(filename, "batterylow0", 11)) { - return "BatteryLow0"; - } else if (!strncmp(filename, "batterylow1", 11)) { - return "BatteryLow1"; - } else if (!strncmp(filename, "glyphcharging", 13)) { - return "BatteryCharging"; - } else if (!strncmp(filename, "glyphplugin", 11)) { - return "BatteryPlugin"; - } else if (!strncmp(filename, "batterycharging0", 16)) { - return "BatteryCharging0"; - } else if (!strncmp(filename, "batterycharging1", 16)) { - return "BatteryCharging1"; - } else if (!strncmp(filename, "batteryfull", 11)) { - return "BatteryFull"; - } else if (!strncmp(filename, "needservice", 11)) { - return "NeedService"; - } else if (!strncmp(filename, "SCAB", 4)) { - return "SCAB"; - } else if (!strncmp(filename, "sep-firmware", 12)) { - return "RestoreSEP"; - } else { - error("WARNING: Unhandled component '%s'", filename); - return NULL; + struct filename_component_map { + const char *fnprefix; + int matchlen; + const char *compname; + }; + struct filename_component_map fn_comp_map[] = { + { "LLB", 3, "LLB" }, + { "iBoot", 5, "iBoot" }, + { "DeviceTree", 10, "DeviceTree" }, + { "applelogo", 9, "AppleLogo" }, + { "liquiddetect", 12, "Liquid" }, + { "lowpowermode", 12, "LowPowerWallet0" }, + { "recoverymode", 12, "RecoveryMode" }, + { "batterylow0", 11, "BatteryLow0" }, + { "batterylow1", 11, "BatteryLow1" }, + { "glyphcharging", 13, "BatteryCharging" }, + { "glyphplugin", 11, "BatteryPlugin" }, + { "batterycharging0", 16, "BatteryCharging0" }, + { "batterycharging1", 16, "BatteryCharging1" }, + { "batteryfull", 11, "BatteryFull" }, + { "needservice", 11, "NeedService" }, + { "SCAB", 4, "SCAB" }, + { "sep-firmware", 12, "RestoreSEP" }, + { NULL, 0, NULL } + }; + int i = 0; + while (fn_comp_map[i].fnprefix) { + if (!strncmp(filename, fn_comp_map[i].fnprefix, fn_comp_map[i].matchlen)) { + return fn_comp_map[i].compname; + } + i++; } + error("WARNING: Unhandled component '%s'", filename); + return NULL; } diff --git a/src/idevicerestore.h b/src/idevicerestore.h index 880f9ee..5afcf1a 100644 --- a/src/idevicerestore.h +++ b/src/idevicerestore.h @@ -47,7 +47,13 @@ extern "C" { #define FLAG_NO_RESTORE (1 << 11) #define FLAG_IGNORE_ERRORS (1 << 12) +#define RESTORE_VARIANT_ERASE_INSTALL "Erase Install (IPSW)" +#define RESTORE_VARIANT_UPGRADE_INSTALL "Upgrade Install (IPSW)" +#define RESTORE_VARIANT_MACOS_RECOVERY_OS "macOS Customer" + struct idevicerestore_client_t; +struct ipsw_archive; +typedef struct ipsw_archive* ipsw_archive_t; enum { RESTORE_STEP_DETECT = 0, @@ -57,6 +63,7 @@ enum { RESTORE_STEP_FLASH_FW, RESTORE_STEP_FLASH_BB, RESTORE_STEP_FUD, + RESTORE_STEP_UPLOAD_IMG, RESTORE_NUM_STEPS }; @@ -86,8 +93,8 @@ const char* idevicerestore_get_error(void); irecv_device_t get_irecv_device(struct idevicerestore_client_t* client); int get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); int is_image4_supported(struct idevicerestore_client_t* client); -int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); -int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); +int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int get_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); int get_local_policy_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); @@ -101,14 +108,13 @@ int build_manifest_get_identity_count(plist_t build_manifest); int build_manifest_check_compatibility(plist_t build_manifest, const char* product); void build_manifest_get_version_information(plist_t build_manifest, struct idevicerestore_client_t* client); plist_t build_manifest_get_build_identity_for_model(plist_t build_manifest, const char *hardware_model); -plist_t build_manifest_get_build_identity_for_model_with_variant(plist_t build_manifest, const char *hardware_model, const char *variant); +plist_t build_manifest_get_build_identity_for_model_with_variant(plist_t build_manifest, const char *hardware_model, const char *variant, int exact); int build_manifest_get_build_count(plist_t build_manifest); void build_identity_print_information(plist_t build_identity); -int build_identity_check_components_in_ipsw(plist_t build_identity, const char* ipsw); int build_identity_has_component(plist_t build_identity, const char* component); int build_identity_get_component_path(plist_t build_identity, const char* component, char** path); -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 ipsw_extract_filesystem(ipsw_archive_t ipsw, plist_t build_identity, char** filesystem); +int extract_component(ipsw_archive_t 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); @@ -24,6 +24,7 @@ #include "common.h" #include "img4.h" +#include "tss.h" #define ASN1_PRIVATE 0xc0 #define ASN1_PRIMITIVE_TAG 0x1f @@ -201,7 +202,7 @@ static void asn1_write_element(unsigned char **p, unsigned int *length, unsigned } } break; default: - fprintf(stderr, "ERROR: %s: type %02x is not implemented", __func__, type); + fprintf(stderr, "ERROR: %s: type %02x is not implemented\n", __func__, type); return; } } @@ -275,28 +276,37 @@ static const char *_img4_get_component_tag(const char *compname) { "ACIBT", "acib" }, { "ACIBTLPEM", "lpbt" }, { "ACIWIFI", "aciw" }, - { "Alamo", "almo" }, { "ANE", "anef" }, { "ANS", "ansf" }, { "AOP", "aopf" }, + { "AVE", "avef" }, + { "Alamo", "almo" }, + { "Ap,ANE1", "ane1" }, + { "Ap,ANE2", "ane2" }, + { "Ap,ANE3", "ane3" }, { "Ap,AudioAccessibilityBootChime", "auac" }, { "Ap,AudioBootChime", "aubt" }, { "Ap,AudioPowerAttachChime", "aupr" }, + { "Ap,BootabilityBrainTrustCache", "trbb" }, { "Ap,CIO", "ciof" }, { "Ap,HapticAssets", "hpas" }, { "Ap,LocalBoot", "lobo" }, { "Ap,LocalPolicy", "lpol" }, { "Ap,NextStageIM4MHash", "nsih" }, { "Ap,RecoveryOSPolicyNonceHash", "ronh" }, + { "Ap,RestoreANE1", "ran1" }, + { "Ap,RestoreANE2", "ran2" }, + { "Ap,RestoreANE3", "ran3" }, { "Ap,RestoreCIO", "rcio" }, { "Ap,RestoreTMU", "rtmu" }, { "Ap,Scorpius", "scpf" }, { "Ap,SystemVolumeCanonicalMetadata", "msys" }, { "Ap,TMU", "tmuf" }, { "Ap,VolumeUUID", "vuid" }, + { "Ap,rOSLogo1", "rlg1" }, + { "Ap,rOSLogo2", "rlg2" }, { "AppleLogo", "logo" }, { "AudioCodecFirmware", "acfw" }, - { "AVE", "avef" }, { "BatteryCharging", "glyC" }, { "BatteryCharging0", "chg0" }, { "BatteryCharging1", "chg1" }, @@ -305,28 +315,22 @@ static const char *_img4_get_component_tag(const char *compname) { "BatteryLow1", "bat1" }, { "BatteryPlugin", "glyP" }, { "CFELoader", "cfel" }, - { "Dali", "dali" }, + { "CrownFirmware", "crwn" }, { "DCP", "dcpf" }, + { "Dali", "dali" }, { "DeviceTree", "dtre" }, { "Diags", "diag" }, { "EngineeringTrustCache", "dtrs" }, { "ExtDCP", "edcp" }, - { "ftap", "ftap" }, - { "ftsp", "ftsp" }, { "GFX", "gfxf" }, { "Hamm", "hamf" }, { "Homer", "homr" }, - { "iBEC", "ibec" }, - { "iBoot", "ibot" }, - { "iBootData", "ibdt" }, - { "iBootTest", "itst" }, - { "iBSS", "ibss" }, - { "InputDevice", "ipdf" }, { "ISP", "ispf" }, + { "InputDevice", "ipdf" }, { "KernelCache", "krnl" }, + { "LLB", "illb" }, { "LeapHaptics", "lphp" }, { "Liquid", "liqd" }, - { "LLB", "illb" }, { "LoadableTrustCache", "ltrs" }, { "LowPowerWallet0", "lpw0" }, { "LowPowerWallet1", "lpw1" }, @@ -337,12 +341,13 @@ static const char *_img4_get_component_tag(const char *compname) { "NeedService", "nsrv" }, { "OS", "OS\0\0" }, { "OSRamdisk", "osrd" }, - { "PersonalizedDMG", "pdmg" }, { "PEHammer", "hmmr" }, { "PERTOS", "pert" }, { "PHLEET", "phlt" }, { "PMP", "pmpf" }, + { "PersonalizedDMG", "pdmg" }, { "RBM", "rmbt" }, + { "RTP", "rtpf" }, { "Rap,SoftwareBinaryDsp1", "sbd1" }, { "Rap,RTKitOS", "rkos" }, { "Rap,RestoreRTKitOS", "rrko" }, @@ -353,12 +358,10 @@ static const char *_img4_get_component_tag(const char *compname) { "RestoreExtDCP", "recp" }, { "RestoreKernelCache", "rkrn" }, { "RestoreLogo", "rlgo" }, + { "RestoreRTP", "rrtp" }, { "RestoreRamDisk", "rdsk" }, { "RestoreSEP", "rsep" }, { "RestoreTrustCache", "rtsc" }, - { "rfta", "rfta" }, - { "rfts", "rfts" }, - { "RTP", "rtpf" }, { "SCE", "scef" }, { "SCE1Firmware", "sc1f" }, { "SEP", "sepi" }, @@ -367,6 +370,16 @@ static const char *_img4_get_component_tag(const char *compname) { "SystemLocker", "lckr" }, { "SystemVolume", "isys" }, { "WCHFirmwareUpdater", "wchf" }, + { "ftap", "ftap" }, + { "ftsp", "ftsp" }, + { "iBEC", "ibec" }, + { "iBSS", "ibss" }, + { "iBoot", "ibot" }, + { "iBootData", "ibdt" }, + { "iBootDataStage1", "ibd1" }, + { "iBootTest", "itst" }, + { "rfta", "rfta" }, + { "rfts", "rfts" }, { NULL, NULL } }; int i = 0; @@ -381,7 +394,7 @@ static const char *_img4_get_component_tag(const char *compname) return NULL; } -int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size) +int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** img4_data, unsigned int *img4_size) { unsigned char* magic_header = NULL; unsigned int magic_header_size = 0; @@ -392,8 +405,15 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo unsigned int content_size; unsigned char* outbuf; unsigned char* p; + unsigned char* blob = NULL; + unsigned int blob_size = 0; + + if (!component_name || !component_data || component_size == 0 || !tss_response || !img4_data || !img4_size) { + return -1; + } - if (!component_name || !component_data || component_size == 0 || !blob || blob_size == 0 || !img4_data || !img4_size) { + if (tss_response_get_ap_img4_ticket(tss_response, &blob, &blob_size) != 0) { + error("ERROR: %s: Failed to get ApImg4Ticket from TSS response\n", __func__); return -1; } @@ -423,13 +443,152 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo } } + // check if we have a *-TBM entry for the given component + unsigned char *additional_data = NULL; + unsigned int additional_size = 0; + char *tbm_key = malloc(strlen(component_name) + 5); + sprintf(tbm_key, "%s-TBM", component_name); + plist_t tbm_dict = plist_dict_get_item(tss_response, tbm_key); + free(tbm_key); + if (tbm_dict) { + plist_t dt = plist_dict_get_item(tbm_dict, "ucon"); + if (!dt) { + error("ERROR: %s: Missing ucon node in %s-TBM dictionary\n", __func__, component_name); + return -1; + } + uint64_t ucon_size = 0; + const char* ucon_data = plist_get_data_ptr(dt, &ucon_size); + if (!ucon_data) { + error("ERROR: %s: Missing ucon data in %s-TBM dictionary\n", __func__, component_name); + return -1; + } + dt = plist_dict_get_item(tbm_dict, "ucer"); + if (!dt) { + error("ERROR: %s: Missing ucer data node in %s-TBM dictionary\n", __func__, component_name); + return -1; + } + uint64_t ucer_size = 0; + const char* ucer_data = plist_get_data_ptr(dt, &ucer_size); + if (!ucer_data) { + error("ERROR: %s: Missing ucer data in %s-TBM dictionary\n", __func__, component_name); + return -1; + } + + unsigned char *im4rset = (unsigned char*)malloc(16 + 8 + 8 + ucon_size + 16 + 8 + 8 + ucer_size + 16); + unsigned char *p_im4rset = im4rset; + unsigned int im4rlen = 0; + + // ----------- ucon ------------ + // write priv ucon element + asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"nocu"); + + // write ucon IA5STRING and ucon data + unsigned char ucon_seq[16]; + unsigned char *p_ucon_seq = &ucon_seq[0]; + unsigned int ucon_seq_hdr_len = 0; + asn1_write_element(&p_ucon_seq, &ucon_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucon", -1); + asn1_write_element_header(ASN1_OCTET_STRING, ucon_size, &p_ucon_seq, &ucon_seq_hdr_len); + + // write ucon sequence + unsigned char elem_seq[8]; + unsigned char *p = &elem_seq[0]; + unsigned int seq_hdr_len = 0; + asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, ucon_seq_hdr_len + ucon_size, &p, &seq_hdr_len); + + // add size to priv ucon element + asn1_write_size(ucon_seq_hdr_len + ucon_size + seq_hdr_len, &p_im4rset, &im4rlen); + + // put it together + memcpy(p_im4rset, elem_seq, seq_hdr_len); + p_im4rset += seq_hdr_len; + im4rlen += seq_hdr_len; + memcpy(p_im4rset, ucon_seq, ucon_seq_hdr_len); + p_im4rset += ucon_seq_hdr_len; + im4rlen += ucon_seq_hdr_len; + memcpy(p_im4rset, ucon_data, ucon_size); + p_im4rset += ucon_size; + im4rlen += ucon_size; + + // ----------- ucer ------------ + // write priv ucer element + asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"recu"); + + // write ucon IA5STRING and ucer data + unsigned char ucer_seq[16]; + unsigned char *p_ucer_seq = &ucer_seq[0]; + unsigned int ucer_seq_hdr_len = 0; + asn1_write_element(&p_ucer_seq, &ucer_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucer", -1); + asn1_write_element_header(ASN1_OCTET_STRING, ucer_size, &p_ucer_seq, &ucer_seq_hdr_len); + + p = &elem_seq[0]; + seq_hdr_len = 0; + asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, ucer_seq_hdr_len + ucer_size, &p, &seq_hdr_len); + + // add size to priv ucer element + asn1_write_size(ucer_seq_hdr_len + ucer_size + seq_hdr_len, &p_im4rset, &im4rlen); + + // put it together + memcpy(p_im4rset, elem_seq, seq_hdr_len); + p_im4rset += seq_hdr_len; + im4rlen += seq_hdr_len; + memcpy(p_im4rset, ucer_seq, ucer_seq_hdr_len); + p_im4rset += ucer_seq_hdr_len; + im4rlen += ucer_seq_hdr_len; + memcpy(p_im4rset, ucer_data, ucer_size); + p_im4rset += ucer_size; + im4rlen += ucer_size; + + // now construct IM4R + + /* write inner set */ + unsigned char inner_set_[8]; + unsigned char *inner_set = &inner_set_[0]; + unsigned int inner_set_len = 0; + asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, im4rlen, &inner_set, &inner_set_len); + + /* write header values */ + unsigned char hdrdata_[16]; + unsigned char *hdrdata = &hdrdata_[0]; + unsigned int hdrdata_len = 0; + asn1_write_element(&hdrdata, &hdrdata_len, ASN1_IA5_STRING, (void*)"IM4R", -1); + + /* write sequence now that we know the entire size */ + unsigned char seq_[8]; + unsigned char *seq = &seq_[0]; + unsigned int seq_len = 0; + asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, im4rlen + inner_set_len + hdrdata_len, &seq, &seq_len); + + /* write outer cont[1] */ + unsigned char cont_[8]; + unsigned char *cont = &cont_[0]; + unsigned int cont_len = 0; + asn1_write_element_header(ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1, im4rlen + inner_set_len + hdrdata_len + seq_len, &cont, &cont_len); + + // now put everything together + additional_data = malloc(im4rlen + inner_set_len + hdrdata_len + seq_len + cont_len); + p = additional_data; + memcpy(p, cont_, cont_len); + p += cont_len; + memcpy(p, seq_, seq_len); + p += seq_len; + memcpy(p, hdrdata_, hdrdata_len); + p += hdrdata_len; + memcpy(p, inner_set_, inner_set_len); + p += inner_set_len; + memcpy(p, im4rset, im4rlen); + p += im4rlen; + additional_size = (unsigned int)(p - additional_data); + + free(im4rset); + } + // create element header for the "IMG4" magic asn1_create_element_header(ASN1_IA5_STRING, IMG4_MAGIC_SIZE, &magic_header, &magic_header_size); // create element header for the blob (ApImg4Ticket) asn1_create_element_header(ASN1_CONTEXT_SPECIFIC|ASN1_CONSTRUCTED, blob_size, &blob_header, &blob_header_size); // calculate the size for the final IMG4 file (asn1 sequence) - content_size = magic_header_size + IMG4_MAGIC_SIZE + component_size + blob_header_size + blob_size; + content_size = magic_header_size + IMG4_MAGIC_SIZE + component_size + blob_header_size + blob_size + additional_size; // create element header for the final IMG4 asn1 blob asn1_create_element_header(ASN1_SEQUENCE|ASN1_CONSTRUCTED, content_size, &img4header, &img4header_size); @@ -445,6 +604,7 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo if (img4header) { free(img4header); } + free(additional_data); error("ERROR: out of memory when personalizing IMG4 component %s\n", component_name); return -1; } @@ -463,6 +623,10 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo p += blob_header_size; memcpy(p, blob, blob_size); p += blob_size; + if (additional_size) { + memcpy(p, additional_data, additional_size); + p += additional_size; + } *img4_data = outbuf; *img4_size = (p - outbuf); @@ -476,6 +640,7 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo if (img4header) { free(img4header); } + free(additional_data); return 0; } @@ -540,13 +705,11 @@ static void _manifest_write_component(unsigned char **p, unsigned int *length, c node = plist_dict_get_item(comp, "Digest"); if (node) { - char *digest = NULL; uint64_t digest_len = 0; - plist_get_data_val(node, &digest, &digest_len); + const char *digest = plist_get_data_ptr(node, &digest_len); if (digest_len > 0) { - _manifest_write_key_value(&tmp, &tmp_len, "DGST", ASN1_OCTET_STRING, digest, digest_len); + _manifest_write_key_value(&tmp, &tmp_len, "DGST", ASN1_OCTET_STRING, (void*)digest, digest_len); } - free(digest); } node = plist_dict_get_item(comp, "Trusted"); @@ -575,9 +738,8 @@ static void _manifest_write_component(unsigned char **p, unsigned int *length, c node = plist_dict_get_item(comp, "TBMDigests"); if (node) { - char *data = NULL; uint64_t datalen = 0; - plist_get_data_val(node, &data, &datalen); + const char *data = plist_get_data_ptr(node, &datalen); const char *tbmtag = NULL; if (!strcmp(tag, "sepi")) { tbmtag = "tbms"; @@ -587,9 +749,8 @@ static void _manifest_write_component(unsigned char **p, unsigned int *length, c if (!tbmtag) { error("ERROR: Unexpected TMBDigests for comp '%s'\n", tag); } else { - _manifest_write_key_value(&tmp, &tmp_len, tbmtag, ASN1_OCTET_STRING, data, datalen); + _manifest_write_key_value(&tmp, &tmp_len, tbmtag, ASN1_OCTET_STRING, (void*)data, datalen); } - free(data); } asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, tmp_len, &inner_start, &inner_length); @@ -627,30 +788,28 @@ int img4_create_local_manifest(plist_t request, plist_t build_identity, plist_t* unsigned int length = 0; uint64_t uintval = 0; unsigned int boolval = 0; - char *strval = NULL; - plist_t node = NULL; unsigned char tmp_[1024]; unsigned char *tmp = &tmp_[0]; unsigned int tmp_len = 0; /* write manifest properties */ - uintval = _plist_dict_get_uint(request, "ApBoardID"); + uintval = plist_dict_get_uint(request, "ApBoardID"); _manifest_write_key_value(&tmp, &tmp_len, "BORD", ASN1_INTEGER, &uintval, -1); uintval = 0; _manifest_write_key_value(&tmp, &tmp_len, "CEPO", ASN1_INTEGER, &uintval, -1); - uintval = _plist_dict_get_uint(request, "ApChipID"); + uintval = plist_dict_get_uint(request, "ApChipID"); _manifest_write_key_value(&tmp, &tmp_len, "CHIP", ASN1_INTEGER, &uintval, -1); - boolval = _plist_dict_get_bool(request, "ApProductionMode"); + boolval = plist_dict_get_bool(request, "ApProductionMode"); _manifest_write_key_value(&tmp, &tmp_len, "CPRO", ASN1_BOOLEAN, &boolval, -1); boolval = 0; _manifest_write_key_value(&tmp, &tmp_len, "CSEC", ASN1_BOOLEAN, &boolval, -1); - uintval = _plist_dict_get_uint(request, "ApSecurityDomain"); + uintval = plist_dict_get_uint(request, "ApSecurityDomain"); _manifest_write_key_value(&tmp, &tmp_len, "SDOM", ASN1_INTEGER, &uintval, -1); /* create manifest properties set */ @@ -681,13 +840,12 @@ int img4_create_local_manifest(plist_t request, plist_t build_identity, plist_t* comp = _img4_get_component_tag(key); } if (!comp) { - error("ERROR: %s: Unhandled component '%s' - can't create manifest\n", __func__, key); - free(iter); - free(buf); - return -1; + debug("DEBUG: %s: Unhandled component '%s'\n", __func__, key); + _manifest_write_component(&p, &length, key, val); + } else { + debug("DEBUG: found component %s (%s)\n", comp, key); + _manifest_write_component(&p, &length, comp, val); } - debug("DEBUG: found component %s (%s)\n", comp, key); - _manifest_write_component(&p, &length, comp, val); } free(key); } while (val); @@ -26,7 +26,7 @@ extern "C" { #endif -int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size); +int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** img4_data, unsigned int *img4_size); int img4_create_local_manifest(plist_t request, plist_t build_identity, plist_t* manifest); #ifdef __cplusplus @@ -45,26 +45,18 @@ #endif #include <libimobiledevice-glue/termcolors.h> +#include <plist/plist.h> #include "ipsw.h" #include "locking.h" #include "download.h" #include "common.h" #include "idevicerestore.h" -#include "json_plist.h" #define BUFSIZE 0x100000 -typedef struct { - struct zip* zip; - char *path; -} ipsw_archive; - static int cancel_flag = 0; -ipsw_archive* ipsw_open(const char* ipsw); -void ipsw_close(ipsw_archive* archive); - static char* build_path(const char* path, const char* file) { size_t plen = strlen(path); @@ -118,11 +110,14 @@ int ipsw_print_info(const char* path) uint32_t plist_len = 0; if (memcmp(&magic, "PK\x03\x04", 4) == 0) { + ipsw_archive_t ipsw = ipsw_open(thepath); unsigned int rlen = 0; - if (ipsw_extract_to_memory(thepath, "BuildManifest.plist", (unsigned char**)&plist_buf, &rlen) < 0) { + if (ipsw_extract_to_memory(ipsw, "BuildManifest.plist", (unsigned char**)&plist_buf, &rlen) < 0) { + ipsw_close(ipsw); error("ERROR: Failed to extract BuildManifest.plist from IPSW!\n"); return -1; } + ipsw_close(ipsw); plist_len = (uint32_t)rlen; } else { size_t rlen = 0; @@ -134,7 +129,7 @@ int ipsw_print_info(const char* path) } plist_t manifest = NULL; - plist_from_memory(plist_buf, plist_len, &manifest); + plist_from_memory(plist_buf, plist_len, &manifest, NULL); free(plist_buf); plist_t val; @@ -152,10 +147,10 @@ int ipsw_print_info(const char* path) plist_get_string_val(val, &build_ver); } - cprintf(COLOR_WHITE "Product Version: " COLOR_BRIGHT_YELLOW "%s" COLOR_RESET COLOR_WHITE " Build: " COLOR_BRIGHT_YELLOW "%s" COLOR_RESET "\n", prod_ver, build_ver); + cprintf(FG_WHITE "Product Version: " FG_BRIGHT_YELLOW "%s" COLOR_RESET FG_WHITE " Build: " FG_BRIGHT_YELLOW "%s" COLOR_RESET "\n", prod_ver, build_ver); free(prod_ver); free(build_ver); - cprintf(COLOR_WHITE "Supported Product Types:" COLOR_RESET); + cprintf(FG_WHITE "Supported Product Types:" COLOR_RESET); val = plist_dict_get_item(manifest, "SupportedProductTypes"); if (val) { plist_array_iter iter = NULL; @@ -167,7 +162,7 @@ int ipsw_print_info(const char* path) if (item) { char* item_str = NULL; plist_get_string_val(item, &item_str); - cprintf(" " COLOR_BRIGHT_CYAN "%s" COLOR_RESET, item_str); + cprintf(" " FG_BRIGHT_CYAN "%s" COLOR_RESET, item_str); free(item_str); } } while (item); @@ -176,7 +171,7 @@ int ipsw_print_info(const char* path) } cprintf("\n"); - cprintf(COLOR_WHITE "Build Identities:" COLOR_RESET "\n"); + cprintf(FG_WHITE "Build Identities:" COLOR_RESET "\n"); plist_t build_ids_grouped = plist_new_dict(); @@ -201,7 +196,15 @@ int ipsw_print_info(const char* path) if (!group) { group = plist_new_dict(); node = plist_access_path(build_identity, 2, "Info", "RestoreBehavior"); - plist_dict_set_item(group, "RestoreBehavior", plist_copy(node)); + if (node) { + plist_dict_set_item(group, "RestoreBehavior", plist_copy(node)); + } else { + if (strstr(variant_str, "Upgrade")) { + plist_dict_set_item(group, "RestoreBehavior", plist_new_string("Update")); + } else if (strstr(variant_str, "Erase")) { + plist_dict_set_item(group, "RestoreBehavior", plist_new_string("Erase")); + } + } entries = plist_new_array(); plist_dict_set_item(group, "Entries", entries); plist_dict_set_item(build_ids_grouped, variant_str, group); @@ -232,7 +235,7 @@ int ipsw_print_info(const char* path) group_no++; node = plist_dict_get_item(group, "RestoreBehavior"); plist_get_string_val(node, &rbehavior); - cprintf(" " COLOR_WHITE "[%d] Variant: " COLOR_BRIGHT_CYAN "%s" COLOR_WHITE " Behavior: " COLOR_BRIGHT_CYAN "%s" COLOR_RESET "\n", group_no, key, rbehavior); + cprintf(" " FG_WHITE "[%d] Variant: " FG_BRIGHT_CYAN "%s" FG_WHITE " Behavior: " FG_BRIGHT_CYAN "%s" COLOR_RESET "\n", group_no, key, rbehavior); free(key); free(rbehavior); @@ -285,9 +288,9 @@ int ipsw_print_info(const char* path) irecv_device_t irecvdev = NULL; if (irecv_devices_get_device_by_hardware_model(hwmodel, &irecvdev) == 0) { - cprintf(" ChipID: " COLOR_GREEN "%04x" COLOR_RESET " BoardID: " COLOR_GREEN "%02x" COLOR_RESET " Model: " COLOR_YELLOW "%-8s" COLOR_RESET " " COLOR_MAGENTA "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel, irecvdev->display_name); + cprintf(" ChipID: " FG_GREEN "%04x" COLOR_RESET " BoardID: " FG_GREEN "%02x" COLOR_RESET " Model: " FG_YELLOW "%-8s" COLOR_RESET " " FG_MAGENTA "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel, irecvdev->display_name); } else { - cprintf(" ChipID: " COLOR_GREEN "%04x" COLOR_RESET " BoardID: " COLOR_GREEN "%02x" COLOR_RESET " Model: " COLOR_YELLOW "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel); + cprintf(" ChipID: " FG_GREEN "%04x" COLOR_RESET " BoardID: " FG_GREEN "%02x" COLOR_RESET " Model: " FG_YELLOW "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel); } free(hwmodel); } while (build_id); @@ -302,10 +305,10 @@ int ipsw_print_info(const char* path) return 0; } -ipsw_archive* ipsw_open(const char* ipsw) +ipsw_archive_t ipsw_open(const char* ipsw) { int err = 0; - ipsw_archive* archive = (ipsw_archive*) malloc(sizeof(ipsw_archive)); + ipsw_archive_t archive = (ipsw_archive_t)malloc(sizeof(struct ipsw_archive)); if (archive == NULL) { error("ERROR: Out of memory\n"); return NULL; @@ -316,7 +319,6 @@ ipsw_archive* ipsw_open(const char* ipsw) error("ERROR: ipsw_open %s: %s\n", ipsw, strerror(errno)); return NULL; } - archive->path = strdup(ipsw); if (S_ISDIR(fst.st_mode)) { archive->zip = NULL; } else { @@ -327,7 +329,20 @@ ipsw_archive* ipsw_open(const char* ipsw) return NULL; } } - return archive; + archive->path = strdup(ipsw); + return (ipsw_archive_t)archive; +} + +void ipsw_close(ipsw_archive_t ipsw) +{ + if (ipsw != NULL) { + free(ipsw->path); + if (ipsw->zip) { + zip_unchange_all(ipsw->zip); + zip_close(ipsw->zip); + } + free(ipsw); + } } int ipsw_is_directory(const char* ipsw) @@ -340,37 +355,33 @@ int ipsw_is_directory(const char* ipsw) return S_ISDIR(fst.st_mode); } -int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size) +int ipsw_get_file_size(ipsw_archive_t ipsw, const char* infile, uint64_t* size) { - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { + if (ipsw == NULL) { error("ERROR: Invalid archive\n"); return -1; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); if (zindex < 0) { error("ERROR: zip_name_locate: %s\n", infile); - ipsw_close(archive); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { + if (zip_stat_index(ipsw->zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); - ipsw_close(archive); return -1; } *size = zstat.size; } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); struct stat fst; if (stat(filepath, &fst) != 0) { free(filepath); - ipsw_close(archive); return -1; } free(filepath); @@ -378,23 +389,22 @@ int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size) *size = fst.st_size; } - ipsw_close(archive); return 0; } -int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, const char* outfile, int print_progress) +int ipsw_extract_to_file_with_progress(ipsw_archive_t ipsw, const char* infile, const char* outfile, int print_progress) { int ret = 0; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { - error("ERROR: Invalid archive\n"); + + if (!ipsw || !infile || !outfile) { + error("ERROR: Invalid argument\n"); return -1; } cancel_flag = 0; - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); if (zindex < 0) { error("ERROR: zip_name_locate: %s\n", infile); return -1; @@ -402,7 +412,7 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { + if (zip_stat_index(ipsw->zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); return -1; } @@ -413,7 +423,7 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con return -1; } - struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); + struct zip_file* zfile = zip_fopen_index(ipsw->zip, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index: %s\n", infile); return -1; @@ -442,7 +452,7 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con break; } if (fwrite(buffer, 1, count, fd) != count) { - error("ERROR: frite: %s\n", outfile); + error("ERROR: Writing to '%s' failed: %s\n", outfile, strerror(errno)); ret = -1; break; } @@ -457,9 +467,13 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con fclose(fd); zip_fclose(zfile); } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); char actual_filepath[PATH_MAX+1]; char actual_outfile[PATH_MAX+1]; + if (!filepath) { + ret = -1; + goto leave; + } if (!realpath(filepath, actual_filepath)) { error("ERROR: realpath failed on %s: %s\n", filepath, strerror(errno)); ret = -1; @@ -515,7 +529,7 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con break; } if (fwrite(buffer, 1, r, fo) != r) { - error("ERROR: fwrite failed\n"); + error("ERROR: Writing to '%s' failed: %s\n", actual_outfile, strerror(errno)); ret = -1; break; } @@ -534,76 +548,66 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con leave: free(filepath); } - ipsw_close(archive); if (cancel_flag) { ret = -2; } return ret; } -int ipsw_extract_to_file(const char* ipsw, const char* infile, const char* outfile) +int ipsw_extract_to_file(ipsw_archive_t ipsw, const char* infile, const char* outfile) { return ipsw_extract_to_file_with_progress(ipsw, infile, outfile, 0); } -int ipsw_file_exists(const char* ipsw, const char* infile) +int ipsw_file_exists(ipsw_archive_t ipsw, const char* infile) { - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { + if (!ipsw) { return 0; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); if (zindex < 0) { - ipsw_close(archive); return 0; } } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); if (access(filepath, R_OK) != 0) { free(filepath); - ipsw_close(archive); return 0; } free(filepath); } - ipsw_close(archive); - return 1; } -int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize) +int ipsw_extract_to_memory(ipsw_archive_t ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize) { size_t size = 0; unsigned char* buffer = NULL; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { + if (ipsw == NULL) { error("ERROR: Invalid archive\n"); return -1; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); if (zindex < 0) { debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); - ipsw_close(archive); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { + if (zip_stat_index(ipsw->zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); - ipsw_close(archive); return -1; } - struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); + struct zip_file* zfile = zip_fopen_index(ipsw->zip, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index: %s\n", infile); - ipsw_close(archive); return -1; } @@ -612,7 +616,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** if (buffer == NULL) { error("ERROR: Out of memory\n"); zip_fclose(zfile); - ipsw_close(archive); return -1; } @@ -620,7 +623,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: zip_fread: %s\n", infile); zip_fclose(zfile); free(buffer); - ipsw_close(archive); return -1; } @@ -628,7 +630,7 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** zip_fclose(zfile); } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); struct stat fst; #ifdef WIN32 if (stat(filepath, &fst) != 0) { @@ -637,7 +639,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** #endif error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); - ipsw_close(archive); return -1; } size = fst.st_size; @@ -645,7 +646,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** if (buffer == NULL) { error("ERROR: Out of memory\n"); free(filepath); - ipsw_close(archive); return -1; } @@ -655,7 +655,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -1; } } else { @@ -665,7 +664,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -2; } if (fread(buffer, 1, size, f) != size) { @@ -673,7 +671,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -1; } fclose(f); @@ -684,14 +681,143 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** free(filepath); } - ipsw_close(archive); *pbuffer = buffer; *psize = size; return 0; } -int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *tss_enabled) +int ipsw_extract_send(ipsw_archive_t ipsw, const char* infile, int blocksize, ipsw_send_cb send_callback, void* ctx) +{ + unsigned char* buffer = NULL; + size_t done = 0; + size_t total_size = 0; + + if (ipsw == NULL) { + error("ERROR: Invalid archive\n"); + return -1; + } + + if (ipsw->zip) { + int zindex = zip_name_locate(ipsw->zip, infile, 0); + if (zindex < 0) { + debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); + return -1; + } + + struct zip_stat zstat; + zip_stat_init(&zstat); + if (zip_stat_index(ipsw->zip, zindex, 0, &zstat) != 0) { + error("ERROR: zip_stat_index: %s\n", infile); + return -1; + } + + struct zip_file* zfile = zip_fopen_index(ipsw->zip, zindex, 0); + if (zfile == NULL) { + error("ERROR: zip_fopen_index: %s\n", infile); + return -1; + } + + total_size = zstat.size; + buffer = (unsigned char*) malloc(blocksize); + if (buffer == NULL) { + error("ERROR: Out of memory\n"); + zip_fclose(zfile); + return -1; + } + + while (done < total_size) { + size_t size = total_size-done; + if (size > blocksize) size = blocksize; + zip_int64_t zr = zip_fread(zfile, buffer, size); + if (zr < 0) { + error("ERROR: %s: zip_fread: %s\n", __func__, infile); + break; + } else if (zr == 0) { + // EOF + break; + } + if (send_callback(ctx, buffer, zr, done, total_size) < 0) { + error("ERROR: %s: send failed\n", __func__); + break; + } + done += zr; + } + free(buffer); + } else { + char *filepath = build_path(ipsw->path, infile); + struct stat fst; +#ifdef WIN32 + if (stat(filepath, &fst) != 0) { +#else + if (lstat(filepath, &fst) != 0) { +#endif + error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); + free(filepath); + return -1; + } + total_size = fst.st_size; + buffer = (unsigned char*)malloc(blocksize); + if (buffer == NULL) { + error("ERROR: Out of memory\n"); + free(filepath); + return -1; + } + +#ifndef WIN32 + if (S_ISLNK(fst.st_mode)) { + ssize_t rl = readlink(filepath, (char*)buffer, (total_size > blocksize) ? blocksize : total_size); + if (rl < 0) { + error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); + free(filepath); + free(buffer); + return -1; + } + send_callback(ctx, buffer, (size_t)rl, 0, 0); + } else { +#endif + FILE *f = fopen(filepath, "rb"); + if (!f) { + error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); + free(filepath); + free(buffer); + return -2; + } + + while (done < total_size) { + size_t size = total_size-done; + if (size > blocksize) size = blocksize; + size_t fr = fread(buffer, 1, size, f); + if (fr != size) { + error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); + break; + } + if (send_callback(ctx, buffer, fr, done, total_size) < 0) { + error("ERROR: %s: send failed\n", __func__); + break; + } + done += fr; + } + fclose(f); +#ifndef WIN32 + } +#endif + free(filepath); + free(buffer); + } + + if (done < total_size) { + error("ERROR: %s: Sending file data for %s failed (sent %" PRIu64 "/%" PRIu64 ")\n", __func__, infile, (uint64_t)done, (uint64_t)total_size); + return -1; + } + + // send a NULL buffer to mark end of transfer + send_callback(ctx, NULL, 0, done, total_size); + + return 0; +} + +int ipsw_extract_build_manifest(ipsw_archive_t ipsw, plist_t* buildmanifest, int *tss_enabled) { unsigned int size = 0; unsigned char* data = NULL; @@ -721,7 +847,7 @@ int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *t return -1; } -int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist) +int ipsw_extract_restore_plist(ipsw_archive_t ipsw, plist_t* restore_plist) { unsigned int size = 0; unsigned char* data = NULL; @@ -735,10 +861,10 @@ int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist) return -1; } -static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, ipsw_list_cb cb, void *ctx) +static int ipsw_list_contents_recurse(ipsw_archive_t ipsw, const char *path, ipsw_list_cb cb, void *ctx) { int ret = 0; - char *base = build_path(archive->path, path); + char *base = build_path(ipsw->path, path); DIR *dirp = opendir(base); @@ -776,10 +902,10 @@ static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, i break; } - ret = cb(ctx, archive->path, subpath, &st); + ret = cb(ctx, ipsw, subpath, &st); if (ret >= 0 && S_ISDIR(st.st_mode)) - ipsw_list_contents_recurse(archive, subpath, cb, ctx); + ipsw_list_contents_recurse(ipsw, subpath, cb, ctx); free(fpath); free(subpath); @@ -790,21 +916,19 @@ static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, i return ret; } -int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) +int ipsw_list_contents(ipsw_archive_t ipsw, ipsw_list_cb cb, void *ctx) { int ret = 0; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { - error("ERROR: Invalid archive\n"); + if (ipsw == NULL) { + error("ERROR: Invalid IPSW archive\n"); return -1; } - if (archive->zip) { - int64_t entries = zip_get_num_entries(archive->zip, 0); + if (ipsw->zip) { + int64_t entries = zip_get_num_entries(ipsw->zip, 0); if (entries < 0) { error("ERROR: zip_get_num_entries failed\n"); - ipsw_close(archive); return -1; } @@ -812,7 +936,7 @@ int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) zip_stat_t stat; zip_stat_init(&stat); - if (zip_stat_index(archive->zip, index, 0, &stat) < 0) { + if (zip_stat_index(ipsw->zip, index, 0, &stat) < 0) { error("ERROR: zip_stat_index failed for %s\n", stat.name); ret = -1; continue; @@ -820,7 +944,7 @@ int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) uint8_t opsys; uint32_t attributes; - if (zip_file_get_external_attributes(archive->zip, index, 0, &opsys, &attributes) < 0) { + if (zip_file_get_external_attributes(ipsw->zip, index, 0, &opsys, &attributes) < 0) { error("ERROR: zip_file_get_external_attributes failed for %s\n", stat.name); ret = -1; continue; @@ -850,25 +974,12 @@ int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) break; } } else { - ret = ipsw_list_contents_recurse(archive, "", cb, ctx); + ret = ipsw_list_contents_recurse(ipsw, "", cb, ctx); } - ipsw_close(archive); return ret; } -void ipsw_close(ipsw_archive* archive) -{ - if (archive != NULL) { - free(archive->path); - if (archive->zip) { - zip_unchange_all(archive->zip); - zip_close(archive->zip); - } - free(archive); - } -} - int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares) { char url[256]; @@ -892,7 +1003,7 @@ int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares) error("ERROR: Download from %s failed.\n", url); return -1; } - dict = json_to_plist(jdata); + plist_from_json(jdata, jsize, &dict); free(jdata); if (!dict || plist_get_node_type(dict) != PLIST_DICT) { error("ERROR: Failed to parse json data.\n"); @@ -1192,3 +1303,107 @@ void ipsw_cancel(void) { cancel_flag++; } + +ipsw_file_handle_t ipsw_file_open(ipsw_archive_t ipsw, const char* path) +{ + ipsw_file_handle_t handle = (ipsw_file_handle_t)calloc(1, sizeof(struct ipsw_file_handle)); + if (ipsw->zip) { + zip_stat_t zst; + zip_int64_t zindex = zip_name_locate(ipsw->zip, path, 0); + if (zindex < 0) { + error("ERROR: zip_name_locate: %s not found\n", path); + free(handle); + return NULL; + } + handle->zfile = zip_fopen_index(ipsw->zip, zindex, 0); + if (handle->zfile == NULL) { + error("ERROR: zip_fopen_index: %s could not be opened\n", path); + free(handle); + return NULL; + } + zip_stat_init(&zst); + zip_stat(ipsw->zip, path, 0, &zst); + handle->size = zst.size; + handle->seekable = (zst.comp_method == ZIP_CM_STORE); + } else { + struct stat st; + char *filepath = build_path(ipsw->path, path); + handle->file = fopen(filepath, "rb"); + free(filepath); + if (!handle->file) { + error("ERROR: fopen: %s could not be opened\n", path); + free(handle); + return NULL; + } + fstat(fileno(handle->file), &st); + handle->size = st.st_size; + handle->seekable = 1; + } + return handle; +} + +void ipsw_file_close(ipsw_file_handle_t handle) +{ + if (handle && handle->zfile) { + zip_fclose(handle->zfile); + } else if (handle && handle->file) { + fclose(handle->file); + } + free(handle); +} + +uint64_t ipsw_file_size(ipsw_file_handle_t handle) +{ + if (handle) { + return handle->size; + } + return 0; +} + +int64_t ipsw_file_read(ipsw_file_handle_t handle, void* buffer, size_t size) +{ + if (handle && handle->zfile) { + zip_int64_t zr = zip_fread(handle->zfile, buffer, size); + return (int64_t)zr; + } else if (handle && handle->file) { + return fread(buffer, 1, size, handle->file); + } else { + error("ERROR: %s: Invalid file handle\n", __func__); + return -1; + } +} + +int ipsw_file_seek(ipsw_file_handle_t handle, int64_t offset, int whence) +{ + if (handle && handle->zfile) { + return zip_fseek(handle->zfile, offset, whence); + } else if (handle && handle->file) { +#ifdef WIN32 + if (whence == SEEK_SET) { + rewind(handle->file); + } + return (_lseeki64(fileno(handle->file), offset, whence) < 0) ? -1 : 0; +#else + return fseeko(handle->file, offset, whence); +#endif + } else { + error("ERROR: %s: Invalid file handle\n", __func__); + return -1; + } +} + +int64_t ipsw_file_tell(ipsw_file_handle_t handle) +{ + if (handle && handle->zfile) { + return zip_ftell(handle->zfile); + } else if (handle && handle->file) { +#ifdef WIN32 + return _lseeki64(fileno(handle->file), 0, SEEK_CUR); +#else + return ftello(handle->file); +#endif + } else { + error("ERROR: %s: Invalid file handle\n", __func__); + return -1; + } +} @@ -32,19 +32,47 @@ extern "C" { #include <plist/plist.h> #include <sys/stat.h> +struct ipsw_archive { + struct zip* zip; + char *path; +}; +typedef struct ipsw_archive* ipsw_archive_t; + +ipsw_archive_t ipsw_open(const char* ipsw); +void ipsw_close(ipsw_archive_t ipsw); + int ipsw_print_info(const char* ipsw); -typedef int (*ipsw_list_cb)(void *ctx, const char* ipsw, const char *name, struct stat *stat); +typedef int (*ipsw_list_cb)(void *ctx, ipsw_archive_t ipsw, const char *name, struct stat *stat); +typedef int (*ipsw_send_cb)(void *ctx, void *data, size_t size, size_t done, size_t total_size); + +struct ipsw_file_handle { + FILE* file; + struct zip_file* zfile; + uint64_t size; + int seekable; +}; +typedef struct ipsw_file_handle* ipsw_file_handle_t; + +ipsw_file_handle_t ipsw_file_open(ipsw_archive_t, const char* path); +void ipsw_file_close(ipsw_file_handle_t handle); + +uint64_t ipsw_file_size(ipsw_file_handle_t handle); +int64_t ipsw_file_read(ipsw_file_handle_t handle, void* buffer, size_t size); +int ipsw_file_seek(ipsw_file_handle_t handle, int64_t offset, int whence); +int64_t ipsw_file_tell(ipsw_file_handle_t handle); int ipsw_is_directory(const char* ipsw); -int ipsw_file_exists(const char* ipsw, const char* infile); -int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size); -int ipsw_extract_to_file(const char* ipsw, const char* infile, const char* outfile); -int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, const char* outfile, int print_progress); -int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize); -int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *tss_enabled); -int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist); -int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx); + +int ipsw_file_exists(ipsw_archive_t ipsw, const char* infile); +int ipsw_get_file_size(ipsw_archive_t ipsw, const char* infile, uint64_t* size); +int ipsw_extract_to_file(ipsw_archive_t ipsw, const char* infile, const char* outfile); +int ipsw_extract_to_file_with_progress(ipsw_archive_t ipsw, const char* infile, const char* outfile, int print_progress); +int ipsw_extract_to_memory(ipsw_archive_t ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize); +int ipsw_extract_send(ipsw_archive_t ipsw, const char* infile, int blocksize, ipsw_send_cb send_callback, void* ctx); +int ipsw_extract_build_manifest(ipsw_archive_t ipsw, plist_t* buildmanifest, int *tss_enabled); +int ipsw_extract_restore_plist(ipsw_archive_t ipsw, plist_t* restore_plist); +int ipsw_list_contents(ipsw_archive_t ipsw, ipsw_list_cb cb, void *ctx); int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares); int ipsw_download_fw(const char *fwurl, unsigned char* isha1, const char* todir, char** ipswfile); diff --git a/src/jsmn.c b/src/jsmn.c deleted file mode 100644 index a56f20a..0000000 --- a/src/jsmn.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * jsmn.c - * Simple JSON parser - * - * Copyright (c) 2010 Serge A. Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include <stdlib.h> - -#include "jsmn.h" - -/** - * Allocates a fresh unused token from the token pull. - */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *tok; - if (parser->toknext >= num_tokens) { - return NULL; - } - tok = &tokens[parser->toknext++]; - tok->start = tok->end = -1; - tok->size = 0; -#ifdef JSMN_PARENT_LINKS - tok->parent = -1; -#endif - return tok; -} - -/** - * Fills token type and boundaries. - */ -static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, - int start, int end) { - token->type = type; - token->start = start; - token->end = end; - token->size = 0; -} - -/** - * Fills next available token with JSON primitive. - */ -static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - int start; - - start = parser->pos; - - for (; js[parser->pos] != '\0'; parser->pos++) { - switch (js[parser->pos]) { -#ifndef JSMN_STRICT - /* In strict mode primitive must be followed by "," or "}" or "]" */ - case ':': -#endif - case '\t' : case '\r' : case '\n' : case ' ' : - case ',' : case ']' : case '}' : - goto found; - } - if (js[parser->pos] < 32 || js[parser->pos] >= 127) { - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } -#ifdef JSMN_STRICT - /* In strict mode primitive must be followed by a comma/object/array */ - parser->pos = start; - return JSMN_ERROR_PART; -#endif - -found: - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - parser->pos--; - return JSMN_SUCCESS; -} - -/** - * Filsl next token with JSON string. - */ -static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - - int start = parser->pos; - - parser->pos++; - - /* Skip starting quote */ - for (; js[parser->pos] != '\0'; parser->pos++) { - char c = js[parser->pos]; - - /* Quote: end of string */ - if (c == '\"') { - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - return JSMN_SUCCESS; - } - - /* Backslash: Quoted symbol expected */ - if (c == '\\') { - parser->pos++; - switch (js[parser->pos]) { - /* Allowed escaped symbols */ - case '\"': case '/' : case '\\' : case 'b' : - case 'f' : case 'r' : case 'n' : case 't' : - break; - /* Allows escaped symbol \uXXXX */ - case 'u': - /* TODO */ - break; - /* Unexpected symbol */ - default: - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } - } - parser->pos = start; - return JSMN_ERROR_PART; -} - -/** - * Parse JSON string and fill tokens. - */ -jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, - unsigned int num_tokens) { - jsmnerr_t r; - int i; - jsmntok_t *token; - - for (; js[parser->pos] != '\0'; parser->pos++) { - char c; - jsmntype_t type; - - c = js[parser->pos]; - switch (c) { - case '{': case '[': - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) - return JSMN_ERROR_NOMEM; - if (parser->toksuper != -1) { - tokens[parser->toksuper].size++; -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - } - token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); - token->start = parser->pos; - parser->toksuper = parser->toknext - 1; - break; - case '}': case ']': - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); -#ifdef JSMN_PARENT_LINKS - if (parser->toknext < 1) { - return JSMN_ERROR_INVAL; - } - token = &tokens[parser->toknext - 1]; - for (;;) { - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - token->end = parser->pos + 1; - parser->toksuper = token->parent; - break; - } - if (token->parent == -1) { - break; - } - token = &tokens[token->parent]; - } -#else - for (i = parser->toknext - 1; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - parser->toksuper = -1; - token->end = parser->pos + 1; - break; - } - } - /* Error if unmatched closing bracket */ - if (i == -1) return JSMN_ERROR_INVAL; - for (; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - parser->toksuper = i; - break; - } - } -#endif - break; - case '\"': - r = jsmn_parse_string(parser, js, tokens, num_tokens); - if (r < 0) return r; - if (parser->toksuper != -1) - tokens[parser->toksuper].size++; - break; - case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': - break; -#ifdef JSMN_STRICT - /* In strict mode primitives are: numbers and booleans */ - case '-': case '0': case '1' : case '2': case '3' : case '4': - case '5': case '6': case '7' : case '8': case '9': - case 't': case 'f': case 'n' : -#else - /* In non-strict mode every unquoted value is a primitive */ - default: -#endif - r = jsmn_parse_primitive(parser, js, tokens, num_tokens); - if (r < 0) return r; - if (parser->toksuper != -1) - tokens[parser->toksuper].size++; - break; - -#ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; -#endif - - } - } - - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) { - return JSMN_ERROR_PART; - } - } - - return JSMN_SUCCESS; -} - -/** - * Creates a new parser based over a given buffer with an array of tokens - * available. - */ -void jsmn_init(jsmn_parser *parser) { - parser->pos = 0; - parser->toknext = 0; - parser->toksuper = -1; -} - diff --git a/src/jsmn.h b/src/jsmn.h deleted file mode 100644 index f12dc5a..0000000 --- a/src/jsmn.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * jsmn.h - * Simple JSON parser (header file) - * - * Copyright (c) 2010 Serge A. Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef __JSMN_H_ -#define __JSMN_H_ - -/** - * JSON type identifier. Basic types are: - * o Object - * o Array - * o String - * o Other primitive: number, boolean (true/false) or null - */ -typedef enum { - JSMN_PRIMITIVE = 0, - JSMN_OBJECT = 1, - JSMN_ARRAY = 2, - JSMN_STRING = 3 -} jsmntype_t; - -typedef enum { - /* Not enough tokens were provided */ - JSMN_ERROR_NOMEM = -1, - /* Invalid character inside JSON string */ - JSMN_ERROR_INVAL = -2, - /* The string is not a full JSON packet, more bytes expected */ - JSMN_ERROR_PART = -3, - /* Everything was fine */ - JSMN_SUCCESS = 0 -} jsmnerr_t; - -/** - * JSON token description. - * @param type type (object, array, string etc.) - * @param start start position in JSON data string - * @param end end position in JSON data string - */ -typedef struct { - jsmntype_t type; - int start; - int end; - int size; -#ifdef JSMN_PARENT_LINKS - int parent; -#endif -} jsmntok_t; - -/** - * JSON parser. Contains an array of token blocks available. Also stores - * the string being parsed now and current position in that string - */ -typedef struct { - unsigned int pos; /* offset in the JSON string */ - int toknext; /* next token to allocate */ - int toksuper; /* superior token node, e.g parent object or array */ -} jsmn_parser; - -/** - * Create JSON parser over an array of tokens - */ -void jsmn_init(jsmn_parser *parser); - -/** - * Run JSON parser. It parses a JSON data string into and array of tokens, each describing - * a single JSON object. - */ -jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, - jsmntok_t *tokens, unsigned int num_tokens); - -#endif /* __JSMN_H_ */ diff --git a/src/json_plist.c b/src/json_plist.c deleted file mode 100644 index 7bbead0..0000000 --- a/src/json_plist.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * json_plist.c - * JSON/property list functions - * - * Copyright (c) 2013 Nikias Bassen. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <jsmn.h> -#include <plist/plist.h> - -#include "json_plist.h" - -static plist_t parse_primitive(const char* js, jsmntok_t* tokens, int* index); -static plist_t parse_string(const char* js, jsmntok_t* tokens, int* index); -static plist_t parse_array(const char* js, jsmntok_t* tokens, int* index); -static plist_t parse_object(const char* js, jsmntok_t* tokens, int* index); - -static char* get_string_value(const char* js, jsmntok_t token) -{ - int len = (token.end - token.start); - char* str = malloc(len+1); - memcpy(str, js + token.start, len); - str[len] = 0; - return str; -} - -static plist_t parse_primitive(const char* js, jsmntok_t* tokens, int* index) -{ - if (tokens[*index].type != JSMN_PRIMITIVE) { - fprintf(stderr, "%s: ERROR: token type != JSMN_PRIMITIVE?!\n", __func__); - return NULL; - } - plist_t val = NULL; - char* strval = get_string_value(js, tokens[*index]); - if (strval[0] == 'f') { - val = plist_new_bool(0); - } else if (strval[0] == 't') { - val = plist_new_bool(1); - } else if ((strval[0] == '-') || ((strval[0] >= '0') && (strval[0] <= '9'))) { - val = plist_new_uint(strtoll(strval, NULL, 10)); - } else { - fprintf(stderr, "%s: WARNING: invalid primitive value '%s' encountered, will return as string\n", __func__, strval); - val = plist_new_string(strval); - } - free(strval); - (*index)++; - return val; -} - -static plist_t parse_string(const char* js, jsmntok_t* tokens, int* index) -{ - if (tokens[*index].type != JSMN_STRING) { - fprintf(stderr, "%s: ERROR: token type != JSMN_STRING?!\n", __func__); - return NULL; - } - char* str = get_string_value(js, tokens[*index]); - plist_t val = plist_new_string(str); - free(str); - (*index)++; - return val; -} - -static plist_t parse_array(const char* js, jsmntok_t* tokens, int* index) -{ - if (tokens[*index].type != JSMN_ARRAY) { - fprintf(stderr, "%s: ERROR: token type != JSMN_ARRAY?!\n", __func__); - return NULL; - } - plist_t arr = plist_new_array(); - int num_tokens = tokens[*index].size; - int num; - int j = (*index)+1; - for (num = 0; num < num_tokens; num++) { - plist_t val = NULL; - switch (tokens[j].type) { - case JSMN_OBJECT: - val = parse_object(js, tokens, &j); - break; - case JSMN_ARRAY: - val = parse_array(js, tokens, &j); - break; - case JSMN_STRING: - val = parse_string(js, tokens, &j); - break; - case JSMN_PRIMITIVE: - val = parse_primitive(js, tokens, &j); - break; - default: - break; - } - if (val) { - plist_array_append_item(arr, val); - } - } - *(index) = j; - return arr; -} - -static plist_t parse_object(const char* js, jsmntok_t* tokens, int* index) -{ - if (tokens[*index].type != JSMN_OBJECT) { - fprintf(stderr, "%s: ERROR: token type != JSMN_OBJECT?!\n", __func__); - return NULL; - } - plist_t obj = plist_new_dict(); - int num_tokens = tokens[*index].size; - int num; - int j = (*index)+1; - for (num = 0; num < num_tokens; num++) { - if (tokens[j].type == JSMN_STRING) { - char* key = get_string_value(js, tokens[j]); - plist_t val = NULL; - j++; - num++; - switch (tokens[j].type) { - case JSMN_OBJECT: - val = parse_object(js, tokens, &j); - break; - case JSMN_ARRAY: - val = parse_array(js, tokens, &j); - break; - case JSMN_STRING: - val = parse_string(js, tokens, &j); - break; - case JSMN_PRIMITIVE: - val = parse_primitive(js, tokens, &j); - break; - default: - break; - } - if (val) { - plist_dict_set_item(obj, key, val); - } - free(key); - } else { - fprintf(stderr, "%s: keys must be of type STRING\n", __func__); - return NULL; - } - } - (*index) = j; - return obj; -} - -plist_t json_to_plist(const char* json_string) -{ - jsmn_parser parser; - jsmn_init(&parser); - int maxtoks = 256; - jsmntok_t *tokens; - - if (!json_string) { - fprintf(stderr, "%s: ERROR: no JSON string given.\n", __func__); - return NULL; - } - - tokens = malloc(sizeof(jsmntok_t)*maxtoks); - if (!tokens) { - fprintf(stderr, "%s: Out of memory\n", __func__); - return NULL; - } - - int r = 0; -reparse: - r = jsmn_parse(&parser, json_string, tokens, maxtoks); - if (r == JSMN_ERROR_NOMEM) { - //printf("not enough tokens (%d), retrying...\n", maxtoks); - maxtoks+=256; - jsmntok_t* newtokens = realloc(tokens, sizeof(jsmntok_t)*maxtoks); - if (newtokens) { - tokens = newtokens; - goto reparse; - } - } - - switch(r) { - case JSMN_ERROR_NOMEM: - fprintf(stderr, "%s: ERROR: Out of memory...\n", __func__); - return NULL; - case JSMN_ERROR_INVAL: - fprintf(stderr, "%s: ERROR: Invalid character inside JSON string\n", __func__); - return NULL; - case JSMN_ERROR_PART: - fprintf(stderr, "%s: ERROR: The string is not a full JSON packet, more bytes expected\n", __func__); - return NULL; - default: - break; - } - - int startindex = 0; - plist_t plist = NULL; - switch (tokens[startindex].type) { - case JSMN_PRIMITIVE: - plist = parse_primitive(json_string, tokens, &startindex); - break; - case JSMN_STRING: - plist = parse_string(json_string, tokens, &startindex); - break; - case JSMN_ARRAY: - plist = parse_array(json_string, tokens, &startindex); - break; - case JSMN_OBJECT: - plist = parse_object(json_string, tokens, &startindex); - break; - default: - break; - } - - free(tokens); - - return plist; -} - diff --git a/src/json_plist.h b/src/json_plist.h deleted file mode 100644 index d6eec0b..0000000 --- a/src/json_plist.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * json_plist.h - * JSON/property list functions (header file) - * - * Copyright (c) 2013 Nikias Bassen. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __JSON_PLIST_H -#define __JSON_PLIST_H - -#ifdef __cplusplus -extern "C" { -#endif - -plist_t json_to_plist(const char* json_string); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/normal.c b/src/normal.c index 15d3e29..e699bbe 100644 --- a/src/normal.c +++ b/src/normal.c @@ -29,6 +29,7 @@ #include <libimobiledevice/libimobiledevice.h> #include <libimobiledevice/lockdown.h> #include <libimobiledevice/preboard.h> +#include <usbmuxd.h> #include "common.h" #include "normal.h" @@ -41,6 +42,7 @@ static int normal_idevice_new(struct idevicerestore_client_t* client, idevice_t* idevice_t dev = NULL; idevice_error_t device_error; lockdownd_client_t lockdown = NULL; + plist_t node = NULL; *device = NULL; @@ -65,6 +67,10 @@ static int normal_idevice_new(struct idevicerestore_client_t* client, idevice_t* return -1; } free(type); + if (lockdownd_get_value(lockdown, NULL, "UniqueChipID", &node) == LOCKDOWN_E_SUCCESS) { + plist_get_uint_val(node, &client->ecid); + plist_free(node); + } lockdownd_client_free(lockdown); lockdown = NULL; @@ -107,7 +113,7 @@ static int normal_idevice_new(struct idevicerestore_client_t* client, idevice_t* } free(type); - plist_t node = NULL; + node = NULL; if ((lockdownd_get_value(lockdown, NULL, "UniqueChipID", &node) != LOCKDOWN_E_SUCCESS) || !node || (plist_get_node_type(node) != PLIST_UINT)){ if (node) { plist_free(node); @@ -228,12 +234,6 @@ int normal_enter_recovery(struct idevicerestore_client_t* client) return -1; } - /* unpair the device */ - lockdown_error = lockdownd_unpair(lockdown, NULL); - if (lockdown_error != LOCKDOWN_E_SUCCESS) { - error("WARNING: Could not unpair device\n"); - } - lockdown_error = lockdownd_enter_recovery(lockdown); if (lockdown_error == LOCKDOWN_E_SESSION_INACTIVE) { lockdownd_client_free(lockdown); @@ -280,6 +280,9 @@ int normal_enter_recovery(struct idevicerestore_client_t* client) return -1; } + /* remove pair record for given device */ + usbmuxd_delete_pair_record(client->udid); + return 0; } @@ -320,7 +323,7 @@ plist_t normal_get_lockdown_value(struct idevicerestore_client_t* client, const return node; } -static int normal_get_nonce_by_key(struct idevicerestore_client_t* client, const char* key, unsigned char** nonce, int* nonce_size) +static int normal_get_nonce_by_key(struct idevicerestore_client_t* client, const char* key, unsigned char** nonce, unsigned int* nonce_size) { plist_t nonce_node = normal_get_lockdown_value(client, NULL, key); @@ -331,18 +334,18 @@ static int normal_get_nonce_by_key(struct idevicerestore_client_t* client, const uint64_t n_size = 0; plist_get_data_val(nonce_node, (char**)nonce, &n_size); - *nonce_size = (int)n_size; + *nonce_size = (unsigned int)n_size; plist_free(nonce_node); return 0; } -int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { return normal_get_nonce_by_key(client, "SEPNonce", nonce, nonce_size); } -int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { return normal_get_nonce_by_key(client, "ApNonce", nonce, nonce_size); } @@ -362,27 +365,26 @@ int normal_is_image4_supported(struct idevicerestore_client_t* client) return bval; } -int normal_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid) +int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info) { - plist_t unique_chip_node = normal_get_lockdown_value(client, NULL, "UniqueChipID"); - if (!unique_chip_node || plist_get_node_type(unique_chip_node) != PLIST_UINT) { - error("ERROR: Unable to get ECID\n"); - return -1; - } - plist_get_uint_val(unique_chip_node, ecid); - plist_free(unique_chip_node); + uint8_t has_telephony_capability = 0; + plist_t node; - return 0; -} + node = normal_get_lockdown_value(client, NULL, "TelephonyCapability"); + plist_get_bool_val(node, &has_telephony_capability); + plist_free(node); -int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info) -{ - plist_t node = normal_get_lockdown_value(client, NULL, "FirmwarePreflightInfo"); - if (!node || plist_get_node_type(node) != PLIST_DICT) { - error("ERROR: Unable to get FirmwarePreflightInfo\n"); - return -1; + if (has_telephony_capability) { + node = normal_get_lockdown_value(client, NULL, "FirmwarePreflightInfo"); + if (!node || plist_get_node_type(node) != PLIST_DICT) { + error("ERROR: Unable to get FirmwarePreflightInfo\n"); + return -1; + } + *preflight_info = node; + } else { + debug("DEBUG: Device does not have TelephonyCapability, no FirmwarePreflightInfo\n"); + *preflight_info = NULL; } - *preflight_info = node; return 0; } @@ -460,13 +462,13 @@ int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_ } else { plist_t node; - if (_plist_dict_get_bool(pl, "Skip")) { + if (plist_dict_get_bool(pl, "Skip")) { result = 0; info("Device does not require stashbag.\n"); break; } - if (_plist_dict_get_bool(pl, "ShowDialog")) { + 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" @@ -489,13 +491,13 @@ int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_ plist_free(pl); break; } - if (_plist_dict_get_bool(pl, "Timeout")) { + 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")) { + if (plist_dict_get_bool(pl, "HideDialog")) { plist_free(pl); /* hide dialog */ result = 1; @@ -577,7 +579,6 @@ int normal_handle_commit_stashbag(struct idevicerestore_client_t* client, plist_ 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; @@ -587,7 +588,7 @@ int normal_handle_commit_stashbag(struct idevicerestore_client_t* client, plist_ } error("ERROR: Could not commit stashbag: %s\n", (strval) ? strval : "(Unknown error)"); free(strval); - } else if (_plist_dict_get_bool(pl, "StashbagCommitComplete")) { + } else if (plist_dict_get_bool(pl, "StashbagCommitComplete")) { info("Stashbag committed!\n"); result = 0; } else { diff --git a/src/normal.h b/src/normal.h index f438f9b..7741ac5 100644 --- a/src/normal.h +++ b/src/normal.h @@ -35,10 +35,9 @@ extern "C" { int normal_check_mode(struct idevicerestore_client_t* client); irecv_device_t normal_get_irecv_device(struct idevicerestore_client_t* client); int normal_enter_recovery(struct idevicerestore_client_t* client); -int normal_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); int normal_is_image4_supported(struct idevicerestore_client_t* client); -int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); -int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); +int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned 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); diff --git a/src/recovery.c b/src/recovery.c index b6a6f51..e3fb4d1 100644 --- a/src/recovery.c +++ b/src/recovery.c @@ -124,7 +124,7 @@ int recovery_enter_restore(struct idevicerestore_client_t* client, plist_t build { if (client->build_major >= 8) { client->restore_boot_args = strdup("rd=md0 nand-enable-reformat=1 -progress"); - } else if (client->build_major >= 20) { + } else if (client->macos_variant) { client->restore_boot_args = strdup("rd=md0 nand-enable-reformat=1 -progress -restore"); } @@ -158,6 +158,20 @@ int recovery_enter_restore(struct idevicerestore_client_t* client, plist_t build free(value); value = NULL; + unsigned long boot_stage = 0; + irecv_getenv(client->recovery->client, "boot-stage", &value); + if (value) { + boot_stage = strtoul(value, NULL, 0); + } + if (boot_stage > 0) { + info("iBoot boot-stage=%s\n", value); + free(value); + value = NULL; + if (boot_stage != 2) { + error("ERROR: iBoot should be at boot stage 2, continuing anyway...\n"); + } + } + unsigned long radio_error = 0; irecv_getenv(client->recovery->client, "radio-error", &value); if (value) { @@ -344,7 +358,7 @@ int recovery_send_ibec(struct idevicerestore_client_t* client, plist_t build_ide return -1; } - recovery_error = irecv_send_command(client->recovery->client, "go"); + recovery_error = irecv_send_command_breq(client->recovery->client, "go", 1); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); return -1; @@ -498,7 +512,7 @@ int recovery_send_kernelcache(struct idevicerestore_client_t* client, plist_t bu recovery_error = irecv_send_command(client->recovery->client, setba); } - recovery_error = irecv_send_command(client->recovery->client, "bootx"); + recovery_error = irecv_send_command_breq(client->recovery->client, "bootx", 1); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); return -1; @@ -507,24 +521,6 @@ int recovery_send_kernelcache(struct idevicerestore_client_t* client, plist_t bu return 0; } -int recovery_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid) -{ - if(client->recovery == NULL) { - if (recovery_client_new(client) < 0) { - return -1; - } - } - - const struct irecv_device_info *device_info = irecv_get_device_info(client->recovery->client); - if (!device_info) { - return -1; - } - - *ecid = device_info->ecid; - - return 0; -} - int recovery_is_image4_supported(struct idevicerestore_client_t* client) { if(client->recovery == NULL) { @@ -541,7 +537,7 @@ int recovery_is_image4_supported(struct idevicerestore_client_t* client) return (device_info->ibfl & IBOOT_FLAG_IMAGE4_AWARE); } -int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { @@ -566,7 +562,7 @@ int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char* return 0; } -int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { @@ -593,7 +589,7 @@ int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char int recovery_send_reset(struct idevicerestore_client_t* client) { - irecv_send_command(client->recovery->client, "reset"); + irecv_send_command_breq(client->recovery->client, "reset", 1); return 0; } diff --git a/src/recovery.h b/src/recovery.h index 154d7d1..d7413e0 100644 --- a/src/recovery.h +++ b/src/recovery.h @@ -54,10 +54,9 @@ int recovery_send_loaded_by_iboot(struct idevicerestore_client_t* client, plist_ int recovery_send_reset(struct idevicerestore_client_t* client); int recovery_send_ticket(struct idevicerestore_client_t* client); int recovery_set_autoboot(struct idevicerestore_client_t* client, int enable); -int recovery_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); int recovery_is_image4_supported(struct idevicerestore_client_t* client); -int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); -int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); +int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); #ifdef __cplusplus diff --git a/src/restore.c b/src/restore.c index 7b61766..050ee3b 100644 --- a/src/restore.c +++ b/src/restore.c @@ -29,6 +29,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <libgen.h> #include <libimobiledevice/restore.h> #ifdef HAVE_REVERSE_PROXY #include <libimobiledevice/reverse_proxy.h> @@ -278,18 +279,12 @@ irecv_device_t restore_get_irecv_device(struct idevicerestore_client_t* client) } if (client->srnm == NULL) { - restore_error = restored_get_value(restore, "SerialNumber", &node); - if (restore_error != RESTORE_E_SUCCESS || !node || plist_get_node_type(node) != PLIST_STRING) { - error("ERROR: Unable to get SerialNumber from restored\n"); - restored_client_free(restore); - idevice_free(device); - return NULL; + if (restored_get_value(restore, "SerialNumber", &node) == RESTORE_E_SUCCESS) { + plist_get_string_val(node, &client->srnm); + info("INFO: device serial number is %s\n", client->srnm); + plist_free(node); + node = NULL; } - - plist_get_string_val(node, &client->srnm); - info("INFO: device serial number is %s\n", client->srnm); - plist_free(node); - node = NULL; } restore_error = restored_get_value(restore, "HardwareModel", &node); @@ -379,8 +374,8 @@ static int restore_is_current_device(struct idevicerestore_client_t* client, con if (!client) { return 0; } - if (!client->srnm) { - error("ERROR: %s: no SerialNumber given in client data\n", __func__); + if (!client->ecid) { + error("ERROR: %s: no ECID given in client data\n", __func__); return 0; } @@ -413,35 +408,35 @@ static int restore_is_current_device(struct idevicerestore_client_t* client, con return 0; } - plist_t node = NULL; - restore_error = restored_get_value(restored, "SerialNumber", &node); - if ((restore_error != RESTORE_E_SUCCESS) || !node || (plist_get_node_type(node) != PLIST_STRING)) { - error("ERROR: %s: Unable to get SerialNumber from restored\n", __func__); + plist_t hwinfo = NULL; + restore_error = restored_query_value(restored, "HardwareInfo", &hwinfo); + if ((restore_error != RESTORE_E_SUCCESS) || !hwinfo) { + error("ERROR: %s: Unable to get HardwareInfo from restored\n", __func__); restored_client_free(restored); idevice_free(device); - if (node) { - plist_free(node); - } + plist_free(hwinfo); return 0; } restored_client_free(restored); idevice_free(device); - char* this_srnm = NULL; - plist_get_string_val(node, &this_srnm); - plist_free(node); + uint64_t this_ecid = 0; + plist_t node = plist_dict_get_item(hwinfo, "UniqueChipID"); + if (node && plist_get_node_type(node) == PLIST_UINT) { + plist_get_uint_val(node, &this_ecid); + } + plist_free(hwinfo); - if (!this_srnm) { + if (this_ecid == 0) { + error("ERROR: %s: Unable to get ECID from restored\n", __func__); return 0; } - return (strcasecmp(this_srnm, client->srnm) == 0); + return (this_ecid == client->ecid); } int restore_open_with_timeout(struct idevicerestore_client_t* client) { - int i = 0; - int attempts = 180; char *type = NULL; uint64_t version = 0; idevice_t device = NULL; @@ -450,17 +445,17 @@ int restore_open_with_timeout(struct idevicerestore_client_t* client) restored_error_t restore_error = RESTORE_E_SUCCESS; // no context exists so bail - if(client == NULL) { + if (client == NULL) { return -1; } - if(client->srnm == NULL) { - error("ERROR: no SerialNumber in client data!\n"); + if (client->ecid == 0) { + error("ERROR: no ECID in client data!\n"); return -1; } // create our restore client if it doesn't yet exist - if(client->restore == NULL) { + if (client->restore == NULL) { client->restore = (struct restore_client_t*) malloc(sizeof(struct restore_client_t)); if(client->restore == NULL) { error("ERROR: Out of memory\n"); @@ -748,6 +743,7 @@ int restore_handle_status_msg(restored_client_t client, plist_t msg) case 27: info("Status: Failed to mount filesystems.\n"); break; + case 50: case 51: info("Status: Failed to load SEP Firmware.\n"); break; @@ -902,13 +898,37 @@ static void restore_asr_progress_cb(double progress, void* userdata) } } -int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t device, const char* filesystem) +int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t device, plist_t build_identity) { asr_client_t asr = NULL; info("About to send filesystem...\n"); + ipsw_archive_t ipsw_dummy = NULL; + ipsw_file_handle_t file = NULL; + char* fsname = NULL; + if (build_identity_get_component_path(build_identity, "OS", &fsname) < 0) { + error("ERROR: Unable to get path for filesystem component\n"); + return -1; + } + if (client->filesystem) { + char* path = strdup(client->filesystem); + const char* fsname_base = path_get_basename(path); + char* parent_dir = dirname(path); + ipsw_dummy = ipsw_open(parent_dir); + file = ipsw_file_open(ipsw_dummy, fsname_base); + free(path); + } else { + file = ipsw_file_open(client->ipsw, fsname); + } + if (!file) { + error("ERROR: Unable to open '%s' in ipsw\n", fsname); + free(fsname); + } + if (asr_open_with_timeout(device, &asr) < 0) { + ipsw_file_close(file); + ipsw_close(ipsw_dummy); error("ERROR: Unable to connect to ASR\n"); return -1; } @@ -919,7 +939,9 @@ int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t de // this step sends requested chunks of data from various offsets to asr so // it can validate the filesystem before installing it info("Validating the filesystem\n"); - if (asr_perform_validation(asr, filesystem) < 0) { + if (asr_perform_validation(asr, file) < 0) { + ipsw_file_close(file); + ipsw_close(ipsw_dummy); error("ERROR: ASR was unable to validate the filesystem\n"); asr_free(asr); return -1; @@ -929,11 +951,16 @@ int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t de // once the target filesystem has been validated, ASR then requests the // entire filesystem to be sent. info("Sending filesystem now...\n"); - if (asr_send_payload(asr, filesystem) < 0) { + if (asr_send_payload(asr, file) < 0) { + ipsw_file_close(file); + ipsw_close(ipsw_dummy); error("ERROR: Unable to send payload to ASR\n"); asr_free(asr); return -1; } + ipsw_file_close(file); + ipsw_close(ipsw_dummy); + info("Done sending filesystem\n"); asr_free(asr); @@ -1109,7 +1136,7 @@ int restore_send_component(restored_client_t restore, struct idevicerestore_clie return 0; } -int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity) +int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message) { char* llb_path = NULL; char* llb_filename = NULL; @@ -1127,10 +1154,15 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* unsigned char* nor_data = NULL; plist_t norimage = NULL; plist_t firmware_files = NULL; - uint32_t i; + int flash_version_1 = 0; info("About to send NORData...\n"); + plist_t arguments = plist_dict_get_item(message, "Arguments"); + if (arguments && plist_get_node_type(arguments) == PLIST_DICT) { + flash_version_1 = plist_dict_get_item(arguments, "FlashVersion1") ? 1 : 0; + } + if (client->tss) { if (tss_response_get_path_by_entry(client->tss, "LLB", &llb_path) < 0) { debug("NOTE: Could not get LLB path from TSS data, will fetch from build identity\n"); @@ -1247,12 +1279,10 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* } dict = plist_new_dict(); - plist_dict_set_item(dict, "LlbImageData", plist_new_data((char*)llb_data, (uint64_t) llb_size)); + plist_dict_set_item(dict, "LlbImageData", plist_new_data((char*)llb_data, llb_size)); free(llb_data); - if (client->build_major >= 20) { - // Starting with M1 macs, it seems that NorImageData is now a dict. - // Sending an array like previous versions results in restore success but the machine will SOS after rebooting. + if (flash_version_1) { norimage = plist_new_dict(); } else { norimage = plist_new_array(); @@ -1308,14 +1338,14 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* component_data = NULL; component_size = 0; - if (client->build_major >= 20) { - plist_dict_set_item(norimage, component, plist_new_data((char*)nor_data, (uint64_t)nor_size)); + if (flash_version_1) { + plist_dict_set_item(norimage, component, plist_new_data((char*)nor_data, nor_size)); } else { /* make sure iBoot is the first entry in the array */ if (!strncmp("iBoot", component, 5)) { - plist_array_insert_item(norimage, plist_new_data((char*)nor_data, (uint64_t)nor_size), 0); + plist_array_insert_item(norimage, plist_new_data((char*)nor_data, nor_size), 0); } else { - plist_array_append_item(norimage, plist_new_data((char*)nor_data, (uint64_t)nor_size)); + plist_array_append_item(norimage, plist_new_data((char*)nor_data, nor_size)); } } @@ -1351,7 +1381,7 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* return -1; } - plist_dict_set_item(dict, "RestoreSEPImageData", plist_new_data((char*)personalized_data, (uint64_t) personalized_size)); + plist_dict_set_item(dict, "RestoreSEPImageData", plist_new_data((char*)personalized_data, personalized_size)); free(personalized_data); personalized_data = NULL; personalized_size = 0; @@ -1376,7 +1406,32 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* return -1; } - plist_dict_set_item(dict, "SEPImageData", plist_new_data((char*)personalized_data, (uint64_t) personalized_size)); + plist_dict_set_item(dict, "SEPImageData", plist_new_data((char*)personalized_data, personalized_size)); + free(personalized_data); + personalized_data = NULL; + personalized_size = 0; + } + + if (build_identity_has_component(build_identity, "SepStage1") && + build_identity_get_component_path(build_identity, "SepStage1", &sep_path) == 0) { + component = "SepStage1"; + ret = extract_component(client->ipsw, sep_path, &component_data, &component_size); + free(sep_path); + if (ret < 0) { + error("ERROR: Unable to extract component: %s\n", component); + return -1; + } + + ret = personalize_component(component, component_data, component_size, client->tss, &personalized_data, &personalized_size); + free(component_data); + component_data = NULL; + component_size = 0; + if (ret < 0) { + error("ERROR: Unable to get personalized component: %s\n", component); + return -1; + } + + plist_dict_set_item(dict, "SEPPatchImageData", plist_new_data((char*)personalized_data, personalized_size)); free(personalized_data); personalized_data = NULL; personalized_size = 0; @@ -1455,12 +1510,12 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned } unsigned char* buffer = NULL; - unsigned char* blob = NULL; + const unsigned char* blob = NULL; unsigned char* fdata = NULL; uint64_t fsize = 0; uint64_t blob_size = 0; int zerr = 0; - int zindex = -1; + int64_t zindex = -1; struct zip_stat zstat; struct zip_file* zfile = NULL; struct zip* za = NULL; @@ -1482,7 +1537,7 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned } int is_fls = 0; - int signed_file_idxs[16]; + int64_t signed_file_idxs[16]; int signed_file_count = 0; char* key = NULL; plist_t node = NULL; @@ -1511,13 +1566,13 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned zip_stat_init(&zstat); if (zip_stat_index(za, zindex, 0, &zstat) != 0) { - error("ERROR: zip_stat_index failed for index %d\n", zindex); + error("ERROR: zip_stat_index failed for index %" PRIi64 "\n", zindex); goto leave; } zfile = zip_fopen_index(za, zindex, 0); if (zfile == NULL) { - error("ERROR: zip_fopen_index failed for index %d\n", zindex); + error("ERROR: zip_fopen_index failed for index %" PRIi64 "\n", zindex); goto leave; } @@ -1552,9 +1607,8 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned free(buffer); buffer = NULL; - blob = NULL; blob_size = 0; - plist_get_data_val(node, (char**)&blob, &blob_size); + blob = (const unsigned char*)plist_get_data_ptr(node, &blob_size); if (!blob) { error("ERROR: could not get %s-Blob data\n", key); goto leave; @@ -1571,8 +1625,6 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned goto leave; } } - free(blob); - blob = NULL; fsize = (is_fls ? fls->size : mbn->size); fdata = (unsigned char*)malloc(fsize); @@ -1597,7 +1649,7 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned goto leave; } - if (zip_replace(za, zindex, zs) == -1) { + if (zip_file_replace(za, zindex, zs, 0) == -1) { error("ERROR: could not update signed '%s' in archive\n", signfn); goto leave; } @@ -1615,9 +1667,10 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned free(iter); // remove everything but required files - int i, j, keep, numf = zip_get_num_files(za); + int64_t i, numf = zip_get_num_entries(za, 0); for (i = 0; i < numf; i++) { - keep = 0; + int j; + int keep = 0; // check for signed file index for (j = 0; j < signed_file_count; j++) { if (i == signed_file_idxs[j]) { @@ -1651,13 +1704,13 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned zip_stat_init(&zstat); if (zip_stat_index(za, zindex, 0, &zstat) != 0) { - error("ERROR: zip_stat_index failed for index %d\n", zindex); + error("ERROR: zip_stat_index failed for index %" PRIi64 "\n", zindex); goto leave; } zfile = zip_fopen_index(za, zindex, 0); if (zfile == NULL) { - error("ERROR: zip_fopen_index failed for index %d\n", zindex); + error("ERROR: zip_fopen_index failed for index %" PRIi64 "\n", zindex); goto leave; } @@ -1684,9 +1737,8 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned goto leave; } - blob = NULL; blob_size = 0; - plist_get_data_val(bbticket, (char**)&blob, &blob_size); + blob = (const unsigned char*)plist_get_data_ptr(bbticket, &blob_size); if (!blob) { error("ERROR: could not get BBTicket data\n"); goto leave; @@ -1696,8 +1748,6 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned error("ERROR: could not insert BBTicket to ebl.fls\n"); goto leave; } - free(blob); - blob = NULL; fsize = fls->size; fdata = (unsigned char*)malloc(fsize); @@ -1716,28 +1766,26 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned goto leave; } - if (zip_replace(za, zindex, zs) == -1) { + if (zip_file_replace(za, zindex, zs, 0) == -1) { error("ERROR: could not update archive with ticketed ebl.fls\n"); goto leave; } } else { // add BBTicket as bbticket.der - blob = NULL; blob_size = 0; - plist_get_data_val(bbticket, (char**)&blob, &blob_size); + blob = (const unsigned char*)plist_get_data_ptr(bbticket, &blob_size); if (!blob) { error("ERROR: could not get BBTicket data\n"); goto leave; } - zs = zip_source_buffer(za, blob, blob_size, 1); + zs = zip_source_buffer(za, blob, blob_size, 0); if (!zs) { error("ERROR: out of memory\n"); goto leave; } - blob = NULL; - if (zip_add(za, "bbticket.der", zs) == -1) { + if (zip_file_add(za, "bbticket.der", zs, ZIP_FL_OVERWRITE) == -1) { error("ERROR: could not add bbticket.der to archive\n"); goto leave; } @@ -1768,7 +1816,6 @@ leave: mbn_free(mbn); fls_free(fls); free(buffer); - free(blob); return res; } @@ -1823,7 +1870,7 @@ static int restore_send_baseband_data(restored_client_t restore, struct idevicer plist_dict_set_item(parameters, "BbGoldCertId", plist_new_uint(bb_cert_id)); plist_dict_set_item(parameters, "BbSNUM", plist_new_data((const char*)bb_snum, bb_snum_size)); - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); /* create baseband request */ plist_t request = tss_request_new(NULL); @@ -1914,7 +1961,7 @@ static int restore_send_baseband_data(restored_client_t restore, struct idevicer // send file dict = plist_new_dict(); - plist_dict_set_item(dict, "BasebandData", plist_new_data(buffer, (uint64_t)sz)); + plist_dict_set_item(dict, "BasebandData", plist_new_data(buffer, sz)); free(buffer); buffer = NULL; @@ -1978,7 +2025,7 @@ static int restore_send_image_data(restored_client_t restore, struct idevicerest int want_image_list = 0; arguments = plist_dict_get_item(message, "Arguments"); - want_image_list = _plist_dict_get_bool(arguments, image_list_k); + want_image_list = plist_dict_get_bool(arguments, image_list_k); node = plist_dict_get_item(arguments, "ImageName"); if (node) { plist_get_string_val(node, &image_name); @@ -2108,7 +2155,7 @@ static int restore_send_image_data(restored_client_t restore, struct idevicerest return 0; } -static plist_t restore_get_se_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +static plist_t restore_get_se_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info, plist_t arguments) { const char *comp_name = NULL; char *comp_path = NULL; @@ -2117,6 +2164,7 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; + plist_t p_dgr = NULL; int ret; uint64_t chip_id = 0; plist_t node = plist_dict_get_item(p_info, "SE,ChipID"); @@ -2125,7 +2173,7 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id } if (chip_id == 0x20211) { comp_name = "SE,Firmware"; - } else if (chip_id == 0x73 || chip_id == 0x64 || chip_id == 0xC8 || chip_id == 0xD2) { + } else if (chip_id == 0x73 || chip_id == 0x64 || chip_id == 0xC8 || chip_id == 0xD2 || chip_id == 0x2C || chip_id == 0x36) { comp_name = "SE,UpdatePayload"; } else { info("WARNING: Unknown SE,ChipID 0x%" PRIx64 " detected. Restore might fail.\n", (uint64_t)chip_id); @@ -2140,6 +2188,14 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id debug("DEBUG: %s: using %s\n", __func__, comp_name); } + p_dgr = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (!p_dgr) { + info("NOTE: %s: No DeviceGeneratedRequest in firmware updater data request. Continuing anyway.\n", __func__); + } else if (!PLIST_IS_DICT(p_dgr)) { + error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); + return NULL; + } + if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to get path for '%s' component\n", comp_name); return NULL; @@ -2164,13 +2220,13 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); /* add SE,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for SE TSS request */ - tss_request_add_se_tags(request, parameters, NULL); + tss_request_add_se_tags(request, parameters, p_dgr); plist_free(parameters); @@ -2183,13 +2239,15 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id return NULL; } - if (plist_dict_get_item(response, "SE,Ticket")) { - info("Received SE ticket\n"); + if (plist_dict_get_item(response, "SE2,Ticket")) { + info("Received SE2,Ticket\n"); + } else if (plist_dict_get_item(response, "SE,Ticket")) { + info("Received SE,Ticket\n"); } else { - error("ERROR: No 'SE,Ticket' in TSS response, this might not work\n"); + error("ERROR: No 'SE ticket' in TSS response, this might not work\n"); } - plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, (uint64_t) component_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; @@ -2207,7 +2265,6 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; - plist_t node = NULL; int ret; /* create Savage request */ @@ -2220,7 +2277,7 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); /* add Savage,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); @@ -2281,7 +2338,7 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc *(uint32_t*)(component_data + 4) = htole32((uint32_t)component_size); component_size += 16; - plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, (uint64_t) component_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; @@ -2293,13 +2350,11 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru { char *comp_name = NULL; char *comp_path = NULL; - plist_t comp_node = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; - plist_t node = NULL; int ret; /* create Yonkers request */ @@ -2314,7 +2369,7 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); /* add Yonkers,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); @@ -2365,7 +2420,7 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru comp_name = NULL; plist_t firmware_data = plist_new_dict(); - plist_dict_set_item(firmware_data, "YonkersFirmware", plist_new_data((char *)component_data, (uint64_t)component_size)); + plist_dict_set_item(firmware_data, "YonkersFirmware", plist_new_data((char*)component_data, component_size)); plist_dict_set_item(response, "FirmwareData", firmware_data); free(component_data); @@ -2375,11 +2430,10 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru return response; } -static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info, plist_t arguments) { char *comp_name = NULL; char *comp_path = NULL; - plist_t comp_node = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; ftab_t ftab = NULL; @@ -2388,7 +2442,6 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; - plist_t node = NULL; int ret; /* create Rose request */ @@ -2402,7 +2455,7 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); if (client->image4supported) { @@ -2412,8 +2465,14 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0)); } - /* add Rap,* tags from info dictionary to parameters */ - plist_dict_merge(¶meters, p_info); + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (device_generated_request) { + /* use DeviceGeneratedRequest if present */ + plist_dict_merge(&request, device_generated_request); + } else { + /* add Rap,* tags from info dictionary to parameters */ + plist_dict_merge(¶meters, p_info); + } /* add required tags for Rose TSS request */ tss_request_add_rose_tags(request, parameters, NULL); @@ -2435,6 +2494,12 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct error("ERROR: No 'Rap,Ticket' in TSS response, this might not work\n"); } + /* skip FirmwareData for newer versions */ + if (client->build_major >= 20) { + debug("DEBUG: Not adding FirmwareData.\n"); + return response; + } + comp_name = "Rap,RTKitOS"; if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to get path for '%s' component\n", comp_name); @@ -2504,7 +2569,7 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct ftab_write(ftab, &component_data, &component_size); ftab_free(ftab); - plist_dict_set_item(response, "FirmwareData", plist_new_data((char *)component_data, (uint64_t)component_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; @@ -2516,13 +2581,11 @@ static plist_t restore_get_veridian_firmware_data(restored_client_t restore, str { char *comp_name = "BMU,FirmwareMap"; char *comp_path = NULL; - plist_t comp_node = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; - plist_t node = NULL; int ret; /* create Veridian request */ @@ -2536,7 +2599,7 @@ static plist_t restore_get_veridian_firmware_data(restored_client_t restore, str parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); /* add BMU,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); @@ -2604,23 +2667,76 @@ static plist_t restore_get_veridian_firmware_data(restored_client_t restore, str plist_to_bin(fw_map, &bin_plist, &bin_size); plist_free(fw_map); - plist_dict_set_item(response, "FirmwareData", plist_new_data(bin_plist, (uint64_t)bin_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data(bin_plist, bin_size)); free(bin_plist); return response; } +static plist_t restore_get_generic_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info, plist_t arguments) +{ + plist_t request = NULL; + plist_t response = NULL; + + plist_t p_updater_name = plist_dict_get_item(arguments, "MessageArgUpdaterName"); + const char* s_updater_name = plist_get_string_ptr(p_updater_name, NULL); + + plist_t response_tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "ResponseTags"); + const char* response_ticket = NULL; + if (PLIST_IS_ARRAY(response_tags)) { + plist_t tag0 = plist_array_get_item(response_tags, 0); + if (tag0) { + response_ticket = plist_get_string_ptr(tag0, NULL); + } + } + if (response_ticket == NULL) { + error("ERROR: Unable to determine response ticket from device generated tags"); + return NULL; + } + + /* create TSS request */ + request = tss_request_new(NULL); + if (request == NULL) { + error("ERROR: Unable to create %s TSS request\n", s_updater_name); + return NULL; + } + + /* add device generated request data to request */ + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (!device_generated_request) { + error("ERROR: Could not find DeviceGeneratedRequest in arguments dictionary\n"); + plist_free(request); + return NULL; + } + plist_dict_merge(&request, device_generated_request); + + info("Sending %s TSS request...\n", s_updater_name); + response = tss_request_send(request, client->tss_url); + plist_free(request); + if (response == NULL) { + error("ERROR: Unable to fetch %s ticket\n", s_updater_name); + return NULL; + } + + if (plist_dict_get_item(response, response_ticket)) { + info("Received %s\n", response_ticket); + } else { + error("ERROR: No '%s' in TSS response, this might not work\n", response_ticket); + debug_plist(response); + } + + return response; +} + static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) { char *comp_name = "Baobab,TCON"; char *comp_path = NULL; - plist_t comp_node = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; - plist_t node = NULL; int ret; /* create Baobab request */ @@ -2634,7 +2750,7 @@ static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity); + tss_parameters_add_from_manifest(parameters, build_identity, true); /* add Baobab,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); @@ -2673,7 +2789,7 @@ static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct return NULL; } - plist_dict_set_item(response, "FirmwareData", plist_new_data((char *)component_data, (uint64_t)component_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; @@ -2681,6 +2797,305 @@ static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct return response; } +static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +{ + char comp_name[64]; + char *comp_path = NULL; + unsigned char* component_data = NULL; + unsigned int component_size = 0; + ftab_t ftab = NULL; + ftab_t rftab = NULL; + uint32_t ftag = 0; + plist_t parameters = NULL; + plist_t request = NULL; + plist_t response = NULL; + const char* ticket_name = NULL; + uint32_t tag = 0; + int ret; + + /* create Timer request */ + request = tss_request_new(NULL); + if (request == NULL) { + error("ERROR: Unable to create Timer TSS request\n"); + return NULL; + } + + parameters = plist_new_dict(); + + /* add manifest for current build_identity to parameters */ + tss_parameters_add_from_manifest(parameters, build_identity, true); + + plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); + if (client->image4supported) { + plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(1)); + plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(1)); + } else { + plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0)); + } + + /* add Timer,* tags from info dictionary to parameters */ + plist_t info_array = plist_dict_get_item(p_info, "InfoArray"); + if (!info_array) { + error("ERROR: Could not find InfoArray in info dictionary\n"); + plist_free(parameters); + return NULL; + } else { + plist_t info_dict = plist_array_get_item(info_array, 0); + plist_t hwid = plist_dict_get_item(info_dict, "HardwareID"); + tag = (uint32_t)plist_dict_get_uint(info_dict, "TagNumber"); + char key[64]; + + plist_dict_set_item(parameters, "TagNumber", plist_new_uint(tag)); + plist_t node = plist_dict_get_item(info_dict, "TicketName"); + if (node) { + ticket_name = plist_get_string_ptr(node, NULL); + plist_dict_set_item(parameters, "TicketName", plist_copy(node)); + } + + sprintf(key, "Timer,ChipID,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "ChipID"); + + sprintf(key, "Timer,BoardID,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "BoardID"); + + sprintf(key, "Timer,ECID,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "ECID"); + + sprintf(key, "Timer,Nonce,%u", tag); + plist_dict_copy_data(parameters, hwid, key, "Nonce"); + + sprintf(key, "Timer,SecurityMode,%u", tag); + plist_dict_copy_bool(parameters, hwid, key, "SecurityMode"); + + sprintf(key, "Timer,SecurityDomain,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "SecurityDomain"); + + sprintf(key, "Timer,ProductionMode,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "ProductionStatus"); + } + plist_t ap_info = plist_dict_get_item(p_info, "APInfo"); + if (!ap_info) { + error("ERROR: Could not find APInfo in info dictionary\n"); + plist_free(parameters); + return NULL; + } else { + plist_dict_merge(¶meters, ap_info); + } + + /* add required tags for Timer TSS request */ + tss_request_add_timer_tags(request, parameters, NULL); + + plist_free(parameters); + + info("Sending %s TSS request...\n", ticket_name); + response = tss_request_send(request, client->tss_url); + plist_free(request); + if (response == NULL) { + error("ERROR: Unable to fetch %s\n", ticket_name); + return NULL; + } + + if (plist_dict_get_item(response, ticket_name)) { + info("Received %s\n", ticket_name); + } else { + error("ERROR: No '%s' in TSS response, this might not work\n", ticket_name); + } + + sprintf(comp_name, "Timer,RTKitOS,%u", tag); + if (build_identity_has_component(build_identity, comp_name)) { + if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + error("ERROR: Unable to get path for '%s' component\n", comp_name); + return NULL; + } + ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); + free(comp_path); + comp_path = NULL; + if (ret < 0) { + error("ERROR: Unable to extract '%s' component\n", comp_name); + return NULL; + } + if (ftab_parse(component_data, component_size, &ftab, &ftag) != 0) { + free(component_data); + error("ERROR: Failed to parse '%s' component data.\n", comp_name); + return NULL; + } + free(component_data); + component_data = NULL; + component_size = 0; + if (ftag != 'rkos') { + error("WARNING: Unexpected tag 0x%08x, expected 0x%08x; continuing anyway.\n", ftag, 'rkos'); + } + } else { + info("NOTE: Build identity does not have a '%s' component.\n", comp_name); + } + + sprintf(comp_name, "Timer,RestoreRTKitOS,%u", tag); + if (build_identity_has_component(build_identity, comp_name)) { + if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + ftab_free(ftab); + error("ERROR: Unable to get path for '%s' component\n", comp_name); + return NULL; + } + ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); + free(comp_path); + comp_path = NULL; + if (ret < 0) { + ftab_free(ftab); + error("ERROR: Unable to extract '%s' component\n", comp_name); + return NULL; + } + + ftag = 0; + if (ftab_parse(component_data, component_size, &rftab, &ftag) != 0) { + free(component_data); + ftab_free(ftab); + error("ERROR: Failed to parse '%s' component data.\n", comp_name); + return NULL; + } + free(component_data); + component_data = NULL; + component_size = 0; + if (ftag != 'rkos') { + error("WARNING: Unexpected tag 0x%08x, expected 0x%08x; continuing anyway.\n", ftag, 'rkos'); + } + + if (ftab_get_entry_ptr(rftab, 'rrko', &component_data, &component_size) == 0) { + ftab_add_entry(ftab, 'rrko', component_data, component_size); + } else { + error("ERROR: Could not find 'rrko' entry in ftab. This will probably break things.\n"); + } + ftab_free(rftab); + component_data = NULL; + component_size = 0; + } else { + info("NOTE: Build identity does not have a '%s' component.\n", comp_name); + } + + ftab_write(ftab, &component_data, &component_size); + ftab_free(ftab); + + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); + free(component_data); + component_data = NULL; + component_size = 0; + + return response; +} + +static plist_t restore_get_cryptex1_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info, plist_t arguments) +{ + plist_t parameters = NULL; + plist_t request = NULL; + plist_t response = NULL; + + plist_t p_updater_name = plist_dict_get_item(arguments, "MessageArgUpdaterName"); + const char* s_updater_name = plist_get_string_ptr(p_updater_name, NULL); + + plist_t response_tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "ResponseTags"); + const char* response_ticket = "Cryptex1,Ticket"; + if (PLIST_IS_ARRAY(response_tags)) { + plist_t tag0 = plist_array_get_item(response_tags, 0); + if (tag0) { + response_ticket = plist_get_string_ptr(tag0, NULL); + } + } + + /* create Cryptex1 request */ + request = tss_request_new(NULL); + if (request == NULL) { + error("ERROR: Unable to create %s TSS request\n", s_updater_name); + return NULL; + } + + parameters = plist_new_dict(); + + /* merge data from MessageArgInfo */ + plist_dict_merge(¶meters, p_info); + + /* add tags from manifest to parameters */ + plist_t build_identity_tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "BuildIdentityTags"); + if (PLIST_IS_ARRAY(build_identity_tags)) { + uint32_t i = 0; + for (i = 0; i < plist_array_get_size(build_identity_tags); i++) { + plist_t node = plist_array_get_item(build_identity_tags, i); + const char* key = plist_get_string_ptr(node, NULL); + plist_t item = plist_dict_get_item(build_identity, key); + if (item) { + plist_dict_set_item(parameters, key, plist_copy(item)); + } + } + } + + /* make sure we always have these required tags defined */ + if (!plist_dict_get_item(parameters, "ApProductionMode")) { + plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); + } + if (!plist_dict_get_item(parameters, "ApSecurityMode")) { + plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(1)); + } + if (!plist_dict_get_item(parameters, "ApChipID")) { + plist_dict_copy_uint(parameters, build_identity, "ApChipID", NULL); + } + if (!plist_dict_get_item(parameters, "ApBoardID")) { + plist_dict_copy_uint(parameters, build_identity, "ApBoardID", NULL); + } + + /* add device generated request data to parameters */ + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (!device_generated_request) { + error("ERROR: Could not find DeviceGeneratedRequest in arguments dictionary\n"); + plist_free(parameters); + return NULL; + } + plist_dict_merge(¶meters, device_generated_request); + + /* add Cryptex1 tags to request */ + tss_request_add_cryptex_tags(request, parameters, NULL); + + plist_free(parameters); + + info("Sending %s TSS request...\n", s_updater_name); + response = tss_request_send(request, client->tss_url); + plist_free(request); + if (response == NULL) { + error("ERROR: Unable to fetch %s ticket\n", s_updater_name); + return NULL; + } + + if (plist_dict_get_item(response, response_ticket)) { + info("Received %s\n", response_ticket); + } else { + error("ERROR: No '%s' in TSS response, this might not work\n", response_ticket); + debug_plist(response); + } + + return response; +} + +static int restore_send_firmware_updater_preflight(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message) +{ + plist_t dict = NULL; + int restore_error; + + if (idevicerestore_debug) { + debug("DEBUG: %s: Got FirmwareUpdaterPreflight request:\n", __func__); + debug_plist(message); + } + + dict = plist_new_dict(); + + info("Sending FirmwareResponsePreflight now...\n"); + restore_error = restored_send(restore, dict); + plist_free(dict); + if (restore_error != RESTORE_E_SUCCESS) { + error("ERROR: Couldn't send FirmwareResponsePreflight data (%d)\n", restore_error); + return -1; + } + + info("Done sending FirmwareUpdaterPreflight response\n"); + return 0; +} + static int restore_send_firmware_updater_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message) { plist_t arguments; @@ -2738,7 +3153,7 @@ static int restore_send_firmware_updater_data(restored_client_t restore, struct plist_get_string_val(p_updater_name, &s_updater_name); if (strcmp(s_updater_name, "SE") == 0) { - fwdict = restore_get_se_firmware_data(restore, client, build_identity, p_info); + fwdict = restore_get_se_firmware_data(restore, client, build_identity, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get SE firmware data\n", __func__); goto error_out; @@ -2757,7 +3172,7 @@ static int restore_send_firmware_updater_data(restored_client_t restore, struct goto error_out; } } else if (strcmp(s_updater_name, "Rose") == 0) { - fwdict = restore_get_rose_firmware_data(restore, client, build_identity, p_info); + fwdict = restore_get_rose_firmware_data(restore, client, build_identity, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get Rose firmware data\n", __func__); goto error_out; @@ -2774,9 +3189,37 @@ static int restore_send_firmware_updater_data(restored_client_t restore, struct error("ERROR: %s: Couldn't get AppleTCON firmware data\n", __func__); goto error_out; } + } else if (strcmp(s_updater_name, "PS190") == 0) { + fwdict = restore_get_generic_firmware_data(restore, client, build_identity, p_info, arguments); + if (fwdict == NULL) { + error("ERROR: %s: Couldn't get PCON1 firmware data\n", __func__); + goto error_out; + } + } else if (strcmp(s_updater_name, "AppleTypeCRetimer") == 0) { + fwdict = restore_get_timer_firmware_data(restore, client, build_identity, p_info); + if (fwdict == NULL) { + error("ERROR: %s: Couldn't get AppleTypeCRetimer firmware data\n", __func__); + goto error_out; + } + } else if ((strcmp(s_updater_name, "Cryptex1") == 0) || (strcmp(s_updater_name, "Cryptex1LocalPolicy") == 0)) { + fwdict = restore_get_cryptex1_firmware_data(restore, client, build_identity, p_info, arguments); + if (fwdict == NULL) { + error("ERROR: %s: Couldn't get %s firmware data\n", __func__, s_updater_name); + goto error_out; + } + } else if (strcmp(s_updater_name, "Ace3") == 0) { + fwdict = restore_get_generic_firmware_data(restore, client, build_identity, p_info, arguments); + if (fwdict == NULL) { + error("ERROR: %s: Couldn't get %s firmware data\n", __func__, s_updater_name); + goto error_out; + } } else { - error("ERROR: %s: Got unknown updater name '%s'.\n", __func__, s_updater_name); - goto error_out; + error("ERROR: %s: Got unknown updater name '%s', trying to discover from device generated request.\n", __func__, s_updater_name); + fwdict = restore_get_generic_firmware_data(restore, client, build_identity, p_info, arguments); + if (fwdict == NULL) { + error("ERROR: %s: Couldn't get %s firmware data\n", __func__, s_updater_name); + goto error_out; + } } free(s_updater_name); s_updater_name = NULL; @@ -2803,6 +3246,37 @@ error_out: return -1; } +static int restore_send_receipt_manifest(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity) +{ + plist_t dict; + int restore_error; + + plist_t manifest = plist_dict_get_item(build_identity, "Manifest"); + if (!manifest) { + error("failed to get Manifest node from build_identity"); + goto error_out; + } + + dict = plist_new_dict(); + plist_dict_set_item(dict, "ReceiptManifest", plist_copy(manifest)); + + info("Sending ReceiptManifest data now...\n"); + restore_error = restored_send(restore, dict); + plist_free(dict); + if (restore_error != RESTORE_E_SUCCESS) { + error("ERROR: Couldn't send ReceiptManifest data (%d)\n", restore_error); + goto error_out; + } + + info("Done sending ReceiptManifest data\n"); + + return 0; + +error_out: + return -1; +} + + struct cpio_odc_header { char c_magic[6]; char c_dev[6]; @@ -2869,7 +3343,7 @@ static int cpio_send_file(idevice_connection_t connection, const char *name, str return 0; } -static int restore_bootability_send_one(void *ctx, const char *ipsw, const char *name, struct stat *stat) +static int restore_bootability_send_one(void *ctx, ipsw_archive_t ipsw, const char *name, struct stat *stat) { idevice_connection_t connection = (idevice_connection_t)ctx; const char *prefix = "BootabilityBundle/Restore/Bootability/"; @@ -2950,24 +3424,27 @@ static int restore_send_bootability_bundle_data(restored_client_t restore, struc return 0; } -plist_t restore_get_build_identity(struct idevicerestore_client_t* client, uint8_t is_recover_os) +plist_t restore_get_build_identity(struct idevicerestore_client_t* client, uint8_t is_recovery_os) { const char *variant; - if (is_recover_os) - variant = "macOS Customer"; + if (is_recovery_os) + variant = RESTORE_VARIANT_MACOS_RECOVERY_OS; else if (client->flags & FLAG_ERASE) - variant = "Customer Erase Install (IPSW)"; + variant = RESTORE_VARIANT_ERASE_INSTALL; else - variant = "Customer Upgrade Install (IPSW)"; + variant = RESTORE_VARIANT_UPGRADE_INSTALL; plist_t build_identity = build_manifest_get_build_identity_for_model_with_variant( client->build_manifest, client->device->hardware_model, - variant); + variant, 0); plist_t unique_id_node = plist_dict_get_item(client->build_manifest, "UniqueBuildID"); - debug_plist(unique_id_node); + if (unique_id_node) { + printf("UniqueBuildID: "); + plist_write_to_stream(unique_id_node, stdout, PLIST_FORMAT_PRINT, PLIST_OPT_NONE); + } return build_identity; } @@ -2975,11 +3452,7 @@ plist_t restore_get_build_identity(struct idevicerestore_client_t* client, uint8 plist_t restore_get_build_identity_from_request(struct idevicerestore_client_t* client, plist_t msg) { plist_t args = plist_dict_get_item(msg, "Arguments"); - plist_t is_recovery_node = plist_dict_get_item(args, "IsRecoveryOS"); - uint8_t is_recovery = 0; - plist_get_bool_val(is_recovery_node, &is_recovery); - - return restore_get_build_identity(client, is_recovery); + return restore_get_build_identity(client, plist_dict_get_bool(args, "IsRecoveryOS")); } int extract_macos_variant(plist_t build_identity, char** output) @@ -3000,27 +3473,32 @@ int extract_macos_variant(plist_t build_identity, char** output) return 0; } -int extract_global_manifest(struct idevicerestore_client_t* client, plist_t build_identity, unsigned char** pbuffer, unsigned int* psize) +static char* extract_global_manifest_path(plist_t build_identity, char *variant) { plist_t build_info = plist_dict_get_item(build_identity, "Info"); if (!build_info) { error("ERROR: build identity does not contain an 'Info' element\n"); - return -1; + return NULL; } plist_t device_class_node = plist_dict_get_item(build_info, "DeviceClass"); if (!device_class_node) { error("ERROR: build identity info does not contain a DeviceClass\n"); - return -1; + return NULL; } char *device_class = NULL; plist_get_string_val(device_class_node, &device_class); char *macos_variant = NULL; - int ret = extract_macos_variant(build_identity, &macos_variant); - if (ret != 0) { - free(device_class); - return -1; + int ret; + if (variant) { + macos_variant = variant; + } else { + ret = extract_macos_variant(build_identity, &macos_variant); + if (ret != 0) { + free(device_class); + return NULL; + } } // The path of the global manifest is hardcoded. There's no pointer to in the build manifest. @@ -3030,7 +3508,17 @@ int extract_global_manifest(struct idevicerestore_client_t* client, plist_t buil free(device_class); free(macos_variant); - ret = ipsw_extract_to_memory(client->ipsw, ticket_path, pbuffer, psize); + return ticket_path; +} + +int extract_global_manifest(struct idevicerestore_client_t* client, plist_t build_identity, char *variant, unsigned char** pbuffer, unsigned int* psize) +{ + char* ticket_path = extract_global_manifest_path(build_identity, variant); + if (!ticket_path) { + error("ERROR: failed to get global manifest path\n"); + return -1; + } + int ret = ipsw_extract_to_memory(client->ipsw, ticket_path, pbuffer, psize); if (ret != 0) { free(ticket_path); error("ERROR: failed to read global manifest\n"); @@ -3041,9 +3529,46 @@ int extract_global_manifest(struct idevicerestore_client_t* client, plist_t buil return 0; } +struct _restore_send_file_data_ctx { + struct idevicerestore_client_t* client; + restored_client_t restore; + int last_progress; +}; + +static int _restore_send_file_data(struct _restore_send_file_data_ctx* rctx, void* data, size_t size, size_t done, size_t total_size) +{ + plist_t dict = plist_new_dict(); + if (data != NULL) { + // Send a chunk of file data + plist_dict_set_item(dict, "FileData", plist_new_data((char*)data, size)); + } else { + // Send FileDataDone to mark end of transfer + plist_dict_set_item(dict, "FileDataDone", plist_new_bool(1)); + } + restored_error_t restore_error = restored_send(rctx->restore, dict); + if (restore_error != RESTORE_E_SUCCESS) { + plist_free(dict); + error("ERROR: %s: Failed to send data (%d)\n", __func__, restore_error); + return -1; + } + plist_free(dict); + if (total_size > 0x1000000) { + double progress = (double)done / (double)total_size; + int progress_int = (int)(progress*100.0); + if (progress_int > rctx->last_progress) { + idevicerestore_progress(rctx->client, RESTORE_STEP_UPLOAD_IMG, progress); + rctx->last_progress = progress_int; + } + } + return 0; +} + int restore_send_personalized_boot_object_v3(restored_client_t restore, struct idevicerestore_client_t* client, plist_t msg, plist_t build_identity) { - debug_plist(msg); + if (idevicerestore_debug) { + debug("DEBUG: %s: Got PersonalizedBootObjectV3 request:\n", __func__); + debug_plist(msg); + } char *image_name = NULL; plist_t node = plist_access_path(msg, 2, "Arguments", "ImageName"); @@ -3064,12 +3589,11 @@ int restore_send_personalized_boot_object_v3(restored_client_t restore, struct i plist_t blob = NULL; plist_t dict = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; - char *component_name = component; - info("About to send %s...\n", component_name); + info("About to send %s...\n", component); if (strcmp(image_name, "__GlobalManifest__") == 0) { - int ret = extract_global_manifest(client, build_identity, &data, &size); + int ret = extract_global_manifest(client, build_identity, NULL, &data, &size); if (ret != 0) { return -1; } @@ -3115,7 +3639,7 @@ int restore_send_personalized_boot_object_v3(restored_client_t restore, struct i return -1; } - // Personalize IMG40 + // Personalize IMG4 ret = personalize_component(component, component_data, component_size, client->tss, &data, &size); free(component_data); component_data = NULL; @@ -3125,49 +3649,37 @@ int restore_send_personalized_boot_object_v3(restored_client_t restore, struct i } } - // Make plist - info("Sending %s now...\n", component_name); + info("Sending %s now (%" PRIu64 " bytes)...\n", component, (uint64_t)size); + + struct _restore_send_file_data_ctx rctx; + rctx.client = client; + rctx.restore = restore; + rctx.last_progress = 0; int64_t i = size; while (i > 0) { int blob_size = i > 8192 ? 8192 : i; - - dict = plist_new_dict(); - blob = plist_new_data((char *) (data + size - i), blob_size); - plist_dict_set_item(dict, "FileData", blob); - - restore_error = restored_send(restore, dict); - if (restore_error != RESTORE_E_SUCCESS) { - error("ERROR: Unable to send component %s data\n", component_name); + if (_restore_send_file_data(&rctx, (data + size - i), blob_size, size-i, size) < 0) { + free(data); + error("ERROR: Unable to send component %s data\n", component); return -1; } - - plist_free(dict); - i -= blob_size; } - debug("\n"); - - // Send FileDataDone - dict = plist_new_dict(); - plist_dict_set_item(dict, "FileDataDone", plist_new_bool(1)); - - restore_error = restored_send(restore, dict); - if (restore_error != RESTORE_E_SUCCESS) { - error("ERROR: Unable to send component %s data\n", component_name); - return -1; - } - - plist_free(dict); free(data); - info("Done sending %s\n", component_name); + _restore_send_file_data(&rctx, NULL, 0, size-i, size); + + info("Done sending %s\n", component); return 0; } int restore_send_source_boot_object_v4(restored_client_t restore, struct idevicerestore_client_t* client, plist_t msg, plist_t build_identity) { - debug_plist(msg); + if (idevicerestore_debug) { + debug("DEBUG: %s: Got SourceBootObjectV4 request:\n", __func__); + debug_plist(msg); + } char *image_name = NULL; plist_t node = plist_access_path(msg, 2, "Arguments", "ImageName"); @@ -3190,27 +3702,27 @@ int restore_send_source_boot_object_v4(restored_client_t restore, struct idevice plist_t blob = NULL; plist_t dict = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; - char *component_name = component; - info("About to send %s...\n", component_name); + info("About to send %s...\n", component); if (strcmp(image_name, "__GlobalManifest__") == 0) { - int ret = extract_global_manifest(client, build_identity, &data, &size); - if (ret != 0) { + char *variant = NULL; + plist_t node = plist_access_path(msg, 2, "Arguments", "Variant"); + if (!node || plist_get_node_type(node) != PLIST_STRING) { + debug("Failed to parse arguments from SourceBootObjectV4 plist\n"); return -1; } - } else if (strcmp(image_name, "__RestoreVersion__") == 0) { - int ret = ipsw_extract_to_memory(client->ipsw, "RestoreVersion.plist", &data, &size); - if (ret != 0) { - error("ERROR: failed to read global manifest\n"); + plist_get_string_val(node, &variant); + if (!variant) { + debug("Failed to parse arguments from SourceBootObjectV4 as string\n"); return -1; } + + path = extract_global_manifest_path(build_identity, variant); + } else if (strcmp(image_name, "__RestoreVersion__") == 0) { + path = strdup("RestoreVersion.plist"); } else if (strcmp(image_name, "__SystemVersion__") == 0) { - int ret = ipsw_extract_to_memory(client->ipsw, "SystemVersion.plist", &data, &size); - if (ret != 0) { - error("ERROR: failed to read global manifest\n"); - return -1; - } + path = strdup("SystemVersion.plist"); } else { // Get component path if (client->tss) { @@ -3225,53 +3737,31 @@ int restore_send_source_boot_object_v4(restored_client_t restore, struct idevice return -1; } } - - int ret = extract_component(client->ipsw, path, &data, &size); - free(path); - path = NULL; - if (ret < 0) { - error("ERROR: Unable to extract component %s\n", component); - return -1; - } } - // Make plist - info("Sending %s now...\n", component_name); - - int64_t i = size; - while (i > 0) { - int blob_size = i > 8192 ? 8192 : i; - - dict = plist_new_dict(); - blob = plist_new_data((char *) (data + size - i), blob_size); - plist_dict_set_item(dict, "FileData", blob); - - restore_error = restored_send(restore, dict); - if (restore_error != RESTORE_E_SUCCESS) { - error("ERROR: Unable to send component %s data\n", component_name); - return -1; - } + if (!path) { + error("ERROR: Failed to get path for component %s\n", component); + return -1; + } - plist_free(dict); + uint64_t fsize = 0; + ipsw_get_file_size(client->ipsw, path, &fsize); - i -= blob_size; - } - debug("\n"); + info("Sending %s now (%" PRIu64 " bytes)\n", component, fsize); - // Send FileDataDone - dict = plist_new_dict(); - plist_dict_set_item(dict, "FileDataDone", plist_new_bool(1)); + struct _restore_send_file_data_ctx rctx; + rctx.client = client; + rctx.restore = restore; + rctx.last_progress = 0; - restore_error = restored_send(restore, dict); - if (restore_error != RESTORE_E_SUCCESS) { - error("ERROR: Unable to send component %s data\n", component_name); + if (ipsw_extract_send(client->ipsw, path, 8192, (ipsw_send_cb)_restore_send_file_data, &rctx) < 0) { + free(path); + error("ERROR: Failed to send component %s\n", component); return -1; } + free(path); - plist_free(dict); - free(data); - - info("Done sending %s\n", component_name); + info("Done sending %s\n", component); return 0; } @@ -3352,7 +3842,7 @@ int restore_send_buildidentity(restored_client_t restore, struct idevicerestore_ return 0; } -int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idevice_t device, restored_client_t restore, plist_t message, plist_t build_identity, const char* filesystem) +int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idevice_t device, restored_client_t restore, plist_t message, plist_t build_identity) { plist_t node = NULL; @@ -3364,7 +3854,7 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev // this request is sent when restored is ready to receive the filesystem if (!strcmp(type, "SystemImageData")) { - if(restore_send_filesystem(client, device, filesystem) < 0) { + if(restore_send_filesystem(client, device, build_identity) < 0) { error("ERROR: Unable to send filesystem\n"); return -2; } @@ -3400,7 +3890,7 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev // this request is sent when restored is ready to receive the filesystem else if (!strcmp(type, "RecoveryOSASRImage")) { - if(restore_send_filesystem(client, device, filesystem) < 0) { + if(restore_send_filesystem(client, device, build_identity) < 0) { error("ERROR: Unable to send filesystem\n"); return -2; } @@ -3452,7 +3942,7 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev else if (!strcmp(type, "NORData")) { if((client->flags & FLAG_EXCLUDE) == 0) { - if(restore_send_nor(restore, client, build_identity) < 0) { + if(restore_send_nor(restore, client, build_identity, message) < 0) { error("ERROR: Unable to send NOR data\n"); return -1; } @@ -3483,6 +3973,13 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev } } + else if (!strcmp(type, "FirmwareUpdaterPreflight")) { + if(restore_send_firmware_updater_preflight(restore, client, build_identity, message) < 0) { + error("ERROR: Unable to send FirmwareUpdaterPreflight\n"); + return -1; + } + } + else if (!strcmp(type, "FirmwareUpdaterData")) { if(restore_send_firmware_updater_data(restore, client, build_identity, message) < 0) { error("ERROR: Unable to send FirmwareUpdater data\n"); @@ -3511,6 +4008,20 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev } } + else if (!strcmp(type, "ReceiptManifest")) { + if (restore_send_receipt_manifest(restore, client, build_identity) < 0) { + error("ERROR: Unable to send ReceiptManifest data\n"); + return -1; + } + } + + else if (!strcmp(type, "BasebandUpdaterOutputData")) { + if (restore_handle_baseband_updater_output_data(restore, client, device, message) < 0) { + error("ERROR: Unable to send BasebandUpdaterOutputData data\n"); + return -1; + } + } + else { // Unknown DataType!! error("Unknown data request '%s' received\n", type); @@ -3532,6 +4043,7 @@ plist_t restore_supported_data_types() plist_dict_set_item(dict, "BootabilityBundle", plist_new_bool(0)); plist_dict_set_item(dict, "BuildIdentityDict", plist_new_bool(0)); plist_dict_set_item(dict, "BuildIdentityDictV2", plist_new_bool(0)); + plist_dict_set_item(dict, "Cryptex1LocalPolicy", plist_new_bool(1)); plist_dict_set_item(dict, "DataType", plist_new_bool(0)); plist_dict_set_item(dict, "DiagData", plist_new_bool(0)); plist_dict_set_item(dict, "EANData", plist_new_bool(0)); @@ -3580,6 +4092,12 @@ plist_t restore_supported_data_types() plist_dict_set_item(dict, "SystemImageRootHash", plist_new_bool(0)); plist_dict_set_item(dict, "USBCFWData", plist_new_bool(0)); plist_dict_set_item(dict, "USBCOverride", plist_new_bool(0)); + plist_dict_set_item(dict, "FirmwareUpdaterPreflight", plist_new_bool(1)); + plist_dict_set_item(dict, "ReceiptManifest", plist_new_bool(1)); + plist_dict_set_item(dict, "FirmwareUpdaterDataV2", plist_new_bool(0)); + plist_dict_set_item(dict, "RestoreLocalPolicy", plist_new_bool(1)); + plist_dict_set_item(dict, "AuthInstallCACert", plist_new_bool(1)); + plist_dict_set_item(dict, "OverlayRootDataForKeyIndex", plist_new_bool(1)); return dict; } @@ -3615,7 +4133,7 @@ static void rp_status_cb(reverse_proxy_client_t client, reverse_proxy_status_t s } #endif -int restore_device(struct idevicerestore_client_t* client, plist_t build_identity, const char* filesystem) +int restore_device(struct idevicerestore_client_t* client, plist_t build_identity) { int err = 0; char* type = NULL; @@ -3755,7 +4273,6 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit plist_dict_set_item(opts, "AutoBootDelay", plist_new_uint(0)); if (client->preflight_info) { - plist_t node; plist_t bbus = plist_copy(client->preflight_info); plist_dict_remove_item(bbus, "FusingStatus"); @@ -3763,17 +4280,14 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit plist_dict_set_item(opts, "BBUpdaterState", bbus); - node = plist_dict_get_item(client->preflight_info, "Nonce"); - if (node) { - plist_dict_set_item(opts, "BasebandNonce", plist_copy(node)); - } + plist_dict_copy_data(opts, client->preflight_info, "BasebandNonce", "Nonce"); } plist_dict_set_item(opts, "SupportedDataTypes", restore_supported_data_types()); plist_dict_set_item(opts, "SupportedMessageTypes", restore_supported_message_types()); // FIXME: Should be adjusted for update behaviors - if (client->build_major >= 20) { + if (client->macos_variant) { plist_dict_set_item(opts, "AddSystemPartitionPadding", plist_new_bool(1)); plist_dict_set_item(opts, "AllowUntetheredRestore", plist_new_bool(0)); plist_dict_set_item(opts, "AuthInstallEnableSso", plist_new_bool(0)); @@ -3896,7 +4410,7 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit // this is the restore process loop, it reads each message in from // restored and passes that data on to it's specific handler while (!(client->flags & FLAG_QUIT)) { - if (client->flags & FLAG_IGNORE_ERRORS) { + if (err != 0 && client->flags & FLAG_IGNORE_ERRORS) { error("WARNING: Attempting to continue after critical error, restore might fail...\n"); err = 0; } @@ -3942,7 +4456,7 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit // files sent to the server by the client. these data requests include // SystemImageData, RootTicket, KernelCache, NORData and BasebandData requests if (!strcmp(type, "DataRequestMsg")) { - err = restore_handle_data_request_msg(client, device, restore, message, build_identity, filesystem); + err = restore_handle_data_request_msg(client, device, restore, message, build_identity); } // restore logs are available if a previous restore failed @@ -3971,29 +4485,45 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit else if (!strcmp(type, "CheckpointMsg")) { uint64_t ckpt_id; - uint64_t ckpt_res; + int64_t ckpt_res; uint8_t ckpt_complete = 0; + const char* ckpt_name = NULL; // Get checkpoint id node = plist_dict_get_item(message, "CHECKPOINT_ID"); - if (!node || plist_get_node_type(node) != PLIST_UINT) { + if (!node || plist_get_node_type(node) != PLIST_INT) { debug("Failed to parse checkpoint id from checkpoint plist\n"); return -1; } plist_get_uint_val(node, &ckpt_id); + // Get checkpoint_name + node = plist_dict_get_item(message, "CHECKPOINT_NAME"); + ckpt_name = (node) ? plist_get_string_ptr(node, NULL) : "unknown"; // Get checkpoint result node = plist_dict_get_item(message, "CHECKPOINT_RESULT"); - if (!node || plist_get_node_type(node) != PLIST_UINT) { + if (!node || plist_get_node_type(node) != PLIST_INT) { debug("Failed to parse checkpoint result from checkpoint plist\n"); return -1; } - plist_get_uint_val(node, &ckpt_res); + plist_get_int_val(node, &ckpt_res); // Get checkpoint complete node = plist_dict_get_item(message, "CHECKPOINT_COMPLETE"); if (PLIST_IS_BOOLEAN(node)) { plist_get_bool_val(node, &ckpt_complete); } - if (ckpt_complete) - info("Checkpoint %" PRIu64 " complete with code %" PRIu64 "\n", ckpt_id, ckpt_res); + + if (ckpt_complete) { + info("Checkpoint completed id: 0x%" PRIX64 " (%s) result=%" PRIi64 "\n", ckpt_id, ckpt_name, ckpt_res); + } else { + info("Checkpoint started id: 0x%" PRIX64 " (%s)\n", ckpt_id, ckpt_name); + } + node = plist_dict_get_item(message, "CHECKPOINT_WARNING"); + if (node) { + info("Checkpoint WARNING id: 0x%" PRIX64 " result=%" PRIi64 ": %s\n", ckpt_id, ckpt_res, plist_get_string_ptr(node, NULL)); + } + node = plist_dict_get_item(message, "CHECKPOINT_ERROR"); + if (node) { + info("Checkpoint FAILURE id: 0x%" PRIX64 " result=%" PRIi64 ": %s\n", ckpt_id, ckpt_res, plist_get_string_ptr(node, NULL)); + } } // baseband update message diff --git a/src/restore.h b/src/restore.h index c93e325..765f374 100644 --- a/src/restore.h +++ b/src/restore.h @@ -38,7 +38,6 @@ struct restore_client_t { idevice_t device; char* udid; unsigned int operation; - const char* filesystem; uint64_t protocol_version; restored_client_t client; }; @@ -52,13 +51,13 @@ int restore_reboot(struct idevicerestore_client_t* client); const char* restore_progress_string(unsigned int operation); int restore_handle_status_msg(restored_client_t client, plist_t msg); int restore_handle_progress_msg(struct idevicerestore_client_t* client, plist_t msg); -int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idevice_t device, restored_client_t restore, plist_t message, plist_t build_identity, const char* filesystem); -int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity); +int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idevice_t device, restored_client_t restore, plist_t message, plist_t build_identity); +int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message); int restore_send_root_ticket(restored_client_t restore, struct idevicerestore_client_t* client); int restore_send_component(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, const char* component, const char* component_name); -int restore_device(struct idevicerestore_client_t* client, plist_t build_identity, const char* filesystem); +int restore_device(struct idevicerestore_client_t* client, plist_t build_identity); int restore_open_with_timeout(struct idevicerestore_client_t* client); -int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t device, const char* filesystem); +int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t device, plist_t build_identity); int restore_send_fdr_trust_data(restored_client_t restore, idevice_t device); #ifdef __cplusplus @@ -35,7 +35,7 @@ #include "endianness.h" -#define AUTH_VERSION "850.0.2" +#define AUTH_VERSION "973.40.2" #ifdef WIN32 #define TSS_CLIENT_VERSION_STRING "libauthinstall_Win-"AUTH_VERSION"" @@ -49,7 +49,8 @@ typedef struct { char* content; } tss_response; -char* ecid_to_string(uint64_t ecid) { +char* ecid_to_string(uint64_t ecid) +{ char* ecid_string = malloc(ECID_STRSIZE); memset(ecid_string, '\0', ECID_STRSIZE); if (ecid == 0) { @@ -60,11 +61,10 @@ char* ecid_to_string(uint64_t ecid) { return ecid_string; } -plist_t tss_request_new(plist_t overrides) { - +plist_t tss_request_new(plist_t overrides) +{ plist_t request = plist_new_dict(); - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@HostPlatformInfo", #ifdef WIN32 plist_new_string("windows") @@ -90,580 +90,279 @@ plist_t tss_request_new(plist_t overrides) { int tss_request_add_local_policy_tags(plist_t request, plist_t parameters) { - plist_t node = NULL; - plist_dict_set_item(request, "@ApImg4Ticket", plist_new_bool(1)); - /* Ap,LocalBoot */ - node = plist_dict_get_item(parameters, "Ap,LocalBoot"); - if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { + if (plist_dict_copy_bool(request, parameters, "Ap,LocalBoot", NULL) < 0) { error("ERROR: Unable to find required Ap,LocalBoot in parameters\n"); return -1; } - plist_dict_set_item(request, "Ap,LocalBoot", plist_copy(node)); - node = NULL; - /* Ap,LocalPolicy */ - node = plist_dict_get_item(parameters, "Ap,LocalPolicy"); - if (!node || plist_get_node_type(node) != PLIST_DICT) { + if (plist_dict_copy_item(request, parameters, "Ap,LocalPolicy", NULL) < 0) { error("ERROR: Unable to find required Ap,LocalPolicy in parameters\n"); return -1; } - plist_dict_set_item(request, "Ap,LocalPolicy", plist_copy(node)); - node = NULL; - /* Ap,NextStageIM4MHash */ - node = plist_dict_get_item(parameters, "Ap,NextStageIM4MHash"); - if (!node || plist_get_node_type(node) != PLIST_DATA) { + if (plist_dict_copy_data(request, parameters, "Ap,NextStageIM4MHash", NULL) < 0) { error("ERROR: Unable to find required Ap,NextStageIM4MHash in parameters\n"); return -1; } - plist_dict_set_item(request, "Ap,NextStageIM4MHash", plist_copy(node)); - node = NULL; - - /* Ap,RecoveryOSPolicyNonceHash */ - node = plist_dict_get_item(parameters, "Ap,RecoveryOSPolicyNonceHash"); - if (node) { - plist_dict_set_item(request, "Ap,RecoveryOSPolicyNonceHash", plist_copy(node)); - } - node = NULL; - - /* Ap,VolumeUUID */ - node = plist_dict_get_item(parameters, "Ap,VolumeUUID"); - if (node) { - plist_dict_set_item(request, "Ap,VolumeUUID", plist_copy(node)); - } - node = NULL; - - /* ApECID */ - node = plist_dict_get_item(parameters, "ApECID"); - if (node) { - plist_dict_set_item(request, "ApECID", plist_copy(node)); - } - node = NULL; - - /* ApChipID */ - node = plist_dict_get_item(parameters, "ApChipID"); - if (node) { - plist_dict_set_item(request, "ApChipID", plist_copy(node)); - } - node = NULL; - /* ApBoardID */ - node = plist_dict_get_item(parameters, "ApBoardID"); - if (node) { - plist_dict_set_item(request, "ApBoardID", plist_copy(node)); - } - node = NULL; - - /* ApSecurityDomain */ - node = plist_dict_get_item(parameters, "ApSecurityDomain"); - if (node) { - plist_dict_set_item(request, "ApSecurityDomain", plist_copy(node)); - } - node = NULL; - - /* ApNonce */ - node = plist_dict_get_item(parameters, "ApNonce"); - if (node) { - plist_dict_set_item(request, "ApNonce", plist_copy(node)); - } - node = NULL; + plist_dict_copy_data(request, parameters, "Ap,RecoveryOSPolicyNonceHash", NULL); + plist_dict_copy_data(request, parameters, "Ap,VolumeUUID", NULL); + plist_dict_copy_uint(request, parameters, "ApECID", NULL); + plist_dict_copy_uint(request, parameters, "ApChipID", NULL); + plist_dict_copy_uint(request, parameters, "ApBoardID", NULL); + plist_dict_copy_uint(request, parameters, "ApSecurityDomain", NULL); + plist_dict_copy_data(request, parameters, "ApNonce", NULL); - /* ApSecurityMode */ - node = plist_dict_get_item(request, "ApSecurityMode"); - if (!node) { + if (!plist_dict_get_item(request, "ApSecurityMode")) { /* copy from parameters if available */ - node = plist_dict_get_item(parameters, "ApSecurityMode"); - if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { + if (plist_dict_copy_bool(request, parameters, "ApSecurityMode", NULL) < 0) { error("ERROR: Unable to find required ApSecurityMode in parameters\n"); return -1; } - plist_dict_set_item(request, "ApSecurityMode", plist_copy(node)); - node = NULL; } - - node = plist_dict_get_item(request, "ApProductionMode"); - if (!node) { - /* ApProductionMode */ - node = plist_dict_get_item(parameters, "ApProductionMode"); - if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { + if (!plist_dict_get_item(request, "ApProductionMode")) { + /* copy from parameters if available */ + if (plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL) < 0) { error("ERROR: Unable to find required ApProductionMode in parameters\n"); return -1; } - plist_dict_set_item(request, "ApProductionMode", plist_copy(node)); - node = NULL; } return 0; } -int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity) +int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity, bool include_manifest) { plist_t node = NULL; - char* string = NULL; - /* UniqueBuildID */ - node = plist_dict_get_item(build_identity, "UniqueBuildID"); - if (!node || plist_get_node_type(node) != PLIST_DATA) { + if (plist_dict_copy_data(parameters, build_identity, "UniqueBuildID", NULL) < 0) { error("ERROR: Unable to find UniqueBuildID node\n"); return -1; } - plist_dict_set_item(parameters, "UniqueBuildID", plist_copy(node)); - node = NULL; - /* Ap,OSLongVersion */ - node = plist_dict_get_item(build_identity, "Ap,OSLongVersion"); - if (node) { - plist_dict_set_item(parameters, "Ap,OSLongVersion", plist_copy(node)); - } + plist_dict_copy_string(parameters, build_identity, "Ap,OSLongVersion", NULL); - /* ApChipID */ - int chip_id = 0; - node = plist_dict_get_item(build_identity, "ApChipID"); - if (!node || plist_get_node_type(node) != PLIST_STRING) { + if (plist_dict_copy_uint(parameters, build_identity, "ApChipID", NULL) < 0) {; error("ERROR: Unable to find ApChipID node\n"); return -1; } - plist_get_string_val(node, &string); - sscanf(string, "%x", &chip_id); - plist_dict_set_item(parameters, "ApChipID", plist_new_uint(chip_id)); - free(string); - string = NULL; - node = NULL; - /* ApBoardID */ - int board_id = 0; - node = plist_dict_get_item(build_identity, "ApBoardID"); - if (!node || plist_get_node_type(node) != PLIST_STRING) { + if (plist_dict_copy_uint(parameters, build_identity, "ApBoardID", NULL) < 0) { error("ERROR: Unable to find ApBoardID node\n"); return -1; } - plist_get_string_val(node, &string); - sscanf(string, "%x", &board_id); - plist_dict_set_item(parameters, "ApBoardID", plist_new_uint(board_id)); - free(string); - string = NULL; - node = NULL; - /* ApSecurityDomain */ - int security_domain = 0; - node = plist_dict_get_item(build_identity, "ApSecurityDomain"); - if (!node || plist_get_node_type(node) != PLIST_STRING) { - error("ERROR: Unable to find ApSecurityDomain node\n"); - return -1; - } - plist_get_string_val(node, &string); - sscanf(string, "%x", &security_domain); - plist_dict_set_item(parameters, "ApSecurityDomain", plist_new_uint(security_domain)); - free(string); - string = NULL; - node = NULL; - - /* BMU,BoardID */ - node = plist_dict_get_item(build_identity, "BMU,BoardID"); - if (node) { - plist_dict_set_item(parameters, "BMU,BoardID", plist_copy(node)); - } + plist_dict_copy_uint(parameters, build_identity, "ApSecurityDomain", NULL); + plist_dict_copy_uint(parameters, build_identity, "BMU,BoardID", NULL); + plist_dict_copy_uint(parameters, build_identity, "BMU,ChipID", NULL); - /* BMU,ChipID */ - node = plist_dict_get_item(build_identity, "BMU,ChipID"); - if (node) { - plist_dict_set_item(parameters, "BMU,ChipID", plist_copy(node)); - } - - /* BbChipID */ - int bb_chip_id = 0; - char* bb_chip_id_string = NULL; - node = plist_dict_get_item(build_identity, "BbChipID"); - if (node && plist_get_node_type(node) == PLIST_STRING) { - plist_get_string_val(node, &bb_chip_id_string); - sscanf(bb_chip_id_string, "%x", &bb_chip_id); - plist_dict_set_item(parameters, "BbChipID", plist_new_uint(bb_chip_id)); - } else { + if (plist_dict_copy_uint(parameters, build_identity, "BbChipID", NULL) < 0) { debug("NOTE: Unable to find BbChipID node\n"); } - node = NULL; - /* BbProvisioningManifestKeyHash */ - node = plist_dict_get_item(build_identity, "BbProvisioningManifestKeyHash"); - if (node && plist_get_node_type(node) == PLIST_DATA) { - plist_dict_set_item(parameters, "BbProvisioningManifestKeyHash", plist_copy(node)); - } else { + if (plist_dict_copy_data(parameters, build_identity, "BbProvisioningManifestKeyHash", NULL) < 0) { debug("NOTE: Unable to find BbProvisioningManifestKeyHash node\n"); } - node = NULL; - /* BbActivationManifestKeyHash - Used by Qualcomm MDM6610 */ - node = plist_dict_get_item(build_identity, "BbActivationManifestKeyHash"); - if (node && plist_get_node_type(node) == PLIST_DATA) { - plist_dict_set_item(parameters, "BbActivationManifestKeyHash", plist_copy(node)); - } else { + if (plist_dict_copy_data(parameters, build_identity, "BbActivationManifestKeyHash", NULL) < 0) { debug("NOTE: Unable to find BbActivationManifestKeyHash node\n"); } - node = NULL; - node = plist_dict_get_item(build_identity, "BbCalibrationManifestKeyHash"); - if (node && plist_get_node_type(node) == PLIST_DATA) { - plist_dict_set_item(parameters, "BbCalibrationManifestKeyHash", plist_copy(node)); - } else { + if (plist_dict_copy_data(parameters, build_identity, "BbCalibrationManifestKeyHash", NULL) < 0) { debug("NOTE: Unable to find BbCalibrationManifestKeyHash node\n"); } - node = NULL; - /* BbFactoryActivationManifestKeyHash */ - node = plist_dict_get_item(build_identity, "BbFactoryActivationManifestKeyHash"); - if (node && plist_get_node_type(node) == PLIST_DATA) { - plist_dict_set_item(parameters, "BbFactoryActivationManifestKeyHash", plist_copy(node)); - } else { + if (plist_dict_copy_data(parameters, build_identity, "BbFactoryActivationManifestKeyHash", NULL) < 0) { debug("NOTE: Unable to find BbFactoryActivationManifestKeyHash node\n"); } - node = NULL; - /* BbFDRSecurityKeyHash */ - node = plist_dict_get_item(build_identity, "BbFDRSecurityKeyHash"); - if (node && plist_get_node_type(node) == PLIST_DATA) { - plist_dict_set_item(parameters, "BbFDRSecurityKeyHash", plist_copy(node)); - } else { + if (plist_dict_copy_data(parameters, build_identity, "BbFDRSecurityKeyHash", NULL) < 0) { debug("NOTE: Unable to find BbFDRSecurityKeyHash node\n"); } - node = NULL; /* BbSkeyId - Used by XMM 6180/GSM */ - node = plist_dict_get_item(build_identity, "BbSkeyId"); - if (node && plist_get_node_type(node) == PLIST_DATA) { - plist_dict_set_item(parameters, "BbSkeyId", plist_copy(node)); - } else { + if (plist_dict_copy_data(parameters, build_identity, "BbSkeyId", NULL) < 0) { debug("NOTE: Unable to find BbSkeyId node\n"); } - node = NULL; /* SE,ChipID - Used for SE firmware request */ - node = plist_dict_get_item(build_identity, "SE,ChipID"); - if (node) { - if (plist_get_node_type(node) == PLIST_STRING) { - char *strval = NULL; - int intval = 0; - plist_get_string_val(node, &strval); - sscanf(strval, "%x", &intval); - plist_dict_set_item(parameters, "SE,ChipID", plist_new_uint(intval)); - } else { - plist_dict_set_item(parameters, "SE,ChipID", plist_copy(node)); - } - } - node = NULL; + plist_dict_copy_uint(parameters, build_identity, "SE,ChipID", NULL); /* Savage,ChipID - Used for Savage firmware request */ - node = plist_dict_get_item(build_identity, "Savage,ChipID"); - if (node) { - if (plist_get_node_type(node) == PLIST_STRING) { - char *strval = NULL; - int intval = 0; - plist_get_string_val(node, &strval); - sscanf(strval, "%x", &intval); - plist_dict_set_item(parameters, "Savage,ChipID", plist_new_uint(intval)); - } else { - plist_dict_set_item(parameters, "Savage,ChipID", plist_copy(node)); - } - } - node = NULL; + plist_dict_copy_uint(parameters, build_identity, "Savage,ChipID", NULL); /* add Savage,PatchEpoch - Used for Savage firmware request */ - node = plist_dict_get_item(build_identity, "Savage,PatchEpoch"); - if (node) { - if (plist_get_node_type(node) == PLIST_STRING) { - char *strval = NULL; - int intval = 0; - plist_get_string_val(node, &strval); - sscanf(strval, "%x", &intval); - plist_dict_set_item(parameters, "Savage,PatchEpoch", plist_new_uint(intval)); - } else { - plist_dict_set_item(parameters, "Savage,PatchEpoch", plist_copy(node)); - } - } - node = NULL; + plist_dict_copy_uint(parameters, build_identity, "Savage,PatchEpoch", NULL); /* Yonkers,BoardID - Used for Yonkers firmware request */ - node = plist_dict_get_item(build_identity, "Yonkers,BoardID"); - if (node) { - if (plist_get_node_type(node) == PLIST_STRING) { - char *strval = NULL; - int intval = 0; - plist_get_string_val(node, &strval); - sscanf(strval, "%x", &intval); - plist_dict_set_item(parameters, "Yonkers,BoardID", plist_new_uint(intval)); - } else { - plist_dict_set_item(parameters, "Yonkers,BoardID", plist_copy(node)); - } - } - node = NULL; + plist_dict_copy_uint(parameters, build_identity, "Yonkers,BoardID", NULL); /* Yonkers,ChipID - Used for Yonkers firmware request */ - node = plist_dict_get_item(build_identity, "Yonkers,ChipID"); - if (node) { - if (plist_get_node_type(node) == PLIST_STRING) { - char *strval = NULL; - int intval = 0; - plist_get_string_val(node, &strval); - sscanf(strval, "%x", &intval); - plist_dict_set_item(parameters, "Yonkers,ChipID", plist_new_uint(intval)); - } else { - plist_dict_set_item(parameters, "Yonkers,ChipID", plist_copy(node)); - } - } - node = NULL; + plist_dict_copy_uint(parameters, build_identity, "Yonkers,ChipID", NULL); /* add Yonkers,PatchEpoch - Used for Yonkers firmware request */ - node = plist_dict_get_item(build_identity, "Yonkers,PatchEpoch"); + plist_dict_copy_uint(parameters, build_identity, "Yonkers,PatchEpoch", NULL); + + plist_dict_copy_uint(parameters, build_identity, "Rap,BoardID", NULL); + plist_dict_copy_uint(parameters, build_identity, "Rap,ChipID", NULL); + plist_dict_copy_uint(parameters, build_identity, "Rap,SecurityDomain", NULL); + + plist_dict_copy_uint(parameters, build_identity, "Baobab,BoardID", NULL); + plist_dict_copy_uint(parameters, build_identity, "Baobab,ChipID", NULL); + plist_dict_copy_uint(parameters, build_identity, "Baobab,ManifestEpoch", NULL); + plist_dict_copy_uint(parameters, build_identity, "Baobab,SecurityDomain", NULL); + + plist_dict_copy_uint(parameters, build_identity, "eUICC,ChipID", NULL); + + plist_dict_copy_uint(parameters, build_identity, "NeRDEpoch", NULL); + plist_dict_copy_data(parameters, build_identity, "PearlCertificationRootPub", NULL); + + plist_dict_copy_uint(parameters, build_identity, "Timer,BoardID,1", NULL); + plist_dict_copy_uint(parameters, build_identity, "Timer,BoardID,2", NULL); + plist_dict_copy_uint(parameters, build_identity, "Timer,ChipID,1", NULL); + plist_dict_copy_uint(parameters, build_identity, "Timer,ChipID,2", NULL); + plist_dict_copy_uint(parameters, build_identity, "Timer,SecurityDomain,1", NULL); + plist_dict_copy_uint(parameters, build_identity, "Timer,SecurityDomain,2", NULL); + + plist_dict_copy_item(parameters, build_identity, "Cryptex1,ChipID", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,Type", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,SubType", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,ProductClass", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,UseProductClass", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,NonceDomain", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,Version", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,PreauthorizationVersion", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,FakeRoot", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,SystemOS", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,SystemVolume", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,SystemTrustCache", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,AppOS", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,AppVolume", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,AppTrustCache", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,MobileAssetBrainOS", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,MobileAssetBrainVolume", NULL); + plist_dict_copy_item(parameters, build_identity, "Cryptex1,MobileAssetBrainTrustCache", NULL); + + plist_dict_copy_item(parameters, build_identity, "USBPortController1,BoardID", NULL); + plist_dict_copy_item(parameters, build_identity, "USBPortController1,ChipID", NULL); + plist_dict_copy_item(parameters, build_identity, "USBPortController1,SecurityDomain", NULL); + + node = plist_dict_get_item(build_identity, "Info"); if (node) { - if (plist_get_node_type(node) == PLIST_STRING) { - char *strval = NULL; - int intval = 0; - plist_get_string_val(node, &strval); - sscanf(strval, "%x", &intval); - plist_dict_set_item(parameters, "Yonkers,PatchEpoch", plist_new_uint(intval)); - } else { - plist_dict_set_item(parameters, "Yonkers,PatchEpoch", plist_copy(node)); - } - } - node = NULL; - - /* add Rap,BoardID */ - node = plist_dict_get_item(build_identity, "Rap,BoardID"); - if (node) { - plist_dict_set_item(parameters, "Rap,BoardID", plist_copy(node)); - } - node = NULL; - - /* add Rap,ChipID */ - node = plist_dict_get_item(build_identity, "Rap,ChipID"); - if (node) { - plist_dict_set_item(parameters, "Rap,ChipID", plist_copy(node)); - } - node = NULL; - - /* add Rap,SecurityDomain */ - node = plist_dict_get_item(build_identity, "Rap,SecurityDomain"); - if (node) { - plist_dict_set_item(parameters, "Rap,SecurityDomain", plist_copy(node)); - } - node = NULL; - - /* add Baobab,BoardID */ - node = plist_dict_get_item(build_identity, "Baobab,BoardID"); - if (node) { - plist_dict_set_item(parameters, "Baobab,BoardID", plist_copy(node)); - } - node = NULL; - - /* add Baobab,ChipID */ - node = plist_dict_get_item(build_identity, "Baobab,ChipID"); - if (node) { - plist_dict_set_item(parameters, "Baobab,ChipID", plist_copy(node)); - } - node = NULL; - - /* add Baobab,ManifestEpoch */ - node = plist_dict_get_item(build_identity, "Baobab,ManifestEpoch"); - if (node) { - plist_dict_set_item(parameters, "Baobab,ManifestEpoch", plist_copy(node)); + plist_dict_copy_bool(parameters, node, "RequiresUIDMode", NULL); } - node = NULL; - /* add Baobab,SecurityDomain */ - node = plist_dict_get_item(build_identity, "Baobab,SecurityDomain"); - if (node) { - plist_dict_set_item(parameters, "Baobab,SecurityDomain", plist_copy(node)); - } - node = NULL; - - /* add eUICC,ChipID */ - node = plist_dict_get_item(build_identity, "eUICC,ChipID"); - if (node) { - plist_dict_set_item(parameters, "eUICC,ChipID", plist_copy(node)); - } - node = NULL; - - node = plist_dict_get_item(build_identity, "PearlCertificationRootPub"); - if (node) { - plist_dict_set_item(parameters, "PearlCertificationRootPub", plist_copy(node)); - } - node = NULL; - - /* add build identity manifest dictionary */ - node = plist_dict_get_item(build_identity, "Manifest"); - if (!node || plist_get_node_type(node) != PLIST_DICT) { - error("ERROR: Unable to find Manifest node\n"); - return -1; + if (include_manifest) { + /* add build identity manifest dictionary */ + node = plist_dict_get_item(build_identity, "Manifest"); + if (!node || plist_get_node_type(node) != PLIST_DICT) { + error("ERROR: Unable to find Manifest node\n"); + return -1; + } + plist_dict_set_item(parameters, "Manifest", plist_copy(node)); } - plist_dict_set_item(parameters, "Manifest", plist_copy(node)); return 0; } -int tss_request_add_ap_img4_tags(plist_t request, plist_t parameters) { - plist_t node = NULL; - +int tss_request_add_ap_img4_tags(plist_t request, plist_t parameters) +{ if (!parameters) { error("ERROR: Missing required AP parameters\n"); return -1; } - /* Ap,OSLongVersion */ - node = plist_dict_get_item(parameters, "Ap,OSLongVersion"); - if (node) { - plist_dict_set_item(request, "Ap,OSLongVersion", plist_copy(node)); - } + plist_dict_copy_string(request, parameters, "Ap,OSLongVersion", NULL); - /* ApNonce */ - node = plist_dict_get_item(parameters, "ApNonce"); - if (!node || plist_get_node_type(node) != PLIST_DATA) { + if (plist_dict_copy_data(request, parameters, "ApNonce", NULL) < 0) { error("ERROR: Unable to find required ApNonce in parameters\n"); return -1; } - plist_dict_set_item(request, "ApNonce", plist_copy(node)); - node = NULL; plist_dict_set_item(request, "@ApImg4Ticket", plist_new_bool(1)); - /* ApSecurityMode */ - node = plist_dict_get_item(request, "ApSecurityMode"); - if (!node) { + if (!plist_dict_get_item(request, "ApSecurityMode")) { /* copy from parameters if available */ - node = plist_dict_get_item(parameters, "ApSecurityMode"); - if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { + if (plist_dict_copy_bool(request, parameters, "ApSecurityMode", NULL) < 0) { error("ERROR: Unable to find required ApSecurityMode in parameters\n"); return -1; } - plist_dict_set_item(request, "ApSecurityMode", plist_copy(node)); - node = NULL; } - - node = plist_dict_get_item(request, "ApProductionMode"); - if (!node) { + if (!plist_dict_get_item(request, "ApProductionMode")) { /* ApProductionMode */ - node = plist_dict_get_item(parameters, "ApProductionMode"); - if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { + if (plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL) < 0) { error("ERROR: Unable to find required ApProductionMode in parameters\n"); return -1; } - plist_dict_set_item(request, "ApProductionMode", plist_copy(node)); - node = NULL; } - /* ApSepNonce */ - node = plist_dict_get_item(parameters, "ApSepNonce"); - if (!node || plist_get_node_type(node) != PLIST_DATA) { - error("ERROR: Unable to find required ApSepNonce in parameters\n"); - return -1; + plist_dict_copy_data(request, parameters, "SepNonce", "ApSepNonce"); + plist_dict_copy_uint(request, parameters, "NeRDEpoch", NULL); + plist_dict_copy_data(request, parameters, "PearlCertificationRootPub", NULL); + + if (plist_dict_get_item(parameters, "UID_MODE")) { + plist_dict_copy_item(request, parameters, "UID_MODE", NULL); + } else if (plist_dict_get_bool(parameters, "RequiresUIDMode")) { + // The logic here is missing why this value is expected to be 'false' + plist_dict_set_item(request, "UID_MODE", plist_new_bool(0)); } - plist_dict_set_item(request, "SepNonce", plist_copy(node)); - node = NULL; - /* PearlCertificationRootPub */ - node = plist_dict_get_item(parameters, "PearlCertificationRootPub"); - if (node) { - plist_dict_set_item(request, "PearlCertificationRootPub", plist_copy(node)); + // FIXME: I didn't understand yet when this value is set, so for now we use a workaround + if (plist_dict_get_item(parameters, "ApSikaFuse")) { + plist_dict_copy_item(request, parameters, "Ap,SikaFuse", "ApSikaFuse"); + } else if (plist_dict_get_bool(parameters, "RequiresUIDMode")) { + // Workaround: We have only seen Ap,SikaFuse together with UID_MODE + plist_dict_set_item(request, "Ap,SikaFuse", plist_new_int(0)); } return 0; } -int tss_request_add_ap_img3_tags(plist_t request, plist_t parameters) { - plist_t node = NULL; - +int tss_request_add_ap_img3_tags(plist_t request, plist_t parameters) +{ if (!parameters) { error("ERROR: Missing required AP parameters\n"); return -1; } - /* ApNonce */ - node = plist_dict_get_item(parameters, "ApNonce"); - if (node) { - if (plist_get_node_type(node) != PLIST_DATA) { - error("ERROR: Unable to find required ApNonce in parameters\n"); - return -1; - } - plist_dict_set_item(request, "ApNonce", plist_copy(node)); - node = NULL; + if (plist_dict_copy_data(request, parameters, "ApNonce", NULL) < 0) { + error("WARNING: Unable to find ApNonce in parameters\n"); } - /* @APTicket */ plist_dict_set_item(request, "@APTicket", plist_new_bool(1)); - /* ApBoardID */ - node = plist_dict_get_item(request, "ApBoardID"); - if (!node || plist_get_node_type(node) != PLIST_UINT) { + if (plist_dict_copy_uint(request, parameters, "ApBoardID", NULL) < 0) { error("ERROR: Unable to find required ApBoardID in request\n"); return -1; } - node = NULL; - /* ApChipID */ - node = plist_dict_get_item(request, "ApChipID"); - if (!node || plist_get_node_type(node) != PLIST_UINT) { + if (plist_dict_copy_uint(request, parameters, "ApChipID", NULL) < 0) { error("ERROR: Unable to find required ApChipID in request\n"); return -1; } - node = NULL; - /* ApSecurityDomain */ - node = plist_dict_get_item(request, "ApSecurityDomain"); - if (!node || plist_get_node_type(node) != PLIST_UINT) { + if (plist_dict_copy_uint(request, parameters, "ApSecurityDomain", NULL) < 0) { error("ERROR: Unable to find required ApSecurityDomain in request\n"); return -1; } - node = NULL; - /* ApProductionMode */ - node = plist_dict_get_item(parameters, "ApProductionMode"); - if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { + if (plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL) < 0) { error("ERROR: Unable to find required ApProductionMode in parameters\n"); return -1; } - plist_dict_set_item(request, "ApProductionMode", plist_copy(node)); - node = NULL; return 0; } -int tss_request_add_common_tags(plist_t request, plist_t parameters, plist_t overrides) { - plist_t node = NULL; - - /* ApECID */ - node = plist_dict_get_item(parameters, "ApECID"); - if (node) { - plist_dict_set_item(request, "ApECID", plist_copy(node)); - } - node = NULL; - - /* UniqueBuildID */ - node = plist_dict_get_item(parameters, "UniqueBuildID"); - if (node) { - plist_dict_set_item(request, "UniqueBuildID", plist_copy(node)); - } - node = NULL; - - /* ApChipID */ - node = plist_dict_get_item(parameters, "ApChipID"); - if (node) { - plist_dict_set_item(request, "ApChipID", plist_copy(node)); - } - node = NULL; - - /* ApBoardID */ - node = plist_dict_get_item(parameters, "ApBoardID"); - if (node) { - plist_dict_set_item(request, "ApBoardID", plist_copy(node)); - } - node = NULL; - - /* ApSecurityDomain */ - node = plist_dict_get_item(parameters, "ApSecurityDomain"); - if (node) { - plist_dict_set_item(request, "ApSecurityDomain", plist_copy(node)); - } - node = NULL; +int tss_request_add_common_tags(plist_t request, plist_t parameters, plist_t overrides) +{ + plist_dict_copy_uint(request, parameters, "ApECID", NULL); + plist_dict_copy_data(request, parameters, "UniqueBuildID", NULL); + plist_dict_copy_uint(request, parameters, "ApChipID", NULL); + plist_dict_copy_uint(request, parameters, "ApBoardID", NULL); + plist_dict_copy_uint(request, parameters, "ApSecurityDomain", NULL); /* apply overrides */ if (overrides) { @@ -750,7 +449,8 @@ static void tss_entry_apply_restore_request_rules(plist_t tss_entry, plist_t par } } -int tss_request_add_ap_recovery_tags(plist_t request, plist_t parameters, plist_t overrides) { +int tss_request_add_ap_recovery_tags(plist_t request, plist_t parameters, plist_t overrides) +{ /* loop over components from build manifest */ plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { @@ -876,14 +576,21 @@ int tss_request_add_ap_recovery_tags(plist_t request, plist_t parameters, plist_ continue; } - if (_plist_dict_get_bool(parameters, "_OnlyFWComponents")) { - if (!_plist_dict_get_bool(manifest_entry, "Trusted")) { + if (plist_dict_get_bool(parameters, "_OnlyFWComponents")) { + if (!plist_dict_get_bool(manifest_entry, "Trusted")) { debug("DEBUG: %s: Skipping '%s' as it is not trusted\n", __func__, key); continue; } - if (!_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 nor FUD firmware payload\n", __func__, key); + if (!plist_dict_get_bool(info_dict, "IsFirmwarePayload") + && !plist_dict_get_bool(info_dict, "IsSecondaryFirmwarePayload") + && !plist_dict_get_bool(info_dict, "IsFUDFirmware") + && !plist_dict_get_bool(info_dict, "IsLoadedByiBoot") + && !plist_dict_get_bool(info_dict, "IsEarlyAccessFirmware") + && !plist_dict_get_bool(info_dict, "IsiBootEANFirmware") + && !plist_dict_get_bool(info_dict, "IsiBootNonEssentialFirmware")) + { + debug("DEBUG: %s: Skipping '%s' as it is not a firmware payload\n", __func__, key); continue; } } @@ -902,14 +609,9 @@ int tss_request_add_ap_recovery_tags(plist_t request, plist_t parameters, plist_ } /* Make sure we have a Digest key for Trusted items even if empty */ - plist_t node = plist_dict_get_item(manifest_entry, "Trusted"); - if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { - uint8_t trusted; - plist_get_bool_val(node, &trusted); - if (trusted && !plist_access_path(manifest_entry, 1, "Digest")) { - debug("DEBUG: No Digest data, using empty value for entry %s\n", key); - plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0)); - } + if (plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { + debug("DEBUG: No Digest data, using empty value for entry %s\n", key); + plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0)); } /* finally add entry to request */ @@ -926,7 +628,8 @@ int tss_request_add_ap_recovery_tags(plist_t request, plist_t parameters, plist_ return 0; } -int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrides) { +int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrides) +{ /* loop over components from build manifest */ plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { @@ -976,25 +679,43 @@ int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrid continue; } - if (_plist_dict_get_bool(parameters, "ApSupportsImg4")) { + if (plist_dict_get_bool(parameters, "ApSupportsImg4")) { if (!plist_dict_get_item(info_dict, "RestoreRequestRules")) { debug("DEBUG: %s: Skipping '%s' as it doesn't have RestoreRequestRules\n", __func__, key); continue; } } - if (_plist_dict_get_bool(parameters, "_OnlyFWComponents")) { - if (!_plist_dict_get_bool(manifest_entry, "Trusted")) { + int is_fw_payload = plist_dict_get_bool(info_dict, "IsFirmwarePayload") + || plist_dict_get_bool(info_dict, "IsSecondaryFirmwarePayload") + || plist_dict_get_bool(info_dict, "IsFUDFirmware") + || plist_dict_get_bool(info_dict, "IsLoadedByiBoot") + || plist_dict_get_bool(info_dict, "IsEarlyAccessFirmware") + || plist_dict_get_bool(info_dict, "IsiBootEANFirmware") + || plist_dict_get_bool(info_dict, "IsiBootNonEssentialFirmware"); + + if (plist_dict_get_bool(parameters, "_OnlyFWOrTrustedComponents")) { + if (!plist_dict_get_bool(manifest_entry, "Trusted") && !is_fw_payload) { + debug("DEBUG: %s: Skipping '%s' as it is neither firmware payload nor trusted\n", __func__, key); + continue; + } + } else if (plist_dict_get_bool(parameters, "_OnlyFWComponents")) { + if (!plist_dict_get_bool(manifest_entry, "Trusted")) { debug("DEBUG: %s: Skipping '%s' as it is not trusted\n", __func__, key); continue; } - - if (!_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 nor FUD firmware payload\n", __func__, key); + if (!is_fw_payload) { + debug("DEBUG: %s: Skipping '%s' as it is not a firmware payload\n", __func__, key); continue; } } + /* skip components with IsFTAB:true */ + if (plist_dict_get_bool(info_dict, "IsFTAB")) { + debug("DEBUG: %s: Skipping FTAB component '%s'\n", __func__, key); + continue; + } + /* copy this entry */ plist_t tss_entry = plist_copy(manifest_entry); @@ -1009,14 +730,9 @@ int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrid } /* Make sure we have a Digest key for Trusted items even if empty */ - plist_t node = plist_dict_get_item(manifest_entry, "Trusted"); - if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { - uint8_t trusted; - plist_get_bool_val(node, &trusted); - if (trusted && !plist_access_path(manifest_entry, 1, "Digest")) { - debug("DEBUG: No Digest data, using empty value for entry %s\n", key); - plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0)); - } + if (plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { + debug("DEBUG: No Digest data, using empty value for entry %s\n", key); + plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0)); } /* finally add entry to request */ @@ -1033,88 +749,31 @@ int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrid return 0; } -int tss_request_add_baseband_tags(plist_t request, plist_t parameters, plist_t overrides) { +int tss_request_add_baseband_tags(plist_t request, plist_t parameters, plist_t overrides) +{ plist_t node = NULL; - /* BbChipID */ - uint64_t bb_chip_id = _plist_dict_get_uint(parameters, "BbChipID"); - if (bb_chip_id) { - plist_dict_set_item(request, "BbChipID", plist_new_uint(bb_chip_id)); - } - - /* BbProvisioningManifestKeyHash */ - node = plist_dict_get_item(parameters, "BbProvisioningManifestKeyHash"); - if (node) { - plist_dict_set_item(request, "BbProvisioningManifestKeyHash", plist_copy(node)); - } - node = NULL; + plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); + plist_dict_copy_uint(request, parameters, "BbChipID", NULL); + plist_dict_copy_data(request, parameters, "BbProvisioningManifestKeyHash", NULL); /* BbActivationManifestKeyHash - Used by Qualcomm MDM6610 */ - node = plist_dict_get_item(parameters, "BbActivationManifestKeyHash"); - if (node) { - plist_dict_set_item(request, "BbActivationManifestKeyHash", plist_copy(node)); - } - node = NULL; - - node = plist_dict_get_item(parameters, "BbCalibrationManifestKeyHash"); - if (node) { - plist_dict_set_item(request, "BbCalibrationManifestKeyHash", plist_copy(node)); - } - node = NULL; - - /* BbFactoryActivationManifestKeyHash */ - node = plist_dict_get_item(parameters, "BbFactoryActivationManifestKeyHash"); - if (node) { - plist_dict_set_item(request, "BbFactoryActivationManifestKeyHash", plist_copy(node)); - } - node = NULL; - - /* BbFDRSecurityKeyHash */ - node = plist_dict_get_item(parameters, "BbFDRSecurityKeyHash"); - if (node) { - plist_dict_set_item(request, "BbFDRSecurityKeyHash", plist_copy(node)); - } - node = NULL; - + plist_dict_copy_data(request, parameters, "BbActivationManifestKeyHash", NULL); + plist_dict_copy_data(request, parameters, "BbCalibrationManifestKeyHash", NULL); + plist_dict_copy_data(request, parameters, "BbFactoryActivationManifestKeyHash", NULL); + plist_dict_copy_data(request, parameters, "BbFDRSecurityKeyHash", NULL); /* BbSkeyId - Used by XMM 6180/GSM */ - node = plist_dict_get_item(parameters, "BbSkeyId"); - if (node) { - plist_dict_set_item(request, "BbSkeyId", plist_copy(node)); - } - node = NULL; + plist_dict_copy_data(request, parameters, "BbSkeyId", NULL); + plist_dict_copy_data(request, parameters, "BbNonce", NULL); + plist_dict_copy_uint(request, parameters, "BbGoldCertId", NULL); - /* BbNonce */ - node = plist_dict_get_item(parameters, "BbNonce"); - if (node) { - plist_dict_set_item(request, "BbNonce", plist_copy(node)); - } - node = NULL; - - /* @BBTicket */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); + uint64_t bb_chip_id = plist_dict_get_uint(request, "BbChipID"); + int32_t bb_cert_id = (int32_t)plist_dict_get_uint(request, "BbGoldCertId"); - /* BbGoldCertId */ - node = plist_dict_get_item(parameters, "BbGoldCertId"); - if (!node || plist_get_node_type(node) != PLIST_UINT) { - error("ERROR: Unable to find required BbGoldCertId in parameters\n"); - return -1; - } - node = plist_copy(node); - uint64_t val; - plist_get_uint_val(node, &val); - int32_t bb_cert_id = (int32_t)val; - plist_set_uint_val(node, bb_cert_id); - plist_dict_set_item(request, "BbGoldCertId", node); - node = NULL; - - /* BbSNUM */ - node = plist_dict_get_item(parameters, "BbSNUM"); - if (!node || plist_get_node_type(node) != PLIST_DATA) { + if (plist_dict_copy_data(request, parameters, "BbSNUM", NULL) < 0) { error("ERROR: Unable to find required BbSNUM in parameters\n"); return -1; } - plist_dict_set_item(request, "BbSNUM", plist_copy(node)); - node = NULL; /* BasebandFirmware */ node = plist_access_path(parameters, 2, "Manifest", "BasebandFirmware"); @@ -1151,60 +810,36 @@ int tss_request_add_baseband_tags(plist_t request, plist_t parameters, plist_t o int tss_request_add_se_tags(plist_t request, plist_t parameters, plist_t overrides) { - plist_t node = NULL; - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); return -1; } - /* add tags indicating we want to get the SE,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_set_item(request, "@SE,Ticket", plist_new_bool(1)); - /* add SE,ChipID */ - node = plist_dict_get_item(parameters, "SE,ChipID"); - if (!node || plist_get_node_type(node) != PLIST_UINT) { + if (plist_dict_copy_uint(request, parameters, "SE,ChipID", NULL) < 0) { error("ERROR: %s: Unable to find required SE,ChipID in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "SE,ChipID", plist_copy(node)); - node = NULL; - /* add SE,ID */ - node = plist_dict_get_item(parameters, "SE,ID"); - if (!node) { + if (plist_dict_copy_data(request, parameters, "SE,ID", NULL) < 0) { error("ERROR: %s: Unable to find required SE,ID in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "SE,ID", plist_copy(node)); - node = NULL; - /* add SE,Nonce */ - node = plist_dict_get_item(parameters, "SE,Nonce"); - if (!node) { + if (plist_dict_copy_data(request, parameters, "SE,Nonce", NULL) < 0) { error("ERROR: %s: Unable to find required SE,Nonce in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "SE,Nonce", plist_copy(node)); - node = NULL; - /* add SE,RootKeyIdentifier */ - node = plist_dict_get_item(parameters, "SE,RootKeyIdentifier"); - if (!node) { + if (plist_dict_copy_data(request, parameters, "SE,RootKeyIdentifier", NULL) < 0) { error("ERROR: %s: Unable to find required SE,RootKeyIdentifier in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "SE,RootKeyIdentifier", plist_copy(node)); - node = NULL; /* 'IsDev' determines whether we have Production or Development */ - uint8_t is_dev = 0; - node = plist_dict_get_item(parameters, "SE,IsDev"); - if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { - plist_get_bool_val(node, &is_dev); - } + uint8_t is_dev = plist_dict_get_bool(parameters, "SE,IsDev"); /* add SE,* components from build manifest to request */ char* key = NULL; @@ -1257,6 +892,11 @@ int tss_request_add_se_tags(plist_t request, plist_t parameters, plist_t overrid plist_dict_merge(&request, overrides); } + /* fallback in case no @SE2,Ticket or @SE,Ticket was provided */ + if (!plist_dict_get_item(request, "@SE2,Ticket") && !plist_dict_get_item(request, "@SE,Ticket")) { + plist_dict_set_item(request, "@SE,Ticket", plist_new_bool(1)); + } + return 0; } @@ -1274,14 +914,10 @@ int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t ove plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@Savage,Ticket", plist_new_bool(1)); - /* add Savage,UID */ - node = plist_dict_get_item(parameters, "Savage,UID"); - if (!node) { + if (plist_dict_copy_data(request, parameters, "Savage,UID", NULL) < 0) { error("ERROR: %s: Unable to find required Savage,UID in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "Savage,UID", plist_copy(node)); - node = NULL; /* add SEP */ node = plist_access_path(manifest_node, 2, "SEP", "Digest"); @@ -1293,61 +929,40 @@ int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t ove plist_dict_set_item(dict, "Digest", plist_copy(node)); plist_dict_set_item(request, "SEP", dict); - /* add Savage,PatchEpoch */ - node = plist_dict_get_item(parameters, "Savage,PatchEpoch"); - if (!node) { + if (plist_dict_copy_uint(request, parameters, "Savage,PatchEpoch", NULL) < 0) { error("ERROR: %s: Unable to find required Savage,PatchEpoch in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "Savage,PatchEpoch", plist_copy(node)); - node = NULL; - /* add Savage,ChipID */ - node = plist_dict_get_item(parameters, "Savage,ChipID"); - if (!node) { + if (plist_dict_copy_uint(request, parameters, "Savage,ChipID", NULL) < 0) { error("ERROR: %s: Unable to find required Savage,ChipID in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "Savage,ChipID", plist_copy(node)); - node = NULL; - /* add Savage,AllowOfflineBoot */ - node = plist_dict_get_item(parameters, "Savage,AllowOfflineBoot"); - if (!node) { + if (plist_dict_copy_bool(request, parameters, "Savage,AllowOfflineBoot", NULL) < 0) { error("ERROR: %s: Unable to find required Savage,AllowOfflineBoot in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "Savage,AllowOfflineBoot", plist_copy(node)); - node = NULL; - /* add Savage,ReadFWKey */ - node = plist_dict_get_item(parameters, "Savage,ReadFWKey"); - if (!node) { + if (plist_dict_copy_bool(request, parameters, "Savage,ReadFWKey", NULL) < 0) { error("ERROR: %s: Unable to find required Savage,ReadFWKey in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "Savage,ReadFWKey", plist_copy(node)); - node = NULL; - /* add Savage,ProductionMode */ - node = plist_dict_get_item(parameters, "Savage,ProductionMode"); - if (!node) { + if (plist_dict_copy_bool(request, parameters, "Savage,ProductionMode", NULL) < 0) { error("ERROR: %s: Unable to find required Savage,ProductionMode in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "Savage,ProductionMode", plist_copy(node)); + const char *comp_name = NULL; - uint8_t isprod = 0; - plist_get_bool_val(node, &isprod); - node = NULL; + uint8_t isprod = plist_dict_get_bool(request, "Savage,ProductionMode"); /* get the right component name */ comp_name = (isprod) ? "Savage,B0-Prod-Patch" : "Savage,B0-Dev-Patch"; node = plist_dict_get_item(parameters, "Savage,Revision"); if (node && (plist_get_node_type(node) == PLIST_DATA)) { - unsigned char *savage_rev = NULL; uint64_t savage_rev_len = 0; - plist_get_data_val(node, (char**)&savage_rev, &savage_rev_len); + const unsigned char *savage_rev = (const unsigned char*)plist_get_data_ptr(node, &savage_rev_len); if (savage_rev_len > 0) { if (((savage_rev[0] | 0x10) & 0xF0) == 0x30) { comp_name = (isprod) ? "Savage,B2-Prod-Patch" : "Savage,B2-Dev-Patch"; @@ -1355,7 +970,6 @@ int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t ove comp_name = (isprod) ? "Savage,BA-Prod-Patch" : "Savage,BA-Dev-Patch"; } } - free(savage_rev); } /* add Savage,B?-*-Patch */ @@ -1372,23 +986,15 @@ int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t ove *component_name = strdup(comp_name); } - /* add Savage,Nonce */ - node = plist_dict_get_item(parameters, "Savage,Nonce"); - if (!node) { + if (plist_dict_copy_data(request, parameters, "Savage,Nonce", NULL) < 0) { error("ERROR: %s: Unable to find required Savage,Nonce in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "Savage,Nonce", plist_copy(node)); - node = NULL; - /* add Savage,ReadECKey */ - node = plist_dict_get_item(parameters, "Savage,ReadECKey"); - if (!node) { + if (plist_dict_copy_bool(request, parameters, "Savage,ReadECKey", NULL) < 0) { error("ERROR: %s: Unable to find required Savage,ReadECKey in parameters\n", __func__); return -1; } - plist_dict_set_item(request, "Savage,ReadECKey", plist_copy(node)); - node = NULL; /* apply overrides */ if (overrides) { @@ -1437,18 +1043,8 @@ int tss_request_add_yonkers_tags(plist_t request, plist_t parameters, plist_t ov char *comp_name = NULL; plist_t comp_node = NULL; - uint8_t isprod = 1; - uint64_t fabrevision = (uint64_t)-1; - - node = plist_dict_get_item(parameters, "Yonkers,ProductionMode"); - if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { - plist_get_bool_val(node, &isprod); - } - - node = plist_dict_get_item(parameters, "Yonkers,FabRevision"); - if (node && (plist_get_node_type(node) == PLIST_UINT)) { - plist_get_uint_val(node, &fabrevision); - } + uint8_t isprod = plist_dict_get_bool(parameters, "Yonkers,ProductionMode"); + uint64_t fabrevision = plist_dict_get_uint(parameters, "Yonkers,FabRevision"); plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); @@ -1522,17 +1118,27 @@ int tss_request_add_vinyl_tags(plist_t request, plist_t parameters, plist_t over plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@eUICC,Ticket", plist_new_bool(1)); - node = plist_dict_get_item(parameters, "eUICC,ChipID"); - if (node) { - plist_dict_set_item(request, "eUICC,ChipID", plist_copy(node)); - } - node = plist_dict_get_item(parameters, "eUICC,EID"); - if (node) { - plist_dict_set_item(request, "eUICC,EID", plist_copy(node)); + plist_dict_copy_bool(request, parameters, "eUICC,ApProductionMode", "ApProductionMode"); + plist_dict_copy_uint(request, parameters, "eUICC,ChipID", NULL); + plist_dict_copy_data(request, parameters, "eUICC,EID", NULL); + plist_dict_copy_data(request, parameters, "eUICC,RootKeyIdentifier", NULL); + + if (!plist_dict_get_item(request, "eUICC,Gold")) { + plist_t n = plist_access_path(parameters, 2, "Manifest", "eUICC,Gold"); + if (n) { + plist_t p = plist_new_dict(); + plist_dict_copy_data(p, n, "Digest", NULL); + plist_dict_set_item(request, "eUICC,Gold", p); + } } - node = plist_dict_get_item(parameters, "eUICC,RootKeyIdentifier"); - if (node) { - plist_dict_set_item(request, "eUICC,RootKeyIdentifier", plist_copy(node)); + + if (!plist_dict_get_item(request, "eUICC,Main")) { + plist_t n = plist_access_path(parameters, 2, "Manifest", "eUICC,Main"); + if (n) { + plist_t p = plist_new_dict(); + plist_dict_copy_data(p, n, "Digest", NULL); + plist_dict_set_item(request, "eUICC,Main", p); + } } /* set Nonce for eUICC,Gold component */ @@ -1575,31 +1181,14 @@ int tss_request_add_rose_tags(plist_t request, plist_t parameters, plist_t overr plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@Rap,Ticket", plist_new_bool(1)); - uint64_t u64val = 0; - uint8_t bval = 0; - - u64val = _plist_dict_get_uint(parameters, "Rap,BoardID"); - plist_dict_set_item(request, "Rap,BoardID", plist_new_uint(u64val)); - - u64val = _plist_dict_get_uint(parameters, "Rap,ChipID"); - plist_dict_set_item(request, "Rap,ChipID", plist_new_uint(u64val)); - - u64val = _plist_dict_get_uint(parameters, "Rap,ECID"); - plist_dict_set_item(request, "Rap,ECID", plist_new_uint(u64val)); - - node = plist_dict_get_item(parameters, "Rap,Nonce"); - if (node) { - plist_dict_set_item(request, "Rap,Nonce", plist_copy(node)); - } - - bval = _plist_dict_get_bool(parameters, "Rap,ProductionMode"); - plist_dict_set_item(request, "Rap,ProductionMode", plist_new_bool(bval)); - - u64val = _plist_dict_get_uint(parameters, "Rap,SecurityDomain"); - plist_dict_set_item(request, "Rap,SecurityDomain", plist_new_uint(u64val)); - - bval = _plist_dict_get_bool(parameters, "Rap,SecurityMode"); - plist_dict_set_item(request, "Rap,SecurityMode", plist_new_bool(bval)); + plist_dict_copy_uint(request, parameters, "Rap,BoardID", NULL); + plist_dict_copy_uint(request, parameters, "Rap,ChipID", NULL); + plist_dict_copy_uint(request, parameters, "Rap,ECID", NULL); + plist_dict_copy_data(request, parameters, "Rap,Nonce", NULL); + plist_dict_copy_bool(request, parameters, "Rap,ProductionMode", NULL); + plist_dict_copy_uint(request, parameters, "Rap,SecurityDomain", NULL); + plist_dict_copy_bool(request, parameters, "Rap,SecurityMode", NULL); + plist_dict_copy_data(request, parameters, "Rap,FdrRootCaDigest", NULL); char *comp_name = NULL; plist_dict_iter iter = NULL; @@ -1623,14 +1212,9 @@ int tss_request_add_rose_tags(plist_t request, plist_t parameters, plist_t overr } /* Make sure we have a Digest key for Trusted items even if empty */ - plist_t node = plist_dict_get_item(manifest_entry, "Trusted"); - if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { - uint8_t trusted; - plist_get_bool_val(node, &trusted); - if (trusted && !plist_access_path(manifest_entry, 1, "Digest")) { - debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); - plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); - } + if (plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { + debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); + plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); } plist_dict_remove_item(manifest_entry, "Info"); @@ -1664,25 +1248,11 @@ int tss_request_add_veridian_tags(plist_t request, plist_t parameters, plist_t o plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@BMU,Ticket", plist_new_bool(1)); - uint64_t u64val = 0; - uint8_t bval = 0; - - u64val = _plist_dict_get_uint(parameters, "BMU,BoardID"); - plist_dict_set_item(request, "BMU,BoardID", plist_new_uint(u64val)); - - u64val = _plist_dict_get_uint(parameters, "ChipID"); - plist_dict_set_item(request, "BMU,ChipID", plist_new_uint(u64val)); - - node = plist_dict_get_item(parameters, "Nonce"); - if (node) { - plist_dict_set_item(request, "BMU,Nonce", plist_copy(node)); - } - - bval = _plist_dict_get_bool(parameters, "ProductionMode"); - plist_dict_set_item(request, "BMU,ProductionMode", plist_new_bool(bval)); - - u64val = _plist_dict_get_uint(parameters, "UniqueID"); - plist_dict_set_item(request, "BMU,UniqueID", plist_new_uint(u64val)); + plist_dict_copy_uint(request, parameters, "BMU,BoardID", NULL); + plist_dict_copy_uint(request, parameters, "BMU,ChipID", "ChipID"); + plist_dict_copy_data(request, parameters, "BMU,Nonce", "Nonce"); + plist_dict_copy_bool(request, parameters, "BMU,ProductionMode", "ProductionMode"); + plist_dict_copy_uint(request, parameters, "BMU,UniqueID", "UniqueID"); char *comp_name = NULL; plist_dict_iter iter = NULL; @@ -1706,14 +1276,9 @@ int tss_request_add_veridian_tags(plist_t request, plist_t parameters, plist_t o } /* Make sure we have a Digest key for Trusted items even if empty */ - plist_t node = plist_dict_get_item(manifest_entry, "Trusted"); - if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { - uint8_t trusted; - plist_get_bool_val(node, &trusted); - if (trusted && !plist_access_path(manifest_entry, 1, "Digest")) { - debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); - plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); - } + if (plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { + debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); + plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); } plist_dict_remove_item(manifest_entry, "Info"); @@ -1747,37 +1312,94 @@ int tss_request_add_tcon_tags(plist_t request, plist_t parameters, plist_t overr plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@Baobab,Ticket", plist_new_bool(1)); - uint64_t u64val = 0; - uint8_t bval = 0; - uint8_t isprod = 0; + plist_dict_copy_uint(request, parameters, "Baobab,BoardID", NULL); + plist_dict_copy_uint(request, parameters, "Baobab,ChipID", NULL); + plist_dict_copy_data(request, parameters, "Baobab,ECID", NULL); + plist_dict_copy_uint(request, parameters, "Baobab,Life", NULL); + plist_dict_copy_uint(request, parameters, "Baobab,ManifestEpoch", NULL); + plist_dict_copy_bool(request, parameters, "Baobab,ProductionMode", NULL); + plist_dict_copy_uint(request, parameters, "Baobab,SecurityDomain", NULL); + plist_dict_copy_data(request, parameters, "Baobab,UpdateNonce", NULL); - u64val = _plist_dict_get_uint(parameters, "Baobab,BoardID"); - plist_dict_set_item(request, "Baobab,BoardID", plist_new_uint(u64val)); + uint8_t isprod = plist_dict_get_bool(parameters, "Baobab,ProductionMode"); - u64val = _plist_dict_get_uint(parameters, "Baobab,ChipID"); - plist_dict_set_item(request, "Baobab,ChipID", plist_new_uint(u64val)); + char *comp_name = NULL; + plist_dict_iter iter = NULL; + plist_dict_new_iter(manifest_node, &iter); + while (iter) { + node = NULL; + comp_name = NULL; + plist_dict_next_item(manifest_node, iter, &comp_name, &node); + if (comp_name == NULL) { + node = NULL; + break; + } + if (strncmp(comp_name, "Baobab,", 7) == 0) { + plist_t manifest_entry = plist_copy(node); - node = plist_dict_get_item(parameters, "Baobab,ECID"); - if (node) { - plist_dict_set_item(request, "Baobab,ECID", plist_copy(node)); + plist_dict_remove_item(manifest_entry, "Info"); + plist_dict_set_item(manifest_entry, "EPRO", plist_new_bool(isprod)); + + /* finally add entry to request */ + plist_dict_set_item(request, comp_name, manifest_entry); + } + free(comp_name); } + free(iter); - u64val = _plist_dict_get_uint(parameters, "Baobab,Life"); - plist_dict_set_item(request, "Baobab,Life", plist_new_uint(u64val)); + /* apply overrides */ + if (overrides) { + plist_dict_merge(&request, overrides); + } + return 0; +} - u64val = _plist_dict_get_uint(parameters, "Baobab,ManifestEpoch"); - plist_dict_set_item(request, "Baobab,ManifestEpoch", plist_new_uint(u64val)); +int tss_request_add_timer_tags(plist_t request, plist_t parameters, plist_t overrides) +{ + plist_t node = NULL; + uint32_t tag = 0; - isprod = _plist_dict_get_bool(parameters, "Baobab,ProductionMode"); - plist_dict_set_item(request, "Baobab,ProductionMode", plist_new_bool(isprod)); + plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); + if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { + error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); + return -1; + } - u64val = _plist_dict_get_uint(parameters, "Baobab,SecurityDomain"); - plist_dict_set_item(request, "Baobab,SecurityDomain", plist_new_uint(u64val)); + /* add tags indicating we want to get the Timer ticket */ + plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - node = plist_dict_get_item(parameters, "Baobab,UpdateNonce"); - if (node) { - plist_dict_set_item(request, "Baobab,UpdateNonce", plist_copy(node)); + node = plist_dict_get_item(parameters, "TicketName"); + if (!node) { + error("ERROR: %s: Missing TicketName\n", __func__); + return -1; } + char key[64]; + sprintf(key, "@%s", plist_get_string_ptr(node, NULL)); + + plist_dict_set_item(request, key, plist_new_bool(1)); + + tag = (uint32_t)plist_dict_get_uint(parameters, "TagNumber"); + + sprintf(key, "Timer,BoardID,%u", tag); + plist_dict_copy_uint(request, parameters, key, NULL); + + sprintf(key, "Timer,ChipID,%u", tag); + plist_dict_copy_uint(request, parameters, key, NULL); + + sprintf(key, "Timer,SecurityDomain,%u", tag); + plist_dict_copy_uint(request, parameters, key, NULL); + + sprintf(key, "Timer,SecurityMode,%u", tag); + plist_dict_copy_bool(request, parameters, key, NULL); + + sprintf(key, "Timer,ProductionMode,%u", tag); + plist_dict_copy_bool(request, parameters, key, NULL); + + sprintf(key, "Timer,ECID,%u", tag); + plist_dict_copy_uint(request, parameters, key, NULL); + + sprintf(key, "Timer,Nonce,%u", tag); + plist_dict_copy_data(request, parameters, key, NULL); char *comp_name = NULL; plist_dict_iter iter = NULL; @@ -1790,11 +1412,23 @@ int tss_request_add_tcon_tags(plist_t request, plist_t parameters, plist_t overr node = NULL; break; } - if (strncmp(comp_name, "Baobab,", 7) == 0) { + if (!strncmp(comp_name, "Timer,", 6)) { plist_t manifest_entry = plist_copy(node); + /* handle RestoreRequestRules */ + plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); + if (rules) { + debug("DEBUG: Applying restore request rules for entry %s\n", comp_name); + tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules); + } + + /* Make sure we have a Digest key for Trusted items even if empty */ + if (plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { + debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); + plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); + } + plist_dict_remove_item(manifest_entry, "Info"); - plist_dict_set_item(manifest_entry, "EPRO", plist_new_bool(isprod)); /* finally add entry to request */ plist_dict_set_item(request, comp_name, manifest_entry); @@ -1807,10 +1441,50 @@ int tss_request_add_tcon_tags(plist_t request, plist_t parameters, plist_t overr if (overrides) { plist_dict_merge(&request, overrides); } + return 0; } -static size_t tss_write_callback(char* data, size_t size, size_t nmemb, tss_response* response) { +int tss_request_add_cryptex_tags(plist_t request, plist_t parameters, plist_t overrides) +{ + tss_request_add_common_tags(request, parameters, NULL); + + if (plist_dict_get_item(parameters, "Ap,LocalPolicy")) { + /* Cryptex1LocalPolicy */ + tss_request_add_local_policy_tags(request, parameters); + plist_dict_copy_data(request, parameters, "Ap,NextStageCryptex1IM4MHash", NULL); + } else { + /* Cryptex1 */ + plist_dict_set_item(request, "@Cryptex1,Ticket", plist_new_bool(1)); + + plist_dict_copy_bool(request, parameters, "ApSecurityMode", NULL); + plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL); + + plist_dict_iter iter = NULL; + plist_dict_new_iter(parameters, &iter); + plist_t value = NULL; + while (1) { + char *key = NULL; + plist_dict_next_item(parameters, iter, &key, &value); + if (key == NULL) + break; + if (strncmp(key, "Cryptex1", 8) == 0) { + plist_dict_set_item(request, key, plist_copy(value)); + } + free(key); + } + } + + /* apply overrides */ + if (overrides) { + plist_dict_merge(&request, overrides); + } + + return 0; +} + +static size_t tss_write_callback(char* data, size_t size, size_t nmemb, tss_response* response) +{ size_t total = size * nmemb; if (total != 0) { response->content = realloc(response->content, response->length + total + 1); @@ -1822,8 +1496,8 @@ static size_t tss_write_callback(char* data, size_t size, size_t nmemb, tss_resp return total; } -plist_t tss_request_send(plist_t tss_request, const char* server_url_string) { - +plist_t tss_request_send(plist_t tss_request, const char* server_url_string) +{ if (idevicerestore_debug) { debug_plist(tss_request); } @@ -1882,6 +1556,7 @@ plist_t tss_request_send(plist_t tss_request, const char* server_url_string) { curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, strlen(request)); if (server_url_string) { curl_easy_setopt(handle, CURLOPT_URL, server_url_string); + info("Request URL set to %s\n", server_url_string); } else { int url_index = (retry - 1) % 6; curl_easy_setopt(handle, CURLOPT_URL, urls[url_index]); @@ -1974,15 +1649,15 @@ plist_t tss_request_send(plist_t tss_request, const char* server_url_string) { return tss_response; } -static int tss_response_get_data_by_key(plist_t response, const char* name, unsigned char** buffer, unsigned int* length) { - +static int tss_response_get_data_by_key(plist_t response, const char* name, unsigned char** buffer, unsigned int* length) +{ plist_t node = plist_dict_get_item(response, name); if (!node || plist_get_node_type(node) != PLIST_DATA) { debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, name); return -1; } - char *data = NULL; + char* data = NULL; uint64_t len = 0; plist_get_data_val(node, &data, &len); if (data) { @@ -1995,19 +1670,23 @@ static int tss_response_get_data_by_key(plist_t response, const char* name, unsi } } -int tss_response_get_ap_img4_ticket(plist_t response, unsigned char** ticket, unsigned int* length) { +int tss_response_get_ap_img4_ticket(plist_t response, unsigned char** ticket, unsigned int* length) +{ return tss_response_get_data_by_key(response, "ApImg4Ticket", ticket, length); } -int tss_response_get_ap_ticket(plist_t response, unsigned char** ticket, unsigned int* length) { +int tss_response_get_ap_ticket(plist_t response, unsigned char** ticket, unsigned int* length) +{ return tss_response_get_data_by_key(response, "APTicket", ticket, length); } -int tss_response_get_baseband_ticket(plist_t response, unsigned char** ticket, unsigned int* length) { +int tss_response_get_baseband_ticket(plist_t response, unsigned char** ticket, unsigned int* length) +{ return tss_response_get_data_by_key(response, "BBTicket", ticket, length); } -int tss_response_get_path_by_entry(plist_t response, const char* entry, char** path) { +int tss_response_get_path_by_entry(plist_t response, const char* entry, char** path) +{ char* path_string = NULL; plist_t path_node = NULL; plist_t entry_node = NULL; @@ -2031,7 +1710,8 @@ int tss_response_get_path_by_entry(plist_t response, const char* entry, char** p return 0; } -int tss_response_get_blob_by_path(plist_t tss, const char* path, unsigned char** blob) { +int tss_response_get_blob_by_path(plist_t tss, const char* path, unsigned char** blob) +{ uint32_t i = 0; uint32_t tss_size = 0; uint64_t blob_size = 0; @@ -2087,7 +1767,8 @@ int tss_response_get_blob_by_path(plist_t tss, const char* path, unsigned char** return 0; } -int tss_response_get_blob_by_entry(plist_t response, const char* entry, unsigned char** blob) { +int tss_response_get_blob_by_entry(plist_t response, const char* entry, unsigned char** blob) +{ uint64_t blob_size = 0; char* blob_data = NULL; plist_t blob_node = NULL; @@ -29,9 +29,10 @@ extern "C" { #endif #include <plist/plist.h> +#include <stdbool.h> /* parameters */ -int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity); +int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity, bool include_manifest); /* request */ plist_t tss_request_new(plist_t overrides); @@ -48,6 +49,8 @@ int tss_request_add_vinyl_tags(plist_t request, plist_t parameters, plist_t over int tss_request_add_rose_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_veridian_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_tcon_tags(plist_t request, plist_t parameters, plist_t overrides); +int tss_request_add_timer_tags(plist_t request, plist_t parameters, plist_t overrides); +int tss_request_add_cryptex_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_ap_img4_tags(plist_t request, plist_t parameters); int tss_request_add_ap_img3_tags(plist_t request, plist_t parameters); |