1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-25 23:21:33 +03:00

Merge pull request #24777 from medhefgo/stub

stub: Use LoadImage/StartImage
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2022-10-18 07:36:20 +02:00 committed by GitHub
commit 8ff92848a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 302 additions and 270 deletions

View File

@ -2379,7 +2379,7 @@ static EFI_STATUS image_start(
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Error preparing initrd: %r", err); return log_error_status_stall(err, L"Error preparing initrd: %r", err);
err = BS->LoadImage(false, parent_image, path, NULL, 0, &image); err = shim_load_image(parent_image, path, &image);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err); return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
@ -2416,21 +2416,22 @@ static EFI_STATUS image_start(
/* Try calling the kernel compat entry point if one exists. */ /* Try calling the kernel compat entry point if one exists. */
if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) { if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) {
uint32_t kernel_entry_address; uint32_t compat_address;
err = pe_kernel_info(loaded_image->ImageBase, &kernel_entry_address, NULL, NULL); err = pe_kernel_info(loaded_image->ImageBase, &compat_address);
if (err != EFI_SUCCESS) { if (err != EFI_SUCCESS) {
if (err != EFI_UNSUPPORTED) if (err != EFI_UNSUPPORTED)
return log_error_status_stall(err, L"Error finding kernel compat entry address: %r", err); return log_error_status_stall(err, L"Error finding kernel compat entry address: %r", err);
} else { } else if (compat_address > 0) {
EFI_IMAGE_ENTRY_POINT kernel_entry = EFI_IMAGE_ENTRY_POINT kernel_entry =
(EFI_IMAGE_ENTRY_POINT) ((uint8_t *) loaded_image->ImageBase + kernel_entry_address); (EFI_IMAGE_ENTRY_POINT) ((uint8_t *) loaded_image->ImageBase + compat_address);
err = kernel_entry(image, ST); err = kernel_entry(image, ST);
graphics_mode(false); graphics_mode(false);
if (err == EFI_SUCCESS) if (err == EFI_SUCCESS)
return EFI_SUCCESS; return EFI_SUCCESS;
} } else
err = EFI_UNSUPPORTED;
} }
return log_error_status_stall(err, L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err); return log_error_status_stall(err, L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err);
@ -2685,12 +2686,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Unable to open root directory: %r", err); return log_error_status_stall(err, L"Unable to open root directory: %r", err);
if (secure_boot_enabled() && shim_loaded()) {
err = security_policy_install();
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Error installing security policy: %r", err);
}
(void) load_drivers(image, loaded_image, root_dir); (void) load_drivers(image, loaded_image, root_dir);
config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir); config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);

View File

