summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHung-Te Lin <hungte@chromium.org>2018-08-27 13:03:36 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-09-05 16:10:58 -0700
commit86e12ef99dfcc71c6e4903e3ed892330e78f66ff (patch)
tree18f1cc0939c4cf30b9ec6a615101fb11dafce775
parente53fbd1be95a79db0f66382cd0207a6bbd990309 (diff)
downloadvboot-86e12ef99dfcc71c6e4903e3ed892330e78f66ff.tar.gz
futility: cmd_update: Check RO and RW signing compatibility before updating
If the RW is not signed by RO root key then verified boot will fail and enter recovery mode. This may happen when user is trying to flash a DEV (or PreMP) signed firmware on a MP-signed device, with write protection enabled. BUG=chromium:875551 TEST=make futil; tests/futility/run_test_scripts.sh $(pwd)/build/futility BRANCH=None Change-Id: I1cee0b5f42f1f403d9baa5f9b2659f75511fbcb8 Signed-off-by: Hung-Te Lin <hungte@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1183659 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--futility/cmd_update.c131
-rwxr-xr-xtests/futility/test_update.sh14
2 files changed, 145 insertions, 0 deletions
diff --git a/futility/cmd_update.c b/futility/cmd_update.c
index ed0b83f2..964f071e 100644
--- a/futility/cmd_update.c
+++ b/futility/cmd_update.c
@@ -12,11 +12,14 @@
#include <stdio.h>
#include <stdlib.h>
+#include "2rsa.h"
#include "crossystem.h"
#include "fmap.h"
#include "futility.h"
#include "host_misc.h"
#include "utility.h"
+#include "util_misc.h"
+#include "vb2_common.h"
#include "vb2_struct.h"
#define COMMAND_BUFFER_SIZE 256
@@ -935,6 +938,22 @@ static int check_compatible_platform(struct updater_config *cfg)
}
/*
+ * Returns a valid root key from GBB header, or NULL on failure.
+ */
+static const struct vb2_packed_key *get_rootkey(
+ const struct vb2_gbb_header *gbb)
+{
+ struct vb2_packed_key *key = NULL;
+
+ key = (struct vb2_packed_key *)((uint8_t *)gbb + gbb->rootkey_offset);
+ if (!packed_key_looks_ok(key, gbb->rootkey_size)) {
+ Error("%s: Invalid root key.\n", __FUNCTION__);
+ return NULL;
+ }
+ return key;
+}
+
+/*
* Returns a key block key from given image section, or NULL on failure.
*/
static const struct vb2_keyblock *get_keyblock(
@@ -954,6 +973,57 @@ static const struct vb2_keyblock *get_keyblock(
}
/*
+ * Duplicates a key block and returns the duplicated block.
+ * The caller must free the returned key block after being used.
+ */
+static struct vb2_keyblock *dupe_keyblock(const struct vb2_keyblock *block)
+{
+ struct vb2_keyblock *new_block;
+
+ new_block = (struct vb2_keyblock *)malloc(block->keyblock_size);
+ assert(new_block);
+ memcpy(new_block, block, block->keyblock_size);
+ return new_block;
+}
+
+/*
+ * Verifies if keyblock is signed with given key.
+ * Returns 0 on success, otherwise failure.
+ */
+static int verify_keyblock(const struct vb2_keyblock *block,
+ const struct vb2_packed_key *sign_key) {
+ int r;
+ uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE];
+ struct vb2_workbuf wb;
+ struct vb2_public_key key;
+ struct vb2_keyblock *new_block;
+
+ if (block->keyblock_signature.sig_size == 0) {
+ Error("%s: Keyblock is not signed.\n", __FUNCTION__);
+ return -1;
+ }
+ vb2_workbuf_init(&wb, workbuf, sizeof(workbuf));
+ if (VB2_SUCCESS != vb2_unpack_key(&key, sign_key)) {
+ Error("%s: Invalid signing key,\n", __FUNCTION__);
+ return -1;
+ }
+
+ /*
+ * vb2_verify_keyblock will destroy the signature inside keyblock
+ * so we have to verify with a local copy.
+ */
+ new_block = dupe_keyblock(block);
+ r = vb2_verify_keyblock(new_block, new_block->keyblock_size, &key, &wb);
+ free(new_block);
+
+ if (r != VB2_SUCCESS) {
+ Error("%s: Error verifying key block.\n", __FUNCTION__);
+ return -1;
+ }
+ return 0;
+}
+
+/*
* Gets the data key and firmware version from a section on firmware image.
* The section should contain a vb2_keyblock and a vb2_fw_preamble immediately
* after key block so we can decode and save the data key and firmware version
@@ -981,6 +1051,61 @@ static int get_key_versions(const struct firmware_image *image,
}
/*
+ * Checks if the root key in ro_image can verify vblocks in rw_image.
+ * Returns 0 for success, otherwise failure.
+ */
+static int check_compatible_root_key(const struct firmware_image *ro_image,
+ const struct firmware_image *rw_image)
+{
+ const struct vb2_gbb_header *gbb = find_gbb(ro_image);
+ const struct vb2_packed_key *rootkey;
+ const struct vb2_keyblock *keyblock;
+
+ if (!gbb)
+ return -1;
+
+ rootkey = get_rootkey(gbb);
+ if (!rootkey)
+ return -1;
+
+ /* Assume VBLOCK_A and VBLOCK_B are signed in same way. */
+ keyblock = get_keyblock(rw_image, FMAP_RW_VBLOCK_A);
+ if (!keyblock)
+ return -1;
+
+ if (verify_keyblock(keyblock, rootkey) != 0) {
+ const struct vb2_gbb_header *gbb_rw = find_gbb(rw_image);
+ const struct vb2_packed_key *rootkey_rw = NULL;
+ int is_same_key = 0;
+ /*
+ * Try harder to provide more info.
+ * packed_key_sha1_string uses static buffer so don't call
+ * it twice in args list of one expression.
+ */
+ if (gbb_rw)
+ rootkey_rw = get_rootkey(gbb_rw);
+ if (rootkey_rw) {
+ if (rootkey->key_offset == rootkey_rw->key_offset &&
+ rootkey->key_size == rootkey_rw->key_size &&
+ memcmp(rootkey, rootkey_rw, rootkey->key_size +
+ rootkey->key_offset) == 0)
+ is_same_key = 1;
+ }
+ printf("Current (RO) image root key is %s, ",
+ packed_key_sha1_string(rootkey));
+ if (is_same_key)
+ printf("same with target (RW) image. "
+ "Maybe RW corrupted?\n");
+ else
+ printf("target (RW) image is signed with rootkey %s.\n",
+ rootkey_rw ? packed_key_sha1_string(rootkey_rw) :
+ "<invalid>");
+ return -1;
+ }
+ return 0;
+}
+
+/*
* Checks if the given firmware image is signed with a key that won't be
* blocked by TPM's anti-rollback detection.
* Returns 0 for success, otherwise failure.
@@ -1030,6 +1155,7 @@ enum updater_error_codes {
UPDATE_ERR_WRITE_FIRMWARE,
UPDATE_ERR_PLATFORM,
UPDATE_ERR_TARGET,
+ UPDATE_ERR_ROOT_KEY,
UPDATE_ERR_TPM_ROLLBACK,
UPDATE_ERR_UNKNOWN,
};
@@ -1044,6 +1170,7 @@ static const char * const updater_error_messages[] = {
[UPDATE_ERR_WRITE_FIRMWARE] = "Failed writing firmware.",
[UPDATE_ERR_PLATFORM] = "Your system platform is not compatible.",
[UPDATE_ERR_TARGET] = "No valid RW target to update. Abort.",
+ [UPDATE_ERR_ROOT_KEY] = "RW not signed by same RO root key",
[UPDATE_ERR_TPM_ROLLBACK] = "RW not usable due to TPM anti-rollback.",
[UPDATE_ERR_UNKNOWN] = "Unknown error.",
};
@@ -1070,6 +1197,8 @@ static enum updater_error_codes update_try_rw_firmware(
return UPDATE_ERR_NEED_RO_UPDATE;
printf("Checking compatibility...\n");
+ if (check_compatible_root_key(image_from, image_to))
+ return UPDATE_ERR_ROOT_KEY;
if (check_compatible_tpm_keys(cfg, image_to))
return UPDATE_ERR_TPM_ROLLBACK;
@@ -1126,6 +1255,8 @@ static enum updater_error_codes update_rw_firmrware(
FMAP_RW_SECTION_A, FMAP_RW_SECTION_B, FMAP_RW_SHARED);
printf("Checking compatibility...\n");
+ if (check_compatible_root_key(image_from, image_to))
+ return UPDATE_ERR_ROOT_KEY;
if (check_compatible_tpm_keys(cfg, image_to))
return UPDATE_ERR_TPM_ROLLBACK;
/*
diff --git a/tests/futility/test_update.sh b/tests/futility/test_update.sh
index 26d993d8..d51219ff 100755
--- a/tests/futility/test_update.sh
+++ b/tests/futility/test_update.sh
@@ -62,12 +62,18 @@ unpack_image() {
local image="$2"
mkdir -p "${folder}"
(cd "${folder}" && ${FUTILITY} dump_fmap -x "../${image}")
+ ${FUTILITY} gbb -g --rootkey="${folder}/rootkey" "${image}"
}
# Unpack images so we can prepare expected results by individual sections.
unpack_image "to" "${TO_IMAGE}"
unpack_image "from" "${FROM_IMAGE}"
+# Hack FROM_IMAGE so it has same root key as TO_IMAGE (for RW update).
+FROM_DIFFERENT_ROOTKEY_IMAGE="${FROM_IMAGE}2"
+cp -f "${FROM_IMAGE}" "${FROM_DIFFERENT_ROOTKEY_IMAGE}"
+"${FUTILITY}" gbb -s --rootkey="${TMP}.to/rootkey" "${FROM_IMAGE}"
+
# Generate expected results.
cp -f "${TO_IMAGE}" "${TMP}.expected.full"
cp -f "${FROM_IMAGE}" "${TMP}.expected.rw"
@@ -136,6 +142,10 @@ test_update "RW update (incompatible platform)" \
"${FROM_IMAGE}" "!platform is not compatible" \
-i "${LINK_BIOS}" --wp=1 --sys_props 0,0x10001,1
+test_update "RW update (incompatible rootkey)" \
+ "${FROM_DIFFERENT_ROOTKEY_IMAGE}" "!RW not signed by same RO root key" \
+ -i "${TO_IMAGE}" --wp=1 --sys_props 0,0x10001,1
+
test_update "RW update (TPM Anti-rollback: data key)" \
"${FROM_IMAGE}" "!Data key version rollback detected (2->1)" \
-i "${TO_IMAGE}" --wp=1 --sys_props 1,0x20001,1
@@ -160,6 +170,10 @@ test_update "RW update (incompatible platform)" \
"${FROM_IMAGE}" "!platform is not compatible" \
-i "${LINK_BIOS}" -t --wp=1 --sys_props 0x10001,1
+test_update "RW update (incompatible rootkey)" \
+ "${FROM_DIFFERENT_ROOTKEY_IMAGE}" "!RW not signed by same RO root key" \
+ -i "${TO_IMAGE}" -t --wp=1 --sys_props 0,0x10001,1
+
test_update "RW update (TPM Anti-rollback: data key)" \
"${FROM_IMAGE}" "!Data key version rollback detected (2->1)" \
-i "${TO_IMAGE}" -t --wp=1 --sys_props 1,0x20001,1