Blob Blame History Raw
From 65be350308783a8ef537246c8ad0545b4e6ad069 Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Sat, 25 Jul 2020 22:13:57 -0400
Subject: [PATCH 62/62] Implement lennysz's suggestions for MokListRT

Signed-off-by: Peter Jones <pjones@redhat.com>
---
 mok.c             | 726 ++++++++++++++++++++++++++++++++--------------
 shim.c            |   7 +-
 include/PeImage.h |   3 +-
 3 files changed, 515 insertions(+), 221 deletions(-)

diff --git a/mok.c b/mok.c
index 4e141fb21fc..3e6c7e43025 100644
--- a/mok.c
+++ b/mok.c
@@ -7,6 +7,8 @@
 
 #include <stdint.h>
 
+#include "hexdump.h"
+
 /*
  * Check if a variable exists
  */
@@ -25,6 +27,15 @@ static BOOLEAN check_var(CHAR16 *varname)
 	return FALSE;
 }
 
+#define SetVariable(name, guid, attrs, varsz, var) ({			\
+	EFI_STATUS efi_status_;						\
+	efi_status_ = gRT->SetVariable(name, guid, attrs, varsz, var);	\
+	dprint_(L"%a:%d:%a() SetVariable(\"%s\", ... varsz=0x%llx) = %r\n",\
+		 __FILE__, __LINE__, __func__,				\
+		name, varsz, efi_status_);				\
+	efi_status_;							\
+})
+
 /*
  * If the OS has set any of these variables we need to drop into MOK and
  * handle them appropriately
@@ -193,33 +204,296 @@ should_mirror_build_cert(struct mok_state_variable *v)
 
 static const uint8_t null_sha256[32] = { 0, };
 
+typedef UINTN SIZE_T;
+
+static EFI_STATUS
+get_max_var_sz(UINT32 attrs, SIZE_T *max_var_szp)
+{
+	EFI_STATUS efi_status;
+	uint64_t max_storage_sz = 0;
+	uint64_t remaining_sz = 0;
+	uint64_t max_var_sz = 0;
+
+	*max_var_szp = 0;
+	efi_status = gRT->QueryVariableInfo(attrs, &max_storage_sz,
+					    &remaining_sz, &max_var_sz);
+	if (EFI_ERROR(efi_status)) {
+		perror(L"Could not get variable storage info: %r\n", efi_status);
+		return efi_status;
+	}
+
+	/*
+	 * I just don't trust implementations to not be showing static data
+	 * for max_var_sz
+	 */
+	*max_var_szp = (max_var_sz < remaining_sz) ? max_var_sz : remaining_sz;
+	dprint("max_var_sz:%lx remaining_sz:%lx max_storage_sz:%lx\n",
+		max_var_sz, remaining_sz, max_storage_sz);
+	return efi_status;
+}
+
+/*
+ * If any entries fit in < maxsz, and nothing goes wrong, create a variable
+ * of the given name and guid with as many esd entries as possible in it,
+ * and updates *esdp with what would be the next entry (even if makes *esdp
+ * > esl+esl->SignatureListSize), and returns whatever SetVariable()
+ * returns
+ *
+ * If no entries fit (i.e. sizeof(esl) + esl->SignatureSize > maxsz),
+ * returns EFI_BUFFER_TOO_SMALL;
+ */
+static EFI_STATUS
+mirror_one_esl(CHAR16 *name, EFI_GUID *guid, UINT32 attrs,
+	       EFI_SIGNATURE_LIST *esl, EFI_SIGNATURE_DATA *esd,
+	       UINTN *newsz, SIZE_T maxsz)
+{
+	EFI_STATUS efi_status;
+	SIZE_T howmany, varsz = 0, esdsz;
+	UINT8 *var, *data;
+
+	howmany = min((maxsz - sizeof(*esl)) / esl->SignatureSize,
+		      (esl->SignatureListSize - sizeof(*esl)) / esl->SignatureSize);
+	if (howmany < 1) {
+		return EFI_BUFFER_TOO_SMALL;
+	}
+
+	/*
+	 * We always assume esl->SignatureHeaderSize is 0 (and so far,
+	 * that's true as per UEFI 2.8)
+	 */
+	esdsz = howmany * esl->SignatureSize;
+	data = (UINT8 *)esd;
+	dprint(L"Trying to add %lx signatures to \"%s\" of size %lx\n",
+	       howmany, name, esl->SignatureSize);
+
+	/*
+	 * Because of the semantics of variable_create_esl(), the first
+	 * owner guid from the data is not part of esdsz, or the data.
+	 *
+	 * Compensate here.
+	 */
+	efi_status = variable_create_esl(data + sizeof(EFI_GUID),
+					 esdsz - sizeof(EFI_GUID),
+					 &esl->SignatureType,
+					 &esd->SignatureOwner,
+					 &var, &varsz);
+	if (EFI_ERROR(efi_status) || !var || !varsz) {
+		LogError(L"Couldn't allocate %lu bytes for mok variable \"%s\": %r\n",
+			 varsz, var, efi_status);
+		return efi_status;
+	}
+
+	dprint(L"new esl:\n");
+	dhexdumpat(var, varsz, 0);
+
+	efi_status = SetVariable(name, guid, attrs, varsz, var);
+	FreePool(var);
+	if (EFI_ERROR(efi_status)) {
+		LogError(L"Couldn't create mok variable \"%s\": %r\n",
+			 varsz, var, efi_status);
+		return efi_status;
+	}
+
+	*newsz = esdsz;
+
+	return efi_status;
+}
+
+static EFI_STATUS
+mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs,
+	      UINT8 *FullData, SIZE_T FullDataSize, BOOLEAN only_first)
+{
+	EFI_STATUS efi_status = EFI_SUCCESS;
+	SIZE_T max_var_sz;
+
+	if (only_first) {
+		efi_status = get_max_var_sz(attrs, &max_var_sz);
+		if (EFI_ERROR(efi_status)) {
+			LogError(L"Could not get maximum variable size: %r",
+				 efi_status);
+			return efi_status;
+		}
+
+		if (FullDataSize <= max_var_sz) {
+			efi_status = SetVariable(name, guid, attrs,
+						 FullDataSize, FullData);
+			return efi_status;
+		}
+	}
+
+	CHAR16 *namen;
+	CHAR8 *namen8;
+	UINTN namelen, namesz;
+
+	namelen = StrLen(name);
+	namesz = namelen * 2;
+	if (only_first) {
+		namen = name;
+		namen8 = name8;
+	} else {
+		namelen += 18;
+		namesz += 34;
+		namen = AllocateZeroPool(namesz);
+		if (!namen) {
+			LogError(L"Could not allocate %lu bytes", namesz);
+			return EFI_OUT_OF_RESOURCES;
+		}
+		namen8 = AllocateZeroPool(namelen);
+		if (!namen8) {
+			FreePool(namen);
+			LogError(L"Could not allocate %lu bytes", namelen);
+			return EFI_OUT_OF_RESOURCES;
+		}
+	}
+
+	UINTN pos, i;
+	const SIZE_T minsz = sizeof(EFI_SIGNATURE_LIST)
+			     + sizeof(EFI_SIGNATURE_DATA)
+			     + SHA1_DIGEST_SIZE;
+	BOOLEAN did_one = FALSE;
+
+	/*
+	 * Create any entries that can fit.
+	 */
+	if (!only_first) {
+		dprint(L"full data for \"%s\":\n", name);
+		dhexdumpat(FullData, FullDataSize, 0);
+	}
+	EFI_SIGNATURE_LIST *esl = NULL;
+	UINTN esl_end_pos = 0;
+	for (i = 0, pos = 0; FullDataSize - pos >= minsz && FullData; ) {
+		EFI_SIGNATURE_DATA *esd = NULL;
+
+		dprint(L"pos:0x%llx FullDataSize:0x%llx\n", pos, FullDataSize);
+		if (esl == NULL || pos >= esl_end_pos) {
+			UINT8 *nesl = FullData + pos;
+			dprint(L"esl:0x%llx->0x%llx\n", esl, nesl);
+			esl = (EFI_SIGNATURE_LIST *)nesl;
+			esl_end_pos = pos + esl->SignatureListSize;
+			dprint(L"pos:0x%llx->0x%llx\n", pos, pos + sizeof(*esl));
+			pos += sizeof(*esl);
+		}
+		esd = (EFI_SIGNATURE_DATA *)(FullData + pos);
+		if (pos >= FullDataSize)
+			break;
+		if (esl->SignatureListSize == 0 || esl->SignatureSize == 0)
+			break;
+
+		dprint(L"esl[%lu] 0x%llx = {sls=0x%lx, ss=0x%lx} esd:0x%llx\n",
+		       i, esl, esl->SignatureListSize, esl->SignatureSize, esd);
+
+		if (!only_first) {
+			SPrint(namen, namelen, L"%s%lu", name, i);
+			namen[namelen-1] = 0;
+			/* uggggh */
+			UINTN j;
+			for (j = 0; j < namelen; j++)
+				namen8[j] = (CHAR8)(namen[j] & 0xff);
+			namen8[namelen - 1] = 0;
+		}
+
+		/*
+		 * In case max_var_sz is computed dynamically, refresh the
+		 * value here.
+		 */
+		efi_status = get_max_var_sz(attrs, &max_var_sz);
+		if (EFI_ERROR(efi_status)) {
+			LogError(L"Could not get maximum variable size: %r",
+				 efi_status);
+			if (!only_first) {
+				FreePool(namen);
+				FreePool(namen8);
+			}
+			return efi_status;
+		}
+
+		SIZE_T howmany;
+		UINTN adj = 0;
+		howmany = min((max_var_sz - sizeof(*esl)) / esl->SignatureSize,
+			      (esl->SignatureListSize - sizeof(*esl)) / esl->SignatureSize);
+		if (!only_first && i == 0 && howmany >= 1) {
+			adj = howmany * esl->SignatureSize;
+			dprint(L"pos:0x%llx->0x%llx\n", pos, pos + adj);
+			pos += adj;
+			i++;
+			continue;
+
+		}
+
+		efi_status = mirror_one_esl(namen, guid, attrs,
+					    esl, esd, &adj, max_var_sz);
+		dprint(L"esd:0x%llx adj:0x%llx\n", esd, adj);
+		if (EFI_ERROR(efi_status) && efi_status != EFI_BUFFER_TOO_SMALL) {
+			LogError(L"Could not mirror mok variable \"%s\": %r\n",
+				 namen, efi_status);
+			break;
+		}
+
+		if (!EFI_ERROR(efi_status)) {
+			did_one = TRUE;
+			if (only_first)
+				break;
+			dprint(L"pos:0x%llx->0x%llx\n", pos, pos + adj);
+			pos += adj;
+			i++;
+		}
+	}
+
+	if (only_first && !did_one) {
+		/*
+		 * In this case we're going to try to create a
+		 * dummy variable so that there's one there.  It
+		 * may or may not work, because on some firmware
+		 * builds when the SetVariable call above fails it
+		 * does actually set the variable(!), so aside from
+		 * not using the allocation if it doesn't work, we
+		 * don't care about failures here.
+		 */
+		UINT8 *var;
+		UINTN varsz;
+
+		efi_status = variable_create_esl(
+				null_sha256, sizeof(null_sha256),
+				&EFI_CERT_SHA256_GUID, &SHIM_LOCK_GUID,
+				&var, &varsz);
+		/*
+		 * from here we don't really care if it works or
+		 * doesn't.
+		 */
+		if (!EFI_ERROR(efi_status) && var && varsz) {
+			SetVariable(name, guid,
+				    EFI_VARIABLE_BOOTSERVICE_ACCESS
+				    | EFI_VARIABLE_RUNTIME_ACCESS,
+				    varsz, var);
+			FreePool(var);
+		}
+		efi_status = EFI_INVALID_PARAMETER;
+	} else if (EFI_ERROR(efi_status)) {
+		perror(L"Failed to set %s: %r\n", name, efi_status);
+	}
+	return efi_status;
+}
+
+
 static EFI_STATUS nonnull(1)
