summaryrefslogtreecommitdiff
path: root/sbat.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbat.c')
-rw-r--r--sbat.c385
1 files changed, 326 insertions, 59 deletions
diff --git a/sbat.c b/sbat.c
index a08c5b2a..06956122 100644
--- a/sbat.c
+++ b/sbat.c
@@ -4,18 +4,23 @@
*/
#include "shim.h"
+#include "ssp.h"
+#include "ssp_var_defs.h"
extern struct {
- UINT32 previous_offset;
+ UINT32 automatic_offset;
UINT32 latest_offset;
} sbat_var_payload_header;
+static UINT8 sbat_policy = POLICY_NOTREAD;
+static UINT8 ssp_policy = POLICY_NOTREAD;
+
EFI_STATUS
parse_sbat_section(char *section_base, size_t section_size,
size_t *n_entries,
struct sbat_section_entry ***entriesp)
{
- struct sbat_section_entry *entry = NULL, **entries;
+ struct sbat_section_entry *entry = NULL, **entries = NULL;
EFI_STATUS efi_status = EFI_SUCCESS;
list_t csv, *pos = NULL;
char * end = section_base + section_size - 1;
@@ -67,6 +72,13 @@ parse_sbat_section(char *section_base, size_t section_size,
n++;
}
+ /*
+ * Not necessarily actually an *error* since we eat newlines and
+ * the like; it could actually just be /empty/.
+ */
+ if (n == 0)
+ goto out;
+
strtab = AllocateZeroPool(allocsz);
if (!strtab) {
efi_status = EFI_OUT_OF_RESOURCES;
@@ -101,6 +113,7 @@ parse_sbat_section(char *section_base, size_t section_size,
entry++;
n++;
}
+out:
*entriesp = entries;
*n_entries = n;
err:
@@ -289,7 +302,7 @@ err:
}
EFI_STATUS
-parse_sbat_var(list_t *entries)
+parse_sbat_var(list_t *entries, char *sbat_var_candidate)
{
UINT8 *data = 0;
UINTN datasize;
@@ -301,10 +314,17 @@ parse_sbat_var(list_t *entries)
return EFI_INVALID_PARAMETER;
}
- efi_status = get_variable(SBAT_VAR_NAME, &data, &datasize, SHIM_LOCK_GUID);
- if (EFI_ERROR(efi_status)) {
- LogError(L"Failed to read SBAT variable\n", efi_status);
- return efi_status;
+ if (sbat_var_candidate == NULL) {
+ dprint(L"sbat_var_candidate is NULL, reading variable\n");
+ efi_status = get_variable(SBAT_VAR_NAME, &data, &datasize, SHIM_LOCK_GUID);
+ if (EFI_ERROR(efi_status)) {
+ LogError(L"Failed to read SBAT variable\n", efi_status);
+ return efi_status;
+ }
+ } else {
+ datasize = strlen(sbat_var_candidate);
+ data = AllocatePool(datasize + 1);
+ memcpy(data, sbat_var_candidate, datasize);
}
/*
@@ -312,8 +332,10 @@ parse_sbat_var(list_t *entries)
* allocations, so use that here.
*/
efi_status = parse_sbat_var_data(entries, data, datasize+1);
- if (EFI_ERROR(efi_status))
- return efi_status;
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"parse_sbat_var_data() failed datasize: %d\n", datasize);
+ goto out;
+ }
dprint(L"SBAT variable entries:\n");
list_for_each(pos, entries) {
@@ -324,6 +346,8 @@ parse_sbat_var(list_t *entries)
entry->component_generation, entry->sbat_datestamp);
}
+out:
+ FreePool(data);
return efi_status;
}
@@ -388,6 +412,49 @@ preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes,
return false;
}
+/*
+ * This looks kind of weird, but it comes directly from the MS
+ * documentation:
+ * https://support.microsoft.com/en-us/topic/kb5027455-guidance-for-blocking-vulnerable-windows-boot-managers-522bb851-0a61-44ad-aa94-ad11119c5e91
+ */
+static UINT64
+ssp_ver_to_ull(UINT16 *ver)
+{
+ dprint("major: %u\n", ver[0]);
+ dprint("minor: %u\n", ver[1]);
+ dprint("rev: %u\n", ver[2]);
+ dprint("build: %u\n", ver[3]);
+
+ return ((UINT64)ver[0] << 48)
+ + ((UINT64)ver[1] << 32)
+ + ((UINT64)ver[2] << 16)
+ + ver[3];
+}
+
+static bool
+preserve_ssp_uefi_variable(UINT8 *ssp_applied, UINTN sspversize, UINT32 attributes,
+ uint8_t *ssp_candidate)
+{
+ UINT64 old, new;
+
+ if (ssp_applied == NULL || ssp_candidate == NULL)
+ return false;
+
+ if (sspversize != SSPVER_SIZE)
+ return false;
+
+ if (!check_sbat_var_attributes(attributes))
+ return false;
+
+ old = ssp_ver_to_ull((UINT16 *)ssp_applied);
+ new = ssp_ver_to_ull((UINT16 *)ssp_candidate);
+
+ if (new > old)
+ return false;
+ else
+ return true;
+}
+
static void
clear_sbat_policy()
{
@@ -399,74 +466,80 @@ clear_sbat_policy()
}
EFI_STATUS
-set_sbat_uefi_variable(void)
+set_sbat_uefi_variable(char *sbat_var_automatic, 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;
+ char *sbat_var_candidate = 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 == 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;
+ dprint("Default sbat policy: automatic\n");
+ if (secure_mode()) {
+ sbat_var_candidate = sbat_var_automatic;
+ } else {
+ reset_sbat = true;
+ sbat_var_candidate = SBAT_VAR_ORIGINAL;
+ }
} 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);
- sbat_var = sbat_var_previous;
- clear_sbat_policy();
- break;
+ switch (sbat_policy) {
+ case POLICY_LATEST:
+ dprint("Custom sbat policy: latest\n");
+ sbat_var_candidate = sbat_var_latest;
+ break;
+ case POLICY_AUTOMATIC:
+ dprint("Custom sbat policy: automatic\n");
+ sbat_var_candidate = sbat_var_automatic;
+ break;
+ case POLICY_RESET:
+ if (secure_mode()) {
+ console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n");
+ sbat_var_candidate = sbat_var_automatic;
+ } else {
+ dprint(L"Custom SBAT policy: reset OK\n");
+ reset_sbat = true;
+ sbat_var_candidate = SBAT_VAR_ORIGINAL;
+ }
+ break;
+ default:
+ console_error(L"SBAT policy state %llu is invalid",
+ EFI_INVALID_PARAMETER);
+ if (secure_mode()) {
+ sbat_var_candidate = sbat_var_automatic;
+ } else {
+ reset_sbat = true;
+ sbat_var_candidate = SBAT_VAR_ORIGINAL;
+ }
+ 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.
- *
- * Don't try to set the SbatLevel UEFI variable if attributes match
- * and the signature matches.
*/
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) {
+ } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes,
+ sbat_var_candidate) &&
+ !reset_sbat) {
dprint(L"preserving %s variable it is %d bytes, attributes are 0x%08x\n",
SBAT_VAR_NAME, sbatsize, attributes);
FreePool(sbat);
@@ -474,6 +547,29 @@ set_sbat_uefi_variable(void)
} else {
FreePool(sbat);
+ /*
+ * parse the candidate SbatLevel and check that shim will not
+ * self revoke before writing SbatLevel variable
+ */
+ dprint(L"shim SBAT reparse before application\n");
+ efi_status = parse_sbat_var(&sbat_var, sbat_var_candidate);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"proposed SbatLevel failed to parse\n");
+ return efi_status;
+ }
+
+#ifndef SHIM_UNIT_TEST
+ char *sbat_start = (char *)&_sbat;
+ char *sbat_end = (char *)&_esbat;
+ efi_status = verify_sbat_section(sbat_start, sbat_end - sbat_start - 1);
+ if (EFI_ERROR(efi_status)) {
+ CHAR16 *title = L"New SbatLevel would self-revoke current shim. Not applied";
+ CHAR16 *message = L"Press any key to continue";
+ console_countdown(title, message, 10);
+ return efi_status;
+ }
+#endif /* SHIM_UNIT_TEST */
+
/* delete previous variable */
dprint("%s variable is %d bytes, attributes are 0x%08x\n",
SBAT_VAR_NAME, sbatsize, attributes);
@@ -489,7 +585,7 @@ set_sbat_uefi_variable(void)
/* set variable */
efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, SBAT_VAR_ATTRS,
- strlen(sbat_var), sbat_var);
+ strlen(sbat_var_candidate), sbat_var_candidate);
if (EFI_ERROR(efi_status)) {
dprint(L"%s variable writing failed %r\n", SBAT_VAR_NAME,
efi_status);
@@ -504,18 +600,189 @@ set_sbat_uefi_variable(void)
return efi_status;
}
- if (sbatsize != strlen(sbat_var) ||
- strncmp((const char *)sbat, sbat_var, strlen(sbat_var)) != 0) {
+ if (sbatsize != strlen(sbat_var_candidate) ||
+ strncmp((const char *)sbat, sbat_var_candidate,
+ strlen(sbat_var_candidate)) != 0) {
dprint("new sbatsize is %d, expected %d\n", sbatsize,
- strlen(sbat_var));
+ strlen(sbat_var_candidate));
efi_status = EFI_INVALID_PARAMETER;
} else {
dprint(L"%s variable initialization succeeded\n", SBAT_VAR_NAME);
}
FreePool(sbat);
+ return efi_status;
+}
+
+EFI_STATUS
+set_sbat_uefi_variable_internal(void)
+{
+ char *sbat_var_automatic;
+ char *sbat_var_latest;
+
+ sbat_var_automatic = (char *)&sbat_var_payload_header +
+ sbat_var_payload_header.automatic_offset;
+ sbat_var_latest = (char *)&sbat_var_payload_header +
+ sbat_var_payload_header.latest_offset;
+
+ return set_sbat_uefi_variable(sbat_var_automatic, sbat_var_latest);
+}
+
+static void
+clear_ssp_policy(void)
+{
+ EFI_STATUS efi_status = EFI_SUCCESS;
+
+ efi_status = del_variable(SSP_POLICY, SHIM_LOCK_GUID);
+ if (EFI_ERROR(efi_status))
+ console_error(L"Could not reset SSP Policy", efi_status);
+}
+
+static EFI_STATUS
+clear_ssp_uefi_variables(void)
+{
+ EFI_STATUS efi_status, rc = EFI_SUCCESS;
+
+ /* delete previous variable */
+ dprint("Deleting %s variable.\n", SSPVER_VAR_NAME);
+ efi_status = del_variable(SSPVER_VAR_NAME, SECUREBOOT_EFI_NAMESPACE_GUID);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"%s variable delete failed %r\n", SSPVER_VAR_NAME,
+ efi_status);
+ rc = efi_status;
+ }
+ dprint("Deleting %s variable.\n", SSPSIG_VAR_NAME);
+ efi_status = del_variable(SSPSIG_VAR_NAME, SECUREBOOT_EFI_NAMESPACE_GUID);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"%s variable delete failed %r\n", SSPSIG_VAR_NAME,
+ efi_status);
+ rc = efi_status;
+ }
+
+ return rc;
+}
+
+EFI_STATUS
+set_ssp_uefi_variable(uint8_t *ssp_ver_automatic, uint8_t *ssp_sig_automatic,
+ uint8_t *ssp_ver_latest, uint8_t *ssp_sig_latest)
+{
+ EFI_STATUS efi_status = EFI_SUCCESS;
+ UINT32 attributes = 0;
+
+ UINT8 *sspver = NULL;
+ UINT8 *policyp = NULL;
+ UINTN sspversize = 0;
+ UINTN policysize = 0;
+
+ uint8_t *ssp_ver = NULL;
+ uint8_t *ssp_sig = NULL;
+ bool reset_ssp = false;
+
+ _Static_assert(sizeof(SkuSiPolicyVersion) == SSPVER_SIZE,
+ "SkuSiPolicyVersion has unexpected size");
+ _Static_assert(sizeof(SkuSiPolicyUpdateSigners) == SSPSIG_SIZE,
+ "SkuSiPolicyUpdateSigners has unexpected size");
+
+ if (ssp_policy == POLICY_NOTREAD) {
+ efi_status = get_variable_attr(SSP_POLICY, &policyp,
+ &policysize, SHIM_LOCK_GUID,
+ &attributes);
+ if (!EFI_ERROR(efi_status)) {
+ ssp_policy = *policyp;
+ clear_ssp_policy();
+ }
+ }
+
+ if (EFI_ERROR(efi_status)) {
+ dprint("Default SSP policy: automatic\n");
+ ssp_ver = ssp_ver_automatic;
+ ssp_sig = ssp_sig_automatic;
+ } else {
+ switch (ssp_policy) {
+ case POLICY_LATEST:
+ dprint("Custom SSP policy: latest\n");\
+ ssp_ver = ssp_ver_latest;
+ ssp_sig = ssp_sig_latest;
+ break;
+ case POLICY_AUTOMATIC:
+ dprint("Custom SSP policy: automatic\n");
+ ssp_ver = ssp_ver_automatic;
+ ssp_sig = ssp_sig_automatic;
+ break;
+ case POLICY_RESET:
+ if (secure_mode()) {
+ console_print(L"Cannot reset SSP policy: Secure Boot is enabled.\n");
+ ssp_ver = ssp_ver_automatic;
+ ssp_sig = ssp_sig_automatic;
+ } else {
+ dprint(L"Custom SSP policy: reset OK\n");
+ reset_ssp = true;
+ }
+ break;
+ default:
+ console_error(L"SSP policy state %llu is invalid",
+ EFI_INVALID_PARAMETER);
+ ssp_ver = ssp_ver_automatic;
+ ssp_sig = ssp_sig_automatic;
+ break;
+ }
+ }
+
+ if (!ssp_ver && !ssp_sig && !reset_ssp) {
+ dprint(L"No supplied SSP data, not setting variables\n");
+ return EFI_SUCCESS;
+ }
+
+ efi_status = get_variable_attr(SSPVER_VAR_NAME, &sspver, &sspversize,
+ SECUREBOOT_EFI_NAMESPACE_GUID, &attributes);
+ /*
+ * Since generally we want bootmgr to manage its own revocations,
+ * we are much less agressive trying to set those variables
+ */
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"SkuSiPolicyVersion read failed %r\n", efi_status);
+ } else if (preserve_ssp_uefi_variable(sspver, sspversize, attributes, ssp_ver)
+ && !reset_ssp) {
+ FreePool(sspver);
+
+ dprint(L"preserving %s variable it is %d bytes, attributes are 0x%08x\n",
+ SSPVER_VAR_NAME, sspversize, attributes);
+ return EFI_SUCCESS;
+ } else {
+ FreePool(sspver);
+
+ efi_status = clear_ssp_uefi_variables();
+ }
+
+ if (reset_ssp)
+ return efi_status;
+
+ /* set variable */
+ efi_status = set_variable(SSPVER_VAR_NAME, SECUREBOOT_EFI_NAMESPACE_GUID,
+ SSP_VAR_ATTRS, SSPVER_SIZE, ssp_ver);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"%s variable writing failed %r\n", SSPVER_VAR_NAME,
+ efi_status);
+ return efi_status;
+ }
+ dprint("done setting %s variable.\n", SSPSIG_VAR_NAME);
+
+ efi_status = set_variable(SSPSIG_VAR_NAME, SECUREBOOT_EFI_NAMESPACE_GUID,
+ SSP_VAR_ATTRS, SSPSIG_SIZE, ssp_sig);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"%s variable writing failed %r\n", SSPSIG_VAR_NAME,
+ efi_status);
+ return efi_status;
+ }
+ dprint("done setting %s variable.\n", SSPSIG_VAR_NAME);
return efi_status;
}
+EFI_STATUS
+set_ssp_uefi_variable_internal(void)
+{
+ return set_ssp_uefi_variable(NULL, NULL, SkuSiPolicyVersion,
+ SkuSiPolicyUpdateSigners);
+}
// vim:fenc=utf-8:tw=75:noet