Add a new plugin that can parse the TPM event log

Some devices do not have a stable PCR0 for the same firmware version, and I'd
like to collect the TPM event log for affected machines to debug why.
This commit is contained in:
Richard Hughes 2019-12-02 11:31:46 +00:00
parent dad3597e76
commit f5c6e1d276
20 changed files with 985 additions and 0 deletions

View File

@ -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/*

View File

@ -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

View File

@ -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");

View File

@ -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')

View File

@ -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.

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* 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;
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupd.h>
#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 ();
}

View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* 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);
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <gio/gio.h>
#include <tss2/tss2_tpm2_types.h>
#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);

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <tss2/tss2_esys.h>
#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));
}

View File

@ -0,0 +1,17 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* 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);

View File

@ -0,0 +1,290 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <tss2/tss2_esys.h>
#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);
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* 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);

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuTpmEventlog"
#include "config.h"
#include <fwupd.h>
#include <glib/gi18n.h>
#include <locale.h>
#include <stdlib.h>
#include <unistd.h>
#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;
}

View File

@ -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,
],
)

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -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);
}

View File

@ -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