@ -14,110 +14,119 @@
#include "initrd.h" #include "initrd.h"
#include "linux.h" #include "linux.h"
#include "pe.h" #include "pe.h"
#include "secure-boot.h"
#include "util.h" #include "util.h"
static EFI_LOADED_IMAGE_PROTOCOL *loaded_image_free(EFI_LOADED_IMAGE_PROTOCOL *img) { #define STUB_PAYLOAD_GUID \
if (!img) { 0x55c5d1f8, 0x04cd, 0x46b5, { 0x8a, 0x20, 0xe5, 0x6c, 0xbb, 0x30, 0x52, 0xd0 } }
return NULL;
mfree(img->LoadOptions);
return mfree(img);
}
static EFI_STATUS loaded_image_register( static EFIAPI EFI_STATUS security_hook(
const char *cmdline, UINTN cmdline_len, const SecurityOverride *this, uint32_t authentication_status, const EFI_DEVICE_PATH *file) {
const void *linux_buffer, UINTN linux_length,
EFI_HANDLE *ret_image) {
EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL; assert(this);
EFI_STATUS err; assert(this->hook == security_hook);
assert(cmdline || cmdline_len > 0); if (file == this->payload_device_path)
assert(linux_buffer && linux_length > 0);
assert(ret_image);
/* create and install new LoadedImage Protocol */
loaded_image = xnew(EFI_LOADED_IMAGE_PROTOCOL, 1);
*loaded_image = (EFI_LOADED_IMAGE_PROTOCOL) {
.ImageBase = (void *) linux_buffer,
.ImageSize = linux_length
};
/* if a cmdline is set convert it to UCS2 */
if (cmdline) {
loaded_image->LoadOptions = xstra_to_str(cmdline);
loaded_image->LoadOptionsSize = strsize16(loaded_image->LoadOptions);
}
/* install a new LoadedImage protocol. ret_handle is a new image handle */
err = BS->InstallMultipleProtocolInterfaces(
ret_image,
&LoadedImageProtocol, loaded_image,
NULL);
if (err != EFI_SUCCESS)
loaded_image = loaded_image_free(loaded_image);
return err;
}
static EFI_STATUS loaded_image_unregister(EFI_HANDLE loaded_image_handle) {
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
EFI_STATUS err;
if (!loaded_image_handle)
return EFI_SUCCESS; return EFI_SUCCESS;
/* get the LoadedImage protocol that we allocated earlier */ return this->original_security->FileAuthenticationState(
err = BS->OpenProtocol( this->original_security, authentication_status, file);
loaded_image_handle, &LoadedImageProtocol, (void **) &loaded_image,
NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (err != EFI_SUCCESS)
return err;
/* close the handle */
(void) BS->CloseProtocol(loaded_image_handle, &LoadedImageProtocol, NULL, NULL);
err = BS->UninstallMultipleProtocolInterfaces(
loaded_image_handle,
&LoadedImageProtocol, loaded_image,
NULL);
if (err != EFI_SUCCESS)
return err;
loaded_image_handle = NULL;
loaded_image = loaded_image_free(loaded_image);
return EFI_SUCCESS;
} }
static inline void cleanup_loaded_image(EFI_HANDLE *loaded_image_handle) { static EFIAPI EFI_STATUS security2_hook(
(void) loaded_image_unregister(*loaded_image_handle); const SecurityOverride *this,
*loaded_image_handle = NULL; 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 and it allows us to identify
* the loaded image from within the security hooks. */
struct {
VENDOR_DEVICE_PATH payload;
EFI_DEVICE_PATH end;
} _packed_ payload_device_path = {
.payload = {
.Header = {
.Type = MEDIA_DEVICE_PATH,
.SubType = MEDIA_VENDOR_DP,
.Length = { sizeof(payload_device_path.payload), 0 },
},
.Guid = STUB_PAYLOAD_GUID,
},
.end = {
.Type = END_DEVICE_PATH_TYPE,
.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
.Length = { sizeof(payload_device_path.end), 0 },
},
};
/* 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( EFI_STATUS linux_exec(
EFI_HANDLE image, EFI_HANDLE parent,
const char *cmdline, UINTN cmdline_len, const char *cmdline, UINTN cmdline_len,
const void *linux_buffer, UINTN linux_length, const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length) { const void *initrd_buffer, UINTN initrd_length) {
_cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL; uint32_t compat_address;
_cleanup_(cleanup_loaded_image) EFI_HANDLE loaded_image_handle = NULL;
uint32_t kernel_alignment, kernel_size_of_image, kernel_entry_address;
EFI_IMAGE_ENTRY_POINT kernel_entry;
void *new_buffer;
EFI_STATUS err; EFI_STATUS err;
assert(image); assert(parent);
assert(cmdline || cmdline_len == 0); assert(cmdline || cmdline_len == 0);
assert(linux_buffer && linux_length > 0); assert(linux_buffer && linux_length > 0);
assert(initrd_buffer || initrd_length == 0); assert(initrd_buffer || initrd_length == 0);
/* get the necessary fields from the PE header */ err = pe_kernel_info(linux_buffer, &compat_address);
err = pe_kernel_info(linux_buffer, &kernel_entry_address, &kernel_size_of_image, &kernel_alignment);
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
if (err == EFI_UNSUPPORTED) if (err == EFI_UNSUPPORTED)
/* Kernel is too old to support LINUX_INITRD_MEDIA_GUID, try the deprecated EFI handover /* Kernel is too old to support LINUX_INITRD_MEDIA_GUID, try the deprecated EFI handover
* protocol. */ * protocol. */
return linux_exec_efi_handover( return linux_exec_efi_handover(
image, parent,
cmdline, cmdline,
cmdline_len, cmdline_len,
linux_buffer, linux_buffer,
@ -126,46 +135,36 @@ EFI_STATUS linux_exec(
initrd_length); initrd_length);
#endif #endif
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return err; return log_error_status_stall(err, u"Bad kernel image: %r", err);
/* sanity check */
assert(kernel_size_of_image >= linux_length);
/* Linux kernel complains if it's not loaded at a properly aligned memory address. The correct alignment _cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL;
is provided by Linux as the SegmentAlignment in the PeOptionalHeader. Additionally the kernel needs to err = load_image(parent, linux_buffer, linux_length, &kernel_image);
be in a memory segment that's SizeOfImage (again from PeOptionalHeader) large, so that the Kernel has
space for its BSS section. SizeOfImage is always larger than linux_length, which is only the size of
Code, (static) Data and Headers.
Interrestingly only ARM/Aarch64 and RISC-V kernel stubs check these assertions and can even boot (with warnings)
if they are not met. x86 and x86_64 kernel stubs don't do checks and fail if the BSS section is too small.
*/
/* allocate SizeOfImage + SectionAlignment because the new_buffer can move up to Alignment-1 bytes */
_cleanup_pages_ Pages kernel = xmalloc_pages(
AllocateAnyPages,
EfiLoaderCode,
EFI_SIZE_TO_PAGES(ALIGN_TO(kernel_size_of_image, kernel_alignment) + kernel_alignment),
0);
new_buffer = PHYSICAL_ADDRESS_TO_POINTER(ALIGN_TO(kernel.addr, kernel_alignment));
if (!new_buffer) /* Silence gcc 11.2.0, assert(new_buffer) doesn't work. */
return EFI_OUT_OF_RESOURCES;
memcpy(new_buffer, linux_buffer, linux_length);
/* zero out rest of memory (probably not needed, but BSS section should be 0) */
memset((uint8_t *)new_buffer + linux_length, 0, kernel_size_of_image - linux_length);
/* get the entry point inside the relocated kernel */
kernel_entry = (EFI_IMAGE_ENTRY_POINT) ((const uint8_t *)new_buffer + kernel_entry_address);
/* register a LoadedImage Protocol in order to pass on the commandline */
err = loaded_image_register(cmdline, cmdline_len, new_buffer, linux_length, &loaded_image_handle);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return err; return log_error_status_stall(err, u"Error loading kernel image: %r", err);
/* register a LINUX_INITRD_MEDIA DevicePath to serve the initrd */ EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
err = BS->HandleProtocol(kernel_image, &LoadedImageProtocol, (void **) &loaded_image);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, u"Error getting kernel loaded image protocol: %r", err);
if (cmdline) {
loaded_image->LoadOptions = xstra_to_str(cmdline);
loaded_image->LoadOptionsSize = strsize16(loaded_image->LoadOptions);
}
_cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
err = initrd_register(initrd_buffer, initrd_length, &initrd_handle); err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return err; return log_error_status_stall(err, u"Error registering initrd: %r", err);
/* call the kernel */ err = BS->StartImage(kernel_image, NULL, NULL);
return kernel_entry(loaded_image_handle, ST);
/* Try calling the kernel compat entry point if one exists. */
if (err == EFI_UNSUPPORTED && compat_address > 0) {
EFI_IMAGE_ENTRY_POINT compat_entry =
(EFI_IMAGE_ENTRY_POINT) ((uint8_t *) loaded_image->ImageBase + compat_address);
err = compat_entry(kernel_image, ST);
}
return log_error_status_stall(err, u"Error starting kernel image: %r", err);
} }

View File

@ -4,12 +4,12 @@
#include <efi.h> #include <efi.h>
EFI_STATUS linux_exec( EFI_STATUS linux_exec(
EFI_HANDLE image, EFI_HANDLE parent,
const char *cmdline, UINTN cmdline_len, const char *cmdline, UINTN cmdline_len,
const void *linux_buffer, UINTN linux_length, const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length); const void *initrd_buffer, UINTN initrd_length);
EFI_STATUS linux_exec_efi_handover( EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE image, EFI_HANDLE parent,
const char *cmdline, UINTN cmdline_len, const char *cmdline, UINTN cmdline_len,
const void *linux_buffer, UINTN linux_length, const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length); const void *initrd_buffer, UINTN initrd_length);

