blob: 26d9dcfbc2f0ec1f9351a982c95e552c395ff7e0 [file] [log] [blame]
/*
* Code Copyright 2012 Red Hat, Inc <mjg@redhat.com>
*
* Functions cut and pasted from
*
* git://github.com/mjg59/shim.git
*
* 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.
*
* ---
*
* This file is a functional simplification of Original code from TianoCore
* (http://tianocore.sf.net)
*
* MdePkg/Library/BasePeCoffLib/BasePeCoff.c
*
* Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
* Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
* This program and the accompanying materials
* are licensed and made available under the terms and conditions of the BSD License
* which accompanies this distribution. The full text of the license may be found at
* http://opensource.org/licenses/bsd-license.php.
*
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#include <efi.h>
#include <efilib.h>
#ifdef CONFIG_arm
#ifndef BUILD_EFI
/* FIXME:
* arm efi leaves a visibilit pragma pushed that won't work for
* non efi programs, so eliminate it */
#pragma GCC visibility pop
#endif
#endif
#include <pecoff.h>
#include <guid.h>
#include <simple_file.h>
#include <variables.h>
#include <sha256.h>
#include <errors.h>
#include <execute.h>
#include <buildefi.h>
EFI_STATUS
pecoff_read_header(PE_COFF_LOADER_IMAGE_CONTEXT *context, void *data)
{
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 &&
PEHdr->Pe32.OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
Print(L"Only IA32 or X64 images supported\n");
return EFI_UNSUPPORTED;
}
context->PEHdr = PEHdr;
if (PEHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
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->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
context->FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment;
} else if (PEHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase;
context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage;
context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders;
context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
context->FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment; }
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));
if (context->SecDir->VirtualAddress >= context->ImageSize) {
Print(L"Malformed security header\n");
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
EFI_STATUS
pecoff_image_layout(PE_COFF_LOADER_IMAGE_CONTEXT *context, void **data)
{
void *buffer = AllocatePool(context->ImageSize);
EFI_IMAGE_SECTION_HEADER *s;
int i, size;
char *base, *end;
CopyMem(buffer, *data, context->SizeOfHeaders);
for (i = 0; i < context->NumberOfSections; i++) {
s = &context->FirstSection[i];
size = ALIGN_VALUE(s->SizeOfRawData, context->FileAlignment);
base = pecoff_image_address(buffer, context->ImageSize, s->VirtualAddress);
end = pecoff_image_address(buffer, context->ImageSize, s->VirtualAddress + size - 1);
if (!base || !end) {
Print(L"Invalid section size\n");
return EFI_UNSUPPORTED;
}
if (s->SizeOfRawData > 0)
CopyMem(base, *data + s->PointerToRawData, size);
if (size < s->Misc.VirtualSize)
ZeroMem (base + size, s->Misc.VirtualSize - size);
}
//FreePool(*data);
*data = buffer;
return EFI_SUCCESS;
}
EFI_STATUS
pecoff_relocate(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;
EFI_STATUS efi_status;
efi_status = pecoff_image_layout(context, data);
if (efi_status != EFI_SUCCESS) {
Print(L"pecoff_image_layout: failed to layout image\n");
return efi_status;
}
if (context->PEHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)*data;
} else if (context->PEHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
context->PEHdr->Pe32.OptionalHeader.ImageBase = (UINT32)(long)*data;
}
if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
Print(L"Image has no relocation entry\n");
return EFI_UNSUPPORTED;
}
RelocBase = pecoff_image_address(*data, size, context->RelocDir->VirtualAddress);
RelocBaseEnd = pecoff_image_address(*data, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1);
if (!RelocBase || !RelocBaseEnd) {
Print(L"Reloc table overflows binary %d %d\n",
context->RelocDir->VirtualAddress,
context->RelocDir->VirtualAddress + context->RelocDir->Size - 1);
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 = pecoff_image_address(*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;
}
EFI_STATUS
pecoff_get_signature(PE_COFF_LOADER_IMAGE_CONTEXT *context, void *buffer,
WIN_CERTIFICATE **data, int signum)
{
WIN_CERTIFICATE *cert;
UINT32 offset;
int i;
if (!context->SecDir->Size)
return EFI_NOT_FOUND;
offset = context->SecDir->VirtualAddress;
cert = (WIN_CERTIFICATE *)(buffer + offset);
for (i = 0; i < signum; i++) {
offset += ALIGN_VALUE(cert->dwLength, 8);
cert = (WIN_CERTIFICATE *)(buffer + offset);
if (offset >= context->SecDir->VirtualAddress + context->SecDir->Size)
break;
}
if (i != signum)
return EFI_NOT_FOUND;
*data = cert;
return EFI_SUCCESS;
}
#ifdef BUILD_EFI
EFI_STATUS
pecoff_check_mok(EFI_HANDLE image, CHAR16 *name)
{
EFI_STATUS status;
UINT8 hash[SHA256_DIGEST_SIZE];
UINT8 *data;
UINTN len;
UINT32 attr;
/* first check is MokSBState. If we're in insecure mode, boot
* anyway regardless of dbx contents */
status = get_variable_attr(L"MokSBState", &data, &len,
MOK_OWNER, &attr);
if (status == EFI_SUCCESS) {
UINT8 MokSBState = data[0];
FreePool(data);
if ((attr & EFI_VARIABLE_RUNTIME_ACCESS) == 0
&& MokSBState)
return EFI_SUCCESS;
}
status = sha256_get_pecoff_digest(image, name, hash);
if (status != EFI_SUCCESS)
return status;
if (find_in_variable_esl(L"dbx", SIG_DB, hash, SHA256_DIGEST_SIZE)
== EFI_SUCCESS)
/* MOK list cannot override dbx */
goto check_tmplist;
status = get_variable_attr(L"MokList", &data, &len, MOK_OWNER, &attr);
if (status != EFI_SUCCESS)
goto check_tmplist;
FreePool(data);
if (attr & EFI_VARIABLE_RUNTIME_ACCESS)
goto check_tmplist;
if (find_in_variable_esl(L"MokList", MOK_OWNER, hash, SHA256_DIGEST_SIZE) == EFI_SUCCESS)
return EFI_SUCCESS;
check_tmplist:
status = get_variable_attr(L"tmpHashList", &data, &len, MOK_OWNER,
&attr);
if (status == EFI_SUCCESS && attr == EFI_VARIABLE_BOOTSERVICE_ACCESS
&& find_in_variable_esl(L"tmpHashList", MOK_OWNER, hash,
SHA256_DIGEST_SIZE) == EFI_SUCCESS)
return EFI_SUCCESS;
return EFI_SECURITY_VIOLATION;
}
EFI_STATUS
pecoff_execute_checked(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab, CHAR16 *name)
{
EFI_STATUS status;
EFI_LOADED_IMAGE *li;
EFI_DEVICE_PATH *loadpath = NULL;
CHAR16 *PathName = NULL;
EFI_HANDLE h;
EFI_FILE *file;
status = BS->HandleProtocol(image, &IMAGE_PROTOCOL, (VOID **)&li);
if (status != EFI_SUCCESS)
return status;
status = generate_path(name, li, &loadpath, &PathName);
if (status != EFI_SUCCESS)
return status;
status = BS->LoadImage(FALSE, image, loadpath, NULL, 0, &h);
if (status == EFI_SECURITY_VIOLATION || status == EFI_ACCESS_DENIED)
status = pecoff_check_mok(image, name);
if (status != EFI_SUCCESS)
/* this will fail if signature validation fails */
return status;
BS->UnloadImage(h);
status = simple_file_open(image, name, &file, EFI_FILE_MODE_READ);
if (status != EFI_SUCCESS)
return status;
pecoff_execute_image(file, name, image, systab);
simple_file_close(file);
return status;
}
EFI_STATUS
pecoff_execute_image(EFI_FILE *file, CHAR16 *name, EFI_HANDLE image,
EFI_SYSTEM_TABLE *systab)
{
UINTN DataSize;
void *buffer;
EFI_STATUS efi_status;
PE_COFF_LOADER_IMAGE_CONTEXT context;
EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
efi_status = simple_file_read_all(file, &DataSize, &buffer);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to read %s\n", name);
return efi_status;
}
Print(L"Read %d bytes from %s\n", DataSize, name);
efi_status = pecoff_read_header(&context, buffer);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to read header\n");
goto out;
}
efi_status = pecoff_relocate(&context, &buffer);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to relocate image\n");
goto out;
}
entry_point = pecoff_image_address(buffer, context.ImageSize, context.EntryPoint);
if (!entry_point) {
Print(L"Invalid entry point\n");
efi_status = EFI_UNSUPPORTED;
goto out;
}
efi_status = entry_point(image, systab);
out:
FreePool(buffer);
return efi_status;
}
#endif