From 0848fab98d011c785870d7f7c9d889b829e60f26 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 11 Oct 2012 15:54:36 -0400 Subject: [PATCH] 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. --- MokManager.c | 326 ++++++++++++++++++++++++++++++++++++++------------- shim.c | 159 +++++++++---------------- shim.h | 22 ++++ signature.h | 4 +- 4 files changed, 320 insertions(+), 191 deletions(-) diff --git a/MokManager.c b/MokManager.c index 4b3b0a5af..0d07c33ff 100644 --- a/MokManager.c +++ b/MokManager.c @@ -3,6 +3,8 @@ #include #include #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 @@ static EFI_STATUS get_sha1sum (void *Data, int DataSize, UINT8 *hash) 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; + 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; + } - if (remain < 0) { - Print(L"the list was corrupted\n"); - FreePool(list); - return NULL; + if ((CompareGuid (&CertList->SignatureType, &HashType) == 0) && + (CertList->SignatureSize != 48)) { + dbsize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList + + CertList->SignatureSize); + continue; } - ptr += sizeof(UINT32); - list[i].Mok = ptr; - ptr += list[i].MokSize; + 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; - - CopyMem(&MokNum, MokNew, sizeof(UINT32)); - if (MokNum == 0) { - Print(L"No key exists\n"); + EFI_SIGNATURE_LIST *CertList = MokNew; + EFI_GUID CertType = EfiCertX509Guid; + EFI_GUID HashType = EfiHashSha256Guid; + UINTN dbsize = MokNewSize; + + 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); - } - MokNew->MokSize = readsize; + 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)); + } - status = uefi_call_wrapper(file->Read, 3, file, &readsize, &MokNew->Mok); + CertList->SignatureListSize = mokbuffersize; + CertList->SignatureHeaderSize = 0; + CertData->SignatureOwner = shim_lock_guid; - if (status != EFI_SUCCESS) - goto out; + if (!hash) { + if (!verify_certificate(CertData->SignatureData, buffersize)) + goto out; + } - mok_enrollment_prompt(mokbuffer, MokNewSize, FALSE); + mok_enrollment_prompt(mokbuffer, mokbuffersize, FALSE); out: if (buffer) FreePool(buffer); @@ -795,7 +930,7 @@ static INTN file_callback (void *data, void *data2) { 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++; diff --git a/shim.c b/shim.c index 3f5d7ea55..c1af0c8f9 100644 --- a/shim.c +++ b/shim.c @@ -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 @@ static EFI_STATUS generate_hash (char *data, int datasize, 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; diff --git a/shim.h b/shim.h index b917ea24d..081925910 100644 --- a/shim.h +++ b/shim.h @@ -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; diff --git a/signature.h b/signature.h index 5737881db..722dbe640 100644 --- a/signature.h +++ b/signature.h @@ -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;