From 87c9faeb4c7a94b2de1b2cf44d8db1383909ccc2 Mon Sep 17 00:00:00 2001 From: Aaron Durbin Date: Fri, 22 Jan 2016 15:26:04 -0600 Subject: chromeos/vboot: provide support for x86 memory init verification For x86 systems which resume through the reset vector one needs to ensure the the RW slot taken at resume time matches the one at boot time. The reason is that any assets pulled out of the boot media need to match how the platform previously booted. To do that one needs obtain the hash digest of the chosen slot, and it needs to be saved in a secure place on the normal boot path. On resume one needs to retrieve the hash digest back to compare it with the chosen slot. If they don't match resuming won't be possible. BUG=chrome-os-partner:46049 BRANCH=glados TEST=Suspended and resumed on chell. Also, tested with an EC build which returns a bad hash to ensure that is properly caught. CQ-DEPEND=CL:323460 Change-Id: I90ce26813b67f46913aa4026b42d9490a564bb6c Signed-off-by: Patrick Georgi Original-Commit-Id: 01a42c0ecfc6d60d1d2e5e36a86781d91d5c47a9 Original-Change-Id: I6c6bdce7e06712bc06cc620a3d7a6a6250c59c95 Original-Signed-off-by: Aaron Durbin Original-Reviewed-on: https://chromium-review.googlesource.com/323500 Original-Reviewed-by: Patrick Georgi Original-Reviewed-by: Duncan Laurie Reviewed-on: https://review.coreboot.org/13574 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- src/Kconfig | 9 +++ .../google/chromeos/vboot2/vboot_logic.c | 70 +++++++++++++++++++++- src/vendorcode/google/chromeos/vboot_common.h | 18 ++++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/Kconfig b/src/Kconfig index 3f02843f6f..feefc918c5 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -503,6 +503,15 @@ config HAVE_ACPI_RESUME bool default n +config RESUME_PATH_SAME_AS_BOOT + bool + default y if ARCH_X86 + depends on HAVE_ACPI_RESUME + help + This option indicates that when a system resumes it takes the + same path as a regular boot. e.g. an x86 system runs from the + reset vector at 0xfffffff0 on both resume and warm/cold boot. + config HAVE_HARD_RESET bool default n diff --git a/src/vendorcode/google/chromeos/vboot2/vboot_logic.c b/src/vendorcode/google/chromeos/vboot2/vboot_logic.c index b72de93245..0d08d6adb2 100644 --- a/src/vendorcode/google/chromeos/vboot2/vboot_logic.c +++ b/src/vendorcode/google/chromeos/vboot2/vboot_logic.c @@ -26,6 +26,9 @@ #include "../chromeos.h" #include "misc.h" +/* The max hash size to expect is for SHA512. */ +#define VBOOT_MAX_HASH_SIZE VB2_SHA512_DIGEST_SIZE + #define TODO_BLOCK_SIZE 1024 static int is_slot_a(struct vb2_context *ctx) @@ -111,15 +114,77 @@ int vb2ex_hwcrypto_digest_finalize(uint8_t *digest, uint32_t digest_size) return VB2_ERROR_UNKNOWN; } +static int handle_digest_result(void *slot_hash, size_t slot_hash_sz) +{ + int is_resume; + + /* + * Nothing to do since resuming on the platform doesn't require + * vboot verification again. + */ + if (!IS_ENABLED(CONFIG_RESUME_PATH_SAME_AS_BOOT)) + return 0; + + /* + * Assume that if vboot doesn't start in bootblock verified + * RW memory init code is not employed. i.e. memory init code + * lives in RO CBFS. + */ + if (!IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK)) + return 0; + + is_resume = vboot_platform_is_resuming(); + + if (is_resume > 0) { + uint8_t saved_hash[VBOOT_MAX_HASH_SIZE]; + const size_t saved_hash_sz = sizeof(saved_hash); + + assert(slot_hash_sz == saved_hash_sz); + + printk(BIOS_DEBUG, "Platform is resuming.\n"); + + if (vboot_retrieve_hash(saved_hash, saved_hash_sz)) { + printk(BIOS_ERR, "Couldn't retrieve saved hash.\n"); + return -1; + } + + if (memcmp(saved_hash, slot_hash, slot_hash_sz)) { + printk(BIOS_ERR, "Hash mismatch on resume.\n"); + return -1; + } + } else if (is_resume < 0) + printk(BIOS_ERR, "Unable to determine if platform resuming.\n"); + + printk(BIOS_DEBUG, "Saving vboot hash.\n"); + + /* Always save the hash for the current boot. */ + if (vboot_save_hash(slot_hash, slot_hash_sz)) { + printk(BIOS_ERR, "Error saving vboot hash.\n"); + /* Though this is an error don't report it up since it could + * lead to a reboot loop. The consequence of this is that + * we will most likely fail resuming because of EC issues or + * the hash digest not matching. */ + return 0; + } + + return 0; +} + static int hash_body(struct vb2_context *ctx, struct region_device *fw_main) { uint64_t load_ts; uint32_t expected_size; uint8_t block[TODO_BLOCK_SIZE]; + uint8_t hash_digest[VBOOT_MAX_HASH_SIZE]; + const size_t hash_digest_sz = sizeof(hash_digest); size_t block_size = sizeof(block); size_t offset; int rv; + /* Clear the full digest so that any hash digests less than the + * max have trailing zeros. */ + memset(hash_digest, 0, hash_digest_sz); + /* * Since loading the firmware and calculating its hash is intertwined, * we use this little trick to measure them separately and pretend it @@ -160,12 +225,15 @@ static int hash_body(struct vb2_context *ctx, struct region_device *fw_main) timestamp_add_now(TS_DONE_HASHING); /* Check the result (with RSA signature verification) */ - rv = vb2api_check_hash(ctx); + rv = vb2api_check_hash_get_digest(ctx, hash_digest, hash_digest_sz); if (rv) return rv; timestamp_add_now(TS_END_HASH_BODY); + if (handle_digest_result(hash_digest, hash_digest_sz)) + return VB2_ERROR_UNKNOWN; + return VB2_SUCCESS; } diff --git a/src/vendorcode/google/chromeos/vboot_common.h b/src/vendorcode/google/chromeos/vboot_common.h index fbffc29d54..a658d62ab4 100644 --- a/src/vendorcode/google/chromeos/vboot_common.h +++ b/src/vendorcode/google/chromeos/vboot_common.h @@ -46,6 +46,24 @@ int vboot_recovery_reason(void); void vboot_reboot(void); +/* + * Save the provided hash digest to a secure location to check against in + * the resume path. Returns 0 on success, < 0 on error. + */ +int vboot_save_hash(void *digest, size_t digest_size); + +/* + * Retrieve the previously saved hash digest. Returns 0 on success, + * < 0 on error. + */ +int vboot_retrieve_hash(void *digest, size_t digest_size); + +/* + * Determine if the platform is resuming from suspend. Returns 0 when + * not resuming, > 0 if resuming, and < 0 on error. + */ +int vboot_platform_is_resuming(void); + /* Main logic for verified boot. verstage() is the stage entry point * while the verstage_main() is just the core logic. */ void verstage_main(void); -- cgit v1.2.3