summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Janssen <medhefgo@web.de>2022-09-21 12:56:20 +0200
committerJan Janssen <medhefgo@web.de>2022-10-17 14:48:28 +0200
commit6731a102da4b5827ae10355670c34396e89e265b (patch)
treecc107379d44ce3ea1a988b9c6fb28aec39f2854a
parent0e3c374e8c0dbf3586fa9ac0262c953585456201 (diff)
downloadsystemd-6731a102da4b5827ae10355670c34396e89e265b.tar.gz
stub: Allow loading unsigned kernel images
-rw-r--r--src/boot/efi/linux.c57
-rw-r--r--src/boot/efi/secure-boot.c67
-rw-r--r--src/boot/efi/secure-boot.h23
3 files changed, 145 insertions, 2 deletions
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 <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);