Skip to content

Commit

Permalink
Load additional certs from a signed binary
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
esnowberg committed Feb 10, 2022
1 parent 8159f2a commit ac25527
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 1 deletion.
3 changes: 3 additions & 0 deletions globals.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions include/mok.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion mok.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

/*
Expand Down Expand Up @@ -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;
}
}

/*
Expand Down
127 changes: 127 additions & 0 deletions shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions shim.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit ac25527

Please sign in to comment.