efi-boot-shim/shim.c
Gary Ching-Pang Lin 31d3bd054a Retrieve attributes of variables
We have to make sure the machine owner key is stored in a BS
variable.
2012-09-11 16:31:05 +08:00

1003 lines
26 KiB
C

/*
* shim - trivial UEFI first-stage bootloader
*
* Copyright 2012 Red Hat, Inc <mjg@redhat.com>
*
* 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 <efi.h>
#include <efilib.h>
#include <Library/BaseCryptLib.h>
#include "PeImage.h"
#include "shim.h"
#include "signature.h"
#define SECOND_STAGE L"\\grub.efi"
#define MOK_MANAGER L"\\MokManager.efi"
static EFI_SYSTEM_TABLE *systab;
static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
/*
* The vendor certificate used for validating the second stage loader
*/
extern UINT8 vendor_cert[];
extern UINT32 vendor_cert_size;
#define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }}
typedef enum {
DATA_FOUND,
DATA_NOT_FOUND,
VAR_NOT_FOUND
} CHECK_STATUS;
static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes,
UINTN *size, void **buffer)
{
EFI_STATUS efi_status;
char allocate = !(*size);
efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
attributes, size, buffer);
if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) {
return efi_status;
}
if (allocate)
*buffer = AllocatePool(*size);
if (!*buffer) {
Print(L"Unable to allocate variable buffer\n");
return EFI_OUT_OF_RESOURCES;
}
efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
attributes, size, *buffer);
return efi_status;
}
/*
* 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_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
void *data)
{
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 *)data + size;
context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)data;
if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
Print(L"Image has no relocation entry\n");
return EFI_UNSUPPORTED;
}
RelocBase = ImageAddress(data, size, context->RelocDir->VirtualAddress);
RelocBaseEnd = ImageAddress(data, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1);
if (!RelocBase || !RelocBaseEnd) {
Print(L"Reloc table overflows binary\n");
return EFI_UNSUPPORTED;
}
Adjust = (UINT64)data - context->ImageAddress;
while (RelocBase < RelocBaseEnd) {
Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock);
if ((void *)RelocEnd < data || (void *)RelocEnd > ImageEnd) {
Print(L"Reloc entry overflows binary\n");
return EFI_UNSUPPORTED;
}
FixupBase = ImageAddress(data, 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 CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash)
{
EFI_STATUS efi_status;
EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
UINTN dbsize = 0;
UINTN CertCount, Index;
UINT32 attributes;
BOOLEAN IsFound = FALSE;
void *db;
EFI_GUID CertType = EfiCertX509Guid;
efi_status = get_variable(dbname, secure_var, &attributes, &dbsize, &db);
if (efi_status != EFI_SUCCESS)
return VAR_NOT_FOUND;
CertList = db;
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
if (CompareGuid (&CertList->SignatureType, &CertType) == 0) {
CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize;
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
for (Index = 0; Index < CertCount; Index++) {
IsFound = AuthenticodeVerify (data->CertData,
data->Hdr.dwLength - sizeof(data->Hdr),
Cert->SignatureData,
CertList->SignatureSize,
hash, SHA256_DIGEST_SIZE);
if (IsFound)
break;
}
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
FreePool(db);
if (IsFound)
return DATA_FOUND;
return DATA_NOT_FOUND;
}
static CHECK_STATUS check_db_hash(CHAR16 *dbname, UINT8 *data)
{
EFI_STATUS efi_status;
EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
UINTN dbsize = 0;
UINTN CertCount, Index;
UINT32 attributes;
BOOLEAN IsFound = FALSE;
void *db;
unsigned int SignatureSize = SHA256_DIGEST_SIZE;
EFI_GUID CertType = EfiHashSha256Guid;
efi_status = get_variable(dbname, secure_var, &attributes, &dbsize, &db);
if (efi_status != EFI_SUCCESS) {
return VAR_NOT_FOUND;
}
CertList = db;
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize;
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
if (CompareGuid(&CertList->SignatureType, &CertType) == 0) {
for (Index = 0; Index < CertCount; Index++) {
if (CompareMem (Cert->SignatureData, data, SignatureSize) == 0) {
//
// Find the signature in database.
//
IsFound = TRUE;
break;
}
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
if (IsFound) {
break;
}
}
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
FreePool(db);
if (IsFound)
return DATA_FOUND;
return DATA_NOT_FOUND;
}
static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *hash)
{
if (check_db_hash(L"dbx", hash) == DATA_FOUND)
return EFI_ACCESS_DENIED;
if (check_db_cert(L"dbx", cert, hash) == DATA_FOUND)
return EFI_ACCESS_DENIED;
return EFI_SUCCESS;
}
static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *hash)
{
if (check_db_hash(L"db", hash) == DATA_FOUND)
return EFI_SUCCESS;
if (check_db_cert(L"db", cert, hash) == DATA_FOUND)
return EFI_SUCCESS;
return EFI_ACCESS_DENIED;
}
/*
* Check whether we're in Secure Boot and user mode
*/
static BOOLEAN secure_mode (void)
{
EFI_STATUS status;
EFI_GUID global_var = EFI_GLOBAL_VARIABLE;
UINTN charsize = sizeof(char);
UINT8 sb, setupmode;
UINT32 attributes;
status = get_variable(L"SecureBoot", global_var, &attributes, &charsize,
(void *)&sb);
/* FIXME - more paranoia here? */
if (status != EFI_SUCCESS || sb != 1) {
Print(L"Secure boot not enabled\n");
return FALSE;
}
status = get_variable(L"SetupMode", global_var, &attributes, &charsize,
(void *)&setupmode);
if (status == EFI_SUCCESS && setupmode == 1) {
Print(L"Platform is in setup mode\n");
return FALSE;
}
return TRUE;
}
/*
* 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, int whitelist)
{
unsigned int size = datasize;
unsigned int ctxsize;
void *ctx = NULL;
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 = NULL;
EFI_IMAGE_SECTION_HEADER *SectionCache;
cert = ImageAddress (data, size, context->SecDir->VirtualAddress);
if (!cert) {
Print(L"Certificate located outside the image\n");
return EFI_INVALID_PARAMETER;
}
if (cert->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
Print(L"Unsupported certificate type %x\n",
cert->Hdr.wCertificateType);
return EFI_UNSUPPORTED;
}
/* FIXME: Check which kind of hash */
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 = data;
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]) - data);
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 >= datasize) {
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(data, 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 = data + 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;
}
status = check_blacklist(cert, hash);
if (status != EFI_SUCCESS) {
Print(L"Binary is blacklisted\n");
goto done;
}
if (whitelist) {
status = check_whitelist(cert, hash);
if (status == EFI_SUCCESS) {
Print(L"Binary is whitelisted\n");
goto done;
}
}
if (!AuthenticodeVerify(cert->CertData,
context->SecDir->Size - sizeof(cert->Hdr),
vendor_cert, vendor_cert_size, hash,
SHA256_DIGEST_SIZE)) {
Print(L"Invalid signature\n");
status = EFI_ACCESS_DENIED;
} else {
status = EFI_SUCCESS;
}
done:
if (SectionHeader)
FreePool(SectionHeader);
if (ctx)
FreePool(ctx);
return 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;
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE)
PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + 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 >= datasize) {
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_image (void *data, unsigned int datasize,
EFI_LOADED_IMAGE *li)
{
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(data, datasize, &context);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to read header\n");
return efi_status;
}
if (secure_mode ()) {
efi_status = verify_buffer(data, datasize, &context, 0);
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, data, 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, data + Section->PointerToRawData, size);
if (size < Section->Misc.VirtualSize)
ZeroMem (base + size, Section->Misc.VirtualSize - size);
Section += 1;
}
efi_status = relocate_coff(&context, buffer);
if (efi_status != EFI_SUCCESS) {
Print(L"Relocation failed\n");
FreePool(buffer);
return efi_status;
}
entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
li->ImageBase = buffer;
li->ImageSize = context.ImageSize;
if (!entry_point) {
Print(L"Invalid entry point\n");
FreePool(buffer);
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
EFI_DEVICE_PATH **grubpath, CHAR16 **PathName)
{
EFI_DEVICE_PATH *devpath;
EFI_HANDLE device;
int i;
unsigned int pathlen = 0;
EFI_STATUS efi_status = EFI_SUCCESS;
CHAR16 *bootpath;
device = li->DeviceHandle;
devpath = li->FilePath;
bootpath = DevicePathToStr(devpath);
pathlen = StrLen(bootpath);
for (i=pathlen; i>0; i--) {
if (bootpath[i] == '\\')
break;
}
bootpath[i+1] = '\0';
if (bootpath[i-i] == '\\')
bootpath[i] = '\0';
*PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
if (!*PathName) {
Print(L"Failed to allocate path buffer\n");
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
*PathName[0] = '\0';
StrCat(*PathName, bootpath);
StrCat(*PathName, ImagePath);
*grubpath = FileDevicePath(device, *PathName);
error:
return efi_status;
}
/*
* Locate the second stage bootloader and read it into a buffer
*/
static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
int *datasize, CHAR16 *PathName)
{
EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL;
EFI_GUID file_info_id = EFI_FILE_INFO_ID;
EFI_STATUS efi_status;
EFI_HANDLE device;
EFI_FILE_INFO *fileinfo = NULL;
EFI_FILE_IO_INTERFACE *drive;
EFI_FILE *root, *grub;
UINTN buffersize = sizeof(EFI_FILE_INFO);
device = li->DeviceHandle;
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
&simple_file_system_protocol, &drive);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to find fs\n");
goto error;
}
efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to open fs\n");
goto error;
}
efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName,
EFI_FILE_MODE_READ, 0);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to open %s - %lx\n", PathName, efi_status);
goto error;
}
fileinfo = AllocatePool(buffersize);
if (!fileinfo) {
Print(L"Unable to allocate file info buffer\n");
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id,
&buffersize, fileinfo);
if (efi_status == EFI_BUFFER_TOO_SMALL) {
fileinfo = AllocatePool(buffersize);
if (!fileinfo) {
Print(L"Unable to allocate file info buffer\n");
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub,
&file_info_id, &buffersize,
fileinfo);
}
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to get file info\n");
goto error;
}
buffersize = fileinfo->FileSize;
*data = AllocatePool(buffersize);
if (!*data) {
Print(L"Unable to allocate file buffer\n");
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize,
*data);
if (efi_status == EFI_BUFFER_TOO_SMALL) {
FreePool(*data);
*data = AllocatePool(buffersize);
efi_status = uefi_call_wrapper(grub->Read, 3, grub,
&buffersize, *data);
}
if (efi_status != EFI_SUCCESS) {
Print(L"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize);
goto error;
}
*datasize = buffersize;
return EFI_SUCCESS;
error:
if (*data) {
FreePool(*data);
*data = NULL;
}
if (PathName)
FreePool(PathName);
if (fileinfo)
FreePool(fileinfo);
return efi_status;
}
EFI_STATUS shim_verify (void *buffer, UINT32 size)
{
EFI_STATUS status;
PE_COFF_LOADER_IMAGE_CONTEXT context;
if (!secure_mode())
return EFI_SUCCESS;
status = read_header(buffer, size, &context);
if (status != EFI_SUCCESS)
return status;
status = verify_buffer(buffer, size, &context, 1);
return status;
}
EFI_STATUS start_image(EFI_HANDLE image_handle, EFI_LOADED_IMAGE *li, CHAR16 *PathName)
{
EFI_STATUS efi_status;
EFI_LOADED_IMAGE li_bak;
void *data = NULL;
int datasize;
efi_status = load_image(li, &data, &datasize, PathName);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to load image\n");
goto done;
}
CopyMem(&li_bak, li, sizeof(li_bak));
efi_status = handle_image(data, datasize, li);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to load image\n");
CopyMem(li, &li_bak, sizeof(li_bak));
goto done;
}
efi_status = uefi_call_wrapper(entry_point, 3, image_handle, systab);
CopyMem(li, &li_bak, sizeof(li_bak));
done:
return efi_status;
}
EFI_STATUS init_grub(EFI_HANDLE image_handle)
{
EFI_STATUS efi_status;
EFI_HANDLE grub_handle = NULL;
EFI_LOADED_IMAGE *li;
EFI_DEVICE_PATH *grubpath;
CHAR16 *PathName;
EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
&loaded_image_protocol, &li);
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to init protocol\n");
return efi_status;
}
efi_status = generate_path(li, SECOND_STAGE, &grubpath, &PathName);
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to generate grub path\n");
goto done;
}
efi_status = uefi_call_wrapper(BS->LoadImage, 6, FALSE, image_handle,
grubpath, NULL, 0, &grub_handle);
if (efi_status == EFI_SUCCESS) {
/* Image validates - start it */
Print(L"Starting file via StartImage\n");
efi_status = uefi_call_wrapper(BS->StartImage, 3, grub_handle, NULL,
NULL);
uefi_call_wrapper(BS->UnloadImage, 1, grub_handle);
goto done;
}
efi_status = start_image(image_handle, li, PathName);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to start grub\n");
goto done;
}
done:
return efi_status;
}
EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
{
EFI_STATUS efi_status;
EFI_LOADED_IMAGE *li;
EFI_DEVICE_PATH *mokpath;
CHAR16 *PathName;
EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
/* TODO Check whether there is a request */
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
&loaded_image_protocol, &li);
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to init protocol\n");
return efi_status;
}
efi_status = generate_path(li, MOK_MANAGER, &mokpath, &PathName);
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to generate MokManager path\n");
goto done;
}
efi_status = start_image(image_handle, li, PathName);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to start MokManager\n");
goto done;
}
done:
return efi_status;
}
EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
static SHIM_LOCK shim_lock_interface;
EFI_HANDLE handle = NULL;
EFI_STATUS efi_status;
shim_lock_interface.Verify = shim_verify;
systab = passed_systab;
InitializeLib(image_handle, systab);
efi_status = check_mok_request(image_handle);
uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle,
&shim_lock_guid, EFI_NATIVE_INTERFACE,
&shim_lock_interface);
efi_status = init_grub(image_handle);
uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle,
&shim_lock_guid, &shim_lock_interface);
return efi_status;
}