summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArd Biesheuvel <ardb@kernel.org>2023-06-29 17:58:18 +0200
committerPeter Jones <pjones@redhat.com>2025-02-11 10:43:37 -0500
commitbb114a3b92a96875dc71e5e4925bedba5c02f958 (patch)
tree1f960d204e30292741b2f7af1142624bd078db47
parent83850cd8df2db60a00b96e7757c6ff9c1d8cccec (diff)
downloadefi-boot-shim-bb114a3b92a96875dc71e5e4925bedba5c02f958.tar.gz
efi-boot-shim-bb114a3b92a96875dc71e5e4925bedba5c02f958.zip
Implement shim image load protocol
Define a new protocol for loading and starting images, encapsulating shim's PE loading facilities and verification/authentication against the same set of certificates that shim_lock::verify() authenticates against. This removes the need for loaders like GRUB to implement their own PE loader in order to be able to invoke loaded images as PE applications, rather than implementing a bespoke OS dependent handover protocol (e.g., invoke Linux via its EFI stub) Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
-rw-r--r--include/guid.h2
-rw-r--r--lib/guid.c2
-rw-r--r--replacements.c11
-rw-r--r--shim.c107
-rw-r--r--shim.h18
5 files changed, 134 insertions, 6 deletions
diff --git a/include/guid.h b/include/guid.h
index 898c4fad..e32dfc07 100644
--- a/include/guid.h
+++ b/include/guid.h
@@ -36,6 +36,8 @@ extern EFI_GUID SECURITY_PROTOCOL_GUID;
extern EFI_GUID SECURITY2_PROTOCOL_GUID;
extern EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
extern EFI_GUID SHIM_LOCK_GUID;
+extern EFI_GUID SHIM_IMAGE_LOADER_GUID;
+extern EFI_GUID SHIM_LOADED_IMAGE_GUID;
extern EFI_GUID MOK_VARIABLE_STORE;
extern EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID;
diff --git a/lib/guid.c b/lib/guid.c
index 6e92cea3..1dc90ca9 100644
--- a/lib/guid.c
+++ b/lib/guid.c
@@ -35,5 +35,7 @@ EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD
EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } };
EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID = { 0xf4560cf6, 0x40ec, 0x4b4a, {0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89} };
EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } };
+EFI_GUID SHIM_IMAGE_LOADER_GUID = {0x1f492041, 0xfadb, 0x4e59, {0x9e, 0x57, 0x7c, 0xaf, 0xe7, 0x3a, 0x55, 0xab } };
+EFI_GUID SHIM_LOADED_IMAGE_GUID = {0x6e6baeb8, 0x7108, 0x4179, {0x94, 0x9d, 0xa3, 0x49, 0x34, 0x15, 0xec, 0x97 } };
EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} };
EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID = {0x77fa9abd, 0x0359, 0x4d32, {0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b} };
diff --git a/replacements.c b/replacements.c
index 469e73aa..d840616f 100644
--- a/replacements.c
+++ b/replacements.c
@@ -148,6 +148,17 @@ do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus,
UINTN ExitDataSize, CHAR16 *ExitData)
{
EFI_STATUS efi_status;
+ SHIM_LOADED_IMAGE *image;
+
+ efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID,
+ (void **)&image);
+ if (!EFI_ERROR(efi_status)) {
+ image->exit_status = ExitStatus;
+ image->exit_data_size = ExitDataSize;
+ image->exit_data = ExitData;
+
+ longjmp(image->longjmp_buf, 1);
+ }
shim_fini();
diff --git a/shim.c b/shim.c
index 14355d27..60b5e720 100644
--- a/shim.c
+++ b/shim.c
@@ -1314,6 +1314,7 @@ init_openssl(void)
}
static SHIM_LOCK shim_lock_interface;
+static SHIM_IMAGE_LOADER shim_image_loader_interface;
static EFI_HANDLE shim_lock_handle;
EFI_STATUS
@@ -1346,10 +1347,12 @@ install_shim_protocols(void)
/*
* Install the protocol
*/
- efi_status = BS->InstallProtocolInterface(&shim_lock_handle,
- &SHIM_LOCK_GUID,
- EFI_NATIVE_INTERFACE,
- &shim_lock_interface);
+ efi_status = BS->InstallMultipleProtocolInterfaces(&shim_lock_handle,
+ &SHIM_LOCK_GUID,
+ &shim_lock_interface,
+ &SHIM_IMAGE_LOADER_GUID,
+ &shim_image_loader_interface,
+ NULL);
if (EFI_ERROR(efi_status)) {
console_error(L"Could not install security protocol",
efi_status);
@@ -1375,8 +1378,12 @@ uninstall_shim_protocols(void)
/*
* If we're back here then clean everything up before exiting
*/
- BS->UninstallProtocolInterface(shim_lock_handle, &SHIM_LOCK_GUID,
- &shim_lock_interface);
+ BS->UninstallMultipleProtocolInterfaces(shim_lock_handle,
+ &SHIM_LOCK_GUID,
+ &shim_lock_interface,
+ &SHIM_IMAGE_LOADER_GUID,
+ &shim_image_loader_interface,
+ NULL);
if (!secure_mode())
return;
@@ -1908,6 +1915,91 @@ devel_egress(devel_egress_action action UNUSED)
#endif
}
+static EFI_STATUS EFIAPI
+shim_load_image(IN BOOLEAN BootPolicy, IN EFI_HANDLE ParentImageHandle,
+ IN EFI_DEVICE_PATH *FilePath, IN VOID *SourceBuffer,
+ IN UINTN SourceSize, OUT EFI_HANDLE *ImageHandle)
+{
+ SHIM_LOADED_IMAGE *image;
+ EFI_STATUS efi_status;
+
+ (void)FilePath;
+
+ if (BootPolicy || !SourceBuffer || !SourceSize)
+ return EFI_UNSUPPORTED;
+
+ image = AllocatePool(sizeof(*image));
+ if (!image)
+ return EFI_OUT_OF_RESOURCES;
+
+ SetMem(image, sizeof(*image), 0);
+
+ image->li.Revision = 0x1000;
+ image->li.ParentHandle = ParentImageHandle;
+ image->li.SystemTable = systab;
+
+ efi_status = handle_image(SourceBuffer, SourceSize, &image->li,
+ &image->entry_point, &image->alloc_address,
+ &image->alloc_pages);
+ if (EFI_ERROR(efi_status))
+ goto free_image;
+
+ *ImageHandle = NULL;
+ efi_status = BS->InstallMultipleProtocolInterfaces(ImageHandle,
+ &SHIM_LOADED_IMAGE_GUID, image,
+ &EFI_LOADED_IMAGE_GUID, &image->li,
+ NULL);
+ if (EFI_ERROR(efi_status))
+ goto free_alloc;
+
+ return EFI_SUCCESS;
+
+free_alloc:
+ BS->FreePages(image->alloc_address, image->alloc_pages);
+free_image:
+ FreePool(image);
+ return efi_status;
+}
+
+static EFI_STATUS EFIAPI
+shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize,
+ OUT CHAR16 **ExitData OPTIONAL)
+{
+ SHIM_LOADED_IMAGE *image;
+ EFI_STATUS efi_status;
+
+ efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID,
+ (void **)&image);
+ if (EFI_ERROR(efi_status) || image->started)
+ return EFI_INVALID_PARAMETER;
+
+ if (!setjmp(image->longjmp_buf)) {
+ image->started = true;
+ efi_status =
+ image->entry_point(ImageHandle, image->li.SystemTable);
+ } else {
+ if (ExitData) {
+ *ExitDataSize = image->exit_data_size;
+ *ExitData = (CHAR16 *)image->exit_data;
+ }
+ efi_status = image->exit_status;
+ }
+
+ //
+ // We only support EFI applications, so we can unload and free the
+ // image unconditionally.
+ //
+ BS->UninstallMultipleProtocolInterfaces(ImageHandle,
+ &EFI_LOADED_IMAGE_GUID, image,
+ &SHIM_LOADED_IMAGE_GUID, &image->li,
+ NULL);
+
+ BS->FreePages(image->alloc_address, image->alloc_pages);
+ FreePool(image);
+
+ return efi_status;
+}
+
EFI_STATUS
efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
{
@@ -1951,6 +2043,9 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
shim_lock_interface.Hash = shim_hash;
shim_lock_interface.Context = shim_read_header;
+ shim_image_loader_interface.LoadImage = shim_load_image;
+ shim_image_loader_interface.StartImage = shim_start_image;
+
systab = passed_systab;
image_handle = global_image_handle = passed_image_handle;
diff --git a/shim.h b/shim.h
index 43fcb191..704e34ea 100644
--- a/shim.h
+++ b/shim.h
@@ -55,6 +55,7 @@
#ifndef SHIM_UNIT_TEST
#include <efi.h>
#include <efilib.h>
+#include <efisetjmp.h>
#undef uefi_call_wrapper
#include <efierr.h>
#include <efiip.h>
@@ -237,6 +238,11 @@ typedef struct _SHIM_LOCK {
EFI_SHIM_LOCK_CONTEXT Context;
} SHIM_LOCK;
+typedef struct _SHIM_IMAGE_LOADER {
+ EFI_IMAGE_LOAD LoadImage;
+ EFI_IMAGE_START StartImage;
+} SHIM_IMAGE_LOADER;
+
extern EFI_STATUS shim_init(void);
extern void shim_fini(void);
extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func,
@@ -326,4 +332,16 @@ verify_buffer (char *data, int datasize,
char *translate_slashes(char *out, const char *str);
+typedef struct {
+ EFI_LOADED_IMAGE li;
+ EFI_IMAGE_ENTRY_POINT entry_point;
+ EFI_PHYSICAL_ADDRESS alloc_address;
+ UINTN alloc_pages;
+ EFI_STATUS exit_status;
+ CONST CHAR16 *exit_data;
+ UINTN exit_data_size;
+ jmp_buf longjmp_buf;
+ BOOLEAN started;
+} SHIM_LOADED_IMAGE;
+
#endif /* SHIM_H_ */