diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/Kconfig.cbfs_verification | 57 | ||||
-rw-r--r-- | src/lib/Makefile.inc | 1 | ||||
-rw-r--r-- | src/lib/cbfs.c | 53 | ||||
-rw-r--r-- | src/lib/fmap.c | 27 | ||||
-rw-r--r-- | src/lib/metadata_hash.c | 27 | ||||
-rw-r--r-- | src/lib/program.ld | 1 |
6 files changed, 152 insertions, 14 deletions
diff --git a/src/lib/Kconfig.cbfs_verification b/src/lib/Kconfig.cbfs_verification new file mode 100644 index 0000000000..34993458cd --- /dev/null +++ b/src/lib/Kconfig.cbfs_verification @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later +# +# This file is part of the coreboot project. +# +# This file is sourced from src/security/Kconfig for menuconfig convenience. + +#menu "CBFS verification" # TODO: enable once it works + +config CBFS_VERIFICATION + bool # TODO: make user selectable once it works + depends on !COMPRESS_BOOTBLOCK # TODO: figure out decompressor anchor + depends on !VBOOT_STARTS_BEFORE_BOOTBLOCK # this is gonna get tricky... + select VBOOT_LIB + help + Work in progress. Do not use (yet). + +config TOCTOU_SAFETY + bool + depends on CBFS_VERIFICATION + depends on !NO_FMAP_CACHE + depends on !NO_CBFS_MCACHE + help + Work in progress. Not actually TOCTOU safe yet. Do not use. + + Design idea here is that mcache overflows in this mode are only legal + for the RW CBFS, because it's relatively easy to retrieve the RW + metadata hash from persistent vboot context at any time, but the RO + metadata hash is lost after the bootblock is unloaded. This avoids the + need to carry yet another piece forward through the stages. Mcache + overflows are mostly a concern for RW updates (if an update adds more + files than originally planned for), for the RO section it should + always be possible to dimension the mcache correctly beforehand, so + this should be an acceptable limitation. + +config CBFS_HASH_ALGO + int + default 1 if CBFS_HASH_SHA1 + default 2 if CBFS_HASH_SHA256 + default 3 if CBFS_HASH_SHA512 + +choice + prompt "--> hash type" + depends on CBFS_VERIFICATION + default CBFS_HASH_SHA256 + +config CBFS_HASH_SHA1 + bool "SHA-1" + +config CBFS_HASH_SHA256 + bool "SHA-256" + +config CBFS_HASH_SHA512 + bool "SHA-512" + +endchoice + +#endmenu diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index 6cff03dc63..9e601eb055 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -47,6 +47,7 @@ bootblock-y += prog_ops.c bootblock-y += cbfs.c bootblock-$(CONFIG_GENERIC_GPIO_LIB) += gpio.c bootblock-y += libgcc.c +bootblock-$(CONFIG_CBFS_VERIFICATION) += metadata_hash.c bootblock-$(CONFIG_GENERIC_UDELAY) += timer.c bootblock-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index beab74ec4d..5df1d8bd85 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -10,6 +10,7 @@ #include <console/console.h> #include <fmap.h> #include <lib.h> +#include <metadata_hash.h> #include <security/tpm/tspi/crtm.h> #include <security/vboot/vboot_common.h> #include <stdlib.h> @@ -29,8 +30,21 @@ cb_err_t cbfs_boot_lookup(const char *name, bool force_ro, if (!CONFIG(NO_CBFS_MCACHE) && !ENV_SMM) err = cbfs_mcache_lookup(cbd->mcache, cbd->mcache_size, name, mdata, &data_offset); - if (err == CB_CBFS_CACHE_FULL) - err = cbfs_lookup(&cbd->rdev, name, mdata, &data_offset, NULL); + if (err == CB_CBFS_CACHE_FULL) { + struct vb2_hash *metadata_hash = NULL; + if (CONFIG(TOCTOU_SAFETY)) { + if (ENV_SMM) /* Cannot provide TOCTOU safety for SMM */ + dead_code(); + /* We can only reach this for the RW CBFS -- an mcache + overflow in the RO CBFS would have been caught when + building the mcache in cbfs_get_boot_device(). + (Note that TOCTOU_SAFETY implies !NO_CBFS_MCACHE.) */ + assert(cbd == vboot_get_cbfs_boot_device()); + /* TODO: set metadata_hash to RW metadata hash here. */ + } + err = cbfs_lookup(&cbd->rdev, name, mdata, &data_offset, + metadata_hash); + } if (CONFIG(VBOOT_ENABLE_CBFS_FALLBACK) && !force_ro && err == CB_CBFS_NOT_FOUND) { @@ -405,6 +419,26 @@ void cbfs_boot_device_find_mcache(struct cbfs_boot_device *cbd, uint32_t id) } } +cb_err_t cbfs_init_boot_device(const struct cbfs_boot_device *cbd, + struct vb2_hash *metadata_hash) +{ + /* If we have an mcache, mcache_build() will also check mdata hash. */ + if (!CONFIG(NO_CBFS_MCACHE) && !ENV_SMM && cbd->mcache_size > 0) + return cbfs_mcache_build(&cbd->rdev, cbd->mcache, + cbd->mcache_size, metadata_hash); + + /* No mcache and no verification means we have nothing special to do. */ + if (!CONFIG(CBFS_VERIFICATION) || !metadata_hash) + return CB_SUCCESS; + + /* Verification only: use cbfs_walk() without a walker() function to + just run through the CBFS once, will return NOT_FOUND by default. */ + cb_err_t err = cbfs_walk(&cbd->rdev, NULL, NULL, metadata_hash, 0); + if (err == CB_CBFS_NOT_FOUND) + err = CB_SUCCESS; + return err; +} + const struct cbfs_boot_device *cbfs_get_boot_device(bool force_ro) { static struct cbfs_boot_device ro; @@ -426,15 +460,18 @@ const struct cbfs_boot_device *cbfs_get_boot_device(bool force_ro) return &ro; if (fmap_locate_area_as_rdev("COREBOOT", &ro.rdev)) - return NULL; + die("Cannot locate primary CBFS"); cbfs_boot_device_find_mcache(&ro, CBMEM_ID_CBFS_RO_MCACHE); - if (ENV_INITIAL_STAGE && !CONFIG(NO_CBFS_MCACHE)) { - cb_err_t err = cbfs_mcache_build(&ro.rdev, ro.mcache, - ro.mcache_size, NULL); - if (err && err != CB_CBFS_CACHE_FULL) - die("Failed to build RO mcache"); + if (ENV_INITIAL_STAGE) { + cb_err_t err = cbfs_init_boot_device(&ro, metadata_hash_get()); + if (err == CB_CBFS_HASH_MISMATCH) + die("RO CBFS metadata hash verification failure"); + else if (CONFIG(TOCTOU_SAFETY) && err == CB_CBFS_CACHE_FULL) + die("RO mcache overflow breaks TOCTOU safety!\n"); + else if (err && err != CB_CBFS_CACHE_FULL) + die("RO CBFS initialization error: %d", err); } return &ro; diff --git a/src/lib/fmap.c b/src/lib/fmap.c index 377123afdc..2abe138cdd 100644 --- a/src/lib/fmap.c +++ b/src/lib/fmap.c @@ -4,6 +4,7 @@ #include <cbmem.h> #include <console/console.h> #include <fmap.h> +#include <metadata_hash.h> #include <stddef.h> #include <string.h> #include <symbols.h> @@ -27,9 +28,20 @@ uint64_t get_fmap_flash_offset(void) return FMAP_OFFSET; } -static int check_signature(const struct fmap *fmap) +static int verify_fmap(const struct fmap *fmap) { - return memcmp(fmap->signature, FMAP_SIGNATURE, sizeof(fmap->signature)); + if (memcmp(fmap->signature, FMAP_SIGNATURE, sizeof(fmap->signature))) + return -1; + + static bool done = false; + if (!CONFIG(CBFS_VERIFICATION) || !ENV_INITIAL_STAGE || done) + return 0; /* Only need to check hash in first stage. */ + + if (metadata_hash_verify_fmap(fmap, FMAP_SIZE) != VB2_SUCCESS) + return -1; + + done = true; + return 0; } static void report(const struct fmap *fmap) @@ -63,10 +75,12 @@ static void setup_preram_cache(struct mem_region_device *cache_mrdev) if (!(ENV_INITIAL_STAGE)) { /* NOTE: This assumes that the first stage will make at least one FMAP access (usually from finding CBFS). */ - if (!check_signature(fmap)) + if (!verify_fmap(fmap)) goto register_cache; printk(BIOS_ERR, "ERROR: FMAP cache corrupted?!\n"); + if (CONFIG(TOCTOU_SAFETY)) + die("TOCTOU safety relies on FMAP cache"); } /* In case we fail below, make sure the cache is invalid. */ @@ -80,7 +94,7 @@ static void setup_preram_cache(struct mem_region_device *cache_mrdev) /* memlayout statically guarantees that the FMAP_CACHE is big enough. */ if (rdev_readat(boot_rdev, fmap, FMAP_OFFSET, FMAP_SIZE) != FMAP_SIZE) return; - if (check_signature(fmap)) + if (verify_fmap(fmap)) return; report(fmap); @@ -111,8 +125,9 @@ static int find_fmap_directory(struct region_device *fmrd) if (fmap == NULL) return -1; - if (check_signature(fmap)) { - printk(BIOS_DEBUG, "No FMAP found at %zx offset.\n", offset); + if (verify_fmap(fmap)) { + printk(BIOS_ERR, "FMAP missing or corrupted at offset 0x%zx!\n", + offset); rdev_munmap(boot, fmap); return -1; } diff --git a/src/lib/metadata_hash.c b/src/lib/metadata_hash.c new file mode 100644 index 0000000000..f296cf58a5 --- /dev/null +++ b/src/lib/metadata_hash.c @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* This file is part of the coreboot project. */ + +#include <assert.h> +#include <cbmem.h> +#include <metadata_hash.h> +#include <symbols.h> + +__attribute__((used, section(".metadata_hash_anchor"))) +static struct metadata_hash_anchor metadata_hash_anchor = { + /* This is the only place in all of coreboot where we actually need to use this. */ + .magic = DO_NOT_USE_METADATA_HASH_ANCHOR_MAGIC_DO_NOT_USE, + .cbfs_hash = { .algo = CONFIG_CBFS_HASH_ALGO } +}; + +struct vb2_hash *metadata_hash_get(void) +{ + return &metadata_hash_anchor.cbfs_hash; +} + +vb2_error_t metadata_hash_verify_fmap(const void *fmap_buffer, size_t fmap_size) +{ + struct vb2_hash hash = { .algo = metadata_hash_anchor.cbfs_hash.algo }; + memcpy(hash.raw, metadata_hash_anchor_fmap_hash(&metadata_hash_anchor), + vb2_digest_size(hash.algo)); + return vb2_hash_verify(fmap_buffer, fmap_size, &hash); +} diff --git a/src/lib/program.ld b/src/lib/program.ld index 3b6aa2ecba..94ba409ced 100644 --- a/src/lib/program.ld +++ b/src/lib/program.ld @@ -18,6 +18,7 @@ #if !ENV_X86 && (ENV_DECOMPRESSOR || ENV_BOOTBLOCK && !CONFIG(COMPRESS_BOOTBLOCK)) KEEP(*(.id)); #endif + KEEP(*(.metadata_hash_anchor)); *(.text); *(.text.*); |