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>
This commit is contained in:
parent
c75d846971
commit
7e7cc1a8c9
@ -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)))
|
if (rdev_chain(rdev, &cbd->rdev, data_offset, be32toh(mdata->h.len)))
|
||||||
return CB_ERR;
|
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;
|
return CB_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,9 +130,8 @@ int cbfs_locate_file_in_region(struct cbfsf *fh, const char *region_name,
|
|||||||
type = &dummy_type;
|
type = &dummy_type;
|
||||||
|
|
||||||
ret = cbfs_locate(fh, &rdev, name, type);
|
ret = cbfs_locate(fh, &rdev, name, type);
|
||||||
if (!ret)
|
/* No more measuring here, this function will be removed next patch. */
|
||||||
if (tspi_measure_cbfs_hook(&rdev, name, *type))
|
|
||||||
LOG("error measuring %s in region %s\n", name, region_name);
|
|
||||||
return ret;
|
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,
|
static bool cbfs_file_hash_mismatch(const void *buffer, size_t size,
|
||||||
const union cbfs_mdata *mdata, bool skip_verification)
|
const union cbfs_mdata *mdata, bool skip_verification)
|
||||||
{
|
{
|
||||||
/* Avoid linking hash functions when verification is disabled. */
|
/* Avoid linking hash functions when verification and measurement are disabled. */
|
||||||
if (!CONFIG(CBFS_VERIFICATION) || skip_verification)
|
if (!CONFIG(CBFS_VERIFICATION) && !CONFIG(TPM_MEASURED_BOOT))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const struct vb2_hash *hash = cbfs_file_hash(mdata);
|
const struct vb2_hash *hash = NULL;
|
||||||
if (!hash) {
|
|
||||||
ERROR("'%s' does not have a file hash!\n", mdata->h.filename);
|
if (CONFIG(CBFS_VERIFICATION) && !skip_verification) {
|
||||||
return true;
|
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) {
|
if (CONFIG(TPM_MEASURED_BOOT) && !ENV_SMM) {
|
||||||
ERROR("'%s' file hash mismatch!\n", mdata->h.filename);
|
struct vb2_hash calculated_hash;
|
||||||
return true;
|
|
||||||
|
/* 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;
|
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)))
|
if (rdev_chain(&file_rdev, &area_rdev, data_offset, be32toh(mdata.h.len)))
|
||||||
return NULL;
|
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);
|
return do_alloc(&mdata, &file_rdev, allocator, arg, size_out, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ void tcpa_log_dump(void *unused);
|
|||||||
* @return TPM_SUCCESS on success. If not a tpm error is returned
|
* @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,
|
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);
|
const char *name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,37 +6,6 @@
|
|||||||
#include "crtm.h"
|
#include "crtm.h"
|
||||||
#include <string.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 int tcpa_log_initialized;
|
||||||
static inline int tcpa_log_available(void)
|
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)
|
static uint32_t tspi_init_crtm(void)
|
||||||
{
|
{
|
||||||
struct prog bootblock = PROG_INIT(PROG_BOOTBLOCK, "bootblock");
|
|
||||||
|
|
||||||
/* Initialize TCPA PRERAM log. */
|
/* Initialize TCPA PRERAM log. */
|
||||||
if (!tcpa_log_available()) {
|
if (!tcpa_log_available()) {
|
||||||
tcpa_preram_log_clear();
|
tcpa_preram_log_clear();
|
||||||
@ -87,7 +54,6 @@ static uint32_t tspi_init_crtm(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* measure bootblock from RO */
|
/* measure bootblock from RO */
|
||||||
struct cbfsf bootblock_data;
|
|
||||||
struct region_device bootblock_fmap;
|
struct region_device bootblock_fmap;
|
||||||
if (fmap_locate_area_as_rdev("BOOTBLOCK", &bootblock_fmap) == 0) {
|
if (fmap_locate_area_as_rdev("BOOTBLOCK", &bootblock_fmap) == 0) {
|
||||||
if (tpm_measure_region(&bootblock_fmap,
|
if (tpm_measure_region(&bootblock_fmap,
|
||||||
@ -95,16 +61,16 @@ static uint32_t tspi_init_crtm(void)
|
|||||||
"FMAP: BOOTBLOCK"))
|
"FMAP: BOOTBLOCK"))
|
||||||
return VB2_ERROR_UNKNOWN;
|
return VB2_ERROR_UNKNOWN;
|
||||||
} else {
|
} else {
|
||||||
if (cbfs_boot_locate(&bootblock_data,
|
/* Mapping measures the file. We know we can safely map here because
|
||||||
prog_name(&bootblock), NULL)) {
|
bootblock-as-a-file is only used on x86, where we don't need cache to map. */
|
||||||
/*
|
enum cbfs_type type = CBFS_TYPE_BOOTBLOCK;
|
||||||
* measurement is done in
|
void *mapping = cbfs_ro_type_map("bootblock", NULL, &type);
|
||||||
* tspi_measure_cbfs_hook()
|
if (!mapping) {
|
||||||
*/
|
|
||||||
printk(BIOS_INFO,
|
printk(BIOS_INFO,
|
||||||
"TSPI: Couldn't measure bootblock into CRTM!\n");
|
"TSPI: Couldn't measure bootblock into CRTM!\n");
|
||||||
return VB2_ERROR_UNKNOWN;
|
return VB2_ERROR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
cbfs_unmap(mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
return VB2_SUCCESS;
|
return VB2_SUCCESS;
|
||||||
@ -129,8 +95,7 @@ static bool is_runtime_data(const char *name)
|
|||||||
return !strcmp(allowlist, name);
|
return !strcmp(allowlist, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t tspi_measure_cbfs_hook(const struct region_device *rdev, const char *name,
|
uint32_t tspi_cbfs_measurement(const char *name, uint32_t type, const struct vb2_hash *hash)
|
||||||
uint32_t cbfs_type)
|
|
||||||
{
|
{
|
||||||
uint32_t pcr_index;
|
uint32_t pcr_index;
|
||||||
char tcpa_metadata[TCPA_PCR_HASH_NAME];
|
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");
|
printk(BIOS_DEBUG, "CRTM initialized.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cbfs_type) {
|
switch (type) {
|
||||||
case CBFS_TYPE_MRC_CACHE:
|
case CBFS_TYPE_MRC_CACHE:
|
||||||
pcr_index = TPM_RUNTIME_DATA_PCR;
|
pcr_index = TPM_RUNTIME_DATA_PCR;
|
||||||
break;
|
break;
|
||||||
@ -166,10 +131,10 @@ uint32_t tspi_measure_cbfs_hook(const struct region_device *rdev, const char *na
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (create_tcpa_metadata(rdev, name, tcpa_metadata) < 0)
|
snprintf(tcpa_metadata, TCPA_PCR_HASH_NAME, "CBFS: %s", name);
|
||||||
return VB2_ERROR_UNKNOWN;
|
|
||||||
|
|
||||||
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)
|
int tspi_measure_cache_to_pcr(void)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <program_loading.h>
|
#include <program_loading.h>
|
||||||
#include <security/tpm/tspi.h>
|
#include <security/tpm/tspi.h>
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
|
#include <vb2_sha.h>
|
||||||
|
|
||||||
/* CRTM */
|
/* CRTM */
|
||||||
#define TPM_CRTM_PCR 2
|
#define TPM_CRTM_PCR 2
|
||||||
@ -16,21 +17,16 @@
|
|||||||
*/
|
*/
|
||||||
#define TPM_RUNTIME_DATA_PCR 3
|
#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
|
* Measure digests cached in TCPA log entries into PCRs
|
||||||
*/
|
*/
|
||||||
int tspi_measure_cache_to_pcr(void);
|
int tspi_measure_cache_to_pcr(void);
|
||||||
|
|
||||||
#if !ENV_SMM && CONFIG(TPM_MEASURED_BOOT)
|
/**
|
||||||
/*
|
* Extend a measurement hash taken for a CBFS file into the appropriate PCR.
|
||||||
* Measures cbfs data via hook (cbfs)
|
|
||||||
* rdev covers the file data (not metadata)
|
|
||||||
* return 0 if successful, else an error
|
|
||||||
*/
|
*/
|
||||||
uint32_t tspi_measure_cbfs_hook(const struct region_device *rdev,
|
uint32_t tspi_cbfs_measurement(const char *name, uint32_t type, const struct vb2_hash *hash);
|
||||||
const char *name, uint32_t cbfs_type);
|
|
||||||
#else
|
|
||||||
#define tspi_measure_cbfs_hook(rdev, name, cbfs_type) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __SECURITY_TSPI_CRTM_H__ */
|
#endif /* __SECURITY_TSPI_CRTM_H__ */
|
||||||
|
@ -220,7 +220,7 @@ uint32_t tpm_clear_and_reenable(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo,
|
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;
|
uint32_t result;
|
||||||
|
|
||||||
@ -234,16 +234,22 @@ uint32_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo,
|
|||||||
return result;
|
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);
|
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;
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONFIG(TPM_MEASURED_BOOT))
|
if (CONFIG(TPM_MEASURED_BOOT))
|
||||||
tcpa_log_add_table_entry(name, pcr, digest_algo,
|
tcpa_log_add_table_entry(name, pcr, digest_algo,
|
||||||
digest, digest_len);
|
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;
|
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 digest[TPM_PCR_MAX_LEN], digest_len;
|
||||||
uint8_t buf[HASH_DATA_CHUNK_SIZE];
|
uint8_t buf[HASH_DATA_CHUNK_SIZE];
|
||||||
uint32_t result, offset;
|
uint32_t offset;
|
||||||
size_t len;
|
size_t len;
|
||||||
struct vb2_digest_context ctx;
|
struct vb2_digest_context ctx;
|
||||||
enum vb2_hash_algorithm hash_alg;
|
|
||||||
|
|
||||||
if (!rdev || !rname)
|
if (!rdev || !rname)
|
||||||
return TPM_E_INVALID_ARG;
|
return TPM_E_INVALID_ARG;
|
||||||
|
|
||||||
if (CONFIG(TPM1)) {
|
digest_len = vb2_digest_size(TPM_MEASURE_ALGO);
|
||||||
hash_alg = VB2_HASH_SHA1;
|
|
||||||
} else { /* CONFIG_TPM2 */
|
|
||||||
hash_alg = VB2_HASH_SHA256;
|
|
||||||
}
|
|
||||||
|
|
||||||
digest_len = vb2_digest_size(hash_alg);
|
|
||||||
assert(digest_len <= sizeof(digest));
|
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");
|
printk(BIOS_ERR, "TPM: Error initializing hash.\n");
|
||||||
return TPM_E_HASH_ERROR;
|
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");
|
printk(BIOS_ERR, "TPM: Error finalizing hash.\n");
|
||||||
return TPM_E_HASH_ERROR;
|
return TPM_E_HASH_ERROR;
|
||||||
}
|
}
|
||||||
result = tpm_extend_pcr(pcr, hash_alg, digest, digest_len, rname);
|
return tpm_extend_pcr(pcr, TPM_MEASURE_ALGO, 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;
|
|
||||||
}
|
}
|
||||||
#endif /* VBOOT_LIB */
|
#endif /* VBOOT_LIB */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user