mirror of
				https://git.proxmox.com/git/efi-boot-shim
				synced 2025-10-30 23:26:02 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			686 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			686 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <efi.h>
 | |
| #include <efilib.h>
 | |
| #include <Library/BaseCryptLib.h>
 | |
| #include <openssl/x509.h>
 | |
| #include "shim.h"
 | |
| 
 | |
| typedef struct {
 | |
| 	UINT32 MokSize;
 | |
| 	UINT8 *Mok;
 | |
| } MokListNode;
 | |
| 
 | |
| static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes,
 | |
| 				UINTN *size, void **buffer)
 | |
| {
 | |
| 	EFI_STATUS efi_status;
 | |
| 	char allocate = !(*size);
 | |
| 
 | |
| 	efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
 | |
| 				       attributes, size, buffer);
 | |
| 
 | |
| 	if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) {
 | |
| 		return efi_status;
 | |
| 	}
 | |
| 
 | |
| 	if (allocate)
 | |
| 		*buffer = AllocatePool(*size);
 | |
| 
 | |
| 	if (!*buffer) {
 | |
| 		Print(L"Unable to allocate variable buffer\n");
 | |
| 		return EFI_OUT_OF_RESOURCES;
 | |
| 	}
 | |
| 
 | |
| 	efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
 | |
| 				       attributes, size, *buffer);
 | |
| 
 | |
| 	return efi_status;
 | |
| }
 | |
| 
 | |
| static EFI_STATUS delete_variable (CHAR16 *name, EFI_GUID guid)
 | |
| {
 | |
| 	EFI_STATUS efi_status;
 | |
| 
 | |
| 	efi_status = uefi_call_wrapper(RT->SetVariable, 5, name, &guid,
 | |
| 				       0, 0, (UINT8 *)NULL);
 | |
| 
 | |
| 	return efi_status;
 | |
| }
 | |
| 
 | |
| static EFI_INPUT_KEY get_keystroke (void)
 | |
| {
 | |
| 	EFI_INPUT_KEY key;
 | |
| 	UINTN EventIndex;
 | |
| 
 | |
| 	uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey,
 | |
| 			  &EventIndex);
 | |
| 	uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
 | |
| 
 | |
| 	return key;
 | |
| }
 | |
| 
 | |
| static EFI_STATUS get_sha256sum (void *Data, int DataSize, UINT8 *hash)
 | |