-mirror_one_mok_variable(struct mok_state_variable *v)
+mirror_one_mok_variable(struct mok_state_variable *v,
+			BOOLEAN only_first)
 {
 	EFI_STATUS efi_status = EFI_SUCCESS;
 	uint8_t *FullData = NULL;
 	size_t FullDataSize = 0;
 	vendor_addend_category_t addend_category = VENDOR_ADDEND_NONE;
 	uint8_t *p = NULL;
-
+	uint32_t attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+			 EFI_VARIABLE_RUNTIME_ACCESS;
+	BOOLEAN measure = v->flags & MOK_VARIABLE_MEASURE;
+	BOOLEAN log = v->flags & MOK_VARIABLE_LOG;
 	size_t build_cert_esl_sz = 0, addend_esl_sz = 0;
+	bool reuse = FALSE;
 
 	if (v->categorize_addend)
 		addend_category = v->categorize_addend(v);
 
-	/*
-	 * we're always mirroring the original data, whether this is an efi
-	 * security database or not
-	 */
-	dprint(L"v->name:\"%s\" v->rtname:\"%s\"\n", v->name, v->rtname);
-	dprint(L"v->data_size:%lu v->data:0x%08llx\n", v->data_size, v->data);
-	dprint(L"FullDataSize:%lu FullData:0x%08llx\n", FullDataSize, FullData);
-	if (v->data_size) {
-		FullDataSize = v->data_size;
-		dprint(L"FullDataSize:%lu FullData:0x%08llx\n",
-		       FullDataSize, FullData);
-	}
-
 	/*
 	 * if it is, there's more data
 	 */
@@ -227,7 +501,7 @@ mirror_one_mok_variable(struct mok_state_variable *v)
 
 		/*
 		 * We're mirroring (into) an efi security database, aka an
-		 * array of efi_signature_list_t.  Its layout goes like:
+		 * array of EFI_SIGNATURE_LIST.  Its layout goes like:
 		 *
 		 *   existing_variable_data
 		 *   existing_variable_data_size
@@ -251,30 +525,7 @@ mirror_one_mok_variable(struct mok_state_variable *v)
 		 */
 
 		/*
-		 * first bit is existing data, but we added that above
-		 */
-
-		/*
-		 * then the build cert if it's there
-		 */
-		if (should_mirror_build_cert(v)) {
-			efi_status = fill_esl(*v->build_cert,
-					      *v->build_cert_size,
-					      &EFI_CERT_TYPE_X509_GUID,
-					      &SHIM_LOCK_GUID,
-					      NULL, &build_cert_esl_sz);
-			if (efi_status != EFI_BUFFER_TOO_SMALL) {
-				perror(L"Could not add built-in cert to %s: %r\n",
-				       v->name, efi_status);
-				return efi_status;
-			}
-			FullDataSize += build_cert_esl_sz;
-			dprint(L"FullDataSize:%lu FullData:0x%08llx\n",
-			       FullDataSize, FullData);
-		}
-
-		/*
-		 * then the addend data
+		 * *first* vendor_db or vendor_cert
 		 */
 		switch (addend_category) {
 		case VENDOR_ADDEND_DB:
@@ -282,7 +533,7 @@ mirror_one_mok_variable(struct mok_state_variable *v)
 			 * if it's an ESL already, we use it wholesale
 			 */
 			FullDataSize += *v->addend_size;
-			dprint(L"FullDataSize:%lu FullData:0x%08llx\n",
+			dprint(L"FullDataSize:%lu FullData:0x%llx\n",
 			       FullDataSize, FullData);
 			break;
 		case VENDOR_ADDEND_X509:
@@ -296,17 +547,51 @@ mirror_one_mok_variable(struct mok_state_variable *v)
 				return efi_status;
 			}
 			FullDataSize += addend_esl_sz;
-			dprint(L"FullDataSize:%lu FullData:0x%08llx\n",
+			dprint(L"FullDataSize:%lu FullData:0x%llx\n",
 				      FullDataSize, FullData);
 			break;
 		default:
 		case VENDOR_ADDEND_NONE:
-			dprint(L"FullDataSize:%lu FullData:0x%08llx\n",
+			dprint(L"FullDataSize:%lu FullData:0x%llx\n",
 				      FullDataSize, FullData);
 			break;
 		}
+
+		/*
+		 * then the build cert if it's there
+		 */
+		if (should_mirror_build_cert(v)) {
+			efi_status = fill_esl(*v->build_cert,
+					      *v->build_cert_size,
+					      &EFI_CERT_TYPE_X509_GUID,
+					      &SHIM_LOCK_GUID,
+					      NULL, &build_cert_esl_sz);
+			if (efi_status != EFI_BUFFER_TOO_SMALL) {
+				perror(L"Could not add built-in cert to %s: %r\n",
+				       v->name, efi_status);
+				return efi_status;
+			}
+			FullDataSize += build_cert_esl_sz;
+			dprint(L"FullDataSize:0x%lx FullData:0x%llx\n",
+			       FullDataSize, FullData);
+		}
+
 	}
 
+	/*
+	 * we're always mirroring the original data, whether this is an efi
+	 * security database or not
+	 */
+	dprint(L"v->name:\"%s\" v->rtname:\"%s\"\n", v->name, v->rtname);
+	dprint(L"v->data_size:%lu v->data:0x%llx\n", v->data_size, v->data);
+	dprint(L"FullDataSize:%lu FullData:0x%llx\n", FullDataSize, FullData);
+	if (v->data_size) {
+		FullDataSize += v->data_size;
+		dprint(L"FullDataSize:%lu FullData:0x%llx\n",
+		       FullDataSize, FullData);
+	}
+	if (v->data_size == FullDataSize)
+		reuse = TRUE;
 
 	/*
 	 * Now we have the full size
@@ -316,38 +601,33 @@ mirror_one_mok_variable(struct mok_state_variable *v)
 		 * allocate the buffer, or use the old one if it's just the
 		 * existing data.
 		 */
-		if (FullDataSize != v->data_size) {
-			dprint(L"FullDataSize:%lu FullData:0x%08llx allocating FullData\n",
+		if (FullDataSize == v->data_size) {
+			FullData = v->data;
+			FullDataSize = v->data_size;
+			p = FullData + FullDataSize;
+			dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
+			       FullDataSize, FullData, p, p-(uintptr_t)FullData);
+			v->data = NULL;
+			v->data_size = 0;
+		} else {
+			dprint(L"FullDataSize:%lu FullData:0x%llx allocating FullData\n",
 			       FullDataSize, FullData);
-			FullData = AllocatePool(FullDataSize);
+			/*
+			 * make sure we've got some zeroes at the end, just
+			 * in case.
+			 */
+			UINTN allocsz = FullDataSize + sizeof(EFI_SIGNATURE_LIST);
+			allocsz = ALIGN_VALUE(allocsz, 4096);
+			FullData = AllocateZeroPool(FullDataSize);
 			if (!FullData) {
-				FreePool(v->data);
-				v->data = NULL;
-				v->data_size = 0;
 				perror(L"Failed to allocate %lu bytes for %s\n",
 				       FullDataSize, v->name);
 				return EFI_OUT_OF_RESOURCES;
 			}
 			p = FullData;
-			dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
-			       FullDataSize, FullData, p, p-(uintptr_t)FullData);
-			if (v->data && v->data_size) {
-				CopyMem(p, v->data, v->data_size);
-				p += v->data_size;
-			}
-			dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
-			       FullDataSize, FullData, p, p-(uintptr_t)FullData);
-		} else {
-			FullData = v->data;
-			FullDataSize = v->data_size;
-			p = FullData + FullDataSize;
-			dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
-			       FullDataSize, FullData, p, p-(uintptr_t)FullData);
-			v->data = NULL;
-			v->data_size = 0;
 		}
 	}
