Check system integrity when installing UEFI updates

Recently we had an update that changed the system-defined Platform Key, and
we've certainly had updates in the past that changed the Boot#### variables.

Store some core ACPI and UEFI system integrity state from before and after the
update which can be used to mark (waivable) test failures on the LVFS.
This commit is contained in:
Richard Hughes 2022-10-19 13:57:32 +01:00
parent e79f0f23cb
commit 1e7d741601
9 changed files with 154 additions and 4 deletions

View File

@ -448,6 +448,8 @@ fwupd_plugin_flag_to_string(FwupdPluginFlags plugin_flag)
return "secure-config";
if (plugin_flag == FWUPD_PLUGIN_FLAG_MODULAR)
return "modular";
if (plugin_flag == FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY)
return "measure-system-integrity";
return NULL;
}
@ -496,6 +498,8 @@ fwupd_plugin_flag_from_string(const gchar *plugin_flag)
return FWUPD_PLUGIN_FLAG_SECURE_CONFIG;
if (g_strcmp0(plugin_flag, "modular") == 0)
return FWUPD_PLUGIN_FLAG_MODULAR;
if (g_strcmp0(plugin_flag, "measure-system-integrity") == 0)
return FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY;
return FWUPD_DEVICE_FLAG_UNKNOWN;
}

View File

@ -879,6 +879,14 @@ typedef enum {
* Since: 1.8.6
*/
#define FWUPD_PLUGIN_FLAG_MODULAR (1u << 14)
/**
* FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY:
*
* The plugin will be checked that it preserves system state such as `KEK`, `PK`, `BOOT####` etc.
*
* Since: 1.8.7
*/
#define FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY (1u << 15)
/**
* FWUPD_PLUGIN_FLAG_UNKNOWN:
*

View File

@ -170,7 +170,7 @@ fwupd_enums_func(void)
g_assert_cmpstr(tmp, !=, NULL);
g_assert_cmpint(fwupd_device_problem_from_string(tmp), ==, i);
}
for (guint64 i = 1; i <= FWUPD_PLUGIN_FLAG_MODULAR; i *= 2) {
for (guint64 i = 1; i <= FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY; i *= 2) {
const gchar *tmp = fwupd_plugin_flag_to_string(i);
if (tmp == NULL)
g_warning("missing plugin flag 0x%x", (guint)i);

View File

@ -339,6 +339,7 @@ fu_flashrom_plugin_constructed(GObject *obj)
fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "linux_lockdown");
fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "coreboot"); /* obsoleted */
fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_REQUIRE_HWID);
fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY);
}
static void

View File

