From b017e4554b44a8e4ee9ce6579eaab261cdd69afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= Date: Mon, 24 Mar 2025 10:18:24 +0100 Subject: [PATCH] New upstream version 16.0 --- .github/workflows/pullrequest.yml | 88 +++- .gitignore | 2 + .gitmodules | 2 +- BUILDING | 12 +- CODE_OF_CONDUCT.md | 2 +- Cryptlib/InternalCryptLib.h | 35 +- Cryptlib/Library/BaseCryptLib.h | 42 ++ Cryptlib/Makefile | 10 + Cryptlib/OpenSSL/Makefile | 3 + Cryptlib/Pk/CryptPkcs7Verify.c | 18 + Cryptlib/Pk/CryptPkcs7VerifyEku.c | 516 ++++++++++++++++++ Delivering_Sbat_Revocations.md | 29 + Make.defaults | 8 +- Makefile | 41 +- MokManager.c | 9 +- MokVars.txt | 35 +- README.tpm | 10 +- SBAT.md | 121 +++-- SbatLevel_Variable.txt | 35 +- commit | 1 + dp.c | 43 ++ errlog.c | 181 +++++++ fallback.c | 219 +++----- fuzz-pe-relocate.c | 2 +- generate_sbat_var_defs.c | 173 ++++++ globals.c | 7 +- gnu-efi/ia32/gnuefi/crt0-efi-ia32.o | Bin 776 -> 0 bytes gnu-efi/ia32/gnuefi/libgnuefi.a | Bin 1034 -> 0 bytes gnu-efi/ia32/gnuefi/reloc_ia32.o | Bin 888 -> 0 bytes gnu-efi/ia32/lib/boxdraw.o | Bin 1644 -> 0 bytes gnu-efi/ia32/lib/cmdline.o | Bin 2032 -> 0 bytes gnu-efi/ia32/lib/console.o | Bin 1876 -> 0 bytes gnu-efi/ia32/lib/crc.o | Bin 2672 -> 0 bytes gnu-efi/ia32/lib/data.o | Bin 5100 -> 0 bytes gnu-efi/ia32/lib/debug.o | Bin 1244 -> 0 bytes gnu-efi/ia32/lib/dpath.o | Bin 15556 -> 0 bytes gnu-efi/ia32/lib/error.o | Bin 3104 -> 0 bytes gnu-efi/ia32/lib/event.o | Bin 2096 -> 0 bytes gnu-efi/ia32/lib/exit.o | Bin 1164 -> 0 bytes gnu-efi/ia32/lib/guid.o | Bin 5240 -> 0 bytes gnu-efi/ia32/lib/hand.o | Bin 4712 -> 0 bytes gnu-efi/ia32/lib/hw.o | Bin 1724 -> 0 bytes gnu-efi/ia32/lib/ia32/initplat.o | Bin 760 -> 0 bytes gnu-efi/ia32/lib/ia32/math.o | Bin 1112 -> 0 bytes gnu-efi/ia32/lib/ia32/setjmp.o | Bin 476 -> 0 bytes gnu-efi/ia32/lib/init.o | Bin 2916 -> 0 bytes gnu-efi/ia32/lib/libefi.a | Bin 96448 -> 0 bytes gnu-efi/ia32/lib/lock.o | Bin 1004 -> 0 bytes gnu-efi/ia32/lib/misc.o | Bin 5828 -> 0 bytes gnu-efi/ia32/lib/pause.o | Bin 1212 -> 0 bytes gnu-efi/ia32/lib/print.o | Bin 11144 -> 0 bytes gnu-efi/ia32/lib/runtime/efirtlib.o | Bin 1520 -> 0 bytes gnu-efi/ia32/lib/runtime/rtdata.o | Bin 740 -> 0 bytes gnu-efi/ia32/lib/runtime/rtlock.o | Bin 1612 -> 0 bytes gnu-efi/ia32/lib/runtime/rtstr.o | Bin 2212 -> 0 bytes gnu-efi/ia32/lib/runtime/vm.o | Bin 1796 -> 0 bytes gnu-efi/ia32/lib/smbios.o | Bin 1576 -> 0 bytes gnu-efi/ia32/lib/sread.o | Bin 2504 -> 0 bytes gnu-efi/ia32/lib/str.o | Bin 3724 -> 0 bytes gnu-efi/inc/efiapi.h | 72 ++- gnu-efi/inc/efierr.h | 5 + gnu-efi/inc/efilib.h | 6 + gnu-efi/inc/efiprot.h | 63 +++ gnu-efi/lib/data.c | 3 + gnu-efi/lib/error.c | 5 + httpboot.c | 58 +- include/compiler.h | 9 +- include/console.h | 1 + include/dp.h | 14 + include/errlog.h | 22 + include/errors.h | 3 + include/fanalyzer.mk | 2 +- include/guid.h | 12 + include/hexdump.h | 6 +- include/load-options.h | 1 + include/{replacements.h => loader-proto.h} | 12 +- include/memattrs.h | 18 + include/mock-variables.h | 3 + include/mok.h | 48 ++ include/netboot.h | 5 +- include/pe.h | 6 +- include/peimage.h | 13 +- include/sbat.h | 3 +- include/sbat_var_defs.h | 42 +- include/test-data-efivars-1.h | 11 + include/test.h | 18 +- include/test.mk | 11 +- include/utils.h | 9 + lib/console.c | 1 + lib/guid.c | 2 + lib/simple_file.c | 27 +- lib/variables.c | 2 + load-options.c | 32 +- loader-proto.c | 445 ++++++++++++++++ make-archive | 7 +- make-certs | 5 + memattrs.c | 550 +++++++++++++++++++ mock-variables.c | 114 ++-- mok.c | 363 +++++++++++-- netboot.c | 68 ++- pe-relocate.c | 127 ++++- pe.c | 149 +----- post-process-pe.c | 60 ++- replacements.c | 222 -------- sbat.c | 6 +- sbat_var.S | 1 + shim.c | 296 +++++++---- shim.h | 32 +- test-load-options.c | 77 ++- test-mock-variables.c | 37 +- test-mok-mirror.c | 581 ++++++++++++++++----- test-sbat.c | 1 + tpm.c | 35 ++ utils.c | 86 +++ 114 files changed, 4408 insertions(+), 1043 deletions(-) create mode 100644 Cryptlib/Pk/CryptPkcs7VerifyEku.c create mode 100644 Delivering_Sbat_Revocations.md create mode 100644 commit create mode 100644 dp.c create mode 100644 generate_sbat_var_defs.c delete mode 100644 gnu-efi/ia32/gnuefi/crt0-efi-ia32.o delete mode 100644 gnu-efi/ia32/gnuefi/libgnuefi.a delete mode 100644 gnu-efi/ia32/gnuefi/reloc_ia32.o delete mode 100644 gnu-efi/ia32/lib/boxdraw.o delete mode 100644 gnu-efi/ia32/lib/cmdline.o delete mode 100644 gnu-efi/ia32/lib/console.o delete mode 100644 gnu-efi/ia32/lib/crc.o delete mode 100644 gnu-efi/ia32/lib/data.o delete mode 100644 gnu-efi/ia32/lib/debug.o delete mode 100644 gnu-efi/ia32/lib/dpath.o delete mode 100644 gnu-efi/ia32/lib/error.o delete mode 100644 gnu-efi/ia32/lib/event.o delete mode 100644 gnu-efi/ia32/lib/exit.o delete mode 100644 gnu-efi/ia32/lib/guid.o delete mode 100644 gnu-efi/ia32/lib/hand.o delete mode 100644 gnu-efi/ia32/lib/hw.o delete mode 100644 gnu-efi/ia32/lib/ia32/initplat.o delete mode 100644 gnu-efi/ia32/lib/ia32/math.o delete mode 100644 gnu-efi/ia32/lib/ia32/setjmp.o delete mode 100644 gnu-efi/ia32/lib/init.o delete mode 100644 gnu-efi/ia32/lib/libefi.a delete mode 100644 gnu-efi/ia32/lib/lock.o delete mode 100644 gnu-efi/ia32/lib/misc.o delete mode 100644 gnu-efi/ia32/lib/pause.o delete mode 100644 gnu-efi/ia32/lib/print.o delete mode 100644 gnu-efi/ia32/lib/runtime/efirtlib.o delete mode 100644 gnu-efi/ia32/lib/runtime/rtdata.o delete mode 100644 gnu-efi/ia32/lib/runtime/rtlock.o delete mode 100644 gnu-efi/ia32/lib/runtime/rtstr.o delete mode 100644 gnu-efi/ia32/lib/runtime/vm.o delete mode 100644 gnu-efi/ia32/lib/smbios.o delete mode 100644 gnu-efi/ia32/lib/sread.o delete mode 100644 gnu-efi/ia32/lib/str.o create mode 100644 include/dp.h create mode 100644 include/errlog.h rename include/{replacements.h => loader-proto.h} (70%) create mode 100644 include/memattrs.h create mode 100644 include/utils.h create mode 100644 loader-proto.c create mode 100644 memattrs.c delete mode 100644 replacements.c create mode 100644 utils.c diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 7a80738..6027d4d 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -22,66 +22,70 @@ jobs: efiarch: aa64 gccarch: aarch64 makearch: aarch64 - distro: f36 + distro: f41 - arch: amd64 efiarch: aa64 gccarch: aarch64 makearch: aarch64 - distro: f35 + distro: f40 - arch: amd64 efiarch: arm gccarch: arm makearch: arm - distro: f36 + distro: f41 - arch: amd64 efiarch: arm gccarch: arm makearch: arm - distro: f35 + distro: f40 - arch: amd64 efiarch: arm gccarch: arm makearch: arm - distro: f34 + distro: f39 - arch: amd64 efiarch: x64 gccarch: x86_64 makearch: x86_64 - distro: f36 + distro: f41 - arch: amd64 efiarch: x64 gccarch: x86_64 makearch: x86_64 - distro: f35 + distro: f40 - arch: amd64 efiarch: x64 gccarch: x86_64 makearch: x86_64 - distro: f34 + distro: f39 - arch: amd64 efiarch: ia32 gccarch: x86_64 makearch: ia32 - distro: f36 + distro: f41 - arch: amd64 efiarch: ia32 gccarch: x86_64 makearch: ia32 - distro: f35 + distro: f40 - arch: amd64 efiarch: ia32 gccarch: x86_64 makearch: ia32 - distro: f34 + distro: f39 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: # otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger) ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 submodules: recursive + - name: Work around directory ownership issue + id: ignore-ownership + run: | + git config --global --add safe.directory /__w/shim/shim - name: Update submodules on ${{ matrix.distro }} for ${{ matrix.efiarch }} id: update-submodules run: | @@ -118,15 +122,15 @@ jobs: - arch: amd64 efiarch: x64 makearch: x86_64 - distro: f36 + distro: f41 - arch: amd64 efiarch: x64 makearch: x86_64 - distro: f35 + distro: f40 - arch: amd64 efiarch: x64 makearch: x86_64 - distro: f34 + distro: f39 - arch: amd64 efiarch: x64 makearch: x86_64 @@ -138,15 +142,7 @@ jobs: - arch: amd64 efiarch: ia32 makearch: ia32 - distro: f36 - - arch: amd64 - efiarch: ia32 - makearch: ia32 - distro: f35 - - arch: amd64 - efiarch: ia32 - makearch: ia32 - distro: f34 + distro: f39 - arch: amd64 efiarch: ia32 makearch: ia32 @@ -154,12 +150,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: # otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger) ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 submodules: recursive + - name: Work around directory ownership issue + id: ignore-ownership + run: | + git config --global --add safe.directory /__w/shim/shim - name: Update submodules on ${{ matrix.distro }} for ${{ matrix.efiarch }} id: update-submodules run: | @@ -182,3 +182,41 @@ jobs: make ARCH=${{ matrix.makearch }} PREFIX=/usr DESTDIR=/destdir EFIDIR=test ENABLE_SHIM_HASH=true install echo 'results:' find /destdir -type f + + build-pull-request-intel-compile-commands-json: + runs-on: ubuntu-20.04 + container: vathpela/efi-ci:${{ matrix.distro }}-x64 + name: ${{ matrix.distro }} ${{ matrix.efiarch }} build compile_commands.json + + strategy: + matrix: + include: + - arch: amd64 + efiarch: x64 + makearch: x86_64 + distro: f41 + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger) + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + submodules: recursive + - name: Work around directory ownership issue + id: ignore-ownership + run: | + git config --global --add safe.directory /__w/shim/shim + - name: Update submodules on ${{ matrix.distro }} for ${{ matrix.efiarch }} + id: update-submodules + run: | + make update + - name: Do 'make clean' on ${{ matrix.distro }} for ${{ matrix.efiarch }} + id: clean + run: | + make ARCH=${{ matrix.makearch }} PREFIX=/usr DESTDIR=/destdir EFIDIR=test ENABLE_SHIM_HASH=true clean + - name: Build compile_commands.json on ${{ matrix.distro }} for ${{ matrix.efiarch }} + id: compile_commands + run: | + make ARCH=${{ matrix.makearch }} PREFIX=/usr DESTDIR=/destdir EFIDIR=test ENABLE_SHIM_HASH=true compile_commands.json diff --git a/.gitignore b/.gitignore index 9085e5a..d829669 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,8 @@ Make.local /crash-* /fuzz-* !/fuzz-*.c +/generate_sbat_var_defs +/generated_sbat_var_defs.h /leak-* /post-process-pe /random.bin diff --git a/.gitmodules b/.gitmodules index d0f1973..41842dc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "gnu-efi"] path = gnu-efi url = https://github.com/rhboot/gnu-efi.git - branch = shim-15.8 + branch = shim-16.0 diff --git a/BUILDING b/BUILDING index 17cd98d..5005868 100644 --- a/BUILDING +++ b/BUILDING @@ -37,15 +37,6 @@ Variables you could set to customize the build: debugger only on the development branch and not the OS you need to boot to scp in a new development build. Likewise, we look for SHIM_DEVEL_VERBOSE rather than SHIM_VERBOSE. -- DISABLE_EBS_PROTECTION - On systems where a second stage bootloader is not used, and the Linux - Kernel is embedded in the same EFI image as shim and booted directly - from shim, shim's ExitBootServices() hook can cause problems as the - kernel never calls the shim's verification protocol. In this case - calling the shim verification protocol is unnecessary and redundant as - shim has already verified the kernel when shim loaded the kernel as the - second stage loader. In such a case, and only in this case, you should - use DISABLE_EBS_PROTECTION=y to build. - DISABLE_REMOVABLE_LOAD_OPTIONS Do not parse load options when invoked as boot*.efi. This prevents boot failures because of unexpected data in boot entries automatically generated @@ -81,6 +72,9 @@ Variables you could set to customize the build: - POST_PROCESS_PE_FLAGS This allows you to add flags to the invocation of "post-process-pe", for example to disable the NX compatibility flag. +- ENABLE_CODESIGN_EKU + This changes the certificate validation logic to require Extended Key + Usage 1.3.6.1.5.5.7.3.3 ("Code Signing"). Vendor SBAT data: It will sometimes be requested by reviewers that a build includes extra diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 59a2c94..2144aed 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -61,7 +61,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -rharwood AT redhat DOT com. +contact AT linux-uefi DOT org. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/Cryptlib/InternalCryptLib.h b/Cryptlib/InternalCryptLib.h index dc1a95e..0ad2ef7 100644 --- a/Cryptlib/InternalCryptLib.h +++ b/Cryptlib/InternalCryptLib.h @@ -32,5 +32,38 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define OBJ_length(o) ((o)->length) #endif -#endif +#if defined(ENABLE_CODESIGN_EKU) +/** + Check input P7Data is a wrapped ContentInfo structure or not. If not construct + a new structure to wrap P7Data. + Caution: This function may receive untrusted input. + UEFI Authenticated Variable is external input, so this function will do basic + check for PKCS#7 data structure. + + @param[in] P7Data Pointer to the PKCS#7 message to verify. + @param[in] P7Length Length of the PKCS#7 message in bytes. + @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise + return FALSE. + @param[out] WrapData If return status of this function is TRUE: + 1) when WrapFlag is TRUE, pointer to P7Data. + 2) when WrapFlag is FALSE, pointer to a new ContentInfo + structure. It's caller's responsibility to free this + buffer. + @param[out] WrapDataSize Length of ContentInfo structure in bytes. + + @retval TRUE The operation is finished successfully. + @retval FALSE The operation is failed due to lack of resources. + +**/ +BOOLEAN +WrapPkcs7Data ( + IN CONST UINT8 *P7Data, + IN UINTN P7Length, + OUT BOOLEAN *WrapFlag, + OUT UINT8 **WrapData, + OUT UINTN *WrapDataSize + ); + +#endif +#endif diff --git a/Cryptlib/Library/BaseCryptLib.h b/Cryptlib/Library/BaseCryptLib.h index 2df8bd2..439f051 100644 --- a/Cryptlib/Library/BaseCryptLib.h +++ b/Cryptlib/Library/BaseCryptLib.h @@ -2403,6 +2403,48 @@ Pkcs7Verify ( IN UINTN DataLength ); +#if defined(ENABLE_CODESIGN_EKU) +/** + This function receives a PKCS#7 formatted signature blob, + looks for the EKU SEQUENCE blob, and if found then looks + for all the required EKUs. This function was created so that + the Surface team can cut down on the number of Certificate + Authorities (CA's) by checking EKU's on leaf signers for + a specific product. This prevents one product's certificate + from signing another product's firmware or unlock blobs. + + Note that this function does not validate the certificate chain. + That needs to be done before using this function. + + @param[in] Pkcs7Signature The PKCS#7 signed information content block. An array + containing the content block with both the signature, + the signer's certificate, and any necessary intermediate + certificates. + @param[in] Pkcs7SignatureSize Number of bytes in Pkcs7Signature. + @param[in] RequiredEKUs Array of null-terminated strings listing OIDs of + required EKUs that must be present in the signature. + @param[in] RequiredEKUsSize Number of elements in the RequiredEKUs string array. + @param[in] RequireAllPresent If this is TRUE, then all of the specified EKU's + must be present in the leaf signer. If it is + FALSE, then we will succeed if we find any + of the specified EKU's. + + @retval EFI_SUCCESS The required EKUs were found in the signature. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND One or more EKU's were not found in the signature. + +**/ +EFI_STATUS +EFIAPI +VerifyEKUsInPkcs7Signature ( + IN CONST UINT8 *Pkcs7Signature, + IN CONST UINT32 SignatureSize, + IN CONST CHAR8 *RequiredEKUs[], + IN CONST UINT32 RequiredEKUsSize, + IN BOOLEAN RequireAllPresent + ); +#endif + /** Extracts the attached content from a PKCS#7 signed data if existed. The input signed data could be wrapped in a ContentInfo structure. diff --git a/Cryptlib/Makefile b/Cryptlib/Makefile index 626788c..68a9395 100644 --- a/Cryptlib/Makefile +++ b/Cryptlib/Makefile @@ -14,6 +14,9 @@ INCLUDES = -I$(CRYPTDIR) -I$(CRYPTDIR)/Include \ WARNFLAGS += -Wno-unused-parameter \ -Wno-unused-but-set-variable +WERRFLAGS += -Wno-error=unused-but-set-variable \ + -Wno-error=unused-parameter + CFLAGS = $(FEATUREFLAGS) \ $(OPTIMIZATIONS) \ $(WARNFLAGS) \ @@ -37,6 +40,9 @@ endif ifeq ($(ARCH),arm) DEFINES += -DMDE_CPU_ARM endif +ifeq ($(ENABLE_CODESIGN_EKU),1) +DEFINES += -DENABLE_CODESIGN_EKU +endif LDFLAGS = -nostdlib -znocombreloc @@ -67,6 +73,10 @@ OBJS = Hash/CryptMd4Null.o \ SysCall/BaseMemAllocation.o \ SysCall/BaseStrings.o +ifeq ($(ENABLE_CODESIGN_EKU),1) + OBJS += Pk/CryptPkcs7VerifyEku.o +endif + all: $(TARGET) libcryptlib.a: $(OBJS) diff --git a/Cryptlib/OpenSSL/Makefile b/Cryptlib/OpenSSL/Makefile index d59c5d7..e517bcf 100644 --- a/Cryptlib/OpenSSL/Makefile +++ b/Cryptlib/OpenSSL/Makefile @@ -26,6 +26,9 @@ WARNFLAGS += -Wno-empty-body \ -Wno-unused-but-set-variable \ -Wno-unused-parameter +WERRFLAGS += -Wno-error=unused-but-set-variable \ + -Wno-error=unused-parameter + CFLAGS = $(FEATUREFLAGS) \ $(OPTIMIZATIONS) \ $(WARNFLAGS) \ diff --git a/Cryptlib/Pk/CryptPkcs7Verify.c b/Cryptlib/Pk/CryptPkcs7Verify.c index c189384..640b01d 100644 --- a/Cryptlib/Pk/CryptPkcs7Verify.c +++ b/Cryptlib/Pk/CryptPkcs7Verify.c @@ -29,6 +29,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 }; +#if defined(ENABLE_CODESIGN_EKU) +/* EKU CodeSign */ +CHAR8 mOidCodeSign[] = "1.3.6.1.5.5.7.3.3"; +#endif #if 1 #if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -846,6 +850,10 @@ Pkcs7Verify ( CONST UINT8 *Temp; UINTN SignedDataSize; BOOLEAN Wrapped; +#if defined(ENABLE_CODESIGN_EKU) + CONST CHAR8 *Ekus[1]; + EFI_STATUS EFI_Status; +#endif // // Check input parameters. @@ -859,6 +867,9 @@ Pkcs7Verify ( DataBio = NULL; Cert = NULL; CertStore = NULL; +#if defined(ENABLE_CODESIGN_EKU) + Ekus[0] = mOidCodeSign; +#endif // // Register & Initialize necessary digest algorithms for PKCS#7 Handling @@ -958,6 +969,13 @@ Pkcs7Verify ( // X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY); +#if defined(ENABLE_CODESIGN_EKU) + EFI_Status = VerifyEKUsInPkcs7Signature(P7Data, P7Length, Ekus, 1, TRUE); + if (EFI_Status != EFI_SUCCESS) { + goto _Exit; + } +#endif + // // Verifies the PKCS#7 signedData structure // diff --git a/Cryptlib/Pk/CryptPkcs7VerifyEku.c b/Cryptlib/Pk/CryptPkcs7VerifyEku.c new file mode 100644 index 0000000..f277f1d --- /dev/null +++ b/Cryptlib/Pk/CryptPkcs7VerifyEku.c @@ -0,0 +1,516 @@ +/** @file + This module verifies that Enhanced Key Usages (EKU's) are present within + a PKCS7 signature blob using OpenSSL. + + Copyright (C) Microsoft Corporation. All Rights Reserved. + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include "InternalCryptLib.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + This function will return the leaf signer certificate in a chain. This is + required because certificate chains are not guaranteed to have the + certificates in the order that they were issued. + + A typical certificate chain looks like this: + + + ---------------------------- + | Root | + ---------------------------- + ^ + | + ---------------------------- + | Policy CA | <-- Typical Trust Anchor. + ---------------------------- + ^ + | + ---------------------------- + | Issuing CA | + ---------------------------- + ^ + | + ----------------------------- + / End-Entity (leaf) signer / <-- Bottom certificate. + ----------------------------- EKU: "1.3.6.1.4.1.311.76.9.21.1" + (Firmware Signing) + + + @param[in] CertChain Certificate chain. + + @param[out] SignerCert Last certificate in the chain. For PKCS7 signatures, + this will be the end-entity (leaf) signer cert. + + @retval EFI_SUCCESS The required EKUs were found in the signature. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND The number of signers found was not 1. + +**/ +EFI_STATUS +GetSignerCertificate ( + IN CONST PKCS7 *CertChain, + OUT X509 **SignerCert + ) +{ + EFI_STATUS Status; + STACK_OF(X509) *Signers; + INT32 NumberSigners; + + Status = EFI_SUCCESS; + Signers = NULL; + NumberSigners = 0; + + if (CertChain == NULL || SignerCert == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Get the signers from the chain. + // + Signers = PKCS7_get0_signers ((PKCS7*) CertChain, NULL, PKCS7_BINARY); + if (Signers == NULL) { + // + // Fail to get signers form PKCS7 + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // There should only be one signer in the PKCS7 stack. + // + NumberSigners = sk_X509_num (Signers); + if (NumberSigners != 1) { + // + // The number of singers should have been 1 + // + Status = EFI_NOT_FOUND; + goto Exit; + } + + *SignerCert = sk_X509_value (Signers, 0); + +Exit: + // + // Release Resources + // + if (Signers != NULL) { + sk_X509_free (Signers); + } + + return Status; +} + + +/** + Determines if the specified EKU represented in ASN1 form is present + in a given certificate. + + @param[in] Cert The certificate to check. + + @param[in] Asn1ToFind The EKU to look for. + + @retval EFI_SUCCESS We successfully identified the signing type. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND One or more EKU's were not found in the signature. + +**/ +EFI_STATUS +IsEkuInCertificate ( + IN CONST X509 *Cert, + IN ASN1_OBJECT *Asn1ToFind + ) +{ + EFI_STATUS Status; + X509 *ClonedCert; + X509_EXTENSION *Extension; + EXTENDED_KEY_USAGE *Eku; + INT32 ExtensionIndex; + INTN NumExtensions; + ASN1_OBJECT *Asn1InCert; + INTN Index; + + Status = EFI_NOT_FOUND; + ClonedCert = NULL; + Extension = NULL; + Eku = NULL; + ExtensionIndex = -1; + NumExtensions = 0; + Asn1InCert = NULL; + + if (Cert == NULL || Asn1ToFind == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Clone the certificate. This is required because the Extension API's + // only work once per instance of an X509 object. + // + ClonedCert = X509_dup ((X509*)Cert); + if (ClonedCert == NULL) { + // + // Fail to duplicate cert. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Look for the extended key usage. + // + ExtensionIndex = X509_get_ext_by_NID (ClonedCert, NID_ext_key_usage, -1); + + if (ExtensionIndex < 0) { + // + // Fail to find 'NID_ext_key_usage' in Cert. + // + goto Exit; + } + + Extension = X509_get_ext (ClonedCert, ExtensionIndex); + if (Extension == NULL) { + // + // Fail to get Extension form cert. + // + goto Exit; + } + + Eku = (EXTENDED_KEY_USAGE*)X509V3_EXT_d2i (Extension); + if (Eku == NULL) { + // + // Fail to get Eku from extension. + // + goto Exit; + } + + NumExtensions = sk_ASN1_OBJECT_num (Eku); + + // + // Now loop through the extensions, looking for the specified Eku. + // + for (Index = 0; Index < NumExtensions; Index++) { + Asn1InCert = sk_ASN1_OBJECT_value (Eku, (INT32)Index); + if (Asn1InCert == NULL) { + // + // Fail to get ASN object from Eku. + // + goto Exit; + } + + if (OBJ_cmp(Asn1InCert, Asn1ToFind) == 0) { + // + // Found Eku in certificate. + // + Status = EFI_SUCCESS; + goto Exit; + } + } + +Exit: + + // + // Release Resources + // + if (ClonedCert != NULL) { + X509_free (ClonedCert); + } + + if (Eku != NULL) { + sk_ASN1_OBJECT_pop_free (Eku, ASN1_OBJECT_free); + } + + return Status; +} + + +/** + Determines if the specified EKUs are present in a signing certificate. + + @param[in] SignerCert The certificate to check. + @param[in] RequiredEKUs The EKUs to look for. + @param[in] RequiredEKUsSize The number of EKUs + @param[in] RequireAllPresent If TRUE, then all the specified EKUs + must be present in the certificate. + + @retval EFI_SUCCESS We successfully identified the signing type. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND One or more EKU's were not found in the signature. +**/ +EFI_STATUS +CheckEKUs( + IN CONST X509 *SignerCert, + IN CONST CHAR8 *RequiredEKUs[], + IN CONST UINT32 RequiredEKUsSize, + IN BOOLEAN RequireAllPresent + ) +{ + EFI_STATUS Status; + ASN1_OBJECT *Asn1ToFind; + UINT32 NumEkusFound; + UINT32 Index; + + Status = EFI_NOT_FOUND; + Asn1ToFind = NULL; + NumEkusFound = 0; + + if (SignerCert == NULL || RequiredEKUs == NULL || RequiredEKUsSize == 0) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + for (Index = 0; Index < RequiredEKUsSize; Index++) { + // + // Finding required EKU in cert. + // + if (Asn1ToFind != NULL) { + ASN1_OBJECT_free(Asn1ToFind); + Asn1ToFind = NULL; + } + + Asn1ToFind = OBJ_txt2obj (RequiredEKUs[Index], 0); + if (Asn1ToFind == NULL) { + // + // Fail to convert required EKU to ASN1. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + Status = IsEkuInCertificate (SignerCert, Asn1ToFind); + if (Status == EFI_SUCCESS) { + NumEkusFound++; + if (!RequireAllPresent) { + // + // Found at least one, so we are done. + // + goto Exit; + } + } else { + // + // Fail to find Eku in cert + break; + } + } + +Exit: + + if (Asn1ToFind != NULL) { + ASN1_OBJECT_free(Asn1ToFind); + } + + if (RequireAllPresent && + NumEkusFound == RequiredEKUsSize) { + // + // Found all required EKUs in certificate. + // + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + This function receives a PKCS#7 formatted signature blob, + looks for the EKU SEQUENCE blob, and if found then looks + for all the required EKUs. This function was created so that + the Surface team can cut down on the number of Certificate + Authorities (CA's) by checking EKU's on leaf signers for + a specific product. This prevents one product's certificate + from signing another product's firmware or unlock blobs. + + Note that this function does not validate the certificate chain. + That needs to be done before using this function. + + @param[in] Pkcs7Signature The PKCS#7 signed information content block. An array + containing the content block with both the signature, + the signer's certificate, and any necessary intermediate + certificates. + @param[in] Pkcs7SignatureSize Number of bytes in Pkcs7Signature. + @param[in] RequiredEKUs Array of null-terminated strings listing OIDs of + required EKUs that must be present in the signature. + @param[in] RequiredEKUsSize Number of elements in the RequiredEKUs string array. + @param[in] RequireAllPresent If this is TRUE, then all of the specified EKU's + must be present in the leaf signer. If it is + FALSE, then we will succeed if we find any + of the specified EKU's. + + @retval EFI_SUCCESS The required EKUs were found in the signature. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND One or more EKU's were not found in the signature. + +**/ +EFI_STATUS +EFIAPI +VerifyEKUsInPkcs7Signature ( + IN CONST UINT8 *Pkcs7Signature, + IN CONST UINT32 SignatureSize, + IN CONST CHAR8 *RequiredEKUs[], + IN CONST UINT32 RequiredEKUsSize, + IN BOOLEAN RequireAllPresent + ) +{ + EFI_STATUS Status; + PKCS7 *Pkcs7; + STACK_OF(X509) *CertChain; + INT32 SignatureType; + INT32 NumberCertsInSignature; + X509 *SignerCert; + UINT8 *SignedData; + UINT8 *Temp; + UINTN SignedDataSize; + BOOLEAN IsWrapped; + BOOLEAN Ok; + + Status = EFI_SUCCESS; + Pkcs7 = NULL; + CertChain = NULL; + SignatureType = 0; + NumberCertsInSignature = 0; + SignerCert = NULL; + SignedData = NULL; + SignedDataSize = 0; + IsWrapped = FALSE; + Ok = FALSE; + + // + //Validate the input parameters. + // + if (Pkcs7Signature == NULL || + SignatureSize == 0 || + RequiredEKUs == NULL || + RequiredEKUsSize == 0) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + if (RequiredEKUsSize == 1) { + RequireAllPresent = TRUE; + } + + // + // Wrap the PKCS7 data if needed. + // + Ok = WrapPkcs7Data (Pkcs7Signature, + SignatureSize, + &IsWrapped, + &SignedData, + &SignedDataSize); + if (!Ok) { + // + // Fail to Wrap the PKCS7 data. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + Temp = SignedData; + + // + // Create the PKCS7 object. + // + Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (INT32)SignedDataSize); + if (Pkcs7 == NULL) { + // + // Fail to read PKCS7 data. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Get the certificate chain. + // + SignatureType = OBJ_obj2nid (Pkcs7->type); + switch (SignatureType) { + case NID_pkcs7_signed: + if (Pkcs7->d.sign != NULL) { + CertChain = Pkcs7->d.sign->cert; + } + break; + case NID_pkcs7_signedAndEnveloped: + if (Pkcs7->d.signed_and_enveloped != NULL) { + CertChain = Pkcs7->d.signed_and_enveloped->cert; + } + break; + default: + break; + } + + // + // Ensure we have a certificate stack + // + if (CertChain == NULL) { + // + // Fail to get the certificate stack from signature. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Find out how many certificates were in the PKCS7 signature. + // + NumberCertsInSignature = sk_X509_num (CertChain); + + if (NumberCertsInSignature == 0) { + // + // Fail to find any certificates in signature. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Get the leaf signer. + // + Status = GetSignerCertificate (Pkcs7, &SignerCert); + if (Status != EFI_SUCCESS || SignerCert == NULL) { + // + // Fail to get the end-entity leaf signer certificate. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + Status = CheckEKUs (SignerCert, RequiredEKUs, RequiredEKUsSize, RequireAllPresent); + if (Status != EFI_SUCCESS) { + goto Exit; + } + +Exit: + + // + // Release Resources + // + // If the signature was not wrapped, then the call to WrapData() will allocate + // the data and add a header to it + // + if (!IsWrapped && SignedData) { + free (SignedData); + } + + if (Pkcs7 != NULL) { + PKCS7_free (Pkcs7); + } + + return Status; +} + diff --git a/Delivering_Sbat_Revocations.md b/Delivering_Sbat_Revocations.md new file mode 100644 index 0000000..d3e5060 --- /dev/null +++ b/Delivering_Sbat_Revocations.md @@ -0,0 +1,29 @@ +When new sbat based revocations become public they are added to +https://github.com/rhboot/shim/blob/main/SbatLevel_Variable.txt They +are identified by their year, month, day, counter YYYYMMDDCC field in +the header. + +If secure boot is disabled, shim will always clear the applied +revocations. + +shim binaries will include the opt-in latest revocation payload +available at the time that they are built. This can be applied by +running mokutil --set-sbat-policy latest and rebooting with the new +shim binary in place. A shim build can also specify a +-DSBAT_AUTOMATIC_DATE=YYYYMMDDCC on the command line which will +include and automatically apply that revocation. shim will never +downgrade a revocation. The only way to roll back is to disable secure +boot, load shim to clear the revocations and then re-apply the desired +level. + +In addition to building revocation levels into shim, they can also be +delivered via a revocations_sbat.efi binary. These binaries can be +created from the https://github.com/rhboot/certwrapper +repository. This repository uses the same +https://github.com/rhboot/shim/blob/main/SbatLevel_Variable.txt file +as the source of the revocation metadata. Both +SBAT_LATEST_DATE=YYYYMMDDCC and SBAT_AUTOMATIC_DATE=YYYYMMDDCC can be +specified there. These files need to be signed with a certificate that +your shim trusts. These files can be created without the need to +deliver a new shim and can be set to have shim automatically apply a +new revocations whey they are delivered into the system partition. diff --git a/Make.defaults b/Make.defaults index e75cd3c..c5fa32b 100644 --- a/Make.defaults +++ b/Make.defaults @@ -109,7 +109,7 @@ INCLUDES = -nostdinc \ override DEFAULT_FEATUREFLAGS = \ -std=gnu11 \ - -ggdb \ + -ggdb -gdwarf-4 -gstrict-dwarf \ -ffreestanding \ $(shell $(CC) -fmacro-prefix-map=./=./ -E -x c /dev/null >/dev/null 2>&1 && echo -fmacro-prefix-map='$(TOPDIR)/=$(DEBUGSRC)') \ -fno-stack-protector \ @@ -149,10 +149,6 @@ ifneq ($(origin REQUIRE_TPM), undefined) DEFINES += -DREQUIRE_TPM endif -ifneq ($(origin DISABLE_EBS_PROTECTION), undefined) - DEFINES += -DDISABLE_EBS_PROTECTION -endif - ifneq ($(origin DISABLE_REMOVABLE_LOAD_OPTIONS), undefined) DEFINES += -DDISABLE_REMOVABLE_LOAD_OPTIONS endif @@ -201,4 +197,6 @@ ifneq ($(VERBOSE),) export VERBOSE endif +export DEFINES + # vim:filetype=make diff --git a/Makefile b/Makefile index 8283d56..d394439 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ default : all NAME = shim -VERSION = 15.8 +VERSION = 16.0 ifneq ($(origin RELEASE),undefined) DASHRELEASE ?= -$(RELEASE) else @@ -38,12 +38,12 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o globals.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o +OBJS = shim.o globals.o memattrs.o mok.o netboot.o cert.o dp.o loader-proto.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o utils.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer -ORIG_SOURCES = shim.c globals.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S -MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o +ORIG_SOURCES = shim.c globals.c memattrs.c mok.c netboot.c dp.c loader-proto.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S +MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o dp.o ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h) -FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o +FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o utils.o ORIG_FALLBACK_SRCS = fallback.c SBATPATH = $(TOPDIR)/data/sbat.csv @@ -69,16 +69,24 @@ ifneq ($(origin FALLBACK_VERBOSE_WAIT), undefined) CFLAGS += -DFALLBACK_VERBOSE_WAIT=$(FALLBACK_VERBOSE_WAIT) endif -all: confcheck $(TARGETS) +all: confcheck certcheck $(TARGETS) confcheck: ifneq ($(origin EFI_PATH),undefined) $(error EFI_PATH is no longer supported, you must build using the supplied copy of gnu-efi) endif +certcheck: +ifneq ($(origin VENDOR_CERT_FILE), undefined) + @if grep -q "BEGIN" $(VENDOR_CERT_FILE); then \ + echo "$(VENDOR_CERT_FILE) is PEM-format, convert to DER!"; \ + exit 1; \ + fi +endif + compile_commands.json : Makefile Make.rules Make.defaults make clean - bear -- make COMPILER=clang test all + bear -- make COMPILER=clang WARNFLAGS+="-Wno-#warnings" test all sed -i \ -e 's/"-maccumulate-outgoing-args",//g' \ $@ @@ -92,6 +100,7 @@ shim.crt: shim.cer: shim.crt $(OPENSSL) x509 -outform der -in $< -out $@ + .NOTPARALLEL: shim_cert.h shim_cert.h: shim.cer echo "static UINT8 shim_cert[] __attribute__((__unused__)) = {" > $@ @@ -113,8 +122,12 @@ shim.o: $(SOURCES) ifneq ($(origin ENABLE_SHIM_CERT),undefined) shim.o: shim_cert.h endif +# Both of these need to be here so that when TOPDIR is unset, make isn't trying +# to match against ./sbat_var.S, which isn't a target it will ever try to build. +$(TOPDIR)/sbat_var.S sbat_var.S: generated_sbat_var_defs.h shim.o: $(wildcard $(TOPDIR)/*.h) + sbat.%.csv : data/sbat.%.csv $(DOS2UNIX) $(D2UFLAGS) $< $@ tail -c1 $@ | read -r _ || echo >> $@ # ensure a trailing newline @@ -180,6 +193,13 @@ lib/lib.a: | $(TOPDIR)/lib/Makefile $(wildcard $(TOPDIR)/include/*.[ch]) post-process-pe : $(TOPDIR)/post-process-pe.c $(HOSTCC) -std=gnu11 -Og -g3 -Wall -Wextra -Wno-missing-field-initializers -Werror -o $@ $< +generate_sbat_var_defs: $(TOPDIR)/generate_sbat_var_defs.c + $(HOSTCC) -std=gnu11 -Og -g3 -Wall -Wextra -Wno-missing-field-initializers -Werror -o $@ $< + +.NOTPARALLEL: generated_sbat_var_defs.h +generated_sbat_var_defs.h: generate_sbat_var_defs + ./generate_sbat_var_defs $(TOPDIR) > $@ + buildid : $(TOPDIR)/buildid.c $(HOSTCC) -I/usr/include -Og -g3 -Wall -Werror -Wextra -o $@ $< -lelf @@ -261,6 +281,7 @@ endif -j .dynamic -j .rodata -j .rel* \ -j .rela* -j .dyn -j .reloc -j .eh_frame \ -j .vendor_cert -j .sbat -j .sbatlevel \ + --file-alignment 0x1000 \ $(FORMAT) $< $@ ./post-process-pe -vv $(POST_PROCESS_PE_FLAGS) $@ @@ -280,6 +301,7 @@ endif -j .debug_info -j .debug_abbrev -j .debug_aranges \ -j .debug_line -j .debug_str -j .debug_ranges \ -j .note.gnu.build-id \ + --file-alignment 0x1000 \ $< $@ ifneq ($(origin ENABLE_SBSIGN),undefined) @@ -302,7 +324,7 @@ fuzz fuzz-clean fuzz-coverage fuzz-lto : EFI_INCLUDES="$(EFI_INCLUDES)" \ fuzz-clean $@ -test test-clean test-coverage test-lto : +test test-clean test-coverage test-lto : generated_sbat_var_defs.h @make -f $(TOPDIR)/include/test.mk \ COMPILER="$(COMPILER)" \ CROSS_COMPILE="$(CROSS_COMPILE)" \ @@ -346,6 +368,7 @@ clean-lib-objs: clean-shim-objs: @rm -rvf $(TARGET) *.o $(SHIM_OBJS) $(MOK_OBJS) $(FALLBACK_OBJS) $(KEYS) certdb $(BOOTCSVNAME) @rm -vf *.debug *.so *.efi *.efi.* *.tar.* version.c buildid post-process-pe compile_commands.json + @rm -vf generate_sbat_var_defs generated_sbat_var_defs.h @rm -vf Cryptlib/*.[oa] Cryptlib/*/*.[oa] @if [ -d .git ] ; then git clean -f -d -e 'Cryptlib/OpenSSL/*'; fi @@ -361,7 +384,7 @@ clean-cryptlib-objs: clean: clean-shim-objs clean-fuzz-objs clean-test-objs clean-gnu-efi clean-openssl-objs clean-cryptlib-objs clean-lib-objs -GITTAG = $(VERSION) +GITTAG = $(shell echo $(VERSION) | sed 's/~/-/g') test-archive: @./make-archive $(if $(call get-config,shim.origin),--origin "$(call get-config,shim.origin)") --test "$(VERSION)" diff --git a/MokManager.c b/MokManager.c index ffcd6a6..52f5c0a 100644 --- a/MokManager.c +++ b/MokManager.c @@ -142,17 +142,14 @@ static UINT32 count_keys(void *Data, UINTN DataSize) void *end = Data + DataSize; while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { - /* Use ptr arithmetics to ensure bounded access. Do not allow 0 - * SignatureListSize that will cause endless loop. */ - if ((void *)(CertList + 1) > end - || CertList->SignatureListSize == 0) { + /* Use ptr arithmetics to ensure bounded access. */ + if ((void *)(CertList + 1) > end) { console_notify (L"Invalid MOK detected! Ignoring MOK List."); return 0; } - if (CertList->SignatureListSize == 0 || - CertList->SignatureListSize <= CertList->SignatureSize) { + if (CertList->SignatureListSize <= CertList->SignatureSize) { console_errorbox(L"Corrupted signature list"); return 0; } diff --git a/MokVars.txt b/MokVars.txt index baf8db9..6ad8ce7 100644 --- a/MokVars.txt +++ b/MokVars.txt @@ -63,28 +63,51 @@ State variables: MokList: A list of authorized keys and hashes. An EFI_SIGNATURE_LIST as described in the UEFI specification. BS,NV -MokListRT: A copy of MokList made available to the kernel at runtime. RT +MokListRT: A copy of MokList made available to the kernel at runtime. BS,RT MokListX: A list of forbidden keys and hashes. An EFI_SIGNATURE_LIST as described in the UEFI specification. BS,NV -MokListXRT: A copy of MokListX made available to the kernel at runtime. RT +MokListXRT: A copy of MokListX made available to the kernel at runtime. BS,RT MokSBState: An 8-bit unsigned integer. If 1, shim will switch to insecure mode. BS,NV +MokSBStateRT: A copy of MokSBState made available to the kernel at runtime. +This allows the OS to query the shim secure mode setting for its own +verification purposes. BS,RT + MokDBState: An 8-bit unsigned integer. If 1, shim will not use db for verification. BS,NV -MokIgnoreDB: An 8-bit unsigned integer. This allows the OS to query whether -or not to import DB certs for its own verification purposes. +MokIgnoreDB: A copy of MokDBState made available to the kernel at runtime. +This allows the OS to query whether or not to import DB certs for its own +verification purposes. BS,RT MokPWStore: A SHA-256 representation of the password set by the user via MokPW. The user will be prompted to enter this password in order -to interact with MokManager. +to interact with MokManager. BS,NV MokListTrusted: An 8-bit unsigned integer. If 1, it signifies to Linux to trust CA keys in the MokList. BS,NV MokListTrustedRT: A copy of MokListTrusted made available to the kernel -at runtime. RT +at runtime. BS,RT + +HSIStatus: Status of various security features: + heap-is-executable: 0: heap allocations are not executable by default + 1: heap allocations are executable + stack-is-executable: 0: UEFI stack is not executable + 1: UEFI stack is executable + ro-sections-are-writable: 0: read-only sections are not writable + 1: read-only sections are writable + has-memory-attribute-protocol: 0: platform does not provide the EFI Memory Attribute Protocol + 1: platform does provide the EFI Memory Attribute Protocol + has-dxe-services-table: 0: platform does not provide the DXE Services Table + 1: platform does provide the DXE Services Table + has-get-memory-space-descriptor: 0: platform's DST does not populate GetMemorySpaceDescriptor + 1: platform's DST does populate GetMemorySpaceDescriptor + has-set-memory-space-descriptor: 0: platform's DST does not populate SetMemorySpaceDescriptor + 1: platform's DST does populate SetMemorySpaceDescriptor + shim-has-nx-compat-set: 0: the running shim binary does not have NX_COMPAT bit set + 1: the running shim binary does have the NX_COMPAT bit set diff --git a/README.tpm b/README.tpm index 9e830b7..e768dc0 100644 --- a/README.tpm +++ b/README.tpm @@ -13,14 +13,20 @@ PCR7: - MokListX - the Mok denylist, logged as "MokListX" - vendor_dbx - shim's built-in vendor denylist, logged as "dbx" - DB - the system allowlist, logged as "db" - - vendor_db - shim's built-in vendor allowlist, logged as "db" - - MokList the Mok allowlist, logged as "MokList" + - vendor_db - shim's built-in vendor allowlist, logged as "vendor_db" + - MokListRT the runtime Mok allowlist, logged as "MokListRT" - vendor_cert - shim's built-in vendor allowlist, logged as "Shim" - shim_cert - shim's build-time generated allowlist, logged as "Shim" - MokSBState will be extended into PCR7 if it is set, logged as "MokSBState". - SBAT will be extended into PCR7 if it is set, logged as "SBAT" +Note: In the past this document called out that vendor_db was logged as + "db", when in fact the code didn't do that. Since changing the code + risks breaking recorded logs, the documentation is update to reflect + reality. vendor_dbx is in fact logged as "dbx". + + PCR8: - If you're using the grub2 TPM patchset we cary in Fedora, the kernel command line and all grub commands (including all of grub.cfg that gets run) are diff --git a/SBAT.md b/SBAT.md index 998f753..81e27aa 100644 --- a/SBAT.md +++ b/SBAT.md @@ -170,7 +170,7 @@ specific product's component, vendors may ask for a product-specific generation number to be published for one of their product's components. This avoids triggering an industry wide re-publishing of otherwise safe components. -In the example above, 1 is sbat's minimum global generation number. +In the example above, 1 in `sbat,1` is sbat's minimum global generation number. A **product-specific minimum generation number** only applies to the instance of that component that is signed with that product name. Another product's @@ -184,7 +184,8 @@ entire industry that uses that component re-release, just that product's minimum generation number would be incremented and that product's component re-released along with a UEFI variable update specifying that requirement. -In the example above, 1 is grub.acme's product-specific minimum generation number. +In the example above, 1 in `grub.acme,1` is grub.acme's product-specific minimum +generation number. The global and product-specific generation number name spaces are not tied to each other. The global number is managed externally, and the vast majority of @@ -213,68 +214,92 @@ product. Setting a product-specific generation number for such an event eliminates the need for other vendors to have to re-release the binaries for their products with an incremented global number. -However, once the global number is bumped for the next upstream CVE fix there -will be no further need to carry that product-specific generation number. -Satisfying the check of the global number will also exclude any of the older -product-specific binaries. +Both generation numbers should only ever go up; they should never be reset. -For example: There is a global CVE disclosure and all vendors coordinate to -release fixed components on the disclosure date. This release bumps the global -generation number for GRUB to 4. +#### Example: a vendor forking a global project -SBAT revocation data would then require a GRUB with a global generation number -of 4. +Let's imagine a fictional company named "Vendor C" having an active fork of the +upstream GNU GRUB2. Therefore, Vendor C provides their own product-specific +generation number. This is happening at the point in time, when the upstream +product's entry starts with `grub,3`, hence why Vendor C's product ships with +entries similar to: + +``` +sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md +grub,3,Free Software Foundation,[...] +grub.vendorc,1,Vendor C,[...] +``` + +Suddenly there is a global CVE disclosure and all vendors coordinate +to release fixed components on the disclosure date. This release bumps the +global generation number for GRUB from 3 to 4. Vendor C's product's binaries +are now shipped with the entries: + +``` +sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md +grub,4,Free Software Foundation,[...] +grub.vendorc,1,Vendor C,[...] +``` + +After this, the UEFI SBAT revocation variable (named *SbatLevel*) would be +updated to raise the mandatory minimal global generation number for GRUB to 4. However, Vendor C mis-merges the patches into one of their products and does not become aware of the fact that this mis-merge created an additional -vulnerability until after they have published a signed binary in that, -vulnerable, state. +vulnerability until after they have published a signed binary in that vulnerable +state. Vendor C's GRUB binary can now be abused to compromise their systems. -Vendor C's GRUB binary can now be used to compromise anyone's system. +To remedy this, Vendor C will release a security-fixed binary with the same +global generation number and an updated product-specific generation number (set +to 2): -To remedy this, Vendor C will release a fixed binary with the same global -generation number and the product-specific generation number set to 1. +``` +sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md +grub,4,Free Software Foundation,[...] +grub.vendorc,2,Vendor C,[...] +``` -SBAT revocation data would then require a GRUB with a global generation number -of 4, as well as a product-specific generation number of 1 for the product that -had the vulnerable binary. +Again, in the perfect scenario, to provide the perfect security, the UEFI SBAT +revocation variable would then be set, so that GRUB with a global generation +number of only 4 or higher would be able to be booted, as well as Vendor C's +products with their number of only 2 or higher. See for yourself, how this looks +like, in the [SbatLevel_Variable.txt](./SbatLevel_Variable.txt) file. -If and when there is another upstream fix for a CVE that would bump the global -number, this product-specific number can be dropped from the UEFI revocation -variable. +If and when there is another upstream fix for a CVE that would bump the GRUB +global number to 5, this product-specific number can be dropped from the UEFI +*SbatLevel* variable (because the binaries starting with upstream's `grub,4` +entry would get denylisted anyway), but with the current consensus it's +important to keep the product-specific number shipped with the product's binary, +like in the case of Vendor C: -If this same Vendor C has a similar event after the global number is -incremented, they would again set their product-specific or **version-specific -number** to 1. If they have a second event on the same component, they would -set their product-specific or version-specific number to 2. +``` +sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md +grub,5,Free Software Foundation,[...] +grub.vendorc,2,Vendor C,[...] +``` -In such an event, a vendor would set the product-specific or version-specific -generation number based on whether the mis-merge occurred in all of their -branches or in just a subset of them. The goal is generally to limit end -customer impact with as few re-releases as possible, while not creating an -unnecessarily large UEFI revocation variable payload. +The goal is generally to limit end user impact with as few +re-releases as possible, while not creating an unnecessarily large UEFI +revocation variable payload. -| | prior to
disclosure\* | after
disclosure | after Vendor C's
first update | after Vendor C's
second update | after next global
disclosure | -|--------------------------------------------------------------------------------------|--------------------------|---------------------|----------------------------------|-----------------------------------|---------------------------------| -| GRUB global
generation number in
artifacts .sbat section | 3 | 4 | 4 | 4 | 5 | -| Vendor C's product-specific
generation number in artifact's
.sbat section | 1 | 1 | 2 | 3 | 1 | -| GRUB global
generation number in
UEFI SBAT revocation variable | 3 | 4 | 4 | 4 | 5 | -| Vendor C's product-specific
generation number in
UEFI SBAT revocation variable | not set | not set | 2 | 3 | not set | +| | prior to GRUB
first disclosure\* | after GRUB
first disclosure\* | after Vendor C's
first update | after Vendor C's
second update | after GRUB
second disclosure\* | +|--------------------------------------------------------------------------------------|-------------------------------------|----------------------------------|----------------------------------|-----------------------------------|-----------------------------------| +| GRUB global
generation number in
artifacts .sbat section | 3 | 4 | 4 | 4 | 5 | +| Vendor C's product
generation number in
artifact's .sbat section | 1 | 1 | 2 | 3 | 3 | +| GRUB global
generation number in
UEFI *SbatLevel* variable | 3 | 4 | 4 | 4 | 5 | +| Vendor C's product
generation number in
UEFI *SbatLevel* variable | not set | not set | 2 | 3 | not set | \* A disclosure is the event/date where a CVE and fixes for it are made public. -The product-specific generation number does not reset and continues to -monotonically increase over the course of these non-global events. Continuity of more -specific generation numbers must be maintained in this way in order to satisfy -checks against older revocation data. +The product-specific generation number does not reset and continues to increase +over the course of these non-global events. Continuity of more specific +generation numbers must be maintained in this way in order to satisfy checks +against older revocation data. -The variable payload will be stored publicly in the shim source base and -identify the global generation associated with a product or version-specific -one. The payload is also built into shim to additionally limit exposure. - -At this time of writing, all version-numbers are set to 1. Presumably at some point, -updated numbers will be published on the respective websites of the associated vendors -and components. +The UEFI *SbatLevel* variable payload will be stored publicly in the shim source +base and identify the global generation associated with a product or +version-specific one. The payload is also built into shim to additionally limit +exposure. #### Retiring Signed Releases diff --git a/SbatLevel_Variable.txt b/SbatLevel_Variable.txt index 42a388e..7afdcd0 100644 --- a/SbatLevel_Variable.txt +++ b/SbatLevel_Variable.txt @@ -1,6 +1,15 @@ -In order to apply SBAT based revocations on systems that will never -run shim, code running in boot services context needs to set the -following variable: +This file is the single source for SbatLevel revocations the format +follows the variable payload and should not have any leading or +trailing whitespace on the same line. + +Short descriptions of the revocations as well as CVE assignments (when +available) should be provided when an entry is added. + +On systems that run shim, shim will manage these revocations. Sytems +that never run shim, primarily Windows, but this applies to any OS +that supports UEFI Secure Boot under the UEFI CA without shim can +apply SBAT based revocations by setting the following variable +from code running in boot services context. Name: SbatLevel Attributes: (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) @@ -97,12 +106,22 @@ shim,4 grub,3 grub.debian,4 -Since http boot shim CVE is considerably more serious than then GRUB -ntfs CVEs shim is delivering the shim revocation without the updated -GRUB revocation as a latest payload. -To revoke both the impacted shim and impacted GRUB binaries: +Revocations for: + - January 2024 shim CVEs + - October 2023 grub CVEs + - Debian/Ubuntu (peimage) CVE-2024-2312 -sbat,1,2024 +sbat,1,2024040900 shim,4 grub,4 +grub.peimage,2 + + +Revocations for: + - Februady 2025 GRUB CVEs + +sbat,1,2025021800 +shim,4 +grub,5 + diff --git a/commit b/commit new file mode 100644 index 0000000..ef14204 --- /dev/null +++ b/commit @@ -0,0 +1 @@ +18d98bfb34be583a5fe2987542e4b15e0db9cb61 \ No newline at end of file diff --git a/dp.c b/dp.c new file mode 100644 index 0000000..3fc46f8 --- /dev/null +++ b/dp.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * dp.c - device path helpers + * Copyright Peter Jones + */ + +#include "shim.h" + +int +is_removable_media_path(EFI_LOADED_IMAGE *li) +{ + unsigned int pathlen = 0; + CHAR16 *bootpath = NULL; + int ret = 0; + + bootpath = DevicePathToStr(li->FilePath); + + /* Check the beginning of the string and the end, to avoid + * caring about which arch this is. */ + /* I really don't know why, but sometimes bootpath gives us + * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here... + */ + if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && + StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15) && + StrnCaseCmp(bootpath, L"EFI\\BOOT\\BOOT", 13) && + StrnCaseCmp(bootpath, L"EFI\\BOOT\\/BOOT", 14)) + goto error; + + pathlen = StrLen(bootpath); + if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) + goto error; + + ret = 1; + +error: + if (bootpath) + FreePool(bootpath); + + return ret; +} + + +// vim:fenc=utf-8:tw=75:noet diff --git a/errlog.c b/errlog.c index 3c5e0af..b43a4bc 100644 --- a/errlog.c +++ b/errlog.c @@ -99,4 +99,185 @@ ClearErrors(VOID) errs = NULL; } +static size_t +format_error_log(UINT8 *dest, size_t dest_sz) +{ + size_t err_log_sz = 0; + size_t pos = 0; + + for (UINTN i = 0; i < nerrs; i++) + err_log_sz += StrSize(errs[i]); + + if (!dest || dest_sz < err_log_sz) + return err_log_sz; + + ZeroMem(dest, err_log_sz); + for (UINTN i = 0; i < nerrs; i++) { + UINTN sz = StrSize(errs[i]); + CopyMem(&dest[pos], errs[i], sz); + pos += sz; + } + + return err_log_sz; +} + +static UINT8 *debug_log = NULL; +static size_t debug_log_sz = 0; +static size_t debug_log_alloc = 0; + +UINTN EFIAPI +log_debug_print(const CHAR16 *fmt, ...) +{ + ms_va_list args; + CHAR16 *buf; + size_t buf_sz; + UINTN ret = 0; + + ms_va_start(args, fmt); + buf = VPoolPrint(fmt, args); + if (!buf) + return 0; + ms_va_end(args); + + ret = StrLen(buf); + buf_sz = StrSize(buf); + if (debug_log_sz + buf_sz > debug_log_alloc) { + size_t new_alloc_sz = debug_log_alloc; + CHAR16 *new_debug_log; + + new_alloc_sz += buf_sz; + new_alloc_sz = ALIGN_UP(new_alloc_sz, EFI_PAGE_SIZE); + + new_debug_log = ReallocatePool(debug_log, debug_log_alloc, new_alloc_sz); + if (!new_debug_log) + return 0; + debug_log = (UINT8 *)new_debug_log; + debug_log_alloc = new_alloc_sz; + } + + CopyMem(&debug_log[debug_log_sz], buf, buf_sz); + debug_log_sz += buf_sz; + FreePool(buf); + return ret; +} + +static size_t +format_debug_log(UINT8 *dest, size_t dest_sz) +{ + if (!dest || dest_sz < debug_log_sz) + return debug_log_sz; + + ZeroMem(dest, debug_log_sz); + CopyMem(dest, debug_log, debug_log_sz); + return debug_log_sz; +} + +void +replace_config_table(EFI_CONFIGURATION_TABLE *CT, EFI_PHYSICAL_ADDRESS new_table, UINTN new_table_pages) +{ + EFI_GUID bogus_guid = { 0x29f2f0db, 0xd025, 0x4aa6, { 0x99, 0x58, 0xa0, 0x21, 0x8b, 0x1d, 0xec, 0x0e }}; + EFI_STATUS efi_status; + + if (CT) { + CopyMem(&CT->VendorGuid, &bogus_guid, sizeof(bogus_guid)); + if (CT->VendorTable && + CT->VendorTable == (void *)(uintptr_t)mok_config_table) { + BS->FreePages(mok_config_table, mok_config_table_pages); + CT->VendorTable = NULL; + } + } + + efi_status = BS->InstallConfigurationTable(&MOK_VARIABLE_STORE, + (void *)(uintptr_t)new_table); + if (EFI_ERROR(efi_status)) { + console_print(L"Could not re-install MoK configuration table: %r\n", efi_status); + } else { + mok_config_table = new_table; + mok_config_table_pages = new_table_pages; + } +} + +void +save_logs(void) +{ + struct mok_variable_config_entry *cfg_table = NULL; + struct mok_variable_config_entry *new_table = NULL; + struct mok_variable_config_entry *entry = NULL; + EFI_PHYSICAL_ADDRESS physaddr = 0; + UINTN new_table_pages = 0; + size_t new_table_sz; + UINTN pos = 0; + EFI_STATUS efi_status; + size_t errlog_sz, dbglog_sz; + + errlog_sz = format_error_log(NULL, 0); + dbglog_sz = format_debug_log(NULL, 0); + + if (errlog_sz == 0 && dbglog_sz == 0) { + console_print(L"No console or debug log?!?!?\n"); + return; + } + + for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *CT; + CT = &ST->ConfigurationTable[i]; + + if (CompareGuid(&MOK_VARIABLE_STORE, &CT->VendorGuid) == 0) { + cfg_table = CT->VendorTable; + break; + } + CT = NULL; + } + + entry = cfg_table; + while (entry && entry->name[0] != 0) { + size_t entry_sz; + entry = (struct mok_variable_config_entry *)((uintptr_t)cfg_table + pos); + + if (entry->name[0] != 0) { + entry_sz = sizeof(*entry); + entry_sz += entry->data_size; + pos += entry_sz; + } + } + + new_table_sz = pos + + (errlog_sz ? sizeof(*entry) + errlog_sz : 0) + + (dbglog_sz ? sizeof(*entry) + dbglog_sz : 0) + + sizeof(*entry); + new_table = NULL; + new_table_pages = ALIGN_UP(new_table_sz + 4*EFI_PAGE_SIZE, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + efi_status = BS->AllocatePages(AllocateAnyPages, EfiRuntimeServicesData, new_table_pages, &physaddr); + if (EFI_ERROR(efi_status)) { + perror(L"Couldn't allocate %llu pages\n", new_table_pages); + return; + } + new_table = (void *)(uintptr_t)physaddr; + if (!new_table) + return; + ZeroMem(new_table, new_table_pages * EFI_PAGE_SIZE); + CopyMem(new_table, cfg_table, pos); + + entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos); + if (errlog_sz) { + strcpy(entry->name, "shim-err.txt"); + entry->data_size = errlog_sz; + format_error_log(&entry->data[0], errlog_sz); + + pos += sizeof(*entry) + errlog_sz; + entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos); + } + if (dbglog_sz) { + strcpy(entry->name, "shim-dbg.txt"); + entry->data_size = dbglog_sz; + format_debug_log(&entry->data[0], dbglog_sz); + + pos += sizeof(*entry) + dbglog_sz; + + entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos); + } + + replace_config_table((EFI_CONFIGURATION_TABLE *)cfg_table, physaddr, new_table_pages); +} + // vim:fenc=utf-8:tw=75 diff --git a/fallback.c b/fallback.c index 600cc7a..86ebe23 100644 --- a/fallback.c +++ b/fallback.c @@ -94,89 +94,6 @@ FindSubDevicePath(EFI_DEVICE_PATH *In, UINT8 Type, UINT8 SubType, return EFI_NOT_FOUND; } -static EFI_STATUS -get_file_size(EFI_FILE_HANDLE fh, UINTN *retsize) -{ - EFI_STATUS efi_status; - void *buffer = NULL; - UINTN bs = 0; - - /* The API here is "Call it once with bs=0, it fills in bs, - * then allocate a buffer and ask again to get it filled. */ - efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, NULL); - if (EFI_ERROR(efi_status) && efi_status != EFI_BUFFER_TOO_SMALL) - return efi_status; - if (bs == 0) - return EFI_SUCCESS; - - buffer = AllocateZeroPool(bs); - if (!buffer) { - console_print(L"Could not allocate memory\n"); - return EFI_OUT_OF_RESOURCES; - } - efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, buffer); - /* This checks *either* the error from the first GetInfo, if it isn't - * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo - * call in *any* case. */ - if (EFI_ERROR(efi_status)) { - console_print(L"Could not get file info: %r\n", efi_status); - if (buffer) - FreePool(buffer); - return efi_status; - } - EFI_FILE_INFO *fi = buffer; - *retsize = fi->FileSize; - FreePool(buffer); - return EFI_SUCCESS; -} - -EFI_STATUS -read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs) -{ - EFI_FILE_HANDLE fh2; - EFI_STATUS efi_status; - - efi_status = fh->Open(fh, &fh2, fullpath, EFI_FILE_READ_ONLY, 0); - if (EFI_ERROR(efi_status)) { - console_print(L"Couldn't open \"%s\": %r\n", fullpath, efi_status); - return efi_status; - } - - UINTN len = 0; - CHAR16 *b = NULL; - efi_status = get_file_size(fh2, &len); - if (EFI_ERROR(efi_status)) { - console_print(L"Could not get file size for \"%s\": %r\n", - fullpath, efi_status); - fh2->Close(fh2); - return efi_status; - } - - if (len > 1024 * PAGE_SIZE) { - fh2->Close(fh2); - return EFI_BAD_BUFFER_SIZE; - } - - b = AllocateZeroPool(len + 2); - if (!b) { - console_print(L"Could not allocate memory\n"); - fh2->Close(fh2); - return EFI_OUT_OF_RESOURCES; - } - - efi_status = fh->Read(fh, &len, b); - if (EFI_ERROR(efi_status)) { - FreePool(b); - fh2->Close(fh2); - console_print(L"Could not read file: %r\n", efi_status); - return efi_status; - } - *buffer = b; - *bs = len; - fh2->Close(fh2); - return EFI_SUCCESS; -} - EFI_STATUS make_full_path(CHAR16 *dirname, CHAR16 *filename, CHAR16 **out, UINT64 *outlen) { @@ -202,8 +119,8 @@ make_full_path(CHAR16 *dirname, CHAR16 *filename, CHAR16 **out, UINT64 *outlen) return EFI_SUCCESS; } -CHAR16 *bootorder = NULL; -int nbootorder = 0; +UINT16 *bootorder = NULL; +UINTN nbootorder = 0; EFI_DEVICE_PATH *first_new_option = NULL; VOID *first_new_option_args = NULL; @@ -211,7 +128,8 @@ UINTN first_new_option_size = 0; EFI_STATUS add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp, - CHAR16 *filename, CHAR16 *label, CHAR16 *arguments) + CHAR16 *filename, CHAR16 *label, CHAR16 *arguments, + UINT16 **newbootentries, UINTN *nnewbootentries) { static int i = 0; CHAR16 varname[] = L"Boot0000"; @@ -226,9 +144,11 @@ add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp, void *var = LibGetVariable(varname, &GV_GUID); if (!var) { + int arg_size = StrLen(arguments) ? StrLen(arguments) * sizeof (CHAR16) + + sizeof (CHAR16) : 0; int size = sizeof(UINT32) + sizeof (UINT16) + StrLen(label)*2 + 2 + DevicePathSize(hddp) + - StrLen(arguments) * 2; + arg_size; CHAR8 *data, *cursor; cursor = data = AllocateZeroPool(size + 2); @@ -252,7 +172,7 @@ add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp, if (!first_new_option) { first_new_option = DuplicateDevicePath(fulldp); first_new_option_args = StrDuplicate(arguments); - first_new_option_size = StrLen(arguments) * sizeof (CHAR16); + first_new_option_size = arg_size; } efi_status = RT->SetVariable(varname, &GV_GUID, @@ -269,24 +189,21 @@ add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp, return efi_status; } - CHAR16 *newbootorder = AllocateZeroPool(sizeof (CHAR16) - * (nbootorder + 1)); + UINT16 *newbootorder = AllocateZeroPool(sizeof (UINT16) * (*nnewbootentries + 1)); if (!newbootorder) return EFI_OUT_OF_RESOURCES; - int j = 0; - newbootorder[0] = i & 0xffff; - if (nbootorder) { - for (j = 0; j < nbootorder; j++) - newbootorder[j+1] = bootorder[j]; - FreePool(bootorder); - } - bootorder = newbootorder; - nbootorder += 1; - VerbosePrint(L"nbootorder: %d\nBootOrder: ", - nbootorder); - for (j = 0 ; j < nbootorder ; j++) - VerbosePrintUnprefixed(L"%04x ", bootorder[j]); + UINTN j = 0; + CopyMem(newbootorder, *newbootentries, sizeof (UINT16) * (*nnewbootentries)); + newbootorder[*nnewbootentries] = i & 0xffff; + if (*newbootentries) + FreePool(*newbootentries); + *newbootentries = newbootorder; + *nnewbootentries += 1; + VerbosePrint(L"nnewbootentries: %d\nnewbootentries: ", + *nnewbootentries); + for (j = 0 ; j < *nnewbootentries ; j++) + VerbosePrintUnprefixed(L"%04x ", (*newbootentries)[j]); VerbosePrintUnprefixed(L"\n"); return EFI_SUCCESS; @@ -400,9 +317,11 @@ find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp, UINT16 *optnum) { unsigned int label_size = StrLen(label)*2 + 2; + int arg_size = StrLen(arguments) ? StrLen(arguments) * sizeof (CHAR16) + + sizeof (CHAR16) : 0; unsigned int size = sizeof(UINT32) + sizeof (UINT16) + label_size + DevicePathSize(dp) + - StrLen(arguments) * 2; + arg_size; CHAR8 *data = AllocateZeroPool(size + 2); if (!data) @@ -486,7 +405,7 @@ find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp, if (!first_new_option) { first_new_option = DuplicateDevicePath(fulldp); first_new_option_args = StrDuplicate(arguments); - first_new_option_size = StrLen(arguments) * sizeof (CHAR16); + first_new_option_size = arg_size; } *optnum = xtoi(varname + 4); @@ -503,13 +422,13 @@ find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp, EFI_STATUS set_boot_order(void) { - CHAR16 *oldbootorder; + UINT16 *oldbootorder; UINTN size; oldbootorder = LibGetVariableAndSize(L"BootOrder", &GV_GUID, &size); if (oldbootorder) { - int i; - nbootorder = size / sizeof (CHAR16); + UINTN i; + nbootorder = size / sizeof (UINT16); bootorder = oldbootorder; VerbosePrint(L"Original nbootorder: %d\nOriginal BootOrder: ", @@ -523,23 +442,41 @@ set_boot_order(void) } EFI_STATUS -update_boot_order(void) +update_boot_order(UINT16 *newbootentries, UINTN nnewbootentries) { UINTN size; UINTN len = 0; - CHAR16 *newbootorder = NULL; + UINT16 *newbootorder = NULL; EFI_STATUS efi_status; + UINTN i; - size = nbootorder * sizeof(CHAR16); + VerbosePrint(L"old boot order: "); + for (i = 0; i < nbootorder; i++) + VerbosePrintUnprefixed(L"%04x ", bootorder[i]); + VerbosePrintUnprefixed(L"\n"); + VerbosePrint(L"new boot entries: "); + for (i = 0; i < nnewbootentries; i++) + VerbosePrintUnprefixed(L"%04x ", newbootentries[i]); + VerbosePrintUnprefixed(L"\n"); + + size = nbootorder * sizeof(UINT16) + nnewbootentries * sizeof(UINT16); newbootorder = AllocateZeroPool(size); if (!newbootorder) return EFI_OUT_OF_RESOURCES; - CopyMem(newbootorder, bootorder, size); + for (i = 0 ; i < nnewbootentries; i++) { + newbootorder[i] = newbootentries[i]; + } + CopyMem(&newbootorder[i], bootorder, nbootorder * sizeof(UINT16)); - VerbosePrint(L"nbootorder: %d\nBootOrder: ", size / sizeof (CHAR16)); - UINTN j; - for (j = 0 ; j < size / sizeof (CHAR16); j++) - VerbosePrintUnprefixed(L"%04x ", newbootorder[j]); + if (bootorder) + FreePool(bootorder); + nbootorder = nnewbootentries + nbootorder; + bootorder = newbootorder; + + VerbosePrint(L"updated nbootorder: %d\n", nbootorder); + VerbosePrint(L"updated bootoder: "); + for (i = 0; i < nbootorder; i++) + VerbosePrintUnprefixed(L"%04x ", bootorder[i]); VerbosePrintUnprefixed(L"\n"); efi_status = RT->GetVariable(L"BootOrder", &GV_GUID, NULL, &len, NULL); if (efi_status == EFI_BUFFER_TOO_SMALL) @@ -549,13 +486,13 @@ update_boot_order(void) EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, - size, newbootorder); - FreePool(newbootorder); + size, bootorder); return efi_status; } EFI_STATUS -add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments) +add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments, + UINT16 **newbootentries, UINTN *nnewbootentries) { CHAR16 *fullpath = NULL; UINT64 pathlen = 0; @@ -614,19 +551,19 @@ add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *argum arguments, &option); if (EFI_ERROR(efi_status)) { add_boot_option(dp, full_device_path, fullpath, label, - arguments); + arguments, newbootentries, nnewbootentries); goto done; } UINT16 bootnum; - CHAR16 *newbootorder; + UINT16 *newbootorder; /* Search for the option in the current bootorder */ for (bootnum = 0; bootnum < nbootorder; bootnum++) if (bootorder[bootnum] == option) break; if (bootnum == nbootorder) { /* Option not found, prepend option and copy the rest */ - newbootorder = AllocateZeroPool(sizeof(CHAR16) + newbootorder = AllocateZeroPool(sizeof(UINT16) * (nbootorder + 1)); if (!newbootorder) { efi_status = EFI_OUT_OF_RESOURCES; @@ -634,30 +571,30 @@ add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *argum } newbootorder[0] = option; CopyMem(newbootorder + 1, bootorder, - sizeof(CHAR16) * nbootorder); + sizeof(UINT16) * nbootorder); FreePool(bootorder); bootorder = newbootorder; nbootorder += 1; } else { /* Option found, put first and slice the rest */ newbootorder = AllocateZeroPool( - sizeof(CHAR16) * nbootorder); + sizeof(UINT16) * nbootorder); if (!newbootorder) { efi_status = EFI_OUT_OF_RESOURCES; goto done; } newbootorder[0] = option; CopyMem(newbootorder + 1, bootorder, - sizeof(CHAR16) * bootnum); + sizeof(UINT16) * bootnum); CopyMem(newbootorder + 1 + bootnum, bootorder + bootnum + 1, - sizeof(CHAR16) * (nbootorder - bootnum - 1)); + sizeof(UINT16) * (nbootorder - bootnum - 1)); FreePool(bootorder); bootorder = newbootorder; } VerbosePrint(L"New nbootorder: %d\nBootOrder: ", nbootorder); - for (int i = 0 ; i < nbootorder ; i++) + for (UINTN i = 0 ; i < nbootorder ; i++) VerbosePrintUnprefixed(L"%04x ", bootorder[i]); VerbosePrintUnprefixed(L"\n"); @@ -672,7 +609,8 @@ done: } EFI_STATUS -populate_stanza(CHAR16 *dirname, CHAR16 *filename UNUSED, CHAR16 *csv) +populate_stanza(CHAR16 *dirname, CHAR16 *filename UNUSED, CHAR16 *csv, + UINT16 **newbootentries, UINTN *nnewbootentries) { CHAR16 *file = csv; VerbosePrint(L"CSV data: \"%s\"\n", csv); @@ -696,13 +634,14 @@ populate_stanza(CHAR16 *dirname, CHAR16 *filename UNUSED, CHAR16 *csv) /* This one is optional, so don't check if comma2 is 0 */ VerbosePrint(L"arguments: \"%s\"\n", arguments); - add_to_boot_list(dirname, file, label, arguments); + add_to_boot_list(dirname, file, label, arguments, newbootentries, nnewbootentries); return EFI_SUCCESS; } EFI_STATUS -try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename) +try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename, + UINT16 **newbootentries, UINTN *nnewbootentries) { CHAR16 *fullpath = NULL; UINT64 pathlen = 0; @@ -751,7 +690,7 @@ try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename) CHAR16 c = start[l]; start[l] = L'\0'; - populate_stanza(dirname, filename, start); + populate_stanza(dirname, filename, start, newbootentries, nnewbootentries); start[l] = c; start += l; @@ -762,7 +701,8 @@ try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename) } EFI_STATUS -find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname) +find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, + UINT16 **newbootentries, UINTN *nnewbootentries) { EFI_STATUS efi_status; void *buffer = NULL; @@ -861,7 +801,8 @@ find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname) console_print(L"Couldn't open \\EFI\\%s\\%s: %r\n", dirname, bootarchcsv, efi_status); } else { - efi_status = try_boot_csv(fh2, dirname, bootarchcsv); + efi_status = try_boot_csv(fh2, dirname, bootarchcsv, + newbootentries, nnewbootentries); fh2->Close(fh2); if (EFI_ERROR(efi_status)) console_print(L"Could not process \\EFI\\%s\\%s: %r\n", @@ -876,7 +817,8 @@ find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname) console_print(L"Couldn't open \\EFI\\%s\\%s: %r\n", dirname, bootcsv, efi_status); } else { - efi_status = try_boot_csv(fh2, dirname, bootcsv); + efi_status = try_boot_csv(fh2, dirname, bootcsv, + newbootentries, nnewbootentries); fh2->Close(fh2); if (EFI_ERROR(efi_status)) console_print(L"Could not process \\EFI\\%s\\%s: %r\n", @@ -891,6 +833,8 @@ find_boot_options(EFI_HANDLE device) { EFI_STATUS efi_status; EFI_FILE_IO_INTERFACE *fio = NULL; + UINT16 *newbootentries = NULL; + UINTN nnewbootentries = 0; efi_status = BS->HandleProtocol(device, &FileSystemProtocol, (void **) &fio); @@ -982,7 +926,8 @@ find_boot_options(EFI_HANDLE device) continue; } - efi_status = find_boot_csv(fh3, fi->FileName); + efi_status = find_boot_csv(fh3, fi->FileName, + &newbootentries, &nnewbootentries); fh3->Close(fh3); FreePool(buffer); buffer = NULL; @@ -991,8 +936,8 @@ find_boot_options(EFI_HANDLE device) } while (1); - if (!EFI_ERROR(efi_status) && nbootorder > 0) - efi_status = update_boot_order(); + if (!EFI_ERROR(efi_status) && (nbootorder > 0 || nnewbootentries > 0)) + efi_status = update_boot_order(newbootentries, nnewbootentries); fh2->Close(fh2); fh->Close(fh); diff --git a/fuzz-pe-relocate.c b/fuzz-pe-relocate.c index 1f62234..09d3833 100644 --- a/fuzz-pe-relocate.c +++ b/fuzz-pe-relocate.c @@ -28,7 +28,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) memcpy(data_copy, data, size); data_copy[size] = 0; - status = read_header(data_copy, size, &context); + status = read_header(data_copy, size, &context, true); free(data_copy); diff --git a/generate_sbat_var_defs.c b/generate_sbat_var_defs.c new file mode 100644 index 0000000..1258e1b --- /dev/null +++ b/generate_sbat_var_defs.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +/* + * This generates the header files that produce the actual revocation + * string payload. On the one hand this grabs the defintions from the + * human readable SbatLevel_Variable.txt file which is nice. On the other + * hand it's one off c code. + */ + +#include +#include +#include + +typedef struct sbat_revocation sbat_revocation; + +struct sbat_revocation { + int date; + char *revocations; + sbat_revocation *next; +}; + +static sbat_revocation *revlisthead; + +int +readfile(char *SbatLevel_Variable) +{ + FILE *varfilep; + char line[1024]; + int date; + int ret = -1; + + unsigned int revocationsp = 0; + + sbat_revocation *revlistlast = NULL; + sbat_revocation *revlistentry = NULL; + + revlisthead = NULL; + + varfilep = fopen(SbatLevel_Variable, "r"); + if (varfilep == NULL) + return -1; + + while (fgets(line, sizeof(line), varfilep) != NULL) { + if (sscanf(line, "sbat,1,%d\n", &date) && strlen(line) == 18) { + revlistentry = calloc(1, sizeof(sbat_revocation)); + if (revlistentry == NULL) + goto err; + if (revlisthead == NULL) + revlisthead = revlistentry; + else + revlistlast->next = revlistentry; + + revlistlast = revlistentry; + + revlistentry->date = date; + while (line[0] != '\n' && + fgets(line, sizeof(line), varfilep) != NULL) { + char *new = NULL; + new = realloc(revlistentry->revocations, + revocationsp + strlen(line) + 1); + if (new == NULL) { + ret = -1; + goto err; + } + revlistentry->revocations = new; + if (strlen(line) > 1) { + line[strlen(line) - 1] = 0; + sprintf(revlistentry->revocations + + revocationsp, + "%s\\n", line); + revocationsp = + revocationsp + strlen(line) + 2; + } + } + revocationsp = 0; + } + } + + ret = 1; +err: + if (ret < 0 && revlisthead) { + sbat_revocation *rle = revlisthead; + while (rle) { + sbat_revocation *next = rle->next; + if (rle->revocations) + free(rle->revocations); + free(rle); + rle = next; + } + revlisthead = NULL; + } + fclose(varfilep); + return ret; +} + +int +writefile() +{ + int epochfound = 0; + int epochdate = 2021030218; + int latestdate = 0; + + sbat_revocation *revlistentry; + sbat_revocation *latest_revlistentry = NULL; + + revlistentry = revlisthead; + + while (revlistentry != NULL) { + if (revlistentry->date == epochdate) { + printf("#ifndef GEN_SBAT_VAR_DEFS_H_\n" + "#define GEN_SBAT_VAR_DEFS_H_\n" + "#ifndef ENABLE_SHIM_DEVEL\n\n" + "#ifndef SBAT_AUTOMATIC_DATE\n" + "#define SBAT_AUTOMATIC_DATE 2024040900\n" + "#endif /* SBAT_AUTOMATIC_DATE */\n" + "#if SBAT_AUTOMATIC_DATE == %d\n" + "#define SBAT_VAR_AUTOMATIC_REVOCATIONS\n", + revlistentry->date); + epochfound = 1; + } else if (epochfound == 1) { + printf("#elif SBAT_AUTOMATIC_DATE == %d\n" + "#define SBAT_VAR_AUTOMATIC_REVOCATIONS \"%s\"\n", + revlistentry->date, + revlistentry->revocations); + } + if (revlistentry->date > latestdate) { + latest_revlistentry = revlistentry; + latestdate = revlistentry->date; + } + revlistentry = revlistentry->next; + } + + if (epochfound == 0 || !latest_revlistentry) + return -1; + + printf("#else\n" + "#error \"Unknown SBAT_AUTOMATIC_DATE\"\n" + "#endif /* SBAT_AUTOMATIC_DATE == */\n\n" + "#define SBAT_VAR_AUTOMATIC_DATE QUOTEVAL(SBAT_AUTOMATIC_DATE)\n" + "#define SBAT_VAR_AUTOMATIC \\\n" + " SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE \"\\n\" \\\n" + " SBAT_VAR_AUTOMATIC_REVOCATIONS\n\n"); + + printf("#define SBAT_VAR_LATEST_DATE \"%d\"\n" + "#define SBAT_VAR_LATEST_REVOCATIONS \"%s\"\n", + latest_revlistentry->date, + latest_revlistentry->revocations); + + printf("#define SBAT_VAR_LATEST \\\n" + " SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE \"\\n\" \\\n" + " SBAT_VAR_LATEST_REVOCATIONS\n\n" + "#endif /* !ENABLE_SHIM_DEVEL */\n" + "#endif /* !GEN_SBAT_VAR_DEFS_H_ */\n"); + + return 0; +} + + +int +main(int argc, char *argv[]) +{ + char SbatLevel_Variable[2048]; + + if (argc == 2) + snprintf(SbatLevel_Variable, 2048, "%s/SbatLevel_Variable.txt", argv[1]); + else + snprintf(SbatLevel_Variable, 2048, "SbatLevel_Variable.txt"); + + if (readfile(SbatLevel_Variable)) + return writefile(); + else + return -1; +} diff --git a/globals.c b/globals.c index b4e80dd..1119712 100644 --- a/globals.c +++ b/globals.c @@ -24,13 +24,18 @@ UINT8 *build_cert; * indicator of how an image has been verified */ verification_method_t verification_method; -int loader_is_participating; + +SHIM_IMAGE_LOADER shim_image_loader_interface; UINT8 user_insecure_mode; +UINTN hsi_status = 0; UINT8 ignore_db; UINT8 trust_mok_list; UINT8 mok_policy = 0; UINT32 verbose = 0; +EFI_PHYSICAL_ADDRESS mok_config_table = 0; +UINTN mok_config_table_pages = 0; + // vim:fenc=utf-8:tw=75:noet diff --git a/gnu-efi/ia32/gnuefi/crt0-efi-ia32.o b/gnu-efi/ia32/gnuefi/crt0-efi-ia32.o deleted file mode 100644 index 8dfe2ab3b1a36196f8b11c5c933d873aaa8e97ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 776 zcma)4%SyvQ6g}xfTM?{o6a;l66ot@&;KGfMDi%}(5tJ^%)J9t{3e5!CmAG)xouA;R z2wnLhLO()?_oSH`g9{JbIrrqAJ9m=F)8WaHWmzz=kj0Mz-eR#88OkW4fXl&W7|e!2 zVZ?`5gJC`={5+hI_2PW=6^3D>@$w$zqx54)7s2{p;0SwCurNKznPf4S(u)aQlGCpI z(m*+-t0}#c(y-lLN5gIfp4YF$T!rIn)zzqb>dxI)z6+;zeYSU6KY-J9?;mfJcH!Q3 z98Yx~upoP8^1u>#3Roq}i)@e!ve2#1^tGk19aYAQ@qGiv CC`lv$ diff --git a/gnu-efi/ia32/gnuefi/libgnuefi.a b/gnu-efi/ia32/gnuefi/libgnuefi.a deleted file mode 100644 index e1a6097cde109290141c9266ac4d9f10e95847aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1034 zcma)3&ubGw6n?YWmTinRts=z=PVrz1cHK>r5JjY+O{-8mSlWW3=_XxCu}LJEfEIrg zmZ)X_3;zm_k@~B8k=~?dFXo~b5s~P7yEAk>>4P`#d+&Yo&CHvzi%z3lxfDOg|28|D zvD2ws_I!$SD`-Vm&h4Dyuf_p@p)VI3?nb@r2p0lSTz95YW}TFyYAMUgV8yaf5Z7!v z&3P`T}z*}L1HSNuZ4e`qW(+WTHd_wW8{ ze;3^A4U44LJ1UNNy`fgZPqLrget)E^`G(gqx}UZ`4!mpWcBgx~y(dnzcg6Ac{-dv3 zo4V2J_ciu|<=Fd)58W>>@Z7B%w~9A+V18!i0w14sOY2UJ+qOAnT9fvKM0BlV^0I96 z8n6cj8%^l?Gu(d`>nCNeMlnBVw6(dzeX~zvg(te#r(NI4zsbMNzs3!c7Yzqf0np*w z?`V&vG$;$CJxo)Jd_)-z{)b~<({M4H_p8DwVHAxpRumHMJptyLBOEYG%_f-TdbR4- zuvV)J*POq)G}#nR`3{(Pi0M{}w;N8?Ma}=TQRLBk`X!(KGnk3Y)mz6fCmfUY=Q66G zcxSL+p#O_Ue?WUIbTZOA6`*jT9(hK_unO=}0U3!yb_Ja3 diff --git a/gnu-efi/ia32/gnuefi/reloc_ia32.o b/gnu-efi/ia32/gnuefi/reloc_ia32.o deleted file mode 100644 index 58fb22b5aadd2e6f035951e5d39f1d0aff054cc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 888 zcma)3&r2Io5T3VL!`eohs1&J%ru0w)_2K#hiYJ3cTM!QwTd1^Q6IUXdM3NWKP+MWy zwzB_4{|b*0`^$PMxrygqx|jA+N|CfPyYJ!N?7+NlzL{@k-_CoLTbN^vfiea$xL^P+ zNhSipFkFQzuxz%?yj;TDH6AaSXHHA^w$9yOBJFhgMAGT>i)&6N)kt_r_Q#KV zgTCe&PRsC5_rG?3Zs=yqzvH&W4fj}Fch6q_*lX%WBkpPJH@nZiWKR6=2l(B+mFM|q zM=(D#GnF2mwF~Q3C2g8~oR5x}85z;F2IS>g^AWHY1{+G~`djS3hxwbbRw2yx8m>0i z(>41hmV2!`UE0&5?8oe<><4U+x+p~Vpu^9<(Hd>3S0-q7A8oB>Z&r)>b!&W#7a<=o zOW2Ucx~Ad5>pNv(6%a)&#FC1Hy)A&RTEYTesMmoPtL3s?!Ca*(Y(BrZJW>}{aRWGx z<#s8*RSaoOv5TR(S6Bj2c)AoItwWz mFfoltz4ULSOGY?`fi0kJP!U9$LzY5UJ0ZS_g(V84DcwJnDRRL8 diff --git a/gnu-efi/ia32/lib/boxdraw.o b/gnu-efi/ia32/lib/boxdraw.o deleted file mode 100644 index 9827434289dfd4f0751e7ae247e8a8c084f072f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1644 zcma)6OKclu5FWpRTb$O3Diml%9n_IR7q4qkp@Iym<2*oeo6y)PP&?bTy^fbSc4Tkb zP%T2pGIeS=R7i+p4?S>1<+3JCI4RH^_BIdr zCMTL{|Kf5U;g9^|&_5r1?Icfb`t$WwEs%fo*ZF(uS2fS6ac86FoTGkI^PSr7YPjxr zYAyGj8%GweO-bo?a`84*OV`!{`Py&N);C)1WVl7K1Kxb?_wM7HFE|k3`daOlBi-ie z|0%c>bmBX@4}T8%@srdxY^D0}Mra4N^k_W$NAXKt#`p9L zzOQHT13ib|3JR_YdHh0{#&3iIUK3ROTzDRT5@zs6p@?4#4ZOMgG`=mI!~3T$qW_6Y zxbxT&_C!wi_f5UtfA29qS2MD!4WqHxV}XGoC8NrvKsYQuEba-0A2dCHZRQ0yP8>3JZ_>5aX}Z{Z9-#DPA5FD zk%nGp7wofPUjLwpm&o=w-Jp=Q&eKctD}=56GloogAy<))i&==PnWw6ga#77Clm%_H zBF`1nY!xJFVc#R-w4zCKSxGC*muAGA3esqNY%m&^#zsaah7(dEIv5|8AX!qg<(!fz zk7uK$sybGQR0rB6@BxcLAvr38hrLv}oqX(10 zswQV=K;%kFLCRO;Sp~%DN_l>cmj1s|C1Q__%>NQEDZecp+2|GSmz|{CAN+4&iI7_T z#MtVc?c{GF-A~HWpnK>e%k}80jCx$3{t(6-z9yDiq}&&l1WZRD%Y2J>)>_ESTW}9y zV}HB|3G?iIZt?i7*k?Y=TLk!QmT-wQXv9kF?VQ$%Xr49NU*_n9g?yK1#uXw98-p^aqCteT6i z?|JIK{b)V9bkC@?u5p9enH4f>?3q>TCU>=M{^qXXLviyMLfp(_c`vrwU0$m`r)|Fb zUi9=5$G2q9H~G+k+spNzoTT}Cqv=o)}GOa*UMtXkS8`n24xpHK`d|Iul9jh91)=LfO$_`3)*Y zJ5z|n)tL5Fu!Vx@k@wqm1XD&vK3t@7Gee)DrVaUZipc>AX-2UL+%YboXmtAM5qU&2 zqLZm5Mhx#uhyTpzCi=y}$3Ry-Q6=tx{VzD! z=-?>_#~qw<@Qj0guE$+O82LfTnX^el9ReEZ5+;O|)+S^_n>I)uRt=Tpp-hJ4v|b_{ zkhdS19Mh9Y%%0?wZfJ7o#K579p~l8hjE-6&8s|$Tc_gi8C$WY9D<2>cN8b|uJB_DK zUMNOfeAoQ8cSCX9trz?j?BZ`0$igv}MraMR70P0u_$rY5@vo12asOAKT!V2JWkob_ zxS$8YIbYxJ jXPnD|oM7$TAy_i2d;!D9_u4buWq}pve0Va<))b diff --git a/gnu-efi/ia32/lib/console.o b/gnu-efi/ia32/lib/console.o deleted file mode 100644 index 1d3fe24f2e475514f33f2945110dad9d73ed9034..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1876 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 diff --git a/gnu-efi/ia32/lib/debug.o b/gnu-efi/ia32/lib/debug.o deleted file mode 100644 index 91098aa7818995e1878dfd2940b8d98b933ff2fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1244 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 diff --git a/gnu-efi/ia32/lib/error.o b/gnu-efi/ia32/lib/error.o deleted file mode 100644 index c0d875cbd6007db1cefd825e825e34f2072c6a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3104 zcma)-O>9(E6vxke07DtDMf?Do_%vWJ$_vDXR6`8YGE+zfm~^Ivs5rivKH8x(^Xi*7 z*b<`CR1BmN6Jl7obVZ0^;nElkm6Zz@E?f}fLK94g5o1gjB=!Hl@7~T#CBZX&?|1LH z=bm%!x%a+)n9gP*k%$62bHPW8QqD=RDW-$v0KyZqbu;#eZT zcshAyo~Da4@r(Zi+ZS#nUOTnid~ms$ygZh;;x51S>JdEwsw#&dR8Av#`_~j^5ra zr!YS+O)2B&N&`QUta#$cgjkXmA3qO|$Wf`wIq0iWQDZr@eLOYH3-Hip3^omTo57E> zsGop-Nc$VY*ctq2Z6R(&&ZEuoPJID$JvCla@v5*m4ov{vDD0Wr9CU}#J_jGnygS#E z@Ly5UEy)P>>TEDSBih3MFUAt>ukPKq3)tc&i zSQRVUJ%?wC`Wo_f5VayD*s$W`c>BntqSnbX%KhUGabM?PkyHM-`|OP?%c}Om+6b~7 z#nT-zjS-&gaOc~*BRK1wBN_N(CjUM6S;XKTHbL$rZw$`|*T9oj!%XfAqa0TGIIuX0 zmXCSi?pW%kOr!6~`*>S2f@pK_F!cyqo^$5H`dg^7qjF;{nBQ8@ooD}XwvWGe2+$?# z(Tj+*o(AzTYP|x+z#JF{ng2TQ5(wMYb#Mdt4VVCb0`Wa-ErUJa4mjHc4uVNA2W|#S z;1=)$@CooL*b9CM_JMc6t>9hoN$?MF8@L`xJq5l9BFEMN@EI@*J`0w>?O+Ss0bU1p zfw)UHaqLLcG*- zJ9Q`Z9cvx0{Nb57$>-pR zpFg#KQ7jGTQF`#=8aCokzCmLDj(|}d%0UtfW-TMo<6J7@d?|8&sNbc@JHY;c=0Q!? zfcmUvLGx|R3C*e|lcfCx&5txc(fmyFbImU_Z)kp{`L*Vf=J%REYTniSRr9{)-j=C*?luSW_b$3yRJH9XdjxUw|$A*C21N&x@*$iHR+n9Cil)n z9JYkmg=_F__#ne4!M7MV=w;}j2v=0^fFoufQK5S3~VQ~GOo9|mMWkXN+@;iTi z-#O==@4NRyH8J7y`ACY70<>!pUF+#aPP#Bi5jy0?_rqph2rJ$CJ@w~($8nZBewb}_ zr?DN?9P6-SMeGJV5mO zY{$_?ZDd@F*faNk?4VX=+FPx$J!D^s*jBjpPw&0%CbNe9?eu1Hp7@tX152pf`oXPT zD|T6#>3P3nZ93X1TMf0;+qSydSPQq*-`eVi)>dze?m*H;-U6|uu8W?uaXY=vo&H>V zu5oF@_iY~v92&p1^<=W0-oOea=Y8141tV=^r*-S?v-*tNdUs4Wc}&wOeo8BB&qSvk z3nPeNVs0F5pzEb8pJHwy;lWtUr<#9T>u9ZiKB#p^fc#re+bcX`dvqX~Olgi5aG#m_G z#vw=H>*oMuf&XF$LrwqKfu2|+;GetR0gVRX9>wDLcUTWN@s+?w!A6h&t4{1j>}Kpb z$U}nn0&>6S<;GS5noHJq$%sd=K4Tsv*ag>jP3Y1m%m={tQ%aETIZ|f~;OCV9@r~Y< z0B&}#F!`DK`~f<)8-~4~Mm#q6nDp8>;xueI58lrgQ9d)L5uka=|=^F9{-uLb@pa7*A2 z4%v^SuK<~!U^`57mJtVdk?{c0GO+hx_b&S}+i~n?Tom{@Bd)|ZjC`S#YQ1P?b3n7w z#k|xi#)4uRHItNcSu;yYu39D6p*(!nkBnhj%51q<Aa?%)LCm(~`$$#T;JGVqt{tUVuBl ztLzXW)uXWay%`n$JT_m3&UN{X<6NBoc`V)|-@-dG0emdddo E1>zBTcmMzZ diff --git a/gnu-efi/ia32/lib/exit.o b/gnu-efi/ia32/lib/exit.o deleted file mode 100644 index 689a98b7e2d72713ad959d55cd6f7c3b2ba1a401..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1164 zcma)5O=}ZT6umP^W2$M1#f4D8hzb?Sqd}1>E^KO&N(T#VvlE6%@|uKpl9J3oEP{(7 zD0R`F(*K}8z=c1+jUed8y*QpTA7=ajy>RA!op;~6_q};uv+9OnNFs)0Wa^1LF{8}w zP%X*4%!TJ4AebS$`Re1*mwezC1HW*5LTcdWgVWI1njF0?e*S)(z4k^LYisvP zOAmFc?+i+nin^ti%at3kB^Stt7>8S8Jj)x4g>3dE%3nn6QkW4bQ*X@YUzpXo8JI<2 zg)vArQh$8Y{bc29kS1g#ZZHmJBq#aU=3Wb?iH@hTVCLlN)D8E)oAy!e{%^kMN#9d# zsX>WtPgd@zuJ-JMw(a%&!Jcv^VmGYK>Z)aL*6Z7~9eZcBYSnD1Zj04#J#w9{UUvqa zeT~_mwu#0$|FigTHnV3$u2AFPC1@NuJcF5mqsC8N&uKw>qe%A>Yozy;rzf7Mj^jC^ zTH~>V0rmGos`k)z^#J_A(9^2%xLF>1PJ2%jE9suSJ91ntsyiC`2iW?5Weno^6q#?0 zcW@5i9rIp^%K-al?-b5Cd=H@)IieZsB>p-vVx83EKD6U)g}n8cLzSEX z&*zu9yu%{?Kb#BamCPkw3t@Q=oB#!Y9D?h(CYcFL>0sdojaXQR!n;lLJ|J-zW};8R F`vH`Fbvyt7 diff --git a/gnu-efi/ia32/lib/guid.o b/gnu-efi/ia32/lib/guid.o deleted file mode 100644 index b1d3e9568f36ddb93eae37077fd81b4da8bf0007..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5240 zcma)AU2Ggz6+UY_sgvf%34sDu0AZ6^YXGu|EBgJ*YE zv$Kg4RRWYB5<(DdkqAz|4{DYm&9RDie#1*genP+nO(-XIR(_`1# z&P?1oaiwkbsvoMfAB^wxAAxBjjT4XTF+`7t9x0kWwnM?Yi{Ff z>D8=Sa%WF+w8h=IsJq^?=11TB_SCai<X3-wU$R1d^-X4=0BqIG{%RDsBvi2^p0jbtCOKoa~pb7jBAtF06df+E5 zqllv9%a__g&%mCM%=lx{*K#%qxgC*Bz}Auiq($Fj7WFLr7vu<{ckst~z+rP+yyzrn3NIBdLMWmt8Btk zkgFgEgXni?nJ$OenV}4_7wxlrw~QcDm1X)qfBwwhCn?My3-=uhV_-QIfSgmjp!lld z8;XBcyrFnY@xO|96j$quX|v*kiaQjaQp_qIRIDhTRD414g5pKRONy5j-%`A(ct>#q zUvem06+0C7D1Ke>pyC{`7xoK4?u8!$+kig>CV>}$EPqk_C-6*n`&AiEr=LuA44BiF zQBHPsrFN$}cXmCdEs-rjH%T1bCyD1;6A!FwX*q=$>3eIaQ>2Vza|)|ewgWhTXEmN* zQP#Ji^+eOa`c?fuXg=98c5GGCi&y*J?7QCgM>vL>KDg6Z$X6+ccLOJKXscDt` zI;MX;#{U-M@5Q(utjZYQRt6v8PsI5C7{x$ac4};BC_gwpHQv{s9h~B5R@oEw&stSaMr*e1`=1@0v4(AD+OQ`L z*D|K1g9Sipj%^#BTqLaZ6f zbQ?@_3ueDjMVQ6LY(;cw!?vf5!t$ZGr?74E72Cp53th{g>pJcV@?Zfuk0cXf6yXF0o8jYuZOhkATEQJ&|q54MBQmEn5hM=Ifl0>hzp{P#js9W21ewCj73 z4R|Fs;ddbK8*4X6RO&dj?Rz5+F5A@H-@gBOMoUd5gu_Qhn+#M3o^P89W zjL147xc^PQFBpgXe#%ktE<>IqepaMic?a=xBD}{?-${Hx@GC6fJw$Zgqr8{+`QXk& z`@Q53^0!Q2pCf*OzXbw*fS45d77_Lbi4Tc?mnNSh|Dxco9Ox^=hl4v3^$X^@ZfAL4G2kCfjbc8h$h zoOhGNSFrxX7OX$|L;EB-HeI{wJBaum^8QbKr}A#)J<9he z?^Qmae6Mo;USR)m#U$dzRnFf>#J$QFi0e?Lsz*wmF%9(G0O!J%YuYIfCtp%U!!sl` zU9C#Wbpk>qTPZeNCxs7)VT*5{TKTJ)DmbMQUQd!LJD!;u9-Y`(_3);FPq;AWre<8c z$t5-GI<*QS`^4o1Sn9ua{61JF|2*=nQ;u}_4~_$u&A;OU{Im31HndR3f@6o8}+Jw!iN$yvA_P-BhO*M^6_!rY-LHz7!egMo+faqmR!x*_;EJ7@{*{tqqXJ^%m! diff --git a/gnu-efi/ia32/lib/hand.o b/gnu-efi/ia32/lib/hand.o deleted file mode 100644 index dae96ec44a6b3e050bfe1483fae771b6b6f60fbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4712 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 diff --git a/gnu-efi/ia32/lib/ia32/initplat.o b/gnu-efi/ia32/lib/ia32/initplat.o deleted file mode 100644 index 5222a6d581f26053ebe3fcff8f8b2c7c3dc760b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 760 zcma)4O-sW-5S?v?^}F38zj>^~Sfzfa zBll&y)!DU-*6lDcJ16m26OFp(z3`kBu5Y-WjlVmHW6^y>x!9!sD(qzbkQfzmG}%0k9W(mgki1bwkqq;kWHe0@opLRm z8ZG;$SFPNr=$2%rCsL2EN0~}A&-|w~SckVQSUvVxeyihKwqCWmLmORix*aam7=P*Z zSc5Zd1`P8y3pRJ?0jRs#=v=~;%=HFb1IIdH(BT`-JWF~U7a4)g-#OyG0CoT!czG75 n^vnntIE%MIhyTXqObdHuzdfggclTT6%!HG6fsNt&aR#(9 zGUNN9)huftV@8&BFE4&tS?6gK0wqMh(^+pqeti6lIXK~!s&37+ZEMsT8Md>6;oyFn zj-lL}1Z7!M2J}?w5x($Y>`_ugcz&O@qF(4tUw@5`xOYxyf75TEej4)tCdl>0o%PGu zzJhseThI5V)qHHAULum;AO+ukC#2{LX`(^T(x)oWhe%x3gz{NPKaRD$H`U;~ykeE1 zFjc9}ap%++Om4H{;yiVhMo+**^^Rb;-o#Y#Jmdt<3Va|Ctj1lRyCpd_m((V zH(l<6RcbWADlhoHSHoUyfqPc|sx#c+Zuu5ib#LDCDl>C+*Y|Mnf5rg%XwP;DKb_Yn zf)mL7JSupP(B2D_Abqr7w8#JQ4WWnDj9^mGydx2dFQieP6c~2_zDOca-uII5iQf*s zV*NPb#iJMn!|QI$TvP!J;Q|vmJu~eA`H0h8ISVP!S-*`)Yw6v{XC!K*QT#%mEek}N TL$M!I?g`Ze96m)vg!KIZinE!o diff --git a/gnu-efi/ia32/lib/ia32/setjmp.o b/gnu-efi/ia32/lib/ia32/setjmp.o deleted file mode 100644 index ac753822fa0dd65a6619331b0d7378194a57c916..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 476 zcmb<-^>JflWMqH=Mh0dE1doB?2vouZOlvT(Gq5p4c8jQVN_7^obXIY6X7O|;2^b#u z|1csdqFbuFh^4!V17;W!jcle6&<2niF{nI<&rqCNl9gM)kdvR64x$&y)sAgWSOeRR;2tB9slHU~UA7f!NF-K_Cc) zP;e5aP8}!$@&_SxAoWae-3$y7K(+x8vjQ`oW5{Chpy@Egi QWOXxud>xn~APrLo05ygtkN^Mx diff --git a/gnu-efi/ia32/lib/init.o b/gnu-efi/ia32/lib/init.o deleted file mode 100644 index d785857fdcdd27361ab92830fbb6adf29ac39e4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2916 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 XOKhBH;GyTci9E0Sio%wP;8k}4R|Z^@ diff --git a/gnu-efi/ia32/lib/misc.o b/gnu-efi/ia32/lib/misc.o deleted file mode 100644 index 860b8373ad71ddf68257d92f15c9a6a3029782fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5828 zcma)AeQZ?|iS_SnrC%Q_Af>@&>E?Ta82Z?Qm^ zz$L)xPtxghAz53EtBKj=$)>q2<}(mkI_^yWnh%1j6Q@0+W5dNMWsVf4GIxFg^yb8D zx!LY)=4)_@UK$KG2MlG7tH6WSZ2#o^M9z)5hp+kY8Z5B24NJJp5sw*nj;8hdAZ(S3 z855)L>vxTg={Ii}ak(2tTmi795yUn*o+n zJndZ0{mD6Qcfm2-CpNIS3jsJ&MTLy*nJF=gzCwez&$J@~7u*G;J8O);DKKD{WL2bs7RbHH()o2LG9?SabiM;7;(uG${MRbMr8)l^=_gdW3|{60A{xjQ6ciuQ9MNa;x7 z7*INNX|pvldYQ}F`k||ci-1~b^`94Y=*32Q_MZ3@Z$ay`?$KUgN|~mYVb6(E1nzxb z1-OqNdXd93e*}!b?Hqs0Q}QabFF@pjKXH8-#`~6X=Ajga1LpuKWr_zr;-aZRA~0;5-K7Z>9xdF^ zG#gu;Y+RhKY?^)4QTgZCZE597;*{r_jz{TOL0XwkjCm3x(}nKQBcGHU(ISs{`d9-lf^5v~Jyd3_^R201QE4NiWvUbceE9%tFaA=_1@As|st-0I3N{kr3 zgqc>bJID$ia~9mh z!eQG*)Qo$cmlp8eNB`yx+=Sg0$s~sMFa&!q-pbEfIn9|x$loE@zYY6CR(q|;QA;j1 zTI)2puYtRYxbmEKWY!}46zqj~Sm(2^hTVa=KG-K|zC6e3C+z`#ASZLqU?%=L@bR#) zr?GB^U$>-C3I2&IL<)RT6rtt=U zl)DSyyNek6u9YuycQ8li@nGN7upQn==bcAaF^_Izo=z;01xk->4)$JJjtTAA+nLeY z8Gb5Rnw`Lo?U!I-b8(G_A{}yCf*CZdzyG42kY$~b+yz;-Qri+XY>~;|GbD>TBmNH|%Q_V!@U?I*D~^MAcQf z0BI1^gKc>VtN6e`OpWR-T1zM#+1yj3Y5L}9H=>;%bHu5$Q)lZ zlZ@7IvmOl(^ul$0xHbI1fUZV+LS1Sgs_9yn7U9%7^^q`CA-%6Ys`YOQ4Rm9U)9>8scOBsKwh~E#H_%Y&v{~f{m z72!>c{hrXx*q3m(3GXGuI^PiTZo<#wH!_gc`31>kjL~m>TJK|$@ewP)T_^hrAldH+ zmfVW(Jju5(HcAMd2_)WOlE1*%F(C0?AQ}E&Cq&$D0f{#!>_zz2kbW_c^tS?O-dfVH zz&kDUTS&i>u^oiyA$0O@5Mo~^g?@|>^WPvn{JtyjIecenzbAp@=Pa-UAC7;J9v|Zw zAuq#0(0-N!iFXgM#E1M5A}?)ZzX!ix2-h%{1UmeT{f+XC^SMgsW$Ybs4(}4;$IxN& zb0^+%!d02ynKtC0itNB{fkOh12s|P1qQF^!ba80jYJuAXJ|^%-0?!GY7U;wrnzvqH zpTGkGj|#jf&_VgbTU|zo*jE!`&sBuQ2*wv1?$<+|Ks}nlJ~@i25udIO>CD$1(nHMG z8H+JrRLclt@Xb@!cqI9x`D}P+7PR5LTF{24!q=ts_p1YlWkAzaU&Di~cgOTl*DeHv zDAm4>o@l6FWxn3179Yg6{@?Zzh$y#gTZa*@BP&J6wdTLSkUo~r+X$WH;kqrztF!QM zeHY|4T6ilgJbFiP^!$ARuom>6xPWI`|ZK8`Wn zvnq6Iui-MD1Jb#9(1}OqW?c&+JAk*X9P81)U&&59;?r6dH|!|>r}0J^1Z*-sy7N}v M5jcDWI@u)ezkT#K{{R30 diff --git a/gnu-efi/ia32/lib/pause.o b/gnu-efi/ia32/lib/pause.o deleted file mode 100644 index fec13d53db19dd9297100d427bce08d6c200810a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1212 zcma)4+iDY06y1}gG1a!l;zOYY16F8}9vT!86-m-2c?mW)38GMjNgR{3&4tWFV)24T zL5YIDAowTI2miny5Pa}O=wl(SmCVsW3c6sQeOr5<%RbN2xr|{LqG3o-{&*s91Cz|< zv09NinH}fD@JnyL6}}&hMx*CLvid(3c4b!veNqRB>-0`k~!)*?tNVz;X)uB!;yBJ$*vtJmV3ORnL#n z$C);%gIJXFsNq^=Xty)bHEnh5edXFcSJZvWwM3O2N5%nl{E=0q)oAcMs@ZaFmAzG3 zb6l&kCyJfyn)#q(HEdDUPOIC-*8d9!#4|oJ|0b?9zANv|IaAI-I9vWz;s%5>)6jEY zM1V6*c!{q_tia~m;c3Rb*q?J{4WEg4I#It@-R~kI&FwzsW(gq??I=rh;|_}_&EQN;KzjFZQE`g_5ZL&$r` i41^pC>sX`T1g5-1;cXfo^!YejKQD@X4>So{^L_ycafHbL diff --git a/gnu-efi/ia32/lib/print.o b/gnu-efi/ia32/lib/print.o deleted file mode 100644 index 3c8dd17dd8ed1b7d9550d6e5610227106b72f6e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11144 zcmeHMk9Sm6e!uf0VFCj)V05EKoz-Log=`Wc5@lIoqoRe-8b_R z$)5cKdUM|U-0%HsQ))zq6! zeclz8WRJ!wv02twiMYm^-`_D^Np2jpu4F7~5BCLZ$_5o%&A3(j-)*288f(PEY2v#DE&slG(-{3EJN=-Anp|B+*7e@Z0(#Hw1y&Lb%& z9sByo$I!SYR_3);cgda1ZwoROip7sL1rueXM(o}|JKm+HT=Y7Gxj4JuLH#?ZIZgEKvz z=9%#*QLAz`-Z6K2sLzbn?suoQ&ys7=f2uyV3f)gwLeIPJW6FazrN|nX*6287H$|mFFWArz&g5O>kcJolCUuJJ*O2_&B!ATBJN_4JOM6 zIQ77XsL%n4u=~KLcr#3Z5Q$N%d?k8oeu#*hFU!(7^vG4C%^&F;I$sd|G@mm!r*p02 z?dM0q^-koB2@-J!FNXY7vc%VL$yRG04Kj;|t(gO&v>G{i#v=C3|dgMDhn!IH}6>B5I+Dh|Sp(BbUu55;)x zQTdzw7c6M8qOWHtkRxCGR|st0v6sGzISv(H|4u>lma^bX7@g1dlLlttb4~0a%E(pU z=Kn7E3AB@3)`E7F))!Vo`WeXyr-!@FH!9Nk?)UAD7{%d@=T7?~!yC^lT@m@pOOAb# z-Mhk5b*47LE6eE_9M4D*xhn#nKk`RReTa@Fiw)OChNZ~x=cg}iP98o!Jc_65{BZ9% z>|<)N#oVa)&X1fJKGFRv`f=oVH2KVM)O=<* z?$l{1qWB`8jBWY%;Mp^gGZES6Jp9dVvM+T|q8*dTQhYyI`E4vli&?1C1d9s1n9zYQhP*<2A#;?f~9i?x(9`_A~Wu9gh3)*;uLIc@}^}M1+4UkTK<%@^a3=w8I)mUljds8~8qI$^ z6YmU1Z#uQ+Io%lgCe^Xc0Rcf$o_)cCx~5C9v!M&c;N>~CT}IF0rt)m>9-$fxKZUk3 zA&s4dvT-m>K4r79Paqu)d&#Y_4|u)`dDGY+@+$9=)`_uwC{WyX)nk>b?21$2U6Y+- ze@o@Nct4RtUZp%*M{gdRJzXs}pOj7UjZ*cTg!d_%hp|(RHB2xm!eWwnSP=8D8+!RS z6r{1m6RD)Vjf(hSjipZ1qIXa8$TrHn+hd<&jcBnq@qE1d-M^s$G2Pe~8od(gbIN?= zv_k4^V5aJ^QFlF7g+-@}zO$U6O0P%draf}_Q8dLT-dGWuiH8VBWcD>Uw}O*sAeg4v z%o&3H+Y{vz8&1(1e#wi~5T*gTR`dN7eK(z}8b6jTR*GHSMWf%m&f}GLk-KtuweGaS zvj|8LtRmc2Z39v?-Yy>epKFG-Sj5__vFxe6h!(^nP+57c2t;p-6RsU1g-2AI1<@Nl zo><4+sGVSbG>ITPKAEDKihFMKcEvqEdW+)Dj;3j|`~kSDMP4(K(--2Chy`ZES6DQS z7PW#aU1gGp7Wy0uj>^(NYzU3hgpu}$o2xW}68#ny9ObvL2^Uq|1l|md zuWl~ZhCz{1acIfX1sv3}bP@MMSvp94k^1=T!>tbnkBbY%5iC@?d(y31vJFb)G)|Rq zR5*2^OqMRIKcy1HX&fG=#)3}Gh;)%(9dQ9wrBrB~xPIy< z4vwOEnt3IB8kJ-y1sJ@6Og@tihmKE^E8dq6hmOs{WRj&1F@wUfK$6_`T6DT1rP0i# z*(#-nx@<97+8dZf%_EF8v6^&Pj;|DbnQ+gN5GQR0I8~qBt8|m+{8*2*&2Nr1Yg?%p z1OpkSo0-N&6Mvek`ce&sogf>=UQ8s6xszWf$YVphd~!mW#+8_XHf^?X=fCS#q7P(- zP4tWJ3#xCoHfY+`uyy=dbXZ@~^S!F?MK9KLtlf&AQ+73p!V%iI+l;LXR|&QEX~YZL z8uDC&0SgBpgu0yASLQ&eO8W+-9Vu<-)YJX-s@Jm|?o%o?2Uad36J9IyPJGNzmNc-(v>KFE+-dA8lUr5-RUT8MK@JPH+?fn>e{iMQ7OT=gF z98NKPz9ACXIk^k-?pbv2;{3uQcd=BGiR*7JTgQILzMFF~SG@GTcrKGQz|P!F!IqYJ zK(>hmSquIk{D1nhBaq4JIX9EtgPp% zB*Z)bsU7(MYsI?(Zz^lzqv1!%ecU?Lq~t18tQq}EDjqwYbUb+({jAjOzRxwc*uS=^ zev>OFCu?C=_JW*wBF9*&R+*W$c$w)bo9Sk|)w^ox zUTY}Hyr#eKXyIVtd*BndOz_rUi(8Z6x}Xo>8N%};Jgi*UEeu)K^>cH)-4~&9z>iyq zqqn_+T0>7!v>(rW==qG)^~E7e{d@hKL${g{JU(#pfpHr}y_S&m$K((NjwU zLY7+5FS54-oPKbgBaYslJ{Nit_QX2ZB$?PbyY&Y8v5(;k$Hj9zVMHHdJ6g?=zoHF z8m}+K=aDJVTgK>we+_&}0(e>9HOfA`M%jgHlwF-vrddzC$t^uu*(H7#(Paxu*0UTf zdzg@;whOkej9H#nO%?a>!t}c{J9y~ zFAe(dL;iz7z7w+9z^8E;G3XaUzT2Ruee*3FUOXi;`ic3^27e^TzcR?nAn!5Am5@&x z?5%*@Xpn0m|BFFxfZT3quL<%_gS}SBvkY<&@*)HONyw@}zZLSo8SHO^+-uOQkRLY4 z&qMyXflvP+SZvV$9ppz0?fnw+*9QKtAyaw`&m42Iwny_4%wOy`?}|JD$wz?1U5|(O zzXj@v^dk`TbCUF9x)M0YJc}78B(0C@F=6#HllZql*3U?iGa&2dA;}9M>*pTH3nA-g z8p-*P^)rm*2O#U`6v-u!DN)dP4e0fAiR5<;e2VAbB>DG~^sPh>x3 zl018ooH0qh6S98Jk-b%r^)rp+dkp+J=9z36Wc|z{eK}LGmuh`uRXIeKgh2 z1CoCQS>OLjejT!&$Y0jh-n1!@)xfgKb8_yXciuw&&gJhM{?4ZN!hHT-%-@UnoAVdu z@jS1!kk?wsYc1rpa(S&>UMu%rV&?L?xx8*Juba#3=JL9^ylyV9o5Sno@VYsRSY4He zd}URE_HzFw=5B3!qQbwC)p=T5TQrT=U*FQw+E5?xQyE*@Q4T@q>gqZdFUnf)57f0a z)CD#KH$9fs(8=mb%a<1wme(yWDXEgG>#7Tj%B4D1SG;!p@?d~@O3HmzWy}LrVRf|^ zzeRo3ZssYK=)JUBs$ezsEkS>EYni_jA4xVeH3oc(^7KOZ*|cdrD{k71!cK&yq@}e! zP~9rZui;Qf6RU382zygg*1~74v!%hNb#Z2~e{)lV-%}shz>6qGo7`SX;&)4MJ$uC8-b&#tZ(8dOZVJE+UQS`Cs@8(+t`BIHuHpK+PF9l` zh{BGBrluM(UWLXnsarawiFz`@M_A(&1{k-al@$j!wo#auZfb)|Y&NE@Ig_z_34eh5 zjgaZQFwbS|1H!woR)Ev*X6!sL1s9@m!VJbP6W+;~AN!3Nhx9KAaY@U>nlR%B%j1NQ z|61TXqWnD}j|=>k5baoT&!cv(1E%1RcLU9N*bfO8F}4u*AT$0;ln*3c8_D-D)=7wd z{39?0+`E$?<40!>ka#}>Qu$v|IWE#~62i{ALjQr#eIV1Bj{CxxV!`i3lS&6EX12I z2XPkibo?fv@|y_}=WIgwzX(YElYl91##T{zF=O>4W1O}SO8A~dSi;yP(l5bTHACYS z5kg*0xD;nDknBAUr19NG<)yeQ5tiW&M2L3Z0upb4WVCkzNb}(LB!mCogby3MO6vul>UWT6|gs{Jg5b@~|_yQsP+6|=o{{*CV_ES09874&g(o7)w z;U&anZUvC|>w(noPAXrH`7QK4LcdGs_Xz#3g?_Kl_X+(6LVr=916N|QcLO2XyBSFB z-3_GnmI(baLd2_5=+_ASWpk1I-V5-10!W-~iO2{t4@8R18 zVH&dR#E;xEA(s>0itlkkrh6>$>6;GW zYQozXdz8?H?;C{Q$M_T8j^Fu&;I#=15Z=KUsLYH#xm;kUz}*553LF(^Lp|b?zl78- z;dX&<3LF(kcP7#=7Pvv+ivkY{91(a`U~K7peGnN{Pv1QrN}i)T2~*aXIX1I zI#?DJwzp~pq|Ca;p^p=rSG<-E7lJ8uxOh#e!v$wb9qvb24XqnD`f&@)+SD5GXO%wW zThI}xZ+J{|*uSB!vAuqypJlCYZw#ReW7D^uB{d8^yQHDF3!wSl+Tz{8SZn5ywFC##V^jD1i~q@zUNF|d4C0Uk=f zK*eL2d(>Y{AKotck%@=uK*f{Gsr?oeBIi*aM+o;@=VgOuOnSQ%SIT24@I<@|x)LQ6 z7fRW9jQ(x71A7H3k$*TY;{H+msd}<7n1;Psw}E1=BU~x+Ixl?+USJZ>H3e_SBwof8 zyw@i2=1##oCV13uO7p>^vD6dwk)Bfm_3~xl8IvB$&rnEtJY z9|KNj0L2-{Sv<~0TV!J;&V(JXhogdj*Cu-yN^VP^0}$DTbjIG+oq$YsDA8Io@=pE$ Mzhk*Zd+EIY0|D*vCjbBd diff --git a/gnu-efi/ia32/lib/runtime/efirtlib.o b/gnu-efi/ia32/lib/runtime/efirtlib.o deleted file mode 100644 index 80a4d84ace8f0bc330479a4d08b42e357652113e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1520 zcma)6O-vI}5T5NWWrYHj5MzYI9x#C@8yXFv#zTLpiA4-hOi)lVHwr0+v1E@oIFiO32j&T^}u%_~e> zV4zGTSOSz}u?R*$`Fp2IDwoM$JPOjDv4<2^-|NX>ylm9$xPx{%0dpv)9z%|P@&Co6c{BQ>O;D(wecpM!7{xlHtQ7uw8D%Ik5f+jq| WMR_tT;r-FOwe!O0EO8Pd=ludiF4$lI diff --git a/gnu-efi/ia32/lib/runtime/rtdata.o b/gnu-efi/ia32/lib/runtime/rtdata.o deleted file mode 100644 index 920475c37dbc91ef0c7d7d685a9d1367c1ce4a65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 740 zcma)4O-sW-5S_F|t$48D&4cx11-rC}Ca$xe_%)Fibn0)%a@XZNyC2sqS?CCVH}udx2sN6 zr`7A3xPm@Bm$(+ zIV1X{bTYH9nV=|LbMw96tWz;o4}UB#**W#+nd3NNj@obVp@Yv>*iMf8>allSdSngm z|Bg5_&go$)w0|6RQmUOh``C8p?R^MBgi>}|bsnnrQpn!(W`*te7>Y>RbGWMJoUggI z9U5=jk?}TVQmOl~iFq?$GODrZX)zw@0Rr1u3hbzX(MTxt65EU+HpU4c z&Y#4PdJs$whLiis_DLHmqH!IJk47F2?WW31RFwMh>DyCKwWTb$Ma>(qg{h- zbP=LIPM5$@@bg0ameH-yn5J)nY2h479!?uDw zL_@IsXc^ysA0Fy0wn!#8KhQZl=O24{K5Jj3^Dcro8~2KBnJunjmn~nUXgsS}MjqCx zcWhC5b+arivt^NZYFGw|e4{~bKwRFDD|0xVDAXzyvx-{Pnq`X2qt(fVWuUo&S22tF zTHUCaB-ZP-<|elFzjzJdb)T63+h`9BVxVpA`{iR^)IC5<>d?Nf?k0SG$2^z8eQUX1 z=Of?vC@9}eyc4D`=q>xaF28hMlk4Bc;5qX3zlZOKtaled*8fX41&zButjFMbyq1Kl zry=0i9f4n_D|$Mv%Q(j19!PKv9$TK3@o&=ctX%iN(|OyY@8GX|TDgZk#Qgmm#=&EV zasT9fa6h=NtOb`g5jchA|3esq>qRlRmh2r|ieu7Sc06z$2EP@*ZWiR7o9TR{?k@?l B%H;q6 diff --git a/gnu-efi/ia32/lib/runtime/rtstr.o b/gnu-efi/ia32/lib/runtime/rtstr.o deleted file mode 100644 index a75652da6690cea7b6e5920496c16573ff3afc5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2212 zcma)7QD|Fb6h1e()w}l2WFv^uq7f!EXv2yk)D>x(c0<>WwjnL7q+8nEYipadCV!yq ztUko*u37L=eBZ+mLH5#t_|TYY*FBXz>5~c;I_85#DFcQ0oqPZPHp)bQI63D#=R4;= z|M~B|d2ev&tfpzCmPSE3LPT4Sd6Ac0)k!9`QM!A_*{cj1m9!bUts9}+kyd+sQyAr1 z!0y=9+O?4>HhOsaNhb|n(9*YAmAwiD6Hn}G-W_I8kH(kEt&%CK3D1nr8c z+3Pz@G=q{DAaZsUwURdMa&JG0VBdp7q&s`%Un1CNbpOvrqfy=pSM`K5Xzbiy1$4Xc zGmJBXW)W!zZra3niZe@QWjI{P7^CBUfnMsViAbzg)z6G+J8#kGhzdDtVW(s|8KY|b zJ~?$MbD#G_3(d7;p~{-U&2*6V1FD^f)x1&@s?FOFdO7@jSei zE>1Q|;Zdh9Gfii^s?$hSZ`c0NewDr1+xip{Z|F7V%^zdI;1hkAeCbfzbn9ap8D}@N zac^^94{hA3{iMh4-f9hfQPXzo>07P1SP()RUxRUYz=R+5`JL9-U4-deGWlw>bHJLO z%jToq-SL;=r@Oji(jtn;he%r;BkdhSYd3ZM8Xl$&_*rV&))U_*w!MCt8^+l~kn@k=zWWbu zFL)OrZ#;s#*2LvA%z}3V@}$p2k0VEC;F*63dDf4k$AP2xO~k(cTO9c^&MD0cG=J%I z(C`5a{!$#1j#JPl0~(!#W}EMkaXYlSGsYZK_rtggSltKXSAf<08TSFJc{6tB10Sz2 zJ`5?(^l=83nmO~|1Xgop{5G(f9pkIOYF3Ot0#>tOOa)OCh4>7OiIgZLb4$`#T5*k~ zq%;acR$hj(!ZY%|D=%4=7`AcWFp zxtx`UFTW_P__+(|uA<1!%u}4%*6by_kj+_W;(soFAJ7WPzck|iw1@1FOPT!4@^jrM z{a>{ZjYuDJbcg|Y9>N%ZWbS$qGiY9_9)GWx|6jb*;BXFWUR=+8hdt=VINk$MwK;VD zU-sqqj(I9P1Y@fykFbx8AhO$_L=^gRx(&f8oIZVDs7-#9#C?%9|ZhHx!)R+tpU X{61wvF4e*b;AY+e_MK29XvO;rXqT=I diff --git a/gnu-efi/ia32/lib/runtime/vm.o b/gnu-efi/ia32/lib/runtime/vm.o deleted file mode 100644 index 881830855f4b88cea3f9e10a359fd3069b2d5d54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1796 zcma)6TWb?h5S}DkO*PtDd~3l9RlKx|LGgy5X_Hhjv1xj-wzS(#T-#`~iOGpp5wxUU zLM*5P3pSIa{JMR`_%SszU6Z9-cs9&*K4^`Pl-IhdcVzA90LoWB zR(@1Fk4*>K!V!NZ{wbLpPFSne_@~Lq>R4bUq#PSoDnHx8aerdiT18=`mfo)z-=}7@ z=~`9ksa3mrYBV@7aJ+j@*hpn`vpX1652^?Hf_rTXvu)=YA)cE_^4#)!Is=OG0L$uu zuS-UVW*I|U{dc|nTUz?>`O1oSq854^dV}0vgu>8Ss0O98c3$YdZ}6T{Zt+Gr^_@&c zxE(4u4`LsK^gXD%8a|sgzm;v+uEb@AauH`sI**x~ij&h;?3pN_aV zuQBfCxByd4e5Q$CZQ{#K{3-Y>lzYVUKLa|_;l+BdWz{q#MJ$twn0ji#7|#?$NneQT z`FzHlE0U%y9zCqi8A8jaH8Ec@uc)a-(gtHg{h^pP6pfBVMzzsUe=MRAibSuUF^fVs z(*`9+*^w-nB9k?66H0SKCE;=<^-R$iO~l+TqbPC(=}d}&oO#VCU}Z(|r&Z{SybtcL z7oPxd6SmU|<^`b6xQd7kuNr(D}W?~zEjiJ$MI+x8FCTf{E zVW@-W#`@6S^c7Nh7Gqw!RM4{qsdI%~DUW6SuRKEHHdiwLC-MK!7AV>w-ve*6Iqw8~ zP9FE3b69sk`K_>zB@BHft@?*!Eb?4{rXHd%Mqu13=VhR#=qpv+j(4DQm`dI(~ diff --git a/gnu-efi/ia32/lib/smbios.o b/gnu-efi/ia32/lib/smbios.o deleted file mode 100644 index 7f406a51b9171c5a766a0c6b0573c57bd0934dd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1576 zcma)6-%C?*6hC*HI?W$TA_R*R4D5#&M?aX=gWK9xG<7&|a; zI1WtXzz$^w@XcPToY$cp>Y+}rS0azCk~06{W+C7lj25)o0yL~dIPTS0*WPq1Yi(HZ z6US`7+w>$bdW*Y4eY=UyAWE!^7p_@h`XR#2xHwi;ZHZAX{0$RPK* ztv{kybI)D*^{4d!*~Qlxr4ZY8Zxwt^4Nugg`M^&8E4Mk;^`6BFft{>}(=KE)T+_7O zzxye-=Ip=YiWD`c-UaJJ!woClRgvYuPN{JrR!hBYyG;>FCW=LXUrS!rcBWU|Ys=EU zzR!ZAZ39An|D~4pt5RGRQ!Q>ce~$0$aGx?F+Vl~6pmDSPz|A?i=30ki2@ltfxnp_- zAV}z~cP`pObrr#T_PdT;1>5j$dS7~9pj_8pLkZ~~lJ*8^eRUOmC9Q3#EOud;kp|6C zf`)0cMuygJwAtV|qPZ91expv%Xsd)F3#a`kR*voG5Agd3 zIHZ$tSxNIF5SHU1iA8l9WvZMS|2;L6W>PYw$)n!XXjD>Vak5uS#w8U5A>+Bok4sFL z8WC7ROHJ@%282+!&*u#beZgQfFdz(gec^xr(H`IRzUY7$pOmPTxq{x?ucXH0aZMGO ztfX`a_?4-d9w~`?4x!_5A&gj41u%?Q?F5i0Uk&hx5Qq1S5I@FS16v4j4)h*jhLC<& ze0nCy#5f{TOPDYtRhs0PlwrV+icADPo=$_F;QxM9end$or4;H@3X}LyZ>%HD#E}W$ zsgslt##AvW0Y9!P+7vGRzm`VfVT+jl2k^*n2pvbE-#NN>C(!AhQI6~i`WHPaJNlwqi<`~%^{i9E2W32`w5A>35Eaw diff --git a/gnu-efi/ia32/lib/sread.o b/gnu-efi/ia32/lib/sread.o deleted file mode 100644 index 102b66526cb095d5419db25c925e3e7c6dd84586..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2504 zcma)7UuaWj6hAjf>$S$_>aw9IEDseF=UuZ6slzdo^d=jo>uqD={-7~UZ*4T`?UH+K ztCqIJY<{~7dlL2_KJ0l9!eAS?huW%fLzuY2ggOXJVWKaCF~$_`&bhfa&4PoS^5uNz zoZtETefR5}6pwNo2M%%IhBXP`^K}i&YZluH-LTQJccT2;Q`0m%2%TgP4yO`V)&tC! zANMh7#gKSI_cH00A^8nB;b+oq3the@uv%r&cdm|ZhP0ja)z+1Yqs7Xf+Ezmn*c2aJ zWihWIZK+uaAAW1$RuZPZr8-Y4Q}@e;v{IAmBpLJcYs%voxUx9?96FXCd(GZvGO3Og zlxOSQ)>T7T_|=USe&RCpdbvKge@^S)@6n_4r#X*)pt3qWSPgrIPJs2t$yKQ?r&#i) zoykn`WeuYV9Mt2g1%)RaiFE_&l7a#354hqA;JHzsjITUDIlr2U?V6y_F zJIWge{Fszdnss|axf3$wjo`h~y08e@l)dOYf5s`l)GVJN?#R>cAJX?=czTv07Kywb z*x9d)jt()qG6PjY2p9@!SZd9adG% z!pGG{=6~T3)UTY%WfeK2jUQ6qp3s!ySSG7Lzdo7IQO-3pM2m`IrFZJF1nQ`*w}?79 zGoTb8l2=Pgn~m;_3*Q_1Uc2z;Ws>p#%!kO=^Z0HO$BS~2^j!e&qQm+P07sF#ya1<& zZv^;^^oYAeayP&)#C-q}96QN_HoruC6E1vlNx#YF0g8_{+2)_x{4#OGfLNL-Xqge@ zTCu^$on2AMi<&a6fjFAcG9ZqWN?<9(XP>H=RSN}W0`U{7riih3l6y*8CVLV@ilmIE z$BLPP0^;$as!w9-|4StVVrxyJ|I;`^z;OYN*!phi+kFuij>nogGT4vndFs(7vJq{= zMc?QFTtph454{r<4@YIiqxd^;k&hE%$l0Kd$ceWb_k?%M|Ap(deL5)#65(3k948*V zceE!wQ4(>~n?q!ym9)8|*PH@ug%dAlb6z38^9nFRGgrhzw tt`iHl(2Is%RN}R@FoY9ukxy$$$MCTHPElxcwsf|wxb66zw7z)`?jLd9E_VO` diff --git a/gnu-efi/ia32/lib/str.o b/gnu-efi/ia32/lib/str.o deleted file mode 100644 index c16826f40b705cab353154307d3bd574ca7cd260..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3724 zcma)8ZERCj7(To0whjxcLvadfjWZ4ml_@ItNmt58(#craP;k_;-MWUYZMs`Gh#%3* z)OxKkDhV2m{8E1yLyQR;W9(MI@WY5`bkRhKiOQ@dV)%hdoX>N6PANB-aFVyr`@CQ0 z>%I5(@o;2KU0t0}qfWTQSti5--il|r4eer{m}~nRz-P|P&d&Dce~MaRkJan7x_k+% zH8Js>v0&neF)y+5irjp&sF_pR`tr{m#qzw4BJ7^#11-%5N{f$f?JptM3VW?yUq0-~ z_j>#7f~6BI`^1!n84%ECb$Oyzzt?(_D=56j-<5ylynWG)-Lo%}l^c4f`^6|xer+b***wcgbZjaS}1|^PMcs7qlWqVb&y@w0^;r^oLZ;Pyc zPt@La-rbfzY-?D`MA2yJ>5F#f!>4SM6+U%#kMi6TmM=e9SfwS*zx~Fa9mRxou{o(F zCXN{^3R~VtnE%v|)tkTi%Poa1N5PsNZYpf~#5T819(N-v*nHsI%a55q{$)GH>wi{Yg@i#H!&aan1sX-9ERVYTmQ`9vjSGq>Wk9>qa=3u9WsawV+3e+!TOYnHIQ z)AtymF<%0ES^%=d2+X{K+53_qVUswJ*TbdYA*9+lo9KdES{u`4yY0FL?0pdR zK4^pf3}iE;6CxtAZulve=^icx4_9jY;o1-31>X&+#&w#mkwYa;FZ?in1^EW@e=*$K zX~?BmfZ7{vUhpd%Hb3~)4%>3@HivB;_+p3c0q}ssmI1%nVcQM9!eQGBepii+XY>wu zC*%(3)w8$HD09vkeTA4uA^o*+xFc8T2$q7D$b57OI^tXZsA>0S~^+46DOW`KMt;* z3HpC``2Tir4GpB84aPTutLK26?*P^Fh-C(1#!xP`BQQ8FVrwHCI)jnehBa%V;Z3nk z!OloHrfT)3k^|{MeSON%vx)eC&bd&Hz>1Y6WOi1RR7h%>s!}y&LvcezRketos@SSZ zG`Y*3V|6YwoE(T7x(E&rr>S(O)59W^&g|^cN5r_1PKqFZx^yGn6*mTk)QV|HRxJ17 z9<(_^Yy-}@NQgAB34e5Q#JR%mdm{fxF>W7_aqrTPxc7Swi2R!*B28#`QS?Y;UuYK# zv5+_)-yI@e8rKmo6JiPRav^RcE);^_4DCw%#U$b-ayRiB%DBCvgQRM>{V`MW+akRBP)Q;1Kc4Lz{--rl97Mtoq0 z2(Xeq6iZ~|Bf1DYluhR{nEFD;C<1Xs)xk67KXY8Oz?IwI7=CB?PRF+KwWtjKw^`3U z%0V9ouF?LUtRlhip-3y7tcPQ<>#_cI5ay^76H0-NL)E(hI^&i5|KgU(oCZ#U!MNJ* zqpBByk2>Qy@`&T!)S!>jD^c0sRtt~@RWB*y`JW10c_Z wcE)j^tXq>V*5SZe509H2!#E#=IkihVf#i0zDEO4hyRD5ojtO_criP094{_hgKL7v# diff --git a/gnu-efi/inc/efiapi.h b/gnu-efi/inc/efiapi.h index 96e9e4a..9d399a2 100644 --- a/gnu-efi/inc/efiapi.h +++ b/gnu-efi/inc/efiapi.h @@ -971,5 +971,75 @@ typedef struct _EFI_SYSTEM_TABLE { } EFI_SYSTEM_TABLE; -#endif +// +// Not technically EFI, but oh well. +// + +#define EFI_DXE_SERVICES_TABLE_SIGNATURE 0x565245535f455844ULL + +typedef enum { + EFI_GCD_MEMORY_TYPE_NON_EXISTENT, + EFI_GCD_MEMORY_TYPE_RESERVED, + EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY, + EFI_GCD_MEMORY_TYPE_MEMORY_MAPPED_IO, + EFI_GCD_MEMORY_TYPE_PERSISTENT, + EFI_GCD_MEMORY_TYPE_MORE_RELIABLE, + EFI_GCD_MEMORY_TYPE_MAXIMUM +} EFI_GCD_MEMORY_TYPE_T; + +#define DXE_SERVICES_TABLE_GUID \ + { \ + 0x5ad34ba, 0x6f02, 0x4214, {0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9 } \ + } + +struct _EFI_GCD_MEMORY_SPACE_DESCRIPTOR { + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT64 Length; + UINT64 Capabilities; + UINT64 Attributes; + EFI_GCD_MEMORY_TYPE_T GcdMemoryType; + EFI_HANDLE ImageHandle; + EFI_HANDLE DeviceHandle; +} __attribute__((__packed__)); + +typedef struct _EFI_GCD_MEMORY_SPACE_DESCRIPTOR EFI_GCD_MEMORY_SPACE_DESCRIPTOR; + +typedef +EFI_STATUS +(EFIAPI *GET_MEMORY_SPACE_DESCRIPTOR) ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + OUT EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc + ); + +typedef +EFI_STATUS +(EFIAPI *SET_MEMORY_SPACE_ATTRIBUTES) ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +typedef struct _EFI_DXE_SERVICES_TABLE { + EFI_TABLE_HEADER Hdr; + VOID *AddMemorySpace; + VOID *AllocateMemorySpace; + VOID *FreeMemorySpace; + VOID *RemoveMemorySpace; + GET_MEMORY_SPACE_DESCRIPTOR GetMemorySpaceDescriptor; + SET_MEMORY_SPACE_ATTRIBUTES SetMemorySpaceAttributes; + VOID *GetMemorySpaceMap; + VOID *AddIoSpace; + VOID *AllocateIoSpace; + VOID *FreeIoSpace; + VOID *RemoveIoSpace; + VOID *GetIoSpaceDescriptor; + VOID *GetIoSpaceMap; + VOID *Dispatch; + VOID *Schedule; + VOID *Trust; + VOID *ProcessFirmwareVolume; + VOID *SetMemorySpaceCapabilities; +} EFI_DXE_SERVICES_TABLE; + +#endif diff --git a/gnu-efi/inc/efierr.h b/gnu-efi/inc/efierr.h index 5a66e1a..ac9ef7b 100644 --- a/gnu-efi/inc/efierr.h +++ b/gnu-efi/inc/efierr.h @@ -57,12 +57,17 @@ Revision History #define EFI_END_OF_FILE EFIERR(31) #define EFI_INVALID_LANGUAGE EFIERR(32) #define EFI_COMPROMISED_DATA EFIERR(33) +#define EFI_IP_ADDRESS_CONFLICT EFIERR(34) +#define EFI_HTTP_ERROR EFIERR(35) #define EFI_WARN_UNKOWN_GLYPH EFIWARN(1) #define EFI_WARN_UNKNOWN_GLYPH EFIWARN(1) #define EFI_WARN_DELETE_FAILURE EFIWARN(2) #define EFI_WARN_WRITE_FAILURE EFIWARN(3) #define EFI_WARN_BUFFER_TOO_SMALL EFIWARN(4) +#define EFI_WARN_STALE_DATA EFIWARN(5) +#define EFI_WARN_FILE_SYSTEM EFIWARN(6) +#define EFI_WARN_RESET_REQUIRED EFIWARN(7) #endif diff --git a/gnu-efi/inc/efilib.h b/gnu-efi/inc/efilib.h index af47019..a831666 100644 --- a/gnu-efi/inc/efilib.h +++ b/gnu-efi/inc/efilib.h @@ -79,10 +79,16 @@ extern EFI_GUID gEfiDiskIoProtocolGuid; #define DiskIoProtocol gEfiDiskIoProtocolGuid extern EFI_GUID gEfiDiskIo2ProtocolGuid; #define DiskIo2Protocol gEfiDiskIo2ProtocolGuid +extern EFI_GUID gEfiDxeServicesTableGuid; +#define DxeServicesTable gEfiDxeServicesTableGuid extern EFI_GUID gEfiSimpleFileSystemProtocolGuid; #define FileSystemProtocol gEfiSimpleFileSystemProtocolGuid +extern EFI_GUID gEfiLoadedImageDevicePathProtocolGuid; +#define LoadedImageDevicePathProtocol gEfiLoadedImageDevicePathProtocolGuid extern EFI_GUID gEfiLoadFileProtocolGuid; #define LoadFileProtocol gEfiLoadFileProtocolGuid +extern EFI_GUID gEfiLoadFile2ProtocolGuid; +#define LoadFile2Protocol gEfiLoadFile2ProtocolGuid extern EFI_GUID gEfiDeviceIoProtocolGuid; #define DeviceIoProtocol gEfiDeviceIoProtocolGuid extern EFI_GUID VariableStoreProtocol; diff --git a/gnu-efi/inc/efiprot.h b/gnu-efi/inc/efiprot.h index 4013ab2..db29157 100644 --- a/gnu-efi/inc/efiprot.h +++ b/gnu-efi/inc/efiprot.h @@ -554,6 +554,69 @@ typedef struct _EFI_LOAD_FILE_PROTOCOL { typedef struct _EFI_LOAD_FILE_PROTOCOL _EFI_LOAD_FILE_INTERFACE; typedef EFI_LOAD_FILE_PROTOCOL EFI_LOAD_FILE_INTERFACE; + +// +// Load File 2 Protocol +// + +#define EFI_LOAD_FILE2_PROTOCOL_GUID \ + { \ + 0x4006c0c1, 0xfcb3, 0x403e, {0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d } \ + } + +/// +/// Protocol Guid defined by UEFI2.1. +/// +#define LOAD_FILE2_PROTOCOL EFI_LOAD_FILE2_PROTOCOL_GUID + +typedef struct _EFI_LOAD_FILE2_PROTOCOL EFI_LOAD_FILE2_PROTOCOL; + +/** + Causes the driver to load a specified file. + + @param This Protocol instance pointer. + @param FilePath The device specific path of the file to load. + @param BootPolicy Should always be FALSE. + @param BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then no the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED BootPolicy is TRUE. + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NO_MEDIA No medium was present to load the file. + @retval EFI_DEVICE_ERROR The file was not loaded due to a device error. + @retval EFI_NO_RESPONSE The remote system did not respond. + @retval EFI_NOT_FOUND The file was not found + @retval EFI_ABORTED The file load process was manually canceled. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current + directory entry. BufferSize has been updated with + the size needed to complete the request. + + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_LOAD_FILE2)( + IN EFI_LOAD_FILE2_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ); + +/// +/// The EFI_LOAD_FILE_PROTOCOL is a simple protocol used to obtain files from arbitrary devices. +/// +struct _EFI_LOAD_FILE2_PROTOCOL { + EFI_LOAD_FILE2 LoadFile; +}; + // // Device IO protocol // diff --git a/gnu-efi/lib/data.c b/gnu-efi/lib/data.c index 34717d7..3b21c21 100644 --- a/gnu-efi/lib/data.c +++ b/gnu-efi/lib/data.c @@ -102,8 +102,11 @@ EFI_GUID gEfiBlockIoProtocolGuid = EFI_BLOCK_IO_PROTOCOL_GUID EFI_GUID gEfiBlockIo2ProtocolGuid = EFI_BLOCK_IO2_PROTOCOL_GUID; EFI_GUID gEfiDiskIoProtocolGuid = EFI_DISK_IO_PROTOCOL_GUID; EFI_GUID gEfiDiskIo2ProtocolGuid = EFI_DISK_IO2_PROTOCOL_GUID; +EFI_GUID gEfiDxeServicesTableGuid = DXE_SERVICES_TABLE_GUID; EFI_GUID gEfiSimpleFileSystemProtocolGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; +EFI_GUID gEfiLoadedImageDevicePathProtocolGuid = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; EFI_GUID gEfiLoadFileProtocolGuid = EFI_LOAD_FILE_PROTOCOL_GUID; +EFI_GUID gEfiLoadFile2ProtocolGuid = EFI_LOAD_FILE2_PROTOCOL_GUID; EFI_GUID gEfiDeviceIoProtocolGuid = EFI_DEVICE_IO_PROTOCOL_GUID; EFI_GUID gEfiUnicodeCollationProtocolGuid = EFI_UNICODE_COLLATION_PROTOCOL_GUID; EFI_GUID gEfiSerialIoProtocolGuid = EFI_SERIAL_IO_PROTOCOL_GUID; diff --git a/gnu-efi/lib/error.c b/gnu-efi/lib/error.c index 5b36f5f..24864cb 100644 --- a/gnu-efi/lib/error.c +++ b/gnu-efi/lib/error.c @@ -54,12 +54,17 @@ struct { { EFI_END_OF_FILE, L"End of File"}, { EFI_INVALID_LANGUAGE, L"Invalid Languages"}, { EFI_COMPROMISED_DATA, L"Compromised Data"}, + { EFI_IP_ADDRESS_CONFLICT, L"IP Address Conflict"}, + { EFI_HTTP_ERROR, L"HTTP Error"}, // warnings { EFI_WARN_UNKNOWN_GLYPH, L"Warning Unknown Glyph"}, { EFI_WARN_DELETE_FAILURE, L"Warning Delete Failure"}, { EFI_WARN_WRITE_FAILURE, L"Warning Write Failure"}, { EFI_WARN_BUFFER_TOO_SMALL, L"Warning Buffer Too Small"}, + { EFI_WARN_STALE_DATA, L"Warning Stale Data"}, + { EFI_WARN_FILE_SYSTEM, L"Warning File System"}, + { EFI_WARN_RESET_REQUIRED, L"Warning Reset Required"}, { 0, NULL} } ; diff --git a/httpboot.c b/httpboot.c index ac9ea25..9b0947d 100644 --- a/httpboot.c +++ b/httpboot.c @@ -55,6 +55,51 @@ convert_http_status_code (EFI_HTTP_STATUS_CODE status_code) return 0; } +/* Convert an HTTP status code to an EFI status code. */ +static EFI_STATUS +efi_status_from_http_status(EFI_HTTP_STATUS_CODE status_code) +{ + switch (status_code) { + case HTTP_STATUS_400_BAD_REQUEST: + case HTTP_STATUS_411_LENGTH_REQUIRED: + case HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE: + case HTTP_STATUS_414_REQUEST_URI_TOO_LARGE: + case HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE: + case HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED: + case HTTP_STATUS_417_EXPECTATION_FAILED: + return EFI_INVALID_PARAMETER; + case HTTP_STATUS_401_UNAUTHORIZED: + case HTTP_STATUS_402_PAYMENT_REQUIRED: + case HTTP_STATUS_403_FORBIDDEN: + case HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED: + return EFI_ACCESS_DENIED; + case HTTP_STATUS_404_NOT_FOUND: + case HTTP_STATUS_410_GONE: + return EFI_NOT_FOUND; + case HTTP_STATUS_405_METHOD_NOT_ALLOWED: + case HTTP_STATUS_501_NOT_IMPLEMENTED: + return EFI_UNSUPPORTED; + case HTTP_STATUS_406_NOT_ACCEPTABLE: + return EFI_NO_MEDIA; + case HTTP_STATUS_408_REQUEST_TIME_OUT: + case HTTP_STATUS_504_GATEWAY_TIME_OUT: + return EFI_TIMEOUT; + case HTTP_STATUS_409_CONFLICT: + case HTTP_STATUS_412_PRECONDITION_FAILED: + return EFI_MEDIA_CHANGED; + case HTTP_STATUS_500_INTERNAL_SERVER_ERROR: + case HTTP_STATUS_502_BAD_GATEWAY: + return EFI_DEVICE_ERROR; + case HTTP_STATUS_503_SERVICE_UNAVAILABLE: + return EFI_NOT_READY; + case HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED: + return EFI_INCOMPATIBLE_VERSION; + default: + /* Use a generic HTTP error for anything else. */ + return EFI_HTTP_ERROR; + } +} + static EFI_DEVICE_PATH *devpath; static EFI_MAC_ADDRESS mac_addr; static IPv4_DEVICE_PATH ip4_node; @@ -517,7 +562,7 @@ receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size) EFI_HTTP_RESPONSE_DATA response; EFI_HTTP_STATUS_CODE http_status; BOOLEAN response_done; - UINTN i, downloaded; + UINTN i, j, downloaded; CHAR8 rx_buffer[9216]; EFI_STATUS efi_status; EFI_STATUS event_status; @@ -565,7 +610,7 @@ receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size) if (http_status != HTTP_STATUS_200_OK) { perror(L"HTTP Status Code: %d\n", convert_http_status_code(http_status)); - efi_status = EFI_ABORTED; + efi_status = efi_status_from_http_status(http_status); goto error; } @@ -574,6 +619,15 @@ receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size) if (!strcasecmp(rx_message.Headers[i].FieldName, (CHAR8 *)"Content-Length")) { *buf_size = ascii_to_int(rx_message.Headers[i].FieldValue); + for(j = 0; j < i; j++) { + if (!strcasecmp(rx_message.Headers[i].FieldName, + (CHAR8 *)"Content-Length")) { + if (*buf_size != ascii_to_int(rx_message.Headers[j].FieldValue)) { + perror(L"Content-Length is invalid\n"); + goto error; + } + } + } } } diff --git a/include/compiler.h b/include/compiler.h index 8e8a658..6a19217 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -175,14 +175,19 @@ #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg) #endif -#ifndef ALIGN +#ifndef __ALIGN #define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) #define __ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a) - 1) +#endif +#ifndef ALIGN #define ALIGN(x, a) __ALIGN((x), (a)) #endif #ifndef ALIGN_DOWN #define ALIGN_DOWN(x, a) __ALIGN((x) - ((a) - 1), (a)) #endif +#ifndef ALIGN_UP +#define ALIGN_UP(addr, align) (((addr) + (typeof (addr)) (align) - 1) & ~((typeof (addr)) (align) - 1)) +#endif #define MIN(a, b) ({(a) < (b) ? (a) : (b);}) #define MAX(a, b) ({(a) <= (b) ? (b) : (a);}) @@ -205,6 +210,7 @@ #define GNUC_PREREQ(maj, min) 0 #endif +#if !defined(CLANG_PREREQ) #if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) #define CLANG_PREREQ(maj, min) \ ((__clang_major__ > (maj)) || \ @@ -212,6 +218,7 @@ #else #define CLANG_PREREQ(maj, min) 0 #endif +#endif /* CLANG_PREREQ */ #if GNUC_PREREQ(5, 1) || CLANG_PREREQ(3, 8) #define checked_add(addend0, addend1, sum) \ diff --git a/include/console.h b/include/console.h index 7ac4e11..c90e3e7 100644 --- a/include/console.h +++ b/include/console.h @@ -98,6 +98,7 @@ extern UINT32 verbose; #ifndef SHIM_UNIT_TEST #define dprint_(fmt, ...) ({ \ UINTN __dprint_ret = 0; \ + log_debug_print((fmt), ##__VA_ARGS__); \ if (verbose) \ __dprint_ret = console_print((fmt), ##__VA_ARGS__); \ __dprint_ret; \ diff --git a/include/dp.h b/include/dp.h new file mode 100644 index 0000000..884c146 --- /dev/null +++ b/include/dp.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * dp.h - device path helper functions + * Copyright Peter Jones + */ + +#ifndef DP_H_ +#define DP_H_ + +int +is_removable_media_path(EFI_LOADED_IMAGE *li); + +#endif /* !DP_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/errlog.h b/include/errlog.h new file mode 100644 index 0000000..b9f089b --- /dev/null +++ b/include/errlog.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * errlog.h - error logging utilities + * Copyright Peter Jones + */ + +#ifndef ERRLOG_H_ +#define ERRLOG_H_ + +extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, + const CHAR16 *fmt, ...); +extern EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func, + const CHAR16 *fmt, ms_va_list args); +extern VOID LogHexdump_(const char *file, int line, const char *func, + const void *data, size_t sz); +extern VOID PrintErrors(VOID); +extern VOID ClearErrors(VOID); +extern void save_logs(void); +extern UINTN EFIAPI log_debug_print(const CHAR16 *fmt, ...); + +#endif /* !ERRLOG_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/errors.h b/include/errors.h index 67d821e..eab5845 100644 --- a/include/errors.h +++ b/include/errors.h @@ -9,5 +9,8 @@ #ifndef EFI_SECURITY_VIOLATION #define EFI_SECURITY_VIOLATION EFIERR(26) #endif +#ifndef EFI_HTTP_ERROR +#define EFI_HTTP_ERROR EFIERR(35) +#endif #endif /* SHIM_ERRORS_H */ diff --git a/include/fanalyzer.mk b/include/fanalyzer.mk index a0679e3..b22656e 100644 --- a/include/fanalyzer.mk +++ b/include/fanalyzer.mk @@ -21,7 +21,7 @@ fanalyzer-build-all : COMPILER=gcc fanalyzer-build-all : CCACHE_DISABLE=1 fanalyzer-build-all : FEATUREFLAGS+=-fanalyzer fanalyzer-build-all : WERRFLAGS=-Werror=analyzer-null-dereference -fanalyzer-build-all : IGNORE_COMPILER_ERRORS=" || :" +fanalyzer-build-all : IGNORE_COMPILER_ERRORS= || : fanalyzer-build-all : all fanalyzer-no-openssl : | fanalyzer-test diff --git a/include/guid.h b/include/guid.h index 898c4fa..26628d1 100644 --- a/include/guid.h +++ b/include/guid.h @@ -3,6 +3,16 @@ #ifndef SHIM_GUID_H #define SHIM_GUID_H +#define LGUID_FMT L"%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" +#define GUID_FMT "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" + +#define GUID_ARGS(guid) \ + ((EFI_GUID)guid).Data1, ((EFI_GUID)guid).Data2, ((EFI_GUID)guid).Data3, \ + ((EFI_GUID)guid).Data4[1], ((EFI_GUID)guid).Data4[0], \ + ((EFI_GUID)guid).Data4[2], ((EFI_GUID)guid).Data4[3], \ + ((EFI_GUID)guid).Data4[4], ((EFI_GUID)guid).Data4[5], \ + ((EFI_GUID)guid).Data4[6], ((EFI_GUID)guid).Data4[7] + extern EFI_GUID BDS_GUID; extern EFI_GUID GV_GUID; extern EFI_GUID SIG_DB; @@ -36,6 +46,8 @@ extern EFI_GUID SECURITY_PROTOCOL_GUID; extern EFI_GUID SECURITY2_PROTOCOL_GUID; extern EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; extern EFI_GUID SHIM_LOCK_GUID; +extern EFI_GUID SHIM_IMAGE_LOADER_GUID; +extern EFI_GUID SHIM_LOADED_IMAGE_GUID; extern EFI_GUID MOK_VARIABLE_STORE; extern EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID; diff --git a/include/hexdump.h b/include/hexdump.h index e8f4fe1..6f3d5fa 100644 --- a/include/hexdump.h +++ b/include/hexdump.h @@ -89,10 +89,14 @@ vhexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, if (verbose == 0) return; - if (!data || !size) { + if (!data) { dprint(L"hexdump of a NULL pointer!\n"); return; } + if (!size) { + dprint(L"hexdump of a 0 size region!\n"); + return; + } while (offset < size) { char hexbuf[49]; diff --git a/include/load-options.h b/include/load-options.h index d2bee3b..78b1dcc 100644 --- a/include/load-options.h +++ b/include/load-options.h @@ -13,6 +13,7 @@ EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li, EFI_STATUS parse_load_options(EFI_LOADED_IMAGE *li); extern CHAR16 *second_stage; +extern CHAR16 *optional_second_stage; extern void *load_options; extern UINT32 load_options_size; diff --git a/include/replacements.h b/include/loader-proto.h similarity index 70% rename from include/replacements.h rename to include/loader-proto.h index 8b35c85..db8e670 100644 --- a/include/replacements.h +++ b/include/loader-proto.h @@ -16,7 +16,6 @@ typedef enum { } verification_method_t; extern verification_method_t verification_method; -extern int loader_is_participating; extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab); extern void unhook_system_services(void); @@ -24,7 +23,14 @@ extern void unhook_system_services(void); extern void hook_exit(EFI_SYSTEM_TABLE *local_systab); extern void unhook_exit(void); -extern EFI_STATUS install_shim_protocols(void); -extern void uninstall_shim_protocols(void); +typedef struct _SHIM_IMAGE_LOADER { + EFI_IMAGE_LOAD LoadImage; + EFI_IMAGE_START StartImage; + EFI_EXIT Exit; + EFI_IMAGE_UNLOAD UnloadImage; +} SHIM_IMAGE_LOADER; + +extern SHIM_IMAGE_LOADER shim_image_loader_interface; +extern void init_image_loader(void); #endif /* SHIM_REPLACEMENTS_H */ diff --git a/include/memattrs.h b/include/memattrs.h new file mode 100644 index 0000000..193da98 --- /dev/null +++ b/include/memattrs.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * memattrs.h - EFI and DXE memory attribute helpers + * Copyright Peter Jones + */ + +#ifndef SHIM_MEMATTRS_H_ +#define SHIM_MEMATTRS_H_ + +extern EFI_STATUS get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs); +extern EFI_STATUS update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs); + +extern void get_hsi_mem_info(void); +extern char *decode_hsi_bits(UINTN hsi); + +#endif /* !SHIM_MEMATTRS_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/mock-variables.h b/include/mock-variables.h index 9f276e6..b7ee1cb 100644 --- a/include/mock-variables.h +++ b/include/mock-variables.h @@ -115,6 +115,9 @@ void mock_uninstall_query_variable_info(void); void mock_reset_variables(void); void mock_reset_config_table(void); void mock_finalize_vars_and_configs(void); +void mock_set_usage_limits(list_t *limit_list, + struct mock_variable_limits *limits); +void mock_set_default_usage_limits(void); typedef enum { NONE = 0, diff --git a/include/mok.h b/include/mok.h index fb19423..f4468ab 100644 --- a/include/mok.h +++ b/include/mok.h @@ -17,6 +17,14 @@ typedef enum { struct mok_state_variable; typedef vendor_addend_category_t (vendor_addend_categorizer_t)(struct mok_state_variable *); +typedef UINTN (mok_variable_format_helper_t)(UINT8 *buf, size_t sz, struct mok_state_variable *); + +#define MOK_MIRROR_KEYDB 0x01 +#define MOK_MIRROR_DELETE_FIRST 0x02 +#define MOK_VARIABLE_MEASURE 0x04 +#define MOK_VARIABLE_LOG 0x08 +#define MOK_VARIABLE_INVERSE 0x10 +#define MOK_VARIABLE_CONFIG_ONLY 0x20 /* * MoK variables that need to have their storage validated. @@ -81,6 +89,8 @@ struct mok_state_variable { * MOK_MIRROR_DELETE_FIRST delete any existing variable first * MOK_VARIABLE_MEASURE extend PCR 7 and log the hash change * MOK_VARIABLE_LOG measure into whatever .pcr says and log + * MOK_VARIABLE_CONFIG_ONLY don't create a UEFI variable, only add + * it to the config space variables. */ UINTN pcr; /* PCR to measure and hash to */ @@ -89,6 +99,23 @@ struct mok_state_variable { * mirrored. */ UINT8 *state; + + /* + * If this is non-NULL, this function will be called during the + * "import" phase to format the variable data. It'll get called + * twice, once as: + * + * sz = format(NULL, 0, ptr); + * + * a buffer of size sz will then be allocated, and it'll be called + * again to fill the buffer: + * + * format(buf, sz, ptr); + * + * Note that as an implementation detail data and data_size must be + * NULL and 0 respectively for this entry. + */ + mok_variable_format_helper_t *format; }; extern size_t n_mok_state_variables; @@ -100,10 +127,31 @@ struct mok_variable_config_entry { UINT8 data[]; }; +extern EFI_PHYSICAL_ADDRESS mok_config_table; +extern UINTN mok_config_table_pages; + /* * bit definitions for MokPolicy */ #define MOK_POLICY_REQUIRE_NX 1 +extern UINTN hsi_status; +/* heap is executable */ +#define SHIM_HSI_STATUS_HEAPX 0x00000001ULL +/* stack is executable */ +#define SHIM_HSI_STATUS_STACKX 0x00000002ULL +/* read-only sections are writable */ +#define SHIM_HSI_STATUS_ROW 0x00000004ULL +/* platform provides the EFI Memory Attribute Protocol */ +#define SHIM_HSI_STATUS_HASMAP 0x00000008ULL +/* platform provides DXE Services Table */ +#define SHIM_HSI_STATUS_HASDST 0x00000010ULL +/* platform has DST->GetMemorySpaceDescriptor */ +#define SHIM_HSI_STATUS_HASDSTGMSD 0x00000020ULL +/* platform has DST->SetMemorySpaceAttributes */ +#define SHIM_HSI_STATUS_HASDSTSMSA 0x00000040ULL +/* This shim has the NX_COMPAT bit set */ +#define SHIM_HSI_STATUS_NX 0x00000100ULL + #endif /* !SHIM_MOK_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/netboot.h b/include/netboot.h index a7bf6cd..296f10f 100644 --- a/include/netboot.h +++ b/include/netboot.h @@ -3,10 +3,13 @@ #ifndef SHIM_NETBOOT_H #define SHIM_NETBOOT_H +#define SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE 1 + extern BOOLEAN findNetboot(EFI_HANDLE image_handle); extern EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle, CHAR8 *name); -extern EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, UINT64 *bufsiz); +extern EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, + UINT64 *bufsiz, int flags); #endif /* SHIM_NETBOOT_H */ diff --git a/include/pe.h b/include/pe.h index 9ea9eb4..ea40184 100644 --- a/include/pe.h +++ b/include/pe.h @@ -12,7 +12,8 @@ ImageAddress (void *image, uint64_t size, uint64_t address); EFI_STATUS read_header(void *data, unsigned int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context); + PE_COFF_LOADER_IMAGE_CONTEXT *context, + bool check_secdir); EFI_STATUS verify_image(void *data, unsigned int datasize, EFI_LOADED_IMAGE *li, @@ -52,5 +53,8 @@ relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, EFI_IMAGE_SECTION_HEADER *Section, void *orig, void *data); +void +get_shim_nx_capability(EFI_HANDLE image_handle); + #endif /* !PE_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/peimage.h b/include/peimage.h index 6eef105..5d04968 100644 --- a/include/peimage.h +++ b/include/peimage.h @@ -144,12 +144,12 @@ typedef struct { /// /// @attention -/// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and +/// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and /// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary /// after NT additional fields. /// #define EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b - + /// /// Optional Header Standard Fields for PE32. /// @@ -195,7 +195,7 @@ typedef struct { /// /// @attention -/// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and +/// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and /// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary /// after NT additional fields. /// @@ -465,7 +465,7 @@ typedef struct { #define EFI_IMAGE_COMDAT_SELECT_SAME_SIZE 3 #define EFI_IMAGE_COMDAT_SELECT_EXACT_MATCH 4 #define EFI_IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 - + // // the following values only be referred in PeCoff, not defined in PECOFF. // @@ -500,9 +500,9 @@ typedef struct { #define EFI_IMAGE_REL_I386_SECREL 0x000B #define EFI_IMAGE_REL_I386_REL32 0x0014 ///< PC-relative 32-bit reference to the symbols virtual address. -// +// // x64 processor relocation types. -// +// #define IMAGE_REL_AMD64_ABSOLUTE 0x0000 #define IMAGE_REL_AMD64_ADDR64 0x0001 #define IMAGE_REL_AMD64_ADDR32 0x0002 @@ -824,6 +824,7 @@ typedef struct { EFI_IMAGE_DATA_DIRECTORY *RelocDir; EFI_IMAGE_DATA_DIRECTORY *SecDir; UINT64 NumberOfRvaAndSizes; + UINT16 DllCharacteristics; EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr; } PE_COFF_LOADER_IMAGE_CONTEXT; diff --git a/include/sbat.h b/include/sbat.h index bb523e7..093bb64 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -38,7 +38,8 @@ #define POLICY_RESET 3 #define POLICY_NOTREAD 255 -#define REVOCATIONFILE L"revocations.efi" +#define SBATREVOCATIONFILE L"revocations_sbat.efi" +#define SKUSIREVOCATIONFILE L"revocations_sku.efi" extern UINTN _sbat, _esbat; diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index f8cba02..f4f5a27 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -7,7 +7,9 @@ #define QUOTE(s) #s /* - * This is the entry for the sbat data format + * SbatLevel Epoch and SHIM_DEVEL definitions are here + * Actual revocations are now soley defined in + * SbatLevel_Variable.txt */ #define SBAT_VAR_SIG "sbat," #define SBAT_VAR_VERSION "1," @@ -22,46 +24,10 @@ #define SBAT_VAR_LATEST_DATE "2022050100" #define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" -#define SBAT_VAR_LATEST \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ - SBAT_VAR_LATEST_REVOCATIONS -#else /* !ENABLE_SHIM_DEVEL */ -/* - * Some distros may want to apply revocations from 2022052400 - * or 2022111500 automatically. They can be selected by setting - * SBAT_AUTOMATIC_DATE= at build time. Otherwise the - * default is to apply the second to most recent revocations - * automatically. Distros that need to manage automatic updates - * externally from shim can choose the epoch 2021030218 emtpy - * revocations. - */ -#ifndef SBAT_AUTOMATIC_DATE -#define SBAT_AUTOMATIC_DATE 2023012900 -#endif /* SBAT_AUTOMATIC_DATE */ -#if SBAT_AUTOMATIC_DATE == 2021030218 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS -#elif SBAT_AUTOMATIC_DATE == 2022052400 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "grub,2\n" -#elif SBAT_AUTOMATIC_DATE == 2022111500 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\n" -#elif SBAT_AUTOMATIC_DATE == 2023012900 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\ngrub.debian,4\n" -#else -#error "Unknown SBAT_AUTOMATIC_DATE" -#endif /* SBAT_AUTOMATIC_DATE == */ -#define SBAT_VAR_AUTOMATIC_DATE QUOTEVAL(SBAT_AUTOMATIC_DATE) -#define SBAT_VAR_AUTOMATIC \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE "\n" \ - SBAT_VAR_AUTOMATIC_REVOCATIONS - -/* - * Revocations for January 2024 shim CVEs - */ -#define SBAT_VAR_LATEST_DATE "2024010900" -#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\n" #define SBAT_VAR_LATEST \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS #endif /* ENABLE_SHIM_DEVEL */ + #endif /* !SBAT_VAR_DEFS_H_ */ diff --git a/include/test-data-efivars-1.h b/include/test-data-efivars-1.h index 2831bd2..259558e 100644 --- a/include/test-data-efivars-1.h +++ b/include/test-data-efivars-1.h @@ -106,5 +106,16 @@ static const unsigned char test_data_efivars_1_MokListTrustedRT[] ={ 0x01 }; +static const unsigned char test_data_efivars_1_HSIStatus[] = + "heap-is-executable: 0\n" + "stack-is-executable: 0\n" + "ro-sections-are-writable: 0\n" + "has-memory-attribute-protocol: 0\n" + "has-dxe-services-table: 0\n" + "has-get-memory-space-descriptor: 0\n" + "has-set-memory-space-attributes: 0\n" + "shim-has-nx-compat-set: 0\n" + ; + #endif /* !TEST_DATA_EFIVARS_1_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/test.h b/include/test.h index 5261dbd..ccb6114 100644 --- a/include/test.h +++ b/include/test.h @@ -85,14 +85,14 @@ extern EFI_RUNTIME_SERVICES *RT; static inline INT64 guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): Comparing "GUID_FMT" to "GUID_FMT"\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(*guid0), GUID_ARGS(*guid1)); #endif if (guid0->Data1 != guid1->Data1) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data1, (INT64)guid1->Data1, @@ -102,7 +102,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) } if (guid0->Data2 != guid1->Data2) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data2, (INT64)guid1->Data2, @@ -112,7 +112,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) } if (guid0->Data3 != guid1->Data3) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data3, (INT64)guid1->Data3, @@ -126,7 +126,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) * representation of it. */ if (guid0->Data4[1] != guid1->Data4[1]) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data4[1], (INT64)guid1->Data4[1], @@ -136,7 +136,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) } if (guid0->Data4[0] != guid1->Data4[0]) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data4[0], (INT64)guid1->Data4[0], @@ -147,7 +147,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) for (UINTN i = 2; i < 8; i++) { if (guid0->Data4[i] != guid1->Data4[i]) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data4[i], (INT64)guid1->Data4[i], @@ -157,7 +157,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) } } -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x0\n", __FILE__, __LINE__-1, __func__); #endif @@ -177,7 +177,7 @@ guidcmp(const EFI_GUID * const guid0, const EFI_GUID * const guid1) cmp = guidcmp_helper(guida, guidb); ret = cmp < 0 ? -1 : (cmp > 0 ? 1 : 0); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s():CompareGuid("GUID_FMT","GUID_FMT")->%lld (%d)\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(*guida), GUID_ARGS(*guidb), cmp, ret); diff --git a/include/test.mk b/include/test.mk index e6d4659..ee2d2fd 100644 --- a/include/test.mk +++ b/include/test.mk @@ -76,8 +76,12 @@ libefi-test.a : clean test-random.h: - dd if=/dev/urandom bs=512 count=17 of=random.bin - xxd -i random.bin test-random.h + dd if=/dev/urandom bs=512 count=17 status=none | ( \ + echo "unsigned char random_bin[] = {" ; \ + xxd -i - ; \ + echo "};" ; \ + echo "unsigned int random_bin_len = 8704;" ; \ + ) > test-random.h $(wildcard test-*.c) :: %.c : test-random.h $(patsubst %.c,%,$(wildcard test-*.c)) :: | test-random.h @@ -119,7 +123,7 @@ test-coverage : CFLAGS_GCOV+=--coverage test-coverage : $(tests) test-clean : - @rm -vf test-random.h random.bin libefi-test.a + @rm -vf test-random.h libefi-test.a @rm -vf *.gcda *.gcno *.gcov vgcore.* clean : test-clean @@ -127,6 +131,5 @@ clean : test-clean all : test-clean test .PHONY: $(tests) all test clean -.SECONDARY: random.bin # vim:ft=make diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..654f05d --- /dev/null +++ b/include/utils.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +#ifndef UTILS_H_ +#define UTILS_H_ + +EFI_STATUS get_file_size(EFI_FILE_HANDLE fh, UINTN *retsize); +EFI_STATUS +read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs); + +#endif /* UTILS_H_ */ diff --git a/lib/console.c b/lib/console.c index a751f79..f603832 100644 --- a/lib/console.c +++ b/lib/console.c @@ -651,6 +651,7 @@ static struct { { EFI_PROTOCOL_ERROR, L"Protocol Error"}, { EFI_INCOMPATIBLE_VERSION, L"Incompatible Version"}, { EFI_SECURITY_VIOLATION, L"Security Violation"}, + { EFI_HTTP_ERROR, L"HTTP Error"}, // warnings { EFI_WARN_UNKNOWN_GLYPH, L"Warning Unknown Glyph"}, diff --git a/lib/guid.c b/lib/guid.c index 6e92cea..1dc90ca 100644 --- a/lib/guid.c +++ b/lib/guid.c @@ -35,5 +35,7 @@ EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }; EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID = { 0xf4560cf6, 0x40ec, 0x4b4a, {0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89} }; EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }; +EFI_GUID SHIM_IMAGE_LOADER_GUID = {0x1f492041, 0xfadb, 0x4e59, {0x9e, 0x57, 0x7c, 0xaf, 0xe7, 0x3a, 0x55, 0xab } }; +EFI_GUID SHIM_LOADED_IMAGE_GUID = {0x6e6baeb8, 0x7108, 0x4179, {0x94, 0x9d, 0xa3, 0x49, 0x34, 0x15, 0xec, 0x97 } }; EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} }; EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID = {0x77fa9abd, 0x0359, 0x4d32, {0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b} }; diff --git a/lib/simple_file.c b/lib/simple_file.c index f22852d..abbc497 100644 --- a/lib/simple_file.c +++ b/lib/simple_file.c @@ -170,7 +170,7 @@ simple_file_write_all(EFI_FILE *file, UINTN size, void *buffer) EFI_STATUS simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h) { - UINTN count, i; + UINTN count, i, j; EFI_HANDLE *vol_handles = NULL; EFI_STATUS efi_status; CHAR16 **entries; @@ -184,11 +184,11 @@ simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h) if (!count || !vol_handles) return EFI_NOT_FOUND; - entries = AllocatePool(sizeof(CHAR16 *) * (count+1)); + entries = AllocateZeroPool(sizeof(CHAR16 *) * (count+1)); if (!entries) return EFI_OUT_OF_RESOURCES; - for (i = 0; i < count; i++) { + for (i = 0, j = 0; i < count; i++) { char buf[4096]; UINTN size = sizeof(buf); EFI_FILE_SYSTEM_INFO *fi = (void *)buf; @@ -208,19 +208,22 @@ simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h) efi_status = root->GetInfo(root, &EFI_FILE_SYSTEM_INFO_GUID, &size, fi); - if (EFI_ERROR(efi_status)) - continue; + /* If GetInfo fails, try to form a name from DevicePath. */ + if (EFI_ERROR(efi_status)){ + name = NULL; + } else { + name = fi->VolumeLabel; + } - name = fi->VolumeLabel; if (!name || StrLen(name) == 0 || StrCmp(name, L" ") == 0) name = DevicePathToStr(DevicePathFromHandle(vol_handles[i])); - entries[i] = AllocatePool((StrLen(name) + 2) * sizeof(CHAR16)); - if (!entries[i]) + entries[j] = AllocatePool((StrLen(name) + 2) * sizeof(CHAR16)); + if (!entries[j]) break; - StrCpy(entries[i], name); + StrCpy(entries[j++], name); } - entries[i] = NULL; + entries[j] = NULL; val = console_select(title, entries, 0); @@ -285,7 +288,7 @@ simple_dir_filter(EFI_HANDLE image, CHAR16 *name, CHAR16 *filter, goto out; ptr = next = *entries; - for (i = 0; i < tot; i++) { + for (i = 0; next && i < tot; i++) { int len = StrLen(next->FileName); for (c = 0; c < filtercount; c++) { @@ -308,7 +311,7 @@ simple_dir_filter(EFI_HANDLE image, CHAR16 *name, CHAR16 *filter, *count = 0; ptr = next = *entries; - for (i = 0; i < tot; i++) { + for (i = 0; next && i < tot; i++) { int len = StrLen(next->FileName); if (StrCmp(next->FileName, L".") == 0) diff --git a/lib/variables.c b/lib/variables.c index 8e63aa8..1a2c7d4 100644 --- a/lib/variables.c +++ b/lib/variables.c @@ -226,6 +226,8 @@ SetSecureVariable(const CHAR16 * const var, UINT8 *Data, UINTN len, } efi_status = CreateTimeBasedPayload(&DataSize, (UINT8 **)&Cert); if (EFI_ERROR(efi_status)) { + if (Cert && Cert != (EFI_SIGNATURE_LIST *)Data) + FreePool(Cert); console_print(L"Failed to create time based payload %d\n", efi_status); return efi_status; diff --git a/load-options.c b/load-options.c index a8c6e1a..660eaa9 100644 --- a/load-options.c +++ b/load-options.c @@ -6,6 +6,7 @@ #include "shim.h" CHAR16 *second_stage; +CHAR16 *optional_second_stage = NULL; void *load_options; UINT32 load_options_size; @@ -207,14 +208,14 @@ get_load_option_optional_data(VOID *data, UINT32 data_size, */ i += dp.len; } - if (i != fplistlen) + if (i > fplistlen) return EFI_INVALID_PARAMETER; /* - * if there's any space left, it's "optional data" + * Anything left after the file path list is optional data. */ - *od = cur + i; - *ods = limit - i; + *od = cur + fplistlen; + *ods = limit - fplistlen; return EFI_SUCCESS; } @@ -310,8 +311,13 @@ parse_load_options(EFI_LOADED_IMAGE *li) UINT32 remaining_size; CHAR16 *loader_str = NULL; - dprint(L"full load options:\n"); - dhexdumpat(li->LoadOptions, li->LoadOptionsSize, 0); + if (!li->LoadOptions || !li->LoadOptionsSize) { + dprint(L"LoadOptions is empty\n"); + return EFI_SUCCESS; + } else { + dprint(L"full LoadOptions:\n"); + dhexdumpat(li->LoadOptions, li->LoadOptionsSize, 0); + } /* * Sanity check since we make several assumptions about the length @@ -442,15 +448,25 @@ parse_load_options(EFI_LOADED_IMAGE *li) } } + /* + * Windows bcdedit.exe puts "WINDOWS\0" (in 8-bit) in the beginning of + * the options, so if we see that, we know it's not useful to us. + */ + if (li->LoadOptionsSize >= 8) + if (CompareMem(li->LoadOptions, "WINDOWS", 8) == 0) + return EFI_SUCCESS; + loader_str = split_load_options(li->LoadOptions, li->LoadOptionsSize, &remaining, &remaining_size); /* * Set up the name of the alternative loader and the LoadOptions for - * the loader + * the loader if it's not the empty string. */ if (loader_str) { - second_stage = loader_str; + if (*loader_str) { + second_stage = loader_str; + } load_options = remaining; load_options_size = remaining_size; } diff --git a/loader-proto.c b/loader-proto.c new file mode 100644 index 0000000..4581664 --- /dev/null +++ b/loader-proto.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * loader-proto.c - shim's loader protocol + * + * Copyright Red Hat, Inc + * Copyright Canonical, Ltd + */ + +#include "shim.h" + +static EFI_SYSTEM_TABLE *systab; + +EFI_SYSTEM_TABLE * +get_active_systab(void) +{ + if (systab) + return systab; + return ST; +} + +static typeof(systab->BootServices->LoadImage) system_load_image; +static typeof(systab->BootServices->StartImage) system_start_image; +static typeof(systab->BootServices->UnloadImage) system_unload_image; +static typeof(systab->BootServices->Exit) system_exit; + +void +unhook_system_services(void) +{ + if (!systab) + return; + + systab->BootServices->LoadImage = system_load_image; + systab->BootServices->StartImage = system_start_image; + systab->BootServices->Exit = system_exit; + systab->BootServices->UnloadImage = system_unload_image; + BS = systab->BootServices; +} + +typedef struct { + EFI_HANDLE hnd; + EFI_DEVICE_PATH *dp; + void *buffer; + size_t size; +} buffer_properties_t; + +static EFI_STATUS +try_load_from_sfs(EFI_DEVICE_PATH *dp, buffer_properties_t *bprop) +{ + EFI_STATUS status = EFI_SUCCESS; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *sfs = NULL; + EFI_FILE_HANDLE root = NULL; + EFI_FILE_HANDLE file = NULL; + UINT64 tmpsz = 0; + + bprop->buffer = NULL; + + /* look for a handle with SFS support from the input DP */ + bprop->dp = dp; + status = BS->LocateDevicePath(&EFI_SIMPLE_FILE_SYSTEM_GUID, &bprop->dp, &bprop->hnd); + if (EFI_ERROR(status)) { + goto out; + } + + /* make sure the remaining DP portion is really a file path */ + if (DevicePathType(bprop->dp) != MEDIA_DEVICE_PATH || + DevicePathSubType(bprop->dp) != MEDIA_FILEPATH_DP) { + status = EFI_LOAD_ERROR; + goto out; + } + + /* find protocol, open the root directory, then open file */ + status = BS->HandleProtocol(bprop->hnd, &EFI_SIMPLE_FILE_SYSTEM_GUID, (void **)&sfs); + if (EFI_ERROR(status)) + goto out; + status = sfs->OpenVolume(sfs, &root); + if (EFI_ERROR(status)) + goto out; + status = root->Open(root, &file, ((FILEPATH_DEVICE_PATH *) bprop->dp)->PathName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(status)) + goto out; + + /* get file size */ + status = file->SetPosition(file, -1ULL); + if (EFI_ERROR(status)) + goto out; + status = file->GetPosition(file, &tmpsz); + if (EFI_ERROR(status)) + goto out; + bprop->size = (size_t)tmpsz; + status = file->SetPosition(file, 0); + if (EFI_ERROR(status)) + goto out; + + /* allocate buffer */ + bprop->buffer = AllocatePool(bprop->size); + if (bprop->buffer == NULL) { + status = EFI_OUT_OF_RESOURCES; + goto out; + } + + /* read file */ + status = file->Read(file, &bprop->size, bprop->buffer); + +out: + if (EFI_ERROR(status) && bprop->buffer) + FreePool(bprop->buffer); + if (file) + file->Close(file); + if (root) + root->Close(root); + return status; +} + + +static EFI_STATUS +try_load_from_lf2(EFI_DEVICE_PATH *dp, buffer_properties_t *bprop) +{ + EFI_STATUS status = EFI_SUCCESS; + EFI_LOAD_FILE2_PROTOCOL *lf2 = NULL; + + bprop->buffer = NULL; + + /* look for a handle with LF2 support from the input DP */ + bprop->dp = dp; + status = BS->LocateDevicePath(&gEfiLoadFile2ProtocolGuid, &bprop->dp, &bprop->hnd); + if (EFI_ERROR(status)) + goto out; + + /* find protocol */ + status = BS->HandleProtocol(bprop->hnd, &gEfiLoadFile2ProtocolGuid, (void **) &lf2); + if (EFI_ERROR(status)) + goto out; + + /* get file size */ + bprop->size = 0; /* this shouldn't be read when Buffer=NULL but better be safe */ + status = lf2->LoadFile(lf2, bprop->dp, /*BootPolicy=*/false, &bprop->size, NULL); + /* + * NOTE: the spec is somewhat ambiguous what is the correct return + * status code when asking for the buffer size with Buffer=NULL. I am + * assuming EFI_SUCCESS and EFI_BUFFER_TOO_SMALL are the only + * reasonable interpretations. + */ + if (EFI_ERROR(status) && status != EFI_BUFFER_TOO_SMALL) { + status = EFI_LOAD_ERROR; + goto out; + } + + /* allocate buffer */ + bprop->buffer = AllocatePool(bprop->size); + if (!bprop->buffer) { + status = EFI_OUT_OF_RESOURCES; + goto out; + } + + /* read file */ + status = lf2->LoadFile(lf2, bprop->dp, /*BootPolicy=*/false, &bprop->size, bprop->buffer); + if (EFI_ERROR(status)) + goto out; + +out: + if (EFI_ERROR(status) && bprop->buffer) + FreePool(bprop->buffer); + return status; +} + +static EFI_STATUS EFIAPI +shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, + EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer, + UINTN SourceSize, EFI_HANDLE *ImageHandle) +{ + SHIM_LOADED_IMAGE *image; + EFI_STATUS efi_status; + buffer_properties_t bprop = { NULL, NULL, NULL, 0 }; + + if (BootPolicy) + return EFI_UNSUPPORTED; + + if (!SourceBuffer || !SourceSize) { + if (!DevicePath) /* Both SourceBuffer and DevicePath are NULL */ + return EFI_NOT_FOUND; + + if (try_load_from_sfs(DevicePath, &bprop) == EFI_SUCCESS) + ; + else if (try_load_from_lf2(DevicePath, &bprop) == EFI_SUCCESS) + ; + else + /* no buffer given and we cannot load from this device */ + return EFI_LOAD_ERROR; + + SourceBuffer = bprop.buffer; + SourceSize = bprop.size; + } else { + bprop.buffer = NULL; + /* + * Even if we are using a buffer, try populating the + * device_handle and file_path fields the best we can + */ + + bprop.dp = DevicePath; + + if (bprop.dp) { + efi_status = BS->LocateDevicePath(&gEfiDevicePathProtocolGuid, + &bprop.dp, + &bprop.hnd); + if (efi_status != EFI_SUCCESS) { + /* can't seem to pull apart this DP */ + bprop.dp = DevicePath; + bprop.hnd = NULL; + } + } + } + + image = AllocatePool(sizeof(*image)); + if (!image) { + efi_status = EFI_OUT_OF_RESOURCES; + goto free_buffer; + } + + SetMem(image, sizeof(*image), 0); + + image->li.Revision = 0x1000; + image->li.ParentHandle = ParentImageHandle; + image->li.SystemTable = systab; + image->li.DeviceHandle = bprop.hnd; + if (bprop.dp) { + image->li.FilePath = DuplicateDevicePath(bprop.dp); + if (!image->li.FilePath) { + efi_status = EFI_OUT_OF_RESOURCES; + goto free_image; + } + } + if (DevicePath) { + image->loaded_image_device_path = DuplicateDevicePath(DevicePath); + if (!image->loaded_image_device_path) { + efi_status = EFI_OUT_OF_RESOURCES; + goto free_image; + } + } + + in_protocol = 1; + efi_status = handle_image(SourceBuffer, SourceSize, &image->li, + &image->entry_point, &image->alloc_address, + &image->alloc_pages); + in_protocol = 0; + if (EFI_ERROR(efi_status)) + goto free_image; + + *ImageHandle = NULL; + efi_status = BS->InstallMultipleProtocolInterfaces(ImageHandle, + &SHIM_LOADED_IMAGE_GUID, image, + &EFI_LOADED_IMAGE_GUID, &image->li, + &gEfiLoadedImageDevicePathProtocolGuid, + image->loaded_image_device_path, + NULL); + if (EFI_ERROR(efi_status)) + goto free_alloc; + + if (bprop.buffer) + FreePool(bprop.buffer); + + return EFI_SUCCESS; + +free_alloc: + BS->FreePages(image->alloc_address, image->alloc_pages); +free_image: + if (image->loaded_image_device_path) + FreePool(image->loaded_image_device_path); + if (image->li.FilePath) + FreePool(image->li.FilePath); + FreePool(image); +free_buffer: + if (bprop.buffer) + FreePool(bprop.buffer); + return efi_status; +} + +static EFI_STATUS EFIAPI +shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, + OUT CHAR16 **ExitData OPTIONAL) +{ + SHIM_LOADED_IMAGE *image; + EFI_STATUS efi_status; + + efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, + (void **)&image); + + /* + * This image didn't come from shim_load_image(), so it must have come + * from something before shim was involved. + */ + if (efi_status == EFI_UNSUPPORTED) + return system_start_image(ImageHandle, ExitDataSize, ExitData); + + if (EFI_ERROR(efi_status) || image->started) + return EFI_INVALID_PARAMETER; + + if (!setjmp(image->longjmp_buf)) { + image->started = true; + efi_status = + image->entry_point(ImageHandle, image->li.SystemTable); + } else { + if (ExitData) { + *ExitDataSize = image->exit_data_size; + *ExitData = (CHAR16 *)image->exit_data; + } + efi_status = image->exit_status; + } + + // + // We only support EFI applications, so we can unload and free the + // image unconditionally. + // + BS->UninstallMultipleProtocolInterfaces(ImageHandle, + &EFI_LOADED_IMAGE_GUID, image, + &SHIM_LOADED_IMAGE_GUID, &image->li, + &gEfiLoadedImageDevicePathProtocolGuid, + image->loaded_image_device_path, + NULL); + + BS->FreePages(image->alloc_address, image->alloc_pages); + if (image->li.FilePath) + BS->FreePool(image->li.FilePath); + if (image->loaded_image_device_path) + BS->FreePool(image->loaded_image_device_path); + FreePool(image); + + return efi_status; +} + +static EFI_STATUS EFIAPI +shim_unload_image(EFI_HANDLE ImageHandle) +{ + SHIM_LOADED_IMAGE *image; + EFI_STATUS efi_status; + + efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, + (void **)&image); + + if (efi_status == EFI_UNSUPPORTED) + return system_unload_image(ImageHandle); + + BS->FreePages(image->alloc_address, image->alloc_pages); + FreePool(image); + + return EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI +shim_exit(EFI_HANDLE ImageHandle, + EFI_STATUS ExitStatus, + UINTN ExitDataSize, + CHAR16 *ExitData) +{ + EFI_STATUS efi_status; + SHIM_LOADED_IMAGE *image; + + efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, + (void **)&image); + + /* + * If this happens, something above us on the stack of running + * applications called Exit(), and we're getting aborted along with + * it. + */ + if (efi_status == EFI_UNSUPPORTED) { + shim_fini(); + return system_exit(ImageHandle, ExitStatus, ExitDataSize, + ExitData); + } + + if (EFI_ERROR(efi_status)) + return efi_status; + + image->exit_status = ExitStatus; + image->exit_data_size = ExitDataSize; + image->exit_data = ExitData; + + longjmp(image->longjmp_buf, 1); +} + +void +init_image_loader(void) +{ + shim_image_loader_interface.LoadImage = shim_load_image; + shim_image_loader_interface.StartImage = shim_start_image; + shim_image_loader_interface.Exit = shim_exit; + shim_image_loader_interface.UnloadImage = shim_unload_image; +} + +void +hook_system_services(EFI_SYSTEM_TABLE *local_systab) +{ + systab = local_systab; + BS = systab->BootServices; + + /* We need to hook various calls to make this work... */ + + /* + * We need LoadImage() hooked so that we can guarantee everything is + * verified. + */ + system_load_image = systab->BootServices->LoadImage; + systab->BootServices->LoadImage = shim_load_image; + + /* + * We need StartImage() hooked because the system's StartImage() + * doesn't know about our structure layout. + */ + system_start_image = systab->BootServices->StartImage; + systab->BootServices->StartImage = shim_start_image; + + /* + * We need Exit() hooked so that we make sure to use the right jmp_buf + * when an application calls Exit(), but that happens in a separate + * function. + */ + + /* + * We need UnloadImage() to match our LoadImage() + */ + system_unload_image = systab->BootServices->UnloadImage; + systab->BootServices->UnloadImage = shim_unload_image; +} + +void +unhook_exit(void) +{ + systab->BootServices->Exit = system_exit; + BS = systab->BootServices; +} + +void +hook_exit(EFI_SYSTEM_TABLE *local_systab) +{ + systab = local_systab; + BS = local_systab->BootServices; + + /* + * We need to hook Exit() so that we can allow users to quit the + * bootloader and still e.g. start a new one or run an internal + * shell. + */ + system_exit = systab->BootServices->Exit; + systab->BootServices->Exit = shim_exit; +} diff --git a/make-archive b/make-archive index 9ae9eef..d0352b7 100755 --- a/make-archive +++ b/make-archive @@ -1,5 +1,7 @@ #!/bin/sh -set -e +set -eu +set -o pipefail +set -x usage() { status="${1}" @@ -78,10 +80,11 @@ main() { rm -rf "${ARCHIVE_DIR}/shim-${VERSION}" "${ARCHIVE_DIR}/shim-${VERSION}" mkdir -p "${ARCHIVE_DIR}/shim-${VERSION}/gnu-efi" cd gnu-efi || exit 1 + git fetch if [ "x" = "x${GNUEFI_GIT_TAG}" ] ; then git archive --format=tar "$(git log -1 --pretty=format:%h)" | ( cd "${ARCHIVE_DIR}/shim-${VERSION}/gnu-efi" ; tar x ) else - git archive --format=tar "${ORIGIN}/${GNUEFI_GIT_TAG}" | ( cd "${ARCHIVE_DIR}/shim-${VERSION}/gnu-efi" ; tar x ) + git archive --format=tar "${GNUEFI_GIT_TAG}" | ( cd "${ARCHIVE_DIR}/shim-${VERSION}/gnu-efi" ; tar x ) fi cd .. if [ "x" = "x${SHIM_GIT_TAG}" ] ; then diff --git a/make-certs b/make-certs index 6f40b23..e2d3e4b 100755 --- a/make-certs +++ b/make-certs @@ -7,6 +7,11 @@ set -e +if [[ ! -f `which openssl` ]]; then + echo "OpenSSL not found. Install it first, then run this script again." + exit 1 +fi + DOMAIN=xn--u4h.net DAYS=365 KEYTYPE=RSA diff --git a/memattrs.c b/memattrs.c new file mode 100644 index 0000000..ed8a3ae --- /dev/null +++ b/memattrs.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * memattrs.c - EFI and DXE memory attribute helpers + * Copyright Peter Jones + */ + +#include "shim.h" + +static inline uint64_t +shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs) +{ + uint64_t ret = EFI_MEMORY_RP | + EFI_MEMORY_RO | + EFI_MEMORY_XP; + + if (attrs & MEM_ATTR_R) + ret &= ~EFI_MEMORY_RP; + + if (attrs & MEM_ATTR_W) + ret &= ~EFI_MEMORY_RO; + + if (attrs & MEM_ATTR_X) + ret &= ~EFI_MEMORY_XP; + + return ret; +} + +static inline uint64_t +uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs) +{ + uint64_t ret = MEM_ATTR_R | + MEM_ATTR_W | + MEM_ATTR_X; + + if (attrs & EFI_MEMORY_RP) + ret &= ~MEM_ATTR_R; + + if (attrs & EFI_MEMORY_RO) + ret &= ~MEM_ATTR_W; + + if (attrs & EFI_MEMORY_XP) + ret &= ~MEM_ATTR_X; + + return ret; +} + +static void +get_dxe_services_table(EFI_DXE_SERVICES_TABLE **dstp) +{ + static EFI_DXE_SERVICES_TABLE *dst = NULL; + + if (dst == NULL) { + dprint(L"Looking for configuration table " LGUID_FMT L"\n", GUID_ARGS(gEfiDxeServicesTableGuid)); + + for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; + dprint(L"Testing configuration table " LGUID_FMT L"\n", GUID_ARGS(ct->VendorGuid)); + if (CompareMem(&ct->VendorGuid, &gEfiDxeServicesTableGuid, sizeof(EFI_GUID)) != 0) + continue; + + dst = (EFI_DXE_SERVICES_TABLE *)ct->VendorTable; + dprint(L"Looking for DXE Services Signature 0x%16llx, found signature 0x%16llx\n", + EFI_DXE_SERVICES_TABLE_SIGNATURE, dst->Hdr.Signature); + if (dst->Hdr.Signature != EFI_DXE_SERVICES_TABLE_SIGNATURE) + continue; + + if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) { + /* + * purposefully not treating this as an error so that HSIStatus + * can tell us about it later. + */ + dprint(L"DXE Services lacks Get/SetMemorySpace* functions\n"); + } + + dprint(L"Setting dxe_services_table to 0x%llx\n", dst); + *dstp = dst; + return; + } + } else { + *dstp = dst; + return; + } + + dst = NULL; + dprint(L"Couldn't find DXE services\n"); +} + +static EFI_STATUS +dxe_get_mem_attrs(uintptr_t physaddr, size_t size, uint64_t *attrs) +{ + EFI_STATUS status; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc; + EFI_PHYSICAL_ADDRESS start, end, next; + EFI_DXE_SERVICES_TABLE *dst = NULL; + + get_dxe_services_table(&dst); + if (!dst) + return EFI_UNSUPPORTED; + + if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) + return EFI_UNSUPPORTED; + + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { + dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr+size-1), + attrs); + return EFI_SUCCESS; + } + + start = ALIGN_DOWN(physaddr, EFI_PAGE_SIZE); + end = ALIGN_UP(physaddr + size, EFI_PAGE_SIZE); + + for (; start < end; start = next) { + status = dst->GetMemorySpaceDescriptor(start, &desc); + if (EFI_ERROR(status)) { + dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n", + start, status); + return status; + } + + next = desc.BaseAddress + desc.Length; + + if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY) + continue; + + *attrs = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes); + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +static EFI_STATUS +dxe_update_mem_attrs(uintptr_t addr, size_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ +#if 0 + EFI_STATUS status; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc; + EFI_PHYSICAL_ADDRESS start, end, next; + uint64_t before = 0, after = 0, dxe_set_attrs, dxe_clear_attrs; +#endif + EFI_DXE_SERVICES_TABLE *dst = NULL; + + get_dxe_services_table(&dst); + if (!dst) + return EFI_UNSUPPORTED; + + if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) + return EFI_UNSUPPORTED; + + if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(size) || size == 0) { + perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n", + __func__, (unsigned long long)addr, + (unsigned long long)(addr + size - 1), + (unsigned long long)size, + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : ""); + if (!IS_PAGE_ALIGNED(addr)) + perror(L" addr is not page aligned\n"); + if (!IS_PAGE_ALIGNED(size)) + perror(L" size is not page aligned\n"); + if (size == 0) + perror(L" size is 0\n"); + return EFI_SUCCESS; + } + + /* + * We know this only works coincidentally, so nerfing it for now + * until we have a chance to debug more thoroughly on these niche + * systems. + */ +#if 0 + start = ALIGN_DOWN(addr, EFI_PAGE_SIZE); + end = ALIGN_UP(addr + size, EFI_PAGE_SIZE); + + for (; start < end; start = next) { + EFI_PHYSICAL_ADDRESS mod_start; + UINT64 mod_size; + + status = dst->GetMemorySpaceDescriptor(start, &desc); + if (EFI_ERROR(status)) { + dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n", + start, status); + return status; + } + + next = desc.BaseAddress + desc.Length; + + if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY) + continue; + + mod_start = MAX(start, desc.BaseAddress); + mod_size = MIN(end, next) - mod_start; + + before = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes); + dxe_set_attrs = shim_mem_attrs_to_uefi_mem_attrs(set_attrs); + dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, dxe_set_attrs); + dxe_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs(clear_attrs); + dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, dxe_clear_attrs); + desc.Attributes |= dxe_set_attrs; + desc.Attributes &= ~dxe_clear_attrs; + after = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes); + + status = dst->SetMemorySpaceAttributes(mod_start, mod_size, desc.Attributes); + if (EFI_ERROR(status)) { + dprint(L"Failed to update memory attrs:0x%0x addr:0x%llx size:0x%0lx status:%r\n", + desc.Attributes, mod_start, mod_size, status); + return status; + } + + break; + } + + dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : "", + (unsigned long long)addr, (unsigned long long)(addr + size - 1), + (before & MEM_ATTR_R) ? 'r' : '-', + (before & MEM_ATTR_W) ? 'w' : '-', + (before & MEM_ATTR_X) ? 'x' : '-', + (after & MEM_ATTR_R) ? 'r' : '-', + (after & MEM_ATTR_W) ? 'w' : '-', + (after & MEM_ATTR_X) ? 'x' : '-'); +#endif + + return EFI_SUCCESS; +} + +static void +get_efi_mem_attr_protocol(EFI_MEMORY_ATTRIBUTE_PROTOCOL **protop) +{ + static EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + static bool has_mem_access_proto = true; + + if (proto == NULL && has_mem_access_proto == true) { + EFI_STATUS efi_status; + efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, + (VOID **)&proto); + if (EFI_ERROR(efi_status) || !proto) { + has_mem_access_proto = false; + *protop = NULL; + } + } + + *protop = proto; +} + +static EFI_STATUS +efi_get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status; + + get_efi_mem_attr_protocol(&proto); + if (!proto) + return EFI_UNSUPPORTED; + + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { + dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr+size-1), + attrs); + return EFI_SUCCESS; + } + + efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"GetMemoryAttributes(..., 0x%llx, 0x%x, 0x%x): %r\n", + physaddr, size, attrs, efi_status); + } else { + *attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs); + } + + return efi_status; +} + +static EFI_STATUS +efi_update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status, ret; + uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; + + get_efi_mem_attr_protocol(&proto); + if (!proto) + return EFI_UNSUPPORTED; + + efi_status = efi_get_mem_attrs(addr, size, &before); + if (EFI_ERROR(efi_status)) + dprint(L"efi_get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &before, efi_status); + + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0) { + perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr + size - 1), + (unsigned long long)size, + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : ""); + if (!IS_PAGE_ALIGNED(physaddr)) + perror(L" addr is not page aligned\n"); + if (!IS_PAGE_ALIGNED(size)) + perror(L" size is not page aligned\n"); + if (size == 0) + perror(L" size is 0\n"); + return EFI_SUCCESS; + } + + uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs); + dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs); + uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs); + dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs); + efi_status = EFI_SUCCESS; + if (uefi_set_attrs) { + efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"Failed to set memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", + uefi_set_attrs, physaddr, size, efi_status); + } + } + if (!EFI_ERROR(efi_status) && uefi_clear_attrs) { + efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"Failed to clear memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", + uefi_clear_attrs, physaddr, size, efi_status); + } + } + ret = efi_status; + + efi_status = efi_get_mem_attrs(addr, size, &after); + if (EFI_ERROR(efi_status)) + dprint(L"efi_get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &after, efi_status); + + dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : "", + (unsigned long long)addr, (unsigned long long)(addr + size - 1), + (before & MEM_ATTR_R) ? 'r' : '-', + (before & MEM_ATTR_W) ? 'w' : '-', + (before & MEM_ATTR_X) ? 'x' : '-', + (after & MEM_ATTR_R) ? 'r' : '-', + (after & MEM_ATTR_W) ? 'w' : '-', + (after & MEM_ATTR_X) ? 'x' : '-'); + + return ret; +} + +EFI_STATUS +update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ + EFI_STATUS efi_status; + + efi_status = efi_update_mem_attrs(addr, size, set_attrs, clear_attrs); + if (!EFI_ERROR(efi_status)) + return efi_status; + + if (efi_status == EFI_UNSUPPORTED) + efi_status = dxe_update_mem_attrs(addr, size, set_attrs, clear_attrs); + + return efi_status; +} + +EFI_STATUS +get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs) +{ + EFI_STATUS efi_status; + + efi_status = efi_get_mem_attrs(addr, size, attrs); + if (!EFI_ERROR(efi_status)) + return efi_status; + + if (efi_status == EFI_UNSUPPORTED) + efi_status = dxe_get_mem_attrs(addr, size, attrs); + + return efi_status; +} + +char * +decode_hsi_bits(UINTN hsi) +{ + static const struct { + UINTN bit; + char name[16]; + } bits[] = { + {.bit = SHIM_HSI_STATUS_HEAPX, .name = "HEAPX"}, + {.bit = SHIM_HSI_STATUS_STACKX, .name = "STACKX"}, + {.bit = SHIM_HSI_STATUS_ROW, .name = "ROW"}, + {.bit = SHIM_HSI_STATUS_HASMAP, .name = "HASMAP"}, + {.bit = SHIM_HSI_STATUS_HASDST, .name = "HASDST"}, + {.bit = SHIM_HSI_STATUS_HASDSTGMSD, .name = "HASDSTGMSD"}, + {.bit = SHIM_HSI_STATUS_HASDSTSMSA, .name = "HASDSTSMSA"}, + {.bit = SHIM_HSI_STATUS_NX, .name = "NX"}, + {.bit = 0, .name = ""}, + }; + static int x = 0; + static char retbufs[2][sizeof(bits)]; + char *retbuf = &retbufs[x % 2][0]; + char *prev = &retbuf[0]; + char *pos = &retbuf[0]; + + x = ( x + 1 ) % 2; + + ZeroMem(retbuf, sizeof(bits)); + + if (hsi == 0) { + prev = stpcpy(retbuf, "0"); + } else { + for (UINTN i = 0; bits[i].bit != 0; i++) { + if (hsi & bits[i].bit) { + prev = stpcpy(pos, bits[i].name); + pos = stpcpy(prev, "|"); + } + } + } + prev[0] = '\0'; + return retbuf; +} + +void +get_hsi_mem_info(void) +{ + EFI_STATUS efi_status; + uintptr_t addr; + uint64_t attrs = 0; + uint32_t *tmp_alloc; + EFI_MEMORY_ATTRIBUTE_PROTOCOL *efiproto = NULL; + EFI_DXE_SERVICES_TABLE *dst = NULL; + + get_efi_mem_attr_protocol(&efiproto); + if (efiproto) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASMAP)); + hsi_status |= SHIM_HSI_STATUS_HASMAP; + } + + get_dxe_services_table(&dst); + if (dst) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDST)); + hsi_status |= SHIM_HSI_STATUS_HASDST; + if (dst->GetMemorySpaceDescriptor) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTGMSD)); + hsi_status |= SHIM_HSI_STATUS_HASDSTGMSD; + } + if (dst->SetMemorySpaceAttributes) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTSMSA)); + hsi_status |= SHIM_HSI_STATUS_HASDSTSMSA; + } + } + + if (!(hsi_status & SHIM_HSI_STATUS_HASMAP) && + !(hsi_status & SHIM_HSI_STATUS_HASDSTGMSD && + hsi_status & SHIM_HSI_STATUS_HASDSTSMSA)) { + dprint(L"No memory protocol, not testing further\n"); + return; + } + + addr = ((uintptr_t)&get_hsi_mem_info) & ~EFI_PAGE_MASK; + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE); + goto error; + } + + if (attrs & MEM_ATTR_W) { + dprint(L"get_hsi_mem_info() is on a writable page: %a->%a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_ROW)); + hsi_status |= SHIM_HSI_STATUS_ROW; + } + + addr = ((uintptr_t)&addr) & ~EFI_PAGE_MASK; + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE); + goto error; + } + + if (attrs & MEM_ATTR_X) { + dprint(L"Stack variable is on an executable page: %a->%a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_STACKX)); + hsi_status |= SHIM_HSI_STATUS_STACKX; + } + + tmp_alloc = AllocatePool(EFI_PAGE_SIZE); + if (!tmp_alloc) { + dprint(L"Failed to allocate heap variable.\n"); + goto error; + } + + addr = ((uintptr_t)tmp_alloc) & ~EFI_PAGE_MASK; + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + FreePool(tmp_alloc); + if (EFI_ERROR(efi_status)) { + dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE); + goto error; + } + if (attrs & MEM_ATTR_X) { + dprint(L"Heap variable is on an executable page: %a->%a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HEAPX)); + hsi_status |= SHIM_HSI_STATUS_HEAPX; + } + + return; + +error: + /* + * In this case we can't actually tell anything, so assume + * and report the worst case scenario. + */ + hsi_status = SHIM_HSI_STATUS_HEAPX | + SHIM_HSI_STATUS_STACKX | + SHIM_HSI_STATUS_ROW; + dprint(L"Setting HSI to 0x%lx due to error: %r\n", decode_hsi_bits(hsi_status), efi_status); +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/mock-variables.c b/mock-variables.c index 0304454..723cdda 100644 --- a/mock-variables.c +++ b/mock-variables.c @@ -163,7 +163,7 @@ variable_cmp(const struct mock_variable * const v0, ret = CompareGuid(&v0->guid, &v1->guid); ret <<= 8ul; -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if (defined(SHIM_DEBUG) && SHIM_DEBUG > 3) printf("%s:%d:%s(): "GUID_FMT" %s "GUID_FMT" (0x%011"PRIx64" %"PRId64")\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(v0->guid), @@ -177,7 +177,7 @@ variable_cmp(const struct mock_variable * const v0, } ret = StrCmp(v0->name, v1->name); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if (defined(SHIM_DEBUG) && SHIM_DEBUG > 3) printf("%s:%d:%s(): \"%s\" %s \"%s\" (0x%02hhx (%d)\n", __FILE__, __LINE__-1, __func__, Str2str(v0->name), @@ -284,7 +284,7 @@ mock_gnvn_set_result(UINTN *size, CHAR16 *name, EFI_GUID *guid, *size = StrSize(result->name); status = EFI_BUFFER_TOO_SMALL; mock_gnvn_post_hook(size, name, guid, &status); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 3 printf("%s:%d:%s(): returning %lx\n", __FILE__, __LINE__-1, __func__, status); #endif @@ -297,7 +297,7 @@ mock_gnvn_set_result(UINTN *size, CHAR16 *name, EFI_GUID *guid, status = EFI_SUCCESS; mock_gnvn_post_hook(size, name, guid, &status); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 3 printf("%s:%d:%s(): returning %lx\n", __FILE__, __LINE__-1, __func__, status); #endif @@ -351,15 +351,20 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid) struct mock_variable *var; var = list_entry(pos, struct mock_variable, list); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) +# if SHIM_DEBUG > 1 printf("%s:%d:%s(): candidate var:%p &var->guid:%p &var->list:%p\n", __FILE__, __LINE__-1, __func__, var, &var->guid, &var->list); +# elif SHIM_DEBUG > 0 + printf("%s:%d:%s(): candidate var:%p var->guid:" GUID_FMT"\n", + __FILE__, __LINE__-1, __func__, var, GUID_ARGS(var->guid)); +# endif #endif if (name[0] == 0) { if (CompareGuid(&var->guid, guid) == 0) { #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) - printf("%s:%d:%s(): found\n", - __FILE__, __LINE__-1, __func__); + printf("%s:%d:%s(): found guid in entry var:%p var->name:%p\n", + __FILE__, __LINE__-1, __func__, var, var->name); #endif result = var; found = true; @@ -374,14 +379,14 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid) continue; } -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): varcmp("GUID_FMT"-%s, "GUID_FMT"-%s)\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(goal.guid), Str2str(goal.name), GUID_ARGS(var->guid), Str2str(var->name)); #endif if (variable_cmp(&goal, var) == 0) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): found\n", __FILE__, __LINE__-1, __func__); #endif @@ -391,15 +396,15 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid) } #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) if (result) { - printf("%s:%d:%s(): found:%d result:%p &result->guid:%p &result->list:%p\n" + printf("%s:%d:%s(): found:%d result:%p &result->guid:%p &result->list:%p\n", __FILE__, __LINE__-1, __func__, found, result, &result->guid, &result->list); printf("%s:%d:%s(): "GUID_FMT"-%s\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(result->guid), Str2str(result->name)); } else { - printf("%s:%d:%s(): not found\n", - __FILE__, __LINE__-1, __func__); + printf("%s:%d:%s(): not found (found:%d status:0x%016x)\n", + __FILE__, __LINE__-1, __func__, found, status); } #endif @@ -408,13 +413,25 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid) status = EFI_NOT_FOUND; else status = EFI_INVALID_PARAMETER; +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 + printf("%s:%d:%s(): not found (found:%d status:0x%016x)\n", + __FILE__, __LINE__-1, __func__, found, status); +#endif mock_gnvn_post_hook(size, name, guid, &status); +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 + printf("%s:%d:%s(): not found (found:%d status:0x%016x)\n", + __FILE__, __LINE__-1, __func__, found, status); +#endif return status; } if (!result) { status = EFI_NOT_FOUND; mock_gnvn_post_hook(size, name, guid, &status); +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 + printf("%s:%d:%s(): found (found:%d status:0x%016x)\n", + __FILE__, __LINE__-1, __func__, found, status); +#endif return status; } @@ -678,7 +695,7 @@ mock_new_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, } var = (struct mock_variable *)buf; -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): var:%p &var->guid:%p &var->list:%p\n", __FILE__, __LINE__-1, __func__, var, &var->guid, &var->list); #endif @@ -695,7 +712,7 @@ mock_new_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, var->attrs = attrs; INIT_LIST_HEAD(&var->list); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): var: "GUID_FMT"-%s\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(var->guid), Str2str(var->name)); @@ -772,10 +789,10 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, } #endif -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) - printf("%s:%d:%s():Setting "GUID_FMT"-%s\n", +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 + printf("%s:%d:%s():Setting "GUID_FMT"-%s size:0x%"PRIx64"\n", __FILE__, __LINE__ - 1, __func__, - GUID_ARGS(*guid), Str2str(name)); + GUID_ARGS(*guid), Str2str(name), size); #endif switch (mock_variable_sort_policy) { case MOCK_SORT_PREPEND: @@ -800,7 +817,7 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, list_for_each_safe(pos, tmp, &mock_variables) { found = false; var = list_entry(pos, struct mock_variable, list); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): varcmp("GUID_FMT"-%s, "GUID_FMT"-%s)\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(goal.guid), Str2str(goal.name), @@ -832,32 +849,32 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, if (found) break; } -#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s():var_list:%p &mock_variables:%p cmp:%ld\n", __FILE__, __LINE__ - 1, __func__, var_list, &mock_variables, cmp); #endif if (cmp != 0 || (cmp == 0 && var_list == &mock_variables)) { size_t totalsz = size + StrSize(name); -#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s():var:%p attrs:0x%lx\n", __FILE__, __LINE__ - 1, __func__, var, attrs); #endif - status = mock_new_variable(name, guid, attrs, size, data, &var); + status = mock_sv_adjust_usage_data(attrs, size, -totalsz); if (EFI_ERROR(status)) { mock_sv_post_hook(name, guid, attrs, size, data, &status, CREATE); return status; } - mock_sv_adjust_usage_data(attrs, size, totalsz); + status = mock_new_variable(name, guid, attrs, size, data, &var); mock_sv_post_hook(name, guid, attrs, size, data, &status, CREATE); if (EFI_ERROR(status)) { - mock_sv_adjust_usage_data(attrs, 0, -totalsz); + mock_sv_adjust_usage_data(attrs, 0, totalsz); return status; } -#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 printf("%s:%d:%s(): Adding "GUID_FMT"-%s %s %s\n", __FILE__, __LINE__ - 1, __func__, GUID_ARGS(var->guid), Str2str(var->name), @@ -1001,6 +1018,20 @@ static struct mock_variable_limits default_limits[] = { {.attrs = 0, } }; +void +mock_set_usage_limits(list_t *limit_list, + struct mock_variable_limits *limits) +{ + INIT_LIST_HEAD(limit_list); + for (size_t i = 0; limits[i].attrs != 0; i++) { + INIT_LIST_HEAD(&limits[i].list); + list_add_tail(&limits[i].list, limit_list); + } + + mock_qvi_limits = limit_list; + mock_sv_limits = limit_list; +} + void mock_set_default_usage_limits(void) { @@ -1008,12 +1039,7 @@ mock_set_default_usage_limits(void) default_remaining_var_storage = 65536; default_max_var_size = 32768; - INIT_LIST_HEAD(&mock_default_variable_limits); - for (size_t i = 0; default_limits[i].attrs != 0; i++) { - INIT_LIST_HEAD(&default_limits[i].list); - list_add_tail(&default_limits[i].list, - &mock_default_variable_limits); - } + mock_set_usage_limits(&mock_default_variable_limits, &default_limits[0]); } void @@ -1079,7 +1105,8 @@ mock_load_one_variable(int dfd, const char * const dirname, char * const name) name[namelen-1] = 0; #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) - printf("loading %s-%s\n", &name[namelen], name); + printf("%s:%d:%s(): loading %s-%s\n", __FILE__, __LINE__, __func__, + &name[namelen], name); #endif for (size_t i = 0; i < namelen; i++) namebuf[i] = name[i]; @@ -1109,6 +1136,9 @@ mock_load_variables(const char *const dirname, const char *filters[], DIR *d; struct dirent *entry; +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("Started loading variablles from \"%s\"\n", dirname); +#endif d = opendir(dirname); if (!d) err(1, "Could not open directory \"%s\"", dirname); @@ -1121,6 +1151,11 @@ mock_load_variables(const char *const dirname, const char *filters[], while ((entry = readdir(d)) != NULL) { size_t len = strlen(entry->d_name); bool found = false; + if (entry->d_type != DT_REG) + continue; +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("%s:%d:%s(): maybe adding entry \"%s\"\n", __FILE__, __LINE__, __func__, entry->d_name); +#endif if (filters && len > guidstr_size + 1) { char spacebuf[len]; @@ -1131,6 +1166,9 @@ mock_load_variables(const char *const dirname, const char *filters[], if (strlen(filters[i]) > len) continue; if (!strncmp(entry->d_name, filters[i], len)) { +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 + printf("%s:%d:%s(): filter matched for \"%s\" && \"%s\"\n", __FILE__, __LINE__, __func__, entry->d_name, filters[i]); +#endif found = true; break; } @@ -1138,9 +1176,23 @@ mock_load_variables(const char *const dirname, const char *filters[], } if ((found == false && filter_out == true) || (found == true && filter_out == false)) { +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("%s:%d:%s(): Adding \"%s\" because filter %s\n", + __FILE__, __LINE__-1, __func__, entry->d_name, + found ? "matched" : "did not match"); +#endif mock_load_one_variable(dfd, dirname, entry->d_name); + } else { +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("%s:%d:%s(): Skipping \"%s\" because filter %s\n", + __FILE__, __LINE__-1, __func__, entry->d_name, + found ? "matched" : "did not match"); +#endif } } +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("Done loading variablles from \"%s\"\n", dirname); +#endif closedir(d); #if 0 diff --git a/mok.c b/mok.c index 0ac3415..bf71542 100644 --- a/mok.c +++ b/mok.c @@ -34,6 +34,58 @@ static BOOLEAN check_var(CHAR16 *varname) efi_status_; \ }) +static UINTN +format_hsi_status(UINT8 *buf, size_t sz, + struct mok_state_variable *msv UNUSED) +{ + const char heapx[] = "heap-is-executable: "; + const char stackx[] = "\nstack-is-executable: "; + const char row[] = "\nro-sections-are-writable: "; + const char hasmap[] = "\nhas-memory-attribute-protocol: "; + const char hasdxeservices[] = "\nhas-dxe-services-table: "; + const char hasdsgmsd[] = "\nhas-get-memory-space-descriptor: "; + const char hasdssmsa[] = "\nhas-set-memory-space-attributes: "; + const char shimhasnx[] = "\nshim-has-nx-compat-set: "; + const char finale[] = "\n"; + char *pos; + + /* + * sizeof includes the trailing NUL which is where our 0 or 1 value + * fits + */ + UINTN ret = sizeof(heapx) + sizeof(stackx) + + sizeof(row) + sizeof(hasmap) + + sizeof(hasdxeservices) + sizeof(hasdsgmsd) + + sizeof(hasdssmsa) + sizeof(shimhasnx) + + sizeof(finale); + + if (buf == 0 || sz < ret) { + return ret; + } + + buf[0] = 0; + pos = (char *)buf; + pos = stpcpy(pos, heapx); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HEAPX) ? "1" : "0"); + pos = stpcpy(pos, stackx); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_STACKX) ? "1" : "0"); + pos = stpcpy(pos, row); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_ROW) ? "1" : "0"); + pos = stpcpy(pos, hasmap); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASMAP) ? "1" : "0"); + pos = stpcpy(pos, hasdxeservices); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDST) ? "1" : "0"); + pos = stpcpy(pos, hasdsgmsd); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDSTGMSD) ? "1" : "0"); + pos = stpcpy(pos, hasdssmsa); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDSTSMSA) ? "1" : "0"); + pos = stpcpy(pos, shimhasnx); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_NX) ? "1" : "0"); + stpcpy(pos, finale); + + return ret; +} + /* * If the OS has set any of these variables we need to drop into MOK and * handle them appropriately @@ -50,6 +102,32 @@ static EFI_STATUS check_mok_request(EFI_HANDLE image_handle) efi_status = start_image(image_handle, MOK_MANAGER); if (EFI_ERROR(efi_status)) { + /* + * We don't do this in the unit tests because we + * don't have simulation for console_countdown() + * and similar. + */ +#ifndef SHIM_UNIT_TEST + EFI_STATUS efi_status_2; + EFI_LOADED_IMAGE *li; + efi_status_2 = BS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, + (void **)&li); + if (EFI_ERROR(efi_status_2)) + perror (L"Failed to get image: %r\n", efi_status_2); + else if (is_removable_media_path(li) && + efi_status == EFI_NOT_FOUND) { + CHAR16 *title = L"Could not find MokManager"; + CHAR16 *message = L"MokManager is missing on removable media."; + /* + * This occurs when system is booting on + * hard disk's EFI/BOOT/BOOTxxx.EFI entry + * while it should have booted on + * EFI//shimxxx.efi entry + */ + console_countdown(title, message, 10); + RT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL); + } +#endif perror(L"Failed to start MokManager: %r\n", efi_status); return efi_status; } @@ -80,12 +158,6 @@ categorize_deauthorized(struct mok_state_variable *v) return VENDOR_ADDEND_DB; } -#define MOK_MIRROR_KEYDB 0x01 -#define MOK_MIRROR_DELETE_FIRST 0x02 -#define MOK_VARIABLE_MEASURE 0x04 -#define MOK_VARIABLE_LOG 0x08 -#define MOK_VARIABLE_INVERSE 0x10 - struct mok_state_variable mok_state_variable_data[] = { {.name = L"MokList", .name8 = "MokList", @@ -196,6 +268,161 @@ struct mok_state_variable mok_state_variable_data[] = { .pcr = 14, .state = &mok_policy, }, + {.name = L"HSIStatus", + .name8 = "HSIStatus", + .rtname = L"HSIStatus", + .rtname8 = "HSIStatus", + .guid = &SHIM_LOCK_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + .format = format_hsi_status, + }, + {.name = L"AuditMode", + .name8 = "AuditMode", + .rtname = L"AuditMode", + .rtname8 = "AuditMode", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"BootOrder", + .name8 = "BootOrder", + .rtname = L"BootOrder", + .rtname8 = "BootOrder", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"BootCurrent", + .name8 = "BootCurrent", + .rtname = L"BootCurrent", + .rtname8 = "BootCurrent", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"BootNext", + .name8 = "BootNext", + .rtname = L"BootNext", + .rtname8 = "BootNext", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"Boot0000", + .name8 = "Boot0000", + .rtname = L"Boot0000", + .rtname8 = "Boot0000", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"Boot0001", + .name8 = "Boot0001", + .rtname = L"Boot0001", + .rtname8 = "Boot0001", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"Boot0002", + .name8 = "Boot0002", + .rtname = L"Boot0002", + .rtname8 = "Boot0002", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"Boot0003", + .name8 = "Boot0003", + .rtname = L"Boot0003", + .rtname8 = "Boot0003", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"Boot0004", + .name8 = "Boot0004", + .rtname = L"Boot0004", + .rtname8 = "Boot0004", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"Boot0005", + .name8 = "Boot0005", + .rtname = L"Boot0005", + .rtname8 = "Boot0005", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"Boot0006", + .name8 = "Boot0006", + .rtname = L"Boot0006", + .rtname8 = "Boot0006", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"DeployedMode", + .name8 = "DeployedMode", + .rtname = L"DeployedMode", + .rtname8 = "DeployedMode", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"SecureBoot", + .name8 = "SecureBoot", + .rtname = L"SecureBoot", + .rtname8 = "SecureBoot", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"SetupMode", + .name8 = "SetupMode", + .rtname = L"SetupMode", + .rtname8 = "SetupMode", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"SignatureSupport", + .name8 = "SignatureSupport", + .rtname = L"SignatureSupport", + .rtname8 = "SignatureSupport", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"Timeout", + .name8 = "Timeout", + .rtname = L"Timeout", + .rtname8 = "Timeout", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"PK", + .name8 = "PK", + .rtname = L"PK", + .rtname8 = "PK", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"KEK", + .name8 = "KEK", + .rtname = L"KEK", + .rtname8 = "KEK", + .guid = &GV_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"db", + .name8 = "db", + .rtname = L"db", + .rtname8 = "db", + .guid = &SIG_DB, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"dbx", + .name8 = "dbx", + .rtname = L"dbx", + .rtname8 = "dbx", + .guid = &SIG_DB, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, + {.name = L"Kernel_SkuSiStatus", + .name8 = "Kernel_SkuSiStatus", + .rtname = L"Kernel_SkuSiStatus", + .rtname8 = "Kernel_SkuSiStatus", + .guid = &SECUREBOOT_EFI_NAMESPACE_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + }, { NULL, } }; size_t n_mok_state_variables = sizeof(mok_state_variable_data) / sizeof(mok_state_variable_data[0]); @@ -217,6 +444,47 @@ typedef UINTN SIZE_T; #define EFI_MAJOR_VERSION(tablep) ((UINT16)((((tablep)->Hdr.Revision) >> 16) & 0xfffful)) #define EFI_MINOR_VERSION(tablep) ((UINT16)(((tablep)->Hdr.Revision) & 0xfffful)) +static BOOLEAN is_apple_firmware_vendor(void) +{ + CHAR16 vendorbuf[6] = L""; + CHAR16 *vendor = ST->FirmwareVendor; + if (!vendor) + return FALSE; + + ZeroMem(vendorbuf, sizeof(vendorbuf)); + + /* + * We've had a problem where ST->FirmwareVendor is only as big as + * it needs to be (or at least less than the 200 bytes we formerly + * defined vendorbuf as) and it's up against a page that's not + * mapped readable, so we take a fault and reset when copying from + * it. + * + * We modeled this after kernel, which has the 200 byte CHAR16 + * array and copies 198 bytes into it, so that there's a NUL + * terminator. They solve this issue by mapping the whole 200 + * bytes unconditionally and then unmapping it after the copy, but + * we can't take that approach because we don't necessarily have + * page permission primitives at all. + * + * The 200 bytes (CHAR16 [100]) is an arbitrary number anyway, but + * it's likely larger than any sane vendor name, and we still want + * to do the copy into an array larger than our copied data because + * that's how we guard against failure to terminate with a NUL. + * + * So right now we're only copying ten bytes, because Apple is the + * only vendor we're testing against. + */ + CopyMem(vendorbuf, vendor, 10); + + dprint(L"FirmwareVendor: \"%s\"\n", vendor); + + if (StrnCmp(vendor, L"Apple", 5) == 0) + return TRUE; + + return FALSE; +} + static EFI_STATUS get_max_var_sz(UINT32 attrs, SIZE_T *max_var_szp) { @@ -226,7 +494,7 @@ get_max_var_sz(UINT32 attrs, SIZE_T *max_var_szp) uint64_t max_var_sz = 0; *max_var_szp = 0; - if (EFI_MAJOR_VERSION(RT) < 2) { + if (EFI_MAJOR_VERSION(RT) < 2 || is_apple_firmware_vendor()) { dprint(L"EFI %d.%d; no RT->QueryVariableInfo(). Using 1024!\n", EFI_MAJOR_VERSION(RT), EFI_MINOR_VERSION(RT)); max_var_sz = remaining_sz = max_storage_sz = 1024; @@ -310,7 +578,7 @@ mirror_one_esl(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, } static EFI_STATUS -mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs, +mirror_mok_db(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINT8 *FullData, SIZE_T FullDataSize, BOOLEAN only_first) { EFI_STATUS efi_status = EFI_SUCCESS; @@ -336,15 +604,13 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs, return efi_status; } - CHAR16 *namen; - CHAR8 *namen8; + CHAR16 *namen = NULL; UINTN namelen, namesz; namelen = StrLen(name); namesz = namelen * 2; if (only_first) { namen = name; - namen8 = name8; } else { namelen += 18; namesz += 34; @@ -353,12 +619,6 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs, LogError(L"Could not allocate %lu bytes", namesz); return EFI_OUT_OF_RESOURCES; } - namen8 = AllocateZeroPool(namelen); - if (!namen8) { - FreePool(namen); - LogError(L"Could not allocate %lu bytes", namelen); - return EFI_OUT_OF_RESOURCES; - } } UINTN pos, i; @@ -400,11 +660,6 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs, if (!only_first) { SPrint(namen, namelen, L"%s%lu", name, i); namen[namelen-1] = 0; - /* uggggh */ - UINTN j; - for (j = 0; j < namelen; j++) - namen8[j] = (CHAR8)(namen[j] & 0xff); - namen8[namelen - 1] = 0; } /* @@ -417,7 +672,6 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs, efi_status); if (!only_first) { FreePool(namen); - FreePool(namen8); } return efi_status; } @@ -472,6 +726,9 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs, break; i++; } + if (namen && namen != name) { + FreePool(namen); + } if (EFI_ERROR(efi_status)) { perror(L"Failed to set %s: %r\n", name, efi_status); @@ -515,6 +772,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, EFI_STATUS efi_status = EFI_SUCCESS; uint8_t *FullData = NULL; size_t FullDataSize = 0; + bool allocated_full_data = false; vendor_addend_category_t addend_category = VENDOR_ADDEND_NONE; uint8_t *p = NULL; uint32_t attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | @@ -579,6 +837,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, if (efi_status != EFI_BUFFER_TOO_SMALL) { perror(L"Could not add built-in cert to %s: %r\n", v->name, efi_status); + goto err; return efi_status; } FullDataSize += addend_esl_sz; @@ -663,6 +922,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, FullDataSize, v->name); return EFI_OUT_OF_RESOURCES; } + allocated_full_data = true; p = FullData; } } @@ -692,7 +952,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, if (EFI_ERROR(efi_status)) { perror(L"Could not add built-in cert to %s: %r\n", v->name, efi_status); - return efi_status; + goto err; } p += addend_esl_sz; dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n", @@ -719,7 +979,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, if (EFI_ERROR(efi_status)) { perror(L"Could not add built-in cert to %s: %r\n", v->name, efi_status); - return efi_status; + goto err; } p += build_cert_esl_sz; dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n", @@ -758,7 +1018,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, if (EFI_ERROR(efi_status)) { perror(L"Failed to allocate %lu bytes for %s\n", FullDataSize, v->name); - return efi_status; + goto err; } p = FullData + FullDataSize; dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n", @@ -767,15 +1027,17 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n", FullDataSize, FullData, p, p-(uintptr_t)FullData); - if (FullDataSize && v->flags & MOK_MIRROR_KEYDB) { + if (FullDataSize && v->flags & MOK_MIRROR_KEYDB && + !(v->flags & MOK_VARIABLE_CONFIG_ONLY)) { dprint(L"calling mirror_mok_db(\"%s\", datasz=%lu)\n", v->rtname, FullDataSize); - efi_status = mirror_mok_db(v->rtname, (CHAR8 *)v->rtname8, v->guid, + efi_status = mirror_mok_db(v->rtname, v->guid, attrs, FullData, FullDataSize, only_first); dprint(L"mirror_mok_db(\"%s\", datasz=%lu) returned %r\n", v->rtname, FullDataSize, efi_status); - } else if (FullDataSize && only_first) { + } else if (FullDataSize && only_first && + !(v->flags & MOK_VARIABLE_CONFIG_ONLY)) { efi_status = SetVariable(v->rtname, v->guid, attrs, FullDataSize, FullData); } @@ -789,7 +1051,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, if (EFI_ERROR(efi_status)) { dprint(L"tpm_measure_variable(\"%s\",%lu,0x%llx)->%r\n", v->name, FullDataSize, FullData, efi_status); - return efi_status; + goto err; } } @@ -806,7 +1068,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"tpm_log_event(0x%llx, %lu, %lu, \"%s\")->%r\n", FullData, FullDataSize, v->pcr, v->name, efi_status); - return efi_status; + goto err; } } @@ -820,6 +1082,10 @@ mirror_one_mok_variable(struct mok_state_variable *v, v->data_size = FullDataSize; dprint(L"returning %r\n", efi_status); return efi_status; +err: + if (FullData && allocated_full_data) + FreePool(FullData); + return efi_status; } /* @@ -871,7 +1137,8 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, dprint(L"importing mok state for \"%s\"\n", v->name); - if (!v->data && !v->data_size) { + if (!v->data && !v->data_size && + !(v->flags & MOK_VARIABLE_CONFIG_ONLY)) { efi_status = get_variable_attr(v->name, &v->data, &v->data_size, *v->guid, &attrs); @@ -913,6 +1180,36 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, } } } + + if (v->format) { + v->data_size = v->format(NULL, 0, v); + if (v->data_size > 0) { + v->data = AllocatePool(v->data_size); + if (!v->data) { + perror(L"Could not allocate %lu bytes for %s\n", + v->data_size, v->name); + return EFI_OUT_OF_RESOURCES; + } + } + v->format(v->data, v->data_size, v); + } + + if (!v->data && !v->data_size && + (v->flags & MOK_VARIABLE_CONFIG_ONLY) && + !v->format) { + efi_status = get_variable_attr(v->name, + &v->data, &v->data_size, + *v->guid, &attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"Couldn't get variable \"%s\" for mirroring: %r\n", + v->name, efi_status); + if (efi_status != EFI_NOT_FOUND) + return efi_status; + v->data = NULL; + v->data_size = 0; + } + } + if (delete == TRUE) { perror(L"Deleting bad variable %s\n", v->name); efi_status = LibDeleteVariable(v->name, v->guid); @@ -1004,6 +1301,8 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle) config_table = NULL; } else { ZeroMem(config_table, npages << EFI_PAGE_SHIFT); + mok_config_table = (EFI_PHYSICAL_ADDRESS)(uintptr_t)config_table; + mok_config_table_pages = npages; } } diff --git a/netboot.c b/netboot.c index d8b1093..4676542 100644 --- a/netboot.c +++ b/netboot.c @@ -16,6 +16,16 @@ #define ntohs(x) __builtin_bswap16(x) /* supported both by GCC and clang */ #define htons(x) ntohs(x) +/* TFTP error codes from RFC 1350 */ +#define TFTP_ERROR_NOT_DEFINED 0 /* Not defined, see error message (if any). */ +#define TFTP_ERROR_NOT_FOUND 1 /* File not found. */ +#define TFTP_ERROR_ACCESS 2 /* Access violation. */ +#define TFTP_ERROR_NO_SPACE 3 /* Disk full or allocation exceeded. */ +#define TFTP_ERROR_ILLEGAL_OP 4 /* Illegal TFTP operation. */ +#define TFTP_ERROR_UNKNOWN_ID 5 /* Unknown transfer ID. */ +#define TFTP_ERROR_EXISTS 6 /* File already exists. */ +#define TFTP_ERROR_NO_USER 7 /* No such user. */ + static EFI_PXE_BASE_CODE *pxe; static EFI_IP_ADDRESS tftp_addr; static CHAR8 *full_path; @@ -333,7 +343,33 @@ EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle UNUSED, CHAR8 *netbootname) return efi_status; } -EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, UINT64 *bufsiz) +/* Convert a TFTP error code to an EFI status code. */ +static EFI_STATUS +status_from_error(UINT8 error_code) +{ + switch (error_code) { + case TFTP_ERROR_NOT_FOUND: + return EFI_NOT_FOUND; + case TFTP_ERROR_ACCESS: + case TFTP_ERROR_NO_USER: + return EFI_ACCESS_DENIED; + case TFTP_ERROR_NO_SPACE: + return EFI_VOLUME_FULL; + case TFTP_ERROR_ILLEGAL_OP: + return EFI_PROTOCOL_ERROR; + case TFTP_ERROR_UNKNOWN_ID: + return EFI_INVALID_PARAMETER; + case TFTP_ERROR_EXISTS: + return EFI_WRITE_PROTECTED; + case TFTP_ERROR_NOT_DEFINED: + default: + /* Use a generic TFTP error for anything else. */ + return EFI_TFTP_ERROR; + } +} + +EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, + UINT64 *bufsiz, int flags) { EFI_STATUS efi_status; EFI_PXE_BASE_CODE_TFTP_OPCODE read = EFI_PXE_BASE_CODE_TFTP_READ_FILE; @@ -341,7 +377,8 @@ EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, UINT BOOLEAN nobuffer = FALSE; UINTN blksz = 512; - console_print(L"Fetching Netboot Image %a\n", full_path); + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + console_print(L"Fetching Netboot Image %a\n", full_path); if (*buffer == NULL) { *buffer = AllocatePool(4096 * 1024); if (!*buffer) @@ -362,8 +399,31 @@ try_again: goto try_again; } - if (EFI_ERROR(efi_status) && *buffer) { - FreePool(*buffer); + if (EFI_ERROR(efi_status)) { + if (pxe->Mode->TftpErrorReceived) { + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + console_print(L"TFTP error %u: %a\n", + pxe->Mode->TftpError.ErrorCode, + pxe->Mode->TftpError.ErrorString); + + efi_status = status_from_error(pxe->Mode->TftpError.ErrorCode); + } else if (efi_status == EFI_TFTP_ERROR) { + /* + * Unfortunately, some firmware doesn't fill in the + * error details. Treat all TFTP errors like file not + * found so shim falls back to the default loader. + * + * https://github.com/tianocore/edk2/pull/6287 + */ + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + console_print(L"Unknown TFTP error, treating " + "as file not found\n"); + efi_status = EFI_NOT_FOUND; + } + + if (*buffer) { + FreePool(*buffer); + } } return efi_status; } diff --git a/pe-relocate.c b/pe-relocate.c index bde7172..6f2af4e 100644 --- a/pe-relocate.c +++ b/pe-relocate.c @@ -368,21 +368,28 @@ image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr) */ EFI_STATUS read_header(void *data, unsigned int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context) + PE_COFF_LOADER_IMAGE_CONTEXT *context, + bool check_secdir) { EFI_IMAGE_DOS_HEADER *DosHdr = data; EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize; unsigned long FileAlignment = 0; - UINT16 DllFlags; size_t dos_sz = 0; size_t tmpsz0, tmpsz1; + /* + * It has to be big enough to hold the DOS header; right now we + * don't support images without it. + */ if (datasize < sizeof (*DosHdr)) { perror(L"Invalid image\n"); return EFI_UNSUPPORTED; } + /* + * It must have a valid DOS header + */ if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { if (DosHdr->e_lfanew < sizeof (*DosHdr) || DosHdr->e_lfanew > datasize - 4) { @@ -394,11 +401,17 @@ read_header(void *data, unsigned int datasize, PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew); } + /* + * Has to be big enough to hold a PE header + */ if (datasize - dos_sz < sizeof (PEHdr->Pe32)) { perror(L"Invalid image\n"); return EFI_UNSUPPORTED; } + /* + * If it's 64-bit, it has to hold the PE32+ header + */ if (image_is_64_bit(PEHdr) && (datasize - dos_sz < sizeof (PEHdr->Pe32Plus))) { perror(L"Invalid image\n"); @@ -426,6 +439,13 @@ read_header(void *data, unsigned int datasize, OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32); } + /* + * Set up our file alignment and section alignment expectations to + * be mostly sane. + * + * This probably should have a check for /power/ of two not just + * multiple, but in practice it hasn't been an issue. + */ if (FileAlignment % 2 != 0) { perror(L"File Alignment is invalid (%d)\n", FileAlignment); return EFI_UNSUPPORTED; @@ -439,11 +459,24 @@ read_header(void *data, unsigned int datasize, context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections; + /* + * Check and make sure the space for data directory entries is as + * large as we expect. + * + * In truth we could set this number smaller if we needed to - + * currently it's 16 but we only care about #4 and #5 (the fifth + * and sixth ones) - but it hasn't been a problem. If it's too + * weird we'll fail trying to allocate it. + */ if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) { perror(L"Image header too large\n"); return EFI_UNSUPPORTED; } + /* + * Check that the OptionalHeaderSize and the end of the Data + * Directory match up sanely + */ if (checked_mul(sizeof(EFI_IMAGE_DATA_DIRECTORY), EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES, &tmpsz0) || checked_sub(OptHeaderSize, tmpsz0, &HeaderWithoutDataDir) || checked_sub((size_t)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader, HeaderWithoutDataDir, &tmpsz0) || @@ -453,6 +486,9 @@ read_header(void *data, unsigned int datasize, return EFI_UNSUPPORTED; } + /* + * Check that the SectionHeaderOffset field is within the image. + */ if (checked_add((size_t)DosHdr->e_lfanew, sizeof(UINT32), &tmpsz0) || checked_add(tmpsz0, sizeof(EFI_IMAGE_FILE_HEADER), &tmpsz0) || checked_add(tmpsz0, PEHdr->Pe32.FileHeader.SizeOfOptionalHeader, &SectionHeaderOffset)) { @@ -460,18 +496,29 @@ read_header(void *data, unsigned int datasize, return EFI_UNSUPPORTED; } + /* + * Check that the sections headers themselves are within the image + */ if (checked_sub((size_t)context->ImageSize, SectionHeaderOffset, &tmpsz0) || (tmpsz0 / EFI_IMAGE_SIZEOF_SECTION_HEADER <= context->NumberOfSections)) { perror(L"Image sections overflow image size\n"); return EFI_UNSUPPORTED; } + /* + * Check that the section headers fit within the total headers + */ if (checked_sub((size_t)context->SizeOfHeaders, SectionHeaderOffset, &tmpsz0) || (tmpsz0 / EFI_IMAGE_SIZEOF_SECTION_HEADER < (UINT32)context->NumberOfSections)) { perror(L"Image sections overflow section headers\n"); return EFI_UNSUPPORTED; } + /* + * Check that the section headers are actually within the data + * we've read. Might be duplicative of the ImageSize one, but it + * won't hurt. + */ if (checked_mul((size_t)context->NumberOfSections, sizeof(EFI_IMAGE_SECTION_HEADER), &tmpsz0) || checked_add(tmpsz0, SectionHeaderOffset, &tmpsz0) || (tmpsz0 > datasize)) { @@ -479,6 +526,9 @@ read_header(void *data, unsigned int datasize, return EFI_UNSUPPORTED; } + /* + * Check that the optional header fits in the image. + */ if (checked_sub((size_t)(uintptr_t)PEHdr, (size_t)(uintptr_t)data, &tmpsz0) || checked_add(tmpsz0, sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION), &tmpsz0) || (tmpsz0 > datasize)) { @@ -486,11 +536,17 @@ read_header(void *data, unsigned int datasize, return EFI_UNSUPPORTED; } + /* + * Check that this claims to be a PE binary + */ if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { perror(L"Unsupported image type\n"); return EFI_UNSUPPORTED; } + /* + * Check that relocations aren't stripped, because that won't work. + */ if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) { perror(L"Unsupported image - Relocations have been stripped\n"); return EFI_UNSUPPORTED; @@ -503,27 +559,37 @@ read_header(void *data, unsigned int datasize, 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]; - DllFlags = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; + context->DllCharacteristics = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; } 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]; - DllFlags = PEHdr->Pe32.OptionalHeader.DllCharacteristics; + context->DllCharacteristics = PEHdr->Pe32.OptionalHeader.DllCharacteristics; } + /* + * If NX_COMPAT is required, check that it's set. + */ if ((mok_policy & MOK_POLICY_REQUIRE_NX) && - !(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { + !(context->DllCharacteristics & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { perror(L"Policy requires NX, but image does not support NX\n"); return EFI_UNSUPPORTED; } + /* + * Check that the file header fits within the image. + */ if (checked_add((size_t)(uintptr_t)PEHdr, PEHdr->Pe32.FileHeader.SizeOfOptionalHeader, &tmpsz0) || checked_add(tmpsz0, sizeof(UINT32), &tmpsz0) || checked_add(tmpsz0, sizeof(EFI_IMAGE_FILE_HEADER), &tmpsz0)) { perror(L"Invalid image\n"); return EFI_UNSUPPORTED; } + + /* + * Check that the first section header is within the image data + */ context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)(uintptr_t)tmpsz0; if ((uint64_t)(uintptr_t)(context->FirstSection) > (uint64_t)(uintptr_t)data + datasize) { @@ -531,24 +597,69 @@ read_header(void *data, unsigned int datasize, return EFI_UNSUPPORTED; } + /* + * Check that the headers fit within the image. + */ if (context->ImageSize < context->SizeOfHeaders) { perror(L"Invalid image\n"); return EFI_UNSUPPORTED; } + /* + * check that the data directory fits within the image. + */ if (checked_sub((size_t)(uintptr_t)context->SecDir, (size_t)(uintptr_t)data, &tmpsz0) || (tmpsz0 > 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)) { + /* + * Check that the certificate table is within the binary - + * "VirtualAddress" is a misnomer here, it's a relative offset to + * the image's load address, so compared to datasize it should be + * absolute. + */ + if (check_secdir && + (context->SecDir->VirtualAddress > datasize || + (context->SecDir->VirtualAddress == datasize && + context->SecDir->Size > 0))) { + dprint(L"context->SecDir->VirtualAddress:0x%llx context->SecDir->Size:0x%llx datasize:0x%llx\n", + context->SecDir->VirtualAddress, context->SecDir->Size, datasize); perror(L"Malformed security header\n"); return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } +void +get_shim_nx_capability(EFI_HANDLE image_handle) +{ + EFI_LOADED_IMAGE_PROTOCOL*li = NULL; + EFI_STATUS efi_status; + PE_COFF_LOADER_IMAGE_CONTEXT context; + + efi_status = BS->HandleProtocol(image_handle, &gEfiLoadedImageProtocolGuid, (void **)&li); + if (EFI_ERROR(efi_status) || !li) { + dprint(L"Could not get loaded image protocol: %r\n", efi_status); + return; + } + + ZeroMem(&context, sizeof(context)); + efi_status = read_header(li->ImageBase, li->ImageSize, &context, false); + if (EFI_ERROR(efi_status)) { + dprint(L"Couldn't parse image header: %r\n", efi_status); + return; + } + + dprint(L"DllCharacteristics:0x%lx\n", context.DllCharacteristics); + if (context.DllCharacteristics & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_NX)); + hsi_status |= SHIM_HSI_STATUS_NX; + } +} + + // vim:fenc=utf-8:tw=75:noet diff --git a/pe.c b/pe.c index 3305601..d785c44 100644 --- a/pe.c +++ b/pe.c @@ -395,149 +395,6 @@ err: return efi_status; } -static inline uint64_t -shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs) -{ - uint64_t ret = EFI_MEMORY_RP | - EFI_MEMORY_RO | - EFI_MEMORY_XP; - - if (attrs & MEM_ATTR_R) - ret &= ~EFI_MEMORY_RP; - - if (attrs & MEM_ATTR_W) - ret &= ~EFI_MEMORY_RO; - - if (attrs & MEM_ATTR_X) - ret &= ~EFI_MEMORY_XP; - - return ret; -} - -static inline uint64_t -uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs) -{ - uint64_t ret = MEM_ATTR_R | - MEM_ATTR_W | - MEM_ATTR_X; - - if (attrs & EFI_MEMORY_RP) - ret &= ~MEM_ATTR_R; - - if (attrs & EFI_MEMORY_RO) - ret &= ~MEM_ATTR_W; - - if (attrs & EFI_MEMORY_XP) - ret &= ~MEM_ATTR_X; - - return ret; -} - -static EFI_STATUS -get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs) -{ - EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; - EFI_PHYSICAL_ADDRESS physaddr = addr; - EFI_STATUS efi_status; - - efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, - (VOID **)&proto); - if (EFI_ERROR(efi_status) || !proto) - return efi_status; - - if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { - dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", - __func__, (unsigned long long)physaddr, - (unsigned long long)(physaddr+size-1), - attrs); - return EFI_SUCCESS; - } - - efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs); - *attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs); - - return efi_status; -} - -static EFI_STATUS -update_mem_attrs(uintptr_t addr, uint64_t size, - uint64_t set_attrs, uint64_t clear_attrs) -{ - EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; - EFI_PHYSICAL_ADDRESS physaddr = addr; - EFI_STATUS efi_status, ret; - uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; - - efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, - (VOID **)&proto); - if (EFI_ERROR(efi_status) || !proto) - return efi_status; - - efi_status = get_mem_attrs (addr, size, &before); - if (EFI_ERROR(efi_status)) - dprint(L"get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n", - (unsigned long long)addr, (unsigned long long)size, - &before, efi_status); - - if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0) { - dprint(L"%a called on 0x%llx-0x%llx (size 0x%llx) +%a%a%a -%a%a%a\n", - __func__, (unsigned long long)physaddr, - (unsigned long long)(physaddr + size - 1), - (unsigned long long)size, - (set_attrs & MEM_ATTR_R) ? "r" : "", - (set_attrs & MEM_ATTR_W) ? "w" : "", - (set_attrs & MEM_ATTR_X) ? "x" : "", - (clear_attrs & MEM_ATTR_R) ? "r" : "", - (clear_attrs & MEM_ATTR_W) ? "w" : "", - (clear_attrs & MEM_ATTR_X) ? "x" : ""); - return 0; - } - - uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs); - dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs); - uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs); - dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs); - efi_status = EFI_SUCCESS; - if (uefi_set_attrs) { - efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs); - if (EFI_ERROR(efi_status)) { - dprint(L"Failed to set memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", - uefi_set_attrs, physaddr, size, efi_status); - } - } - if (!EFI_ERROR(efi_status) && uefi_clear_attrs) { - efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs); - if (EFI_ERROR(efi_status)) { - dprint(L"Failed to clear memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", - uefi_clear_attrs, physaddr, size, efi_status); - } - } - ret = efi_status; - - efi_status = get_mem_attrs (addr, size, &after); - if (EFI_ERROR(efi_status)) - dprint(L"get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n", - (unsigned long long)addr, (unsigned long long)size, - &after, efi_status); - - dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", - (set_attrs & MEM_ATTR_R) ? "r" : "", - (set_attrs & MEM_ATTR_W) ? "w" : "", - (set_attrs & MEM_ATTR_X) ? "x" : "", - (clear_attrs & MEM_ATTR_R) ? "r" : "", - (clear_attrs & MEM_ATTR_W) ? "w" : "", - (clear_attrs & MEM_ATTR_X) ? "x" : "", - (unsigned long long)addr, (unsigned long long)(addr + size - 1), - (before & MEM_ATTR_R) ? 'r' : '-', - (before & MEM_ATTR_W) ? 'w' : '-', - (before & MEM_ATTR_X) ? 'x' : '-', - (after & MEM_ATTR_R) ? 'r' : '-', - (after & MEM_ATTR_W) ? 'w' : '-', - (after & MEM_ATTR_X) ? 'x' : '-'); - - return ret; -} - EFI_STATUS verify_image(void *data, unsigned int datasize, EFI_LOADED_IMAGE *li, PE_COFF_LOADER_IMAGE_CONTEXT *context) @@ -549,7 +406,7 @@ EFI_STATUS verify_image(void *data, unsigned int datasize, /* * The binary header contains relevant context and section pointers */ - efi_status = read_header(data, datasize, context); + efi_status = read_header(data, datasize, context, true); if (EFI_ERROR(efi_status)) { perror(L"Failed to read header: %r\n", efi_status); return efi_status; @@ -625,7 +482,7 @@ handle_image (void *data, unsigned int datasize, /* * The binary header contains relevant context and section pointers */ - efi_status = read_header(data, datasize, &context); + efi_status = read_header(data, datasize, &context, true); if (EFI_ERROR(efi_status)) { perror(L"Failed to read header: %r\n", efi_status); return efi_status; @@ -640,7 +497,7 @@ handle_image (void *data, unsigned int datasize, sha1hash); if (EFI_ERROR(efi_status)) { - if (verbose) + if (verbose || in_protocol) console_print(L"Verification failed: %r\n", efi_status); else console_error(L"Verification failed", efi_status); diff --git a/post-process-pe.c b/post-process-pe.c index de8f4a3..008da93 100644 --- a/post-process-pe.c +++ b/post-process-pe.c @@ -43,6 +43,7 @@ static int verbosity; }) static bool set_nx_compat = false; +static bool require_nx_compat = false; typedef uint8_t UINT8; typedef uint16_t UINT16; @@ -162,6 +163,7 @@ load_pe(const char *const file, void *const data, const size_t datasize, ctx->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage; ctx->SectionAlignment = PEHdr->Pe32Plus.OptionalHeader.SectionAlignment; + ctx->DllCharacteristics = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment; OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64); } else { @@ -172,6 +174,7 @@ load_pe(const char *const file, void *const data, const size_t datasize, ctx->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage; ctx->SectionAlignment = PEHdr->Pe32.OptionalHeader.SectionAlignment; + ctx->DllCharacteristics = PEHdr->Pe32.OptionalHeader.DllCharacteristics; FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment; OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32); } @@ -358,6 +361,50 @@ set_dll_characteristics(PE_COFF_LOADER_IMAGE_CONTEXT *ctx) } else { ctx->PEHdr->Pe32.OptionalHeader.DllCharacteristics = newflags; } + ctx->DllCharacteristics = newflags; +} + +static int +validate_nx_compat(PE_COFF_LOADER_IMAGE_CONTEXT *ctx) +{ + EFI_IMAGE_SECTION_HEADER *Section; + int i; + bool nx_compat_flag; + const int level = require_nx_compat ? ERROR : WARNING; + int ret = 0; + + nx_compat_flag = EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT + & ctx->DllCharacteristics; + debug(NOISE, "NX-Compat-Flag: %s\n", nx_compat_flag ? "set" : "unset"); + if (!nx_compat_flag) { + debug(level, "NX Compatibility flag is not set\n"); + if (require_nx_compat) + ret = -1; + } + + debug(NOISE, "Section alignment is 0x%x, page size is 0x%x\n", + ctx->SectionAlignment, PAGE_SIZE); + if (ctx->SectionAlignment != PAGE_SIZE) { + debug(level, "Section alignment is not page aligned\n"); + if (require_nx_compat) + ret = -1; + } + + Section = ctx->FirstSection; + for (i=0, Section = ctx->FirstSection; i < ctx->NumberOfSections; i++, Section++) { + debug(NOISE, "Section %d has WRITE=%d and EXECUTE=%d\n", i, + (Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) ? 1 : 0, + (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) ? 1 : 0); + + if ((Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) && + (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE)) { + debug(level, "Section %d is writable and executable\n", i); + if (require_nx_compat) + ret = -1; + } + } + + return ret; } static void @@ -449,6 +496,10 @@ handle_one(char *f) set_dll_characteristics(&ctx); + rc = validate_nx_compat(&ctx); + if (rc < 0) + err(2, "NX compatibility check failed\n"); + fix_timestamp(&ctx); fix_checksum(&ctx, map, sz); @@ -483,6 +534,7 @@ static void __attribute__((__noreturn__)) usage(int status) fprintf(out, " -v Be more verbose\n"); fprintf(out, " -N Disable the NX compatibility flag\n"); fprintf(out, " -n Enable the NX compatibility flag\n"); + fprintf(out, " -x Error on NX incompatibility\n"); fprintf(out, " -h Print this help text and exit\n"); exit(status); @@ -510,11 +562,14 @@ int main(int argc, char **argv) {.name = "verbose", .val = 'v', }, + {.name = "error-nx-compat", + .val = 'x', + }, {.name = ""} }; int longindex = -1; - while ((i = getopt_long(argc, argv, "hNnqv", options, &longindex)) != -1) { + while ((i = getopt_long(argc, argv, "hNnqvx", options, &longindex)) != -1) { switch (i) { case 'h': case '?': @@ -532,6 +587,9 @@ int main(int argc, char **argv) case 'v': verbosity = MIN(verbosity + 1, MAX_VERBOSITY); break; + case 'x': + require_nx_compat = true; + break; } } diff --git a/replacements.c b/replacements.c deleted file mode 100644 index 469e73a..0000000 --- a/replacements.c +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause-Patent -/* - * shim - trivial UEFI first-stage bootloader - * - * Copyright Red Hat, Inc - */ - -/* Chemical agents lend themselves to covert use in sabotage against - * which it is exceedingly difficult to visualize any really effective - * defense... I will not dwell upon this use of CBW because, as one - * pursues the possibilities of such covert uses, one discovers that the - * scenarios resemble that in which the components of a nuclear weapon - * are smuggled into New York City and assembled in the basement of the - * Empire State Building. - * In other words, once the possibility is recognized to exist, about - * all that one can do is worry about it. - * -- Dr. Ivan L Bennett, Jr., testifying before the Subcommittee on - * National Security Policy and Scientific Developments, November 20, - * 1969. - */ -#include "shim.h" - -static EFI_SYSTEM_TABLE *systab; - -EFI_SYSTEM_TABLE * -get_active_systab(void) -{ - if (systab) - return systab; - return ST; -} - -static typeof(systab->BootServices->LoadImage) system_load_image; -static typeof(systab->BootServices->StartImage) system_start_image; -static typeof(systab->BootServices->Exit) system_exit; -#if !defined(DISABLE_EBS_PROTECTION) -static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services; -#endif /* !defined(DISABLE_EBS_PROTECTION) */ - -static EFI_HANDLE last_loaded_image; - -void -unhook_system_services(void) -{ - if (!systab) - return; - - systab->BootServices->LoadImage = system_load_image; - systab->BootServices->StartImage = system_start_image; -#if !defined(DISABLE_EBS_PROTECTION) - systab->BootServices->ExitBootServices = system_exit_boot_services; -#endif /* !defined(DISABLE_EBS_PROTECTION) */ - BS = systab->BootServices; -} - -static EFI_STATUS EFIAPI -load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, - EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer, - UINTN SourceSize, EFI_HANDLE *ImageHandle) -{ - EFI_STATUS efi_status; - - unhook_system_services(); - efi_status = BS->LoadImage(BootPolicy, ParentImageHandle, DevicePath, - SourceBuffer, SourceSize, ImageHandle); - hook_system_services(systab); - if (EFI_ERROR(efi_status)) - last_loaded_image = NULL; - else - last_loaded_image = *ImageHandle; - return efi_status; -} - -static EFI_STATUS EFIAPI -replacement_start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 **exit_data) -{ - EFI_STATUS efi_status; - unhook_system_services(); - - if (image_handle == last_loaded_image) { - UINT8 retain_protocol = 0; - UINTN retain_protocol_size = sizeof(retain_protocol); - UINT32 retain_protocol_attrs = 0; - - loader_is_participating = 1; - - /* If a boot component asks us, keep our protocol around - it will be used to - * validate further PE payloads (e.g.: by the UKI stub, before the kernel is booted). - * But also check that the variable was set by a boot component, to ensure that - * nobody at runtime can attempt to change shim's behaviour. */ - efi_status = RT->GetVariable(SHIM_RETAIN_PROTOCOL_VAR_NAME, - &SHIM_LOCK_GUID, - &retain_protocol_attrs, - &retain_protocol_size, - &retain_protocol); - if (EFI_ERROR(efi_status) || - (retain_protocol_attrs & EFI_VARIABLE_NON_VOLATILE) || - !(retain_protocol_attrs & EFI_VARIABLE_BOOTSERVICE_ACCESS) || - retain_protocol_size != sizeof(retain_protocol) || - retain_protocol == 0) - uninstall_shim_protocols(); - } - efi_status = BS->StartImage(image_handle, exit_data_size, exit_data); - if (EFI_ERROR(efi_status)) { - if (image_handle == last_loaded_image) { - EFI_STATUS efi_status2 = install_shim_protocols(); - - if (EFI_ERROR(efi_status2)) { - console_print(L"Something has gone seriously wrong: %r\n", - efi_status2); - console_print(L"shim cannot continue, sorry.\n"); - usleep(5000000); - RT->ResetSystem(EfiResetShutdown, - EFI_SECURITY_VIOLATION, - 0, NULL); - } - } - hook_system_services(systab); - loader_is_participating = 0; - } - return efi_status; -} - -#if !defined(DISABLE_EBS_PROTECTION) -static EFI_STATUS EFIAPI -exit_boot_services(EFI_HANDLE image_key, UINTN map_key) -{ - if (loader_is_participating || - verification_method == VERIFIED_BY_HASH) { - unhook_system_services(); - EFI_STATUS efi_status; - efi_status = BS->ExitBootServices(image_key, map_key); - if (EFI_ERROR(efi_status)) - hook_system_services(systab); - return efi_status; - } - - console_print(L"Bootloader has not verified loaded image.\n"); - console_print(L"System is compromised. halting.\n"); - usleep(5000000); - RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); - return EFI_SECURITY_VIOLATION; -} -#endif /* !defined(DISABLE_EBS_PROTECTION) */ - -static EFI_STATUS EFIAPI -do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, - UINTN ExitDataSize, CHAR16 *ExitData) -{ - EFI_STATUS efi_status; - - shim_fini(); - - restore_loaded_image(); - - efi_status = BS->Exit(ImageHandle, ExitStatus, - ExitDataSize, ExitData); - if (EFI_ERROR(efi_status)) { - EFI_STATUS efi_status2 = shim_init(); - - if (EFI_ERROR(efi_status2)) { - console_print(L"Something has gone seriously wrong: %r\n", - efi_status2); - console_print(L"shim cannot continue, sorry.\n"); - usleep(5000000); - RT->ResetSystem(EfiResetShutdown, - EFI_SECURITY_VIOLATION, 0, NULL); - } - } - return efi_status; -} - -void -hook_system_services(EFI_SYSTEM_TABLE *local_systab) -{ - systab = local_systab; - BS = systab->BootServices; - - /* We need to hook various calls to make this work... */ - - /* We need LoadImage() hooked so that fallback.c can load shim - * without having to fake LoadImage as well. This allows it - * to call the system LoadImage(), and have us track the output - * and mark loader_is_participating in replacement_start_image. This - * means anything added by fallback has to be verified by the system - * db, which we want to preserve anyway, since that's all launching - * through BDS gives us. */ - system_load_image = systab->BootServices->LoadImage; - systab->BootServices->LoadImage = load_image; - - /* we need StartImage() so that we can allow chain booting to an - * image trusted by the firmware */ - system_start_image = systab->BootServices->StartImage; - systab->BootServices->StartImage = replacement_start_image; - -#if !defined(DISABLE_EBS_PROTECTION) - /* we need to hook ExitBootServices() so a) we can enforce the policy - * and b) we can unwrap when we're done. */ - system_exit_boot_services = systab->BootServices->ExitBootServices; - systab->BootServices->ExitBootServices = exit_boot_services; -#endif /* defined(DISABLE_EBS_PROTECTION) */ -} - -void -unhook_exit(void) -{ - systab->BootServices->Exit = system_exit; - BS = systab->BootServices; -} - -void -hook_exit(EFI_SYSTEM_TABLE *local_systab) -{ - systab = local_systab; - BS = local_systab->BootServices; - - /* we need to hook Exit() so that we can allow users to quit the - * bootloader and still e.g. start a new one or run an internal - * shell. */ - system_exit = systab->BootServices->Exit; - systab->BootServices->Exit = do_exit; -} diff --git a/sbat.c b/sbat.c index 0695612..f31d945 100644 --- a/sbat.c +++ b/sbat.c @@ -537,9 +537,9 @@ set_sbat_uefi_variable(char *sbat_var_automatic, char *sbat_var_latest) */ if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); - } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes, - sbat_var_candidate) && - !reset_sbat) { + } else if (!reset_sbat && + preserve_sbat_uefi_variable(sbat, sbatsize, attributes, + sbat_var_candidate)) { dprint(L"preserving %s variable it is %d bytes, attributes are 0x%08x\n", SBAT_VAR_NAME, sbatsize, attributes); FreePool(sbat); diff --git a/sbat_var.S b/sbat_var.S index ed82a46..7292406 100644 --- a/sbat_var.S +++ b/sbat_var.S @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-2-Clause-Patent #include "include/sbat_var_defs.h" +#include "generated_sbat_var_defs.h" .section .sbatlevel, "a", %progbits .balignl 4, 0 diff --git a/shim.c b/shim.c index 633163a..8b933d7 100644 --- a/shim.c +++ b/shim.c @@ -52,8 +52,6 @@ extern struct { UINT32 vendor_deauthorized_offset; } cert_table; -#define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }} - typedef enum { DATA_FOUND, DATA_NOT_FOUND, @@ -780,39 +778,6 @@ verify_buffer (char *data, int datasize, return verify_buffer_sbat(data, datasize, context); } -static int -is_removable_media_path(EFI_LOADED_IMAGE *li) -{ - unsigned int pathlen = 0; - CHAR16 *bootpath = NULL; - int ret = 0; - - bootpath = DevicePathToStr(li->FilePath); - - /* Check the beginning of the string and the end, to avoid - * caring about which arch this is. */ - /* I really don't know why, but sometimes bootpath gives us - * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here... - */ - if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && - StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15) && - StrnCaseCmp(bootpath, L"EFI\\BOOT\\BOOT", 13) && - StrnCaseCmp(bootpath, L"EFI\\BOOT\\/BOOT", 14)) - goto error; - - pathlen = StrLen(bootpath); - if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) - goto error; - - ret = 1; - -error: - if (bootpath) - FreePool(bootpath); - - return ret; -} - static int should_use_fallback(EFI_HANDLE image_handle) { @@ -994,10 +959,9 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size) if ((INT32)size < 0) return EFI_INVALID_PARAMETER; - loader_is_participating = 1; in_protocol = 1; - efi_status = read_header(buffer, size, &context); + efi_status = read_header(buffer, size, &context, true); if (EFI_ERROR(efi_status)) goto done; @@ -1052,7 +1016,7 @@ static EFI_STATUS shim_read_header(void *data, unsigned int datasize, EFI_STATUS efi_status; in_protocol = 1; - efi_status = read_header(data, datasize, context); + efi_status = read_header(data, datasize, context, true); in_protocol = 0; return efi_status; @@ -1091,7 +1055,8 @@ str16_to_str8(CHAR16 *str16, CHAR8 **str8) * Load and run an EFI executable */ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, - CHAR16 **PathName, void **data, int *datasize) + CHAR16 **PathName, void **data, int *datasize, + int flags) { EFI_STATUS efi_status; void *sourcebuffer = NULL; @@ -1128,10 +1093,11 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, } FreePool(netbootname); efi_status = FetchNetbootimage(image_handle, &sourcebuffer, - &sourcesize); + &sourcesize, flags); if (EFI_ERROR(efi_status)) { - perror(L"Unable to fetch TFTP image: %r\n", - efi_status); + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + perror(L"Unable to fetch TFTP image: %r\n", + efi_status); return efi_status; } *data = sourcebuffer; @@ -1143,8 +1109,9 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, &sourcesize, netbootname); if (EFI_ERROR(efi_status)) { - perror(L"Unable to fetch HTTP image %a: %r\n", - netbootname, efi_status); + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + perror(L"Unable to fetch HTTP image %a: %r\n", + netbootname, efi_status); return efi_status; } *data = sourcebuffer; @@ -1156,7 +1123,7 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, 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); + *PathName, efi_status); PrintErrors(); ClearErrors(); return efi_status; @@ -1183,7 +1150,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) int datasize = 0; efi_status = read_image(image_handle, ImagePath, &PathName, &data, - &datasize); + &datasize, 0); if (EFI_ERROR(efi_status)) goto done; @@ -1215,7 +1182,9 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) goto restore; } - loader_is_participating = 0; +#if 0 + save_logs(); +#endif /* * The binary is trusted and relocated. Run it @@ -1257,10 +1226,15 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) use_fb ? FALLBACK : second_stage); } - // If the filename is invalid, or the file does not exist, - // just fallback to the default loader. + /* + * If the filename is invalid, or the file does not exist, just fall + * back to the default loader. Also fall back to the default loader + * if we get a TFTP error or HTTP error. + */ if (!use_fb && (efi_status == EFI_INVALID_PARAMETER || - efi_status == EFI_NOT_FOUND)) { + efi_status == EFI_NOT_FOUND || + efi_status == EFI_HTTP_ERROR || + efi_status == EFI_TFTP_ERROR)) { console_print( L"start_image() returned %r, falling back to default loader\n", efi_status); @@ -1286,7 +1260,7 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) EFI_STATUS efi_status; EFI_LOADED_IMAGE *li = NULL; - second_stage = DEFAULT_LOADER; + second_stage = (optional_second_stage) ? optional_second_stage : DEFAULT_LOADER; load_options = NULL; load_options_size = 0; @@ -1373,13 +1347,17 @@ install_shim_protocols(void) if (!EFI_ERROR(efi_status)) uninstall_shim_protocols(); + init_image_loader(); + /* * Install the protocol */ - efi_status = BS->InstallProtocolInterface(&shim_lock_handle, - &SHIM_LOCK_GUID, - EFI_NATIVE_INTERFACE, - &shim_lock_interface); + efi_status = BS->InstallMultipleProtocolInterfaces(&shim_lock_handle, + &SHIM_LOCK_GUID, + &shim_lock_interface, + &SHIM_IMAGE_LOADER_GUID, + &shim_image_loader_interface, + NULL); if (EFI_ERROR(efi_status)) { console_error(L"Could not install security protocol", efi_status); @@ -1405,8 +1383,12 @@ uninstall_shim_protocols(void) /* * If we're back here then clean everything up before exiting */ - BS->UninstallProtocolInterface(shim_lock_handle, &SHIM_LOCK_GUID, - &shim_lock_interface); + BS->UninstallMultipleProtocolInterfaces(shim_lock_handle, + &SHIM_LOCK_GUID, + &shim_lock_interface, + &SHIM_IMAGE_LOADER_GUID, + &shim_image_loader_interface, + NULL); if (!secure_mode()) return; @@ -1444,7 +1426,7 @@ check_section_helper(char *section_name, int len, void **pointer, section, data, datasize, minsize) EFI_STATUS -load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) +load_revocations_file(EFI_HANDLE image_handle, CHAR16 *FileName, CHAR16 *PathName) { EFI_STATUS efi_status = EFI_SUCCESS; PE_COFF_LOADER_IMAGE_CONTEXT context; @@ -1459,12 +1441,12 @@ load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) uint8_t *ssps_latest = NULL; uint8_t *sspv_latest = NULL; - efi_status = read_image(image_handle, L"revocations.efi", &PathName, - &data, &datasize); - if (EFI_ERROR(efi_status)) - return efi_status; + efi_status = read_image(image_handle, FileName, &PathName, + &data, &datasize, + SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE); + if (!EFI_ERROR(efi_status)) + efi_status = verify_image(data, datasize, shim_li, &context); - efi_status = verify_image(data, datasize, shim_li, &context); if (EFI_ERROR(efi_status)) { dprint(L"revocations failed to verify\n"); return efi_status; @@ -1510,7 +1492,8 @@ load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) } EFI_STATUS -load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName) +load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName, + int flags) { EFI_STATUS efi_status; PE_COFF_LOADER_IMAGE_CONTEXT context; @@ -1518,37 +1501,58 @@ load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName) EFI_SIGNATURE_LIST *certlist; void *pointer; UINT32 original; + UINT32 offset; int datasize = 0; void *data = NULL; int i; efi_status = read_image(image_handle, filename, &PathName, - &data, &datasize); + &data, &datasize, flags); if (EFI_ERROR(efi_status)) return efi_status; efi_status = verify_image(data, datasize, shim_li, &context); - if (EFI_ERROR(efi_status)) + if (EFI_ERROR(efi_status)) { + FreePool(data); return efi_status; + } Section = context.FirstSection; for (i = 0; i < context.NumberOfSections; i++, Section++) { + UINT32 sec_size = MIN(Section->Misc.VirtualSize, Section->SizeOfRawData); + if (CompareMem(Section->Name, ".db\0\0\0\0\0", 8) == 0) { - original = user_cert_size; - if (Section->SizeOfRawData < sizeof(EFI_SIGNATURE_LIST)) { - continue; + offset = 0; + while ((sec_size - offset) >= sizeof(EFI_SIGNATURE_LIST)) { + UINT8 *tmp; + + original = user_cert_size; + pointer = ImageAddress(data, datasize, + Section->PointerToRawData + offset); + if (!pointer) { + break; + } + certlist = pointer; + + if (certlist->SignatureListSize < sizeof(EFI_SIGNATURE_LIST) || + checked_add(offset, certlist->SignatureListSize, &offset) || + offset > sec_size || + checked_add(user_cert_size, certlist->SignatureListSize, + &user_cert_size)) { + break; + } + + tmp = ReallocatePool(user_cert, original, + user_cert_size); + if (!tmp) { + FreePool(data); + return EFI_OUT_OF_RESOURCES; + } + user_cert = tmp; + + CopyMem(user_cert + original, pointer, + certlist->SignatureListSize); } - pointer = ImageAddress(data, datasize, - Section->PointerToRawData); - if (!pointer) { - continue; - } - certlist = pointer; - user_cert_size += certlist->SignatureListSize;; - user_cert = ReallocatePool(user_cert, original, - user_cert_size); - CopyMem(user_cert + original, pointer, - certlist->SignatureListSize); } } FreePool(data); @@ -1565,6 +1569,7 @@ load_unbundled_trust(EFI_HANDLE image_handle) EFI_STATUS efi_status; EFI_LOADED_IMAGE *li = NULL; CHAR16 *PathName = NULL; + static CHAR16 FileName[] = L"shim_certificate_0.efi"; EFI_FILE *root, *dir; EFI_FILE_INFO *info; EFI_HANDLE device; @@ -1572,6 +1577,7 @@ load_unbundled_trust(EFI_HANDLE image_handle) UINTN buffersize = 0; void *buffer = NULL; BOOLEAN search_revocations = TRUE; + int i = 0; efi_status = gBS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, (void **)&li); @@ -1592,11 +1598,17 @@ load_unbundled_trust(EFI_HANDLE image_handle) efi_status); /* * Network boot cases do not support reading a directory. Try - * to read revocations.efi to pull in any unbundled SBATLevel + * to read revocations to pull in any unbundled SBATLevel * updates unconditionally in those cases. This may produce * console noise when the file is not present. */ - load_cert_file(image_handle, REVOCATIONFILE, PathName); + load_revocations_file(image_handle, SKUSIREVOCATIONFILE, PathName); + load_revocations_file(image_handle, SBATREVOCATIONFILE, PathName); + while (load_cert_file(image_handle, FileName, PathName, + SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) == EFI_SUCCESS + && i++ < 10) { + FileName[17]++; + } goto done; } @@ -1666,17 +1678,17 @@ load_unbundled_trust(EFI_HANDLE image_handle) } /* - * In the event that there are unprocessed revocation + * In the event that there are unprocessed sbat revocation * additions, they could be intended to ban any *new* trust * anchors we find here. With that in mind, we always want to * do a pass of loading revocations before we try to add * anything new to our allowlist. This is done by making two * passes over the directory, first to search for the - * revocations.efi file then to search for shim_certificate.efi + * revocations_sbat.efi file then to search for shim_certificate*.efi */ if (search_revocations && - StrCaseCmp(info->FileName, REVOCATIONFILE) == 0) { - load_revocations_file(image_handle, PathName); + StrCaseCmp(info->FileName, SBATREVOCATIONFILE) == 0) { + load_revocations_file(image_handle, SBATREVOCATIONFILE, PathName); search_revocations = FALSE; efi_status = root->Open(root, &dir, PathName, EFI_FILE_MODE_READ, 0); @@ -1687,9 +1699,14 @@ load_unbundled_trust(EFI_HANDLE image_handle) } } - if (!search_revocations && - StrCaseCmp(info->FileName, L"shim_certificate.efi") == 0) { - load_cert_file(image_handle, info->FileName, PathName); + if (!search_revocations) { + if (StrnCaseCmp(info->FileName, L"shim_certificate", 16) == 0) { + load_cert_file(image_handle, info->FileName, PathName, 0); + } + if (StrCaseCmp(info->FileName, SKUSIREVOCATIONFILE) == 0) { + load_revocations_file(image_handle, + SKUSIREVOCATIONFILE, PathName); + } } } done: @@ -1698,6 +1715,86 @@ done: return efi_status; } +/* Read optional options file */ +EFI_STATUS +load_shim_options(EFI_HANDLE image_handle) +{ + EFI_STATUS efi_status; + EFI_HANDLE device; + EFI_LOADED_IMAGE *li = NULL; + EFI_FILE_IO_INTERFACE *drive; + EFI_FILE *root; + EFI_FILE_HANDLE ofile; + CHAR16 *PathName = NULL; + CHAR16 *buffer; + UINTN comma0; + UINT64 bs; + + 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; + } + + efi_status = generate_path_from_image_path(li, L"options.csv", &PathName); + if (EFI_ERROR(efi_status)) + goto done; + + device = li->DeviceHandle; + + efi_status = BS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID, + (void **) &drive); + if (EFI_ERROR(efi_status)) + goto done; + + efi_status = drive->OpenVolume(drive, &root); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to open fs: %r\n", efi_status); + goto done; + } + + efi_status = root->Open(root, &ofile, PathName, EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(efi_status)) { + if (efi_status != EFI_NOT_FOUND) + perror(L"Failed to open %s - %r\n", PathName, efi_status); + goto done; + } + + dprint(L"Loading optional second stage info from options.csv\n"); + efi_status = read_file(ofile, PathName, &buffer, &bs); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to read file\n"); + goto done; + } + + /* + * This file may or may not start with the Unicode byte order marker. + * Since UEFI is defined as LE, this file must also be LE. + * If we find the LE byte order marker, just skip its. + */ + if (*buffer == 0xfeff) + buffer++; + + comma0 = StrCSpn(buffer, L","); + if (comma0 == 0) { + perror(L"Invalid csv file\n"); + goto done; + } + + /* + * Currently the options.csv file allows one entry for the optional + * secondary boot stage, anything afterwards is skipped. + */ + buffer[comma0] = L'\0'; + dprint(L"Optional 2nd stage:\"%s\"\n", buffer); + optional_second_stage=buffer; + +done: + FreePool(PathName); + return EFI_SUCCESS; +} + EFI_STATUS shim_init(void) { @@ -1720,7 +1817,6 @@ shim_init(void) * validation of the next image. */ hook_system_services(systab); - loader_is_participating = 0; } } @@ -1746,11 +1842,12 @@ shim_fini(void) uninstall_shim_protocols(); if (secure_mode()) { - - /* - * Remove our hooks from system services. - */ - unhook_system_services(); + if (vendor_authorized_size || vendor_deauthorized_size) { + /* + * Remove our hooks from system services. + */ + unhook_system_services(); + } } unhook_exit(); @@ -1860,7 +1957,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) L"shim_init() failed", L"import of SBAT data failed", L"SBAT self-check failed", - SBAT_VAR_NAME L" UEFI variable setting failed", + SBAT_VAR_NAME L" UEFI variable setting failed", // NOLINT(bugprone-suspicious-missing-comma) NULL }; enum { @@ -1898,6 +1995,8 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) */ debug_hook(); + get_shim_nx_capability(image_handle); + efi_status = set_sbat_uefi_variable_internal(); if (EFI_ERROR(efi_status) && secure_mode()) { perror(L"%s variable initialization failed\n", SBAT_VAR_NAME); @@ -1929,7 +2028,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) efi_status = verify_sbat_section(sbat_start, sbat_end - sbat_start - 1); if (EFI_ERROR(efi_status)) { - perror(L"Verifiying shim SBAT data failed: %r\n", + perror(L"Verifying shim SBAT data failed: %r\n", efi_status); msg = SBAT_SELF_CHECK; goto die; @@ -1938,6 +2037,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) } init_openssl(); + get_hsi_mem_info(); efi_status = load_unbundled_trust(global_image_handle); if (EFI_ERROR(efi_status)) { @@ -1981,6 +2081,8 @@ die: */ (void) del_variable(SHIM_RETAIN_PROTOCOL_VAR_NAME, SHIM_LOCK_GUID); + load_shim_options(image_handle); + efi_status = shim_init(); if (EFI_ERROR(efi_status)) { msg = SHIM_INIT; diff --git a/shim.h b/shim.h index 5791a03..59d9062 100644 --- a/shim.h +++ b/shim.h @@ -55,6 +55,7 @@ #ifndef SHIM_UNIT_TEST #include #include +#include #undef uefi_call_wrapper #include #include @@ -163,7 +164,9 @@ #include "include/configtable.h" #include "include/console.h" #include "include/crypt_blowfish.h" +#include "include/dp.h" #include "include/efiauthenticated.h" +#include "include/errlog.h" #include "include/errors.h" #include "include/execute.h" #include "include/guid.h" @@ -172,12 +175,13 @@ #include "include/ip4config2.h" #include "include/ip6config.h" #include "include/load-options.h" +#include "include/loader-proto.h" +#include "include/memattrs.h" #include "include/mok.h" #include "include/netboot.h" #include "include/passwordcrypt.h" #include "include/peimage.h" #include "include/pe.h" -#include "include/replacements.h" #include "include/sbat.h" #include "include/sbat_var_defs.h" #include "include/ssp.h" @@ -187,6 +191,7 @@ #include "include/simple_file.h" #include "include/str.h" #include "include/tpm.h" +#include "include/utils.h" #include "include/cc.h" #include "include/ucs2.h" #include "include/variables.h" @@ -237,17 +242,11 @@ typedef struct _SHIM_LOCK { extern EFI_STATUS shim_init(void); extern void shim_fini(void); -extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, - const CHAR16 *fmt, ...); -extern EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func, - const CHAR16 *fmt, ms_va_list args); -extern VOID LogHexdump_(const char *file, int line, const char *func, - const void *data, size_t sz); -extern VOID PrintErrors(VOID); -extern VOID ClearErrors(VOID); extern VOID restore_loaded_image(VOID); extern EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath); extern EFI_STATUS import_mok_state(EFI_HANDLE image_handle); +extern EFI_STATUS install_shim_protocols(void); +extern void uninstall_shim_protocols(void); extern UINT32 vendor_authorized_size; extern UINT8 *vendor_authorized; @@ -324,4 +323,19 @@ verify_buffer (char *data, int datasize, char *translate_slashes(char *out, const char *str); +#include + +typedef struct { + EFI_LOADED_IMAGE li; + EFI_IMAGE_ENTRY_POINT entry_point; + EFI_PHYSICAL_ADDRESS alloc_address; + UINTN alloc_pages; + EFI_STATUS exit_status; + CONST CHAR16 *exit_data; + UINTN exit_data_size; + jmp_buf longjmp_buf; + BOOLEAN started; + EFI_DEVICE_PATH *loaded_image_device_path; +} SHIM_LOADED_IMAGE; + #endif /* SHIM_H_ */ diff --git a/test-load-options.c b/test-load-options.c index daf02d9..dfabe86 100644 --- a/test-load-options.c +++ b/test-load-options.c @@ -68,9 +68,12 @@ test_parse_load_options(char *load_option_data, assert_false_goto(EFI_ERROR(status), err, "\n"); assert_nonzero_goto(second_stage, err, "\n"); - assert_not_equal_goto(second_stage, dummy_second_stage, err, "%p == %p\n"); - assert_zero_goto(StrnCmp(second_stage, target_second_stage, 90), - err_print_second_stage, "%d != 0\n"); + if (target_second_stage) { + assert_not_equal_goto(second_stage, dummy_second_stage, err, "%p == %p\n"); + assert_zero_goto(StrnCmp(second_stage, target_second_stage, 90), + err_print_second_stage, "%d != 0\n"); + } else + assert_equal_goto(second_stage, dummy_second_stage, err, "%p != %p\n"); assert_equal_goto(load_options_size, target_remaining_size, err_remaining, "%zu != %zu\n"); assert_equal_goto(load_options, target_remaining, err_remaining, "%p != %p\n"); @@ -255,6 +258,73 @@ test_bds_2(void) target_remaining_size); } +int +test_multi_end_dp(void) +{ + /* +00000000 01 00 00 00 d0 00 4f 00 6e 00 62 00 6f 00 61 00 |......O.n.b.o.a.| +00000010 72 00 64 00 20 00 4e 00 49 00 43 00 28 00 49 00 |r.d. .N.I.C.(.I.| +00000020 50 00 56 00 34 00 29 00 00 00 02 01 0c 00 d0 41 |P.V.4.)........A| +00000030 03 0a 00 00 00 00 01 01 06 00 06 1f 03 0b 25 00 |..............%.| +00000040 2c ea 7f 0a 9f 69 00 00 00 00 00 00 00 00 00 00 |,....i..........| +00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000060 00 03 0c 1b 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000070 00 00 00 00 00 00 00 00 00 00 00 00 7f ff 04 00 |................| +00000080 01 04 76 00 ef 47 64 2d c9 3b a0 41 ac 19 4d 51 |..v..Gd-.;.A..MQ| +00000090 d0 1b 4c e6 50 00 58 00 45 00 20 00 49 00 50 00 |..L.P.X.E. .I.P.| +000000a0 34 00 20 00 49 00 6e 00 74 00 65 00 6c 00 28 00 |4. .I.n.t.e.l.(.| +000000b0 52 00 29 00 20 00 45 00 74 00 68 00 65 00 72 00 |R.). .E.t.h.e.r.| +000000c0 6e 00 65 00 74 00 20 00 43 00 6f 00 6e 00 6e 00 |n.e.t. .C.o.n.n.| +000000d0 65 00 63 00 74 00 69 00 6f 00 6e 00 20 00 28 00 |e.c.t.i.o.n. .(.| +000000e0 36 00 29 00 20 00 49 00 32 00 31 00 39 00 2d 00 |6.). .I.2.1.9.-.| +000000f0 4c 00 4d 00 00 00 7f ff 04 00 00 00 42 4f |L.M.........BO| + */ + char load_option_data [] = { + 0x01, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x4f, 0x00, + 0x6e, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x61, 0x00, + 0x72, 0x00, 0x64, 0x00, 0x20, 0x00, 0x4e, 0x00, + 0x49, 0x00, 0x43, 0x00, 0x28, 0x00, 0x49, 0x00, + 0x50, 0x00, 0x56, 0x00, 0x34, 0x00, 0x29, 0x00, + 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, + 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x06, 0x00, 0x06, 0x1f, 0x03, 0x0b, 0x25, 0x00, + 0x2c, 0xea, 0x7f, 0x0a, 0x9f, 0x69, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x0c, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x04, 0x00, + 0x01, 0x04, 0x76, 0x00, 0xef, 0x47, 0x64, 0x2d, + 0xc9, 0x3b, 0xa0, 0x41, 0xac, 0x19, 0x4d, 0x51, + 0xd0, 0x1b, 0x4c, 0xe6, 0x50, 0x00, 0x58, 0x00, + 0x45, 0x00, 0x20, 0x00, 0x49, 0x00, 0x50, 0x00, + 0x34, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x28, 0x00, + 0x52, 0x00, 0x29, 0x00, 0x20, 0x00, 0x45, 0x00, + 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x20, 0x00, + 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6e, 0x00, + 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x28, 0x00, + 0x36, 0x00, 0x29, 0x00, 0x20, 0x00, 0x49, 0x00, + 0x32, 0x00, 0x31, 0x00, 0x39, 0x00, 0x2d, 0x00, + 0x4c, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x7f, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x42, 0x4f + }; + size_t load_option_data_size = sizeof(load_option_data); + char *target_remaining = &load_option_data[load_option_data_size - 2]; + size_t target_remaining_size = 2; + + return test_parse_load_options(load_option_data, + load_option_data_size, + "test.efi", + NULL, + target_remaining, + target_remaining_size); +} + int main(void) { @@ -263,6 +333,7 @@ main(void) test(test_bds_0); test(test_bds_1); test(test_bds_2); + test(test_multi_end_dp); return status; } diff --git a/test-mock-variables.c b/test-mock-variables.c index c7e42b0..f869300 100644 --- a/test-mock-variables.c +++ b/test-mock-variables.c @@ -207,19 +207,35 @@ test_gnvn_helper(char *testvars) const char *mok_rt_vars[n_mok_state_variables]; for (size_t i = 0; i < n_mok_state_variables; i++) { + /* + * We don't want to filter out the variables we've added to + * mok mirroring that aren't really from mok; right now + * this is a reasonable heuristic for that. + */ + if (mok_state_variables[i].flags & MOK_VARIABLE_CONFIG_ONLY) + continue; mok_rt_vars[i] = mok_state_variables[i].rtname8; } mock_load_variables(testvars, mok_rt_vars, true); +#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 + dump_mock_variables(__FILE__, __LINE__, __func__); +#endif + + /* + * This tests the sort policy, filtering for only variables in the + * EFI "global" namespace. If ascending the first thing should + * be Boot0000, if descending it should be dbxDefault + */ +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("Testing mock variable sorting in the global namespace\n"); +#endif size = sizeof(buf); buf[0] = L'\0'; status = RT->GetNextVariableName(&size, buf, &GV_GUID); assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); -#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 - dump_mock_variables(__FILE__, __LINE__, __func__); -#endif switch (mock_variable_sort_policy) { case MOCK_SORT_DESCENDING: dump_mock_variables_if_wrong(__FILE__, __LINE__, __func__, @@ -236,6 +252,14 @@ test_gnvn_helper(char *testvars) break; } + /* + * Do it again but test for only variables in the Secure Boot + * policy guid namespace. Ascending should be "db", descending + * "dbx". + */ +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("Testing mock variable sorting in the Secure Boot GUID namespace\n"); +#endif size = sizeof(buf); buf[0] = 0; status = RT->GetNextVariableName(&size, buf, &EFI_SECURE_BOOT_DB_GUID); @@ -284,6 +308,13 @@ test_get_variable_0(void) const char *mok_rt_vars[n_mok_state_variables]; for (size_t i = 0; i < n_mok_state_variables; i++) { + /* + * We don't want to filter out the variables we've added to + * mok mirroring that aren't really from mok; right now + * this is a reasonable heuristic for that. + */ + if (mok_state_variables[i].flags & MOK_VARIABLE_CONFIG_ONLY) + continue; mok_rt_vars[i] = mok_state_variables[i].rtname8; } diff --git a/test-mok-mirror.c b/test-mok-mirror.c index f34f62a..38b7ed9 100644 --- a/test-mok-mirror.c +++ b/test-mok-mirror.c @@ -8,6 +8,7 @@ #include "mock-variables.h" #include "test-data-efivars-1.h" +#include #include #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -21,21 +22,51 @@ start_image(EFI_HANDLE image_handle UNUSED, CHAR16 *mm) #define N_TEST_VAR_OPS 40 struct test_var { + /* + * The GUID, name, and attributes of the variables + */ EFI_GUID guid; CHAR16 *name; UINT32 attrs; - UINTN n_ops; + /* + * If the variable is required to be present, with the attributes + * specified above, for a test to pass + */ bool must_be_present; + /* + * If the variable is required to be absent, no matter what the + * attributes, for a test to pass + */ bool must_be_absent; + /* + * The number of operations on this variable that we've seen + */ + UINTN n_ops; + /* + * the operations that have occurred on this variable + */ mock_variable_op_t ops[N_TEST_VAR_OPS]; + /* + * the result codes of those operations + */ EFI_STATUS results[N_TEST_VAR_OPS]; }; static struct test_var *test_vars; struct mock_mok_variable_config_entry { + /* + * The name of an entry we expect to see in the MokVars + * configuration table + */ CHAR8 name[256]; + /* + * The size of its data + */ UINT64 data_size; + /* + * A pointer to what the data should be + */ const unsigned char *data; }; @@ -94,16 +125,217 @@ getvar_post(CHAR16 *name, EFI_GUID *guid, } } +static EFI_STATUS +check_variables(struct test_var *vars) +{ + for (size_t i = 0; vars[i].name != NULL; i++) { + struct test_var *tv = &vars[i]; + list_t *pos = NULL; + bool found = false; + char buf[1]; + UINTN size = 0; + UINT32 attrs = 0; + bool present = false; + + list_for_each(pos, &mock_variables) { + struct mock_variable *var; + bool deleted; + bool created; + int gets = 0; + + var = list_entry(pos, struct mock_variable, list); + if (CompareGuid(&tv->guid, &var->guid) != 0 || + StrCmp(var->name, tv->name) != 0) + continue; + found = true; + assert_equal_goto(var->attrs, tv->attrs, err, + "\"%s\": wrong attrs; got %s expected %s\n", + Str2str(tv->name), + format_var_attrs(var->attrs), + format_var_attrs(tv->attrs)); + for (UINTN j = 0; j < N_TEST_VAR_OPS + && tv->ops[j] != NONE; j++) { + switch (tv->ops[j]) { + case NONE: + default: + break; + case CREATE: + if (tv->results[j] == EFI_SUCCESS) + created = true; + break; + case DELETE: + assert_goto(tv->results[j] != EFI_SUCCESS, err, + "Tried to delete absent variable \"%s\"\n", + Str2str(tv->name)); + assert_goto(created == false, err, + "Deleted variable \"%s\" was previously created.\n", + Str2str(tv->name)); + break; + case APPEND: + assert_goto(false, err, + "No append action should have been tested\n"); + break; + case REPLACE: + assert_goto(false, err, + "No replace action should have been tested\n"); + break; + case GET: + if (tv->results[j] == EFI_SUCCESS) + gets += 1; + break; + } + } + assert_goto(gets == 0 || gets == 1, err, + "Variable should not be read %d times.\n", gets); + } + if (tv->must_be_present) { + /* + * This asserts if it isn't present, and if that's + * the case, then the attributes are already + * validated in the search loop + */ + assert_goto(found == true, err, + "variable \"%s\" was not found.\n", + Str2str(tv->name)); + } + + if (tv->must_be_absent) { + /* + * deliberately does not check the attributes at + * this time. + */ + assert_goto(found == false, err, + "variable \"%s\" was found.\n", + Str2str(tv->name)); + } + } + + return EFI_SUCCESS; +err: + return EFI_INVALID_PARAMETER; +} + +static EFI_STATUS +check_config_table(struct mock_mok_variable_config_entry *test_configs, + uint8_t *config_pos) +{ + size_t i = 0; + struct mok_variable_config_entry *mok_entry = NULL; + struct mock_mok_variable_config_entry *mock_entry = NULL; + + while (config_pos) { + mock_entry = &test_configs[i]; + mok_entry = (struct mok_variable_config_entry *)config_pos; + + /* + * If the tables are different lengths, this will trigger. + */ + assert_equal_goto(mok_entry->name[0], mock_entry->name[0], err, + "mok.name[0] %ld != test.name[0] %ld\n"); + if (mock_entry->name[0] == 0) + break; + + assert_nonzero_goto(mok_entry->name[0], err, "%ld != %ld\n"); + assert_zero_goto(strncmp(mok_entry->name, mock_entry->name, + sizeof(mock_entry->name)), + err, "%ld != %ld: strcmp(\"%s\",\"%s\")\n", + mok_entry->name, mock_entry->name); + + /* + * As of 7501b6bb449f ("mok: fix potential buffer overrun in + * import_mok_state"), we should not see any mok config + * variables with data_size == 0. + */ + assert_nonzero_goto(mok_entry->data_size, err, "%ld != 0\n"); + + assert_equal_goto(mok_entry->data_size, mock_entry->data_size, + err, "%ld != %ld\n"); + assert_zero_goto(CompareMem(mok_entry->data, mock_entry->data, + mok_entry->data_size), + err, "%ld != %ld\n"); + config_pos += offsetof(struct mok_variable_config_entry, data) + + mok_entry->data_size; + i += 1; + } + + return EFI_SUCCESS; +err: + warnx("Failed on entry %zu mok.name:\"%s\" mock.name:\"%s\"", i, + mok_entry->name, mock_entry->name); + if ((mok_entry && mock_entry) && (!mok_entry->name[0] || !mock_entry->name[0])) + warnx("Entry is missing in %s variable list.", mok_entry->name[0] ? "expected" : "found"); + + return EFI_INVALID_PARAMETER; +} + static int -test_mok_mirror_0(void) +test_mok_mirror(struct test_var *vars, + struct mock_mok_variable_config_entry *configs, + EFI_STATUS expected_status) +{ + EFI_STATUS status; + EFI_GUID mok_config_guid = MOK_VARIABLE_STORE; + int ret = -1; + + status = import_mok_state(NULL); + assert_equal_goto(status, expected_status, err, + "got 0x%016lx, expected 0x%016lx\n", + expected_status); + + test_vars = vars; + + status = check_variables(vars); + if (EFI_ERROR(status)) + goto err; + + uint8_t *pos = NULL; + for (size_t i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; + + if (CompareGuid(&ct->VendorGuid, &mok_config_guid) != 0) + continue; + + pos = (void *)ct->VendorTable; + break; + } + + assert_nonzero_goto(pos, err, "%p != 0\n"); + + status = check_config_table(configs, pos); + if (EFI_ERROR(status)) + goto err; + + ret = 0; +err: + for (UINTN k = 0; k < n_mok_state_variables; k++) { + struct mok_state_variable *v = + &mok_state_variables[k]; + if (v->data_size && v->data) { + free(v->data); + v->data = NULL; + v->data_size = 0; + } + } + + test_vars = NULL; + + return ret; +} + +/* + * This tests mirroring of mok variables on fairly optimistic conditions: + * there's enough space for everything, and so we expect to see all the + * RT variables for which we have data mirrored + */ +static int +test_mok_mirror_with_enough_space(void) { const char *mok_rt_vars[n_mok_state_variables]; EFI_STATUS status; EFI_GUID guid = SHIM_LOCK_GUID; - EFI_GUID mok_config_guid = MOK_VARIABLE_STORE; int ret = -1; - struct test_var test_mok_mirror_0_vars[] = { + struct test_var test_mok_mirror_with_enough_space_vars[] = { {.guid = SHIM_LOCK_GUID, .name = L"MokList", .must_be_present = true, @@ -166,11 +398,20 @@ test_mok_mirror_0(void) EFI_VARIABLE_RUNTIME_ACCESS, .ops = { NONE, }, }, + {.guid = SHIM_LOCK_GUID, + .name = L"HSIStatus", + .attrs = 0, + .ops = { NONE, }, + }, {.guid = { 0, }, .name = NULL, } }; + /* + * We must see the supplied values of MokListRT, MokListXRT, and + * SbatLevelRT in the config table + */ struct mock_mok_variable_config_entry test_mok_config_table[] = { {.name = "MokListRT", .data_size = sizeof(test_data_efivars_1_MokListRT), @@ -188,6 +429,10 @@ test_mok_mirror_0(void) .data_size = sizeof(test_data_efivars_1_MokListTrustedRT), .data = test_data_efivars_1_MokListTrustedRT }, + {.name = "HSIStatus", + .data_size = sizeof(test_data_efivars_1_HSIStatus), + .data = test_data_efivars_1_HSIStatus + }, {.name = { 0, }, .data_size = 0, .data = NULL, @@ -202,147 +447,214 @@ test_mok_mirror_0(void) mock_set_variable_post_hook = setvar_post; mock_get_variable_post_hook = getvar_post; - test_vars = &test_mok_mirror_0_vars[0]; - import_mok_state(NULL); + ret = test_mok_mirror(&test_mok_mirror_with_enough_space_vars[0], + test_mok_config_table, + EFI_SUCCESS); - for (size_t i = 0; test_mok_mirror_0_vars[i].name != NULL; i++) { - struct test_var *tv = &test_mok_mirror_0_vars[i]; - list_t *pos = NULL; - bool found = false; - char buf[1]; - UINTN size = 0; - UINT32 attrs = 0; - bool present = false; + mock_set_variable_post_hook = NULL; + mock_get_variable_post_hook = NULL; + return ret; +} - list_for_each(pos, &mock_variables) { - struct mock_variable *var; - bool deleted; - bool created; - int gets = 0; +static int +test_mok_mirror_setvar_out_of_resources(void) +{ + const char *mok_rt_vars[n_mok_state_variables]; + EFI_STATUS status; + EFI_GUID guid = SHIM_LOCK_GUID; + EFI_GUID mok_config_guid = MOK_VARIABLE_STORE; + int ret = -1; - var = list_entry(pos, struct mock_variable, list); - if (CompareGuid(&tv->guid, &var->guid) != 0 || - StrCmp(var->name, tv->name) != 0) - continue; - found = true; - assert_equal_goto(var->attrs, tv->attrs, err, - "\"%s\": wrong attrs; got %s expected %s\n", - Str2str(tv->name), - format_var_attrs(var->attrs), - format_var_attrs(tv->attrs)); - for (UINTN j = 0; j < N_TEST_VAR_OPS - && tv->ops[j] != NONE; j++) { - switch (tv->ops[j]) { - case NONE: - default: - break; - case CREATE: - if (tv->results[j] == EFI_SUCCESS) - created = true; - break; - case DELETE: - assert_goto(tv->results[j] != EFI_SUCCESS, err, - "Tried to delete absent variable \"%s\"\n", - Str2str(tv->name)); - assert_goto(created == false, err, - "Deleted variable \"%s\" was previously created.\n", - Str2str(tv->name)); - break; - case APPEND: - assert_goto(false, err, - "No append action should have been tested\n"); - break; - case REPLACE: - assert_goto(false, err, - "No replace action should have been tested\n"); - break; - case GET: - if (tv->results[j] == EFI_SUCCESS) - gets += 1; - break; - } - } - assert_goto(gets == 0 || gets == 1, err, - "Variable should not be read %d times.\n", gets); - } - if (tv->must_be_present) { - assert_goto(found == true, err, - "variable \"%s\" was not found.\n", - Str2str(tv->name)); - } + /* + * These sizes are picked specifically so that MokListRT will fail + * to get mirrored with the test data in test-data/efivars-1 + */ + list_t mock_obnoxious_variable_limits; + UINT64 obnoxious_max_var_storage = 0xffe4; + UINT64 obnoxious_remaining_var_storage = 919+0x3c; + UINT64 obnoxious_max_var_size = 919; - if (tv->must_be_absent) { - assert_goto(found == false, err, - "variable \"%s\" was found.\n", - Str2str(tv->name)); - } - } - - uint8_t *pos = NULL; - for (size_t i = 0; i < ST->NumberOfTableEntries; i++) { - EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; - - if (CompareGuid(&ct->VendorGuid, &mok_config_guid) != 0) - continue; - - pos = (void *)ct->VendorTable; - break; - } - - assert_nonzero_goto(pos, err, "%p != 0\n"); - - size_t i = 0; - while (pos) { - struct mock_mok_variable_config_entry *mock_entry = - &test_mok_config_table[i]; - struct mok_variable_config_entry *mok_entry = - (struct mok_variable_config_entry *)pos; + struct mock_variable_limits obnoxious_limits[] = { + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS, + .max_var_storage = &obnoxious_max_var_storage, + .remaining_var_storage = &obnoxious_remaining_var_storage, + .max_var_size = &obnoxious_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .max_var_storage = &obnoxious_max_var_storage, + .remaining_var_storage = &obnoxious_remaining_var_storage, + .max_var_size = &obnoxious_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .max_var_storage = &obnoxious_max_var_storage, + .remaining_var_storage = &obnoxious_remaining_var_storage, + .max_var_size = &obnoxious_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .max_var_storage = &obnoxious_max_var_storage, + .remaining_var_storage = &obnoxious_remaining_var_storage, + .max_var_size = &obnoxious_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = 0, } + }; + struct test_var test_mok_mirror_enospc_vars[] = { /* - * If the tables are different lengths, this will trigger. + * We must to see a BS|NV MokList */ - assert_equal_goto(mok_entry->name[0], mock_entry->name[0], err, - "mok.name[0] %ld != test.name[0] %ld\n"); - if (mock_entry->name[0] == 0) - break; - - assert_nonzero_goto(mok_entry->name[0], err, "%ld != %ld\n"); - assert_zero_goto(strncmp(mok_entry->name, mock_entry->name, - sizeof(mock_entry->name)), - err, "%ld != %ld: strcmp(\"%s\",\"%s\")\n", - mok_entry->name, mock_entry->name); - + {.guid = SHIM_LOCK_GUID, + .name = L"MokList", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, /* - * As of 7501b6bb449f ("mok: fix potential buffer overrun in - * import_mok_state"), we should not see any mok config - * variables with data_size == 0. + * We must *NOT* see a BS|RT MokListRT */ - assert_nonzero_goto(mok_entry->data_size, err, "%ld != 0\n"); - - assert_equal_goto(mok_entry->data_size, mock_entry->data_size, - err, "%ld != %ld\n"); - assert_zero_goto(CompareMem(mok_entry->data, mock_entry->data, - mok_entry->data_size), - err, "%ld != %ld\n"); - pos += offsetof(struct mok_variable_config_entry, data) - + mok_entry->data_size; - i += 1; - } - - ret = 0; -err: - for (UINTN k = 0; k < n_mok_state_variables; k++) { - struct mok_state_variable *v = - &mok_state_variables[k]; - if (v->data_size && v->data) { - free(v->data); - v->data = NULL; - v->data_size = 0; + {.guid = SHIM_LOCK_GUID, + .name = L"MokListRT", + .must_be_absent = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + /* + * We must see a BS|NV MokListX + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokListX", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + /* + * We must see a BS|RT MokListXRT + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokListXRT", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + /* + * We must see a BS|NV SbatLevel + */ + {.guid = SHIM_LOCK_GUID, + .name = L"SbatLevel", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + /* + * We must see a BS|RT SbatLevelRT + */ + {.guid = SHIM_LOCK_GUID, + .name = L"SbatLevelRT", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + /* + * We must not see a MokIgnoreDB + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokIgnoreDB", + .must_be_absent = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + /* + * We must not see MokSBState + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokSBState", + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + /* + * We must not see MokSBStateRT + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokSBStateRT", + .must_be_absent = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + {.guid = { 0, }, + .name = NULL, } + }; + + /* + * We must see the supplied values of MokListRT, MokListXRT, and + * SbatLevelRT in the config table + */ + struct mock_mok_variable_config_entry test_mok_config_table[] = { + {.name = "MokListRT", + .data_size = sizeof(test_data_efivars_1_MokListRT), + .data = test_data_efivars_1_MokListRT + }, + {.name = "MokListXRT", + .data_size = sizeof(test_data_efivars_1_MokListXRT), + .data = test_data_efivars_1_MokListXRT + }, + {.name = "SbatLevelRT", + .data_size = sizeof(test_data_efivars_1_SbatLevelRT), + .data = test_data_efivars_1_SbatLevelRT + }, + {.name = "MokListTrustedRT", + .data_size = sizeof(test_data_efivars_1_MokListTrustedRT), + .data = test_data_efivars_1_MokListTrustedRT + }, + {.name = "HSIStatus", + .data_size = sizeof(test_data_efivars_1_HSIStatus), + .data = test_data_efivars_1_HSIStatus + }, + {.name = { 0, }, + .data_size = 0, + .data = NULL, + } + }; + + UINT64 max_storage_sz = 0; + UINT64 max_var_sz = 0; + UINT64 remaining_sz = 0; + + for (size_t i = 0; i < n_mok_state_variables; i++) { + mok_rt_vars[i] = mok_state_variables[i].rtname8; } - test_vars = NULL; + mock_load_variables("test-data/efivars-1", mok_rt_vars, true); + + mock_set_variable_post_hook = setvar_post; + mock_get_variable_post_hook = getvar_post; + + mock_set_usage_limits(&mock_obnoxious_variable_limits, + &obnoxious_limits[0]); + + ret = test_mok_mirror(&test_mok_mirror_enospc_vars[0], + test_mok_config_table, + EFI_OUT_OF_RESOURCES); + + mock_set_default_usage_limits(); + mock_set_variable_post_hook = NULL; mock_get_variable_post_hook = NULL; return ret; @@ -391,7 +703,10 @@ main(void) del_policy_names[j]); mock_variable_delete_attr_policy = delete_policies[j]; - test(test_mok_mirror_0); + test(test_mok_mirror_with_enough_space); + mock_finalize_vars_and_configs(); + + test(test_mok_mirror_setvar_out_of_resources); mock_finalize_vars_and_configs(); if (delete_policies[j] == 0) diff --git a/test-sbat.c b/test-sbat.c index b37efcd..21f2b24 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -8,6 +8,7 @@ #include "sbat_var_defs.h" #endif #include "shim.h" +#include "generated_sbat_var_defs.h" #include diff --git a/tpm.c b/tpm.c index 388f8d1..7f4a1b0 100644 --- a/tpm.c +++ b/tpm.c @@ -11,6 +11,7 @@ typedef struct { UINTN measuredcount = 0; VARIABLE_RECORD *measureddata = NULL; static BOOLEAN tpm_defective = FALSE; +static BOOLEAN log_full_already_warned = FALSE; static BOOLEAN tpm_present(efi_tpm_protocol_t *tpm) { @@ -108,6 +109,16 @@ static EFI_STATUS tpm_locate_protocol(efi_tpm_protocol_t **tpm, return EFI_NOT_FOUND; } +static void warn_first_log_full(void) +{ + if (!log_full_already_warned) { + perror(L"TPM extend operation occurred, but the event could" + " not be written to one or more event logs. Applications" + " reliant on a valid event log will not function.\n"); + log_full_already_warned = TRUE; + } +} + static EFI_STATUS cc_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, const CHAR8 *log, UINTN logsize, UINT32 type, BOOLEAN is_pe_image) @@ -143,6 +154,14 @@ static EFI_STATUS cc_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, CopyMem(event->Event, (VOID *)log, logsize); efi_status = cc->hash_log_extend_event(cc, flags, buf, (UINT64)size, event); + /* Per spec: The extend operation occurred, but the event could + * not be written to one or more event logs. We can still safely + * boot in this case, but also show a warning to let the user know. + */ + if (efi_status == EFI_VOLUME_FULL) { + warn_first_log_full(); + efi_status = EFI_SUCCESS; + } FreePool(event); return efi_status; } @@ -201,11 +220,19 @@ static EFI_STATUS tpm_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, */ efi_status = tpm2->hash_log_extend_event(tpm2, PE_COFF_IMAGE, buf, (UINT64) size, event); + if (efi_status == EFI_VOLUME_FULL) { + warn_first_log_full(); + efi_status = EFI_SUCCESS; + } } if (!hash || EFI_ERROR(efi_status)) { efi_status = tpm2->hash_log_extend_event(tpm2, 0, buf, (UINT64) size, event); + if (efi_status == EFI_VOLUME_FULL) { + warn_first_log_full(); + efi_status = EFI_SUCCESS; + } } FreePool(event); return efi_status; @@ -239,10 +266,18 @@ static EFI_STATUS tpm_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, CopyMem(event->digest, hash, sizeof(event->digest)); efi_status = tpm->log_extend_event(tpm, 0, 0, TPM_ALG_SHA, event, &eventnum, &lastevent); + if (efi_status == EFI_VOLUME_FULL) { + warn_first_log_full(); + efi_status = EFI_SUCCESS; + } } else { efi_status = tpm->log_extend_event(tpm, buf, (UINT64)size, TPM_ALG_SHA, event, &eventnum, &lastevent); + if (efi_status == EFI_VOLUME_FULL) { + warn_first_log_full(); + efi_status = EFI_SUCCESS; + } } if (efi_status == EFI_UNSUPPORTED) { perror(L"Could not write TPM event: %r. Considering " diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..0272229 --- /dev/null +++ b/utils.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +#include "shim.h" + +EFI_STATUS +get_file_size(EFI_FILE_HANDLE fh, UINTN *retsize) +{ + EFI_STATUS efi_status; + void *buffer = NULL; + UINTN bs = 0; + + /* The API here is "Call it once with bs=0, it fills in bs, + * then allocate a buffer and ask again to get it filled. */ + efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, NULL); + if (EFI_ERROR(efi_status) && efi_status != EFI_BUFFER_TOO_SMALL) + return efi_status; + if (bs == 0) + return EFI_SUCCESS; + + buffer = AllocateZeroPool(bs); + if (!buffer) { + console_print(L"Could not allocate memory\n"); + return EFI_OUT_OF_RESOURCES; + } + efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, buffer); + /* This checks *either* the error from the first GetInfo, if it isn't + * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo + * call in *any* case. */ + if (EFI_ERROR(efi_status)) { + console_print(L"Could not get file info: %r\n", efi_status); + if (buffer) + FreePool(buffer); + return efi_status; + } + EFI_FILE_INFO *fi = buffer; + *retsize = fi->FileSize; + FreePool(buffer); + return EFI_SUCCESS; +} + +EFI_STATUS +read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs) +{ + EFI_FILE_HANDLE fh2; + EFI_STATUS efi_status; + + efi_status = fh->Open(fh, &fh2, fullpath, EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(efi_status)) { + console_print(L"Couldn't open \"%s\": %r\n", fullpath, efi_status); + return efi_status; + } + + UINTN len = 0; + CHAR16 *b = NULL; + efi_status = get_file_size(fh2, &len); + if (EFI_ERROR(efi_status)) { + console_print(L"Could not get file size for \"%s\": %r\n", + fullpath, efi_status); + fh2->Close(fh2); + return efi_status; + } + + if (len > 1024 * PAGE_SIZE) { + fh2->Close(fh2); + return EFI_BAD_BUFFER_SIZE; + } + + b = AllocateZeroPool(len + 2); + if (!b) { + console_print(L"Could not allocate memory\n"); + fh2->Close(fh2); + return EFI_OUT_OF_RESOURCES; + } + + efi_status = fh->Read(fh, &len, b); + if (EFI_ERROR(efi_status)) { + FreePool(b); + fh2->Close(fh2); + console_print(L"Could not read file: %r\n", efi_status); + return efi_status; + } + *buffer = b; + *bs = len; + fh2->Close(fh2); + return EFI_SUCCESS; +}