From 3dc8ba8e026cb266b55566ac103ec3da59cf94cb Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Tue, 29 Aug 2023 17:25:42 -0400 Subject: [PATCH] Add configuration option to boot an alternative 2nd stage Add the ability for shim to load an optional configuration file. This new file is called "options.csv". The configuration file is completely optional. If used, it is located in the same directory as the booted shim. The "options.csv" file currently allows a single entry. Other options could be added to it in the future. The first and only entry in the file is the name of the secondary boot loader shim will load. The "options.csv" file is in Unicode LE format. This allows a signed shim to directly load a UKI without the need to rename it to grub. Shim's transitive trust is maintained. If the alternative 2nd stage can not be verified, it will not boot. Signed-off-by: Eric Snowberg --- Makefile | 2 +- include/load-options.h | 1 + load-options.c | 1 + shim.c | 83 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 231437b22..e86ada3ea 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o globals.o mok.o netboot.o cert.o dp.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o +OBJS = shim.o globals.o mok.o netboot.o cert.o dp.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o utils.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer ORIG_SOURCES = shim.c globals.c mok.c netboot.c dp.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o dp.o diff --git a/include/load-options.h b/include/load-options.h index d2bee3bb7..78b1dccea 100644 --- a/include/load-options.h +++ b/include/load-options.h @@ -13,6 +13,7 @@ EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li, EFI_STATUS parse_load_options(EFI_LOADED_IMAGE *li); extern CHAR16 *second_stage; +extern CHAR16 *optional_second_stage; extern void *load_options; extern UINT32 load_options_size; diff --git a/load-options.c b/load-options.c index e7b644714..091cb18b1 100644 --- a/load-options.c +++ b/load-options.c @@ -6,6 +6,7 @@ #include "shim.h" CHAR16 *second_stage; +CHAR16 *optional_second_stage = NULL; void *load_options; UINT32 load_options_size; diff --git a/shim.c b/shim.c index 684566560..20ef5d153 100644 --- a/shim.c +++ b/shim.c @@ -1256,7 +1256,7 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) EFI_STATUS efi_status; EFI_LOADED_IMAGE *li = NULL; - second_stage = DEFAULT_LOADER; + second_stage = (optional_second_stage) ? optional_second_stage : DEFAULT_LOADER; load_options = NULL; load_options_size = 0; @@ -1668,6 +1668,85 @@ load_unbundled_trust(EFI_HANDLE image_handle) return efi_status; } +/* Read optional options file */ +EFI_STATUS +load_shim_options(EFI_HANDLE image_handle) +{ + EFI_STATUS efi_status; + EFI_HANDLE device; + EFI_LOADED_IMAGE *li = NULL; + EFI_FILE_IO_INTERFACE *drive; + EFI_FILE *root; + EFI_FILE_HANDLE ofile; + CHAR16 *PathName = NULL; + CHAR16 *buffer; + UINTN comma0; + UINT64 bs; + + efi_status = gBS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, + (void **)&li); + if (EFI_ERROR(efi_status)) { + perror(L"Unable to init protocol\n"); + return efi_status; + } + + efi_status = generate_path_from_image_path(li, L"options.csv", &PathName); + if (EFI_ERROR(efi_status)) + goto done; + + device = li->DeviceHandle; + + efi_status = BS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID, + (void **) &drive); + if (EFI_ERROR(efi_status)) + goto done; + + efi_status = drive->OpenVolume(drive, &root); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to open fs: %r\n", efi_status); + goto done; + } + + efi_status = root->Open(root, &ofile, PathName, EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(efi_status)) { + if (efi_status != EFI_NOT_FOUND) + perror(L"Failed to open %s - %r\n", PathName, efi_status); + goto done; + } + + efi_status = read_file(ofile, PathName, &buffer, &bs); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to read file\n"); + goto done; + } + + /* + * This file may or may not start with the Unicode byte order marker. + * Since UEFI is defined as LE, this file must also be LE. + * If we find the LE byte order marker, just skip its. + */ + if (*buffer == 0xfeff) + buffer++; + + comma0 = StrCSpn(buffer, L","); + if (comma0 == 0) { + perror(L"Invalid csv file\n"); + goto done; + } + + /* + * Currently the options.csv file allows one entry for the optional + * secondary boot stage, anything afterwards is skipped. + */ + buffer[comma0] = L'\0'; + console_print(L"Optional 2nd stage: %s\n", buffer); + optional_second_stage=buffer; + +done: + FreePool(PathName); + return EFI_SUCCESS; +} + EFI_STATUS shim_init(void) { @@ -1952,6 +2031,8 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) */ (void) del_variable(SHIM_RETAIN_PROTOCOL_VAR_NAME, SHIM_LOCK_GUID); + load_shim_options(image_handle); + efi_status = shim_init(); if (EFI_ERROR(efi_status)) { msg = SHIM_INIT;