diff options
| author | Christian Breunig <christian@breunig.cc> | 2025-07-06 21:59:18 +0200 |
|---|---|---|
| committer | Christian Breunig <christian@breunig.cc> | 2025-07-06 21:59:18 +0200 |
| commit | 02acad285c74015e8120ade2b41d51b39ae66b63 (patch) | |
| tree | 980533ac963ac23bc9e090e3e4212bdb9e225a05 /loader-proto.c | |
| parent | 1c1d50da810e6c49e804a74719c2675b88b033a6 (diff) | |
| parent | 18d98bfb34be583a5fe2987542e4b15e0db9cb61 (diff) | |
| download | efi-boot-shim-02acad285c74015e8120ade2b41d51b39ae66b63.tar.gz efi-boot-shim-02acad285c74015e8120ade2b41d51b39ae66b63.zip | |
Merge tag '16.0' into vyos/current
shim-16.0
What's Changed
* Validate that a supplied vendor cert is not in PEM format by @steve-mcintyre in https://github.com/rhboot/shim/pull/646
* sbat: Add grub.peimage,2 to latest (CVE-2024-2312) by @julian-klode in https://github.com/rhboot/shim/pull/651
* sbat: Also bump latest for grub,4 (and to todays date) by @julian-klode in https://github.com/rhboot/shim/pull/653
* undo change that limits certificate files to a single file by @jsetje in https://github.com/rhboot/shim/pull/659
* shim: don't set second_stage to the empty string by @jjd27 in https://github.com/rhboot/shim/pull/640
* Fix SBAT.md for today's consensus about numbers by @aronowski in https://github.com/rhboot/shim/pull/672
* Update Code of Conduct contact address by @aronowski in https://github.com/rhboot/shim/pull/683
* make-certs: Handle missing OpenSSL installation by @aronowski in https://github.com/rhboot/shim/pull/595
* Update MokVars.txt by @mikebeaton in https://github.com/rhboot/shim/pull/598
* export DEFINES for sub makefile by @bryteise in https://github.com/rhboot/shim/pull/600
* Drop unused EFI_IMAGE_SECURITY_DATABASE_GUID definition by @vittyvk in https://github.com/rhboot/shim/pull/609
* Null-terminate 'arguments' in fallback by @vittyvk in https://github.com/rhboot/shim/pull/611
* Fix "Verifiying" typo in error message by @chrisbainbridge in https://github.com/rhboot/shim/pull/706
* Update Fedora CI targets by @vathpela in https://github.com/rhboot/shim/pull/708
* Force gcc to produce DWARF4 so that gdb can use it by @mikebeaton in https://github.com/rhboot/shim/pull/607
* Minor housekeeping 2024121700 by @vathpela in https://github.com/rhboot/shim/pull/709
* Discard load-options that start with WINDOWS by @Metabolix in https://github.com/rhboot/shim/pull/621
* Fix the issue that the gBS->LoadImage pointer was empty. by @15058718379 in https://github.com/rhboot/shim/pull/703
* shim: Allow data after the end of device path node in load options by @dbnicholson in https://github.com/rhboot/shim/pull/694
* Handle network file not found like disks by @dbnicholson in https://github.com/rhboot/shim/pull/695
* Update gnu-efi submodule for EFI_HTTP_ERROR by @vathpela in https://github.com/rhboot/shim/pull/674
* Increase EFI file alignment by @lumag in https://github.com/rhboot/shim/pull/673
* avoid EFIv2 runtime services on Apple x86 machines by @eduardacatrinei in https://github.com/rhboot/shim/pull/690
* Improve shortcut performance when comparing two boolean expressions by @dennis-tseng99 in https://github.com/rhboot/shim/pull/667
* Provide better error message when MokManager is not found by @rmetrich in https://github.com/rhboot/shim/pull/663
* tpm: Boot with a warning if the event log is full by @kukrimate in https://github.com/rhboot/shim/pull/657
* MokManager: remove redundant logical constraints by @xypron in https://github.com/rhboot/shim/pull/409
* Test import_mok_state() when MokListRT would be bigger than available size by @vathpela in https://github.com/rhboot/shim/pull/417
* test-mok-mirror: minor bug fix by @vathpela in https://github.com/rhboot/shim/pull/715
* Fix file system browser hang when enrolling MOK from disk by @miczyg1 in https://github.com/rhboot/shim/pull/622
* Ignore a minor clang-tidy nit by @vathpela in https://github.com/rhboot/shim/pull/716
* Allow fallback to default loader when encountering errors on network boot by @nathan-omeara in https://github.com/rhboot/shim/pull/666
* test.mk: don't use a temporary random.bin by @vathpela in https://github.com/rhboot/shim/pull/718
* pe: Enhance debug report for update_mem_attrs by @jongwu in https://github.com/rhboot/shim/pull/594
* Multiple certificate handling improvements by @rosslagerwall in https://github.com/rhboot/shim/pull/644
* Generate SbatLevel Metadata from SbatLevel_Variable.txt by @jsetje in https://github.com/rhboot/shim/pull/711
* Apply EKU check with compile option by @dennis-tseng99 in https://github.com/rhboot/shim/pull/664
* Add configuration option to boot an alternative 2nd stage by @esnowberg in https://github.com/rhboot/shim/pull/608
* Loader protocol (with Device Path resolution support) by @kukrimate in https://github.com/rhboot/shim/pull/656
* netboot cleanup for additional files by @jsetje in https://github.com/rhboot/shim/pull/686
* Document how revocations can be delivered by @jsetje in https://github.com/rhboot/shim/pull/722
* post-process-pe: add tests to validate NX compliance by @vathpela in https://github.com/rhboot/shim/pull/705
* regression: CopyMem() in ad8692e copies out of bounds by @jsetje in https://github.com/rhboot/shim/pull/725
* Save the debug and error logs in mok-variables by @vathpela in https://github.com/rhboot/shim/pull/726
* Add features for the Host Security ID program by @vathpela in https://github.com/rhboot/shim/pull/660
* Mirror some more efi variables to mok-variables by @vathpela in https://github.com/rhboot/shim/pull/723
* This adds DXE Services measurements to HSI and uses them for NX by @vathpela in https://github.com/rhboot/shim/pull/724
* Add shim's current NX_COMPAT status to HSIStatus by @vathpela in https://github.com/rhboot/shim/pull/727
* README.tpm: reflect that vendor_db is in fact logged as "vendor_db" by @jsetje in https://github.com/rhboot/shim/pull/728
* Reject HTTP message with duplicate Content-Length header fields by @dennis-tseng99 in https://github.com/rhboot/shim/pull/637
* Disable log saving by @vathpela in https://github.com/rhboot/shim/pull/729
* fallback: don't add new boot order entries backwards by @vathpela in https://github.com/rhboot/shim/pull/730
* Misc fixes... by @vathpela in https://github.com/rhboot/shim/pull/735
* README.tpm: Update MokList entry to MokListRT by @trungams in https://github.com/rhboot/shim/pull/732
* SBAT Level update for February 2025 GRUB CVEs by @jsetje in https://github.com/rhboot/shim/pull/736
New Contributors
* @jjd27 made their first contribution in https://github.com/rhboot/shim/pull/640
* @mikebeaton made their first contribution in https://github.com/rhboot/shim/pull/598
* @bryteise made their first contribution in https://github.com/rhboot/shim/pull/600
* @vittyvk made their first contribution in https://github.com/rhboot/shim/pull/609
* @chrisbainbridge made their first contribution in https://github.com/rhboot/shim/pull/706
* @Metabolix made their first contribution in https://github.com/rhboot/shim/pull/621
* @15058718379 made their first contribution in https://github.com/rhboot/shim/pull/703
* @dbnicholson made their first contribution in https://github.com/rhboot/shim/pull/694
* @lumag made their first contribution in https://github.com/rhboot/shim/pull/673
* @eduardacatrinei made their first contribution in https://github.com/rhboot/shim/pull/690
* @kukrimate made their first contribution in https://github.com/rhboot/shim/pull/657
* @miczyg1 made their first contribution in https://github.com/rhboot/shim/pull/622
* @nathan-omeara made their first contribution in https://github.com/rhboot/shim/pull/666
* @jongwu made their first contribution in https://github.com/rhboot/shim/pull/594
* @rosslagerwall made their first contribution in https://github.com/rhboot/shim/pull/644
* @trungams made their first contribution in https://github.com/rhboot/shim/pull/732
**Full Changelog**: https://github.com/rhboot/shim/compare/15.8...16.0
* tag '16.0': (451 commits)
Update version to 16.0
SBAT Level update for February 2025 GRUB CVEs
README.tpm: Update MokList entry to MokListRT
Make 'make fanalyzer' work again.
simple_dir_filter(): test our 'next' pointer
shim_load_image(): initialize the buffer fully
mirror_mok_db(): Free our mok variable name correctly
mirror_one_mok_variable(): fix a memory leak on TPM log error.
mirror_mok_db(): get rid of an unused variable+allocation
generate_sbat_var_defs: Ensure revlistentry->revocations is initialized.
generate_sbat_var_defs: Fix memory leak on realloc failure and fd leak.
generate_sbat_var_defs: run clang-format on readfile()
SetSecureVariable(): free Cert on failure
Update version to 16.0~rc1
make-archive: some minor housekeeping
makefiles: Make GITTAG swizzle tildes to dashes
fallback: don't add new boot order entries backwards
Disable log saving for now.
Some save_logs() improvements.
reject message with different values in multiple Content-Length header field
...
Diffstat (limited to 'loader-proto.c')
| -rw-r--r-- | loader-proto.c | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/loader-proto.c b/loader-proto.c new file mode 100644 index 00000000..4581664b --- /dev/null +++ b/loader-proto.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * loader-proto.c - shim's loader protocol + * + * Copyright Red Hat, Inc + * Copyright Canonical, Ltd + */ + +#include "shim.h" + +static EFI_SYSTEM_TABLE *systab; + +EFI_SYSTEM_TABLE * +get_active_systab(void) +{ + if (systab) + return systab; + return ST; +} + +static typeof(systab->BootServices->LoadImage) system_load_image; +static typeof(systab->BootServices->StartImage) system_start_image; +static typeof(systab->BootServices->UnloadImage) system_unload_image; +static typeof(systab->BootServices->Exit) system_exit; + +void +unhook_system_services(void) +{ + if (!systab) + return; + + systab->BootServices->LoadImage = system_load_image; + systab->BootServices->StartImage = system_start_image; + systab->BootServices->Exit = system_exit; + systab->BootServices->UnloadImage = system_unload_image; + BS = systab->BootServices; +} + +typedef struct { + EFI_HANDLE hnd; + EFI_DEVICE_PATH *dp; + void *buffer; + size_t size; +} buffer_properties_t; + +static EFI_STATUS +try_load_from_sfs(EFI_DEVICE_PATH *dp, buffer_properties_t *bprop) +{ + EFI_STATUS status = EFI_SUCCESS; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *sfs = NULL; + EFI_FILE_HANDLE root = NULL; + EFI_FILE_HANDLE file = NULL; + UINT64 tmpsz = 0; + + bprop->buffer = NULL; + + /* look for a handle with SFS support from the input DP */ + bprop->dp = dp; + status = BS->LocateDevicePath(&EFI_SIMPLE_FILE_SYSTEM_GUID, &bprop->dp, &bprop->hnd); + if (EFI_ERROR(status)) { + goto out; + } + + /* make sure the remaining DP portion is really a file path */ + if (DevicePathType(bprop->dp) != MEDIA_DEVICE_PATH || + DevicePathSubType(bprop->dp) != MEDIA_FILEPATH_DP) { + status = EFI_LOAD_ERROR; + goto out; + } + + /* find protocol, open the root directory, then open file */ + status = BS->HandleProtocol(bprop->hnd, &EFI_SIMPLE_FILE_SYSTEM_GUID, (void **)&sfs); + if (EFI_ERROR(status)) + goto out; + status = sfs->OpenVolume(sfs, &root); + if (EFI_ERROR(status)) + goto out; + status = root->Open(root, &file, ((FILEPATH_DEVICE_PATH *) bprop->dp)->PathName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(status)) + goto out; + + /* get file size */ + status = file->SetPosition(file, -1ULL); + if (EFI_ERROR(status)) + goto out; + status = file->GetPosition(file, &tmpsz); + if (EFI_ERROR(status)) + goto out; + bprop->size = (size_t)tmpsz; + status = file->SetPosition(file, 0); + if (EFI_ERROR(status)) + goto out; + + /* allocate buffer */ + bprop->buffer = AllocatePool(bprop->size); + if (bprop->buffer == NULL) { + status = EFI_OUT_OF_RESOURCES; + goto out; + } + + /* read file */ + status = file->Read(file, &bprop->size, bprop->buffer); + +out: + if (EFI_ERROR(status) && bprop->buffer) + FreePool(bprop->buffer); + if (file) + file->Close(file); + if (root) + root->Close(root); + return status; +} + + +static EFI_STATUS +try_load_from_lf2(EFI_DEVICE_PATH *dp, buffer_properties_t *bprop) +{ + EFI_STATUS status = EFI_SUCCESS; + EFI_LOAD_FILE2_PROTOCOL *lf2 = NULL; + + bprop->buffer = NULL; + + /* look for a handle with LF2 support from the input DP */ + bprop->dp = dp; + status = BS->LocateDevicePath(&gEfiLoadFile2ProtocolGuid, &bprop->dp, &bprop->hnd); + if (EFI_ERROR(status)) + goto out; + + /* find protocol */ + status = BS->HandleProtocol(bprop->hnd, &gEfiLoadFile2ProtocolGuid, (void **) &lf2); + if (EFI_ERROR(status)) + goto out; + + /* get file size */ + bprop->size = 0; /* this shouldn't be read when Buffer=NULL but better be safe */ + status = lf2->LoadFile(lf2, bprop->dp, /*BootPolicy=*/false, &bprop->size, NULL); + /* + * NOTE: the spec is somewhat ambiguous what is the correct return + * status code when asking for the buffer size with Buffer=NULL. I am + * assuming EFI_SUCCESS and EFI_BUFFER_TOO_SMALL are the only + * reasonable interpretations. + */ + if (EFI_ERROR(status) && status != EFI_BUFFER_TOO_SMALL) { + status = EFI_LOAD_ERROR; + goto out; + } + + /* allocate buffer */ + bprop->buffer = AllocatePool(bprop->size); + if (!bprop->buffer) { + status = EFI_OUT_OF_RESOURCES; + goto out; + } + + /* read file */ + status = lf2->LoadFile(lf2, bprop->dp, /*BootPolicy=*/false, &bprop->size, bprop->buffer); + if (EFI_ERROR(status)) + goto out; + +out: + if (EFI_ERROR(status) && bprop->buffer) + FreePool(bprop->buffer); + return status; +} + +static EFI_STATUS EFIAPI +shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, + EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer, + UINTN SourceSize, EFI_HANDLE *ImageHandle) +{ + SHIM_LOADED_IMAGE *image; + EFI_STATUS efi_status; + buffer_properties_t bprop = { NULL, NULL, NULL, 0 }; + + if (BootPolicy) + return EFI_UNSUPPORTED; + + if (!SourceBuffer || !SourceSize) { + if (!DevicePath) /* Both SourceBuffer and DevicePath are NULL */ + return EFI_NOT_FOUND; + + if (try_load_from_sfs(DevicePath, &bprop) == EFI_SUCCESS) + ; + else if (try_load_from_lf2(DevicePath, &bprop) == EFI_SUCCESS) + ; + else + /* no buffer given and we cannot load from this device */ + return EFI_LOAD_ERROR; + + SourceBuffer = bprop.buffer; + SourceSize = bprop.size; + } else { + bprop.buffer = NULL; + /* + * Even if we are using a buffer, try populating the + * device_handle and file_path fields the best we can + */ + + bprop.dp = DevicePath; + + if (bprop.dp) { + efi_status = BS->LocateDevicePath(&gEfiDevicePathProtocolGuid, + &bprop.dp, + &bprop.hnd); + if (efi_status != EFI_SUCCESS) { + /* can't seem to pull apart this DP */ + bprop.dp = DevicePath; + bprop.hnd = NULL; + } + } + } + + image = AllocatePool(sizeof(*image)); + if (!image) { + efi_status = EFI_OUT_OF_RESOURCES; + goto free_buffer; + } + + SetMem(image, sizeof(*image), 0); + + image->li.Revision = 0x1000; + image->li.ParentHandle = ParentImageHandle; + image->li.SystemTable = systab; + image->li.DeviceHandle = bprop.hnd; + if (bprop.dp) { + image->li.FilePath = DuplicateDevicePath(bprop.dp); + if (!image->li.FilePath) { + efi_status = EFI_OUT_OF_RESOURCES; + goto free_image; + } + } + if (DevicePath) { + image->loaded_image_device_path = DuplicateDevicePath(DevicePath); + if (!image->loaded_image_device_path) { + efi_status = EFI_OUT_OF_RESOURCES; + goto free_image; + } + } + + in_protocol = 1; + efi_status = handle_image(SourceBuffer, SourceSize, &image->li, + &image->entry_point, &image->alloc_address, + &image->alloc_pages); + in_protocol = 0; + 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, + &gEfiLoadedImageDevicePathProtocolGuid, + image->loaded_image_device_path, + NULL); + if (EFI_ERROR(efi_status)) + goto free_alloc; + + if (bprop.buffer) + FreePool(bprop.buffer); + + return EFI_SUCCESS; + +free_alloc: + BS->FreePages(image->alloc_address, image->alloc_pages); +free_image: + if (image->loaded_image_device_path) + FreePool(image->loaded_image_device_path); + if (image->li.FilePath) + FreePool(image->li.FilePath); + FreePool(image); +free_buffer: + if (bprop.buffer) + FreePool(bprop.buffer); + 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); + + /* + * This image didn't come from shim_load_image(), so it must have come + * from something before shim was involved. + */ + if (efi_status == EFI_UNSUPPORTED) + return system_start_image(ImageHandle, ExitDataSize, ExitData); + + 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, + &gEfiLoadedImageDevicePathProtocolGuid, + image->loaded_image_device_path, + NULL); + + BS->FreePages(image->alloc_address, image->alloc_pages); + if (image->li.FilePath) + BS->FreePool(image->li.FilePath); + if (image->loaded_image_device_path) + BS->FreePool(image->loaded_image_device_path); + FreePool(image); + + return efi_status; +} + +static EFI_STATUS EFIAPI +shim_unload_image(EFI_HANDLE ImageHandle) +{ + SHIM_LOADED_IMAGE *image; + EFI_STATUS efi_status; + + efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, + (void **)&image); + + if (efi_status == EFI_UNSUPPORTED) + return system_unload_image(ImageHandle); + + BS->FreePages(image->alloc_address, image->alloc_pages); + FreePool(image); + + return EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI +shim_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 this happens, something above us on the stack of running + * applications called Exit(), and we're getting aborted along with + * it. + */ + if (efi_status == EFI_UNSUPPORTED) { + shim_fini(); + return system_exit(ImageHandle, ExitStatus, ExitDataSize, + ExitData); + } + + if (EFI_ERROR(efi_status)) + return efi_status; + + image->exit_status = ExitStatus; + image->exit_data_size = ExitDataSize; + image->exit_data = ExitData; + + longjmp(image->longjmp_buf, 1); +} + +void +init_image_loader(void) +{ + shim_image_loader_interface.LoadImage = shim_load_image; + shim_image_loader_interface.StartImage = shim_start_image; + shim_image_loader_interface.Exit = shim_exit; + shim_image_loader_interface.UnloadImage = shim_unload_image; +} + +void +hook_system_services(EFI_SYSTEM_TABLE *local_systab) +{ + systab = local_systab; + BS = systab->BootServices; + + /* We need to hook various calls to make this work... */ + + /* + * We need LoadImage() hooked so that we can guarantee everything is + * verified. + */ + system_load_image = systab->BootServices->LoadImage; + systab->BootServices->LoadImage = shim_load_image; + + /* + * We need StartImage() hooked because the system's StartImage() + * doesn't know about our structure layout. + */ + system_start_image = systab->BootServices->StartImage; + systab->BootServices->StartImage = shim_start_image; + + /* + * We need Exit() hooked so that we make sure to use the right jmp_buf + * when an application calls Exit(), but that happens in a separate + * function. + */ + + /* + * We need UnloadImage() to match our LoadImage() + */ + system_unload_image = systab->BootServices->UnloadImage; + systab->BootServices->UnloadImage = shim_unload_image; +} + +void +unhook_exit(void) +{ + systab->BootServices->Exit = system_exit; + BS = systab->BootServices; +} + +void +hook_exit(EFI_SYSTEM_TABLE *local_systab) +{ + systab = local_systab; + BS = local_systab->BootServices; + + /* + * We need to hook Exit() so that we can allow users to quit the + * bootloader and still e.g. start a new one or run an internal + * shell. + */ + system_exit = systab->BootServices->Exit; + systab->BootServices->Exit = shim_exit; +} |
