Blob Blame History Raw
From 8d0b70887a09b9d4a8b669620579d3b6780f0755 Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Tue, 15 Nov 2022 18:53:02 +0100
Subject: [PATCH] boot: Replace firmware security hooks directly

For some firmware, replacing their own security arch instance with our
override using ReinstallProtocolInterface() is not enough as they will
not use it. This commit goes back to how this was done before by
directly modifying the security protocols.

Fixes: #25336
(cherry picked from commit 967a868563996e928f1fade5bcafc82a7219742b)

Related: #2138081
---
 src/boot/efi/secure-boot.c | 119 +++++++++++++------------------------
 1 file changed, 40 insertions(+), 79 deletions(-)

diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c
index 0e615c55e0..65457bf423 100644
--- a/src/boot/efi/secure-boot.c
+++ b/src/boot/efi/secure-boot.c
@@ -128,15 +128,10 @@ out_deallocate:
 }
 
 static struct SecurityOverride {
-        /* Our own security arch instances that we register onto original_handle, thereby replacing the
-         * firmware provided instances. */
-        EFI_SECURITY_ARCH_PROTOCOL override;
-        EFI_SECURITY2_ARCH_PROTOCOL override2;
-
-        /* These are saved so we can uninstall our own instance later. */
-        EFI_HANDLE original_handle, original_handle2;
-        EFI_SECURITY_ARCH_PROTOCOL *original_security;
-        EFI_SECURITY2_ARCH_PROTOCOL *original_security2;
+        EFI_SECURITY_ARCH_PROTOCOL *security;
+        EFI_SECURITY2_ARCH_PROTOCOL *security2;
+        EFI_SECURITY_FILE_AUTHENTICATION_STATE original_hook;
+        EFI_SECURITY2_FILE_AUTHENTICATION original_hook2;
 
         security_validator_t validator;
         const void *validator_ctx;
@@ -148,13 +143,13 @@ static EFIAPI EFI_STATUS security_hook(
                 const EFI_DEVICE_PATH *file) {
 
         assert(security_override.validator);
-        assert(security_override.original_security);
+        assert(security_override.security);
+        assert(security_override.original_hook);
 
         if (security_override.validator(security_override.validator_ctx, file, NULL, 0))
                 return EFI_SUCCESS;
 
-        return security_override.original_security->FileAuthenticationState(
-                        security_override.original_security, authentication_status, file);
+        return security_override.original_hook(security_override.security, authentication_status, file);
 }
 
 static EFIAPI EFI_STATUS security2_hook(
@@ -165,92 +160,58 @@ static EFIAPI EFI_STATUS security2_hook(
                 BOOLEAN boot_policy) {
 
         assert(security_override.validator);
-        assert(security_override.original_security2);
+        assert(security_override.security2);
+        assert(security_override.original_hook2);
 
         if (security_override.validator(security_override.validator_ctx, device_path, file_buffer, file_size))
                 return EFI_SUCCESS;
 
-        return security_override.original_security2->FileAuthentication(
-                        security_override.original_security2, device_path, file_buffer, file_size, boot_policy);
+        return security_override.original_hook2(
+                        security_override.security2, device_path, file_buffer, file_size, boot_policy);
 }
 
-static EFI_STATUS install_security_override_one(
-                EFI_GUID guid, void *override, EFI_HANDLE *ret_original_handle, void **ret_original_security) {
+/* This replaces the platform provided security arch protocols hooks (defined in the UEFI Platform
+ * Initialization Specification) with our own that uses the given validator to decide if a image is to be
+ * trusted. If not running in secure boot or the protocols are not available nothing happens. The override
+ * must be removed with uninstall_security_override() after LoadImage() has been called.
+ *
+ * This is a hack as we do not own the security protocol instances and modifying them is not an official part
+ * of their spec. But there is little else we can do to circumvent secure boot short of implementing our own
+ * PE loader. We could replace the firmware instances with our own instance using
+ * ReinstallProtocolInterface(), but some firmware will still use the old ones. */
+void install_security_override(security_validator_t validator, const void *validator_ctx) {
         EFI_STATUS err;
 
-        assert(override);
-        assert(ret_original_handle);
-        assert(ret_original_security);
-
-        _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);
-
-        *ret_original_security = security;
-        *ret_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 necessary info to undo this
- * in uninstall_security_override(). */
-void install_security_override(security_validator_t validator, const void *validator_ctx) {
         assert(validator);
 
         if (!secure_boot_enabled())
                 return;
 
         security_override = (struct SecurityOverride) {
-                { .FileAuthenticationState = security_hook, },
-                { .FileAuthentication = security2_hook, },
                 .validator = validator,
                 .validator_ctx = validator_ctx,
         };
 
-        (void) install_security_override_one(
-                        (EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID,
-                        &security_override.override,
-                        &security_override.original_handle,
-                        (void **) &security_override.original_security);
-        (void) install_security_override_one(
-                        (EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID,
-                        &security_override.override2,
-                        &security_override.original_handle2,
-                        (void **) &security_override.original_security2);
+        EFI_SECURITY_ARCH_PROTOCOL *security = NULL;
+        err = BS->LocateProtocol(&(EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, NULL, (void **) &security);
+        if (err == EFI_SUCCESS) {
+                security_override.security = security;
+                security_override.original_hook = security->FileAuthenticationState;
+                security->FileAuthenticationState = security_hook;
+        }
+
+        EFI_SECURITY2_ARCH_PROTOCOL *security2 = NULL;
+        err = BS->LocateProtocol(&(EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, NULL, (void **) &security2);
+        if (err == EFI_SUCCESS) {
+                security_override.security2 = security2;
+                security_override.original_hook2 = security2->FileAuthentication;
+                security2->FileAuthentication = security2_hook;
+        }
 }
 
 void uninstall_security_override(void) {
-        /* 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 (security_override.original_handle)
-                assert_se(BS->ReinstallProtocolInterface(
-                                security_override.original_handle,
-                                &(EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID,
-                                &security_override.override,
-                                security_override.original_security) == EFI_SUCCESS);
-
-        if (security_override.original_handle2)
-                assert_se(BS->ReinstallProtocolInterface(
-                                security_override.original_handle2,
-                                &(EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID,
-                                &security_override.override2,
-                                security_override.original_security2) == EFI_SUCCESS);
+        if (security_override.original_hook)
+                security_override.security->FileAuthenticationState = security_override.original_hook;
+        if (security_override.original_hook2)
+                security_override.security2->FileAuthentication = security_override.original_hook2;
 }