From b2fe1780947117d5c6b6b79ccf36d73d1f889977 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 11 Apr 2012 13:59:55 -0400 Subject: Initial commit --- shim.c | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 shim.c (limited to 'shim.c') diff --git a/shim.c b/shim.c new file mode 100644 index 00000000..2f5956c9 --- /dev/null +++ b/shim.c @@ -0,0 +1,335 @@ +/* Read path */ +/* Load real bootloader */ +/* Check signature */ +/* Relocate real bootloader */ +/* Jump to real bootloader */ + +#include +#include +#include "PeImage.h" + +EFI_SYSTEM_TABLE *systab; +EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table); + +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; + EFI_GUID file_info_id = EFI_FILE_INFO_ID; + EFI_STATUS efi_status; + EFI_LOADED_IMAGE *li; + EFI_FILE_IO_INTERFACE *drive; + EFI_FILE_INFO *fileinfo = NULL; + EFI_FILE *root, *grub; + FILEPATH_DEVICE_PATH *FilePath; + CHAR16 *PathName; + EFI_HANDLE device; + unsigned int buffersize = 0; + + efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, + &loaded_image_protocol, &li); + + if (efi_status != EFI_SUCCESS) + return efi_status; + + if (DevicePathType(li->FilePath) != MEDIA_DEVICE_PATH || + DevicePathSubType(li->FilePath) != MEDIA_FILEPATH_DP) + return EFI_NOT_FOUND; + + FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath; + + PathName = AllocatePool(StrLen(FilePath->PathName) + StrLen(L"grub.efi")); + + if (!PathName) + return EFI_LOAD_ERROR; + + StrCpy(PathName, FilePath->PathName); + + StrCat(PathName, L"grub.efi"); + + 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"); + return efi_status; + } + + efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to open fs\n"); + return efi_status; + } + + 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 grub\n"); + return efi_status; + } + + efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, + &buffersize, fileinfo); + + if (efi_status == EFI_BUFFER_TOO_SMALL) { + fileinfo = AllocatePool(buffersize); + 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"); + return efi_status; + } + + buffersize = fileinfo->FileSize; + *grubdata = AllocatePool(buffersize); + + efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, + *grubdata); + + if (efi_status != EFI_SUCCESS) { + Print(L"Unexpected return from initial read\n"); + return efi_status; + } + + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to read grub\n"); + return efi_status; + } + + *grubsize = buffersize; + + return EFI_SUCCESS; +} + +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->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; + 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)); + + return EFI_SUCCESS; +} + +void *ImageAddress (void *image, int size, unsigned int address) +{ + if (address > size) + return 0; + + return image + address; +} + +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; + + 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; + +} + +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, *maxend = 0; + 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; + } + + buffer = AllocatePool(context.ImageSize); + + 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 (end > maxend) + maxend = end; + + 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 = (void *)(context.EntryPoint + (UINT64)buffer); + + return EFI_SUCCESS; +} + +EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) +{ + EFI_STATUS efi_status; + void *grubdata; + int grubsize; + + systab = passed_systab; + + InitializeLib(image_handle, systab); + + efi_status = load_grub(image_handle, &grubdata, &grubsize); + + if (efi_status != EFI_SUCCESS) + return efi_status; + + efi_status = handle_grub(grubdata, grubsize); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to load grub\n"); + return efi_status; + } + + return uefi_call_wrapper(entry_point, 3, image_handle, passed_systab); +} -- cgit v1.2.3 From 0e6b01958a6b0dc6e00da3e6231a9d101e50acfb Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 11 Apr 2012 17:13:07 -0400 Subject: Some additional paranoia --- shim.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 2f5956c9..6dcbc27c 100644 --- a/shim.c +++ b/shim.c @@ -77,6 +77,10 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize) if (efi_status == EFI_BUFFER_TOO_SMALL) { 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); @@ -90,6 +94,11 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize) buffersize = fileinfo->FileSize; *grubdata = AllocatePool(buffersize); + if (!*grubdata) { + Print(L"Unable to allocate file buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, *grubdata); @@ -257,7 +266,7 @@ EFI_STATUS handle_grub (void *grubdata, int grubsize) char *buffer; int i, size; EFI_IMAGE_SECTION_HEADER *Section; - char *base, *end, *maxend = 0; + char *base, *end; PE_COFF_LOADER_IMAGE_CONTEXT context; efi_status = read_header(grubdata, &context); @@ -268,6 +277,11 @@ EFI_STATUS handle_grub (void *grubdata, int grubsize) 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; @@ -285,9 +299,6 @@ EFI_STATUS handle_grub (void *grubdata, int grubsize) return EFI_UNSUPPORTED; } - if (end > maxend) - maxend = end; - if (Section->SizeOfRawData > 0) CopyMem(base, grubdata + Section->PointerToRawData, size); @@ -304,7 +315,11 @@ EFI_STATUS handle_grub (void *grubdata, int grubsize) return efi_status; } - entry_point = (void *)(context.EntryPoint + (UINT64)buffer); + entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); + if (!entry_point) { + Print(L"Invalid entry point\n"); + return EFI_UNSUPPORTED; + } return EFI_SUCCESS; } -- cgit v1.2.3 From 9d56c38fd14eca24fe3aef1262104c45ca13ff1a Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 8 May 2012 03:00:51 -0400 Subject: Fix path generation --- shim.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 11 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 6dcbc27c..36f96ebd 100644 --- a/shim.c +++ b/shim.c @@ -23,8 +23,10 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize) EFI_FILE *root, *grub; FILEPATH_DEVICE_PATH *FilePath; CHAR16 *PathName; + CHAR16 *Dir; EFI_HANDLE device; unsigned int buffersize = 0; + int i; efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, &loaded_image_protocol, &li); @@ -36,16 +38,6 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize) DevicePathSubType(li->FilePath) != MEDIA_FILEPATH_DP) return EFI_NOT_FOUND; - FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath; - - PathName = AllocatePool(StrLen(FilePath->PathName) + StrLen(L"grub.efi")); - - if (!PathName) - return EFI_LOAD_ERROR; - - StrCpy(PathName, FilePath->PathName); - - StrCat(PathName, L"grub.efi"); device = li->DeviceHandle; @@ -64,11 +56,70 @@ EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, int *grubsize) return efi_status; } + FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath; + + efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, + FilePath->PathName, EFI_FILE_MODE_READ, + 0); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to open %s - %lx\n", FilePath->PathName, efi_status); + return efi_status; + } + + 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 info buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + 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"); + return efi_status; + } + + efi_status = uefi_call_wrapper(grub->Close, 1, grub); + + if (fileinfo->Attribute & EFI_FILE_DIRECTORY) { + Dir = FilePath->PathName; + } else { + for (i=StrLen(FilePath->PathName); i > 0; i--) { + if (FilePath->PathName[i] == '\\') + break; + } + + if (i) { + Dir = AllocatePool(i * 2); + CopyMem(Dir, FilePath->PathName, i * 2); + Dir[i] = '\0'; + } else { + Dir = AllocatePool(2); + Dir[0] = '\0'; + } + } + + PathName = AllocatePool(StrLen(Dir) + StrLen(L"grub.efi")); + + if (!PathName) + return EFI_LOAD_ERROR; + + StrCpy(PathName, Dir); + + StrCat(PathName, L"\\grub.efi"); + 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 grub\n"); + Print(L"Failed to open %s - %lx\n", PathName, efi_status); return efi_status; } -- cgit v1.2.3 From 7f0553356c47fbba7e0b3aaa7cc4128d6e40adf5 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 30 May 2012 18:36:46 -0400 Subject: Add image verification --- Makefile | 17 ++--- PeImage.h | 17 +++++ cert.h | 1 + shim.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 242 insertions(+), 30 deletions(-) create mode 100644 cert.h (limited to 'shim.c') 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 #include +#include #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); -- cgit v1.2.3 From f898777d22ef8c0bfe9e47c4ab29d2e9826d89e5 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 30 May 2012 22:08:09 -0400 Subject: Some cleanups --- cert.h | 2 +- shim.c | 659 ++++++++++++++++++++++++++++++++++++----------------------------- 2 files changed, 366 insertions(+), 295 deletions(-) (limited to 'shim.c') diff --git a/cert.h b/cert.h index 2b862bbd..380bc045 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 b99b81f7..f6311ada 100644 --- a/shim.c +++ b/shim.c @@ -1,234 +1,174 @@ +/* + * 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" -static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, - int *grubsize) +/* + * Perform basic bounds checking of the intra-image pointers + */ +static void *ImageAddress (void *image, int size, unsigned int address) { - EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; - EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL; - EFI_GUID file_info_id = EFI_FILE_INFO_ID; - EFI_STATUS efi_status; - EFI_LOADED_IMAGE *li; - EFI_FILE_IO_INTERFACE *drive; - EFI_FILE_INFO *fileinfo = NULL; - EFI_FILE *root, *grub; - FILEPATH_DEVICE_PATH *FilePath; - CHAR16 *PathName; - CHAR16 *Dir; - EFI_HANDLE device; - unsigned int buffersize = sizeof(EFI_FILE_INFO); - int i; - - efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, - &loaded_image_protocol, &li); - - if (efi_status != EFI_SUCCESS) - return efi_status; - - if (DevicePathType(li->FilePath) != MEDIA_DEVICE_PATH || - DevicePathSubType(li->FilePath) != MEDIA_FILEPATH_DP) - return EFI_NOT_FOUND; - - 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"); - return efi_status; - } - - efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); - - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to open fs\n"); - return efi_status; - } - - FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath; + if (address > size) + return NULL; - efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, - FilePath->PathName, EFI_FILE_MODE_READ, - 0); + return image + address; +} - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to open %s - %lx\n", FilePath->PathName, - efi_status); - return efi_status; - } +/* + * 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; - fileinfo = AllocatePool(buffersize); + context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)grubdata; - if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); - return EFI_OUT_OF_RESOURCES; + if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { + Print(L"Image has no relocation entry\n"); + return EFI_UNSUPPORTED; } - efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, - &buffersize, fileinfo); + RelocBase = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress); + RelocBaseEnd = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1); - if (efi_status == EFI_BUFFER_TOO_SMALL) { - 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); + if (!RelocBase || !RelocBaseEnd) { + Print(L"Reloc table overflows binary\n"); + return EFI_UNSUPPORTED; } - if (efi_status != EFI_SUCCESS) { - Print(L"Unable to get file info\n"); - return efi_status; - } + Adjust = (UINT64)grubdata - context->ImageAddress; - efi_status = uefi_call_wrapper(grub->Close, 1, grub); + while (RelocBase < RelocBaseEnd) { + Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); + RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); - if (fileinfo->Attribute & EFI_FILE_DIRECTORY) { - Dir = FilePath->PathName; - } else { - for (i=StrLen(FilePath->PathName); i > 0; i--) { - if (FilePath->PathName[i] == '\\') - break; + if ((void *)RelocEnd < grubdata || (void *)RelocEnd > ImageEnd) { + Print(L"Reloc entry overflows binary\n"); + return EFI_UNSUPPORTED; } - if (i) { - Dir = AllocatePool(i * 2); - CopyMem(Dir, FilePath->PathName, i * 2); - Dir[i] = '\0'; - } else { - Dir = AllocatePool(2); - Dir[0] = '\0'; + FixupBase = ImageAddress(grubdata, size, RelocBase->VirtualAddress); + if (!FixupBase) { + Print(L"Invalid fixupbase\n"); + return EFI_UNSUPPORTED; } - } - - PathName = AllocatePool(StrLen(Dir) + StrLen(L"grub.efi")); - - if (!PathName) - return EFI_LOAD_ERROR; - StrCpy(PathName, Dir); + while (Reloc < RelocEnd) { + Fixup = FixupBase + (*Reloc & 0xFFF); + switch ((*Reloc) >> 12) { + case EFI_IMAGE_REL_BASED_ABSOLUTE: + break; - StrCat(PathName, L"grub.efi"); + 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; - efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName, - EFI_FILE_MODE_READ, 0); + 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; - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to open %s - %lx\n", PathName, efi_status); - return efi_status; - } + 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; - efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, - &buffersize, fileinfo); + 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; - if (efi_status == EFI_BUFFER_TOO_SMALL) { - fileinfo = AllocatePool(buffersize); - if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); - return EFI_OUT_OF_RESOURCES; + default: + Print(L"Unknown relocation\n"); + return EFI_UNSUPPORTED; + } + Reloc += 1; } - 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"); - return efi_status; - } - - buffersize = fileinfo->FileSize; - *grubdata = AllocatePool(buffersize); - - if (!*grubdata) { - Print(L"Unable to allocate file buffer\n"); - return EFI_OUT_OF_RESOURCES; - } - - efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, - *grubdata); - - if (efi_status != EFI_SUCCESS) { - Print(L"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize); - return efi_status; - } - - if (efi_status != EFI_SUCCESS) { - Print(L"Unable to read grub\n"); - return efi_status; - } - - *grubsize = buffersize; - - 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; + RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; } 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) +/* + * 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; @@ -237,7 +177,7 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub EFI_STATUS status = EFI_ACCESS_DENIED; char *hashbase; unsigned int hashsize; - WIN_CERTIFICATE_EFI_PKCS *cer; + WIN_CERTIFICATE_EFI_PKCS *cert; unsigned int SumOfBytesHashed, SumOfSectionBytes; unsigned int index, pos; EFI_IMAGE_SECTION_HEADER *Section; @@ -246,13 +186,18 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub cert = ImageAddress (grubdata, size, context->SecDir->VirtualAddress); - if (cer->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + 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; } - /* Check which kind of hash */ + /* FIXME: Check which kind of hash */ ctxsize = Sha256GetContextSize(); ctx = AllocatePool(ctxsize); @@ -300,7 +245,7 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub goto done; } - /* Sort sections and hash SizeOfRawData of each section */ + /* Sort sections */ SumOfBytesHashed = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders; Section = (EFI_IMAGE_SECTION_HEADER *) ( @@ -348,6 +293,11 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub 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; @@ -377,10 +327,10 @@ static EFI_STATUS verify_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, char *grub goto done; } - if (!AuthenticodeVerify(WinCertificate->CertData, - context->SecDir->Size - sizeof(WinCertificate->Hdr), - cert, sizeof(cert), hash, - SHA256_DIGEST_SIZE)) { + 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 { @@ -391,110 +341,63 @@ 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; - } + return status; +} - FixupBase = ImageAddress(grubdata, size, RelocBase->VirtualAddress); - if (!FixupBase) { - Print(L"Invalid fixupbase\n"); - return EFI_UNSUPPORTED; - } +/* + * 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; - while (Reloc < RelocEnd) { - Fixup = FixupBase + (*Reloc & 0xFFF); - switch ((*Reloc) >> 12) { - case EFI_IMAGE_REL_BASED_ABSOLUTE: - break; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) + PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)grubdata + DosHdr->e_lfanew); - 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; + if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { + Print(L"Unsupported image type\n"); + return EFI_UNSUPPORTED; + } - 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; + if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) { + Print(L"Unsupported image - Relocations have been stripped\n"); + return EFI_UNSUPPORTED; + } - 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; + if (PEHdr->Pe32.OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + Print(L"Only 64-bit images supported\n"); + return EFI_UNSUPPORTED; + } - 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; + 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]; - default: - Print(L"Unknown relocation\n"); - return EFI_UNSUPPORTED; - } - Reloc += 1; - } - RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; + if (context->SecDir->VirtualAddress >= context->ImageSize) { + Print(L"Malformed security header\n"); + return EFI_INVALID_PARAMETER; } - return EFI_SUCCESS; + 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; @@ -566,6 +469,174 @@ static EFI_STATUS handle_grub (void *grubdata, int grubsize) 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) +{ + EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; + EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL; + EFI_GUID file_info_id = EFI_FILE_INFO_ID; + EFI_STATUS efi_status; + EFI_LOADED_IMAGE *li; + EFI_FILE_IO_INTERFACE *drive; + EFI_FILE_INFO *fileinfo = NULL; + EFI_FILE *root, *grub; + FILEPATH_DEVICE_PATH *FilePath; + CHAR16 *PathName; + CHAR16 *Dir; + EFI_HANDLE device; + unsigned int buffersize = sizeof(EFI_FILE_INFO); + int i; + + efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, + &loaded_image_protocol, &li); + + if (efi_status != EFI_SUCCESS) + return efi_status; + + if (DevicePathType(li->FilePath) != MEDIA_DEVICE_PATH || + DevicePathSubType(li->FilePath) != MEDIA_FILEPATH_DP) + return EFI_NOT_FOUND; + + 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"); + return efi_status; + } + + efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to open fs\n"); + return efi_status; + } + + FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath; + + efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, + FilePath->PathName, EFI_FILE_MODE_READ, + 0); + + if (efi_status != EFI_SUCCESS) { + 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); + + if (efi_status == EFI_BUFFER_TOO_SMALL) { + 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); + } + + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to get file info\n"); + return efi_status; + } + + efi_status = uefi_call_wrapper(grub->Close, 1, grub); + + if (fileinfo->Attribute & EFI_FILE_DIRECTORY) { + Dir = FilePath->PathName; + } else { + for (i=StrLen(FilePath->PathName); i > 0; i--) { + if (FilePath->PathName[i] == '\\') + break; + } + + if (i) { + Dir = AllocatePool(i * 2); + CopyMem(Dir, FilePath->PathName, i * 2); + Dir[i] = '\0'; + } else { + Dir = AllocatePool(2); + Dir[0] = '\0'; + } + } + + PathName = AllocatePool(StrLen(Dir) + StrLen(SECOND_STAGE)); + + if (!PathName) + return EFI_LOAD_ERROR; + + StrCpy(PathName, Dir); + + StrCat(PathName, SECOND_STAGE); + + 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); + return efi_status; + } + + 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 info buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + 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"); + return efi_status; + } + + buffersize = fileinfo->FileSize; + *grubdata = AllocatePool(buffersize); + + if (!*grubdata) { + Print(L"Unable to allocate file buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + + efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, + *grubdata); + + if (efi_status != EFI_SUCCESS) { + Print(L"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize); + return efi_status; + } + + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to read grub\n"); + return efi_status; + } + + *grubsize = buffersize; + + return EFI_SUCCESS; +} + EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) { EFI_STATUS efi_status; -- cgit v1.2.3 From f4b2473401728a05c2f0dde032cc2ec10c13da70 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 5 Jun 2012 10:52:30 -0400 Subject: Install a protocol for sharing code with grub --- shim.c | 32 +++++++++++++++++++++++++++++--- shim.h | 15 +++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 shim.h (limited to 'shim.c') diff --git a/shim.c b/shim.c index f6311ada..edaad0e5 100644 --- a/shim.c +++ b/shim.c @@ -37,6 +37,7 @@ #include #include #include "PeImage.h" +#include "shim.h" #define SECOND_STAGE L"grub.efi" @@ -167,8 +168,8 @@ static EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, /* * 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) +static EFI_STATUS verify_grub (char *grubdata, int grubsize, + PE_COFF_LOADER_IMAGE_CONTEXT *context) { unsigned int size = grubsize; unsigned int ctxsize; @@ -413,7 +414,7 @@ static EFI_STATUS handle_grub (void *grubdata, int grubsize) return efi_status; } - efi_status = verify_grub(&context, grubdata, grubsize); + efi_status = verify_grub(grubdata, grubsize, &context); if (efi_status != EFI_SUCCESS) { Print(L"Verification failed\n"); @@ -637,16 +638,41 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, return EFI_SUCCESS; } +EFI_STATUS verify_buffer (void *buffer, int size) +{ + EFI_STATUS status; + PE_COFF_LOADER_IMAGE_CONTEXT context; + + status = read_header(buffer, &context); + + if (status != EFI_SUCCESS) + return status; + + status = verify_grub(buffer, size, &context); + + return status; +} + EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) { EFI_STATUS efi_status; + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; void *grubdata; int grubsize; + static SHIM_LOCK shim_lock_interface; + EFI_HANDLE handle = NULL; + + shim_lock_interface.Verify = verify_buffer; systab = passed_systab; InitializeLib(image_handle, systab); + efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4, + &handle, &shim_lock_guid, + EFI_NATIVE_INTERFACE, + &shim_lock_interface); + efi_status = load_grub(image_handle, &grubdata, &grubsize); if (efi_status != EFI_SUCCESS) { diff --git a/shim.h b/shim.h new file mode 100644 index 00000000..ca325c0a --- /dev/null +++ b/shim.h @@ -0,0 +1,15 @@ +#define SHIM_LOCK_GUID \ + { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} } + +INTERFACE_DECL(_SHIM_LOCK); + +typedef +EFI_STATUS +(EFIAPI *EFI_SHIM_LOCK_VERIFY) ( + IN VOID *buffer; + IN UINT32 size; + ); + +typedef struct _SHIM_LOCK { + EFI_SHIM_LOCK_VERIFY Verify; +} SHIM_LOCK; -- cgit v1.2.3 From 7db60bd8c2eaecb428c5a095951bd20f9132e82f Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 5 Jun 2012 10:56:45 -0400 Subject: Rename variables --- shim.c | 82 +++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index edaad0e5..2f29166a 100644 --- a/shim.c +++ b/shim.c @@ -64,8 +64,8 @@ static void *ImageAddress (void *image, int size, unsigned int address) /* * Perform the actual relocation */ -static EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, - void *grubdata) +static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, + void *data) { EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; UINT64 Adjust; @@ -75,35 +75,35 @@ static EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, UINT32 *Fixup32; UINT64 *Fixup64; int size = context->ImageSize; - void *ImageEnd = (char *)grubdata + size; + void *ImageEnd = (char *)data + size; - context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)grubdata; + 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(grubdata, size, context->RelocDir->VirtualAddress); - RelocBaseEnd = ImageAddress(grubdata, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1); + 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)grubdata - context->ImageAddress; + 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 < grubdata || (void *)RelocEnd > ImageEnd) { + if ((void *)RelocEnd < data || (void *)RelocEnd > ImageEnd) { Print(L"Reloc entry overflows binary\n"); return EFI_UNSUPPORTED; } - FixupBase = ImageAddress(grubdata, size, RelocBase->VirtualAddress); + FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress); if (!FixupBase) { Print(L"Invalid fixupbase\n"); return EFI_UNSUPPORTED; @@ -168,10 +168,10 @@ static EFI_STATUS relocate_grub (PE_COFF_LOADER_IMAGE_CONTEXT *context, /* * Check that the signature is valid and matches the binary */ -static EFI_STATUS verify_grub (char *grubdata, int grubsize, - PE_COFF_LOADER_IMAGE_CONTEXT *context) +static EFI_STATUS verify_buffer (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context) { - unsigned int size = grubsize; + unsigned int size = datasize; unsigned int ctxsize; void *ctx; UINT8 hash[SHA256_DIGEST_SIZE]; @@ -185,7 +185,7 @@ static EFI_STATUS verify_grub (char *grubdata, int grubsize, EFI_IMAGE_SECTION_HEADER *SectionHeader; EFI_IMAGE_SECTION_HEADER *SectionCache; - cert = ImageAddress (grubdata, size, context->SecDir->VirtualAddress); + cert = ImageAddress (data, size, context->SecDir->VirtualAddress); if (!cert) { Print(L"Certificate located outside the image\n"); @@ -215,7 +215,7 @@ static EFI_STATUS verify_grub (char *grubdata, int grubsize, } /* Hash start to checksum */ - hashbase = grubdata; + hashbase = data; hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum - hashbase; @@ -239,7 +239,7 @@ static EFI_STATUS verify_grub (char *grubdata, int grubsize, /* 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); + (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; @@ -261,7 +261,7 @@ static EFI_STATUS verify_grub (char *grubdata, int grubsize, SumOfSectionBytes += SectionCache->SizeOfRawData; } - if (SumOfSectionBytes >= grubsize) { + if (SumOfSectionBytes >= datasize) { Print(L"Malformed binary: %x %x\n", SumOfSectionBytes, size); status = EFI_INVALID_PARAMETER; goto done; @@ -291,7 +291,7 @@ static EFI_STATUS verify_grub (char *grubdata, int grubsize, if (Section->SizeOfRawData == 0) { continue; } - hashbase = ImageAddress(grubdata, size, Section->PointerToRawData); + hashbase = ImageAddress(data, size, Section->PointerToRawData); hashsize = (unsigned int) Section->SizeOfRawData; if (!hashbase) { @@ -309,7 +309,7 @@ static EFI_STATUS verify_grub (char *grubdata, int grubsize, /* Hash all remaining data */ if (size > SumOfBytesHashed) { - hashbase = grubdata + SumOfBytesHashed; + hashbase = data + SumOfBytesHashed; hashsize = (unsigned int)( size - context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - @@ -348,14 +348,14 @@ done: /* * Read the binary header and grab appropriate information from it */ -static EFI_STATUS read_header(void *grubdata, +static EFI_STATUS read_header(void *data, PE_COFF_LOADER_IMAGE_CONTEXT *context) { - EFI_IMAGE_DOS_HEADER *DosHdr = grubdata; - EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = grubdata; + 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 *)grubdata + DosHdr->e_lfanew); + 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"); @@ -399,7 +399,7 @@ static EFI_STATUS read_header(void *grubdata, /* * Once the image has been loaded it needs to be validated and relocated */ -static EFI_STATUS handle_grub (void *grubdata, int grubsize) +static EFI_STATUS handle_grub (void *data, int datasize) { EFI_STATUS efi_status; char *buffer; @@ -408,13 +408,13 @@ static EFI_STATUS handle_grub (void *grubdata, int grubsize) char *base, *end; PE_COFF_LOADER_IMAGE_CONTEXT context; - efi_status = read_header(grubdata, &context); + efi_status = read_header(data, &context); if (efi_status != EFI_SUCCESS) { Print(L"Failed to read header\n"); return efi_status; } - efi_status = verify_grub(grubdata, grubsize, &context); + efi_status = verify_buffer(data, datasize, &context); if (efi_status != EFI_SUCCESS) { Print(L"Verification failed\n"); @@ -428,7 +428,7 @@ static EFI_STATUS handle_grub (void *grubdata, int grubsize) return EFI_OUT_OF_RESOURCES; } - CopyMem(buffer, grubdata, context.SizeOfHeaders); + CopyMem(buffer, data, context.SizeOfHeaders); Section = context.FirstSection; for (i = 0; i < context.NumberOfSections; i++) { @@ -446,7 +446,7 @@ static EFI_STATUS handle_grub (void *grubdata, int grubsize) } if (Section->SizeOfRawData > 0) - CopyMem(base, grubdata + Section->PointerToRawData, size); + CopyMem(base, data + Section->PointerToRawData, size); if (size < Section->Misc.VirtualSize) ZeroMem (base + size, Section->Misc.VirtualSize - size); @@ -454,7 +454,7 @@ static EFI_STATUS handle_grub (void *grubdata, int grubsize) Section += 1; } - efi_status = relocate_grub(&context, buffer); + efi_status = relocate_coff(&context, buffer); if (efi_status != EFI_SUCCESS) { Print(L"Relocation failed\n"); @@ -473,8 +473,8 @@ static EFI_STATUS handle_grub (void *grubdata, int grubsize) /* * Locate the second stage bootloader and read it into a buffer */ -static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, - int *grubsize) +static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **data, + int *datasize) { EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL; @@ -613,15 +613,15 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, } buffersize = fileinfo->FileSize; - *grubdata = AllocatePool(buffersize); + *data = AllocatePool(buffersize); - if (!*grubdata) { + if (!*data) { Print(L"Unable to allocate file buffer\n"); return EFI_OUT_OF_RESOURCES; } efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, - *grubdata); + *data); if (efi_status != EFI_SUCCESS) { Print(L"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize); @@ -633,12 +633,12 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **grubdata, return efi_status; } - *grubsize = buffersize; + *datasize = buffersize; return EFI_SUCCESS; } -EFI_STATUS verify_buffer (void *buffer, int size) +EFI_STATUS shim_verify (void *buffer, int size) { EFI_STATUS status; PE_COFF_LOADER_IMAGE_CONTEXT context; @@ -648,7 +648,7 @@ EFI_STATUS verify_buffer (void *buffer, int size) if (status != EFI_SUCCESS) return status; - status = verify_grub(buffer, size, &context); + status = verify_buffer(buffer, size, &context); return status; } @@ -657,12 +657,12 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) { EFI_STATUS efi_status; EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - void *grubdata; - int grubsize; + void *data; + int datasize; static SHIM_LOCK shim_lock_interface; EFI_HANDLE handle = NULL; - shim_lock_interface.Verify = verify_buffer; + shim_lock_interface.Verify = shim_verify; systab = passed_systab; @@ -673,14 +673,14 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) EFI_NATIVE_INTERFACE, &shim_lock_interface); - efi_status = load_grub(image_handle, &grubdata, &grubsize); + efi_status = load_grub(image_handle, &data, &datasize); if (efi_status != EFI_SUCCESS) { Print(L"Failed to load grub\n"); return efi_status; } - efi_status = handle_grub(grubdata, grubsize); + efi_status = handle_grub(data, datasize); if (efi_status != EFI_SUCCESS) { Print(L"Failed to load grub\n"); -- cgit v1.2.3 From 0db1af8aeb4b30ac9aef9485946dc31a6fa9d5bb Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 7 Jun 2012 14:00:48 -0400 Subject: Minor cleanups --- shim.c | 191 +++++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 109 insertions(+), 82 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 2f29166a..603d8f29 100644 --- a/shim.c +++ b/shim.c @@ -39,7 +39,7 @@ #include "PeImage.h" #include "shim.h" -#define SECOND_STAGE L"grub.efi" +#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); @@ -182,7 +182,7 @@ static EFI_STATUS verify_buffer (char *data, int datasize, unsigned int SumOfBytesHashed, SumOfSectionBytes; unsigned int index, pos; EFI_IMAGE_SECTION_HEADER *Section; - EFI_IMAGE_SECTION_HEADER *SectionHeader; + EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL; EFI_IMAGE_SECTION_HEADER *SectionCache; cert = ImageAddress (data, size, context->SecDir->VirtualAddress); @@ -240,6 +240,7 @@ static EFI_STATUS verify_buffer (char *data, int datasize, 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; @@ -339,6 +340,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize, } done: + if (SectionHeader) + FreePool(SectionHeader); if (ctx) FreePool(ctx); @@ -458,12 +461,14 @@ static EFI_STATUS handle_grub (void *data, int datasize) if (efi_status != EFI_SUCCESS) { Print(L"Relocation failed\n"); + FreePool(buffer); return efi_status; } entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); if (!entry_point) { Print(L"Invalid entry point\n"); + FreePool(buffer); return EFI_UNSUPPORTED; } @@ -480,16 +485,17 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **data, EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL; EFI_GUID file_info_id = EFI_FILE_INFO_ID; EFI_STATUS efi_status; + EFI_DEVICE_PATH *devpath; + EFI_FILE_INFO *fileinfo = NULL; EFI_LOADED_IMAGE *li; EFI_FILE_IO_INTERFACE *drive; - EFI_FILE_INFO *fileinfo = NULL; EFI_FILE *root, *grub; - FILEPATH_DEVICE_PATH *FilePath; - CHAR16 *PathName; - CHAR16 *Dir; + FILEPATH_DEVICE_PATH *FilePath; + CHAR16 *PathName = NULL; EFI_HANDLE device; unsigned int buffersize = sizeof(EFI_FILE_INFO); - int i; + unsigned int pathlen = 0; + int len; efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, &loaded_image_protocol, &li); @@ -497,100 +503,107 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **data, if (efi_status != EFI_SUCCESS) return efi_status; - if (DevicePathType(li->FilePath) != MEDIA_DEVICE_PATH || - DevicePathSubType(li->FilePath) != MEDIA_FILEPATH_DP) - return EFI_NOT_FOUND; - device = li->DeviceHandle; + devpath = li->FilePath; - efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, - &simple_file_system_protocol, &drive); + while (!IsDevicePathEnd(devpath) && + !IsDevicePathEnd(NextDevicePathNode(devpath))) { + FilePath = (FILEPATH_DEVICE_PATH *)devpath; + len = StrLen(FilePath->PathName); - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to find fs\n"); - return efi_status; - } + pathlen += len; - efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); - - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to open fs\n"); - return efi_status; - } + if (len == 1 && FilePath->PathName[0] == '\\') { + devpath = NextDevicePathNode(devpath); + continue; + } - FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath; + /* If no leading \, need to add one */ + if (FilePath->PathName[0] != '\\') + pathlen++; - efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, - FilePath->PathName, EFI_FILE_MODE_READ, - 0); + /* If trailing \, need to strip it */ + if (FilePath->PathName[len-1] == '\\') + pathlen--; - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to open %s - %lx\n", FilePath->PathName, - efi_status); - return efi_status; + devpath = NextDevicePathNode(devpath); } - fileinfo = AllocatePool(buffersize); + PathName = AllocatePool(pathlen + StrLen(SECOND_STAGE)); + PathName[0] = '\0'; - if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); - return EFI_OUT_OF_RESOURCES; + if (!PathName) { + Print(L"Failed to allocate path buffer\n"); + efi_status = EFI_OUT_OF_RESOURCES; + goto error; } - efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, - &buffersize, fileinfo); + devpath = li->FilePath; - if (efi_status == EFI_BUFFER_TOO_SMALL) { - fileinfo = AllocatePool(buffersize); - if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); + while (!IsDevicePathEnd(devpath) && + !IsDevicePathEnd(NextDevicePathNode(devpath))) { + CHAR16 *tmpbuffer; + FilePath = (FILEPATH_DEVICE_PATH *)devpath; + len = StrLen(FilePath->PathName); + + if (len == 1 && FilePath->PathName[0] == '\\') { + devpath = NextDevicePathNode(devpath); + continue; + } + + tmpbuffer = AllocatePool(len + 1); + + if (!tmpbuffer) { + Print(L"Unable to allocate temporary buffer\n"); return EFI_OUT_OF_RESOURCES; } - 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"); - return efi_status; - } + StrCpy(tmpbuffer, FilePath->PathName); - efi_status = uefi_call_wrapper(grub->Close, 1, grub); + /* If no leading \, need to add one */ + if (tmpbuffer[0] != '\\') + StrCat(PathName, L"\\"); - if (fileinfo->Attribute & EFI_FILE_DIRECTORY) { - Dir = FilePath->PathName; - } else { - for (i=StrLen(FilePath->PathName); i > 0; i--) { - if (FilePath->PathName[i] == '\\') - break; - } + /* If trailing \, need to strip it */ + if (tmpbuffer[len-1] == '\\') + tmpbuffer[len=1] = '\0'; - if (i) { - Dir = AllocatePool(i * 2); - CopyMem(Dir, FilePath->PathName, i * 2); - Dir[i] = '\0'; - } else { - Dir = AllocatePool(2); - Dir[0] = '\0'; - } + StrCat(PathName, tmpbuffer); + FreePool(tmpbuffer); + devpath = NextDevicePathNode(devpath); } - PathName = AllocatePool(StrLen(Dir) + StrLen(SECOND_STAGE)); + StrCat(PathName, SECOND_STAGE); - if (!PathName) - return EFI_LOAD_ERROR; + efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, + &simple_file_system_protocol, &drive); - StrCpy(PathName, Dir); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to find fs\n"); + goto error; + } - StrCat(PathName, SECOND_STAGE); + 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); - return 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, @@ -599,8 +612,9 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **data, if (efi_status == EFI_BUFFER_TOO_SMALL) { fileinfo = AllocatePool(buffersize); if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); - return EFI_OUT_OF_RESOURCES; + 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, @@ -609,33 +623,46 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **data, if (efi_status != EFI_SUCCESS) { Print(L"Unable to get file info\n"); - return efi_status; + goto error; } buffersize = fileinfo->FileSize; + *data = AllocatePool(buffersize); if (!*data) { Print(L"Unable to allocate file buffer\n"); - return EFI_OUT_OF_RESOURCES; + efi_status = EFI_OUT_OF_RESOURCES; + goto error; } - 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); - return efi_status; + 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"Unable to read grub\n"); - return efi_status; + 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, int size) @@ -657,7 +684,7 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) { EFI_STATUS efi_status; EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - void *data; + void *data = NULL; int datasize; static SHIM_LOCK shim_lock_interface; EFI_HANDLE handle = NULL; -- cgit v1.2.3 From 5bc80cec922b3e029f042c45519e984755f5e822 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 18 Jun 2012 17:31:42 -0400 Subject: Check that platform is in user mode before doing any validation --- TODO | 1 - shim.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) (limited to 'shim.c') diff --git a/TODO b/TODO index 0332f855..bdf43cfe 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,3 @@ -Fall through if not in user mode Check against blacklist Attempt to just LoadImage and StartImage? Support for netbooting \ No newline at end of file diff --git a/shim.c b/shim.c index 603d8f29..cd079452 100644 --- a/shim.c +++ b/shim.c @@ -50,6 +50,33 @@ static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TAB #include "cert.h" +static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, + unsigned int *size,void **buffer) +{ + EFI_STATUS efi_status; + UINT32 attributes; + char allocate = !!(*size); + + efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, + &attributes, size, NULL); + + 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 */ @@ -173,17 +200,33 @@ static EFI_STATUS verify_buffer (char *data, int datasize, { unsigned int size = datasize; unsigned int ctxsize; - void *ctx; + void *ctx = NULL; + UINT8 sb, setupmode; 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; + unsigned int index, pos, charsize = sizeof(char); EFI_IMAGE_SECTION_HEADER *Section; EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL; EFI_IMAGE_SECTION_HEADER *SectionCache; + EFI_GUID global_var = EFI_GLOBAL_VARIABLE; + + status = get_variable(L"SecureBoot", global_var, &charsize, (void *)&sb); + + /* FIXME - more paranoia here? */ + if (status != EFI_SUCCESS || sb != 1) { + status = EFI_SUCCESS; + goto done; + } + + status = get_variable(L"SetupMode", global_var, &charsize, (void *)&setupmode); + + if (status == EFI_SUCCESS && setupmode == 1) { + goto done; + } cert = ImageAddress (data, size, context->SecDir->VirtualAddress); -- cgit v1.2.3 From db54b0a4c6fec60960f7bdc63530cc91cb682b45 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 18 Jun 2012 17:31:42 -0400 Subject: Attempt to start image using LoadImage/StartImage first --- TODO | 1 - shim.c | 139 +++++++++++++++++++++++++++++++++++++++++++---------------------- 2 files changed, 93 insertions(+), 47 deletions(-) (limited to 'shim.c') diff --git a/TODO b/TODO index bdf43cfe..3f31658e 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,2 @@ Check against blacklist -Attempt to just LoadImage and StartImage? Support for netbooting \ No newline at end of file diff --git a/shim.c b/shim.c index cd079452..106b8585 100644 --- a/shim.c +++ b/shim.c @@ -218,6 +218,7 @@ static EFI_STATUS verify_buffer (char *data, int datasize, /* FIXME - more paranoia here? */ if (status != EFI_SUCCESS || sb != 1) { + Print(L"Secure boot not enabled\n"); status = EFI_SUCCESS; goto done; } @@ -225,6 +226,7 @@ static EFI_STATUS verify_buffer (char *data, int datasize, status = get_variable(L"SetupMode", global_var, &charsize, (void *)&setupmode); if (status == EFI_SUCCESS && setupmode == 1) { + Print(L"Platform is in setup mode\n"); goto done; } @@ -518,33 +520,14 @@ static EFI_STATUS handle_grub (void *data, int datasize) return EFI_SUCCESS; } -/* - * Locate the second stage bootloader and read it into a buffer - */ -static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **data, - int *datasize) +static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath, CHAR16 **PathName) { - EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; - EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL; - EFI_GUID file_info_id = EFI_FILE_INFO_ID; - EFI_STATUS efi_status; EFI_DEVICE_PATH *devpath; - EFI_FILE_INFO *fileinfo = NULL; - EFI_LOADED_IMAGE *li; - EFI_FILE_IO_INTERFACE *drive; - EFI_FILE *root, *grub; - FILEPATH_DEVICE_PATH *FilePath; - CHAR16 *PathName = NULL; EFI_HANDLE device; - unsigned int buffersize = sizeof(EFI_FILE_INFO); - unsigned int pathlen = 0; + FILEPATH_DEVICE_PATH *FilePath; int len; - - efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, - &loaded_image_protocol, &li); - - if (efi_status != EFI_SUCCESS) - return efi_status; + unsigned int pathlen = 0; + EFI_STATUS efi_status = EFI_SUCCESS; device = li->DeviceHandle; devpath = li->FilePath; @@ -572,10 +555,10 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **data, devpath = NextDevicePathNode(devpath); } - PathName = AllocatePool(pathlen + StrLen(SECOND_STAGE)); - PathName[0] = '\0'; + *PathName = AllocatePool(pathlen + StrLen(SECOND_STAGE)); + *PathName[0] = '\0'; - if (!PathName) { + if (!*PathName) { Print(L"Failed to allocate path buffer\n"); efi_status = EFI_OUT_OF_RESOURCES; goto error; @@ -605,21 +588,44 @@ static EFI_STATUS load_grub (EFI_HANDLE image_handle, void **data, /* If no leading \, need to add one */ if (tmpbuffer[0] != '\\') - StrCat(PathName, L"\\"); + StrCat(*PathName, L"\\"); /* If trailing \, need to strip it */ if (tmpbuffer[len-1] == '\\') tmpbuffer[len=1] = '\0'; - StrCat(PathName, tmpbuffer); + StrCat(*PathName, tmpbuffer); FreePool(tmpbuffer); devpath = NextDevicePathNode(devpath); } - StrCat(PathName, SECOND_STAGE); + StrCat(*PathName, SECOND_STAGE); + + *grubpath = FileDevicePath(device, *PathName); + +error: + return efi_status; +} + +/* + * Locate the second stage bootloader and read it into a buffer + */ +static EFI_STATUS load_grub (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; + unsigned int buffersize = sizeof(EFI_FILE_INFO); + + device = li->DeviceHandle; efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, - &simple_file_system_protocol, &drive); + &simple_file_system_protocol, &drive); if (efi_status != EFI_SUCCESS) { Print(L"Failed to find fs\n"); @@ -708,7 +714,7 @@ error: return efi_status; } -EFI_STATUS shim_verify (void *buffer, int size) +EFI_STATUS shim_verify (void *buffer, UINT32 size) { EFI_STATUS status; PE_COFF_LOADER_IMAGE_CONTEXT context; @@ -723,39 +729,80 @@ EFI_STATUS shim_verify (void *buffer, int size) return status; } -EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) +EFI_STATUS init_grub(EFI_HANDLE image_handle) { EFI_STATUS efi_status; - EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_HANDLE grub_handle = NULL; + EFI_LOADED_IMAGE *li; + EFI_DEVICE_PATH *grubpath; + CHAR16 *PathName; + EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; void *data = NULL; int datasize; - static SHIM_LOCK shim_lock_interface; - EFI_HANDLE handle = NULL; - shim_lock_interface.Verify = shim_verify; + efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, + &loaded_image_protocol, &li); - systab = passed_systab; + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to init protocol\n"); + return efi_status; + } - InitializeLib(image_handle, systab); + efi_status = generate_path(li, &grubpath, &PathName); + + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to generate grub path\n"); + goto done; + } - efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4, - &handle, &shim_lock_guid, - EFI_NATIVE_INTERFACE, - &shim_lock_interface); + efi_status = uefi_call_wrapper(BS->LoadImage, 6, FALSE, image_handle, + grubpath, NULL, 0, &grub_handle); - efi_status = load_grub(image_handle, &data, &datasize); + + 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 = load_grub(li, &data, &datasize, PathName); if (efi_status != EFI_SUCCESS) { Print(L"Failed to load grub\n"); - return efi_status; + goto done; } efi_status = handle_grub(data, datasize); if (efi_status != EFI_SUCCESS) { Print(L"Failed to load grub\n"); - return efi_status; + goto done; } - return uefi_call_wrapper(entry_point, 3, image_handle, passed_systab); + efi_status = uefi_call_wrapper(entry_point, 3, image_handle, systab); +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; + + shim_lock_interface.Verify = shim_verify; + + systab = passed_systab; + + InitializeLib(image_handle, systab); + + uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle, + &shim_lock_guid, EFI_NATIVE_INTERFACE, + &shim_lock_interface); + + return init_grub(image_handle); } -- cgit v1.2.3 From 3df68c187cac181c1d40dc52517b88b9fb992852 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 18 Jun 2012 17:31:42 -0400 Subject: Check binary against blacklist --- shim.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 106b8585..1bd595bc 100644 --- a/shim.c +++ b/shim.c @@ -38,6 +38,7 @@ #include #include "PeImage.h" #include "shim.h" +#include "signature.h" #define SECOND_STAGE L"\\grub.efi" @@ -51,7 +52,7 @@ static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TAB #include "cert.h" static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, - unsigned int *size,void **buffer) + UINTN *size, void **buffer) { EFI_STATUS efi_status; UINT32 attributes; @@ -192,6 +193,59 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, return EFI_SUCCESS; } +static EFI_STATUS check_blacklist (UINT8 *cert, UINT8 *hash) +{ + EFI_STATUS efi_status; + EFI_GUID global_var = EFI_GLOBAL_VARIABLE; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *Cert; + UINTN dbxsize = 0; + UINTN CertCount, Index; + BOOLEAN IsFound; + void *db; + unsigned int SignatureSize = SHA256_DIGEST_SIZE; + EFI_GUID CertType = EfiCertSha256Guid; + + efi_status = get_variable(L"dbx", global_var, &dbxsize, &db); + + /* If we can't read it then it can't be blacklisted */ + if (efi_status != EFI_SUCCESS) + return EFI_SUCCESS; + + CertList = db; + + while ((dbxsize > 0) && (dbxsize >= CertList->SignatureListSize)) { + CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize; + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + if ((CertList->SignatureSize == sizeof(EFI_SIGNATURE_DATA) - 1 + SignatureSize) && (CompareGuid(&CertList->SignatureType, &CertType))) { + for (Index = 0; Index < CertCount; Index++) { + if (CompareMem (Cert->SignatureData, hash, SignatureSize) == 0) { + // + // Find the signature in database. + // + IsFound = TRUE; + break; + } + + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + } + if (IsFound) { + break; + } + } + + dbxsize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + + FreePool(db); + + if (IsFound) + return EFI_ACCESS_DENIED; + + return EFI_SUCCESS; +} + /* * Check that the signature is valid and matches the binary */ @@ -208,7 +262,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize, unsigned int hashsize; WIN_CERTIFICATE_EFI_PKCS *cert; unsigned int SumOfBytesHashed, SumOfSectionBytes; - unsigned int index, pos, charsize = sizeof(char); + unsigned int index, pos; + UINTN charsize = sizeof(char); EFI_IMAGE_SECTION_HEADER *Section; EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL; EFI_IMAGE_SECTION_HEADER *SectionCache; @@ -374,6 +429,13 @@ static EFI_STATUS verify_buffer (char *data, int datasize, goto done; } + status = check_blacklist(vendor_cert, hash); + + if (status != EFI_SUCCESS) { + Print(L"Binary is blacklisted\n"); + goto done; + } + if (!AuthenticodeVerify(cert->CertData, context->SecDir->Size - sizeof(cert->Hdr), vendor_cert, sizeof(cert), hash, -- cgit v1.2.3 From 0a232ca95c77cffed5f46b65a16172a96a836afe Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 18 Jun 2012 17:31:42 -0400 Subject: Uninstall protocol on exit --- shim.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 1bd595bc..f524aa65 100644 --- a/shim.c +++ b/shim.c @@ -855,6 +855,7 @@ 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; @@ -866,5 +867,10 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) &shim_lock_guid, EFI_NATIVE_INTERFACE, &shim_lock_interface); - return init_grub(image_handle); + efi_status = init_grub(image_handle); + + uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle, + &shim_lock_guid, &shim_lock_interface); + + return efi_status; } -- cgit v1.2.3 From 3e890667fef402d981dd8623ec89dd7e52d9d2fd Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 19 Jun 2012 15:25:02 -0400 Subject: Fix cert size --- shim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index f524aa65..a479f33d 100644 --- a/shim.c +++ b/shim.c @@ -438,7 +438,7 @@ static EFI_STATUS verify_buffer (char *data, int datasize, if (!AuthenticodeVerify(cert->CertData, context->SecDir->Size - sizeof(cert->Hdr), - vendor_cert, sizeof(cert), hash, + vendor_cert, sizeof(vendor_cert), hash, SHA256_DIGEST_SIZE)) { Print(L"Invalid signature\n"); status = EFI_ACCESS_DENIED; -- cgit v1.2.3 From c16548d08b3b1fb645cc794a3f433fc00767d6cc Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 25 Jun 2012 10:59:08 -0400 Subject: Add black/white listing --- shim.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ signature.h | 3 +- 2 files changed, 93 insertions(+), 11 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index a479f33d..fa4f2c50 100644 --- a/shim.c +++ b/shim.c @@ -51,6 +51,12 @@ static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TAB #include "cert.h" +typedef enum { + DATA_FOUND, + DATA_NOT_FOUND, + VAR_NOT_FOUND +} CHECK_STATUS; + static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINTN *size, void **buffer) { @@ -193,33 +199,81 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, return EFI_SUCCESS; } -static EFI_STATUS check_blacklist (UINT8 *cert, UINT8 *hash) +static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash) { EFI_STATUS efi_status; EFI_GUID global_var = EFI_GLOBAL_VARIABLE; EFI_SIGNATURE_LIST *CertList; EFI_SIGNATURE_DATA *Cert; - UINTN dbxsize = 0; + UINTN dbsize = 0; + UINTN CertCount, Index; + BOOLEAN IsFound; + void *db; + EFI_GUID CertType = EfiCertX509Guid; + + efi_status = get_variable(dbname, global_var, &dbsize, &db); + + if (efi_status != EFI_SUCCESS) + return VAR_NOT_FOUND; + + CertList = db; + + while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { + if (CompareGuid (&CertList->SignatureType, &CertType)) { + 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 global_var = EFI_GLOBAL_VARIABLE; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *Cert; + UINTN dbsize = 0; UINTN CertCount, Index; BOOLEAN IsFound; void *db; unsigned int SignatureSize = SHA256_DIGEST_SIZE; - EFI_GUID CertType = EfiCertSha256Guid; + EFI_GUID CertType = EfiHashSha256Guid; - efi_status = get_variable(L"dbx", global_var, &dbxsize, &db); + efi_status = get_variable(dbname, global_var, &dbsize, &db); - /* If we can't read it then it can't be blacklisted */ if (efi_status != EFI_SUCCESS) - return EFI_SUCCESS; + return VAR_NOT_FOUND; CertList = db; - while ((dbxsize > 0) && (dbxsize >= CertList->SignatureListSize)) { + 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 ((CertList->SignatureSize == sizeof(EFI_SIGNATURE_DATA) - 1 + SignatureSize) && (CompareGuid(&CertList->SignatureType, &CertType))) { for (Index = 0; Index < CertCount; Index++) { - if (CompareMem (Cert->SignatureData, hash, SignatureSize) == 0) { + if (CompareMem (Cert->SignatureData, data, SignatureSize) == 0) { // // Find the signature in database. // @@ -234,18 +288,38 @@ static EFI_STATUS check_blacklist (UINT8 *cert, UINT8 *hash) } } - dbxsize -= CertList->SignatureListSize; + 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 that the signature is valid and matches the binary */ @@ -429,13 +503,20 @@ static EFI_STATUS verify_buffer (char *data, int datasize, goto done; } - status = check_blacklist(vendor_cert, hash); + status = check_blacklist(cert, hash); if (status != EFI_SUCCESS) { Print(L"Binary is blacklisted\n"); goto done; } + 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, sizeof(vendor_cert), hash, diff --git a/signature.h b/signature.h index 154da5e6..d2a8843e 100644 --- a/signature.h +++ b/signature.h @@ -1,6 +1,7 @@ #define SHA256_DIGEST_SIZE 32 -EFI_GUID EfiCertSha256Guid = { 0xc1c41626, 0x504c, 0x4092, {0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 }}; +EFI_GUID EfiHashSha256Guid = { 0xc1c41626, 0x504c, 0x4092, {0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 }}; +EFI_GUID EfiCertX509Guid = { 0xa5c059a1, 0x94e4, 0x4aa7, {0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 }}; typedef struct { /// -- cgit v1.2.3 From f23d76972708dff20d612d90b941f75fc907e4b1 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 25 Jun 2012 17:46:11 -0400 Subject: Fix get_variable --- shim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index fa4f2c50..532bff35 100644 --- a/shim.c +++ b/shim.c @@ -65,7 +65,7 @@ static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, char allocate = !!(*size); efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, - &attributes, size, NULL); + &attributes, size, buffer); if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) return efi_status; -- cgit v1.2.3 From 6eb1eca4f3fd8e2116572bb2a8ccf68920261581 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 2 Jul 2012 11:54:21 -0400 Subject: Fix type of buffersize --- shim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 532bff35..83d4b875 100644 --- a/shim.c +++ b/shim.c @@ -763,7 +763,7 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data, EFI_FILE_INFO *fileinfo = NULL; EFI_FILE_IO_INTERFACE *drive; EFI_FILE *root, *grub; - unsigned int buffersize = sizeof(EFI_FILE_INFO); + UINTN buffersize = sizeof(EFI_FILE_INFO); device = li->DeviceHandle; -- cgit v1.2.3 From 13484482550add26f0552bd8f2c9d3c8a7a020a7 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 2 Jul 2012 13:49:32 -0400 Subject: Remove whitelisting - the firmware will handle it via LoadImage/StartImage --- shim.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 83d4b875..50f90a7e 100644 --- a/shim.c +++ b/shim.c @@ -310,16 +310,6 @@ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *hash) 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 that the signature is valid and matches the binary */ @@ -510,13 +500,6 @@ static EFI_STATUS verify_buffer (char *data, int datasize, goto done; } - 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, sizeof(vendor_cert), hash, -- cgit v1.2.3 From c13fc2f71f799fad650257bbf4dcd8844e726925 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 2 Jul 2012 14:43:18 -0400 Subject: Fix up blacklist checking This was not quite as bugfree as would be hoped for. --- shim.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 50f90a7e..2026b82d 100644 --- a/shim.c +++ b/shim.c @@ -51,6 +51,8 @@ static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TAB #include "cert.h" +#define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }} + typedef enum { DATA_FOUND, DATA_NOT_FOUND, @@ -62,13 +64,14 @@ static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, { EFI_STATUS efi_status; UINT32 attributes; - char allocate = !!(*size); + char allocate = !(*size); efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, &attributes, size, buffer); - if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) + if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) { return efi_status; + } if (allocate) *buffer = AllocatePool(*size); @@ -202,16 +205,16 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash) { EFI_STATUS efi_status; - EFI_GUID global_var = EFI_GLOBAL_VARIABLE; + EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; EFI_SIGNATURE_LIST *CertList; EFI_SIGNATURE_DATA *Cert; UINTN dbsize = 0; UINTN CertCount, Index; - BOOLEAN IsFound; + BOOLEAN IsFound = FALSE; void *db; EFI_GUID CertType = EfiCertX509Guid; - efi_status = get_variable(dbname, global_var, &dbsize, &db); + efi_status = get_variable(dbname, secure_var, &dbsize, &db); if (efi_status != EFI_SUCCESS) return VAR_NOT_FOUND; @@ -219,7 +222,7 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data CertList = db; while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { - if (CompareGuid (&CertList->SignatureType, &CertType)) { + 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++) { @@ -251,27 +254,28 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data static CHECK_STATUS check_db_hash(CHAR16 *dbname, UINT8 *data) { EFI_STATUS efi_status; - EFI_GUID global_var = EFI_GLOBAL_VARIABLE; + EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; EFI_SIGNATURE_LIST *CertList; EFI_SIGNATURE_DATA *Cert; UINTN dbsize = 0; UINTN CertCount, Index; - BOOLEAN IsFound; + BOOLEAN IsFound = FALSE; void *db; unsigned int SignatureSize = SHA256_DIGEST_SIZE; EFI_GUID CertType = EfiHashSha256Guid; - efi_status = get_variable(dbname, global_var, &dbsize, &db); + efi_status = get_variable(dbname, secure_var, &dbsize, &db); - if (efi_status != EFI_SUCCESS) + 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 ((CertList->SignatureSize == sizeof(EFI_SIGNATURE_DATA) - 1 + SignatureSize) && (CompareGuid(&CertList->SignatureType, &CertType))) { + if (CompareGuid(&CertList->SignatureType, &CertType) == 0) { for (Index = 0; Index < CertCount; Index++) { if (CompareMem (Cert->SignatureData, data, SignatureSize) == 0) { // -- cgit v1.2.3 From 6279b58e835414aea1123fb28be6515afb08d05c Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 5 Jul 2012 12:51:12 -0400 Subject: Check whether secure boot is enabled before performing verify call --- shim.c | 61 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 23 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 2026b82d..5c864ac3 100644 --- a/shim.c +++ b/shim.c @@ -314,6 +314,35 @@ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *hash) return EFI_SUCCESS; } +/* + * 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; + + status = get_variable(L"SecureBoot", global_var, &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, &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 */ @@ -323,7 +352,6 @@ static EFI_STATUS verify_buffer (char *data, int datasize, unsigned int size = datasize; unsigned int ctxsize; void *ctx = NULL; - UINT8 sb, setupmode; UINT8 hash[SHA256_DIGEST_SIZE]; EFI_STATUS status = EFI_ACCESS_DENIED; char *hashbase; @@ -331,27 +359,9 @@ static EFI_STATUS verify_buffer (char *data, int datasize, WIN_CERTIFICATE_EFI_PKCS *cert; unsigned int SumOfBytesHashed, SumOfSectionBytes; unsigned int index, pos; - UINTN charsize = sizeof(char); EFI_IMAGE_SECTION_HEADER *Section; EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL; EFI_IMAGE_SECTION_HEADER *SectionCache; - EFI_GUID global_var = EFI_GLOBAL_VARIABLE; - - status = get_variable(L"SecureBoot", global_var, &charsize, (void *)&sb); - - /* FIXME - more paranoia here? */ - if (status != EFI_SUCCESS || sb != 1) { - Print(L"Secure boot not enabled\n"); - status = EFI_SUCCESS; - goto done; - } - - status = get_variable(L"SetupMode", global_var, &charsize, (void *)&setupmode); - - if (status == EFI_SUCCESS && setupmode == 1) { - Print(L"Platform is in setup mode\n"); - goto done; - } cert = ImageAddress (data, size, context->SecDir->VirtualAddress); @@ -592,11 +602,13 @@ static EFI_STATUS handle_grub (void *data, int datasize) return efi_status; } - efi_status = verify_buffer(data, datasize, &context); + if (secure_mode ()) { + efi_status = verify_buffer(data, datasize, &context); - if (efi_status != EFI_SUCCESS) { - Print(L"Verification failed\n"); - return efi_status; + if (efi_status != EFI_SUCCESS) { + Print(L"Verification failed\n"); + return efi_status; + } } buffer = AllocatePool(context.ImageSize); @@ -849,6 +861,9 @@ 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, &context); if (status != EFI_SUCCESS) -- cgit v1.2.3 From b2058cf8973ce8b0e98437293ac54f88decaf48a Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 5 Jul 2012 16:39:25 -0400 Subject: Re-add whitelisting - needed for protocol validation --- shim.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 5c864ac3..7d7c3f19 100644 --- a/shim.c +++ b/shim.c @@ -314,6 +314,16 @@ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *hash) 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 */ @@ -347,7 +357,7 @@ static BOOLEAN secure_mode (void) * 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) + PE_COFF_LOADER_IMAGE_CONTEXT *context, int whitelist) { unsigned int size = datasize; unsigned int ctxsize; @@ -514,6 +524,15 @@ static EFI_STATUS verify_buffer (char *data, int datasize, 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, sizeof(vendor_cert), hash, @@ -603,7 +622,7 @@ static EFI_STATUS handle_grub (void *data, int datasize) } if (secure_mode ()) { - efi_status = verify_buffer(data, datasize, &context); + efi_status = verify_buffer(data, datasize, &context, 0); if (efi_status != EFI_SUCCESS) { Print(L"Verification failed\n"); @@ -869,7 +888,7 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size) if (status != EFI_SUCCESS) return status; - status = verify_buffer(buffer, size, &context); + status = verify_buffer(buffer, size, &context, 1); return status; } -- cgit v1.2.3 From 5fe882ba746e41558736f22e704cf693a9bdc3e0 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 11 Jul 2012 10:57:46 -0400 Subject: Make sure ImageBase is set appropriately in the loaded_image protocol --- shim.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 7d7c3f19..40be6ced 100644 --- a/shim.c +++ b/shim.c @@ -606,7 +606,7 @@ static EFI_STATUS read_header(void *data, /* * Once the image has been loaded it needs to be validated and relocated */ -static EFI_STATUS handle_grub (void *data, int datasize) +static EFI_STATUS handle_grub (void *data, int datasize, EFI_LOADED_IMAGE *li) { EFI_STATUS efi_status; char *buffer; @@ -672,6 +672,9 @@ static EFI_STATUS handle_grub (void *data, int datasize) } 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); @@ -897,7 +900,7 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) { EFI_STATUS efi_status; EFI_HANDLE grub_handle = NULL; - EFI_LOADED_IMAGE *li; + EFI_LOADED_IMAGE *li, li_bak; EFI_DEVICE_PATH *grubpath; CHAR16 *PathName; EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; @@ -939,14 +942,19 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) goto done; } - efi_status = handle_grub(data, datasize); + CopyMem(&li_bak, li, sizeof(li_bak)); + + efi_status = handle_grub(data, datasize, li); if (efi_status != EFI_SUCCESS) { Print(L"Failed to load grub\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; -- cgit v1.2.3 From bc6aaefa2d86017868087790a42cf31f96d3ec93 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 11 Jul 2012 10:58:15 -0400 Subject: Make path generation more sensible --- shim.c | 70 ++++++++++++------------------------------------------------------ 1 file changed, 12 insertions(+), 58 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 40be6ced..39a7f478 100644 --- a/shim.c +++ b/shim.c @@ -688,39 +688,26 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath { EFI_DEVICE_PATH *devpath; EFI_HANDLE device; - FILEPATH_DEVICE_PATH *FilePath; - int len; + int i; unsigned int pathlen = 0; EFI_STATUS efi_status = EFI_SUCCESS; + CHAR16 *bootpath; device = li->DeviceHandle; devpath = li->FilePath; - while (!IsDevicePathEnd(devpath) && - !IsDevicePathEnd(NextDevicePathNode(devpath))) { - FilePath = (FILEPATH_DEVICE_PATH *)devpath; - len = StrLen(FilePath->PathName); + bootpath = DevicePathToStr(devpath); - pathlen += len; + pathlen = StrLen(bootpath); - if (len == 1 && FilePath->PathName[0] == '\\') { - devpath = NextDevicePathNode(devpath); - continue; - } - - /* If no leading \, need to add one */ - if (FilePath->PathName[0] != '\\') - pathlen++; - - /* If trailing \, need to strip it */ - if (FilePath->PathName[len-1] == '\\') - pathlen--; - - devpath = NextDevicePathNode(devpath); + for (i=pathlen; i>0; i--) { + if (bootpath[i] == '\\') + break; } - *PathName = AllocatePool(pathlen + StrLen(SECOND_STAGE)); - *PathName[0] = '\0'; + bootpath[i+1] = '\0'; + + *PathName = AllocatePool(StrSize(bootpath) + StrSize(SECOND_STAGE)); if (!*PathName) { Print(L"Failed to allocate path buffer\n"); @@ -728,41 +715,8 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath goto error; } - devpath = li->FilePath; - - while (!IsDevicePathEnd(devpath) && - !IsDevicePathEnd(NextDevicePathNode(devpath))) { - CHAR16 *tmpbuffer; - FilePath = (FILEPATH_DEVICE_PATH *)devpath; - len = StrLen(FilePath->PathName); - - if (len == 1 && FilePath->PathName[0] == '\\') { - devpath = NextDevicePathNode(devpath); - continue; - } - - tmpbuffer = AllocatePool(len + 1); - - if (!tmpbuffer) { - Print(L"Unable to allocate temporary buffer\n"); - return EFI_OUT_OF_RESOURCES; - } - - StrCpy(tmpbuffer, FilePath->PathName); - - /* If no leading \, need to add one */ - if (tmpbuffer[0] != '\\') - StrCat(*PathName, L"\\"); - - /* If trailing \, need to strip it */ - if (tmpbuffer[len-1] == '\\') - tmpbuffer[len=1] = '\0'; - - StrCat(*PathName, tmpbuffer); - FreePool(tmpbuffer); - devpath = NextDevicePathNode(devpath); - } - + *PathName[0] = '\0'; + StrCat(*PathName, bootpath); StrCat(*PathName, SECOND_STAGE); *grubpath = FileDevicePath(device, *PathName); -- cgit v1.2.3 From 00ced0c1259b400ef135e9415d917f908e9a4500 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 13 Jul 2012 00:30:22 -0400 Subject: Handle slightly stranger device paths --- shim.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 39a7f478..fc3dafc3 100644 --- a/shim.c +++ b/shim.c @@ -707,6 +707,9 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath bootpath[i+1] = '\0'; + if (bootpath[i-i] == '\\') + bootpath[i] = '\0'; + *PathName = AllocatePool(StrSize(bootpath) + StrSize(SECOND_STAGE)); if (!*PathName) { -- cgit v1.2.3 From 8518b8cc1ffb548c26c846c609e3bc38717850be Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 6 Sep 2012 12:13:44 -0400 Subject: Allow specification of vendor_cert through a build command line option. This allows you to specify the vendor_cert as a file on the command line during build. --- Makefile | 16 +++++++++++----- cert.S | 32 ++++++++++++++++++++++++++++++++ cert.h | 1 - shim.c | 6 +++--- 4 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 cert.S delete mode 100644 cert.h (limited to 'shim.c') diff --git a/Makefile b/Makefile index 1e3a020e..66b105ff 100644 --- a/Makefile +++ b/Makefile @@ -14,24 +14,30 @@ EFI_LIBS = -lefi -lgnuefi --start-group Cryptlib/libcryptlib.a Cryptlib/OpenSSL/ EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o EFI_LDS = $(EFI_PATH)/elf_$(ARCH)_efi.lds - CFLAGS = -ggdb -O0 -fno-stack-protector -fno-strict-aliasing -fpic -fshort-wchar \ -Wall -mno-red-zone \ $(EFI_INCLUDES) ifeq ($(ARCH),x86_64) CFLAGS += -DEFI_FUNCTION_WRAPPER endif +ifneq ($(origin VENDOR_CERT_FILE), undefined) + CFLAGS += -DVENDOR_CERT_FILE=\"$(VENDOR_CERT_FILE)\" +endif + LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) -LCryptlib -LCryptlib/OpenSSL $(EFI_CRT_OBJS) -TARGET = shim.efi -OBJS = shim.o shim.so -SOURCES = shim.c shim.h signature.h PeImage.h cert.h +TARGET = shim.efi +OBJS = shim.o cert.o +SOURCES = shim.c shim.h signature.h PeImage.h all: $(TARGET) shim.o: $(SOURCES) -shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a +cert.o : cert.S + $(CC) $(CFLAGS) -c -o $@ $< + +shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a cert.o $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) Cryptlib/libcryptlib.a: diff --git a/cert.S b/cert.S new file mode 100644 index 00000000..129bab55 --- /dev/null +++ b/cert.S @@ -0,0 +1,32 @@ +#if defined(VENDOR_CERT_FILE) + .globl vendor_cert + .data + .align 16 + .type vendor_cert, @object + .size vendor_cert_size, vendor_cert_size-vendor_cert +vendor_cert: +.incbin VENDOR_CERT_FILE + + .globl vendor_cert_size + .data + .align 16 + .type vendor_cert_size, @object + .size vendor_cert_size, 4 +vendor_cert_size: + .long vendor_cert_size - vendor_cert +#else + .globl vendor_cert + .bss + .type vendor_cert, @object + .size vendor_cert, 1 +vendor_cert: + .zero 1 + + .globl vendor_cert_size + .data + .align 4 + .type vendor_cert_size, @object + .size vendor_cert_size, 4 +vendor_cert_size: + .long 1 +#endif diff --git a/cert.h b/cert.h deleted file mode 100644 index 380bc045..00000000 --- a/cert.h +++ /dev/null @@ -1 +0,0 @@ -static UINT8 vendor_cert[] = {0x00}; diff --git a/shim.c b/shim.c index fc3dafc3..2d9044df 100644 --- a/shim.c +++ b/shim.c @@ -48,8 +48,8 @@ static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TAB /* * The vendor certificate used for validating the second stage loader */ - -#include "cert.h" +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 }} @@ -535,7 +535,7 @@ static EFI_STATUS verify_buffer (char *data, int datasize, if (!AuthenticodeVerify(cert->CertData, context->SecDir->Size - sizeof(cert->Hdr), - vendor_cert, sizeof(vendor_cert), hash, + vendor_cert, vendor_cert_size, hash, SHA256_DIGEST_SIZE)) { Print(L"Invalid signature\n"); status = EFI_ACCESS_DENIED; -- cgit v1.2.3 From ce78d2d250b316f11d0170eef77653e8136c2035 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 6 Sep 2012 12:13:44 -0400 Subject: Use the file size, not the image size field, for verification. --- shim.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 2d9044df..6a3c054d 100644 --- a/shim.c +++ b/shim.c @@ -555,7 +555,7 @@ done: /* * Read the binary header and grab appropriate information from it */ -static EFI_STATUS read_header(void *data, +static EFI_STATUS read_header(void *data, unsigned int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context) { EFI_IMAGE_DOS_HEADER *DosHdr = data; @@ -590,7 +590,7 @@ static EFI_STATUS read_header(void *data, 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) { + if (context->SecDir->VirtualAddress >= datasize) { Print(L"Malformed security header\n"); return EFI_INVALID_PARAMETER; } @@ -606,7 +606,8 @@ static EFI_STATUS read_header(void *data, /* * Once the image has been loaded it needs to be validated and relocated */ -static EFI_STATUS handle_grub (void *data, int datasize, EFI_LOADED_IMAGE *li) +static EFI_STATUS handle_grub (void *data, unsigned int datasize, + EFI_LOADED_IMAGE *li) { EFI_STATUS efi_status; char *buffer; @@ -615,7 +616,7 @@ static EFI_STATUS handle_grub (void *data, int datasize, EFI_LOADED_IMAGE *li) char *base, *end; PE_COFF_LOADER_IMAGE_CONTEXT context; - efi_status = read_header(data, &context); + efi_status = read_header(data, datasize, &context); if (efi_status != EFI_SUCCESS) { Print(L"Failed to read header\n"); return efi_status; @@ -843,7 +844,7 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size) if (!secure_mode()) return EFI_SUCCESS; - status = read_header(buffer, &context); + status = read_header(buffer, size, &context); if (status != EFI_SUCCESS) return status; -- cgit v1.2.3 From 7430b90148438b6e23b316be93aa04cba32561ca Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 6 Sep 2012 12:13:44 -0400 Subject: Break out of our db checking loop at the appropriate time. The break in check_db_cert is at the wrong level due to a typo in indentation, and as a result only the last cert in the list can correctly match. Rectify that. Signed-off-by: Peter Jones --- shim.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 6a3c054d..59825099 100644 --- a/shim.c +++ b/shim.c @@ -231,9 +231,8 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data Cert->SignatureData, CertList->SignatureSize, hash, SHA256_DIGEST_SIZE); - } - if (IsFound) { - break; + if (IsFound) + break; } Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); -- cgit v1.2.3