diff options
Diffstat (limited to 'futility/updater_archive.c')
-rw-r--r-- | futility/updater_archive.c | 903 |
1 files changed, 19 insertions, 884 deletions
diff --git a/futility/updater_archive.c b/futility/updater_archive.c index dafce3c7..c6e8f289 100644 --- a/futility/updater_archive.c +++ b/futility/updater_archive.c @@ -6,15 +6,11 @@ */ #include <assert.h> -#include <ctype.h> #include <errno.h> #if defined(__OpenBSD__) #include <sys/types.h> #endif #include <fts.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> @@ -35,64 +31,15 @@ #include <zip.h> #endif -#ifdef HAVE_CROSID -#include <crosid.h> -#endif - #include "host_misc.h" #include "updater.h" -#include "util_misc.h" /* * A firmware update package (archive) is a file packed by either shar(1) or * zip(1). See https://chromium.googlesource.com/chromiumos/platform/firmware/ * for more information. - * - * A package for single board (i.e., not Unified Build) will have all the image - * files in top folder: - * - host: 'image.bin' (or 'bios.bin' as legacy name before CL:1318712) - * - ec: 'ec.bin' - * - pd: 'pd.bin' - * If custom label is supported, a 'keyset/' folder will be available, with key - * files in it: - * - rootkey.$CLTAG - * - vblock_A.$CLTAG - * - vblock_B.$CLTAG - * The $CLTAG should come from VPD value 'custom_label_tag'. For legacy devices, - * the VPD name may be 'whitelabel_tag', or 'customization_id'. - * The 'customization_id' has a different format: LOEM[-VARIANT] and we can only - * take LOEM as $CLTAG, for example A-B => $CLTAG=A. - * - * A package for Unified Build is more complicated. There will be a models/ - * folder, and each model (by $(mosys platform model) ) should appear as a sub - * folder, with a 'setvars.sh' file inside. The 'setvars.sh' is a shell script - * describing what files should be used and the signature ID ($SIGID) to use. - * - * Similar to custom label in non-Unified-Build, the keys and vblock files will - * be in 'keyset/' folder: - * - rootkey.$SIGID - * - vblock_A.$SIGID - * - vblock_B.$SIGID - * If $SIGID starts with 'sig-id-in-*' then we have to replace it by VPD value - * 'custom_label_tag' as '$MODEL-$CLTAG'. */ -static const char * const SETVARS_IMAGE_MAIN = "IMAGE_MAIN", - * const SETVARS_IMAGE_EC = "IMAGE_EC", - * const SETVARS_IMAGE_PD = "IMAGE_PD", - * const SETVARS_SIGNATURE_ID = "SIGNATURE_ID", - * const SIG_ID_IN_VPD_PREFIX = "sig-id-in", - * const DIR_KEYSET = "keyset", - * const DIR_MODELS = "models", - * const DEFAULT_MODEL_NAME = "default", - * const VPD_CUSTOM_LABEL_TAG = "custom_label_tag", - * const VPD_CUSTOM_LABEL_TAG_LEGACY = "whitelabel_tag", - * const VPD_CUSTOMIZATION_ID = "customization_id", - * const ENV_VAR_MODEL_DIR = "${MODEL_DIR}", - * const PATH_STARTSWITH_KEYSET = "keyset/", - * const PATH_SIGNER_CONFIG = "signer_config.csv", - * const PATH_ENDSWITH_SETVARS = "/setvars.sh"; - struct u_archive { void *handle; @@ -109,7 +56,7 @@ struct u_archive { }; /* - * -- Begin of archive implementations -- + * -- The fallback driver (using general file system). -- */ /* Callback for archive_open on a general file system. */ @@ -243,6 +190,10 @@ static int archive_fallback_write_file(void *handle, const char *fname, return r; } +/* + * -- The cache driver (used by other drivers). -- + */ + #ifdef HAVE_LIBARCHIVE /* @@ -318,6 +269,10 @@ static void *archive_cache_free(struct archive_cache *c) return NULL; } +/* + * -- The libarchive driver (multiple formats but very slow). -- + */ + enum { FILTER_IGNORE, FILTER_ABORT, @@ -463,6 +418,10 @@ static int archive_libarchive_write_file( } #endif +/* + * -- The libzip driver (for ZIP, the official format for CrOS fw updater). -- + */ + #ifdef HAVE_LIBZIP /* Callback for archive_open on a ZIP file. */ @@ -578,6 +537,10 @@ static int archive_zip_write_file(void *handle, const char *fname, #endif /* + * -- The public functions for using u_archive. -- + */ + +/* * Opens an archive from given path. * The type of archive will be determined automatically. * Returns a pointer to reference to archive (must be released by archive_close @@ -690,8 +653,8 @@ int archive_has_entry(struct u_archive *ar, const char *name) * The arg argument will also be passed to callback. * Returns 0 on success otherwise non-zero as failure. */ -static int archive_walk(struct u_archive *ar, void *arg, - int (*callback)(const char *path, void *arg)) +int archive_walk(struct u_archive *ar, void *arg, + int (*callback)(const char *path, void *arg)) { if (!ar) return archive_fallback_walk(NULL, arg, callback); @@ -762,831 +725,3 @@ int archive_copy(struct u_archive *from, struct u_archive *to) struct _copy_arg arg = { .from = from, .to = to }; return archive_walk(from, &arg, archive_copy_callback); } - -/* - * -- End of archive implementations -- - */ - -/* Utility function to convert a string. */ -static void str_convert(char *s, int (*convert)(int c)) -{ - int c; - - for (; *s; s++) { - c = *s; - if (!isascii(c)) - continue; - *s = convert(c); - } -} - -/* Returns 1 if name ends by given pattern, otherwise 0. */ -static int str_endswith(const char *name, const char *pattern) -{ - size_t name_len = strlen(name), pattern_len = strlen(pattern); - if (name_len < pattern_len) - return 0; - return strcmp(name + name_len - pattern_len, pattern) == 0; -} - -/* Returns 1 if name starts by given pattern, otherwise 0. */ -static int str_startswith(const char *name, const char *pattern) -{ - return strncmp(name, pattern, strlen(pattern)) == 0; -} - -/* Returns the VPD value by given key name, or NULL on error (or no value). */ -static char *vpd_get_value(const char *fpath, const char *key) -{ - char *command, *result; - - assert(fpath); - ASPRINTF(&command, "vpd -g %s -f %s 2>/dev/null", key, fpath); - result = host_shell(command); - free(command); - - if (result && !*result) { - free(result); - result = NULL; - } - return result; -} - -/* - * Reads and parses a setvars type file from archive, then stores into config. - * Returns 0 on success (at least one entry found), otherwise failure. - */ -static int model_config_parse_setvars_file( - struct model_config *cfg, struct u_archive *archive, - const char *fpath) -{ - uint8_t *data; - uint32_t len; - - char *ptr_line = NULL, *ptr_token = NULL; - char *line, *k, *v; - int valid = 0; - - if (archive_read_file(archive, fpath, &data, &len, NULL) != 0) { - ERROR("Failed reading: %s\n", fpath); - return -1; - } - - /* Valid content should end with \n, or \"; ensure ASCIIZ for parsing */ - if (len) - data[len - 1] = '\0'; - - for (line = strtok_r((char *)data, "\n\r", &ptr_line); line; - line = strtok_r(NULL, "\n\r", &ptr_line)) { - char *expand_path = NULL; - int found_valid = 1; - - /* Format: KEY="value" */ - k = strtok_r(line, "=", &ptr_token); - if (!k) - continue; - v = strtok_r(NULL, "\"", &ptr_token); - if (!v) - continue; - - /* Some legacy updaters may be still using ${MODEL_DIR}. */ - if (str_startswith(v, ENV_VAR_MODEL_DIR)) { - ASPRINTF(&expand_path, "%s/%s%s", DIR_MODELS, cfg->name, - v + strlen(ENV_VAR_MODEL_DIR)); - } - - if (strcmp(k, SETVARS_IMAGE_MAIN) == 0) - cfg->image = strdup(v); - else if (strcmp(k, SETVARS_IMAGE_EC) == 0) - cfg->ec_image = strdup(v); - else if (strcmp(k, SETVARS_IMAGE_PD) == 0) - cfg->pd_image = strdup(v); - else if (strcmp(k, SETVARS_SIGNATURE_ID) == 0) { - cfg->signature_id = strdup(v); - if (str_startswith(v, SIG_ID_IN_VPD_PREFIX)) - cfg->is_custom_label = 1; - } else - found_valid = 0; - free(expand_path); - valid += found_valid; - } - free(data); - return valid == 0; -} - -/* - * Changes the rootkey in firmware GBB to given new key. - * Returns 0 on success, otherwise failure. - */ -static int change_gbb_rootkey(struct firmware_image *image, - const char *section_name, - const uint8_t *rootkey, uint32_t rootkey_len) -{ - const struct vb2_gbb_header *gbb = find_gbb(image); - uint8_t *gbb_rootkey; - if (!gbb) { - ERROR("Cannot find GBB in image %s.\n", image->file_name); - return -1; - } - if (gbb->rootkey_size < rootkey_len) { - ERROR("New root key (%u bytes) larger than GBB (%u bytes).\n", - rootkey_len, gbb->rootkey_size); - return -1; - } - - gbb_rootkey = (uint8_t *)gbb + gbb->rootkey_offset; - /* See cmd_gbb_utility: root key must be first cleared with zero. */ - memset(gbb_rootkey, 0, gbb->rootkey_size); - memcpy(gbb_rootkey, rootkey, rootkey_len); - return 0; -} - -/* - * Changes the VBlock in firmware section to new data. - * Returns 0 on success, otherwise failure. - */ -static int change_vblock(struct firmware_image *image, const char *section_name, - const uint8_t *vblock, uint32_t vblock_len) -{ - struct firmware_section section; - - find_firmware_section(§ion, image, section_name); - if (!section.data) { - ERROR("Need section %s in image %s.\n", section_name, - image->file_name); - return -1; - } - if (section.size < vblock_len) { - ERROR("Section %s too small (%zu bytes) for vblock (%u bytes).\n", - section_name, section.size, vblock_len); - return -1; - } - memcpy(section.data, vblock, vblock_len); - return 0; -} - -/* - * Applies a key file to firmware image. - * Returns 0 on success, otherwise failure. - */ -static int apply_key_file( - struct firmware_image *image, const char *path, - struct u_archive *archive, const char *section_name, - int (*apply)(struct firmware_image *image, const char *section, - const uint8_t *data, uint32_t len)) -{ - int r = 0; - uint8_t *data = NULL; - uint32_t len; - - r = archive_read_file(archive, path, &data, &len, NULL); - if (r == 0) { - VB2_DEBUG("Loaded file: %s\n", path); - r = apply(image, section_name, data, len); - if (r) - ERROR("Failed applying %s to %s\n", path, section_name); - } else { - ERROR("Failed reading: %s\n", path); - } - free(data); - return r; -} - -/* - * Modifies a firmware image from patch information specified in model config. - * Returns 0 on success, otherwise number of failures. - */ -int patch_image_by_model( - struct firmware_image *image, const struct model_config *model, - struct u_archive *archive) -{ - int err = 0; - if (model->patches.rootkey) - err += !!apply_key_file( - image, model->patches.rootkey, archive, - FMAP_RO_GBB, change_gbb_rootkey); - if (model->patches.vblock_a) - err += !!apply_key_file( - image, model->patches.vblock_a, archive, - FMAP_RW_VBLOCK_A, change_vblock); - if (model->patches.vblock_b) - err += !!apply_key_file( - image, model->patches.vblock_b, archive, - FMAP_RW_VBLOCK_B, change_vblock); - return err; -} - -/* - * Finds available patch files by given model. - * Updates `model` argument with path of patch files. - */ -static void find_patches_for_model(struct model_config *model, - struct u_archive *archive, - const char *signature_id) -{ - char *path; - int i; - - const char *names[] = { - "rootkey", - "vblock_A", - "vblock_B", - }; - - char **targets[] = { - &model->patches.rootkey, - &model->patches.vblock_a, - &model->patches.vblock_b, - }; - - assert(ARRAY_SIZE(names) == ARRAY_SIZE(targets)); - for (i = 0; i < ARRAY_SIZE(names); i++) { - ASPRINTF(&path, "%s/%s.%s", DIR_KEYSET, names[i], signature_id); - if (archive_has_entry(archive, path)) - *targets[i] = path; - else - free(path); - } -} - -/* - * Adds and copies one new model config to the existing list of given manifest. - * Returns a pointer to the newly allocated config, or NULL on failure. - */ -static struct model_config *manifest_add_model( - struct manifest *manifest, - const struct model_config *cfg) -{ - struct model_config *model; - manifest->num++; - manifest->models = (struct model_config *)realloc( - manifest->models, manifest->num * sizeof(*model)); - if (!manifest->models) { - ERROR("Internal error: failed to allocate buffer.\n"); - return NULL; - } - model = &manifest->models[manifest->num - 1]; - memcpy(model, cfg, sizeof(*model)); - return model; -} - -/* - * A callback function for manifest to scan files in archive. - * Returns 0 to keep scanning, or non-zero to stop. - */ -static int manifest_scan_entries(const char *name, void *arg) -{ - struct manifest *manifest = (struct manifest *)arg; - struct u_archive *archive = manifest->archive; - struct model_config model = {0}; - char *slash; - - if (str_startswith(name, PATH_STARTSWITH_KEYSET)) - manifest->has_keyset = 1; - if (!str_endswith(name, PATH_ENDSWITH_SETVARS)) - return 0; - - /* name: models/$MODEL/setvars.sh */ - model.name = strdup(strchr(name, '/') + 1); - slash = strchr(model.name, '/'); - if (slash) - *slash = '\0'; - - VB2_DEBUG("Found model <%s> setvars: %s\n", model.name, name); - if (model_config_parse_setvars_file(&model, archive, name)) { - ERROR("Invalid setvars file: %s\n", name); - return 0; - } - - /* In legacy setvars.sh, the ec_image and pd_image may not exist. */ - if (model.ec_image && !archive_has_entry(archive, model.ec_image)) { - VB2_DEBUG("Ignore non-exist EC image: %s\n", model.ec_image); - free(model.ec_image); - model.ec_image = NULL; - } - if (model.pd_image && !archive_has_entry(archive, model.pd_image)) { - VB2_DEBUG("Ignore non-exist PD image: %s\n", model.pd_image); - free(model.pd_image); - model.pd_image = NULL; - } - - /* Find patch files. */ - if (model.signature_id) - find_patches_for_model(&model, archive, model.signature_id); - - return !manifest_add_model(manifest, &model); -} - -/* - * A callback function for manifest to scan files in raw /firmware archive. - * Returns 0 to keep scanning, or non-zero to stop. - */ -static int manifest_scan_raw_entries(const char *name, void *arg) -{ - struct manifest *manifest = (struct manifest *)arg; - struct u_archive *archive = manifest->archive; - struct model_config model = {0}; - char *ec_name = NULL, *zephyr_name = NULL; - int chars_read = 0; - - /* - * /build/$BOARD/firmware (or CPFE firmware archives) layout: - * - image-${MODEL}{,.serial,.dev...}.bin - * - ${MODEL}/ec.bin or ${MODEL}/zephyr.bin - */ - - if (sscanf(name, "image-%m[^.].bin%n", &model.name, &chars_read) != 1) - return 0; - - /* Ignore the names with extra modifiers like image-$MODEL.serial.bin */ - if (!chars_read || name[chars_read]) { - free(model.name); - return 0; - } - - VB2_DEBUG("Found model <%s>: %s\n", model.name, name); - model.image = strdup(name); - - ASPRINTF(&ec_name, "%s/ec.bin", model.name); - ASPRINTF(&zephyr_name, "%s/zephyr.bin", model.name); - if (archive_has_entry(archive, ec_name)) - model.ec_image = strdup(ec_name); - else if (archive_has_entry(archive, zephyr_name)) - model.ec_image = strdup(zephyr_name); - free(ec_name); - free(zephyr_name); - - return !manifest_add_model(manifest, &model); -} - -/* Returns the matched model config from the manifest, or NULL if not found. */ -static struct model_config *manifest_get_model_config( - const struct manifest *manifest, const char *name) -{ - int i = 0; - - for (i = 0; i < manifest->num; i++) { - if (!strcmp(name, manifest->models[i].name)) - return &manifest->models[i]; - } - return NULL; -} - -/* - * Creates the manifest from the 'signer_config.csv' file. - * Returns 0 on success (loaded), otherwise failure. - */ -static int manifest_from_signer_config(struct manifest *manifest) -{ - struct u_archive *archive = manifest->archive; - uint32_t size; - uint8_t *data; - char *s, *tok_ptr = NULL; - - if (!archive_has_entry(archive, PATH_SIGNER_CONFIG)) - return -1; - - /* - * CSV format: model_name,firmware_image,key_id,ec_image - * - * Note the key_id is not signature_id and won't be used, and ec_image - * may be optional (for example sarien). - */ - - if (archive_read_file(archive, PATH_SIGNER_CONFIG, &data, &size,NULL)) { - ERROR("Failed reading: %s\n", PATH_SIGNER_CONFIG); - return -1; - } - - /* Skip headers. */ - s = strtok_r((char *)data, "\n", &tok_ptr); - if (!s || !strchr(s, ',')) { - ERROR("Invalid %s: missing header.\n", PATH_SIGNER_CONFIG); - free(data); - return -1; - } - - for (s = strtok_r(NULL, "\n", &tok_ptr); s != NULL; - s = strtok_r(NULL, "\n", &tok_ptr)) { - - struct model_config model = {0}; - int discard_model = 0; - - /* - * Both keyid (%3) and ec_image (%4) are optional so we want to - * read at least 2 fields. - */ - if (sscanf(s, "%m[^,],%m[^,],%*[^,],%m[^,]", - &model.name, &model.image, &model.ec_image) < 2) { - ERROR("Invalid entry(%s): %s\n", PATH_SIGNER_CONFIG, s); - discard_model = 1; - } else if (strchr(model.name, '-')) { - /* format: BaseModel-CustomLabel */ - char *tok_dash; - char *base_model; - struct model_config *base_model_config; - - VB2_DEBUG("Found custom-label: %s\n", model.name); - discard_model = 1; - base_model = strtok_r(model.name, "-", &tok_dash); - assert(base_model); - - /* - * Currently we assume the base model (e.g., base_model) - * is always listed before CL models in the CSV file - - * this is based on how the signerbot and the - * chromeos-config works today (validated on octopus). - */ - base_model_config = manifest_get_model_config( - manifest, base_model); - - if (!base_model_config) { - ERROR("Invalid CL-model: %s\n", base_model); - } else if (!base_model_config->is_custom_label) { - base_model_config->is_custom_label = 1; - /* - * Rewriting signature_id is not necessary, - * but in order to generate the same manifest - * from setvars, we want to temporarily use - * the special value. - */ - free(base_model_config->signature_id); - base_model_config->signature_id = strdup( - "sig-id-in-customization-id"); - } - } - - if (discard_model) { - free(model.name); - free(model.image); - free(model.ec_image); - continue; - } - - model.signature_id = strdup(model.name); - if (!manifest_add_model(manifest, &model)) - break; - } - free(data); - return 0; -} - -/* - * Creates the manifest from a simple (legacy) folder with only 1 set of - * firmware images. - * Returns 0 on success (loaded), otherwise failure. - */ -static int manifest_from_simple_folder(struct manifest *manifest) -{ - const char * const host_image_name = "image.bin", - * const old_host_image_name = "bios.bin", - * const ec_name = "ec.bin", - * const pd_name = "pd.bin"; - struct u_archive *archive = manifest->archive; - const char *image_name = NULL; - struct firmware_image image = {0}; - struct model_config model = {0}; - - /* Try to load from current folder. */ - if (archive_has_entry(archive, old_host_image_name)) - image_name = old_host_image_name; - else if (archive_has_entry(archive, host_image_name)) - image_name = host_image_name; - else - return 1; - - model.image = strdup(image_name); - if (archive_has_entry(archive, ec_name)) - model.ec_image = strdup(ec_name); - if (archive_has_entry(archive, pd_name)) - model.pd_image = strdup(pd_name); - /* Extract model name from FWID: $Vendor_$Platform.$Version */ - if (!load_firmware_image(&image, image_name, archive)) { - char *token = NULL; - if (strtok(image.ro_version, "_")) - token = strtok(NULL, "."); - if (token && *token) { - str_convert(token, tolower); - model.name = strdup(token); - } - free_firmware_image(&image); - } - if (!model.name) - model.name = strdup(DEFAULT_MODEL_NAME); - if (manifest->has_keyset) - model.is_custom_label = 1; - manifest_add_model(manifest, &model); - manifest->default_model = manifest->num - 1; - - return 0; -} - -/** - * get_manifest_key() - Wrapper to get the firmware manifest key from crosid - * - * @manifest_key_out - Output parameter of the firmware manifest key. - * - * Returns: - * - <0 if libcrosid is unavailable or there was an error reading - * device data - * - >=0 (the matched device index) success - */ -static int get_manifest_key(char **manifest_key_out) -{ -#ifdef HAVE_CROSID - return crosid_get_firmware_manifest_key(manifest_key_out); -#else - ERROR("This version of futility was compiled without libcrosid " - "(perhaps compiled outside of the Chrome OS build system?) and " - "the update command is not fully supported. Either compile " - "from the Chrome OS build, or pass --model to manually specify " - "the machine model.\n"); - return -1; -#endif -} - -/* - * Finds the existing model_config from manifest that best matches current - * system (as defined by model_name). - * Returns a model_config from manifest, or NULL if not found. - */ -const struct model_config *manifest_find_model(const struct manifest *manifest, - const char *model_name) -{ - char *manifest_key = NULL; - const struct model_config *model = NULL; - int i; - int matched_index; - - /* - * For manifest with single model defined, we should just return because - * there are other mechanisms like platform name check to double confirm - * if the firmware is valid. - */ - if (manifest->num == 1) - return &manifest->models[0]; - - if (!model_name) { - matched_index = get_manifest_key(&manifest_key); - if (matched_index < 0) { - ERROR("Failed to get device identity. " - "Run \"crosid -v\" for explanation.\n"); - return NULL; - } - - INFO("Identified the device using libcrosid, " - "matched chromeos-config index: %d, " - "manifest key (model): %s\n", - matched_index, manifest_key); - model_name = manifest_key; - } - - model = manifest_get_model_config(manifest, model_name); - - if (!model) { - ERROR("Unsupported model: '%s'.\n", model_name); - - fprintf(stderr, - "The firmware manifest key '%s' is not present in this " - "updater archive. The known keys to this updater " - "archive are:\n", model_name); - - for (i = 0; i < manifest->num; i++) - fprintf(stderr, " %s", manifest->models[i].name); - fprintf(stderr, "\n\n"); - fprintf(stderr, - "Perhaps you are trying to use an updater archive for " - "the wrong board, or designed for an older OS version " - "before this model was supported.\n"); - fprintf(stderr, - "Hint: Read the FIRMWARE_MANIFEST_KEY from the output " - "of the crosid command.\n"); - } - - - free(manifest_key); - return model; -} - -/* - * Determines the signature ID to use for custom label. - * Returns the signature ID for looking up rootkey and vblock files. - * Caller must free the returned string. - */ -static char *resolve_signature_id(struct model_config *model, const char *image) -{ - int is_unibuild = model->signature_id ? 1 : 0; - char *tag = vpd_get_value(image, VPD_CUSTOM_LABEL_TAG); - char *sig_id = NULL; - - if (tag == NULL) - tag = vpd_get_value(image, VPD_CUSTOM_LABEL_TAG_LEGACY); - - /* Unified build: $model.$tag, or $model (b/126800200). */ - if (is_unibuild) { - if (!tag) { - WARN("No VPD '%s' set for custom label. " - "Use model name '%s' as default.\n", - VPD_CUSTOM_LABEL_TAG, model->name); - return strdup(model->name); - } - - ASPRINTF(&sig_id, "%s-%s", model->name, tag); - free(tag); - return sig_id; - } - - /* Non-Unibuild: Upper($tag), or Upper(${cid%%-*}). */ - if (!tag) { - char *cid = vpd_get_value(image, VPD_CUSTOMIZATION_ID); - if (cid) { - /* customization_id in format LOEM[-VARIANT]. */ - char *dash = strchr(cid, '-'); - if (dash) - *dash = '\0'; - tag = cid; - } - } - if (tag) - str_convert(tag, toupper); - return tag; -} - -/* - * Applies custom label information to an existing model configuration. - * Collects signature ID information from either parameter signature_id or - * image file (via VPD) and updates model.patches for key files. - * Returns 0 on success, otherwise failure. - */ -int model_apply_custom_label( - struct model_config *model, - struct u_archive *archive, - const char *signature_id, - const char *image) -{ - char *sig_id = NULL; - int r = 0; - - if (!signature_id) { - sig_id = resolve_signature_id(model, image); - signature_id = sig_id; - } - - if (signature_id) { - VB2_DEBUG("Find custom label patches by signature ID: '%s'.\n", - signature_id); - find_patches_for_model(model, archive, signature_id); - } else { - signature_id = ""; - WARN("No VPD '%s' set for custom label - use default keys.\n", - VPD_CUSTOM_LABEL_TAG); - } - if (!model->patches.rootkey) { - ERROR("No keys found for signature_id: '%s'\n", signature_id); - r = 1; - } else { - INFO("Applied for custom label: %s\n", signature_id); - } - free(sig_id); - return r; -} - -/* - * Creates a new manifest object by scanning files in archive. - * Returns the manifest on success, otherwise NULL for failure. - */ -struct manifest *new_manifest_from_archive(struct u_archive *archive) -{ - struct manifest manifest = {0}, *new_manifest; - - manifest.archive = archive; - manifest.default_model = -1; - - VB2_DEBUG("Try to build a manifest from *%s\n", PATH_ENDSWITH_SETVARS); - archive_walk(archive, &manifest, manifest_scan_entries); - - if (manifest.num == 0) { - VB2_DEBUG("Try to build a manifest from %s\n", - PATH_SIGNER_CONFIG); - manifest_from_signer_config(&manifest); - } - if (manifest.num == 0) { - VB2_DEBUG("Try to build a manifest from a */firmware folder\n"); - archive_walk(archive, &manifest, manifest_scan_raw_entries); - } - if (manifest.num == 0) { - VB2_DEBUG("Try to build a manifest from a simple folder\n"); - manifest_from_simple_folder(&manifest); - } - - VB2_DEBUG("%d model(s) loaded.\n", manifest.num); - if (!manifest.num) { - ERROR("No valid configurations found from archive.\n"); - return NULL; - } - - new_manifest = (struct manifest *)malloc(sizeof(manifest)); - if (!new_manifest) { - ERROR("Internal error: memory allocation error.\n"); - return NULL; - } - memcpy(new_manifest, &manifest, sizeof(manifest)); - return new_manifest; -} - -/* Releases all resources allocated by given manifest object. */ -void delete_manifest(struct manifest *manifest) -{ - int i; - assert(manifest); - for (i = 0; i < manifest->num; i++) { - struct model_config *model = &manifest->models[i]; - free(model->name); - free(model->signature_id); - free(model->image); - free(model->ec_image); - free(model->pd_image); - free(model->patches.rootkey); - free(model->patches.vblock_a); - free(model->patches.vblock_b); - } - free(manifest->models); - free(manifest); -} - -static const char *get_gbb_key_hash(const struct vb2_gbb_header *gbb, - int32_t offset, int32_t size) -{ - struct vb2_packed_key *key; - - if (!gbb) - return "<No GBB>"; - key = (struct vb2_packed_key *)((uint8_t *)gbb + offset); - if (vb2_packed_key_looks_ok(key, size)) - return "<Invalid key>"; - return packed_key_sha1_string(key); -} - -/* Prints the information of given image file in JSON format. */ -static void print_json_image( - const char *name, const char *fpath, struct model_config *m, - struct u_archive *archive, int indent, int is_host) -{ - struct firmware_image image = {0}; - const struct vb2_gbb_header *gbb = NULL; - if (!fpath) - return; - if (load_firmware_image(&image, fpath, archive)) - return; - if (!is_host) - printf(",\n"); - printf("%*s\"%s\": { \"versions\": { \"ro\": \"%s\", \"rw\": \"%s\" },", - indent, "", name, image.ro_version, image.rw_version_a); - indent += 2; - if (!is_host) { - /* No extra information to be printed */ - } else if (patch_image_by_model(&image, m, archive) != 0) { - ERROR("Failed to patch images by model: %s\n", m->name); - } else if (NULL != (gbb = find_gbb(&image))) { - printf("\n%*s\"keys\": { \"root\": \"%s\", ", - indent, "", - get_gbb_key_hash(gbb, gbb->rootkey_offset, - gbb->rootkey_size)); - printf("\"recovery\": \"%s\" },", - get_gbb_key_hash(gbb, gbb->recovery_key_offset, - gbb->recovery_key_size)); - } - printf("\n%*s\"image\": \"%s\" }", indent, "", fpath); - free_firmware_image(&image); -} - -/* Prints the information of objects in manifest (models and images) in JSON. */ -void print_json_manifest(const struct manifest *manifest) -{ - int i, indent; - struct u_archive *ar = manifest->archive; - - printf("{\n"); - for (i = 0, indent = 2; i < manifest->num; i++) { - struct model_config *m = &manifest->models[i]; - printf("%s%*s\"%s\": {\n", i ? ",\n" : "", indent, "", m->name); - indent += 2; - print_json_image("host", m->image, m, ar, indent, 1); - print_json_image("ec", m->ec_image, m, ar, indent, 0); - print_json_image("pd", m->pd_image, m, ar, indent, 0); - if (m->patches.rootkey) { - struct patch_config *p = &m->patches; - printf(",\n%*s\"patches\": { \"rootkey\": \"%s\", " - "\"vblock_a\": \"%s\", \"vblock_b\": \"%s\" }", - indent, "", p->rootkey, p->vblock_a, - p->vblock_b); - } - if (m->signature_id) - printf(",\n%*s\"signature_id\": \"%s\"", indent, "", - m->signature_id); - printf("\n }"); - indent -= 2; - assert(indent == 2); - } - printf("\n}\n"); -} |