mirror of
https://git.proxmox.com/git/efi-boot-shim
synced 2025-06-13 17:40:06 +00:00

Without this, if a Mok variable doesn't exist in Boot Services, it will also not be copied to Runtime, even if we have data to be added to it (vendor cert). This patch makes sure that if we have extra data to append, we still mirror the variable. Signed-off-by: Patrick Uiterwijk <patrick@puiterwijk.org> Upstream-commit-id: 9ab0d796bdc
347 lines
8.6 KiB
C
347 lines
8.6 KiB
C
/*
|
|
* mok.c
|
|
* Copyright 2017 Peter Jones <pjones@redhat.com>
|
|
*
|
|
* Distributed under terms of the GPLv3 license.
|
|
*/
|
|
|
|
#include "shim.h"
|
|
|
|
/*
|
|
* Check if a variable exists
|
|
*/
|
|
static BOOLEAN check_var(CHAR16 *varname)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
UINTN size = sizeof(UINT32);
|
|
UINT32 MokVar;
|
|
UINT32 attributes;
|
|
|
|
efi_status = gRT->GetVariable(varname, &SHIM_LOCK_GUID, &attributes,
|
|
&size, (void *)&MokVar);
|
|
if (!EFI_ERROR(efi_status) || efi_status == EFI_BUFFER_TOO_SMALL)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* If the OS has set any of these variables we need to drop into MOK and
|
|
* handle them appropriately
|
|
*/
|
|
static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
|
|
if (check_var(L"MokNew") || check_var(L"MokSB") ||
|
|
check_var(L"MokPW") || check_var(L"MokAuth") ||
|
|
check_var(L"MokDel") || check_var(L"MokDB") ||
|
|
check_var(L"MokXNew") || check_var(L"MokXDel") ||
|
|
check_var(L"MokXAuth")) {
|
|
efi_status = start_image(image_handle, MOK_MANAGER);
|
|
|
|
if (EFI_ERROR(efi_status)) {
|
|
perror(L"Failed to start MokManager: %r\n", efi_status);
|
|
return efi_status;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* MoK variables that need to have their storage validated.
|
|
*
|
|
* The order here is important, since this is where we measure for the
|
|
* tpm as well.
|
|
*/
|
|
struct mok_state_variable {
|
|
CHAR16 *name;
|
|
char *name8;
|
|
CHAR16 *rtname;
|
|
EFI_GUID *guid;
|
|
UINT8 *data;
|
|
UINTN data_size;
|
|
/*
|
|
* These two are indirect pointers just to make initialization
|
|
* saner...
|
|
*/
|
|
UINT8 **addend_source;
|
|
UINT32 *addend_size;
|
|
UINT32 yes_attr;
|
|
UINT32 no_attr;
|
|
UINT32 flags;
|
|
UINTN pcr;
|
|
UINT8 *state;
|
|
};
|
|
|
|
#define MOK_MIRROR_KEYDB 0x01
|
|
#define MOK_MIRROR_DELETE_FIRST 0x02
|
|
#define MOK_VARIABLE_MEASURE 0x04
|
|
#define MOK_VARIABLE_LOG 0x08
|
|
|
|
struct mok_state_variable mok_state_variables[] = {
|
|
{.name = L"MokList",
|
|
.name8 = "MokList",
|
|
.rtname = L"MokListRT",
|
|
.guid = &SHIM_LOCK_GUID,
|
|
.yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
|
EFI_VARIABLE_NON_VOLATILE,
|
|
.no_attr = EFI_VARIABLE_RUNTIME_ACCESS,
|
|
.addend_source = &vendor_cert,
|
|
.addend_size = &vendor_cert_size,
|
|
.flags = MOK_MIRROR_KEYDB |
|
|
MOK_VARIABLE_LOG,
|
|
.pcr = 14,
|
|
},
|
|
{.name = L"MokListX",
|
|
.name8 = "MokListX",
|
|
.rtname = L"MokListXRT",
|
|
.guid = &SHIM_LOCK_GUID,
|
|
.yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
|
EFI_VARIABLE_NON_VOLATILE,
|
|
.no_attr = EFI_VARIABLE_RUNTIME_ACCESS,
|
|
.flags = MOK_MIRROR_KEYDB |
|
|
MOK_VARIABLE_LOG,
|
|
.pcr = 14,
|
|
},
|
|
{.name = L"MokSBState",
|
|
.name8 = "MokSBState",
|
|
.rtname = L"MokSBStateRT",
|
|
.guid = &SHIM_LOCK_GUID,
|
|
.yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
|
EFI_VARIABLE_NON_VOLATILE,
|
|
.no_attr = EFI_VARIABLE_RUNTIME_ACCESS,
|
|
.flags = MOK_MIRROR_DELETE_FIRST |
|
|
MOK_VARIABLE_MEASURE |
|
|
MOK_VARIABLE_LOG,
|
|
.pcr = 14,
|
|
.state = &user_insecure_mode,
|
|
},
|
|
{.name = L"MokDBState",
|
|
.name8 = "MokDBState",
|
|
.rtname = L"MokIgnoreDB",
|
|
.guid = &SHIM_LOCK_GUID,
|
|
.yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
|
EFI_VARIABLE_NON_VOLATILE,
|
|
.no_attr = EFI_VARIABLE_RUNTIME_ACCESS,
|
|
.state = &ignore_db,
|
|
},
|
|
{ NULL, }
|
|
};
|
|
|
|
static EFI_STATUS mirror_one_mok_variable(struct mok_state_variable *v)
|
|
{
|
|
EFI_STATUS efi_status = EFI_SUCCESS;
|
|
void *FullData = NULL;
|
|
UINTN FullDataSize = 0;
|
|
uint8_t *p = NULL;
|
|
|
|
if ((v->flags & MOK_MIRROR_KEYDB) &&
|
|
v->addend_source && *v->addend_source &&
|
|
v->addend_size && *v->addend_size) {
|
|
EFI_SIGNATURE_LIST *CertList = NULL;
|
|
EFI_SIGNATURE_DATA *CertData = NULL;
|
|
FullDataSize = v->data_size
|
|
+ sizeof (*CertList)
|
|
+ sizeof (EFI_GUID)
|
|
+ *v->addend_size;
|
|
FullData = AllocatePool(FullDataSize);
|
|
if (!FullData) {
|
|
perror(L"Failed to allocate space for MokListRT\n");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
p = FullData;
|
|
|
|
if (!EFI_ERROR(efi_status) && v->data_size > 0) {
|
|
CopyMem(p, v->data, v->data_size);
|
|
p += v->data_size;
|
|
}
|
|
CertList = (EFI_SIGNATURE_LIST *)p;
|
|
p += sizeof (*CertList);
|
|
CertData = (EFI_SIGNATURE_DATA *)p;
|
|
p += sizeof (EFI_GUID);
|
|
|
|
CertList->SignatureType = EFI_CERT_TYPE_X509_GUID;
|
|
CertList->SignatureListSize = *v->addend_size
|
|
+ sizeof (*CertList)
|
|
+ sizeof (*CertData)
|
|
-1;
|
|
CertList->SignatureHeaderSize = 0;
|
|
CertList->SignatureSize = *v->addend_size + sizeof (EFI_GUID);
|
|
|
|
CertData->SignatureOwner = SHIM_LOCK_GUID;
|
|
CopyMem(p, *v->addend_source, *v->addend_size);
|
|
|
|
if (v->data && v->data_size)
|
|
FreePool(v->data);
|
|
v->data = FullData;
|
|
v->data_size = FullDataSize;
|
|
} else {
|
|
FullDataSize = v->data_size;
|
|
FullData = v->data;
|
|
}
|
|
|
|
if (FullDataSize) {
|
|
efi_status = gRT->SetVariable(v->rtname, v->guid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
|
EFI_VARIABLE_RUNTIME_ACCESS,
|
|
FullDataSize, FullData);
|
|
if (EFI_ERROR(efi_status)) {
|
|
perror(L"Failed to set %s: %r\n",
|
|
v->rtname, efi_status);
|
|
}
|
|
}
|
|
|
|
return efi_status;
|
|
}
|
|
|
|
/*
|
|
* Verify our non-volatile MoK state. This checks the variables above
|
|
* accessable and have valid attributes. If they don't, it removes
|
|
* them. If any of them can't be removed, our ability to do this is
|
|
* comprimized, so return EFI_SECURITY_VIOLATION.
|
|
*
|
|
* Any variable that isn't deleted and has ->measure == TRUE is then
|
|
* measured into the tpm.
|
|
*
|
|
* Any variable with a ->rtname element is then mirrored to a
|
|
* runtime-accessable version. The new ones won't be marked NV, so the OS
|
|
* can't modify them.
|
|
*/
|
|
EFI_STATUS import_mok_state(EFI_HANDLE image_handle)
|
|
{
|
|
UINTN i;
|
|
EFI_STATUS ret = EFI_SUCCESS;
|
|
EFI_STATUS efi_status;
|
|
|
|
user_insecure_mode = 0;
|
|
ignore_db = 0;
|
|
|
|
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, present, addend;
|
|
|
|
addend = (v->addend_source && v->addend_size &&
|
|
*v->addend_source && *v->addend_size)
|
|
? TRUE : FALSE;
|
|
|
|
efi_status = get_variable_attr(v->name,
|
|
&v->data, &v->data_size,
|
|
*v->guid, &attrs);
|
|
if (efi_status == EFI_NOT_FOUND) {
|
|
if (v->rtname && addend) {
|
|
efi_status = mirror_one_mok_variable(v);
|
|
if (EFI_ERROR(efi_status) &&
|
|
ret != EFI_SECURITY_VIOLATION)
|
|
ret = efi_status;
|
|
}
|
|
/*
|
|
* after possibly adding, we can continue, no
|
|
* further checks to be done.
|
|
*/
|
|
continue;
|
|
}
|
|
if (EFI_ERROR(efi_status)) {
|
|
perror(L"Could not verify %s: %r\n", v->name,
|
|
efi_status);
|
|
/*
|
|
* don't clobber EFI_SECURITY_VIOLATION from some
|
|
* other variable in the list.
|
|
*/
|
|
if (ret != EFI_SECURITY_VIOLATION)
|
|
ret = efi_status;
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
continue;
|
|
}
|
|
|
|
if (v->data && v->data_size == sizeof(UINT8) && v->state) {
|
|
*v->state = v->data[0];
|
|
}
|
|
|
|
present = (v->data && v->data_size) ? TRUE : FALSE;
|
|
|
|
if (v->flags & MOK_VARIABLE_MEASURE && present) {
|
|
/*
|
|
* 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 && present) {
|
|
/*
|
|
* 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;
|
|
}
|
|
}
|
|
|
|
if (v->rtname && present && addend) {
|
|
if (v->flags & MOK_MIRROR_DELETE_FIRST)
|
|
LibDeleteVariable(v->rtname, v->guid);
|
|
|
|
efi_status = mirror_one_mok_variable(v);
|
|
if (EFI_ERROR(efi_status) &&
|
|
ret != EFI_SECURITY_VIOLATION)
|
|
ret = efi_status;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Enter MokManager if necessary. Any actual *changes* here will
|
|
* cause MokManager to demand a machine reboot, so this is safe to
|
|
* have after the entire loop.
|
|
*/
|
|
efi_status = check_mok_request(image_handle);
|
|
if (EFI_ERROR(efi_status)) {
|
|
if (ret != EFI_SECURITY_VIOLATION)
|
|
ret = efi_status;
|
|
return ret;
|
|
}
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
// vim:fenc=utf-8:tw=75
|