diff options
| author | Steve Langasek <steve.langasek@canonical.com> | 2013-07-03 22:48:01 +0000 |
|---|---|---|
| committer | Steve Langasek <steve.langasek@canonical.com> | 2013-07-03 22:48:01 +0000 |
| commit | 44ecc6a350e738a0a8bcd727b3990ce9dadd527f (patch) | |
| tree | 4c8e06ffef75e74c569e2dc7c3a31dc96fa626d8 | |
| parent | 0c50644a00803821ff960506b42490e1a2cff3b0 (diff) | |
| download | efi-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-patches | 1 | ||||
| -rw-r--r-- | .pc/no-output-by-default.patch/shim.c | 1543 | ||||
| -rw-r--r-- | debian/changelog | 4 | ||||
| -rw-r--r-- | debian/patches/no-output-by-default.patch | 51 | ||||
| -rw-r--r-- | debian/patches/series | 1 | ||||
| -rw-r--r-- | shim.c | 5 |
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 @@ -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; } |
