stub: Allow loading unsigned kernel images

This commit is contained in:
Jan Janssen
2022-10-17 14:48:28 +02:00
parent 0e3c374e8c
commit 6731a102da
3 changed files with 145 additions and 2 deletions
+55 -2
View File
@@ -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(
+67
View 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);
}
+23
View 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);