diff --git a/contrib/debian/fwupd.install b/contrib/debian/fwupd.install index 0c7c8f78d..dd31791be 100644 --- a/contrib/debian/fwupd.install +++ b/contrib/debian/fwupd.install @@ -12,6 +12,7 @@ usr/lib/*/fwupd usr/lib/*/fwupdagent usr/lib/*/fwupdoffline usr/lib/*/fwupdtool +usr/lib/*/fwupdtpmevlog usr/share/man/man1/* lib/systemd/system/* lib/systemd/system-preset/* diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 80305034d..5a07dbfc9 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -249,6 +249,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %{_libexecdir}/fwupd/fwupdtool %{_libexecdir}/fwupd/fwupdagent %{_libexecdir}/fwupd/fwupdoffline +%{_libexecdir}/fwupd/fwupdtpmevlog %if 0%{?have_uefi} %{_libexecdir}/fwupd/efi/*.efi %{_libexecdir}/fwupd/efi/*.efi.signed @@ -353,6 +354,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %{_libdir}/fwupd-plugins-3/libfu_plugin_thunderbolt.so %{_libdir}/fwupd-plugins-3/libfu_plugin_thunderbolt_power.so %{_libdir}/fwupd-plugins-3/libfu_plugin_tpm.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_tpm_eventlog.so %if 0%{?have_uefi} %{_libdir}/fwupd-plugins-3/libfu_plugin_uefi.so %{_libdir}/fwupd-plugins-3/libfu_plugin_uefi_recovery.so diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 67cf6b7a2..a9aba340c 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -732,6 +732,7 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) dev = fu_device_new (); fu_device_set_physical_id (dev, "DEVNAME=/dev/tpm0"); fu_device_add_instance_id (dev, tpm_guid_raw); + fu_device_add_instance_id (dev, "system-tpm"); fu_device_set_vendor (dev, "Dell Inc."); fu_device_set_name (dev, pretty_tpm_name); fu_device_set_summary (dev, "Platform TPM device"); diff --git a/plugins/meson.build b/plugins/meson.build index cce9b252a..f49f4bb86 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -31,6 +31,7 @@ subdir('csr') if get_option('plugin_tpm') and get_option('gudev') subdir('tpm') +subdir('tpm-eventlog') endif if get_option('plugin_emmc') and get_option('gudev') diff --git a/plugins/tpm-eventlog/README.md b/plugins/tpm-eventlog/README.md new file mode 100644 index 000000000..ce18d1613 --- /dev/null +++ b/plugins/tpm-eventlog/README.md @@ -0,0 +1,12 @@ +TPM Event Log Support +===================== + +Introduction +------------ + +The TPM Event Log records which events are registered for the PCR0 hash, which +may help in explaining why PCR0 values are differing for some firmware. + +The device exposed is not upgradable in any way and is just for debugging. +The created device will be a child device of the system TPM device, which may +or may not be upgradable. diff --git a/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c b/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c new file mode 100644 index 000000000..bcf372dff --- /dev/null +++ b/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-hash.h" +#include "fu-plugin-vfuncs.h" + +#include "fu-tpm-eventlog-device.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + gsize bufsz = 0; + const gchar *fn = "/sys/kernel/security/tpm0/binary_bios_measurements"; + g_autofree gchar *str = NULL; + g_autofree guint8 *buf = NULL; + g_autoptr(FuTpmEventlogDevice) dev = NULL; + + if (!g_file_get_contents (fn, (gchar **) &buf, &bufsz, error)) + return FALSE; + if (bufsz == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to read data from %s", fn); + return FALSE; + } + dev = fu_tpm_eventlog_device_new (buf, bufsz, error); + if (dev == NULL) + return FALSE; + if (!fu_device_setup (FU_DEVICE (dev), error)) + return FALSE; + + /* add optional report metadata */ + str = fu_tpm_eventlog_device_report_metadata (dev); + g_debug ("using TPM event log report data of:\n%s", str); + fu_plugin_add_report_metadata (plugin, "TpmEventLog", str); + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} + diff --git a/plugins/tpm-eventlog/fu-self-test.c b/plugins/tpm-eventlog/fu-self-test.c new file mode 100644 index 000000000..7d287ca31 --- /dev/null +++ b/plugins/tpm-eventlog/fu-self-test.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-tpm-eventlog-common.h" +#include "fu-tpm-eventlog-device.h" + +static void +fu_test_tpm_eventlog_parse_v1_func (void) +{ + gboolean ret; + gsize bufsz = 0; + g_autofree gchar *fn = NULL; + g_autofree guint8 *buf = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuTpmEventlogDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v1", NULL); + ret = g_file_get_contents (fn, (gchar **) &buf, &bufsz, &error); + g_assert_no_error (error); + g_assert_true (ret); + + dev = fu_tpm_eventlog_device_new (buf, bufsz, &error); + g_assert_no_error (error); + g_assert_nonnull (dev); + str = fu_device_to_string (FU_DEVICE (dev)); + g_print ("%s\n", str); + g_assert_nonnull (g_strstr_len (str, -1, "231f248f12ef9f38549f1bda7a859b781b5caab0")); + g_assert_nonnull (g_strstr_len (str, -1, "9069ca78e7450a285173431b3e52c5c25299e473")); +} + +static void +fu_test_tpm_eventlog_parse_v2_func (void) +{ + gboolean ret; + gsize bufsz = 0; + g_autofree gchar *fn = NULL; + g_autofree guint8 *buf = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuTpmEventlogDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v2", NULL); + ret = g_file_get_contents (fn, (gchar **) &buf, &bufsz, &error); + g_assert_no_error (error); + g_assert_true (ret); + + dev = fu_tpm_eventlog_device_new (buf, bufsz, &error); + g_assert_no_error (error); + g_assert_nonnull (dev); + str = fu_device_to_string (FU_DEVICE (dev)); + g_print ("%s\n", str); + g_assert_nonnull (g_strstr_len (str, -1, "19ce8e1347a709d2b485d519695e3ce10b939485")); + g_assert_nonnull (g_strstr_len (str, -1, "9069ca78e7450a285173431b3e52c5c25299e473")); + g_assert_nonnull (g_strstr_len (str, -1, "Boot Guard Measured")); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + g_test_add_func ("/tpm-eventlog/parse{v1}", fu_test_tpm_eventlog_parse_v1_func); + g_test_add_func ("/tpm-eventlog/parse{v2}", fu_test_tpm_eventlog_parse_v2_func); + return g_test_run (); +} diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-common.c b/plugins/tpm-eventlog/fu-tpm-eventlog-common.c new file mode 100644 index 000000000..21172de5c --- /dev/null +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-common.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-tpm-eventlog-common.h" + +const gchar * +fu_tpm_eventlog_pcr_to_string (gint pcr) +{ + if (pcr == 0) + return "BIOS"; + if (pcr == 1) + return "BIOS Configuration"; + if (pcr == 2) + return "Option ROMs"; + if (pcr == 3) + return "Option ROM configuration"; + if (pcr == 4) + return "Initial program loader code"; + if (pcr == 5) + return "Initial program loader code configuration"; + if (pcr == 6) + return "State transitions and wake events"; + if (pcr == 7) + return "Platform manufacturer specifc measurements"; + if (pcr >= 8 && pcr <= 15) + return "Static operating system"; + if (pcr == 16) + return "Debug"; + if (pcr == 17) + return "Dynamic root of trust measurement and launch control policy"; + if (pcr >= 18 && pcr <= 22) + return "Trusted OS"; + if (pcr == 23) + return "Application support"; + return "Undefined"; +} + +const gchar * +fu_tpm_eventlog_hash_to_string (TPM2_ALG_ID hash_kind) +{ + if (hash_kind == TPM2_ALG_SHA1) + return "SHA1"; + if (hash_kind == TPM2_ALG_SHA256) + return "SHA256"; + if (hash_kind == TPM2_ALG_SHA384) + return "SHA384"; + if (hash_kind == TPM2_ALG_SHA512) + return "SHA512"; + return NULL; +} + +guint32 +fu_tpm_eventlog_hash_get_size (TPM2_ALG_ID hash_kind) +{ + if (hash_kind == TPM2_ALG_SHA1) + return TPM2_SHA1_DIGEST_SIZE; + if (hash_kind == TPM2_ALG_SHA256) + return TPM2_SHA256_DIGEST_SIZE; + if (hash_kind == TPM2_ALG_SHA384) + return TPM2_SHA384_DIGEST_SIZE; + if (hash_kind == TPM2_ALG_SHA512) + return TPM2_SHA512_DIGEST_SIZE; + return 0; +} + +const gchar * +fu_tpm_eventlog_item_kind_to_string (FuTpmEventlogItemKind event_type) +{ + if (event_type == EV_PREBOOT_CERT) + return "EV_PREBOOT_CERT"; + if (event_type == EV_POST_CODE) + return "EV_POST_CODE"; + if (event_type == EV_NO_ACTION) + return "EV_NO_ACTION"; + if (event_type == EV_SEPARATOR) + return "EV_SEPARATOR"; + if (event_type == EV_ACTION) + return "EV_ACTION"; + if (event_type == EV_EVENT_TAG) + return "EV_EVENT_TAG"; + if (event_type == EV_S_CRTM_CONTENTS) + return "EV_S_CRTM_CONTENTS"; + if (event_type == EV_S_CRTM_VERSION) + return "EV_S_CRTM_VERSION"; + if (event_type == EV_CPU_MICROCODE) + return "EV_CPU_MICROCODE"; + if (event_type == EV_PLATFORM_CONFIG_FLAGS) + return "EV_PLATFORM_CONFIG_FLAGS"; + if (event_type == EV_TABLE_OF_DEVICES) + return "EV_TABLE_OF_DEVICES"; + if (event_type == EV_COMPACT_HASH) + return "EV_COMPACT_HASH"; + if (event_type == EV_NONHOST_CODE) + return "EV_NONHOST_CODE"; + if (event_type == EV_NONHOST_CONFIG) + return "EV_NONHOST_CONFIG"; + if (event_type == EV_NONHOST_INFO) + return "EV_NONHOST_INFO"; + if (event_type == EV_OMIT_BOOT_DEVICE_EVENTS) + return "EV_OMIT_BOOT_DEVICE_EVENTS"; + if (event_type == EV_EFI_EVENT_BASE) + return "EV_EFI_EVENT_BASE"; + if (event_type == EV_EFI_VARIABLE_DRIVER_CONFIG) + return "EV_EFI_VARIABLE_DRIVER_CONFIG"; + if (event_type == EV_EFI_VARIABLE_BOOT) + return "EV_EFI_VARIABLE_BOOT"; + if (event_type == EV_EFI_BOOT_SERVICES_APPLICATION) + return "EV_BOOT_SERVICES_APPLICATION"; + if (event_type == EV_EFI_BOOT_SERVICES_DRIVER) + return "EV_EFI_BOOT_SERVICES_DRIVER"; + if (event_type == EV_EFI_RUNTIME_SERVICES_DRIVER) + return "EV_EFI_RUNTIME_SERVICES_DRIVER"; + if (event_type == EV_EFI_GPT_EVENT) + return "EV_EFI_GPT_EVENT"; + if (event_type == EV_EFI_ACTION) + return "EV_EFI_ACTION"; + if (event_type == EV_EFI_PLATFORM_FIRMWARE_BLOB) + return "EV_EFI_PLATFORM_FIRMWARE_BLOB"; + if (event_type == EV_EFI_HANDOFF_TABLES) + return "EV_EFI_HANDOFF_TABLES"; + if (event_type == EV_EFI_HCRTM_EVENT) + return "EV_EFI_HCRTM_EVENT"; + if (event_type == EV_EFI_VARIABLE_AUTHORITY) + return "EV_EFI_EFI_VARIABLE_AUTHORITY"; + return NULL; +} + +gchar * +fu_tpm_eventlog_blobstr (GBytes *blob) +{ + gboolean has_printable = FALSE; + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data (blob, &bufsz); + g_autoptr(GString) str = g_string_new (NULL); + + for (gsize i = 0; i < bufsz; i++) { + gchar chr = buf[i]; + if (g_ascii_isprint (chr)) { + g_string_append_c (str, chr); + has_printable = TRUE; + } else { + g_string_append_c (str, '.'); + } + } + if (!has_printable) + return NULL; + return g_string_free (g_steal_pointer (&str), FALSE); +} diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-common.h b/plugins/tpm-eventlog/fu-tpm-eventlog-common.h new file mode 100644 index 000000000..8c9cf0e77 --- /dev/null +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-common.h @@ -0,0 +1,49 @@ + /* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#include "fu-plugin.h" + +typedef enum { + EV_PREBOOT_CERT = 0x00000000, + EV_POST_CODE = 0x00000001, + EV_NO_ACTION = 0x00000003, + EV_SEPARATOR = 0x00000004, + EV_ACTION = 0x00000005, + EV_EVENT_TAG = 0x00000006, + EV_S_CRTM_CONTENTS = 0x00000007, + EV_S_CRTM_VERSION = 0x00000008, + EV_CPU_MICROCODE = 0x00000009, + EV_PLATFORM_CONFIG_FLAGS = 0x0000000a, + EV_TABLE_OF_DEVICES = 0x0000000b, + EV_COMPACT_HASH = 0x0000000c, + EV_NONHOST_CODE = 0x0000000f, + EV_NONHOST_CONFIG = 0x00000010, + EV_NONHOST_INFO = 0x00000011, + EV_OMIT_BOOT_DEVICE_EVENTS = 0x00000012, + EV_EFI_EVENT_BASE = 0x80000000, + EV_EFI_VARIABLE_DRIVER_CONFIG = 0x80000001, + EV_EFI_VARIABLE_BOOT = 0x80000002, + EV_EFI_BOOT_SERVICES_APPLICATION = 0x80000003, + EV_EFI_BOOT_SERVICES_DRIVER = 0x80000004, + EV_EFI_RUNTIME_SERVICES_DRIVER = 0x80000005, + EV_EFI_GPT_EVENT = 0x80000006, + EV_EFI_ACTION = 0x80000007, + EV_EFI_PLATFORM_FIRMWARE_BLOB = 0x80000008, + EV_EFI_HANDOFF_TABLES = 0x80000009, + EV_EFI_HCRTM_EVENT = 0x80000010, + EV_EFI_VARIABLE_AUTHORITY = 0x800000e0 +} FuTpmEventlogItemKind; + +const gchar *fu_tpm_eventlog_pcr_to_string (gint pcr); +const gchar *fu_tpm_eventlog_hash_to_string (TPM2_ALG_ID hash_kind); +guint32 fu_tpm_eventlog_hash_get_size (TPM2_ALG_ID hash_kind); +const gchar *fu_tpm_eventlog_item_kind_to_string (FuTpmEventlogItemKind event_type); +gchar *fu_tpm_eventlog_blobstr (GBytes *blob); diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-device.c b/plugins/tpm-eventlog/fu-tpm-eventlog-device.c new file mode 100644 index 000000000..6960a711e --- /dev/null +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-device.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" + +#include "fu-tpm-eventlog-device.h" +#include "fu-tpm-eventlog-parser.h" + +struct _FuTpmEventlogDevice { + FuDevice parent_instance; + GPtrArray *items; +}; + +G_DEFINE_TYPE (FuTpmEventlogDevice, fu_tpm_eventlog_device, FU_TYPE_DEVICE) + +static void +fu_tpm_eventlog_device_to_string (FuDevice *device, guint idt, GString *str) +{ + FuTpmEventlogDevice *self = FU_TPM_EVENTLOG_DEVICE (device); + if (self->items->len > 0) { + fu_common_string_append_kv (str, idt, "Items", NULL); + for (guint i = 0; i < self->items->len; i++) { + FuTpmEventlogItem *item = g_ptr_array_index (self->items, i); + fu_tpm_eventlog_item_to_string (item, idt + 1, str); + } + } +} + +gchar * +fu_tpm_eventlog_device_report_metadata (FuTpmEventlogDevice *self) +{ + GString *str = g_string_new (""); + for (guint i = 0; i < self->items->len; i++) { + FuTpmEventlogItem *item = g_ptr_array_index (self->items, i); + g_autofree gchar *blobstr = fu_tpm_eventlog_blobstr (item->blob); + g_string_append_printf (str, "0x%08x %s", item->kind, item->checksum_sha1); + if (blobstr != NULL) + g_string_append_printf (str, " [%s]", blobstr); + g_string_append (str, "\n"); + } + if (str->len > 0) + g_string_truncate (str, str->len - 1); + return g_string_free (str, FALSE); +} + +static void +fu_tpm_eventlog_device_init (FuTpmEventlogDevice *self) +{ + fu_device_set_name (FU_DEVICE (self), "Event Log"); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_set_physical_id (FU_DEVICE (self), "DEVNAME=/dev/tpm0"); + fu_device_set_logical_id (FU_DEVICE (self), "eventlog"); + fu_device_add_parent_guid (FU_DEVICE (self), "system-tpm"); + fu_device_add_instance_id (FU_DEVICE (self), "system-tpm-eventlog"); +} + +static void +fu_tpm_eventlog_device_finalize (GObject *object) +{ + FuTpmEventlogDevice *self = FU_TPM_EVENTLOG_DEVICE (object); + + g_ptr_array_unref (self->items); + + G_OBJECT_CLASS (fu_tpm_eventlog_device_parent_class)->finalize (object); +} + +static void +fu_tpm_eventlog_device_class_init (FuTpmEventlogDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + object_class->finalize = fu_tpm_eventlog_device_finalize; + klass_device->to_string = fu_tpm_eventlog_device_to_string; +} + +FuTpmEventlogDevice * +fu_tpm_eventlog_device_new (const guint8 *buf, gsize bufsz, GError **error) +{ + g_autoptr(FuTpmEventlogDevice) self = NULL; + + g_return_val_if_fail (buf != NULL, NULL); + + /* create object */ + self = g_object_new (FU_TYPE_TPM_EVENTLOG_DEVICE, NULL); + self->items = fu_tpm_eventlog_parser_new (buf, bufsz, + FU_TPM_EVENTLOG_PARSER_FLAG_NONE, + error); + if (self->items == NULL) + return NULL; + return FU_TPM_EVENTLOG_DEVICE (g_steal_pointer (&self)); +} diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-device.h b/plugins/tpm-eventlog/fu-tpm-eventlog-device.h new file mode 100644 index 000000000..a77e9a657 --- /dev/null +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +#define FU_TYPE_TPM_EVENTLOG_DEVICE (fu_tpm_eventlog_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuTpmEventlogDevice, fu_tpm_eventlog_device, FU, TPM_EVENTLOG_DEVICE, FuDevice) + +FuTpmEventlogDevice *fu_tpm_eventlog_device_new (const guint8 *buf, + gsize bufsz, + GError **error); +gchar *fu_tpm_eventlog_device_report_metadata (FuTpmEventlogDevice *self); diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-parser.c b/plugins/tpm-eventlog/fu-tpm-eventlog-parser.c new file mode 100644 index 000000000..0b59383b3 --- /dev/null +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-parser.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" + +#include "fu-tpm-eventlog-parser.h" + +#define FU_TPM_EVENTLOG_V1_IDX_PCR 0x00 +#define FU_TPM_EVENTLOG_V1_IDX_TYPE 0x04 +#define FU_TPM_EVENTLOG_V1_IDX_DIGEST 0x08 +#define FU_TPM_EVENTLOG_V1_IDX_EVENT_SIZE 0x1c +#define FU_TPM_EVENTLOG_V1_SIZE 0x20 + +#define FU_TPM_EVENTLOG_V2_HDR_IDX_SIGNATURE 0x00 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_PLATFORM_CLASS 0x10 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_SPEC_VERSION_MINOR 0x14 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_SPEC_VERSION_MAJOR 0X15 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_SPEC_ERRATA 0x16 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_UINTN_SIZE 0x17 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_NUMBER_OF_ALGS 0x18 + +#define FU_TPM_EVENTLOG_V2_HDR_SIGNATURE "Spec ID Event03" + +#define FU_TPM_EVENTLOG_V2_IDX_PCR 0x00 +#define FU_TPM_EVENTLOG_V2_IDX_TYPE 0x04 +#define FU_TPM_EVENTLOG_V2_IDX_DIGEST_COUNT 0x08 +#define FU_TPM_EVENTLOG_V2_SIZE 0x0c + +static void +fu_tpm_eventlog_parser_item_free (FuTpmEventlogItem *item) +{ + g_bytes_unref (item->blob); + g_free (item->checksum_sha1); + g_free (item->checksum_sha256); + g_free (item); +} + +void +fu_tpm_eventlog_item_to_string (FuTpmEventlogItem *item, guint idt, GString *str) +{ + const gchar *tmp; + g_autofree gchar *blobstr = fu_tpm_eventlog_blobstr (item->blob); + g_autofree gchar *pcrstr = g_strdup_printf ("%s (%u)", + fu_tpm_eventlog_pcr_to_string (item->pcr), + item->pcr); + fu_common_string_append_kv (str, idt, "PCR", pcrstr); + fu_common_string_append_kx (str, idt, "Type", item->kind); + tmp = fu_tpm_eventlog_item_kind_to_string (item->kind); + if (tmp != NULL) + fu_common_string_append_kv (str, idt, "Description", tmp);; + fu_common_string_append_kv (str, idt, "ChecksumSha1", item->checksum_sha1); + if (item->checksum_sha256 != NULL) + fu_common_string_append_kv (str, idt, "ChecksumSha256", item->checksum_sha256); + if (blobstr != NULL) + fu_common_string_append_kv (str, idt, "BlobStr", blobstr); +} + +static gchar * +fu_tpm_eventlog_read_strhex_safe (const guint8 *buf, + gsize bufsz, + gsize offset, + gsize length, + GError **error) +{ + g_autoptr(GString) csum = g_string_new (NULL); + g_autofree guint8 *digest = g_malloc0 (length); + if (!fu_memcpy_safe (digest, length, 0x0, /* dst */ + buf, bufsz, offset, /* src */ + length, error)) + return FALSE; + for (guint i = 0; i < length; i++) + g_string_append_printf (csum, "%02x", digest[i]); + return g_string_free (g_steal_pointer (&csum), FALSE); +} + +static GPtrArray * +fu_tpm_eventlog_parser_parse_blob_v2 (const guint8 *buf, gsize bufsz, + FuTpmEventlogParserFlags flags, + GError **error) +{ + guint32 hdrsz = 0x0; + g_autoptr(GPtrArray) items = NULL; + + /* advance over the header block */ + if (!fu_common_read_uint32_safe (buf, bufsz, + FU_TPM_EVENTLOG_V1_IDX_EVENT_SIZE, + &hdrsz, G_LITTLE_ENDIAN, error)) + return NULL; + items = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_tpm_eventlog_parser_item_free); + for (gsize idx = FU_TPM_EVENTLOG_V1_SIZE + hdrsz; idx < bufsz;) { + guint32 pcr = 0; + guint32 event_type = 0; + guint32 digestcnt = 0; + guint32 datasz = 0; + g_autofree gchar *checksum_sha1 = NULL; + g_autofree gchar *checksum_sha256 = NULL; + + /* read entry */ + if (!fu_common_read_uint32_safe (buf, bufsz, + idx + FU_TPM_EVENTLOG_V2_IDX_PCR, + &pcr, G_LITTLE_ENDIAN, error)) + return NULL; + if (!fu_common_read_uint32_safe (buf, bufsz, + idx + FU_TPM_EVENTLOG_V2_IDX_TYPE, + &event_type, G_LITTLE_ENDIAN, error)) + return NULL; + if (!fu_common_read_uint32_safe (buf, bufsz, + idx + FU_TPM_EVENTLOG_V2_IDX_DIGEST_COUNT, + &digestcnt, G_LITTLE_ENDIAN, error)) + return NULL; + + /* read checksum block */ + idx += FU_TPM_EVENTLOG_V2_SIZE; + for (guint i = 0; i < digestcnt; i++) { + guint16 alg_type = 0; + guint32 alg_size = 0; + + /* get checksum type */ + if (!fu_common_read_uint16_safe (buf, bufsz, idx, + &alg_type, G_LITTLE_ENDIAN, error)) + return NULL; + alg_size = fu_tpm_eventlog_hash_get_size (alg_type); + if (alg_size == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "hash algorithm 0x%x size not known", + alg_type); + return NULL; + } + + /* build checksum */ + idx += sizeof(alg_type); + if (alg_type == TPM2_ALG_SHA1 || + flags & FU_TPM_EVENTLOG_PARSER_FLAG_ALL_ALGS) { + g_autofree gchar *csum_tmp = NULL; + csum_tmp = fu_tpm_eventlog_read_strhex_safe (buf, + bufsz, + idx, + alg_size, + error); + if (csum_tmp == NULL) + return NULL; + + /* save this for analysis */ + if (alg_type == TPM2_ALG_SHA1) + checksum_sha1 = g_steal_pointer (&csum_tmp); + else if (alg_type == TPM2_ALG_SHA256) + checksum_sha256 = g_steal_pointer (&csum_tmp); + } + + /* next block */ + idx += alg_size; + } + + /* read data block */ + if (!fu_common_read_uint32_safe (buf, bufsz, idx, + &datasz, G_LITTLE_ENDIAN, error)) + return NULL; + if (datasz > 1024 * 1024) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "event log item too large"); + return NULL; + } + + /* save blob if PCR=0 */ + idx += sizeof(datasz); + if (pcr == ESYS_TR_PCR0 || + flags & FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS) { + FuTpmEventlogItem *item; + g_autofree guint8 *data = NULL; + + /* build item */ + data = g_malloc0 (datasz); + if (!fu_memcpy_safe (data, datasz, 0x0, /* dst */ + buf, bufsz, idx, datasz, /* src */ + error)) + return NULL; + + /* not normally required */ + if (g_getenv ("FWUPD_TPM_EVENTLOG_VERBOSE") != NULL) { + fu_common_dump_full (G_LOG_DOMAIN, "Event Data", + data, datasz, 20, + FU_DUMP_FLAGS_SHOW_ASCII); + } + item = g_new0 (FuTpmEventlogItem, 1); + item->pcr = pcr; + item->kind = event_type; + item->checksum_sha1 = g_steal_pointer (&checksum_sha1); + item->checksum_sha256 = g_steal_pointer (&checksum_sha256); + item->blob = g_bytes_new_take (g_steal_pointer (&data), datasz); + g_ptr_array_add (items, item); + } + + /* next entry */ + idx += datasz; + } + + /* success */ + return g_steal_pointer (&items); +} + +GPtrArray * +fu_tpm_eventlog_parser_new (const guint8 *buf, gsize bufsz, + FuTpmEventlogParserFlags flags, + GError **error) +{ + gchar sig[] = FU_TPM_EVENTLOG_V2_HDR_SIGNATURE; + g_autoptr(GPtrArray) items = NULL; + + g_return_val_if_fail (buf != NULL, NULL); + + /* look for TCG v2 signature */ + if (!fu_memcpy_safe ((guint8 *) sig, sizeof(sig), 0x0, /* dst */ + buf, bufsz, FU_TPM_EVENTLOG_V1_SIZE, /* src */ + sizeof(sig), error)) + return NULL; + if (g_strcmp0 (sig, FU_TPM_EVENTLOG_V2_HDR_SIGNATURE) == 0) + return fu_tpm_eventlog_parser_parse_blob_v2 (buf, bufsz, flags, error); + + /* assume v1 structure */ + items = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_tpm_eventlog_parser_item_free); + for (gsize idx = 0; idx < bufsz; idx += FU_TPM_EVENTLOG_V1_SIZE) { + guint32 datasz = 0; + guint32 pcr = 0; + guint32 event_type = 0; + if (!fu_common_read_uint32_safe (buf, bufsz, + idx + FU_TPM_EVENTLOG_V1_IDX_PCR, + &pcr, G_LITTLE_ENDIAN, error)) + return NULL; + if (!fu_common_read_uint32_safe (buf, bufsz, + idx + FU_TPM_EVENTLOG_V1_IDX_TYPE, + &event_type, G_LITTLE_ENDIAN, error)) + return NULL; + if (!fu_common_read_uint32_safe (buf, bufsz, + idx + FU_TPM_EVENTLOG_V1_IDX_EVENT_SIZE, + &datasz, G_LITTLE_ENDIAN, error)) + return NULL; + if (datasz > 1024 * 1024) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "event log item too large"); + return NULL; + } + if (pcr == ESYS_TR_PCR0 || + flags & FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS) { + FuTpmEventlogItem *item; + g_autofree gchar *csum = NULL; + g_autofree guint8 *data = NULL; + + /* build checksum */ + csum = fu_tpm_eventlog_read_strhex_safe (buf, bufsz, + idx + FU_TPM_EVENTLOG_V1_IDX_DIGEST, + TPM2_SHA1_DIGEST_SIZE, + error); + if (csum == NULL) + return NULL; + + /* build item */ + data = g_malloc0 (datasz); + if (!fu_memcpy_safe (data, datasz, 0x0, /* dst */ + buf, bufsz, idx + FU_TPM_EVENTLOG_V1_SIZE, datasz, /* src */ + error)) + return NULL; + item = g_new0 (FuTpmEventlogItem, 1); + item->pcr = pcr; + item->kind = event_type; + item->checksum_sha1 = g_steal_pointer (&csum); + item->blob = g_bytes_new_take (g_steal_pointer (&data), datasz); + g_ptr_array_add (items, item); + + /* not normally required */ + if (g_getenv ("FWUPD_TPM_EVENTLOG_VERBOSE") != NULL) + fu_common_dump_bytes (G_LOG_DOMAIN, "Event Data", item->blob); + } + idx += datasz; + } + return g_steal_pointer (&items); + +} diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-parser.h b/plugins/tpm-eventlog/fu-tpm-eventlog-parser.h new file mode 100644 index 000000000..d951c46c7 --- /dev/null +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-parser.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +#include "fu-tpm-eventlog-common.h" + +typedef struct { + guint8 pcr; + FuTpmEventlogItemKind kind; + gchar *checksum_sha1; + gchar *checksum_sha256; + GBytes *blob; +} FuTpmEventlogItem; + +typedef enum { + FU_TPM_EVENTLOG_PARSER_FLAG_NONE = 0, + FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS = 1 << 0, + FU_TPM_EVENTLOG_PARSER_FLAG_ALL_ALGS = 1 << 1, + FU_TPM_EVENTLOG_PARSER_FLAG_LAST +} FuTpmEventlogParserFlags; + +GPtrArray *fu_tpm_eventlog_parser_new (const guint8 *buf, + gsize bufsz, + FuTpmEventlogParserFlags flags, + GError **error); +void fu_tpm_eventlog_item_to_string (FuTpmEventlogItem *item, + guint idt, + GString *str); diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog.c b/plugins/tpm-eventlog/fu-tpm-eventlog.c new file mode 100644 index 000000000..ebd3256e5 --- /dev/null +++ b/plugins/tpm-eventlog/fu-tpm-eventlog.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuTpmEventlog" + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "fu-tpm-eventlog-parser.h" + +static gint +fu_tmp_eventlog_sort_cb (gconstpointer a, gconstpointer b) +{ + FuTpmEventlogItem *item_a = *((FuTpmEventlogItem **) a); + FuTpmEventlogItem *item_b = *((FuTpmEventlogItem **) b); + if (item_a->pcr > item_b->pcr) + return 1; + if (item_a->pcr < item_b->pcr) + return -1; + return 0; +} + +static gboolean +fu_tmp_eventlog_process (const gchar *fn, gint pcr, GError **error) +{ + gsize bufsz = 0; + g_autofree guint8 *buf = NULL; + g_autoptr(GPtrArray) items = NULL; + g_autoptr(GString) str = g_string_new (NULL); + + /* parse this */ + if (!g_file_get_contents (fn, (gchar **) &buf, &bufsz, error)) + return FALSE; + items = fu_tpm_eventlog_parser_new (buf, bufsz, + FU_TPM_EVENTLOG_PARSER_FLAG_ALL_ALGS | + FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS, + error); + if (items == NULL) + return FALSE; + g_ptr_array_sort (items, fu_tmp_eventlog_sort_cb); + + for (guint i = 0; i < items->len; i++) { + FuTpmEventlogItem *item = g_ptr_array_index (items, i); + if (pcr >= 0 && item->pcr != pcr) + continue; + fu_tpm_eventlog_item_to_string (item, 0, str); + g_string_append (str, "\n"); + } + + /* success */ + g_print ("%s", str->str); + return TRUE; +} + +int +main (int argc, char *argv[]) +{ + const gchar *fn; + gboolean verbose = FALSE; + gint pcr = -1; + g_autoptr(GError) error = NULL; + g_autoptr(GOptionContext) context = g_option_context_new (NULL); + const GOptionEntry options[] = { + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + /* TRANSLATORS: command line option */ + _("Show extra debugging information"), NULL }, + { "pcr", 'p', 0, G_OPTION_ARG_INT, &pcr, + /* TRANSLATORS: command line option */ + _("Only show single PCR value"), NULL }, + { NULL} + }; + + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + /* TRANSLATORS: program name */ + g_option_context_add_main_entries (context, options, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + /* TRANSLATORS: the user didn't read the man page */ + g_print ("%s: %s\n", _("Failed to parse arguments"), + error->message); + return EXIT_FAILURE; + } + + /* set verbose? */ + if (verbose) { + g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); + g_setenv ("FWUPD_TPM_EVENTLOG_VERBOSE", "1", FALSE); + } + + /* allow user to chose a local file */ + fn = argc <= 1 ? "/sys/kernel/security/tpm0/binary_bios_measurements" : argv[1]; + if (!fu_tmp_eventlog_process (fn, pcr, &error)) { + /* TRANSLATORS: failed to read measurements file */ + g_printerr ("%s: %s\n", _("Failed to parse file"), + error->message); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/plugins/tpm-eventlog/meson.build b/plugins/tpm-eventlog/meson.build new file mode 100644 index 000000000..b8acf26fa --- /dev/null +++ b/plugins/tpm-eventlog/meson.build @@ -0,0 +1,90 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginTpmEventlog"'] + +shared_module('fu_plugin_tpm_eventlog', + fu_hash, + sources : [ + 'fu-plugin-tpm-eventlog.c', + 'fu-tpm-eventlog-common.c', + 'fu-tpm-eventlog-device.c', + 'fu-tpm-eventlog-parser.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupdplugin, + fwupd, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + tpm2tss, + ], +) + +if get_option('tests') + testdatadir = join_paths(meson.current_source_dir(), 'tests') + cargs += '-DTESTDATADIR="' + testdatadir + '"' + e = executable( + 'tpm-eventlog-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-tpm-eventlog-common.c', + 'fu-tpm-eventlog-device.c', + 'fu-tpm-eventlog-parser.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + link_with : [ + fwupdplugin, + fwupd, + ], + dependencies : [ + plugin_deps, + tpm2tss, + ], + c_args : cargs + ) + test('tpm-eventlog-self-test', e) +endif + +fwupdtpmevlog = executable( + 'fwupdtpmevlog', + sources : [ + 'fu-tpm-eventlog.c', + 'fu-tpm-eventlog-common.c', + 'fu-tpm-eventlog-parser.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies : [ + plugin_deps, + tpm2tss, + ], + link_with : [ + fwupd, + fwupdplugin, + ], + install : true, + install_dir : join_paths(libexecdir, 'fwupd') +) + +run_target('fuzz-tpm-eventlog', + command: [ + join_paths(meson.source_root(), 'contrib/afl-fuzz.py'), + '-i', join_paths(meson.current_source_dir(), 'tests'), + '-o', join_paths(meson.current_build_dir(), 'findings'), + fwupdtpmevlog, + ], +) diff --git a/plugins/tpm-eventlog/tests/binary_bios_measurements-v1 b/plugins/tpm-eventlog/tests/binary_bios_measurements-v1 new file mode 100644 index 000000000..0618456e9 Binary files /dev/null and b/plugins/tpm-eventlog/tests/binary_bios_measurements-v1 differ diff --git a/plugins/tpm-eventlog/tests/binary_bios_measurements-v2 b/plugins/tpm-eventlog/tests/binary_bios_measurements-v2 new file mode 100644 index 000000000..5aacd4197 Binary files /dev/null and b/plugins/tpm-eventlog/tests/binary_bios_measurements-v2 differ diff --git a/plugins/tpm/fu-tpm-device.c b/plugins/tpm/fu-tpm-device.c index 0ebd0311c..18f3deeea 100644 --- a/plugins/tpm/fu-tpm-device.c +++ b/plugins/tpm/fu-tpm-device.c @@ -238,6 +238,7 @@ fu_tpm_device_init (FuTpmDevice *self) fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_icon (FU_DEVICE (self), "computer"); fu_udev_device_set_flags (FU_UDEV_DEVICE (self), FU_UDEV_DEVICE_FLAG_NONE); + fu_device_add_instance_id (FU_DEVICE (self), "system-tpm"); } static void diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index ec590423d..ea84a0928 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -37,6 +37,7 @@ fu_plugin_init (FuPlugin *plugin) FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); data->bgrt = fu_uefi_bgrt_new (); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "upower"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "tpm_eventlog"); fu_plugin_add_compile_version (plugin, "com.redhat.efivar", EFIVAR_LIBRARY_VERSION); fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); } diff --git a/po/POTFILES.in b/po/POTFILES.in index 7494ccf29..1c13355c7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -2,6 +2,7 @@ data/remotes.d/lvfs.metainfo.xml data/remotes.d/lvfs-testing.metainfo.xml policy/org.freedesktop.fwupd.policy.in plugins/dfu/dfu-tool.c +plugins/tpm-eventlog/fu-tpm-eventlog.c plugins/uefi/fu-plugin-uefi.c plugins/uefi/fu-uefi-tool.c src/fu-agent.c