mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-24 21:34:08 +03:00
Merge pull request #24777 from medhefgo/stub
stub: Use LoadImage/StartImage
This commit is contained in:
commit
8ff92848a6
@ -2379,7 +2379,7 @@ static EFI_STATUS image_start(
|
||||
if (err != EFI_SUCCESS)
|
||||
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)
|
||||
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. */
|
||||
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_UNSUPPORTED)
|
||||
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) ((uint8_t *) loaded_image->ImageBase + kernel_entry_address);
|
||||
(EFI_IMAGE_ENTRY_POINT) ((uint8_t *) loaded_image->ImageBase + compat_address);
|
||||
|
||||
err = kernel_entry(image, ST);
|
||||
graphics_mode(false);
|
||||
if (err == 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);
|
||||
@ -2685,12 +2686,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
if (err != EFI_SUCCESS)
|
||||
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);
|
||||
|
||||
config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);
|
||||
|
@ -14,110 +14,119 @@
|
||||
#include "initrd.h"
|
||||
#include "linux.h"
|
||||
#include "pe.h"
|
||||
#include "secure-boot.h"
|
||||
#include "util.h"
|
||||
|
||||
static EFI_LOADED_IMAGE_PROTOCOL *loaded_image_free(EFI_LOADED_IMAGE_PROTOCOL *img) {
|
||||
if (!img)
|
||||
return NULL;
|
||||
mfree(img->LoadOptions);
|
||||
return mfree(img);
|
||||
}
|
||||
#define STUB_PAYLOAD_GUID \
|
||||
{ 0x55c5d1f8, 0x04cd, 0x46b5, { 0x8a, 0x20, 0xe5, 0x6c, 0xbb, 0x30, 0x52, 0xd0 } }
|
||||
|
||||
static EFI_STATUS loaded_image_register(
|
||||
const char *cmdline, UINTN cmdline_len,
|
||||
const void *linux_buffer, UINTN linux_length,
|
||||
EFI_HANDLE *ret_image) {
|
||||
static EFIAPI EFI_STATUS security_hook(
|
||||
const SecurityOverride *this, uint32_t authentication_status, const EFI_DEVICE_PATH *file) {
|
||||
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL;
|
||||
EFI_STATUS err;
|
||||
assert(this);
|
||||
assert(this->hook == security_hook);
|
||||
|
||||
assert(cmdline || cmdline_len > 0);
|
||||
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)
|
||||
if (file == this->payload_device_path)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
/* get the LoadedImage protocol that we allocated earlier */
|
||||
err = BS->OpenProtocol(
|
||||
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;
|
||||
return this->original_security->FileAuthenticationState(
|
||||
this->original_security, authentication_status, file);
|
||||
}
|
||||
|
||||
static inline void cleanup_loaded_image(EFI_HANDLE *loaded_image_handle) {
|
||||
(void) loaded_image_unregister(*loaded_image_handle);
|
||||
*loaded_image_handle = NULL;
|
||||
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 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_HANDLE image,
|
||||
EFI_HANDLE parent,
|
||||
const char *cmdline, UINTN cmdline_len,
|
||||
const void *linux_buffer, UINTN linux_length,
|
||||
const void *initrd_buffer, UINTN initrd_length) {
|
||||
|
||||
_cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
|
||||
_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;
|
||||
uint32_t compat_address;
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(image);
|
||||
assert(parent);
|
||||
assert(cmdline || cmdline_len == 0);
|
||||
assert(linux_buffer && linux_length > 0);
|
||||
assert(initrd_buffer || initrd_length == 0);
|
||||
|
||||
/* get the necessary fields from the PE header */
|
||||
err = pe_kernel_info(linux_buffer, &kernel_entry_address, &kernel_size_of_image, &kernel_alignment);
|
||||
err = pe_kernel_info(linux_buffer, &compat_address);
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if (err == EFI_UNSUPPORTED)
|
||||
/* Kernel is too old to support LINUX_INITRD_MEDIA_GUID, try the deprecated EFI handover
|
||||
* protocol. */
|
||||
return linux_exec_efi_handover(
|
||||
image,
|
||||
parent,
|
||||
cmdline,
|
||||
cmdline_len,
|
||||
linux_buffer,
|
||||
@ -126,46 +135,36 @@ EFI_STATUS linux_exec(
|
||||
initrd_length);
|
||||
#endif
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
/* sanity check */
|
||||
assert(kernel_size_of_image >= linux_length);
|
||||
return log_error_status_stall(err, u"Bad kernel image: %r", err);
|
||||
|
||||
/* Linux kernel complains if it's not loaded at a properly aligned memory address. The correct alignment
|
||||
is provided by Linux as the SegmentAlignment in the PeOptionalHeader. Additionally the kernel needs to
|
||||
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);
|
||||
_cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL;
|
||||
err = load_image(parent, linux_buffer, linux_length, &kernel_image);
|
||||
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);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
return log_error_status_stall(err, u"Error registering initrd: %r", err);
|
||||
|
||||
/* call the kernel */
|
||||
return kernel_entry(loaded_image_handle, ST);
|
||||
err = BS->StartImage(kernel_image, NULL, NULL);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
@ -4,12 +4,12 @@
|
||||
#include <efi.h>
|
||||
|
||||
EFI_STATUS linux_exec(
|
||||
EFI_HANDLE image,
|
||||
EFI_HANDLE parent,
|
||||
const char *cmdline, UINTN cmdline_len,
|
||||
const void *linux_buffer, UINTN linux_length,
|
||||
const void *initrd_buffer, UINTN initrd_length);
|
||||
EFI_STATUS linux_exec_efi_handover(
|
||||
EFI_HANDLE image,
|
||||
EFI_HANDLE parent,
|
||||
const char *cmdline, UINTN cmdline_len,
|
||||
const void *linux_buffer, UINTN linux_length,
|
||||
const void *initrd_buffer, UINTN initrd_length);
|
||||
|
@ -99,10 +99,10 @@ assert_cc(sizeof(BootParams) == 4096);
|
||||
# define __regparm0__
|
||||
#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));
|
||||
|
||||
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);
|
||||
|
||||
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. */
|
||||
|
||||
handover_f handover = (handover_f) kernel;
|
||||
handover(image, ST, params);
|
||||
handover(parent, ST, params);
|
||||
}
|
||||
|
||||
EFI_STATUS linux_exec_efi_handover(
|
||||
EFI_HANDLE image,
|
||||
EFI_HANDLE parent,
|
||||
const char *cmdline, UINTN cmdline_len,
|
||||
const void *linux_buffer, UINTN linux_length,
|
||||
const void *initrd_buffer, UINTN initrd_length) {
|
||||
|
||||
assert(image);
|
||||
assert(parent);
|
||||
assert(cmdline || cmdline_len == 0);
|
||||
assert(linux_buffer);
|
||||
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->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;
|
||||
}
|
||||
|
@ -309,41 +309,34 @@ typedef struct tdEFI_TCG2_PROTOCOL {
|
||||
{0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68} }
|
||||
|
||||
/* UEFI Platform Initialization (Vol2: DXE) */
|
||||
#ifndef SECURITY_PROTOCOL_GUID
|
||||
#ifndef EFI_SECURITY_ARCH_PROTOCOL_GUID
|
||||
|
||||
#define SECURITY_PROTOCOL_GUID \
|
||||
&(const EFI_GUID) { 0xa46423e3, 0x4617, 0x49f1, { 0xb9, 0xff, 0xd1, 0xbf, 0xa9, 0x11, 0x58, 0x39 } }
|
||||
#define SECURITY_PROTOCOL2_GUID \
|
||||
&(const EFI_GUID) { 0x94ab2f58, 0x1438, 0x4ef1, { 0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }
|
||||
#define EFI_SECURITY_ARCH_PROTOCOL_GUID \
|
||||
{ 0xa46423e3, 0x4617, 0x49f1, { 0xb9, 0xff, 0xd1, 0xbf, 0xa9, 0x11, 0x58, 0x39 } }
|
||||
#define EFI_SECURITY2_ARCH_PROTOCOL_GUID \
|
||||
{ 0x94ab2f58, 0x1438, 0x4ef1, { 0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }
|
||||
|
||||
struct _EFI_SECURITY2_PROTOCOL;
|
||||
struct _EFI_SECURITY_PROTOCOL;
|
||||
struct _EFI_DEVICE_PATH_PROTOCOL;
|
||||
typedef struct EFI_SECURITY_ARCH_PROTOCOL EFI_SECURITY_ARCH_PROTOCOL;
|
||||
typedef struct EFI_SECURITY2_ARCH_PROTOCOL EFI_SECURITY2_ARCH_PROTOCOL;
|
||||
|
||||
typedef struct _EFI_SECURITY2_PROTOCOL EFI_SECURITY2_PROTOCOL;
|
||||
typedef struct _EFI_SECURITY_PROTOCOL EFI_SECURITY_PROTOCOL;
|
||||
typedef struct _EFI_DEVICE_PATH_PROTOCOL EFI_DEVICE_PATH_PROTOCOL;
|
||||
typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE)(
|
||||
const EFI_SECURITY_ARCH_PROTOCOL *This,
|
||||
uint32_t AuthenticationStatus,
|
||||
const EFI_DEVICE_PATH *File);
|
||||
|
||||
typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE) (
|
||||
const EFI_SECURITY_PROTOCOL *This,
|
||||
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_ARCH_PROTOCOL {
|
||||
EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState;
|
||||
};
|
||||
|
||||
struct _EFI_SECURITY_PROTOCOL {
|
||||
EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState;
|
||||
typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION)(
|
||||
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
|
||||
|
@ -215,23 +215,15 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const
|
||||
return 0;
|
||||
}
|
||||
|
||||
EFI_STATUS pe_kernel_info(
|
||||
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;
|
||||
|
||||
EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
|
||||
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))
|
||||
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))
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
@ -239,21 +231,17 @@ EFI_STATUS pe_kernel_info(
|
||||
if (pe->OptionalHeader.MajorImageVersion < 1)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
uint32_t entry_address = pe->OptionalHeader.AddressOfEntryPoint;
|
||||
|
||||
/* Look for a compat entry point. */
|
||||
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;
|
||||
if (pe->FileHeader.Machine == TARGET_MACHINE_TYPE) {
|
||||
*ret_compat_address = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
*ret_entry_point_address = entry_address;
|
||||
if (ret_size_of_image)
|
||||
*ret_size_of_image = pe->OptionalHeader.SizeOfImage;
|
||||
if (ret_section_alignment)
|
||||
*ret_section_alignment = pe->OptionalHeader.SectionAlignment;
|
||||
uint32_t compat_address = get_compatibility_entry_address(dos, pe);
|
||||
if (compat_address == 0)
|
||||
/* Image type not supported and no compat entry found. */
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
*ret_compat_address = compat_address;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,4 @@ EFI_STATUS pe_file_locate_sections(
|
||||
UINTN *offsets,
|
||||
UINTN *sizes);
|
||||
|
||||
EFI_STATUS pe_kernel_info(
|
||||
const void *base,
|
||||
uint32_t *ret_entry_point_address,
|
||||
uint32_t *ret_size_of_image,
|
||||
uint32_t *ret_section_alignment);
|
||||
EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "missing_efi.h"
|
||||
#include "util.h"
|
||||
#include "secure-boot.h"
|
||||
#include "shim.h"
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
/* Handle to the original authenticator for security1 protocol */
|
||||
static EFI_SECURITY_FILE_AUTHENTICATION_STATE esfas = NULL;
|
||||
|
||||
/* Handle to the original authenticator for security2 protocol */
|
||||
static EFI_SECURITY2_FILE_AUTHENTICATION es2fa = NULL;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
static EFIAPI EFI_STATUS security2_hook(
|
||||
const SecurityOverride *this,
|
||||
const EFI_DEVICE_PATH *device_path,
|
||||
void *file_buffer,
|
||||
UINTN file_size,
|
||||
BOOLEAN boot_policy) {
|
||||
|
||||
assert(this);
|
||||
/* device_path and file_buffer may be NULL */
|
||||
|
||||
/* 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;
|
||||
assert(this->hook == security2_hook);
|
||||
|
||||
if (shim_validate(file_buffer, file_size))
|
||||
return EFI_SUCCESS;
|
||||
|
||||
return err;
|
||||
return this->original_security2->FileAuthentication(
|
||||
this->original_security2, device_path, file_buffer, file_size, boot_policy);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform both shim/MOK and platform Secure Boot authentication. This function loads
|
||||
* the file and performs shim/MOK authentication first simply to avoid double loads
|
||||
* of Linux kernels, which are much more likely to be shim/MOK-signed than platform-signed,
|
||||
* 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) {
|
||||
static EFIAPI EFI_STATUS security_hook(
|
||||
const SecurityOverride *this,
|
||||
uint32_t authentication_status,
|
||||
const EFI_DEVICE_PATH *device_path) {
|
||||
|
||||
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->hook == security_hook);
|
||||
|
||||
if (!device_path_const)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (!device_path)
|
||||
return this->original_security->FileAuthenticationState(
|
||||
this->original_security, authentication_status, device_path);
|
||||
|
||||
EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) device_path_const;
|
||||
err = BS->LocateDevicePath(&FileSystemProtocol, &dp, &h);
|
||||
EFI_HANDLE device_handle;
|
||||
EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) device_path;
|
||||
err = BS->LocateDevicePath(&FileSystemProtocol, &dp, &device_handle);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
_cleanup_(file_closep) EFI_FILE *root = NULL;
|
||||
err = open_volume(h, &root);
|
||||
err = open_volume(device_handle, &root);
|
||||
if (err != EFI_SUCCESS)
|
||||
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)
|
||||
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)
|
||||
return err;
|
||||
|
||||
if (shim_validate(file_buffer, file_size))
|
||||
return EFI_SUCCESS;
|
||||
|
||||
/* Try using the platform's native policy.... */
|
||||
return esfas(this, authentication_status, device_path_const);
|
||||
return this->original_security->FileAuthenticationState(
|
||||
this->original_security, authentication_status, device_path);
|
||||
}
|
||||
|
||||
EFI_STATUS security_policy_install(void) {
|
||||
EFI_SECURITY_PROTOCOL *security_protocol;
|
||||
EFI_SECURITY2_PROTOCOL *security2_protocol = NULL;
|
||||
EFI_STATUS err;
|
||||
EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image) {
|
||||
assert(device_path);
|
||||
assert(ret_image);
|
||||
|
||||
/* Already Installed */
|
||||
if (esfas)
|
||||
return EFI_ALREADY_STARTED;
|
||||
bool have_shim = shim_loaded();
|
||||
|
||||
/*
|
||||
* Don't bother with status here. The call is allowed
|
||||
* to fail, since SECURITY2 was introduced in PI 1.2.1.
|
||||
* Use security2_protocol == NULL as indicator.
|
||||
*/
|
||||
BS->LocateProtocol((EFI_GUID*) SECURITY_PROTOCOL2_GUID, NULL, (void**) &security2_protocol);
|
||||
SecurityOverride security_override = {
|
||||
.hook = security_hook,
|
||||
}, security2_override = {
|
||||
.hook = security2_hook,
|
||||
};
|
||||
|
||||
err = BS->LocateProtocol((EFI_GUID*) SECURITY_PROTOCOL_GUID, NULL, (void**) &security_protocol);
|
||||
/* This one is mandatory, so there's a serious problem */
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
if (have_shim)
|
||||
install_security_override(&security_override, &security2_override);
|
||||
|
||||
esfas = security_protocol->FileAuthenticationState;
|
||||
security_protocol->FileAuthenticationState = security_policy_authentication;
|
||||
EFI_STATUS ret = BS->LoadImage(
|
||||
/*BootPolicy=*/false, parent, (EFI_DEVICE_PATH *) device_path, NULL, 0, ret_image);
|
||||
|
||||
if (security2_protocol) {
|
||||
es2fa = security2_protocol->FileAuthentication;
|
||||
security2_protocol->FileAuthentication = security2_policy_authentication;
|
||||
}
|
||||
if (have_shim)
|
||||
uninstall_security_override(&security_override, &security2_override);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
return ret;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool shim_loaded(void);
|
||||
|
||||
EFI_STATUS security_policy_install(void);
|
||||
EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image);
|
||||
|
@ -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(initrd_base), initrd_size);
|
||||
graphics_mode(false);
|
||||
return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
|
||||
return err;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user