mirror of
				https://git.proxmox.com/git/efi-boot-shim
				synced 2025-10-31 16:12:44 +00:00 
			
		
		
		
	 4181a16f62
			
		
	
	
		4181a16f62
		
	
	
	
	
		
			
			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
 |