Message ID | 20201016120113.29486-2-ivan.hu@canonical.com |
---|---|
State | Superseded |
Headers | show |
Series | [1/2] lib: fwts_tpm: move some tpm funcitons to lib | expand |
On 16/10/2020 13:01, Ivan Hu wrote: > Signed-off-by: Ivan Hu <ivan.hu@canonical.com> > --- > src/Makefile.am | 1 + > src/tpm/tpmevlog/tpmevlog.c | 441 ++++++++++++++++++++++++++++++++++++ > 2 files changed, 442 insertions(+) > create mode 100644 src/tpm/tpmevlog/tpmevlog.c > > diff --git a/src/Makefile.am b/src/Makefile.am > index 5d78411b..b7f3f7db 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -184,6 +184,7 @@ fwts_SOURCES = main.c \ > pci/aspm/aspm.c \ > pci/crs/crs.c \ > pci/maxreadreq/maxreadreq.c \ > + tpm/tpmevlog/tpmevlog.c \ > tpm/tpmevlogdump/tpmevlogdump.c \ > uefi/csm/csm.c \ > uefi/uefidump/uefidump.c \ > diff --git a/src/tpm/tpmevlog/tpmevlog.c b/src/tpm/tpmevlog/tpmevlog.c > new file mode 100644 > index 00000000..1d184fd0 > --- /dev/null > +++ b/src/tpm/tpmevlog/tpmevlog.c > @@ -0,0 +1,441 @@ > +/* > + * Copyright (C) 2020 Canonical > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > + * > + */ > +#include "fwts.h" > + > +#include <sys/types.h> > +#include <dirent.h> > +#include <errno.h> > +#include <sys/stat.h> > +#include <fcntl.h> > + > +#include "fwts_tpm.h" > + > +#define FWTS_TPM_LOG_DIR_PATH "/sys/kernel/security" > + > +static int tpmevlog_pcrindex_value_check(fwts_framework *fw, uint32_t pcr) Maybe may pcr a const uint32_t > +{ > + > + if ((pcr > 16) && (pcr != 23)) { What are magic values 16 and 23 referring to? Perhaps some #defined values for this would help. > + fwts_failed(fw, LOG_LEVEL_HIGH, "PCRIndexValue", > + "The PCR Index value is undefined, 0x%8.8" PRIx32 ".", > + pcr); > + return FWTS_ERROR; > + } > + > + return FWTS_OK; > +} > + > +static int tpmevlog_eventtype_check(fwts_framework *fw, fwts_tpmlog_event_type event_type) Maybe make event_type const too > +{ > + > + switch (event_type) { > + case EV_PREBOOT_CERT: > + case EV_POST_CODE: > + case EV_UNUSED: > + case EV_NO_ACTION: > + case EV_SEPARATOR: > + case EV_ACTION: > + case EV_EVENT_TAG: > + case EV_S_CRTM_CONTENTS: > + case EV_S_CRTM_VERSION: > + case EV_CPU_MICROCODE: > + case EV_PLATFORM_CONFIG_FLAGS: > + case EV_TABLE_OF_DEVICES: > + case EV_IPL: > + case EV_IPL_PARTITION_DATA: > + case EV_NONHOST_CODE: > + case EV_NONHOST_CONFIG: > + case EV_NONHOST_INFO: > + case EV_OMIT_BOOT_DEVICE_EVENTS: > + case EV_EFI_EVENT_BASE: > + case EV_EFI_VARIABLE_DRIVER_CONFIG: > + case EV_EFI_VARIABLE_BOOT: > + case EV_EFI_BOOT_SERVICES_APPLICATION: > + case EV_EFI_BOOT_SERVICES_DRIVER: > + case EV_EFI_RUNTIME_SERVICES_DRIVER: > + case EV_EFI_GPT_EVENT: > + case EV_EFI_ACTION: > + case EV_EFI_PLATFORM_FIRMWARE_BLOB: > + case EV_EFI_HANDOFF_TABLES: > + case EV_EFI_HCRTM_EVENT: > + case EV_EFI_VARIABLE_AUTHORITY: > + return FWTS_OK; > + default: > + fwts_failed(fw, LOG_LEVEL_HIGH, "PCREventType", > + "The Event Type is undefined, 0x%8.8" PRIx32 ".", > + event_type); > + return FWTS_ERROR; > + } > + > + return FWTS_OK; > +} > + > +static int tpmevlog_algid_check(fwts_framework *fw, TPM2_ALG_ID hash) maybe make hash const > +{ > + > + switch (hash) { > + case TPM2_ALG_RSA: > + case TPM2_ALG_TDES: > + case TPM2_ALG_SHA1: > + case TPM2_ALG_HMAC: > + case TPM2_ALG_AES: > + case TPM2_ALG_MGF1: > + case TPM2_ALG_KEYEDHASH: > + case TPM2_ALG_XOR: > + case TPM2_ALG_SHA256: > + case TPM2_ALG_SHA384: > + case TPM2_ALG_SHA512: > + case TPM2_ALG_NULL: > + case TPM2_ALG_SM3_256: > + case TPM2_ALG_SM4: > + case TPM2_ALG_RSASSA: > + case TPM2_ALG_RSAES: > + case TPM2_ALG_RSAPSS: > + case TPM2_ALG_OAEP: > + case TPM2_ALG_ECDSA: > + case TPM2_ALG_ECDH: > + case TPM2_ALG_ECDAA: > + case TPM2_ALG_SM2: > + case TPM2_ALG_ECSCHNORR: > + case TPM2_ALG_ECMQV: > + case TPM2_ALG_KDF1_SP800_56A: > + case TPM2_ALG_KDF2: > + case TPM2_ALG_KDF1_SP800_108: > + case TPM2_ALG_ECC: > + case TPM2_ALG_SYMCIPHER: > + case TPM2_ALG_CAMELLIA: > + case TPM2_ALG_CMAC: > + case TPM2_ALG_CTR: > + case TPM2_ALG_SHA3_256: > + case TPM2_ALG_SHA3_384: > + case TPM2_ALG_SHA3_512: > + case TPM2_ALG_OFB: > + case TPM2_ALG_CBC: > + case TPM2_ALG_CFB: > + case TPM2_ALG_ECB: > + return FWTS_OK; > + default: > + fwts_failed(fw, LOG_LEVEL_HIGH, "AlgorithmID", > + "The AlgorithmID is undefined, 0x%4.4" PRIx16 ".", > + hash); > + return FWTS_ERROR; > + } > + > + return FWTS_OK; > +} > + > +static int tpmevlog_v2_check(fwts_framework *fw, uint8_t *data, size_t len> +{ > + int ret = FWTS_OK; > + size_t len_remain = len; > + uint8_t *pdata = data; > + int i = 0; > + uint8_t vendor_info_size = 0; > + uint8_t hash_size = 0; > + uint32_t event_size = 0; > + > + /* specid_event_check */ > + if (len < sizeof(fwts_pc_client_pcr_event)) { > + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength", > + "The length of the Specid event is %zd bytes " > + "is smaller than the PCClientPCREvent %zd bytes.", > + len_remain, > + sizeof(fwts_pc_client_pcr_event)); > + return FWTS_ERROR; > + } > + > + fwts_pc_client_pcr_event *pc_event = (fwts_pc_client_pcr_event *)pdata; > + ret = tpmevlog_pcrindex_value_check(fw, pc_event->pcr_index); > + if (ret != FWTS_OK) > + return ret; > + ret = tpmevlog_eventtype_check(fw, pc_event->event_type); > + if (ret != FWTS_OK) > + return ret; > + for (i = 0; i < TPM2_SHA1_DIGEST_SIZE; i++) { > + if (pc_event->digest[i] != 0) { > + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvDigest", > + "The digest filed of SpecId event should be all zero."); > + fwts_tpm_data_hexdump(fw, pc_event->digest, sizeof(pc_event->digest), "Digest"); > + } > + } > + > + pdata += sizeof(fwts_pc_client_pcr_event); > + len_remain -= sizeof(fwts_pc_client_pcr_event); > + > + /* check the data length specid event */ > + if (len_remain < sizeof(fwts_efi_spec_id_event)) { > + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength", > + "The length of the Specid event is %zd bytes " > + "is smaller than the SpecId event %zd bytes.", > + len_remain, > + sizeof(fwts_efi_spec_id_event)); > + return FWTS_ERROR; > + } > + > + fwts_efi_spec_id_event *specid_evcent = (fwts_efi_spec_id_event *)pdata; > + if (strcmp((char *)specid_evcent->signature, FWTS_TPM_EVENTLOG_V2_SIGNATURE) != 0) { > + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvSignature", > + "The signature of SpecId event is not the same as expected " > + "Spec ID Event03, got %s.", > + (char *)specid_evcent->signature); > + return FWTS_ERROR; > + } > + > + /* > + * Check the platform class value which defined in TCG ACPI Specification, > + * 0 for client platforms, 1 for server platforms. > + */ > + if (specid_evcent->platform_class > 1) { > + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvPlatformClass", > + "The PlatformClass value of SpecId event is unexpected " > + "0 for client platforms, 1 for server platforms, " > + "got 0x%8.8" PRIx32 ".", specid_evcent->platform_class); > + return FWTS_ERROR; > + } > + > + if (specid_evcent->uintn_size < 1 || specid_evcent->uintn_size > 2) { > + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvUINTNFields", > + "The size of the UINTN fieldsof SpecId event is unexpected " > + "0x01 indicates UINT32 and 0x02 indicates UINT64, " > + "got 0x%" PRIx8 ".", specid_evcent->uintn_size); > + return FWTS_ERROR; > + } > + > + if (specid_evcent->number_of_alg < 1) { > + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvAlgNumber", > + "The number of Hash algorithms of SpecId event must " > + "be set to a value of 0x01 or greater " > + "got 0x%" PRIx8 ".", specid_evcent->number_of_alg); > + return FWTS_ERROR; > + } > + > + pdata += sizeof(fwts_efi_spec_id_event); > + len_remain -= sizeof(fwts_efi_spec_id_event); > + fwts_spec_id_event_alg_sz *alg_sz = (fwts_spec_id_event_alg_sz *)pdata; > + for (i = 0; i < specid_evcent->number_of_alg; i++) { > + if (len_remain < sizeof(fwts_spec_id_event_alg_sz)) { > + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength", > + "The length of the Specid event is %zd bytes " > + "is smaller than AlgorithmSize %zd bytes.", > + len_remain, > + sizeof(fwts_spec_id_event_alg_sz)); > + return FWTS_ERROR; > + } > + > + ret = tpmevlog_algid_check(fw, alg_sz->algorithm_id); > + if (ret != FWTS_OK) > + return ret; > + > + pdata += sizeof(fwts_spec_id_event_alg_sz); > + len_remain -= sizeof(fwts_spec_id_event_alg_sz); > + alg_sz = (fwts_spec_id_event_alg_sz *)pdata; > + } > + > + vendor_info_size = *(uint8_t *)pdata; > + pdata += sizeof(vendor_info_size); > + len_remain -= sizeof(vendor_info_size); > + if (vendor_info_size > 0) { > + if (len_remain < vendor_info_size) { > + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength", > + "The remain length of the Specid event is " > + "is too small (%zd bytes) for " > + "vendor info size.", > + len_remain); > + return FWTS_ERROR; > + } > + len_remain -= vendor_info_size; > + pdata += vendor_info_size; > + } > + > + /* Check the Crypto agile log format event */ > + while (len_remain > 0) { > + if (len_remain < sizeof(fwts_tcg_pcr_event2)) { > + fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length", > + "The length of the event2 is %zd bytes " > + "is smaller than the tcg pcr event2 %zd bytes.", > + len_remain, > + sizeof(fwts_tcg_pcr_event2)); > + return FWTS_ERROR; > + } > + > + fwts_tcg_pcr_event2 *pcr_event2 = (fwts_tcg_pcr_event2 *)pdata; > + ret = tpmevlog_pcrindex_value_check(fw, pcr_event2->pcr_index); > + if (ret != FWTS_OK) > + return ret; > + ret = tpmevlog_eventtype_check(fw, pcr_event2->event_type); > + if (ret != FWTS_OK) > + return ret; > + > + pdata += sizeof(fwts_tcg_pcr_event2); > + len_remain -= sizeof(fwts_tcg_pcr_event2); > + for (i = 0; i < pcr_event2->digests_count; i++) { > + > + hash_size = 0; > + TPM2_ALG_ID alg_id = *(TPM2_ALG_ID *)pdata; > + > + ret = tpmevlog_algid_check(fw, alg_id); > + if (ret != FWTS_OK) > + return ret; > + > + pdata += sizeof(TPM2_ALG_ID); > + len_remain -= sizeof(TPM2_ALG_ID); > + > + hash_size = fwts_tpm_get_hash_size(alg_id); > + if (!hash_size) { > + fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2HashSize", > + "The hash sie of the event2 is %zd bytes " > + "is smaller than the tcg pcr event2 %zd bytes.", > + len_remain, > + sizeof(fwts_tcg_pcr_event2)); > + return FWTS_ERROR; > + } > + > + pdata += hash_size; > + len_remain -= hash_size; > + } > + > + event_size = *(uint32_t *)pdata; > + > + if (len_remain < event_size) { > + fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length", > + "The remain length of the event2 is %zd bytes " > + "is smaller than required event2 length %zd bytes.", > + len_remain, > + sizeof(event_size)); > + return FWTS_ERROR; > + } > + pdata += (event_size + sizeof(event_size)); > + len_remain -= (event_size + sizeof(event_size)); > + > + } > + fwts_passed(fw, "Check Tpm crypto agile event log test passed."); > + return FWTS_OK; > +} > + > +static uint8_t *tpmevlog_load_file(const int fd, size_t *length) > +{ > + uint8_t *ptr = NULL, *tmp; > + size_t size = 0; > + char buffer[4096]; > + > + *length = 0; > + > + for (;;) { > + ssize_t n = read(fd, buffer, sizeof(buffer)); > + > + if (n == 0) > + break; > + if (n < 0) { > + if (errno != EINTR && errno != EAGAIN) { > + free(ptr); > + return NULL; > + } > + continue; > + } > + if (n > (ssize_t)sizeof(buffer)) > + goto err; > + if (size + n > 0xffffffff) > + goto err; > + > + if ((tmp = (uint8_t*)realloc(ptr, size + n + 1)) == NULL) { > + free(ptr); > + return NULL; > + } > + ptr = tmp; > + memcpy(ptr + size, buffer, n); > + size += n; > + } > + > + if (!ptr || !size) > + goto err_no_data; > + > + *length = size; > + return ptr; > + > +err: > + free(ptr); > +err_no_data: > + *length = 0; > + return NULL; > +} > + > +static int tpmevlog_test1(fwts_framework *fw) > +{ > + DIR *dir; > + struct dirent *tpmdir; > + bool tpm_logfile_found = false; > + > + if (!(dir = opendir(FWTS_TPM_LOG_DIR_PATH))) { > + fwts_log_info(fw, "Cannot find the tpm event log. Aborted."); > + return FWTS_ABORTED; > + } > + > + do { > + tpmdir = readdir(dir); > + if (tpmdir && strstr(tpmdir->d_name, "tpm")) { > + char path[PATH_MAX]; > + uint8_t *data; > + int fd; > + size_t length; > + > + fwts_log_nl(fw); > + fwts_log_info_verbatim(fw, "%s", tpmdir->d_name); > + > + snprintf(path, sizeof(path), FWTS_TPM_LOG_DIR_PATH "/%s/binary_bios_measurements", tpmdir->d_name); > + > + if ((fd = open(path, O_RDONLY)) >= 0) { > + data = tpmevlog_load_file(fd, &length); > + tpm_logfile_found = true; > + if (data == NULL) { > + fwts_log_info(fw, "Cannot load the tpm event logs. Aborted."); > + (void)closedir(dir); > + (void)close(fd); > + return FWTS_ABORTED; > + } else { > + /* check if the TPM2 eventlog */ > + if (strstr((char *)(data + sizeof(fwts_pc_client_pcr_event)), FWTS_TPM_EVENTLOG_V2_SIGNATURE)) > + tpmevlog_v2_check(fw, data, length); Coverity is reporting the following: ----- 412 if (strstr((char *)(data + sizeof(fwts_pc_client_pcr_event)), FWTS_TPM_EVENTLOG_V2_SIGNATURE)) CID 103975 (#1 of 1): Untrusted loop bound (TAINTED_SCALAR) 15. tainted_data: Passing tainted expression *data to tpmevlog_v2_check, which uses it as a loop boundary. Ensure that tainted values are properly sanitized, by checking that their values are within a permissible range. 413 tpmevlog_v2_check(fw, data, length); ----- I've had a look at tpmevlog_v2_check() and I think the bounds checking is sane, this seems to be an issue with the loop: for (i = 0; i < specid_evcent->number_of_alg; i++) { } but you have a bounds check on len_remain, so I think this is a false positive. > + > + free(data); > + } > + (void)close(fd); > + } > + } > + } while (tpmdir); > + > + (void)closedir(dir); > + > + if (!tpm_logfile_found) { > + fwts_log_info(fw, "Cannot find the tpm event log. Aborted."); > + return FWTS_ABORTED; > + } > + return FWTS_OK; > +} > + > +static fwts_framework_minor_test tpmevlog_tests[] = { > + { tpmevlog_test1, "Sanity check Tpm event log." }, > + { NULL, NULL } > +}; > + > +static fwts_framework_ops tpmevlog_ops = { > + .description = "Sanity check Tpm event log.", > + .minor_tests = tpmevlog_tests > +}; > + > +FWTS_REGISTER("tpmevlog", &tpmevlog_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_ROOT_PRIV) > Overall this looks OK to me. Just some nit picks. TPM is referred in several literal strings as "Tpm", "tpm". Is it better to refer to it as "TPM"? Colin
diff --git a/src/Makefile.am b/src/Makefile.am index 5d78411b..b7f3f7db 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -184,6 +184,7 @@ fwts_SOURCES = main.c \ pci/aspm/aspm.c \ pci/crs/crs.c \ pci/maxreadreq/maxreadreq.c \ + tpm/tpmevlog/tpmevlog.c \ tpm/tpmevlogdump/tpmevlogdump.c \ uefi/csm/csm.c \ uefi/uefidump/uefidump.c \ diff --git a/src/tpm/tpmevlog/tpmevlog.c b/src/tpm/tpmevlog/tpmevlog.c new file mode 100644 index 00000000..1d184fd0 --- /dev/null +++ b/src/tpm/tpmevlog/tpmevlog.c @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2020 Canonical + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "fwts.h" + +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "fwts_tpm.h" + +#define FWTS_TPM_LOG_DIR_PATH "/sys/kernel/security" + +static int tpmevlog_pcrindex_value_check(fwts_framework *fw, uint32_t pcr) +{ + + if ((pcr > 16) && (pcr != 23)) { + fwts_failed(fw, LOG_LEVEL_HIGH, "PCRIndexValue", + "The PCR Index value is undefined, 0x%8.8" PRIx32 ".", + pcr); + return FWTS_ERROR; + } + + return FWTS_OK; +} + +static int tpmevlog_eventtype_check(fwts_framework *fw, fwts_tpmlog_event_type event_type) +{ + + switch (event_type) { + case EV_PREBOOT_CERT: + case EV_POST_CODE: + case EV_UNUSED: + case EV_NO_ACTION: + case EV_SEPARATOR: + case EV_ACTION: + case EV_EVENT_TAG: + case EV_S_CRTM_CONTENTS: + case EV_S_CRTM_VERSION: + case EV_CPU_MICROCODE: + case EV_PLATFORM_CONFIG_FLAGS: + case EV_TABLE_OF_DEVICES: + case EV_IPL: + case EV_IPL_PARTITION_DATA: + case EV_NONHOST_CODE: + case EV_NONHOST_CONFIG: + case EV_NONHOST_INFO: + case EV_OMIT_BOOT_DEVICE_EVENTS: + case EV_EFI_EVENT_BASE: + case EV_EFI_VARIABLE_DRIVER_CONFIG: + case EV_EFI_VARIABLE_BOOT: + case EV_EFI_BOOT_SERVICES_APPLICATION: + case EV_EFI_BOOT_SERVICES_DRIVER: + case EV_EFI_RUNTIME_SERVICES_DRIVER: + case EV_EFI_GPT_EVENT: + case EV_EFI_ACTION: + case EV_EFI_PLATFORM_FIRMWARE_BLOB: + case EV_EFI_HANDOFF_TABLES: + case EV_EFI_HCRTM_EVENT: + case EV_EFI_VARIABLE_AUTHORITY: + return FWTS_OK; + default: + fwts_failed(fw, LOG_LEVEL_HIGH, "PCREventType", + "The Event Type is undefined, 0x%8.8" PRIx32 ".", + event_type); + return FWTS_ERROR; + } + + return FWTS_OK; +} + +static int tpmevlog_algid_check(fwts_framework *fw, TPM2_ALG_ID hash) +{ + + switch (hash) { + case TPM2_ALG_RSA: + case TPM2_ALG_TDES: + case TPM2_ALG_SHA1: + case TPM2_ALG_HMAC: + case TPM2_ALG_AES: + case TPM2_ALG_MGF1: + case TPM2_ALG_KEYEDHASH: + case TPM2_ALG_XOR: + case TPM2_ALG_SHA256: + case TPM2_ALG_SHA384: + case TPM2_ALG_SHA512: + case TPM2_ALG_NULL: + case TPM2_ALG_SM3_256: + case TPM2_ALG_SM4: + case TPM2_ALG_RSASSA: + case TPM2_ALG_RSAES: + case TPM2_ALG_RSAPSS: + case TPM2_ALG_OAEP: + case TPM2_ALG_ECDSA: + case TPM2_ALG_ECDH: + case TPM2_ALG_ECDAA: + case TPM2_ALG_SM2: + case TPM2_ALG_ECSCHNORR: + case TPM2_ALG_ECMQV: + case TPM2_ALG_KDF1_SP800_56A: + case TPM2_ALG_KDF2: + case TPM2_ALG_KDF1_SP800_108: + case TPM2_ALG_ECC: + case TPM2_ALG_SYMCIPHER: + case TPM2_ALG_CAMELLIA: + case TPM2_ALG_CMAC: + case TPM2_ALG_CTR: + case TPM2_ALG_SHA3_256: + case TPM2_ALG_SHA3_384: + case TPM2_ALG_SHA3_512: + case TPM2_ALG_OFB: + case TPM2_ALG_CBC: + case TPM2_ALG_CFB: + case TPM2_ALG_ECB: + return FWTS_OK; + default: + fwts_failed(fw, LOG_LEVEL_HIGH, "AlgorithmID", + "The AlgorithmID is undefined, 0x%4.4" PRIx16 ".", + hash); + return FWTS_ERROR; + } + + return FWTS_OK; +} + +static int tpmevlog_v2_check(fwts_framework *fw, uint8_t *data, size_t len) +{ + int ret = FWTS_OK; + size_t len_remain = len; + uint8_t *pdata = data; + int i = 0; + uint8_t vendor_info_size = 0; + uint8_t hash_size = 0; + uint32_t event_size = 0; + + /* specid_event_check */ + if (len < sizeof(fwts_pc_client_pcr_event)) { + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength", + "The length of the Specid event is %zd bytes " + "is smaller than the PCClientPCREvent %zd bytes.", + len_remain, + sizeof(fwts_pc_client_pcr_event)); + return FWTS_ERROR; + } + + fwts_pc_client_pcr_event *pc_event = (fwts_pc_client_pcr_event *)pdata; + ret = tpmevlog_pcrindex_value_check(fw, pc_event->pcr_index); + if (ret != FWTS_OK) + return ret; + ret = tpmevlog_eventtype_check(fw, pc_event->event_type); + if (ret != FWTS_OK) + return ret; + for (i = 0; i < TPM2_SHA1_DIGEST_SIZE; i++) { + if (pc_event->digest[i] != 0) { + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvDigest", + "The digest filed of SpecId event should be all zero."); + fwts_tpm_data_hexdump(fw, pc_event->digest, sizeof(pc_event->digest), "Digest"); + } + } + + pdata += sizeof(fwts_pc_client_pcr_event); + len_remain -= sizeof(fwts_pc_client_pcr_event); + + /* check the data length specid event */ + if (len_remain < sizeof(fwts_efi_spec_id_event)) { + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength", + "The length of the Specid event is %zd bytes " + "is smaller than the SpecId event %zd bytes.", + len_remain, + sizeof(fwts_efi_spec_id_event)); + return FWTS_ERROR; + } + + fwts_efi_spec_id_event *specid_evcent = (fwts_efi_spec_id_event *)pdata; + if (strcmp((char *)specid_evcent->signature, FWTS_TPM_EVENTLOG_V2_SIGNATURE) != 0) { + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvSignature", + "The signature of SpecId event is not the same as expected " + "Spec ID Event03, got %s.", + (char *)specid_evcent->signature); + return FWTS_ERROR; + } + + /* + * Check the platform class value which defined in TCG ACPI Specification, + * 0 for client platforms, 1 for server platforms. + */ + if (specid_evcent->platform_class > 1) { + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvPlatformClass", + "The PlatformClass value of SpecId event is unexpected " + "0 for client platforms, 1 for server platforms, " + "got 0x%8.8" PRIx32 ".", specid_evcent->platform_class); + return FWTS_ERROR; + } + + if (specid_evcent->uintn_size < 1 || specid_evcent->uintn_size > 2) { + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvUINTNFields", + "The size of the UINTN fieldsof SpecId event is unexpected " + "0x01 indicates UINT32 and 0x02 indicates UINT64, " + "got 0x%" PRIx8 ".", specid_evcent->uintn_size); + return FWTS_ERROR; + } + + if (specid_evcent->number_of_alg < 1) { + fwts_failed(fw, LOG_LEVEL_HIGH, "SpecIdEvAlgNumber", + "The number of Hash algorithms of SpecId event must " + "be set to a value of 0x01 or greater " + "got 0x%" PRIx8 ".", specid_evcent->number_of_alg); + return FWTS_ERROR; + } + + pdata += sizeof(fwts_efi_spec_id_event); + len_remain -= sizeof(fwts_efi_spec_id_event); + fwts_spec_id_event_alg_sz *alg_sz = (fwts_spec_id_event_alg_sz *)pdata; + for (i = 0; i < specid_evcent->number_of_alg; i++) { + if (len_remain < sizeof(fwts_spec_id_event_alg_sz)) { + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength", + "The length of the Specid event is %zd bytes " + "is smaller than AlgorithmSize %zd bytes.", + len_remain, + sizeof(fwts_spec_id_event_alg_sz)); + return FWTS_ERROR; + } + + ret = tpmevlog_algid_check(fw, alg_sz->algorithm_id); + if (ret != FWTS_OK) + return ret; + + pdata += sizeof(fwts_spec_id_event_alg_sz); + len_remain -= sizeof(fwts_spec_id_event_alg_sz); + alg_sz = (fwts_spec_id_event_alg_sz *)pdata; + } + + vendor_info_size = *(uint8_t *)pdata; + pdata += sizeof(vendor_info_size); + len_remain -= sizeof(vendor_info_size); + if (vendor_info_size > 0) { + if (len_remain < vendor_info_size) { + fwts_failed(fw, LOG_LEVEL_MEDIUM, "SpecidEventLength", + "The remain length of the Specid event is " + "is too small (%zd bytes) for " + "vendor info size.", + len_remain); + return FWTS_ERROR; + } + len_remain -= vendor_info_size; + pdata += vendor_info_size; + } + + /* Check the Crypto agile log format event */ + while (len_remain > 0) { + if (len_remain < sizeof(fwts_tcg_pcr_event2)) { + fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length", + "The length of the event2 is %zd bytes " + "is smaller than the tcg pcr event2 %zd bytes.", + len_remain, + sizeof(fwts_tcg_pcr_event2)); + return FWTS_ERROR; + } + + fwts_tcg_pcr_event2 *pcr_event2 = (fwts_tcg_pcr_event2 *)pdata; + ret = tpmevlog_pcrindex_value_check(fw, pcr_event2->pcr_index); + if (ret != FWTS_OK) + return ret; + ret = tpmevlog_eventtype_check(fw, pcr_event2->event_type); + if (ret != FWTS_OK) + return ret; + + pdata += sizeof(fwts_tcg_pcr_event2); + len_remain -= sizeof(fwts_tcg_pcr_event2); + for (i = 0; i < pcr_event2->digests_count; i++) { + + hash_size = 0; + TPM2_ALG_ID alg_id = *(TPM2_ALG_ID *)pdata; + + ret = tpmevlog_algid_check(fw, alg_id); + if (ret != FWTS_OK) + return ret; + + pdata += sizeof(TPM2_ALG_ID); + len_remain -= sizeof(TPM2_ALG_ID); + + hash_size = fwts_tpm_get_hash_size(alg_id); + if (!hash_size) { + fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2HashSize", + "The hash sie of the event2 is %zd bytes " + "is smaller than the tcg pcr event2 %zd bytes.", + len_remain, + sizeof(fwts_tcg_pcr_event2)); + return FWTS_ERROR; + } + + pdata += hash_size; + len_remain -= hash_size; + } + + event_size = *(uint32_t *)pdata; + + if (len_remain < event_size) { + fwts_failed(fw, LOG_LEVEL_MEDIUM, "EventV2Length", + "The remain length of the event2 is %zd bytes " + "is smaller than required event2 length %zd bytes.", + len_remain, + sizeof(event_size)); + return FWTS_ERROR; + } + pdata += (event_size + sizeof(event_size)); + len_remain -= (event_size + sizeof(event_size)); + + } + fwts_passed(fw, "Check Tpm crypto agile event log test passed."); + return FWTS_OK; +} + +static uint8_t *tpmevlog_load_file(const int fd, size_t *length) +{ + uint8_t *ptr = NULL, *tmp; + size_t size = 0; + char buffer[4096]; + + *length = 0; + + for (;;) { + ssize_t n = read(fd, buffer, sizeof(buffer)); + + if (n == 0) + break; + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) { + free(ptr); + return NULL; + } + continue; + } + if (n > (ssize_t)sizeof(buffer)) + goto err; + if (size + n > 0xffffffff) + goto err; + + if ((tmp = (uint8_t*)realloc(ptr, size + n + 1)) == NULL) { + free(ptr); + return NULL; + } + ptr = tmp; + memcpy(ptr + size, buffer, n); + size += n; + } + + if (!ptr || !size) + goto err_no_data; + + *length = size; + return ptr; + +err: + free(ptr); +err_no_data: + *length = 0; + return NULL; +} + +static int tpmevlog_test1(fwts_framework *fw) +{ + DIR *dir; + struct dirent *tpmdir; + bool tpm_logfile_found = false; + + if (!(dir = opendir(FWTS_TPM_LOG_DIR_PATH))) { + fwts_log_info(fw, "Cannot find the tpm event log. Aborted."); + return FWTS_ABORTED; + } + + do { + tpmdir = readdir(dir); + if (tpmdir && strstr(tpmdir->d_name, "tpm")) { + char path[PATH_MAX]; + uint8_t *data; + int fd; + size_t length; + + fwts_log_nl(fw); + fwts_log_info_verbatim(fw, "%s", tpmdir->d_name); + + snprintf(path, sizeof(path), FWTS_TPM_LOG_DIR_PATH "/%s/binary_bios_measurements", tpmdir->d_name); + + if ((fd = open(path, O_RDONLY)) >= 0) { + data = tpmevlog_load_file(fd, &length); + tpm_logfile_found = true; + if (data == NULL) { + fwts_log_info(fw, "Cannot load the tpm event logs. Aborted."); + (void)closedir(dir); + (void)close(fd); + return FWTS_ABORTED; + } else { + /* check if the TPM2 eventlog */ + if (strstr((char *)(data + sizeof(fwts_pc_client_pcr_event)), FWTS_TPM_EVENTLOG_V2_SIGNATURE)) + tpmevlog_v2_check(fw, data, length); + + free(data); + } + (void)close(fd); + } + } + } while (tpmdir); + + (void)closedir(dir); + + if (!tpm_logfile_found) { + fwts_log_info(fw, "Cannot find the tpm event log. Aborted."); + return FWTS_ABORTED; + } + return FWTS_OK; +} + +static fwts_framework_minor_test tpmevlog_tests[] = { + { tpmevlog_test1, "Sanity check Tpm event log." }, + { NULL, NULL } +}; + +static fwts_framework_ops tpmevlog_ops = { + .description = "Sanity check Tpm event log.", + .minor_tests = tpmevlog_tests +}; + +FWTS_REGISTER("tpmevlog", &tpmevlog_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_ROOT_PRIV)
Signed-off-by: Ivan Hu <ivan.hu@canonical.com> --- src/Makefile.am | 1 + src/tpm/tpmevlog/tpmevlog.c | 441 ++++++++++++++++++++++++++++++++++++ 2 files changed, 442 insertions(+) create mode 100644 src/tpm/tpmevlog/tpmevlog.c