summaryrefslogtreecommitdiffstats
path: root/src/pcrlock/pcrlock-firmware.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/pcrlock/pcrlock-firmware.c168
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;
+}