summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--shim.c419
2 files changed, 302 insertions, 119 deletions
diff --git a/TODO b/TODO
index 0332f855..3f31658e 100644
--- a/TODO
+++ b/TODO
@@ -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
diff --git a/shim.c b/shim.c
index 2f29166a..f524aa65 100644
--- a/shim.c
+++ b/shim.c
@@ -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;
}