Switch to using db format for MokList and MokNew

Using the same format as the UEFI key databases makes it easier for the
kernel to parse and extract keys from MOK, and also permits MOK to contain
multiple key or hash types. Additionally, add support for enrolling hashes.
This commit is contained in:
Matthew Garrett 2012-10-11 15:54:36 -04:00
parent 7f99a97c6b
commit 0848fab98d
4 changed files with 320 additions and 191 deletions

View File

@ -3,6 +3,8 @@
#include <Library/BaseCryptLib.h>
#include <openssl/x509.h>
#include "shim.h"
#include "signature.h"
#include "PeImage.h"
#define PASSWORD_MAX 16
#define PASSWORD_MIN 8
@ -11,11 +13,14 @@
#define SHIM_VENDOR L"Shim"
#endif
#define EFI_VARIABLE_APPEND_WRITE 0x00000040
struct menu_item {
CHAR16 *text;
INTN (* callback)(void *data, void *data2);
INTN (* callback)(void *data, void *data2, void *data3);
void *data;
void *data2;
void *data3;
UINTN colour;
};
@ -75,12 +80,12 @@ done:
static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
MokListNode *list;
INT64 remain = DataSize;
int i;
void *ptr;
if (DataSize < sizeof(UINT32))
return NULL;
EFI_SIGNATURE_LIST *CertList = Data;
EFI_SIGNATURE_DATA *Cert;
EFI_GUID CertType = EfiCertX509Guid;
EFI_GUID HashType = EfiHashSha256Guid;
UINTN dbsize = DataSize;
UINTN count = 0;
list = AllocatePool(sizeof(MokListNode) * num);
@ -89,20 +94,33 @@ static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
return NULL;
}
ptr = Data;
for (i = 0; i < num; i++) {
CopyMem(&list[i].MokSize, ptr, sizeof(UINT32));
remain -= sizeof(UINT32) + list[i].MokSize;
if (remain < 0) {
Print(L"the list was corrupted\n");
FreePool(list);
return NULL;
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
(CompareGuid (&CertList->SignatureType, &HashType) != 0)) {
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
CertList->SignatureSize);
continue;
}
ptr += sizeof(UINT32);
list[i].Mok = ptr;
ptr += list[i].MokSize;
if ((CompareGuid (&CertList->SignatureType, &HashType) == 0) &&
(CertList->SignatureSize != 48)) {
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
CertList->SignatureSize);
continue;
}
Cert = (EFI_SIGNATURE_DATA *) (((UINT8 *) CertList) +
sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
list[count].MokSize = CertList->SignatureSize;
list[count].Mok = (void *)Cert->SignatureData;
count++;
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureSize);
}
return list;
@ -289,16 +307,25 @@ static void show_mok_info (void *Mok, UINTN MokSize)
if (!Mok || MokSize == 0)
return;
if (X509ConstructCertificate(Mok, MokSize, (UINT8 **) &X509Cert) &&
X509Cert != NULL) {
show_x509_info(X509Cert);
X509_free(X509Cert);
if (MokSize != 48) {
if (X509ConstructCertificate(Mok, MokSize, (UINT8 **) &X509Cert) &&
X509Cert != NULL) {
show_x509_info(X509Cert);
X509_free(X509Cert);
} else {
Print(L" Not a valid X509 certificate: %x\n\n",
((UINT32 *)Mok)[0]);
return;
}
} else {
Print(L" Not a valid X509 certificate: %x\n\n",
((UINT32 *)Mok)[0]);
return;
Print(L"SHA256 hash:\n ");
for (i = 0; i < SHA256_DIGEST_SIZE; i++) {
Print(L" %02x", ((UINT8 *)Mok)[i]);
if (i % 10 == 9)
Print(L"\n ");
}
Print(L"\n");
}
efi_status = get_sha1sum(Mok, MokSize, hash);
if (efi_status != EFI_SUCCESS) {
@ -354,20 +381,48 @@ static INTN get_number ()
static UINT8 list_keys (void *MokNew, UINTN MokNewSize)
{
UINT32 MokNum;
UINT32 MokNum = 0;
MokListNode *keys = NULL;
INTN key_num = 0;
UINT8 initial = 1;
EFI_SIGNATURE_LIST *CertList = MokNew;
EFI_GUID CertType = EfiCertX509Guid;
EFI_GUID HashType = EfiHashSha256Guid;
UINTN dbsize = MokNewSize;
CopyMem(&MokNum, MokNew, sizeof(UINT32));
if (MokNum == 0) {
Print(L"No key exists\n");
if (MokNewSize < (sizeof(EFI_SIGNATURE_LIST) +
sizeof(EFI_SIGNATURE_DATA))) {
Print(L"No keys\n");
Pause();
return 0;
}
keys = build_mok_list(MokNum,
(void *)MokNew + sizeof(UINT32),
MokNewSize - sizeof(UINT32));
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
(CompareGuid (&CertList->SignatureType, &HashType) != 0)) {
Print(L"Doesn't look like a key or hash\n");
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureSize);
continue;
}
if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
(CertList->SignatureSize != 48)) {
Print(L"Doesn't look like a valid hash\n");
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureSize);
continue;
}
MokNum++;
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureSize);
}
keys = build_mok_list(MokNum, MokNew, MokNewSize);
if (!keys) {
Print(L"Failed to construct key list in MokNew\n");
@ -466,10 +521,12 @@ static EFI_STATUS compute_pw_hash (void *MokNew, UINTN MokNewSize, CHAR16 *passw
goto done;
}
if (!(Sha256Update(ctx, MokNew, MokNewSize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
if (MokNew && MokNewSize) {
if (!(Sha256Update(ctx, MokNew, MokNewSize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
}
if (!(Sha256Update(ctx, password, pw_length * sizeof(CHAR16)))) {
@ -542,12 +599,24 @@ static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate)
return EFI_ACCESS_DENIED;
}
/* Write new MOK */
efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
&shim_lock_guid,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS,
MokNewSize, MokNew);
if (!MokNewSize) {
/* Delete MOK */
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,
0, NULL);
} else {
/* Write new MOK */
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,
MokNewSize, MokNew);
}
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to set variable %d\n", efi_status);
return efi_status;
@ -583,11 +652,12 @@ static UINTN mok_enrollment_prompt (void *MokNew, UINTN MokNewSize, int auth) {
return -1;
}
static INTN mok_enrollment_prompt_callback (void *MokNew, void *data2) {
static INTN mok_enrollment_prompt_callback (void *MokNew, void *data2,
void *data3) {
return mok_enrollment_prompt(MokNew, (UINTN)data2, TRUE);
}
static INTN mok_deletion_prompt (void *MokNew, void *data2) {
static INTN mok_deletion_prompt (void *MokNew, void *data2, void *data3) {
CHAR16 line[1];
UINT32 length;
EFI_STATUS efi_status;
@ -597,7 +667,7 @@ static INTN mok_deletion_prompt (void *MokNew, void *data2) {
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
efi_status = store_keys(MokNew, sizeof(UINT32), TRUE);
efi_status = store_keys(NULL, 0, TRUE);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to erase keys\n");
@ -713,7 +783,8 @@ static void run_menu (struct menu_item *items, UINTN count, UINTN timeout) {
return;
}
items[pos].callback(items[pos].data, items[pos].data2);
items[pos].callback(items[pos].data, items[pos].data2,
items[pos].data3);
draw_menu (items, count);
pos = 0;
break;
@ -721,20 +792,36 @@ static void run_menu (struct menu_item *items, UINTN count, UINTN timeout) {
}
}
static INTN file_callback (void *data, void *data2) {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
static UINTN verify_certificate(void *cert, UINTN size)
{
X509 *X509Cert;
if (!cert || size == 0)
return FALSE;
if (!(X509ConstructCertificate(cert, size, (UINT8 **) &X509Cert)) ||
X509Cert == NULL) {
Print(L"Invalid X509 certificate\n");
Pause();
return FALSE;
}
X509_free(X509Cert);
return TRUE;
}
static INTN file_callback (void *data, void *data2, void *data3) {
EFI_FILE_INFO *buffer = NULL;
UINTN buffersize = 0, readsize;
UINTN buffersize = 0, mokbuffersize;
EFI_STATUS status;
EFI_FILE *file;
CHAR16 *filename = data;
EFI_FILE *parent = data2;
BOOLEAN hash = !!data3;
EFI_GUID file_info_guid = EFI_FILE_INFO_ID;
void *mokbuffer = NULL, *mok;
UINTN MokSize = 0, MokNewSize;
MokListNode *MokNew;
mok = LibGetVariableAndSize(L"MokList", &shim_lock_guid, &MokSize);
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *CertData;
void *mokbuffer = NULL;
status = uefi_call_wrapper(parent->Open, 5, parent, &file, filename,
EFI_FILE_MODE_READ, 0);
@ -755,36 +842,84 @@ static INTN file_callback (void *data, void *data2) {
if (!buffer)
return 0;
readsize = buffer->FileSize;
buffersize = buffer->FileSize;
if (mok) {
MokNewSize = MokSize + readsize + sizeof(UINT32);
mokbuffer = AllocateZeroPool(MokNewSize);
if (hash) {
void *binary;
UINT8 sha256[SHA256_DIGEST_SIZE];
UINT8 sha1[SHA1_DIGEST_SIZE];
SHIM_LOCK *shim_lock;
EFI_GUID shim_guid = SHIM_LOCK_GUID;
PE_COFF_LOADER_IMAGE_CONTEXT context;
status = LibLocateProtocol(&shim_guid, (VOID **)&shim_lock);
if (status != EFI_SUCCESS)
goto out;
mokbuffersize = sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID) +
SHA256_DIGEST_SIZE;
mokbuffer = AllocatePool(mokbuffersize);
if (!mokbuffer)
goto out;
CopyMem(mokbuffer, mok, MokSize);
((UINT32 *)mokbuffer)[0]++;
MokNew = (MokListNode *)(((char *)mokbuffer) + MokSize);
binary = AllocatePool(buffersize);
status = uefi_call_wrapper(file->Read, 3, file, &buffersize,
binary);
if (status != EFI_SUCCESS)
goto out;
status = shim_lock->Context(binary, buffersize, &context);
if (status != EFI_SUCCESS)
goto out;
status = shim_lock->Hash(binary, buffersize, &context, sha256,
sha1);
if (status != EFI_SUCCESS)
goto out;
CertList = mokbuffer;
CertList->SignatureType = EfiHashSha256Guid;
CertList->SignatureSize = 16 + SHA256_DIGEST_SIZE;
CertData = (EFI_SIGNATURE_DATA *)(((UINT8 *)mokbuffer) +
sizeof(EFI_SIGNATURE_LIST));
CopyMem(CertData->SignatureData, sha256, SHA256_DIGEST_SIZE);
} else {
MokNewSize = readsize + (2 * sizeof(UINT32));
mokbuffer = AllocateZeroPool(MokNewSize);
mokbuffersize = buffersize + sizeof(EFI_SIGNATURE_LIST) +
sizeof(EFI_GUID);
mokbuffer = AllocatePool(mokbuffersize);
if (!mokbuffer)
goto out;
((UINT32 *)mokbuffer)[0]=1;
MokNew = (MokListNode *)(((UINT32 *)mokbuffer) + 1);
CertList = mokbuffer;
CertList->SignatureType = EfiCertX509Guid;
CertList->SignatureSize = 16 + buffersize;
status = uefi_call_wrapper(file->Read, 3, file, &buffersize,
mokbuffer + sizeof(EFI_SIGNATURE_LIST) + 16);
if (status != EFI_SUCCESS)
goto out;
CertData = (EFI_SIGNATURE_DATA *)(((UINT8 *)mokbuffer) +
sizeof(EFI_SIGNATURE_LIST));
}
MokNew->MokSize = readsize;
CertList->SignatureListSize = mokbuffersize;
CertList->SignatureHeaderSize = 0;
CertData->SignatureOwner = shim_lock_guid;
status = uefi_call_wrapper(file->Read, 3, file, &readsize, &MokNew->Mok);
if (!hash) {
if (!verify_certificate(CertData->SignatureData, buffersize))
goto out;
}
if (status != EFI_SUCCESS)
goto out;
mok_enrollment_prompt(mokbuffer, MokNewSize, FALSE);
mok_enrollment_prompt(mokbuffer, mokbuffersize, FALSE);
out:
if (buffer)
FreePool(buffer);
@ -795,7 +930,7 @@ out:
return 0;
}
static INTN directory_callback (void *data, void *data2) {
static INTN directory_callback (void *data, void *data2, void *data3) {
EFI_FILE_INFO *buffer = NULL;
UINTN buffersize = 0;
EFI_STATUS status;
@ -873,12 +1008,14 @@ static INTN directory_callback (void *data, void *data2) {
dircontent[i].callback = directory_callback;
dircontent[i].data = dircontent[i].text;
dircontent[i].data2 = dir;
dircontent[i].data3 = data3;
dircontent[i].colour = EFI_YELLOW;
} else {
dircontent[i].text = StrDuplicate(buffer->FileName);
dircontent[i].callback = file_callback;
dircontent[i].data = dircontent[i].text;
dircontent[i].data2 = dir;
dircontent[i].data3 = data3;
dircontent[i].colour = EFI_WHITE;
}
@ -893,7 +1030,7 @@ static INTN directory_callback (void *data, void *data2) {
return 0;
}
static INTN filesystem_callback (void *data, void *data2) {
static INTN filesystem_callback (void *data, void *data2, void *data3) {
EFI_FILE_INFO *buffer = NULL;
UINTN buffersize = 0;
EFI_STATUS status;
@ -965,12 +1102,14 @@ static INTN filesystem_callback (void *data, void *data2) {
dircontent[i].callback = directory_callback;
dircontent[i].data = dircontent[i].text;
dircontent[i].data2 = root;
dircontent[i].data3 = data3;
dircontent[i].colour = EFI_YELLOW;
} else {
dircontent[i].text = StrDuplicate(buffer->FileName);
dircontent[i].callback = file_callback;
dircontent[i].data = dircontent[i].text;
dircontent[i].data2 = root;
dircontent[i].data3 = data3;
dircontent[i].colour = EFI_WHITE;
}
@ -985,7 +1124,7 @@ static INTN filesystem_callback (void *data, void *data2) {
return 0;
}
static INTN find_fs (void *data, void *data2) {
static INTN find_fs (void *data, void *data2, void *data3) {
EFI_GUID fs_guid = SIMPLE_FILE_SYSTEM_PROTOCOL;
UINTN count, i;
EFI_HANDLE **filesystem_handles;
@ -1058,6 +1197,7 @@ static INTN find_fs (void *data, void *data2) {
filesystems[i].data = root;
filesystems[i].data2 = NULL;
filesystems[i].data3 = data3;
filesystems[i].callback = filesystem_callback;
filesystems[i].colour = EFI_YELLOW;
}
@ -1073,13 +1213,25 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew,
UINTN MokNewSize)
{
struct menu_item *menu_item;
UINT32 MokNum;
UINT32 MokAuth = 0;
UINTN menucount = 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 (MokNew)
menu_item = AllocateZeroPool(sizeof(struct menu_item) * 3);
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth",
&shim_lock_guid,
&attributes, &auth_size, auth);
if ((efi_status == EFI_SUCCESS) && (auth_size == SHA256_DIGEST_SIZE))
MokAuth = 1;
if (MokNew || MokAuth)
menu_item = AllocateZeroPool(sizeof(struct menu_item) * 4);
else
menu_item = AllocateZeroPool(sizeof(struct menu_item) * 2);
menu_item = AllocateZeroPool(sizeof(struct menu_item) * 3);
if (!menu_item)
return EFI_OUT_OF_RESOURCES;
@ -1090,12 +1242,10 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew,
menucount++;
if (MokNew) {
CopyMem(&MokNum, MokNew, sizeof(UINT32));
if (MokNum == 0) {
if (MokNew || MokAuth) {
if (!MokNew) {
menu_item[1].text = StrDuplicate(L"Delete MOK");
menu_item[1].colour = EFI_WHITE;
menu_item[1].data = MokNew;
menu_item[1].callback = mok_deletion_prompt;
} else {
menu_item[1].text = StrDuplicate(L"Enroll MOK");
@ -1110,6 +1260,14 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew,
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;
menucount++;
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;
menucount++;

159
shim.c
View File

@ -92,40 +92,6 @@ static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes,
return efi_status;
}
static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
MokListNode *list;
int i, remain = DataSize;
void *ptr;
if (DataSize < sizeof(UINT32))
return NULL;
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++) {
CopyMem(&list[i].MokSize, ptr, sizeof(UINT32));
remain -= sizeof(UINT32) + list[i].MokSize;
if (remain < 0) {
Print(L"MOK list was corrupted\n");
FreePool(list);
return NULL;
}
ptr += sizeof(UINT32);
list[i].Mok = ptr;
ptr += list[i].MokSize;
}
return list;
}
/*
* Perform basic bounds checking of the intra-image pointers
*/
@ -241,10 +207,10 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
return EFI_SUCCESS;
}
static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash)
static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash)
{
EFI_STATUS efi_status;
EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
UINTN dbsize = 0;
@ -254,7 +220,7 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data
void *db;
EFI_GUID CertType = EfiCertX509Guid;
efi_status = get_variable(dbname, secure_var, &attributes, &dbsize, &db);
efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db);
if (efi_status != EFI_SUCCESS)
return VAR_NOT_FOUND;
@ -290,11 +256,10 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data
return DATA_NOT_FOUND;
}
static CHECK_STATUS check_db_hash(CHAR16 *dbname, UINT8 *data,
static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
int SignatureSize, EFI_GUID CertType)
{
EFI_STATUS efi_status;
EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
UINTN dbsize = 0;
@ -303,7 +268,7 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, UINT8 *data,
BOOLEAN IsFound = FALSE;
void *db;
efi_status = get_variable(dbname, secure_var, &attributes, &dbsize, &db);
efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db);
if (efi_status != EFI_SUCCESS) {
return VAR_NOT_FOUND;
@ -346,13 +311,15 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, UINT8 *data,
static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
UINT8 *sha256hash, UINT8 *sha1hash)
{
if (check_db_hash(L"db", sha256hash, SHA256_DIGEST_SIZE,
EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE,
EfiHashSha256Guid) == DATA_FOUND)
return EFI_ACCESS_DENIED;
if (check_db_hash(L"db", sha1hash, SHA1_DIGEST_SIZE,
if (check_db_hash(L"dbx", secure_var, sha1hash, SHA1_DIGEST_SIZE,
EfiHashSha1Guid) == DATA_FOUND)
return EFI_ACCESS_DENIED;
if (check_db_cert(L"dbx", cert, sha256hash) == DATA_FOUND)
if (check_db_cert(L"dbx", secure_var, cert, sha256hash) == DATA_FOUND)
return EFI_ACCESS_DENIED;
return EFI_SUCCESS;
@ -361,13 +328,21 @@ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
UINT8 *sha256hash, UINT8 *sha1hash)
{
if (check_db_hash(L"db", sha256hash, SHA256_DIGEST_SIZE,
EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
EFI_GUID shim_var = SHIM_LOCK_GUID;
if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE,
EfiHashSha256Guid) == DATA_FOUND)
return EFI_SUCCESS;
if (check_db_hash(L"db", sha1hash, SHA1_DIGEST_SIZE,
if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE,
EfiHashSha1Guid) == DATA_FOUND)
return EFI_SUCCESS;
if (check_db_cert(L"db", cert, sha256hash) == DATA_FOUND)
if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE,
EfiHashSha256Guid) == DATA_FOUND)
return EFI_SUCCESS;
if (check_db_cert(L"db", secure_var, cert, sha256hash) == DATA_FOUND)
return EFI_SUCCESS;
if (check_db_cert(L"MokList", shim_var, cert, sha256hash) == DATA_FOUND)
return EFI_SUCCESS;
return EFI_ACCESS_DENIED;
@ -575,22 +550,38 @@ done:
return status;
}
static EFI_STATUS verify_mok (void) {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS status = EFI_SUCCESS;
void *MokListData = NULL;
UINTN MokListDataSize = 0;
UINT32 attributes;
status = get_variable(L"MokList", shim_lock_guid, &attributes,
&MokListDataSize, &MokListData);
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
Print(L"MokList is compromised!\nErase all keys in MokList!\n");
if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to erase MokList\n");
}
status = EFI_ACCESS_DENIED;
return status;
}
return EFI_SUCCESS;
}
/*
* Check that the signature is valid and matches the binary
*/
static EFI_STATUS verify_buffer (char *data, int datasize,
PE_COFF_LOADER_IMAGE_CONTEXT *context, int whitelist)
PE_COFF_LOADER_IMAGE_CONTEXT *context)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
UINT8 sha256hash[SHA256_DIGEST_SIZE];
UINT8 sha1hash[SHA1_DIGEST_SIZE];
EFI_STATUS status = EFI_ACCESS_DENIED;
WIN_CERTIFICATE_EFI_PKCS *cert;
void *MokListData = NULL;
UINTN MokListDataSize = 0;
UINT32 MokNum, attributes;
MokListNode *list = NULL;
unsigned int i;
unsigned int size = datasize;
cert = ImageAddress (data, size, context->SecDir->VirtualAddress);
@ -611,6 +602,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
if (status != EFI_SUCCESS)
return status;
verify_mok();
status = check_blacklist(cert, sha256hash, sha1hash);
if (status != EFI_SUCCESS) {
@ -618,13 +611,11 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
return status;
}
if (whitelist) {
status = check_whitelist(cert, sha256hash, sha1hash);
status = check_whitelist(cert, sha256hash, sha1hash);
if (status == EFI_SUCCESS) {
Print(L"Binary is whitelisted\n");
return status;
}
if (status == EFI_SUCCESS) {
Print(L"Binary is whitelisted\n");
return status;
}
if (AuthenticodeVerify(cert->CertData,
@ -636,50 +627,6 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
return status;
}
status = get_variable(L"MokList", shim_lock_guid, &attributes,
&MokListDataSize, &MokListData);
if (status != EFI_SUCCESS || MokListDataSize < sizeof(UINT32)) {
status = EFI_ACCESS_DENIED;
Print(L"Invalid signature\n");
return status;
}
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
Print(L"MokList is compromised!\nErase all keys in MokList!\n");
if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to erase MokList\n");
}
status = EFI_ACCESS_DENIED;
return status;
}
CopyMem(&MokNum, MokListData, sizeof(UINT32));
if (MokNum == 0) {
status = EFI_ACCESS_DENIED;
return status;
}
list = build_mok_list(MokNum,
(void *)MokListData + sizeof(UINT32),
MokListDataSize - sizeof(UINT32));
if (!list) {
Print(L"Failed to construct MOK list\n");
status = EFI_OUT_OF_RESOURCES;
return status;
}
for (i = 0; i < MokNum; i++) {
if (AuthenticodeVerify(cert->CertData,
context->SecDir->Size - sizeof(cert->Hdr),
list[i].Mok, list[i].MokSize, sha256hash,
SHA256_DIGEST_SIZE)) {
status = EFI_SUCCESS;
Print(L"Binary is verified by the machine owner key\n");
return status;
}
}
Print(L"Invalid signature\n");
status = EFI_ACCESS_DENIED;
@ -757,7 +704,7 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
}
if (secure_mode ()) {
efi_status = verify_buffer(data, datasize, &context, 0);
efi_status = verify_buffer(data, datasize, &context);
if (efi_status != EFI_SUCCESS) {
Print(L"Verification failed\n");
@ -984,7 +931,7 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size)
if (status != EFI_SUCCESS)
return status;
status = verify_buffer(buffer, size, &context, 1);
status = verify_buffer(buffer, size, &context);
return status;
}
@ -1133,6 +1080,8 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
EFI_STATUS efi_status;
shim_lock_interface.Verify = shim_verify;
shim_lock_interface.Hash = generate_hash;
shim_lock_interface.Context = read_header;
systab = passed_systab;

22
shim.h
View File

@ -1,3 +1,5 @@
#include "PeImage.h"
#define SHIM_LOCK_GUID \
{ 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }
@ -10,6 +12,26 @@ EFI_STATUS
IN UINT32 size
);
typedef
EFI_STATUS
(*EFI_SHIM_LOCK_HASH) (
IN char *data,
IN int datasize,
PE_COFF_LOADER_IMAGE_CONTEXT *context,
UINT8 *sha256hash,
UINT8 *sha1hash
);
typedef
EFI_STATUS
(*EFI_SHIM_LOCK_CONTEXT) (
IN VOID *data,
IN unsigned int datasize,
PE_COFF_LOADER_IMAGE_CONTEXT *context
);
typedef struct _SHIM_LOCK {
EFI_SHIM_LOCK_VERIFY Verify;
EFI_SHIM_LOCK_HASH Hash;
EFI_SHIM_LOCK_CONTEXT Context;
} SHIM_LOCK;

View File

@ -13,7 +13,7 @@ typedef struct {
/// The format of the signature is defined by the SignatureType.
///
UINT8 SignatureData[1];
} EFI_SIGNATURE_DATA;
} __attribute__ ((packed)) EFI_SIGNATURE_DATA;
typedef struct {
///
@ -40,4 +40,4 @@ typedef struct {
/// An array of signatures. Each signature is SignatureSize bytes in length.
/// EFI_SIGNATURE_DATA Signatures[][SignatureSize];
///
} EFI_SIGNATURE_LIST;
} __attribute__ ((packed)) EFI_SIGNATURE_LIST;