summaryrefslogtreecommitdiff
path: root/shim.c
diff options
context:
space:
mode:
Diffstat (limited to 'shim.c')
-rw-r--r--shim.c928
1 files changed, 620 insertions, 308 deletions
diff --git a/shim.c b/shim.c
index 23dd0eeb..8076caa9 100644
--- a/shim.c
+++ b/shim.c
@@ -59,6 +59,14 @@ static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TAB
static CHAR16 *second_stage;
static void *load_options;
static UINT32 load_options_size;
+static UINT8 in_protocol;
+
+#define perror(fmt, ...) ({ \
+ UINTN __perror_ret = 0; \
+ if (!in_protocol) \
+ __perror_ret = Print((fmt), ##__VA_ARGS__); \
+ __perror_ret; \
+ })
EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} };
@@ -85,7 +93,7 @@ int loader_is_participating;
#define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }}
-UINT8 insecure_mode;
+UINT8 user_insecure_mode;
UINT8 ignore_db;
typedef enum {
@@ -102,7 +110,7 @@ typedef struct {
/*
* Perform basic bounds checking of the intra-image pointers
*/
-static void *ImageAddress (void *image, int size, unsigned int address)
+static void *ImageAddress (void *image, unsigned int size, unsigned int address)
{
if (address > size)
return NULL;
@@ -110,11 +118,112 @@ static void *ImageAddress (void *image, int size, unsigned int address)
return image + address;
}
+/* here's a chart:
+ * i686 x86_64 aarch64
+ * 64-on-64: nyet yes yes
+ * 64-on-32: nyet yes nyet
+ * 32-on-32: yes yes no
+ */
+static int
+allow_64_bit(void)
+{
+#if defined(__x86_64__) || defined(__aarch64__)
+ return 1;
+#elif defined(__i386__) || defined(__i686__)
+ /* Right now blindly assuming the kernel will correctly detect this
+ * and /halt the system/ if you're not really on a 64-bit cpu */
+ if (in_protocol)
+ return 1;
+ return 0;
+#else /* assuming everything else is 32-bit... */
+ return 0;
+#endif
+}
+
+static int
+allow_32_bit(void)
+{
+#if defined(__x86_64__)
+#if defined(ALLOW_32BIT_KERNEL_ON_X64)
+ if (in_protocol)
+ return 1;
+ return 0;
+#else
+ return 0;
+#endif
+#elif defined(__i386__) || defined(__i686__)
+ return 1;
+#elif defined(__arch64__)
+ return 0;
+#else /* assuming everything else is 32-bit... */
+ return 1;
+#endif
+}
+
+static int
+image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
+{
+ /* .Magic is the same offset in all cases */
+ if (PEHdr->Pe32Plus.OptionalHeader.Magic
+ == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC)
+ return 1;
+ return 0;
+}
+
+static const UINT16 machine_type =
+#if defined(__x86_64__)
+ IMAGE_FILE_MACHINE_X64;
+#elif defined(__aarch64__)
+ IMAGE_FILE_MACHINE_ARM64;
+#elif defined(__arm__)
+ IMAGE_FILE_MACHINE_ARMTHUMB_MIXED;
+#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
+ IMAGE_FILE_MACHINE_I386;
+#elif defined(__ia64__)
+ IMAGE_FILE_MACHINE_IA64;
+#else
+#error this architecture is not supported by shim
+#endif
+
+static int
+image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
+{
+ /* If the machine type doesn't match the binary, bail, unless
+ * we're in an allowed 64-on-32 scenario */
+ if (PEHdr->Pe32.FileHeader.Machine != machine_type) {
+ if (!(machine_type == IMAGE_FILE_MACHINE_I386 &&
+ PEHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_X64 &&
+ allow_64_bit())) {
+ return 0;
+ }
+ }
+
+ /* If it's not a header type we recognize at all, bail */
+ switch (PEHdr->Pe32Plus.OptionalHeader.Magic) {
+ case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
+ case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
+ break;
+ default:
+ return 0;
+ }
+
+ /* and now just check for general 64-vs-32 compatibility */
+ if (image_is_64_bit(PEHdr)) {
+ if (allow_64_bit())
+ return 1;
+ } else {
+ if (allow_32_bit())
+ return 1;
+ }
+ return 0;
+}
+
/*
* Perform the actual relocation
*/
static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
- void *data)
+ EFI_IMAGE_SECTION_HEADER *Section,
+ void *orig, void *data)
{
EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd;
UINT64 Adjust;
@@ -124,24 +233,54 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
UINT32 *Fixup32;
UINT64 *Fixup64;
int size = context->ImageSize;
- void *ImageEnd = (char *)data + size;
-
- context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)data;
+ void *ImageEnd = (char *)orig + size;
+ int n = 0;
- if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
- Print(L"Image has no relocation entry\n");
- return EFI_UNSUPPORTED;
- }
+ if (image_is_64_bit(context->PEHdr))
+ context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)(unsigned long)data;
+ else
+ context->PEHdr->Pe32.OptionalHeader.ImageBase = (UINT32)(unsigned long)data;
+
+ /* Alright, so here's how this works:
+ *
+ * context->RelocDir gives us two things:
+ * - the VA the table of base relocation blocks are (maybe) to be
+ * mapped at (RelocDir->VirtualAddress)
+ * - the virtual size (RelocDir->Size)
+ *
+ * The .reloc section (Section here) gives us some other things:
+ * - the name! kind of. (Section->Name)
+ * - the virtual size (Section->VirtualSize), which should be the same
+ * as RelocDir->Size
+ * - the virtual address (Section->VirtualAddress)
+ * - the file section size (Section->SizeOfRawData), which is
+ * a multiple of OptHdr->FileAlignment. Only useful for image
+ * validation, not really useful for iteration bounds.
+ * - the file address (Section->PointerToRawData)
+ * - a bunch of stuff we don't use that's 0 in our binaries usually
+ * - Flags (Section->Characteristics)
+ *
+ * and then the thing that's actually at the file address is an array
+ * of EFI_IMAGE_BASE_RELOCATION structs with some values packed behind
+ * them. The SizeOfBlock field of this structure includes the
+ * structure itself, and adding it to that structure's address will
+ * yield the next entry in the array.
+ */
+ RelocBase = ImageAddress(orig, size, Section->PointerToRawData);
+ /* RelocBaseEnd here is the address of the first entry /past/ the
+ * table. */
+ RelocBaseEnd = ImageAddress(orig, size, Section->PointerToRawData +
+ Section->Misc.VirtualSize);
- RelocBase = ImageAddress(data, size, context->RelocDir->VirtualAddress);
- RelocBaseEnd = ImageAddress(data, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1);
+ if (!RelocBase && !RelocBaseEnd)
+ return EFI_SUCCESS;
if (!RelocBase || !RelocBaseEnd) {
- Print(L"Reloc table overflows binary\n");
+ perror(L"Reloc table overflows binary\n");
return EFI_UNSUPPORTED;
}
- Adjust = (UINT64)data - context->ImageAddress;
+ Adjust = (UINTN)data - context->ImageAddress;
if (Adjust == 0)
return EFI_SUCCESS;
@@ -150,19 +289,19 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
if ((RelocBase->SizeOfBlock == 0) || (RelocBase->SizeOfBlock > context->RelocDir->Size)) {
- Print(L"Reloc block size is invalid\n");
+ perror(L"Reloc %d block size %d is invalid\n", n, RelocBase->SizeOfBlock);
return EFI_UNSUPPORTED;
}
RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock);
- if ((void *)RelocEnd < data || (void *)RelocEnd > ImageEnd) {
- Print(L"Reloc entry overflows binary\n");
+ if ((void *)RelocEnd < orig || (void *)RelocEnd > ImageEnd) {
+ perror(L"Reloc %d entry overflows binary\n", n);
return EFI_UNSUPPORTED;
}
FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress);
if (!FixupBase) {
- Print(L"Invalid fixupbase\n");
+ perror(L"Reloc %d Invalid fixupbase\n", n);
return EFI_UNSUPPORTED;
}
@@ -211,55 +350,73 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
break;
default:
- Print(L"Unknown relocation\n");
+ perror(L"Reloc %d Unknown relocation\n", n);
return EFI_UNSUPPORTED;
}
Reloc += 1;
}
RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
+ n++;
}
return EFI_SUCCESS;
}
+static BOOLEAN verify_x509(UINT8 *Cert, UINTN CertSize)
+{
+ UINTN length;
+
+ if (!Cert || CertSize < 4)
+ return FALSE;
+
+ /*
+ * A DER encoding x509 certificate starts with SEQUENCE(0x30),
+ * the number of length bytes, and the number of value bytes.
+ * The size of a x509 certificate is usually between 127 bytes
+ * and 64KB. For convenience, assume the number of value bytes
+ * is 2, i.e. the second byte is 0x82.
+ */
+ if (Cert[0] != 0x30 || Cert[1] != 0x82)
+ return FALSE;
+
+ length = Cert[2]<<8 | Cert[3];
+ if (length != (CertSize - 4))
+ return FALSE;
+
+ return TRUE;
+}
+
static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
UINTN dbsize,
WIN_CERTIFICATE_EFI_PKCS *data,
UINT8 *hash)
{
EFI_SIGNATURE_DATA *Cert;
- UINTN CertCount, Index;
+ UINTN CertSize;
BOOLEAN IsFound = FALSE;
EFI_GUID CertType = X509_GUID;
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
if (CompareGuid (&CertList->SignatureType, &CertType) == 0) {
- CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
- for (Index = 0; Index < CertCount; Index++) {
+ CertSize = CertList->SignatureSize - sizeof(EFI_GUID);
+ if (verify_x509(Cert->SignatureData, CertSize)) {
IsFound = AuthenticodeVerify (data->CertData,
data->Hdr.dwLength - sizeof(data->Hdr),
Cert->SignatureData,
- CertList->SignatureSize,
+ CertSize,
hash, SHA256_DIGEST_SIZE);
if (IsFound)
- break;
-
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
+ return DATA_FOUND;
+ } else if (verbose) {
+ console_notify(L"Not a DER encoding x.509 Certificate");
}
-
}
- if (IsFound)
- break;
-
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
- if (IsFound)
- return DATA_FOUND;
-
return DATA_NOT_FOUND;
}
@@ -446,66 +603,81 @@ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
static BOOLEAN secure_mode (void)
{
- EFI_STATUS status;
- EFI_GUID global_var = EFI_GLOBAL_VARIABLE;
- UINTN len;
- UINT8 *Data;
- UINT8 sb, setupmode;
-
- if (insecure_mode)
- return FALSE;
-
- status = get_variable(L"SecureBoot", &Data, &len, global_var);
- if (status != EFI_SUCCESS) {
- if (verbose)
- console_notify(L"Secure boot not enabled\n");
+ if (user_insecure_mode)
return FALSE;
- }
- sb = *Data;
- FreePool(Data);
- if (sb != 1) {
- if (verbose)
- console_notify(L"Secure boot not enabled\n");
+ if (variable_is_secureboot() != 1) {
+ if (verbose && !in_protocol)
+ console_notify(L"Secure boot not enabled");
return FALSE;
}
- status = get_variable(L"SetupMode", &Data, &len, global_var);
- if (status != EFI_SUCCESS)
- return TRUE;
-
- setupmode = *Data;
- FreePool(Data);
-
- if (setupmode == 1) {
- if (verbose)
- console_notify(L"Platform is in setup mode\n");
+ /* If we /do/ have "SecureBoot", but /don't/ have "SetupMode",
+ * then the implementation is bad, but we assume that secure boot is
+ * enabled according to the status of "SecureBoot". If we have both
+ * of them, then "SetupMode" may tell us additional data, and we need
+ * to consider it.
+ */
+ if (variable_is_setupmode(0) == 1) {
+ if (verbose && !in_protocol)
+ console_notify(L"Platform is in setup mode");
return FALSE;
}
return TRUE;
}
+#define check_size_line(data, datasize_in, hashbase, hashsize, l) ({ \
+ if ((unsigned long)hashbase > \
+ (unsigned long)data + datasize_in) { \
+ perror(L"shim.c:%d Invalid hash base 0x%016x\n", l, \
+ hashbase); \
+ goto done; \
+ } \
+ if ((unsigned long)hashbase + hashsize > \
+ (unsigned long)data + datasize_in) { \
+ perror(L"shim.c:%d Invalid hash size 0x%016x\n", l, \
+ hashsize); \
+ goto done; \
+ } \
+})
+#define check_size(d,ds,h,hs) check_size_line(d,ds,h,hs,__LINE__)
+
/*
* Calculate the SHA1 and SHA256 hashes of a binary
*/
-static EFI_STATUS generate_hash (char *data, int datasize,
+static EFI_STATUS generate_hash (char *data, unsigned int datasize_in,
PE_COFF_LOADER_IMAGE_CONTEXT *context,
UINT8 *sha256hash, UINT8 *sha1hash)
{
unsigned int sha256ctxsize, sha1ctxsize;
- unsigned int size = datasize;
+ unsigned int size = datasize_in;
void *sha256ctx = NULL, *sha1ctx = NULL;
char *hashbase;
unsigned int hashsize;
unsigned int SumOfBytesHashed, SumOfSectionBytes;
unsigned int index, pos;
+ unsigned int datasize;
EFI_IMAGE_SECTION_HEADER *Section;
EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL;
- EFI_IMAGE_SECTION_HEADER *SectionCache;
EFI_STATUS status = EFI_SUCCESS;
+ EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data;
+ unsigned int PEHdr_offset = 0;
+
+ if (datasize_in < 0) {
+ perror(L"Invalid data size\n");
+ return EFI_INVALID_PARAMETER;
+ }
+ size = datasize = (unsigned int)datasize_in;
+
+ if (datasize <= sizeof (*DosHdr) ||
+ DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
+ perror(L"Invalid signature\n");
+ return EFI_INVALID_PARAMETER;
+ }
+ PEHdr_offset = DosHdr->e_lfanew;
sha256ctxsize = Sha256GetContextSize();
sha256ctx = AllocatePool(sha256ctxsize);
@@ -514,12 +686,12 @@ static EFI_STATUS generate_hash (char *data, int datasize,
sha1ctx = AllocatePool(sha1ctxsize);
if (!sha256ctx || !sha1ctx) {
- Print(L"Unable to allocate memory for hash context\n");
+ perror(L"Unable to allocate memory for hash context\n");
return EFI_OUT_OF_RESOURCES;
}
if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) {
- Print(L"Unable to initialise hash\n");
+ perror(L"Unable to initialise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
@@ -528,10 +700,11 @@ static EFI_STATUS generate_hash (char *data, int datasize,
hashbase = data;
hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum -
hashbase;
+ check_size(data, datasize_in, hashbase, hashsize);
if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
!(Sha1Update(sha1ctx, hashbase, hashsize))) {
- Print(L"Unable to generate hash\n");
+ perror(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
@@ -540,54 +713,76 @@ static EFI_STATUS generate_hash (char *data, int datasize,
hashbase = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum +
sizeof (int);
hashsize = (char *)context->SecDir - hashbase;
+ check_size(data, datasize_in, hashbase, hashsize);
if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
!(Sha1Update(sha1ctx, hashbase, hashsize))) {
- Print(L"Unable to generate hash\n");
+ perror(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
/* Hash end of certificate table to end of image header */
- hashbase = (char *) &context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
- hashsize = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders -
- (int) ((char *) (&context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - data);
+ EFI_IMAGE_DATA_DIRECTORY *dd = context->SecDir + 1;
+ hashbase = (char *)dd;
+ hashsize = context->SizeOfHeaders - (unsigned long)((char *)dd - data);
+ if (hashsize > datasize_in) {
+ perror(L"Data Directory size %d is invalid\n", hashsize);
+ status = EFI_INVALID_PARAMETER;
+ goto done;
+ }
+ check_size(data, datasize_in, hashbase, hashsize);
if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
!(Sha1Update(sha1ctx, hashbase, hashsize))) {
- Print(L"Unable to generate hash\n");
+ perror(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
/* Sort sections */
- SumOfBytesHashed = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
-
- Section = (EFI_IMAGE_SECTION_HEADER *) (
- (char *)context->PEHdr + sizeof (UINT32) +
- sizeof (EFI_IMAGE_FILE_HEADER) +
- context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader
- );
-
- SectionCache = Section;
-
- for (index = 0, SumOfSectionBytes = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++, SectionCache++) {
- SumOfSectionBytes += SectionCache->SizeOfRawData;
- }
-
- if (SumOfSectionBytes >= datasize) {
- Print(L"Malformed binary: %x %x\n", SumOfSectionBytes, size);
- status = EFI_INVALID_PARAMETER;
- goto done;
+ SumOfBytesHashed = context->SizeOfHeaders;
+
+ /* Validate section locations and sizes */
+ for (index = 0, SumOfSectionBytes = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
+ EFI_IMAGE_SECTION_HEADER *SectionPtr;
+
+ /* Validate SectionPtr is within image */
+ SectionPtr = ImageAddress(data, datasize,
+ PEHdr_offset +
+ sizeof (UINT32) +
+ sizeof (EFI_IMAGE_FILE_HEADER) +
+ context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader +
+ (index * sizeof(*SectionPtr)));
+ if (!SectionPtr) {
+ perror(L"Malformed section %d\n", index);
+ status = EFI_INVALID_PARAMETER;
+ goto done;
+ }
+ /* Validate section size is within image. */
+ if (SectionPtr->SizeOfRawData >
+ datasize - SumOfBytesHashed - SumOfSectionBytes) {
+ perror(L"Malformed section %d size\n", index);
+ status = EFI_INVALID_PARAMETER;
+ goto done;
+ }
+ SumOfSectionBytes += SectionPtr->SizeOfRawData;
}
SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * context->PEHdr->Pe32.FileHeader.NumberOfSections);
if (SectionHeader == NULL) {
- Print(L"Unable to allocate section header\n");
+ perror(L"Unable to allocate section header\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
+ /* Already validated above */
+ Section = ImageAddress(data, datasize,
+ PEHdr_offset +
+ sizeof (UINT32) +
+ sizeof (EFI_IMAGE_FILE_HEADER) +
+ context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader);
+
/* Sort the section headers */
for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
pos = index;
@@ -606,17 +801,26 @@ static EFI_STATUS generate_hash (char *data, int datasize,
continue;
}
hashbase = ImageAddress(data, size, Section->PointerToRawData);
- hashsize = (unsigned int) Section->SizeOfRawData;
if (!hashbase) {
- Print(L"Malformed section header\n");
+ perror(L"Malformed section header\n");
status = EFI_INVALID_PARAMETER;
goto done;
}
+ /* Verify hashsize within image. */
+ if (Section->SizeOfRawData >
+ datasize - Section->PointerToRawData) {
+ perror(L"Malformed section raw size %d\n", index);
+ status = EFI_INVALID_PARAMETER;
+ goto done;
+ }
+ hashsize = (unsigned int) Section->SizeOfRawData;
+ check_size(data, datasize_in, hashbase, hashsize);
+
if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
!(Sha1Update(sha1ctx, hashbase, hashsize))) {
- Print(L"Unable to generate hash\n");
+ perror(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
@@ -624,16 +828,14 @@ static EFI_STATUS generate_hash (char *data, int datasize,
}
/* Hash all remaining data */
- if (size > SumOfBytesHashed) {
+ if (datasize > SumOfBytesHashed) {
hashbase = data + SumOfBytesHashed;
- hashsize = (unsigned int)(
- size -
- context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -
- SumOfBytesHashed);
+ hashsize = datasize - context->SecDir->Size - SumOfBytesHashed;
+ check_size(data, datasize_in, hashbase, hashsize);
if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
!(Sha1Update(sha1ctx, hashbase, hashsize))) {
- Print(L"Unable to generate hash\n");
+ perror(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
@@ -641,7 +843,7 @@ static EFI_STATUS generate_hash (char *data, int datasize,
if (!(Sha256Final(sha256ctx, sha256hash)) ||
!(Sha1Final(sha1ctx, sha1hash))) {
- Print(L"Unable to finalise hash\n");
+ perror(L"Unable to finalise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
@@ -670,13 +872,12 @@ static EFI_STATUS verify_mok (void) {
status = get_variable_attr(L"MokList", &MokListData, &MokListDataSize,
shim_lock_guid, &attributes);
- if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
- Print(L"MokList is compromised!\nErase all keys in MokList!\n");
+ if (!EFI_ERROR(status) && attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
+ perror(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");
+ perror(L"Failed to erase MokList\n");
+ return EFI_ACCESS_DENIED;
}
- status = EFI_ACCESS_DENIED;
- return status;
}
if (MokListData)
@@ -702,13 +903,13 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
context->SecDir->VirtualAddress);
if (!cert) {
- Print(L"Certificate located outside the image\n");
+ perror(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",
+ perror(L"Unsupported certificate type %x\n",
cert->Hdr.wCertificateType);
return EFI_UNSUPPORTED;
}
@@ -722,7 +923,9 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
/*
* Check that the MOK database hasn't been modified
*/
- verify_mok();
+ status = verify_mok();
+ if (status != EFI_SUCCESS)
+ return status;
/*
* Ensure that the binary isn't blacklisted
@@ -730,7 +933,7 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
status = check_blacklist(cert, sha256hash, sha1hash);
if (status != EFI_SUCCESS) {
- Print(L"Binary is blacklisted\n");
+ perror(L"Binary is blacklisted\n");
return status;
}
@@ -746,7 +949,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
/*
* Check against the shim build key
*/
- if (AuthenticodeVerify(cert->CertData,
+ if (sizeof(shim_cert) &&
+ AuthenticodeVerify(cert->CertData,
context->SecDir->Size - sizeof(cert->Hdr),
shim_cert, sizeof(shim_cert), sha256hash,
SHA256_DIGEST_SIZE)) {
@@ -754,14 +958,13 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
return status;
}
-
/*
* 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)) {
+ if (vendor_cert_size && AuthenticodeVerify(cert->CertData,
+ context->SecDir->Size - sizeof(cert->Hdr),
+ vendor_cert, vendor_cert_size, sha256hash,
+ SHA256_DIGEST_SIZE)) {
status = EFI_SUCCESS;
return status;
}
@@ -780,90 +983,108 @@ static EFI_STATUS read_header(void *data, unsigned int datasize,
{
EFI_IMAGE_DOS_HEADER *DosHdr = data;
EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
- unsigned long HeaderWithoutDataDir, SectionHeaderOffset;
+ unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize;
- if (datasize < sizeof(EFI_IMAGE_DOS_HEADER)) {
- Print(L"Invalid image\n");
+ if (datasize < sizeof (PEHdr->Pe32)) {
+ perror(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 (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES
- < PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes) {
- Print(L"Image header too small\n");
+ if (!image_is_loadable(PEHdr)) {
+ perror(L"Platform does not support this image\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ if (image_is_64_bit(PEHdr)) {
+ context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
+ context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
+ context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage;
+ OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64);
+ } else {
+ context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
+ context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders;
+ context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage;
+ OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32);
+ }
+
+ context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections;
+
+ if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) {
+ perror(L"Image header too small\n");
return EFI_UNSUPPORTED;
}
- HeaderWithoutDataDir = sizeof (EFI_IMAGE_OPTIONAL_HEADER64)
+ HeaderWithoutDataDir = OptHeaderSize
- sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
- if (((UINT32)PEHdr->Pe32Plus.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) !=
- PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes
- * sizeof (EFI_IMAGE_DATA_DIRECTORY)) {
- Print(L"Image header overflows data directory\n");
+ if (((UINT32)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) !=
+ context->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) {
+ perror(L"Image header overflows data directory\n");
return EFI_UNSUPPORTED;
}
SectionHeaderOffset = DosHdr->e_lfanew
+ sizeof (UINT32)
+ sizeof (EFI_IMAGE_FILE_HEADER)
- + PEHdr->Pe32Plus.FileHeader.SizeOfOptionalHeader;
- if ((PEHdr->Pe32Plus.OptionalHeader.SizeOfImage - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
- <= PEHdr->Pe32Plus.FileHeader.NumberOfSections) {
- Print(L"Image sections overflow image size\n");
+ + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader;
+ if (((UINT32)context->ImageSize - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
+ <= context->NumberOfSections) {
+ perror(L"Image sections overflow image size\n");
return EFI_UNSUPPORTED;
}
- if ((PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
- < (UINT32)PEHdr->Pe32Plus.FileHeader.NumberOfSections) {
- Print(L"Image sections overflow section headers\n");
+ if ((context->SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
+ < (UINT32)context->NumberOfSections) {
+ perror(L"Image sections overflow section headers\n");
return EFI_UNSUPPORTED;
}
if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) {
- Print(L"Invalid image\n");
+ perror(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
- Print(L"Unsupported image type\n");
+ perror(L"Unsupported image type\n");
return EFI_UNSUPPORTED;
}
if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) {
- Print(L"Unsupported image - Relocations have been stripped\n");
+ perror(L"Unsupported image - Relocations have been stripped\n");
return EFI_UNSUPPORTED;
}
- if (PEHdr->Pe32.OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
- Print(L"Only 64-bit images supported\n");
- return EFI_UNSUPPORTED;
+ context->PEHdr = PEHdr;
+
+ if (image_is_64_bit(PEHdr)) {
+ context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase;
+ context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
+ context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
+ context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ } else {
+ context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase;
+ context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
+ context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
+ context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
}
- context->PEHdr = PEHdr;
- context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase;
- context->ImageSize = (UINT64)PEHdr->Pe32Plus.OptionalHeader.SizeOfImage;
- context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
- context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
- context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
- context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
- context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections;
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->ImageSize < context->SizeOfHeaders) {
- Print(L"Invalid image\n");
+ perror(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
- if (((UINT8 *)context->SecDir - (UINT8 *)data) > (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
- Print(L"Invalid image\n");
+ if ((unsigned long)((UINT8 *)context->SecDir - (UINT8 *)data) >
+ (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
+ perror(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
if (context->SecDir->VirtualAddress >= datasize) {
- Print(L"Malformed security header\n");
+ perror(L"Malformed security header\n");
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
@@ -877,7 +1098,8 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
{
EFI_STATUS efi_status;
char *buffer;
- int i, size;
+ int i;
+ unsigned int size;
EFI_IMAGE_SECTION_HEADER *Section;
char *base, *end;
PE_COFF_LOADER_IMAGE_CONTEXT context;
@@ -887,7 +1109,7 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
*/
efi_status = read_header(data, datasize, &context);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to read header\n");
+ perror(L"Failed to read header: %r\n", efi_status);
return efi_status;
}
@@ -909,17 +1131,27 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
buffer = AllocatePool(context.ImageSize);
if (!buffer) {
- Print(L"Failed to allocate image buffer\n");
+ perror(L"Failed to allocate image buffer\n");
return EFI_OUT_OF_RESOURCES;
}
CopyMem(buffer, data, context.SizeOfHeaders);
+ char *RelocBase, *RelocBaseEnd;
+ RelocBase = ImageAddress(buffer, datasize,
+ context.RelocDir->VirtualAddress);
+ /* RelocBaseEnd here is the address of the last byte of the table */
+ RelocBaseEnd = ImageAddress(buffer, datasize,
+ context.RelocDir->VirtualAddress +
+ context.RelocDir->Size - 1);
+
+ EFI_IMAGE_SECTION_HEADER *RelocSection = NULL;
+
/*
* Copy the executable's sections to their desired offsets
*/
Section = context.FirstSection;
- for (i = 0; i < context.NumberOfSections; i++) {
+ for (i = 0; i < context.NumberOfSections; i++, Section++) {
size = Section->Misc.VirtualSize;
if (size > Section->SizeOfRawData)
@@ -927,36 +1159,66 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
base = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress);
end = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress + size - 1);
-
if (!base || !end) {
- Print(L"Invalid section size\n");
+ perror(L"Invalid section size\n");
return EFI_UNSUPPORTED;
}
if (Section->VirtualAddress < context.SizeOfHeaders ||
Section->PointerToRawData < context.SizeOfHeaders) {
- Print(L"Section is inside image headers\n");
+ perror(L"Section is inside image headers\n");
return EFI_UNSUPPORTED;
}
+ /* We do want to process .reloc, but it's often marked
+ * discardable, so we don't want to memcpy it. */
+ if (CompareMem(Section->Name, ".reloc\0\0", 8) == 0) {
+ if (RelocSection) {
+ perror(L"Image has multiple relocation sections\n");
+ return EFI_UNSUPPORTED;
+ }
+ /* If it has nonzero sizes, and our bounds check
+ * made sense, and the VA and size match RelocDir's
+ * versions, then we believe in this section table. */
+ if (Section->SizeOfRawData &&
+ Section->Misc.VirtualSize &&
+ base && end &&
+ RelocBase == base &&
+ RelocBaseEnd == end) {
+ RelocSection = Section;
+ }
+ }
+
+ if (Section->Characteristics & 0x02000000) {
+ /* section has EFI_IMAGE_SCN_MEM_DISCARDABLE attr set */
+ continue;
+ }
+
if (Section->SizeOfRawData > 0)
CopyMem(base, data + Section->PointerToRawData, size);
if (size < Section->Misc.VirtualSize)
ZeroMem (base + size, Section->Misc.VirtualSize - size);
+ }
- Section += 1;
+ if (context.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
+ perror(L"Image has no relocation entry\n");
+ FreePool(buffer);
+ return EFI_UNSUPPORTED;
}
- /*
- * Run the relocation fixups
- */
- efi_status = relocate_coff(&context, buffer);
+ if (context.RelocDir->Size && RelocSection) {
+ /*
+ * Run the relocation fixups
+ */
+ efi_status = relocate_coff(&context, RelocSection, data,
+ buffer);
- if (efi_status != EFI_SUCCESS) {
- Print(L"Relocation failed\n");
- FreePool(buffer);
- return efi_status;
+ if (efi_status != EFI_SUCCESS) {
+ perror(L"Relocation failed: %r\n", efi_status);
+ FreePool(buffer);
+ return efi_status;
+ }
}
entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
@@ -972,7 +1234,7 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
li->LoadOptionsSize = load_options_size;
if (!entry_point) {
- Print(L"Invalid entry point\n");
+ perror(L"Invalid entry point\n");
FreePool(buffer);
return EFI_UNSUPPORTED;
}
@@ -986,16 +1248,17 @@ should_use_fallback(EFI_HANDLE image_handle)
EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
EFI_LOADED_IMAGE *li;
unsigned int pathlen = 0;
- CHAR16 *bootpath;
+ CHAR16 *bootpath = NULL;
EFI_FILE_IO_INTERFACE *fio = NULL;
EFI_FILE *vh;
EFI_FILE *fh;
EFI_STATUS rc;
+ int ret = 0;
rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
&loaded_image_protocol, (void **)&li);
if (EFI_ERROR(rc)) {
- Print(L"Could not get image for bootx64.efi: %d\n", rc);
+ perror(L"Could not get image for bootx64.efi: %r\n", rc);
return 0;
}
@@ -1008,23 +1271,23 @@ should_use_fallback(EFI_HANDLE image_handle)
*/
if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) &&
StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15))
- return 0;
+ goto error;
pathlen = StrLen(bootpath);
if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI"))
- return 0;
+ goto error;
rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle,
&FileSystemProtocol, (void **)&fio);
if (EFI_ERROR(rc)) {
- Print(L"Could not get fio for li->DeviceHandle: %d\n", rc);
- return 0;
+ perror(L"Could not get fio for li->DeviceHandle: %r\n", rc);
+ goto error;
}
-
+
rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &vh);
if (EFI_ERROR(rc)) {
- Print(L"Could not open fio volume: %d\n", rc);
- return 0;
+ perror(L"Could not open fio volume: %r\n", rc);
+ goto error;
}
rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK,
@@ -1037,12 +1300,17 @@ should_use_fallback(EFI_HANDLE image_handle)
* rc);
*/
uefi_call_wrapper(vh->Close, 1, vh);
- return 0;
+ goto error;
}
uefi_call_wrapper(fh->Close, 1, fh);
uefi_call_wrapper(vh->Close, 1, vh);
- return 1;
+ ret = 1;
+error:
+ if (bootpath)
+ FreePool(bootpath);
+
+ return ret;
}
/*
@@ -1050,16 +1318,15 @@ should_use_fallback(EFI_HANDLE image_handle)
* of the executable
*/
static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
- EFI_DEVICE_PATH **grubpath, CHAR16 **PathName)
+ CHAR16 **PathName)
{
EFI_DEVICE_PATH *devpath;
- EFI_HANDLE device;
- int i, j, last = -1;
+ unsigned int i;
+ int j, last = -1;
unsigned int pathlen = 0;
EFI_STATUS efi_status = EFI_SUCCESS;
CHAR16 *bootpath;
- device = li->DeviceHandle;
devpath = li->FilePath;
bootpath = DevicePathToStr(devpath);
@@ -1102,7 +1369,7 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
*PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
if (!*PathName) {
- Print(L"Failed to allocate path buffer\n");
+ perror(L"Failed to allocate path buffer\n");
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
@@ -1112,8 +1379,6 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
StrCat(*PathName, bootpath);
StrCat(*PathName, ImagePath);
- *grubpath = FileDevicePath(device, *PathName);
-
error:
FreePool(bootpath);
@@ -1145,14 +1410,14 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
(void **)&drive);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to find fs\n");
+ perror(L"Failed to find fs: %r\n", efi_status);
goto error;
}
efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to open fs\n");
+ perror(L"Failed to open fs: %r\n", efi_status);
goto error;
}
@@ -1163,14 +1428,14 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
EFI_FILE_MODE_READ, 0);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to open %s - %lx\n", PathName, efi_status);
+ perror(L"Failed to open %s - %r\n", PathName, efi_status);
goto error;
}
fileinfo = AllocatePool(buffersize);
if (!fileinfo) {
- Print(L"Unable to allocate file info buffer\n");
+ perror(L"Unable to allocate file info buffer\n");
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
@@ -1186,7 +1451,7 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
FreePool(fileinfo);
fileinfo = AllocatePool(buffersize);
if (!fileinfo) {
- Print(L"Unable to allocate file info buffer\n");
+ perror(L"Unable to allocate file info buffer\n");
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
@@ -1196,7 +1461,7 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
}
if (efi_status != EFI_SUCCESS) {
- Print(L"Unable to get file info\n");
+ perror(L"Unable to get file info: %r\n", efi_status);
goto error;
}
@@ -1205,7 +1470,7 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
*data = AllocatePool(buffersize);
if (!*data) {
- Print(L"Unable to allocate file buffer\n");
+ perror(L"Unable to allocate file buffer\n");
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
@@ -1224,7 +1489,7 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
}
if (efi_status != EFI_SUCCESS) {
- Print(L"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize);
+ perror(L"Unexpected return from initial read: %r, buffersize %x\n", efi_status, buffersize);
goto error;
}
@@ -1254,6 +1519,7 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size)
PE_COFF_LOADER_IMAGE_CONTEXT context;
loader_is_participating = 1;
+ in_protocol = 1;
if (!secure_mode())
return EFI_SUCCESS;
@@ -1261,9 +1527,35 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size)
status = read_header(buffer, size, &context);
if (status != EFI_SUCCESS)
- return status;
+ goto done;
status = verify_buffer(buffer, size, &context);
+done:
+ in_protocol = 0;
+ return status;
+}
+
+static EFI_STATUS shim_hash (char *data, int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context,
+ UINT8 *sha256hash, UINT8 *sha1hash)
+{
+ EFI_STATUS status;
+
+ in_protocol = 1;
+ status = generate_hash(data, datasize, context, sha256hash, sha1hash);
+ in_protocol = 0;
+
+ return status;
+}
+
+static EFI_STATUS shim_read_header(void *data, unsigned int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context)
+{
+ EFI_STATUS status;
+
+ in_protocol = 1;
+ status = read_header(data, datasize, context);
+ in_protocol = 0;
return status;
}
@@ -1276,7 +1568,6 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
EFI_STATUS efi_status;
EFI_LOADED_IMAGE *li, li_bak;
- EFI_DEVICE_PATH *path;
CHAR16 *PathName = NULL;
void *sourcebuffer = NULL;
UINT64 sourcesize = 0;
@@ -1291,30 +1582,30 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
&loaded_image_protocol, (void **)&li);
if (efi_status != EFI_SUCCESS) {
- Print(L"Unable to init protocol\n");
+ perror(L"Unable to init protocol\n");
return efi_status;
}
/*
* Build a new path from the existing one plus the executable name
*/
- efi_status = generate_path(li, ImagePath, &path, &PathName);
+ efi_status = generate_path(li, ImagePath, &PathName);
if (efi_status != EFI_SUCCESS) {
- Print(L"Unable to generate path: %s\n", ImagePath);
+ perror(L"Unable to generate path %s: %r\n", ImagePath, efi_status);
goto done;
}
- if (findNetboot(image_handle)) {
+ if (findNetboot(li->DeviceHandle)) {
efi_status = parseNetbootinfo(image_handle);
if (efi_status != EFI_SUCCESS) {
- Print(L"Netboot parsing failed: %d\n", efi_status);
+ perror(L"Netboot parsing failed: %r\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");
+ perror(L"Unable to fetch TFTP image: %r\n", efi_status);
return efi_status;
}
data = sourcebuffer;
@@ -1326,7 +1617,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
efi_status = load_image(li, &data, &datasize, PathName);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to load image\n");
+ perror(L"Failed to load image %s: %r\n", PathName, efi_status);
goto done;
}
}
@@ -1343,7 +1634,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
efi_status = handle_image(data, datasize, li);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to load image\n");
+ perror(L"Failed to load image: %r\n", efi_status);
CopyMem(li, &li_bak, sizeof(li_bak));
goto done;
}
@@ -1408,37 +1699,42 @@ EFI_STATUS mirror_mok_list()
if (efi_status != EFI_SUCCESS)
DataSize = 0;
- FullDataSize = DataSize
- + sizeof (*CertList)
- + sizeof (EFI_GUID)
- + vendor_cert_size
- ;
- FullData = AllocatePool(FullDataSize);
- if (!FullData) {
- Print(L"Failed to allocate space for MokListRT\n");
- return EFI_OUT_OF_RESOURCES;
- }
- p = FullData;
+ if (vendor_cert_size) {
+ FullDataSize = DataSize
+ + sizeof (*CertList)
+ + sizeof (EFI_GUID)
+ + vendor_cert_size
+ ;
+ FullData = AllocatePool(FullDataSize);
+ if (!FullData) {
+ perror(L"Failed to allocate space for MokListRT\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+ p = FullData;
- if (efi_status == EFI_SUCCESS && DataSize > 0) {
- CopyMem(p, Data, DataSize);
- p += DataSize;
+ if (efi_status == EFI_SUCCESS && DataSize > 0) {
+ CopyMem(p, Data, DataSize);
+ p += DataSize;
+ }
+ CertList = (EFI_SIGNATURE_LIST *)p;
+ p += sizeof (*CertList);
+ CertData = (EFI_SIGNATURE_DATA *)p;
+ p += sizeof (EFI_GUID);
+
+ CertList->SignatureType = EFI_CERT_X509_GUID;
+ CertList->SignatureListSize = vendor_cert_size
+ + sizeof (*CertList)
+ + sizeof (*CertData)
+ -1;
+ CertList->SignatureHeaderSize = 0;
+ CertList->SignatureSize = vendor_cert_size + sizeof (EFI_GUID);
+
+ CertData->SignatureOwner = SHIM_LOCK_GUID;
+ CopyMem(p, vendor_cert, vendor_cert_size);
+ } else {
+ FullDataSize = DataSize;
+ FullData = Data;
}
- CertList = (EFI_SIGNATURE_LIST *)p;
- p += sizeof (*CertList);
- CertData = (EFI_SIGNATURE_DATA *)p;
- p += sizeof (EFI_GUID);
-
- CertList->SignatureType = EFI_CERT_X509_GUID;
- CertList->SignatureListSize = vendor_cert_size
- + sizeof (*CertList)
- + sizeof (*CertData)
- -1;
- CertList->SignatureHeaderSize = 0;
- CertList->SignatureSize = vendor_cert_size + sizeof (EFI_GUID);
-
- CertData->SignatureOwner = SHIM_LOCK_GUID;
- CopyMem(p, vendor_cert, vendor_cert_size);
efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListRT",
&shim_lock_guid,
@@ -1446,7 +1742,7 @@ EFI_STATUS mirror_mok_list()
| EFI_VARIABLE_RUNTIME_ACCESS,
FullDataSize, FullData);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to set MokListRT %d\n", efi_status);
+ perror(L"Failed to set MokListRT: %r\n", efi_status);
}
return efi_status;
@@ -1487,7 +1783,7 @@ EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
efi_status = start_image(image_handle, MOK_MANAGER);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to start MokManager\n");
+ perror(L"Failed to start MokManager: %r\n", efi_status);
return efi_status;
}
}
@@ -1503,16 +1799,15 @@ static EFI_STATUS check_mok_sb (void)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS status = EFI_SUCCESS;
- UINT8 *MokSBState = NULL;
- UINTN MokSBStateSize = 0;
+ UINT8 MokSBState;
+ UINTN MokSBStateSize = sizeof(MokSBState);
UINT32 attributes;
- insecure_mode = 0;
+ user_insecure_mode = 0;
ignore_db = 0;
- status = get_variable_attr(L"MokSBState", &MokSBState, &MokSBStateSize,
- shim_lock_guid, &attributes);
-
+ status = uefi_call_wrapper(RT->GetVariable, 5, L"MokSBState", &shim_lock_guid,
+ &attributes, &MokSBStateSize, &MokSBState);
if (status != EFI_SUCCESS)
return EFI_ACCESS_DENIED;
@@ -1521,19 +1816,17 @@ static EFI_STATUS check_mok_sb (void)
* modified by the OS
*/
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
- Print(L"MokSBState is compromised! Clearing it\n");
+ perror(L"MokSBState is compromised! Clearing it\n");
if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) {
- Print(L"Failed to erase MokSBState\n");
+ perror(L"Failed to erase MokSBState\n");
}
status = EFI_ACCESS_DENIED;
} else {
- if (*(UINT8 *)MokSBState == 1) {
- insecure_mode = 1;
+ if (MokSBState == 1) {
+ user_insecure_mode = 1;
}
}
- FreePool(MokSBState);
-
return status;
}
@@ -1545,13 +1838,12 @@ static EFI_STATUS check_mok_db (void)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS status = EFI_SUCCESS;
- UINT8 *MokDBState = NULL;
- UINTN MokDBStateSize = 0;
+ UINT8 MokDBState;
+ UINTN MokDBStateSize = sizeof(MokDBStateSize);
UINT32 attributes;
- status = get_variable_attr(L"MokDBState", &MokDBState, &MokDBStateSize,
- shim_lock_guid, &attributes);
-
+ status = uefi_call_wrapper(RT->GetVariable, 5, L"MokDBState", &shim_lock_guid,
+ &attributes, &MokDBStateSize, &MokDBState);
if (status != EFI_SUCCESS)
return EFI_ACCESS_DENIED;
@@ -1562,19 +1854,17 @@ static EFI_STATUS check_mok_db (void)
* modified by the OS
*/
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
- Print(L"MokDBState is compromised! Clearing it\n");
+ perror(L"MokDBState is compromised! Clearing it\n");
if (LibDeleteVariable(L"MokDBState", &shim_lock_guid) != EFI_SUCCESS) {
- Print(L"Failed to erase MokDBState\n");
+ perror(L"Failed to erase MokDBState\n");
}
status = EFI_ACCESS_DENIED;
} else {
- if (*(UINT8 *)MokDBState == 1) {
+ if (MokDBState == 1) {
ignore_db = 1;
}
}
- FreePool(MokDBState);
-
return status;
}
@@ -1594,7 +1884,7 @@ static EFI_STATUS mok_ignore_db()
| EFI_VARIABLE_RUNTIME_ACCESS,
DataSize, (void *)&Data);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to set MokIgnoreDB %d\n", efi_status);
+ perror(L"Failed to set MokIgnoreDB: %r\n", efi_status);
}
}
@@ -1610,9 +1900,10 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
EFI_STATUS status;
EFI_LOADED_IMAGE *li;
CHAR16 *start = NULL, *c;
- int i, remaining_size = 0;
+ unsigned int i;
+ int remaining_size = 0;
CHAR16 *loader_str = NULL;
- int loader_len = 0;
+ unsigned int loader_len = 0;
second_stage = DEFAULT_LOADER;
load_options = NULL;
@@ -1621,7 +1912,7 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
&LoadedImageProtocol, (void **) &li);
if (status != EFI_SUCCESS) {
- Print (L"Failed to get load options\n");
+ perror (L"Failed to get load options: %r\n", status);
return status;
}
@@ -1665,7 +1956,7 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
if (loader_len > 0) {
loader_str = AllocatePool((loader_len + 1) * sizeof(CHAR16));
if (!loader_str) {
- Print(L"Failed to allocate loader string\n");
+ perror(L"Failed to allocate loader string\n");
return EFI_OUT_OF_RESOURCES;
}
for (i = 0; i < loader_len; i++)
@@ -1680,11 +1971,56 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
return EFI_SUCCESS;
}
-EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
+static SHIM_LOCK shim_lock_interface;
+static EFI_HANDLE shim_lock_handle;
+
+EFI_STATUS
+install_shim_protocols(void)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
- static SHIM_LOCK shim_lock_interface;
- EFI_HANDLE handle = NULL;
+ EFI_STATUS efi_status;
+ /*
+ * Install the protocol
+ */
+ efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4,
+ &shim_lock_handle, &shim_lock_guid,
+ EFI_NATIVE_INTERFACE, &shim_lock_interface);
+ if (EFI_ERROR(efi_status)) {
+ console_error(L"Could not install security protocol",
+ efi_status);
+ return efi_status;
+ }
+
+#if defined(OVERRIDE_SECURITY_POLICY)
+ /*
+ * Install the security protocol hook
+ */
+ security_policy_install(shim_verify);
+#endif
+
+ return EFI_SUCCESS;
+}
+
+void
+uninstall_shim_protocols(void)
+{
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+#if defined(OVERRIDE_SECURITY_POLICY)
+ /*
+ * Clean up the security protocol hook
+ */
+ security_policy_uninstall();
+#endif
+
+ /*
+ * If we're back here then clean everything up before exiting
+ */
+ uefi_call_wrapper(BS->UninstallProtocolInterface, 3, shim_lock_handle,
+ &shim_lock_guid, &shim_lock_interface);
+}
+
+EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
+{
EFI_STATUS efi_status;
verification_method = VERIFIED_BY_NOTHING;
@@ -1699,8 +2035,8 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
* 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;
+ shim_lock_interface.Hash = shim_hash;
+ shim_lock_interface.Context = shim_read_header;
systab = passed_systab;
@@ -1726,35 +2062,24 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
/*
* Tell the user that we're in insecure mode if necessary
*/
- if (!secure_mode()) {
+ if (user_insecure_mode) {
Print(L"Booting in insecure mode\n");
uefi_call_wrapper(BS->Stall, 1, 2000000);
- } else {
- /*
- * Install our hooks for ExitBootServices() and StartImage()
- */
- hook_system_services(systab);
- loader_is_participating = 0;
+ } else if (secure_mode()) {
+ if (vendor_cert_size || vendor_dbx_size) {
+ /*
+ * If shim includes its own certificates then ensure
+ * that anything it boots has performed some
+ * validation of the next image.
+ */
+ hook_system_services(systab);
+ loader_is_participating = 0;
+ }
}
- /*
- * Install the protocol
- */
- efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4,
- &handle, &shim_lock_guid, EFI_NATIVE_INTERFACE,
- &shim_lock_interface);
- if (EFI_ERROR(efi_status)) {
- console_error(L"Could not install security protocol",
- efi_status);
+ efi_status = install_shim_protocols();
+ if (EFI_ERROR(efi_status))
return efi_status;
- }
-
-#if defined(OVERRIDE_SECURITY_POLICY)
- /*
- * Install the security protocol hook
- */
- security_policy_install(shim_verify);
-#endif
/*
* Enter MokManager if necessary
@@ -1779,20 +2104,7 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
efi_status = init_grub(image_handle);
-#if defined(OVERRIDE_SECURITY_POLICY)
- /*
- * Clean up the security protocol hook
- */
- security_policy_uninstall();
-#endif
-
- /*
- * If we're back here then clean everything up before exiting
- */
- uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle,
- &shim_lock_guid, &shim_lock_interface);
-
-
+ uninstall_shim_protocols();
/*
* Remove our hooks from system services.
*/