mirror of
https://git.proxmox.com/git/fwupd
synced 2025-07-27 14:39:49 +00:00
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:
parent
e79f0f23cb
commit
1e7d741601
@ -448,6 +448,8 @@ fwupd_plugin_flag_to_string(FwupdPluginFlags plugin_flag)
|
|||||||
return "secure-config";
|
return "secure-config";
|
||||||
if (plugin_flag == FWUPD_PLUGIN_FLAG_MODULAR)
|
if (plugin_flag == FWUPD_PLUGIN_FLAG_MODULAR)
|
||||||
return "modular";
|
return "modular";
|
||||||
|
if (plugin_flag == FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY)
|
||||||
|
return "measure-system-integrity";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,6 +498,8 @@ fwupd_plugin_flag_from_string(const gchar *plugin_flag)
|
|||||||
return FWUPD_PLUGIN_FLAG_SECURE_CONFIG;
|
return FWUPD_PLUGIN_FLAG_SECURE_CONFIG;
|
||||||
if (g_strcmp0(plugin_flag, "modular") == 0)
|
if (g_strcmp0(plugin_flag, "modular") == 0)
|
||||||
return FWUPD_PLUGIN_FLAG_MODULAR;
|
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;
|
return FWUPD_DEVICE_FLAG_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -879,6 +879,14 @@ typedef enum {
|
|||||||
* Since: 1.8.6
|
* Since: 1.8.6
|
||||||
*/
|
*/
|
||||||
#define FWUPD_PLUGIN_FLAG_MODULAR (1u << 14)
|
#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:
|
* FWUPD_PLUGIN_FLAG_UNKNOWN:
|
||||||
*
|
*
|
||||||
|
@ -170,7 +170,7 @@ fwupd_enums_func(void)
|
|||||||
g_assert_cmpstr(tmp, !=, NULL);
|
g_assert_cmpstr(tmp, !=, NULL);
|
||||||
g_assert_cmpint(fwupd_device_problem_from_string(tmp), ==, i);
|
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);
|
const gchar *tmp = fwupd_plugin_flag_to_string(i);
|
||||||
if (tmp == NULL)
|
if (tmp == NULL)
|
||||||
g_warning("missing plugin flag 0x%x", (guint)i);
|
g_warning("missing plugin flag 0x%x", (guint)i);
|
||||||
|
@ -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_METADATA_SOURCE, "linux_lockdown");
|
||||||
fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "coreboot"); /* obsoleted */
|
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_REQUIRE_HWID);
|
||||||
|
fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -997,6 +997,7 @@ static void
|
|||||||
fu_uefi_capsule_plugin_init(FuUefiCapsulePlugin *self)
|
fu_uefi_capsule_plugin_init(FuUefiCapsulePlugin *self)
|
||||||
{
|
{
|
||||||
self->bgrt = fu_uefi_bgrt_new();
|
self->bgrt = fu_uefi_bgrt_new();
|
||||||
|
fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MEASURE_SYSTEM_INTEGRITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: LGPL-2.1+
|
* 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);
|
target = g_build_filename(directory, "devices.json", NULL);
|
||||||
return g_file_set_contents(target, data, (gssize)len, error);
|
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);
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: LGPL-2.1+
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
*/
|
*/
|
||||||
@ -11,3 +12,8 @@ gboolean
|
|||||||
fu_engine_update_motd(FuEngine *self, GError **error);
|
fu_engine_update_motd(FuEngine *self, GError **error);
|
||||||
gboolean
|
gboolean
|
||||||
fu_engine_update_devices_file(FuEngine *self, GError **error);
|
fu_engine_update_devices_file(FuEngine *self, GError **error);
|
||||||
|
|
||||||
|
GHashTable *
|
||||||
|
fu_engine_integrity_new(GError **error);
|
||||||
|
gchar *
|
||||||
|
fu_engine_integrity_to_string(GHashTable *self);
|
||||||
|
@ -2413,6 +2413,16 @@ fu_engine_install_releases(FuEngine *self,
|
|||||||
return TRUE;
|
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
|
static gboolean
|
||||||
fu_engine_add_release_metadata(FuEngine *self, FuRelease *release, FuPlugin *plugin, GError **error)
|
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;
|
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 */
|
/* the system is running with the new firmware version */
|
||||||
if (fu_version_compare(fu_device_get_version(dev),
|
if (fu_version_compare(fu_device_get_version(dev),
|
||||||
fwupd_release_get_version(rel_history),
|
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 */
|
/* 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))
|
if (!fu_plugin_runner_get_results(plugin, dev, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
@ -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 */
|
/* TRANSLATORS: the plugin was created from a .so object, and was not built-in */
|
||||||
return _("Loaded from an external module");
|
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) {
|
if (plugin_flag == FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED) {
|
||||||
/* TRANSLATORS: the user is using Gentoo/Arch and has screwed something up */
|
/* TRANSLATORS: the user is using Gentoo/Arch and has screwed something up */
|
||||||
return _("Required efivarfs filesystem was not found");
|
return _("Required efivarfs filesystem was not found");
|
||||||
|
Loading…
Reference in New Issue
Block a user