mirror of
				https://git.proxmox.com/git/efi-boot-shim
				synced 2025-11-04 13:10:38 +00:00 
			
		
		
		
	This makes it so shim's idea of Mok variables all resides in one table of data, and we don't need a bunch of nearly identical ad-hoc functions to handle each of them. Signed-off-by: Peter Jones <pjones@redhat.com>
		
			
				
	
	
		
			335 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			335 lines
		
	
	
		
			8.4 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;
 | 
						|
 | 
						|
		efi_status = get_variable_attr(v->name,
 | 
						|
					       &v->data, &v->data_size,
 | 
						|
					       *v->guid, &attrs);
 | 
						|
		if (efi_status == EFI_NOT_FOUND)
 | 
						|
			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;
 | 
						|
		addend = (v->addend_source && v->addend_size &&
 | 
						|
			  *v->addend_source && *v->addend_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
 |