summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Snowberg <eric.snowberg@oracle.com>2022-02-01 15:49:51 -0500
committerPeter Jones <pjones@redhat.com>2022-05-17 18:30:52 -0400
commit35d7378d29b9ad6f664df20efc4121e210859e65 (patch)
tree40fe172a4f6049fcc14f495405e42876cd4aed96
parentacfd48f45b9047fc07b0a184feb91ae31aa41a21 (diff)
downloadefi-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.c3
-rw-r--r--include/mok.h3
-rw-r--r--mok.c9
-rw-r--r--shim.c127
-rw-r--r--shim.h3
5 files changed, 144 insertions, 1 deletions
diff --git a/globals.c b/globals.c
index 4a1f432f..30d10630 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 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
diff --git a/mok.c b/mok.c
index f4de8081..94101843 100644
--- a/mok.c
+++ b/mok.c
@@ -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;
+ }
}
/*
diff --git a/shim.c b/shim.c
index 51e72e7e..c4f9461a 100644
--- a/shim.c
+++ b/shim.c
@@ -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.
diff --git a/shim.h b/shim.h
index db264cb7..703c1145 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;