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

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