diff options
| author | Eric Snowberg <eric.snowberg@oracle.com> | 2022-02-01 15:49:51 -0500 |
|---|---|---|
| committer | Peter Jones <pjones@redhat.com> | 2022-05-17 18:30:52 -0400 |
| commit | 35d7378d29b9ad6f664df20efc4121e210859e65 (patch) | |
| tree | 40fe172a4f6049fcc14f495405e42876cd4aed96 | |
| parent | acfd48f45b9047fc07b0a184feb91ae31aa41a21 (diff) | |
| download | efi-boot-shim-35d7378d29b9ad6f664df20efc4121e210859e65.tar.gz efi-boot-shim-35d7378d29b9ad6f664df20efc4121e210859e65.zip | |
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 <eric.snowberg@oracle.com>
| -rw-r--r-- | globals.c | 3 | ||||
| -rw-r--r-- | include/mok.h | 3 | ||||
| -rw-r--r-- | mok.c | 9 | ||||
| -rw-r--r-- | shim.c | 127 | ||||
| -rw-r--r-- | shim.h | 3 |
5 files changed, 144 insertions, 1 deletions
@@ -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 96da397a..6f99a105 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 @@ -98,6 +98,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, @@ -588,7 +590,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; } /* @@ -702,6 +705,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; + } } /* @@ -1392,6 +1392,126 @@ uninstall_shim_protocols(void) } 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) { EFI_STATUS efi_status; @@ -1626,6 +1746,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. @@ -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; |