-	dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
+	dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
 	       FullDataSize, FullData, p, p-(uintptr_t)FullData);
 
 	/*
@@ -355,35 +635,13 @@ mirror_one_mok_variable(struct mok_state_variable *v)
 	 */
 	if (v->flags & MOK_MIRROR_KEYDB) {
 		/*
-		 * first bit is existing data, but again, we added that above
+		 * first vendor_cert or vendor_db
 		 */
-
-		/*
-		 * second is the build cert
-		 */
-		dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
-		       FullDataSize, FullData, p, p-(uintptr_t)FullData);
-		if (should_mirror_build_cert(v)) {
-			efi_status = fill_esl(*v->build_cert,
-					      *v->build_cert_size,
-					      &EFI_CERT_TYPE_X509_GUID,
-					      &SHIM_LOCK_GUID,
-					      p, &build_cert_esl_sz);
-			if (EFI_ERROR(efi_status)) {
-				perror(L"Could not add built-in cert to %s: %r\n",
-				       v->name, efi_status);
-				return efi_status;
-			}
-			p += build_cert_esl_sz;
-			dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
-			       FullDataSize, FullData, p, p-(uintptr_t)FullData);
-		}
-
 		switch (addend_category) {
 		case VENDOR_ADDEND_DB:
 			CopyMem(p, *v->addend, *v->addend_size);
 			p += *v->addend_size;
-			dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
+			dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
 			       FullDataSize, FullData, p, p-(uintptr_t)FullData);
 			break;
 		case VENDOR_ADDEND_X509:
