summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/pullrequest.yml88
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules2
-rw-r--r--BUILDING12
-rw-r--r--CODE_OF_CONDUCT.md2
-rw-r--r--Cryptlib/InternalCryptLib.h35
-rw-r--r--Cryptlib/Library/BaseCryptLib.h42
-rw-r--r--Cryptlib/Makefile10
-rw-r--r--Cryptlib/OpenSSL/Makefile3
-rw-r--r--Cryptlib/Pk/CryptPkcs7Verify.c18
-rw-r--r--Cryptlib/Pk/CryptPkcs7VerifyEku.c516
-rw-r--r--Delivering_Sbat_Revocations.md29
-rw-r--r--Make.defaults8
-rw-r--r--Makefile41
-rw-r--r--MokManager.c9
-rw-r--r--MokVars.txt35
-rw-r--r--README.tpm10
-rw-r--r--SBAT.md121
-rw-r--r--SbatLevel_Variable.txt35
-rw-r--r--dp.c43
-rw-r--r--errlog.c181
-rw-r--r--fallback.c219
-rw-r--r--fuzz-pe-relocate.c2
-rw-r--r--generate_sbat_var_defs.c173
-rw-r--r--globals.c7
-rw-r--r--gnu-efi/inc/efiapi.h72
-rw-r--r--gnu-efi/inc/efierr.h5
-rw-r--r--gnu-efi/inc/efilib.h6
-rw-r--r--gnu-efi/inc/efiprot.h63
-rw-r--r--gnu-efi/lib/data.c3
-rw-r--r--gnu-efi/lib/error.c5
-rw-r--r--httpboot.c58
-rw-r--r--include/compiler.h9
-rw-r--r--include/console.h1
-rw-r--r--include/dp.h14
-rw-r--r--include/errlog.h22
-rw-r--r--include/errors.h3
-rw-r--r--include/fanalyzer.mk2
-rw-r--r--include/guid.h12
-rw-r--r--include/hexdump.h6
-rw-r--r--include/load-options.h1
-rw-r--r--include/loader-proto.h (renamed from include/replacements.h)12
-rw-r--r--include/memattrs.h18
-rw-r--r--include/mock-variables.h3
-rw-r--r--include/mok.h48
-rw-r--r--include/netboot.h5
-rw-r--r--include/pe.h6
-rw-r--r--include/peimage.h13
-rw-r--r--include/sbat.h3
-rw-r--r--include/sbat_var_defs.h42
-rw-r--r--include/test-data-efivars-1.h11
-rw-r--r--include/test.h18
-rw-r--r--include/test.mk11
-rw-r--r--include/utils.h9
-rw-r--r--lib/console.c1
-rw-r--r--lib/guid.c2
-rw-r--r--lib/simple_file.c27
-rw-r--r--lib/variables.c2
-rw-r--r--load-options.c32
-rw-r--r--loader-proto.c445
-rwxr-xr-xmake-archive7
-rwxr-xr-xmake-certs5
-rw-r--r--memattrs.c550
-rw-r--r--mock-variables.c114
-rw-r--r--mok.c363
-rw-r--r--netboot.c68
-rw-r--r--pe-relocate.c127
-rw-r--r--pe.c149
-rw-r--r--post-process-pe.c60
-rw-r--r--replacements.c222
-rw-r--r--sbat.c6
-rw-r--r--sbat_var.S1
-rw-r--r--shim.c296
-rw-r--r--shim.h32
-rw-r--r--test-load-options.c77
-rw-r--r--test-mock-variables.c37
-rw-r--r--test-mok-mirror.c575
-rw-r--r--test-sbat.c1
-rw-r--r--tpm.c35
-rw-r--r--utils.c86
80 files changed, 4404 insertions, 1040 deletions
diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml
index 7a807384..6027d4d0 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 9085e5a7..d8296699 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 d0f1973a..41842dc8 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 17cd98d3..5005868a 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 59a2c94a..2144aedf 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 dc1a95e6..0ad2ef70 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 2df8bd2f..439f0516 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 626788c9..68a9395e 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 d59c5d7a..e517bcfd 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 c1893848..640b01d0 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 <openssl/pkcs7.h>
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 00000000..f277f1dd
--- /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.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include "InternalCryptLib.h"
+#include <openssl/x509v3.h>
+#include <openssl/asn1.h>
+#include <openssl/x509.h>
+#include <openssl/bio.h>
+#include <openssl/x509.h>
+#include <openssl/pkcs7.h>
+#include <openssl/bn.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/asn1.h>
+
+/**
+ 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 00000000..d3e50604
--- /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 e75cd3cd..c5fa32be 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 8283d56f..d394439a 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 ffcd6a6e..52f5c0a0 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 baf8db9a..6ad8ce70 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 9e830b72..e768dc03 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 998f7539..81e27aa6 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<br>disclosure\* | after<br>disclosure | after Vendor C's<br>first update | after Vendor C's<br>second update | after next global<br>disclosure |
-|--------------------------------------------------------------------------------------|--------------------------|---------------------|----------------------------------|-----------------------------------|---------------------------------|
-| GRUB global<br>generation number in<br>artifacts .sbat section | 3 | 4 | 4 | 4 | 5 |
-| Vendor C's product-specific<br>generation number in artifact's<br>.sbat section | 1 | 1 | 2 | 3 | 1 |
-| GRUB global<br>generation number in<br>UEFI SBAT revocation variable | 3 | 4 | 4 | 4 | 5 |
-| Vendor C's product-specific<br>generation number in<br>UEFI SBAT revocation variable | not set | not set | 2 | 3 | not set |
+| | prior to GRUB<br>first disclosure\* | after GRUB<br>first disclosure\* | after Vendor C's<br>first update | after Vendor C's<br>second update | after GRUB<br>second disclosure\* |
+|--------------------------------------------------------------------------------------|-------------------------------------|----------------------------------|----------------------------------|-----------------------------------|-----------------------------------|
+| GRUB global<br>generation number in<br>artifacts .sbat section | 3 | 4 | 4 | 4 | 5 |
+| Vendor C's product<br>generation number in<br>artifact's .sbat section | 1 | 1 | 2 | 3 | 3 |
+| GRUB global<br>generation number in<br>UEFI *SbatLevel* variable | 3 | 4 | 4 | 4 | 5 |
+| Vendor C's product<br>generation number in<br>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 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.
+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.
-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 42a388e4..7afdcd0d 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<date TBD>
+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/dp.c b/dp.c
new file mode 100644
index 00000000..3fc46f8d
--- /dev/null
+++ b/dp.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+/*
+ * dp.c - device path helpers
+ * Copyright Peter Jones <pjones@redhat.com>
+ */
+
+#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 3c5e0af8..b43a4bc2 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 600cc7aa..86ebe234 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;
+
+ 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(CHAR16);
+ 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));
+
+ if (bootorder)
+ FreePool(bootorder);
+ nbootorder = nnewbootentries + nbootorder;
+ bootorder = newbootorder;
- VerbosePrint(L"nbootorder: %d\nBootOrder: ", size / sizeof (CHAR16));
- UINTN j;
- for (j = 0 ; j < size / sizeof (CHAR16); j++)
- VerbosePrintUnprefixed(L"%04x ", newbootorder[j]);
+ 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 1f62234d..09d38331 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 00000000..1258e1b2
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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 b4e80dd3..11197128 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/inc/efiapi.h b/gnu-efi/inc/efiapi.h
index 96e9e4a0..9d399a28 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 5a66e1a0..ac9ef7ba 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 af470191..a8316661 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 4013ab25..db291579 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 34717d77..3b21c21c 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 5b36f5fd..24864cbe 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 ac9ea25c..9b0947d8 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 8e8a658d..6a19217c 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 7ac4e113..c90e3e71 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 00000000..884c1460
--- /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 <pjones@redhat.com>
+ */
+
+#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 00000000..b9f089b8
--- /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 <pjones@redhat.com>
+ */
+
+#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 67d821e0..eab58453 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 a0679e3e..b22656ea 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 898c4fad..26628d1e 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 e8f4fe1a..6f3d5fa5 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 d2bee3bb..78b1dcce 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
index 8b35c857..db8e670e 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 00000000..193da988
--- /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 <pjones@redhat.com>
+ */
+
+#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 9f276e63..b7ee1cb4 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 fb19423b..f4468ab0 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 a7bf6cd8..296f10f0 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 9ea9eb44..ea40184b 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 6eef1051..5d049686 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 bb523e7e..093bb64a 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 f8cba029..f4f5a27b 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=<datestamp> 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 2831bd23..259558e0 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 5261dbd9..ccb61148 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 e6d46594..ee2d2fde 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 00000000..654f05d8
--- /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 a751f79d..f6038320 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 6e92cea3..1dc90ca9 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 f22852d4..abbc4975 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 8e63aa8f..1a2c7d48 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 a8c6e1a3..660eaa91 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 00000000..4581664b
--- /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 9ae9eef0..d0352b70 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 6f40b234..e2d3e4b4 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 00000000..ed8a3ae2
--- /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 <pjones@redhat.com>
+ */
+
+#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 03044549..723cdda2 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),
@@ -1002,18 +1019,27 @@ static struct mock_variable_limits default_limits[] = {
};
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)
{
default_max_var_storage = 65536;
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 0ac34158..bf71542b 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/<os>/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 d8b10935..46765427 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 bde71729..6f2af4e4 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 33056017..d785c44e 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 de8f4a38..008da93b 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 469e73aa..00000000
--- 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 06956122..f31d9454 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 ed82a46c..72924067 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 633163a0..8b933d7b 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,
@@ -781,39 +779,6 @@ verify_buffer (char *data, int datasize,
}
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)
{
EFI_LOADED_IMAGE *li;
@@ -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;
- }
- pointer = ImageAddress(data, datasize,
- Section->PointerToRawData);
- if (!pointer) {
- 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);
}
- 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 5791a031..59d90629 100644
--- a/shim.h
+++ b/shim.h
@@ -55,6 +55,7 @@
#ifndef SHIM_UNIT_TEST
#include <efi.h>
#include <efilib.h>
+#include <efisetjmp.h>
#undef uefi_call_wrapper
#include <efierr.h>
#include <efiip.h>
@@ -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 <efisetjmp_arch.h>
+
+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 daf02d93..dfabe861 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");
@@ -256,6 +259,73 @@ test_bds_2(void)
}
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)
{
int status = 0;
@@ -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 c7e42b05..f8693007 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 f34f62a9..38b7ed97 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 <err.h>
#include <stdio.h>
#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(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_0(void)
+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;
+
+ 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, }
+ };
- if (tv->must_be_absent) {
- assert_goto(found == false, err,
- "variable \"%s\" was found.\n",
- Str2str(tv->name));
+ struct test_var test_mok_mirror_enospc_vars[] = {
+ /*
+ * We must to see a BS|NV MokList
+ */
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"MokList",
+ .must_be_present = true,
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ .ops = { NONE, },
+ },
+ /*
+ * We must *NOT* see a BS|RT MokListRT
+ */
+ {.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,
}
- }
+ };
- uint8_t *pos = NULL;
- for (size_t i = 0; i < ST->NumberOfTableEntries; i++) {
- EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i];
+ /*
+ * 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,
+ }
+ };
- if (CompareGuid(&ct->VendorGuid, &mok_config_guid) != 0)
- continue;
+ UINT64 max_storage_sz = 0;
+ UINT64 max_var_sz = 0;
+ UINT64 remaining_sz = 0;
- pos = (void *)ct->VendorTable;
- break;
+ for (size_t i = 0; i < n_mok_state_variables; i++) {
+ mok_rt_vars[i] = mok_state_variables[i].rtname8;
}
- assert_nonzero_goto(pos, err, "%p != 0\n");
+ mock_load_variables("test-data/efivars-1", mok_rt_vars, true);
- 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;
+ mock_set_variable_post_hook = setvar_post;
+ mock_get_variable_post_hook = getvar_post;
- /*
- * 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;
+ mock_set_usage_limits(&mock_obnoxious_variable_limits,
+ &obnoxious_limits[0]);
- 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);
+ ret = test_mok_mirror(&test_mok_mirror_enospc_vars[0],
+ test_mok_config_table,
+ EFI_OUT_OF_RESOURCES);
- /*
- * 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");
+ mock_set_default_usage_limits();
- 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;
- }
- }
-
- test_vars = NULL;
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 b37efcdd..21f2b24c 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 <stdio.h>
diff --git a/tpm.c b/tpm.c
index 388f8d12..7f4a1b09 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 00000000..02722294
--- /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;
+}