fwupd/plugins/uefi-dbx/fu-uefi-dbx-device.c
Richard Hughes eddaed0c11 Allow specifying more than one VendorID for a device
Asking the user for the UID mapping isn't working very well, as it requires lots
of manual handholding. It also doesn't work very well when the device vendor
does not actually have a PCI ID or if the vendor has split into two entities.

Just use the OUI address as an additional VendorID and match any of the device
IDs against any of the metadata-supplied values.
2021-01-04 22:30:20 +00:00

184 lines
5.4 KiB
C

/*
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fu-efivar.h"
#include "fu-efi-signature-common.h"
#include "fu-efi-signature-parser.h"
#include "fu-uefi-dbx-common.h"
#include "fu-uefi-dbx-device.h"
struct _FuUefiDbxDevice {
FuDevice parent_instance;
};
G_DEFINE_TYPE (FuUefiDbxDevice, fu_uefi_dbx_device, FU_TYPE_DEVICE)
static gboolean
fu_uefi_dbx_device_write_firmware (FuDevice *device,
FuFirmware *firmware,
FwupdInstallFlags install_flags,
GError **error)
{
const guint8 *buf;
gsize bufsz = 0;
g_autoptr(GBytes) fw = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* write entire chunk to efivarfs */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
buf = g_bytes_get_data (fw, &bufsz);
if (!fu_efivar_set_data (FU_EFIVAR_GUID_SECURITY_DATABASE,
"dbx", buf, bufsz,
FU_EFIVAR_ATTR_APPEND_WRITE |
FU_EFIVAR_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS |
FU_EFIVAR_ATTR_RUNTIME_ACCESS |
FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS |
FU_EFIVAR_ATTR_NON_VOLATILE,
error)) {
return FALSE;
}
/* success! */
return TRUE;
}
static gboolean
fu_uefi_dbx_device_set_version_number (FuDevice *device, GError **error)
{
gsize bufsz = 0;
g_autofree gchar *version = NULL;
g_autofree guint8 *buf = NULL;
g_autoptr(GPtrArray) dbx = NULL;
/* use the number of checksums in the dbx as a version number, ignoring
* some owners that do not make sense */
if (!fu_efivar_get_data (FU_EFIVAR_GUID_SECURITY_DATABASE, "dbx",
&buf, &bufsz, NULL, error))
return FALSE;
dbx = fu_efi_signature_parser_new (buf, bufsz,
FU_EFI_SIGNATURE_PARSER_FLAGS_NONE,
error);
if (dbx == NULL)
return FALSE;
version = g_strdup_printf ("%u", fu_efi_signature_list_array_version (dbx));
fu_device_set_version (device, version);
fu_device_set_version_lowest (device, version);
return TRUE;
}
static FuFirmware *
fu_uefi_dbx_prepare_firmware (FuDevice *device,
GBytes *fw,
FwupdInstallFlags flags,
GError **error)
{
const guint8 *buf;
gsize bufsz = 0;
g_autoptr(GPtrArray) siglists = NULL;
/* parse dbx */
buf = g_bytes_get_data (fw, &bufsz);
siglists = fu_efi_signature_parser_new (buf, bufsz,
FU_EFI_SIGNATURE_PARSER_FLAGS_IGNORE_HEADER,
error);
if (siglists == NULL)
return NULL;
/* validate this is safe to apply */
if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY);
if (!fu_uefi_dbx_signature_list_validate (siglists, error)) {
g_prefix_error (error,
"Blocked executable in the ESP, "
"ensure grub and shim are up to date: ");
return NULL;
}
}
/* default blob */
return fu_firmware_new_from_bytes (fw);
}
static gboolean
fu_uefi_dbx_device_probe (FuDevice *device, GError **error)
{
gsize bufsz = 0;
g_autofree gchar *arch_up = NULL;
g_autofree guint8 *buf = NULL;
g_autoptr(GPtrArray) kek = NULL;
/* use each of the certificates in the KEK to generate the GUIDs */
if (!fu_efivar_get_data (FU_EFIVAR_GUID_EFI_GLOBAL, "KEK",
&buf, &bufsz, NULL, error))
return FALSE;
kek = fu_efi_signature_parser_new (buf, bufsz,
FU_EFI_SIGNATURE_PARSER_FLAGS_NONE,
error);
if (kek == NULL)
return FALSE;
arch_up = g_utf8_strup (EFI_MACHINE_TYPE_NAME, -1);
for (guint i = 0; i < kek->len; i++) {
FuEfiSignatureList *siglist = g_ptr_array_index (kek, i);
GPtrArray *sigs = fu_efi_signature_list_get_all (siglist);
for (guint j = 0; j < sigs->len; j++) {
FuEfiSignature *sig = g_ptr_array_index (sigs, j);
g_autofree gchar *checksum_up = NULL;
g_autofree gchar *devid1 = NULL;
g_autofree gchar *devid2 = NULL;
checksum_up = g_utf8_strup (fu_efi_signature_get_checksum (sig), -1);
devid1 = g_strdup_printf ("UEFI\\CRT_%s", checksum_up);
fu_device_add_instance_id (device, devid1);
devid2 = g_strdup_printf ("UEFI\\CRT_%s&ARCH_%s",
checksum_up, arch_up);
fu_device_add_instance_id (device, devid2);
}
}
return fu_uefi_dbx_device_set_version_number (device, error);
}
static void
fu_uefi_dbx_device_init (FuUefiDbxDevice *self)
{
fu_device_set_physical_id (FU_DEVICE (self), "dbx");
fu_device_set_name (FU_DEVICE (self), "UEFI dbx");
fu_device_set_summary (FU_DEVICE (self), "UEFI Revocation Database");
fu_device_add_vendor_id (FU_DEVICE (self), "UEFI:Linux Foundation");
fu_device_set_protocol (FU_DEVICE (self), "org.uefi.dbx");
fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_NUMBER);
fu_device_set_install_duration (FU_DEVICE (self), 1);
fu_device_add_icon (FU_DEVICE (self), "computer");
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL);
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT);
fu_device_add_parent_guid (FU_DEVICE (self), "main-system-firmware");
if (!fu_common_is_live_media ())
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
}
static void
fu_uefi_dbx_device_class_init (FuUefiDbxDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
klass_device->probe = fu_uefi_dbx_device_probe;
klass_device->write_firmware = fu_uefi_dbx_device_write_firmware;
klass_device->prepare_firmware = fu_uefi_dbx_prepare_firmware;
}
FuUefiDbxDevice *
fu_uefi_dbx_device_new (void)
{
FuUefiDbxDevice *self;
self = g_object_new (FU_TYPE_UEFI_DBX_DEVICE, NULL);
return self;
}