From patchwork Wed Oct 21 10:09:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ivan Hu X-Patchwork-Id: 1385470 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=fwts-devel-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CGR7p1dBhz9sTD for ; Wed, 21 Oct 2020 21:10:12 +1100 (AEDT) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kVB4F-00021R-Mj; Wed, 21 Oct 2020 10:10:03 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kVB4E-00021C-01 for fwts-devel@lists.ubuntu.com; Wed, 21 Oct 2020 10:10:02 +0000 Received: from [106.104.70.34] (helo=canonical.com) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kVB4D-0002TH-4i for fwts-devel@lists.ubuntu.com; Wed, 21 Oct 2020 10:10:01 +0000 From: Ivan Hu To: fwts-devel@lists.ubuntu.com Subject: [PATCH 2/2][V2] tpmevlog: add tests for snatic check of the Tpm event log Date: Wed, 21 Oct 2020 18:09:56 +0800 Message-Id: <20201021100956.25470-1-ivan.hu@canonical.com> X-Mailer: git-send-email 2.17.1 X-BeenThere: fwts-devel@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Firmware Test Suite Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: fwts-devel-bounces@lists.ubuntu.com Sender: "fwts-devel" Signed-off-by: Ivan Hu Acked-by: Colin Ian King --- src/Makefile.am | 1 + src/tpm/tpmevlog/tpmevlog.c | 446 ++++++++++++++++++++++++++++++++++++ 2 files changed, 447 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..70810bb6 --- /dev/null +++ b/src/tpm/tpmevlog/tpmevlog.c @@ -0,0 +1,446 @@ +/* + * 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 +#include +#include +#include +#include + +#include "fwts_tpm.h" + +#define FWTS_TPM_LOG_DIR_PATH "/sys/kernel/security" + +static int tpmevlog_pcrindex_value_check(fwts_framework *fw, const uint32_t pcr) +{ + /* + * Current PCRs defined from 0 to 16, and 23 if for application support, + * define from TCG PC Client Platform Firmware Profile Specification + * https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/ + * 2.3.4 PCR Usage + */ + 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, const 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, const 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)