Blame SOURCES/0061-Also-use-a-config-table-to-mirror-mok-variables.patch

d1e1c8
From fecc2dfb8e408526221091923d9345796b8e294e Mon Sep 17 00:00:00 2001
d1e1c8
From: Peter Jones <pjones@redhat.com>
d1e1c8
Date: Thu, 23 Jul 2020 22:09:03 -0400
d1e1c8
Subject: [PATCH 61/62] Also use a config table to mirror mok variables.
d1e1c8
d1e1c8
Everything was going just fine until I made a vendor_db with 17kB of
d1e1c8
sha256 sums in it.  And then the same source tree that had worked fine
d1e1c8
without that threw errors and failed all over the place.  I wrote some
d1e1c8
code to diagnose the problem, and of course it was a failure in
d1e1c8
mirroring MokList to MokListRT.
d1e1c8
d1e1c8
As Patrick noted in 741c61abba7, some systems have obnoxiously low
d1e1c8
amounts of variable storage available:
d1e1c8
d1e1c8
mok.c:550:import_mok_state() BS+RT variable info:
d1e1c8
		     MaximumVariableStorageSize:0x000000000000DFE4
d1e1c8
		     RemainingVariableStorageSize:0x000000000000D21C
d1e1c8
		     MaximumVariableSize:0x0000000000001FC4
d1e1c8
d1e1c8
The most annoying part is that on at least this edk2 build,
d1e1c8
SetVariable() /does actually appear to set the variable/, but it returns
d1e1c8
EFI_INVALID_PARAMETER.  I'm not planning on relying on that behavior.
d1e1c8
d1e1c8
So... yeah, the largest *volatile* (i.e. RAM only) variable this edk2
d1e1c8
build will let you create is less than two pages.  It's only got 7.9G
d1e1c8
free, so I guess it's feeling like space is a little tight.
d1e1c8
d1e1c8
We're also not quite preserving that return code well enough for his
d1e1c8
workaround to work.
d1e1c8
d1e1c8
New plan.  We try to create variables the normal way, but we don't
d1e1c8
consider not having enough space to be fatal.  In that case, we create
d1e1c8
an EFI_SECURITY_LIST with one sha256sum in it, with a value of all 0,
d1e1c8
and try to add that so we're sure there's /something/ there that's
d1e1c8
innocuous.  On systems where the first SetVariable() /
d1e1c8
QueryVariableInfo() lied to us, the correct variable should be there,
d1e1c8
otherwise the one with the zero-hash will be.
d1e1c8
d1e1c8
We then also build a config table to hold this info and install that.
d1e1c8
d1e1c8
The config table is a packed array of this struct:
d1e1c8
d1e1c8
struct mok_variable_config_entry {
d1e1c8
       CHAR8 name[256];
d1e1c8
       UINT64 data_size;
d1e1c8
       UINT8 data[];
d1e1c8
};
d1e1c8
d1e1c8
There will be N+1 entries, and the last entry is all 0 for name and
d1e1c8
data_size.  The total allocation size will always be a multiple of 4096.
d1e1c8
In the typical RHEL 7.9 case that means it'll be around 5 pages.
d1e1c8
d1e1c8
It's installed with this guid:
d1e1c8
d1e1c8
c451ed2b-9694-45d3-baba-ed9f8988a389
d1e1c8
d1e1c8
Anything that can go wrong will.
d1e1c8
d1e1c8
Signed-off-by: Peter Jones <pjones@redhat.com>
d1e1c8
Upstream: not yet, I don't want people to read this before Wednesday.
d1e1c8
Signed-off-by: Peter Jones <pjones@redhat.com>
d1e1c8
---
d1e1c8
 lib/guid.c     |   2 +
d1e1c8
 mok.c          | 150 ++++++++++++++++++++++++++++++++++++++++++++-----
d1e1c8
 include/guid.h |   2 +
d1e1c8
 3 files changed, 140 insertions(+), 14 deletions(-)
d1e1c8
d1e1c8
diff --git a/lib/guid.c b/lib/guid.c
d1e1c8
index 57c02fbeecd..99ff400a0ab 100644
d1e1c8
--- a/lib/guid.c
d1e1c8
+++ b/lib/guid.c
d1e1c8
@@ -36,4 +36,6 @@ EFI_GUID EFI_SECURE_BOOT_DB_GUID =  { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc,
d1e1c8
 EFI_GUID EFI_SIMPLE_FILE_SYSTEM_GUID = SIMPLE_FILE_SYSTEM_PROTOCOL;
d1e1c8
 EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD1, 0xBF, 0xA9, 0x11, 0x58, 0x39 } };