@ -997,6 +997,7 @@ static void
fu_uefi_capsule_plugin_init(FuUefiCapsulePlugin *self)
{
self->bgrt = fu_uefi_bgrt_new();
fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY);
}
static void

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
@ -187,3 +188,106 @@ fu_engine_update_devices_file(FuEngine *self, GError **error)
target = g_build_filename(directory, "devices.json", NULL);
return g_file_set_contents(target, data, (gssize)len, error);
}
static void
fu_engine_integrity_add_measurement(GHashTable *self, const gchar *id, GBytes *blob)
{
g_autofree gchar *csum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA256, blob);
g_hash_table_insert(self, g_strdup(id), g_steal_pointer(&csum));
}
static void
fu_engine_integrity_measure_acpi(GHashTable *self)
{
g_autofree gchar *path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES);
const gchar *tables[] = {"SLIC", "MSDM", "TPM2", NULL};
for (guint i = 0; tables[i] != NULL; i++) {
g_autofree gchar *fn = g_build_filename(path, tables[i], NULL);
g_autoptr(GBytes) blob = NULL;
blob = fu_bytes_get_contents(fn, NULL);
if (blob != NULL && g_bytes_get_size(blob) > 0) {
g_autofree gchar *id = g_strdup_printf("ACPI:%s", tables[i]);
fu_engine_integrity_add_measurement(self, id, blob);
}
}
}
static void
fu_engine_integrity_measure_uefi(GHashTable *self)
{
struct {
const gchar *guid;
const gchar *name;
} keys[] = {{FU_EFIVAR_GUID_EFI_GLOBAL, "BootOrder"},
{FU_EFIVAR_GUID_EFI_GLOBAL, "BootCurrent"},
{FU_EFIVAR_GUID_EFI_GLOBAL, "KEK"},
{FU_EFIVAR_GUID_EFI_GLOBAL, "PK"},
{FU_EFIVAR_GUID_SECURITY_DATABASE, "db"},
{FU_EFIVAR_GUID_SECURITY_DATABASE, "dbx"},
{NULL, NULL}};
/* important keys */
for (guint i = 0; keys[i].guid != NULL; i++) {
g_autoptr(GBytes) blob =
fu_efivar_get_data_bytes(keys[i].guid, keys[i].name, NULL, NULL);
if (blob != NULL) {
g_autofree gchar *id = g_strdup_printf("UEFI:%s", keys[i].name);
fu_engine_integrity_add_measurement(self, id, blob);
}
}
/* Boot#### */
for (guint i = 0; i < 0xFF; i++) {
g_autofree gchar *name = g_strdup_printf("Boot%04X", i);
g_autoptr(GBytes) blob =
fu_efivar_get_data_bytes(FU_EFIVAR_GUID_EFI_GLOBAL, name, NULL, NULL);
if (blob != NULL && g_bytes_get_size(blob) > 0) {
g_autofree gchar *id = g_strdup_printf("UEFI:%s", name);
fu_engine_integrity_add_measurement(self, id, blob);
}
}
}
GHashTable *
fu_engine_integrity_new(GError **error)
{
g_autoptr(GHashTable) self = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
fu_engine_integrity_measure_uefi(self);
fu_engine_integrity_measure_acpi(self);
/* nothing of use */
if (g_hash_table_size(self) == 0) {
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no measurements");
return NULL;
}
/* success */
return g_steal_pointer(&self);
}
gchar *
fu_engine_integrity_to_string(GHashTable *self)
{
GHashTableIter iter;
gpointer key, value;
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free);
g_return_val_if_fail(self != NULL, NULL);
/* sanity check */
if (g_hash_table_size(self) == 0)
return NULL;
/* build into KV array */
g_hash_table_iter_init(&iter, self);
while (g_hash_table_iter_next(&iter, &key, &value)) {
g_ptr_array_add(array,
g_strdup_printf("%s=%s", (const gchar *)key, (const gchar *)value));
}
return fu_strjoin("\n", array);
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
@ -11,3 +12,8 @@ gboolean
fu_engine_update_motd(FuEngine *self, GError **error);
gboolean
fu_engine_update_devices_file(FuEngine *self, GError **error);
GHashTable *
fu_engine_integrity_new(GError **error);
gchar *
fu_engine_integrity_to_string(GHashTable *self);

View File

@ -2413,6 +2413,16 @@ fu_engine_install_releases(FuEngine *self,
return TRUE;
}
static void
fu_engine_update_release_integrity(FuEngine *self, FwupdRelease *release, const gchar *key)
{
g_autoptr(GHashTable) integrity = fu_engine_integrity_new(NULL);
if (integrity != NULL) {
g_autofree gchar *str = fu_engine_integrity_to_string(integrity);
fwupd_release_add_metadata_item(FWUPD_RELEASE(release), key, str);
}
}
static gboolean
fu_engine_add_release_metadata(FuEngine *self, FuRelease *release, FuPlugin *plugin, GError **error)
{
@ -2455,6 +2465,14 @@ fu_engine_add_release_metadata(FuEngine *self, FuRelease *release, FuPlugin *plu
}
}
}
/* measure the "old" system state */
if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY)) {
fu_engine_update_release_integrity(self,
FWUPD_RELEASE(release),
"SystemIntegrityOld");
}
return TRUE;
}
@ -7315,6 +7333,13 @@ fu_engine_update_history_device(FuEngine *self, FuDevice *dev_history, GError **
}
}
/* measure the "new" system state */
plugin = fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(dev), error);
if (plugin == NULL)
return FALSE;
if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY))
fu_engine_update_release_integrity(self, rel_history, "SystemIntegrityNew");
/* the system is running with the new firmware version */
if (fu_version_compare(fu_device_get_version(dev),
fwupd_release_get_version(rel_history),
@ -7338,9 +7363,6 @@ fu_engine_update_history_device(FuEngine *self, FuDevice *dev_history, GError **
}
/* does the plugin know the update failure */
plugin = fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(dev), error);
if (plugin == NULL)
return FALSE;
if (!fu_plugin_runner_get_results(plugin, dev, error))
return FALSE;

View File

@ -1702,6 +1702,10 @@ fu_util_plugin_flag_to_string(FwupdPluginFlags plugin_flag)
/* TRANSLATORS: the plugin was created from a .so object, and was not built-in */
return _("Loaded from an external module");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY) {
/* TRANSLATORS: check various UEFI and ACPI tables are unchanged after the update */
return _("Will measure elements of system integrity around an update");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED) {
/* TRANSLATORS: the user is using Gentoo/Arch and has screwed something up */
return _("Required efivarfs filesystem was not found");