| {
 | |
| 	EFI_STATUS status;
 | |
| 	unsigned int ctxsize;
 | |
| 	void *ctx = NULL;
 | |
| 
 | |
| 	ctxsize = Sha256GetContextSize();
 | |
| 	ctx = AllocatePool(ctxsize);
 | |
| 
 | |
| 	if (!ctx) {
 | |
| 		Print(L"Unable to allocate memory for hash context\n");
 | |
| 		return EFI_OUT_OF_RESOURCES;
 | |
| 	}
 | |
| 
 | |
| 	if (!Sha256Init(ctx)) {
 | |
| 		Print(L"Unable to initialise hash\n");
 | |
| 		status = EFI_OUT_OF_RESOURCES;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	if (!(Sha256Update(ctx, Data, DataSize))) {
 | |
| 		Print(L"Unable to generate hash\n");
 | |
| 		status = EFI_OUT_OF_RESOURCES;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	if (!(Sha256Final(ctx, hash))) {
 | |
| 		Print(L"Unable to finalise hash\n");
 | |
| 		status = EFI_OUT_OF_RESOURCES;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	status = EFI_SUCCESS;
 | |
| done:
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
 | |
| 	MokListNode *list;
 | |
| 	int i, remain = DataSize;
 | |
| 	void *ptr;
 | |
| 
 | |
| 	list = AllocatePool(sizeof(MokListNode) * num);
 | |
| 
 | |
| 	if (!list) {
 | |
| 		Print(L"Unable to allocate MOK list\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	ptr = Data;
 | |
| 	for (i = 0; i < num; i++) {
 | |
| 		if (remain < 0) {
 | |
| 			Print(L"MOK list was corrupted\n");
 | |
| 			FreePool(list);
 | |
| 			return NULL;
 | |
| 		}
 | |
| 
 | |
| 		CopyMem(&list[i].MokSize, ptr, sizeof(UINT32));
 | |
| 		ptr += sizeof(UINT32);
 | |
| 		list[i].Mok = ptr;
 | |
| 		ptr += list[i].MokSize;
 | |
| 
 | |
| 		remain -= sizeof(UINT32) + list[i].MokSize;
 | |
| 	}
 | |
| 
 | |
| 	return list;
 | |
| }
 | |
| 
 | |
| /* XXX MOK functions */
 | |
| static void print_x509_name (X509_NAME *X509Name, char *name)
 | |
| {
 | |
| 	char *str;
 | |
| 
 | |
| 	str = X509_NAME_oneline(X509Name, NULL, 0);
 | |
| 	if (str) {
 | |
| 		APrint((CHAR8 *)"%a: %a\n", name, str);
 | |
| 		OPENSSL_free(str);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static const char *mon[12]= {
 | |
| "Jan","Feb","Mar","Apr","May","Jun",
 | |
| "Jul","Aug","Sep","Oct","Nov","Dec"
 | |
| };
 | |
| 
 | |
| static void print_x509_GENERALIZEDTIME_time (ASN1_TIME *time, char *name) {
 | |
| 	char *v;
 | |
| 	int gmt = 0;
 | |
| 	int i;
 | |
| 	int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
 | |
| 	char *f = NULL;
 | |
| 	int f_len = 0;
 | |
| 
 | |
| 	i=time->length;
 | |
| 	v=(char *)time->data;
 | |
| 
 | |
| 	if (i < 12)
 | |
| 		goto error;
 | |
| 
 | |
| 	if (v[i-1] == 'Z')
 | |
| 		gmt=1;
 | |
| 
 | |
| 	for (i=0; i<12; i++) {
 | |
| 		if ((v[i] > '9') || (v[i] < '0'))
 | |
| 			goto error;
 | |
| 	}
 | |
| 
 | |
| 	y = (v[0]-'0')*1000+(v[1]-'0')*100 + (v[2]-'0')*10+(v[3]-'0');
 | |
| 	M = (v[4]-'0')*10+(v[5]-'0');
 | |
| 
 | |
| 	if ((M > 12) || (M < 1))
 | |
| 		goto error;
 | |
| 
 | |
| 	d = (v[6]-'0')*10+(v[7]-'0');
 | |
| 	h = (v[8]-'0')*10+(v[9]-'0');
 | |
| 	m = (v[10]-'0')*10+(v[11]-'0');
 | |
| 
 | |
| 	if (time->length >= 14 &&
 | |
| 	    (v[12] >= '0') && (v[12] <= '9') &&
 | |
| 	    (v[13] >= '0') && (v[13] <= '9')) {
 | |
| 		s = (v[12]-'0')*10+(v[13]-'0');
 | |
| 		/* Check for fractions of seconds. */
 | |
| 		if (time->length >= 15 && v[14] == '.')	{
 | |
| 			int l = time->length;
 | |
| 			f = &v[14];	/* The decimal point. */
 | |
| 			f_len = 1;
 | |
| 			while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
 | |
| 				++f_len;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	APrint((CHAR8 *)"%a: %a %2d %02d:%02d:%02d%.*a %d%a",
 | |
| 	       name, mon[M-1],d,h,m,s,f_len,f,y,(gmt)?" GMT":"");
 | |
| error:
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| static void print_x509_UTCTIME_time (ASN1_TIME *time, char *name)
 | |
| {
 | |
| 	char *v;
 | |
| 	int gmt=0;
 | |
| 	int i;
 | |
| 	int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
 | |
| 
 | |
| 	i=time->length;
 | |
| 	v=(char *)time->data;
 | |
| 
 | |
| 	if (i < 10)
 | |
| 		goto error;
 | |
| 
 | |
| 	if (v[i-1] == 'Z')
 | |
| 		gmt=1;
 | |
| 
 | |
| 	for (i=0; i<10; i++)
 | |
| 		if ((v[i] > '9') || (v[i] < '0'))
 | |
| 			goto error;
 | |
| 
 | |
| 	y = (v[0]-'0')*10+(v[1]-'0');
 | |
| 
 | |
| 	if (y < 50)
 | |
| 		y+=100;
 | |
| 
 | |
| 	M = (v[2]-'0')*10+(v[3]-'0');
 | |
| 
 | |
| 	if ((M > 12) || (M < 1))
 | |
| 		goto error;
 | |
| 
 | |
| 	d = (v[4]-'0')*10+(v[5]-'0');
 | |
| 	h = (v[6]-'0')*10+(v[7]-'0');
 | |
| 	m = (v[8]-'0')*10+(v[9]-'0');
 | |
| 
 | |
| 	if (time->length >=12 &&
 | |
| 	    (v[10] >= '0') && (v[10] <= '9') &&
 | |
| 	    (v[11] >= '0') && (v[11] <= '9'))
 | |
| 		s = (v[10]-'0')*10+(v[11]-'0');
 | |
| 
 | |
| 	APrint((CHAR8 *)"%a: %a %2d %02d:%02d:%02d %d%a\n",
 | |
| 	       name, mon[M-1],d,h,m,s,y+1900,(gmt)?" GMT":"");
 | |
| error:
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| static void print_x509_time (ASN1_TIME *time, char *name)
 | |
| {
 | |
| 	if(time->type == V_ASN1_UTCTIME)
 | |
| 		print_x509_UTCTIME_time(time, name);
 | |
| 
 | |
| 	if(time->type == V_ASN1_GENERALIZEDTIME)
 | |
| 		print_x509_GENERALIZEDTIME_time(time, name);
 | |
| }
 | |
| 
 | |
| static void show_x509_info (X509 *X509Cert)
 | |
| {
 | |
| 	X509_NAME *X509Name;
 | |
| 	ASN1_TIME *time;
 | |
| 
 | |
| 	X509Name = X509_get_issuer_name(X509Cert);
 | |
| 	if (X509Name) {
 | |
| 		print_x509_name(X509Name, "Issuer");
 | |
| 	}
 | |
| 
 | |
| 	X509Name = X509_get_subject_name(X509Cert);
 | |
| 	if (X509Name) {
 | |
| 		print_x509_name(X509Name, "Subject");
 | |
| 	}
 | |
| 
 | |
| 	time = X509_get_notBefore(X509Cert);
 | |
| 	if (time) {
 | |
| 		print_x509_time(time, "Not Before");
 | |
| 	}
 | |
| 
 | |
| 	time = X509_get_notAfter(X509Cert);
 | |
| 	if (time) {
 | |
| 		print_x509_time(time, "Not After");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void show_mok_info (void *Mok, UINTN MokSize)
 | |
| {
 | |
| 	EFI_STATUS efi_status;
 | |
| 	UINT8 hash[SHA256_DIGEST_SIZE];
 | |
| 	unsigned int i;
 | |
| 	X509 *X509Cert;
 | |
| 
 | |
| 	if (!Mok || MokSize == 0)
 | |
| 		return;
 | |
| 
 | |
| 	efi_status = get_sha256sum(Mok, MokSize, hash);
 | |
| 
 | |
| 	if (efi_status != EFI_SUCCESS) {
 | |
| 		Print(L"Failed to compute MOK fingerprint\n");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (X509ConstructCertificate(Mok, MokSize, (UINT8 **) &X509Cert) &&
 | |
| 	    X509Cert != NULL) {
 | |
| 		show_x509_info(X509Cert);
 | |
| 		X509_free(X509Cert);
 | |
| 	}
 | |
| 
 | |
| 	Print(L"Fingerprint (SHA256):\n");
 | |
| 	for (i = 0; i < SHA256_DIGEST_SIZE; i++) {
 | |
| 		Print(L" %02x", hash[i]);
 | |
| 		if (i % 16 == 15)
 | |
| 			Print(L"\n");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static UINT8 delete_mok(MokListNode *list, UINT32 MokNum, UINT32 delete)
 | |
| {
 | |
| 	if (!list || !MokNum || MokNum <= delete)
 | |
| 		return 0;
 | |
| 
 | |
| 	list[delete].Mok = NULL;
 | |
| 	list[delete].MokSize = 0;
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| static UINT8 mok_deletion_prompt(MokListNode *list, UINT32 MokNum)
 | |
| {
 | |
| 	EFI_INPUT_KEY key;
 | |
| 	CHAR16 line[10];
 | |
| 	unsigned int word_count = 0;
 | |
| 	UINTN delete;
 | |
| 
 | |
| 	Print(L"delete key: ");
 | |
| 	do {
 | |
| 		key = get_keystroke();
 | |
| 		if ((key.UnicodeChar < '0' ||
 | |
| 		     key.UnicodeChar > '9' ||
 | |
| 		     word_count >= 10) &&
 | |
| 		    key.UnicodeChar != CHAR_BACKSPACE)
 | |
| 			continue;
 | |
| 
 | |
| 		if (word_count == 0 && key.UnicodeChar == CHAR_BACKSPACE)
 | |
| 			continue;
 | |
| 
 | |
| 		Print(L"%c", key.UnicodeChar);
 | |
| 
 | |
| 		if (key.UnicodeChar == CHAR_BACKSPACE) {
 | |
| 			word_count--;
 | |
| 			line[word_count] = '\0';
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		line[word_count] = key.UnicodeChar;
 | |
| 		word_count++;
 | |
| 	} while (key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
| 	Print(L"\n");
 | |
| 
 | |
| 	if (word_count == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	line[word_count] = '\0';
 | |
| 	delete = Atoi(line)-1;
 | |
| 
 | |
| 	if (delete >= MokNum) {
 | |
| 		Print(L"No such key\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (!list[delete].Mok) {
 | |
| 		Print(L"Already deleted\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	Print(L"Delete this key?\n");
 | |
| 	show_mok_info(list[delete].Mok, list[delete].MokSize);
 | |
| 	Print(L"(y/N) ");
 | |
| 	key = get_keystroke();
 | |
| 	if (key.UnicodeChar != 'y' && key.UnicodeChar != 'Y') {
 | |
| 		Print(L"N\nAbort\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	Print(L"y\nDelete key %d\n", delete+1);
 | |
| 
 | |
| 	return delete_mok(list, MokNum, delete);
 | |
| }
 | |
| 
 | |
| static void write_mok_list(void *MokListData, UINTN MokListDataSize,
 | |
| 			   MokListNode *list, UINT32 MokNum)
 | |
| {
 | |
| 	EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
 | |
| 	EFI_STATUS efi_status;
 | |
| 	UINT32 new_num = 0;
 | |
| 	unsigned int i;
 | |
| 	UINTN DataSize = 0;
 | |
| 	void *Data, *ptr;
 | |
| 
 | |
| 	if (!MokListData || !list)
 | |
| 		return;
 | |
| 
 | |
| 	for (i = 0; i < MokNum; i++) {
 | |
| 		if (list[i].Mok && list[i].MokSize > 0) {
 | |
| 			DataSize += list[i].MokSize + sizeof(UINT32);
 | |
| 			if (new_num < i) {
 | |
| 				list[new_num].Mok = list[i].Mok;
 | |
| 				list[new_num].MokSize = list[i].MokSize;
 | |
| 			}
 | |
| 			new_num++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (new_num == 0) {
 | |
| 		Data = NULL;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	DataSize += sizeof(UINT32);
 | |
| 
 | |
| 	Data = AllocatePool(DataSize * sizeof(UINT8));
 | |
| 	ptr = Data;
 | |
| 
 | |
| 	CopyMem(Data, &new_num, sizeof(new_num));
 | |
| 	ptr += sizeof(new_num);
 | |
| 
 | |
| 	for (i = 0; i < new_num; i++) {
 | |
| 		CopyMem(ptr, &list[i].MokSize, sizeof(list[i].MokSize));
 | |
| 		ptr += sizeof(list[i].MokSize);
 | |
| 		CopyMem(ptr, list[i].Mok, list[i].MokSize);
 | |
| 		ptr += list[i].MokSize;
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
 | |
| 				       &shim_lock_guid,
 | |
| 				       EFI_VARIABLE_NON_VOLATILE
 | |
| 				       | EFI_VARIABLE_BOOTSERVICE_ACCESS,
 | |
| 				       DataSize, Data);
 | |
| 	if (efi_status != EFI_SUCCESS) {
 | |
| 		Print(L"Failed to set variable %d\n", efi_status);
 | |
| 	}
 | |
| 
 | |
| 	if (Data)
 | |
| 		FreePool(Data);
 | |
| }
 | |
| 
 | |
| static void mok_mgmt_shell (void)
 | |
| {
 | |
| 	EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
 | |
| 	EFI_STATUS efi_status;
 | |
| 	unsigned int i, changed = 0;
 | |
| 	void *MokListData = NULL;
 | |
| 	UINTN MokListDataSize = 0;
 | |
| 	UINT32 MokNum;
 | |
| 	UINT32 attributes;
 | |
| 	MokListNode *list = NULL;
 | |
| 	EFI_INPUT_KEY key;
 | |
| 
 | |
| 	efi_status = get_variable(L"MokList", shim_lock_guid, &attributes,
 | |
| 				  &MokListDataSize, &MokListData);
 | |
| 
 | |
| 	if (efi_status != EFI_SUCCESS) {
 | |
| 		Print(L"Failed to get MokList\n");
 | |
| 		goto error;
 | |
| 	}
 | |
| 
 | |
| 	if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
 | |
| 		Print(L"MokList is compromised!\nErase all keys in MokList!\n");
 | |
| 		if (delete_variable(L"MokList", shim_lock_guid) != EFI_SUCCESS) {
 | |
| 			Print(L"Failed to erase MokList\n");
 | |
| 		}
 | |
| 		goto error;
 | |
| 	}
 | |
| 
 | |
| 	CopyMem(&MokNum, MokListData, sizeof(UINT32));
 | |
| 	if (MokNum == 0) {
 | |
| 		Print(L"No key enrolled\n");
 | |
| 		goto error;
 | |
| 	}
 | |
| 	list = build_mok_list(MokNum,
 | |
| 			      (void *)MokListData + sizeof(UINT32),
 | |
| 			      MokListDataSize - sizeof(UINT32));
 | |
| 
 | |
| 	if (!list) {
 | |
| 		Print(L"Failed to construct MOK list\n");
 | |
| 		goto error;
 | |
| 	}
 | |
| 
 | |
| 	do {
 | |
| 		Print(L"shim) ");
 | |
| 		key = get_keystroke();
 | |
| 		Print(L"%c\n", key.UnicodeChar);
 | |
| 
 | |
| 		switch (key.UnicodeChar) {
 | |
| 			case 'l':
 | |
| 			case 'L':
 | |
| 				for (i = 0; i < MokNum; i++) {
 | |
| 					if (list[i].Mok) {
 | |
| 						Print(L"Key %d\n", i+1);
 | |
| 						show_mok_info(list[i].Mok, list[i].MokSize);
 | |
| 						Print(L"\n");
 | |
| 					}
 | |
| 				}
 | |
| 				break;
 | |
| 			case 'd':
 | |
| 			case 'D':
 | |
| 				if (mok_deletion_prompt(list, MokNum) && changed == 0)
 | |
| 					changed = 1;
 | |
| 				break;
 | |
| 		}
 | |
| 	} while (key.UnicodeChar != 'c' && key.UnicodeChar != 'C');
 | |
| 
 | |
| 	if (changed) {
 | |
| 		write_mok_list(MokListData, MokListDataSize, list, MokNum);
 | |
| 	}
 | |
| 
 | |
| error:
 | |
| 	if (MokListData)
 | |
| 		FreePool(MokListData);
 | |
| 	if (list)
 | |
| 		FreePool(list);
 | |
| }
 | |
| 
 | |
| static UINT8 mok_enrollment_prompt (void *Mok, UINTN MokSize)
 | |
| {
 | |
| 	EFI_INPUT_KEY key;
 | |
| 
 | |
| 	Print(L"New machine owner key:\n\n");
 | |
| 	show_mok_info(Mok, MokSize);
 | |
| 	Print(L"\nEnroll the key? (y/N): ");
 | |
| 
 | |
| 	key = get_keystroke();
 | |
| 	Print(L"%c\n", key.UnicodeChar);
 | |
| 
 | |
| 	if (key.UnicodeChar == 'Y' || key.UnicodeChar == 'y') {
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	Print(L"Abort\n");
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static EFI_STATUS enroll_mok (void *Mok, UINT32 MokSize, void *OldData,
 | |
| 			      UINT32 OldDataSize, UINT32 MokNum)
 | |
| {
 | |
| 	EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
 | |
| 	EFI_STATUS efi_status;
 | |
| 	void *Data, *ptr;
 | |
| 	UINT32 DataSize = 0;
 | |
| 
 | |
| 	if (OldData)
 | |
| 		DataSize += OldDataSize;
 | |
| 	else
 | |
| 		DataSize += sizeof(UINT32);
 | |
| 	DataSize += sizeof(UINT32);
 | |
| 	DataSize += MokSize;
 | |
| 	MokNum += 1;
 | |
| 
 | |
| 	Data = AllocatePool(DataSize);
 | |
| 
 | |
| 	if (!Data) {
 | |
| 		Print(L"Failed to allocate buffer for MOK list\n");
 | |
| 		return EFI_OUT_OF_RESOURCES;
 | |
| 	}
 | |
| 
 | |
| 	ptr = Data;
 | |
| 
 | |
| 	if (OldData) {
 | |
| 		CopyMem(ptr, OldData, OldDataSize);
 | |
| 		CopyMem(ptr, &MokNum, sizeof(MokNum));
 | |
| 		ptr += OldDataSize;
 | |
| 	} else {
 | |
| 		CopyMem(ptr, &MokNum, sizeof(MokNum));
 | |
| 		ptr += sizeof(MokNum);
 | |
| 	}
 | |
| 
 | |
| 	/* Write new MOK */
 | |
| 	CopyMem(ptr, &MokSize, sizeof(MokSize));
 | |
| 	ptr += sizeof(MokSize);
 | |
| 	CopyMem(ptr, Mok, MokSize);
 | |
| 
 | |
| 	efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
 | |
| 				       &shim_lock_guid,
 | |
| 				       EFI_VARIABLE_NON_VOLATILE
 | |
| 				       | EFI_VARIABLE_BOOTSERVICE_ACCESS,
 | |
| 				       DataSize, Data);
 | |
| 	if (efi_status != EFI_SUCCESS) {
 | |
| 		Print(L"Failed to set variable %d\n", efi_status);
 | |
| 		goto error;
 | |
| 	}
 | |
| 
 | |
| error:
 | |
| 	if (Data)
 | |
| 		FreePool(Data);
 | |
| 
 | |
| 	return efi_status;
 | |
| }
 | |
| 
 | |
| static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
 | |
| {
 | |
| 	EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
 | |
| 	EFI_STATUS efi_status;
 | |
| 	UINTN MokSize = 0, MokListDataSize = 0;
 | |
| 	void *Mok = NULL, *MokListData = NULL;
 | |
| 	UINT32 MokNum = 0;
 | |
| 	UINT32 attributes;
 | |
| 	MokListNode *list = NULL;
 | |
| 	UINT8 confirmed;
 | |
| 
 | |
| 	efi_status = get_variable(L"MokNew", shim_lock_guid, &attributes,
 | |
| 				  &MokSize, &Mok);
 | |
| 
 | |
| 	if (efi_status != EFI_SUCCESS) {
 | |
| 		goto error;
 | |
| 	}
 | |
| 
 | |
| 	efi_status = get_variable(L"MokList", shim_lock_guid, &attributes,
 | |
| 				  &MokListDataSize, &MokListData);
 | |
| 
 | |
| 	if (efi_status == EFI_SUCCESS && MokListData) {
 | |
| 		int i;
 | |
| 
 | |
| 		if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
 | |
| 			Print(L"MokList is compromised!\nErase all keys in MokList!\n");
 | |
| 			if (delete_variable(L"MokList", shim_lock_guid) != EFI_SUCCESS) {
 | |
| 				Print(L"Failed to erase MokList\n");
 | |
| 			}
 | |
| 			goto error;
 | |
| 		}
 | |
| 
 | |
| 		CopyMem(&MokNum, MokListData, sizeof(UINT32));
 | |
| 		list = build_mok_list(MokNum,
 | |
| 				      (void *)MokListData + sizeof(UINT32),
 | |
| 				      MokListDataSize - sizeof(UINT32));
 | |
| 
 | |
| 		if (!list) {
 | |
| 			Print(L"Failed to construct MOK list\n");
 | |
| 			goto error;
 | |
| 		}
 | |
| 
 | |
| 		/* check if the key is already enrolled */
 | |
| 		for (i = 0; i < MokNum; i++) {
 | |
| 			if (list[i].MokSize == MokSize &&
 | |
| 			    CompareMem(list[i].Mok, Mok, MokSize) == 0) {
 | |
| 				Print(L"MOK was already enrolled\n");
 | |
| 				goto error;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	confirmed = mok_enrollment_prompt(Mok, MokSize);
 | |
| 
 | |
| 	if (!confirmed)
 | |
| 		goto error;
 | |
| 
 | |
| 	efi_status = enroll_mok(Mok, MokSize, MokListData,
 | |
| 				MokListDataSize, MokNum);
 | |
| 
 | |
| 	if (efi_status != EFI_SUCCESS) {
 | |
| 		Print(L"Failed to enroll MOK\n");
 | |
| 		goto error;
 | |
| 	}
 | |
| 
 | |
| 	mok_mgmt_shell();
 | |
| 
 | |
| error:
 | |
| 	if (Mok) {
 | |
| 		if (delete_variable(L"MokNew", shim_lock_guid) != EFI_SUCCESS) {
 | |
| 			Print(L"Failed to delete MokNew\n");
 | |
| 		}
 | |
| 		FreePool (Mok);
 | |
| 	}
 | |
| 
 | |
| 	if (list)
 | |
| 		FreePool (list);
 | |
| 
 | |
| 	if (MokListData)
 | |
| 		FreePool (MokListData);
 | |
| 
 | |
| 	return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
 | |
| {
 | |
| 	EFI_STATUS efi_status;
 | |
| 
 | |
| 	InitializeLib(image_handle, systab);
 | |
| 
 | |
| 	efi_status = check_mok_request(image_handle);
 | |
| 
 | |
| 	return efi_status;
 | |
| }
 | 
