From 65be350308783a8ef537246c8ad0545b4e6ad069 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sat, 25 Jul 2020 22:13:57 -0400 Subject: [PATCH 62/62] Implement lennysz's suggestions for MokListRT Signed-off-by: Peter Jones --- 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 +#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