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.
This commit is contained in:
Richard Hughes 2020-01-31 20:20:47 +00:00
parent ca3e785c40
commit 59d947ac24
7 changed files with 173 additions and 0 deletions

View File

@ -11,15 +11,30 @@
#include "fu-tpm-eventlog-device.h" #include "fu-tpm-eventlog-device.h"
struct FuPluginData {
GPtrArray *pcr0s;
};
void void
fu_plugin_init (FuPlugin *plugin) 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); 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 gboolean
fu_plugin_coldplug (FuPlugin *plugin, GError **error) fu_plugin_coldplug (FuPlugin *plugin, GError **error)
{ {
FuPluginData *data = fu_plugin_get_data (plugin);
gsize bufsz = 0; gsize bufsz = 0;
const gchar *fn = "/sys/kernel/security/tpm0/binary_bios_measurements"; const gchar *fn = "/sys/kernel/security/tpm0/binary_bios_measurements";
g_autofree gchar *str = NULL; g_autofree gchar *str = NULL;
@ -41,6 +56,15 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error)
if (!fu_device_setup (FU_DEVICE (dev), error)) if (!fu_device_setup (FU_DEVICE (dev), error))
return FALSE; 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 */ /* add optional report metadata */
str = fu_tpm_eventlog_device_report_metadata (dev); str = fu_tpm_eventlog_device_report_metadata (dev);
g_debug ("using TPM event log report data of:\n%s", str); 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; 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!");
}

View File

@ -14,6 +14,7 @@
static void static void
fu_test_tpm_eventlog_parse_v1_func (void) fu_test_tpm_eventlog_parse_v1_func (void)
{ {
const gchar *tmp;
gboolean ret; gboolean ret;
gsize bufsz = 0; gsize bufsz = 0;
g_autofree gchar *fn = NULL; g_autofree gchar *fn = NULL;
@ -21,6 +22,7 @@ fu_test_tpm_eventlog_parse_v1_func (void)
g_autofree gchar *str = NULL; g_autofree gchar *str = NULL;
g_autoptr(FuTpmEventlogDevice) dev = NULL; g_autoptr(FuTpmEventlogDevice) dev = NULL;
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) pcr0s = NULL;
fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v1", NULL); fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v1", NULL);
ret = g_file_get_contents (fn, (gchar **) &buf, &bufsz, &error); 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_print ("%s\n", str);
g_assert_nonnull (g_strstr_len (str, -1, "231f248f12ef9f38549f1bda7a859b781b5caab0")); g_assert_nonnull (g_strstr_len (str, -1, "231f248f12ef9f38549f1bda7a859b781b5caab0"));
g_assert_nonnull (g_strstr_len (str, -1, "9069ca78e7450a285173431b3e52c5c25299e473")); 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 static void
fu_test_tpm_eventlog_parse_v2_func (void) fu_test_tpm_eventlog_parse_v2_func (void)
{ {
const gchar *tmp;
gboolean ret; gboolean ret;
gsize bufsz = 0; gsize bufsz = 0;
g_autofree gchar *fn = NULL; g_autofree gchar *fn = NULL;
@ -46,6 +56,7 @@ fu_test_tpm_eventlog_parse_v2_func (void)
g_autofree gchar *str = NULL; g_autofree gchar *str = NULL;
g_autoptr(FuTpmEventlogDevice) dev = NULL; g_autoptr(FuTpmEventlogDevice) dev = NULL;
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) pcr0s = NULL;
fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v2", NULL); fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v2", NULL);
ret = g_file_get_contents (fn, (gchar **) &buf, &bufsz, &error); 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, "19ce8e1347a709d2b485d519695e3ce10b939485"));
g_assert_nonnull (g_strstr_len (str, -1, "9069ca78e7450a285173431b3e52c5c25299e473")); g_assert_nonnull (g_strstr_len (str, -1, "9069ca78e7450a285173431b3e52c5c25299e473"));
g_assert_nonnull (g_strstr_len (str, -1, "Boot Guard Measured")); 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 int

View File

@ -162,3 +162,72 @@ fu_tpm_eventlog_blobstr (GBytes *blob)
return NULL; return NULL;
return g_string_free (g_steal_pointer (&str), FALSE); 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);
}

View File

@ -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); const gchar *fu_tpm_eventlog_item_kind_to_string (FuTpmEventlogItemKind event_type);
gchar *fu_tpm_eventlog_strhex (GBytes *blob); gchar *fu_tpm_eventlog_strhex (GBytes *blob);
gchar *fu_tpm_eventlog_blobstr (GBytes *blob); gchar *fu_tpm_eventlog_blobstr (GBytes *blob);
GPtrArray *fu_tpm_eventlog_calc_checksums (GPtrArray *items,
guint8 pcr,
GError **error);

View File

@ -20,6 +20,12 @@ struct _FuTpmEventlogDevice {
G_DEFINE_TYPE (FuTpmEventlogDevice, fu_tpm_eventlog_device, FU_TYPE_DEVICE) 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 static void
fu_tpm_eventlog_device_to_string (FuDevice *device, guint idt, GString *str) 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) fu_tpm_eventlog_device_report_metadata (FuTpmEventlogDevice *self)
{ {
GString *str = g_string_new (""); GString *str = g_string_new ("");
g_autoptr(GPtrArray) pcrs = NULL;
for (guint i = 0; i < self->items->len; i++) { for (guint i = 0; i < self->items->len; i++) {
FuTpmEventlogItem *item = g_ptr_array_index (self->items, i); FuTpmEventlogItem *item = g_ptr_array_index (self->items, i);
g_autofree gchar *blobstr = fu_tpm_eventlog_blobstr (item->blob); 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_printf (str, " [%s]", blobstr);
g_string_append (str, "\n"); 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) if (str->len > 0)
g_string_truncate (str, str->len - 1); g_string_truncate (str, str->len - 1);
return g_string_free (str, FALSE); return g_string_free (str, FALSE);

View File

@ -15,3 +15,6 @@ FuTpmEventlogDevice *fu_tpm_eventlog_device_new (const guint8 *buf,
gsize bufsz, gsize bufsz,
GError **error); GError **error);
gchar *fu_tpm_eventlog_device_report_metadata (FuTpmEventlogDevice *self); gchar *fu_tpm_eventlog_device_report_metadata (FuTpmEventlogDevice *self);
GPtrArray *fu_tpm_eventlog_device_get_checksums (FuTpmEventlogDevice *self,
guint8 pcr,
GError **error);

View File

@ -54,6 +54,17 @@ fu_tmp_eventlog_process (const gchar *fn, gint pcr, GError **error)
fu_tpm_eventlog_item_to_string (item, 0, str); fu_tpm_eventlog_item_to_string (item, 0, str);
g_string_append (str, "\n"); 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 */ /* success */
g_print ("%s", str->str); g_print ("%s", str->str);