diff options
| author | Jan Setje-Eilers <jan.setjeeilers@oracle.com> | 2022-11-09 19:37:53 -0800 |
|---|---|---|
| committer | Peter Jones <pjones@redhat.com> | 2023-12-05 13:20:00 -0500 |
| commit | ea0f9dfe8ae49ead3204be4c3166b08cc96fad7e (patch) | |
| tree | c44ce2618578ddccd969b9b7eec6f5f12377d33d | |
| parent | dae82f6bd72cf600e5d48046ec674a441d0f49d7 (diff) | |
| download | efi-boot-shim-ea0f9dfe8ae49ead3204be4c3166b08cc96fad7e.tar.gz efi-boot-shim-ea0f9dfe8ae49ead3204be4c3166b08cc96fad7e.zip | |
Allow SbatLevel data from external binary
Ingest SBAT Levels from revocations binary thereby allowing level
requirements to be updated independently from shipping a new shim.
Do not automatically apply any revocations from a stock shim at
this point.
Signed-off-by: Jan Setje-Eilers <Jan.SetjeEilers@oracle.com>
| -rw-r--r-- | include/sbat.h | 4 | ||||
| -rw-r--r-- | include/sbat_var_defs.h | 17 | ||||
| -rw-r--r-- | sbat.c | 90 | ||||
| -rw-r--r-- | shim.c | 96 | ||||
| -rw-r--r-- | test-sbat.c | 3 |
5 files changed, 148 insertions, 62 deletions
diff --git a/include/sbat.h b/include/sbat.h index c94c4fba..84f5ef01 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -34,6 +34,7 @@ #define SBAT_POLICY_LATEST 1 #define SBAT_POLICY_PREVIOUS 2 #define SBAT_POLICY_RESET 3 +#define SBAT_POLICY_NOTREAD 255 extern UINTN _sbat, _esbat; @@ -52,7 +53,8 @@ extern list_t sbat_var; EFI_STATUS parse_sbat_var(list_t *entries); void cleanup_sbat_var(list_t *entries); -EFI_STATUS set_sbat_uefi_variable(void); +EFI_STATUS set_sbat_uefi_variable_internal(void); +EFI_STATUS set_sbat_uefi_variable(char *, char *); bool preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes, char *sbar_var); diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index 5b1a764f..2ea98e4e 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -13,11 +13,9 @@ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_ORIGINAL_DATE "\n" #if defined(ENABLE_SHIM_DEVEL) -#define SBAT_VAR_PREVIOUS_DATE "2022020101" -#define SBAT_VAR_PREVIOUS_REVOCATIONS "component,2\n" +#define SBAT_VAR_PREVIOUS_DATE "2021030218" #define SBAT_VAR_PREVIOUS \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ - SBAT_VAR_PREVIOUS_REVOCATIONS + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" #define SBAT_VAR_LATEST_DATE "2022050100" #define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" @@ -26,14 +24,13 @@ SBAT_VAR_LATEST_REVOCATIONS #else /* !ENABLE_SHIM_DEVEL */ /* - * As of 2022-11-16, most folks (including Ubuntu, SUSE, openSUSE) don't have - * a "shim,2" yet, so adding that here would end up unbootable. + * At this point we do not want shim to automatically apply a + * previous revocation unless it is delivered by a separately + * installed signed revocations binary. */ -#define SBAT_VAR_PREVIOUS_DATE "2022052400" -#define SBAT_VAR_PREVIOUS_REVOCATIONS "grub,2\n" +#define SBAT_VAR_PREVIOUS_DATE "2021030218" #define SBAT_VAR_PREVIOUS \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ - SBAT_VAR_PREVIOUS_REVOCATIONS + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" /* * Debian's grub.3 update was broken - some binaries included the SBAT @@ -10,6 +10,8 @@ extern struct { UINT32 latest_offset; } sbat_var_payload_header; +static UINT8 sbat_policy = SBAT_POLICY_NOTREAD; + EFI_STATUS parse_sbat_section(char *section_base, size_t section_size, size_t *n_entries, @@ -407,64 +409,62 @@ clear_sbat_policy() } EFI_STATUS -set_sbat_uefi_variable(void) +set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) { EFI_STATUS efi_status = EFI_SUCCESS; UINT32 attributes = 0; - char *sbat_var_previous; - char *sbat_var_latest; - UINT8 *sbat = NULL; - UINT8 *sbat_policy = NULL; + UINT8 *sbat_policyp = NULL; UINTN sbatsize = 0; UINTN sbat_policysize = 0; char *sbat_var = NULL; bool reset_sbat = false; - sbat_var_previous = (char *)&sbat_var_payload_header + sbat_var_payload_header.previous_offset; - sbat_var_latest = (char *)&sbat_var_payload_header + sbat_var_payload_header.latest_offset; + if (sbat_policy == SBAT_POLICY_NOTREAD) { + efi_status = get_variable_attr(SBAT_POLICY, &sbat_policyp, + &sbat_policysize, SHIM_LOCK_GUID, + &attributes); + if (!EFI_ERROR(efi_status)) { + sbat_policy = *sbat_policyp; + clear_sbat_policy(); + } + } - efi_status = get_variable_attr(SBAT_POLICY, &sbat_policy, - &sbat_policysize, SHIM_LOCK_GUID, - &attributes); if (EFI_ERROR(efi_status)) { dprint("Default sbat policy: previous\n"); sbat_var = sbat_var_previous; } else { - switch (*sbat_policy) { - case SBAT_POLICY_LATEST: - dprint("Custom sbat policy: latest\n"); - sbat_var = sbat_var_latest; - clear_sbat_policy(); - break; - case SBAT_POLICY_PREVIOUS: - dprint("Custom sbat policy: previous\n"); - sbat_var = sbat_var_previous; - break; - case SBAT_POLICY_RESET: - if (secure_mode()) { - console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); - sbat_var = sbat_var_previous; - } else { - dprint(L"Custom SBAT policy: reset OK\n"); - reset_sbat = true; - sbat_var = SBAT_VAR_ORIGINAL; - } - clear_sbat_policy(); - break; - default: - console_error(L"SBAT policy state %llu is invalid", - EFI_INVALID_PARAMETER); + switch (sbat_policy) { + case SBAT_POLICY_LATEST: + dprint("Custom sbat policy: latest\n"); + sbat_var = sbat_var_latest; + break; + case SBAT_POLICY_PREVIOUS: + dprint("Custom sbat policy: previous\n"); + sbat_var = sbat_var_previous; + break; + case SBAT_POLICY_RESET: + if (secure_mode()) { + console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); sbat_var = sbat_var_previous; - clear_sbat_policy(); - break; + } else { + dprint(L"Custom SBAT policy: reset OK\n"); + reset_sbat = true; + sbat_var = SBAT_VAR_ORIGINAL; + } + break; + default: + console_error(L"SBAT policy state %llu is invalid", + EFI_INVALID_PARAMETER); + sbat_var = sbat_var_previous; + break; } } efi_status = get_variable_attr(SBAT_VAR_NAME, &sbat, &sbatsize, - SHIM_LOCK_GUID, &attributes); + SHIM_LOCK_GUID, &attributes); /* * Always set the SbatLevel UEFI variable if it fails to read. * @@ -474,7 +474,7 @@ set_sbat_uefi_variable(void) if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes, sbat_var) - && !reset_sbat) { + && !reset_sbat) { dprint(L"preserving %s variable it is %d bytes, attributes are 0x%08x\n", SBAT_VAR_NAME, sbatsize, attributes); FreePool(sbat); @@ -526,4 +526,18 @@ set_sbat_uefi_variable(void) return efi_status; } +EFI_STATUS +set_sbat_uefi_variable_internal(void) +{ + char *sbat_var_previous; + char *sbat_var_latest; + + sbat_var_previous = (char *)&sbat_var_payload_header + + sbat_var_payload_header.previous_offset; + sbat_var_latest = (char *)&sbat_var_payload_header + + sbat_var_payload_header.latest_offset; + + return set_sbat_uefi_variable(sbat_var_previous, sbat_var_latest); +} + // vim:fenc=utf-8:tw=75:noet @@ -1396,6 +1396,65 @@ uninstall_shim_protocols(void) } EFI_STATUS +load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) +{ + EFI_STATUS efi_status = EFI_SUCCESS; + PE_COFF_LOADER_IMAGE_CONTEXT context; + EFI_IMAGE_SECTION_HEADER *Section; + void *pointer; + int datasize = 0; + void *data = NULL; + unsigned int i; + char *sbat_var_previous = NULL; + char *sbat_var_latest = NULL; + + efi_status = read_image(image_handle, L"revocations.efi", &PathName, + &data, &datasize); + if (EFI_ERROR(efi_status)) + return efi_status; + + efi_status = verify_image(data, datasize, shim_li, &context); + if (EFI_ERROR(efi_status)) { + dprint(L"revocations failed to verify\n"); + return efi_status; + } + dprint(L"verified revocations\n"); + + Section = context.FirstSection; + for (i = 0; i < context.NumberOfSections; i++, Section++) { + dprint(L"checking section %a\n", (char *)Section->Name); + if (CompareMem(Section->Name, ".sbatl", 6) == 0) { + pointer = ImageAddress(data, datasize, + Section->PointerToRawData); + if (!pointer) { + continue; + } + sbat_var_latest = (char *)pointer; + dprint(L"found sbatl\n"); + } + if (CompareMem(Section->Name, ".sbatp", 6) == 0) { + pointer = ImageAddress(data, datasize, + Section->PointerToRawData); + if (!pointer) { + continue; + } + sbat_var_previous = (char *)pointer; + dprint(L"found sbatp\n"); + } + } + + if (sbat_var_latest && sbat_var_previous) { + dprint(L"attempting to update SBAT_LEVEL\n"); + efi_status = set_sbat_uefi_variable(sbat_var_previous, + sbat_var_latest); + } else { + dprint(L"no data for SBAT_LEVEL\n"); + } + FreePool(data); + return efi_status; +} + +EFI_STATUS load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName) { EFI_STATUS efi_status; @@ -1441,9 +1500,12 @@ load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName) return EFI_SUCCESS; } -/* Read additional certificates from files (after verifying signatures) */ +/* + * Read additional certificates and SBAT Level requirements from files + * (after verifying signatures) + */ EFI_STATUS -load_certs(EFI_HANDLE image_handle) +load_unbundled_trust(EFI_HANDLE image_handle) { EFI_STATUS efi_status; EFI_LOADED_IMAGE *li = NULL; @@ -1486,8 +1548,20 @@ load_certs(EFI_HANDLE image_handle) goto done; } - while (1) { + if (!secure_mode()) + goto done; + + /* + * In the event that there are unprocessed revocation additions, they + * could be intended to ban any *new* trust anchors we find here. + * With that in mind, we always want to do a pass of loading + * revocations before we try to add anything new to our allowlist. + */ + load_revocations_file(image_handle, PathName); + + while (true) { UINTN old = buffersize; + efi_status = dir->Read(dir, &buffersize, buffer); if (efi_status == EFI_BUFFER_TOO_SMALL) { if (buffersize == old) { @@ -1499,7 +1573,7 @@ load_certs(EFI_HANDLE image_handle) * size, up to a certain point, until the call succeeds. */ perror(L"Error reading directory %s - non-compliant UEFI driver or firmware!\n", - PathName); + PathName); buffersize = (buffersize < 4) ? 4 : buffersize * 2; if (buffersize > 1024) goto done; @@ -1513,7 +1587,7 @@ load_certs(EFI_HANDLE image_handle) continue; } else if (EFI_ERROR(efi_status)) { perror(L"Failed to read directory %s - %r\n", PathName, - efi_status); + efi_status); goto done; } @@ -1521,7 +1595,7 @@ load_certs(EFI_HANDLE image_handle) if (buffersize == 0 || !info) goto done; - if (StrnCaseCmp(info->FileName, L"shim_certificate", 16) == 0) { + if (StrCaseCmp(info->FileName, L"shim_certificate.efi") == 0) { load_cert_file(image_handle, info->FileName, PathName); } } @@ -1731,7 +1805,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) */ debug_hook(); - efi_status = set_sbat_uefi_variable(); + efi_status = set_sbat_uefi_variable_internal(); if (EFI_ERROR(efi_status) && secure_mode()) { perror(L"%s variable initialization failed\n", SBAT_VAR_NAME); msg = SET_SBAT; @@ -1766,11 +1840,9 @@ 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"); - } + efi_status = load_unbundled_trust(global_image_handle); + if (EFI_ERROR(efi_status)) { + LogError(L"Failed to load addon certificates / sbat level\n"); } /* diff --git a/test-sbat.c b/test-sbat.c index 0ee3d694..f59a1723 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -5,6 +5,7 @@ #ifndef SHIM_UNIT_TEST #define SHIM_UNIT_TEST +#include "sbat_var_defs.h" #endif #include "shim.h" @@ -1132,7 +1133,7 @@ test_sbat_var_asciz(void) UINTN size = sizeof(buf); char expected[] = SBAT_VAR_PREVIOUS; - status = set_sbat_uefi_variable(); + status = set_sbat_uefi_variable(SBAT_VAR_PREVIOUS, SBAT_VAR_PREVIOUS); if (status != EFI_SUCCESS) return -1; |
