diff options
author | Steve Langasek <steve.langasek@canonical.com> | 2019-02-09 21:28:06 -0800 |
---|---|---|
committer | Steve Langasek <steve.langasek@canonical.com> | 2019-02-09 21:32:44 -0800 |
commit | ab4c731c1dd379acd3e95971af57401fb0a650a1 (patch) | |
tree | 6a26fb8d0746cbbaa6c2d4b242c73442bcc1df06 /shim.c | |
parent | 0d63079c7da8e86104ce4bbdae2f6cb8d2ea40c6 (diff) | |
parent | 9c12130f9cd2ae11a9336813dd1f1669c0b64ad0 (diff) | |
download | efi-boot-shim-debian/15+1533136590.3beb971-1.tar.gz efi-boot-shim-debian/15+1533136590.3beb971-1.zip |
* New upstream release.debian/15+1533136590.3beb971-1
- debian/patches/second-stage-path: dropped; the default loader path now
includes an arch suffix.
- debian/patches/sbsigntool-no-pesign: dropped; no longer needed.
* Drop remaining patches that were not being applied.
* Sync packaging from Ubuntu:
- debian/copyright: Update upstream source location.
- debian/control: add a Build-Depends on libelf-dev.
- Enable arm64 build.
- debian/patches/fixup_git.patch: don't run git in clean; we're not
really in a git tree.
- debian/rules, debian/shim.install: use the upstream install target as
intended, and move files to the target directory using dh_install.
- define RELEASE and COMMIT_ID for the snapshot.
- Set ENABLE_HTTPBOOT to enable the HTTP Boot feature.
- Update dh_auto_build/dh_auto_clean/dh_auto_install for new upstream
options: set MAKELEVEL.
- Define an EFI_ARCH variable, and use that for paths to shim. This
makes it possible to build a shim for other architectures than amd64.
- Set EFIDIR=$distro for dh_auto_install; that will let files be installed
in the "right" final directories, and makes boot.csv for us.
- Set ENABLE_SHIM_CERT, to keep using ephemeral self-signed certs built
at compile-time for MokManager and fallback.
- Set ENABLE_SBSIGN, to use sbsign instead of pesign for signing fallback
and MokManager.
Diffstat (limited to 'shim.c')
-rw-r--r-- | shim.c | 1309 |
1 files changed, 606 insertions, 703 deletions
@@ -33,51 +33,35 @@ * Corporation. */ -#include <efi.h> -#include <efilib.h> -#include <Library/BaseCryptLib.h> -#include "PeImage.h" #include "shim.h" -#include "netboot.h" -#include "httpboot.h" -#include "shim_cert.h" -#include "replacements.h" -#include "tpm.h" -#include "ucs2.h" - -#include "guid.h" -#include "variables.h" -#include "efiauthenticated.h" -#include "security_policy.h" -#include "console.h" -#include "version.h" #include <stdarg.h> + +#include <openssl/err.h> +#include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/ocsp.h> +#include <openssl/pkcs12.h> +#include <openssl/rand.h> +#include <openssl/crypto.h> +#include <openssl/ssl.h> #include <openssl/x509.h> #include <openssl/x509v3.h> +#include <openssl/rsa.h> +#include <internal/dso.h> + +#include <Library/BaseCryptLib.h> -#define FALLBACK L"\\fb" EFI_ARCH L".efi" -#define MOK_MANAGER L"\\mm" EFI_ARCH L".efi" +#include <stdint.h> #define OID_EKU_MODSIGN "1.3.6.1.4.1.2312.16.1.2" static EFI_SYSTEM_TABLE *systab; -static EFI_HANDLE image_handle; -static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table); +static EFI_HANDLE global_image_handle; static CHAR16 *second_stage; static void *load_options; static UINT32 load_options_size; -static UINT8 in_protocol; - -#define perror(fmt, ...) ({ \ - UINTN __perror_ret = 0; \ - if (!in_protocol) \ - __perror_ret = Print((fmt), ##__VA_ARGS__); \ - __perror_ret; \ - }) - -EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; /* * The vendor certificate used for validating the second stage loader @@ -119,11 +103,17 @@ typedef struct { /* * Perform basic bounds checking of the intra-image pointers */ -static void *ImageAddress (void *image, unsigned int size, unsigned int address) +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; } @@ -237,7 +227,7 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; UINT64 Adjust; UINT16 *Reloc, *RelocEnd; - char *Fixup, *FixupBase, *FixupData = NULL; + char *Fixup, *FixupBase; UINT16 *Fixup16; UINT32 *Fixup32; UINT64 *Fixup64; @@ -245,11 +235,6 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, void *ImageEnd = (char *)orig + size; int n = 0; - if (image_is_64_bit(context->PEHdr)) - context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)(unsigned long)data; - else - context->PEHdr->Pe32.OptionalHeader.ImageBase = (UINT32)(unsigned long)data; - /* Alright, so here's how this works: * * context->RelocDir gives us two things: @@ -297,8 +282,14 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, while (RelocBase < RelocBaseEnd) { Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); - if ((RelocBase->SizeOfBlock == 0) || (RelocBase->SizeOfBlock > context->RelocDir->Size)) { - perror(L"Reloc %d block size %d is invalid\n", n, RelocBase->SizeOfBlock); + 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; } @@ -323,39 +314,21 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, 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: @@ -371,6 +344,14 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, return EFI_SUCCESS; } +static void +drain_openssl_errors(void) +{ + unsigned long err = -1; + while (err != 0) + err = ERR_get_error(); +} + static BOOLEAN verify_x509(UINT8 *Cert, UINTN CertSize) { UINTN length; @@ -422,34 +403,59 @@ static BOOLEAN verify_eku(UINT8 *Cert, UINTN CertSize) X509_free(x509); } - OBJ_cleanup(); - return TRUE; } +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); +} + static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList, UINTN dbsize, WIN_CERTIFICATE_EFI_PKCS *data, - UINT8 *hash) + UINT8 *hash, CHAR16 *dbname, + EFI_GUID guid) { EFI_SIGNATURE_DATA *Cert; UINTN CertSize; BOOLEAN IsFound = FALSE; - EFI_GUID CertType = X509_GUID; while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { - if (CompareGuid (&CertList->SignatureType, &CertType) == 0) { + 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); if (verify_x509(Cert->SignatureData, CertSize)) { if (verify_eku(Cert->SignatureData, CertSize)) { + clear_ca_warning(); IsFound = AuthenticodeVerify (data->CertData, data->Hdr.dwLength - sizeof(data->Hdr), Cert->SignatureData, CertSize, hash, SHA256_DIGEST_SIZE); - if (IsFound) + if (IsFound) { + if (get_ca_warning()) { + show_ca_warning(); + } + tpm_measure_variable(dbname, guid, CertSize, Cert->SignatureData); + drain_openssl_errors(); return DATA_FOUND; + } else { + LogError(L"AuthenticodeVerify(): %d\n", IsFound); + } } } else if (verbose) { console_notify(L"Not a DER encoding x.509 Certificate"); @@ -473,13 +479,12 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid, UINT8 *db; efi_status = get_variable(dbname, &db, &dbsize, guid); - - if (efi_status != EFI_SUCCESS) + if (EFI_ERROR(efi_status)) return VAR_NOT_FOUND; CertList = (EFI_SIGNATURE_LIST *)db; - rc = check_db_cert_in_ram(CertList, dbsize, data, hash); + rc = check_db_cert_in_ram(CertList, dbsize, data, hash, dbname, guid); FreePool(db); @@ -491,7 +496,8 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid, */ static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList, UINTN dbsize, UINT8 *data, - int SignatureSize, EFI_GUID CertType) + int SignatureSize, EFI_GUID CertType, + CHAR16 *dbname, EFI_GUID guid) { EFI_SIGNATURE_DATA *Cert; UINTN CertCount, Index; @@ -507,6 +513,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); break; } @@ -539,15 +546,15 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data, UINT8 *db; efi_status = get_variable(dbname, &db, &dbsize, guid); - - if (efi_status != EFI_SUCCESS) { + if (EFI_ERROR(efi_status)) { return VAR_NOT_FOUND; } CertList = (EFI_SIGNATURE_LIST *)db; CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data, - SignatureSize, CertType); + SignatureSize, CertType, + dbname, guid); FreePool(db); return rc; @@ -560,40 +567,55 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data, static EFI_STATUS check_blacklist (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; EFI_SIGNATURE_LIST *dbx = (EFI_SIGNATURE_LIST *)vendor_dbx; if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha256hash, - SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == - DATA_FOUND) - return EFI_ACCESS_DENIED; - if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha1hash, - SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID) == - DATA_FOUND) - return EFI_ACCESS_DENIED; - if (cert && check_db_cert_in_ram(dbx, vendor_dbx_size, cert, - sha256hash) == DATA_FOUND) - return EFI_ACCESS_DENIED; - - if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE, - EFI_CERT_SHA256_GUID) == DATA_FOUND) - return EFI_ACCESS_DENIED; - if (check_db_hash(L"dbx", secure_var, sha1hash, SHA1_DIGEST_SIZE, - EFI_CERT_SHA1_GUID) == DATA_FOUND) - return EFI_ACCESS_DENIED; - if (cert && check_db_cert(L"dbx", secure_var, cert, sha256hash) == - DATA_FOUND) - return EFI_ACCESS_DENIED; - if (check_db_hash(L"MokListX", shim_var, sha256hash, SHA256_DIGEST_SIZE, - EFI_CERT_SHA256_GUID) == DATA_FOUND) { - return EFI_ACCESS_DENIED; - } - if (cert && check_db_cert(L"MokListX", shim_var, cert, sha256hash) == - DATA_FOUND) { - return EFI_ACCESS_DENIED; + 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, + 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", + EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { + LogError(L"cert sha256hash found in vendor dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (check_db_hash(L"dbx", EFI_SECURE_BOOT_DB_GUID, sha256hash, + SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == DATA_FOUND) { + LogError(L"binary sha256hash found in system dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (check_db_hash(L"dbx", EFI_SECURE_BOOT_DB_GUID, sha1hash, + SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID) == DATA_FOUND) { + LogError(L"binary sha1hash found in system dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (cert && + check_db_cert(L"dbx", EFI_SECURE_BOOT_DB_GUID, + cert, sha256hash) == DATA_FOUND) { + LogError(L"cert sha256hash found in system dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (check_db_hash(L"MokListX", SHIM_LOCK_GUID, sha256hash, + SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == DATA_FOUND) { + LogError(L"binary sha256hash found in Mok dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (cert && + check_db_cert(L"MokListX", SHIM_LOCK_GUID, + cert, sha256hash) == DATA_FOUND) { + LogError(L"cert sha256hash found in Mok dbx\n"); + return EFI_SECURITY_VIOLATION; + } + + drain_openssl_errors(); return EFI_SUCCESS; } @@ -609,44 +631,52 @@ static void update_verification_method(verification_method_t method) 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 (!ignore_db) { - if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE, + if (check_db_hash(L"db", EFI_SECURE_BOOT_DB_GUID, sha256hash, SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == DATA_FOUND) { update_verification_method(VERIFIED_BY_HASH); return EFI_SUCCESS; + } else { + LogError(L"check_db_hash(db, sha256hash) != DATA_FOUND\n"); } - if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE, + if (check_db_hash(L"db", EFI_SECURE_BOOT_DB_GUID, sha1hash, SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID) == DATA_FOUND) { verification_method = VERIFIED_BY_HASH; update_verification_method(VERIFIED_BY_HASH); return EFI_SUCCESS; + } else { + LogError(L"check_db_hash(db, sha1hash) != DATA_FOUND\n"); } - if (cert && check_db_cert(L"db", secure_var, cert, sha256hash) + if (cert && check_db_cert(L"db", EFI_SECURE_BOOT_DB_GUID, cert, sha256hash) == DATA_FOUND) { verification_method = VERIFIED_BY_CERT; update_verification_method(VERIFIED_BY_CERT); return EFI_SUCCESS; + } else { + LogError(L"check_db_cert(db, sha256hash) != DATA_FOUND\n"); } } - if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE, - EFI_CERT_SHA256_GUID) == DATA_FOUND) { + if (check_db_hash(L"MokList", SHIM_LOCK_GUID, sha256hash, + SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) + == DATA_FOUND) { verification_method = VERIFIED_BY_HASH; update_verification_method(VERIFIED_BY_HASH); return EFI_SUCCESS; + } else { + LogError(L"check_db_hash(MokList, sha256hash) != DATA_FOUND\n"); } - if (cert && check_db_cert(L"MokList", shim_var, cert, sha256hash) == - DATA_FOUND) { + if (cert && check_db_cert(L"MokList", SHIM_LOCK_GUID, cert, sha256hash) + == DATA_FOUND) { verification_method = VERIFIED_BY_CERT; update_verification_method(VERIFIED_BY_CERT); return EFI_SUCCESS; + } else { + LogError(L"check_db_cert(MokList, sha256hash) != DATA_FOUND\n"); } update_verification_method(VERIFIED_BY_NOTHING); - return EFI_ACCESS_DENIED; + return EFI_SECURITY_VIOLATION; } /* @@ -686,12 +716,14 @@ static BOOLEAN secure_mode (void) #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; \ @@ -718,15 +750,11 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, unsigned int datasize; EFI_IMAGE_SECTION_HEADER *Section; EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL; - EFI_STATUS status = EFI_SUCCESS; + EFI_STATUS efi_status = EFI_SUCCESS; EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data; unsigned int PEHdr_offset = 0; - if (datasize_in < 0) { - perror(L"Invalid data size\n"); - return EFI_INVALID_PARAMETER; - } - size = datasize = (unsigned int)datasize_in; + size = datasize = datasize_in; if (datasize <= sizeof (*DosHdr) || DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) { @@ -748,7 +776,7 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) { perror(L"Unable to initialise hash\n"); - status = EFI_OUT_OF_RESOURCES; + efi_status = EFI_OUT_OF_RESOURCES; goto done; } @@ -761,7 +789,7 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { perror(L"Unable to generate hash\n"); - status = EFI_OUT_OF_RESOURCES; + efi_status = EFI_OUT_OF_RESOURCES; goto done; } @@ -774,7 +802,7 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { perror(L"Unable to generate hash\n"); - status = EFI_OUT_OF_RESOURCES; + efi_status = EFI_OUT_OF_RESOURCES; goto done; } @@ -784,7 +812,7 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, hashsize = context->SizeOfHeaders - (unsigned long)((char *)dd - data); if (hashsize > datasize_in) { perror(L"Data Directory size %d is invalid\n", hashsize); - status = EFI_INVALID_PARAMETER; + efi_status = EFI_INVALID_PARAMETER; goto done; } check_size(data, datasize_in, hashbase, hashsize); @@ -792,7 +820,7 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { perror(L"Unable to generate hash\n"); - status = EFI_OUT_OF_RESOURCES; + efi_status = EFI_OUT_OF_RESOURCES; goto done; } @@ -812,14 +840,14 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, (index * sizeof(*SectionPtr))); if (!SectionPtr) { perror(L"Malformed section %d\n", index); - status = EFI_INVALID_PARAMETER; + 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); - status = EFI_INVALID_PARAMETER; + efi_status = EFI_INVALID_PARAMETER; goto done; } SumOfSectionBytes += SectionPtr->SizeOfRawData; @@ -828,7 +856,7 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, 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"); - status = EFI_OUT_OF_RESOURCES; + efi_status = EFI_OUT_OF_RESOURCES; goto done; } @@ -838,6 +866,19 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, 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++) { @@ -860,7 +901,7 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, if (!hashbase) { perror(L"Malformed section header\n"); - status = EFI_INVALID_PARAMETER; + efi_status = EFI_INVALID_PARAMETER; goto done; } @@ -868,7 +909,7 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, if (Section->SizeOfRawData > datasize - Section->PointerToRawData) { perror(L"Malformed section raw size %d\n", index); - status = EFI_INVALID_PARAMETER; + efi_status = EFI_INVALID_PARAMETER; goto done; } hashsize = (unsigned int) Section->SizeOfRawData; @@ -877,30 +918,64 @@ static EFI_STATUS generate_hash (char *data, unsigned int datasize_in, if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { perror(L"Unable to generate hash\n"); - status = EFI_OUT_OF_RESOURCES; + 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 - context->SecDir->Size - 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"); - status = EFI_OUT_OF_RESOURCES; + 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"); - status = EFI_OUT_OF_RESOURCES; + efi_status = EFI_OUT_OF_RESOURCES; goto done; } @@ -912,48 +987,23 @@ done: 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; - UINT8 *MokListData = NULL; - UINTN MokListDataSize = 0; - UINT32 attributes; - - status = get_variable_attr(L"MokList", &MokListData, &MokListDataSize, - shim_lock_guid, &attributes); - - if (!EFI_ERROR(status) && attributes & EFI_VARIABLE_RUNTIME_ACCESS) { - perror(L"MokList is compromised!\nErase all keys in MokList!\n"); - if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) { - perror(L"Failed to erase MokList\n"); - return EFI_ACCESS_DENIED; - } - } - - if (MokListData) - FreePool(MokListData); - - return EFI_SUCCESS; + 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) + PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash) { - UINT8 sha256hash[SHA256_DIGEST_SIZE]; - UINT8 sha1hash[SHA1_DIGEST_SIZE]; - EFI_STATUS status = EFI_ACCESS_DENIED; + 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"); @@ -981,64 +1031,94 @@ static EFI_STATUS verify_buffer (char *data, int datasize, } } - status = generate_hash(data, datasize, context, sha256hash, sha1hash); - if (status != EFI_SUCCESS) - return status; - /* - * Check that the MOK database hasn't been modified + * 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. */ - status = verify_mok(); - if (status != EFI_SUCCESS) - return status; + drain_openssl_errors(); + + efi_status = generate_hash(data, datasize, context, sha256hash, sha1hash); + if (EFI_ERROR(efi_status)) { + LogError(L"generate_hash: %r\n", efi_status); + return efi_status; + } /* * Ensure that the binary isn't blacklisted */ - status = check_blacklist(cert, sha256hash, sha1hash); - - if (status != EFI_SUCCESS) { + efi_status = check_blacklist(cert, sha256hash, sha1hash); + if (EFI_ERROR(efi_status)) { perror(L"Binary is blacklisted\n"); - return status; + LogError(L"Binary is blacklisted: %r\n", efi_status); + return efi_status; } /* * Check whether the binary is whitelisted in any of the firmware * databases */ - status = check_whitelist(cert, sha256hash, sha1hash); - if (status == EFI_SUCCESS) - return status; + efi_status = check_whitelist(cert, sha256hash, sha1hash); + if (EFI_ERROR(efi_status)) { + LogError(L"check_whitelist(): %r\n", efi_status); + } else { + drain_openssl_errors(); + return efi_status; + } 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)) { - status = EFI_SUCCESS; - return status; + 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)) { - status = EFI_SUCCESS; - return status; + 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"); } } - status = EFI_ACCESS_DENIED; - - return status; + LogError(L"Binary is not whitelisted\n"); + crypterr(EFI_SECURITY_VIOLATION); + PrintErrors(); + efi_status = EFI_SECURITY_VIOLATION; + return efi_status; } /* @@ -1165,7 +1245,9 @@ static EFI_STATUS read_header(void *data, unsigned int datasize, return EFI_UNSUPPORTED; } - if (context->SecDir->VirtualAddress >= datasize) { + if (context->SecDir->VirtualAddress > datasize || + (context->SecDir->VirtualAddress == datasize && + context->SecDir->Size > 0)) { perror(L"Malformed security header\n"); return EFI_INVALID_PARAMETER; } @@ -1176,7 +1258,10 @@ static EFI_STATUS read_header(void *data, unsigned int datasize, * 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_LOADED_IMAGE *li, + EFI_IMAGE_ENTRY_POINT *entry_point, + EFI_PHYSICAL_ADDRESS *alloc_address, + UINTN *alloc_pages) { EFI_STATUS efi_status; char *buffer; @@ -1184,14 +1269,16 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, EFI_IMAGE_SECTION_HEADER *Section; char *base, *end; PE_COFF_LOADER_IMAGE_CONTEXT context; - unsigned int alignment; + 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 */ efi_status = read_header(data, datasize, &context); - if (efi_status != EFI_SUCCESS) { + if (EFI_ERROR(efi_status)) { perror(L"Failed to read header: %r\n", efi_status); return efi_status; } @@ -1199,15 +1286,32 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, /* * We only need to verify the binary if we're in secure mode */ + 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; + } +#endif + if (secure_mode ()) { - efi_status = verify_buffer(data, datasize, &context); + 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_notify(L"Verification succeeded"); + console_print(L"Verification succeeded\n"); } } @@ -1229,20 +1333,25 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, if (!alignment) alignment = 4096; - buffer = AllocatePool(context.ImageSize + context.SectionAlignment); - buffer = ALIGN_POINTER(buffer, alignment); + alloc_size = ALIGN_VALUE(context.ImageSize + context.SectionAlignment, + PAGE_SIZE); + *alloc_pages = alloc_size / PAGE_SIZE; - if (!buffer) { + 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; } + 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) { + *entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); + if (!*entry_point) { perror(L"Entry point is invalid\n"); - FreePool(buffer); + gBS->FreePages(*alloc_address, *alloc_pages); return EFI_UNSUPPORTED; } @@ -1276,7 +1385,7 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, if (end < base) { perror(L"Section %d has negative size\n", i); - FreePool(buffer); + gBS->FreePages(*alloc_address, *alloc_pages); return EFI_UNSUPPORTED; } @@ -1355,7 +1464,7 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, efi_status = relocate_coff(&context, RelocSection, data, buffer); - if (efi_status != EFI_SUCCESS) { + if (EFI_ERROR(efi_status)) { perror(L"Relocation failed: %r\n", efi_status); FreePool(buffer); return efi_status; @@ -1370,8 +1479,10 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, li->ImageSize = context.ImageSize; /* Pass the load options to the second stage loader */ - li->LoadOptions = load_options; - li->LoadOptionsSize = load_options_size; + if ( load_options ) { + li->LoadOptions = load_options; + li->LoadOptionsSize = load_options_size; + } if (!found_entry_point) { perror(L"Entry point is not within sections\n"); @@ -1388,20 +1499,20 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, 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 = NULL; EFI_FILE_IO_INTERFACE *fio = NULL; EFI_FILE *vh = NULL; EFI_FILE *fh = NULL; - EFI_STATUS rc; + EFI_STATUS efi_status; int ret = 0; - rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, - &loaded_image_protocol, (void **)&li); - if (EFI_ERROR(rc)) { - perror(L"Could not get image for bootx64.efi: %r\n", rc); + efi_status = gBS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, + (void **)&li); + if (EFI_ERROR(efi_status)) { + perror(L"Could not get image for bootx64.efi: %r\n", + efi_status); return 0; } @@ -1422,27 +1533,28 @@ should_use_fallback(EFI_HANDLE image_handle) if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) goto error; - rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle, - &FileSystemProtocol, (void **)&fio); - if (EFI_ERROR(rc)) { - perror(L"Could not get fio for li->DeviceHandle: %r\n", rc); + efi_status = gBS->HandleProtocol(li->DeviceHandle, &FileSystemProtocol, + (void **) &fio); + if (EFI_ERROR(efi_status)) { + perror(L"Could not get fio for li->DeviceHandle: %r\n", + efi_status); goto error; } - rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &vh); - if (EFI_ERROR(rc)) { - perror(L"Could not open fio volume: %r\n", rc); + efi_status = fio->OpenVolume(fio, &vh); + if (EFI_ERROR(efi_status)) { + perror(L"Could not open fio volume: %r\n", efi_status); goto error; } - rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK, - EFI_FILE_MODE_READ, 0); - if (EFI_ERROR(rc)) { + efi_status = vh->Open(vh, &fh, L"\\EFI\\BOOT" FALLBACK, + EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(efi_status)) { /* Do not print the error here - this is an acceptable case * for removable media, where we genuinely don't want * fallback.efi to exist. - * Print(L"Could not open \"\\EFI\\BOOT%s\": %d\n", FALLBACK, - * rc); + * Print(L"Could not open \"\\EFI\\BOOT%s\": %r\n", FALLBACK, + * efi_status); */ goto error; } @@ -1450,9 +1562,9 @@ should_use_fallback(EFI_HANDLE image_handle) ret = 1; error: if (fh) - uefi_call_wrapper(fh->Close, 1, fh); + fh->Close(fh); if (vh) - uefi_call_wrapper(vh->Close, 1, vh); + vh->Close(vh); if (bootpath) FreePool(bootpath); @@ -1463,8 +1575,9 @@ error: * 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, - CHAR16 **PathName) +static EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li, + CHAR16 *ImagePath, + CHAR16 **PathName) { EFI_DEVICE_PATH *devpath; unsigned int i; @@ -1555,8 +1668,6 @@ error: 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; @@ -1569,18 +1680,15 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, /* * Open the device */ - efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, - &simple_file_system_protocol, - (void **)&drive); - - if (efi_status != EFI_SUCCESS) { + efi_status = gBS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID, + (void **) &drive); + if (EFI_ERROR(efi_status)) { perror(L"Failed to find fs: %r\n", efi_status); goto error; } - efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); - - if (efi_status != EFI_SUCCESS) { + efi_status = drive->OpenVolume(drive, &root); + if (EFI_ERROR(efi_status)) { perror(L"Failed to open fs: %r\n", efi_status); goto error; } @@ -1588,10 +1696,8 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, /* * 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) { + efi_status = root->Open(root, &grub, PathName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(efi_status)) { perror(L"Failed to open %s - %r\n", PathName, efi_status); goto error; } @@ -1608,9 +1714,8 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, * 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); - + efi_status = grub->GetInfo(grub, &EFI_FILE_INFO_GUID, &buffersize, + fileinfo); if (efi_status == EFI_BUFFER_TOO_SMALL) { FreePool(fileinfo); fileinfo = AllocatePool(buffersize); @@ -1619,20 +1724,17 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, efi_status = EFI_OUT_OF_RESOURCES; goto error; } - efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, - &file_info_id, &buffersize, - fileinfo); + efi_status = grub->GetInfo(grub, &EFI_FILE_INFO_GUID, + &buffersize, fileinfo); } - if (efi_status != EFI_SUCCESS) { + if (EFI_ERROR(efi_status)) { perror(L"Unable to get file info: %r\n", efi_status); goto error; } buffersize = fileinfo->FileSize; - *data = AllocatePool(buffersize); - if (!*data) { perror(L"Unable to allocate file buffer\n"); efi_status = EFI_OUT_OF_RESOURCES; @@ -1642,18 +1744,15 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, /* * Perform the actual read */ - efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, - *data); - + efi_status = grub->Read(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); + efi_status = grub->Read(grub, &buffersize, *data); } - - if (efi_status != EFI_SUCCESS) { - perror(L"Unexpected return from initial read: %r, buffersize %x\n", efi_status, buffersize); + if (EFI_ERROR(efi_status)) { + perror(L"Unexpected return from initial read: %r, buffersize %x\n", + efi_status, buffersize); goto error; } @@ -1679,48 +1778,75 @@ error: */ EFI_STATUS shim_verify (void *buffer, UINT32 size) { - EFI_STATUS status = EFI_SUCCESS; + EFI_STATUS efi_status = EFI_SUCCESS; PE_COFF_LOADER_IMAGE_CONTEXT context; + UINT8 sha1hash[SHA1_DIGEST_SIZE]; + UINT8 sha256hash[SHA256_DIGEST_SIZE]; + + if ((INT32)size < 0) + return EFI_INVALID_PARAMETER; loader_is_participating = 1; in_protocol = 1; - if (!secure_mode()) + efi_status = read_header(buffer, size, &context); + if (EFI_ERROR(efi_status)) goto done; - status = read_header(buffer, size, &context); - if (status != EFI_SUCCESS) + efi_status = generate_hash(buffer, size, &context, + sha256hash, sha1hash); + if (EFI_ERROR(efi_status)) goto done; - status = verify_buffer(buffer, size, &context); + /* Measure the binary into the TPM */ +#ifdef REQUIRE_TPM + efi_status = +#endif + tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)buffer, size, sha1hash, 4); +#ifdef REQUIRE_TPM + if (EFI_ERROR(efi_status)) + goto done; +#endif + + if (!secure_mode()) { + efi_status = EFI_SUCCESS; + goto done; + } + + efi_status = verify_buffer(buffer, size, &context, + sha256hash, sha1hash); done: in_protocol = 0; - return status; + return efi_status; } static EFI_STATUS shim_hash (char *data, int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context, UINT8 *sha256hash, UINT8 *sha1hash) { - EFI_STATUS status; + EFI_STATUS efi_status; + + if (datasize < 0) + return EFI_INVALID_PARAMETER; in_protocol = 1; - status = generate_hash(data, datasize, context, sha256hash, sha1hash); + efi_status = generate_hash(data, datasize, context, + sha256hash, sha1hash); in_protocol = 0; - return status; + return efi_status; } static EFI_STATUS shim_read_header(void *data, unsigned int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context) { - EFI_STATUS status; + EFI_STATUS efi_status; in_protocol = 1; - status = read_header(data, datasize, context); + efi_status = read_header(data, datasize, context); in_protocol = 0; - return status; + return efi_status; } /* @@ -1728,9 +1854,11 @@ static EFI_STATUS shim_read_header(void *data, unsigned int datasize, */ 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_IMAGE_ENTRY_POINT entry_point; + EFI_PHYSICAL_ADDRESS alloc_address; + UINTN alloc_pages; CHAR16 *PathName = NULL; void *sourcebuffer = NULL; UINT64 sourcesize = 0; @@ -1741,10 +1869,9 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) * 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) { + efi_status = gBS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, + (void **)&li); + if (EFI_ERROR(efi_status)) { perror(L"Unable to init protocol\n"); return efi_status; } @@ -1752,33 +1879,36 @@ 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(li, ImagePath, &PathName); - - if (efi_status != EFI_SUCCESS) { - perror(L"Unable to generate path %s: %r\n", ImagePath, efi_status); + efi_status = generate_path_from_image_path(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)) { efi_status = parseNetbootinfo(image_handle); - if (efi_status != EFI_SUCCESS) { + if (EFI_ERROR(efi_status)) { perror(L"Netboot parsing failed: %r\n", efi_status); return EFI_PROTOCOL_ERROR; } efi_status = FetchNetbootimage(image_handle, &sourcebuffer, &sourcesize); - if (efi_status != EFI_SUCCESS) { - perror(L"Unable to fetch TFTP image: %r\n", efi_status); + if (EFI_ERROR(efi_status)) { + perror(L"Unable to fetch TFTP image: %r\n", + efi_status); return efi_status; } data = sourcebuffer; datasize = sourcesize; #if defined(ENABLE_HTTPBOOT) } else if (find_httpboot(li->DeviceHandle)) { - efi_status = httpboot_fetch_buffer (image_handle, &sourcebuffer, + efi_status = httpboot_fetch_buffer (image_handle, + &sourcebuffer, &sourcesize); - if (efi_status != EFI_SUCCESS) { - perror(L"Unable to fetch HTTP image: %r\n", efi_status); + if (EFI_ERROR(efi_status)) { + perror(L"Unable to fetch HTTP image: %r\n", + efi_status); return efi_status; } data = sourcebuffer; @@ -1789,16 +1919,19 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) * Read the new executable off disk */ efi_status = load_image(li, &data, &datasize, PathName); - - if (efi_status != EFI_SUCCESS) { - perror(L"Failed to load image %s: %r\n", PathName, efi_status); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to load image %s: %r\n", + PathName, efi_status); + PrintErrors(); + ClearErrors(); goto done; } } - /* Measure the binary into the TPM */ - tpm_log_event((EFI_PHYSICAL_ADDRESS)data, datasize, 9, - (CHAR8 *)"Second stage bootloader"); + if (datasize < 0) { + efi_status = EFI_INVALID_PARAMETER; + goto done; + } /* * We need to modify the loaded image protocol entry before running @@ -1809,10 +1942,12 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) /* * Verify and, if appropriate, relocate and execute the executable */ - efi_status = handle_image(data, datasize, li); - - if (efi_status != EFI_SUCCESS) { + efi_status = handle_image(data, datasize, 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; } @@ -1822,7 +1957,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) /* * The binary is trusted and relocated. Run it */ - efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab); + efi_status = entry_point(image_handle, systab); /* * Restore our original loaded image values @@ -1848,12 +1983,12 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) int use_fb = should_use_fallback(image_handle); efi_status = start_image(image_handle, use_fb ? FALLBACK :second_stage); - - if (efi_status == EFI_SECURITY_VIOLATION) { + if (efi_status == EFI_SECURITY_VIOLATION || + efi_status == EFI_ACCESS_DENIED) { efi_status = start_image(image_handle, MOK_MANAGER); - if (efi_status != EFI_SUCCESS) { - Print(L"start_image() returned %r\n", efi_status); - uefi_call_wrapper(BS->Stall, 1, 2000000); + if (EFI_ERROR(efi_status)) { + console_print(L"start_image() returned %r\n", efi_status); + msleep(2000000); return efi_status; } @@ -1861,320 +1996,14 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) use_fb ? FALLBACK : second_stage); } - if (efi_status != EFI_SUCCESS) { - Print(L"start_image() returned %r\n", efi_status); - uefi_call_wrapper(BS->Stall, 1, 2000000); - } - - return efi_status; -} - -/* - * Measure some of the MOK variables into the TPM - */ -EFI_STATUS measure_mok() -{ - EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - EFI_STATUS efi_status; - UINT8 *Data = NULL; - UINTN DataSize = 0; - - efi_status = get_variable(L"MokList", &Data, &DataSize, shim_lock_guid); - if (efi_status != EFI_SUCCESS) - return efi_status; - - efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)Data, DataSize, 14, - (CHAR8 *)"MokList"); - - FreePool(Data); - - if (efi_status != EFI_SUCCESS) - return efi_status; - - efi_status = get_variable(L"MokSBState", &Data, &DataSize, - shim_lock_guid); - - if (efi_status != EFI_SUCCESS) - return efi_status; - - efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)Data, DataSize, 14, - (CHAR8 *)"MokSBState"); - - FreePool(Data); - - 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; - UINT8 *Data = NULL; - UINTN DataSize = 0; - void *FullData = NULL; - UINTN FullDataSize = 0; - EFI_SIGNATURE_LIST *CertList = NULL; - EFI_SIGNATURE_DATA *CertData = NULL; - uint8_t *p = NULL; - - efi_status = get_variable(L"MokList", &Data, &DataSize, shim_lock_guid); - if (efi_status != EFI_SUCCESS) - DataSize = 0; - - if (vendor_cert_size) { - FullDataSize = DataSize - + sizeof (*CertList) - + sizeof (EFI_GUID) - + vendor_cert_size - ; - FullData = AllocatePool(FullDataSize); - if (!FullData) { - perror(L"Failed to allocate space for MokListRT\n"); - return EFI_OUT_OF_RESOURCES; - } - p = FullData; - - if (efi_status == EFI_SUCCESS && DataSize > 0) { - CopyMem(p, Data, DataSize); - p += DataSize; - } - CertList = (EFI_SIGNATURE_LIST *)p; - p += sizeof (*CertList); - CertData = (EFI_SIGNATURE_DATA *)p; - p += sizeof (EFI_GUID); - - CertList->SignatureType = EFI_CERT_X509_GUID; - CertList->SignatureListSize = vendor_cert_size - + sizeof (*CertList) - + sizeof (*CertData) - -1; - CertList->SignatureHeaderSize = 0; - CertList->SignatureSize = vendor_cert_size + sizeof (EFI_GUID); - - CertData->SignatureOwner = SHIM_LOCK_GUID; - CopyMem(p, vendor_cert, vendor_cert_size); - } else { - FullDataSize = DataSize; - FullData = Data; - } - - efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListRT", - &shim_lock_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS - | EFI_VARIABLE_RUNTIME_ACCESS, - FullDataSize, FullData); - if (efi_status != EFI_SUCCESS) { - perror(L"Failed to set MokListRT: %r\n", efi_status); - } - - return efi_status; -} - -/* - * Copy the boot-services only MokListX variable to the runtime-accessible - * MokListXRT variable. It's not marked NV, so the OS can't modify it. - */ -EFI_STATUS mirror_mok_list_x() -{ - EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - EFI_STATUS efi_status; - UINT8 *Data = NULL; - UINTN DataSize = 0; - - efi_status = get_variable(L"MokListX", &Data, &DataSize, shim_lock_guid); - if (efi_status != EFI_SUCCESS) - return efi_status; - - efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListXRT", - &shim_lock_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS - | EFI_VARIABLE_RUNTIME_ACCESS, - DataSize, Data); - if (efi_status != EFI_SUCCESS) { - console_error(L"Failed to set MokListRT", efi_status); - } - - return efi_status; -} - -/* - * Copy the boot-services only MokSBState variable to the runtime-accessible - * MokSBStateRT variable. It's not marked NV, so the OS can't modify it. - */ -EFI_STATUS mirror_mok_sb_state() -{ - EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - EFI_STATUS efi_status; - UINT8 *Data = NULL; - UINTN DataSize = 0; - - efi_status = get_variable(L"MokSBState", &Data, &DataSize, shim_lock_guid); - if (efi_status != EFI_SUCCESS) - return efi_status; - - efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokSBStateRT", - &shim_lock_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS - | EFI_VARIABLE_RUNTIME_ACCESS, - DataSize, Data); - if (efi_status != EFI_SUCCESS) { - console_error(L"Failed to set MokSBStateRT", efi_status); - } - - 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") || check_var(L"MokDB") || - check_var(L"MokXNew") || check_var(L"MokXDel") || - check_var(L"MokXAuth")) { - efi_status = start_image(image_handle, MOK_MANAGER); - - if (efi_status != EFI_SUCCESS) { - perror(L"Failed to start MokManager: %r\n", efi_status); - 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; - UINT8 MokSBState; - UINTN MokSBStateSize = sizeof(MokSBState); - UINT32 attributes; - - user_insecure_mode = 0; - ignore_db = 0; - - status = uefi_call_wrapper(RT->GetVariable, 5, 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) { - perror(L"MokSBState is compromised! Clearing it\n"); - if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) { - perror(L"Failed to erase MokSBState\n"); - } - status = EFI_ACCESS_DENIED; - } else { - if (MokSBState == 1) { - user_insecure_mode = 1; - } - } - - return status; -} - -/* - * Verify that MokDBState is valid, and if appropriate set ignore db mode - */ - -static EFI_STATUS check_mok_db (void) -{ - EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - EFI_STATUS status = EFI_SUCCESS; - UINT8 MokDBState; - UINTN MokDBStateSize = sizeof(MokDBState); - UINT32 attributes; - - status = uefi_call_wrapper(RT->GetVariable, 5, L"MokDBState", &shim_lock_guid, - &attributes, &MokDBStateSize, &MokDBState); - if (status != EFI_SUCCESS) - return EFI_ACCESS_DENIED; - - ignore_db = 0; - - /* - * Delete and ignore the variable if it's been set from or could be - * modified by the OS - */ - if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { - perror(L"MokDBState is compromised! Clearing it\n"); - if (LibDeleteVariable(L"MokDBState", &shim_lock_guid) != EFI_SUCCESS) { - perror(L"Failed to erase MokDBState\n"); - } - status = EFI_ACCESS_DENIED; - } else { - if (MokDBState == 1) { - ignore_db = 1; - } - } - - return status; -} - -static EFI_STATUS mok_ignore_db() -{ - EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - EFI_STATUS efi_status = EFI_SUCCESS; - UINT8 Data = 1; - UINTN DataSize = sizeof(UINT8); - - check_mok_db(); - - if (ignore_db) { - efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokIgnoreDB", - &shim_lock_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS - | EFI_VARIABLE_RUNTIME_ACCESS, - DataSize, (void *)&Data); - if (efi_status != EFI_SUCCESS) { - perror(L"Failed to set MokIgnoreDB: %r\n", efi_status); - } + if (EFI_ERROR(efi_status)) { + console_print(L"start_image() returned %r\n", efi_status); + msleep(2000000); } return efi_status; - } -EFI_GUID bds_guid = { 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } }; - static inline EFI_STATUS get_load_option_optional_data(UINT8 *data, UINTN data_size, UINT8 **od, UINTN *ods) @@ -2285,13 +2114,31 @@ 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) +{ + CHAR16 *dppath = NULL; + int ret = 1; + + dppath = DevicePathToStr(li->FilePath); + if (!dppath) + return 0; + + dprint(L"dppath: %s\n", dppath); + dprint(L"path: %s\n", path); + if (StrnCaseCmp(dppath, path, len)) + ret = 0; + + FreePool(dppath); + return ret; +} + /* * 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; + EFI_STATUS efi_status; + EFI_LOADED_IMAGE *li = NULL; CHAR16 *start = NULL; int remaining_size = 0; CHAR16 *loader_str = NULL; @@ -2302,11 +2149,11 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) load_options = NULL; load_options_size = 0; - status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, - &LoadedImageProtocol, (void **) &li); - if (status != EFI_SUCCESS) { - perror (L"Failed to get load options: %r\n", status); - return status; + efi_status = gBS->HandleProtocol(image_handle, &LoadedImageProtocol, + (void **) &li); + if (EFI_ERROR(efi_status)) { + perror (L"Failed to get load options: %r\n", efi_status); + return efi_status; } /* So, load options are a giant pain in the ass. If we're invoked @@ -2376,11 +2223,17 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) if (li->LoadOptionsSize > 16) { if (CompareGuid((EFI_GUID *)(li->LoadOptions + (li->LoadOptionsSize - 16)), - &bds_guid) == 0) + &BDS_GUID) == 0) li->LoadOptionsSize -= 16; } /* + * Apparently sometimes we get L"\0\0"? Which isn't useful at all. + */ + if (is_all_nuls(li->LoadOptions, li->LoadOptionsSize)) + return EFI_SUCCESS; + + /* * Check and see if this is just a list of strings. If it's an * EFI_LOAD_OPTION, it'll be 0, since we know EndEntire device path * won't pass muster as UCS2-LE. @@ -2398,11 +2251,11 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) * We at least didn't find /enough/ strings. See if it works * as an EFI_LOAD_OPTION. */ - status = get_load_option_optional_data(li->LoadOptions, - li->LoadOptionsSize, - (UINT8 **)&start, - &loader_len); - if (status != EFI_SUCCESS) + efi_status = get_load_option_optional_data(li->LoadOptions, + li->LoadOptionsSize, + (UINT8 **)&start, + &loader_len); + if (EFI_ERROR(efi_status)) return EFI_SUCCESS; remaining_size = 0; @@ -2454,6 +2307,20 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) return EFI_SUCCESS; /* + * 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)) + return EFI_SUCCESS; + + /* * Set up the name of the alternative loader and the LoadOptions for * the loader */ @@ -2469,37 +2336,99 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) loader_str[loader_len/2-1] = L'\0'; second_stage = loader_str; - load_options = remaining_size ? start + loader_len : NULL; + load_options = remaining_size ? start + (loader_len/2) : NULL; load_options_size = remaining_size; } return EFI_SUCCESS; } +static void * +ossl_malloc(size_t num, const char *file, int line) +{ + return AllocatePool(num); +} + +static void +ossl_free(void *addr, const char *file, int line) +{ + FreePool(addr); +} + +static void +init_openssl(void) +{ + CRYPTO_set_mem_functions(ossl_malloc, NULL, ossl_free); + OPENSSL_init(); + CRYPTO_set_mem_functions(ossl_malloc, NULL, ossl_free); + ERR_load_ERR_strings(); + ERR_load_BN_strings(); + ERR_load_RSA_strings(); + ERR_load_DH_strings(); + ERR_load_EVP_strings(); + ERR_load_BUF_strings(); + ERR_load_OBJ_strings(); + ERR_load_PEM_strings(); + ERR_load_X509_strings(); + ERR_load_ASN1_strings(); + ERR_load_CONF_strings(); + ERR_load_CRYPTO_strings(); + ERR_load_COMP_strings(); + ERR_load_BIO_strings(); + ERR_load_PKCS7_strings(); + ERR_load_X509V3_strings(); + ERR_load_PKCS12_strings(); + ERR_load_RAND_strings(); + ERR_load_DSO_strings(); + ERR_load_OCSP_strings(); +} + static SHIM_LOCK shim_lock_interface; static EFI_HANDLE shim_lock_handle; EFI_STATUS install_shim_protocols(void) { - EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + SHIM_LOCK *shim_lock; EFI_STATUS efi_status; - if (!secure_mode()) - return EFI_SUCCESS; + /* + * Did another instance of shim earlier already install the + * protocol? If so, get rid of it. + * + * We have to uninstall shim's protocol here, because if we're + * On the fallback.efi path, then our call pathway is: + * + * shim->fallback->shim->grub + * ^ ^ ^ + * | | \- gets protocol #0 + * | \- installs its protocol (#1) + * \- installs its protocol (#0) + * and if we haven't removed this, then grub will get the *first* + * shim's protocol, but it'll get the second shim's systab + * replacements. So even though it will participate and verify + * the kernel, the systab never finds out. + */ + efi_status = LibLocateProtocol(&SHIM_LOCK_GUID, (VOID **)&shim_lock); + if (!EFI_ERROR(efi_status)) + uninstall_shim_protocols(); /* * Install the protocol */ - efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4, - &shim_lock_handle, &shim_lock_guid, - EFI_NATIVE_INTERFACE, &shim_lock_interface); + efi_status = gBS->InstallProtocolInterface(&shim_lock_handle, + &SHIM_LOCK_GUID, + EFI_NATIVE_INTERFACE, + &shim_lock_interface); if (EFI_ERROR(efi_status)) { console_error(L"Could not install security protocol", efi_status); return efi_status; } + if (!secure_mode()) + return EFI_SUCCESS; + #if defined(OVERRIDE_SECURITY_POLICY) /* * Install the security protocol hook @@ -2513,7 +2442,11 @@ install_shim_protocols(void) void uninstall_shim_protocols(void) { - EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + /* + * If we're back here then clean everything up before exiting + */ + gBS->UninstallProtocolInterface(shim_lock_handle, &SHIM_LOCK_GUID, + &shim_lock_interface); if (!secure_mode()) return; @@ -2524,24 +2457,16 @@ uninstall_shim_protocols(void) */ security_policy_uninstall(); #endif - - /* - * If we're back here then clean everything up before exiting - */ - uefi_call_wrapper(BS->UninstallProtocolInterface, 3, shim_lock_handle, - &shim_lock_guid, &shim_lock_interface); } EFI_STATUS shim_init(void) { - EFI_STATUS status = EFI_SUCCESS; - setup_console(1); setup_verbosity(); - dprinta(shim_version); + dprint(L"%a", shim_version); /* Set the second stage loader */ - set_second_stage (image_handle); + set_second_stage (global_image_handle); if (secure_mode()) { if (vendor_cert_size || vendor_dbx_size) { @@ -2555,20 +2480,20 @@ shim_init(void) } hook_exit(systab); - - status = install_shim_protocols(); } - return status; + + return install_shim_protocols(); } void shim_fini(void) { + /* + * Remove our protocols + */ + uninstall_shim_protocols(); + if (secure_mode()) { - /* - * Remove our protocols - */ - uninstall_shim_protocols(); /* * Remove our hooks from system services. @@ -2583,7 +2508,7 @@ shim_fini(void) if (load_options_size > 0 && second_stage) FreePool(second_stage); - setup_console(0); + console_fini(); } extern EFI_STATUS @@ -2593,7 +2518,6 @@ static void __attribute__((__optimize__("0"))) debug_hook(void) { - EFI_GUID guid = SHIM_LOCK_GUID; UINT8 *data = NULL; UINTN dataSize = 0; EFI_STATUS efi_status; @@ -2603,18 +2527,21 @@ debug_hook(void) if (x) return; - efi_status = get_variable(L"SHIM_DEBUG", &data, &dataSize, guid); + efi_status = get_variable(L"SHIM_DEBUG", &data, &dataSize, + SHIM_LOCK_GUID); if (EFI_ERROR(efi_status)) { return; } - Print(L"add-symbol-file "DEBUGDIR - L"shim" EFI_ARCH L".efi.debug 0x%08x -s .data 0x%08x\n", &_text, - &_data); + FreePool(data); + + console_print(L"add-symbol-file "DEBUGDIR + L"shim" EFI_ARCH L".efi.debug 0x%08x -s .data 0x%08x\n", + &_text, &_data); - Print(L"Pausing for debugger attachment.\n"); - Print(L"To disable this, remove the EFI variable SHIM_DEBUG-%g .\n", - &guid); + 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); x = 1; while (x++) { /* Make this so it can't /totally/ DoS us. */ @@ -2629,7 +2556,7 @@ debug_hook(void) #else if (x > 12000) break; - uefi_call_wrapper(BS->Stall, 1, 5000); + msleep(5000); #endif } x = 1; @@ -2639,6 +2566,7 @@ EFI_STATUS efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) { EFI_STATUS efi_status; + EFI_HANDLE image_handle; verification_method = VERIFIED_BY_NOTHING; @@ -2646,6 +2574,13 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) 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; + CHAR16 *msgs[] = { + L"import_mok_state() failed\n", + L"shim_int() failed\n", + NULL + }; + int msg = 0; + /* * Set up the shim lock protocol so that grub and MokManager can @@ -2656,81 +2591,49 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) shim_lock_interface.Context = shim_read_header; systab = passed_systab; - image_handle = passed_image_handle; + image_handle = global_image_handle = passed_image_handle; /* * Ensure that gnu-efi functions are available */ InitializeLib(image_handle, systab); + init_openssl(); + /* * if SHIM_DEBUG is set, wait for a debugger to attach. */ debug_hook(); /* - * Measure the MOK variables + * Before we do anything else, validate our non-volatile, + * boot-services-only state variables are what we think they are. */ - efi_status = measure_mok(); - if (efi_status != EFI_SUCCESS && efi_status != EFI_NOT_FOUND) { - Print(L"Something has gone seriously wrong: %r\n", efi_status); - Print(L"Shim was unable to measure state into the TPM\n"); - systab->BootServices->Stall(5000000); - systab->RuntimeServices->ResetSystem(EfiResetShutdown, - EFI_SECURITY_VIOLATION, - 0, NULL); + efi_status = import_mok_state(image_handle); + if (EFI_ERROR(efi_status)) { +die: + console_print(L"Something has gone seriously wrong: %s: %r\n", + msgs[msg], efi_status); + msleep(5000000); + gRT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, + 0, NULL); } - /* - * Check whether the user has configured the system to run in - * insecure mode - */ - check_mok_sb(); - efi_status = shim_init(); if (EFI_ERROR(efi_status)) { - Print(L"Something has gone seriously wrong: %r\n", efi_status); - Print(L"shim cannot continue, sorry.\n"); - uefi_call_wrapper(BS->Stall, 1, 5000000); - uefi_call_wrapper(systab->RuntimeServices->ResetSystem, 4, - EfiResetShutdown, EFI_SECURITY_VIOLATION, - 0, NULL); + msg = 1; + goto die; } /* * Tell the user that we're in insecure mode if necessary */ if (user_insecure_mode) { - Print(L"Booting in insecure mode\n"); - uefi_call_wrapper(BS->Stall, 1, 2000000); + console_print(L"Booting in insecure mode\n"); + msleep(2000000); } /* - * 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(); - - efi_status = mirror_mok_list_x(); - - /* - * Copy the MOK SB State to a runtime variable so the kernel can - * make use of it - */ - efi_status = mirror_mok_sb_state(); - - /* - * Create the runtime MokIgnoreDB variable so the kernel can - * make use of it - */ - efi_status = mok_ignore_db(); - - /* * Hand over control to the second stage bootloader */ efi_status = init_grub(image_handle); |