summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHung-Te Lin <hungte@chromium.org>2018-08-27 12:57:14 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-09-05 16:10:57 -0700
commite53fbd1be95a79db0f66382cd0207a6bbd990309 (patch)
tree94214dc46b8d74e61b53e0c19b68b4fc1494fe05
parent4256ddecd72ce6d2b3e7e10fbc328f57bfdc4ac6 (diff)
downloadvboot-e53fbd1be95a79db0f66382cd0207a6bbd990309.tar.gz
futility: cmd_update: Check TPM key versions before updating
In verified boot, the key versions stored in TPM will be checked before being able to load and run a signed RW firmware. This is also known as anti-rollback check. To prevent user installing an incompatible RO (even RW) and then being not able to boot, we should check TPM key versions (by `tpm_fwver` system property) before starting to update. BUG=chromium:875551 TEST=make futil; tests/futility/run_test_scripts.sh $(pwd)/build/futility BRANCH=None Change-Id: I6d50a6e475001d76fbcbe680a3f8b10f62354096 Signed-off-by: Hung-Te Lin <hungte@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1189249 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--futility/cmd_update.c110
-rwxr-xr-xtests/futility/test_update.sh51
2 files changed, 150 insertions, 11 deletions
diff --git a/futility/cmd_update.c b/futility/cmd_update.c
index 5639497f..ed0b83f2 100644
--- a/futility/cmd_update.c
+++ b/futility/cmd_update.c
@@ -17,6 +17,7 @@
#include "futility.h"
#include "host_misc.h"
#include "utility.h"
+#include "vb2_struct.h"
#define COMMAND_BUFFER_SIZE 256
#define RETURN_ON_FAILURE(x) do {int r = (x); if (r) return r;} while (0);
@@ -28,6 +29,7 @@ static const char * const FMAP_RO_FRID = "RO_FRID",
* const FMAP_RO_GBB = "GBB",
* const FMAP_RO_VPD = "RO_VPD",
* const FMAP_RW_VPD = "RW_VPD",
+ * const FMAP_RW_VBLOCK_A = "VBLOCK_A",
* const FMAP_RW_SECTION_A = "RW_SECTION_A",
* const FMAP_RW_SECTION_B = "RW_SECTION_B",
* const FMAP_RW_FWID = "RW_FWID",
@@ -96,6 +98,7 @@ struct system_property {
enum system_property_type {
SYS_PROP_MAINFW_ACT,
+ SYS_PROP_TPM_FWVER,
SYS_PROP_FW_VBOOT2,
SYS_PROP_WP_HW,
SYS_PROP_WP_SW,
@@ -180,6 +183,12 @@ static int host_get_mainfw_act()
return SLOT_UNKNOWN;
}
+/* A helper function to return the "tpm_fwver" system property. */
+static int host_get_tpm_fwver()
+{
+ return VbGetSystemPropertyInt("tpm_fwver");
+}
+
/* A helper function to return the "hardware write protection" status. */
static int host_get_wp_hw()
{
@@ -924,6 +933,93 @@ static int check_compatible_platform(struct updater_config *cfg)
image_from->ro_version);
return strncmp(image_from->ro_version, image_to->ro_version, len);
}
+
+/*
+ * Returns a key block key from given image section, or NULL on failure.
+ */
+static const struct vb2_keyblock *get_keyblock(
+ const struct firmware_image *image,
+ const char *section_name)
+{
+ struct firmware_section section;
+
+ find_firmware_section(&section, image, section_name);
+ /* A keyblock must be followed by a vb2_fw_preamble. */
+ if (section.size < sizeof(struct vb2_keyblock) +
+ sizeof(struct vb2_fw_preamble)) {
+ Error("%s: Invalid section: %s\n", __FUNCTION__, section_name);
+ return NULL;
+ }
+ return (const struct vb2_keyblock *)section.data;
+}
+
+/*
+ * 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
+ * into argument `data_key_version` and `firmware_version`.
+ * Returns 0 for success, otherwise failure.
+ */
+static int get_key_versions(const struct firmware_image *image,
+ const char *section_name,
+ unsigned int *data_key_version,
+ unsigned int *firmware_version)
+{
+ const struct vb2_keyblock *keyblock = get_keyblock(image, section_name);
+ const struct vb2_fw_preamble *pre;
+
+ if (!keyblock)
+ return -1;
+ *data_key_version = keyblock->data_key.key_version;
+ pre = (struct vb2_fw_preamble *)((uint8_t*)keyblock +
+ keyblock->keyblock_size);
+ *firmware_version = pre->firmware_version;
+ Debug("%s: %s: data key version = %d, firmware version = %d\n",
+ __FUNCTION__, image->file_name, *data_key_version,
+ *firmware_version);
+ 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.
+ */
+static int check_compatible_tpm_keys(struct updater_config *cfg,
+ const struct firmware_image *rw_image)
+{
+ unsigned int data_key_version = 0, firmware_version = 0,
+ tpm_data_key_version = 0, tpm_firmware_version = 0,
+ tpm_fwver = 0;
+
+ tpm_fwver = get_system_property(SYS_PROP_TPM_FWVER, cfg);
+ if (tpm_fwver <= 0) {
+ Error("%s: Invalid tpm_fwver: %d.\n", __FUNCTION__, tpm_fwver);
+ return -1;
+ }
+
+ tpm_data_key_version = tpm_fwver >> 16;
+ tpm_firmware_version = tpm_fwver & 0xffff;
+ Debug("%s: TPM: data_key_version = %d, firmware_version = %d\n",
+ __FUNCTION__, tpm_data_key_version, tpm_firmware_version);
+
+ if (get_key_versions(rw_image, FMAP_RW_VBLOCK_A, &data_key_version,
+ &firmware_version) != 0)
+ return -1;
+
+ if (tpm_data_key_version > data_key_version) {
+ Error("%s: Data key version rollback detected (%d->%d).\n",
+ __FUNCTION__, tpm_data_key_version, data_key_version);
+ return -1;
+ }
+ if (tpm_firmware_version > firmware_version) {
+ Error("%s: Firmware version rollback detected (%d->%d).\n",
+ __FUNCTION__, tpm_firmware_version, firmware_version);
+ return -1;
+ }
+ return 0;
+}
+
enum updater_error_codes {
UPDATE_ERR_DONE,
UPDATE_ERR_NEED_RO_UPDATE,
@@ -934,6 +1030,7 @@ enum updater_error_codes {
UPDATE_ERR_WRITE_FIRMWARE,
UPDATE_ERR_PLATFORM,
UPDATE_ERR_TARGET,
+ UPDATE_ERR_TPM_ROLLBACK,
UPDATE_ERR_UNKNOWN,
};
@@ -947,6 +1044,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_TPM_ROLLBACK] = "RW not usable due to TPM anti-rollback.",
[UPDATE_ERR_UNKNOWN] = "Unknown error.",
};
@@ -971,6 +1069,10 @@ static enum updater_error_codes update_try_rw_firmware(
image_from, image_to, FMAP_RO_SECTION))
return UPDATE_ERR_NEED_RO_UPDATE;
+ printf("Checking compatibility...\n");
+ if (check_compatible_tpm_keys(cfg, image_to))
+ return UPDATE_ERR_TPM_ROLLBACK;
+
Debug("%s: Firmware %s vboot2.\n", __FUNCTION__,
is_vboot2 ? "is" : "is NOT");
target = decide_rw_target(cfg, TARGET_SELF, is_vboot2);
@@ -1023,6 +1125,9 @@ static enum updater_error_codes update_rw_firmrware(
printf(">> RW UPDATE: Updating RW sections (%s, %s, and %s).\n",
FMAP_RW_SECTION_A, FMAP_RW_SECTION_B, FMAP_RW_SHARED);
+ printf("Checking compatibility...\n");
+ if (check_compatible_tpm_keys(cfg, image_to))
+ return UPDATE_ERR_TPM_ROLLBACK;
/*
* TODO(hungte) Speed up by flashing multiple sections in one
* command, or provide diff file.
@@ -1049,6 +1154,10 @@ static enum updater_error_codes update_whole_firmware(
printf(">> FULL UPDATE: Updating whole firmware image(s), RO+RW.\n");
preserve_images(cfg);
+ printf("Checking compatibility...\n");
+ if (check_compatible_tpm_keys(cfg, image_to))
+ return UPDATE_ERR_TPM_ROLLBACK;
+
/* FMAP may be different so we should just update all. */
if (write_firmware(cfg, image_to, NULL) ||
write_optional_firmware(cfg, &cfg->ec_image, NULL) ||
@@ -1182,6 +1291,7 @@ static int do_update(int argc, char *argv[])
.emulate = 0,
.system_properties = {
[SYS_PROP_MAINFW_ACT] = {.getter = host_get_mainfw_act},
+ [SYS_PROP_TPM_FWVER] = {.getter = host_get_tpm_fwver},
[SYS_PROP_FW_VBOOT2] = {.getter = host_get_fw_vboot2},
[SYS_PROP_WP_HW] = {.getter = host_get_wp_hw},
[SYS_PROP_WP_SW] = {.getter = host_get_wp_sw},
diff --git a/tests/futility/test_update.sh b/tests/futility/test_update.sh
index 9a56c3da..26d993d8 100755
--- a/tests/futility/test_update.sh
+++ b/tests/futility/test_update.sh
@@ -106,48 +106,77 @@ test_update() {
fi
}
-# --sys_props: mainfw_act, is_vboot2, [wp_hw, wp_sw]
+# --sys_props: mainfw_act, tpm_fwver, is_vboot2, [wp_hw, wp_sw]
+# tpm_fwver = <data key version:16><firmware version:16>.
+# TO_IMAGE is signed with data key version = 1, firmware version = 4 => 0x10004.
# Test Full update.
test_update "Full update" \
"${FROM_IMAGE}" "${TMP}.expected.full" \
- -i "${TO_IMAGE}" --wp=0
+ -i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,1
test_update "Full update (incompatible platform)" \
"${FROM_IMAGE}" "!platform is not compatible" \
- -i "${LINK_BIOS}" --wp=0
+ -i "${LINK_BIOS}" --wp=0 --sys_props 0,0x10001,1
+
+test_update "Full update (TPM Anti-rollback: data key)" \
+ "${FROM_IMAGE}" "!Data key version rollback detected (2->1)" \
+ -i "${TO_IMAGE}" --wp=0 --sys_props 1,0x20001,1
+
+test_update "Full update (TPM Anti-rollback: kernel key)" \
+ "${FROM_IMAGE}" "!Firmware version rollback detected (5->4)" \
+ -i "${TO_IMAGE}" --wp=0 --sys_props 1,0x10005,1
# Test RW-only update.
test_update "RW update" \
"${FROM_IMAGE}" "${TMP}.expected.rw" \
- -i "${TO_IMAGE}" --wp=1
+ -i "${TO_IMAGE}" --wp=1 --sys_props 0,0x10001,1
test_update "RW update (incompatible platform)" \
"${FROM_IMAGE}" "!platform is not compatible" \
- -i "${LINK_BIOS}" --wp=1
+ -i "${LINK_BIOS}" --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
+
+test_update "RW update (TPM Anti-rollback: kernel key)" \
+ "${FROM_IMAGE}" "!Firmware version rollback detected (5->4)" \
+ -i "${TO_IMAGE}" --wp=1 --sys_props 1,0x10005,1
# Test Try-RW update (vboot2).
test_update "RW update (A->B)" \
"${FROM_IMAGE}" "${TMP}.expected.b" \
- -i "${TO_IMAGE}" -t --wp=1 --sys_props 0,1
+ -i "${TO_IMAGE}" -t --wp=1 --sys_props 0,0x10001,1
test_update "RW update (B->A)" \
"${FROM_IMAGE}" "${TMP}.expected.a" \
- -i "${TO_IMAGE}" -t --wp=1 --sys_props 1,1
+ -i "${TO_IMAGE}" -t --wp=1 --sys_props 1,0x10001,1
test_update "RW update -> fallback to RO+RW Full update" \
"${FROM_IMAGE}" "${TMP}.expected.full" \
- -i "${TO_IMAGE}" -t --wp=0 --sys_props 1,1
+ -i "${TO_IMAGE}" -t --wp=0 --sys_props 1,0x10002,1
test_update "RW update (incompatible platform)" \
"${FROM_IMAGE}" "!platform is not compatible" \
- -i "${LINK_BIOS}" -t --wp=1
+ -i "${LINK_BIOS}" -t --wp=1 --sys_props 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
+
+test_update "RW update (TPM Anti-rollback: kernel key)" \
+ "${FROM_IMAGE}" "!Firmware version rollback detected (5->4)" \
+ -i "${TO_IMAGE}" -t --wp=1 --sys_props 1,0x10005,1
+test_update "RW update -> fallback to RO+RW Full update (TPM Anti-rollback)" \
+ "${TO_IMAGE}" "!Firmware version rollback detected (4->2)" \
+ -i "${FROM_IMAGE}" -t --wp=0 --sys_props 1,0x10004,1
# Test Try-RW update (vboot1).
test_update "RW update (vboot1, A->B)" \
"${FROM_IMAGE}" "${TMP}.expected.b" \
- -i "${TO_IMAGE}" -t --wp=1 --sys_props 0,0
+ -i "${TO_IMAGE}" -t --wp=1 --sys_props 0,0 --sys_props 0,0x10001,0
test_update "RW update (vboot1, B->B)" \
"${FROM_IMAGE}" "${TMP}.expected.b" \
- -i "${TO_IMAGE}" -t --wp=1 --sys_props 1,0
+ -i "${TO_IMAGE}" -t --wp=1 --sys_props 1,0 --sys_props 0,0x10001,0