View File

@ -99,10 +99,10 @@ assert_cc(sizeof(BootParams) == 4096);
# define __regparm0__ # define __regparm0__
#endif #endif
typedef void (*handover_f)(void *image, EFI_SYSTEM_TABLE *table, BootParams *params) __regparm0__ typedef void (*handover_f)(void *parent, EFI_SYSTEM_TABLE *table, BootParams *params) __regparm0__
__attribute__((sysv_abi)); __attribute__((sysv_abi));
static void linux_efi_handover(EFI_HANDLE image, uintptr_t kernel, BootParams *params) { static void linux_efi_handover(EFI_HANDLE parent, uintptr_t kernel, BootParams *params) {
assert(params); assert(params);
kernel += (params->hdr.setup_sects + 1) * KERNEL_SECTOR_SIZE; /* 32bit entry address. */ kernel += (params->hdr.setup_sects + 1) * KERNEL_SECTOR_SIZE; /* 32bit entry address. */
@ -121,16 +121,16 @@ static void linux_efi_handover(EFI_HANDLE image, uintptr_t kernel, BootParams *p
* kernel to be booted from a 32bit sd-stub. */ * kernel to be booted from a 32bit sd-stub. */
handover_f handover = (handover_f) kernel; handover_f handover = (handover_f) kernel;
handover(image, ST, params); handover(parent, ST, params);
} }
EFI_STATUS linux_exec_efi_handover( EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE image, EFI_HANDLE parent,
const char *cmdline, UINTN cmdline_len, const char *cmdline, UINTN cmdline_len,
const void *linux_buffer, UINTN linux_length, const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length) { const void *initrd_buffer, UINTN initrd_length) {
assert(image); assert(parent);
assert(cmdline || cmdline_len == 0); assert(cmdline || cmdline_len == 0);
assert(linux_buffer); assert(linux_buffer);
assert(initrd_buffer || initrd_length == 0); assert(initrd_buffer || initrd_length == 0);
@ -203,6 +203,6 @@ EFI_STATUS linux_exec_efi_handover(
boot_params->hdr.ramdisk_size = initrd_length; boot_params->hdr.ramdisk_size = initrd_length;
boot_params->ext_ramdisk_size = ((uint64_t) initrd_length) >> 32; boot_params->ext_ramdisk_size = ((uint64_t) initrd_length) >> 32;
linux_efi_handover(image, (uintptr_t) linux_buffer, boot_params); linux_efi_handover(parent, (uintptr_t) linux_buffer, boot_params);
return EFI_LOAD_ERROR; return EFI_LOAD_ERROR;
} }

View File

@ -309,41 +309,34 @@ typedef struct tdEFI_TCG2_PROTOCOL {
{0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68} } {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68} }
/* UEFI Platform Initialization (Vol2: DXE) */ /* UEFI Platform Initialization (Vol2: DXE) */
#ifndef SECURITY_PROTOCOL_GUID #ifndef EFI_SECURITY_ARCH_PROTOCOL_GUID
#define SECURITY_PROTOCOL_GUID \ #define EFI_SECURITY_ARCH_PROTOCOL_GUID \
&(const EFI_GUID) { 0xa46423e3, 0x4617, 0x49f1, { 0xb9, 0xff, 0xd1, 0xbf, 0xa9, 0x11, 0x58, 0x39 } } { 0xa46423e3, 0x4617, 0x49f1, { 0xb9, 0xff, 0xd1, 0xbf, 0xa9, 0x11, 0x58, 0x39 } }
#define SECURITY_PROTOCOL2_GUID \ #define EFI_SECURITY2_ARCH_PROTOCOL_GUID \
&(const EFI_GUID) { 0x94ab2f58, 0x1438, 0x4ef1, { 0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } } { 0x94ab2f58, 0x1438, 0x4ef1, { 0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }
struct _EFI_SECURITY2_PROTOCOL; typedef struct EFI_SECURITY_ARCH_PROTOCOL EFI_SECURITY_ARCH_PROTOCOL;
struct _EFI_SECURITY_PROTOCOL; typedef struct EFI_SECURITY2_ARCH_PROTOCOL EFI_SECURITY2_ARCH_PROTOCOL;
struct _EFI_DEVICE_PATH_PROTOCOL;
typedef struct _EFI_SECURITY2_PROTOCOL EFI_SECURITY2_PROTOCOL; typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE)(
typedef struct _EFI_SECURITY_PROTOCOL EFI_SECURITY_PROTOCOL; const EFI_SECURITY_ARCH_PROTOCOL *This,
typedef struct _EFI_DEVICE_PATH_PROTOCOL EFI_DEVICE_PATH_PROTOCOL; uint32_t AuthenticationStatus,
const EFI_DEVICE_PATH *File);
typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE) ( struct EFI_SECURITY_ARCH_PROTOCOL {
const EFI_SECURITY_PROTOCOL *This, EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState;
UINT32 AuthenticationStatus,
const EFI_DEVICE_PATH_PROTOCOL *File
);
typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION) (
const EFI_SECURITY2_PROTOCOL *This,
const EFI_DEVICE_PATH_PROTOCOL *DevicePath,
VOID *FileBuffer,
UINTN FileSize,
BOOLEAN BootPolicy
);
struct _EFI_SECURITY2_PROTOCOL {
EFI_SECURITY2_FILE_AUTHENTICATION FileAuthentication;
}; };
struct _EFI_SECURITY_PROTOCOL { typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION)(
EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState; const EFI_SECURITY2_ARCH_PROTOCOL *This,
const EFI_DEVICE_PATH *DevicePath,
void *FileBuffer,
UINTN FileSize,
BOOLEAN BootPolicy);
struct EFI_SECURITY2_ARCH_PROTOCOL {
EFI_SECURITY2_FILE_AUTHENTICATION FileAuthentication;
}; };
#endif #endif

