util/cbmem: add parsing of TPM logs per specs

CBMEM can contain log in different forms (at most one is present):
 - coreboot-specific format (CBMEM_ID_TPM_CB_LOG exported as
   LB_TAG_TPM_CB_LOG)
 - TPM1.2 format (CBMEM_ID_TCPA_TCG_LOG)
 - TPM2 format (CBMEM_ID_TPM2_TCG_LOG)

The last two follow specifications by Trusted Computing Group, but until
now cbmem couldn't print them.  These formats were added not so long ago
in:
 - commit 4191dbf0c9a5 ("security/tpm: add TPM log format as per 1.2
   spec")
 - commit 53db677586e3 ("security/tpm: add TPM log format as per 2.0
   spec")

These changes make cbmem utility check for existence of TPM1.2/TPM2 logs
in CBMEM and add code necessary for parsing and printing of their
entries.

TEST=`cbmem -L` for CONFIG_TPM1=y case
TCPA log:
	Specification: 1.21
	Platform class: PC Client
TCPA log entry 1:
	PCR: 2
	Event type: Action
	Digest: 5622416ea417186aa1ac32b32c527ac09009fb5e
	Event data: FMAP: FMAP

TEST=`cbmem -L` for CONFIG_TPM2=y case
TPM2 log:
	Specification: 2.00
	Platform class: PC Client
TPM2 log entry 1:
	PCR: 2
	Event type: Action
	Digests:
		 SHA256: 68d27f08cb261463a6d004524333ac5db1a3c2166721785a6061327b6538657c
	Event data: FMAP: FMAP

Change-Id: Ib76dc7dec56dd1789a219539a1ac05a958f47a5c
Ticket: https://ticket.coreboot.org/issues/425
Signed-off-by: Krystian Hebel <krystian.hebel@3mdeb.com>
Signed-off-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/68749
Reviewed-by: Paul Menzel <paulepanter@mailbox.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Sergii Dmytruk 2022-10-23 00:55:03 +03:00 committed by Felix Held
parent 6d169aabbd
commit 6da62684de
2 changed files with 300 additions and 5 deletions

View File

@ -9,6 +9,26 @@
#define TCPA_SPEC_ID_EVENT_SIGNATURE "Spec ID Event00"
#define TCG_EFI_SPEC_ID_EVENT_SIGNATURE "Spec ID Event03"
struct tcpa_log_entry {
uint32_t pcr;
uint32_t event_type;
uint8_t digest[20];
uint32_t event_data_size;
uint8_t event[0];
} __packed;
struct tcpa_spec_entry {
struct tcpa_log_entry entry;
uint8_t signature[16];
uint32_t platform_class;
uint8_t spec_version_minor;
uint8_t spec_version_major;
uint8_t spec_errata;
uint8_t reserved;
uint8_t vendor_info_size;
uint8_t vendor_info[0];
} __packed;
#define TPM2_ALG_ERROR 0x0000
#define TPM2_ALG_HMAC 0x0005
#define TPM2_ALG_NULL 0x0010
@ -56,6 +76,28 @@ struct spec_id_event_data {
uint8_t vendor_info_size;
} __packed;
union tpm_hash_digest {
uint8_t sha1[SHA1_DIGEST_SIZE];
uint8_t sha256[SHA256_DIGEST_SIZE];
uint8_t sm3_256[SM3_256_DIGEST_SIZE];
uint8_t sha384[SHA384_DIGEST_SIZE];
uint8_t sha512[SHA512_DIGEST_SIZE];
};
struct tpm_hash_algorithm {
uint16_t hashAlg;
union tpm_hash_digest digest;
} __packed;
struct tcg_pcr_event2_header {
uint32_t pcr_index;
uint32_t event_type;
uint32_t digest_count;
uint8_t digests[0];
/* uint32_t event_size; */
/* uint8_t event[0]; */
} __packed;
struct tpm_digest_sizes {
uint16_t alg_id;
uint16_t digest_size;
@ -78,4 +120,26 @@ struct tcg_efi_spec_id_event {
/* uint8_t vendor_info[vendor_info_size]; */
} __packed;
static const char *tpm_event_types[] __maybe_unused = {
[EV_PREBOOT_CERT] = "Reserved",
[EV_POST_CODE] = "POST code",
[EV_UNUSED] = "Unused",
[EV_NO_ACTION] = "No action",
[EV_SEPARATOR] = "Separator",
[EV_ACTION] = "Action",
[EV_EVENT_TAG] = "Event tag",
[EV_S_CRTM_CONTENTS] = "S-CRTM contents",
[EV_S_CRTM_VERSION] = "S-CRTM version",
[EV_CPU_MICROCODE] = "CPU microcode",
[EV_PLATFORM_CONFIG_FLAGS] = "Platform configuration flags",
[EV_TABLE_OF_DEVICES] = "Table of devices",
[EV_COMPACT_HASH] = "Compact hash",
[EV_IPL] = "IPL",
[EV_IPL_PARTITION_DATA] = "IPL partition data",
[EV_NONHOST_CODE] = "Non-host code",
[EV_NONHOST_CONFIG] = "Non-host configuration",
[EV_NONHOST_INFO] = "Non-host information",
[EV_OMIT_BOOT_DEVICE_EVENTS] = "Omit boot device events",
};
#endif

View File

@ -19,6 +19,7 @@
#include <assert.h>
#include <regex.h>
#include <commonlib/bsd/cbmem_id.h>
#include <commonlib/bsd/tpm_log_defs.h>
#include <commonlib/loglevel.h>
#include <commonlib/timestamp_serialized.h>
#include <commonlib/tpm_log_serialized.h>
@ -841,6 +842,227 @@ static void timestamp_add_now(uint32_t timestamp_id)
unmap_memory(&timestamp_mapping);
}
static bool can_print(const uint8_t *data, size_t len)
{
unsigned int i;
for (i = 0; i < len; i++) {
if (!isprint(data[i]) && !isspace(data[i])) {
/* If printable prefix is followed by zeroes, this is a valid string */
for (; i < len; i++) {
if (data[i] != 0)
return false;
}
return true;
}
}
return true;
}
static void print_hex_string(const uint8_t *hex, size_t len)
{
unsigned int i;
for (i = 0; i < len; i++)
printf("%02x", hex[i]);
}
static void print_hex_line(const uint8_t *hex, size_t len)
{
print_hex_string(hex, len);
printf("\n");
}
static void print_event_type(uint32_t event_type)
{
unsigned int known_event_count = ARRAY_SIZE(tpm_event_types);
if (event_type >= known_event_count)
printf("Unknown (0x%x >= %u)", event_type, known_event_count);
else
printf("%s", tpm_event_types[event_type]);
}
static void parse_tpm12_log(const struct tcpa_spec_entry *spec_log)
{
const uint8_t zero_block[sizeof(struct tcpa_spec_entry)] = {0};
uintptr_t current;
uint32_t counter = 0;
printf("TCPA log:\n");
printf("\tSpecification: %d.%d%d\n",
spec_log->spec_version_major,
spec_log->spec_version_minor,
spec_log->spec_errata);
printf("\tPlatform class: %s\n",
le32toh(spec_log->platform_class) == 0 ? "PC Client" :
le32toh(spec_log->platform_class) == 1 ? "Server" : "Unknown");
current = (uintptr_t)&spec_log->vendor_info[spec_log->vendor_info_size];
while (memcmp((const void *)current, (const void *)zero_block, sizeof(zero_block))) {
uint32_t len;
struct tcpa_log_entry *log_entry = (void *)current;
uint32_t event_type = le32toh(log_entry->event_type);
printf("TCPA log entry %u:\n", ++counter);
printf("\tPCR: %d\n", le32toh(log_entry->pcr));
printf("\tEvent type: ");
print_event_type(event_type);
printf("\n");
printf("\tDigest: ");
print_hex_line(log_entry->digest, SHA1_DIGEST_SIZE);
current += sizeof(struct tcpa_log_entry);
len = le32toh(log_entry->event_data_size);
if (len != 0) {
current += len;
printf("\tEvent data: ");
if (can_print(log_entry->event, len))
printf("%.*s\n", len, log_entry->event);
else
print_hex_line(log_entry->event, len);
} else {
printf("\tEvent data not provided\n");
}
}
}
static uint32_t print_tpm2_digests(struct tcg_pcr_event2_header *log_entry)
{
unsigned int i;
uintptr_t current = (uintptr_t)log_entry->digests;
for (i = 0; i < le32toh(log_entry->digest_count); i++) {
struct tpm_hash_algorithm *hash = (struct tpm_hash_algorithm *)current;
switch (le16toh(hash->hashAlg)) {
case TPM2_ALG_SHA1:
printf("\t\t SHA1: ");
print_hex_line(hash->digest.sha1, SHA1_DIGEST_SIZE);
current += sizeof(hash->hashAlg) + SHA1_DIGEST_SIZE;
break;
case TPM2_ALG_SHA256:
printf("\t\t SHA256: ");
print_hex_line(hash->digest.sha256, SHA256_DIGEST_SIZE);
current += sizeof(hash->hashAlg) + SHA256_DIGEST_SIZE;
break;
case TPM2_ALG_SHA384:
printf("\t\t SHA384: ");
print_hex_line(hash->digest.sha384, SHA384_DIGEST_SIZE);
current += sizeof(hash->hashAlg) + SHA384_DIGEST_SIZE;
break;
case TPM2_ALG_SHA512:
printf("\t\t SHA512: ");
print_hex_line(hash->digest.sha512, SHA512_DIGEST_SIZE);
current += sizeof(hash->hashAlg) + SHA512_DIGEST_SIZE;
break;
case TPM2_ALG_SM3_256:
printf("\t\t SM3: ");
print_hex_line(hash->digest.sm3_256, SM3_256_DIGEST_SIZE);
current += sizeof(hash->hashAlg) + SM3_256_DIGEST_SIZE;
break;
default:
die("Unknown hash algorithm\n");
}
}
return current - (uintptr_t)&log_entry->digest_count;
}
static void parse_tpm2_log(const struct tcg_efi_spec_id_event *tpm2_log)
{
const uint8_t zero_block[12] = {0}; /* Only PCR index, event type and digest count */
uintptr_t current;
uint32_t counter = 0;
printf("TPM2 log:\n");
printf("\tSpecification: %d.%d%d\n",
tpm2_log->spec_version_major,
tpm2_log->spec_version_minor,
tpm2_log->spec_errata);
printf("\tPlatform class: %s\n",
le32toh(tpm2_log->platform_class) == 0 ? "PC Client" :
le32toh(tpm2_log->platform_class) == 1 ? "Server" : "Unknown");
/* Start after the first variable-sized part of the header */
current = (uintptr_t)&tpm2_log->digest_sizes[le32toh(tpm2_log->num_of_algorithms)];
/* current is at `uint8_t vendor_info_size` here */
current += 1 + *(uint8_t *)current;
while (memcmp((const void *)current, (const void *)zero_block, sizeof(zero_block))) {
uint32_t len;
struct tcg_pcr_event2_header *log_entry = (void *)current;
uint32_t event_type = le32toh(log_entry->event_type);
printf("TPM2 log entry %u:\n", ++counter);
printf("\tPCR: %d\n", le32toh(log_entry->pcr_index));
printf("\tEvent type: ");
print_event_type(event_type);
printf("\n");
current = (uintptr_t)&log_entry->digest_count;
if (le32toh(log_entry->digest_count) > 0) {
printf("\tDigests:\n");
current += print_tpm2_digests(log_entry);
} else {
printf("\tNo digests in this log entry\n");
current += sizeof(log_entry->digest_count);
}
/* Now event size and event are left to be parsed */
len = le32toh(*(uint32_t *)current);
current += sizeof(uint32_t);
if (len != 0) {
printf("\tEvent data: %.*s\n", len, (const char *)current);
current += len;
} else {
printf("\tEvent data not provided\n");
}
}
}
/* Dump the TPM log table in format defined by specifications */
static void dump_tpm_std_log(uint64_t addr, size_t size)
{
const void *event_log;
const struct tcpa_spec_entry *tspec_entry;
const struct tcg_efi_spec_id_event *tcg_spec_entry;
struct mapping log_mapping;
event_log = map_memory(&log_mapping, addr, size);
if (!event_log)
die("Unable to map TPM eventlog\n");
tspec_entry = event_log;
if (!strcmp((const char *)tspec_entry->signature, TCPA_SPEC_ID_EVENT_SIGNATURE)) {
if (tspec_entry->spec_version_major == 1 &&
tspec_entry->spec_version_minor == 2 &&
tspec_entry->spec_errata >= 1 &&
le32toh(tspec_entry->entry.event_type) == EV_NO_ACTION) {
parse_tpm12_log(tspec_entry);
} else {
fprintf(stderr, "Unknown TPM1.2 log specification\n");
}
unmap_memory(&log_mapping);
return;
}
tcg_spec_entry = event_log;
if (!strcmp((const char *)tcg_spec_entry->signature, TCG_EFI_SPEC_ID_EVENT_SIGNATURE)) {
if (tcg_spec_entry->spec_version_major == 2 &&
tcg_spec_entry->spec_version_minor == 0 &&
le32toh(tcg_spec_entry->event_type) == EV_NO_ACTION) {
parse_tpm2_log(tcg_spec_entry);
} else {
fprintf(stderr, "Unknown TPM2 log specification.\n");
}
unmap_memory(&log_mapping);
return;
}
fprintf(stderr, "Unknown TPM log specification: %.*s\n",
(int)sizeof(tcg_spec_entry->signature),
(const char *)tcg_spec_entry->signature);
unmap_memory(&log_mapping);
}
/* dump the TPM CB log table */
static void dump_tpm_cb_log(void)
{
@ -872,16 +1094,25 @@ static void dump_tpm_cb_log(void)
const struct tpm_cb_log_entry *tce = &tclt_p->entries[i];
printf(" PCR-%u ", tce->pcr);
for (uint32_t j = 0; j < tce->digest_length; j++)
printf("%02x", tce->digest[j]);
print_hex_string(tce->digest, tce->digest_length);
printf(" %s [%s]\n", tce->digest_type, tce->name);
}
unmap_memory(&log_mapping);
}
static void dump_tpm_log(void)
{
uint64_t start;
size_t size;
if (!find_cbmem_entry(CBMEM_ID_TCPA_TCG_LOG, &start, &size) ||
!find_cbmem_entry(CBMEM_ID_TPM2_TCG_LOG, &start, &size))
dump_tpm_std_log(start, size);
else
dump_tpm_cb_log();
}
struct cbmem_console {
u32 size;
u32 cursor;
@ -1675,7 +1906,7 @@ int main(int argc, char** argv)
dump_timestamps(timestamp_type);
if (print_tcpa_log)
dump_tpm_cb_log();
dump_tpm_log();
unmap_memory(&lbtable_mapping);