diff options
author | Julius Werner <jwerner@chromium.org> | 2021-08-11 18:19:23 -0700 |
---|---|---|
committer | Felix Held <felix-coreboot@felixheld.de> | 2021-12-03 21:20:35 +0000 |
commit | 7e7cc1a8c9a87e33bd772e8526734c7a82ec2db7 (patch) | |
tree | c091beebe657af44f3800402645fa8d257eaab8e | |
parent | c75d846971c2fd96c989a7f8a14011bb70866409 (diff) |
cbfs | tspi: Join hash calculation for verification and measurement
This patch moves the CBFS file measurement when CONFIG_TPM_MEASURED_BOOT
is enabled from the lookup step into the code where a file is actually
loaded or mapped from flash. This has the advantage that CBFS routines
which just look up a file to inspect its metadata (e.g. cbfs_get_size())
do not cause the file to be measured twice. It also removes the existing
inefficiency that files are loaded twice when measurement is enabled
(once to measure and then again when they are used). When CBFS
verification is enabled and uses the same hash algorithm as the TPM, we
are even able to only hash the file a single time and use the result for
both purposes.
Signed-off-by: Julius Werner <jwerner@chromium.org>
Change-Id: I70d7066c6768195077f083c7ffdfa30d9182b2b7
Reviewed-on: https://review.coreboot.org/c/coreboot/+/59681
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Raul Rangel <rrangel@chromium.org>
-rw-r--r-- | src/lib/cbfs.c | 47 | ||||
-rw-r--r-- | src/security/tpm/tspi.h | 2 | ||||
-rw-r--r-- | src/security/tpm/tspi/crtm.c | 57 | ||||
-rw-r--r-- | src/security/tpm/tspi/crtm.h | 16 | ||||
-rw-r--r-- | src/security/tpm/tspi/tspi.c | 34 |
5 files changed, 59 insertions, 97 deletions
diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index b4b6eb1324..0b5891378d 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -81,10 +81,6 @@ cb_err_t _cbfs_boot_lookup(const char *name, bool force_ro, if (rdev_chain(rdev, &cbd->rdev, data_offset, be32toh(mdata->h.len))) return CB_ERR; - if (tspi_measure_cbfs_hook(rdev, name, be32toh(mdata->h.type))) { - printk(BIOS_ERR, "CBFS ERROR: error when measuring '%s'\n", name); - } - return CB_SUCCESS; } @@ -134,9 +130,8 @@ int cbfs_locate_file_in_region(struct cbfsf *fh, const char *region_name, type = &dummy_type; ret = cbfs_locate(fh, &rdev, name, type); - if (!ret) - if (tspi_measure_cbfs_hook(&rdev, name, *type)) - LOG("error measuring %s in region %s\n", name, region_name); + /* No more measuring here, this function will be removed next patch. */ + return ret; } @@ -193,19 +188,36 @@ static inline bool cbfs_lzma_enabled(void) static bool cbfs_file_hash_mismatch(const void *buffer, size_t size, const union cbfs_mdata *mdata, bool skip_verification) { - /* Avoid linking hash functions when verification is disabled. */ - if (!CONFIG(CBFS_VERIFICATION) || skip_verification) + /* Avoid linking hash functions when verification and measurement are disabled. */ + if (!CONFIG(CBFS_VERIFICATION) && !CONFIG(TPM_MEASURED_BOOT)) return false; - const struct vb2_hash *hash = cbfs_file_hash(mdata); - if (!hash) { - ERROR("'%s' does not have a file hash!\n", mdata->h.filename); - return true; + const struct vb2_hash *hash = NULL; + + if (CONFIG(CBFS_VERIFICATION) && !skip_verification) { + hash = cbfs_file_hash(mdata); + if (!hash) { + ERROR("'%s' does not have a file hash!\n", mdata->h.filename); + return true; + } + if (vb2_hash_verify(buffer, size, hash) != VB2_SUCCESS) { + ERROR("'%s' file hash mismatch!\n", mdata->h.filename); + return true; + } } - if (vb2_hash_verify(buffer, size, hash) != VB2_SUCCESS) { - ERROR("'%s' file hash mismatch!\n", mdata->h.filename); - return true; + if (CONFIG(TPM_MEASURED_BOOT) && !ENV_SMM) { + struct vb2_hash calculated_hash; + + /* No need to re-hash file if we already have it from verification. */ + if (!hash || hash->algo != TPM_MEASURE_ALGO) { + vb2_hash_calculate(buffer, size, TPM_MEASURE_ALGO, &calculated_hash); + hash = &calculated_hash; + } + + if (tspi_cbfs_measurement(mdata->h.filename, be32toh(mdata->h.type), hash)) + ERROR("failed to measure '%s' into TCPA log\n", mdata->h.filename); + /* We intentionally continue to boot on measurement errors. */ } return false; @@ -528,9 +540,6 @@ void *_cbfs_unverified_area_alloc(const char *area, const char *name, if (rdev_chain(&file_rdev, &area_rdev, data_offset, be32toh(mdata.h.len))) return NULL; - if (tspi_measure_cbfs_hook(&file_rdev, name, be32toh(mdata.h.type))) - ERROR("error measuring '%s' in '%s'\n", name, area); - return do_alloc(&mdata, &file_rdev, allocator, arg, size_out, true); } diff --git a/src/security/tpm/tspi.h b/src/security/tpm/tspi.h index ed642c33b5..7157b4d730 100644 --- a/src/security/tpm/tspi.h +++ b/src/security/tpm/tspi.h @@ -51,7 +51,7 @@ void tcpa_log_dump(void *unused); * @return TPM_SUCCESS on success. If not a tpm error is returned */ uint32_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo, - uint8_t *digest, size_t digest_len, + const uint8_t *digest, size_t digest_len, const char *name); /** diff --git a/src/security/tpm/tspi/crtm.c b/src/security/tpm/tspi/crtm.c index b64bbbf6c6..8cd0793779 100644 --- a/src/security/tpm/tspi/crtm.c +++ b/src/security/tpm/tspi/crtm.c @@ -6,37 +6,6 @@ #include "crtm.h" #include <string.h> -/* - * This function sets the TCPA log namespace - * for the cbfs file (region) lookup. - */ -static int create_tcpa_metadata(const struct region_device *rdev, - const char *cbfs_name, char log_string[TCPA_PCR_HASH_NAME]) -{ - int i; - struct region_device fmap; - static const char *const fmap_cbfs_names[] = { - "COREBOOT", - "FW_MAIN_A", - "FW_MAIN_B", - "RW_LEGACY" - }; - - for (i = 0; i < ARRAY_SIZE(fmap_cbfs_names); i++) { - if (fmap_locate_area_as_rdev(fmap_cbfs_names[i], &fmap) == 0) { - if (region_is_subregion(region_device_region(&fmap), - region_device_region(rdev))) { - snprintf(log_string, TCPA_PCR_HASH_NAME, - "FMAP: %s CBFS: %s", - fmap_cbfs_names[i], cbfs_name); - return 0; - } - } - } - - return -1; -} - static int tcpa_log_initialized; static inline int tcpa_log_available(void) { @@ -64,8 +33,6 @@ static inline int tcpa_log_available(void) */ static uint32_t tspi_init_crtm(void) { - struct prog bootblock = PROG_INIT(PROG_BOOTBLOCK, "bootblock"); - /* Initialize TCPA PRERAM log. */ if (!tcpa_log_available()) { tcpa_preram_log_clear(); @@ -87,7 +54,6 @@ static uint32_t tspi_init_crtm(void) } /* measure bootblock from RO */ - struct cbfsf bootblock_data; struct region_device bootblock_fmap; if (fmap_locate_area_as_rdev("BOOTBLOCK", &bootblock_fmap) == 0) { if (tpm_measure_region(&bootblock_fmap, @@ -95,16 +61,16 @@ static uint32_t tspi_init_crtm(void) "FMAP: BOOTBLOCK")) return VB2_ERROR_UNKNOWN; } else { - if (cbfs_boot_locate(&bootblock_data, - prog_name(&bootblock), NULL)) { - /* - * measurement is done in - * tspi_measure_cbfs_hook() - */ + /* Mapping measures the file. We know we can safely map here because + bootblock-as-a-file is only used on x86, where we don't need cache to map. */ + enum cbfs_type type = CBFS_TYPE_BOOTBLOCK; + void *mapping = cbfs_ro_type_map("bootblock", NULL, &type); + if (!mapping) { printk(BIOS_INFO, "TSPI: Couldn't measure bootblock into CRTM!\n"); return VB2_ERROR_UNKNOWN; } + cbfs_unmap(mapping); } return VB2_SUCCESS; @@ -129,8 +95,7 @@ static bool is_runtime_data(const char *name) return !strcmp(allowlist, name); } -uint32_t tspi_measure_cbfs_hook(const struct region_device *rdev, const char *name, - uint32_t cbfs_type) +uint32_t tspi_cbfs_measurement(const char *name, uint32_t type, const struct vb2_hash *hash) { uint32_t pcr_index; char tcpa_metadata[TCPA_PCR_HASH_NAME]; @@ -144,7 +109,7 @@ uint32_t tspi_measure_cbfs_hook(const struct region_device *rdev, const char *na printk(BIOS_DEBUG, "CRTM initialized.\n"); } - switch (cbfs_type) { + switch (type) { case CBFS_TYPE_MRC_CACHE: pcr_index = TPM_RUNTIME_DATA_PCR; break; @@ -166,10 +131,10 @@ uint32_t tspi_measure_cbfs_hook(const struct region_device *rdev, const char *na break; } - if (create_tcpa_metadata(rdev, name, tcpa_metadata) < 0) - return VB2_ERROR_UNKNOWN; + snprintf(tcpa_metadata, TCPA_PCR_HASH_NAME, "CBFS: %s", name); - return tpm_measure_region(rdev, pcr_index, tcpa_metadata); + return tpm_extend_pcr(pcr_index, hash->algo, hash->raw, vb2_digest_size(hash->algo), + tcpa_metadata); } int tspi_measure_cache_to_pcr(void) diff --git a/src/security/tpm/tspi/crtm.h b/src/security/tpm/tspi/crtm.h index 011fa26ad7..8ebb661130 100644 --- a/src/security/tpm/tspi/crtm.h +++ b/src/security/tpm/tspi/crtm.h @@ -7,6 +7,7 @@ #include <program_loading.h> #include <security/tpm/tspi.h> #include <types.h> +#include <vb2_sha.h> /* CRTM */ #define TPM_CRTM_PCR 2 @@ -16,21 +17,16 @@ */ #define TPM_RUNTIME_DATA_PCR 3 +#define TPM_MEASURE_ALGO (CONFIG(TPM1) ? VB2_HASH_SHA1 : VB2_HASH_SHA256) + /** * Measure digests cached in TCPA log entries into PCRs */ int tspi_measure_cache_to_pcr(void); -#if !ENV_SMM && CONFIG(TPM_MEASURED_BOOT) -/* - * Measures cbfs data via hook (cbfs) - * rdev covers the file data (not metadata) - * return 0 if successful, else an error +/** + * Extend a measurement hash taken for a CBFS file into the appropriate PCR. */ -uint32_t tspi_measure_cbfs_hook(const struct region_device *rdev, - const char *name, uint32_t cbfs_type); -#else -#define tspi_measure_cbfs_hook(rdev, name, cbfs_type) 0 -#endif +uint32_t tspi_cbfs_measurement(const char *name, uint32_t type, const struct vb2_hash *hash); #endif /* __SECURITY_TSPI_CRTM_H__ */ diff --git a/src/security/tpm/tspi/tspi.c b/src/security/tpm/tspi/tspi.c index b1bea4160d..e2d6d1e56e 100644 --- a/src/security/tpm/tspi/tspi.c +++ b/src/security/tpm/tspi/tspi.c @@ -220,7 +220,7 @@ uint32_t tpm_clear_and_reenable(void) } uint32_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo, - uint8_t *digest, size_t digest_len, const char *name) + const uint8_t *digest, size_t digest_len, const char *name) { uint32_t result; @@ -234,16 +234,22 @@ uint32_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo, return result; } - printk(BIOS_DEBUG, "TPM: Extending digest for %s into PCR %d\n", name, pcr); + printk(BIOS_DEBUG, "TPM: Extending digest for `%s` into PCR %d\n", name, pcr); result = tlcl_extend(pcr, digest, NULL); - if (result != TPM_SUCCESS) + if (result != TPM_SUCCESS) { + printk(BIOS_ERR, "TPM: Extending hash for `%s` into PCR %d failed.\n", + name, pcr); return result; + } } if (CONFIG(TPM_MEASURED_BOOT)) tcpa_log_add_table_entry(name, pcr, digest_algo, digest, digest_len); + printk(BIOS_DEBUG, "TPM: Digest of `%s` to PCR %d %s\n", + name, pcr, tspi_tpm_is_setup() ? "measured" : "logged"); + return TPM_SUCCESS; } @@ -253,23 +259,16 @@ uint32_t tpm_measure_region(const struct region_device *rdev, uint8_t pcr, { uint8_t digest[TPM_PCR_MAX_LEN], digest_len; uint8_t buf[HASH_DATA_CHUNK_SIZE]; - uint32_t result, offset; + uint32_t offset; size_t len; struct vb2_digest_context ctx; - enum vb2_hash_algorithm hash_alg; if (!rdev || !rname) return TPM_E_INVALID_ARG; - if (CONFIG(TPM1)) { - hash_alg = VB2_HASH_SHA1; - } else { /* CONFIG_TPM2 */ - hash_alg = VB2_HASH_SHA256; - } - - digest_len = vb2_digest_size(hash_alg); + digest_len = vb2_digest_size(TPM_MEASURE_ALGO); assert(digest_len <= sizeof(digest)); - if (vb2_digest_init(&ctx, hash_alg)) { + if (vb2_digest_init(&ctx, TPM_MEASURE_ALGO)) { printk(BIOS_ERR, "TPM: Error initializing hash.\n"); return TPM_E_HASH_ERROR; } @@ -294,13 +293,6 @@ uint32_t tpm_measure_region(const struct region_device *rdev, uint8_t pcr, printk(BIOS_ERR, "TPM: Error finalizing hash.\n"); return TPM_E_HASH_ERROR; } - result = tpm_extend_pcr(pcr, hash_alg, digest, digest_len, rname); - if (result != TPM_SUCCESS) { - printk(BIOS_ERR, "TPM: Extending hash into PCR failed.\n"); - return result; - } - printk(BIOS_DEBUG, "TPM: Digest of %s to PCR %d %s\n", - rname, pcr, tspi_tpm_is_setup() ? "measured" : "logged"); - return TPM_SUCCESS; + return tpm_extend_pcr(pcr, TPM_MEASURE_ALGO, digest, digest_len, rname); } #endif /* VBOOT_LIB */ |