View File

@ -215,23 +215,15 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const
return 0; return 0;
} }
EFI_STATUS pe_kernel_info( EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
const void *base,
uint32_t *ret_entry_point_address,
uint32_t *ret_size_of_image,
uint32_t *ret_section_alignment) {
const DosFileHeader *dos;
const PeFileHeader *pe;
assert(base); assert(base);
assert(ret_entry_point_address); assert(ret_compat_address);
dos = (const DosFileHeader *) base; const DosFileHeader *dos = (const DosFileHeader *) base;
if (!verify_dos(dos)) if (!verify_dos(dos))
return EFI_LOAD_ERROR; return EFI_LOAD_ERROR;
pe = (const PeFileHeader *) ((const uint8_t *) base + dos->ExeHeader); const PeFileHeader *pe = (const PeFileHeader *) ((const uint8_t *) base + dos->ExeHeader);
if (!verify_pe(pe, /* allow_compatibility= */ true)) if (!verify_pe(pe, /* allow_compatibility= */ true))
return EFI_LOAD_ERROR; return EFI_LOAD_ERROR;
@ -239,21 +231,17 @@ EFI_STATUS pe_kernel_info(
if (pe->OptionalHeader.MajorImageVersion < 1) if (pe->OptionalHeader.MajorImageVersion < 1)
return EFI_UNSUPPORTED; return EFI_UNSUPPORTED;
uint32_t entry_address = pe->OptionalHeader.AddressOfEntryPoint; if (pe->FileHeader.Machine == TARGET_MACHINE_TYPE) {
*ret_compat_address = 0;
/* Look for a compat entry point. */ return EFI_SUCCESS;
if (pe->FileHeader.Machine != TARGET_MACHINE_TYPE) {
entry_address = get_compatibility_entry_address(dos, pe);
if (entry_address == 0)
/* Image type not supported and no compat entry found. */
return EFI_UNSUPPORTED;
} }
*ret_entry_point_address = entry_address; uint32_t compat_address = get_compatibility_entry_address(dos, pe);
if (ret_size_of_image) if (compat_address == 0)
*ret_size_of_image = pe->OptionalHeader.SizeOfImage; /* Image type not supported and no compat entry found. */
if (ret_section_alignment) return EFI_UNSUPPORTED;
*ret_section_alignment = pe->OptionalHeader.SectionAlignment;
*ret_compat_address = compat_address;
return EFI_SUCCESS; return EFI_SUCCESS;
} }

