diff --git a/Makefile b/Makefile index 61599d8..1f8ac41 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,9 @@ endif CFLAGS += -Os -g -MMD -MP -mno-sse -mno-mmx -fpie -fomit-frame-pointer CFLAGS += -Iinclude -ffreestanding -fno-common -Wall -Werror LDFLAGS += -nostdlib -no-pie -Wl,--build-id=none,--fatal-warnings,--defsym=BITS=$(BITS) +# Keep the code and the small .bss of link.lds in separate LOAD segments, so +# ld 2.39 and later do not warn about an RWX segment. +LDFLAGS += -Wl,-z,max-page-size=0x10 CFLAGS_TPMLIB := -include boot.h -include errno-base.h -include byteswap.h -DEBADRQC=EINVAL diff --git a/event_log.c b/event_log.c index 483697c..c65d500 100644 --- a/event_log.c +++ b/event_log.c @@ -17,11 +17,13 @@ */ #include +#include #include #include #include #include #include "tpmlib/tpm.h" +#include "tpmlib/tpm_common.h" #include "tpmlib/tpm2_constants.h" static u8 *evtlog_base; @@ -43,7 +45,38 @@ static int log_write(const void *data, unsigned size) #define EV_NO_ACTION 0x3 #define EV_TYPE_SLAUNCH 0x502 -#define HASH_COUNT 2 +/* + * One bank per algorithm the SKL knows the digest size of: SHA-1, SHA-256, + * SHA-384, SHA-512 and SM3-256. + */ +#define MAX_BANKS 5 + +/* + * The TPM's allocated banks in its order, which the TPM 2.0 log declares + * and every record of it carries. Where the SKL cannot compute a digest, + * the record carries 01 followed by zeros, as Linux and Xen log for such + * a bank. The Intel TXT Software Development Guide calls it OneDigest. + */ +static struct __packed { + u16 alg; + u16 size; +} banks[MAX_BANKS]; +static u32 nbanks; + +static const u8 one_digest[SHA512_SIZE] = { 1 }; + +static u16 digest_size(u16 alg) +{ + switch ( alg ) + { + case TPM_ALG_SHA1: return SHA1_SIZE; + case TPM_ALG_SHA256: return SHA256_SIZE; + case TPM_ALG_SHA384: return SHA384_SIZE; + case TPM_ALG_SHA512: return SHA512_SIZE; + case TPM_ALG_SM3_256: return SM3256_SIZE; + default: return 0; + } +} /* For compatibility with TXT and easier operations */ @@ -69,6 +102,16 @@ typedef struct __packed { u32 next_record_offset; } txt_event_log_pointer2_1_element; +/* Event log entries */ + +typedef struct __packed { + u32 pcr; + u32 event_type; + u8 digest[SHA1_DIGEST_SIZE]; + u32 event_size; + /* u8 event[]; */ +} tpm12_event_t; + /* Event log headers */ typedef struct __packed { @@ -86,48 +129,38 @@ typedef struct __packed { tpm12_event_log_header hdr; /* AKA u8 vendor_info[]; */ } tpm12_spec_id_ev_t; -typedef struct __packed { - u32 number_of_algorithms; - /* Hardcode table size so we can use sizeof */ - struct { - u16 id; - u16 size; - } digest_sizes[HASH_COUNT]; -} tpm20_digest_sizes_t; - -typedef struct __packed { - common_spec_id_ev_t c; - tpm20_digest_sizes_t sizes; - u8 vendor_info_size; - txt_event_log_pointer2_1_element el; /* AKA u8 vendor_info[]; */ -} tpm20_spec_id_ev_t; +/* + * The TPM 2.0 header is common_spec_id_ev_t, the number of algorithms, + * an id and size pair per bank, and the vendor info with its size. Its + * size depends on the number of banks. + */ +static u32 tpm20_header_size(void) +{ + return sizeof(common_spec_id_ev_t) + sizeof(u32) + nbanks * sizeof(banks[0]) + + 1 + sizeof(txt_event_log_pointer2_1_element); +} -/* Event log entries */ +/* The vendor info of the TPM 2.0 header in the log */ +static txt_event_log_pointer2_1_element *tpm20_el(void) +{ + return (txt_event_log_pointer2_1_element *) + (evtlog_base + sizeof(tpm12_event_t) + tpm20_header_size() + - sizeof(txt_event_log_pointer2_1_element)); +} -typedef struct __packed { - u32 pcr; - u32 event_type; - u8 digest[SHA1_DIGEST_SIZE]; - u32 event_size; - /* u8 event[]; */ -} tpm12_event_t; +/* + * A TPM 2.0 record is the PCR, the type, the digest count, an id and + * digest per bank, the event size and the event: TCG_PCR_EVENT2. + */ +static u32 tpm20_record_size(u32 event_size) +{ + u32 i, size = 3 * sizeof(u32) + sizeof(u32) + event_size; -/* The same as TPML_DIGEST_VALUES but little endian, as event log expects it */ -typedef struct __packed { - u32 count; - u16 sha1_id; - u8 sha1_hash[SHA1_DIGEST_SIZE]; - u16 sha256_id; - u8 sha256_hash[SHA256_DIGEST_SIZE]; -} ev_log_hash_t; + for ( i = 0; i < nbanks; i++ ) + size += sizeof(u16) + banks[i].size; -typedef struct __packed { - u32 pcr; - u32 event_type; - ev_log_hash_t digests; - u32 event_size; - /* u8 event[]; */ -} tpm20_event_t; + return size; +} static const tpm12_spec_id_ev_t tpm12_id_struct = { .c.signature = "Spec ID Event00", @@ -150,20 +183,12 @@ static const tpm12_spec_id_ev_t tpm12_id_struct = { .hdr.next_event_offset = sizeof(tpm12_event_log_header) }; -static const tpm20_spec_id_ev_t tpm20_id_struct = { - .c.signature = "Spec ID Event03", - .c.spec_ver_minor = 0, - .c.spec_ver_major = 2, - .c.errata = 0, - .c.uintn_size = 2, - .sizes.number_of_algorithms = HASH_COUNT, - .sizes.digest_sizes[0].id = TPM_ALG_SHA1, - .sizes.digest_sizes[0].size = 20, - .sizes.digest_sizes[1].id = TPM_ALG_SHA256, - .sizes.digest_sizes[1].size = 32, - .vendor_info_size = sizeof(txt_event_log_pointer2_1_element), - .el.first_record_offset = 0, - .el.next_record_offset = sizeof(tpm20_spec_id_ev_t) + sizeof(tpm12_event_t) +static const common_spec_id_ev_t tpm20_id_common = { + .signature = "Spec ID Event03", + .spec_ver_minor = 0, + .spec_ver_major = 2, + .errata = 0, + .uintn_size = 2, }; int log_event_tpm12(u32 pcr, u8 sha1[SHA1_DIGEST_SIZE], char *event) @@ -187,30 +212,104 @@ int log_event_tpm12(u32 pcr, u8 sha1[SHA1_DIGEST_SIZE], char *event) return 1; } +/* + * Writes a TPM 2.0 record with digests[i] in bank i, or the placeholder + * where that is NULL. Returns non-zero when it does not fit. + */ +static int write_event_tpm20(u32 pcr, u32 type, const u8 *const digests[], + const void *event, u32 event_size) +{ + u32 i, head[3] = { pcr, type, nbanks }; + + if ( !HAS_ENOUGH_SPACE(tpm20_record_size(event_size)) ) + return 1; + + log_write(head, sizeof(head)); + for ( i = 0; i < nbanks; i++ ) + { + log_write(&banks[i].alg, sizeof(banks[i].alg)); + log_write(digests[i] ? digests[i] : one_digest, banks[i].size); + } + log_write(&event_size, sizeof(event_size)); + log_write(event, event_size); + tpm20_el()->next_record_offset += tpm20_record_size(event_size); + + return 0; +} + int log_event_tpm20(u32 pcr, u8 sha1[SHA1_DIGEST_SIZE], u8 sha256[SHA256_DIGEST_SIZE], char *event) { - tpm20_event_t ev; - tpm20_spec_id_ev_t *base = (tpm20_spec_id_ev_t *) - (evtlog_base + sizeof(tpm12_event_t)); + const u8 *digests[MAX_BANKS] = { NULL }; + u32 i; - ev.event_size = strlen(event); + for ( i = 0; i < nbanks; i++ ) + digests[i] = banks[i].alg == TPM_ALG_SHA1 ? sha1 + : banks[i].alg == TPM_ALG_SHA256 ? sha256 : NULL; - if ( HAS_ENOUGH_SPACE(sizeof(ev) + ev.event_size) ) + return write_event_tpm20(pcr, EV_TYPE_SLAUNCH, digests, event, + strlen(event)); +} + +void event_log_extend_placeholders(struct tpm *tpm, u32 pcr) +{ + u32 i; + + for ( i = 0; i < nbanks; i++ ) + if ( banks[i].alg != TPM_ALG_SHA1 && banks[i].alg != TPM_ALG_SHA256 ) + tpm_extend_pcr(tpm, pcr, banks[i].alg, (u8 *)one_digest); +} + +/* + * Asks the TPM 2.0 for its allocated banks, leaving out algorithms the SKL + * does not know. If the query fails, or finds no bank the SKL knows, the + * log declares SHA-1 and SHA-256, the banks most discrete TPMs have + * allocated by default. The query fails behind a locality the SKL does + * not hold, and on every CRB TPM, because crb_recv() does not read the + * reply. + */ +static void find_banks(struct tpm *tpm) +{ + u16 algs[MAX_BANKS]; + u32 n = MAX_BANKS, i; + int rc; + + nbanks = 0; + rc = tpm_get_pcr_banks(tpm, algs, &n); + if ( rc == 0 ) { - ev.pcr = pcr; - ev.event_type = EV_TYPE_SLAUNCH; - ev.digests.count = 2; - ev.digests.sha1_id = TPM_ALG_SHA1; - memcpy(ev.digests.sha1_hash, sha1, 20); - ev.digests.sha256_id = TPM_ALG_SHA256; - memcpy(ev.digests.sha256_hash, sha256, 32); - base->el.next_record_offset += sizeof(ev) + ev.event_size; - log_write(&ev, sizeof(ev)); - return log_write(event, ev.event_size); + for ( i = 0; i < n; i++ ) + { + if ( digest_size(algs[i]) == 0 ) + continue; + banks[nbanks].alg = algs[i]; + banks[nbanks].size = digest_size(algs[i]); + nbanks++; + } + } + else + { + print("Bank query failed: "); + print_u64((u32)rc); + print("\n"); } - return 1; + if ( nbanks == 0 ) + { + banks[0].alg = TPM_ALG_SHA1; + banks[0].size = SHA1_SIZE; + banks[1].alg = TPM_ALG_SHA256; + banks[1].size = SHA256_SIZE; + nbanks = 2; + } + + print("Event log banks:"); + for ( i = 0; i < nbanks; i++ ) + { + print(" "); + print_u64(banks[i].alg); + } + print("\n"); } int event_log_init(struct tpm *tpm) @@ -233,8 +332,9 @@ int event_log_init(struct tpm *tpm) } else if ( tpm->family == TPM20 ) { - min_size += sizeof(tpm20_id_struct); - min_size += 2 * sizeof(tpm20_event_t); /* SKL and kernel hashes */ + find_banks(tpm); + min_size += tpm20_header_size(); + min_size += 2 * tpm20_record_size(0); /* SKL and kernel hashes */ } else { @@ -277,7 +377,7 @@ int event_log_init(struct tpm *tpm) if ( tpm->family == TPM12 ) ev.event_size = sizeof(tpm12_id_struct); else - ev.event_size = sizeof(tpm20_id_struct); + ev.event_size = tpm20_header_size(); log_write(&ev, sizeof(ev)); } @@ -288,10 +388,19 @@ int event_log_init(struct tpm *tpm) log_write(&tpm12_id_struct, sizeof(tpm12_id_struct)); id->hdr.container_size = info->size; } else { - tpm20_spec_id_ev_t *id = (tpm20_spec_id_ev_t *)ptr_current; - log_write(&tpm20_id_struct, sizeof(tpm20_id_struct)); - id->el.allocated_event_container_size = info->size; - id->el.phys_addr = _u(evtlog_base); + u8 vendor_info_size = sizeof(txt_event_log_pointer2_1_element); + txt_event_log_pointer2_1_element el = { + .phys_addr = _u(evtlog_base), + .allocated_event_container_size = info->size, + .first_record_offset = 0, + .next_record_offset = sizeof(tpm12_event_t) + tpm20_header_size(), + }; + + log_write(&tpm20_id_common, sizeof(tpm20_id_common)); + log_write(&nbanks, sizeof(nbanks)); + log_write(banks, nbanks * sizeof(banks[0])); + log_write(&vendor_info_size, sizeof(vendor_info_size)); + log_write(&el, sizeof(el)); } /* Log what was done by SKINIT */ @@ -305,6 +414,10 @@ int event_log_init(struct tpm *tpm) u8 sha256_hash[SHA256_DIGEST_SIZE]; sha256sum(sha256_hash, _start, _end_of_measured - _start); + /* + * SKINIT extended PCR 17 with the real digest in every bank, so + * this record does not replay in a bank that gets the placeholder. + */ return log_event_tpm20(17, hash, sha256_hash, "SKINIT"); } diff --git a/head.S b/head.S index 02b17c0..a84f1b4 100644 --- a/head.S +++ b/head.S @@ -319,7 +319,7 @@ GLOBAL(skinit_reserved) #ifdef __x86_64__ /* 64bit Pagetables, identity map of the first 4G of RAM. */ - .section .bss.page_data, "aw", @nobits + .section .bss..page_data, "aw", @nobits .align PAGE_SIZE l1_identmap: /* 1x L1 page, mapping 2M of RAM. */ diff --git a/include/defs.h b/include/defs.h index 1760244..c51f5ea 100644 --- a/include/defs.h +++ b/include/defs.h @@ -64,7 +64,7 @@ /* Due to the 64k total size limit, group all page aligned data together. */ #define __page_data \ - __attribute__ ((__section__(".bss.page_data"), __aligned__(PAGE_SIZE))) + __attribute__ ((__section__(".bss..page_data"), __aligned__(PAGE_SIZE))) #define unreachable() __builtin_unreachable() #define offsetof(a, b) __builtin_offsetof(a, b) diff --git a/include/event_log.h b/include/event_log.h index 2a75f9a..141433d 100644 --- a/include/event_log.h +++ b/include/event_log.h @@ -22,4 +22,10 @@ int event_log_init(struct tpm *tpm); int log_event_tpm12(u32 pcr, u8 sha1[20], char *event); int log_event_tpm20(u32 pcr, u8 sha1[20], u8 sha256[32], char *event); +/* + * Extends the placeholder digest into the banks of the PCR that the SKL + * cannot hash, so that those banks replay. + */ +void event_log_extend_placeholders(struct tpm *tpm, u32 pcr); + #endif /* __EVENT_LOG_H__ */ diff --git a/link.lds b/link.lds index 46be0bd..606a252 100644 --- a/link.lds +++ b/link.lds @@ -55,9 +55,18 @@ SECTIONS *(.skinit_reserved) } - .bss : { - . = ALIGN(4096); + /* + * The small variables come first, in a section of their own, so they + * fill the space that the page alignment of .bss would otherwise waste. + * Page aligned data is in .bss..page_data, which the second pattern + * leaves out. + */ + .bss_small : { _bss = .; + *(SORT_BY_ALIGNMENT(.bss) SORT_BY_ALIGNMENT(.bss.[!.]*)) + } + + .bss : { /* * Due to the 64k total size constraint, we link all page size/aligned * data together in a single section, to avoid wasting space in the @@ -108,6 +117,8 @@ SECTIONS } ASSERT(_end <= 0x10000, "Landing Zone exceeds 64k"); +/* GRUB puts a 4 KiB SLRT at bootloader_data and refuses an SKL without room. */ +ASSERT(bootloader_data <= 0x10000 - 0x1000, "no room for a 4 KiB SLRT"); ASSERT(SIZEOF(.got) == 0, ".got section not empty - non-hidden symbols used?"); ASSERT(SIZEOF(.data) == 0, ".data section not empty - initialized data used?"); diff --git a/main.c b/main.c index 9425d7a..206eae2 100644 --- a/main.c +++ b/main.c @@ -61,6 +61,7 @@ static void extend_pcr(struct tpm *tpm, void *data, u32 size, u32 pcr, char *ev) print("shasum calculated:\n"); hexdump(sha256_hash, SHA256_DIGEST_SIZE); tpm_extend_pcr(tpm, pcr, TPM_ALG_SHA256, &sha256_hash[0]); + event_log_extend_placeholders(tpm, pcr); log_event_tpm20(pcr, hash, sha256_hash, ev); } diff --git a/test-event_log.c b/test-event_log.c new file mode 100644 index 0000000..7ea7106 --- /dev/null +++ b/test-event_log.c @@ -0,0 +1,295 @@ +/* + * A hosted test of the TPM 2.0 event log: the header and records follow + * the TPM's banks, with the placeholder where the SKL has no digest. + */ +#include +#include +#include +#include +#include + +/* The host's C runtime owns _start, so the SKL's symbols go by other names */ +#define _start skl_start +#define _end_of_measured skl_end_of_measured + +#include "sha1sum.c" +#include "sha256.c" +#include "event_log.c" + +#ifdef DEBUG +void print(const char *unused) { } +void print_u64(u64 unused) { } +#endif + +/* An SLB-sized image whose first 16 bytes are what SKINIT measured */ +const char skl_start[SLB_SIZE] = "SKINIT measured"; +__asm__(".globl skl_end_of_measured\n.set skl_end_of_measured, skl_start + 16"); + +/* + * The SLRT that gives the SKL its log region: one log info entry, then + * the end tag. Its flexible array is initialised with room for both. + */ +#define SLRT_ENTRIES \ + ((sizeof(struct slr_entry_log_info) + sizeof(struct slr_entry_hdr)) / \ + sizeof(struct slr_entry_hdr)) + +struct slr_table bootloader_data = { + .entries = { [SLRT_ENTRIES - 1] = { 0 } }, +}; +static struct slr_entry_log_info *const slrt_log = + (struct slr_entry_log_info *)bootloader_data.entries; +static struct slr_entry_hdr *const slrt_end = + (struct slr_entry_hdr *)bootloader_data.entries + SLRT_ENTRIES - 1; + +static u8 log_region[4096]; + +/* What the TPM answers for its banks */ +static u16 tpm_algs[MAX_BANKS]; +static u32 tpm_nalgs; +static int tpm_banks_rc; + +int tpm_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count) +{ + if ( tpm_banks_rc ) + return tpm_banks_rc; + memcpy(algs, tpm_algs, tpm_nalgs * sizeof(u16)); + *count = tpm_nalgs; + return 0; +} + +static void tpm_has(u32 n, ...) +{ + va_list ap; + u32 i; + + va_start(ap, n); + for ( i = 0; i < n; i++ ) + tpm_algs[i] = va_arg(ap, int); + va_end(ap); + tpm_nalgs = n; + tpm_banks_rc = 0; +} + +/* The extends made: (pcr, alg, whether the placeholder) */ +static struct { + u32 pcr; + u16 alg; + bool placeholder; +} extends[32]; +static unsigned nextends; + +static bool is_placeholder(const u8 *digest, u32 size) +{ + u32 i; + + if ( digest[0] != 1 ) + return false; + for ( i = 1; i < size; i++ ) + if ( digest[i] != 0 ) + return false; + return true; +} + +int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo, u8 *digest) +{ + if ( nextends < 32 ) + { + extends[nextends].pcr = pcr; + extends[nextends].alg = algo; + extends[nextends].placeholder = + is_placeholder(digest, digest_size(algo)); + } + nextends++; + return 0; +} + +static struct tpm tpm20 = { .family = TPM20 }; + +static int failures; + +#define CHECK(cond) do { \ + if ( !(cond) ) { \ + printf("%s:%d: %s\n", __FILE__, __LINE__, #cond); \ + failures++; \ + } \ +} while ( 0 ) + +static void fresh_log(u32 size) +{ + nextends = 0; + bootloader_data.size = sizeof(bootloader_data) + + SLRT_ENTRIES * sizeof(struct slr_entry_hdr); + slrt_log->hdr.tag = SLR_ENTRY_LOG_INFO; + slrt_log->hdr.size = sizeof(*slrt_log); + slrt_log->format = SLR_DRTM_TPM20_LOG; + slrt_log->size = size; + slrt_log->addr = _u(log_region); + slrt_end->tag = SLR_ENTRY_END; + slrt_end->size = sizeof(*slrt_end); + memset(log_region, 0xa5, sizeof(log_region)); + CHECK(event_log_init(&tpm20) == 0); +} + +/* The header as a reader parses it: the algorithm table and the vendor info */ +static struct { + u32 nalgs; + u16 alg[MAX_BANKS]; + u16 size[MAX_BANKS]; + txt_event_log_pointer2_1_element *el; + const u8 *records; +} hdr; + +static void parse_header(void) +{ + const u8 *p = log_region + sizeof(tpm12_event_t); + u32 i; + + CHECK(memcmp(p, "Spec ID Event03", 16) == 0); + p += sizeof(common_spec_id_ev_t); + memcpy(&hdr.nalgs, p, 4); + p += 4; + CHECK(hdr.nalgs >= 1 && hdr.nalgs <= MAX_BANKS); + for ( i = 0; i < hdr.nalgs && i < MAX_BANKS; i++ ) + { + memcpy(&hdr.alg[i], p, 2); + memcpy(&hdr.size[i], p + 2, 2); + p += 4; + } + CHECK(*p == sizeof(*hdr.el)); + hdr.el = (txt_event_log_pointer2_1_element *)(p + 1); + hdr.records = p + 1 + sizeof(*hdr.el); +} + +/* + * The records after the header, parsed as a reader would: each carries + * the header's banks in its order, and for every bank the first digest + * byte and whether it is the placeholder. + */ +struct record { + u32 pcr, type; + u8 first[MAX_BANKS]; + bool placeholder[MAX_BANKS]; +}; + +static unsigned records(struct record *out, unsigned max) +{ + const u8 *p; + unsigned n = 0; + + parse_header(); + p = hdr.records; + while ( n < max ) + { + u32 count, i, len; + + memcpy(&out[n].pcr, p, 4); + memcpy(&out[n].type, p + 4, 4); + memcpy(&count, p + 8, 4); + if ( count == 0 ) + break; + p += 12; + CHECK(count == hdr.nalgs); + for ( i = 0; i < count && i < MAX_BANKS; i++ ) + { + u16 alg; + memcpy(&alg, p, 2); + CHECK(alg == hdr.alg[i]); + out[n].first[i] = p[2]; + out[n].placeholder[i] = is_placeholder(p + 2, hdr.size[i]); + p += 2 + hdr.size[i]; + } + memcpy(&len, p, 4); + p += 4 + len; + n++; + } + return n; +} + +int main(void) +{ + u8 skinit_sha1[20], skinit_sha256[32]; + struct record recs[16]; + unsigned n; + + sha1sum(skinit_sha1, skl_start, 16); + sha256sum(skinit_sha256, skl_start, 16); + + /* A TPM with the two banks a discrete one ships with */ + tpm_has(2, TPM_ALG_SHA1, TPM_ALG_SHA256); + fresh_log(sizeof(log_region)); + n = records(recs, 16); + CHECK(hdr.nalgs == 2 && hdr.alg[0] == TPM_ALG_SHA1 && hdr.size[0] == 20); + CHECK(hdr.alg[1] == TPM_ALG_SHA256 && hdr.size[1] == 32); + CHECK(hdr.el->phys_addr == _u(log_region)); + CHECK(hdr.el->allocated_event_container_size == sizeof(log_region)); + CHECK(hdr.el->first_record_offset == 0); + /* The header event, the header with its two banks, then SKINIT's record */ + CHECK(hdr.records == log_region + 32 + 24 + 4 + 8 + 1 + 20); + CHECK(hdr.el->next_record_offset == 32 + 24 + 4 + 8 + 1 + 20 + 72 + 6); + CHECK(n == 1); + CHECK(recs[0].pcr == 17 && recs[0].type == EV_TYPE_SLAUNCH); + CHECK(recs[0].first[0] == skinit_sha1[0] && !recs[0].placeholder[0]); + CHECK(recs[0].first[1] == skinit_sha256[0] && !recs[0].placeholder[1]); + /* Nothing to extend the placeholder into */ + event_log_extend_placeholders(&tpm20, 17); + CHECK(nextends == 0); + + /* A TPM with only SHA-256: one digest per record and no placeholder */ + tpm_has(1, TPM_ALG_SHA256); + fresh_log(sizeof(log_region)); + n = records(recs, 16); + CHECK(hdr.nalgs == 1 && hdr.alg[0] == TPM_ALG_SHA256); + CHECK(hdr.records == log_region + 32 + 24 + 4 + 4 + 1 + 20); + CHECK(n == 1 && recs[0].first[0] == skinit_sha256[0]); + CHECK(!recs[0].placeholder[0]); + event_log_extend_placeholders(&tpm20, 17); + CHECK(nextends == 0); + + /* + * A TPM with SHA-384 as well, in its own order: the SKL's records get + * the placeholder there, which is extended. + */ + tpm_has(3, TPM_ALG_SHA256, TPM_ALG_SHA1, TPM_ALG_SHA384); + fresh_log(sizeof(log_region)); + n = records(recs, 16); + CHECK(hdr.nalgs == 3 && hdr.alg[0] == TPM_ALG_SHA256); + CHECK(hdr.alg[1] == TPM_ALG_SHA1); + CHECK(hdr.alg[2] == TPM_ALG_SHA384 && hdr.size[2] == 48); + CHECK(n == 1); + CHECK(recs[0].first[0] == skinit_sha256[0] && !recs[0].placeholder[0]); + CHECK(recs[0].first[1] == skinit_sha1[0] && !recs[0].placeholder[1]); + CHECK(recs[0].placeholder[2]); + event_log_extend_placeholders(&tpm20, 17); + CHECK(nextends == 1); + CHECK(extends[0].pcr == 17 && extends[0].alg == TPM_ALG_SHA384); + CHECK(extends[0].placeholder); + + /* A bank of an algorithm the SKL cannot size is left out */ + tpm_has(2, 0x99, TPM_ALG_SHA256); + fresh_log(sizeof(log_region)); + records(recs, 16); + CHECK(hdr.nalgs == 1 && hdr.alg[0] == TPM_ALG_SHA256); + + /* When the TPM does not answer, the log declares SHA-1 and SHA-256 */ + tpm_banks_rc = -1; + fresh_log(sizeof(log_region)); + records(recs, 16); + CHECK(hdr.nalgs == 2 && hdr.alg[0] == TPM_ALG_SHA1); + CHECK(hdr.alg[1] == TPM_ALG_SHA256); + + /* A region too small for the header and two bare records is refused */ + tpm_has(2, TPM_ALG_SHA1, TPM_ALG_SHA256); + nextends = 0; + slrt_log->size = 32 + 57 + 2 * 72 - 1; + CHECK(event_log_init(&tpm20) != 0); + slrt_log->size = 32 + 57 + 2 * 72; + CHECK(event_log_init(&tpm20) == 0); + + if ( failures ) + { + printf("%d failures\n", failures); + return 1; + } + printf("ok\n"); + return 0; +} diff --git a/tpmlib/tis.c b/tpmlib/tis.c index b405f88..4547735 100644 --- a/tpmlib/tis.c +++ b/tpmlib/tis.c @@ -157,25 +157,26 @@ static size_t recv_data(unsigned char *buf, size_t len) return size; } -size_t tis_recv(enum tpm_family f, struct tpmbuff *buf) +size_t tis_recv(__attribute__((unused)) enum tpm_family f, + struct tpmbuff *buf) { u32 expected; u8 *buf_ptr; struct tpm_header *hdr; + int i; if (locality > TPM_MAX_LOCALITY) return 0; - /* ensure that there is data available */ - if (!tis_data_available(locality)) { - if (f == TPM12) - tpm1_timeout_d(); - else - tpm2_timeout_d(); + /* + * Poll for the response for up to 2 s. TIMEOUT_D bounds a register + * access, and a command can take much longer to complete. + */ + for (i = 0; !tis_data_available(locality) && i < 2000; i++) + tpm_mdelay(1); - if (!tis_data_available(locality)) - return 0; - } + if (!tis_data_available(locality)) + return 0; /* read header */ hdr = (struct tpm_header *)buf->head; @@ -206,8 +207,8 @@ size_t tis_recv(enum tpm_family f, struct tpmbuff *buf) if (!tis_data_available(locality)) return 0; - /* read last byte */ - if (recv_data(buf_ptr, 1) != 1) + /* read last byte into its own slot after the bytes already received */ + if (recv_data(buf_ptr + expected - 1, 1) != 1) return 0; /* make sure we read everything */ diff --git a/tpmlib/tpm.c b/tpmlib/tpm.c index 64b3995..e4a5d91 100644 --- a/tpmlib/tpm.c +++ b/tpmlib/tpm.c @@ -152,6 +152,17 @@ int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo, return ret; } +int tpm_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count) +{ + if (t->buff == NULL) + return -EINVAL; + + if (t->family != TPM20) + return -EINVAL; + + return tpm2_get_pcr_banks(t, algs, count); +} + void free_tpm(struct tpm *t) { tpm_relinquish_locality(t); diff --git a/tpmlib/tpm.h b/tpmlib/tpm.h index a0b57e1..ca75a83 100644 --- a/tpmlib/tpm.h +++ b/tpmlib/tpm.h @@ -56,5 +56,6 @@ extern u8 tpm_request_locality(struct tpm *t, u8 l); extern void tpm_relinquish_locality(struct tpm *t); extern int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo, u8 *digest); +extern int tpm_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count); extern void free_tpm(struct tpm *t); #endif diff --git a/tpmlib/tpm2.h b/tpmlib/tpm2.h index 96ed81c..1d1a3cd 100644 --- a/tpmlib/tpm2.h +++ b/tpmlib/tpm2.h @@ -85,4 +85,15 @@ struct tpm2_resp { int tpm2_extend_pcr(struct tpm *t, u32 pcr, struct tpml_digest_values *digests); +/* + * Reads the banks with PCRs allocated, in the TPM's order. On entry + * *count is the room in algs[], and on success it is the number of banks + * found. Returns 0 on success, or: + * - -ENOSPC when the TPM has more banks than that + * - the TPM response code when the TPM fails the command + * - -EIO when the reply is malformed + * - another negative errno when the command cannot be sent or received + */ +int tpm2_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count); + #endif diff --git a/tpmlib/tpm2_cmds.c b/tpmlib/tpm2_cmds.c index 73355af..d76adc7 100644 --- a/tpmlib/tpm2_cmds.c +++ b/tpmlib/tpm2_cmds.c @@ -82,7 +82,7 @@ static u16 convert_digest_list(struct tpml_digest_values *digests) case TPM_ALG_SM3_256: h->alg = cpu_to_be16(h->alg); h = (struct tpmt_ha *)((u8 *)h + SM3256_SIZE); - size += sizeof(u16) + SHA1_SIZE; + size += sizeof(u16) + SM3256_SIZE; break; default: return 0; @@ -158,3 +158,113 @@ int tpm2_extend_pcr(struct tpm *t, u32 pcr, out: return ret; } + +int tpm2_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count) +{ + struct tpmbuff *b = t->buff; + struct tpm2_cmd cmd; + struct tpm_header *hdr; + u8 *p, *end; + u32 nsel, i, n = 0; + int ret = 0; + + if (b == NULL || algs == NULL || count == NULL) + return -EINVAL; + + ret = tpm2_alloc_cmd(b, &cmd, TPM_ST_NO_SESSIONS, + TPM_CC_GET_CAPABILITY); + if (ret < 0) + goto out; + + /* capability, property, propertyCount */ + cmd.params = (u8 *)tpmb_put(b, 3 * sizeof(u32)); + if (cmd.params == NULL) { + ret = -ENOMEM; + goto free; + } + + p = cmd.params; + *(u32 *)p = cpu_to_be32(TPM_CAP_PCRS); + p += sizeof(u32); + *(u32 *)p = cpu_to_be32(0); + p += sizeof(u32); + *(u32 *)p = cpu_to_be32(1); + + cmd.header->size = cpu_to_be32(tpmb_size(b)); + + if (t->ops.send(b) != tpmb_size(b)) { + ret = -EAGAIN; + goto free; + } + + tpmb_trim(b, tpmb_size(b)); + hdr = (struct tpm_header *)b->head; + tpmb_put(b, sizeof(struct tpm_header)); + + if (t->ops.recv(t->family, b) != tpmb_size(b)) { + ret = -EAGAIN; + goto free; + } + + if (hdr->code != 0) { + ret = (int)hdr->code; + goto free; + } + + /* + * Response: moreData, TPMS_CAPABILITY_DATA{capability, + * TPML_PCR_SELECTION{count, TPMS_PCR_SELECTION{hash, sizeofSelect, + * pcrSelect[]}[]}}. A bank with any PCR selected is allocated. + */ + p = b->head + sizeof(struct tpm_header); + end = b->head + hdr->size; + + if (end - p < 1 + sizeof(u32) + sizeof(u32)) { + ret = -EIO; + goto free; + } + p += 1; + if (be32_to_cpu(*(u32 *)p) != TPM_CAP_PCRS) { + ret = -EIO; + goto free; + } + p += sizeof(u32); + nsel = be32_to_cpu(*(u32 *)p); + p += sizeof(u32); + + for (i = 0; i < nsel; i++) { + u16 alg; + u8 size, j, selected = 0; + + if (end - p < sizeof(u16) + 1) { + ret = -EIO; + goto free; + } + alg = be16_to_cpu(*(u16 *)p); + size = p[sizeof(u16)]; + p += sizeof(u16) + 1; + if (end - p < size) { + ret = -EIO; + goto free; + } + for (j = 0; j < size; j++) + selected |= p[j]; + p += size; + + if (selected) { + if (n < *count) + algs[n] = alg; + n++; + } + } + + if (n > *count) + ret = -ENOSPC; + else + *count = n; + +free: + tpmb_free(b); +out: + return ret; +} diff --git a/tpmlib/tpm2_constants.h b/tpmlib/tpm2_constants.h index 35bdc1e..2893cde 100644 --- a/tpmlib/tpm2_constants.h +++ b/tpmlib/tpm2_constants.h @@ -58,8 +58,12 @@ #define TPM_ALG_LAST _AT(u16, 0x0044) /* Table 12 Definition of (UINT32) TPM_CC Constants (Numeric Order) */ +#define TPM_CC_GET_CAPABILITY _AT(u32, 0x0000017A) #define TPM_CC_PCR_EXTEND _AT(u32, 0x00000182) +/* Table 22 Definition of (UINT32) TPM_CAP Constants */ +#define TPM_CAP_PCRS _AT(u32, 0x00000005) + /* Table 19 Definition of (UINT16) TPM_ST Constants */ #define TPM_ST_NO_SESSIONS _AT(u16, 0x8001) #define TPM_ST_SESSIONS _AT(u16, 0x8002)