992 lines
30 KiB
Diff
992 lines
30 KiB
Diff
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
|
|
|