ac3a84
From 519625977d19b7842d9b2ded8be12ed0aecbaefc Mon Sep 17 00:00:00 2001
ac3a84
From: Jan Janssen <medhefgo@web.de>
ac3a84
Date: Tue, 15 Nov 2022 18:22:38 +0100
ac3a84
Subject: [PATCH] boot: Rework security arch override
ac3a84
ac3a84
This simplifies the caller interface for security arch overrides by only
ac3a84
having to pass a validator and an optional context.
ac3a84
ac3a84
(cherry picked from commit 5489c13bae119dc5f6e65be8d7f241aa7d54c023)
ac3a84
ac3a84
Related: #2138081
ac3a84
---
ac3a84
 src/boot/efi/linux.c       |  61 ++++++++-------------
ac3a84
 src/boot/efi/secure-boot.c | 105 +++++++++++++++++++++++++++++--------
ac3a84
 src/boot/efi/secure-boot.h |  28 +++-------
ac3a84
 src/boot/efi/shim.c        | 104 +++++++++++-------------------------
ac3a84
 4 files changed, 146 insertions(+), 152 deletions(-)
ac3a84
ac3a84
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
ac3a84
index 75b9507709..dd7eb48c8c 100644
ac3a84
--- a/src/boot/efi/linux.c
ac3a84
+++ b/src/boot/efi/linux.c
ac3a84
@@ -20,35 +20,26 @@
ac3a84
 #define STUB_PAYLOAD_GUID \
ac3a84
         { 0x55c5d1f8, 0x04cd, 0x46b5, { 0x8a, 0x20, 0xe5, 0x6c, 0xbb, 0x30, 0x52, 0xd0 } }
ac3a84
 
ac3a84
-static EFIAPI EFI_STATUS security_hook(
ac3a84
-                const SecurityOverride *this, uint32_t authentication_status, const EFI_DEVICE_PATH *file) {
ac3a84
+typedef struct {
ac3a84
+        const void *addr;
ac3a84
+        size_t len;
ac3a84
+        const EFI_DEVICE_PATH *device_path;
ac3a84
+} ValidationContext;
ac3a84
 
ac3a84
-        assert(this);
ac3a84
-        assert(this->hook == security_hook);
ac3a84
+static bool validate_payload(
ac3a84
+                const void *ctx, const EFI_DEVICE_PATH *device_path, const void *file_buffer, size_t file_size) {
ac3a84
 
ac3a84
-        if (file == this->payload_device_path)
ac3a84
-                return EFI_SUCCESS;
ac3a84
+        const ValidationContext *payload = ASSERT_PTR(ctx);
ac3a84
 
ac3a84
-        return this->original_security->FileAuthenticationState(
ac3a84
-                        this->original_security, authentication_status, file);
ac3a84
-}
ac3a84
-
ac3a84
-static EFIAPI EFI_STATUS security2_hook(
ac3a84
-                const SecurityOverride *this,
ac3a84
-                const EFI_DEVICE_PATH *device_path,
ac3a84
-                void *file_buffer,
ac3a84
-                size_t file_size,
ac3a84
-                BOOLEAN boot_policy) {
ac3a84
-
ac3a84
-        assert(this);
ac3a84
-        assert(this->hook == security2_hook);
ac3a84
+        if (device_path != payload->device_path)
ac3a84
+                return false;
ac3a84
 
ac3a84
-        if (file_buffer == this->payload && file_size == this->payload_len &&
ac3a84
-            device_path == this->payload_device_path)
ac3a84
-                return EFI_SUCCESS;
ac3a84
+        /* Security arch (1) protocol does not provide a file buffer. Instead we are supposed to fetch the payload
ac3a84
+         * ourselves, which is not needed as we already have everything in memory and the device paths match. */
ac3a84
+        if (file_buffer && (file_buffer != payload->addr || file_size != payload->len))
ac3a84
+                return false;
ac3a84
 
ac3a84
-        return this->original_security2->FileAuthentication(
ac3a84
-                        this->original_security2, device_path, file_buffer, file_size, boot_policy);
ac3a84
+        return true;
ac3a84
 }
ac3a84
 
ac3a84
 static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len, EFI_HANDLE *ret_image) {
ac3a84
@@ -79,19 +70,13 @@ static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len,
ac3a84
 
ac3a84
         /* We want to support unsigned kernel images as payload, which is safe to do under secure boot
ac3a84
          * because it is embedded in this stub loader (and since it is already running it must be trusted). */
ac3a84
-        SecurityOverride security_override = {
ac3a84
-                .hook = security_hook,
ac3a84
-                .payload = source,
ac3a84
-                .payload_len = len,
ac3a84
-                .payload_device_path = &payload_device_path.payload.Header,
ac3a84
-        }, security2_override = {
ac3a84
-                .hook = security2_hook,
ac3a84
-                .payload = source,
ac3a84
-                .payload_len = len,
ac3a84
-                .payload_device_path = &payload_device_path.payload.Header,
ac3a84
-        };
ac3a84
-
ac3a84
-        install_security_override(&security_override, &security2_override);
ac3a84
+        install_security_override(
ac3a84
+                        validate_payload,
ac3a84
+                        &(ValidationContext) {
ac3a84
+                                .addr = source,
ac3a84
+                                .len = len,
ac3a84
+                                .device_path = &payload_device_path.payload.Header,
ac3a84
+                        });
ac3a84
 
ac3a84
         EFI_STATUS ret = BS->LoadImage(
ac3a84
                         /*BootPolicy=*/false,
ac3a84
@@ -101,7 +86,7 @@ static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len,
ac3a84
                         len,
ac3a84
                         ret_image);
ac3a84
 
ac3a84
-        uninstall_security_override(&security_override, &security2_override);
ac3a84
+        uninstall_security_override();
ac3a84
 
ac3a84
         return ret;
ac3a84
 }
