summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHung-Te Lin <hungte@chromium.org>2018-10-09 12:00:10 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-10-15 16:01:59 -0700
commitc5cdf6fce6b0c0116bf4bcf522fce38bb05ab6d4 (patch)
treeb5e22634dbeb381bf822f261cc34792614e48244
parent3e6397d3d612a6436052c163b8686020357e7442 (diff)
downloadvboot-c5cdf6fce6b0c0116bf4bcf522fce38bb05ab6d4.tar.gz
futility: updater: Allow patching rootkey and vblock files
For white label projects, the firmware updater has to select correct root key and corresponding vblock files per different LOEM. In Unified build, multiple models may share same firmware base image, with different key files (per OEM). As a result, we have to apply the key files before using the firmware image files. This change adds the "patch" information when building manifest, and prints the correct key hash in `--manifest` mode. BUG=chromium:875551 TEST=TEST=make futil; tests/futility/run_test_scripts.sh $(pwd)/build/futility BRANCH=None Change-Id: Ib5e31af5262a0989a5a474d0683c83121f24cc78 Signed-off-by: Hung-Te Lin <hungte@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1270323 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--futility/updater.c8
-rw-r--r--futility/updater.h15
-rw-r--r--futility/updater_archive.c193
-rw-r--r--tests/futility/link.manifest.json1
4 files changed, 207 insertions, 10 deletions
diff --git a/futility/updater.c b/futility/updater.c
index 94e672dd..39daf9b4 100644
--- a/futility/updater.c
+++ b/futility/updater.c
@@ -877,7 +877,7 @@ int preserve_firmware_section(const struct firmware_image *image_from,
* Finds the GBB (Google Binary Block) header on a given firmware image.
* Returns a pointer to valid GBB header, or NULL on not found.
*/
-static struct vb2_gbb_header *find_gbb(const struct firmware_image *image)
+const struct vb2_gbb_header *find_gbb(const struct firmware_image *image)
{
struct firmware_section section;
struct vb2_gbb_header *gbb_header;
@@ -907,10 +907,12 @@ static int preserve_gbb(const struct firmware_image *image_from,
{
int len;
uint8_t *hwid_to, *hwid_from;
- struct vb2_gbb_header *gbb_from, *gbb_to;
+ const struct vb2_gbb_header *gbb_from;
+ struct vb2_gbb_header *gbb_to;
gbb_from = find_gbb(image_from);
- gbb_to = find_gbb(image_to);
+ /* We do want to change GBB contents later. */
+ gbb_to = (struct vb2_gbb_header *)find_gbb(image_to);
if (!gbb_from || !gbb_to)
return -1;
diff --git a/futility/updater.h b/futility/updater.h
index 366ecf10..3971c4e6 100644
--- a/futility/updater.h
+++ b/futility/updater.h
@@ -28,6 +28,7 @@ static const char * const FMAP_RO_FRID = "RO_FRID",
* const FMAP_RO_VPD = "RO_VPD",
* const FMAP_RW_VPD = "RW_VPD",
* const FMAP_RW_VBLOCK_A = "VBLOCK_A",
+ * const FMAP_RW_VBLOCK_B = "VBLOCK_B",
* const FMAP_RW_SECTION_A = "RW_SECTION_A",
* const FMAP_RW_SECTION_B = "RW_SECTION_B",
* const FMAP_RW_FWID = "RW_FWID",
@@ -118,9 +119,16 @@ struct updater_config_arguments {
int verbosity;
};
+struct patch_config {
+ char *rootkey;
+ char *vblock_a;
+ char *vblock_b;
+};
+
struct model_config {
char *name;
char *image, *ec_image, *pd_image;
+ struct patch_config patches;
char *signature_id;
};
@@ -243,6 +251,13 @@ int get_system_property(enum system_property_type property_type,
const char * const updater_get_default_quirks(struct updater_config *cfg);
/*
+ * Finds the GBB (Google Binary Block) header on a given firmware image.
+ * Returns a pointer to valid GBB header, or NULL on not found.
+ */
+struct vb2_gbb_header;
+const struct vb2_gbb_header *find_gbb(const struct firmware_image *image);
+
+/*
* Executes a command on current host and returns stripped command output.
* If the command has failed (exit code is not zero), returns an empty string.
* The caller is responsible for releasing the returned string.
diff --git a/futility/updater_archive.c b/futility/updater_archive.c
index 736d459e..0c866bdb 100644
--- a/futility/updater_archive.c
+++ b/futility/updater_archive.c
@@ -20,6 +20,7 @@
#include "host_misc.h"
#include "updater.h"
+#include "util_misc.h"
#include "vb2_common.h"
/*
@@ -428,6 +429,141 @@ static int model_config_parse_setvars_file(
}
/*
+ * 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.", image->file_name);
+ return -1;
+ }
+ if (gbb->rootkey_size < rootkey_len) {
+ ERROR("New root key (%u bytes) larger than GBB (%u bytes).",
+ 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(&section, image, section_name);
+ if (!section.data) {
+ ERROR("Need section %s in image %s.", section_name,
+ image->file_name);
+ return -1;
+ }
+ if (section.size < vblock_len) {
+ ERROR("Section %s too small (%zu bytes) for vblock (%u bytes).",
+ 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 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);
+ if (r == 0) {
+ DEBUG("Loaded file: %s", path);
+ r = apply(image, section_name, data, len);
+ if (r)
+ ERROR("Failed applying %s to %s", path, section_name);
+ } else {
+ ERROR("Failed reading: %s", path);
+ }
+ free(data);
+ return r;
+}
+
+/*
+ * Modifies a firmware image from patch information specified in model config.
+ * Returns 0 on success, otherwise number of failures.
+ */
+static int patch_image_by_model(
+ struct firmware_image *image, const struct model_config *model,
+ struct 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 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.
*/
@@ -487,6 +623,11 @@ static int manifest_scan_entries(const char *name, void *arg)
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);
}
@@ -545,25 +686,56 @@ void delete_manifest(struct manifest *manifest)
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 (!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 archive *archive,
- int indent, int is_host)
+ const char *name, const char *fpath, struct model_config *m,
+ struct archive *archive, int indent, int is_host)
{
struct firmware_image image = {0};
+ const struct vb2_gbb_header *gbb = NULL;
if (!fpath)
return;
load_firmware_image(&image, fpath, archive);
- if (!is_host)
+ if (is_host)
+ gbb = find_gbb(&image);
+ else
printf(",\n");
printf("%*s\"%s\": { \"versions\": { \"ro\": \"%s\", \"rw\": \"%s\" },",
indent, "", name, image.ro_version, image.rw_version_a);
- printf("\n%*s\"image\": \"%s\" }", indent + 2, "", fpath);
+ indent += 2;
+ if (is_host && patch_image_by_model(&image, m, archive) != 0) {
+ ERROR("Failed to patch images by model: %s", m->name);
+ } else if (gbb) {
+ 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);
}
@@ -578,9 +750,16 @@ void print_json_manifest(const struct manifest *manifest)
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, ar, indent, 1);
- print_json_image("ec", m->ec_image, ar, indent, 0);
- print_json_image("pd", m->pd_image, ar, indent, 0);
+ 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);
diff --git a/tests/futility/link.manifest.json b/tests/futility/link.manifest.json
index 2cd5281b..2e11cd3b 100644
--- a/tests/futility/link.manifest.json
+++ b/tests/futility/link.manifest.json
@@ -1,6 +1,7 @@
{
"default": {
"host": { "versions": { "ro": "Google_Link.2695.1.133", "rw": "Google_Link.2695.1.133" },
+ "keys": { "root": "7b5c520ceabce86f13e02b7ca363cfb509fc5b98", "recovery": "7e74cd6d66f361da068c0419d2e0946b4d091e1c" },
"image": "bios.bin" }
}
}