@@ -397,16 +655,53 @@ mirror_one_mok_variable(struct mok_state_variable *v)
 				return efi_status;
 			}
 			p += addend_esl_sz;
-			dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
+			dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
 			       FullDataSize, FullData, p, p-(uintptr_t)FullData);
 			break;
 		default:
 		case VENDOR_ADDEND_NONE:
-			dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
+			dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
 			       FullDataSize, FullData, p, p-(uintptr_t)FullData);
 			break;
 		}
+
+		/*
+		 * then is the build cert
+		 */
+		dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
+		       FullDataSize, FullData, p, p-(uintptr_t)FullData);
+		if (should_mirror_build_cert(v)) {
+			efi_status = fill_esl(*v->build_cert,
+					      *v->build_cert_size,
+					      &EFI_CERT_TYPE_X509_GUID,
+					      &SHIM_LOCK_GUID,
+					      p, &build_cert_esl_sz);
+			if (EFI_ERROR(efi_status)) {
+				perror(L"Could not add built-in cert to %s: %r\n",
+				       v->name, efi_status);
+				return efi_status;
+			}
+			p += build_cert_esl_sz;
+			dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
+			       FullDataSize, FullData, p, p-(uintptr_t)FullData);
+		}
 	}
+
+	/*
+	 * last bit is existing data, unless it's the only thing,
+	 * in which case it's already there.
+	 */
+	if (!reuse) {
+		dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
+		       FullDataSize, FullData, p, p-(uintptr_t)FullData);
+		if (v->data && v->data_size) {
+			CopyMem(p, v->data, v->data_size);
+			p += v->data_size;
+		}
+		dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
+		       FullDataSize, FullData, p, p-(uintptr_t)FullData);
+	}
+
 	/*
 	 * We always want to create our key databases, so in this case we
 	 * need a dummy entry
@@ -422,68 +717,55 @@ mirror_one_mok_variable(struct mok_state_variable *v)
 			return efi_status;
 		}
 		p = FullData + FullDataSize;
-		dprint(L"FullDataSize:%lu FullData:0x%08llx p:0x%08llx pos:%lld\n",
+		dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
 		       FullDataSize, FullData, p, p-(uintptr_t)FullData);
 	}
 
-	dprint(L"FullDataSize:%lu FullData:0x%016llx p:0x%016llx pos:%lld\n",
+	dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
 	       FullDataSize, FullData, p, p-(uintptr_t)FullData);
-	if (FullDataSize) {
-		uint32_t attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
-				 EFI_VARIABLE_RUNTIME_ACCESS;
-		uint64_t max_storage_sz = 0;
-		uint64_t remaining_sz = 0;
-		uint64_t max_var_sz = 0;
-		UINT8 *tmp = NULL;
-		UINTN tmpsz = 0;
-
-		efi_status = gRT->QueryVariableInfo(attrs, &max_storage_sz,
-						    &remaining_sz, &max_var_sz);
-		if (EFI_ERROR(efi_status)) {
-			perror(L"Could not get variable storage info: %r\n", efi_status);
-			return efi_status;
-		}
-		dprint(L"calling SetVariable(\"%s\", 0x%016llx, 0x%08lx, %lu, 0x%016llx)\n",
-		       v->rtname, v->guid,
-		       EFI_VARIABLE_BOOTSERVICE_ACCESS
-		       | EFI_VARIABLE_RUNTIME_ACCESS,
-		       FullDataSize, FullData);
-		efi_status = gRT->SetVariable(v->rtname, v->guid,
-					      EFI_VARIABLE_BOOTSERVICE_ACCESS
-					      | EFI_VARIABLE_RUNTIME_ACCESS,
-					      FullDataSize, FullData);
-		if (efi_status == EFI_INVALID_PARAMETER && max_var_sz < FullDataSize) {
+	if (FullDataSize && v->flags & MOK_MIRROR_KEYDB) {
+		dprint(L"calling mirror_mok_db(\"%s\",  datasz=%lu)\n",
+		       v->rtname, FullDataSize);
+		efi_status = mirror_mok_db(v->rtname, (CHAR8 *)v->rtname8, v->guid,
+					   attrs, FullData, FullDataSize,
+					   only_first);
+		dprint(L"mirror_mok_db(\"%s\",  datasz=%lu) returned %r\n",
+		       v->rtname, FullDataSize, efi_status);
+	} else if (FullDataSize && only_first) {
+		efi_status = SetVariable(v->rtname, v->guid, attrs,
+					 FullDataSize, FullData);
+	}
+	if (FullDataSize && only_first) {
+		if (measure) {
 			/*
-			 * In this case we're going to try to create a
-			 * dummy variable so that there's one there.  It
-			 * may or may not work, because on some firmware
-			 * builds when the SetVariable call above fails it
-			 * does actually set the variable(!), so aside from
-			 * not using the allocation if it doesn't work, we
-			 * don't care about failures here.
+			 * Measure this into PCR 7 in the Microsoft format
 			 */
