169 lines
7.4 KiB
C
169 lines
7.4 KiB
C
/* 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;
|
|
}
|