summaryrefslogtreecommitdiff
path: root/shim.c
diff options
context:
space:
mode:
authorSteve McIntyre <steve@einval.com>2021-03-23 23:49:46 +0000
committerSteve McIntyre <steve@einval.com>2021-03-23 23:49:46 +0000
commit031e5cce385d3f96b1caa1d53495332a7eb03749 (patch)
treeb4988dfbd191b2242b9294e24075b39a608b1155 /shim.c
parent7bf7a6d0852382bb645119b18df3ff461aaba247 (diff)
downloadefi-boot-shim-upstream/15.3.tar.gz
efi-boot-shim-upstream/15.3.zip
New upstream version 15.3upstream/15.3
Diffstat (limited to 'shim.c')
-rw-r--r--shim.c1661
1 files changed, 519 insertions, 1142 deletions
diff --git a/shim.c b/shim.c
index f29f3921..117c8f42 100644
--- a/shim.c
+++ b/shim.c
@@ -1,32 +1,10 @@
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+
/*
* 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.
+ * Copyright Red Hat, Inc
+ * Author: Matthew Garrett
*
* Significant portions of this code are derived from Tianocore
* (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
@@ -34,8 +12,9 @@
*/
#include "shim.h"
-
-#include <stdarg.h>
+#if defined(ENABLE_SHIM_CERT)
+#include "shim_cert.h"
+#endif /* defined(ENABLE_SHIM_CERT) */
#include <openssl/err.h>
#include <openssl/bn.h>
@@ -48,7 +27,7 @@
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
-#include <internal/dso.h>
+#include <openssl/dso.h>
#include <Library/BaseCryptLib.h>
@@ -58,25 +37,35 @@
static EFI_SYSTEM_TABLE *systab;
static EFI_HANDLE global_image_handle;
+static EFI_LOADED_IMAGE *shim_li;
+static EFI_LOADED_IMAGE shim_li_bak;
static CHAR16 *second_stage;
-static void *load_options;
-static UINT32 load_options_size;
+void *load_options;
+UINT32 load_options_size;
+
+list_t sbat_var;
/*
* The vendor certificate used for validating the second stage loader
*/
extern struct {
- UINT32 vendor_cert_size;
- UINT32 vendor_dbx_size;
- UINT32 vendor_cert_offset;
- UINT32 vendor_dbx_offset;
+ UINT32 vendor_authorized_size;
+ UINT32 vendor_deauthorized_size;
+ UINT32 vendor_authorized_offset;
+ UINT32 vendor_deauthorized_offset;
} cert_table;
-UINT32 vendor_cert_size;
-UINT32 vendor_dbx_size;
-UINT8 *vendor_cert;
-UINT8 *vendor_dbx;
+UINT32 vendor_authorized_size = 0;
+UINT8 *vendor_authorized = NULL;
+
+UINT32 vendor_deauthorized_size = 0;
+UINT8 *vendor_deauthorized = NULL;
+
+#if defined(ENABLE_SHIM_CERT)
+UINT32 build_cert_size;
+UINT8 *build_cert;
+#endif /* defined(ENABLE_SHIM_CERT) */
/*
* indicator of how an image has been verified
@@ -100,250 +89,6 @@ typedef struct {
UINT8 *Mok;
} MokListNode;
-/*
- * Perform basic bounds checking of the intra-image pointers
- */
-static void *ImageAddress (void *image, uint64_t size, uint64_t address)
-{
- /* ensure our local pointer isn't bigger than our size */
- if (address > size)
- return NULL;
-
- /* Insure our math won't overflow */
- if (UINT64_MAX - address < (uint64_t)(intptr_t)image)
- return NULL;
-
- /* return the absolute pointer */
- return image + address;
-}
-
-/* here's a chart:
- * i686 x86_64 aarch64
- * 64-on-64: nyet yes yes
- * 64-on-32: nyet yes nyet
- * 32-on-32: yes yes no
- */
-static int
-allow_64_bit(void)
-{
-#if defined(__x86_64__) || defined(__aarch64__)
- return 1;
-#elif defined(__i386__) || defined(__i686__)
- /* Right now blindly assuming the kernel will correctly detect this
- * and /halt the system/ if you're not really on a 64-bit cpu */
- if (in_protocol)
- return 1;
- return 0;
-#else /* assuming everything else is 32-bit... */
- return 0;
-#endif
-}
-
-static int
-allow_32_bit(void)
-{
-#if defined(__x86_64__)
-#if defined(ALLOW_32BIT_KERNEL_ON_X64)
- if (in_protocol)
- return 1;
- return 0;
-#else
- return 0;
-#endif
-#elif defined(__i386__) || defined(__i686__)
- return 1;
-#elif defined(__arch64__)
- return 0;
-#else /* assuming everything else is 32-bit... */
- return 1;
-#endif
-}
-
-static int
-image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
-{
- /* .Magic is the same offset in all cases */
- if (PEHdr->Pe32Plus.OptionalHeader.Magic
- == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC)
- return 1;
- return 0;
-}
-
-static const UINT16 machine_type =
-#if defined(__x86_64__)
- IMAGE_FILE_MACHINE_X64;
-#elif defined(__aarch64__)
- IMAGE_FILE_MACHINE_ARM64;
-#elif defined(__arm__)
- IMAGE_FILE_MACHINE_ARMTHUMB_MIXED;
-#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
- IMAGE_FILE_MACHINE_I386;
-#elif defined(__ia64__)
- IMAGE_FILE_MACHINE_IA64;
-#else
-#error this architecture is not supported by shim
-#endif
-
-static int
-image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
-{
- /* If the machine type doesn't match the binary, bail, unless
- * we're in an allowed 64-on-32 scenario */
- if (PEHdr->Pe32.FileHeader.Machine != machine_type) {
- if (!(machine_type == IMAGE_FILE_MACHINE_I386 &&
- PEHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_X64 &&
- allow_64_bit())) {
- return 0;
- }
- }
-
- /* If it's not a header type we recognize at all, bail */
- switch (PEHdr->Pe32Plus.OptionalHeader.Magic) {
- case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
- case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
- break;
- default:
- return 0;
- }
-
- /* and now just check for general 64-vs-32 compatibility */
- if (image_is_64_bit(PEHdr)) {
- if (allow_64_bit())
- return 1;
- } else {
- if (allow_32_bit())
- return 1;
- }
- return 0;
-}
-
-/*
- * Perform the actual relocation
- */
-static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
- EFI_IMAGE_SECTION_HEADER *Section,
- void *orig, void *data)
-{
- EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd;
- UINT64 Adjust;
- UINT16 *Reloc, *RelocEnd;
- char *Fixup, *FixupBase;
- UINT16 *Fixup16;
- UINT32 *Fixup32;
- UINT64 *Fixup64;
- int size = context->ImageSize;
- void *ImageEnd = (char *)orig + size;
- int n = 0;
-
- /* Alright, so here's how this works:
- *
- * context->RelocDir gives us two things:
- * - the VA the table of base relocation blocks are (maybe) to be
- * mapped at (RelocDir->VirtualAddress)
- * - the virtual size (RelocDir->Size)
- *
- * The .reloc section (Section here) gives us some other things:
- * - the name! kind of. (Section->Name)
- * - the virtual size (Section->VirtualSize), which should be the same
- * as RelocDir->Size
- * - the virtual address (Section->VirtualAddress)
- * - the file section size (Section->SizeOfRawData), which is
- * a multiple of OptHdr->FileAlignment. Only useful for image
- * validation, not really useful for iteration bounds.
- * - the file address (Section->PointerToRawData)
- * - a bunch of stuff we don't use that's 0 in our binaries usually
- * - Flags (Section->Characteristics)
- *
- * and then the thing that's actually at the file address is an array
- * of EFI_IMAGE_BASE_RELOCATION structs with some values packed behind
- * them. The SizeOfBlock field of this structure includes the
- * structure itself, and adding it to that structure's address will
- * yield the next entry in the array.
- */
- RelocBase = ImageAddress(orig, size, Section->PointerToRawData);
- /* RelocBaseEnd here is the address of the first entry /past/ the
- * table. */
- RelocBaseEnd = ImageAddress(orig, size, Section->PointerToRawData +
- Section->Misc.VirtualSize);
-
- if (!RelocBase && !RelocBaseEnd)
- return EFI_SUCCESS;
-
- if (!RelocBase || !RelocBaseEnd) {
- perror(L"Reloc table overflows binary\n");
- return EFI_UNSUPPORTED;
- }
-
- Adjust = (UINTN)data - context->ImageAddress;
-
- if (Adjust == 0)
- return EFI_SUCCESS;
-
- while (RelocBase < RelocBaseEnd) {
- Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
-
- if (RelocBase->SizeOfBlock == 0) {
- perror(L"Reloc %d block size 0 is invalid\n", n);
- return EFI_UNSUPPORTED;
- } else if (RelocBase->SizeOfBlock > context->RelocDir->Size) {
- perror(L"Reloc %d block size %d greater than reloc dir"
- "size %d, which is invalid\n", n,
- RelocBase->SizeOfBlock,
- context->RelocDir->Size);
- return EFI_UNSUPPORTED;
- }
-
- RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock);
- if ((void *)RelocEnd < orig || (void *)RelocEnd > ImageEnd) {
- perror(L"Reloc %d entry overflows binary\n", n);
- return EFI_UNSUPPORTED;
- }
-
- FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress);
- if (!FixupBase) {
- perror(L"Reloc %d Invalid fixupbase\n", 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)));
- break;
-
- case EFI_IMAGE_REL_BASED_LOW:
- Fixup16 = (UINT16 *) Fixup;
- *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust);
- break;
-
- case EFI_IMAGE_REL_BASED_HIGHLOW:
- Fixup32 = (UINT32 *) Fixup;
- *Fixup32 = *Fixup32 + (UINT32) Adjust;
- break;
-
- case EFI_IMAGE_REL_BASED_DIR64:
- Fixup64 = (UINT64 *) Fixup;
- *Fixup64 = *Fixup64 + (UINT64) Adjust;
- break;
-
- default:
- perror(L"Reloc %d Unknown relocation\n", n);
- return EFI_UNSUPPORTED;
- }
- Reloc += 1;
- }
- RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
- n++;
- }
-
- return EFI_SUCCESS;
-}
-
static void
drain_openssl_errors(void)
{
@@ -366,12 +111,18 @@ static BOOLEAN verify_x509(UINT8 *Cert, UINTN CertSize)
* and 64KB. For convenience, assume the number of value bytes
* is 2, i.e. the second byte is 0x82.
*/
- if (Cert[0] != 0x30 || Cert[1] != 0x82)
+ if (Cert[0] != 0x30 || Cert[1] != 0x82) {
+ dprint(L"cert[0:1] is [%02x%02x], should be [%02x%02x]\n",
+ Cert[0], Cert[1], 0x30, 0x82);
return FALSE;
+ }
length = Cert[2]<<8 | Cert[3];
- if (length != (CertSize - 4))
+ if (length != (CertSize - 4)) {
+ dprint(L"Cert length is %ld, expecting %ld\n",
+ length, CertSize);
return FALSE;
+ }
return TRUE;
}
@@ -383,7 +134,9 @@ static BOOLEAN verify_eku(UINT8 *Cert, UINTN CertSize)
EXTENDED_KEY_USAGE *eku;
ASN1_OBJECT *module_signing;
- module_signing = OBJ_nid2obj(OBJ_create(OID_EKU_MODSIGN, NULL, NULL));
+ module_signing = OBJ_nid2obj(OBJ_create(OID_EKU_MODSIGN,
+ "modsign-eku",
+ "modsign-eku"));
x509 = d2i_X509 (NULL, &Temp, (long) CertSize);
if (x509 != NULL) {
@@ -403,25 +156,9 @@ static BOOLEAN verify_eku(UINT8 *Cert, UINTN CertSize)
X509_free(x509);
}
- return TRUE;
-}
+ OBJ_cleanup();
-static void show_ca_warning()
-{
- CHAR16 *text[9];
-
- text[0] = L"WARNING!";
- text[1] = L"";
- text[2] = L"The CA certificate used to verify this image doesn't ";
- text[3] = L"contain the CA flag in Basic Constraints or KeyCertSign";
- text[4] = L"in KeyUsage. Such CA certificates will not be supported";
- text[5] = L"in the future. ";
- text[6] = L"";
- text[7] = L"Please contact the issuer to update the certificate. ";
- text[8] = NULL;
-
- console_reset();
- console_print_box(text, -1);
+ return TRUE;
}
static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
@@ -433,24 +170,24 @@ static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
EFI_SIGNATURE_DATA *Cert;
UINTN CertSize;
BOOLEAN IsFound = FALSE;
+ int i = 0;
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
if (CompareGuid (&CertList->SignatureType, &EFI_CERT_TYPE_X509_GUID) == 0) {
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
CertSize = CertList->SignatureSize - sizeof(EFI_GUID);
+ dprint(L"trying to verify cert %d (%s)\n", i++, dbname);
if (verify_x509(Cert->SignatureData, CertSize)) {
if (verify_eku(Cert->SignatureData, CertSize)) {
- clear_ca_warning();
+ drain_openssl_errors();
IsFound = AuthenticodeVerify (data->CertData,
data->Hdr.dwLength - sizeof(data->Hdr),
Cert->SignatureData,
CertSize,
hash, SHA256_DIGEST_SIZE);
if (IsFound) {
- if (get_ca_warning()) {
- show_ca_warning();
- }
- tpm_measure_variable(dbname, guid, CertSize, Cert->SignatureData);
+ dprint(L"AuthenticodeVerify() succeeded: %d\n", IsFound);
+ tpm_measure_variable(dbname, guid, CertList->SignatureSize, Cert);
drain_openssl_errors();
return DATA_FOUND;
} else {
@@ -458,7 +195,9 @@ static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
}
}
} else if (verbose) {
- console_notify(L"Not a DER encoding x.509 Certificate");
+ console_print(L"Not a DER encoded x.509 Certificate");
+ dprint(L"cert:\n");
+ dhexdumpat(Cert->SignatureData, CertSize, 0);
}
}
@@ -513,7 +252,7 @@ static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList,
// Find the signature in database.
//
IsFound = TRUE;
- tpm_measure_variable(dbname, guid, SignatureSize, data);
+ tpm_measure_variable(dbname, guid, CertList->SignatureSize, Cert);
break;
}
@@ -562,27 +301,27 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
/*
* Check whether the binary signature or hash are present in dbx or the
- * built-in blacklist
+ * built-in denylist
*/
-static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
- UINT8 *sha256hash, UINT8 *sha1hash)
+static EFI_STATUS check_denylist (WIN_CERTIFICATE_EFI_PKCS *cert,
+ UINT8 *sha256hash, UINT8 *sha1hash)
{
- EFI_SIGNATURE_LIST *dbx = (EFI_SIGNATURE_LIST *)vendor_dbx;
+ EFI_SIGNATURE_LIST *dbx = (EFI_SIGNATURE_LIST *)vendor_deauthorized;
- if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha256hash,
+ if (check_db_hash_in_ram(dbx, vendor_deauthorized_size, sha256hash,
SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID, L"dbx",
EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) {
LogError(L"binary sha256hash found in vendor dbx\n");
return EFI_SECURITY_VIOLATION;
}
- if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha1hash,
+ if (check_db_hash_in_ram(dbx, vendor_deauthorized_size, sha1hash,
SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID, L"dbx",
EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) {
LogError(L"binary sha1hash found in vendor dbx\n");
return EFI_SECURITY_VIOLATION;
}
if (cert &&
- check_db_cert_in_ram(dbx, vendor_dbx_size, cert, sha256hash, L"dbx",
+ check_db_cert_in_ram(dbx, vendor_deauthorized_size, cert, sha256hash, L"dbx",
EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) {
LogError(L"cert sha256hash found in vendor dbx\n");
return EFI_SECURITY_VIOLATION;
@@ -628,7 +367,7 @@ static void update_verification_method(verification_method_t method)
/*
* Check whether the binary signature or hash are present in db or MokList
*/
-static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
+static EFI_STATUS check_allowlist (WIN_CERTIFICATE_EFI_PKCS *cert,
UINT8 *sha256hash, UINT8 *sha1hash)
{
if (!ignore_db) {
@@ -652,11 +391,36 @@ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
verification_method = VERIFIED_BY_CERT;
update_verification_method(VERIFIED_BY_CERT);
return EFI_SUCCESS;
- } else {
+ } else if (cert) {
LogError(L"check_db_cert(db, sha256hash) != DATA_FOUND\n");
}
}
+#if defined(VENDOR_DB_FILE)
+ EFI_SIGNATURE_LIST *db = (EFI_SIGNATURE_LIST *)vendor_db;
+
+ if (check_db_hash_in_ram(db, vendor_db_size,
+ sha256hash, SHA256_DIGEST_SIZE,
+ EFI_CERT_SHA256_GUID, L"vendor_db",
+ EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) {
+ verification_method = VERIFIED_BY_HASH;
+ update_verification_method(VERIFIED_BY_HASH);
+ return EFI_SUCCESS;
+ } else {
+ LogError(L"check_db_hash(vendor_db, sha256hash) != DATA_FOUND\n");
+ }
+ if (cert &&
+ check_db_cert_in_ram(db, vendor_db_size,
+ cert, sha256hash, L"vendor_db",
+ EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) {
+ verification_method = VERIFIED_BY_CERT;
+ update_verification_method(VERIFIED_BY_CERT);
+ return EFI_SUCCESS;
+ } else if (cert) {
+ LogError(L"check_db_cert(vendor_db, sha256hash) != DATA_FOUND\n");
+ }
+#endif
+
if (check_db_hash(L"MokList", SHIM_LOCK_GUID, sha256hash,
SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID)
== DATA_FOUND) {
@@ -671,19 +435,18 @@ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
verification_method = VERIFIED_BY_CERT;
update_verification_method(VERIFIED_BY_CERT);
return EFI_SUCCESS;
- } else {
+ } else if (cert) {
LogError(L"check_db_cert(MokList, sha256hash) != DATA_FOUND\n");
}
update_verification_method(VERIFIED_BY_NOTHING);
- return EFI_SECURITY_VIOLATION;
+ return EFI_NOT_FOUND;
}
/*
* Check whether we're in Secure Boot and user mode
*/
-
-static BOOLEAN secure_mode (void)
+BOOLEAN secure_mode (void)
{
static int first = 1;
if (user_insecure_mode)
@@ -713,787 +476,241 @@ static BOOLEAN secure_mode (void)
return TRUE;
}
-#define check_size_line(data, datasize_in, hashbase, hashsize, l) ({ \
- if ((unsigned long)hashbase > \
- (unsigned long)data + datasize_in) { \
- efi_status = EFI_INVALID_PARAMETER; \
- perror(L"shim.c:%d Invalid hash base 0x%016x\n", l, \
- hashbase); \
- goto done; \
- } \
- if ((unsigned long)hashbase + hashsize > \
- (unsigned long)data + datasize_in) { \
- efi_status = EFI_INVALID_PARAMETER; \
- perror(L"shim.c:%d Invalid hash size 0x%016x\n", l, \
- hashsize); \
- goto done; \
- } \
-})
-#define check_size(d,ds,h,hs) check_size_line(d,ds,h,hs,__LINE__)
-
-/*
- * Calculate the SHA1 and SHA256 hashes of a binary
- */
-
-static EFI_STATUS generate_hash (char *data, unsigned int datasize_in,
- PE_COFF_LOADER_IMAGE_CONTEXT *context,
- UINT8 *sha256hash, UINT8 *sha1hash)
-
+static EFI_STATUS
+verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig,
+ UINT8 *sha256hash, UINT8 *sha1hash)
{
- unsigned int sha256ctxsize, sha1ctxsize;
- unsigned int size = datasize_in;
- void *sha256ctx = NULL, *sha1ctx = NULL;
- char *hashbase;
- unsigned int hashsize;
- unsigned int SumOfBytesHashed, SumOfSectionBytes;
- unsigned int index, pos;
- unsigned int datasize;
- EFI_IMAGE_SECTION_HEADER *Section;
- EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL;
- EFI_STATUS efi_status = EFI_SUCCESS;
- EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data;
- unsigned int PEHdr_offset = 0;
-
- size = datasize = datasize_in;
-
- if (datasize <= sizeof (*DosHdr) ||
- DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
- perror(L"Invalid signature\n");
- return EFI_INVALID_PARAMETER;
- }
- PEHdr_offset = DosHdr->e_lfanew;
-
- sha256ctxsize = Sha256GetContextSize();
- sha256ctx = AllocatePool(sha256ctxsize);
-
- sha1ctxsize = Sha1GetContextSize();
- sha1ctx = AllocatePool(sha1ctxsize);
-
- if (!sha256ctx || !sha1ctx) {
- perror(L"Unable to allocate memory for hash context\n");
- return EFI_OUT_OF_RESOURCES;
- }
-
- if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) {
- perror(L"Unable to initialise hash\n");
- efi_status = EFI_OUT_OF_RESOURCES;
- goto done;
- }
-
- /* Hash start to checksum */
- hashbase = data;
- hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum -
- hashbase;
- check_size(data, datasize_in, hashbase, hashsize);
-
- if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
- !(Sha1Update(sha1ctx, hashbase, hashsize))) {
- perror(L"Unable to generate hash\n");
- efi_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;
- check_size(data, datasize_in, hashbase, hashsize);
-
- if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
- !(Sha1Update(sha1ctx, hashbase, hashsize))) {
- perror(L"Unable to generate hash\n");
- efi_status = EFI_OUT_OF_RESOURCES;
- goto done;
- }
-
- /* Hash end of certificate table to end of image header */
- EFI_IMAGE_DATA_DIRECTORY *dd = context->SecDir + 1;
- hashbase = (char *)dd;
- hashsize = context->SizeOfHeaders - (unsigned long)((char *)dd - data);
- if (hashsize > datasize_in) {
- perror(L"Data Directory size %d is invalid\n", hashsize);
- efi_status = EFI_INVALID_PARAMETER;
- goto done;
- }
- check_size(data, datasize_in, hashbase, hashsize);
-
- if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
- !(Sha1Update(sha1ctx, hashbase, hashsize))) {
- perror(L"Unable to generate hash\n");
- efi_status = EFI_OUT_OF_RESOURCES;
- goto done;
- }
-
- /* Sort sections */
- SumOfBytesHashed = context->SizeOfHeaders;
-
- /* Validate section locations and sizes */
- for (index = 0, SumOfSectionBytes = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
- EFI_IMAGE_SECTION_HEADER *SectionPtr;
-
- /* Validate SectionPtr is within image */
- SectionPtr = ImageAddress(data, datasize,
- PEHdr_offset +
- sizeof (UINT32) +
- sizeof (EFI_IMAGE_FILE_HEADER) +
- context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader +
- (index * sizeof(*SectionPtr)));
- if (!SectionPtr) {
- perror(L"Malformed section %d\n", index);
- efi_status = EFI_INVALID_PARAMETER;
- goto done;
- }
- /* Validate section size is within image. */
- if (SectionPtr->SizeOfRawData >
- datasize - SumOfBytesHashed - SumOfSectionBytes) {
- perror(L"Malformed section %d size\n", index);
- efi_status = EFI_INVALID_PARAMETER;
- goto done;
- }
- SumOfSectionBytes += SectionPtr->SizeOfRawData;
- }
-
- SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * context->PEHdr->Pe32.FileHeader.NumberOfSections);
- if (SectionHeader == NULL) {
- perror(L"Unable to allocate section header\n");
- efi_status = EFI_OUT_OF_RESOURCES;
- goto done;
- }
-
- /* Already validated above */
- Section = ImageAddress(data, datasize,
- PEHdr_offset +
- sizeof (UINT32) +
- sizeof (EFI_IMAGE_FILE_HEADER) +
- context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader);
- /* But check it again just for better error messaging, and so
- * clang-analyzer doesn't get confused. */
- if (Section == NULL) {
- uint64_t addr;
-
- addr = PEHdr_offset + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)
- + context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader;
- perror(L"Malformed file header.\n");
- perror(L"Image address for Section 0 is 0x%016llx\n", addr);
- perror(L"File size is 0x%016llx\n", datasize);
- efi_status = EFI_INVALID_PARAMETER;
- 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);
-
- if (!hashbase) {
- perror(L"Malformed section header\n");
- efi_status = EFI_INVALID_PARAMETER;
- goto done;
- }
-
- /* Verify hashsize within image. */
- if (Section->SizeOfRawData >
- datasize - Section->PointerToRawData) {
- perror(L"Malformed section raw size %d\n", index);
- efi_status = EFI_INVALID_PARAMETER;
- goto done;
- }
- hashsize = (unsigned int) Section->SizeOfRawData;
- check_size(data, datasize_in, hashbase, hashsize);
-
- if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
- !(Sha1Update(sha1ctx, hashbase, hashsize))) {
- perror(L"Unable to generate hash\n");
- efi_status = EFI_OUT_OF_RESOURCES;
- goto done;
- }
- SumOfBytesHashed += Section->SizeOfRawData;
- }
-
- /* Hash all remaining data up to SecDir if SecDir->Size is not 0 */
- if (datasize > SumOfBytesHashed && context->SecDir->Size) {
- hashbase = data + SumOfBytesHashed;
- hashsize = datasize - context->SecDir->Size - SumOfBytesHashed;
-
- if ((datasize - SumOfBytesHashed < context->SecDir->Size) ||
- (SumOfBytesHashed + hashsize != context->SecDir->VirtualAddress)) {
- perror(L"Malformed binary after Attribute Certificate Table\n");
- console_print(L"datasize: %u SumOfBytesHashed: %u SecDir->Size: %lu\n",
- datasize, SumOfBytesHashed, context->SecDir->Size);
- console_print(L"hashsize: %u SecDir->VirtualAddress: 0x%08lx\n",
- hashsize, context->SecDir->VirtualAddress);
- efi_status = EFI_INVALID_PARAMETER;
- goto done;
- }
- check_size(data, datasize_in, hashbase, hashsize);
-
- if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
- !(Sha1Update(sha1ctx, hashbase, hashsize))) {
- perror(L"Unable to generate hash\n");
- efi_status = EFI_OUT_OF_RESOURCES;
- goto done;
- }
-
-#if 1
- }
-#else // we have to migrate to doing this later :/
- SumOfBytesHashed += hashsize;
- }
-
- /* Hash all remaining data */
- if (datasize > SumOfBytesHashed) {
- hashbase = data + SumOfBytesHashed;
- hashsize = datasize - SumOfBytesHashed;
-
- check_size(data, datasize_in, hashbase, hashsize);
-
- if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
- !(Sha1Update(sha1ctx, hashbase, hashsize))) {
- perror(L"Unable to generate hash\n");
- efi_status = EFI_OUT_OF_RESOURCES;
- goto done;
- }
-
- SumOfBytesHashed += hashsize;
- }
-#endif
-
- if (!(Sha256Final(sha256ctx, sha256hash)) ||
- !(Sha1Final(sha1ctx, sha1hash))) {
- perror(L"Unable to finalise hash\n");
- efi_status = EFI_OUT_OF_RESOURCES;
- goto done;
- }
-
-done:
- if (SectionHeader)
- FreePool(SectionHeader);
- if (sha1ctx)
- FreePool(sha1ctx);
- if (sha256ctx)
- FreePool(sha256ctx);
-
- return efi_status;
-}
-
-/*
- * 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, UINT8 *sha1hash)
-{
- EFI_STATUS efi_status = EFI_SECURITY_VIOLATION;
- WIN_CERTIFICATE_EFI_PKCS *cert = NULL;
- unsigned int size = datasize;
-
- if (datasize < 0)
- return EFI_INVALID_PARAMETER;
-
- if (context->SecDir->Size != 0) {
- if (context->SecDir->Size >= size) {
- perror(L"Certificate Database size is too large\n");
- return EFI_INVALID_PARAMETER;
- }
-
- cert = ImageAddress (data, size,
- context->SecDir->VirtualAddress);
-
- if (!cert) {
- perror(L"Certificate located outside the image\n");
- return EFI_INVALID_PARAMETER;
- }
-
- if (cert->Hdr.dwLength > context->SecDir->Size) {
- perror(L"Certificate list size is inconsistent with PE headers");
- return EFI_INVALID_PARAMETER;
- }
-
- if (cert->Hdr.wCertificateType !=
- WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
- perror(L"Unsupported certificate type %x\n",
- cert->Hdr.wCertificateType);
- return EFI_UNSUPPORTED;
- }
- }
+ EFI_STATUS efi_status;
/*
- * Clear OpenSSL's error log, because we get some DSO unimplemented
- * errors during its intialization, and we don't want those to look
- * like they're the reason for validation failures.
+ * Ensure that the binary isn't forbidden
*/
drain_openssl_errors();
-
- efi_status = generate_hash(data, datasize, context, sha256hash, sha1hash);
+ efi_status = check_denylist(sig, sha256hash, sha1hash);
if (EFI_ERROR(efi_status)) {
- LogError(L"generate_hash: %r\n", efi_status);
+ perror(L"Binary is forbidden: %r\n", efi_status);
+ PrintErrors();
+ ClearErrors();
+ crypterr(efi_status);
return efi_status;
}
/*
- * Ensure that the binary isn't blacklisted
+ * Check whether the binary is authorized in any of the firmware
+ * databases
*/
- efi_status = check_blacklist(cert, sha256hash, sha1hash);
+ drain_openssl_errors();
+ efi_status = check_allowlist(sig, sha256hash, sha1hash);
if (EFI_ERROR(efi_status)) {
- perror(L"Binary is blacklisted\n");
- LogError(L"Binary is blacklisted: %r\n", efi_status);
+ if (efi_status != EFI_NOT_FOUND) {
+ dprint(L"check_allowlist(): %r\n", efi_status);
+ PrintErrors();
+ ClearErrors();
+ crypterr(efi_status);
+ }
+ } else {
+ drain_openssl_errors();
return efi_status;
}
+ efi_status = EFI_NOT_FOUND;
+#if defined(ENABLE_SHIM_CERT)
/*
- * Check whether the binary is whitelisted in any of the firmware
- * databases
+ * Check against the shim build key
*/
- efi_status = check_whitelist(cert, sha256hash, sha1hash);
- if (EFI_ERROR(efi_status)) {
- LogError(L"check_whitelist(): %r\n", efi_status);
- } else {
+ drain_openssl_errors();
+ if (build_cert && build_cert_size) {
+ dprint("verifying against shim cert\n");
+ }
+ if (build_cert && build_cert_size &&
+ AuthenticodeVerify(sig->CertData,
+ sig->Hdr.dwLength - sizeof(sig->Hdr),
+ build_cert, build_cert_size, sha256hash,
+ SHA256_DIGEST_SIZE)) {
+ dprint(L"AuthenticodeVerify(shim_cert) succeeded\n");
+ update_verification_method(VERIFIED_BY_CERT);
+ tpm_measure_variable(L"Shim", SHIM_LOCK_GUID,
+ build_cert_size, build_cert);
+ efi_status = EFI_SUCCESS;
drain_openssl_errors();
return efi_status;
+ } else {
+ dprint(L"AuthenticodeVerify(shim_cert) failed\n");
+ PrintErrors();
+ ClearErrors();
+ crypterr(EFI_NOT_FOUND);
}
-
- if (cert) {
-#if defined(ENABLE_SHIM_CERT)
- /*
- * Check against the shim build key
- */
- clear_ca_warning();
- if (sizeof(shim_cert) &&
- AuthenticodeVerify(cert->CertData,
- cert->Hdr.dwLength - sizeof(cert->Hdr),
- shim_cert, sizeof(shim_cert), sha256hash,
- SHA256_DIGEST_SIZE)) {
- if (get_ca_warning()) {
- show_ca_warning();
- }
- update_verification_method(VERIFIED_BY_CERT);
- tpm_measure_variable(L"Shim", SHIM_LOCK_GUID,
- sizeof(shim_cert), shim_cert);
- efi_status = EFI_SUCCESS;
- drain_openssl_errors();
- return efi_status;
- } else {
- LogError(L"AuthenticodeVerify(shim_cert) failed\n");
- }
#endif /* defined(ENABLE_SHIM_CERT) */
- /*
- * And finally, check against shim's built-in key
- */
- clear_ca_warning();
- if (vendor_cert_size &&
- AuthenticodeVerify(cert->CertData,
- cert->Hdr.dwLength - sizeof(cert->Hdr),
- vendor_cert, vendor_cert_size,
- sha256hash, SHA256_DIGEST_SIZE)) {
- if (get_ca_warning()) {
- show_ca_warning();
- }
- update_verification_method(VERIFIED_BY_CERT);
- tpm_measure_variable(L"Shim", SHIM_LOCK_GUID,
- vendor_cert_size, vendor_cert);
- efi_status = EFI_SUCCESS;
- drain_openssl_errors();
- return efi_status;
- } else {
- LogError(L"AuthenticodeVerify(vendor_cert) failed\n");
- }
+#if defined(VENDOR_CERT_FILE)
+ /*
+ * And finally, check against shim's built-in key
+ */
+ drain_openssl_errors();
+ if (vendor_cert_size) {
+ dprint("verifying against vendor_cert\n");
+ }
+ if (vendor_cert_size &&
+ AuthenticodeVerify(sig->CertData,
+ sig->Hdr.dwLength - sizeof(sig->Hdr),
+ vendor_cert, vendor_cert_size,
+ sha256hash, SHA256_DIGEST_SIZE)) {
+ dprint(L"AuthenticodeVerify(vendor_cert) succeeded\n");
+ update_verification_method(VERIFIED_BY_CERT);
+ tpm_measure_variable(L"Shim", SHIM_LOCK_GUID,
+ vendor_cert_size, vendor_cert);
+ efi_status = EFI_SUCCESS;
+ drain_openssl_errors();
+ return efi_status;
+ } else {
+ dprint(L"AuthenticodeVerify(vendor_cert) failed\n");
+ PrintErrors();
+ ClearErrors();
+ crypterr(EFI_NOT_FOUND);
}
+#endif /* defined(VENDOR_CERT_FILE) */
- LogError(L"Binary is not whitelisted\n");
- crypterr(EFI_SECURITY_VIOLATION);
- PrintErrors();
- efi_status = EFI_SECURITY_VIOLATION;
return efi_status;
}
/*
- * Read the binary header and grab appropriate information from it
+ * Check that the signature is valid and matches the binary
*/
-static EFI_STATUS read_header(void *data, unsigned int datasize,
- PE_COFF_LOADER_IMAGE_CONTEXT *context)
+EFI_STATUS
+verify_buffer (char *data, int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context,
+ UINT8 *sha256hash, UINT8 *sha1hash)
{
- EFI_IMAGE_DOS_HEADER *DosHdr = data;
- EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
- unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize;
- unsigned long FileAlignment = 0;
-
- if (datasize < sizeof (PEHdr->Pe32)) {
- perror(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 (!image_is_loadable(PEHdr)) {
- perror(L"Platform does not support this image\n");
- return EFI_UNSUPPORTED;
- }
-
- if (image_is_64_bit(PEHdr)) {
- context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
- context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
- context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage;
- context->SectionAlignment = PEHdr->Pe32Plus.OptionalHeader.SectionAlignment;
- FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment;
- OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64);
- } else {
- context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
- context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders;
- context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage;
- context->SectionAlignment = PEHdr->Pe32.OptionalHeader.SectionAlignment;
- FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment;
- OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32);
- }
-
- if (FileAlignment % 2 != 0) {
- perror(L"File Alignment is invalid (%d)\n", FileAlignment);
- return EFI_UNSUPPORTED;
- }
- if (FileAlignment == 0)
- FileAlignment = 0x200;
- if (context->SectionAlignment == 0)
- context->SectionAlignment = PAGE_SIZE;
- if (context->SectionAlignment < FileAlignment)
- context->SectionAlignment = FileAlignment;
-
- context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections;
-
- if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) {
- perror(L"Image header too small\n");
- return EFI_UNSUPPORTED;
- }
-
- HeaderWithoutDataDir = OptHeaderSize
- - sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
- if (((UINT32)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) !=
- context->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) {
- perror(L"Image header overflows data directory\n");
- return EFI_UNSUPPORTED;
- }
-
- SectionHeaderOffset = DosHdr->e_lfanew
- + sizeof (UINT32)
- + sizeof (EFI_IMAGE_FILE_HEADER)
- + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader;
- if (((UINT32)context->ImageSize - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
- <= context->NumberOfSections) {
- perror(L"Image sections overflow image size\n");
- return EFI_UNSUPPORTED;
- }
-
- if ((context->SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
- < (UINT32)context->NumberOfSections) {
- perror(L"Image sections overflow section headers\n");
- return EFI_UNSUPPORTED;
- }
-
- if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) {
- perror(L"Invalid image\n");
- return EFI_UNSUPPORTED;
- }
-
- if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
- perror(L"Unsupported image type\n");
- return EFI_UNSUPPORTED;
- }
+ EFI_STATUS ret_efi_status;
+ size_t size = datasize;
+ size_t offset = 0;
+ unsigned int i = 0;
- if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) {
- perror(L"Unsupported image - Relocations have been stripped\n");
- return EFI_UNSUPPORTED;
- }
-
- context->PEHdr = PEHdr;
-
- if (image_is_64_bit(PEHdr)) {
- context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase;
- context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
- context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
- context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
- } else {
- context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase;
- context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
- context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
- context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
- }
-
- context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER));
-
- if (context->ImageSize < context->SizeOfHeaders) {
- perror(L"Invalid image\n");
- return EFI_UNSUPPORTED;
- }
-
- if ((unsigned long)((UINT8 *)context->SecDir - (UINT8 *)data) >
- (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
- perror(L"Invalid image\n");
- return EFI_UNSUPPORTED;
- }
-
- if (context->SecDir->VirtualAddress > datasize ||
- (context->SecDir->VirtualAddress == datasize &&
- context->SecDir->Size > 0)) {
- perror(L"Malformed security header\n");
+ if (datasize < 0)
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_IMAGE_ENTRY_POINT *entry_point,
- EFI_PHYSICAL_ADDRESS *alloc_address,
- UINTN *alloc_pages)
-{
- EFI_STATUS efi_status;
- char *buffer;
- int i;
- EFI_IMAGE_SECTION_HEADER *Section;
- char *base, *end;
- PE_COFF_LOADER_IMAGE_CONTEXT context;
- unsigned int alignment, alloc_size;
- int found_entry_point = 0;
- UINT8 sha1hash[SHA1_DIGEST_SIZE];
- UINT8 sha256hash[SHA256_DIGEST_SIZE];
/*
- * The binary header contains relevant context and section pointers
+ * Clear OpenSSL's error log, because we get some DSO unimplemented
+ * errors during its intialization, and we don't want those to look
+ * like they're the reason for validation failures.
*/
- efi_status = read_header(data, datasize, &context);
- if (EFI_ERROR(efi_status)) {
- perror(L"Failed to read header: %r\n", efi_status);
- return efi_status;
+ drain_openssl_errors();
+
+ ret_efi_status = generate_hash(data, datasize, context, sha256hash, sha1hash);
+ if (EFI_ERROR(ret_efi_status)) {
+ dprint(L"generate_hash: %r\n", ret_efi_status);
+ PrintErrors();
+ ClearErrors();
+ crypterr(ret_efi_status);
+ return ret_efi_status;
}
/*
- * We only need to verify the binary if we're in secure mode
+ * Ensure that the binary isn't forbidden by hash
*/
- efi_status = generate_hash(data, datasize, &context, sha256hash,
- sha1hash);
- if (EFI_ERROR(efi_status))
- return efi_status;
-
- /* Measure the binary into the TPM */
-#ifdef REQUIRE_TPM
- efi_status =
-#endif
- tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize, sha1hash, 4);
-#ifdef REQUIRE_TPM
- if (efi_status != EFI_SUCCESS) {
- return efi_status;
+ drain_openssl_errors();
+ ret_efi_status = check_denylist(NULL, sha256hash, sha1hash);
+ if (EFI_ERROR(ret_efi_status)) {
+// perror(L"Binary is forbidden\n");
+// dprint(L"Binary is forbidden: %r\n", ret_efi_status);
+ PrintErrors();
+ ClearErrors();
+ crypterr(ret_efi_status);
+ return ret_efi_status;
}
-#endif
-
- if (secure_mode ()) {
- efi_status = verify_buffer(data, datasize, &context,
- sha256hash, sha1hash);
- if (EFI_ERROR(efi_status)) {
- console_error(L"Verification failed", efi_status);
- return efi_status;
- } else {
- if (verbose)
- console_print(L"Verification succeeded\n");
+ /*
+ * Check whether the binary is authorized by hash in any of the
+ * firmware databases
+ */
+ drain_openssl_errors();
+ ret_efi_status = check_allowlist(NULL, sha256hash, sha1hash);
+ if (EFI_ERROR(ret_efi_status)) {
+ LogError(L"check_allowlist(): %r\n", ret_efi_status);
+ dprint(L"check_allowlist: %r\n", ret_efi_status);
+ if (ret_efi_status != EFI_NOT_FOUND) {
+ dprint(L"check_allowlist(): %r\n", ret_efi_status);
+ PrintErrors();
+ ClearErrors();
+ crypterr(ret_efi_status);
+ return ret_efi_status;
}
+ } else {
+ drain_openssl_errors();
+ return ret_efi_status;
}
- /* The spec says, uselessly, of SectionAlignment:
- * =====
- * The alignment (in bytes) of sections when they are loaded into
- * memory. It must be greater than or equal to FileAlignment. The
- * default is the page size for the architecture.
- * =====
- * Which doesn't tell you whose responsibility it is to enforce the
- * "default", or when. It implies that the value in the field must
- * be > FileAlignment (also poorly defined), but it appears visual
- * studio will happily write 512 for FileAlignment (its default) and
- * 0 for SectionAlignment, intending to imply PAGE_SIZE.
- *
- * We only support one page size, so if it's zero, nerf it to 4096.
- */
- alignment = context.SectionAlignment;
- if (!alignment)
- alignment = 4096;
-
- alloc_size = ALIGN_VALUE(context.ImageSize + context.SectionAlignment,
- PAGE_SIZE);
- *alloc_pages = alloc_size / PAGE_SIZE;
-
- efi_status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderCode,
- *alloc_pages, alloc_address);
- if (EFI_ERROR(efi_status)) {
- perror(L"Failed to allocate image buffer\n");
- return EFI_OUT_OF_RESOURCES;
+ if (context->SecDir->Size == 0) {
+ dprint(L"No signatures found\n");
+ return EFI_SECURITY_VIOLATION;
}
- buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment);
-
- CopyMem(buffer, data, context.SizeOfHeaders);
-
- *entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
- if (!*entry_point) {
- perror(L"Entry point is invalid\n");
- gBS->FreePages(*alloc_address, *alloc_pages);
- return EFI_UNSUPPORTED;
+ if (context->SecDir->Size >= size) {
+ perror(L"Certificate Database size is too large\n");
+ return EFI_INVALID_PARAMETER;
}
+ ret_efi_status = EFI_NOT_FOUND;
+ do {
+ WIN_CERTIFICATE_EFI_PKCS *sig = NULL;
+ size_t sz;
- char *RelocBase, *RelocBaseEnd;
- /*
- * These are relative virtual addresses, so we have to check them
- * against the image size, not the data size.
- */
- RelocBase = ImageAddress(buffer, context.ImageSize,
- context.RelocDir->VirtualAddress);
- /*
- * RelocBaseEnd here is the address of the last byte of the table
- */
- RelocBaseEnd = ImageAddress(buffer, context.ImageSize,
- context.RelocDir->VirtualAddress +
- context.RelocDir->Size - 1);
-
- EFI_IMAGE_SECTION_HEADER *RelocSection = NULL;
-
- /*
- * Copy the executable's sections to their desired offsets
- */
- Section = context.FirstSection;
- for (i = 0; i < context.NumberOfSections; i++, Section++) {
- base = ImageAddress (buffer, context.ImageSize,
- Section->VirtualAddress);
- end = ImageAddress (buffer, context.ImageSize,
- Section->VirtualAddress
- + Section->Misc.VirtualSize - 1);
-
- if (end < base) {
- perror(L"Section %d has negative size\n", i);
- gBS->FreePages(*alloc_address, *alloc_pages);
- return EFI_UNSUPPORTED;
- }
-
- if (Section->VirtualAddress <= context.EntryPoint &&
- (Section->VirtualAddress + Section->SizeOfRawData - 1)
- > context.EntryPoint)
- found_entry_point++;
-
- /* We do want to process .reloc, but it's often marked
- * discardable, so we don't want to memcpy it. */
- if (CompareMem(Section->Name, ".reloc\0\0", 8) == 0) {
- if (RelocSection) {
- perror(L"Image has multiple relocation sections\n");
- return EFI_UNSUPPORTED;
- }
- /* If it has nonzero sizes, and our bounds check
- * made sense, and the VA and size match RelocDir's
- * versions, then we believe in this section table. */
- if (Section->SizeOfRawData &&
- Section->Misc.VirtualSize &&
- base && end &&
- RelocBase == base &&
- RelocBaseEnd == end) {
- RelocSection = Section;
- }
- }
+ sig = ImageAddress(data, size,
+ context->SecDir->VirtualAddress + offset);
+ if (!sig)
+ break;
- if (Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) {
- continue;
+ sz = offset + offsetof(WIN_CERTIFICATE_EFI_PKCS, Hdr.dwLength)
+ + sizeof(sig->Hdr.dwLength);
+ if (sz > context->SecDir->Size) {
+ perror(L"Certificate size is too large for secruity database");
+ return EFI_INVALID_PARAMETER;
}
- if (!base) {
- perror(L"Section %d has invalid base address\n", i);
- return EFI_UNSUPPORTED;
- }
- if (!end) {
- perror(L"Section %d has zero size\n", i);
- return EFI_UNSUPPORTED;
+ sz = sig->Hdr.dwLength;
+ if (sz > context->SecDir->Size - offset) {
+ perror(L"Certificate size is too large for secruity database");
+ return EFI_INVALID_PARAMETER;
}
- if (!(Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) &&
- (Section->VirtualAddress < context.SizeOfHeaders ||
- Section->PointerToRawData < context.SizeOfHeaders)) {
- perror(L"Section %d is inside image headers\n", i);
- return EFI_UNSUPPORTED;
+ if (sz < sizeof(sig->Hdr)) {
+ perror(L"Certificate size is too small for certificate data");
+ return EFI_INVALID_PARAMETER;
}
- if (Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
- ZeroMem(base, Section->Misc.VirtualSize);
- } else {
- if (Section->PointerToRawData < context.SizeOfHeaders) {
- perror(L"Section %d is inside image headers\n", i);
- return EFI_UNSUPPORTED;
- }
-
- if (Section->SizeOfRawData > 0)
- CopyMem(base, data + Section->PointerToRawData,
- Section->SizeOfRawData);
-
- if (Section->SizeOfRawData < Section->Misc.VirtualSize)
- ZeroMem(base + Section->SizeOfRawData,
- Section->Misc.VirtualSize - Section->SizeOfRawData);
- }
- }
+ if (sig->Hdr.wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+ EFI_STATUS efi_status;
- if (context.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
- perror(L"Image has no relocation entry\n");
- FreePool(buffer);
- return EFI_UNSUPPORTED;
- }
+ dprint(L"Attempting to verify signature %d:\n", i++);
- if (context.RelocDir->Size && RelocSection) {
- /*
- * Run the relocation fixups
- */
- efi_status = relocate_coff(&context, RelocSection, data,
- buffer);
+ efi_status = verify_one_signature(sig, sha256hash, sha1hash);
- if (EFI_ERROR(efi_status)) {
- perror(L"Relocation failed: %r\n", efi_status);
- FreePool(buffer);
- return efi_status;
+ /*
+ * If we didn't get EFI_SECURITY_VIOLATION from
+ * checking the hashes above, then any dbx entries are
+ * for a certificate, not this individual binary.
+ *
+ * So don't clobber successes with security violation
+ * here; that just means it isn't a success.
+ */
+ if (ret_efi_status != EFI_SUCCESS)
+ ret_efi_status = efi_status;
+ } else {
+ perror(L"Unsupported certificate type %x\n",
+ sig->Hdr.wCertificateType);
}
- }
-
- /*
- * 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 */
- if ( load_options ) {
- li->LoadOptions = load_options;
- li->LoadOptionsSize = load_options_size;
- }
+ offset = ALIGN_VALUE(offset + sz, 8);
+ } while (offset < context->SecDir->Size);
- if (!found_entry_point) {
- perror(L"Entry point is not within sections\n");
- return EFI_UNSUPPORTED;
- }
- if (found_entry_point > 1) {
- perror(L"%d sections contain entry point\n");
- return EFI_UNSUPPORTED;
+ if (ret_efi_status != EFI_SUCCESS) {
+ dprint(L"Binary is not authorized\n");
+ PrintErrors();
+ ClearErrors();
+ crypterr(EFI_SECURITY_VIOLATION);
+ ret_efi_status = EFI_SECURITY_VIOLATION;
}
-
- return EFI_SUCCESS;
+ drain_openssl_errors();
+ return ret_efi_status;
}
static int
@@ -1589,7 +806,7 @@ static EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li,
/*
* Suuuuper lazy technique here, but check and see if this is a full
* path to something on the ESP. Backwards compatibility demands
- * that we don't just use \\, becuase we (not particularly brightly)
+ * that we don't just use \\, because we (not particularly brightly)
* used to require that the relative file path started with that.
*
* If it is a full path, don't try to merge it with the directory
@@ -1640,9 +857,11 @@ static EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li,
bootpath[j] = '\0';
}
- while (*ImagePath == '\\')
- ImagePath++;
+ for (i = 0, last = 0; i < StrLen(ImagePath); i++)
+ if (ImagePath[i] == '\\')
+ last = i + 1;
+ ImagePath = ImagePath + last;
*PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
if (!*PathName) {
@@ -1677,6 +896,7 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
device = li->DeviceHandle;
+ dprint(L"attempting to load %s\n", PathName);
/*
* Open the device
*/
@@ -1802,7 +1022,8 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size)
#ifdef REQUIRE_TPM
efi_status =
#endif
- tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)buffer, size, sha1hash, 4);
+ tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)buffer, size, 0, NULL,
+ sha1hash, 4);
#ifdef REQUIRE_TPM
if (EFI_ERROR(efi_status))
goto done;
@@ -1813,8 +1034,8 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size)
goto done;
}
- efi_status = verify_buffer(buffer, size, &context,
- sha256hash, sha1hash);
+ efi_status = verify_buffer(buffer, size,
+ &context, sha256hash, sha1hash);
done:
in_protocol = 0;
return efi_status;
@@ -1849,13 +1070,24 @@ static EFI_STATUS shim_read_header(void *data, unsigned int datasize,
return efi_status;
}
+VOID
+restore_loaded_image(VOID)
+{
+ if (shim_li->FilePath)
+ FreePool(shim_li->FilePath);
+
+ /*
+ * Restore our original loaded image values
+ */
+ CopyMem(shim_li, &shim_li_bak, sizeof(shim_li_bak));
+}
+
/*
* Load and run an EFI executable
*/
EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
{
EFI_STATUS efi_status;
- EFI_LOADED_IMAGE *li, li_bak;
EFI_IMAGE_ENTRY_POINT entry_point;
EFI_PHYSICAL_ADDRESS alloc_address;
UINTN alloc_pages;
@@ -1863,14 +1095,14 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
void *sourcebuffer = NULL;
UINT64 sourcesize = 0;
void *data = NULL;
- int datasize;
+ int datasize = 0;
/*
* We need to refer to the loaded image protocol on the running
* binary in order to find our path
*/
efi_status = gBS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID,
- (void **)&li);
+ (void **)&shim_li);
if (EFI_ERROR(efi_status)) {
perror(L"Unable to init protocol\n");
return efi_status;
@@ -1879,14 +1111,14 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
/*
* Build a new path from the existing one plus the executable name
*/
- efi_status = generate_path_from_image_path(li, ImagePath, &PathName);
+ efi_status = generate_path_from_image_path(shim_li, ImagePath, &PathName);
if (EFI_ERROR(efi_status)) {
perror(L"Unable to generate path %s: %r\n", ImagePath,
efi_status);
goto done;
}
- if (findNetboot(li->DeviceHandle)) {
+ if (findNetboot(shim_li->DeviceHandle)) {
efi_status = parseNetbootinfo(image_handle);
if (EFI_ERROR(efi_status)) {
perror(L"Netboot parsing failed: %r\n", efi_status);
@@ -1901,8 +1133,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
}
data = sourcebuffer;
datasize = sourcesize;
-#if defined(ENABLE_HTTPBOOT)
- } else if (find_httpboot(li->DeviceHandle)) {
+ } else if (find_httpboot(shim_li->DeviceHandle)) {
efi_status = httpboot_fetch_buffer (image_handle,
&sourcebuffer,
&sourcesize);
@@ -1913,12 +1144,11 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
}
data = sourcebuffer;
datasize = sourcesize;
-#endif
} else {
/*
* Read the new executable off disk
*/
- efi_status = load_image(li, &data, &datasize, PathName);
+ efi_status = load_image(shim_li, &data, &datasize, PathName);
if (EFI_ERROR(efi_status)) {
perror(L"Failed to load image %s: %r\n",
PathName, efi_status);
@@ -1937,19 +1167,28 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
* 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));
+ CopyMem(&shim_li_bak, shim_li, sizeof(shim_li_bak));
+
+ /*
+ * Update the loaded image with the second stage loader file path
+ */
+ shim_li->FilePath = FileDevicePath(NULL, PathName);
+ if (!shim_li->FilePath) {
+ perror(L"Unable to update loaded image file path\n");
+ efi_status = EFI_OUT_OF_RESOURCES;
+ goto restore;
+ }
/*
* Verify and, if appropriate, relocate and execute the executable
*/
- efi_status = handle_image(data, datasize, li, &entry_point,
+ efi_status = handle_image(data, datasize, shim_li, &entry_point,
&alloc_address, &alloc_pages);
if (EFI_ERROR(efi_status)) {
perror(L"Failed to load image: %r\n", efi_status);
PrintErrors();
ClearErrors();
- CopyMem(li, &li_bak, sizeof(li_bak));
- goto done;
+ goto restore;
}
loader_is_participating = 0;
@@ -1959,10 +1198,8 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
*/
efi_status = entry_point(image_handle, systab);
- /*
- * Restore our original loaded image values
- */
- CopyMem(li, &li_bak, sizeof(li_bak));
+restore:
+ restore_loaded_image();
done:
if (PathName)
FreePool(PathName);
@@ -2114,21 +1351,32 @@ get_load_option_optional_data(UINT8 *data, UINTN data_size,
return EFI_SUCCESS;
}
-static int is_our_path(EFI_LOADED_IMAGE *li, CHAR16 *path, UINTN len)
+static int is_our_path(EFI_LOADED_IMAGE *li, CHAR16 *path)
{
CHAR16 *dppath = NULL;
+ CHAR16 *PathName = NULL;
+ EFI_STATUS efi_status;
int ret = 1;
dppath = DevicePathToStr(li->FilePath);
if (!dppath)
return 0;
+ efi_status = generate_path_from_image_path(li, path, &PathName);
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Unable to generate path %s: %r\n", path,
+ efi_status);
+ goto done;
+ }
+
dprint(L"dppath: %s\n", dppath);
dprint(L"path: %s\n", path);
- if (StrnCaseCmp(dppath, path, len))
+ if (StrnCaseCmp(dppath, PathName, StrLen(dppath)))
ret = 0;
+done:
FreePool(dppath);
+ FreePool(PathName);
return ret;
}
@@ -2140,12 +1388,19 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
EFI_STATUS efi_status;
EFI_LOADED_IMAGE *li = NULL;
CHAR16 *start = NULL;
- int remaining_size = 0;
+ UINTN remaining_size = 0;
CHAR16 *loader_str = NULL;
UINTN loader_len = 0;
unsigned int i;
+ UINTN second_stage_len;
- second_stage = DEFAULT_LOADER;
+ second_stage_len = (StrLen(DEFAULT_LOADER) + 1) * sizeof(CHAR16);
+ second_stage = AllocatePool(second_stage_len);
+ if (!second_stage) {
+ perror(L"Could not allocate %lu bytes\n", second_stage_len);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ StrCpy(second_stage, DEFAULT_LOADER);
load_options = NULL;
load_options_size = 0;
@@ -2156,6 +1411,10 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
return efi_status;
}
+ /* Sanity check since we make several assumptions about the length */
+ if (li->LoadOptionsSize % 2 != 0)
+ return EFI_INVALID_PARAMETER;
+
/* So, load options are a giant pain in the ass. If we're invoked
* from the EFI shell, we get something like this:
@@ -2203,6 +1462,12 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
*/
/*
+ * Maybe there just aren't any options...
+ */
+ if (li->LoadOptionsSize == 0)
+ return EFI_SUCCESS;
+
+ /*
* In either case, we've got to have at least a UCS2 NUL...
*/
if (li->LoadOptionsSize < 2)
@@ -2243,6 +1508,31 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
*/
UINTN strings = count_ucs2_strings(li->LoadOptions,
li->LoadOptionsSize);
+
+ /*
+ * In some cases we get strings == 1 because BDS is using L' ' as the
+ * delimeter:
+ * 0000:74 00 65 00 73 00 74 00 2E 00 65 00 66 00 69 00 t.e.s.t...e.f.i.
+ * 0016:20 00 6F 00 6E 00 65 00 20 00 74 00 77 00 6F 00 ..o.n.e...t.w.o.
+ * 0032:20 00 74 00 68 00 72 00 65 00 65 00 00 00 ..t.h.r.e.e...
+ *
+ * If so replace it with NULs since the code already handles that
+ * case.
+ */
+ if (strings == 1) {
+ UINT16 *cur = start = li->LoadOptions;
+
+ /* replace L' ' with L'\0' if we find any */
+ for (i = 0; i < li->LoadOptionsSize / 2; i++) {
+ if (cur[i] == L' ')
+ cur[i] = L'\0';
+ }
+
+ /* redo the string count */
+ strings = count_ucs2_strings(li->LoadOptions,
+ li->LoadOptionsSize);
+ }
+
/*
* If it's not string data, try it as an EFI_LOAD_OPTION.
*/
@@ -2262,70 +1552,41 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
} else if (strings >= 2) {
/*
* UEFI shell copies the whole line of the command into
- * LoadOptions. We ignore the string before the first L' ',
+ * LoadOptions. We ignore the string before the first L'\0',
* i.e. the name of this program.
- * Counting by two bytes is safe, because we know the size is
- * compatible with a UCS2-LE string.
*/
- UINT8 *cur = li->LoadOptions;
- for (i = 0; i < li->LoadOptionsSize - 2; i += 2) {
- CHAR16 c = (cur[i+1] << 8) | cur[i];
- if (c == L' ') {
- start = (CHAR16 *)&cur[i+2];
- remaining_size = li->LoadOptionsSize - i - 2;
+ UINT16 *cur = li->LoadOptions;
+ for (i = 1; i < li->LoadOptionsSize / 2; i++) {
+ if (cur[i - 1] == L'\0') {
+ start = &cur[i];
+ remaining_size = li->LoadOptionsSize - (i * 2);
break;
}
}
- if (!start || remaining_size <= 0 || start[0] == L'\0')
- return EFI_SUCCESS;
-
- for (i = 0; start[i] != '\0'; i++) {
- if (start[i] == L' ')
- start[i] = L'\0';
- if (start[i] == L'\0') {
- loader_len = 2 * i + 2;
- break;
- }
- }
- if (loader_len)
- remaining_size -= loader_len;
- } else {
- /* only find one string */
- start = li->LoadOptions;
- loader_len = li->LoadOptionsSize;
- }
-
- /*
- * Just to be sure all that math is right...
- */
- if (loader_len % 2 != 0)
- return EFI_INVALID_PARAMETER;
-
- strings = count_ucs2_strings((UINT8 *)start, loader_len);
- if (strings < 1)
- return EFI_SUCCESS;
-
- /*
- * And then I found a version of BDS that gives us our own path in
- * LoadOptions:
+ remaining_size -= i * 2 + 2;
+ } else if (strings == 1 && is_our_path(li, start)) {
+ /*
+ * And then I found a version of BDS that gives us our own path
+ * in LoadOptions:
77162C58 5c 00 45 00 46 00 49 00 |\.E.F.I.|
77162C60 5c 00 42 00 4f 00 4f 00 54 00 5c 00 42 00 4f 00 |\.B.O.O.T.\.B.O.|
77162C70 4f 00 54 00 58 00 36 00 34 00 2e 00 45 00 46 00 |O.T.X.6.4...E.F.|
77162C80 49 00 00 00 |I...|
- * which is just cruel... So yeah, just don't use it.
- */
- if (strings == 1 && is_our_path(li, start, loader_len))
+ * which is just cruel... So yeah, just don't use it.
+ */
return EFI_SUCCESS;
+ }
/*
* Set up the name of the alternative loader and the LoadOptions for
* the loader
*/
if (loader_len > 0) {
- loader_str = AllocatePool(loader_len);
+ /* we might not always have a NULL at the end */
+ loader_str = AllocatePool(loader_len + 2);
if (!loader_str) {
perror(L"Failed to allocate loader string\n");
return EFI_OUT_OF_RESOURCES;
@@ -2333,7 +1594,7 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
for (i = 0; i < loader_len / 2; i++)
loader_str[i] = start[i];
- loader_str[loader_len/2-1] = L'\0';
+ loader_str[loader_len/2] = L'\0';
second_stage = loader_str;
load_options = remaining_size ? start + (loader_len/2) : NULL;
@@ -2344,13 +1605,13 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
}
static void *
-ossl_malloc(size_t num, const char *file, int line)
+ossl_malloc(size_t num)
{
return AllocatePool(num);
}
static void
-ossl_free(void *addr, const char *file, int line)
+ossl_free(void *addr)
{
FreePool(addr);
}
@@ -2462,14 +1723,19 @@ uninstall_shim_protocols(void)
EFI_STATUS
shim_init(void)
{
- setup_verbosity();
+ EFI_STATUS efi_status;
+
dprint(L"%a", shim_version);
/* Set the second stage loader */
- set_second_stage (global_image_handle);
+ efi_status = set_second_stage(global_image_handle);
+ if (EFI_ERROR(efi_status)) {
+ perror(L"set_second_stage() failed: %r\n", efi_status);
+ return efi_status;
+ }
if (secure_mode()) {
- if (vendor_cert_size || vendor_dbx_size) {
+ if (vendor_authorized_size || vendor_deauthorized_size) {
/*
* If shim includes its own certificates then ensure
* that anything it boots has performed some
@@ -2479,15 +1745,23 @@ shim_init(void)
loader_is_participating = 0;
}
- hook_exit(systab);
}
- return install_shim_protocols();
+ hook_exit(systab);
+
+ efi_status = install_shim_protocols();
+ if (EFI_ERROR(efi_status))
+ perror(L"install_shim_protocols() failed: %r\n", efi_status);
+
+ return efi_status;
}
void
shim_fini(void)
{
+ if (secure_mode())
+ cleanup_sbat_var(&sbat_var);
+
/*
* Remove our protocols
*/
@@ -2499,9 +1773,10 @@ shim_fini(void)
* Remove our hooks from system services.
*/
unhook_system_services();
- unhook_exit();
}
+ unhook_exit();
+
/*
* Free the space allocated for the alternative 2nd stage loader
*/
@@ -2521,13 +1796,20 @@ debug_hook(void)
UINT8 *data = NULL;
UINTN dataSize = 0;
EFI_STATUS efi_status;
- volatile register UINTN x = 0;
+ register volatile UINTN x = 0;
extern char _text, _data;
+ const CHAR16 * const debug_var_name =
+#ifdef ENABLE_SHIM_DEVEL
+ L"SHIM_DEVEL_DEBUG";
+#else
+ L"SHIM_DEBUG";
+#endif
+
if (x)
return;
- efi_status = get_variable(L"SHIM_DEBUG", &data, &dataSize,
+ efi_status = get_variable(debug_var_name, &data, &dataSize,
SHIM_LOCK_GUID);
if (EFI_ERROR(efi_status)) {
return;
@@ -2540,28 +1822,55 @@ debug_hook(void)
&_text, &_data);
console_print(L"Pausing for debugger attachment.\n");
- console_print(L"To disable this, remove the EFI variable SHIM_DEBUG-%g .\n",
- &SHIM_LOCK_GUID);
+ console_print(L"To disable this, remove the EFI variable %s-%g .\n",
+ debug_var_name, &SHIM_LOCK_GUID);
x = 1;
while (x++) {
/* Make this so it can't /totally/ DoS us. */
#if defined(__x86_64__) || defined(__i386__) || defined(__i686__)
if (x > 4294967294ULL)
break;
- __asm__ __volatile__("pause");
#elif defined(__aarch64__)
if (x > 1000)
break;
- __asm__ __volatile__("wfi");
#else
if (x > 12000)
break;
- msleep(5000);
#endif
+ pause();
}
x = 1;
}
+typedef enum {
+ COLD_RESET,
+ EXIT_FAILURE,
+ EXIT_SUCCESS, // keep this one last
+} devel_egress_action;
+
+void
+devel_egress(devel_egress_action action UNUSED)
+{
+#ifdef ENABLE_SHIM_DEVEL
+ char *reasons[] = {
+ [COLD_RESET] = "reset",
+ [EXIT_FAILURE] = "exit",
+ };
+ if (action == EXIT_SUCCESS)
+ return;
+
+ console_print(L"Waiting to %a...", reasons[action]);
+ for (size_t sleepcount = 0; sleepcount < 10; sleepcount++) {
+ console_print(L"%d...", 10 - sleepcount);
+ msleep(1000000);
+ }
+ console_print(L"\ndoing %a\n", action);
+
+ if (action == COLD_RESET)
+ gRT->ResetSystem(EfiResetCold, EFI_SECURITY_VIOLATION, 0, NULL);
+#endif
+}
+
EFI_STATUS
efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
{
@@ -2570,17 +1879,32 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
verification_method = VERIFIED_BY_NOTHING;
- vendor_cert_size = cert_table.vendor_cert_size;
- vendor_dbx_size = cert_table.vendor_dbx_size;
- vendor_cert = (UINT8 *)&cert_table + cert_table.vendor_cert_offset;
- vendor_dbx = (UINT8 *)&cert_table + cert_table.vendor_dbx_offset;
+ vendor_authorized_size = cert_table.vendor_authorized_size;
+ vendor_authorized = (UINT8 *)&cert_table + cert_table.vendor_authorized_offset;
+
+ vendor_deauthorized_size = cert_table.vendor_deauthorized_size;
+ vendor_deauthorized = (UINT8 *)&cert_table + cert_table.vendor_deauthorized_offset;
+
+#if defined(ENABLE_SHIM_CERT)
+ build_cert_size = sizeof(shim_cert);
+ build_cert = shim_cert;
+#endif /* defined(ENABLE_SHIM_CERT) */
+
CHAR16 *msgs[] = {
- L"import_mok_state() failed\n",
- L"shim_int() failed\n",
+ L"import_mok_state() failed",
+ L"shim_init() failed",
+ L"import of SBAT data failed",
+ L"SBAT self-check failed",
+ L"SBAT UEFI variable setting failed",
NULL
};
- int msg = 0;
-
+ enum {
+ IMPORT_MOK_STATE,
+ SHIM_INIT,
+ IMPORT_SBAT,
+ SBAT_SELF_CHECK,
+ SET_SBAT,
+ } msg = IMPORT_MOK_STATE;
/*
* Set up the shim lock protocol so that grub and MokManager can
@@ -2597,31 +1921,83 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
* Ensure that gnu-efi functions are available
*/
InitializeLib(image_handle, systab);
+ setup_verbosity();
- init_openssl();
+ dprint(L"vendor_authorized:0x%08lx vendor_authorized_size:%lu\n",
+ vendor_authorized, vendor_authorized_size);
+ dprint(L"vendor_deauthorized:0x%08lx vendor_deauthorized_size:%lu\n",
+ vendor_deauthorized, vendor_deauthorized_size);
/*
* if SHIM_DEBUG is set, wait for a debugger to attach.
*/
debug_hook();
+ efi_status = set_sbat_uefi_variable();
+ if (EFI_ERROR(efi_status) && secure_mode()) {
+ perror(L"SBAT variable initialization failed\n");
+ msg = SET_SBAT;
+ goto die;
+ } else if (EFI_ERROR(efi_status)) {
+ dprint(L"SBAT variable initialization failed: %r\n",
+ efi_status);
+ }
+
+ if (secure_mode()) {
+ char *sbat_start = (char *)&_sbat;
+ char *sbat_end = (char *)&_esbat;
+
+ INIT_LIST_HEAD(&sbat_var);
+ efi_status = parse_sbat_var(&sbat_var);
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Parsing SBAT variable failed: %r\n",
+ efi_status);
+ msg = IMPORT_SBAT;
+ goto die;
+ }
+
+ efi_status = handle_sbat(sbat_start, sbat_end - sbat_start);
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Verifiying shim SBAT data failed: %r\n",
+ efi_status);
+ msg = SBAT_SELF_CHECK;
+ goto die;
+ }
+ }
+
+ init_openssl();
+
/*
* Before we do anything else, validate our non-volatile,
* boot-services-only state variables are what we think they are.
*/
efi_status = import_mok_state(image_handle);
- if (EFI_ERROR(efi_status)) {
+ if (!secure_mode() && efi_status == EFI_INVALID_PARAMETER) {
+ /*
+ * Make copy failures fatal only if secure_mode is enabled, or
+ * the error was anything else than EFI_INVALID_PARAMETER.
+ * There are non-secureboot firmware implementations that don't
+ * reserve enough EFI variable memory to fit the variable.
+ */
+ console_print(L"Importing MOK states has failed: %s: %r\n",
+ msgs[msg], efi_status);
+ console_print(L"Continuing boot since secure mode is disabled");
+ } else if (EFI_ERROR(efi_status)) {
die:
console_print(L"Something has gone seriously wrong: %s: %r\n",
msgs[msg], efi_status);
+#if defined(ENABLE_SHIM_DEVEL)
+ devel_egress(COLD_RESET);
+#else
msleep(5000000);
gRT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION,
0, NULL);
+#endif
}
efi_status = shim_init();
if (EFI_ERROR(efi_status)) {
- msg = 1;
+ msg = SHIM_INIT;
goto die;
}
@@ -2639,5 +2015,6 @@ die:
efi_status = init_grub(image_handle);
shim_fini();
+ devel_egress(EFI_ERROR(efi_status) ? EXIT_FAILURE : EXIT_SUCCESS);
return efi_status;
}