diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:49:52 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:49:52 +0000 |
commit | 55944e5e40b1be2afc4855d8d2baf4b73d1876b5 (patch) | |
tree | 33f869f55a1b149e9b7c2b7e201867ca5dd52992 /src/pcrlock/pcrlock-firmware.c | |
parent | Initial commit. (diff) | |
download | systemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.tar.xz systemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.zip |
Adding upstream version 255.4.upstream/255.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/pcrlock/pcrlock-firmware.c')
-rw-r--r-- | src/pcrlock/pcrlock-firmware.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/src/pcrlock/pcrlock-firmware.c b/src/pcrlock/pcrlock-firmware.c new file mode 100644 index 0000000..73c68c2 --- /dev/null +++ b/src/pcrlock/pcrlock-firmware.c @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <openssl/evp.h> + +#include "pcrlock-firmware.h" +#include "unaligned.h" + +static int tcg_pcr_event2_digests_size( + const TCG_EfiSpecIdEventAlgorithmSize *algorithms, + size_t n_algorithms, + size_t *ret) { + + size_t m = 0; + + assert(algorithms || n_algorithms == 0); + assert(ret); + + FOREACH_ARRAY(a, algorithms, n_algorithms) { + + if (a->digestSize > UINT32_MAX - offsetof(TPMT_HA, digest) - m) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Accumulated hash size too large"); + + m += offsetof(TPMT_HA, digest) + a->digestSize; + } + + *ret = m; + return 0; +} + +int validate_firmware_event( + const TCG_PCR_EVENT2 *event, + size_t left, + const TCG_EfiSpecIdEventAlgorithmSize *algorithms, + size_t n_algorithms, + const TCG_PCR_EVENT2 **ret_next_event, + size_t *ret_left, + const void **ret_payload, + size_t *ret_payload_size) { + + size_t digests_size; + int r; + + assert(event); + assert(algorithms || n_algorithms == 0); + assert(ret_next_event); + assert(ret_left); + + if (left == 0) { + *ret_next_event = NULL; + *ret_left = 0; + return 0; + } + + r = tcg_pcr_event2_digests_size(algorithms, n_algorithms, &digests_size); + if (r < 0) + return r; + + if (left < (uint64_t) offsetof(TCG_PCR_EVENT2, digests.digests) + (uint64_t) digests_size + sizeof(uint32_t)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event header too short."); + + if (event->digests.count != n_algorithms) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Number of digests in event doesn't match log."); + + uint32_t eventSize = unaligned_read_ne32((const uint8_t*) &event->digests.digests + digests_size); + uint64_t size = (uint64_t) offsetof(TCG_PCR_EVENT2, digests.digests) + (uint64_t) digests_size + sizeof(uint32_t) + eventSize; + + if (size > left) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event header too short."); + + *ret_next_event = (const TCG_PCR_EVENT2*) ((const uint8_t*) event + size); + *ret_left = left - size; + + if (ret_payload) + *ret_payload = (const uint8_t*) &event->digests.digests + digests_size + sizeof(uint32_t); + if (ret_payload_size) + *ret_payload_size = eventSize; + + return 1; +} + +int validate_firmware_header( + const void *start, + size_t size, + const TCG_EfiSpecIdEventAlgorithmSize **ret_algorithms, + size_t *ret_n_algorithms, + const TCG_PCR_EVENT2 **ret_first, + size_t *ret_left) { + + assert(start || size == 0); + assert(ret_algorithms); + assert(ret_n_algorithms); + assert(ret_first); + assert(ret_left); + + if (size < offsetof(TCG_PCClientPCREvent, event)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log too short for TCG_PCClientPCREvent."); + + const TCG_PCClientPCREvent *h = start; + + if (size < (uint64_t) offsetof(TCG_PCClientPCREvent, event) + (uint64_t) h->eventDataSize) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log too short for TCG_PCClientPCREvent events data."); + + if (h->pcrIndex != 0) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected PCR index %" PRIu32, h->pcrIndex); + if (h->eventType != EV_NO_ACTION) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected event type 0x%" PRIx32, h->eventType); + if (!memeqzero(h->digest, sizeof(h->digest))) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected non-zero digest."); + + if (h->eventDataSize < offsetof(TCG_EfiSpecIDEvent, digestSizes)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header too short for TCG_EfiSpecIdEvent."); + + const TCG_EfiSpecIDEvent *id = (const TCG_EfiSpecIDEvent*) h->event; + + /* Signature as per "TCG PC Client Specific Platform Firmware Profile Specification" + * (https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/), + * section 10.4.5.1 "Specification ID Version Event" (at least in version 1.05 Revision 23 of the + * spec) */ + if (memcmp(id->signature, + (const uint8_t[]) { 0x53, 0x70, 0x65, 0x63, 0x20, 0x49, 0x44, 0x20, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x33, 0x00 }, + sizeof(id->signature)) != 0) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing TPM2 event log signature."); + + if (id->numberOfAlgorithms <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Number of advertised hash algorithms is zero."); + if (id->numberOfAlgorithms > UINT32_MAX / sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Number of advertised hash algorithms too large."); + + log_debug("TPM PC Client Platform Firmware Profile: family %u.%u, revision %u.%u", + id->specVersionMajor, id->specVersionMinor, + id->specErrata / 100, id->specErrata % 100); + + if (h->eventDataSize < (uint64_t) offsetof(TCG_EfiSpecIDEvent, digestSizes) + (uint64_t) (id->numberOfAlgorithms * sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + 1U) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header doesn't fit all algorithms."); + + uint8_t vendorInfoSize = *((const uint8_t*) id + offsetof(TCG_EfiSpecIDEvent, digestSizes) + (id->numberOfAlgorithms * sizeof(TCG_EfiSpecIdEventAlgorithmSize))); + if (h->eventDataSize != offsetof(TCG_EfiSpecIDEvent, digestSizes) + (id->numberOfAlgorithms * sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + 1U + vendorInfoSize) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header doesn't fit vendor info."); + + for (size_t i = 0; i < id->numberOfAlgorithms; i++) { + const EVP_MD *implementation; + const char *a; + + a = tpm2_hash_alg_to_string(id->digestSizes[i].algorithmId); + if (!a) { + log_notice("Event log advertises unknown hash algorithm 0x%4x, can't validate.", id->digestSizes[i].algorithmId); + continue; + } + + implementation = EVP_get_digestbyname(a); + if (!implementation) { + log_notice("Event log advertises hash algorithm '%s' we don't implement, can't validate.", a); + continue; + } + + if (EVP_MD_size(implementation) != id->digestSizes[i].digestSize) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Advertised digest size for '%s' is wrong, refusing.", a); + } + + *ret_algorithms = id->digestSizes; + *ret_n_algorithms = id->numberOfAlgorithms; + + size_t offset = offsetof(TCG_PCClientPCREvent, event) + h->eventDataSize; + *ret_first = (TCG_PCR_EVENT2*) ((const uint8_t*) h + offset); + *ret_left = size - offset; + + return 0; +} |