]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stub: Allow loading unsigned kernel images
authorJan Janssen <medhefgo@web.de>
Wed, 21 Sep 2022 10:56:20 +0000 (12:56 +0200)
committerJan Janssen <medhefgo@web.de>
Mon, 17 Oct 2022 12:48:28 +0000 (14:48 +0200)
src/boot/efi/linux.c
src/boot/efi/secure-boot.c
src/boot/efi/secure-boot.h

index 622869e36c0694a6960436acfcd11e1d7472552a..813e648b6bb41d626b4f3c324d641634423fe40c 100644 (file)
 #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(
index cf7a464d0a4826340cee7db409e4aa3909841a81..6a5c2a9bea02799a7d8ca32b6d4716549eaf4ed7 100644 (file)
@@ -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);
+}
index ff434ed1adbca1606d03b61340bd790b21c4bc99..91b6770edb599f0c83d7bea55b9e987ce66afffd 100644 (file)
@@ -2,7 +2,9 @@
 #pragma once
 
 #include <efi.h>
+
 #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);