View File

@ -17,8 +17,4 @@ EFI_STATUS pe_file_locate_sections(
UINTN *offsets, UINTN *offsets,
UINTN *sizes); UINTN *sizes);
EFI_STATUS pe_kernel_info( EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address);
const void *base,
uint32_t *ret_entry_point_address,
uint32_t *ret_size_of_image,
uint32_t *ret_section_alignment);

View File

@ -126,3 +126,70 @@ out_deallocate:
return err; 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);
}

View File

@ -2,7 +2,9 @@
#pragma once #pragma once
#include <efi.h> #include <efi.h>
#include "efivars-fundamental.h" #include "efivars-fundamental.h"
#include "missing_efi.h"
typedef enum { typedef enum {
ENROLL_OFF, /* no Secure Boot key enrollment whatsoever, even manual entries are not generated */ 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); SecureBootMode secure_boot_mode(void);
EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path); 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);

View File

@ -13,6 +13,7 @@
#include "missing_efi.h" #include "missing_efi.h"
#include "util.h" #include "util.h"
#include "secure-boot.h"
#include "shim.h" #include "shim.h"
#if defined(__x86_64__) || defined(__i386__) #if defined(__x86_64__) || defined(__i386__)
@ -55,116 +56,86 @@ static bool shim_validate(void *data, uint32_t size) {
return shim_lock->shim_verify(data, size) == EFI_SUCCESS; return shim_lock->shim_verify(data, size) == EFI_SUCCESS;
} }
/* Handle to the original authenticator for security1 protocol */ static EFIAPI EFI_STATUS security2_hook(
static EFI_SECURITY_FILE_AUTHENTICATION_STATE esfas = NULL; const SecurityOverride *this,
const EFI_DEVICE_PATH *device_path,
/* Handle to the original authenticator for security2 protocol */ void *file_buffer,
static EFI_SECURITY2_FILE_AUTHENTICATION es2fa = NULL; UINTN file_size,
BOOLEAN boot_policy) {
/*
* Perform shim/MOK and Secure Boot authentication on a binary that's already been
* loaded into memory. This function does the platform SB authentication first
* but preserves its return value in case of its failure, so that it can be
* returned in case of a shim/MOK authentication failure. This is done because
* the SB failure code seems to vary from one implementation to another, and I
* don't want to interfere with that at this time.
*/
static EFIAPI EFI_STATUS security2_policy_authentication (const EFI_SECURITY2_PROTOCOL *this,
const EFI_DEVICE_PATH_PROTOCOL *device_path,
void *file_buffer, UINTN file_size, BOOLEAN boot_policy) {
EFI_STATUS err;
assert(this); assert(this);
/* device_path and file_buffer may be NULL */ assert(this->hook == security2_hook);
/* Chain original security policy */
err = es2fa(this, device_path, file_buffer, file_size, boot_policy);
/* if OK, don't bother with MOK check */
if (err == EFI_SUCCESS)
return err;
if (shim_validate(file_buffer, file_size)) if (shim_validate(file_buffer, file_size))
return EFI_SUCCESS; return EFI_SUCCESS;
return err; return this->original_security2->FileAuthentication(
this->original_security2, device_path, file_buffer, file_size, boot_policy);
} }
/* static EFIAPI EFI_STATUS security_hook(
* Perform both shim/MOK and platform Secure Boot authentication. This function loads const SecurityOverride *this,
* the file and performs shim/MOK authentication first simply to avoid double loads uint32_t authentication_status,
* of Linux kernels, which are much more likely to be shim/MOK-signed than platform-signed, const EFI_DEVICE_PATH *device_path) {
* since kernels are big and can take several seconds to load on some computers and
* filesystems. This also has the effect of returning whatever the platform code is for
* authentication failure, be it EFI_ACCESS_DENIED, EFI_SECURITY_VIOLATION, or something
* else. (This seems to vary between implementations.)
*/
static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROTOCOL *this, uint32_t authentication_status,
const EFI_DEVICE_PATH_PROTOCOL *device_path_const) {
EFI_STATUS err; EFI_STATUS err;
_cleanup_free_ char16_t *dev_path_str = NULL;
EFI_HANDLE h;
_cleanup_free_ char *file_buffer = NULL;
UINTN file_size;
assert(this); assert(this);
assert(this->hook == security_hook);
if (!device_path_const) if (!device_path)
return EFI_INVALID_PARAMETER; return this->original_security->FileAuthenticationState(
this->original_security, authentication_status, device_path);
EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) device_path_const; EFI_HANDLE device_handle;
err = BS->LocateDevicePath(&FileSystemProtocol, &dp, &h); EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) device_path;
err = BS->LocateDevicePath(&FileSystemProtocol, &dp, &device_handle);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return err; return err;
_cleanup_(file_closep) EFI_FILE *root = NULL; _cleanup_(file_closep) EFI_FILE *root = NULL;
err = open_volume(h, &root); err = open_volume(device_handle, &root);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return err; return err;
err = device_path_to_str(dp, &dev_path_str); _cleanup_free_ char16_t *dp_str = NULL;
err = device_path_to_str(dp, &dp_str);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return err; return err;
err = file_read(root, dev_path_str, 0, 0, &file_buffer, &file_size); char *file_buffer;
size_t file_size;
err = file_read(root, dp_str, 0, 0, &file_buffer, &file_size);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return err; return err;
if (shim_validate(file_buffer, file_size)) if (shim_validate(file_buffer, file_size))
return EFI_SUCCESS; return EFI_SUCCESS;
/* Try using the platform's native policy.... */ return this->original_security->FileAuthenticationState(
return esfas(this, authentication_status, device_path_const); this->original_security, authentication_status, device_path);
} }
EFI_STATUS security_policy_install(void) { EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image) {
EFI_SECURITY_PROTOCOL *security_protocol; assert(device_path);
EFI_SECURITY2_PROTOCOL *security2_protocol = NULL; assert(ret_image);
EFI_STATUS err;
/* Already Installed */ bool have_shim = shim_loaded();
if (esfas)
return EFI_ALREADY_STARTED;
/* SecurityOverride security_override = {
* Don't bother with status here. The call is allowed .hook = security_hook,
* to fail, since SECURITY2 was introduced in PI 1.2.1. }, security2_override = {
* Use security2_protocol == NULL as indicator. .hook = security2_hook,
*/ };
BS->LocateProtocol((EFI_GUID*) SECURITY_PROTOCOL2_GUID, NULL, (void**) &security2_protocol);
err = BS->LocateProtocol((EFI_GUID*) SECURITY_PROTOCOL_GUID, NULL, (void**) &security_protocol); if (have_shim)
/* This one is mandatory, so there's a serious problem */ install_security_override(&security_override, &security2_override);
if (err != EFI_SUCCESS)
return err;
esfas = security_protocol->FileAuthenticationState; EFI_STATUS ret = BS->LoadImage(
security_protocol->FileAuthenticationState = security_policy_authentication; /*BootPolicy=*/false, parent, (EFI_DEVICE_PATH *) device_path, NULL, 0, ret_image);
if (security2_protocol) { if (have_shim)
es2fa = security2_protocol->FileAuthentication; uninstall_security_override(&security_override, &security2_override);
security2_protocol->FileAuthentication = security2_policy_authentication;
}
return EFI_SUCCESS; return ret;
} }

View File

@ -10,7 +10,7 @@
#pragma once #pragma once
#include <efi.h> #include <efi.h>
#include <stdbool.h>
bool shim_loaded(void); bool shim_loaded(void);
EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image);
EFI_STATUS security_policy_install(void);

View File

@ -378,5 +378,5 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size, PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size); PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
graphics_mode(false); graphics_mode(false);
return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err); return err;
} }