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"
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!");
}

View File

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

View File

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

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

View File

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

View File

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

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