summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2012-05-30 18:36:46 -0400
committerMatthew Garrett <mjg@redhat.com>2012-05-30 18:36:46 -0400
commit7f0553356c47fbba7e0b3aaa7cc4128d6e40adf5 (patch)
tree1024a1829829397e1e63be88209789a1d1c92601
parent9d56c38fd14eca24fe3aef1262104c45ca13ff1a (diff)
downloadefi-boot-shim-7f0553356c47fbba7e0b3aaa7cc4128d6e40adf5.tar.gz
efi-boot-shim-7f0553356c47fbba7e0b3aaa7cc4128d6e40adf5.zip
Add image verification
-rw-r--r--Makefile17
-rw-r--r--PeImage.h17
-rw-r--r--cert.h1
-rw-r--r--shim.c237
4 files changed, 242 insertions, 30 deletions
diff --git a/Makefile b/Makefile
index ddbb6f28..437d2431 100644
--- a/Makefile
+++ b/Makefile
@@ -3,11 +3,11 @@ ARCH = $(shell uname -m | sed s,i[3456789]86,ia32,)
LIB_PATH = /usr/lib64
EFI_INCLUDE = /usr/include/efi
-EFI_INCLUDES = -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH) -I$(EFI_INCLUDE)/protocol
+EFI_INCLUDES = -nostdinc -I../Cryptlib -I../Cryptlib/Include -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH) -I$(EFI_INCLUDE)/protocol
EFI_PATH = /usr/lib64/gnuefi
LIB_GCC = $(shell $(CC) -print-libgcc-file-name)
-EFI_LIBS = -lefi -lgnuefi $(LIB_GCC)
+EFI_LIBS = -lefi -lgnuefi --start-group ../Cryptlib/libcryptlib.a ../Cryptlib/OpenSSL/libopenssl.a --end-group $(LIB_GCC)
EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o
EFI_LDS = $(EFI_PATH)/elf_$(ARCH)_efi.lds
@@ -19,22 +19,23 @@ CFLAGS = -O2 -fno-stack-protector -fno-strict-aliasing -fpic -fshort-wchar \
ifeq ($(ARCH),x86_64)
CFLAGS += -DEFI_FUNCTION_WRAPPER
endif
-LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) $(EFI_CRT_OBJS)
+LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) -L../Cryptlib -L../Cryptlib/OpenSSL $(EFI_CRT_OBJS)
TARGET = shim.efi
OBJS = shim.o
all: $(TARGET)
-shim.efi: $(OBJS)
+shim.efi: shim.so
-%.efi: %.o
- $(LD) $(LDFLAGS) $^ -o $@ $(EFI_LIBS)
+shim.so: $(OBJS)
+ $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS)
+
+%.efi: %.so
objcopy -j .text -j .sdata -j .data \
-j .dynamic -j .dynsym -j .rel \
-j .rela -j .reloc \
- --target=efi-app-$(ARCH) $@
- strip $@
+ --target=efi-app-$(ARCH) $^ $@
clean:
rm -f $(TARGET) $(OBJS)
diff --git a/PeImage.h b/PeImage.h
index 8ce61d80..ccaa7761 100644
--- a/PeImage.h
+++ b/PeImage.h
@@ -760,6 +760,21 @@ typedef union {
EFI_IMAGE_OPTIONAL_HEADER_UNION *Union;
} EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION;
+typedef struct _WIN_CERTIFICATE {
+ UINT32 dwLength;
+ UINT16 wRevision;
+ UINT16 wCertificateType;
+ //UINT8 bCertificate[ANYSIZE_ARRAY];
+} WIN_CERTIFICATE;
+
+typedef struct {
+ WIN_CERTIFICATE Hdr;
+ UINT8 CertData[1];
+} WIN_CERTIFICATE_EFI_PKCS;
+
+#define SHA256_DIGEST_SIZE 32
+#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002
+
typedef struct {
UINT64 ImageAddress;
UINT64 ImageSize;
@@ -769,7 +784,9 @@ typedef struct {
UINT16 NumberOfSections;
EFI_IMAGE_SECTION_HEADER *FirstSection;
EFI_IMAGE_DATA_DIRECTORY *RelocDir;
+ EFI_IMAGE_DATA_DIRECTORY *SecDir;
UINT64 NumberOfRvaAndSizes;
+ EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr;
} PE_COFF_LOADER_IMAGE_CONTEXT;
#endif
diff --git a/cert.h b/cert.h
new file mode 100644
index 00000000..2b862bbd
--- /dev/null
+++ b/cert.h
@@ -0,0 +1 @@
+static UINT8 cert[] = {0x00};
diff --git a/shim.c b/shim.c
index 36f96ebd..b99b81f7 100644
--- a/shim.c
+++ b/shim.c
@@ -1,17 +1,15 @@
-/* Read path */
-/* Load real bootloader */
-/* Check signature */
-/* Relocate real bootloader */
-/* Jump to real bootloader */
-
#include <efi.h>
#include <efilib.h>
+#include <Library/BaseCryptLib.h>
#include "PeImage.h"
-EFI_SYSTEM_TABLE *systab;
-EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
+static EFI_SYSTEM_TABLE *systab;
+static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
+
+#include "cert.h"
-EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize)
+static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata,
+ int *grubsize)
{
EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL;
@@ -25,7 +23,7 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize)
CHAR16 *PathName;
CHAR16 *Dir;
EFI_HANDLE device;
- unsigned int buffersize = 0;
+ unsigned int buffersize = sizeof(EFI_FILE_INFO);
int i;
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
@@ -38,7 +36,6 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize)
DevicePathSubType(li->FilePath) != MEDIA_FILEPATH_DP)
return EFI_NOT_FOUND;
-
device = li->DeviceHandle;
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
@@ -63,10 +60,18 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize)
0);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to open %s - %lx\n", FilePath->PathName, efi_status);
+ Print(L"Failed to open %s - %lx\n", FilePath->PathName,
+ efi_status);
return efi_status;
}
+ fileinfo = AllocatePool(buffersize);
+
+ if (!fileinfo) {
+ Print(L"Unable to allocate info buffer\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id,
&buffersize, fileinfo);
@@ -113,7 +118,7 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize)
StrCpy(PathName, Dir);
- StrCat(PathName, L"\\grub.efi");
+ StrCat(PathName, L"grub.efi");
efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName,
EFI_FILE_MODE_READ, 0);
@@ -154,7 +159,7 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize)
*grubdata);
if (efi_status != EFI_SUCCESS) {
- Print(L"Unexpected return from initial read\n");
+ Print(L"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize);
return efi_status;
}
@@ -168,7 +173,7 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize)
return EFI_SUCCESS;
}
-EFI_STATUS read_header(void *grubdata, PE_COFF_LOADER_IMAGE_CONTEXT *context)
+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;
@@ -191,8 +196,8 @@ EFI_STATUS read_header(void *grubdata, PE_COFF_LOADER_IMAGE_CONTEXT *context)
return EFI_UNSUPPORTED;
}
+ context->PEHdr = PEHdr;
context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase;
- PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)grubdata;
context->ImageSize = (UINT64)PEHdr->Pe32Plus.OptionalHeader.SizeOfImage;
context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
@@ -200,19 +205,196 @@ EFI_STATUS read_header(void *grubdata, PE_COFF_LOADER_IMAGE_CONTEXT *context)
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;
}
-void *ImageAddress (void *image, int size, unsigned int address)
+static void *ImageAddress (void *image, int size, unsigned int address)
{
if (address > size)
- return 0;
+ return NULL;
return image + address;
}
-EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, void *grubdata)
+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;
@@ -224,6 +406,8 @@ EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, void *grubdata)
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;
@@ -311,7 +495,7 @@ EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, void *grubdata)
}
-EFI_STATUS handle_grub (void *grubdata, int grubsize)
+static EFI_STATUS handle_grub (void *grubdata, int grubsize)
{
EFI_STATUS efi_status;
char *buffer;
@@ -319,13 +503,20 @@ EFI_STATUS handle_grub (void *grubdata, int grubsize)
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) {
@@ -387,8 +578,10 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
efi_status = load_grub(image_handle, &grubdata, &grubsize);
- if (efi_status != EFI_SUCCESS)
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to load grub\n");
return efi_status;
+ }
efi_status = handle_grub(grubdata, grubsize);