Merge branch 'mok' into netboot

Conflicts:
	Makefile
	shim.c
This commit is contained in:
Matthew Garrett 2012-11-01 10:45:22 -04:00
commit 28a3e57c9a
5 changed files with 720 additions and 140 deletions

View File

@ -29,7 +29,7 @@ LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH
VERSION = 0.1
TARGET = shim.efi MokManager.efi
OBJS = shim.o netboot.o cert.o
OBJS = shim.o netboot.o cert.o dbx.o
SOURCES = shim.c shim.h netboot.c signature.h PeImage.h
MOK_OBJS = MokManager.o
MOK_SOURCES = MokManager.c shim.h
@ -41,7 +41,10 @@ shim.o: $(SOURCES)
cert.o : cert.S
$(CC) $(CFLAGS) -c -o $@ $<
shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a cert.o
dbx.o : dbx.S
$(CC) $(CFLAGS) -c -o $@ $<
shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a
$(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS)
MokManager.o: $(SOURCES)

View File

@ -8,6 +8,7 @@
#define PASSWORD_MAX 16
#define PASSWORD_MIN 8
#define SB_PASSWORD_LEN 8
#ifndef SHIM_VENDOR
#define SHIM_VENDOR L"Shim"
@ -15,6 +16,9 @@
#define EFI_VARIABLE_APPEND_WRITE 0x00000040
#define CERT_STRING L"Select an X509 certificate to enroll:\n\n"
#define HASH_STRING L"Select a file to trust:\n\n"
struct menu_item {
CHAR16 *text;
INTN (* callback)(void *data, void *data2, void *data3);
@ -29,6 +33,12 @@ typedef struct {
UINT8 *Mok;
} __attribute__ ((packed)) MokListNode;
typedef struct {
UINT32 MokSBState;
UINT32 PWLen;
CHAR16 Password[PASSWORD_MAX];
} __attribute__ ((packed)) MokSBvar;
static EFI_INPUT_KEY get_keystroke (void)
{
EFI_INPUT_KEY key;
@ -99,7 +109,7 @@ static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
(CompareGuid (&CertList->SignatureType, &HashType) != 0)) {
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
CertList->SignatureSize);
CertList->SignatureListSize);
continue;
}
@ -107,7 +117,7 @@ static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
(CertList->SignatureSize != 48)) {
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
CertList->SignatureSize);
CertList->SignatureListSize);
continue;
}
@ -120,7 +130,7 @@ static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
count++;
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureSize);
CertList->SignatureListSize);
}
return list;
@ -308,8 +318,8 @@ static void show_mok_info (void *Mok, UINTN MokSize)
return;
if (MokSize != 48) {
if (X509ConstructCertificate(Mok, MokSize, (UINT8 **) &X509Cert) &&
X509Cert != NULL) {
if (X509ConstructCertificate(Mok, MokSize,
(UINT8 **) &X509Cert) && X509Cert != NULL) {
show_x509_info(X509Cert);
X509_free(X509Cert);
} else {
@ -317,6 +327,20 @@ static void show_mok_info (void *Mok, UINTN MokSize)
((UINT32 *)Mok)[0]);
return;
}
efi_status = get_sha1sum(Mok, MokSize, hash);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to compute MOK fingerprint\n");
return;
}
Print(L" Fingerprint (SHA1):\n ");
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
Print(L" %02x", hash[i]);
if (i % 10 == 9)
Print(L"\n ");
}
} else {
Print(L"SHA256 hash:\n ");
for (i = 0; i < SHA256_DIGEST_SIZE; i++) {
@ -326,19 +350,7 @@ static void show_mok_info (void *Mok, UINTN MokSize)
}
Print(L"\n");
}
efi_status = get_sha1sum(Mok, MokSize, hash);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to compute MOK fingerprint\n");
return;
}
Print(L" Fingerprint (SHA1):\n ");
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
Print(L" %02x", hash[i]);
if (i % 10 == 9)
Print(L"\n ");
}
Print(L"\n");
}
@ -403,7 +415,7 @@ static UINT8 list_keys (void *MokNew, UINTN MokNewSize)
Print(L"Doesn't look like a key or hash\n");
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureSize);
CertList->SignatureListSize);
continue;
}
@ -412,14 +424,14 @@ static UINT8 list_keys (void *MokNew, UINTN MokNewSize)
Print(L"Doesn't look like a valid hash\n");
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureSize);
CertList->SignatureListSize);
continue;
}
MokNum++;
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureSize);
CertList->SignatureListSize);
}
keys = build_mok_list(MokNum, MokNew, MokNewSize);
@ -604,8 +616,7 @@ static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate)
efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
&shim_lock_guid,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_APPEND_WRITE,
| EFI_VARIABLE_BOOTSERVICE_ACCESS,
0, NULL);
} else {
/* Write new MOK */
@ -653,7 +664,9 @@ static UINTN mok_enrollment_prompt (void *MokNew, UINTN MokNewSize, int auth) {
}
static INTN mok_enrollment_prompt_callback (void *MokNew, void *data2,
void *data3) {
void *data3)
{
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
return mok_enrollment_prompt(MokNew, (UINTN)data2, TRUE);
}
@ -678,7 +691,203 @@ static INTN mok_deletion_prompt (void *MokNew, void *data2, void *data3) {
return 0;
}
static UINTN draw_menu (struct menu_item *items, UINTN count) {
static INTN mok_sb_prompt (void *MokSB, void *data2, void *data3) {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
UINTN MokSBSize = (UINTN)data2;
MokSBvar *var = MokSB;
CHAR16 pass1, pass2, pass3;
UINT8 fail_count = 0;
UINT32 length;
CHAR16 line[1];
UINT8 sbval = 1;
UINT8 pos1, pos2, pos3;
if (MokSBSize != sizeof(MokSBvar)) {
Print(L"Invalid MokSB variable contents\n");
return -1;
}
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
while (fail_count < 3) {
RandomBytes (&pos1, sizeof(pos1));
pos1 = (pos1 % var->PWLen);
do {
RandomBytes (&pos2, sizeof(pos2));
pos2 = (pos2 % var->PWLen);
} while (pos2 == pos1);
do {
RandomBytes (&pos3, sizeof(pos3));
pos3 = (pos3 % var->PWLen) ;
} while (pos3 == pos2 || pos3 == pos1);
Print(L"Enter password character %d: ", pos1 + 1);
get_line(&length, &pass1, 1, 0);
Print(L"Enter password character %d: ", pos2 + 1);
get_line(&length, &pass2, 1, 0);
Print(L"Enter password character %d: ", pos3 + 1);
get_line(&length, &pass3, 1, 0);
if (pass1 != var->Password[pos1] ||
pass2 != var->Password[pos2] ||
pass3 != var->Password[pos3]) {
Print(L"Invalid character\n");
fail_count++;
} else {
break;
}
}
if (fail_count >= 3) {
Print(L"Password limit reached\n");
return -1;
}
if (var->MokSBState == 0) {
Print(L"Disable Secure Boot? (y/n): ");
} else {
Print(L"Enable Secure Boot? (y/n): ");
}
do {
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
if (var->MokSBState == 0) {
efi_status = uefi_call_wrapper(RT->SetVariable,
5, L"MokSBState",
&shim_lock_guid,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS,
1, &sbval);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to set Secure Boot state\n");
return -1;
}
} else {
LibDeleteVariable(L"MokSBState",
&shim_lock_guid);
}
LibDeleteVariable(L"MokSB", &shim_lock_guid);
Print(L"Press a key to reboot system\n");
Pause();
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm,
EFI_SUCCESS, 0, NULL);
Print(L"Failed to reboot\n");
return -1;
}
} while (line[0] != 'N' && line[0] != 'n');
return -1;
}
static INTN mok_pw_prompt (void *MokPW, void *data2, void *data3) {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
UINTN MokPWSize = (UINTN)data2;
UINT8 fail_count = 0;
UINT8 hash[SHA256_DIGEST_SIZE];
CHAR16 password[PASSWORD_MAX];
UINT32 length;
CHAR16 line[1];
if (MokPWSize != SHA256_DIGEST_SIZE) {
Print(L"Invalid MokPW variable contents\n");
return -1;
}
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
SetMem(hash, SHA256_DIGEST_SIZE, 0);
if (CompareMem(MokPW, hash, SHA256_DIGEST_SIZE) == 0) {
Print(L"Clear MOK password? (y/n): ");
do {
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
LibDeleteVariable(L"MokPWStore", &shim_lock_guid);
LibDeleteVariable(L"MokPW", &shim_lock_guid);
}
} while (line[0] != 'N' && line[0] != 'n');
return 0;
}
while (fail_count < 3) {
Print(L"Confirm MOK passphrase: ");
get_line(&length, password, PASSWORD_MAX, 0);
if ((length < PASSWORD_MIN) || (length > PASSWORD_MAX)) {
Print(L"Invalid password length\n");
fail_count++;
continue;
}
efi_status = compute_pw_hash(NULL, 0, password, length, hash);
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to generate password hash\n");
fail_count++;
continue;
}
if (CompareMem(MokPW, hash, SHA256_DIGEST_SIZE) != 0) {
Print(L"Password doesn't match\n");
fail_count++;
continue;
}
break;
}
if (fail_count >= 3) {
Print(L"Password limit reached\n");
return -1;
}
Print(L"Set MOK password? (y/n): ");
do {
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
efi_status = uefi_call_wrapper(RT->SetVariable, 5,
L"MokPWStore",
&shim_lock_guid,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS,
MokPWSize, MokPW);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to set MOK password\n");
return -1;
}
LibDeleteVariable(L"MokPW", &shim_lock_guid);
Print(L"Press a key to reboot system\n");
Pause();
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm,
EFI_SUCCESS, 0, NULL);
Print(L"Failed to reboot\n");
return -1;
}
} while (line[0] != 'N' && line[0] != 'n');
return 0;
}
static UINTN draw_menu (CHAR16 *header, UINTN lines, struct menu_item *items,
UINTN count) {
UINTN i;
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@ -688,6 +897,9 @@ static UINTN draw_menu (struct menu_item *items, UINTN count) {
Print(L"%s UEFI key management\n\n", SHIM_VENDOR);
if (header)
Print(L"%s", header);
for (i = 0; i < count; i++) {
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
items[i].colour | EFI_BACKGROUND_BLACK);
@ -697,7 +909,7 @@ static UINTN draw_menu (struct menu_item *items, UINTN count) {
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 0);
uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
return 2;
return 2 + lines;
}
static void free_menu (struct menu_item *items, UINTN count) {
@ -711,31 +923,42 @@ static void free_menu (struct menu_item *items, UINTN count) {
FreePool(items);
}
static void run_menu (struct menu_item *items, UINTN count, UINTN timeout) {
static void update_time (UINTN position, UINTN timeout)
{
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0,
position);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
EFI_BLACK | EFI_BACKGROUND_BLACK);
Print(L" ", timeout);
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0,
position);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
EFI_WHITE | EFI_BACKGROUND_BLACK);
if (timeout > 1)
Print(L"Booting in %d seconds\n", timeout);
else if (timeout)
Print(L"Booting in %d second\n", timeout);
}
static void run_menu (CHAR16 *header, UINTN lines, struct menu_item *items,
UINTN count, UINTN timeout) {
UINTN index, pos = 0, wait = 0, offset;
EFI_INPUT_KEY key;
EFI_STATUS status;
INTN ret;
if (timeout)
wait = 10000000;
offset = draw_menu (header, lines, items, count);
while (1) {
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
offset = draw_menu (items, count);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2,
ST->ConOut,
EFI_WHITE | EFI_BACKGROUND_BLACK);
if (timeout) {
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3,
ST->ConOut, 0, count + 1 + offset);
if (timeout > 1)
Print(L"Booting in %d seconds\n", timeout);
else
Print(L"Booting in %d second\n", timeout);
}
update_time(count + offset + 1, timeout);
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut,
0, pos + offset);
@ -781,9 +1004,14 @@ static void run_menu (struct menu_item *items, UINTN count, UINTN timeout) {
return;
}
items[pos].callback(items[pos].data, items[pos].data2,
items[pos].data3);
draw_menu (items, count);
ret = items[pos].callback(items[pos].data,
items[pos].data2,
items[pos].data3);
if (ret < 0) {
Print(L"Press a key to continue\n");
Pause();
}
draw_menu (header, lines, items, count);
pos = 0;
break;
}
@ -937,6 +1165,7 @@ static INTN directory_callback (void *data, void *data2, void *data3) {
EFI_FILE *dir;
CHAR16 *filename = data;
EFI_FILE *root = data2;
BOOLEAN hash = !!data3;
status = uefi_call_wrapper(root->Open, 5, root, &dir, filename,
EFI_FILE_MODE_READ, 0);
@ -1023,7 +1252,10 @@ static INTN directory_callback (void *data, void *data2, void *data3) {
buffer = NULL;
}
run_menu(dircontent, dircount, 0);
if (hash)
run_menu(HASH_STRING, 2, dircontent, dircount, 0);
else
run_menu(CERT_STRING, 2, dircontent, dircount, 0);
return 0;
}
@ -1035,6 +1267,7 @@ static INTN filesystem_callback (void *data, void *data2, void *data3) {
UINTN dircount = 0, i = 0;
struct menu_item *dircontent;
EFI_FILE *root = data;
BOOLEAN hash = !!data3;
uefi_call_wrapper(root->SetPosition, 2, root, 0);
@ -1117,7 +1350,10 @@ static INTN filesystem_callback (void *data, void *data2, void *data3) {
buffersize = 0;
}
run_menu(dircontent, dircount, 0);
if (hash)
run_menu(HASH_STRING, 2, dircontent, dircount, 0);
else
run_menu(CERT_STRING, 2, dircontent, dircount, 0);
return 0;
}
@ -1126,8 +1362,9 @@ static INTN find_fs (void *data, void *data2, void *data3) {
EFI_GUID fs_guid = SIMPLE_FILE_SYSTEM_PROTOCOL;
UINTN count, i;
UINTN OldSize, NewSize;
EFI_HANDLE **filesystem_handles;
EFI_HANDLE *filesystem_handles = NULL;
struct menu_item *filesystems;
BOOLEAN hash = !!data3;
uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &fs_guid,
NULL, &count, &filesystem_handles);
@ -1146,7 +1383,7 @@ static INTN find_fs (void *data, void *data2, void *data3) {
filesystems[0].colour = EFI_YELLOW;
for (i=1; i<count; i++) {
EFI_HANDLE *fs = filesystem_handles[i-1];
EFI_HANDLE fs = filesystem_handles[i-1];
EFI_FILE_IO_INTERFACE *fs_interface;
EFI_DEVICE_PATH *path;
EFI_FILE *root;
@ -1157,7 +1394,7 @@ static INTN find_fs (void *data, void *data2, void *data3) {
EFI_GUID file_info_guid = EFI_FILE_INFO_ID;
status = uefi_call_wrapper(BS->HandleProtocol, 3, fs, &fs_guid,
&fs_interface);
(void **)&fs_interface);
if (status != EFI_SUCCESS || !fs_interface)
continue;
@ -1208,23 +1445,90 @@ static INTN find_fs (void *data, void *data2, void *data3) {
uefi_call_wrapper(BS->FreePool, 1, filesystem_handles);
run_menu(filesystems, count, 0);
if (hash)
run_menu(HASH_STRING, 2, filesystems, count, 0);
else
run_menu(CERT_STRING, 2, filesystems, count, 0);
return 0;
}
static BOOLEAN verify_pw(void)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
CHAR16 password[PASSWORD_MAX];
UINT8 fail_count = 0;
UINT8 hash[SHA256_DIGEST_SIZE];
UINT8 pwhash[SHA256_DIGEST_SIZE];
UINTN size = SHA256_DIGEST_SIZE;
UINT32 length;
UINT32 attributes;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokPWStore",
&shim_lock_guid, &attributes, &size,
pwhash);
/*
* If anything can attack the password it could just set it to a
* known value, so there's no safety advantage in failing to validate
* purely because of a failure to read the variable
*/
if (efi_status != EFI_SUCCESS)
return TRUE;
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS)
return TRUE;
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
while (fail_count < 3) {
Print(L"Enter MOK password: ");
get_line(&length, password, PASSWORD_MAX, 0);
if (length < PASSWORD_MIN || length > PASSWORD_MAX) {
Print(L"Invalid password length\n");
fail_count++;
continue;
}
efi_status = compute_pw_hash(NULL, 0, password, length, hash);
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to generate password hash\n");
fail_count++;
continue;
}
if (CompareMem(pwhash, hash, SHA256_DIGEST_SIZE) != 0) {
Print(L"Password doesn't match\n");
fail_count++;
continue;
}
return TRUE;
}
Print(L"Password limit reached\n");
return FALSE;
}
static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew,
UINTN MokNewSize)
UINTN MokNewSize, void *MokSB,
UINTN MokSBSize, void *MokPW, UINTN MokPWSize)
{
struct menu_item *menu_item;
UINT32 MokAuth = 0;
UINTN menucount = 0;
UINTN menucount = 3, i = 0;
EFI_STATUS efi_status;
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
UINT8 auth[SHA256_DIGEST_SIZE];
UINTN auth_size = SHA256_DIGEST_SIZE;
UINT32 attributes;
if (verify_pw() == FALSE)
return EFI_ACCESS_DENIED;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth",
&shim_lock_guid,
&attributes, &auth_size, auth);
@ -1233,49 +1537,73 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew,
MokAuth = 1;
if (MokNew || MokAuth)
menu_item = AllocateZeroPool(sizeof(struct menu_item) * 4);
else
menu_item = AllocateZeroPool(sizeof(struct menu_item) * 3);
menucount++;
if (MokSB)
menucount++;
if (MokPW)
menucount++;
menu_item = AllocateZeroPool(sizeof(struct menu_item) * menucount);
if (!menu_item)
return EFI_OUT_OF_RESOURCES;
menu_item[0].text = StrDuplicate(L"Continue boot");
menu_item[0].colour = EFI_WHITE;
menu_item[0].callback = NULL;
menu_item[i].text = StrDuplicate(L"Continue boot");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = NULL;
menucount++;
i++;
if (MokNew || MokAuth) {
if (!MokNew) {
menu_item[1].text = StrDuplicate(L"Delete MOK");
menu_item[1].colour = EFI_WHITE;
menu_item[1].callback = mok_deletion_prompt;
menu_item[i].text = StrDuplicate(L"Delete MOK");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = mok_deletion_prompt;
} else {
menu_item[1].text = StrDuplicate(L"Enroll MOK");
menu_item[1].colour = EFI_WHITE;
menu_item[1].data = MokNew;
menu_item[1].data2 = (void *)MokNewSize;
menu_item[1].callback = mok_enrollment_prompt_callback;
menu_item[i].text = StrDuplicate(L"Enroll MOK");
menu_item[i].colour = EFI_WHITE;
menu_item[i].data = MokNew;
menu_item[i].data2 = (void *)MokNewSize;
menu_item[i].callback = mok_enrollment_prompt_callback;
}
menucount++;
i++;
}
menu_item[menucount].text = StrDuplicate(L"Enroll key from disk");
menu_item[menucount].colour = EFI_WHITE;
menu_item[menucount].callback = find_fs;
menu_item[menucount].data3 = (void *)FALSE;
if (MokSB) {
menu_item[i].text = StrDuplicate(L"Change Secure Boot state");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = mok_sb_prompt;
menu_item[i].data = MokSB;
menu_item[i].data2 = (void *)MokSBSize;
i++;
}
menucount++;
if (MokPW) {
menu_item[i].text = StrDuplicate(L"Set MOK password");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = mok_pw_prompt;
menu_item[i].data = MokPW;
menu_item[i].data2 = (void *)MokPWSize;
i++;
}
menu_item[menucount].text = StrDuplicate(L"Enroll hash from disk");
menu_item[menucount].colour = EFI_WHITE;
menu_item[menucount].callback = find_fs;
menu_item[menucount].data3 = (void *)TRUE;
menu_item[i].text = StrDuplicate(L"Enroll key from disk");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = find_fs;
menu_item[i].data3 = (void *)FALSE;
menucount++;
i++;
run_menu(menu_item, menucount, 10);
menu_item[i].text = StrDuplicate(L"Enroll hash from disk");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = find_fs;
menu_item[i].data3 = (void *)TRUE;
i++;
run_menu(NULL, 0, menu_item, menucount, 10);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@ -1285,12 +1613,19 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew,
static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
UINTN MokNewSize = 0;
UINTN MokNewSize = 0, MokSBSize = 0, MokPWSize = 0;
void *MokNew = NULL;
void *MokSB = NULL;
void *MokPW = NULL;
MokNew = LibGetVariableAndSize(L"MokNew", &shim_lock_guid, &MokNewSize);
enter_mok_menu(image_handle, MokNew, MokNewSize);
MokSB = LibGetVariableAndSize(L"MokSB", &shim_lock_guid, &MokSBSize);
MokPW = LibGetVariableAndSize(L"MokPW", &shim_lock_guid, &MokPWSize);
enter_mok_menu(image_handle, MokNew, MokNewSize, MokSB, MokSBSize,
MokPW, MokPWSize);
if (MokNew) {
if (LibDeleteVariable(L"MokNew", &shim_lock_guid) != EFI_SUCCESS) {
@ -1298,17 +1633,59 @@ static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
}
FreePool (MokNew);
}
if (MokSB) {
if (LibDeleteVariable(L"MokSB", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to delete MokSB\n");
}
FreePool (MokNew);
}
if (MokPW) {
if (LibDeleteVariable(L"MokPW", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to delete MokPW\n");
}
FreePool (MokNew);
}
LibDeleteVariable(L"MokAuth", &shim_lock_guid);
return EFI_SUCCESS;
}
static EFI_STATUS setup_rand (void)
{
EFI_TIME time;
EFI_STATUS efi_status;
UINT64 seed;
BOOLEAN status;
efi_status = uefi_call_wrapper(RT->GetTime, 2, &time, NULL);
if (efi_status != EFI_SUCCESS)
return efi_status;
seed = ((UINT64)time.Year << 48) | ((UINT64)time.Month << 40) |
((UINT64)time.Day << 32) | ((UINT64)time.Hour << 24) |
((UINT64)time.Minute << 16) | ((UINT64)time.Second << 8) |
((UINT64)time.Daylight);
status = RandomSeed((UINT8 *)&seed, sizeof(seed));
if (!status)
return EFI_ABORTED;
return EFI_SUCCESS;
}
EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
{
EFI_STATUS efi_status;
InitializeLib(image_handle, systab);
setup_rand();
efi_status = check_mok_request(image_handle);
return efi_status;

51
MokVars.txt Normal file
View File

@ -0,0 +1,51 @@
Variables used by Shim and Mokmanager
Request variables:
MokPW: Set by MokUtil when setting a password. A SHA-256 hash of the
UCS-2 representation of the password. The user will be asked to
re-enter the password to confirm. If the hash of the entered password
matches the contents of MokPW, the user will be prompted to copy MokPW
into MokPWState. BS,RT,NV
MokSB: Set by MokUtil when requesting a change in state of signature
validation. A packed structure as follows:
typedef struct {
UINT32 MokSBState;
UINT32 PWLen;
CHAR16 Password[PASSWORD_MAX];
} __attribute__ ((packed)) MokSBvar;
If MokSBState is 0, the user will be prompted to disable signature
validation. Otherwise, the user will be prompted to enable it. PWLen
is the length of the password, in characters. Password is a UCS-2
representation of the password. The user will be prompted to enter
three randomly chosen characters from the password. If successful,
they will then be prompted to change the signature validation
according to MokSBState. BS,RT,NV
MokNew: Set by MokUtil when requesting the addition or removal of keys
from MokList. Is an EFI_SIGNATURE_LIST as described in the UEFI
specification. BS,RT,NV
MokAuth: A hash dependent upon the contents of MokNew and the sealing
password. The user's password in UCS-2 form should be appended to the
contents of MokNew and a SHA-256 hash generated and stored in MokAuth.
The hash will be regenerated by MokManager after the user is requested
to enter their password to confirm enrolment of the keys. If the hash
matches MokAuth, the user will be prompted to enrol the keys. BS,RT,NV
State variables:
MokList: A list of whitelisted keys and hashes. An EFI_SIGNATURE_LIST
as described in the UEFI specification. BS,NV
MokListRT: A copy of MokList made available to the kernel at runtime. RT
MokSBState: An 8-bit unsigned integer. If 1, shim will switch to
insecure mode. BS,NV
MokPWStore: A SHA-256 representation of the password set by the user
via MokPW. The user will be prompted to enter this password in order
to interact with MokManager.

32
dbx.S Normal file
View File

@ -0,0 +1,32 @@
#if defined(VENDOR_DBX_FILE)
.globl vendor_dbx_size
.data
.align 1
.type vendor_dbx_size, @object
.size vendor_dbx_size, 4
vendor_dbx_size:
.long .L0 - vendor_dbx
.globl vendor_dbx
.data
.align 1
.type vendor_dbx, @object
.size vendor_dbx_size, vendor_dbx_size-vendor_dbx
vendor_dbx:
.incbin VENDOR_DBX_FILE
.L0:
#else
.globl vendor_dbx
.bss
.type vendor_dbx, @object
.size vendor_dbx, 1
vendor_dbx:
.zero 1
.globl vendor_dbx_size
.data
.align 4
.type vendor_dbx_size, @object
.size vendor_dbx_size, 4
vendor_dbx_size:
.long 0
#endif

229
shim.c
View File

@ -52,9 +52,13 @@ static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TAB
*/
extern UINT8 vendor_cert[];
extern UINT32 vendor_cert_size;
extern EFI_SIGNATURE_LIST *vendor_dbx;
extern UINT32 vendor_dbx_size;
#define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }}
static UINT8 insecure_mode;
typedef enum {
DATA_FOUND,
DATA_NOT_FOUND,
@ -79,8 +83,7 @@ static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes,
return efi_status;
}
if (allocate)
*buffer = AllocatePool(*size);
*buffer = AllocatePool(*size);
if (!*buffer) {
Print(L"Unable to allocate variable buffer\n");
@ -208,26 +211,16 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
return EFI_SUCCESS;
}
static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash)
static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
UINTN dbsize,
WIN_CERTIFICATE_EFI_PKCS *data,
UINT8 *hash)
{
EFI_STATUS efi_status;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
UINTN dbsize = 0;
UINTN CertCount, Index;
UINT32 attributes;
BOOLEAN IsFound = FALSE;
void *db;
EFI_GUID CertType = EfiCertX509Guid;
efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db);
if (efi_status != EFI_SUCCESS)
return VAR_NOT_FOUND;
CertList = db;
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
if (CompareGuid (&CertList->SignatureType, &CertType) == 0) {
CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize;
@ -240,43 +233,57 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
hash, SHA256_DIGEST_SIZE);
if (IsFound)
break;
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
if (IsFound)
break;
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
FreePool(db);
if (IsFound)
return DATA_FOUND;
return DATA_NOT_FOUND;
}
static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
int SignatureSize, EFI_GUID CertType)
static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash)
{
CHECK_STATUS rc;
EFI_STATUS efi_status;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
UINTN dbsize = 0;
UINTN CertCount, Index;
UINT32 attributes;
BOOLEAN IsFound = FALSE;
void *db;
efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db);
if (efi_status != EFI_SUCCESS) {
if (efi_status != EFI_SUCCESS)
return VAR_NOT_FOUND;
}
CertList = db;
rc = check_db_cert_in_ram(CertList, dbsize, data, hash);
FreePool(db);
return rc;
}
static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList,
UINTN dbsize, UINT8 *data,
int SignatureSize, EFI_GUID CertType)
{
EFI_SIGNATURE_DATA *Cert;
UINTN CertCount, Index;
BOOLEAN IsFound = FALSE;
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize;
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
@ -301,19 +308,53 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
FreePool(db);
if (IsFound)
return DATA_FOUND;
return DATA_NOT_FOUND;
}
static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
int SignatureSize, EFI_GUID CertType)
{
EFI_STATUS efi_status;
EFI_SIGNATURE_LIST *CertList;
UINT32 attributes;
UINTN dbsize = 0;
void *db;
efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db);
if (efi_status != EFI_SUCCESS) {
return VAR_NOT_FOUND;
}
CertList = db;
CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data,
SignatureSize, CertType);
FreePool(db);
return rc;
}
static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
UINT8 *sha256hash, UINT8 *sha1hash)
{
EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
if (check_db_hash_in_ram(vendor_dbx, vendor_dbx_size, sha256hash,
SHA256_DIGEST_SIZE, EfiHashSha256Guid) ==
DATA_FOUND)
return EFI_ACCESS_DENIED;
if (check_db_hash_in_ram(vendor_dbx, vendor_dbx_size, sha1hash,
SHA1_DIGEST_SIZE, EfiHashSha1Guid) ==
DATA_FOUND)
return EFI_ACCESS_DENIED;
if (check_db_cert_in_ram(vendor_dbx, vendor_dbx_size, cert,
sha256hash) == DATA_FOUND)
return EFI_ACCESS_DENIED;
if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE,
EfiHashSha256Guid) == DATA_FOUND)
return EFI_ACCESS_DENIED;
@ -361,6 +402,9 @@ static BOOLEAN secure_mode (void)
UINT8 sb, setupmode;
UINT32 attributes;
if (insecure_mode)
return FALSE;
status = get_variable(L"SecureBoot", global_var, &attributes, &charsize,
(void *)&sb);
@ -505,7 +549,8 @@ static EFI_STATUS generate_hash (char *data, int datasize,
if (!hashbase) {
Print(L"Malformed section header\n");
return EFI_INVALID_PARAMETER;
status = EFI_INVALID_PARAMETER;
goto done;
}
if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
@ -585,6 +630,11 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
WIN_CERTIFICATE_EFI_PKCS *cert;
unsigned int size = datasize;
if (context->SecDir->Size == 0) {
Print(L"Empty security header\n");
return EFI_INVALID_PARAMETER;
}
cert = ImageAddress (data, size, context->SecDir->VirtualAddress);
if (!cert) {
@ -643,9 +693,19 @@ static EFI_STATUS read_header(void *data, unsigned int datasize,
EFI_IMAGE_DOS_HEADER *DosHdr = data;
EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
if (datasize < sizeof(EFI_IMAGE_DOS_HEADER)) {
Print(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE)
PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew);
if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) {
Print(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
Print(L"Unsupported image type\n");
return EFI_UNSUPPORTED;
@ -672,13 +732,18 @@ static EFI_STATUS read_header(void *data, unsigned int datasize,
context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER));
context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
if (context->SecDir->VirtualAddress >= datasize) {
Print(L"Malformed security header\n");
return EFI_INVALID_PARAMETER;
if (context->ImageSize < context->SizeOfHeaders) {
Print(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
if (context->SecDir->Size == 0) {
Print(L"Empty security header\n");
if (((UINT8 *)context->SecDir - (UINT8 *)data) > (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
Print(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
if (context->SecDir->VirtualAddress >= datasize) {
Print(L"Malformed security header\n");
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
@ -790,7 +855,7 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
bootpath[i+1] = '\0';
if (bootpath[i-i] == '\\')
if (i == 0 || bootpath[i-i] == '\\')
bootpath[i] = '\0';
*PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
@ -829,7 +894,8 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
device = li->DeviceHandle;
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
&simple_file_system_protocol, &drive);
&simple_file_system_protocol,
(void **)&drive);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to find fs\n");
@ -863,6 +929,7 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
&buffersize, fileinfo);
if (efi_status == EFI_BUFFER_TOO_SMALL) {
FreePool(fileinfo);
fileinfo = AllocatePool(buffersize);
if (!fileinfo) {
Print(L"Unable to allocate file info buffer\n");
@ -905,14 +972,15 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
*datasize = buffersize;
FreePool(fileinfo);
return EFI_SUCCESS;
error:
if (*data) {
FreePool(*data);
*data = NULL;
}
if (PathName)
FreePool(PathName);
if (fileinfo)
FreePool(fileinfo);
return efi_status;
@ -949,7 +1017,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
int datasize;
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
&loaded_image_protocol, &li);
&loaded_image_protocol, (void **)&li);
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to init protocol\n");
@ -996,10 +1064,16 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
goto done;
}
efi_status = uefi_call_wrapper(entry_point, 3, image_handle, systab);
efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab);
CopyMem(li, &li_bak, sizeof(li_bak));
done:
if (PathName)
FreePool(PathName);
if (data)
FreePool(data);
return efi_status;
}
@ -1048,35 +1122,71 @@ done:
return efi_status;
}
EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
static BOOLEAN check_var(CHAR16 *varname)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
UINTN size = sizeof(UINT32);
UINT32 MokNew;
UINT32 MokVar;
UINT32 attributes;
if (!secure_mode())
return EFI_SUCCESS;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokNew",
efi_status = uefi_call_wrapper(RT->GetVariable, 5, varname,
&shim_lock_guid, &attributes,
&size, (void *)&MokNew);
&size, (void *)&MokVar);
if (efi_status != EFI_SUCCESS && efi_status != EFI_BUFFER_TOO_SMALL)
goto done;
if (efi_status == EFI_SUCCESS || efi_status == EFI_BUFFER_TOO_SMALL)
return TRUE;
efi_status = start_image(image_handle, MOK_MANAGER);
return FALSE;
}
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to start MokManager\n");
goto done;
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")) {
efi_status = start_image(image_handle, MOK_MANAGER);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to start MokManager\n");
return efi_status;
}
}
done:
return efi_status;
return EFI_SUCCESS;
}
static EFI_STATUS check_mok_sb (void)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS status = EFI_SUCCESS;
void *MokSBState = NULL;
UINTN MokSBStateSize = 0;
UINT32 attributes;
status = get_variable(L"MokSBState", shim_lock_guid, &attributes,
&MokSBStateSize, &MokSBState);
if (status != EFI_SUCCESS)
return EFI_ACCESS_DENIED;
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
Print(L"MokSBState is compromised! Clearing it\n");
if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to erase MokSBState\n");
}
status = EFI_ACCESS_DENIED;
} else {
if (*(UINT8 *)MokSBState == 1) {
insecure_mode = 1;
}
}
return status;
}
EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
@ -1092,6 +1202,13 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
InitializeLib(image_handle, systab);
check_mok_sb();
if (insecure_mode) {
Print(L"Booting in insecure mode\n");
uefi_call_wrapper(BS->Stall, 1, 2000000);
}
efi_status = check_mok_request(image_handle);
efi_status = mirror_mok_list();