diff --git a/cert.h b/cert.h index 2b862bb..380bc04 100644 --- a/cert.h +++ b/cert.h @@ -1 +1 @@ -static UINT8 cert[] = {0x00}; +static UINT8 vendor_cert[] = {0x00}; diff --git a/shim.c b/shim.c index b99b81f..f6311ad 100644 --- a/shim.c +++ b/shim.c @@ -1,13 +1,477 @@ +/* + * shim - trivial UEFI first-stage bootloader + * + * Copyright 2012 Red Hat, Inc + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Significant portions of this code are derived from Tianocore + * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel + * Corporation. + */ + #include #include #include #include "PeImage.h" +#define SECOND_STAGE L"grub.efi" + static EFI_SYSTEM_TABLE *systab; static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table); +/* + * The vendor certificate used for validating the second stage loader + */ + #include "cert.h" +/* + * Perform basic bounds checking of the intra-image pointers + */ +static void *ImageAddress (void *image, int size, unsigned int address) +{ + if (address > size) + return NULL; + + return image + address; +} + +/* + * Perform the actual relocation + */ +static EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, + void *grubdata) +{ + EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; + UINT64 Adjust; + UINT16 *Reloc, *RelocEnd; + char *Fixup, *FixupBase, *FixupData = NULL; + UINT16 *Fixup16; + UINT32 *Fixup32; + UINT64 *Fixup64; + int size = context->ImageSize; + void *ImageEnd = (char *)grubdata + size; + + context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)grubdata; + + if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { + Print(L"Image has no relocation entry\n"); + return EFI_UNSUPPORTED; + } + + RelocBase = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress); + RelocBaseEnd = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1); + + if (!RelocBase || !RelocBaseEnd) { + Print(L"Reloc table overflows binary\n"); + return EFI_UNSUPPORTED; + } + + Adjust = (UINT64)grubdata - context->ImageAddress; + + while (RelocBase < RelocBaseEnd) { + Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); + RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); + + if ((void *)RelocEnd < grubdata || (void *)RelocEnd > ImageEnd) { + Print(L"Reloc entry overflows binary\n"); + return EFI_UNSUPPORTED; + } + + FixupBase = ImageAddress(grubdata, size, RelocBase->VirtualAddress); + if (!FixupBase) { + Print(L"Invalid fixupbase\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))); + if (FixupData != NULL) { + *(UINT16 *) FixupData = *Fixup16; + FixupData = FixupData + sizeof (UINT16); + } + break; + + case EFI_IMAGE_REL_BASED_LOW: + Fixup16 = (UINT16 *) Fixup; + *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust); + if (FixupData != NULL) { + *(UINT16 *) FixupData = *Fixup16; + FixupData = FixupData + sizeof (UINT16); + } + break; + + case EFI_IMAGE_REL_BASED_HIGHLOW: + Fixup32 = (UINT32 *) Fixup; + *Fixup32 = *Fixup32 + (UINT32) Adjust; + if (FixupData != NULL) { + FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32)); + *(UINT32 *)FixupData = *Fixup32; + FixupData = FixupData + sizeof (UINT32); + } + break; + + case EFI_IMAGE_REL_BASED_DIR64: + Fixup64 = (UINT64 *) Fixup; + *Fixup64 = *Fixup64 + (UINT64) Adjust; + if (FixupData != NULL) { + FixupData = ALIGN_POINTER (FixupData, sizeof(UINT64)); + *(UINT64 *)(FixupData) = *Fixup64; + FixupData = FixupData + sizeof(UINT64); + } + break; + + default: + Print(L"Unknown relocation\n"); + return EFI_UNSUPPORTED; + } + Reloc += 1; + } + RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; + } + + return EFI_SUCCESS; +} + +/* + * Check that the signature is valid and matches the binary + */ +static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, + char *grubdata, int grubsize) +{ + unsigned int size = grubsize; + unsigned int ctxsize; + void *ctx; + UINT8 hash[SHA256_DIGEST_SIZE]; + EFI_STATUS status = EFI_ACCESS_DENIED; + char *hashbase; + unsigned int hashsize; + WIN_CERTIFICATE_EFI_PKCS *cert; + unsigned int SumOfBytesHashed, SumOfSectionBytes; + unsigned int index, pos; + EFI_IMAGE_SECTION_HEADER *Section; + EFI_IMAGE_SECTION_HEADER *SectionHeader; + EFI_IMAGE_SECTION_HEADER *SectionCache; + + cert = ImageAddress (grubdata, size, context->SecDir->VirtualAddress); + + if (!cert) { + Print(L"Certificate located outside the image\n"); + return EFI_INVALID_PARAMETER; + } + + if (cert->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + Print(L"Unsupported certificate type %x\n", + cert->Hdr.wCertificateType); + return EFI_UNSUPPORTED; + } + + /* FIXME: Check which kind of hash */ + + ctxsize = Sha256GetContextSize(); + ctx = AllocatePool(ctxsize); + + if (!ctx) { + Print(L"Unable to allocate memory for hash context\n"); + return EFI_OUT_OF_RESOURCES; + } + + if (!Sha256Init(ctx)) { + Print(L"Unable to initialise hash\n"); + status = EFI_OUT_OF_RESOURCES; + goto done; + } + + /* Hash start to checksum */ + hashbase = grubdata; + hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum - + hashbase; + + if (!(Sha256Update(ctx, hashbase, hashsize))) { + Print(L"Unable to generate hash\n"); + 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; + + if (!(Sha256Update(ctx, hashbase, hashsize))) { + Print(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]) - grubdata); + if (!(Sha256Update(ctx, hashbase, hashsize))) { + Print(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 >= grubsize) { + Print(L"Malformed binary: %x %x\n", SumOfSectionBytes, size); + status = EFI_INVALID_PARAMETER; + goto done; + } + + 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"); + status = EFI_OUT_OF_RESOURCES; + 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(grubdata, size, Section->PointerToRawData); + hashsize = (unsigned int) Section->SizeOfRawData; + + if (!hashbase) { + Print(L"Malformed section header\n"); + return EFI_INVALID_PARAMETER; + } + + if (!(Sha256Update(ctx, hashbase, hashsize))) { + Print(L"Unable to generate hash\n"); + status = EFI_OUT_OF_RESOURCES; + goto done; + } + SumOfBytesHashed += Section->SizeOfRawData; + } + + /* Hash all remaining data */ + if (size > SumOfBytesHashed) { + hashbase = grubdata + SumOfBytesHashed; + hashsize = (unsigned int)( + size - + context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - + SumOfBytesHashed); + + if (!(Sha256Update(ctx, hashbase, hashsize))) { + Print(L"Unable to generate hash\n"); + status = EFI_OUT_OF_RESOURCES; + goto done; + } + } + + if (!(Sha256Final(ctx, hash))) { + Print(L"Unable to finalise hash\n"); + status = EFI_OUT_OF_RESOURCES; + goto done; + } + + if (!AuthenticodeVerify(cert->CertData, + context->SecDir->Size - sizeof(cert->Hdr), + vendor_cert, sizeof(cert), hash, + SHA256_DIGEST_SIZE)) { + Print(L"Invalid signature\n"); + status = EFI_ACCESS_DENIED; + } else { + status = EFI_SUCCESS; + } + +done: + if (ctx) + FreePool(ctx); + + return status; +} + +/* + * Read the binary header and grab appropriate information from it + */ +static EFI_STATUS read_header(void *grubdata, + PE_COFF_LOADER_IMAGE_CONTEXT *context) +{ + EFI_IMAGE_DOS_HEADER *DosHdr = grubdata; + EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = grubdata; + + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) + PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)grubdata + DosHdr->e_lfanew); + + if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { + Print(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"); + 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; + 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->SecDir->VirtualAddress >= context->ImageSize) { + Print(L"Malformed security header\n"); + return EFI_INVALID_PARAMETER; + } + + if (context->SecDir->Size == 0) { + Print(L"Empty security header\n"); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/* + * Once the image has been loaded it needs to be validated and relocated + */ +static EFI_STATUS handle_grub (void *grubdata, int grubsize) +{ + EFI_STATUS efi_status; + char *buffer; + int i, size; + EFI_IMAGE_SECTION_HEADER *Section; + char *base, *end; + PE_COFF_LOADER_IMAGE_CONTEXT context; + + efi_status = read_header(grubdata, &context); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to read header\n"); + return efi_status; + } + + efi_status = verify_grub(&context, grubdata, grubsize); + + if (efi_status != EFI_SUCCESS) { + Print(L"Verification failed\n"); + return efi_status; + } + + buffer = AllocatePool(context.ImageSize); + + if (!buffer) { + Print(L"Failed to allocate image buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem(buffer, grubdata, context.SizeOfHeaders); + + Section = context.FirstSection; + for (i = 0; i < context.NumberOfSections; i++) { + size = Section->Misc.VirtualSize; + + if (size > Section->SizeOfRawData) + size = Section->SizeOfRawData; + + 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"); + return EFI_UNSUPPORTED; + } + + if (Section->SizeOfRawData > 0) + CopyMem(base, grubdata + Section->PointerToRawData, size); + + if (size < Section->Misc.VirtualSize) + ZeroMem (base + size, Section->Misc.VirtualSize - size); + + Section += 1; + } + + efi_status = relocate_grub(&context, buffer); + + if (efi_status != EFI_SUCCESS) { + Print(L"Relocation failed\n"); + return efi_status; + } + + entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); + if (!entry_point) { + Print(L"Invalid entry point\n"); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/* + * Locate the second stage bootloader and read it into a buffer + */ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize) { @@ -111,14 +575,14 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, } } - PathName = AllocatePool(StrLen(Dir) + StrLen(L"grub.efi")); + PathName = AllocatePool(StrLen(Dir) + StrLen(SECOND_STAGE)); if (!PathName) return EFI_LOAD_ERROR; StrCpy(PathName, Dir); - StrCat(PathName, L"grub.efi"); + StrCat(PathName, SECOND_STAGE); efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName, EFI_FILE_MODE_READ, 0); @@ -173,399 +637,6 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, return EFI_SUCCESS; } -static EFI_STATUS read_header(void *grubdata, PE_COFF_LOADER_IMAGE_CONTEXT *context) -{ - EFI_IMAGE_DOS_HEADER *DosHdr = grubdata; - EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = grubdata; - - if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) - PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)grubdata + DosHdr->e_lfanew); - - if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { - Print(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"); - 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; - 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->SecDir->VirtualAddress >= context->ImageSize) { - Print(L"Malformed security header\n"); - return EFI_INVALID_PARAMETER; - } - - if (context->SecDir->Size == 0) { - Print(L"Empty security header\n"); - return EFI_INVALID_PARAMETER; - } - - return EFI_SUCCESS; -} - -static void *ImageAddress (void *image, int size, unsigned int address) -{ - if (address > size) - return NULL; - - return image + address; -} - -static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grubdata, int grubsize) -{ - unsigned int size = grubsize; - unsigned int ctxsize; - void *ctx; - UINT8 hash[SHA256_DIGEST_SIZE]; - EFI_STATUS status = EFI_ACCESS_DENIED; - char *hashbase; - unsigned int hashsize; - WIN_CERTIFICATE_EFI_PKCS *cer; - unsigned int SumOfBytesHashed, SumOfSectionBytes; - unsigned int index, pos; - EFI_IMAGE_SECTION_HEADER *Section; - EFI_IMAGE_SECTION_HEADER *SectionHeader; - EFI_IMAGE_SECTION_HEADER *SectionCache; - - cert = ImageAddress (grubdata, size, context->SecDir->VirtualAddress); - - if (cer->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { - Print(L"Unsupported certificate type %x\n", - cert->Hdr.wCertificateType); - return EFI_UNSUPPORTED; - } - - /* Check which kind of hash */ - - ctxsize = Sha256GetContextSize(); - ctx = AllocatePool(ctxsize); - - if (!ctx) { - Print(L"Unable to allocate memory for hash context\n"); - return EFI_OUT_OF_RESOURCES; - } - - if (!Sha256Init(ctx)) { - Print(L"Unable to initialise hash\n"); - status = EFI_OUT_OF_RESOURCES; - goto done; - } - - /* Hash start to checksum */ - hashbase = grubdata; - hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum - - hashbase; - - if (!(Sha256Update(ctx, hashbase, hashsize))) { - Print(L"Unable to generate hash\n"); - 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; - - if (!(Sha256Update(ctx, hashbase, hashsize))) { - Print(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]) - grubdata); - if (!(Sha256Update(ctx, hashbase, hashsize))) { - Print(L"Unable to generate hash\n"); - status = EFI_OUT_OF_RESOURCES; - goto done; - } - - /* Sort sections and hash SizeOfRawData of each section */ - 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 >= grubsize) { - Print(L"Malformed binary: %x %x\n", SumOfSectionBytes, size); - status = EFI_INVALID_PARAMETER; - goto done; - } - - 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"); - status = EFI_OUT_OF_RESOURCES; - 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(grubdata, size, Section->PointerToRawData); - hashsize = (unsigned int) Section->SizeOfRawData; - - if (!(Sha256Update(ctx, hashbase, hashsize))) { - Print(L"Unable to generate hash\n"); - status = EFI_OUT_OF_RESOURCES; - goto done; - } - SumOfBytesHashed += Section->SizeOfRawData; - } - - /* Hash all remaining data */ - if (size > SumOfBytesHashed) { - hashbase = grubdata + SumOfBytesHashed; - hashsize = (unsigned int)( - size - - context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - - SumOfBytesHashed); - - if (!(Sha256Update(ctx, hashbase, hashsize))) { - Print(L"Unable to generate hash\n"); - status = EFI_OUT_OF_RESOURCES; - goto done; - } - } - - if (!(Sha256Final(ctx, hash))) { - Print(L"Unable to finalise hash\n"); - status = EFI_OUT_OF_RESOURCES; - goto done; - } - - if (!AuthenticodeVerify(WinCertificate->CertData, - context->SecDir->Size - sizeof(WinCertificate->Hdr), - cert, sizeof(cert), hash, - SHA256_DIGEST_SIZE)) { - Print(L"Invalid signature\n"); - status = EFI_ACCESS_DENIED; - } else { - status = EFI_SUCCESS; - } - -done: - if (ctx) - FreePool(ctx); - - return status; -} - -static EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, void *grubdata) -{ - EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; - UINT64 Adjust; - UINT16 *Reloc, *RelocEnd; - char *Fixup, *FixupBase, *FixupData = NULL; - UINT16 *Fixup16; - UINT32 *Fixup32; - UINT64 *Fixup64; - int size = context->ImageSize; - void *ImageEnd = (char *)grubdata + size; - - context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)grubdata; - - if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { - Print(L"Image has no relocation entry\n"); - return EFI_UNSUPPORTED; - } - - RelocBase = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress); - RelocBaseEnd = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1); - - if (!RelocBase || !RelocBaseEnd) { - Print(L"Reloc table overflows binary\n"); - return EFI_UNSUPPORTED; - } - - Adjust = (UINT64)grubdata - context->ImageAddress; - - while (RelocBase < RelocBaseEnd) { - Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); - RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); - - if ((void *)RelocEnd < grubdata || (void *)RelocEnd > ImageEnd) { - Print(L"Reloc entry overflows binary\n"); - return EFI_UNSUPPORTED; - } - - FixupBase = ImageAddress(grubdata, size, RelocBase->VirtualAddress); - if (!FixupBase) { - Print(L"Invalid fixupbase\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))); - if (FixupData != NULL) { - *(UINT16 *) FixupData = *Fixup16; - FixupData = FixupData + sizeof (UINT16); - } - break; - - case EFI_IMAGE_REL_BASED_LOW: - Fixup16 = (UINT16 *) Fixup; - *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust); - if (FixupData != NULL) { - *(UINT16 *) FixupData = *Fixup16; - FixupData = FixupData + sizeof (UINT16); - } - break; - - case EFI_IMAGE_REL_BASED_HIGHLOW: - Fixup32 = (UINT32 *) Fixup; - *Fixup32 = *Fixup32 + (UINT32) Adjust; - if (FixupData != NULL) { - FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32)); - *(UINT32 *)FixupData = *Fixup32; - FixupData = FixupData + sizeof (UINT32); - } - break; - - case EFI_IMAGE_REL_BASED_DIR64: - Fixup64 = (UINT64 *) Fixup; - *Fixup64 = *Fixup64 + (UINT64) Adjust; - if (FixupData != NULL) { - FixupData = ALIGN_POINTER (FixupData, sizeof(UINT64)); - *(UINT64 *)(FixupData) = *Fixup64; - FixupData = FixupData + sizeof(UINT64); - } - break; - - default: - Print(L"Unknown relocation\n"); - return EFI_UNSUPPORTED; - } - Reloc += 1; - } - RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; - } - - return EFI_SUCCESS; - -} - -static EFI_STATUS handle_grub (void *grubdata, int grubsize) -{ - EFI_STATUS efi_status; - char *buffer; - int i, size; - EFI_IMAGE_SECTION_HEADER *Section; - char *base, *end; - PE_COFF_LOADER_IMAGE_CONTEXT context; - - efi_status = read_header(grubdata, &context); - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to read header\n"); - return efi_status; - } - - efi_status = verify_grub(&context, grubdata, grubsize); - - if (efi_status != EFI_SUCCESS) { - Print(L"Verification failed\n"); - return efi_status; - } - - buffer = AllocatePool(context.ImageSize); - - if (!buffer) { - Print(L"Failed to allocate image buffer\n"); - return EFI_OUT_OF_RESOURCES; - } - - CopyMem(buffer, grubdata, context.SizeOfHeaders); - - Section = context.FirstSection; - for (i = 0; i < context.NumberOfSections; i++) { - size = Section->Misc.VirtualSize; - - if (size > Section->SizeOfRawData) - size = Section->SizeOfRawData; - - 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"); - return EFI_UNSUPPORTED; - } - - if (Section->SizeOfRawData > 0) - CopyMem(base, grubdata + Section->PointerToRawData, size); - - if (size < Section->Misc.VirtualSize) - ZeroMem (base + size, Section->Misc.VirtualSize - size); - - Section += 1; - } - - efi_status = relocate_grub(&context, buffer); - - if (efi_status != EFI_SUCCESS) { - Print(L"Relocation failed\n"); - return efi_status; - } - - entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); - if (!entry_point) { - Print(L"Invalid entry point\n"); - return EFI_UNSUPPORTED; - } - - return EFI_SUCCESS; -} - EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) { EFI_STATUS efi_status;