d1e1c8
 EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } };
d1e1c8
+
d1e1c8
 EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } };
d1e1c8
+EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} };
d1e1c8
diff --git a/mok.c b/mok.c
d1e1c8
index e69857f3c37..4e141fb21fc 100644
d1e1c8
--- a/mok.c
d1e1c8
+++ b/mok.c
d1e1c8
@@ -68,6 +68,7 @@ struct mok_state_variable {
d1e1c8
 	CHAR16 *name;
d1e1c8
 	char *name8;
d1e1c8
 	CHAR16 *rtname;
d1e1c8
+	char *rtname8;
d1e1c8
 	EFI_GUID *guid;
d1e1c8
 
d1e1c8
 	UINT8 *data;
d1e1c8
@@ -121,6 +122,7 @@ struct mok_state_variable mok_state_variables[] = {
d1e1c8
 	{.name = L"MokList",
d1e1c8
 	 .name8 = "MokList",
d1e1c8
 	 .rtname = L"MokListRT",
d1e1c8
+	 .rtname8 = "MokListRT",
d1e1c8
 	 .guid = &SHIM_LOCK_GUID,
d1e1c8
 	 .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
d1e1c8
 		     EFI_VARIABLE_NON_VOLATILE,
d1e1c8
@@ -133,12 +135,14 @@ struct mok_state_variable mok_state_variables[] = {
d1e1c8
 	 .build_cert_size = &build_cert_size,
d1e1c8
 #endif /* defined(ENABLE_SHIM_CERT) */
d1e1c8
 	 .flags = MOK_MIRROR_KEYDB |
d1e1c8
+		  MOK_MIRROR_DELETE_FIRST |
d1e1c8
 		  MOK_VARIABLE_LOG,
d1e1c8
 	 .pcr = 14,
d1e1c8
 	},
d1e1c8
 	{.name = L"MokListX",
d1e1c8
 	 .name8 = "MokListX",
d1e1c8
 	 .rtname = L"MokListXRT",
d1e1c8
+	 .rtname8 = "MokListXRT",
d1e1c8
 	 .guid = &SHIM_LOCK_GUID,
d1e1c8
 	 .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
d1e1c8
 		     EFI_VARIABLE_NON_VOLATILE,
d1e1c8
@@ -147,12 +151,14 @@ struct mok_state_variable mok_state_variables[] = {
d1e1c8
 	 .addend = &vendor_deauthorized,
d1e1c8
 	 .addend_size = &vendor_deauthorized_size,
d1e1c8
 	 .flags = MOK_MIRROR_KEYDB |
d1e1c8
+		  MOK_MIRROR_DELETE_FIRST |
d1e1c8
 		  MOK_VARIABLE_LOG,
d1e1c8
 	 .pcr = 14,
d1e1c8
 	},
d1e1c8
 	{.name = L"MokSBState",
d1e1c8
 	 .name8 = "MokSBState",
d1e1c8
 	 .rtname = L"MokSBStateRT",
d1e1c8
+	 .rtname8 = "MokSBStateRT",
d1e1c8
 	 .guid = &SHIM_LOCK_GUID,
d1e1c8
 	 .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
d1e1c8
 		     EFI_VARIABLE_NON_VOLATILE,
d1e1c8
@@ -166,6 +172,7 @@ struct mok_state_variable mok_state_variables[] = {
d1e1c8
 	{.name = L"MokDBState",
d1e1c8
 	 .name8 = "MokDBState",
d1e1c8
 	 .rtname = L"MokIgnoreDB",
d1e1c8
+	 .rtname8 = "MokIgnoreDB",
d1e1c8
 	 .guid = &SHIM_LOCK_GUID,
d1e1c8
 	 .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
d1e1c8
 		     EFI_VARIABLE_NON_VOLATILE,
d1e1c8
@@ -204,6 +211,7 @@ mirror_one_mok_variable(struct mok_state_variable *v)
d1e1c8
 	 * we're always mirroring the original data, whether this is an efi
d1e1c8
 	 * security database or not
d1e1c8
 	 */
d1e1c8
+	dprint(L"v->name:\"%s\" v->rtname:\"%s\"\n", v->name, v->rtname);
d1e1c8
 	dprint(L"v->data_size:%lu v->data:0x%08llx\n", v->data_size, v->data);
d1e1c8
 	dprint(L"FullDataSize:%lu FullData:0x%08llx\n", FullDataSize, FullData);
d1e1c8
 	if (v->data_size) {
d1e1c8
@@ -299,6 +307,7 @@ mirror_one_mok_variable(struct mok_state_variable *v)
d1e1c8
 		}
d1e1c8
 	}
d1e1c8
 
d1e1c8
+
d1e1c8
 	/*
d1e1c8
 	 * Now we have the full size
d1e1c8
 	 */
d1e1c8
@@ -417,28 +426,72 @@ mirror_one_mok_variable(struct mok_state_variable *v)
d1e1c8
 		       FullDataSize, FullData, p, p-(uintptr_t)FullData);
d1e1c8
 	}
d1e1c8
 
d1e1c8
-	dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
d1e1c8
+	dprint(L"FullDataSize:%lu FullData:0x%016llx p:0x%016llx pos:%lld\n",
d1e1c8
 	       FullDataSize, FullData, p, p-(uintptr_t)FullData);
d1e1c8
 	if (FullDataSize) {
d1e1c8
-		dprint(L"Setting %s with %lu bytes of data\n",
d1e1c8
-		       v->rtname, FullDataSize);
d1e1c8
+		uint32_t attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
d1e1c8
+				 EFI_VARIABLE_RUNTIME_ACCESS;
d1e1c8
+		uint64_t max_storage_sz = 0;
d1e1c8
+		uint64_t remaining_sz = 0;
d1e1c8
+		uint64_t max_var_sz = 0;
d1e1c8
+		UINT8 *tmp = NULL;
d1e1c8
+		UINTN tmpsz = 0;
d1e1c8
+
d1e1c8
+		efi_status = gRT->QueryVariableInfo(attrs, &max_storage_sz,
d1e1c8
+						    &remaining_sz, &max_var_sz);
d1e1c8
+		if (EFI_ERROR(efi_status)) {
d1e1c8
+			perror(L"Could not get variable storage info: %r\n", efi_status);
d1e1c8
+			return efi_status;
d1e1c8
+		}
d1e1c8
+		dprint(L"calling SetVariable(\"%s\", 0x%016llx, 0x%08lx, %lu, 0x%016llx)\n",
d1e1c8
+		       v->rtname, v->guid,
d1e1c8
+		       EFI_VARIABLE_BOOTSERVICE_ACCESS
d1e1c8
+		       | EFI_VARIABLE_RUNTIME_ACCESS,
d1e1c8
+		       FullDataSize, FullData);
d1e1c8
 		efi_status = gRT->SetVariable(v->rtname, v->guid,
d1e1c8
-					      EFI_VARIABLE_BOOTSERVICE_ACCESS |
d1e1c8
-					      EFI_VARIABLE_RUNTIME_ACCESS,
d1e1c8
+					      EFI_VARIABLE_BOOTSERVICE_ACCESS
d1e1c8
+					      | EFI_VARIABLE_RUNTIME_ACCESS,
d1e1c8
 					      FullDataSize, FullData);
d1e1c8
-		if (EFI_ERROR(efi_status)) {
d1e1c8
-			perror(L"Failed to set %s: %r\n",
d1e1c8
-			       v->rtname, efi_status);
d1e1c8
+		if (efi_status == EFI_INVALID_PARAMETER && max_var_sz < FullDataSize) {
d1e1c8
+			/*
d1e1c8
+			 * In this case we're going to try to create a
d1e1c8
+			 * dummy variable so that there's one there.  It
d1e1c8
+			 * may or may not work, because on some firmware
d1e1c8
+			 * builds when the SetVariable call above fails it
d1e1c8
+			 * does actually set the variable(!), so aside from
d1e1c8
+			 * not using the allocation if it doesn't work, we
d1e1c8
+			 * don't care about failures here.
d1e1c8
+			 */
d1e1c8
+			console_print(L"WARNING: Maximum volatile variable size is %lu.\n", max_var_sz);
d1e1c8
+			console_print(L"WARNING: Cannot set %s (%lu bytes)\n", v->rtname, FullDataSize);
d1e1c8
+			perror(L"Failed to set %s: %r\n", v->rtname, efi_status);
d1e1c8
+			efi_status = variable_create_esl(
d1e1c8
+					null_sha256, sizeof(null_sha256),
d1e1c8
+					&EFI_CERT_SHA256_GUID, &SHIM_LOCK_GUID,
d1e1c8
+					&tmp, &tmpsz);
d1e1c8
+			/*
d1e1c8
+			 * from here we don't really care if it works or
d1e1c8
+			 * doens't.
d1e1c8
+			 */
d1e1c8
+			if (!EFI_ERROR(efi_status) && tmp && tmpsz) {
d1e1c8
+				gRT->SetVariable(v->rtname, v->guid,
d1e1c8
+						 EFI_VARIABLE_BOOTSERVICE_ACCESS
d1e1c8
+						 | EFI_VARIABLE_RUNTIME_ACCESS,
d1e1c8
+						 tmpsz, tmp);
d1e1c8
+				FreePool(tmp);
d1e1c8
+			}
d1e1c8
+			efi_status = EFI_INVALID_PARAMETER;
d1e1c8
+		} else if (EFI_ERROR(efi_status)) {
d1e1c8
+			perror(L"Failed to set %s: %r\n", v->rtname, efi_status);
d1e1c8
 		}
d1e1c8
 	}
d1e1c8
-	if (v->data && v->data_size) {
d1e1c8
+	if (v->data && v->data_size && v->data != FullData) {
d1e1c8
 		FreePool(v->data);
d1e1c8
 		v->data = NULL;
d1e1c8
 		v->data_size = 0;
d1e1c8
 	}
d1e1c8
-	if (FullData && FullDataSize) {
d1e1c8
-		FreePool(FullData);
d1e1c8
-	}
d1e1c8
+	v->data = FullData;
d1e1c8
+	v->data_size = FullDataSize;
d1e1c8
 	dprint(L"returning %r\n", efi_status);
d1e1c8
 	return efi_status;
d1e1c8
 }
d1e1c8
@@ -454,8 +507,11 @@ maybe_mirror_one_mok_variable(struct mok_state_variable *v, EFI_STATUS ret)
d1e1c8
 	BOOLEAN present = FALSE;
d1e1c8
 
d1e1c8
 	if (v->rtname) {
d1e1c8
-		if (v->flags & MOK_MIRROR_DELETE_FIRST)
d1e1c8
-			LibDeleteVariable(v->rtname, v->guid);
d1e1c8
+		if (v->flags & MOK_MIRROR_DELETE_FIRST) {
d1e1c8
+			dprint(L"deleting \"%s\"\n", v->rtname);
d1e1c8
+			efi_status = LibDeleteVariable(v->rtname, v->guid);
d1e1c8
+			dprint(L"LibDeleteVariable(\"%s\",...) => %r\n", v->rtname, efi_status);
d1e1c8
+		}
d1e1c8
 
d1e1c8
 		efi_status = mirror_one_mok_variable(v);
d1e1c8
 		if (EFI_ERROR(efi_status)) {
d1e1c8
@@ -505,6 +561,12 @@ maybe_mirror_one_mok_variable(struct mok_state_variable *v, EFI_STATUS ret)
d1e1c8
 	return ret;
d1e1c8
 }
d1e1c8
 
d1e1c8
+struct mok_variable_config_entry {
d1e1c8
+	CHAR8 name[256];
d1e1c8
+	UINT64 data_size;
d1e1c8
+	UINT8 data[];
d1e1c8
+};
d1e1c8
+
d1e1c8
 /*
d1e1c8
  * Verify our non-volatile MoK state.  This checks the variables above
d1e1c8
  * accessable and have valid attributes.  If they don't, it removes
d1e1c8
@@ -527,6 +589,11 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle)
d1e1c8
 	user_insecure_mode = 0;
d1e1c8
 	ignore_db = 0;
d1e1c8
 
d1e1c8
+	UINT64 config_sz = 0;
d1e1c8
+	UINT8 *config_table = NULL;
d1e1c8
+	size_t npages = 0;
d1e1c8
+	struct mok_variable_config_entry config_template;
d1e1c8
+
d1e1c8
 	dprint(L"importing mok state\n");
d1e1c8
 	for (i = 0; mok_state_variables[i].name != NULL; i++) {
d1e1c8
 		struct mok_state_variable *v = &mok_state_variables[i];
d1e1c8
@@ -579,6 +646,61 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle)
d1e1c8
 		}
d1e1c8
 
d1e1c8
 		ret = maybe_mirror_one_mok_variable(v, ret);
d1e1c8
+		if (v->data && v->data_size) {
d1e1c8
+			config_sz += v->data_size;
d1e1c8
+			config_sz += sizeof(config_template);
d1e1c8
+		}
d1e1c8
+	}
d1e1c8
+
d1e1c8
+	/*
d1e1c8
+	 * Alright, so we're going to copy these to a config table.  The
d1e1c8
+	 * table is a packed array of N+1 struct mok_variable_config_entry
d1e1c8
+	 * items, with the last item having all zero's in name and
d1e1c8
+	 * data_size.
d1e1c8
+	 */
d1e1c8
+	if (config_sz) {
d1e1c8
+		config_sz += sizeof(config_template);
d1e1c8
+		npages = ALIGN_VALUE(config_sz, PAGE_SIZE) >> EFI_PAGE_SHIFT;
d1e1c8
+		config_table = NULL;
d1e1c8
+		efi_status = gBS->AllocatePages(AllocateAnyPages,
d1e1c8
+						EfiRuntimeServicesData,
d1e1c8
+						npages,
d1e1c8
+						(EFI_PHYSICAL_ADDRESS *)&config_table);
d1e1c8
+		if (EFI_ERROR(efi_status) || !config_table) {
d1e1c8
+			console_print(L"Allocating %lu pages for mok config table failed: %r\n",
d1e1c8
+				      npages, efi_status);
d1e1c8
+			if (ret != EFI_SECURITY_VIOLATION)
d1e1c8
+				ret = efi_status;
d1e1c8
+			config_table = NULL;
d1e1c8
+		} else {
d1e1c8
+			ZeroMem(config_table, npages << EFI_PAGE_SHIFT);
d1e1c8
+		}
d1e1c8
+	}
d1e1c8
+
d1e1c8
+	UINT8 *p = (UINT8 *)config_table;
d1e1c8
+	for (i = 0; p && mok_state_variables[i].name != NULL; i++) {
d1e1c8
+		struct mok_state_variable *v = &mok_state_variables[i];
d1e1c8
+
d1e1c8
+		ZeroMem(&config_template, sizeof(config_template));
d1e1c8
+		strncpya(config_template.name, (CHAR8 *)v->rtname8, 255);
d1e1c8
+		config_template.name[255] = '\0';
d1e1c8
+
d1e1c8
+		config_template.data_size = v->data_size;
d1e1c8
+
d1e1c8
+		CopyMem(p, &config_template, sizeof(config_template));
d1e1c8
+		p += sizeof(config_template);
d1e1c8
+		CopyMem(p, v->data, v->data_size);
d1e1c8
+		p += v->data_size;
d1e1c8
+	}
d1e1c8
+	if (p) {
d1e1c8
+		ZeroMem(&config_template, sizeof(config_template));
d1e1c8
+		CopyMem(p, &config_template, sizeof(config_template));
d1e1c8
+
d1e1c8
+		efi_status = gBS->InstallConfigurationTable(&MOK_VARIABLE_STORE,
d1e1c8
+							    config_table);
d1e1c8
+		if (EFI_ERROR(efi_status)) {
d1e1c8
+			console_print(L"Couldn't install MoK configuration table\n");
d1e1c8
+		}
d1e1c8
 	}
d1e1c8
 
d1e1c8
 	/*
d1e1c8
diff --git a/include/guid.h b/include/guid.h
d1e1c8
index 81689d6cc1a..91b14d96146 100644
d1e1c8
--- a/include/guid.h
d1e1c8
+++ b/include/guid.h
d1e1c8
@@ -35,4 +35,6 @@ extern EFI_GUID SECURITY_PROTOCOL_GUID;
d1e1c8
 extern EFI_GUID SECURITY2_PROTOCOL_GUID;
d1e1c8
 extern EFI_GUID SHIM_LOCK_GUID;
d1e1c8
 
d1e1c8
+extern EFI_GUID MOK_VARIABLE_STORE;
d1e1c8
+
d1e1c8
 #endif /* SHIM_GUID_H */
d1e1c8
-- 
d1e1c8
2.26.2
d1e1c8