diff options
| author | Mate Kukri <mate.kukri@canonical.com> | 2024-04-18 10:58:30 +0100 |
|---|---|---|
| committer | Peter Jones <pjones@redhat.com> | 2025-02-11 10:43:37 -0500 |
| commit | 2bff46034aeefe4b266b6d6dd7d6cd771c1bf4de (patch) | |
| tree | de9ec99cc7275db0d0a15f0a99e6745915838a5b | |
| parent | e43aea89f1fe8aaa996cb7e5b0612b09ce812a45 (diff) | |
| download | efi-boot-shim-2bff46034aeefe4b266b6d6dd7d6cd771c1bf4de.tar.gz efi-boot-shim-2bff46034aeefe4b266b6d6dd7d6cd771c1bf4de.zip | |
loader-proto: Add support for loading files from disk to LoadImage()
Currently the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and EFI_LOAD_FILE2_PROTOCOL
are supported.
Signed-off-by: Mate Kukri <mate.kukri@canonical.com>
| -rw-r--r-- | loader-proto.c | 205 | ||||
| -rw-r--r-- | shim.h | 1 |
2 files changed, 188 insertions, 18 deletions
diff --git a/loader-proto.c b/loader-proto.c index f0df122c..dcfa1a68 100644 --- a/loader-proto.c +++ b/loader-proto.c @@ -3,21 +3,9 @@ * loader-proto.c - shim's loader protocol * * Copyright Red Hat, Inc + * Copyright Canonical, Ltd */ -/* Chemical agents lend themselves to covert use in sabotage against - * which it is exceedingly difficult to visualize any really effective - * defense... I will not dwell upon this use of CBW because, as one - * pursues the possibilities of such covert uses, one discovers that the - * scenarios resemble that in which the components of a nuclear weapon - * are smuggled into New York City and assembled in the basement of the - * Empire State Building. - * In other words, once the possibility is recognized to exist, about - * all that one can do is worry about it. - * -- Dr. Ivan L Bennett, Jr., testifying before the Subcommittee on - * National Security Policy and Scientific Developments, November 20, - * 1969. - */ #include "shim.h" static EFI_SYSTEM_TABLE *systab; @@ -48,6 +36,133 @@ unhook_system_services(void) 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 *FilePath, VOID *SourceBuffer, @@ -55,21 +170,59 @@ shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, { SHIM_LOADED_IMAGE *image; EFI_STATUS efi_status; + buffer_properties_t bprop; - (void)FilePath; - - if (BootPolicy || !SourceBuffer || !SourceSize) + if (BootPolicy) return EFI_UNSUPPORTED; + if (!SourceBuffer || !SourceSize) { + if (try_load_from_sfs(FilePath, &bprop) == EFI_SUCCESS) + ; + else if (try_load_from_lf2(FilePath, &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 = FilePath; + efi_status = BS->LocateDevicePath(&gEfiDevicePathProtocolGuid, + &bprop.dp, + &bprop.hnd); + if (efi_status != EFI_SUCCESS) { + /* can't seem to pull apart this DP */ + bprop.dp = FilePath; + bprop.hnd = NULL; + } + + } + image = AllocatePool(sizeof(*image)); - if (!image) - return EFI_OUT_OF_RESOURCES; + 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; + image->li.FilePath = DuplicateDevicePath(bprop.dp); + image->loaded_image_device_path = DuplicateDevicePath(FilePath); + if (!image->li.FilePath || + !image->loaded_image_device_path) { + efi_status = EFI_OUT_OF_RESOURCES; + goto free_image; + } efi_status = handle_image(SourceBuffer, SourceSize, &image->li, &image->entry_point, &image->alloc_address, @@ -81,16 +234,28 @@ shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, 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; } @@ -133,9 +298,13 @@ shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, 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); + BS->FreePool(image->li.FilePath); + BS->FreePool(image->loaded_image_device_path); FreePool(image); return efi_status; @@ -341,6 +341,7 @@ typedef struct { UINTN exit_data_size; jmp_buf longjmp_buf; BOOLEAN started; + EFI_DEVICE_PATH *loaded_image_device_path; } SHIM_LOADED_IMAGE; #endif /* SHIM_H_ */ |
