This series of patches extends the TPM2 code to extend the BIOS related PCRs 0-7 in all available banks. This prevents that these PCRs remain untouched and filled with bogus values by applications. For example, the SHA1 hash is extended into the SHA256 bank. The value that is extended into this bank is essentially a SHA1 with zero bytes used for filling it to the size of a sha256 hash. This is done for all PCR banks of the TPM2 where these PCRs are available.
In v2 of this series I also extended the log functions for logging the additional hashes. So there are more patches now.
Regards, Stefan
Stefan Berger (6): tpm: Retrieve the PCR Bank configuration tpm: Restructure tpm20_extend to use buffer and take hash as parameter tpm: Extend tpm20_extend to support extending to multiple PCR banks tpm: Move tpm_log_init to a later point tpm: Adjust the TPM2 log header to show all hashes tpm: Append to TPM2 log the hashes used for PCR extension
src/std/tcg.h | 78 +++++++++++-- src/tcgbios.c | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 371 insertions(+), 55 deletions(-)
Implement tpm20_get_capability and retrieve the PCR Bank configuration from the TPM using this function.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/std/tcg.h | 29 +++++++++++++++++++++++++++++ src/tcgbios.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+)
diff --git a/src/std/tcg.h b/src/std/tcg.h index c59f671..d60ee09 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -394,12 +394,16 @@ struct tpm_res_sha1complete { #define TPM2_CC_SelfTest 0x143 #define TPM2_CC_Startup 0x144 #define TPM2_CC_StirRandom 0x146 +#define TPM2_CC_GetCapability 0x17a #define TPM2_CC_GetRandom 0x17b #define TPM2_CC_PCR_Extend 0x182
/* TPM 2 error codes */ #define TPM2_RC_INITIALIZE 0x100
+/* TPM 2 Capabilities */ +#define TPM2_CAP_PCRS 0x00000005 + /* TPM 2 data structures */
struct tpm2b_stir { @@ -475,6 +479,31 @@ struct tpm2_req_hierarchycontrol { u8 state; } PACKED;
+struct tpm2_req_getcapability { + struct tpm_req_header hdr; + u32 capability; + u32 property; + u32 propertycount; +} PACKED; + +struct tpm2_res_getcapability { + struct tpm_rsp_header hdr; + u8 moreData; + u32 capability; + u8 data[0]; /* capability dependent data */ +} PACKED; + +struct tpms_pcr_selection { + u16 hashAlg; + u8 sizeOfSelect; + u8 pcrSelect[0]; +} PACKED; + +struct tpml_pcr_selection { + u32 count; + struct tpms_pcr_selection selections[0]; +} PACKED; + /* TPM 2 log entry */
struct tpml_digest_values_sha1 { diff --git a/src/tcgbios.c b/src/tcgbios.c index 334d99b..8aa6942 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -24,6 +24,7 @@ #include "tcgbios.h"// tpm_*, prototypes #include "util.h" // printf, get_keystroke #include "stacks.h" // wait_threads, reset +#include "malloc.h" // malloc_high
/**************************************************************** * TPM 1.2 commands @@ -76,6 +77,9 @@ static int TPM_has_physical_presence;
static TPMVersion TPM_version;
+static u32 tpm20_pcr_selection_size; +static struct tpml_pcr_selection *tpm20_pcr_selection; + static struct tcpa_descriptor_rev2 * find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp) { @@ -181,6 +185,57 @@ tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event return 0; }
+static int +tpm20_getcapability(u32 capability, u32 property, u32 count, + struct tpm_rsp_header *rsp, u32 rsize) +{ + struct tpm2_req_getcapability trg = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trg)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability), + .capability = cpu_to_be32(capability), + .property = cpu_to_be32(property), + .propertycount = cpu_to_be32(count), + }; + + u32 resp_size = rsize; + int ret = tpmhw_transmit(0, &trg.hdr, rsp, &resp_size, + TPM_DURATION_TYPE_SHORT); + ret = (ret || + rsize < be32_to_cpu(rsp->totlen)) ? -1 : be32_to_cpu(rsp->errcode); + + dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n", + ret); + + return ret; +} + +static int +tpm20_get_pcrbanks(void) +{ + u8 buffer[128]; + struct tpm2_res_getcapability *trg = + (struct tpm2_res_getcapability *)&buffer; + + int ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr, + sizeof(buffer)); + if (ret) + return ret; + + u32 size = be32_to_cpu(trg->hdr.totlen) - + offsetof(struct tpm2_res_getcapability, data); + tpm20_pcr_selection = malloc_high(size); + if (tpm20_pcr_selection) { + memcpy(tpm20_pcr_selection, &trg->data, size); + tpm20_pcr_selection_size = size; + } else { + warn_noalloc(); + ret = -1; + } + + return ret; +} + /* * Initialize the log; a TPM2 log needs a special TPM 1.2 log entry * as the first entry serving identification purposes @@ -701,6 +756,10 @@ tpm20_startup(void) if (ret) goto err_exit;
+ ret = tpm20_get_pcrbanks(); + if (ret) + goto err_exit; + return 0;
err_exit:
On Tue, Jul 26, 2016 at 11:19:48AM -0400, Stefan Berger wrote:
Implement tpm20_get_capability and retrieve the PCR Bank configuration from the TPM using this function.
[...]
--- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -24,6 +24,7 @@ #include "tcgbios.h"// tpm_*, prototypes #include "util.h" // printf, get_keystroke #include "stacks.h" // wait_threads, reset +#include "malloc.h" // malloc_high
/****************************************************************
- TPM 1.2 commands
@@ -76,6 +77,9 @@ static int TPM_has_physical_presence;
static TPMVersion TPM_version;
+static u32 tpm20_pcr_selection_size; +static struct tpml_pcr_selection *tpm20_pcr_selection;
static struct tcpa_descriptor_rev2 * find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp) { @@ -181,6 +185,57 @@ tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event return 0; }
+static int +tpm20_getcapability(u32 capability, u32 property, u32 count,
struct tpm_rsp_header *rsp, u32 rsize)
The tcgbios.c file is getting pretty large - 2200 lines after this series. This patch adds tpm20_getcapability() in the section of the file currently labeled "ACPI TCPA table interface", but it seems very similar to tpm12_getcapability() and to other wrappers around tpmhw_transmit() that are located elsewhere in the C file.
I think it would help if the code could be segmented into like functionality, or split into multiple C files of like functionality if necessary.
-Kevin
On 07/28/2016 12:38 PM, Kevin O'Connor wrote:
On Tue, Jul 26, 2016 at 11:19:48AM -0400, Stefan Berger wrote:
Implement tpm20_get_capability and retrieve the PCR Bank configuration from the TPM using this function.
[...]
--- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -24,6 +24,7 @@ #include "tcgbios.h"// tpm_*, prototypes #include "util.h" // printf, get_keystroke #include "stacks.h" // wait_threads, reset +#include "malloc.h" // malloc_high
/****************************************************************
- TPM 1.2 commands
@@ -76,6 +77,9 @@ static int TPM_has_physical_presence;
static TPMVersion TPM_version;
+static u32 tpm20_pcr_selection_size; +static struct tpml_pcr_selection *tpm20_pcr_selection;
- static struct tcpa_descriptor_rev2 * find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp) {
@@ -181,6 +185,57 @@ tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event return 0; }
+static int +tpm20_getcapability(u32 capability, u32 property, u32 count,
struct tpm_rsp_header *rsp, u32 rsize)
The tcgbios.c file is getting pretty large - 2200 lines after this series. This patch adds tpm20_getcapability() in the section of the file currently labeled "ACPI TCPA table interface", but it seems very similar to tpm12_getcapability() and to other wrappers around tpmhw_transmit() that are located elsewhere in the C file.
I think it would help if the code could be segmented into like functionality, or split into multiple C files of like functionality if necessary.
I can try to move this function elsewhere and add a function prototype if necessary.
Regarding the split up. We could externalize the ACPI and log stuff into their own files. The rest has API functions, functions that are called internally, and then TPM 1.2 and TPM 2 functions. I am not sure how to split this up.
Stefan
-Kevin
Restructure the tpm20_extend function to use a buffer for the command to send to the TPM. The size of the buffer is calculated from the size of tpm2_req_extend structure and the appended SHA1 hash.
Add the hash algorithm that's being used as a parameter to this function.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/std/tcg.h | 2 +- src/tcgbios.c | 31 +++++++++++++++++-------------- 2 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/src/std/tcg.h b/src/std/tcg.h index d60ee09..1644684 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -442,7 +442,6 @@ struct tpm2_req_hierarchychangeauth { } PACKED;
struct tpm2_digest_value { - u32 count; /* 1 entry only */ u16 hashalg; /* TPM2_ALG_SHA1 */ u8 sha1[SHA1_BUFSIZE]; } PACKED; @@ -452,6 +451,7 @@ struct tpm2_req_extend { u32 pcrindex; u32 authblocksize; struct tpm2_authblock authblock; + u32 count; struct tpm2_digest_value digest; } PACKED;
diff --git a/src/tcgbios.c b/src/tcgbios.c index 8aa6942..204f5ad 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -497,30 +497,33 @@ tpm12_extend(u32 pcrindex, const u8 *digest) return 0; }
-static int tpm20_extend(u32 pcrindex, const u8 *digest) +static int tpm20_extend(u32 pcrindex, const u8 *digest, u16 hashAlg) { - struct tpm2_req_extend tre = { + struct tpm2_req_extend tmp_tre = { .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), - .hdr.totlen = cpu_to_be32(sizeof(tre)), + .hdr.totlen = cpu_to_be32(sizeof(tmp_tre)), .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend), .pcrindex = cpu_to_be32(pcrindex), - .authblocksize = cpu_to_be32(sizeof(tre.authblock)), + .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)), .authblock = { .handle = cpu_to_be32(TPM2_RS_PW), .noncesize = cpu_to_be16(0), .contsession = TPM2_YES, .pwdsize = cpu_to_be16(0), }, - .digest = { - .count = cpu_to_be32(1), - .hashalg = cpu_to_be16(TPM2_ALG_SHA1), - }, }; - memcpy(tre.digest.sha1, digest, sizeof(tre.digest.sha1)); + u32 count = 1; + u8 buffer[sizeof(tmp_tre) + sizeof(struct tpm2_digest_value)]; + struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer; + + memcpy(tre, &tmp_tre, sizeof(tmp_tre)); + tre->count = cpu_to_be32(count); + tre->digest.hashalg = cpu_to_be16(hashAlg); + memcpy(tre->digest.sha1, digest, sizeof(tmp_tre.digest.sha1));
struct tpm_rsp_header rsp; u32 resp_length = sizeof(rsp); - int ret = tpmhw_transmit(0, &tre.hdr, &rsp, &resp_length, + int ret = tpmhw_transmit(0, &tre->hdr, &rsp, &resp_length, TPM_DURATION_TYPE_SHORT); if (ret || resp_length != sizeof(rsp) || rsp.errcode) return -1; @@ -529,13 +532,13 @@ static int tpm20_extend(u32 pcrindex, const u8 *digest) }
static int -tpm_extend(u32 pcrindex, const u8 *digest) +tpm_extend(u32 pcrindex, const u8 *digest, u16 hashAlg) { switch (TPM_version) { case TPM_VERSION_1_2: return tpm12_extend(pcrindex, digest); case TPM_VERSION_2: - return tpm20_extend(pcrindex, digest); + return tpm20_extend(pcrindex, digest, hashAlg); } return -1; } @@ -568,7 +571,7 @@ tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, .digests[0].hashtype = TPM2_ALG_SHA1, }; sha1(hashdata, hashdata_length, entry.digests[0].sha1); - int ret = tpm_extend(entry.pcrindex, entry.digests[0].sha1); + int ret = tpm_extend(entry.pcrindex, entry.digests[0].sha1, TPM2_ALG_SHA1); if (ret) { tpm_set_failure(); return; @@ -1099,7 +1102,7 @@ hash_log_extend(struct pcpes *pcpes, const void *hashdata, u32 hashdata_length if (hashdata) sha1(hashdata, hashdata_length, pcpes->digest); if (extend) { - int ret = tpm_extend(pcpes->pcrindex, pcpes->digest); + int ret = tpm_extend(pcpes->pcrindex, pcpes->digest, TPM2_ALG_SHA1); if (ret) return TCG_TCG_COMMAND_ERROR; }
Extend the tpm20_extend function to support extending a hash to multiple PCR banks. The sha1 hash that's being extended into the sha256 bank for example, will be filled with zero-bytes to the size of a sha256 hash.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
v1->v2: - renamed function to tpm2_write_tpml_dig_values - write the count value inside the function - pass size of given fixed-sized buffer and check that it is large enough --- src/std/tcg.h | 29 ++++++++++++-- src/tcgbios.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 138 insertions(+), 10 deletions(-)
diff --git a/src/std/tcg.h b/src/std/tcg.h index 1644684..3500867 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -100,6 +100,19 @@ enum irq_ids { #define EV_IPL_PARTITION_DATA 14
#define SHA1_BUFSIZE 20 +#define SHA256_BUFSIZE 32 +#define SHA384_BUFSIZE 48 +#define SHA512_BUFSIZE 64 +#define SM3_256_BUFSIZE 32 + +/* maximum size the tpml_digest_values structure can have with these hashes */ +#define MAX_TPML_DIGEST_VALUES_SIZE \ + (4 + /* u32 count */ \ + 2 + SHA1_BUFSIZE + \ + 2 + SHA256_BUFSIZE + \ + 2 + SHA384_BUFSIZE + \ + 2 + SHA512_BUFSIZE + \ + 2 + SM3_256_BUFSIZE)
/* Input and Output blocks for the TCG BIOS commands */
@@ -381,6 +394,10 @@ struct tpm_res_sha1complete { #define TPM2_RH_PLATFORM 0x4000000c
#define TPM2_ALG_SHA1 0x0004 +#define TPM2_ALG_SHA256 0x000b +#define TPM2_ALG_SHA384 0x000c +#define TPM2_ALG_SHA512 0x000d +#define TPM2_ALG_SM3_256 0x0012
/* TPM 2 command tags */ #define TPM2_ST_NO_SESSIONS 0x8001 @@ -442,8 +459,13 @@ struct tpm2_req_hierarchychangeauth { } PACKED;
struct tpm2_digest_value { - u16 hashalg; /* TPM2_ALG_SHA1 */ - u8 sha1[SHA1_BUFSIZE]; + u16 hashAlg; /* TPM2_ALG_SHA1 */ + u8 hash[0]; /* size depends on hashAlg */ +} PACKED; + +struct tpml_digest_values { + u32 count; + struct tpm2_digest_value digest[0]; } PACKED;
struct tpm2_req_extend { @@ -451,8 +473,7 @@ struct tpm2_req_extend { u32 pcrindex; u32 authblocksize; struct tpm2_authblock authblock; - u32 count; - struct tpm2_digest_value digest; + u8 data[0]; /* struct tpml_digest_values digests; */ } PACKED;
struct tpm2_req_clearcontrol { diff --git a/src/tcgbios.c b/src/tcgbios.c index 204f5ad..206c296 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -26,6 +26,8 @@ #include "stacks.h" // wait_threads, reset #include "malloc.h" // malloc_high
+#define min(a,b) ((a) < (b)) ? (a) : (b) + /**************************************************************** * TPM 1.2 commands ****************************************************************/ @@ -80,6 +82,107 @@ static TPMVersion TPM_version; static u32 tpm20_pcr_selection_size; static struct tpml_pcr_selection *tpm20_pcr_selection;
+static int +tpm20_get_hash_buffersize(u16 hashAlg) +{ + switch (hashAlg) { + case TPM2_ALG_SHA1: + return SHA1_BUFSIZE; + case TPM2_ALG_SHA256: + return SHA256_BUFSIZE; + case TPM2_ALG_SHA384: + return SHA384_BUFSIZE; + case TPM2_ALG_SHA512: + return SHA512_BUFSIZE; + case TPM2_ALG_SM3_256: + return SM3_256_BUFSIZE; + default: + return -1; + } +} + +/* + * Write the TPM2 tpml_digest_values data structure from the given hash. + * Follow the PCR bank configuration of the TPM and write the same hash + * in either truncated or zero-padded form in the areas of all the other + * hashes. For example, write the sha1 hash in the area of the sha256 + * hash and fill the remaining bytes with zeros. Or truncate the sha256 + * hash when writing it in the area of the sha1 hash. + * + * dest: destination buffer to write into; if NULL, nothing is written + * destlen: length of destination buffer + * pcrindex: the PCR index + * hash: the hash value to write + * hashAlg: the hash alogorithm determining the size of the hash + * count: pointer to a counter for how many entries were writte + * + * Returns the number of bytes needed in the buffer; -1 on fatal error + */ +static int +tpm20_write_tpml_dig_values(u8 *dest, size_t destlen, u32 pcrindex, + const u8 *hash, u16 hashAlg) +{ + if (!tpm20_pcr_selection) + return -1; + + int hsize; + struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; + void *nsel, *end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size; + int hash_size = tpm20_get_hash_buffersize(hashAlg); + int offset = offsetof(struct tpml_digest_values, digest); + u32 count; + + for (count = 0; + count < be32_to_cpu(tpm20_pcr_selection->count); + count++) { + u8 sizeOfSelect = sel->sizeOfSelect; + + nsel = (void *)sel + + offsetof(struct tpms_pcr_selection, pcrSelect) + + sizeOfSelect; + if (nsel > end) + break; + + u8 idx = pcrindex >> 3; + u8 mask = (1 << (pcrindex & 7)); + /* skip if PCR not used in this bank */ + if (idx > sizeOfSelect || !(sel->pcrSelect[idx] & mask)) + continue; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf(DEBUG_tcg, "TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + if (dest) { + struct tpm2_digest_value *v = + (struct tpm2_digest_value *)&dest[offset]; + v->hashAlg = sel->hashAlg; + memset(v->hash, 0, hsize); + memcpy(v->hash, hash, min(hash_size, hsize)); + } + offset += sizeof(sel->hashAlg) + hsize; + sel = nsel; + } + + if (sel != end) { + dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n"); + return -1; + } + + if (dest && offset > destlen) + panic("buffer for tpml_digest_values is too small\n"); + + if (dest) { + struct tpml_digest_values *v = (struct tpml_digest_values *)dest; + v->count = cpu_to_be32(count); + } + + return offset; +} + static struct tcpa_descriptor_rev2 * find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp) { @@ -501,7 +604,7 @@ static int tpm20_extend(u32 pcrindex, const u8 *digest, u16 hashAlg) { struct tpm2_req_extend tmp_tre = { .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), - .hdr.totlen = cpu_to_be32(sizeof(tmp_tre)), + .hdr.totlen = cpu_to_be32(0), .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend), .pcrindex = cpu_to_be32(pcrindex), .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)), @@ -512,14 +615,18 @@ static int tpm20_extend(u32 pcrindex, const u8 *digest, u16 hashAlg) .pwdsize = cpu_to_be16(0), }, }; - u32 count = 1; - u8 buffer[sizeof(tmp_tre) + sizeof(struct tpm2_digest_value)]; + u8 buffer[sizeof(tmp_tre) + MAX_TPML_DIGEST_VALUES_SIZE]; struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer;
memcpy(tre, &tmp_tre, sizeof(tmp_tre)); - tre->count = cpu_to_be32(count); - tre->digest.hashalg = cpu_to_be16(hashAlg); - memcpy(tre->digest.sha1, digest, sizeof(tmp_tre.digest.sha1)); + + int size = tpm20_write_tpml_dig_values(&tre->data[0], + sizeof(buffer) - offsetof(struct tpm2_req_extend, data), + pcrindex, digest, hashAlg); + if (size < 0) + return size; + + tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + size);
struct tpm_rsp_header rsp; u32 resp_length = sizeof(rsp);
On Tue, Jul 26, 2016 at 11:19:50AM -0400, Stefan Berger wrote:
Extend the tpm20_extend function to support extending a hash to multiple PCR banks. The sha1 hash that's being extended into the sha256 bank for example, will be filled with zero-bytes to the size of a sha256 hash.
[...]
+/*
- Write the TPM2 tpml_digest_values data structure from the given hash.
- Follow the PCR bank configuration of the TPM and write the same hash
- in either truncated or zero-padded form in the areas of all the other
- hashes. For example, write the sha1 hash in the area of the sha256
- hash and fill the remaining bytes with zeros. Or truncate the sha256
- hash when writing it in the area of the sha1 hash.
- dest: destination buffer to write into; if NULL, nothing is written
- destlen: length of destination buffer
- pcrindex: the PCR index
- hash: the hash value to write
- hashAlg: the hash alogorithm determining the size of the hash
- count: pointer to a counter for how many entries were writte
- Returns the number of bytes needed in the buffer; -1 on fatal error
- */
+static int +tpm20_write_tpml_dig_values(u8 *dest, size_t destlen, u32 pcrindex,
const u8 *hash, u16 hashAlg)
So, if I understand this correctly, the current code creates a "digest" with just a sha1 hash. However, the hardware has a description of what the digest should look like, and this patch takes the current digest and produces the digest format desired by the hardware. Patch 6 does the same for the log.
If so, could the code instead build the digest according to the hardware description instead of trying to reformat it after it is built? Specifically, the only callers of tpm_extend() and tpm_log_event() produce a 'struct tcg_pcr_event2_sha1' with the digest in the simple hash format - could these locations create tcg_pcr_event2_sha1 in the desired hardware specified format initially and thus avoid needing to reformat that digest?
[...]
- if (dest && offset > destlen)
panic("buffer for tpml_digest_values is too small\n");
panic() should be avoided. On real hardware if the BIOS were to panic() it could effectively brick the machine.
-Kevin
On 07/28/2016 12:54 PM, Kevin O'Connor wrote:
On Tue, Jul 26, 2016 at 11:19:50AM -0400, Stefan Berger wrote:
Extend the tpm20_extend function to support extending a hash to multiple PCR banks. The sha1 hash that's being extended into the sha256 bank for example, will be filled with zero-bytes to the size of a sha256 hash.
[...]
+/*
- Write the TPM2 tpml_digest_values data structure from the given hash.
- Follow the PCR bank configuration of the TPM and write the same hash
- in either truncated or zero-padded form in the areas of all the other
- hashes. For example, write the sha1 hash in the area of the sha256
- hash and fill the remaining bytes with zeros. Or truncate the sha256
- hash when writing it in the area of the sha1 hash.
- dest: destination buffer to write into; if NULL, nothing is written
- destlen: length of destination buffer
- pcrindex: the PCR index
- hash: the hash value to write
- hashAlg: the hash alogorithm determining the size of the hash
- count: pointer to a counter for how many entries were writte
- Returns the number of bytes needed in the buffer; -1 on fatal error
- */
+static int +tpm20_write_tpml_dig_values(u8 *dest, size_t destlen, u32 pcrindex,
const u8 *hash, u16 hashAlg)
So, if I understand this correctly, the current code creates a "digest" with just a sha1 hash. However, the hardware has a description of what the digest should look like, and this patch takes the current digest and produces the digest format desired by the hardware. Patch 6 does the same for the log.
The TPM 2 supports multiple PCR banks with SHA1 , SHA256, SHA384 etc. Multiple of these banks can be active at the same time. To avoid abuse of these banks during attestation (filling them with bogus values and then quoting), we want to use all banks. Since we don't support hashing the data will all these different hashes at the same time, we would use the SHA 1 digest of the data for extension in the SHA256 bank etc.
If so, could the code instead build the digest according to the hardware description instead of trying to reformat it after it is built? Specifically, the only callers of tpm_extend() and tpm_log_event() produce a 'struct tcg_pcr_event2_sha1' with the digest in the simple hash format - could these locations create tcg_pcr_event2_sha1 in the desired hardware specified format initially and thus avoid needing to reformat that digest?
Yes, that would also be possible.
[...]
- if (dest && offset > destlen)
panic("buffer for tpml_digest_values is too small\n");
panic() should be avoided. On real hardware if the BIOS were to panic() it could effectively brick the machine.
Assuming I calculated the size correct, this error should never happen. So I have to remove this error report altogether or can I convert it into a print statement? I introduced this since I am not calculating the buffer size anymore...
Stefan
-Kevin
Move tpm_log_init to a point after tpm_startup since in the subsequent steps we will need to be able to call TPM2_GetCapabilties from this function to learn about the TPM's PCR bank configuration. We can move it there, since tpm_startup does not write anything into the log.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/tcgbios.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/tcgbios.c b/src/tcgbios.c index 206c296..1eaf45e 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -909,17 +909,19 @@ tpm_setup(void) if (ret) return;
- tpm_log_init(); - TPM_working = 1;
- if (runningOnXen()) + if (runningOnXen()) { + tpm_log_init(); return; + }
ret = tpm_startup(); if (ret) return;
+ tpm_log_init(); + tpm_smbios_measure(); tpm_add_action(2, "Start Option ROM Scan"); }
Adjust the TPM2's log header event to show all hashes that are found in the subsequent log entries.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/std/tcg.h | 18 ++++++---- src/tcgbios.c | 112 +++++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 99 insertions(+), 31 deletions(-)
diff --git a/src/std/tcg.h b/src/std/tcg.h index 3500867..c3b0632 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -541,6 +541,14 @@ struct tcg_pcr_event2_sha1 { u8 event[0]; } PACKED;
+struct TCG_EfiSpecIdEventAlgorithms { + u32 numberOfAlgorithms; + struct TCG_EfiSpecIDEventAlgorithmSize { + u16 algorithmId; + u16 digestSize; + } digestSizes[0]; +} PACKED; + struct TCG_EfiSpecIdEventStruct { u8 signature[16]; u32 platformClass; @@ -548,14 +556,12 @@ struct TCG_EfiSpecIdEventStruct { u8 specVersionMajor; u8 specErrata; u8 uintnSize; - u32 numberOfAlgorithms; - struct TCG_EfiSpecIdEventAlgorithmSize { - u16 algorithmId; - u16 digestSize; - } digestSizes[1]; + struct TCG_EfiSpecIdEventAlgorithms algs; + /* u8 vendorInfoSize; u8 vendorInfo[0]; -}; + */ +} PACKED;
#define TPM_TCPA_ACPI_CLASS_CLIENT 0
diff --git a/src/tcgbios.c b/src/tcgbios.c index 1eaf45e..80466b5 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -183,6 +183,78 @@ tpm20_write_tpml_dig_values(u8 *dest, size_t destlen, u32 pcrindex, return offset; }
+int +tpm20_write_EfiSpecIdEventStruct(char *dest, size_t destlen) +{ + if (!tpm20_pcr_selection) + return -1; + + struct TCG_EfiSpecIdEventStruct temp = { + .signature = "Spec ID Event03", + .platformClass = TPM_TCPA_ACPI_CLASS_CLIENT, + .specVersionMinor = 0, + .specVersionMajor = 2, + .specErrata = 0, + .uintnSize = 2, + .algs = { + .numberOfAlgorithms = 0, /* fill below */ + }, + }, *event = (struct TCG_EfiSpecIdEventStruct *)dest; + + int hsize; + struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; + void *nsel, *end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size; + struct TCG_EfiSpecIdEventAlgorithms *algs = &event->algs; + + int offset = offsetof(struct TCG_EfiSpecIdEventStruct, + algs.digestSizes[0]); + u32 count; + + memcpy(event, &temp, offset); + + for (count = 0; + count < be32_to_cpu(tpm20_pcr_selection->count); + count++) { + u8 sizeOfSelect = sel->sizeOfSelect; + + nsel = (void *)sel + + offsetof(struct tpms_pcr_selection, pcrSelect) + + sizeOfSelect; + if (nsel > end) + break; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf(DEBUG_tcg, "TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + algs->digestSizes[count].algorithmId = be16_to_cpu(sel->hashAlg); + algs->digestSizes[count].digestSize = hsize; + + offset += sizeof(algs->digestSizes[0]); + + sel = nsel; + } + + if (sel != end) { + dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n"); + return -1; + } + event->algs.numberOfAlgorithms = count; + + u32 *vendorInfoSize = (u32 *)&dest[offset]; + *vendorInfoSize = 0; + offset += sizeof(*vendorInfoSize); + + if (dest && offset > destlen) + panic("buffer for log event is too small (%d < %d)\n", + destlen, offset); + + return offset; +} + static struct tcpa_descriptor_rev2 * find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp) { @@ -346,31 +418,25 @@ tpm20_get_pcrbanks(void) static void tpm_log_init(void) { - struct TCG_EfiSpecIdEventStruct event = { - .signature = "Spec ID Event03", - .platformClass = TPM_TCPA_ACPI_CLASS_CLIENT, - .specVersionMinor = 0, - .specVersionMajor = 2, - .specErrata = 0, - .uintnSize = 2, - .numberOfAlgorithms = 1, - .digestSizes[0] = { - .algorithmId = TPM2_ALG_SHA1, - .digestSize = SHA1_BUFSIZE, - }, - .vendorInfoSize = 0, - }; - struct tcg_pcr_event2_sha1 entry = { - .eventtype = EV_NO_ACTION, - .eventdatasize = sizeof(event), - }; - switch (TPM_version) { case TPM_VERSION_1_2: break; - case TPM_VERSION_2: + case TPM_VERSION_2: ; + int ret = tpm20_get_pcrbanks(); + /* if this failed, no entry will be written */ + if (ret) + return; + /* write a 1.2 type of entry */ - tpm_log_event(&entry, &event, TPM_VERSION_1_2); + char buf[256]; + ret = tpm20_write_EfiSpecIdEventStruct(buf, sizeof(buf)); + if (ret < 0) + return; + struct tcg_pcr_event2_sha1 entry = { + .eventtype = EV_NO_ACTION, + .eventdatasize = ret, + }; + tpm_log_event(&entry, buf, TPM_VERSION_1_2); } }
@@ -866,10 +932,6 @@ tpm20_startup(void) if (ret) goto err_exit;
- ret = tpm20_get_pcrbanks(); - if (ret) - goto err_exit; - return 0;
err_exit:
Extend the function that writes the TPM2 log to write those hashes into the log that were also extended into PCRs.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/std/tcg.h | 2 +- src/tcgbios.c | 39 +++++++++++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/src/std/tcg.h b/src/std/tcg.h index c3b0632..86e41f7 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -528,6 +528,7 @@ struct tpml_pcr_selection { /* TPM 2 log entry */
struct tpml_digest_values_sha1 { + u32 count; /* number of digests */ u16 hashtype; u8 sha1[SHA1_BUFSIZE]; }; @@ -535,7 +536,6 @@ struct tpml_digest_values_sha1 { struct tcg_pcr_event2_sha1 { u32 pcrindex; u32 eventtype; - u32 count; /* number of digests */ struct tpml_digest_values_sha1 digests[1]; u32 eventdatasize; u8 event[0]; diff --git a/src/tcgbios.c b/src/tcgbios.c index 80466b5..6e0bbd2 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -327,8 +327,24 @@ tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event
if (tpm_state.log_area_next_entry == NULL) return -1; + int tpml_size = 0; + + u32 size = 0; + switch (tpm_version) { + case TPM_VERSION_1_2: + size = sizeof(*entry) + entry->eventdatasize; + break; + case TPM_VERSION_2: ; + tpml_size = tpm20_write_tpml_dig_values(NULL, 0, entry->pcrindex, + entry->digests[0].sha1, + entry->digests[0].hashtype); + if (tpml_size < 0) + return -1; + size = offsetof(struct tcg_pcr_event2_sha1, digests) + tpml_size + + sizeof(entry->eventdatasize) + entry->eventdatasize; + break; + }
- u32 size = sizeof(*entry) + entry->eventdatasize; u32 logsize = (tpm_state.log_area_next_entry + size - tpm_state.log_area_start_address); if (logsize > tpm_state.log_area_minimum_length) { @@ -347,9 +363,20 @@ tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event size = sizeof(*pcpes) + entry->eventdatasize; break; case TPM_VERSION_2: ; - struct tcg_pcr_event2_sha1 *e = (void*)tpm_state.log_area_next_entry; - memcpy(e, entry, sizeof(*e)); - memcpy(e->event, event, entry->eventdatasize); + u8 *dest = tpm_state.log_area_next_entry; + + unsigned int offset = offsetof(struct tcg_pcr_event2_sha1, digests); + + memcpy(dest, entry, offset); + offset += tpm20_write_tpml_dig_values(&dest[offset], tpml_size, + entry->pcrindex, + entry->digests[0].sha1, + entry->digests[0].hashtype); + u32 *eventdatasize = (u32 *)&dest[offset]; + *eventdatasize = entry->eventdatasize; + offset += sizeof(entry->eventdatasize); + + memcpy(&dest[offset], event, entry->eventdatasize); break; }
@@ -740,7 +767,7 @@ tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, .pcrindex = pcrindex, .eventtype = event_type, .eventdatasize = event_length, - .count = 1, + .digests[0].count = 1, .digests[0].hashtype = TPM2_ALG_SHA1, }; sha1(hashdata, hashdata_length, entry.digests[0].sha1); @@ -1281,7 +1308,7 @@ hash_log_extend(struct pcpes *pcpes, const void *hashdata, u32 hashdata_length .pcrindex = pcpes->pcrindex, .eventtype = pcpes->eventtype, .eventdatasize = pcpes->eventdatasize, - .count = 1, + .digests[0].count = 1, .digests[0].hashtype = TPM2_ALG_SHA1, }; memcpy(entry.digests[0].sha1, pcpes->digest, sizeof(entry.digests[0].sha1));