summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Setje-Eilers <jan.setjeeilers@oracle.com>2022-11-09 19:37:53 -0800
committerPeter Jones <pjones@redhat.com>2023-12-05 13:20:00 -0500
commitea0f9dfe8ae49ead3204be4c3166b08cc96fad7e (patch)
treec44ce2618578ddccd969b9b7eec6f5f12377d33d
parentdae82f6bd72cf600e5d48046ec674a441d0f49d7 (diff)
downloadefi-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.h4
-rw-r--r--include/sbat_var_defs.h17
-rw-r--r--sbat.c90
-rw-r--r--shim.c96
-rw-r--r--test-sbat.c3
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
diff --git a/sbat.c b/sbat.c
index 4e51f2e8..aebc6074 100644
--- a/sbat.c
+++ b/sbat.c
@@ -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
diff --git a/shim.c b/shim.c
index 84a98cab..12ce10d6 100644
--- a/shim.c
+++ b/shim.c
@@ -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;