summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Langasek <steve.langasek@canonical.com>2013-07-03 22:48:01 +0000
committerSteve Langasek <steve.langasek@canonical.com>2013-07-03 22:48:01 +0000
commit44ecc6a350e738a0a8bcd727b3990ce9dadd527f (patch)
tree4c8e06ffef75e74c569e2dc7c3a31dc96fa626d8
parent0c50644a00803821ff960506b42490e1a2cff3b0 (diff)
downloadefi-boot-shim-44ecc6a350e738a0a8bcd727b3990ce9dadd527f.tar.gz
efi-boot-shim-44ecc6a350e738a0a8bcd727b3990ce9dadd527f.zip
debian/patches/no-output-by-default.patch: Don't print any
informational messages. Closes LP: #1074302.
-rw-r--r--.pc/applied-patches1
-rw-r--r--.pc/no-output-by-default.patch/shim.c1543
-rw-r--r--debian/changelog4
-rw-r--r--debian/patches/no-output-by-default.patch51
-rw-r--r--debian/patches/series1
-rw-r--r--shim.c5
6 files changed, 1599 insertions, 6 deletions
diff --git a/.pc/applied-patches b/.pc/applied-patches
index 78756329..39007dbb 100644
--- a/.pc/applied-patches
+++ b/.pc/applied-patches
@@ -1,3 +1,4 @@
prototypes
second-stage-path
sbsigntool-not-pesign
+no-output-by-default.patch
diff --git a/.pc/no-output-by-default.patch/shim.c b/.pc/no-output-by-default.patch/shim.c
new file mode 100644
index 00000000..ef688bb2
--- /dev/null
+++ b/.pc/no-output-by-default.patch/shim.c
@@ -0,0 +1,1543 @@
+/*
+ * shim - trivial UEFI first-stage bootloader
+ *
+ * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
+ *
+ * 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 <efi.h>
+#include <efilib.h>
+#include <Library/BaseCryptLib.h>
+#include "PeImage.h"
+#include "shim.h"
+#include "signature.h"
+#include "netboot.h"
+#include "shim_cert.h"
+#include "ucs2.h"
+
+#define DEFAULT_LOADER L"\\grubx64.efi"
+#define FALLBACK L"\\fallback.efi"
+#define MOK_MANAGER L"\\MokManager.efi"
+
+static EFI_SYSTEM_TABLE *systab;
+static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
+
+static CHAR16 *second_stage;
+static void *load_options;
+static UINT32 load_options_size;
+
+/*
+ * The vendor certificate used for validating the second stage loader
+ */
+extern UINT8 vendor_cert[];
+extern UINT32 vendor_cert_size;
+extern EFI_SIGNATURE_LIST *vendor_dbx;
+extern UINT32 vendor_dbx_size;
+
+#define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }}
+
+static UINT8 insecure_mode;
+
+typedef enum {
+ DATA_FOUND,
+ DATA_NOT_FOUND,
+ VAR_NOT_FOUND
+} CHECK_STATUS;
+
+typedef struct {
+ UINT32 MokSize;
+ UINT8 *Mok;
+} MokListNode;
+
+static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes,
+ UINTN *size, void **buffer)
+{
+ EFI_STATUS efi_status;
+ char allocate = !(*size);
+
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
+ attributes, size, buffer);
+
+ if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) {
+ return efi_status;
+ }
+
+ *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
+ */
+static void *ImageAddress (void *image, int size, unsigned int address)
+{
+ if (address > size)
+ return NULL;
+
+ return image + address;
+}
+
+/*
+ * Perform the actual relocation
+ */
+static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
+ void *data)
+{
+ EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd;
+ UINT64 Adjust;
+ UINT16 *Reloc, *RelocEnd;
+ char *Fixup, *FixupBase, *FixupData = NULL;
+ UINT16 *Fixup16;
+ UINT32 *Fixup32;
+ UINT64 *Fixup64;
+ int size = context->ImageSize;
+ void *ImageEnd = (char *)data + size;
+
+ 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(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)data - context->ImageAddress;
+
+ while (RelocBase < RelocBaseEnd) {
+ Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
+ RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock);
+
+ if ((void *)RelocEnd < data || (void *)RelocEnd > ImageEnd) {
+ Print(L"Reloc entry overflows binary\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress);
+ if (!FixupBase) {
+ Print(L"Invalid fixupbase\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ while (Reloc < RelocEnd) {
+ Fixup = FixupBase + (*Reloc & 0xFFF);
+ switch ((*Reloc) >> 12) {
+ case EFI_IMAGE_REL_BASED_ABSOLUTE:
+ break;
+
+ case EFI_IMAGE_REL_BASED_HIGH:
+ Fixup16 = (UINT16 *) Fixup;
+ *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16)));
+ if (FixupData != NULL) {
+ *(UINT16 *) FixupData = *Fixup16;
+ FixupData = FixupData + sizeof (UINT16);
+ }
+ break;
+
+ case EFI_IMAGE_REL_BASED_LOW:
+ Fixup16 = (UINT16 *) Fixup;
+ *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust);
+ if (FixupData != NULL) {
+ *(UINT16 *) FixupData = *Fixup16;
+ FixupData = FixupData + sizeof (UINT16);
+ }
+ break;
+
+ case EFI_IMAGE_REL_BASED_HIGHLOW:
+ Fixup32 = (UINT32 *) Fixup;
+ *Fixup32 = *Fixup32 + (UINT32) Adjust;
+ if (FixupData != NULL) {
+ FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32));
+ *(UINT32 *)FixupData = *Fixup32;
+ FixupData = FixupData + sizeof (UINT32);
+ }
+ break;
+
+ case EFI_IMAGE_REL_BASED_DIR64:
+ Fixup64 = (UINT64 *) Fixup;
+ *Fixup64 = *Fixup64 + (UINT64) Adjust;
+ if (FixupData != NULL) {
+ FixupData = ALIGN_POINTER (FixupData, sizeof(UINT64));
+ *(UINT64 *)(FixupData) = *Fixup64;
+ FixupData = FixupData + sizeof(UINT64);
+ }
+ break;
+
+ default:
+ Print(L"Unknown relocation\n");
+ return EFI_UNSUPPORTED;
+ }
+ Reloc += 1;
+ }
+ RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
+ UINTN dbsize,
+ WIN_CERTIFICATE_EFI_PKCS *data,
+ UINT8 *hash)
+{
+ EFI_SIGNATURE_DATA *Cert;
+ UINTN CertCount, Index;
+ BOOLEAN IsFound = FALSE;
+ EFI_GUID CertType = EfiCertX509Guid;
+
+ while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
+ 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++) {
+ 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);
+ }
+
+ }
+
+ if (IsFound)
+ break;
+
+ dbsize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
+
+ if (IsFound)
+ return DATA_FOUND;
+
+ return DATA_NOT_FOUND;
+}
+
+static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
+ WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash)
+{
+ CHECK_STATUS rc;
+ EFI_STATUS efi_status;
+ EFI_SIGNATURE_LIST *CertList;
+ UINTN dbsize = 0;
+ UINT32 attributes;
+ void *db;
+
+ efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db);
+
+ if (efi_status != EFI_SUCCESS)
+ return VAR_NOT_FOUND;
+
+ CertList = db;
+
+ rc = check_db_cert_in_ram(CertList, dbsize, data, hash);
+
+ FreePool(db);
+
+ return rc;
+}
+
+/*
+ * Check a hash against an EFI_SIGNATURE_LIST in a buffer
+ */
+static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList,
+ UINTN dbsize, UINT8 *data,
+ int SignatureSize, EFI_GUID CertType)
+{
+ EFI_SIGNATURE_DATA *Cert;
+ UINTN CertCount, Index;
+ BOOLEAN IsFound = FALSE;
+
+ 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 (CompareGuid(&CertList->SignatureType, &CertType) == 0) {
+ for (Index = 0; Index < CertCount; Index++) {
+ if (CompareMem (Cert->SignatureData, data, SignatureSize) == 0) {
+ //
+ // Find the signature in database.
+ //
+ IsFound = TRUE;
+ break;
+ }
+
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
+ }
+ if (IsFound) {
+ break;
+ }
+ }
+
+ dbsize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
+
+ if (IsFound)
+ return DATA_FOUND;
+
+ return DATA_NOT_FOUND;
+}
+
+/*
+ * Check a hash against an EFI_SIGNATURE_LIST in a UEFI variable
+ */
+static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
+ int SignatureSize, EFI_GUID CertType)
+{
+ EFI_STATUS efi_status;
+ EFI_SIGNATURE_LIST *CertList;
+ UINT32 attributes;
+ UINTN dbsize = 0;
+ void *db;
+
+ efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db);
+
+ if (efi_status != EFI_SUCCESS) {
+ return VAR_NOT_FOUND;
+ }
+
+ CertList = db;
+
+ CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data,
+ SignatureSize, CertType);
+ FreePool(db);
+ return rc;
+
+}
+
+/*
+ * Check whether the binary signature or hash are present in dbx or the
+ * built-in blacklist
+ */
+static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
+ UINT8 *sha256hash, UINT8 *sha1hash)
+{
+ EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
+
+ if (check_db_hash_in_ram(vendor_dbx, vendor_dbx_size, sha256hash,
+ SHA256_DIGEST_SIZE, EfiHashSha256Guid) ==
+ DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+ if (check_db_hash_in_ram(vendor_dbx, vendor_dbx_size, sha1hash,
+ SHA1_DIGEST_SIZE, EfiHashSha1Guid) ==
+ DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+ if (check_db_cert_in_ram(vendor_dbx, vendor_dbx_size, cert,
+ sha256hash) == DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+
+ if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE,
+ EfiHashSha256Guid) == DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+ if (check_db_hash(L"dbx", secure_var, sha1hash, SHA1_DIGEST_SIZE,
+ EfiHashSha1Guid) == DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+ if (check_db_cert(L"dbx", secure_var, cert, sha256hash) == DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Check whether the binary signature or hash are present in db or MokList
+ */
+static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
+ UINT8 *sha256hash, UINT8 *sha1hash)
+{
+ EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
+ EFI_GUID shim_var = SHIM_LOCK_GUID;
+
+ if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE,
+ EfiHashSha256Guid) == DATA_FOUND)
+ return EFI_SUCCESS;
+ if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE,
+ EfiHashSha1Guid) == DATA_FOUND)
+ return EFI_SUCCESS;
+ if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE,
+ EfiHashSha256Guid) == DATA_FOUND)
+ return EFI_SUCCESS;
+ if (check_db_cert(L"db", secure_var, cert, sha256hash) == DATA_FOUND)
+ return EFI_SUCCESS;
+ if (check_db_cert(L"MokList", shim_var, cert, sha256hash) == DATA_FOUND)
+ return EFI_SUCCESS;
+
+ return EFI_ACCESS_DENIED;
+}
+
+/*
+ * 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;
+ UINT32 attributes;
+
+ if (insecure_mode)
+ return FALSE;
+
+ status = get_variable(L"SecureBoot", global_var, &attributes, &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, &attributes, &charsize,
+ (void *)&setupmode);
+
+ if (status == EFI_SUCCESS && setupmode == 1) {
+ Print(L"Platform is in setup mode\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Calculate the SHA1 and SHA256 hashes of a binary
+ */
+
+static EFI_STATUS generate_hash (char *data, int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context,
+ UINT8 *sha256hash, UINT8 *sha1hash)
+
+{
+ unsigned int sha256ctxsize, sha1ctxsize;
+ unsigned int size = datasize;
+ void *sha256ctx = NULL, *sha1ctx = NULL;
+ char *hashbase;
+ unsigned int hashsize;
+ unsigned int SumOfBytesHashed, SumOfSectionBytes;
+ unsigned int index, pos;
+ EFI_IMAGE_SECTION_HEADER *Section;
+ EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL;
+ EFI_IMAGE_SECTION_HEADER *SectionCache;
+ EFI_STATUS status = EFI_SUCCESS;
+
+ sha256ctxsize = Sha256GetContextSize();
+ sha256ctx = AllocatePool(sha256ctxsize);
+
+ sha1ctxsize = Sha1GetContextSize();
+ sha1ctx = AllocatePool(sha1ctxsize);
+
+ if (!sha256ctx || !sha1ctx) {
+ Print(L"Unable to allocate memory for hash context\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) {
+ Print(L"Unable to initialise hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+
+ /* Hash start to checksum */
+ hashbase = data;
+ hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum -
+ hashbase;
+
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, 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(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, 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]) - data);
+
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
+ Print(L"Unable to generate hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+
+ /* Sort sections */
+ 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 >= datasize) {
+ 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(data, size, Section->PointerToRawData);
+ hashsize = (unsigned int) Section->SizeOfRawData;
+
+ if (!hashbase) {
+ Print(L"Malformed section header\n");
+ status = EFI_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, 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 = data + SumOfBytesHashed;
+ hashsize = (unsigned int)(
+ size -
+ context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -
+ SumOfBytesHashed);
+
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
+ Print(L"Unable to generate hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+ }
+
+ if (!(Sha256Final(sha256ctx, sha256hash)) ||
+ !(Sha1Final(sha1ctx, sha1hash))) {
+ Print(L"Unable to finalise hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+
+done:
+ if (SectionHeader)
+ FreePool(SectionHeader);
+ if (sha1ctx)
+ FreePool(sha1ctx);
+ if (sha256ctx)
+ FreePool(sha256ctx);
+
+ return status;
+}
+
+/*
+ * Ensure that the MOK database hasn't been set or modified from an OS
+ */
+static EFI_STATUS verify_mok (void) {
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS status = EFI_SUCCESS;
+ void *MokListData = NULL;
+ UINTN MokListDataSize = 0;
+ UINT32 attributes;
+
+ status = get_variable(L"MokList", shim_lock_guid, &attributes,
+ &MokListDataSize, &MokListData);
+
+ if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
+ Print(L"MokList is compromised!\nErase all keys in MokList!\n");
+ if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) {
+ Print(L"Failed to erase MokList\n");
+ }
+ status = EFI_ACCESS_DENIED;
+ return status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * 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)
+{
+ UINT8 sha256hash[SHA256_DIGEST_SIZE];
+ UINT8 sha1hash[SHA1_DIGEST_SIZE];
+ EFI_STATUS status = EFI_ACCESS_DENIED;
+ WIN_CERTIFICATE_EFI_PKCS *cert;
+ unsigned int size = datasize;
+
+ if (context->SecDir->Size == 0) {
+ Print(L"Empty security header\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ cert = ImageAddress (data, size, context->SecDir->VirtualAddress);
+
+ 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;
+ }
+
+ status = generate_hash(data, datasize, context, sha256hash, sha1hash);
+
+ if (status != EFI_SUCCESS)
+ return status;
+
+ /*
+ * Check that the MOK database hasn't been modified
+ */
+ verify_mok();
+
+ /*
+ * Ensure that the binary isn't blacklisted
+ */
+ status = check_blacklist(cert, sha256hash, sha1hash);
+
+ if (status != EFI_SUCCESS) {
+ Print(L"Binary is blacklisted\n");
+ return status;
+ }
+
+ /*
+ * Check whether the binary is whitelisted in any of the firmware
+ * databases
+ */
+ status = check_whitelist(cert, sha256hash, sha1hash);
+
+ if (status == EFI_SUCCESS) {
+ Print(L"Binary is whitelisted\n");
+ return status;
+ }
+
+ /*
+ * Check against the shim build key
+ */
+ if (AuthenticodeVerify(cert->CertData,
+ context->SecDir->Size - sizeof(cert->Hdr),
+ shim_cert, sizeof(shim_cert), sha256hash,
+ SHA256_DIGEST_SIZE)) {
+ status = EFI_SUCCESS;
+ Print(L"Binary is verified by the vendor certificate\n");
+ return status;
+ }
+
+
+ /*
+ * And finally, check against shim's built-in key
+ */
+ if (AuthenticodeVerify(cert->CertData,
+ context->SecDir->Size - sizeof(cert->Hdr),
+ vendor_cert, vendor_cert_size, sha256hash,
+ SHA256_DIGEST_SIZE)) {
+ status = EFI_SUCCESS;
+ Print(L"Binary is verified by the vendor certificate\n");
+ return status;
+ }
+
+ Print(L"Invalid signature\n");
+ status = EFI_ACCESS_DENIED;
+
+ return status;
+}
+
+/*
+ * Read the binary header and grab appropriate information from it
+ */
+static EFI_STATUS read_header(void *data, unsigned int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context)
+{
+ EFI_IMAGE_DOS_HEADER *DosHdr = data;
+ EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
+
+ if (datasize < sizeof(EFI_IMAGE_DOS_HEADER)) {
+ Print(L"Invalid image\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE)
+ PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew);
+
+ if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) {
+ Print(L"Invalid image\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ 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->ImageSize < context->SizeOfHeaders) {
+ Print(L"Invalid image\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ if (((UINT8 *)context->SecDir - (UINT8 *)data) > (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
+ Print(L"Invalid image\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ if (context->SecDir->VirtualAddress >= datasize) {
+ Print(L"Malformed 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_image (void *data, unsigned int datasize,
+ EFI_LOADED_IMAGE *li)
+{
+ EFI_STATUS efi_status;
+ char *buffer;
+ int i, size;
+ EFI_IMAGE_SECTION_HEADER *Section;
+ char *base, *end;
+ PE_COFF_LOADER_IMAGE_CONTEXT context;
+
+ /*
+ * The binary header contains relevant context and section pointers
+ */
+ efi_status = read_header(data, datasize, &context);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to read header\n");
+ return efi_status;
+ }
+
+ /*
+ * We only need to verify the binary if we're in secure mode
+ */
+ if (secure_mode ()) {
+ efi_status = verify_buffer(data, datasize, &context);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Verification failed\n");
+ return efi_status;
+ }
+ }
+
+ buffer = AllocatePool(context.ImageSize);
+
+ if (!buffer) {
+ Print(L"Failed to allocate image buffer\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem(buffer, data, context.SizeOfHeaders);
+
+ /*
+ * Copy the executable's sections to their desired offsets
+ */
+ 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 (Section->SizeOfRawData > 0)
+ CopyMem(base, data + Section->PointerToRawData, size);
+
+ if (size < Section->Misc.VirtualSize)
+ ZeroMem (base + size, Section->Misc.VirtualSize - size);
+
+ Section += 1;
+ }
+
+ /*
+ * Run the relocation fixups
+ */
+ efi_status = relocate_coff(&context, buffer);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Relocation failed\n");
+ FreePool(buffer);
+ return efi_status;
+ }
+
+ entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
+ /*
+ * grub needs to know its location and size in memory, so fix up
+ * the loaded image protocol values
+ */
+ li->ImageBase = buffer;
+ li->ImageSize = context.ImageSize;
+
+ /* Pass the load options to the second stage loader */
+ li->LoadOptions = load_options;
+ li->LoadOptionsSize = load_options_size;
+
+ if (!entry_point) {
+ Print(L"Invalid entry point\n");
+ FreePool(buffer);
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static int
+should_use_fallback(EFI_HANDLE image_handle)
+{
+ EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
+ EFI_LOADED_IMAGE *li;
+ unsigned int pathlen = 0;
+ CHAR16 *bootpath;
+ EFI_FILE_IO_INTERFACE *fio = NULL;
+ EFI_FILE *vh;
+ EFI_FILE *fh;
+ EFI_STATUS rc;
+
+ rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
+ &loaded_image_protocol, (void **)&li);
+ if (EFI_ERROR(rc)) {
+ Print(L"Could not get image for bootx64.efi: %d\n", rc);
+ return 0;
+ }
+
+ bootpath = DevicePathToStr(li->FilePath);
+
+ /* Check the beginning of the string and the end, to avoid
+ * caring about which arch this is. */
+ /* I really don't know why, but sometimes bootpath gives us
+ * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here...
+ */
+ if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) &&
+ StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15))
+ return 0;
+
+ pathlen = StrLen(bootpath);
+ if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI"))
+ return 0;
+
+ rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle,
+ &FileSystemProtocol, (void **)&fio);
+ if (EFI_ERROR(rc)) {
+ Print(L"Could not get fio for li->DeviceHandle: %d\n", rc);
+ return 0;
+ }
+
+ rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &vh);
+ if (EFI_ERROR(rc)) {
+ Print(L"Could not open fio volume: %d\n", rc);
+ return 0;
+ }
+
+ rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK,
+ EFI_FILE_MODE_READ, 0);
+ if (EFI_ERROR(rc)) {
+ Print(L"Could not open \"\\EFI\\BOOT%s\": %d\n", FALLBACK, rc);
+ uefi_call_wrapper(vh->Close, 1, vh);
+ return 0;
+ }
+ uefi_call_wrapper(fh->Close, 1, fh);
+ uefi_call_wrapper(vh->Close, 1, vh);
+
+ return 1;
+}
+
+/*
+ * Generate the path of an executable given shim's path and the name
+ * of the executable
+ */
+static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
+ EFI_DEVICE_PATH **grubpath, CHAR16 **PathName)
+{
+ EFI_DEVICE_PATH *devpath;
+ EFI_HANDLE device;
+ int i;
+ unsigned int pathlen = 0;
+ EFI_STATUS efi_status = EFI_SUCCESS;
+ CHAR16 *bootpath;
+
+ device = li->DeviceHandle;
+ devpath = li->FilePath;
+
+ bootpath = DevicePathToStr(devpath);
+
+ pathlen = StrLen(bootpath);
+
+ for (i=pathlen; i>0; i--) {
+ if (bootpath[i] == '\\')
+ break;
+ }
+
+ bootpath[i+1] = '\0';
+
+ if (i == 0 || bootpath[i-i] == '\\')
+ bootpath[i] = '\0';
+
+ *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
+
+ if (!*PathName) {
+ Print(L"Failed to allocate path buffer\n");
+ efi_status = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+
+ *PathName[0] = '\0';
+ if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath)))
+ StrCat(*PathName, bootpath);
+ StrCat(*PathName, ImagePath);
+
+ *grubpath = FileDevicePath(device, *PathName);
+
+error:
+ return efi_status;
+}
+
+/*
+ * Open the second stage bootloader and read it into a buffer
+ */
+static EFI_STATUS load_image (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;
+ UINTN buffersize = sizeof(EFI_FILE_INFO);
+
+ device = li->DeviceHandle;
+
+ /*
+ * Open the device
+ */
+ efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
+ &simple_file_system_protocol,
+ (void **)&drive);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to find fs\n");
+ goto error;
+ }
+
+ efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to open fs\n");
+ goto error;
+ }
+
+ /*
+ * And then open the file
+ */
+ 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);
+ goto error;
+ }
+
+ fileinfo = AllocatePool(buffersize);
+
+ if (!fileinfo) {
+ Print(L"Unable to allocate file info buffer\n");
+ efi_status = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+
+ /*
+ * Find out how big the file is in order to allocate the storage
+ * buffer
+ */
+ efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id,
+ &buffersize, fileinfo);
+
+ if (efi_status == EFI_BUFFER_TOO_SMALL) {
+ FreePool(fileinfo);
+ 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, &buffersize,
+ fileinfo);
+ }
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to get file info\n");
+ goto error;
+ }
+
+ buffersize = fileinfo->FileSize;
+
+ *data = AllocatePool(buffersize);
+
+ if (!*data) {
+ Print(L"Unable to allocate file buffer\n");
+ efi_status = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+
+ /*
+ * Perform the actual read
+ */
+ efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize,
+ *data);
+
+ 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"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize);
+ goto error;
+ }
+
+ *datasize = buffersize;
+
+ FreePool(fileinfo);
+
+ return EFI_SUCCESS;
+error:
+ if (*data) {
+ FreePool(*data);
+ *data = NULL;
+ }
+
+ if (fileinfo)
+ FreePool(fileinfo);
+ return efi_status;
+}
+
+/*
+ * Protocol entry point. If secure boot is enabled, verify that the provided
+ * buffer is signed with a trusted key.
+ */
+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, size, &context);
+
+ if (status != EFI_SUCCESS)
+ return status;
+
+ status = verify_buffer(buffer, size, &context);
+
+ return status;
+}
+
+/*
+ * Load and run an EFI executable
+ */
+EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
+{
+ EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
+ EFI_STATUS efi_status;
+ EFI_LOADED_IMAGE *li, li_bak;
+ EFI_DEVICE_PATH *path;
+ CHAR16 *PathName = NULL;
+ void *sourcebuffer = NULL;
+ UINTN sourcesize = 0;
+ void *data = NULL;
+ int datasize;
+
+ /*
+ * We need to refer to the loaded image protocol on the running
+ * binary in order to find our path
+ */
+ efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
+ &loaded_image_protocol, (void **)&li);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to init protocol\n");
+ return efi_status;
+ }
+
+ /*
+ * Build a new path from the existing one plus the executable name
+ */
+ efi_status = generate_path(li, ImagePath, &path, &PathName);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to generate path: %s\n", ImagePath);
+ goto done;
+ }
+
+ if (findNetboot(image_handle)) {
+ efi_status = parseNetbootinfo(image_handle);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Netboot parsing failed: %d\n", efi_status);
+ return EFI_PROTOCOL_ERROR;
+ }
+ efi_status = FetchNetbootimage(image_handle, &sourcebuffer,
+ &sourcesize);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to fetch TFTP image\n");
+ return efi_status;
+ }
+ data = sourcebuffer;
+ datasize = sourcesize;
+ } else {
+ /*
+ * Read the new executable off disk
+ */
+ efi_status = load_image(li, &data, &datasize, PathName);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to load image\n");
+ goto done;
+ }
+ }
+
+ /*
+ * We need to modify the loaded image protocol entry before running
+ * the new binary, so back it up
+ */
+ CopyMem(&li_bak, li, sizeof(li_bak));
+
+ /*
+ * Verify and, if appropriate, relocate and execute the executable
+ */
+ efi_status = handle_image(data, datasize, li);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to load image\n");
+ CopyMem(li, &li_bak, sizeof(li_bak));
+ goto done;
+ }
+
+ /*
+ * The binary is trusted and relocated. Run it
+ */
+ efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab);
+
+ /*
+ * Restore our original loaded image values
+ */
+ CopyMem(li, &li_bak, sizeof(li_bak));
+done:
+ if (PathName)
+ FreePool(PathName);
+
+ if (data)
+ FreePool(data);
+
+ return efi_status;
+}
+
+/*
+ * Load and run grub. If that fails because grub isn't trusted, load and
+ * run MokManager.
+ */
+EFI_STATUS init_grub(EFI_HANDLE image_handle)
+{
+ EFI_STATUS efi_status;
+
+ if (should_use_fallback(image_handle))
+ efi_status = start_image(image_handle, FALLBACK);
+ else
+ efi_status = start_image(image_handle, second_stage);
+
+ if (efi_status != EFI_SUCCESS)
+ efi_status = start_image(image_handle, MOK_MANAGER);
+
+ return efi_status;
+}
+
+/*
+ * Copy the boot-services only MokList variable to the runtime-accessible
+ * MokListRT variable. It's not marked NV, so the OS can't modify it.
+ */
+EFI_STATUS mirror_mok_list()
+{
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS efi_status;
+ UINT32 attributes;
+ void *Data = NULL;
+ UINTN DataSize = 0;
+
+ efi_status = get_variable(L"MokList", shim_lock_guid, &attributes,
+ &DataSize, &Data);
+
+ if (efi_status != EFI_SUCCESS) {
+ goto done;
+ }
+
+ efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListRT",
+ &shim_lock_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS,
+ DataSize, Data);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to set MokListRT %d\n", efi_status);
+ }
+
+done:
+ return efi_status;
+}
+
+/*
+ * Check if a variable exists
+ */
+static BOOLEAN check_var(CHAR16 *varname)
+{
+ EFI_STATUS efi_status;
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ UINTN size = sizeof(UINT32);
+ UINT32 MokVar;
+ UINT32 attributes;
+
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5, varname,
+ &shim_lock_guid, &attributes,
+ &size, (void *)&MokVar);
+
+ if (efi_status == EFI_SUCCESS || efi_status == EFI_BUFFER_TOO_SMALL)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * If the OS has set any of these variables we need to drop into MOK and
+ * handle them appropriately
+ */
+EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
+{
+ EFI_STATUS efi_status;
+
+ if (check_var(L"MokNew") || check_var(L"MokSB") ||
+ check_var(L"MokPW") || check_var(L"MokAuth") ||
+ check_var(L"MokDel")) {
+ efi_status = start_image(image_handle, MOK_MANAGER);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to start MokManager\n");
+ return efi_status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Verify that MokSBState is valid, and if appropriate set insecure mode
+ */
+
+static EFI_STATUS check_mok_sb (void)
+{
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS status = EFI_SUCCESS;
+ void *MokSBState = NULL;
+ UINTN MokSBStateSize = 0;
+ UINT32 attributes;
+
+ status = get_variable(L"MokSBState", shim_lock_guid, &attributes,
+ &MokSBStateSize, &MokSBState);
+
+ if (status != EFI_SUCCESS)
+ return EFI_ACCESS_DENIED;
+
+ /*
+ * Delete and ignore the variable if it's been set from or could be
+ * modified by the OS
+ */
+ if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
+ Print(L"MokSBState is compromised! Clearing it\n");
+ if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) {
+ Print(L"Failed to erase MokSBState\n");
+ }
+ status = EFI_ACCESS_DENIED;
+ } else {
+ if (*(UINT8 *)MokSBState == 1) {
+ insecure_mode = 1;
+ }
+ }
+
+ return status;
+}
+
+/*
+ * Check the load options to specify the second stage loader
+ */
+EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
+{
+ EFI_STATUS status;
+ EFI_LOADED_IMAGE *li;
+ CHAR16 *start = NULL, *c;
+ int i, remaining_size = 0;
+ CHAR16 *loader_str = NULL;
+ int loader_len = 0;
+
+ second_stage = DEFAULT_LOADER;
+ load_options = NULL;
+ load_options_size = 0;
+
+ status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
+ &LoadedImageProtocol, (void **) &li);
+ if (status != EFI_SUCCESS) {
+ Print (L"Failed to get load options\n");
+ return status;
+ }
+
+ /* Expect a CHAR16 string with at least one CHAR16 */
+ if (li->LoadOptionsSize < 4 || li->LoadOptionsSize % 2 != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+ c = (CHAR16 *)(li->LoadOptions + (li->LoadOptionsSize - 2));
+ if (*c != L'\0') {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ /*
+ * UEFI shell copies the whole line of the command into LoadOptions.
+ * We ignore the string before the first L' ', i.e. the name of this
+ * program.
+ */
+ for (i = 0; i < li->LoadOptionsSize; i += 2) {
+ c = (CHAR16 *)(li->LoadOptions + i);
+ if (*c == L' ') {
+ *c = L'\0';
+ start = c + 1;
+ remaining_size = li->LoadOptionsSize - i - 2;
+ break;
+ }
+ }
+
+ if (!start || remaining_size <= 0)
+ return EFI_SUCCESS;
+
+ for (i = 0; start[i] != '\0'; i++) {
+ if (start[i] == L' ' || start[i] == L'\0')
+ break;
+ loader_len++;
+ }
+
+ /*
+ * Setup the name of the alternative loader and the LoadOptions for
+ * the loader
+ */
+ if (loader_len > 0) {
+ loader_str = AllocatePool((loader_len + 1) * sizeof(CHAR16));
+ if (!loader_str) {
+ Print(L"Failed to allocate loader string\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+ for (i = 0; i < loader_len; i++)
+ loader_str[i] = start[i];
+ loader_str[loader_len] = L'\0';
+
+ second_stage = loader_str;
+ load_options = start;
+ load_options_size = remaining_size;
+ }
+
+ return EFI_SUCCESS;
+}
+
+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;
+
+ /*
+ * Set up the shim lock protocol so that grub and MokManager can
+ * call back in and use shim functions
+ */
+ shim_lock_interface.Verify = shim_verify;
+ shim_lock_interface.Hash = generate_hash;
+ shim_lock_interface.Context = read_header;
+
+ systab = passed_systab;
+
+ /*
+ * Ensure that gnu-efi functions are available
+ */
+ InitializeLib(image_handle, systab);
+
+ /* Set the second stage loader */
+ set_second_stage (image_handle);
+
+ /*
+ * Check whether the user has configured the system to run in
+ * insecure mode
+ */
+ check_mok_sb();
+
+ /*
+ * Tell the user that we're in insecure mode if necessary
+ */
+ if (insecure_mode) {
+ Print(L"Booting in insecure mode\n");
+ uefi_call_wrapper(BS->Stall, 1, 2000000);
+ }
+
+ /*
+ * Install the protocol
+ */
+ uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle,
+ &shim_lock_guid, EFI_NATIVE_INTERFACE,
+ &shim_lock_interface);
+
+ /*
+ * Enter MokManager if necessary
+ */
+ efi_status = check_mok_request(image_handle);
+
+ /*
+ * Copy the MOK list to a runtime variable so the kernel can make
+ * use of it
+ */
+ efi_status = mirror_mok_list();
+
+ /*
+ * Hand over control to the second stage bootloader
+ */
+
+ efi_status = init_grub(image_handle);
+
+ /*
+ * If we're back here then clean everything up before exiting
+ */
+ uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle,
+ &shim_lock_guid, &shim_lock_interface);
+
+ /*
+ * Free the space allocated for the alternative 2nd stage loader
+ */
+ if (load_options_size > 0)
+ FreePool(second_stage);
+
+ return efi_status;
+}
diff --git a/debian/changelog b/debian/changelog
index 50df2f28..1969c7e8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,10 @@
shim (0.4-0ubuntu3) UNRELEASED; urgency=low
* Install MokManager.efi.signed in the package.
+ * debian/patches/no-output-by-default.patch: Don't print any
+ informational messages. Closes LP: #1074302.
- -- Steve Langasek <steve.langasek@ubuntu.com> Wed, 03 Jul 2013 12:01:56 -0700
+ -- Steve Langasek <steve.langasek@ubuntu.com> Wed, 03 Jul 2013 12:02:12 -0700
shim (0.4-0ubuntu2) saucy; urgency=low
diff --git a/debian/patches/no-output-by-default.patch b/debian/patches/no-output-by-default.patch
new file mode 100644
index 00000000..52cda502
--- /dev/null
+++ b/debian/patches/no-output-by-default.patch
@@ -0,0 +1,51 @@
+Description: Don't print any informational messages
+ Unless the operation has failed, we shouldn't print anything at all while
+ booting. Suppress informational messages about the current SecureBoot
+ policy or how the image's signature was verified.
+Author: Steve Langasek <steve.langasek@canonical.com>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1074302
+
+Index: shim/shim.c
+===================================================================
+--- shim.orig/shim.c
++++ shim/shim.c
+@@ -430,7 +430,6 @@
+
+ /* FIXME - more paranoia here? */
+ if (status != EFI_SUCCESS || sb != 1) {
+- Print(L"Secure boot not enabled\n");
+ return FALSE;
+ }
+
+@@ -438,7 +437,6 @@
+ (void *)&setupmode);
+
+ if (status == EFI_SUCCESS && setupmode == 1) {
+- Print(L"Platform is in setup mode\n");
+ return FALSE;
+ }
+
+@@ -698,7 +696,6 @@
+ status = check_whitelist(cert, sha256hash, sha1hash);
+
+ if (status == EFI_SUCCESS) {
+- Print(L"Binary is whitelisted\n");
+ return status;
+ }
+
+@@ -710,7 +707,6 @@
+ shim_cert, sizeof(shim_cert), sha256hash,
+ SHA256_DIGEST_SIZE)) {
+ status = EFI_SUCCESS;
+- Print(L"Binary is verified by the vendor certificate\n");
+ return status;
+ }
+
+@@ -723,7 +719,6 @@
+ vendor_cert, vendor_cert_size, sha256hash,
+ SHA256_DIGEST_SIZE)) {
+ status = EFI_SUCCESS;
+- Print(L"Binary is verified by the vendor certificate\n");
+ return status;
+ }
+
diff --git a/debian/patches/series b/debian/patches/series
index 78756329..39007dbb 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,3 +1,4 @@
prototypes
second-stage-path
sbsigntool-not-pesign
+no-output-by-default.patch
diff --git a/shim.c b/shim.c
index ef688bb2..8ce2b318 100644
--- a/shim.c
+++ b/shim.c
@@ -430,7 +430,6 @@ static BOOLEAN secure_mode (void)
/* FIXME - more paranoia here? */
if (status != EFI_SUCCESS || sb != 1) {
- Print(L"Secure boot not enabled\n");
return FALSE;
}
@@ -438,7 +437,6 @@ static BOOLEAN secure_mode (void)
(void *)&setupmode);
if (status == EFI_SUCCESS && setupmode == 1) {
- Print(L"Platform is in setup mode\n");
return FALSE;
}
@@ -698,7 +696,6 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
status = check_whitelist(cert, sha256hash, sha1hash);
if (status == EFI_SUCCESS) {
- Print(L"Binary is whitelisted\n");
return status;
}
@@ -710,7 +707,6 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
shim_cert, sizeof(shim_cert), sha256hash,
SHA256_DIGEST_SIZE)) {
status = EFI_SUCCESS;
- Print(L"Binary is verified by the vendor certificate\n");
return status;
}
@@ -723,7 +719,6 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
vendor_cert, vendor_cert_size, sha256hash,
SHA256_DIGEST_SIZE)) {
status = EFI_SUCCESS;
- Print(L"Binary is verified by the vendor certificate\n");
return status;
}