diff options
Diffstat (limited to 'shim.c')
| -rw-r--r-- | shim.c | 925 |
1 files changed, 7 insertions, 918 deletions
@@ -62,8 +62,8 @@ static EFI_SYSTEM_TABLE *systab; static EFI_HANDLE global_image_handle; static CHAR16 *second_stage; -static void *load_options; -static UINT32 load_options_size; +void *load_options; +UINT32 load_options_size; /* * The vendor certificate used for validating the second stage loader @@ -108,250 +108,6 @@ typedef struct { UINT8 *Mok; } MokListNode; -/* - * Perform basic bounds checking of the intra-image pointers - */ -static void *ImageAddress (void *image, uint64_t size, uint64_t address) -{ - /* ensure our local pointer isn't bigger than our size */ - if (address > size) - return NULL; - - /* Insure our math won't overflow */ - if (UINT64_MAX - address < (uint64_t)(intptr_t)image) - return NULL; - - /* return the absolute pointer */ - 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(__aarch64__) - 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, - EFI_IMAGE_SECTION_HEADER *Section, - void *orig, void *data) -{ - EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; - UINT64 Adjust; - UINT16 *Reloc, *RelocEnd; - char *Fixup, *FixupBase; - UINT16 *Fixup16; - UINT32 *Fixup32; - UINT64 *Fixup64; - int size = context->ImageSize; - void *ImageEnd = (char *)orig + size; - int n = 0; - - /* 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); - - if (!RelocBase && !RelocBaseEnd) - return EFI_SUCCESS; - - if (!RelocBase || !RelocBaseEnd) { - perror(L"Reloc table overflows binary\n"); - return EFI_UNSUPPORTED; - } - - Adjust = (UINTN)data - context->ImageAddress; - - if (Adjust == 0) - return EFI_SUCCESS; - - while (RelocBase < RelocBaseEnd) { - Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); - - if (RelocBase->SizeOfBlock == 0) { - perror(L"Reloc %d block size 0 is invalid\n", n); - return EFI_UNSUPPORTED; - } else if (RelocBase->SizeOfBlock > context->RelocDir->Size) { - perror(L"Reloc %d block size %d greater than reloc dir" - "size %d, which is invalid\n", n, - RelocBase->SizeOfBlock, - context->RelocDir->Size); - return EFI_UNSUPPORTED; - } - - RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); - 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) { - perror(L"Reloc %d Invalid fixupbase\n", n); - return EFI_UNSUPPORTED; - } - - while (Reloc < RelocEnd) { - Fixup = FixupBase + (*Reloc & 0xFFF); - switch ((*Reloc) >> 12) { - case EFI_IMAGE_REL_BASED_ABSOLUTE: - break; - - case EFI_IMAGE_REL_BASED_HIGH: - Fixup16 = (UINT16 *) Fixup; - *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16))); - break; - - case EFI_IMAGE_REL_BASED_LOW: - Fixup16 = (UINT16 *) Fixup; - *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust); - break; - - case EFI_IMAGE_REL_BASED_HIGHLOW: - Fixup32 = (UINT32 *) Fixup; - *Fixup32 = *Fixup32 + (UINT32) Adjust; - break; - - case EFI_IMAGE_REL_BASED_DIR64: - Fixup64 = (UINT64 *) Fixup; - *Fixup64 = *Fixup64 + (UINT64) Adjust; - break; - - default: - perror(L"Reloc %d Unknown relocation\n", n); - return EFI_UNSUPPORTED; - } - Reloc += 1; - } - RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; - n++; - } - - return EFI_SUCCESS; -} - static void drain_openssl_errors(void) { @@ -709,8 +465,7 @@ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, /* * Check whether we're in Secure Boot and user mode */ - -static BOOLEAN secure_mode (void) +BOOLEAN secure_mode (void) { static int first = 1; if (user_insecure_mode) @@ -740,288 +495,6 @@ static BOOLEAN secure_mode (void) return TRUE; } -#define check_size_line(data, datasize_in, hashbase, hashsize, l) ({ \ - if ((unsigned long)hashbase > \ - (unsigned long)data + datasize_in) { \ - efi_status = EFI_INVALID_PARAMETER; \ - 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) { \ - efi_status = EFI_INVALID_PARAMETER; \ - 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, unsigned int datasize_in, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - UINT8 *sha256hash, UINT8 *sha1hash) - -{ - unsigned int sha256ctxsize, sha1ctxsize; - 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_STATUS efi_status = EFI_SUCCESS; - EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data; - unsigned int PEHdr_offset = 0; - - size = datasize = 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); - - sha1ctxsize = Sha1GetContextSize(); - sha1ctx = AllocatePool(sha1ctxsize); - - if (!sha256ctx || !sha1ctx) { - perror(L"Unable to allocate memory for hash context\n"); - return EFI_OUT_OF_RESOURCES; - } - - if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) { - perror(L"Unable to initialise hash\n"); - efi_status = EFI_OUT_OF_RESOURCES; - goto done; - } - - /* Hash start to checksum */ - 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))) { - perror(L"Unable to generate hash\n"); - efi_status = EFI_OUT_OF_RESOURCES; - goto done; - } - - /* Hash post-checksum to start of certificate table */ - 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))) { - perror(L"Unable to generate hash\n"); - efi_status = EFI_OUT_OF_RESOURCES; - goto done; - } - - /* Hash end of certificate table to end of image header */ - 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); - efi_status = EFI_INVALID_PARAMETER; - goto done; - } - check_size(data, datasize_in, hashbase, hashsize); - - if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || - !(Sha1Update(sha1ctx, hashbase, hashsize))) { - perror(L"Unable to generate hash\n"); - efi_status = EFI_OUT_OF_RESOURCES; - goto done; - } - - /* Sort sections */ - 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); - efi_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); - efi_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) { - perror(L"Unable to allocate section header\n"); - efi_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); - /* But check it again just for better error messaging, and so - * clang-analyzer doesn't get confused. */ - if (Section == NULL) { - uint64_t addr; - - addr = PEHdr_offset + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER) - + context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader; - perror(L"Malformed file header.\n"); - perror(L"Image address for Section 0 is 0x%016llx\n", addr); - perror(L"File size is 0x%016llx\n", datasize); - efi_status = EFI_INVALID_PARAMETER; - goto done; - } - - /* Sort the section headers */ - for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) { - pos = index; - while ((pos > 0) && (Section->PointerToRawData < SectionHeader[pos - 1].PointerToRawData)) { - CopyMem (&SectionHeader[pos], &SectionHeader[pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER)); - pos--; - } - CopyMem (&SectionHeader[pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER)); - Section += 1; - } - - /* Hash the sections */ - for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) { - Section = &SectionHeader[index]; - if (Section->SizeOfRawData == 0) { - continue; - } - hashbase = ImageAddress(data, size, Section->PointerToRawData); - - if (!hashbase) { - perror(L"Malformed section header\n"); - efi_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); - efi_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))) { - perror(L"Unable to generate hash\n"); - efi_status = EFI_OUT_OF_RESOURCES; - goto done; - } - SumOfBytesHashed += Section->SizeOfRawData; - } - - /* Hash all remaining data up to SecDir if SecDir->Size is not 0 */ - if (datasize > SumOfBytesHashed && context->SecDir->Size) { - hashbase = data + SumOfBytesHashed; - hashsize = datasize - context->SecDir->Size - SumOfBytesHashed; - - if ((datasize - SumOfBytesHashed < context->SecDir->Size) || - (SumOfBytesHashed + hashsize != context->SecDir->VirtualAddress)) { - perror(L"Malformed binary after Attribute Certificate Table\n"); - console_print(L"datasize: %u SumOfBytesHashed: %u SecDir->Size: %lu\n", - datasize, SumOfBytesHashed, context->SecDir->Size); - console_print(L"hashsize: %u SecDir->VirtualAddress: 0x%08lx\n", - hashsize, context->SecDir->VirtualAddress); - efi_status = EFI_INVALID_PARAMETER; - goto done; - } - check_size(data, datasize_in, hashbase, hashsize); - - if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || - !(Sha1Update(sha1ctx, hashbase, hashsize))) { - perror(L"Unable to generate hash\n"); - efi_status = EFI_OUT_OF_RESOURCES; - goto done; - } - -#if 1 - } -#else // we have to migrate to doing this later :/ - SumOfBytesHashed += hashsize; - } - - /* Hash all remaining data */ - if (datasize > SumOfBytesHashed) { - hashbase = data + SumOfBytesHashed; - hashsize = datasize - SumOfBytesHashed; - - check_size(data, datasize_in, hashbase, hashsize); - - if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || - !(Sha1Update(sha1ctx, hashbase, hashsize))) { - perror(L"Unable to generate hash\n"); - efi_status = EFI_OUT_OF_RESOURCES; - goto done; - } - - SumOfBytesHashed += hashsize; - } -#endif - - if (!(Sha256Final(sha256ctx, sha256hash)) || - !(Sha1Final(sha1ctx, sha1hash))) { - perror(L"Unable to finalise hash\n"); - efi_status = EFI_OUT_OF_RESOURCES; - goto done; - } - - dprint(L"sha1 authenticode hash:\n"); - dhexdumpat(sha1hash, SHA1_DIGEST_SIZE, 0); - dprint(L"sha256 authenticode hash:\n"); - dhexdumpat(sha256hash, SHA256_DIGEST_SIZE, 0); - -done: - if (SectionHeader) - FreePool(SectionHeader); - if (sha1ctx) - FreePool(sha1ctx); - if (sha256ctx) - FreePool(sha256ctx); - - return efi_status; -} - static EFI_STATUS verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig, UINT8 *sha256hash, UINT8 *sha1hash) @@ -1122,9 +595,10 @@ verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig, /* * Check that the signature is valid and matches the binary */ -static EFI_STATUS verify_buffer (char *data, int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - UINT8 *sha256hash, UINT8 *sha1hash) +EFI_STATUS +verify_buffer (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash) { EFI_STATUS ret_efi_status; size_t size = datasize; @@ -1257,391 +731,6 @@ static EFI_STATUS verify_buffer (char *data, int datasize, return ret_efi_status; } -/* - * Read the binary header and grab appropriate information from it - */ -static EFI_STATUS read_header(void *data, unsigned int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context) -{ - EFI_IMAGE_DOS_HEADER *DosHdr = data; - EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; - unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize; - unsigned long FileAlignment = 0; - - 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 (!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; - context->SectionAlignment = PEHdr->Pe32Plus.OptionalHeader.SectionAlignment; - FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment; - 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; - context->SectionAlignment = PEHdr->Pe32.OptionalHeader.SectionAlignment; - FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment; - OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32); - } - - if (FileAlignment % 2 != 0) { - perror(L"File Alignment is invalid (%d)\n", FileAlignment); - return EFI_UNSUPPORTED; - } - if (FileAlignment == 0) - FileAlignment = 0x200; - if (context->SectionAlignment == 0) - context->SectionAlignment = PAGE_SIZE; - if (context->SectionAlignment < FileAlignment) - context->SectionAlignment = FileAlignment; - - 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 = OptHeaderSize - - sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; - 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->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 ((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) { - perror(L"Invalid image\n"); - return EFI_UNSUPPORTED; - } - - if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { - perror(L"Unsupported image type\n"); - return EFI_UNSUPPORTED; - } - - if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) { - perror(L"Unsupported image - Relocations have been stripped\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->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)); - - if (context->ImageSize < context->SizeOfHeaders) { - perror(L"Invalid image\n"); - return EFI_UNSUPPORTED; - } - - 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 || - (context->SecDir->VirtualAddress == datasize && - context->SecDir->Size > 0)) { - perror(L"Malformed security header\n"); - return EFI_INVALID_PARAMETER; - } - return EFI_SUCCESS; -} - -/* - * Once the image has been loaded it needs to be validated and relocated - */ -static EFI_STATUS handle_image (void *data, unsigned int datasize, - EFI_LOADED_IMAGE *li, - EFI_IMAGE_ENTRY_POINT *entry_point, - EFI_PHYSICAL_ADDRESS *alloc_address, - UINTN *alloc_pages) -{ - EFI_STATUS efi_status; - char *buffer; - int i; - EFI_IMAGE_SECTION_HEADER *Section; - char *base, *end; - PE_COFF_LOADER_IMAGE_CONTEXT context; - unsigned int alignment, alloc_size; - int found_entry_point = 0; - UINT8 sha1hash[SHA1_DIGEST_SIZE]; - UINT8 sha256hash[SHA256_DIGEST_SIZE]; - - /* - * The binary header contains relevant context and section pointers - */ - efi_status = read_header(data, datasize, &context); - if (EFI_ERROR(efi_status)) { - perror(L"Failed to read header: %r\n", efi_status); - return efi_status; - } - - /* - * We only need to verify the binary if we're in secure mode - */ - efi_status = generate_hash(data, datasize, &context, sha256hash, - sha1hash); - if (EFI_ERROR(efi_status)) - return efi_status; - - /* Measure the binary into the TPM */ -#ifdef REQUIRE_TPM - efi_status = -#endif - tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize, - (EFI_PHYSICAL_ADDRESS)(UINTN)context.ImageAddress, - li->FilePath, sha1hash, 4); -#ifdef REQUIRE_TPM - if (efi_status != EFI_SUCCESS) { - return efi_status; - } -#endif - - if (secure_mode ()) { - efi_status = verify_buffer(data, datasize, &context, - sha256hash, sha1hash); - - if (EFI_ERROR(efi_status)) { - if (verbose) - console_print(L"Verification failed: %r\n", efi_status); - else - console_error(L"Verification failed", efi_status); - return efi_status; - } else { - if (verbose) - console_print(L"Verification succeeded\n"); - } - } - - /* The spec says, uselessly, of SectionAlignment: - * ===== - * The alignment (in bytes) of sections when they are loaded into - * memory. It must be greater than or equal to FileAlignment. The - * default is the page size for the architecture. - * ===== - * Which doesn't tell you whose responsibility it is to enforce the - * "default", or when. It implies that the value in the field must - * be > FileAlignment (also poorly defined), but it appears visual - * studio will happily write 512 for FileAlignment (its default) and - * 0 for SectionAlignment, intending to imply PAGE_SIZE. - * - * We only support one page size, so if it's zero, nerf it to 4096. - */ - alignment = context.SectionAlignment; - if (!alignment) - alignment = 4096; - - alloc_size = ALIGN_VALUE(context.ImageSize + context.SectionAlignment, - PAGE_SIZE); - *alloc_pages = alloc_size / PAGE_SIZE; - - efi_status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderCode, - *alloc_pages, alloc_address); - if (EFI_ERROR(efi_status)) { - perror(L"Failed to allocate image buffer\n"); - return EFI_OUT_OF_RESOURCES; - } - - buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment); - - CopyMem(buffer, data, context.SizeOfHeaders); - - *entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); - if (!*entry_point) { - perror(L"Entry point is invalid\n"); - gBS->FreePages(*alloc_address, *alloc_pages); - return EFI_UNSUPPORTED; - } - - - char *RelocBase, *RelocBaseEnd; - /* - * These are relative virtual addresses, so we have to check them - * against the image size, not the data size. - */ - RelocBase = ImageAddress(buffer, context.ImageSize, - context.RelocDir->VirtualAddress); - /* - * RelocBaseEnd here is the address of the last byte of the table - */ - RelocBaseEnd = ImageAddress(buffer, context.ImageSize, - 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++, Section++) { - /* Don't try to copy discardable sections with zero size */ - if ((Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) && - !Section->Misc.VirtualSize) - continue; - - base = ImageAddress (buffer, context.ImageSize, - Section->VirtualAddress); - end = ImageAddress (buffer, context.ImageSize, - Section->VirtualAddress - + Section->Misc.VirtualSize - 1); - - if (end < base) { - perror(L"Section %d has negative size\n", i); - gBS->FreePages(*alloc_address, *alloc_pages); - return EFI_UNSUPPORTED; - } - - if (Section->VirtualAddress <= context.EntryPoint && - (Section->VirtualAddress + Section->SizeOfRawData - 1) - > context.EntryPoint) - found_entry_point++; - - /* 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 & EFI_IMAGE_SCN_MEM_DISCARDABLE) { - continue; - } - - if (!base) { - perror(L"Section %d has invalid base address\n", i); - return EFI_UNSUPPORTED; - } - if (!end) { - perror(L"Section %d has zero size\n", i); - return EFI_UNSUPPORTED; - } - - if (!(Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) && - (Section->VirtualAddress < context.SizeOfHeaders || - Section->PointerToRawData < context.SizeOfHeaders)) { - perror(L"Section %d is inside image headers\n", i); - return EFI_UNSUPPORTED; - } - - if (Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) { - ZeroMem(base, Section->Misc.VirtualSize); - } else { - if (Section->PointerToRawData < context.SizeOfHeaders) { - perror(L"Section %d is inside image headers\n", i); - return EFI_UNSUPPORTED; - } - - if (Section->SizeOfRawData > 0) - CopyMem(base, data + Section->PointerToRawData, - Section->SizeOfRawData); - - if (Section->SizeOfRawData < Section->Misc.VirtualSize) - ZeroMem(base + Section->SizeOfRawData, - Section->Misc.VirtualSize - Section->SizeOfRawData); - } - } - - if (context.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { - perror(L"Image has no relocation entry\n"); - FreePool(buffer); - return EFI_UNSUPPORTED; - } - - if (context.RelocDir->Size && RelocSection) { - /* - * Run the relocation fixups - */ - efi_status = relocate_coff(&context, RelocSection, data, - buffer); - - if (EFI_ERROR(efi_status)) { - perror(L"Relocation failed: %r\n", efi_status); - FreePool(buffer); - return efi_status; - } - } - - /* - * grub needs to know its location and size in memory, so fix up - * the loaded image protocol values - */ - li->ImageBase = buffer; - li->ImageSize = context.ImageSize; - - /* Pass the load options to the second stage loader */ - if ( load_options ) { - li->LoadOptions = load_options; - li->LoadOptionsSize = load_options_size; - } - - if (!found_entry_point) { - perror(L"Entry point is not within sections\n"); - return EFI_UNSUPPORTED; - } - if (found_entry_point > 1) { - perror(L"%d sections contain entry point\n"); - return EFI_UNSUPPORTED; - } - - return EFI_SUCCESS; -} - static int should_use_fallback(EFI_HANDLE image_handle) { |