-			console_print(L"WARNING: Maximum volatile variable size is %lu.\n", max_var_sz);
-			console_print(L"WARNING: Cannot set %s (%lu bytes)\n", v->rtname, FullDataSize);
-			perror(L"Failed to set %s: %r\n", v->rtname, efi_status);
-			efi_status = variable_create_esl(
-					null_sha256, sizeof(null_sha256),
-					&EFI_CERT_SHA256_GUID, &SHIM_LOCK_GUID,
-					&tmp, &tmpsz);
+			efi_status = tpm_measure_variable(v->name, *v->guid,
+							  FullDataSize, FullData);
+			if (EFI_ERROR(efi_status)) {
+				dprint(L"tpm_measure_variable(\"%s\",%lu,0x%llx)->%r\n",
+				       v->name, FullDataSize, FullData, efi_status);
+				return efi_status;
+			}
+		}
+
+		if (log) {
 			/*
-			 * from here we don't really care if it works or
-			 * doens't.
+			 * Log this variable into whichever PCR the table
+			 * says.
 			 */
-			if (!EFI_ERROR(efi_status) && tmp && tmpsz) {
-				gRT->SetVariable(v->rtname, v->guid,
-						 EFI_VARIABLE_BOOTSERVICE_ACCESS
-						 | EFI_VARIABLE_RUNTIME_ACCESS,
-						 tmpsz, tmp);
-				FreePool(tmp);
+			EFI_PHYSICAL_ADDRESS datap =
+					(EFI_PHYSICAL_ADDRESS)(UINTN)FullData,
+			efi_status = tpm_log_event(datap, FullDataSize,
+						   v->pcr, (CHAR8 *)v->name8);
+			if (EFI_ERROR(efi_status)) {
+				dprint(L"tpm_log_event(0x%llx, %lu, %lu, \"%s\")->%r\n",
+				       FullData, FullDataSize, v->pcr, v->name,
+				       efi_status);
+				return efi_status;
 			}
-			efi_status = EFI_INVALID_PARAMETER;
-		} else if (EFI_ERROR(efi_status)) {
-			perror(L"Failed to set %s: %r\n", v->rtname, efi_status);
 		}
+
 	}
 	if (v->data && v->data_size && v->data != FullData) {
 		FreePool(v->data);
@@ -501,19 +783,20 @@ mirror_one_mok_variable(struct mok_state_variable *v)
  * EFI_SECURITY_VIOLATION status at the same time.
  */
 static EFI_STATUS nonnull(1)
-maybe_mirror_one_mok_variable(struct mok_state_variable *v, EFI_STATUS ret)
+maybe_mirror_one_mok_variable(struct mok_state_variable *v,
+			      EFI_STATUS ret, BOOLEAN only_first)
 {
 	EFI_STATUS efi_status;
 	BOOLEAN present = FALSE;
 
 	if (v->rtname) {
-		if (v->flags & MOK_MIRROR_DELETE_FIRST) {
+		if (!only_first && (v->flags & MOK_MIRROR_DELETE_FIRST)) {
 			dprint(L"deleting \"%s\"\n", v->rtname);
 			efi_status = LibDeleteVariable(v->rtname, v->guid);
 			dprint(L"LibDeleteVariable(\"%s\",...) => %r\n", v->rtname, efi_status);
 		}
 
-		efi_status = mirror_one_mok_variable(v);
+		efi_status = mirror_one_mok_variable(v, only_first);
 		if (EFI_ERROR(efi_status)) {
 			if (ret != EFI_SECURITY_VIOLATION)
 				ret = efi_status;
@@ -530,34 +813,6 @@ maybe_mirror_one_mok_variable(struct mok_state_variable *v, EFI_STATUS ret)
 		*v->state = v->data[0];
 	}
 
-	if (v->flags & MOK_VARIABLE_MEASURE) {
-		/*
-		 * Measure this into PCR 7 in the Microsoft format
-		 */
-		efi_status = tpm_measure_variable(v->name, *v->guid,
-						  v->data_size,
-						  v->data);
-		if (EFI_ERROR(efi_status)) {
-			if (ret != EFI_SECURITY_VIOLATION)
-				ret = efi_status;
-		}
-	}
-
-	if (v->flags & MOK_VARIABLE_LOG) {
-		/*
-		 * Log this variable into whichever PCR the table
-		 * says.
-		 */
-		EFI_PHYSICAL_ADDRESS datap =
-				(EFI_PHYSICAL_ADDRESS)(UINTN)v->data,
-		efi_status = tpm_log_event(datap, v->data_size,
-					   v->pcr, (CHAR8 *)v->name8);
-		if (EFI_ERROR(efi_status)) {
-			if (ret != EFI_SECURITY_VIOLATION)
-				ret = efi_status;
-		}
-	}
-
 	return ret;
 }
 
@@ -567,6 +822,66 @@ struct mok_variable_config_entry {
 	UINT8 data[];
 };
 
+EFI_STATUS import_one_mok_state(struct mok_state_variable *v,
+				BOOLEAN only_first)
+{
+	EFI_STATUS ret = EFI_SUCCESS;
+	EFI_STATUS efi_status;
+
+	user_insecure_mode = 0;
+	ignore_db = 0;
+
+	UINT32 attrs = 0;
+	BOOLEAN delete = FALSE;
+
+	dprint(L"importing mok state for \"%s\"\n", v->name);
+
+	efi_status = get_variable_attr(v->name,
+				       &v->data, &v->data_size,
+				       *v->guid, &attrs);
+	if (efi_status == EFI_NOT_FOUND) {
+		v->data = NULL;
+		v->data_size = 0;
+	} else if (EFI_ERROR(efi_status)) {
+		perror(L"Could not verify %s: %r\n", v->name,
+		       efi_status);
+		delete = TRUE;
+	} else {
+		if (!(attrs & v->yes_attr)) {
+			perror(L"Variable %s is missing attributes:\n",
+			       v->name);
+			perror(L"  0x%08x should have 0x%08x set.\n",
+			       attrs, v->yes_attr);
+			delete = TRUE;
+		}
+		if (attrs & v->no_attr) {
+			perror(L"Variable %s has incorrect attribute:\n",
+			       v->name);
+			perror(L"  0x%08x should not have 0x%08x set.\n",
+			       attrs, v->no_attr);
+			delete = TRUE;
+		}
+	}
+	if (delete == TRUE) {
+		perror(L"Deleting bad variable %s\n", v->name);
+		efi_status = LibDeleteVariable(v->name, v->guid);
+		if (EFI_ERROR(efi_status)) {
+			perror(L"Failed to erase %s\n", v->name);
+			ret = EFI_SECURITY_VIOLATION;
+		}
+		FreePool(v->data);
+		v->data = NULL;
+		v->data_size = 0;
+	}
+
+	dprint(L"maybe mirroring \"%s\".  original data:\n", v->name);
+	dhexdumpat(v->data, v->data_size, 0);
+
+	ret = maybe_mirror_one_mok_variable(v, ret, only_first);
+	dprint(L"returning %r\n", ret);
+	return ret;
+}
+
 /*
  * Verify our non-volatile MoK state.  This checks the variables above
  * accessable and have valid attributes.  If they don't, it removes
@@ -594,58 +909,22 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle)
 	size_t npages = 0;
 	struct mok_variable_config_entry config_template;
 
-	dprint(L"importing mok state\n");
+	dprint(L"importing minimal mok state variables\n");
 	for (i = 0; mok_state_variables[i].name != NULL; i++) {
 		struct mok_state_variable *v = &mok_state_variables[i];
-		UINT32 attrs = 0;
-		BOOLEAN delete = FALSE;
 
-		efi_status = get_variable_attr(v->name,
-					       &v->data, &v->data_size,
-					       *v->guid, &attrs);
-		dprint(L"maybe mirroring %s\n", v->name);
-		if (efi_status == EFI_NOT_FOUND) {
-			v->data = NULL;
-			v->data_size = 0;
-		} else if (EFI_ERROR(efi_status)) {
-			perror(L"Could not verify %s: %r\n", v->name,
-			       efi_status);
+		efi_status = import_one_mok_state(v, TRUE);
+		if (EFI_ERROR(efi_status)) {
+			dprint(L"import_one_mok_state(ih, \"%s\", TRUE): %r\n",
+			       v->rtname);
 			/*
 			 * don't clobber EFI_SECURITY_VIOLATION from some
 			 * other variable in the list.
 			 */
 			if (ret != EFI_SECURITY_VIOLATION)
 				ret = efi_status;
-			delete = TRUE;
-		} else {
-			if (!(attrs & v->yes_attr)) {
-				perror(L"Variable %s is missing attributes:\n",
-				       v->name);
-				perror(L"  0x%08x should have 0x%08x set.\n",
-				       attrs, v->yes_attr);
-				delete = TRUE;
-			}
-			if (attrs & v->no_attr) {
-				perror(L"Variable %s has incorrect attribute:\n",
-				       v->name);
-				perror(L"  0x%08x should not have 0x%08x set.\n",
-				       attrs, v->no_attr);
-				delete = TRUE;
-			}
-		}
-		if (delete == TRUE) {
-			perror(L"Deleting bad variable %s\n", v->name);
-			efi_status = LibDeleteVariable(v->name, v->guid);
-			if (EFI_ERROR(efi_status)) {
-				perror(L"Failed to erase %s\n", v->name);
-				ret = EFI_SECURITY_VIOLATION;
-			}
-			FreePool(v->data);
-			v->data = NULL;
-			v->data_size = 0;
 		}
 
-		ret = maybe_mirror_one_mok_variable(v, ret);
 		if (v->data && v->data_size) {
 			config_sz += v->data_size;
 			config_sz += sizeof(config_template);
@@ -669,8 +948,6 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle)
 		if (EFI_ERROR(efi_status) || !config_table) {
 			console_print(L"Allocating %lu pages for mok config table failed: %r\n",
 				      npages, efi_status);
-			if (ret != EFI_SECURITY_VIOLATION)
-				ret = efi_status;
 			config_table = NULL;
 		} else {
 			ZeroMem(config_table, npages << EFI_PAGE_SHIFT);
@@ -703,6 +980,16 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle)
 		}
 	}
 
