diff options
| -rw-r--r-- | TODO | 2 | ||||
| -rw-r--r-- | shim.c | 419 |
2 files changed, 302 insertions, 119 deletions
@@ -1,4 +1,2 @@ -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 @@ -38,8 +38,9 @@ #include <Library/BaseCryptLib.h> #include "PeImage.h" #include "shim.h" +#include "signature.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); @@ -50,6 +51,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, + UINTN *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 */ @@ -165,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 */ @@ -173,7 +254,8 @@ 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; @@ -181,9 +263,27 @@ 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; + 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); @@ -240,6 +340,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; @@ -328,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, @@ -339,6 +447,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize, } done: + if (SectionHeader) + FreePool(SectionHeader); if (ctx) FreePool(ctx); @@ -458,139 +568,153 @@ 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; } 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_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_DEVICE_PATH *devpath; 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); + FILEPATH_DEVICE_PATH *FilePath; + int len; + unsigned int pathlen = 0; + EFI_STATUS efi_status = EFI_SUCCESS; - if (efi_status != EFI_SUCCESS) - return efi_status; + device = li->DeviceHandle; + devpath = li->FilePath; - if (DevicePathType(li->FilePath) != MEDIA_DEVICE_PATH || - DevicePathSubType(li->FilePath) != MEDIA_FILEPATH_DP) - return EFI_NOT_FOUND; + while (!IsDevicePathEnd(devpath) && + !IsDevicePathEnd(NextDevicePathNode(devpath))) { + FilePath = (FILEPATH_DEVICE_PATH *)devpath; + len = StrLen(FilePath->PathName); - device = li->DeviceHandle; + pathlen += len; - efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, - &simple_file_system_protocol, &drive); + if (len == 1 && FilePath->PathName[0] == '\\') { + devpath = NextDevicePathNode(devpath); + continue; + } - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to find fs\n"); - return efi_status; - } + /* If no leading \, need to add one */ + if (FilePath->PathName[0] != '\\') + pathlen++; - efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); + /* If trailing \, need to strip it */ + if (FilePath->PathName[len-1] == '\\') + pathlen--; - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to open fs\n"); - return efi_status; + devpath = NextDevicePathNode(devpath); } - FilePath = (FILEPATH_DEVICE_PATH *)li->FilePath; - - efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, - FilePath->PathName, EFI_FILE_MODE_READ, - 0); + *PathName = AllocatePool(pathlen + StrLen(SECOND_STAGE)); + *PathName[0] = '\0'; - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to open %s - %lx\n", FilePath->PathName, - efi_status); - return efi_status; + if (!*PathName) { + Print(L"Failed to allocate path buffer\n"); + efi_status = EFI_OUT_OF_RESOURCES; + goto error; } - fileinfo = AllocatePool(buffersize); + devpath = li->FilePath; - if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); - return EFI_OUT_OF_RESOURCES; - } + while (!IsDevicePathEnd(devpath) && + !IsDevicePathEnd(NextDevicePathNode(devpath))) { + CHAR16 *tmpbuffer; + FilePath = (FILEPATH_DEVICE_PATH *)devpath; + len = StrLen(FilePath->PathName); - efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, - &buffersize, fileinfo); + if (len == 1 && FilePath->PathName[0] == '\\') { + devpath = NextDevicePathNode(devpath); + continue; + } - if (efi_status == EFI_BUFFER_TOO_SMALL) { - fileinfo = AllocatePool(buffersize); - if (!fileinfo) { - Print(L"Unable to allocate info buffer\n"); + 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); + + *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); - if (!PathName) - return EFI_LOAD_ERROR; + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to find fs\n"); + goto error; + } - StrCpy(PathName, Dir); + efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); - StrCat(PathName, SECOND_STAGE); + 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 +723,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,36 +734,49 @@ 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) +EFI_STATUS shim_verify (void *buffer, UINT32 size) { EFI_STATUS status; PE_COFF_LOADER_IMAGE_CONTEXT context; @@ -653,39 +791,86 @@ 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; - void *data; + 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->LoadImage, 6, FALSE, image_handle, + grubpath, NULL, 0, &grub_handle); - efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4, - &handle, &shim_lock_guid, - EFI_NATIVE_INTERFACE, - &shim_lock_interface); - 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; + EFI_STATUS efi_status; + + 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); + + efi_status = init_grub(image_handle); + + uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle, + &shim_lock_guid, &shim_lock_interface); + + return efi_status; } |
