From 59d947ac24aff2343e98d671ceb36170453345cc Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 31 Jan 2020 20:20:47 +0000 Subject: [PATCH] tpm-eventlog: Replay the TPM event log to get the PCRx values In theory, these should always match the reported PCRx values from the TPM. If the reconstructed event log checksum does not match the TPM value then something is either implemented wrongly, or something bad has happened. --- plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c | 54 +++++++++++++++ plugins/tpm-eventlog/fu-self-test.c | 18 +++++ plugins/tpm-eventlog/fu-tpm-eventlog-common.c | 69 +++++++++++++++++++ plugins/tpm-eventlog/fu-tpm-eventlog-common.h | 3 + plugins/tpm-eventlog/fu-tpm-eventlog-device.c | 15 ++++ plugins/tpm-eventlog/fu-tpm-eventlog-device.h | 3 + plugins/tpm-eventlog/fu-tpm-eventlog.c | 11 +++ 7 files changed, 173 insertions(+) diff --git a/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c b/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c index bcf372dff..9fff4cc2e 100644 --- a/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c +++ b/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c @@ -11,15 +11,30 @@ #include "fu-tpm-eventlog-device.h" +struct FuPluginData { + GPtrArray *pcr0s; +}; + void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_BEFORE, "uefi"); fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); } +void +fu_plugin_destroy (FuPlugin *plugin) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + if (data->pcr0s != NULL) + g_ptr_array_unref (data->pcr0s); +} + gboolean fu_plugin_coldplug (FuPlugin *plugin, GError **error) { + FuPluginData *data = fu_plugin_get_data (plugin); gsize bufsz = 0; const gchar *fn = "/sys/kernel/security/tpm0/binary_bios_measurements"; g_autofree gchar *str = NULL; @@ -41,6 +56,15 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) if (!fu_device_setup (FU_DEVICE (dev), error)) return FALSE; + /* save this so we can compare against system-firmware */ + data->pcr0s = fu_tpm_eventlog_device_get_checksums (dev, 0, error); + if (data->pcr0s == NULL) + return FALSE; + for (guint i = 0; i < data->pcr0s->len; i++) { + const gchar *csum = g_ptr_array_index (data->pcr0s, i); + fu_device_add_checksum (FU_DEVICE (dev), csum); + } + /* add optional report metadata */ str = fu_tpm_eventlog_device_report_metadata (dev); g_debug ("using TPM event log report data of:\n%s", str); @@ -49,3 +73,33 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) return TRUE; } +void +fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + GPtrArray *checksums; + + /* only care about UEFI devices from ESRT */ + if (g_strcmp0 (fu_device_get_plugin (device), "uefi") != 0) + return; + + /* only the system-firmware device gets checksums */ + checksums = fu_device_get_checksums (device); + if (checksums->len == 0) + return; + for (guint i = 0; i < checksums->len; i++) { + const gchar *checksum = g_ptr_array_index (checksums, i); + for (guint j = 0; j < data->pcr0s->len; j++) { + const gchar *checksum_tmp = g_ptr_array_index (data->pcr0s, j); + if (g_strcmp0 (checksum, checksum_tmp) == 0) { + g_debug ("TPM reconstructed event log matched PCR0 reading"); + return; + } + } + } + + /* urgh, this is unexpected */ + fu_device_set_update_error (device, + "TPM PCR0 differs from reconstruction, " + "please report!"); +} diff --git a/plugins/tpm-eventlog/fu-self-test.c b/plugins/tpm-eventlog/fu-self-test.c index 7d287ca31..0e238f63f 100644 --- a/plugins/tpm-eventlog/fu-self-test.c +++ b/plugins/tpm-eventlog/fu-self-test.c @@ -14,6 +14,7 @@ static void fu_test_tpm_eventlog_parse_v1_func (void) { + const gchar *tmp; gboolean ret; gsize bufsz = 0; g_autofree gchar *fn = NULL; @@ -21,6 +22,7 @@ fu_test_tpm_eventlog_parse_v1_func (void) g_autofree gchar *str = NULL; g_autoptr(FuTpmEventlogDevice) dev = NULL; g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v1", NULL); ret = g_file_get_contents (fn, (gchar **) &buf, &bufsz, &error); @@ -34,11 +36,19 @@ fu_test_tpm_eventlog_parse_v1_func (void) g_print ("%s\n", str); g_assert_nonnull (g_strstr_len (str, -1, "231f248f12ef9f38549f1bda7a859b781b5caab0")); g_assert_nonnull (g_strstr_len (str, -1, "9069ca78e7450a285173431b3e52c5c25299e473")); + + pcr0s = fu_tpm_eventlog_device_get_checksums (dev, 0, &error); + g_assert_no_error (error); + g_assert_nonnull (pcr0s); + g_assert_cmpint (pcr0s->len, ==, 1); + tmp = g_ptr_array_index (pcr0s, 0); + g_assert_cmpstr (tmp, ==, "543ae96e57b6fc4003531cd0dab1d9ba7f8166e0"); } static void fu_test_tpm_eventlog_parse_v2_func (void) { + const gchar *tmp; gboolean ret; gsize bufsz = 0; g_autofree gchar *fn = NULL; @@ -46,6 +56,7 @@ fu_test_tpm_eventlog_parse_v2_func (void) g_autofree gchar *str = NULL; g_autoptr(FuTpmEventlogDevice) dev = NULL; g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v2", NULL); ret = g_file_get_contents (fn, (gchar **) &buf, &bufsz, &error); @@ -60,6 +71,13 @@ fu_test_tpm_eventlog_parse_v2_func (void) 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")); + + pcr0s = fu_tpm_eventlog_device_get_checksums (dev, 0, &error); + g_assert_no_error (error); + g_assert_nonnull (pcr0s); + g_assert_cmpint (pcr0s->len, ==, 1); + tmp = g_ptr_array_index (pcr0s, 0); + g_assert_cmpstr (tmp, ==, "ebead4b31c7c49e193c440cd6ee90bc1b61a3ca6"); } int diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-common.c b/plugins/tpm-eventlog/fu-tpm-eventlog-common.c index 961a56740..29b234ab9 100644 --- a/plugins/tpm-eventlog/fu-tpm-eventlog-common.c +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-common.c @@ -162,3 +162,72 @@ fu_tpm_eventlog_blobstr (GBytes *blob) return NULL; return g_string_free (g_steal_pointer (&str), FALSE); } + +GPtrArray * +fu_tpm_eventlog_calc_checksums (GPtrArray *items, guint8 pcr, GError **error) +{ + guint cnt_sha1 = 0; + guint cnt_sha256 = 0; + guint8 digest_sha1[TPM2_SHA1_DIGEST_SIZE] = { 0x0 }; + guint8 digest_sha256[TPM2_SHA256_DIGEST_SIZE] = { 0x0 }; + gsize digest_sha1_len = sizeof(digest_sha1); + gsize digest_sha256_len = sizeof(digest_sha256); + g_autoptr(GPtrArray) csums = g_ptr_array_new_with_free_func (g_free); + + /* sanity check */ + if (items->len == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no event log data"); + return NULL; + } + + /* take existing PCR hash, append new measurement to that, + * hash that with the same algorithm */ + for (guint i = 0; i < items->len; i++) { + FuTpmEventlogItem *item = g_ptr_array_index (items, i); + if (item->pcr != pcr) + continue; + if (item->checksum_sha1 != NULL) { + g_autoptr(GChecksum) csum_sha1 = g_checksum_new (G_CHECKSUM_SHA1); + g_checksum_update (csum_sha1, + (const guchar *) digest_sha1, + digest_sha1_len); + g_checksum_update (csum_sha1, + (const guchar *) g_bytes_get_data (item->checksum_sha1, NULL), + g_bytes_get_size (item->checksum_sha1)); + g_checksum_get_digest (csum_sha1, digest_sha1, &digest_sha1_len); + cnt_sha1++; + } + if (item->checksum_sha256 != NULL) { + g_autoptr(GChecksum) csum_sha256 = g_checksum_new (G_CHECKSUM_SHA256); + g_checksum_update (csum_sha256, + (const guchar *) digest_sha256, + digest_sha256_len); + g_checksum_update (csum_sha256, + (const guchar *) g_bytes_get_data (item->checksum_sha256, NULL), + g_bytes_get_size (item->checksum_sha256)); + g_checksum_get_digest (csum_sha256, digest_sha256, &digest_sha256_len); + cnt_sha256++; + } + } + if (cnt_sha1 == 0 && cnt_sha256 == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no SHA1 or SHA256 data"); + return NULL; + } + if (cnt_sha1 > 0) { + g_autoptr(GBytes) blob_sha1 = NULL; + blob_sha1 = g_bytes_new_static (digest_sha1, sizeof(digest_sha1)); + g_ptr_array_add (csums, fu_tpm_eventlog_strhex (blob_sha1)); + } + if (cnt_sha256 > 0) { + g_autoptr(GBytes) blob_sha256 = NULL; + blob_sha256 = g_bytes_new_static (digest_sha256, sizeof(digest_sha256)); + g_ptr_array_add (csums, fu_tpm_eventlog_strhex (blob_sha256)); + } + return g_steal_pointer (&csums); +} diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-common.h b/plugins/tpm-eventlog/fu-tpm-eventlog-common.h index c34bc0082..3e82e9e27 100644 --- a/plugins/tpm-eventlog/fu-tpm-eventlog-common.h +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-common.h @@ -56,3 +56,6 @@ 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_strhex (GBytes *blob); gchar *fu_tpm_eventlog_blobstr (GBytes *blob); +GPtrArray *fu_tpm_eventlog_calc_checksums (GPtrArray *items, + guint8 pcr, + GError **error); diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-device.c b/plugins/tpm-eventlog/fu-tpm-eventlog-device.c index bc5cb8d78..33e572745 100644 --- a/plugins/tpm-eventlog/fu-tpm-eventlog-device.c +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-device.c @@ -20,6 +20,12 @@ struct _FuTpmEventlogDevice { G_DEFINE_TYPE (FuTpmEventlogDevice, fu_tpm_eventlog_device, FU_TYPE_DEVICE) +GPtrArray * +fu_tpm_eventlog_device_get_checksums (FuTpmEventlogDevice *self, guint8 pcr, GError **error) +{ + return fu_tpm_eventlog_calc_checksums (self->items, pcr, error); +} + static void fu_tpm_eventlog_device_to_string (FuDevice *device, guint idt, GString *str) { @@ -37,6 +43,8 @@ gchar * fu_tpm_eventlog_device_report_metadata (FuTpmEventlogDevice *self) { GString *str = g_string_new (""); + g_autoptr(GPtrArray) pcrs = NULL; + 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); @@ -46,6 +54,13 @@ fu_tpm_eventlog_device_report_metadata (FuTpmEventlogDevice *self) g_string_append_printf (str, " [%s]", blobstr); g_string_append (str, "\n"); } + pcrs = fu_tpm_eventlog_calc_checksums (self->items, 0, NULL); + if (pcrs != NULL) { + for (guint j = 0; j < pcrs->len; j++) { + const gchar *csum = g_ptr_array_index (pcrs, j); + g_string_append_printf (str, "PCR0: %s\n", csum); + } + } if (str->len > 0) g_string_truncate (str, str->len - 1); return g_string_free (str, FALSE); diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-device.h b/plugins/tpm-eventlog/fu-tpm-eventlog-device.h index a77e9a657..408d4162d 100644 --- a/plugins/tpm-eventlog/fu-tpm-eventlog-device.h +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-device.h @@ -15,3 +15,6 @@ FuTpmEventlogDevice *fu_tpm_eventlog_device_new (const guint8 *buf, gsize bufsz, GError **error); gchar *fu_tpm_eventlog_device_report_metadata (FuTpmEventlogDevice *self); +GPtrArray *fu_tpm_eventlog_device_get_checksums (FuTpmEventlogDevice *self, + guint8 pcr, + GError **error); diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog.c b/plugins/tpm-eventlog/fu-tpm-eventlog.c index ebd3256e5..1524b59db 100644 --- a/plugins/tpm-eventlog/fu-tpm-eventlog.c +++ b/plugins/tpm-eventlog/fu-tpm-eventlog.c @@ -54,6 +54,17 @@ fu_tmp_eventlog_process (const gchar *fn, gint pcr, GError **error) fu_tpm_eventlog_item_to_string (item, 0, str); g_string_append (str, "\n"); } + fu_common_string_append_kv (str, 0, "PCRs", NULL); + for (guint8 i = 0; i < 10; i++) { + g_autoptr(GPtrArray) pcrs = fu_tpm_eventlog_calc_checksums (items, i, NULL); + if (pcrs == NULL) + continue; + for (guint j = 0; j < pcrs->len; j++) { + const gchar *csum = g_ptr_array_index (pcrs, j); + g_autofree gchar *title = g_strdup_printf ("%u", i); + fu_common_string_append_kv (str, 1, title, csum); + } + } /* success */ g_print ("%s", str->str);