mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-15 10:29:47 +00:00
uefi-dbx: Validate the dbx update is safe to apply
To do this mount all ESP partitions and check all the binaries there to see if they match any entries in the new dbx. If we applied the update when a hash matched, we would unintentially 'brick' the users machine, as the grub and shim binaries *have* to be updated first. This functionality does reimplement the PE hashing functionality found in sbsigntools and pesign. This was done for 4 main reasons: * There were some memory safety issues found when fuzzing random binaries * Executing the tools hundreds of times was a lot of overhead * Operating from a blob of immutable mmap'd memory is much faster * We only need a very small amount of functionality from both tools
This commit is contained in:
parent
adabe53e2b
commit
1abb32c623
@ -221,6 +221,18 @@ main (int argc, char *argv[])
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* validate this is safe to apply */
|
||||||
|
if (!force) {
|
||||||
|
/* TRANSLATORS: ESP refers to the EFI System Partition */
|
||||||
|
g_print ("%s\n", _("Validating ESP contents…"));
|
||||||
|
if (!fu_uefi_dbx_signature_list_validate (dbx_update, &error)) {
|
||||||
|
/* TRANSLATORS: something with a blocked hash exists
|
||||||
|
* in the users ESP -- which would be bad! */
|
||||||
|
g_printerr ("%s: %s\n", _("Failed to validate ESP contents"), error->message);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* TRANSLATORS: actually sending the update to the hardware */
|
/* TRANSLATORS: actually sending the update to the hardware */
|
||||||
g_print ("%s\n", _("Applying update…"));
|
g_print ("%s\n", _("Applying update…"));
|
||||||
if (!fu_efivar_set_data (FU_EFIVAR_GUID_SECURITY_DATABASE,
|
if (!fu_efivar_set_data (FU_EFIVAR_GUID_SECURITY_DATABASE,
|
||||||
|
332
plugins/uefi-dbx/fu-efi-image.c
Normal file
332
plugins/uefi-dbx/fu-efi-image.c
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "fu-common.h"
|
||||||
|
#include "fu-efi-image.h"
|
||||||
|
|
||||||
|
struct _FuEfiImage {
|
||||||
|
GObject parent_instance;
|
||||||
|
gchar *checksum;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gsize offset;
|
||||||
|
gsize size;
|
||||||
|
gchar *name;
|
||||||
|
} FuEfiImageRegion;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
guint32 addr;
|
||||||
|
guint32 size;
|
||||||
|
} FuEfiImageDataDirEntry;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (FuEfiImage, fu_efi_image, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
#define _DOS_OFFSET_SIGNATURE 0x00
|
||||||
|
#define _DOS_OFFSET_TO_PE_HEADER 0x3c
|
||||||
|
|
||||||
|
#define _PEI_OFFSET_SIGNATURE 0x00
|
||||||
|
#define _PEI_OFFSET_MACHINE 0x04
|
||||||
|
#define _PEI_OFFSET_NUMBER_OF_SECTIONS 0x06
|
||||||
|
#define _PEI_OFFSET_OPTIONAL_HEADER_SIZE 0x14
|
||||||
|
#define _PEI_HEADER_SIZE 0x18
|
||||||
|
|
||||||
|
#define _PE_OFFSET_SIZE_OF_HEADERS 0x54
|
||||||
|
#define _PE_OFFSET_CHECKSUM 0x58
|
||||||
|
#define _PE_OFFSET_DEBUG_TABLE_OFFSET 0x98
|
||||||
|
|
||||||
|
#define _PEP_OFFSET_SIZE_OF_HEADERS 0x54
|
||||||
|
#define _PEP_OFFSET_CHECKSUM 0x58
|
||||||
|
#define _PEP_OFFSET_DEBUG_TABLE_OFFSET 0xa8
|
||||||
|
|
||||||
|
#define _SECTION_HEADER_OFFSET_NAME 0x0
|
||||||
|
#define _SECTION_HEADER_OFFSET_SIZE 0x10
|
||||||
|
#define _SECTION_HEADER_OFFSET_PTR 0x14
|
||||||
|
#define _SECTION_HEADER_SIZE 0x28
|
||||||
|
|
||||||
|
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||||
|
#define IMAGE_FILE_MACHINE_I386 0x014c
|
||||||
|
#define IMAGE_FILE_MACHINE_THUMB 0x01c2
|
||||||
|
#define IMAGE_FILE_MACHINE_AARCH64 0xaa64
|
||||||
|
|
||||||
|
static gint
|
||||||
|
fu_efi_image_region_sort_cb (gconstpointer a, gconstpointer b)
|
||||||
|
{
|
||||||
|
const FuEfiImageRegion *r1 = *((const FuEfiImageRegion **) a);
|
||||||
|
const FuEfiImageRegion *r2 = *((const FuEfiImageRegion **) b);
|
||||||
|
if (r1->offset < r2->offset)
|
||||||
|
return -1;
|
||||||
|
if (r1->offset > r2->offset)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FuEfiImageRegion *
|
||||||
|
fu_efi_image_add_region (GPtrArray *checksum_regions,
|
||||||
|
const gchar *name,
|
||||||
|
gsize offset_start,
|
||||||
|
gsize offset_end)
|
||||||
|
{
|
||||||
|
FuEfiImageRegion *r = g_new0 (FuEfiImageRegion, 1);
|
||||||
|
r->name = g_strdup (name);
|
||||||
|
r->offset = offset_start;
|
||||||
|
r->size = offset_end - offset_start;
|
||||||
|
g_ptr_array_add (checksum_regions, r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_efi_image_region_free (FuEfiImageRegion *r)
|
||||||
|
{
|
||||||
|
g_free (r->name);
|
||||||
|
g_free (r);
|
||||||
|
}
|
||||||
|
|
||||||
|
FuEfiImage *
|
||||||
|
fu_efi_image_new (GBytes *data, GError **error)
|
||||||
|
{
|
||||||
|
FuEfiImageRegion *r;
|
||||||
|
const guint8 *buf;
|
||||||
|
gsize bufsz;
|
||||||
|
gsize image_bytes = 0;
|
||||||
|
gsize checksum_offset;
|
||||||
|
gsize data_dir_debug_offset;
|
||||||
|
gsize offset_tmp;
|
||||||
|
guint16 dos_sig = 0;
|
||||||
|
guint16 machine = 0;
|
||||||
|
guint16 opthdrsz;
|
||||||
|
guint16 sections;
|
||||||
|
guint32 baseaddr = 0;
|
||||||
|
guint32 cert_table_size;
|
||||||
|
guint32 header_size;
|
||||||
|
guint32 nt_sig = 0;
|
||||||
|
g_autoptr(FuEfiImage) self = g_object_new (FU_TYPE_EFI_IMAGE, NULL);
|
||||||
|
g_autoptr(GChecksum) checksum = g_checksum_new (G_CHECKSUM_SHA256);
|
||||||
|
g_autoptr(GPtrArray) checksum_regions = NULL;
|
||||||
|
|
||||||
|
/* verify this is a DOS file */
|
||||||
|
buf = g_bytes_get_data (data, &bufsz);
|
||||||
|
if (!fu_common_read_uint16_safe (buf, bufsz,
|
||||||
|
_DOS_OFFSET_SIGNATURE,
|
||||||
|
&dos_sig, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
if (dos_sig != 0x5a4d) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Invalid DOS header magic %04x", dos_sig);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify the PE signature */
|
||||||
|
if (!fu_common_read_uint32_safe (buf, bufsz,
|
||||||
|
_DOS_OFFSET_TO_PE_HEADER,
|
||||||
|
&baseaddr, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_common_read_uint32_safe (buf, bufsz,
|
||||||
|
baseaddr + _PEI_OFFSET_SIGNATURE,
|
||||||
|
&nt_sig, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
if (nt_sig != 0x4550) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Invalid PE header signature %08x", nt_sig);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* which machine type are we reading */
|
||||||
|
if (!fu_common_read_uint16_safe (buf, bufsz,
|
||||||
|
baseaddr + _PEI_OFFSET_MACHINE,
|
||||||
|
&machine, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
if (machine == IMAGE_FILE_MACHINE_AMD64 ||
|
||||||
|
machine == IMAGE_FILE_MACHINE_AARCH64) {
|
||||||
|
|
||||||
|
/* a.out header directly follows PE header */
|
||||||
|
if (!fu_common_read_uint16_safe (buf, bufsz,
|
||||||
|
baseaddr + _PEI_HEADER_SIZE,
|
||||||
|
&machine, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
if (machine != 0x020b) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Invalid a.out machine type %04x", machine);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!fu_common_read_uint32_safe (buf, bufsz,
|
||||||
|
baseaddr + _PEP_OFFSET_SIZE_OF_HEADERS,
|
||||||
|
&header_size, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
checksum_offset = baseaddr + _PEP_OFFSET_CHECKSUM;
|
||||||
|
|
||||||
|
/* now, this is odd. sbsigntools seems to think that we're
|
||||||
|
* skipping the CertificateTable -- but we actually seems to be
|
||||||
|
* ignoring Debug instead */
|
||||||
|
data_dir_debug_offset = baseaddr + _PEP_OFFSET_DEBUG_TABLE_OFFSET;
|
||||||
|
|
||||||
|
} else if (machine == IMAGE_FILE_MACHINE_I386 ||
|
||||||
|
machine == IMAGE_FILE_MACHINE_THUMB) {
|
||||||
|
|
||||||
|
/* a.out header directly follows PE header */
|
||||||
|
if (!fu_common_read_uint16_safe (buf, bufsz,
|
||||||
|
baseaddr + _PEI_HEADER_SIZE,
|
||||||
|
&machine, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
if (machine != 0x010b) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Invalid a.out machine type %04x", machine);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!fu_common_read_uint32_safe (buf, bufsz,
|
||||||
|
baseaddr + _PE_OFFSET_SIZE_OF_HEADERS,
|
||||||
|
&header_size, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
checksum_offset = baseaddr + _PE_OFFSET_CHECKSUM;
|
||||||
|
data_dir_debug_offset = baseaddr + _PE_OFFSET_DEBUG_TABLE_OFFSET;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Invalid PE header machine %04x", machine);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get sections */
|
||||||
|
if (!fu_common_read_uint32_safe (buf, bufsz,
|
||||||
|
data_dir_debug_offset + sizeof(guint32),
|
||||||
|
&cert_table_size, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_common_read_uint16_safe (buf, bufsz,
|
||||||
|
baseaddr + _PEI_OFFSET_NUMBER_OF_SECTIONS,
|
||||||
|
§ions, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
g_debug ("number_of_sections: %u", sections);
|
||||||
|
|
||||||
|
/* get header size */
|
||||||
|
if (!fu_common_read_uint16_safe (buf, bufsz,
|
||||||
|
baseaddr + _PEI_OFFSET_OPTIONAL_HEADER_SIZE,
|
||||||
|
&opthdrsz, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
g_debug ("optional_header_size: 0x%x", opthdrsz);
|
||||||
|
|
||||||
|
/* first region: beginning to checksum_offset field */
|
||||||
|
checksum_regions = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_efi_image_region_free);
|
||||||
|
r = fu_efi_image_add_region (checksum_regions, "begin->cksum", 0x0, checksum_offset);
|
||||||
|
image_bytes += r->size + sizeof(guint32);
|
||||||
|
|
||||||
|
/* second region: end of checksum_offset to certificate table entry */
|
||||||
|
r = fu_efi_image_add_region (checksum_regions, "cksum->datadir[DEBUG]",
|
||||||
|
checksum_offset + sizeof(guint32),
|
||||||
|
data_dir_debug_offset);
|
||||||
|
image_bytes += r->size + sizeof(FuEfiImageDataDirEntry);
|
||||||
|
|
||||||
|
/* third region: end of checksum_offset to end of headers */
|
||||||
|
r = fu_efi_image_add_region (checksum_regions, "datadir[DEBUG]->headers",
|
||||||
|
data_dir_debug_offset + sizeof(FuEfiImageDataDirEntry),
|
||||||
|
header_size);
|
||||||
|
image_bytes += r->size;
|
||||||
|
|
||||||
|
/* add COFF sections */
|
||||||
|
offset_tmp = baseaddr + _PEI_HEADER_SIZE + opthdrsz;
|
||||||
|
for (guint i = 0; i < sections; i++) {
|
||||||
|
guint32 file_offset = 0;
|
||||||
|
guint32 file_size = 0;
|
||||||
|
gchar name[9] = { '\0' };
|
||||||
|
|
||||||
|
if (!fu_common_read_uint32_safe (buf, bufsz,
|
||||||
|
offset_tmp + _SECTION_HEADER_OFFSET_PTR,
|
||||||
|
&file_offset, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_common_read_uint32_safe (buf, bufsz,
|
||||||
|
offset_tmp + _SECTION_HEADER_OFFSET_SIZE,
|
||||||
|
&file_size, G_LITTLE_ENDIAN, error))
|
||||||
|
return NULL;
|
||||||
|
if (file_size == 0)
|
||||||
|
continue;
|
||||||
|
if (!fu_memcpy_safe ((guint8 *) name, sizeof(name), 0x0, /* dst */
|
||||||
|
buf, bufsz,
|
||||||
|
offset_tmp + _SECTION_HEADER_OFFSET_NAME, /* src */
|
||||||
|
sizeof(name) - 1, error))
|
||||||
|
return NULL;
|
||||||
|
r = fu_efi_image_add_region (checksum_regions, name, file_offset, file_offset + file_size);
|
||||||
|
image_bytes += r->size;
|
||||||
|
|
||||||
|
if (file_offset + r->size > bufsz) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"file-aligned section %s extends beyond end of file",
|
||||||
|
r->name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
offset_tmp += _SECTION_HEADER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure in order */
|
||||||
|
g_ptr_array_sort (checksum_regions, fu_efi_image_region_sort_cb);
|
||||||
|
|
||||||
|
/* for the data at the end of the image */
|
||||||
|
if (image_bytes + cert_table_size < bufsz) {
|
||||||
|
fu_efi_image_add_region (checksum_regions, "endjunk",
|
||||||
|
image_bytes, bufsz - cert_table_size);
|
||||||
|
} else if (image_bytes + cert_table_size > bufsz) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"checksum_offset areas outside image size");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate the checksum we would find in the dbx */
|
||||||
|
for (guint i = 0; i < checksum_regions->len; i++) {
|
||||||
|
r = g_ptr_array_index (checksum_regions, i);
|
||||||
|
g_debug ("region %s: 0x%04x -> 0x%04x [0x%04x]",
|
||||||
|
r->name,
|
||||||
|
(guint) r->offset,
|
||||||
|
(guint) (r->offset + r->size - 1),
|
||||||
|
(guint) r->size);
|
||||||
|
g_checksum_update (checksum,
|
||||||
|
(const guchar *) buf + r->offset,
|
||||||
|
(gssize) r->size);
|
||||||
|
}
|
||||||
|
self->checksum = g_strdup (g_checksum_get_string (checksum));
|
||||||
|
return g_steal_pointer (&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
fu_efi_image_get_checksum (FuEfiImage *self)
|
||||||
|
{
|
||||||
|
return self->checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_efi_image_finalize (GObject *obj)
|
||||||
|
{
|
||||||
|
FuEfiImage *self = FU_EFI_IMAGE (obj);
|
||||||
|
g_free (self->checksum);
|
||||||
|
G_OBJECT_CLASS (fu_efi_image_parent_class)->finalize (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_efi_image_class_init (FuEfiImageClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
object_class->finalize = fu_efi_image_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_efi_image_init (FuEfiImage *self)
|
||||||
|
{
|
||||||
|
}
|
16
plugins/uefi-dbx/fu-efi-image.h
Normal file
16
plugins/uefi-dbx/fu-efi-image.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#define FU_TYPE_EFI_IMAGE (fu_efi_image_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (FuEfiImage, fu_efi_image, FU, EFI_IMAGE, GObject)
|
||||||
|
|
||||||
|
FuEfiImage *fu_efi_image_new (GBytes *data,
|
||||||
|
GError **error);
|
||||||
|
const gchar *fu_efi_image_get_checksum (FuEfiImage *self);
|
@ -9,7 +9,7 @@
|
|||||||
#include "fu-efi-signature-common.h"
|
#include "fu-efi-signature-common.h"
|
||||||
#include "fu-efi-signature-list.h"
|
#include "fu-efi-signature-list.h"
|
||||||
|
|
||||||
static gboolean
|
gboolean
|
||||||
fu_efi_signature_list_array_has_checksum (GPtrArray *siglists, const gchar *checksum)
|
fu_efi_signature_list_array_has_checksum (GPtrArray *siglists, const gchar *checksum)
|
||||||
{
|
{
|
||||||
for (guint i = 0; i < siglists->len; i++) {
|
for (guint i = 0; i < siglists->len; i++) {
|
||||||
|
@ -11,3 +11,5 @@
|
|||||||
gboolean fu_efi_signature_list_array_inclusive (GPtrArray *outer,
|
gboolean fu_efi_signature_list_array_inclusive (GPtrArray *outer,
|
||||||
GPtrArray *inner);
|
GPtrArray *inner);
|
||||||
guint fu_efi_signature_list_array_version (GPtrArray *siglists);
|
guint fu_efi_signature_list_array_version (GPtrArray *siglists);
|
||||||
|
gboolean fu_efi_signature_list_array_has_checksum (GPtrArray *siglists,
|
||||||
|
const gchar *checksum);
|
||||||
|
@ -8,9 +8,41 @@
|
|||||||
|
|
||||||
#include <fwupd.h>
|
#include <fwupd.h>
|
||||||
|
|
||||||
|
#include "fu-common.h"
|
||||||
#include "fu-uefi-dbx-common.h"
|
#include "fu-uefi-dbx-common.h"
|
||||||
|
#include "fu-efi-image.h"
|
||||||
#include "fu-efi-signature-parser.h"
|
#include "fu-efi-signature-parser.h"
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
fu_test_get_filename (const gchar *filename)
|
||||||
|
{
|
||||||
|
g_autofree gchar *path = NULL;
|
||||||
|
path = g_build_filename (TESTDATADIR, filename, NULL);
|
||||||
|
return fu_common_realpath (path, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_efi_image_func (void)
|
||||||
|
{
|
||||||
|
const gchar *csum = NULL;
|
||||||
|
g_autofree gchar *fn = NULL;
|
||||||
|
g_autoptr(FuEfiImage) img = NULL;
|
||||||
|
g_autoptr(GBytes) bytes = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
|
fn = fu_test_get_filename ("fwupdx64.efi");
|
||||||
|
g_assert_nonnull (fn);
|
||||||
|
bytes = fu_common_get_contents_bytes (fn, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (bytes);
|
||||||
|
|
||||||
|
img = fu_efi_image_new (bytes, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (img);
|
||||||
|
csum = fu_efi_image_get_checksum (img);
|
||||||
|
g_assert_cmpstr (csum, ==, "e99707d4378140c01eb3f867240d5cc9e237b126d3db0c3b4bbcd3da1720ddff");
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fu_efi_signature_list_parse_func (void)
|
fu_efi_signature_list_parse_func (void)
|
||||||
{
|
{
|
||||||
@ -55,6 +87,7 @@ main (int argc, char **argv)
|
|||||||
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
|
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
|
||||||
|
|
||||||
/* tests go here */
|
/* tests go here */
|
||||||
|
g_test_add_func ("/uefi-dbx/image", fu_efi_image_func);
|
||||||
g_test_add_func ("/uefi-dbx/file-parse", fu_efi_signature_list_parse_func);
|
g_test_add_func ("/uefi-dbx/file-parse", fu_efi_signature_list_parse_func);
|
||||||
return g_test_run ();
|
return g_test_run ();
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,10 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "fu-common.h"
|
#include "fu-common.h"
|
||||||
|
#include "fu-efi-image.h"
|
||||||
|
#include "fu-efi-signature-common.h"
|
||||||
|
#include "fu-volume.h"
|
||||||
|
|
||||||
#include "fu-uefi-dbx-common.h"
|
#include "fu-uefi-dbx-common.h"
|
||||||
|
|
||||||
gchar *
|
gchar *
|
||||||
@ -26,3 +30,85 @@ fu_uefi_dbx_get_dbxupdate (GError **error)
|
|||||||
return NULL;
|
return NULL;
|
||||||
return g_strdup (g_ptr_array_index (files, 0));
|
return g_strdup (g_ptr_array_index (files, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gchar *
|
||||||
|
fu_uefi_dbx_get_authenticode_hash (const gchar *fn, GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(FuEfiImage) img = NULL;
|
||||||
|
g_autoptr(GBytes) bytes = NULL;
|
||||||
|
g_autoptr(GMappedFile) mmap = NULL;
|
||||||
|
|
||||||
|
g_debug ("getting Authenticode hash of %s", fn);
|
||||||
|
mmap = g_mapped_file_new (fn, FALSE, error);
|
||||||
|
if (mmap == NULL)
|
||||||
|
return NULL;
|
||||||
|
bytes = g_mapped_file_get_bytes (mmap);
|
||||||
|
|
||||||
|
img = fu_efi_image_new (bytes, error);
|
||||||
|
if (img == NULL)
|
||||||
|
return NULL;
|
||||||
|
g_debug ("SHA256 was %s", fu_efi_image_get_checksum (img));
|
||||||
|
return g_strdup (fu_efi_image_get_checksum (img));
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_uefi_dbx_signature_list_validate_volume (GPtrArray *siglists, FuVolume *esp, GError **error)
|
||||||
|
{
|
||||||
|
g_autofree gchar *esp_path = NULL;
|
||||||
|
g_autoptr(GPtrArray) files = NULL;
|
||||||
|
|
||||||
|
/* get list of files contained in the ESP */
|
||||||
|
esp_path = fu_volume_get_mount_point (esp);
|
||||||
|
if (esp_path == NULL)
|
||||||
|
return TRUE;
|
||||||
|
files = fu_common_get_files_recursive (esp_path, error);
|
||||||
|
if (files == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* verify each file does not exist in the ESP */
|
||||||
|
for (guint i = 0; i < files->len; i++) {
|
||||||
|
const gchar *fn = g_ptr_array_index (files, i);
|
||||||
|
g_autofree gchar *checksum = NULL;
|
||||||
|
g_autoptr(GError) error_local = NULL;
|
||||||
|
|
||||||
|
/* get checksum of file */
|
||||||
|
checksum = fu_uefi_dbx_get_authenticode_hash (fn, &error_local);
|
||||||
|
if (checksum == NULL) {
|
||||||
|
g_debug ("failed to get checksum for %s: %s", fn, error_local->message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Authenticode signature is present in dbx! */
|
||||||
|
g_debug ("fn=%s, checksum=%s", fn, checksum);
|
||||||
|
if (fu_efi_signature_list_array_has_checksum (siglists, checksum)) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"%s Authenticode checksum [%s] is present in dbx",
|
||||||
|
fn, checksum);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fu_uefi_dbx_signature_list_validate (GPtrArray *siglists, GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GPtrArray) volumes = NULL;
|
||||||
|
volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
|
||||||
|
if (volumes == NULL)
|
||||||
|
return FALSE;
|
||||||
|
for (guint i = 0; i < volumes->len; i++) {
|
||||||
|
FuVolume *esp = g_ptr_array_index (volumes, i);
|
||||||
|
g_autoptr(FuDeviceLocker) locker = NULL;
|
||||||
|
locker = fu_volume_locker (esp, error);
|
||||||
|
if (locker == NULL)
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_uefi_dbx_signature_list_validate_volume (siglists, esp, error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
@ -9,3 +9,7 @@
|
|||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
|
||||||
gchar *fu_uefi_dbx_get_dbxupdate (GError **error);
|
gchar *fu_uefi_dbx_get_dbxupdate (GError **error);
|
||||||
|
gchar *fu_uefi_dbx_get_authenticode_hash (const gchar *fn,
|
||||||
|
GError **error);
|
||||||
|
gboolean fu_uefi_dbx_signature_list_validate (GPtrArray *siglists,
|
||||||
|
GError **error);
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "fu-efi-signature-common.h"
|
#include "fu-efi-signature-common.h"
|
||||||
#include "fu-efi-signature-parser.h"
|
#include "fu-efi-signature-parser.h"
|
||||||
|
#include "fu-uefi-dbx-common.h"
|
||||||
#include "fu-uefi-dbx-device.h"
|
#include "fu-uefi-dbx-device.h"
|
||||||
|
|
||||||
struct _FuUefiDbxDevice {
|
struct _FuUefiDbxDevice {
|
||||||
@ -75,6 +76,40 @@ fu_uefi_dbx_device_set_version_number (FuDevice *device, GError **error)
|
|||||||
return TRUE;
|
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 */
|
||||||
|
fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING);
|
||||||
|
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
|
static gboolean
|
||||||
fu_uefi_dbx_device_probe (FuDevice *device, GError **error)
|
fu_uefi_dbx_device_probe (FuDevice *device, GError **error)
|
||||||
{
|
{
|
||||||
@ -137,6 +172,7 @@ fu_uefi_dbx_device_class_init (FuUefiDbxDeviceClass *klass)
|
|||||||
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
|
||||||
klass_device->probe = fu_uefi_dbx_device_probe;
|
klass_device->probe = fu_uefi_dbx_device_probe;
|
||||||
klass_device->write_firmware = fu_uefi_dbx_device_write_firmware;
|
klass_device->write_firmware = fu_uefi_dbx_device_write_firmware;
|
||||||
|
klass_device->prepare_firmware = fu_uefi_dbx_prepare_firmware;
|
||||||
}
|
}
|
||||||
|
|
||||||
FuUefiDbxDevice *
|
FuUefiDbxDevice *
|
||||||
|
@ -6,6 +6,7 @@ shared_module('fu_plugin_uefi_dbx',
|
|||||||
'fu-plugin-uefi-dbx.c',
|
'fu-plugin-uefi-dbx.c',
|
||||||
'fu-uefi-dbx-common.c',
|
'fu-uefi-dbx-common.c',
|
||||||
'fu-uefi-dbx-device.c',
|
'fu-uefi-dbx-device.c',
|
||||||
|
'fu-efi-image.c',
|
||||||
'fu-efi-signature.c',
|
'fu-efi-signature.c',
|
||||||
'fu-efi-signature-common.c',
|
'fu-efi-signature-common.c',
|
||||||
'fu-efi-signature-list.c',
|
'fu-efi-signature-list.c',
|
||||||
@ -29,12 +30,15 @@ shared_module('fu_plugin_uefi_dbx',
|
|||||||
)
|
)
|
||||||
|
|
||||||
if get_option('tests')
|
if get_option('tests')
|
||||||
|
testdatadir = join_paths(meson.current_source_dir(), 'tests')
|
||||||
|
cargs += '-DTESTDATADIR="' + testdatadir + '"'
|
||||||
e = executable(
|
e = executable(
|
||||||
'uefi-dbx-self-test',
|
'uefi-dbx-self-test',
|
||||||
fu_hash,
|
fu_hash,
|
||||||
sources : [
|
sources : [
|
||||||
'fu-self-test.c',
|
'fu-self-test.c',
|
||||||
'fu-uefi-dbx-common.c',
|
'fu-uefi-dbx-common.c',
|
||||||
|
'fu-efi-image.c',
|
||||||
'fu-efi-signature.c',
|
'fu-efi-signature.c',
|
||||||
'fu-efi-signature-common.c',
|
'fu-efi-signature-common.c',
|
||||||
'fu-efi-signature-list.c',
|
'fu-efi-signature-list.c',
|
||||||
@ -52,6 +56,7 @@ if get_option('tests')
|
|||||||
fwupd,
|
fwupd,
|
||||||
fwupdplugin,
|
fwupdplugin,
|
||||||
],
|
],
|
||||||
|
c_args : cargs,
|
||||||
)
|
)
|
||||||
test('uefi-dbx-self-test', e)
|
test('uefi-dbx-self-test', e)
|
||||||
endif
|
endif
|
||||||
@ -60,10 +65,12 @@ uefi_dbx_fuzzer = executable(
|
|||||||
'uefi-dbx-fuzzer',
|
'uefi-dbx-fuzzer',
|
||||||
sources : [
|
sources : [
|
||||||
'fu-fuzzer.c',
|
'fu-fuzzer.c',
|
||||||
|
'fu-efi-image.c',
|
||||||
'fu-efi-signature.c',
|
'fu-efi-signature.c',
|
||||||
'fu-efi-signature-common.c',
|
'fu-efi-signature-common.c',
|
||||||
'fu-efi-signature-list.c',
|
'fu-efi-signature-list.c',
|
||||||
'fu-efi-signature-parser.c',
|
'fu-efi-signature-parser.c',
|
||||||
|
'fu-uefi-dbx-common.c',
|
||||||
],
|
],
|
||||||
include_directories : [
|
include_directories : [
|
||||||
root_incdir,
|
root_incdir,
|
||||||
@ -84,6 +91,7 @@ dbxtool = executable(
|
|||||||
sources : [
|
sources : [
|
||||||
'fu-dbxtool.c',
|
'fu-dbxtool.c',
|
||||||
'fu-uefi-dbx-common.c',
|
'fu-uefi-dbx-common.c',
|
||||||
|
'fu-efi-image.c',
|
||||||
'fu-efi-signature.c',
|
'fu-efi-signature.c',
|
||||||
'fu-efi-signature-common.c',
|
'fu-efi-signature-common.c',
|
||||||
'fu-efi-signature-list.c',
|
'fu-efi-signature-list.c',
|
||||||
|
BIN
plugins/uefi-dbx/tests/fwupdx64.efi
Executable file
BIN
plugins/uefi-dbx/tests/fwupdx64.efi
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user