+	/*
+	 * This is really just to make it easy for userland.
+	 */
+	dprint(L"importing full mok state variables\n");
+	for (i = 0; mok_state_variables[i].name != NULL; i++) {
+		struct mok_state_variable *v = &mok_state_variables[i];
+
+		import_one_mok_state(v, FALSE);
+	}
+
 	/*
 	 * Enter MokManager if necessary.  Any actual *changes* here will
 	 * cause MokManager to demand a machine reboot, so this is safe to
@@ -712,6 +999,9 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle)
 	efi_status = check_mok_request(image_handle);
 	dprint(L"mok returned %r\n", efi_status);
 	if (EFI_ERROR(efi_status)) {
+		/*
+		 * don't clobber EFI_SECURITY_VIOLATION
+		 */
 		if (ret != EFI_SECURITY_VIOLATION)
 			ret = efi_status;
 		return ret;
diff --git a/shim.c b/shim.c
index 9248642bd57..1a4d7bb9ded 100644
--- a/shim.c
+++ b/shim.c
@@ -1445,7 +1445,10 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
 					   sha256hash, sha1hash);
 
 		if (EFI_ERROR(efi_status)) {
-			console_error(L"Verification failed", efi_status);
+			if (verbose)
+				console_print(L"Verification failed: %r\n", efi_status);
+			else
+				console_error(L"Verification failed", efi_status);
 			return efi_status;
 		} else {
 			if (verbose)
@@ -2648,7 +2651,6 @@ shim_init(void)
 {
 	EFI_STATUS efi_status;
 
-	setup_verbosity();
 	dprint(L"%a", shim_version);
 
 	/* Set the second stage loader */
@@ -2797,6 +2799,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
 	 * Ensure that gnu-efi functions are available
 	 */
 	InitializeLib(image_handle, systab);
+	setup_verbosity();
 
 	dprint(L"vendor_authorized:0x%08lx vendor_authorized_size:%lu\n",
 		      __FILE__, __LINE__, __func__, vendor_authorized, vendor_authorized_size);
diff --git a/include/PeImage.h b/include/PeImage.h
index a606e8b2a9f..209b96fb8ff 100644
--- a/include/PeImage.h
+++ b/include/PeImage.h
@@ -768,7 +768,8 @@ typedef struct {
 	UINT8           CertData[1];
 } WIN_CERTIFICATE_EFI_PKCS;
 
-#define SHA256_DIGEST_SIZE  32
+#define SHA1_DIGEST_SIZE	20
+#define SHA256_DIGEST_SIZE	32
 #define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002
 
 typedef struct {
-- 
2.26.2