summaryrefslogtreecommitdiff
path: root/shim.c
diff options
context:
space:
mode:
Diffstat (limited to 'shim.c')
-rw-r--r--shim.c723
1 files changed, 581 insertions, 142 deletions
diff --git a/shim.c b/shim.c
index 59825099..c3aae9e0 100644
--- a/shim.c
+++ b/shim.c
@@ -39,8 +39,11 @@
#include "PeImage.h"
#include "shim.h"
#include "signature.h"
+#include "netboot.h"
+#include "shim_cert.h"
#define SECOND_STAGE L"\\grub.efi"
+#define MOK_MANAGER L"\\MokManager.efi"
static EFI_SYSTEM_TABLE *systab;
static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
@@ -50,31 +53,38 @@ 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,
VAR_NOT_FOUND
} CHECK_STATUS;
-static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid,
+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;
- UINT32 attributes;
char allocate = !(*size);
efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
- &attributes, size, buffer);
+ attributes, size, buffer);
if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) {
return efi_status;
}
- if (allocate)
- *buffer = AllocatePool(*size);
+ *buffer = AllocatePool(*size);
if (!*buffer) {
Print(L"Unable to allocate variable buffer\n");
@@ -82,7 +92,7 @@ static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid,
}
efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
- &attributes, size, *buffer);
+ attributes, size, *buffer);
return efi_status;
}
@@ -202,25 +212,16 @@ 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_in_ram(EFI_SIGNATURE_LIST *CertList,
+ UINTN dbsize,
+ 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;
UINTN CertCount, Index;
BOOLEAN IsFound = FALSE;
- void *db;
EFI_GUID CertType = EfiCertX509Guid;
- efi_status = get_variable(dbname, secure_var, &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;
@@ -233,44 +234,60 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data
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, UINT8 *data)
+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_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
EFI_SIGNATURE_LIST *CertList;
- EFI_SIGNATURE_DATA *Cert;
UINTN dbsize = 0;
- UINTN CertCount, Index;
- BOOLEAN IsFound = FALSE;
+ UINT32 attributes;
void *db;
- unsigned int SignatureSize = SHA256_DIGEST_SIZE;
- EFI_GUID CertType = EfiHashSha256Guid;
- efi_status = get_variable(dbname, secure_var, &dbsize, &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;
+}
+
+/*
+ * Check a hash against an EFI_SIGNATURE_LIST in a buffer
+ */
+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);
@@ -295,29 +312,93 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, UINT8 *data)
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
- FreePool(db);
-
if (IsFound)
return DATA_FOUND;
return DATA_NOT_FOUND;
}
-static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *hash)
+/*
+ * Check a hash against an EFI_SIGNATURE_LIST in a UEFI variable
+ */
+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;
+
+}
+
+/*
+ * Check whether the binary signature or hash are present in dbx or the
+ * built-in blacklist
+ */
+static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
+ UINT8 *sha256hash, UINT8 *sha1hash)
{
- if (check_db_hash(L"dbx", hash) == DATA_FOUND)
+ 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(L"dbx", cert, hash) == DATA_FOUND)
+ 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;
+ 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", secure_var, cert, sha256hash) == DATA_FOUND)
return EFI_ACCESS_DENIED;
return EFI_SUCCESS;
}
-static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *hash)
+/*
+ * Check whether the binary signature or hash are present in db or MokList
+ */
+static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
+ UINT8 *sha256hash, UINT8 *sha1hash)
{
- if (check_db_hash(L"db", hash) == DATA_FOUND)
+ 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", secure_var, sha1hash, SHA1_DIGEST_SIZE,
+ EfiHashSha1Guid) == DATA_FOUND)
return EFI_SUCCESS;
- if (check_db_cert(L"db", cert, hash) == 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;
@@ -333,8 +414,13 @@ static BOOLEAN secure_mode (void)
EFI_GUID global_var = EFI_GLOBAL_VARIABLE;
UINTN charsize = sizeof(char);
UINT8 sb, setupmode;
+ UINT32 attributes;
+
+ if (insecure_mode)
+ return FALSE;
- status = get_variable(L"SecureBoot", global_var, &charsize, (void *)&sb);
+ status = get_variable(L"SecureBoot", global_var, &attributes, &charsize,
+ (void *)&sb);
/* FIXME - more paranoia here? */
if (status != EFI_SUCCESS || sb != 1) {
@@ -342,7 +428,8 @@ static BOOLEAN secure_mode (void)
return FALSE;
}
- status = get_variable(L"SetupMode", global_var, &charsize, (void *)&setupmode);
+ status = get_variable(L"SetupMode", global_var, &attributes, &charsize,
+ (void *)&setupmode);
if (status == EFI_SUCCESS && setupmode == 1) {
Print(L"Platform is in setup mode\n");
@@ -353,49 +440,38 @@ static BOOLEAN secure_mode (void)
}
/*
- * Check that the signature is valid and matches the binary
+ * Calculate the SHA1 and SHA256 hashes of a binary
*/
-static EFI_STATUS verify_buffer (char *data, int datasize,
- PE_COFF_LOADER_IMAGE_CONTEXT *context, int whitelist)
+
+static EFI_STATUS generate_hash (char *data, int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context,
+ UINT8 *sha256hash, UINT8 *sha1hash)
+
{
+ unsigned int sha256ctxsize, sha1ctxsize;
unsigned int size = datasize;
- unsigned int ctxsize;
- void *ctx = NULL;
- UINT8 hash[SHA256_DIGEST_SIZE];
- EFI_STATUS status = EFI_ACCESS_DENIED;
+ void *sha256ctx = NULL, *sha1ctx = NULL;
char *hashbase;
unsigned int hashsize;
- WIN_CERTIFICATE_EFI_PKCS *cert;
unsigned int SumOfBytesHashed, SumOfSectionBytes;
unsigned int index, pos;
EFI_IMAGE_SECTION_HEADER *Section;
EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL;
EFI_IMAGE_SECTION_HEADER *SectionCache;
+ EFI_STATUS status = EFI_SUCCESS;
- cert = ImageAddress (data, size, context->SecDir->VirtualAddress);
-
- if (!cert) {
- Print(L"Certificate located outside the image\n");
- return EFI_INVALID_PARAMETER;
- }
-
- if (cert->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
- Print(L"Unsupported certificate type %x\n",
- cert->Hdr.wCertificateType);
- return EFI_UNSUPPORTED;
- }
-
- /* FIXME: Check which kind of hash */
+ sha256ctxsize = Sha256GetContextSize();
+ sha256ctx = AllocatePool(sha256ctxsize);
- ctxsize = Sha256GetContextSize();
- ctx = AllocatePool(ctxsize);
+ sha1ctxsize = Sha1GetContextSize();
+ sha1ctx = AllocatePool(sha1ctxsize);
- if (!ctx) {
+ if (!sha256ctx || !sha1ctx) {
Print(L"Unable to allocate memory for hash context\n");
return EFI_OUT_OF_RESOURCES;
}
- if (!Sha256Init(ctx)) {
+ if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) {
Print(L"Unable to initialise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -406,7 +482,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum -
hashbase;
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -417,7 +494,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
sizeof (int);
hashsize = (char *)context->SecDir - hashbase;
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -428,7 +506,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
hashsize = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders -
(int) ((char *) (&context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - data);
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -484,10 +563,12 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
if (!hashbase) {
Print(L"Malformed section header\n");
- return EFI_INVALID_PARAMETER;
+ status = EFI_INVALID_PARAMETER;
+ goto done;
}
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -503,50 +584,145 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -
SumOfBytesHashed);
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
}
- if (!(Sha256Final(ctx, hash))) {
+ if (!(Sha256Final(sha256ctx, sha256hash)) ||
+ !(Sha1Final(sha1ctx, sha1hash))) {
Print(L"Unable to finalise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
- status = check_blacklist(cert, hash);
+done:
+ if (SectionHeader)
+ FreePool(SectionHeader);
+ if (sha1ctx)
+ FreePool(sha1ctx);
+ if (sha256ctx)
+ FreePool(sha256ctx);
+
+ return status;
+}
+
+/*
+ * Ensure that the MOK database hasn't been set or modified from an OS
+ */
+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)
+{
+ UINT8 sha256hash[SHA256_DIGEST_SIZE];
+ UINT8 sha1hash[SHA1_DIGEST_SIZE];
+ EFI_STATUS status = EFI_ACCESS_DENIED;
+ 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) {
+ Print(L"Certificate located outside the image\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (cert->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+ Print(L"Unsupported certificate type %x\n",
+ cert->Hdr.wCertificateType);
+ return EFI_UNSUPPORTED;
+ }
+
+ status = generate_hash(data, datasize, context, sha256hash, sha1hash);
+
+ if (status != EFI_SUCCESS)
+ return status;
+
+ /*
+ * Check that the MOK database hasn't been modified
+ */
+ verify_mok();
+
+ /*
+ * Ensure that the binary isn't blacklisted
+ */
+ status = check_blacklist(cert, sha256hash, sha1hash);
if (status != EFI_SUCCESS) {
Print(L"Binary is blacklisted\n");
- goto done;
+ return status;
}
- if (whitelist) {
- status = check_whitelist(cert, hash);
+ /*
+ * Check whether the binary is whitelisted in any of the firmware
+ * databases
+ */
+ status = check_whitelist(cert, sha256hash, sha1hash);
- if (status == EFI_SUCCESS) {
- Print(L"Binary is whitelisted\n");
- goto done;
- }
+ if (status == EFI_SUCCESS) {
+ Print(L"Binary is whitelisted\n");
+ return status;
}
- if (!AuthenticodeVerify(cert->CertData,
- context->SecDir->Size - sizeof(cert->Hdr),
- vendor_cert, vendor_cert_size, hash,
- SHA256_DIGEST_SIZE)) {
- Print(L"Invalid signature\n");
- status = EFI_ACCESS_DENIED;
- } else {
+ /*
+ * Check against the shim build key
+ */
+ if (AuthenticodeVerify(cert->CertData,
+ context->SecDir->Size - sizeof(cert->Hdr),
+ shim_cert, sizeof(shim_cert), sha256hash,
+ SHA256_DIGEST_SIZE)) {
status = EFI_SUCCESS;
+ Print(L"Binary is verified by the vendor certificate\n");
+ return status;
}
-done:
- if (SectionHeader)
- FreePool(SectionHeader);
- if (ctx)
- FreePool(ctx);
+
+ /*
+ * And finally, check against shim's built-in key
+ */
+ if (AuthenticodeVerify(cert->CertData,
+ context->SecDir->Size - sizeof(cert->Hdr),
+ vendor_cert, vendor_cert_size, sha256hash,
+ SHA256_DIGEST_SIZE)) {
+ status = EFI_SUCCESS;
+ Print(L"Binary is verified by the vendor certificate\n");
+ return status;
+ }
+
+ Print(L"Invalid signature\n");
+ status = EFI_ACCESS_DENIED;
return status;
}
@@ -560,9 +736,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;
@@ -589,24 +775,28 @@ 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");
- return EFI_INVALID_PARAMETER;
+ 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;
}
/*
* Once the image has been loaded it needs to be validated and relocated
*/
-static EFI_STATUS handle_grub (void *data, unsigned int datasize,
- EFI_LOADED_IMAGE *li)
+static EFI_STATUS handle_image (void *data, unsigned int datasize,
+ EFI_LOADED_IMAGE *li)
{
EFI_STATUS efi_status;
char *buffer;
@@ -615,14 +805,20 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
char *base, *end;
PE_COFF_LOADER_IMAGE_CONTEXT context;
+ /*
+ * The binary header contains relevant context and section pointers
+ */
efi_status = read_header(data, datasize, &context);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to read header\n");
return efi_status;
}
+ /*
+ * We only need to verify the binary if we're in secure mode
+ */
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");
@@ -639,6 +835,9 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
CopyMem(buffer, data, context.SizeOfHeaders);
+ /*
+ * Copy the executable's sections to their desired offsets
+ */
Section = context.FirstSection;
for (i = 0; i < context.NumberOfSections; i++) {
size = Section->Misc.VirtualSize;
@@ -663,6 +862,9 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
Section += 1;
}
+ /*
+ * Run the relocation fixups
+ */
efi_status = relocate_coff(&context, buffer);
if (efi_status != EFI_SUCCESS) {
@@ -672,6 +874,10 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
}
entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
+ /*
+ * grub needs to know its location and size in memory, so fix up
+ * the loaded image protocol values
+ */
li->ImageBase = buffer;
li->ImageSize = context.ImageSize;
@@ -684,7 +890,12 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
return EFI_SUCCESS;
}
-static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath, CHAR16 **PathName)
+/*
+ * Generate the path of an executable given shim's path and the name
+ * of the executable
+ */
+static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
+ EFI_DEVICE_PATH **grubpath, CHAR16 **PathName)
{
EFI_DEVICE_PATH *devpath;
EFI_HANDLE device;
@@ -707,10 +918,10 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath
bootpath[i+1] = '\0';
- if (bootpath[i-i] == '\\')
+ if (i == 0 || bootpath[i-i] == '\\')
bootpath[i] = '\0';
- *PathName = AllocatePool(StrSize(bootpath) + StrSize(SECOND_STAGE));
+ *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
if (!*PathName) {
Print(L"Failed to allocate path buffer\n");
@@ -720,7 +931,7 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath
*PathName[0] = '\0';
StrCat(*PathName, bootpath);
- StrCat(*PathName, SECOND_STAGE);
+ StrCat(*PathName, ImagePath);
*grubpath = FileDevicePath(device, *PathName);
@@ -729,10 +940,10 @@ error:
}
/*
- * Locate the second stage bootloader and read it into a buffer
+ * Open the second stage bootloader and read it into a buffer
*/
-static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
- int *datasize, CHAR16 *PathName)
+static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
+ int *datasize, CHAR16 *PathName)
{
EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL;
EFI_GUID file_info_id = EFI_FILE_INFO_ID;
@@ -745,8 +956,12 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
device = li->DeviceHandle;
+ /*
+ * Open the device
+ */
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");
@@ -760,6 +975,9 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
goto error;
}
+ /*
+ * And then open the file
+ */
efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName,
EFI_FILE_MODE_READ, 0);
@@ -776,10 +994,15 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
goto error;
}
+ /*
+ * Find out how big the file is in order to allocate the storage
+ * buffer
+ */
efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id,
&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");
@@ -805,6 +1028,10 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
+
+ /*
+ * Perform the actual read
+ */
efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize,
*data);
@@ -822,19 +1049,24 @@ static EFI_STATUS load_grub (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;
}
+/*
+ * Protocol entry point. If secure boot is enabled, verify that the provided
+ * buffer is signed with a trusted key.
+ */
EFI_STATUS shim_verify (void *buffer, UINT32 size)
{
EFI_STATUS status;
@@ -848,75 +1080,238 @@ 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;
}
-EFI_STATUS init_grub(EFI_HANDLE image_handle)
+/*
+ * Load and run an EFI executable
+ */
+EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
{
+ EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
EFI_STATUS efi_status;
- EFI_HANDLE grub_handle = NULL;
EFI_LOADED_IMAGE *li, li_bak;
- EFI_DEVICE_PATH *grubpath;
- CHAR16 *PathName;
- EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
+ EFI_DEVICE_PATH *path;
+ CHAR16 *PathName = NULL;
+ void *sourcebuffer = NULL;
+ UINTN sourcesize = 0;
void *data = NULL;
int datasize;
+ /*
+ * We need to refer to the loaded image protocol on the running
+ * binary in order to find our path
+ */
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");
return efi_status;
}
- efi_status = generate_path(li, &grubpath, &PathName);
+ /*
+ * Build a new path from the existing one plus the executable name
+ */
+ efi_status = generate_path(li, ImagePath, &path, &PathName);
if (efi_status != EFI_SUCCESS) {
- Print(L"Unable to generate grub path\n");
+ Print(L"Unable to generate path: %s\n", ImagePath);
goto done;
}
- efi_status = uefi_call_wrapper(BS->LoadImage, 6, FALSE, image_handle,
- grubpath, NULL, 0, &grub_handle);
-
+ if (findNetboot(image_handle)) {
+ efi_status = parseNetbootinfo(image_handle);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Netboot parsing failed: %d\n", efi_status);
+ return EFI_PROTOCOL_ERROR;
+ }
+ efi_status = FetchNetbootimage(image_handle, &sourcebuffer,
+ &sourcesize);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to fetch TFTP image\n");
+ return efi_status;
+ }
+ data = sourcebuffer;
+ datasize = sourcesize;
+ } else {
+ /*
+ * Read the new executable off disk
+ */
+ efi_status = load_image(li, &data, &datasize, PathName);
- if (efi_status == EFI_SUCCESS) {
- /* Image validates - start it */
- Print(L"Starting file via StartImage\n");
- efi_status = uefi_call_wrapper(BS->StartImage, 3, grub_handle, NULL,
- NULL);
- uefi_call_wrapper(BS->UnloadImage, 1, grub_handle);
- goto done;
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to load image\n");
+ goto done;
+ }
}
- efi_status = load_grub(li, &data, &datasize, PathName);
+ /*
+ * We need to modify the loaded image protocol entry before running
+ * the new binary, so back it up
+ */
+ CopyMem(&li_bak, li, sizeof(li_bak));
+
+ /*
+ * Verify and, if appropriate, relocate and execute the executable
+ */
+ efi_status = handle_image(data, datasize, li);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to load grub\n");
+ Print(L"Failed to load image\n");
+ CopyMem(li, &li_bak, sizeof(li_bak));
goto done;
}
- CopyMem(&li_bak, li, sizeof(li_bak));
+ /*
+ * The binary is trusted and relocated. Run it
+ */
+ efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab);
- efi_status = handle_grub(data, datasize, li);
+ /*
+ * Restore our original loaded image values
+ */
+ CopyMem(li, &li_bak, sizeof(li_bak));
+done:
+ if (PathName)
+ FreePool(PathName);
+
+ if (data)
+ FreePool(data);
+
+ return efi_status;
+}
+
+/*
+ * Load and run grub. If that fails because grub isn't trusted, load and
+ * run MokManager.
+ */
+EFI_STATUS init_grub(EFI_HANDLE image_handle)
+{
+ EFI_STATUS efi_status;
+
+ efi_status = start_image(image_handle, SECOND_STAGE);
+
+ if (efi_status != EFI_SUCCESS)
+ efi_status = start_image(image_handle, MOK_MANAGER);
+done:
+
+ return efi_status;
+}
+
+/*
+ * Copy the boot-services only MokList variable to the runtime-accessible
+ * MokListRT variable. It's not marked NV, so the OS can't modify it.
+ */
+EFI_STATUS mirror_mok_list()
+{
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS efi_status;
+ UINT32 attributes;
+ void *Data = NULL;
+ UINTN DataSize = 0;
+
+ efi_status = get_variable(L"MokList", shim_lock_guid, &attributes,
+ &DataSize, &Data);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to load grub\n");
- CopyMem(li, &li_bak, sizeof(li_bak));
goto done;
}
- efi_status = uefi_call_wrapper(entry_point, 3, image_handle, systab);
+ efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListRT",
+ &shim_lock_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS,
+ DataSize, Data);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to set MokListRT %d\n", efi_status);
+ }
- CopyMem(li, &li_bak, sizeof(li_bak));
done:
-
return efi_status;
}
+/*
+ * Check if a variable exists
+ */
+static BOOLEAN check_var(CHAR16 *varname)
+{
+ EFI_STATUS efi_status;
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ UINTN size = sizeof(UINT32);
+ UINT32 MokVar;
+ UINT32 attributes;
+
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5, varname,
+ &shim_lock_guid, &attributes,
+ &size, (void *)&MokVar);
+
+ if (efi_status == EFI_SUCCESS || efi_status == EFI_BUFFER_TOO_SMALL)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * If the OS has set any of these variables we need to drop into MOK and
+ * handle them appropriately
+ */
+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;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Verify that MokSBState is valid, and if appropriate set insecure mode
+ */
+
+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;
+
+ /*
+ * Delete and ignore the variable if it's been set from or could be
+ * modified by the OS
+ */
+ 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;
@@ -924,18 +1319,62 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
EFI_HANDLE handle = NULL;
EFI_STATUS efi_status;
+ /*
+ * Set up the shim lock protocol so that grub and MokManager can
+ * call back in and use shim functions
+ */
shim_lock_interface.Verify = shim_verify;
+ shim_lock_interface.Hash = generate_hash;
+ shim_lock_interface.Context = read_header;
systab = passed_systab;
+ /*
+ * Ensure that gnu-efi functions are available
+ */
InitializeLib(image_handle, systab);
+ /*
+ * Check whether the user has configured the system to run in
+ * insecure mode
+ */
+ check_mok_sb();
+
+ /*
+ * Tell the user that we're in insecure mode if necessary
+ */
+ if (insecure_mode) {
+ Print(L"Booting in insecure mode\n");
+ uefi_call_wrapper(BS->Stall, 1, 2000000);
+ }
+
+ /*
+ * Install the protocol
+ */
uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle,
&shim_lock_guid, EFI_NATIVE_INTERFACE,
&shim_lock_interface);
+ /*
+ * Enter MokManager if necessary
+ */
+ efi_status = check_mok_request(image_handle);
+
+ /*
+ * Copy the MOK list to a runtime variable so the kernel can make
+ * use of it
+ */
+ efi_status = mirror_mok_list();
+
+ /*
+ * Hand over control to the second stage bootloader
+ */
+
efi_status = init_grub(image_handle);
+ /*
+ * If we're back here then clean everything up before exiting
+ */
uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle,
&shim_lock_guid, &shim_lock_interface);