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

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