From 6731a102da4b5827ae10355670c34396e89e265b Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Wed, 21 Sep 2022 12:56:20 +0200 Subject: stub: Allow loading unsigned kernel images --- src/boot/efi/linux.c | 57 +++++++++++++++++++++++++++++++++++++-- src/boot/efi/secure-boot.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++ src/boot/efi/secure-boot.h | 23 ++++++++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) (limited to 'src/boot') diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index 622869e36c..813e648b6b 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -14,17 +14,50 @@ #include "initrd.h" #include "linux.h" #include "pe.h" +#include "secure-boot.h" #include "util.h" #define STUB_PAYLOAD_GUID \ { 0x55c5d1f8, 0x04cd, 0x46b5, { 0x8a, 0x20, 0xe5, 0x6c, 0xbb, 0x30, 0x52, 0xd0 } } +static EFIAPI EFI_STATUS security_hook( + const SecurityOverride *this, uint32_t authentication_status, const EFI_DEVICE_PATH *file) { + + assert(this); + assert(this->hook == security_hook); + + if (file == this->payload_device_path) + return EFI_SUCCESS; + + return this->original_security->FileAuthenticationState( + this->original_security, authentication_status, file); +} + +static EFIAPI EFI_STATUS security2_hook( + const SecurityOverride *this, + const EFI_DEVICE_PATH *device_path, + void *file_buffer, + size_t file_size, + BOOLEAN boot_policy) { + + assert(this); + assert(this->hook == security2_hook); + + if (file_buffer == this->payload && file_size == this->payload_len && + device_path == this->payload_device_path) + return EFI_SUCCESS; + + return this->original_security2->FileAuthentication( + this->original_security2, device_path, file_buffer, file_size, boot_policy); +} + EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len, EFI_HANDLE *ret_image) { assert(parent); assert(source); assert(ret_image); - /* We could pass a NULL device path, but it's nicer to provide something. */ + /* We could pass a NULL device path, but it's nicer to provide something and it allows us to identify + * the loaded image from within the security hooks. */ struct { VENDOR_DEVICE_PATH payload; EFI_DEVICE_PATH end; @@ -44,13 +77,33 @@ EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len, EFI_HAN }, }; - return BS->LoadImage( + /* We want to support unsigned kernel images as payload, which is safe to do under secure boot + * because it is embedded in this stub loader (and since it is already running it must be trusted). */ + SecurityOverride security_override = { + .hook = security_hook, + .payload = source, + .payload_len = len, + .payload_device_path = &payload_device_path.payload.Header, + }, security2_override = { + .hook = security2_hook, + .payload = source, + .payload_len = len, + .payload_device_path = &payload_device_path.payload.Header, + }; + + install_security_override(&security_override, &security2_override); + + EFI_STATUS ret = BS->LoadImage( /*BootPolicy=*/false, parent, &payload_device_path.payload.Header, (void *) source, len, ret_image); + + uninstall_security_override(&security_override, &security2_override); + + return ret; } EFI_STATUS linux_exec( diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c index cf7a464d0a..6a5c2a9bea 100644 --- a/src/boot/efi/secure-boot.c +++ b/src/boot/efi/secure-boot.c @@ -126,3 +126,70 @@ out_deallocate: return err; } + +static EFI_STATUS install_security_override_one(EFI_GUID guid, SecurityOverride *override) { + EFI_STATUS err; + + assert(override); + + _cleanup_free_ EFI_HANDLE *handles = NULL; + size_t n_handles = 0; + + err = BS->LocateHandleBuffer(ByProtocol, &guid, NULL, &n_handles, &handles); + if (err != EFI_SUCCESS) + /* No security arch protocol around? */ + return err; + + /* There should only ever be one security arch protocol instance, but let's be paranoid here. */ + assert(n_handles == 1); + + void *security = NULL; + err = BS->LocateProtocol(&guid, NULL, &security); + if (err != EFI_SUCCESS) + return log_error_status_stall(err, u"Error getting security arch protocol: %r", err); + + err = BS->ReinstallProtocolInterface(handles[0], &guid, security, override); + if (err != EFI_SUCCESS) + return log_error_status_stall(err, u"Error overriding security arch protocol: %r", err); + + override->original = security; + override->original_handle = handles[0]; + return EFI_SUCCESS; +} + +/* This replaces the platform provided security arch protocols (defined in the UEFI Platform Initialization + * Specification) with the provided override instances. If not running in secure boot or the protocols are + * not available nothing happens. The override instances are provided with the neccessary info to undo this + * in uninstall_security_override(). */ +void install_security_override(SecurityOverride *override, SecurityOverride *override2) { + assert(override); + assert(override2); + + if (!secure_boot_enabled()) + return; + + (void) install_security_override_one((EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, override); + (void) install_security_override_one((EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, override2); +} + +void uninstall_security_override(SecurityOverride *override, SecurityOverride *override2) { + assert(override); + assert(override2); + + /* We use assert_se here to guarantee the system is not in a weird state in the unlikely case of an + * error restoring the original protocols. */ + + if (override->original_handle) + assert_se(BS->ReinstallProtocolInterface( + override->original_handle, + &(EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, + override, + override->original) == EFI_SUCCESS); + + if (override2->original_handle) + assert_se(BS->ReinstallProtocolInterface( + override2->original_handle, + &(EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, + override2, + override2->original) == EFI_SUCCESS); +} diff --git a/src/boot/efi/secure-boot.h b/src/boot/efi/secure-boot.h index ff434ed1ad..91b6770edb 100644 --- a/src/boot/efi/secure-boot.h +++ b/src/boot/efi/secure-boot.h @@ -2,7 +2,9 @@ #pragma once #include + #include "efivars-fundamental.h" +#include "missing_efi.h" typedef enum { ENROLL_OFF, /* no Secure Boot key enrollment whatsoever, even manual entries are not generated */ @@ -14,3 +16,24 @@ bool secure_boot_enabled(void); SecureBootMode secure_boot_mode(void); EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path); + +typedef struct { + void *hook; + + /* End of EFI_SECURITY_ARCH(2)_PROTOCOL. The rest is our own protocol instance data. */ + + EFI_HANDLE original_handle; + union { + void *original; + EFI_SECURITY_ARCH_PROTOCOL *original_security; + EFI_SECURITY2_ARCH_PROTOCOL *original_security2; + }; + + /* Used by the stub to identify the embedded image. */ + const void *payload; + size_t payload_len; + const EFI_DEVICE_PATH *payload_device_path; +} SecurityOverride; + +void install_security_override(SecurityOverride *override, SecurityOverride *override2); +void uninstall_security_override(SecurityOverride *override, SecurityOverride *override2); -- cgit v1.2.1