ac3a84
diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c
ac3a84
index 171b2c96b3..0e615c55e0 100644
ac3a84
--- a/src/boot/efi/secure-boot.c
ac3a84
+++ b/src/boot/efi/secure-boot.c
ac3a84
@@ -127,10 +127,60 @@ out_deallocate:
ac3a84
         return err;
ac3a84
 }
ac3a84
 
ac3a84
-static EFI_STATUS install_security_override_one(EFI_GUID guid, SecurityOverride *override) {
ac3a84
+static struct SecurityOverride {
ac3a84
+        /* Our own security arch instances that we register onto original_handle, thereby replacing the
ac3a84
+         * firmware provided instances. */
ac3a84
+        EFI_SECURITY_ARCH_PROTOCOL override;
ac3a84
+        EFI_SECURITY2_ARCH_PROTOCOL override2;
ac3a84
+
ac3a84
+        /* These are saved so we can uninstall our own instance later. */
ac3a84
+        EFI_HANDLE original_handle, original_handle2;
ac3a84
+        EFI_SECURITY_ARCH_PROTOCOL *original_security;
ac3a84
+        EFI_SECURITY2_ARCH_PROTOCOL *original_security2;
ac3a84
+
ac3a84
+        security_validator_t validator;
ac3a84
+        const void *validator_ctx;
ac3a84
+} security_override;
ac3a84
+
ac3a84
+static EFIAPI EFI_STATUS security_hook(
ac3a84
+                const EFI_SECURITY_ARCH_PROTOCOL *this,
ac3a84
+                uint32_t authentication_status,
ac3a84
+                const EFI_DEVICE_PATH *file) {
ac3a84
+
ac3a84
+        assert(security_override.validator);
ac3a84
+        assert(security_override.original_security);
ac3a84
+
ac3a84
+        if (security_override.validator(security_override.validator_ctx, file, NULL, 0))
ac3a84
+                return EFI_SUCCESS;
ac3a84
+
ac3a84
+        return security_override.original_security->FileAuthenticationState(
ac3a84
+                        security_override.original_security, authentication_status, file);
ac3a84
+}
ac3a84
+
ac3a84
+static EFIAPI EFI_STATUS security2_hook(
ac3a84
+                const EFI_SECURITY2_ARCH_PROTOCOL *this,
ac3a84
+                const EFI_DEVICE_PATH *device_path,
ac3a84
+                void *file_buffer,
ac3a84
+                size_t file_size,
ac3a84
+                BOOLEAN boot_policy) {
ac3a84
+
ac3a84
+        assert(security_override.validator);
ac3a84
+        assert(security_override.original_security2);
ac3a84
+
ac3a84
+        if (security_override.validator(security_override.validator_ctx, device_path, file_buffer, file_size))
ac3a84
+                return EFI_SUCCESS;
ac3a84
+
ac3a84
+        return security_override.original_security2->FileAuthentication(
ac3a84
+                        security_override.original_security2, device_path, file_buffer, file_size, boot_policy);
ac3a84
+}
ac3a84
+
ac3a84
+static EFI_STATUS install_security_override_one(
ac3a84
+                EFI_GUID guid, void *override, EFI_HANDLE *ret_original_handle, void **ret_original_security) {
ac3a84
         EFI_STATUS err;
ac3a84
 
ac3a84
         assert(override);
ac3a84
+        assert(ret_original_handle);
ac3a84
+        assert(ret_original_security);
ac3a84
 
ac3a84
         _cleanup_free_ EFI_HANDLE *handles = NULL;
ac3a84
         size_t n_handles = 0;
ac3a84
@@ -152,8 +202,8 @@ static EFI_STATUS install_security_override_one(EFI_GUID guid, SecurityOverride
ac3a84
         if (err != EFI_SUCCESS)
ac3a84
                 return log_error_status_stall(err, u"Error overriding security arch protocol: %r", err);
ac3a84
 
ac3a84
-        override->original = security;
ac3a84
-        override->original_handle = handles[0];
ac3a84
+        *ret_original_security = security;
ac3a84
+        *ret_original_handle = handles[0];
ac3a84
         return EFI_SUCCESS;
ac3a84
 }
ac3a84
 
ac3a84
@@ -161,35 +211,46 @@ static EFI_STATUS install_security_override_one(EFI_GUID guid, SecurityOverride
ac3a84
  * Specification) with the provided override instances. If not running in secure boot or the protocols are
ac3a84
  * not available nothing happens. The override instances are provided with the necessary info to undo this
ac3a84
  * in uninstall_security_override(). */
ac3a84
-void install_security_override(SecurityOverride *override, SecurityOverride *override2) {
ac3a84
-        assert(override);
ac3a84
-        assert(override2);
ac3a84
+void install_security_override(security_validator_t validator, const void *validator_ctx) {
ac3a84
+        assert(validator);
ac3a84
 
ac3a84
         if (!secure_boot_enabled())
ac3a84
                 return;
ac3a84
 
ac3a84
-        (void) install_security_override_one((EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, override);
ac3a84
-        (void) install_security_override_one((EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, override2);
ac3a84
-}
ac3a84
+        security_override = (struct SecurityOverride) {
ac3a84
+                { .FileAuthenticationState = security_hook, },
ac3a84
+                { .FileAuthentication = security2_hook, },
ac3a84
+                .validator = validator,
ac3a84
+                .validator_ctx = validator_ctx,
ac3a84
+        };
ac3a84
 
ac3a84
-void uninstall_security_override(SecurityOverride *override, SecurityOverride *override2) {
ac3a84
-        assert(override);
ac3a84
-        assert(override2);
ac3a84
+        (void) install_security_override_one(
ac3a84
+                        (EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID,
ac3a84
+                        &security_override.override,
ac3a84
+                        &security_override.original_handle,
ac3a84
+                        (void **) &security_override.original_security);
ac3a84
+        (void) install_security_override_one(
ac3a84
+                        (EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID,
ac3a84
+                        &security_override.override2,
ac3a84
+                        &security_override.original_handle2,
ac3a84
+                        (void **) &security_override.original_security2);
ac3a84
+}
ac3a84
 
ac3a84
+void uninstall_security_override(void) {
ac3a84
         /* We use assert_se here to guarantee the system is not in a weird state in the unlikely case of an
ac3a84
          * error restoring the original protocols. */
ac3a84
 
ac3a84
-        if (override->original_handle)
ac3a84
+        if (security_override.original_handle)
ac3a84
                 assert_se(BS->ReinstallProtocolInterface(
ac3a84
-                                          override->original_handle,
ac3a84
-                                          &(EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID,
ac3a84
-                                          override,
ac3a84
-                                          override->original) == EFI_SUCCESS);
ac3a84
+                                security_override.original_handle,
ac3a84
+                                &(EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID,
ac3a84
+                                &security_override.override,
ac3a84
+                                security_override.original_security) == EFI_SUCCESS);
ac3a84
 
ac3a84
-        if (override2->original_handle)
ac3a84
+        if (security_override.original_handle2)
ac3a84
                 assert_se(BS->ReinstallProtocolInterface(
ac3a84
-                                          override2->original_handle,
ac3a84
-                                          &(EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID,
ac3a84
-                                          override2,
ac3a84
-                                          override2->original) == EFI_SUCCESS);
ac3a84
+                                security_override.original_handle2,
ac3a84
+                                &(EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID,
ac3a84
+                                &security_override.override2,
ac3a84
+                                security_override.original_security2) == EFI_SUCCESS);
ac3a84
 }
ac3a84
diff --git a/src/boot/efi/secure-boot.h b/src/boot/efi/secure-boot.h
ac3a84
index 91b6770edb..e98de81c2a 100644
ac3a84
--- a/src/boot/efi/secure-boot.h
ac3a84
+++ b/src/boot/efi/secure-boot.h
ac3a84
@@ -17,23 +17,11 @@ SecureBootMode secure_boot_mode(void);
ac3a84
 
ac3a84
 EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path);
ac3a84
 
ac3a84
-typedef struct {
ac3a84
-        void *hook;
ac3a84
-
ac3a84
-        /* End of EFI_SECURITY_ARCH(2)_PROTOCOL. The rest is our own protocol instance data. */
ac3a84
-
ac3a84
-        EFI_HANDLE original_handle;
ac3a84
-        union {
ac3a84
-                void *original;
ac3a84
-                EFI_SECURITY_ARCH_PROTOCOL *original_security;
ac3a84
-                EFI_SECURITY2_ARCH_PROTOCOL *original_security2;
ac3a84
-        };
ac3a84
-
ac3a84
-        /* Used by the stub to identify the embedded image. */
ac3a84
-        const void *payload;
ac3a84
-        size_t payload_len;
ac3a84
-        const EFI_DEVICE_PATH *payload_device_path;
ac3a84
-} SecurityOverride;
ac3a84
-
ac3a84
-void install_security_override(SecurityOverride *override, SecurityOverride *override2);
ac3a84
-void uninstall_security_override(SecurityOverride *override, SecurityOverride *override2);
ac3a84
+typedef bool (*security_validator_t)(
ac3a84
+                const void *ctx,
ac3a84
+                const EFI_DEVICE_PATH *device_path,
ac3a84
+                const void *file_buffer,
ac3a84
+                size_t file_size);
ac3a84
+
ac3a84
+void install_security_override(security_validator_t validator, const void *validator_ctx);
ac3a84
+void uninstall_security_override(void);
ac3a84
diff --git a/src/boot/efi/shim.c b/src/boot/efi/shim.c
ac3a84
index 3ae058cb84..ac224336bc 100644
ac3a84
--- a/src/boot/efi/shim.c
ac3a84
+++ b/src/boot/efi/shim.c
ac3a84
@@ -23,7 +23,7 @@
ac3a84
 #endif
ac3a84
 
ac3a84
 struct ShimLock {
ac3a84
-        EFI_STATUS __sysv_abi__ (*shim_verify) (void *buffer, uint32_t size);
ac3a84
+        EFI_STATUS __sysv_abi__ (*shim_verify) (const void *buffer, uint32_t size);
ac3a84
 
ac3a84
         /* context is actually a struct for the PE header, but it isn't needed so void is sufficient just do define the interface
ac3a84
          * see shim.c/shim.h and PeHeader.h in the github shim repo */
ac3a84
@@ -41,79 +41,45 @@ bool shim_loaded(void) {
ac3a84
         return BS->LocateProtocol((EFI_GUID*) SHIM_LOCK_GUID, NULL, (void**) &shim_lock) == EFI_SUCCESS;
ac3a84
 }
ac3a84
 
ac3a84
-static bool shim_validate(void *data, uint32_t size) {
ac3a84
-        struct ShimLock *shim_lock;
ac3a84
-
ac3a84
-        if (!data)
ac3a84
-                return false;
ac3a84
-
ac3a84
-        if (BS->LocateProtocol((EFI_GUID*) SHIM_LOCK_GUID, NULL, (void**) &shim_lock) != EFI_SUCCESS)
ac3a84
-                return false;
ac3a84
-
ac3a84
-        if (!shim_lock)
ac3a84
-                return false;
ac3a84
-
ac3a84
-        return shim_lock->shim_verify(data, size) == EFI_SUCCESS;
ac3a84
-}
ac3a84
-
ac3a84
-static EFIAPI EFI_STATUS security2_hook(
ac3a84
-                const SecurityOverride *this,
ac3a84
-                const EFI_DEVICE_PATH *device_path,
ac3a84
-                void *file_buffer,
ac3a84
-                UINTN file_size,
ac3a84
-                BOOLEAN boot_policy) {
ac3a84
-
ac3a84
-        assert(this);
ac3a84
-        assert(this->hook == security2_hook);
ac3a84
-
ac3a84
-        if (shim_validate(file_buffer, file_size))
ac3a84
-                return EFI_SUCCESS;
ac3a84
-
ac3a84
-        return this->original_security2->FileAuthentication(
ac3a84
-                        this->original_security2, device_path, file_buffer, file_size, boot_policy);
ac3a84
-}
ac3a84
-
ac3a84
-static EFIAPI EFI_STATUS security_hook(
ac3a84
-                const SecurityOverride *this,
ac3a84
-                uint32_t authentication_status,
ac3a84
-                const EFI_DEVICE_PATH *device_path) {
ac3a84
+static bool shim_validate(
ac3a84
+                const void *ctx, const EFI_DEVICE_PATH *device_path, const void *file_buffer, size_t file_size) {
ac3a84
 
ac3a84
         EFI_STATUS err;
ac3a84
+        _cleanup_free_ char *file_buffer_owned = NULL;
ac3a84
 
ac3a84
-        assert(this);
ac3a84
-        assert(this->hook == security_hook);
ac3a84
+        if (!file_buffer) {
ac3a84
+                if (!device_path)
ac3a84
+                        return false;
ac3a84
 
ac3a84
-        if (!device_path)
ac3a84
-                return this->original_security->FileAuthenticationState(
ac3a84
-                                this->original_security, authentication_status, device_path);
ac3a84
+                EFI_HANDLE device_handle;
ac3a84
+                EFI_DEVICE_PATH *file_dp = (EFI_DEVICE_PATH *) device_path;
ac3a84
+                err = BS->LocateDevicePath(&FileSystemProtocol, &file_dp, &device_handle);
ac3a84
+                if (err != EFI_SUCCESS)
ac3a84
+                        return false;
ac3a84
 
ac3a84
-        EFI_HANDLE device_handle;
ac3a84
-        EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) device_path;
ac3a84
-        err = BS->LocateDevicePath(&FileSystemProtocol, &dp, &device_handle);
ac3a84
-        if (err != EFI_SUCCESS)
ac3a84
-                return err;
ac3a84
+                _cleanup_(file_closep) EFI_FILE *root = NULL;
ac3a84
+                err = open_volume(device_handle, &root);
ac3a84
+                if (err != EFI_SUCCESS)
ac3a84
+                        return false;
ac3a84
 
ac3a84
-        _cleanup_(file_closep) EFI_FILE *root = NULL;
ac3a84
-        err = open_volume(device_handle, &root);
ac3a84
-        if (err != EFI_SUCCESS)
ac3a84
-                return err;
ac3a84
+                _cleanup_free_ char16_t *dp_str = NULL;
ac3a84
+                err = device_path_to_str(file_dp, &dp_str);
ac3a84
+                if (err != EFI_SUCCESS)
ac3a84
+                        return false;
ac3a84
 
ac3a84
-        _cleanup_free_ char16_t *dp_str = NULL;
ac3a84
-        err = device_path_to_str(dp, &dp_str);
ac3a84
-        if (err != EFI_SUCCESS)
ac3a84
-                return err;
ac3a84
+                err = file_read(root, dp_str, 0, 0, &file_buffer_owned, &file_size);
ac3a84
+                if (err != EFI_SUCCESS)
ac3a84
+                        return false;
ac3a84
 
ac3a84
-        char *file_buffer;
ac3a84
-        size_t file_size;
ac3a84
-        err = file_read(root, dp_str, 0, 0, &file_buffer, &file_size);
ac3a84
-        if (err != EFI_SUCCESS)
ac3a84
-                return err;
ac3a84
+                file_buffer = file_buffer_owned;
ac3a84
+        }
ac3a84
 
ac3a84
-        if (shim_validate(file_buffer, file_size))
ac3a84
-                return EFI_SUCCESS;
ac3a84
+        struct ShimLock *shim_lock;
ac3a84
+        err = BS->LocateProtocol((EFI_GUID *) SHIM_LOCK_GUID, NULL, (void **) &shim_lock);
ac3a84
+        if (err != EFI_SUCCESS)
ac3a84
+                return false;
ac3a84
 
ac3a84
-        return this->original_security->FileAuthenticationState(
ac3a84
-                        this->original_security, authentication_status, device_path);
ac3a84
+        return shim_lock->shim_verify(file_buffer, file_size) == EFI_SUCCESS;
ac3a84
 }
ac3a84
 
ac3a84
 EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image) {
ac3a84
@@ -122,20 +88,14 @@ EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path
ac3a84
 
ac3a84
         bool have_shim = shim_loaded();
ac3a84
 
ac3a84
-        SecurityOverride security_override = {
ac3a84
-                .hook = security_hook,
ac3a84
-        }, security2_override = {
ac3a84
-                .hook = security2_hook,
ac3a84
-        };
ac3a84
-
ac3a84
         if (have_shim)
ac3a84
-                install_security_override(&security_override, &security2_override);
ac3a84
+                install_security_override(shim_validate, NULL);
ac3a84
 
ac3a84
         EFI_STATUS ret = BS->LoadImage(
ac3a84
                         /*BootPolicy=*/false, parent, (EFI_DEVICE_PATH *) device_path, NULL, 0, ret_image);
ac3a84
 
ac3a84
         if (have_shim)
ac3a84
-                uninstall_security_override(&security_override, &security2_override);
ac3a84
+                uninstall_security_override();
ac3a84
 
ac3a84
         return ret;
ac3a84
 }