diff options
Diffstat (limited to 'shim.c')
| -rw-r--r-- | shim.c | 928 |
1 files changed, 620 insertions, 308 deletions
@@ -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. */ |
