From ac25527fac506df1dc2511c083959ca9144e231b Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Tue, 1 Feb 2022 15:49:51 -0500 Subject: [PATCH] Load additional certs from a signed binary Heavily inspired by Matthew Garrett's patch "Allow additional certificates to be loaded from a signed binary". Add support for loading a binary, verifying its signature, and then scanning it for embedded certificates. This is intended to make it possible to decouple shim builds from vendor signatures. In order to add new signatures to shim, an EFI Signature List should be generated and then added to the .db section of a well-formed EFI binary. This binary should then be signed with a key that shim already trusts (either a built-in key, one present in the platform firmware or one present in MOK) and placed in the same directory as shim with a filename starting "shim_certificate" (eg, "shim_certificate_oracle"). Shim will read multiple files and incorporate the signatures from all of them. Note that each section *must* be an EFI Signature List, not a raw certificate. Signed-off-by: Eric Snowberg --- globals.c | 3 ++ include/mok.h | 3 ++ mok.c | 9 +++- shim.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++ shim.h | 3 ++ 5 files changed, 144 insertions(+), 1 deletion(-) diff --git a/globals.c b/globals.c index 4a1f432fd..30d10630e 100644 --- a/globals.c +++ b/globals.c @@ -12,6 +12,9 @@ UINT8 *vendor_authorized = NULL; UINT32 vendor_deauthorized_size = 0; UINT8 *vendor_deauthorized = NULL; +UINT32 user_cert_size; +UINT8 *user_cert; + #if defined(ENABLE_SHIM_CERT) UINT32 build_cert_size; UINT8 *build_cert; diff --git a/include/mok.h b/include/mok.h index 96da397a7..6f99a1059 100644 --- a/include/mok.h +++ b/include/mok.h @@ -59,6 +59,9 @@ struct mok_state_variable { UINT8 **addend; UINT32 *addend_size; + UINT8 **user_cert; + UINT32 *user_cert_size; + /* * build_cert is our build-time cert. Like addend, this is added * to the input variable, as part of the runtime variable, so that diff --git a/mok.c b/mok.c index 930e52a2c..7716a7a46 100644 --- a/mok.c +++ b/mok.c @@ -97,6 +97,8 @@ struct mok_state_variable mok_state_variable_data[] = { .categorize_addend = categorize_authorized, .addend = &vendor_authorized, .addend_size = &vendor_authorized_size, + .user_cert = &user_cert, + .user_cert_size = &user_cert_size, #if defined(ENABLE_SHIM_CERT) .build_cert = &build_cert, .build_cert_size = &build_cert_size, @@ -586,7 +588,8 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"FullDataSize:0x%lx FullData:0x%llx\n", FullDataSize, FullData); } - + if (*v->user_cert_size) + FullDataSize += *v->user_cert_size; } /* @@ -700,6 +703,10 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n", FullDataSize, FullData, p, p-(uintptr_t)FullData); } + if (*v->user_cert_size) { + CopyMem(p, *v->user_cert, *v->user_cert_size); + p += *v->user_cert_size; + } } /* diff --git a/shim.c b/shim.c index 99fc4f87e..0368ab2c5 100644 --- a/shim.c +++ b/shim.c @@ -1326,6 +1326,126 @@ uninstall_shim_protocols(void) #endif } +EFI_STATUS +load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename) +{ + EFI_STATUS efi_status; + EFI_LOADED_IMAGE *li = NULL; + PE_COFF_LOADER_IMAGE_CONTEXT context; + EFI_IMAGE_SECTION_HEADER *Section; + EFI_SIGNATURE_LIST *certlist; + CHAR16 *PathName = NULL; + void *pointer; + UINT32 original; + int datasize; + void *data; + int i; + + efi_status = read_image(image_handle, filename, PathName, + &data, &datasize); + + if (EFI_ERROR(efi_status)) + return efi_status; + + efi_status = verify_image(data, datasize, li, &context); + if (EFI_ERROR(efi_status)) + return efi_status; + + Section = context.FirstSection; + for (i = 0; i < context.NumberOfSections; i++, Section++) { + if (CompareMem(Section->Name, ".db\0\0\0\0\0", 8) == 0) { + original = user_cert_size; + if (Section->SizeOfRawData < sizeof(EFI_SIGNATURE_LIST)) { + continue; + } + pointer = ImageAddress(data, datasize, + Section->PointerToRawData); + if (!pointer) { + continue; + } + certlist = pointer; + user_cert_size += certlist->SignatureListSize;; + user_cert = ReallocatePool(user_cert, original, + user_cert_size); + memcpy(user_cert + original, pointer, + certlist->SignatureListSize); + } + } + FreePool(data); + return EFI_SUCCESS; +} + +/* Read additional certificates from files (after verifying signatures) */ +EFI_STATUS +load_certs(EFI_HANDLE image_handle) +{ + EFI_STATUS efi_status; + EFI_LOADED_IMAGE *li = NULL; + CHAR16 *PathName = NULL; + EFI_FILE *root, *dir; + EFI_FILE_INFO *info; + EFI_HANDLE device; + EFI_FILE_IO_INTERFACE *drive; + UINTN buffersize = 0; + void *buffer = NULL; + + 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"", &PathName); + if (EFI_ERROR(efi_status)) + goto done; + + device = li->DeviceHandle; + efi_status = gBS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID, + (void **)&drive); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to find fs: %r\n", 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, &dir, PathName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to open %s - %r\n", PathName, efi_status); + goto done; + } + + while (1) { + int old = buffersize; + efi_status = dir->Read(dir, &buffersize, buffer); + if (efi_status == EFI_BUFFER_TOO_SMALL) { + buffer = ReallocatePool(buffer, old, buffersize); + continue; + } else if (EFI_ERROR(efi_status)) { + perror(L"Failed to read directory %s - %r\n", PathName, + efi_status); + goto done; + } + + if (buffersize == 0) + goto done; + + info = (EFI_FILE_INFO *)buffer; + if (StrnCaseCmp(info->FileName, L"shim_certificate", 16) == 0) { + load_cert_file(image_handle, info->FileName); + } + } +done: + FreePool(buffer); + FreePool(PathName); + return efi_status; +} + EFI_STATUS shim_init(void) { @@ -1568,6 +1688,13 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) init_openssl(); + if (secure_mode()) { + efi_status = load_certs(global_image_handle); + if (EFI_ERROR(efi_status)) { + LogError(L"Failed to load addon certificates\n"); + } + } + /* * Before we do anything else, validate our non-volatile, * boot-services-only state variables are what we think they are. diff --git a/shim.h b/shim.h index 69442da3f..6fbfb4de0 100644 --- a/shim.h +++ b/shim.h @@ -248,6 +248,9 @@ extern UINT8 *vendor_authorized; extern UINT32 vendor_deauthorized_size; extern UINT8 *vendor_deauthorized; +extern UINT32 user_cert_size; +extern UINT8 *user_cert; + #if defined(ENABLE_SHIM_CERT) extern UINT32 build_cert_size; extern UINT8 *build_cert;