summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Kconfig.cbfs_verification57
-rw-r--r--src/lib/Makefile.inc1
-rw-r--r--src/lib/cbfs.c53
-rw-r--r--src/lib/fmap.c27
-rw-r--r--src/lib/metadata_hash.c27
-rw-r--r--src/lib/program.ld1
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.*);