summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Peter A.2026-08-24 11:13:45 +0200
committerGravatar Peter A.2026-08-24 11:26:34 +0200
commitd9aa264b673fdd14fc0cb7f7daa1b9127ede4916 (patch)
tree44bedd48cadf7f7963cfb34515c1c8b15e6b43a7 /src
parent540c352c4c44896f7415abef87a166e8bbaea9b0 (diff)
downloadidevicerestore-d9aa264b673fdd14fc0cb7f7daa1b9127ede4916.tar.gz
idevicerestore-d9aa264b673fdd14fc0cb7f7daa1b9127ede4916.tar.bz2
restore: honor requested global manifest path
SourceBootObjectV4 ignored GlobalManifestPrefix and GlobalManifestSuffix and always selected apticket.<DeviceClass>.im4m, supplying the AP manifest when another manifest family was requested. Use the requested path components and send FileDataDone when an optional manifest is absent. Requests without these fields retain the existing AP-ticket path.
Diffstat (limited to 'src')
-rw-r--r--src/restore.c133
1 files changed, 101 insertions, 32 deletions
diff --git a/src/restore.c b/src/restore.c
index 877b4a8..1913835 100644
--- a/src/restore.c
+++ b/src/restore.c
@@ -4134,7 +4134,7 @@ int extract_macos_variant(plist_t build_identity, char** output)
return 0;
}
-static char* extract_global_manifest_path(plist_t build_identity, char *variant)
+static char* extract_global_manifest_path(plist_t build_identity, const char* variant, const char* prefix, const char* suffix)
{
plist_t build_info = plist_dict_get_item(build_identity, "Info");
if (!build_info) {
@@ -4149,33 +4149,51 @@ static char* extract_global_manifest_path(plist_t build_identity, char *variant)
}
char *device_class = NULL;
plist_get_string_val(device_class_node, &device_class);
+ if (!device_class) {
+ logger(LL_ERROR, "failed to get DeviceClass from build identity info\n");
+ return NULL;
+ }
- char *macos_variant = NULL;
- int ret;
- if (variant) {
- macos_variant = variant;
- } else {
- ret = extract_macos_variant(build_identity, &macos_variant);
- if (ret != 0) {
+ char *owned_macos_variant = NULL;
+ const char *macos_variant = variant;
+ if (!macos_variant) {
+ if (extract_macos_variant(build_identity, &owned_macos_variant) != 0 || !owned_macos_variant) {
free(device_class);
return NULL;
}
+ macos_variant = owned_macos_variant;
}
- // The path of the global manifest is hardcoded. There's no pointer to in the build manifest.
- size_t psize = 42+strlen(macos_variant)+strlen(device_class)+1;
- char *ticket_path = malloc(psize);
- snprintf(ticket_path, psize, "Firmware/Manifests/restore/%s/apticket.%s.im4m", macos_variant, device_class);
+ // There is no path reference to the global manifest in the build manifest.
+ int length = snprintf(NULL, 0, "Firmware/Manifests/restore/%s/%s.%s%s.im4m", macos_variant, prefix, device_class, suffix);
+ if (length < 0) {
+ logger(LL_ERROR, "failed to format global manifest path\n");
+ free(device_class);
+ free(owned_macos_variant);
+ return NULL;
+ }
+ char *ticket_path = malloc((size_t)length + 1);
+ if (!ticket_path) {
+ logger(LL_ERROR, "Out of memory\n");
+ free(device_class);
+ free(owned_macos_variant);
+ return NULL;
+ }
+ if (snprintf(ticket_path, (size_t)length + 1, "Firmware/Manifests/restore/%s/%s.%s%s.im4m", macos_variant, prefix, device_class, suffix) != length) {
+ logger(LL_ERROR, "failed to format global manifest path\n");
+ free(ticket_path);
+ ticket_path = NULL;
+ }
free(device_class);
- free(macos_variant);
+ free(owned_macos_variant);
return ticket_path;
}
int extract_global_manifest(struct idevicerestore_client_t* client, plist_t build_identity, char *variant, void** pbuffer, size_t* psize)
{
- char* ticket_path = extract_global_manifest_path(build_identity, variant);
+ char* ticket_path = extract_global_manifest_path(build_identity, variant, "apticket", "");
if (!ticket_path) {
logger(LL_ERROR, "failed to get global manifest path\n");
return -1;
@@ -4217,7 +4235,7 @@ static int _restore_send_file_data(struct _restore_send_file_data_ctx* rctx, con
plist_free(dict);
/* special handling for AEA image format */
- if (done == 0 && (memcmp(data, "AEA1", 4) == 0)) {
+ if (data != NULL && size >= 4 && done == 0 && (memcmp(data, "AEA1", 4) == 0)) {
logger(LL_VERBOSE, "Encountered First Chunk in AEA image\n");
plist_t message = NULL;
property_list_service_error_t err = _restore_service_recv_timeout(rctx->service, &message, 3000);
@@ -4376,39 +4394,77 @@ int restore_send_source_boot_object_v4(struct idevicerestore_client_t* client, p
logger_dump_plist(LL_DEBUG, message, 1);
}
- char *image_name = NULL;
- plist_t node = plist_access_path(message, 2, "Arguments", "ImageName");
+ plist_t arguments = plist_dict_get_item(message, "Arguments");
+ if (!arguments || plist_get_node_type(arguments) != PLIST_DICT) {
+ logger(LL_DEBUG, "Failed to parse arguments from SourceBootObjectV4 plist\n");
+ return -1;
+ }
+ plist_t node = plist_dict_get_item(arguments, "ImageName");
if (!node || plist_get_node_type(node) != PLIST_STRING) {
logger(LL_DEBUG, "Failed to parse arguments from SourceBootObjectV4 plist\n");
return -1;
}
- plist_get_string_val(node, &image_name);
+ const char *image_name = plist_get_string_ptr(node, NULL);
if (!image_name) {
logger(LL_DEBUG, "Failed to parse arguments from SourceBootObjectV4 as string\n");
return -1;
}
- char *component = image_name;
+ const char *component = image_name;
// Fork from restore_send_component
//
char *path = NULL;
+ int path_optional = 0;
logger(LL_INFO, "About to send %s...\n", component);
if (strcmp(image_name, "__GlobalManifest__") == 0) {
- char *variant = NULL;
- plist_t node = plist_access_path(message, 2, "Arguments", "Variant");
+ plist_t build_identity = restore_get_build_identity_from_request(client, message);
+ if (!build_identity) {
+ logger(LL_ERROR, "Unable to find a matching build identity\n");
+ return -1;
+ }
+
+ node = plist_dict_get_item(arguments, "Variant");
if (!node || plist_get_node_type(node) != PLIST_STRING) {
logger(LL_DEBUG, "Failed to parse arguments from SourceBootObjectV4 plist\n");
return -1;
}
- plist_get_string_val(node, &variant);
+ const char *variant = plist_get_string_ptr(node, NULL);
if (!variant) {
logger(LL_DEBUG, "Failed to parse arguments from SourceBootObjectV4 as string\n");
return -1;
}
- path = extract_global_manifest_path(client->restore->build_identity, variant);
+ const char *prefix = "apticket";
+ node = plist_dict_get_item(arguments, "GlobalManifestPrefix");
+ if (node && plist_get_node_type(node) == PLIST_STRING) {
+ const char *value = plist_get_string_ptr(node, NULL);
+ if (value) {
+ prefix = value;
+ }
+ }
+
+ const char *suffix = "";
+ node = plist_dict_get_item(arguments, "GlobalManifestSuffix");
+ if (node && plist_get_node_type(node) == PLIST_STRING) {
+ const char *value = plist_get_string_ptr(node, NULL);
+ if (value) {
+ suffix = value;
+ }
+ }
+
+ path = extract_global_manifest_path(build_identity, variant, prefix, suffix);
+ if (!path) {
+ logger(LL_ERROR, "Failed to get global manifest path\n");
+ return -1;
+ }
+ if (!ipsw_file_exists(client->ipsw, path)) {
+ free(path);
+ path = NULL;
+ }
+ path_optional = !plist_access_path(build_identity, 2, "Info", "VariantSupportsGlobalSigning")
+ || plist_dict_get_bool(arguments, "GlobalManifestOptional");
} else if (strcmp(image_name, "__RestoreVersion__") == 0) {
path = strdup("RestoreVersion.plist");
} else if (strcmp(image_name, "__SystemVersion__") == 0) {
@@ -4429,41 +4485,54 @@ int restore_send_source_boot_object_v4(struct idevicerestore_client_t* client, p
}
}
- if (!path) {
+ if (!path && !path_optional) {
logger(LL_ERROR, "Failed to get path for component %s\n", component);
return -1;
}
uint64_t fsize = 0;
- ipsw_get_file_size(client->ipsw, path, &fsize);
+ if (path) {
+ ipsw_get_file_size(client->ipsw, path, &fsize);
+ }
restore_service_client_t service = _restore_get_service_client_for_data_request(client, message);
if (!service) {
+ free(path);
logger(LL_ERROR, "%s: Unable to connect to service client\n", __func__);
return -1;
}
- logger(LL_INFO, "Sending %s now (%" PRIu64 " bytes)\n", component, fsize);
-
struct _restore_send_file_data_ctx rctx;
rctx.client = client;
rctx.service = service;
rctx.last_progress = 0;
rctx.tag = progress_get_next_tag();
- register_progress(rctx.tag, component);
-
- if (ipsw_extract_send(client->ipsw, path, 8192, (ipsw_send_cb)_restore_send_file_data, &rctx) < 0) {
+ int sent_file = path != NULL;
+ int ret;
+ if (sent_file) {
+ logger(LL_INFO, "Sending %s now (%" PRIu64 " bytes)\n", component, fsize);
+ register_progress(rctx.tag, component);
+ ret = ipsw_extract_send(client->ipsw, path, 8192, (ipsw_send_cb)_restore_send_file_data, &rctx);
+ } else {
+ logger(LL_INFO, "Skipping missing optional component %s\n", component);
+ ret = _restore_send_file_data(&rctx, NULL, 0, 0, 0);
+ }
+ if (ret < 0) {
free(path);
_restore_service_free(service);
- finalize_progress(rctx.tag);
+ if (sent_file) {
+ finalize_progress(rctx.tag);
+ }
logger(LL_ERROR, "Failed to send component %s\n", component);
return -1;
}
free(path);
_restore_service_free(service);
- finalize_progress(rctx.tag);
+ if (sent_file) {
+ finalize_progress(rctx.tag);
+ }
logger(LL_INFO, "Done sending %s\n", component);
return 0;