Version in base suite: 15.8-1~deb12u1 Base version: shim_15.8-1~deb12u1 Target version: shim_16.1-2~deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/shim/shim_15.8-1~deb12u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/shim/shim_16.1-2~deb12u1.dsc .github/workflows/mkosi.yml | 88 .github/workflows/pullrequest.yml | 92 .gitignore | 10 .gitmodules | 2 BUILDING | 22 CODE_OF_CONDUCT.md | 2 Cryptlib/InternalCryptLib.h | 35 Cryptlib/Library/BaseCryptLib.h | 42 Cryptlib/Makefile | 10 Cryptlib/OpenSSL/Makefile | 3 Cryptlib/Pk/CryptPkcs7Verify.c | 18 Cryptlib/Pk/CryptPkcs7VerifyEku.c | 516 ++++ Delivering_Sbat_Revocations.md | 29 Make.defaults | 8 Makefile | 42 MokManager.c | 9 MokVars.txt | 35 README.md | 28 README.tpm | 10 SBAT.md | 137 - SbatLevel_Variable.txt | 42 commit | 2 debian/changelog | 40 debian/control | 3 debian/copyright | 42 debian/patches/0001-sbat-Add-grub.peimage-2-to-latest-CVE-2024-2312.patch | 42 debian/patches/0002-sbat-Also-bump-latest-for-grub-4-and-to-todays-date.patch | 47 debian/patches/series | 2 debian/rules | 9 debian/source/lintian-overrides | 3 dp.c | 43 errlog.c | 181 + fallback.c | 219 - fuzz-pe-relocate.c | 2 generate_sbat_var_defs.c | 173 + globals.c | 7 gnu-efi/inc/efiapi.h | 72 gnu-efi/inc/efierr.h | 5 gnu-efi/inc/efilib.h | 14 gnu-efi/inc/efiprot.h | 63 gnu-efi/lib/data.c | 3 gnu-efi/lib/error.c | 5 gnu-efi/lib/print.c | 37 httpboot.c | 57 include/compiler.h | 9 include/console.h | 1 include/dp.h | 14 include/errlog.h | 22 include/errors.h | 3 include/fanalyzer.mk | 2 include/fuzz.mk | 2 include/guid.h | 12 include/hexdump.h | 6 include/load-options.h | 1 include/loader-proto.h | 36 include/memattrs.h | 19 include/mock-variables.h | 3 include/mok.h | 48 include/netboot.h | 5 include/pe.h | 17 include/peimage.h | 13 include/replacements.h | 30 include/sbat.h | 3 include/sbat_var_defs.h | 42 include/test-data-efivars-1.h | 11 include/test.h | 18 include/test.mk | 13 include/utils.h | 9 include/verify.h | 41 lib/console.c | 1 lib/guid.c | 2 lib/simple_file.c | 27 lib/variables.c | 2 load-options.c | 32 loader-proto.c | 528 ++++ make-archive | 7 make-certs | 5 memattrs.c | 550 ++++ mkosi/mkosi.build.chroot | 26 mkosi/mkosi.clean | 4 mkosi/mkosi.conf | 65 mkosi/mkosi.conf.d/centos-fedora/mkosi.conf | 17 mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-arm64.conf | 5 mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-x86-64.conf | 5 mkosi/mkosi.conf.d/centos/certs/shim.crt | 21 mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf | 19 mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-arm64.conf | 5 mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-x86-64.conf | 5 mkosi/mkosi.conf.d/debian/certs/shim.crt | 22 mkosi/mkosi.conf.d/fedora/certs/mok/redhat-test.crt | 25 mkosi/mkosi.conf.d/fedora/certs/shim.crt | 26 mkosi/mkosi.conf.d/fedora/mkosi.conf | 9 mkosi/mkosi.conf.d/ubuntu/certs/shim.crt | 25 mkosi/mkosi.conf.d/ubuntu/mkosi.conf | 5 mkosi/mkosi.extra/usr/bin/mkosi-test.sh | 17 mkosi/mkosi.extra/usr/lib/systemd/system/mkosi-test.service | 12 mkosi/mkosi.finalize | 18 mkosi/mkosi.postoutput | 80 mkosi/mkosi.repart/00-esp.conf | 7 mkosi/mkosi.repart/10-root.conf | 6 mock-variables.c | 118 - mok.c | 484 ++++ netboot.c | 109 pe-relocate.c | 154 + pe.c | 395 +-- post-process-pe.c | 60 replacements.c | 222 -- sbat.c | 6 sbat_var.S | 1 shim.c | 1101 +--------- shim.h | 39 test-load-options.c | 77 test-mock-variables.c | 41 test-mok-mirror.c | 575 ++++- test-sbat.c | 1 tpm.c | 35 utils.c | 86 verify.c | 831 +++++++ 118 files changed, 6466 insertions(+), 2078 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp1y5hgjhw/shim_15.8-1~deb12u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp1y5hgjhw/shim_16.1-2~deb12u1.dsc: no acceptable signature found diff -Nru shim-15.8/.github/workflows/mkosi.yml shim-16.1/.github/workflows/mkosi.yml --- shim-15.8/.github/workflows/mkosi.yml 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/.github/workflows/mkosi.yml 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,88 @@ +name: mkosi + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + boot: + runs-on: ${{ matrix.runner }} + concurrency: + group: ${{ github.workflow }}-${{ matrix.distro }}-${{ matrix.bootloader }}-${{ matrix.uki }}-${{ matrix.runner }}-${{ github.ref }} + cancel-in-progress: true + strategy: + fail-fast: false + matrix: + distro: + - fedora + - centos + - ubuntu + - debian + bootloader: + # Locally signed systemd-boot + - systemd-boot + # Distro signed grub2 + - grub-signed + uki: + # BLS #1 boot, kernel + initrd + - none + # BLS #2 boot, locally built UKI (unsigned because it is not provided by the distro) + - unsigned + runner: + - ubuntu-24.04 + - ubuntu-24.04-arm + include: + # Debian provides distro-signed systemd-boot + - distro: debian + bootloader: systemd-boot-signed + uki: unsigned + runner: ubuntu-24.04 + - distro: debian + bootloader: systemd-boot-signed + uki: unsigned + runner: ubuntu-24.04-arm + exclude: + # The systemd-boot version in 24.04 fails to boot the arm64 compressed kernel + - distro: ubuntu + bootloader: systemd-boot + uki: none + runner: ubuntu-24.04-arm + # grub fails to load UKI with: error: ../../grub-core/script/function.c:119:can't find command `chainloader' + - distro: centos + bootloader: grub-signed + uki: unsigned + runner: ubuntu-24.04-arm + - distro: fedora + bootloader: grub-signed + uki: unsigned + runner: ubuntu-24.04-arm + # kernel is not signed + - distro: fedora + bootloader: grub-signed + uki: none + runner: ubuntu-24.04-arm + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + submodules: recursive + - uses: systemd/mkosi@main + + - name: Generate key + run: mkosi genkey + + - name: Summary + run: mkosi summary + + - name: Build tools tree + run: mkosi -f sandbox -- true + + - name: Build image + run: mkosi sandbox -- mkosi --distribution ${{ matrix.distro }} --bootloader ${{ matrix.bootloader }} --unified-kernel-images ${{ matrix.uki }} --kernel-command-line=systemd.unit=mkosi-test.service -f build + + - name: Run smoke tests + run: test "$(timeout -k 30 5m mkosi sandbox -- mkosi --distribution ${{ matrix.distro }} --kernel-command-line-extra=systemd.unit=mkosi-test.service qemu 1>&2; echo $?)" -eq 123 diff -Nru shim-15.8/.github/workflows/pullrequest.yml shim-16.1/.github/workflows/pullrequest.yml --- shim-15.8/.github/workflows/pullrequest.yml 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/.github/workflows/pullrequest.yml 1970-01-01 00:00:00.000000000 +0000 @@ -10,7 +10,7 @@ jobs: cross-build-pull-request: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 container: vathpela/efi-ci:${{ matrix.distro }}-x64 name: ${{ matrix.distro }} ${{ matrix.efiarch }} cross-build @@ -22,66 +22,70 @@ 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: | @@ -108,7 +112,7 @@ find /destdir -type f build-pull-request-intel: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 container: vathpela/efi-ci:${{ matrix.distro }}-x64 name: ${{ matrix.distro }} ${{ matrix.efiarch }} build @@ -118,15 +122,15 @@ - 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 @@ - 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 @@ 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 @@ 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-24.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 -Nru shim-15.8/.gitignore shim-16.1/.gitignore --- shim-15.8/.gitignore 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -38,6 +38,8 @@ /crash-* /fuzz-* !/fuzz-*.c +/generate_sbat_var_defs +/generated_sbat_var_defs.h /leak-* /post-process-pe /random.bin @@ -50,3 +52,11 @@ !/test-data/ /test-random.h version.c +/.vscode/ +/.mkosi-private/ +/mkosi.tools/ +/mkosi.tools.manifest +/mkosi/mkosi.builddir/ +/mkosi/mkosi.cache/ +/mkosi/mkosi.local.conf +/mkosi/mkosi.output/ diff -Nru shim-15.8/.gitmodules shim-16.1/.gitmodules --- shim-15.8/.gitmodules 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/.gitmodules 1970-01-01 00:00:00.000000000 +0000 @@ -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.1 diff -Nru shim-15.8/BUILDING shim-16.1/BUILDING --- shim-15.8/BUILDING 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/BUILDING 1970-01-01 00:00:00.000000000 +0000 @@ -37,15 +37,6 @@ 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 @@ - 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 @@ -88,4 +82,14 @@ name sbat.FOO.csv, where foo is your EFI subdirectory name. The build system will automatically include any such files. +Building and booting local VM to test shim changes with other 2nd/3rd stages: +- pick the target distribution of your choice, currently supported are Fedora/CentOS/Debian/Ubuntu +- ensure the package manager for the target distribution is installed locally - dnf+rpm or apt+dpkg +- install python3 +- have mkosi available, either via the distribution, or pip, or having bin/mkosi in PATH from git clone of https://github.com/systemd/mkosi +- (one time step) mkosi genkey +- mkosi sandbox -- mkosi --firmware-variables mkosi/mkosi.output/ovmf_vars.fd --distribution qemu +- this will build and cache a local VM, build shim in it, and boot it with systemd-boot and a UKI by default +- to pick different 2nd/3rd stages, run mkosi sandbox -- mkosi --distribution --bootloader <2nd stage> --unified-kernel-images -f -i build + # vim:filetype=mail:tw=74 diff -Nru shim-15.8/CODE_OF_CONDUCT.md shim-16.1/CODE_OF_CONDUCT.md --- shim-15.8/CODE_OF_CONDUCT.md 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/CODE_OF_CONDUCT.md 1970-01-01 00:00:00.000000000 +0000 @@ -61,7 +61,7 @@ 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 -Nru shim-15.8/Cryptlib/InternalCryptLib.h shim-16.1/Cryptlib/InternalCryptLib.h --- shim-15.8/Cryptlib/InternalCryptLib.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/Cryptlib/InternalCryptLib.h 1970-01-01 00:00:00.000000000 +0000 @@ -32,5 +32,38 @@ #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 -Nru shim-15.8/Cryptlib/Library/BaseCryptLib.h shim-16.1/Cryptlib/Library/BaseCryptLib.h --- shim-15.8/Cryptlib/Library/BaseCryptLib.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/Cryptlib/Library/BaseCryptLib.h 1970-01-01 00:00:00.000000000 +0000 @@ -2403,6 +2403,48 @@ 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 -Nru shim-15.8/Cryptlib/Makefile shim-16.1/Cryptlib/Makefile --- shim-15.8/Cryptlib/Makefile 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/Cryptlib/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -14,6 +14,9 @@ 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 @@ 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 @@ SysCall/BaseMemAllocation.o \ SysCall/BaseStrings.o +ifeq ($(ENABLE_CODESIGN_EKU),1) + OBJS += Pk/CryptPkcs7VerifyEku.o +endif + all: $(TARGET) libcryptlib.a: $(OBJS) diff -Nru shim-15.8/Cryptlib/OpenSSL/Makefile shim-16.1/Cryptlib/OpenSSL/Makefile --- shim-15.8/Cryptlib/OpenSSL/Makefile 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/Cryptlib/OpenSSL/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -26,6 +26,9 @@ -Wno-unused-but-set-variable \ -Wno-unused-parameter +WERRFLAGS += -Wno-error=unused-but-set-variable \ + -Wno-error=unused-parameter + CFLAGS = $(FEATUREFLAGS) \ $(OPTIMIZATIONS) \ $(WARNFLAGS) \ diff -Nru shim-15.8/Cryptlib/Pk/CryptPkcs7Verify.c shim-16.1/Cryptlib/Pk/CryptPkcs7Verify.c --- shim-15.8/Cryptlib/Pk/CryptPkcs7Verify.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/Cryptlib/Pk/CryptPkcs7Verify.c 1970-01-01 00:00:00.000000000 +0000 @@ -29,6 +29,10 @@ #include UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 }; +#if defined(ENABLE_CODESIGN_EKU) +/* EKU CodeSign */ +CHAR8 mOidCodeSign[] = "1.3.6.1.5.5.7.3.3"; +#endif #if 1 #if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -846,6 +850,10 @@ 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 @@ 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 @@ // 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 -Nru shim-15.8/Cryptlib/Pk/CryptPkcs7VerifyEku.c shim-16.1/Cryptlib/Pk/CryptPkcs7VerifyEku.c --- shim-15.8/Cryptlib/Pk/CryptPkcs7VerifyEku.c 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/Cryptlib/Pk/CryptPkcs7VerifyEku.c 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,516 @@ +/** @file + This module verifies that Enhanced Key Usages (EKU's) are present within + a PKCS7 signature blob using OpenSSL. + + Copyright (C) Microsoft Corporation. All Rights Reserved. + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include "InternalCryptLib.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + This function will return the leaf signer certificate in a chain. This is + required because certificate chains are not guaranteed to have the + certificates in the order that they were issued. + + A typical certificate chain looks like this: + + + ---------------------------- + | Root | + ---------------------------- + ^ + | + ---------------------------- + | Policy CA | <-- Typical Trust Anchor. + ---------------------------- + ^ + | + ---------------------------- + | Issuing CA | + ---------------------------- + ^ + | + ----------------------------- + / End-Entity (leaf) signer / <-- Bottom certificate. + ----------------------------- EKU: "1.3.6.1.4.1.311.76.9.21.1" + (Firmware Signing) + + + @param[in] CertChain Certificate chain. + + @param[out] SignerCert Last certificate in the chain. For PKCS7 signatures, + this will be the end-entity (leaf) signer cert. + + @retval EFI_SUCCESS The required EKUs were found in the signature. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND The number of signers found was not 1. + +**/ +EFI_STATUS +GetSignerCertificate ( + IN CONST PKCS7 *CertChain, + OUT X509 **SignerCert + ) +{ + EFI_STATUS Status; + STACK_OF(X509) *Signers; + INT32 NumberSigners; + + Status = EFI_SUCCESS; + Signers = NULL; + NumberSigners = 0; + + if (CertChain == NULL || SignerCert == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Get the signers from the chain. + // + Signers = PKCS7_get0_signers ((PKCS7*) CertChain, NULL, PKCS7_BINARY); + if (Signers == NULL) { + // + // Fail to get signers form PKCS7 + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // There should only be one signer in the PKCS7 stack. + // + NumberSigners = sk_X509_num (Signers); + if (NumberSigners != 1) { + // + // The number of singers should have been 1 + // + Status = EFI_NOT_FOUND; + goto Exit; + } + + *SignerCert = sk_X509_value (Signers, 0); + +Exit: + // + // Release Resources + // + if (Signers != NULL) { + sk_X509_free (Signers); + } + + return Status; +} + + +/** + Determines if the specified EKU represented in ASN1 form is present + in a given certificate. + + @param[in] Cert The certificate to check. + + @param[in] Asn1ToFind The EKU to look for. + + @retval EFI_SUCCESS We successfully identified the signing type. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND One or more EKU's were not found in the signature. + +**/ +EFI_STATUS +IsEkuInCertificate ( + IN CONST X509 *Cert, + IN ASN1_OBJECT *Asn1ToFind + ) +{ + EFI_STATUS Status; + X509 *ClonedCert; + X509_EXTENSION *Extension; + EXTENDED_KEY_USAGE *Eku; + INT32 ExtensionIndex; + INTN NumExtensions; + ASN1_OBJECT *Asn1InCert; + INTN Index; + + Status = EFI_NOT_FOUND; + ClonedCert = NULL; + Extension = NULL; + Eku = NULL; + ExtensionIndex = -1; + NumExtensions = 0; + Asn1InCert = NULL; + + if (Cert == NULL || Asn1ToFind == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Clone the certificate. This is required because the Extension API's + // only work once per instance of an X509 object. + // + ClonedCert = X509_dup ((X509*)Cert); + if (ClonedCert == NULL) { + // + // Fail to duplicate cert. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Look for the extended key usage. + // + ExtensionIndex = X509_get_ext_by_NID (ClonedCert, NID_ext_key_usage, -1); + + if (ExtensionIndex < 0) { + // + // Fail to find 'NID_ext_key_usage' in Cert. + // + goto Exit; + } + + Extension = X509_get_ext (ClonedCert, ExtensionIndex); + if (Extension == NULL) { + // + // Fail to get Extension form cert. + // + goto Exit; + } + + Eku = (EXTENDED_KEY_USAGE*)X509V3_EXT_d2i (Extension); + if (Eku == NULL) { + // + // Fail to get Eku from extension. + // + goto Exit; + } + + NumExtensions = sk_ASN1_OBJECT_num (Eku); + + // + // Now loop through the extensions, looking for the specified Eku. + // + for (Index = 0; Index < NumExtensions; Index++) { + Asn1InCert = sk_ASN1_OBJECT_value (Eku, (INT32)Index); + if (Asn1InCert == NULL) { + // + // Fail to get ASN object from Eku. + // + goto Exit; + } + + if (OBJ_cmp(Asn1InCert, Asn1ToFind) == 0) { + // + // Found Eku in certificate. + // + Status = EFI_SUCCESS; + goto Exit; + } + } + +Exit: + + // + // Release Resources + // + if (ClonedCert != NULL) { + X509_free (ClonedCert); + } + + if (Eku != NULL) { + sk_ASN1_OBJECT_pop_free (Eku, ASN1_OBJECT_free); + } + + return Status; +} + + +/** + Determines if the specified EKUs are present in a signing certificate. + + @param[in] SignerCert The certificate to check. + @param[in] RequiredEKUs The EKUs to look for. + @param[in] RequiredEKUsSize The number of EKUs + @param[in] RequireAllPresent If TRUE, then all the specified EKUs + must be present in the certificate. + + @retval EFI_SUCCESS We successfully identified the signing type. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND One or more EKU's were not found in the signature. +**/ +EFI_STATUS +CheckEKUs( + IN CONST X509 *SignerCert, + IN CONST CHAR8 *RequiredEKUs[], + IN CONST UINT32 RequiredEKUsSize, + IN BOOLEAN RequireAllPresent + ) +{ + EFI_STATUS Status; + ASN1_OBJECT *Asn1ToFind; + UINT32 NumEkusFound; + UINT32 Index; + + Status = EFI_NOT_FOUND; + Asn1ToFind = NULL; + NumEkusFound = 0; + + if (SignerCert == NULL || RequiredEKUs == NULL || RequiredEKUsSize == 0) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + for (Index = 0; Index < RequiredEKUsSize; Index++) { + // + // Finding required EKU in cert. + // + if (Asn1ToFind != NULL) { + ASN1_OBJECT_free(Asn1ToFind); + Asn1ToFind = NULL; + } + + Asn1ToFind = OBJ_txt2obj (RequiredEKUs[Index], 0); + if (Asn1ToFind == NULL) { + // + // Fail to convert required EKU to ASN1. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + Status = IsEkuInCertificate (SignerCert, Asn1ToFind); + if (Status == EFI_SUCCESS) { + NumEkusFound++; + if (!RequireAllPresent) { + // + // Found at least one, so we are done. + // + goto Exit; + } + } else { + // + // Fail to find Eku in cert + break; + } + } + +Exit: + + if (Asn1ToFind != NULL) { + ASN1_OBJECT_free(Asn1ToFind); + } + + if (RequireAllPresent && + NumEkusFound == RequiredEKUsSize) { + // + // Found all required EKUs in certificate. + // + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + This function receives a PKCS#7 formatted signature blob, + looks for the EKU SEQUENCE blob, and if found then looks + for all the required EKUs. This function was created so that + the Surface team can cut down on the number of Certificate + Authorities (CA's) by checking EKU's on leaf signers for + a specific product. This prevents one product's certificate + from signing another product's firmware or unlock blobs. + + Note that this function does not validate the certificate chain. + That needs to be done before using this function. + + @param[in] Pkcs7Signature The PKCS#7 signed information content block. An array + containing the content block with both the signature, + the signer's certificate, and any necessary intermediate + certificates. + @param[in] Pkcs7SignatureSize Number of bytes in Pkcs7Signature. + @param[in] RequiredEKUs Array of null-terminated strings listing OIDs of + required EKUs that must be present in the signature. + @param[in] RequiredEKUsSize Number of elements in the RequiredEKUs string array. + @param[in] RequireAllPresent If this is TRUE, then all of the specified EKU's + must be present in the leaf signer. If it is + FALSE, then we will succeed if we find any + of the specified EKU's. + + @retval EFI_SUCCESS The required EKUs were found in the signature. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND One or more EKU's were not found in the signature. + +**/ +EFI_STATUS +EFIAPI +VerifyEKUsInPkcs7Signature ( + IN CONST UINT8 *Pkcs7Signature, + IN CONST UINT32 SignatureSize, + IN CONST CHAR8 *RequiredEKUs[], + IN CONST UINT32 RequiredEKUsSize, + IN BOOLEAN RequireAllPresent + ) +{ + EFI_STATUS Status; + PKCS7 *Pkcs7; + STACK_OF(X509) *CertChain; + INT32 SignatureType; + INT32 NumberCertsInSignature; + X509 *SignerCert; + UINT8 *SignedData; + UINT8 *Temp; + UINTN SignedDataSize; + BOOLEAN IsWrapped; + BOOLEAN Ok; + + Status = EFI_SUCCESS; + Pkcs7 = NULL; + CertChain = NULL; + SignatureType = 0; + NumberCertsInSignature = 0; + SignerCert = NULL; + SignedData = NULL; + SignedDataSize = 0; + IsWrapped = FALSE; + Ok = FALSE; + + // + //Validate the input parameters. + // + if (Pkcs7Signature == NULL || + SignatureSize == 0 || + RequiredEKUs == NULL || + RequiredEKUsSize == 0) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + if (RequiredEKUsSize == 1) { + RequireAllPresent = TRUE; + } + + // + // Wrap the PKCS7 data if needed. + // + Ok = WrapPkcs7Data (Pkcs7Signature, + SignatureSize, + &IsWrapped, + &SignedData, + &SignedDataSize); + if (!Ok) { + // + // Fail to Wrap the PKCS7 data. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + Temp = SignedData; + + // + // Create the PKCS7 object. + // + Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (INT32)SignedDataSize); + if (Pkcs7 == NULL) { + // + // Fail to read PKCS7 data. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Get the certificate chain. + // + SignatureType = OBJ_obj2nid (Pkcs7->type); + switch (SignatureType) { + case NID_pkcs7_signed: + if (Pkcs7->d.sign != NULL) { + CertChain = Pkcs7->d.sign->cert; + } + break; + case NID_pkcs7_signedAndEnveloped: + if (Pkcs7->d.signed_and_enveloped != NULL) { + CertChain = Pkcs7->d.signed_and_enveloped->cert; + } + break; + default: + break; + } + + // + // Ensure we have a certificate stack + // + if (CertChain == NULL) { + // + // Fail to get the certificate stack from signature. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Find out how many certificates were in the PKCS7 signature. + // + NumberCertsInSignature = sk_X509_num (CertChain); + + if (NumberCertsInSignature == 0) { + // + // Fail to find any certificates in signature. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Get the leaf signer. + // + Status = GetSignerCertificate (Pkcs7, &SignerCert); + if (Status != EFI_SUCCESS || SignerCert == NULL) { + // + // Fail to get the end-entity leaf signer certificate. + // + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + Status = CheckEKUs (SignerCert, RequiredEKUs, RequiredEKUsSize, RequireAllPresent); + if (Status != EFI_SUCCESS) { + goto Exit; + } + +Exit: + + // + // Release Resources + // + // If the signature was not wrapped, then the call to WrapData() will allocate + // the data and add a header to it + // + if (!IsWrapped && SignedData) { + free (SignedData); + } + + if (Pkcs7 != NULL) { + PKCS7_free (Pkcs7); + } + + return Status; +} + diff -Nru shim-15.8/Delivering_Sbat_Revocations.md shim-16.1/Delivering_Sbat_Revocations.md --- shim-15.8/Delivering_Sbat_Revocations.md 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/Delivering_Sbat_Revocations.md 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru shim-15.8/Make.defaults shim-16.1/Make.defaults --- shim-15.8/Make.defaults 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/Make.defaults 1970-01-01 00:00:00.000000000 +0000 @@ -109,7 +109,7 @@ 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 @@ 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 @@ export VERBOSE endif +export DEFINES + # vim:filetype=make diff -Nru shim-15.8/Makefile shim-16.1/Makefile --- shim-15.8/Makefile 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +1,7 @@ default : all NAME = shim -VERSION = 15.8 +VERSION = 16.1 ifneq ($(origin RELEASE),undefined) DASHRELEASE ?= -$(RELEASE) else @@ -38,12 +38,12 @@ 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 verify.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 verify.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 @@ 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.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 @@ 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 @@ 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 @@ -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 @@ -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 @@ 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-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: 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)" @@ -377,3 +400,4 @@ export ARCH CC CROSS_COMPILE LD OBJCOPY EFI_INCLUDE EFI_INCLUDES OPTIMIZATIONS export FEATUREFLAGS WARNFLAGS WERRFLAGS +unexport CFLAGS CPPFLAGS LDFLAGS diff -Nru shim-15.8/MokManager.c shim-16.1/MokManager.c --- shim-15.8/MokManager.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/MokManager.c 1970-01-01 00:00:00.000000000 +0000 @@ -142,17 +142,14 @@ 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 -Nru shim-15.8/MokVars.txt shim-16.1/MokVars.txt --- shim-15.8/MokVars.txt 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/MokVars.txt 1970-01-01 00:00:00.000000000 +0000 @@ -63,28 +63,51 @@ 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 -Nru shim-15.8/README.md shim-16.1/README.md --- shim-15.8/README.md 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -8,16 +8,42 @@ this succeeds and if the binary or signing key are not forbidden then shim will relocate and execute the binary. +## protocols + +### shim lock protocol + shim will also install a protocol which permits the second-stage bootloader to perform similar binary validation. This protocol has a GUID as described in the shim.h header file and provides a single entry point. On 64-bit systems this entry point expects to be called with SysV ABI rather than MSABI, so calls to it should not be wrapped. +### shim loader protocol + +Since version 16.1 shim overrides the system table and installs its own version +of the LoadImage()/StartImage()/UnloadImage()/Exit() functions, so that second +stages can simply call them from the system table, and it will work whether shim +is first stage or not, without requiring shim-specific code in the second stages. + +When this protocol is installed, signed UKIs +[Unified Kernel Images](https://uapi-group.org/specifications/specs/unified_kernel_image/) +can be loaded even if the nested kernel is not signed, as after the UKI is loaded +and validated, shim builds an internal allowlist of all the sections that are +contained in the UKI. When an image is loaded from one such section, it is +validated against denylists (DBX/MOKX/SBAT at the time of writing), but it is +not checked against allowlists (DB/MOK hashes/signatures), as the outer image +was already validated and the inner image is thus covered by those signatures or +hashes. Furthermore, the inner image is not measured in the TPM, to avoid double +measurements. + +## TPM + On systems with a TPM chip enabled and supported by the system firmware, shim will extend various PCRs with the digests of the targets it is loading. A full list is in the file [README.tpm](README.tpm) . +## builds and tests + To use shim, simply place a DER-encoded public certificate in a file such as pub.cer and build with `make VENDOR_CERT_FILE=pub.cer`. @@ -26,6 +52,8 @@ See the [test plan](testplan.txt), and file a ticket if anything fails! +## contacts + In the event that the developers need to be contacted related to a security incident or vulnerability, please mail [secalert@redhat.com]. diff -Nru shim-15.8/README.tpm shim-16.1/README.tpm --- shim-15.8/README.tpm 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/README.tpm 1970-01-01 00:00:00.000000000 +0000 @@ -13,14 +13,20 @@ - 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 -Nru shim-15.8/SBAT.md shim-16.1/SBAT.md --- shim-15.8/SBAT.md 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/SBAT.md 1970-01-01 00:00:00.000000000 +0000 @@ -170,7 +170,7 @@ 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 @@ 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 @@ 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. - -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. +Both generation numbers should only ever go up; they should never be reset. -SBAT revocation data would then require a GRUB with a global generation number -of 4. +#### Example: a vendor forking a global project -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. +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,[...] +``` -Vendor C's GRUB binary can now be used to compromise anyone's system. +After this, the UEFI SBAT revocation variable (named *SbatLevel*) would be +updated to raise the mandatory minimal global generation number for GRUB to 4. -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. +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. Vendor C's GRUB binary can now be abused to compromise their systems. -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. - -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 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. - -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. - -| | prior to
disclosure\* | after
disclosure | after Vendor C's
first update | after Vendor C's
second update | after next global
disclosure | -|--------------------------------------------------------------------------------------|--------------------------|---------------------|----------------------------------|-----------------------------------|---------------------------------| -| GRUB global
generation number in
artifacts .sbat section | 3 | 4 | 4 | 4 | 5 | -| Vendor C's product-specific
generation number in artifact's
.sbat section | 1 | 1 | 2 | 3 | 1 | -| GRUB global
generation number in
UEFI SBAT revocation variable | 3 | 4 | 4 | 4 | 5 | -| Vendor C's product-specific
generation number in
UEFI SBAT revocation variable | not set | not set | 2 | 3 | not set | +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): + +``` +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,[...] +``` + +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 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: + +``` +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,[...] +``` + +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 GRUB
first disclosure\* | after GRUB
first disclosure\* | after Vendor C's
first update | after Vendor C's
second update | after GRUB
second disclosure\* | +|--------------------------------------------------------------------------------------|-------------------------------------|----------------------------------|----------------------------------|-----------------------------------|-----------------------------------| +| GRUB global
generation number in
artifacts .sbat section | 3 | 4 | 4 | 4 | 5 | +| Vendor C's product
generation number in
artifact's .sbat section | 1 | 1 | 2 | 3 | 3 | +| GRUB global
generation number in
UEFI *SbatLevel* variable | 3 | 4 | 4 | 4 | 5 | +| Vendor C's product
generation number in
UEFI *SbatLevel* variable | not set | not set | 2 | 3 | not set | \* A disclosure is the event/date where a CVE and fixes for it are made public. -The product-specific generation number does not reset and continues to -monotonically increase over the course of these non-global events. Continuity of more -specific generation numbers must be maintained in this way in order to satisfy -checks against older revocation data. - -The variable payload will be stored publicly in the shim source base and -identify the global generation associated with a product or version-specific -one. The payload is also built into shim to additionally limit exposure. - -At this time of writing, all version-numbers are set to 1. Presumably at some point, -updated numbers will be published on the respective websites of the associated vendors -and components. +The product-specific generation number does not reset and continues to increase +over the course of these non-global events. Continuity of more specific +generation numbers must be maintained in this way in order to satisfy checks +against older revocation data. + +The 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 -Nru shim-15.8/SbatLevel_Variable.txt shim-16.1/SbatLevel_Variable.txt --- shim-15.8/SbatLevel_Variable.txt 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/SbatLevel_Variable.txt 1970-01-01 00:00:00.000000000 +0000 @@ -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,29 @@ grub,3 grub.debian,4 -Since http boot shim CVE is considerably more serious than then GRUB -ntfs CVEs shim is delivering the shim revocation without the updated -GRUB revocation as a latest payload. -To revoke both the impacted shim and impacted GRUB binaries: +Revocations for: + - January 2024 shim CVEs + - October 2023 grub CVEs + - Debian/Ubuntu (peimage) CVE-2024-2312 -sbat,1,2024 +sbat,1,2024040900 shim,4 grub,4 +grub.peimage,2 + + +Revocations for: + - February 2025 GRUB CVEs + +sbat,1,2025021800 +shim,4 +grub,5 + +Revocations for +- July 2025 Proxmox-specific Grub issue (PSA-2025-00012-1) + +sbat,1,2025051000 +shim,4 +grub,5 +grub.proxmox,2 diff -Nru shim-15.8/commit shim-16.1/commit --- shim-15.8/commit 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/commit 1970-01-01 00:00:00.000000000 +0000 @@ -1 +1 @@ -5914984a1ffeab841f482c791426d7ca9935a5e6 \ No newline at end of file +afc49558b34548644c1cd0ad1b6526a9470182ed \ No newline at end of file diff -Nru shim-15.8/debian/changelog shim-16.1/debian/changelog --- shim-15.8/debian/changelog 2024-05-04 20:28:21.000000000 +0000 +++ shim-16.1/debian/changelog 2026-04-03 16:11:35.000000000 +0000 @@ -1,3 +1,43 @@ +shim (16.1-2~deb12u1) bookworm; urgency=medium + + [ Steve McIntyre ] + * Backport new shim release to bookworm + + Needed so we have a new shim signed with both Microsoft UEFI + Root CAs + * Disable NX for the bookworm build + + We don't have a complete NX boot chain here. + * Also switch to using the default version of gcc in bookworm + + -- Steve McIntyre <93sam@debian.org> Fri, 03 Apr 2026 17:11:35 +0100 + +shim (16.1-2) unstable; urgency=medium + + * Bump SBAT revocation level to 2025021800 aka + "shim,4\ngrub,5\n" + after discussion with shim reviewers team. Let's block the older + GRUB binaries which we know are problematic. + * Switch to using the default version of gcc. Closes: #1132054 + + -- Steve McIntyre <93sam@debian.org> Fri, 03 Apr 2026 12:34:36 +0100 + +shim (16.1-1) unstable; urgency=medium + + * New upstream release: 16.1 + * Switch to gcc-14 + * Drop old patches, no longer needed + + 0001-sbat-Add-grub.peimage-2-to-latest-CVE-2024-2312.patch + + 0002-sbat-Also-bump-latest-for-grub-4-and-to-todays-date.patch + * Add new patch from upstream: + + 0001-Fix-build-with-binutils-2.46.patch. Closes: #1125741 + * Add lintian overrides: + + Ignore included binaries for unit tests + * Bump SBAT revocation level to 2024040900 aka + "shim,4\ngrub,4\ngrub.peimage,2\n" + * Enable NX for the sid/forky build + + We should have a complete NX boot chain now... + + -- Steve McIntyre <93sam@debian.org> Mon, 23 Mar 2026 20:19:01 +0000 + shim (15.8-1~deb12u1) bookworm; urgency=medium [ Steve McIntyre ] diff -Nru shim-15.8/debian/control shim-16.1/debian/control --- shim-15.8/debian/control 2024-05-04 20:28:21.000000000 +0000 +++ shim-16.1/debian/control 2026-04-03 16:11:35.000000000 +0000 @@ -2,14 +2,13 @@ Section: admin Priority: optional Maintainer: Debian EFI team -Uploaders: Steve Langasek , Steve McIntyre <93sam@debian.org> +Uploaders: Steve McIntyre <93sam@debian.org> Standards-Version: 4.6.2 Build-Depends: debhelper-compat (= 12), gnu-efi (>= 3.0u), sbsigntool, openssl, libelf-dev, - gcc-12, dos2unix, pesign (>= 0.112-5), efivar, diff -Nru shim-15.8/debian/copyright shim-16.1/debian/copyright --- shim-15.8/debian/copyright 2024-04-17 20:37:29.000000000 +0000 +++ shim-16.1/debian/copyright 2026-03-23 21:17:29.000000000 +0000 @@ -30,12 +30,12 @@ Copyright: 2015 SUSE LINUX GmbH License: BSD-2-Clause -Files: include/Http.h +Files: include/http.h Copyright: 2016 Intel Corporation 2015 Hewlett Packard Enterprise Development LP License: BSD-2-Clause -Files: include/PeImage.h +Files: include/peimage.h Copyright: 2006-2010 Intel Corporation 2008-2009 Apple Inc License: BSD-2-Clause @@ -250,10 +250,6 @@ Copyright: 2014 by John Cronin License: Expat -Files: gnu-efi/inc/protocol/efidbg.h gnu-efi/inc/protocol/ia64/eficontext.h -Copyright: 1999-2007 Intel Corp. -License: BSD-4-clause-Intel - Files: gnu-efi/inc/aarch64/efibind.h gnu-efi/inc/arm/efibind.h gnu-efi/gnuefi/crt0-efi-arm.S @@ -327,40 +323,6 @@ ON THIS WEB SITE ARE PROVIDED "AS IS" WITH NO WARRANTIES, AND ARE SUBJECT TO CHANGE WITHOUT NOTICE. -License: BSD-4-clause-Intel - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - . - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - . - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - . - 3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - . - This product includes software developed by Intel Corporation and - its contributors. - . - 4. Neither the name of Intel Corporation or its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - . - THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION AND CONTRIBUTORS ``AS IS'' - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. - License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -Nru shim-15.8/debian/patches/0001-sbat-Add-grub.peimage-2-to-latest-CVE-2024-2312.patch shim-16.1/debian/patches/0001-sbat-Add-grub.peimage-2-to-latest-CVE-2024-2312.patch --- shim-15.8/debian/patches/0001-sbat-Add-grub.peimage-2-to-latest-CVE-2024-2312.patch 2024-05-04 20:28:21.000000000 +0000 +++ shim-16.1/debian/patches/0001-sbat-Add-grub.peimage-2-to-latest-CVE-2024-2312.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -From 63edf92f8ae11b884bc7d24aecb8229cbc4ae014 Mon Sep 17 00:00:00 2001 -From: Julian Andres Klode -Date: Fri, 5 Apr 2024 21:57:07 +0200 -Subject: [PATCH 1/2] sbat: Add grub.peimage,2 to latest (CVE-2024-2312) - -Add the previous latest level to the switch for automatic. - -Signed-off-by: Julian Andres Klode ---- - include/sbat_var_defs.h | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h -index f8cba029..04d708f2 100644 ---- a/include/sbat_var_defs.h -+++ b/include/sbat_var_defs.h -@@ -47,6 +47,8 @@ - #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" -+#elif SBAT_AUTOMATIC_DATE == 2024010900 -+#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\n" - #else - #error "Unknown SBAT_AUTOMATIC_DATE" - #endif /* SBAT_AUTOMATIC_DATE == */ -@@ -56,10 +58,10 @@ - SBAT_VAR_AUTOMATIC_REVOCATIONS - - /* -- * Revocations for January 2024 shim CVEs -+ * Revocations for January 2024 shim CVEs + Debian/Ubuntu (peimage) CVE-2024-2312 - */ --#define SBAT_VAR_LATEST_DATE "2024010900" --#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\n" -+#define SBAT_VAR_LATEST_DATE "2024040500" -+#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\ngrub.peimage,2\n" - #define SBAT_VAR_LATEST \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ - SBAT_VAR_LATEST_REVOCATIONS --- -2.39.2 - diff -Nru shim-15.8/debian/patches/0002-sbat-Also-bump-latest-for-grub-4-and-to-todays-date.patch shim-16.1/debian/patches/0002-sbat-Also-bump-latest-for-grub-4-and-to-todays-date.patch --- shim-15.8/debian/patches/0002-sbat-Also-bump-latest-for-grub-4-and-to-todays-date.patch 2024-05-04 20:28:21.000000000 +0000 +++ shim-16.1/debian/patches/0002-sbat-Also-bump-latest-for-grub-4-and-to-todays-date.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -From 3e1394e8e6fd0071a69196230f991612a960c154 Mon Sep 17 00:00:00 2001 -From: Julian Andres Klode -Date: Tue, 9 Apr 2024 18:55:12 +0200 -Subject: [PATCH 2/2] sbat: Also bump latest for grub,4 (and to todays date) - -Back in January we decided to bump the SBAT level for the shim -CVE without bumping the grub level for the previous NTFS issues -- CVE-2023-4692 CVE-2023-4693 - as not every vendor was signing -the ntfs module. - -Catch up on this revocation to ensure it doesn't get lost. Doing -so also allows us to remove the grub.debian,4 revocation as this -happened before grub,4 and hence is obsolete. - -Also bump the date of the sbat variable to today's. Don't copy -the April 5 one to a previous selection, as it wasn't shipped -to anyone. - -Signed-off-by: Julian Andres Klode ---- - include/sbat_var_defs.h | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h -index 04d708f2..5c7115b9 100644 ---- a/include/sbat_var_defs.h -+++ b/include/sbat_var_defs.h -@@ -58,10 +58,13 @@ - SBAT_VAR_AUTOMATIC_REVOCATIONS - - /* -- * Revocations for January 2024 shim CVEs + Debian/Ubuntu (peimage) CVE-2024-2312 -+ * Revocations for: -+ * - January 2024 shim CVEs -+ * - October 2023 grub CVEs -+ * - Debian/Ubuntu (peimage) CVE-2024-2312 - */ --#define SBAT_VAR_LATEST_DATE "2024040500" --#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\ngrub.peimage,2\n" -+#define SBAT_VAR_LATEST_DATE "2024040900" -+#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,4\ngrub.peimage,2\n" - #define SBAT_VAR_LATEST \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ - SBAT_VAR_LATEST_REVOCATIONS --- -2.39.2 - diff -Nru shim-15.8/debian/patches/series shim-16.1/debian/patches/series --- shim-15.8/debian/patches/series 2024-05-04 20:28:21.000000000 +0000 +++ shim-16.1/debian/patches/series 2026-04-03 16:11:35.000000000 +0000 @@ -1,2 +0,0 @@ -0001-sbat-Add-grub.peimage-2-to-latest-CVE-2024-2312.patch -0002-sbat-Also-bump-latest-for-grub-4-and-to-todays-date.patch diff -Nru shim-15.8/debian/rules shim-16.1/debian/rules --- shim-15.8/debian/rules 2024-05-04 20:28:21.000000000 +0000 +++ shim-16.1/debian/rules 2026-04-03 16:11:35.000000000 +0000 @@ -38,20 +38,23 @@ COMMON_OPTIONS += \ RELEASE=$(plain_upstream_version) \ - COMMIT_ID=657b2483ca6e9fcf2ad8ac7ee577ff546d24c3aa \ + COMMIT_ID=afc49558b34548644c1cd0ad1b6526a9470182ed \ MAKELEVEL=0 \ ENABLE_HTTPBOOT=true \ VENDOR_CERT_FILE=$(cert) \ VENDOR_DBX_FILE=$(DBX_LIST) \ EFIDIR=$(distributor) \ CROSS_COMPILE=$(DEB_HOST_GNU_TYPE)- \ - CC=$(DEB_HOST_GNU_TYPE)-gcc-12 \ + CC=gcc \ $(NULL) # Force shim to use the latest revocations by default to block some # older grub / peimage issues. This is: # "shim,4\ngrub,4\ngrub.peimage,2\n" -COMMON_OPTIONS += SBAT_AUTOMATIC_DATE=2024010900 +COMMON_OPTIONS += SBAT_AUTOMATIC_DATE=2025021800 + +# Disable NX for this build +COMMON_OPTIONS += "POST_PROCESS_PE_FLAGS=-N" $(DBX_LIST): $(DBX_HASHES) ./debian/generate_dbx_list $(EFI_ARCH) $< $@ diff -Nru shim-15.8/debian/source/lintian-overrides shim-16.1/debian/source/lintian-overrides --- shim-15.8/debian/source/lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/debian/source/lintian-overrides 2026-03-23 21:09:18.000000000 +0000 @@ -0,0 +1,3 @@ +shim: source-contains-prebuilt-windows-binary [test-data/grubx64.0.76.el7.1.efi] +shim: source-contains-prebuilt-windows-binary [test-data/grubx64.0.76.el7.efi] +shim: source-contains-prebuilt-windows-binary [test-data/grubx64.0.80.el7.efi] diff -Nru shim-15.8/dp.c shim-16.1/dp.c --- shim-15.8/dp.c 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/dp.c 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * dp.c - device path helpers + * Copyright Peter Jones + */ + +#include "shim.h" + +int +is_removable_media_path(EFI_LOADED_IMAGE *li) +{ + unsigned int pathlen = 0; + CHAR16 *bootpath = NULL; + int ret = 0; + + bootpath = DevicePathToStr(li->FilePath); + + /* Check the beginning of the string and the end, to avoid + * caring about which arch this is. */ + /* I really don't know why, but sometimes bootpath gives us + * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here... + */ + if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && + StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15) && + StrnCaseCmp(bootpath, L"EFI\\BOOT\\BOOT", 13) && + StrnCaseCmp(bootpath, L"EFI\\BOOT\\/BOOT", 14)) + goto error; + + pathlen = StrLen(bootpath); + if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) + goto error; + + ret = 1; + +error: + if (bootpath) + FreePool(bootpath); + + return ret; +} + + +// vim:fenc=utf-8:tw=75:noet diff -Nru shim-15.8/errlog.c shim-16.1/errlog.c --- shim-15.8/errlog.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/errlog.c 1970-01-01 00:00:00.000000000 +0000 @@ -99,4 +99,185 @@ 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 -Nru shim-15.8/fallback.c shim-16.1/fallback.c --- shim-15.8/fallback.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/fallback.c 1970-01-01 00:00:00.000000000 +0000 @@ -94,89 +94,6 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ } 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 @@ } 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 @@ /* 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ { 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 @@ 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 @@ } 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 -Nru shim-15.8/fuzz-pe-relocate.c shim-16.1/fuzz-pe-relocate.c --- shim-15.8/fuzz-pe-relocate.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/fuzz-pe-relocate.c 1970-01-01 00:00:00.000000000 +0000 @@ -28,7 +28,7 @@ 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 -Nru shim-15.8/generate_sbat_var_defs.c shim-16.1/generate_sbat_var_defs.c --- shim-15.8/generate_sbat_var_defs.c 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/generate_sbat_var_defs.c 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +/* + * This generates the header files that produce the actual revocation + * string payload. On the one hand this grabs the defintions from the + * human readable SbatLevel_Variable.txt file which is nice. On the other + * hand it's one off c code. + */ + +#include +#include +#include + +typedef struct sbat_revocation sbat_revocation; + +struct sbat_revocation { + int date; + char *revocations; + sbat_revocation *next; +}; + +static sbat_revocation *revlisthead; + +int +readfile(char *SbatLevel_Variable) +{ + FILE *varfilep; + char line[1024]; + int date; + int ret = -1; + + unsigned int revocationsp = 0; + + sbat_revocation *revlistlast = NULL; + sbat_revocation *revlistentry = NULL; + + revlisthead = NULL; + + varfilep = fopen(SbatLevel_Variable, "r"); + if (varfilep == NULL) + return -1; + + while (fgets(line, sizeof(line), varfilep) != NULL) { + if (sscanf(line, "sbat,1,%d\n", &date) && strlen(line) == 18) { + revlistentry = calloc(1, sizeof(sbat_revocation)); + if (revlistentry == NULL) + goto err; + if (revlisthead == NULL) + revlisthead = revlistentry; + else + revlistlast->next = revlistentry; + + revlistlast = revlistentry; + + revlistentry->date = date; + while (line[0] != '\n' && + fgets(line, sizeof(line), varfilep) != NULL) { + char *new = NULL; + new = realloc(revlistentry->revocations, + revocationsp + strlen(line) + 2); + 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 -Nru shim-15.8/globals.c shim-16.1/globals.c --- shim-15.8/globals.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/globals.c 1970-01-01 00:00:00.000000000 +0000 @@ -24,13 +24,18 @@ * 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 -Nru shim-15.8/gnu-efi/inc/efiapi.h shim-16.1/gnu-efi/inc/efiapi.h --- shim-15.8/gnu-efi/inc/efiapi.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/gnu-efi/inc/efiapi.h 1970-01-01 00:00:00.000000000 +0000 @@ -971,5 +971,75 @@ } 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 -Nru shim-15.8/gnu-efi/inc/efierr.h shim-16.1/gnu-efi/inc/efierr.h --- shim-15.8/gnu-efi/inc/efierr.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/gnu-efi/inc/efierr.h 1970-01-01 00:00:00.000000000 +0000 @@ -57,12 +57,17 @@ #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 -Nru shim-15.8/gnu-efi/inc/efilib.h shim-16.1/gnu-efi/inc/efilib.h --- shim-15.8/gnu-efi/inc/efilib.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/gnu-efi/inc/efilib.h 1970-01-01 00:00:00.000000000 +0000 @@ -79,10 +79,16 @@ #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; @@ -590,6 +596,14 @@ __builtin_ms_va_list args ); +UINTN EFIAPI +AsciiSPrint ( + OUT CHAR8 *Str, + IN UINTN StrSize, + IN CONST CHAR8 *fmt, + ... + ); + VOID ValueToHex ( IN CHAR16 *Buffer, diff -Nru shim-15.8/gnu-efi/inc/efiprot.h shim-16.1/gnu-efi/inc/efiprot.h --- shim-15.8/gnu-efi/inc/efiprot.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/gnu-efi/inc/efiprot.h 1970-01-01 00:00:00.000000000 +0000 @@ -554,6 +554,69 @@ 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 -Nru shim-15.8/gnu-efi/lib/data.c shim-16.1/gnu-efi/lib/data.c --- shim-15.8/gnu-efi/lib/data.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/gnu-efi/lib/data.c 1970-01-01 00:00:00.000000000 +0000 @@ -102,8 +102,11 @@ 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 -Nru shim-15.8/gnu-efi/lib/error.c shim-16.1/gnu-efi/lib/error.c --- shim-15.8/gnu-efi/lib/error.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/gnu-efi/lib/error.c 1970-01-01 00:00:00.000000000 +0000 @@ -54,12 +54,17 @@ { 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 -Nru shim-15.8/gnu-efi/lib/print.c shim-16.1/gnu-efi/lib/print.c --- shim-15.8/gnu-efi/lib/print.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/gnu-efi/lib/print.c 1970-01-01 00:00:00.000000000 +0000 @@ -899,6 +899,43 @@ return Len; } +UINTN EFIAPI +AsciiSPrint ( + OUT CHAR8 *Str, + IN UINTN StrSize, + IN CONST CHAR8 *fmt, + ... + ) +/*++ + +Routine Description: + + Prints a formatted unicode string to a buffer + +Arguments: + + Str - Output buffer to print the formatted string into + + StrSize - Size of Str. String is truncated to this size. + A size of 0 means there is no limit + + fmt - The format string + +Returns: + + String length returned in buffer + +--*/ +{ + ms_va_list args; + UINTN len; + + ms_va_start (args, fmt); + len = AsciiVSPrint(Str, StrSize, fmt, args); + ms_va_end (args); + + return len; +} STATIC VOID diff -Nru shim-15.8/httpboot.c shim-16.1/httpboot.c --- shim-15.8/httpboot.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/httpboot.c 1970-01-01 00:00:00.000000000 +0000 @@ -55,6 +55,51 @@ 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; @@ -521,6 +566,8 @@ CHAR8 rx_buffer[9216]; EFI_STATUS efi_status; EFI_STATUS event_status; + UINT64 new_buf_size; + BOOLEAN buf_size_set = false; /* Initialize the rx message and buffer */ response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS; @@ -565,7 +612,7 @@ 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; } @@ -573,7 +620,13 @@ for (i = 0; i < rx_message.HeaderCount; i++) { if (!strcasecmp(rx_message.Headers[i].FieldName, (CHAR8 *)"Content-Length")) { - *buf_size = ascii_to_int(rx_message.Headers[i].FieldValue); + new_buf_size = ascii_to_int(rx_message.Headers[i].FieldValue); + if (buf_size_set && new_buf_size != *buf_size) { + perror(L"Content-Length is invalid\n"); + goto error; + } + *buf_size = new_buf_size; + buf_size_set = true; } } diff -Nru shim-15.8/include/compiler.h shim-16.1/include/compiler.h --- shim-15.8/include/compiler.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/compiler.h 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru shim-15.8/include/console.h shim-16.1/include/console.h --- shim-15.8/include/console.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/console.h 1970-01-01 00:00:00.000000000 +0000 @@ -98,6 +98,7 @@ #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 -Nru shim-15.8/include/dp.h shim-16.1/include/dp.h --- shim-15.8/include/dp.h 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/include/dp.h 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * dp.h - device path helper functions + * Copyright Peter Jones + */ + +#ifndef DP_H_ +#define DP_H_ + +int +is_removable_media_path(EFI_LOADED_IMAGE *li); + +#endif /* !DP_H_ */ +// vim:fenc=utf-8:tw=75:noet diff -Nru shim-15.8/include/errlog.h shim-16.1/include/errlog.h --- shim-15.8/include/errlog.h 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/include/errlog.h 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * errlog.h - error logging utilities + * Copyright Peter Jones + */ + +#ifndef ERRLOG_H_ +#define ERRLOG_H_ + +extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, + const CHAR16 *fmt, ...); +extern EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func, + const CHAR16 *fmt, ms_va_list args); +extern VOID LogHexdump_(const char *file, int line, const char *func, + const void *data, size_t sz); +extern VOID PrintErrors(VOID); +extern VOID ClearErrors(VOID); +extern void save_logs(void); +extern UINTN EFIAPI log_debug_print(const CHAR16 *fmt, ...); + +#endif /* !ERRLOG_H_ */ +// vim:fenc=utf-8:tw=75:noet diff -Nru shim-15.8/include/errors.h shim-16.1/include/errors.h --- shim-15.8/include/errors.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/errors.h 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru shim-15.8/include/fanalyzer.mk shim-16.1/include/fanalyzer.mk --- shim-15.8/include/fanalyzer.mk 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/fanalyzer.mk 1970-01-01 00:00:00.000000000 +0000 @@ -21,7 +21,7 @@ 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 -Nru shim-15.8/include/fuzz.mk shim-16.1/include/fuzz.mk --- shim-15.8/include/fuzz.mk 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/fuzz.mk 1970-01-01 00:00:00.000000000 +0000 @@ -5,7 +5,7 @@ .SUFFIXES: -include Make.defaults +include $(TOPDIR)/Make.defaults CC = clang VALGRIND ?= diff -Nru shim-15.8/include/guid.h shim-16.1/include/guid.h --- shim-15.8/include/guid.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/guid.h 1970-01-01 00:00:00.000000000 +0000 @@ -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 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 -Nru shim-15.8/include/hexdump.h shim-16.1/include/hexdump.h --- shim-15.8/include/hexdump.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/hexdump.h 1970-01-01 00:00:00.000000000 +0000 @@ -89,10 +89,14 @@ 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 -Nru shim-15.8/include/load-options.h shim-16.1/include/load-options.h --- shim-15.8/include/load-options.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/load-options.h 1970-01-01 00:00:00.000000000 +0000 @@ -13,6 +13,7 @@ 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 -Nru shim-15.8/include/loader-proto.h shim-16.1/include/loader-proto.h --- shim-15.8/include/loader-proto.h 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/include/loader-proto.h 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +/* + * Copyright Red Hat, Inc + * Copyright Peter Jones + */ +#ifndef SHIM_REPLACEMENTS_H +#define SHIM_REPLACEMENTS_H + +extern EFI_SYSTEM_TABLE *get_active_systab(void); + +typedef enum { + VERIFIED_BY_NOTHING, + VERIFIED_BY_CERT, + VERIFIED_BY_HASH +} verification_method_t; + +extern verification_method_t verification_method; + +extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab); +extern void unhook_system_services(void); + +extern void hook_exit(EFI_SYSTEM_TABLE *local_systab); +extern void unhook_exit(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 -Nru shim-15.8/include/memattrs.h shim-16.1/include/memattrs.h --- shim-15.8/include/memattrs.h 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/include/memattrs.h 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * memattrs.h - EFI and DXE memory attribute helpers + * Copyright Peter Jones + */ + +#ifndef SHIM_MEMATTRS_H_ +#define SHIM_MEMATTRS_H_ + +extern EFI_STATUS get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs); +extern EFI_STATUS update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs); + +extern void get_hsi_mem_info(void); +extern char *decode_hsi_bits(UINTN hsi); +extern void set_shim_nx_policy(void); + +#endif /* !SHIM_MEMATTRS_H_ */ +// vim:fenc=utf-8:tw=75:noet diff -Nru shim-15.8/include/mock-variables.h shim-16.1/include/mock-variables.h --- shim-15.8/include/mock-variables.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/mock-variables.h 1970-01-01 00:00:00.000000000 +0000 @@ -115,6 +115,9 @@ 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 -Nru shim-15.8/include/mok.h shim-16.1/include/mok.h --- shim-15.8/include/mok.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/mok.h 1970-01-01 00:00:00.000000000 +0000 @@ -17,6 +17,14 @@ 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 @@ * 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 @@ * 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 @@ 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 -Nru shim-15.8/include/netboot.h shim-16.1/include/netboot.h --- shim-15.8/include/netboot.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/netboot.h 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru shim-15.8/include/pe.h shim-16.1/include/pe.h --- shim-15.8/include/pe.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/pe.h 1970-01-01 00:00:00.000000000 +0000 @@ -12,7 +12,8 @@ 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, @@ -37,10 +38,17 @@ EFI_STATUS handle_image (void *data, unsigned int datasize, - EFI_LOADED_IMAGE *li, + EFI_LOADED_IMAGE *li, EFI_HANDLE image_handle, EFI_IMAGE_ENTRY_POINT *entry_point, EFI_PHYSICAL_ADDRESS *alloc_address, - UINTN *alloc_pages); + UINTN *alloc_pages, unsigned int *alloc_alignment, + bool parent_verified); + +EFI_STATUS +validate_cached_section(EFI_HANDLE parent_image_handle, + void *addr, UINTN size); +void +flush_cached_sections(EFI_HANDLE parent_image_handle); EFI_STATUS generate_hash (char *data, unsigned int datasize, @@ -52,5 +60,8 @@ 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 -Nru shim-15.8/include/peimage.h shim-16.1/include/peimage.h --- shim-15.8/include/peimage.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/peimage.h 1970-01-01 00:00:00.000000000 +0000 @@ -144,12 +144,12 @@ /// /// @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 @@ /// /// @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 @@ #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 @@ #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 @@ 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 -Nru shim-15.8/include/replacements.h shim-16.1/include/replacements.h --- shim-15.8/include/replacements.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/replacements.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause-Patent - -/* - * Copyright Red Hat, Inc - * Copyright Peter Jones - */ -#ifndef SHIM_REPLACEMENTS_H -#define SHIM_REPLACEMENTS_H - -extern EFI_SYSTEM_TABLE *get_active_systab(void); - -typedef enum { - VERIFIED_BY_NOTHING, - VERIFIED_BY_CERT, - VERIFIED_BY_HASH -} 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); - -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); - -#endif /* SHIM_REPLACEMENTS_H */ diff -Nru shim-15.8/include/sbat.h shim-16.1/include/sbat.h --- shim-15.8/include/sbat.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/sbat.h 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru shim-15.8/include/sbat_var_defs.h shim-16.1/include/sbat_var_defs.h --- shim-15.8/include/sbat_var_defs.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/sbat_var_defs.h 1970-01-01 00:00:00.000000000 +0000 @@ -7,7 +7,9 @@ #define QUOTE(s) #s /* - * This is the entry for the sbat data format + * SbatLevel Epoch and SHIM_DEVEL definitions are here + * Actual revocations are now soley defined in + * SbatLevel_Variable.txt */ #define SBAT_VAR_SIG "sbat," #define SBAT_VAR_VERSION "1," @@ -22,46 +24,10 @@ #define SBAT_VAR_LATEST_DATE "2022050100" #define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" -#define SBAT_VAR_LATEST \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ - SBAT_VAR_LATEST_REVOCATIONS -#else /* !ENABLE_SHIM_DEVEL */ - -/* - * Some distros may want to apply revocations from 2022052400 - * or 2022111500 automatically. They can be selected by setting - * SBAT_AUTOMATIC_DATE= at build time. Otherwise the - * default is to apply the second to most recent revocations - * automatically. Distros that need to manage automatic updates - * externally from shim can choose the epoch 2021030218 emtpy - * revocations. - */ -#ifndef SBAT_AUTOMATIC_DATE -#define SBAT_AUTOMATIC_DATE 2023012900 -#endif /* SBAT_AUTOMATIC_DATE */ -#if SBAT_AUTOMATIC_DATE == 2021030218 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS -#elif SBAT_AUTOMATIC_DATE == 2022052400 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "grub,2\n" -#elif SBAT_AUTOMATIC_DATE == 2022111500 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\n" -#elif SBAT_AUTOMATIC_DATE == 2023012900 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\ngrub.debian,4\n" -#else -#error "Unknown SBAT_AUTOMATIC_DATE" -#endif /* SBAT_AUTOMATIC_DATE == */ -#define SBAT_VAR_AUTOMATIC_DATE QUOTEVAL(SBAT_AUTOMATIC_DATE) -#define SBAT_VAR_AUTOMATIC \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE "\n" \ - SBAT_VAR_AUTOMATIC_REVOCATIONS -/* - * Revocations for January 2024 shim CVEs - */ -#define SBAT_VAR_LATEST_DATE "2024010900" -#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\n" #define SBAT_VAR_LATEST \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS #endif /* ENABLE_SHIM_DEVEL */ + #endif /* !SBAT_VAR_DEFS_H_ */ diff -Nru shim-15.8/include/test-data-efivars-1.h shim-16.1/include/test-data-efivars-1.h --- shim-15.8/include/test-data-efivars-1.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/test-data-efivars-1.h 1970-01-01 00:00:00.000000000 +0000 @@ -106,5 +106,16 @@ 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 -Nru shim-15.8/include/test.h shim-16.1/include/test.h --- shim-15.8/include/test.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/test.h 1970-01-01 00:00:00.000000000 +0000 @@ -85,14 +85,14 @@ 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 @@ } 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 @@ } 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 @@ * 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 @@ } 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 @@ 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 @@ } } -#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 @@ 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 -Nru shim-15.8/include/test.mk shim-16.1/include/test.mk --- shim-15.8/include/test.mk 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/include/test.mk 1970-01-01 00:00:00.000000000 +0000 @@ -5,7 +5,7 @@ .SUFFIXES: -include Make.defaults +include $(TOPDIR)/Make.defaults CC = gcc VALGRIND ?= @@ -76,8 +76,12 @@ 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 : $(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 @@ all : test-clean test .PHONY: $(tests) all test clean -.SECONDARY: random.bin # vim:ft=make diff -Nru shim-15.8/include/utils.h shim-16.1/include/utils.h --- shim-15.8/include/utils.h 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/include/utils.h 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru shim-15.8/include/verify.h shim-16.1/include/verify.h --- shim-15.8/include/verify.h 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/include/verify.h 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * verify.h - verification routines for UEFI Secure Boot + * Copyright Peter Jones + * Copyright Matthew Garrett + * + * Significant portions of this code are derived from Tianocore + * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel + * Corporation. + */ +#pragma once + +EFI_STATUS +verify_buffer (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash, + bool parent_verified); + +void +init_openssl(void); + +/* + * Protocol v1 entry points. + */ + +/* + * If secure boot is enabled, verify that the provided buffer is signed + * with a trusted key. + */ +EFI_STATUS +shim_verify(void *buffer, UINT32 size); + +EFI_STATUS +shim_hash(char *data, int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash); + +EFI_STATUS +shim_read_header(void *data, unsigned int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context); + +// vim:fenc=utf-8:tw=75:noet diff -Nru shim-15.8/lib/console.c shim-16.1/lib/console.c --- shim-15.8/lib/console.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/lib/console.c 1970-01-01 00:00:00.000000000 +0000 @@ -651,6 +651,7 @@ { 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 -Nru shim-15.8/lib/guid.c shim-16.1/lib/guid.c --- shim-15.8/lib/guid.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/lib/guid.c 1970-01-01 00:00:00.000000000 +0000 @@ -35,5 +35,7 @@ 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 -Nru shim-15.8/lib/simple_file.c shim-16.1/lib/simple_file.c --- shim-15.8/lib/simple_file.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/lib/simple_file.c 1970-01-01 00:00:00.000000000 +0000 @@ -170,7 +170,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ *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 -Nru shim-15.8/lib/variables.c shim-16.1/lib/variables.c --- shim-15.8/lib/variables.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/lib/variables.c 1970-01-01 00:00:00.000000000 +0000 @@ -226,6 +226,8 @@ } 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 -Nru shim-15.8/load-options.c shim-16.1/load-options.c --- shim-15.8/load-options.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/load-options.c 1970-01-01 00:00:00.000000000 +0000 @@ -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 @@ */ 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 @@ 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 @@ } } + /* + * 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 -Nru shim-15.8/loader-proto.c shim-16.1/loader-proto.c --- shim-15.8/loader-proto.c 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/loader-proto.c 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,528 @@ +// 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; + bool allocated_buffer; +} buffer_properties_t; + +static EFI_STATUS +try_load_from_cached_section(EFI_HANDLE parent_image_handle, + EFI_DEVICE_PATH *dp, buffer_properties_t *bprop) +{ + EFI_STATUS status; + + status = validate_cached_section(parent_image_handle, + bprop->buffer, bprop->size); + if (EFI_ERROR(status)) + return status; + + bprop->allocated_buffer = false; + bprop->dp = dp; + + return EFI_SUCCESS; +} + +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; + } + bprop->allocated_buffer = true; + + /* 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; + } + bprop->allocated_buffer = true; + + /* 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 void +free_pages_alloc_image(SHIM_LOADED_IMAGE *image) +{ + char *buffer; + + if (!image || !image->alloc_address || !image->alloc_pages) + return; + + /* EDKII overwrites memory pages with a fixed pattern in at least + * production Debian/Ubuntu builds as of 2025.02. If the W- X+ bits + * are set on a loaded image, this will cause a page fault when it + * is freed. Ensure W+ X- are set instead before freeing. */ + + buffer = (void *)ALIGN_VALUE((unsigned long)image->alloc_address, image->alloc_alignment); + update_mem_attrs((uintptr_t)buffer, image->alloc_pages * PAGE_SIZE, MEM_ATTR_R|MEM_ATTR_W, + MEM_ATTR_X); + + BS->FreePages(image->alloc_address, image->alloc_pages); + image->alloc_address = 0; + image->alloc_pages = 0; +} + +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 }; + bool parent_verified = false; + + if (BootPolicy) + return EFI_UNSUPPORTED; + + /* + * First we're checking if it's cached - since that is quite fast + * unless we have a match, and any match is okay, we don't need to + * do anything more complicated. + */ + if (SourceSize && SourceBuffer) { + bprop.buffer = SourceBuffer; + bprop.size = SourceSize; + efi_status = try_load_from_cached_section(ParentImageHandle, + DevicePath, &bprop); + if (!EFI_ERROR(efi_status)) { + parent_verified = true; + } else if (efi_status != EFI_NOT_FOUND) { + return EFI_LOAD_ERROR; + } + } + + /* + * If we don't already have a cached, verified copy of the object + * being loaded, go about it the old way. + */ + if (!parent_verified) { + 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; + } + } + + *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_image; + + in_protocol = 1; + efi_status = handle_image(SourceBuffer, SourceSize, &image->li, + *ImageHandle, &image->entry_point, + &image->alloc_address, &image->alloc_pages, + &image->alloc_alignment, parent_verified); + in_protocol = 0; + if (EFI_ERROR(efi_status)) + goto free_alloc; + + if (bprop.buffer && bprop.allocated_buffer) + FreePool(bprop.buffer); + + return EFI_SUCCESS; + +free_alloc: + BS->UninstallMultipleProtocolInterfaces(ImageHandle, + &SHIM_LOADED_IMAGE_GUID, image, + &EFI_LOADED_IMAGE_GUID, &image->li, + &gEfiLoadedImageDevicePathProtocolGuid, + image->loaded_image_device_path, + NULL); + *ImageHandle = NULL; + free_pages_alloc_image(image); +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 && bprop.allocated_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; + } + + flush_cached_sections(ImageHandle); + + // + // We only support EFI applications, so we can unload and free the + // image unconditionally. + // + BS->UninstallMultipleProtocolInterfaces(ImageHandle, + &SHIM_LOADED_IMAGE_GUID, image, + &EFI_LOADED_IMAGE_GUID, &image->li, + &gEfiLoadedImageDevicePathProtocolGuid, + image->loaded_image_device_path, + NULL); + + free_pages_alloc_image(image); + 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); + else if (efi_status != EFI_SUCCESS) + return efi_status; + + flush_cached_sections(ImageHandle); + free_pages_alloc_image(image); + 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_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 -Nru shim-15.8/make-archive shim-16.1/make-archive --- shim-15.8/make-archive 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/make-archive 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +1,7 @@ #!/bin/sh -set -e +set -eu +set -o pipefail +set -x usage() { status="${1}" @@ -78,10 +80,11 @@ 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 -Nru shim-15.8/make-certs shim-16.1/make-certs --- shim-15.8/make-certs 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/make-certs 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru shim-15.8/memattrs.c shim-16.1/memattrs.c --- shim-15.8/memattrs.c 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/memattrs.c 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * memattrs.c - EFI and DXE memory attribute helpers + * Copyright Peter Jones + */ + +#include "shim.h" + +static inline uint64_t +shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs) +{ + uint64_t ret = EFI_MEMORY_RP | + EFI_MEMORY_RO | + EFI_MEMORY_XP; + + if (attrs & MEM_ATTR_R) + ret &= ~EFI_MEMORY_RP; + + if (attrs & MEM_ATTR_W) + ret &= ~EFI_MEMORY_RO; + + if (attrs & MEM_ATTR_X) + ret &= ~EFI_MEMORY_XP; + + return ret; +} + +static inline uint64_t +uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs) +{ + uint64_t ret = MEM_ATTR_R | + MEM_ATTR_W | + MEM_ATTR_X; + + if (attrs & EFI_MEMORY_RP) + ret &= ~MEM_ATTR_R; + + if (attrs & EFI_MEMORY_RO) + ret &= ~MEM_ATTR_W; + + if (attrs & EFI_MEMORY_XP) + ret &= ~MEM_ATTR_X; + + return ret; +} + +static void +get_dxe_services_table(EFI_DXE_SERVICES_TABLE **dstp) +{ + static EFI_DXE_SERVICES_TABLE *dst = NULL; + + if (dst == NULL) { + dprint(L"Looking for configuration table " LGUID_FMT L"\n", GUID_ARGS(gEfiDxeServicesTableGuid)); + + for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; + dprint(L"Testing configuration table " LGUID_FMT L"\n", GUID_ARGS(ct->VendorGuid)); + if (CompareMem(&ct->VendorGuid, &gEfiDxeServicesTableGuid, sizeof(EFI_GUID)) != 0) + continue; + + dst = (EFI_DXE_SERVICES_TABLE *)ct->VendorTable; + dprint(L"Looking for DXE Services Signature 0x%16llx, found signature 0x%16llx\n", + EFI_DXE_SERVICES_TABLE_SIGNATURE, dst->Hdr.Signature); + if (dst->Hdr.Signature != EFI_DXE_SERVICES_TABLE_SIGNATURE) + continue; + + if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) { + /* + * purposefully not treating this as an error so that HSIStatus + * can tell us about it later. + */ + dprint(L"DXE Services lacks Get/SetMemorySpace* functions\n"); + } + + dprint(L"Setting dxe_services_table to 0x%llx\n", dst); + *dstp = dst; + return; + } + } else { + *dstp = dst; + return; + } + + dst = NULL; + dprint(L"Couldn't find DXE services\n"); +} + +static EFI_STATUS +dxe_get_mem_attrs(uintptr_t physaddr, size_t size, uint64_t *attrs) +{ + EFI_STATUS status; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc; + EFI_PHYSICAL_ADDRESS start, end, next; + EFI_DXE_SERVICES_TABLE *dst = NULL; + + get_dxe_services_table(&dst); + if (!dst) + return EFI_UNSUPPORTED; + + if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) + return EFI_UNSUPPORTED; + + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { + dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr+size-1), + attrs); + return EFI_SUCCESS; + } + + start = ALIGN_DOWN(physaddr, EFI_PAGE_SIZE); + end = ALIGN_UP(physaddr + size, EFI_PAGE_SIZE); + + for (; start < end; start = next) { + status = dst->GetMemorySpaceDescriptor(start, &desc); + if (EFI_ERROR(status)) { + dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n", + start, status); + return status; + } + + next = desc.BaseAddress + desc.Length; + + if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY) + continue; + + *attrs = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes); + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +static EFI_STATUS +dxe_update_mem_attrs(uintptr_t addr, size_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ +#if 0 + EFI_STATUS status; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc; + EFI_PHYSICAL_ADDRESS start, end, next; + uint64_t before = 0, after = 0, dxe_set_attrs, dxe_clear_attrs; +#endif + EFI_DXE_SERVICES_TABLE *dst = NULL; + + get_dxe_services_table(&dst); + if (!dst) + return EFI_UNSUPPORTED; + + if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) + return EFI_UNSUPPORTED; + + if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(size) || size == 0) { + perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n", + __func__, (unsigned long long)addr, + (unsigned long long)(addr + size - 1), + (unsigned long long)size, + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : ""); + if (!IS_PAGE_ALIGNED(addr)) + perror(L" addr is not page aligned\n"); + if (!IS_PAGE_ALIGNED(size)) + perror(L" size is not page aligned\n"); + if (size == 0) + perror(L" size is 0\n"); + return EFI_SUCCESS; + } + + /* + * We know this only works coincidentally, so nerfing it for now + * until we have a chance to debug more thoroughly on these niche + * systems. + */ +#if 0 + start = ALIGN_DOWN(addr, EFI_PAGE_SIZE); + end = ALIGN_UP(addr + size, EFI_PAGE_SIZE); + + for (; start < end; start = next) { + EFI_PHYSICAL_ADDRESS mod_start; + UINT64 mod_size; + + status = dst->GetMemorySpaceDescriptor(start, &desc); + if (EFI_ERROR(status)) { + dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n", + start, status); + return status; + } + + next = desc.BaseAddress + desc.Length; + + if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY) + continue; + + mod_start = MAX(start, desc.BaseAddress); + mod_size = MIN(end, next) - mod_start; + + before = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes); + dxe_set_attrs = shim_mem_attrs_to_uefi_mem_attrs(set_attrs); + dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, dxe_set_attrs); + dxe_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs(clear_attrs); + dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, dxe_clear_attrs); + desc.Attributes |= dxe_set_attrs; + desc.Attributes &= ~dxe_clear_attrs; + after = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes); + + status = dst->SetMemorySpaceAttributes(mod_start, mod_size, desc.Attributes); + if (EFI_ERROR(status)) { + dprint(L"Failed to update memory attrs:0x%0x addr:0x%llx size:0x%0lx status:%r\n", + desc.Attributes, mod_start, mod_size, status); + return status; + } + + break; + } + + dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : "", + (unsigned long long)addr, (unsigned long long)(addr + size - 1), + (before & MEM_ATTR_R) ? 'r' : '-', + (before & MEM_ATTR_W) ? 'w' : '-', + (before & MEM_ATTR_X) ? 'x' : '-', + (after & MEM_ATTR_R) ? 'r' : '-', + (after & MEM_ATTR_W) ? 'w' : '-', + (after & MEM_ATTR_X) ? 'x' : '-'); +#endif + + return EFI_SUCCESS; +} + +static void +get_efi_mem_attr_protocol(EFI_MEMORY_ATTRIBUTE_PROTOCOL **protop) +{ + static EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + static bool has_mem_access_proto = true; + + if (proto == NULL && has_mem_access_proto == true) { + EFI_STATUS efi_status; + efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, + (VOID **)&proto); + if (EFI_ERROR(efi_status) || !proto) { + has_mem_access_proto = false; + *protop = NULL; + } + } + + *protop = proto; +} + +static EFI_STATUS +efi_get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status; + + get_efi_mem_attr_protocol(&proto); + if (!proto) + return EFI_UNSUPPORTED; + + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { + dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr+size-1), + attrs); + return EFI_SUCCESS; + } + + efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"GetMemoryAttributes(..., 0x%llx, 0x%x, 0x%x): %r\n", + physaddr, size, attrs, efi_status); + } else { + *attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs); + } + + return efi_status; +} + +static EFI_STATUS +efi_update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status, ret; + uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; + + get_efi_mem_attr_protocol(&proto); + if (!proto) + return EFI_UNSUPPORTED; + + efi_status = efi_get_mem_attrs(addr, size, &before); + if (EFI_ERROR(efi_status)) + dprint(L"efi_get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &before, efi_status); + + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0) { + perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr + size - 1), + (unsigned long long)size, + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : ""); + if (!IS_PAGE_ALIGNED(physaddr)) + perror(L" addr is not page aligned\n"); + if (!IS_PAGE_ALIGNED(size)) + perror(L" size is not page aligned\n"); + if (size == 0) + perror(L" size is 0\n"); + return EFI_SUCCESS; + } + + uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs); + dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs); + uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs); + dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs); + efi_status = EFI_SUCCESS; + if (uefi_set_attrs) { + efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"Failed to set memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", + uefi_set_attrs, physaddr, size, efi_status); + } + } + if (!EFI_ERROR(efi_status) && uefi_clear_attrs) { + efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"Failed to clear memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", + uefi_clear_attrs, physaddr, size, efi_status); + } + } + ret = efi_status; + + efi_status = efi_get_mem_attrs(addr, size, &after); + if (EFI_ERROR(efi_status)) + dprint(L"efi_get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &after, efi_status); + + dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : "", + (unsigned long long)addr, (unsigned long long)(addr + size - 1), + (before & MEM_ATTR_R) ? 'r' : '-', + (before & MEM_ATTR_W) ? 'w' : '-', + (before & MEM_ATTR_X) ? 'x' : '-', + (after & MEM_ATTR_R) ? 'r' : '-', + (after & MEM_ATTR_W) ? 'w' : '-', + (after & MEM_ATTR_X) ? 'x' : '-'); + + return ret; +} + +EFI_STATUS +update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ + EFI_STATUS efi_status; + + efi_status = efi_update_mem_attrs(addr, size, set_attrs, clear_attrs); + if (!EFI_ERROR(efi_status)) + return efi_status; + + if (efi_status == EFI_UNSUPPORTED) + efi_status = dxe_update_mem_attrs(addr, size, set_attrs, clear_attrs); + + return efi_status; +} + +EFI_STATUS +get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs) +{ + EFI_STATUS efi_status; + + efi_status = efi_get_mem_attrs(addr, size, attrs); + if (!EFI_ERROR(efi_status)) + return efi_status; + + if (efi_status == EFI_UNSUPPORTED) + efi_status = dxe_get_mem_attrs(addr, size, attrs); + + return efi_status; +} + +char * +decode_hsi_bits(UINTN hsi) +{ + static const struct { + UINTN bit; + char name[16]; + } bits[] = { + {.bit = SHIM_HSI_STATUS_HEAPX, .name = "HEAPX"}, + {.bit = SHIM_HSI_STATUS_STACKX, .name = "STACKX"}, + {.bit = SHIM_HSI_STATUS_ROW, .name = "ROW"}, + {.bit = SHIM_HSI_STATUS_HASMAP, .name = "HASMAP"}, + {.bit = SHIM_HSI_STATUS_HASDST, .name = "HASDST"}, + {.bit = SHIM_HSI_STATUS_HASDSTGMSD, .name = "HASDSTGMSD"}, + {.bit = SHIM_HSI_STATUS_HASDSTSMSA, .name = "HASDSTSMSA"}, + {.bit = SHIM_HSI_STATUS_NX, .name = "NX"}, + {.bit = 0, .name = ""}, + }; + static int x = 0; + static char retbufs[2][sizeof(bits)]; + char *retbuf = &retbufs[x % 2][0]; + char *prev = &retbuf[0]; + char *pos = &retbuf[0]; + + x = ( x + 1 ) % 2; + + ZeroMem(retbuf, sizeof(bits)); + + if (hsi == 0) { + prev = stpcpy(retbuf, "0"); + } else { + for (UINTN i = 0; bits[i].bit != 0; i++) { + if (hsi & bits[i].bit) { + prev = stpcpy(pos, bits[i].name); + pos = stpcpy(prev, "|"); + } + } + } + prev[0] = '\0'; + return retbuf; +} + +void +get_hsi_mem_info(void) +{ + EFI_STATUS efi_status; + uintptr_t addr; + uint64_t attrs = 0; + uint32_t *tmp_alloc; + EFI_MEMORY_ATTRIBUTE_PROTOCOL *efiproto = NULL; + EFI_DXE_SERVICES_TABLE *dst = NULL; + + get_efi_mem_attr_protocol(&efiproto); + if (efiproto) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASMAP)); + hsi_status |= SHIM_HSI_STATUS_HASMAP; + } + + get_dxe_services_table(&dst); + if (dst) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDST)); + hsi_status |= SHIM_HSI_STATUS_HASDST; + if (dst->GetMemorySpaceDescriptor) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTGMSD)); + hsi_status |= SHIM_HSI_STATUS_HASDSTGMSD; + } + if (dst->SetMemorySpaceAttributes) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTSMSA)); + hsi_status |= SHIM_HSI_STATUS_HASDSTSMSA; + } + } + + if (!(hsi_status & SHIM_HSI_STATUS_HASMAP) && + !(hsi_status & SHIM_HSI_STATUS_HASDSTGMSD && + hsi_status & SHIM_HSI_STATUS_HASDSTSMSA)) { + dprint(L"No memory protocol, not testing further\n"); + return; + } + + addr = ((uintptr_t)&get_hsi_mem_info) & ~EFI_PAGE_MASK; + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE); + goto error; + } + + if (attrs & MEM_ATTR_W) { + dprint(L"get_hsi_mem_info() is on a writable page: %a->%a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_ROW)); + hsi_status |= SHIM_HSI_STATUS_ROW; + } + + addr = ((uintptr_t)&addr) & ~EFI_PAGE_MASK; + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE); + goto error; + } + + if (attrs & MEM_ATTR_X) { + dprint(L"Stack variable is on an executable page: %a->%a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_STACKX)); + hsi_status |= SHIM_HSI_STATUS_STACKX; + } + + tmp_alloc = AllocatePool(EFI_PAGE_SIZE); + if (!tmp_alloc) { + dprint(L"Failed to allocate heap variable.\n"); + goto error; + } + + addr = ((uintptr_t)tmp_alloc) & ~EFI_PAGE_MASK; + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + FreePool(tmp_alloc); + if (EFI_ERROR(efi_status)) { + dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE); + goto error; + } + if (attrs & MEM_ATTR_X) { + dprint(L"Heap variable is on an executable page: %a->%a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HEAPX)); + hsi_status |= SHIM_HSI_STATUS_HEAPX; + } + + return; + +error: + /* + * In this case we can't actually tell anything, so assume + * and report the worst case scenario. + */ + hsi_status = SHIM_HSI_STATUS_HEAPX | + SHIM_HSI_STATUS_STACKX | + SHIM_HSI_STATUS_ROW; + dprint(L"Setting HSI to 0x%lx due to error: %r\n", decode_hsi_bits(hsi_status), efi_status); +} + +// vim:fenc=utf-8:tw=75:noet diff -Nru shim-15.8/mkosi/mkosi.build.chroot shim-16.1/mkosi/mkosi.build.chroot --- shim-15.8/mkosi/mkosi.build.chroot 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.build.chroot 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +if [ "$ARCHITECTURE" = "x86-64" ]; then + EFI_ARCHITECTURE="x64" +elif [ "$ARCHITECTURE" = "x86" ]; then + EFI_ARCHITECTURE="ia32" +elif [ "$ARCHITECTURE" = "arm64" ]; then + EFI_ARCHITECTURE="aa64" +else + EFI_ARCHITECTURE="$ARCHITECTURE" +fi + +cd "$BUILDDIR" + +openssl x509 -inform PEM -in "$SRCDIR/mkosi/mkosi.conf.d/$DISTRIBUTION/certs/shim.crt" -outform DER -out shim.der + +export VENDOR_CERT_FILE=$PWD/shim.der +export EFIDIR=$DISTRIBUTION +export DEBUG=1 + +make TOPDIR="$SRCDIR" -f "$SRCDIR/Makefile" -j1 + +for b in shim fb mm; do + install -D "${b}${EFI_ARCHITECTURE}.efi" -t "$DESTDIR/usr/lib/shim/" -m 0755 +done diff -Nru shim-15.8/mkosi/mkosi.clean shim-16.1/mkosi/mkosi.clean --- shim-15.8/mkosi/mkosi.clean 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.clean 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,4 @@ +#!/bin/sh + +rm -f "$OUTPUTDIR/ovmf_vars.fd" +rm -rf "$OUTPUTDIR/mok/" diff -Nru shim-15.8/mkosi/mkosi.conf shim-16.1/mkosi/mkosi.conf --- shim-15.8/mkosi/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,65 @@ +[Config] +MinimumVersion=commit:dec7c3e754810ae6d2382d0b5e4762d7250b0254 + +[Output] +RepartDirectories=mkosi.repart +OutputDirectory=mkosi.output + +[Build] +History=yes +ToolsTree=default +BuildDirectory=mkosi.builddir +CacheDirectory=mkosi.cache +Incremental=yes + +[Validation] +SecureBoot=yes +SecureBootAutoEnroll=no + +[Content] +Bootable=yes +ShimBootloader=unsigned + +# Default configuration is systemd-boot + UKI, can be overridden +# on the command line or via mkosi/mkosi.local.conf +Bootloader=systemd-boot-signed +UnifiedKernelImages=unsigned + +SELinuxRelabel=no +KernelInitrdModules=default +KernelCommandLine= + systemd.show_status=0 + systemd.log_ratelimit_kmsg=0 + printk.devkmsg=on + rw + selinux=0 + systemd.firstboot=no + oops=panic + panic=-1 + softlockup_panic=1 + panic_on_warn=1 + mitigations=off + +Packages= + mokutil + openssl + +[Runtime] +FirmwareVariables=%O/ovmf_vars.fd +Firmware=uefi-secure-boot +Credentials= + journal.storage=persistent + tty.serial.hvc0.agetty.autologin=root + tty.serial.hvc0.login.noauth=yes + tty.console.agetty.autologin=root + tty.console.login.noauth=yes + tty.virtual.tty1.agetty.autologin=root + tty.virtual.tty1.login.noauth=yes +RuntimeBuildSources=yes +RuntimeScratch=no +CPUs=2 +VSock=yes +TPM=yes + +[Include] +Include=mkosi-vm diff -Nru shim-15.8/mkosi/mkosi.conf.d/centos/certs/shim.crt shim-16.1/mkosi/mkosi.conf.d/centos/certs/shim.crt --- shim-15.8/mkosi/mkosi.conf.d/centos/certs/shim.crt 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/centos/certs/shim.crt 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDYjCCAkqgAwIBAgIJAIlReu6IOzL7MA0GCSqGSIb3DQEBCwUAMEYxIDAeBgNV +BAMMF0NlbnRPUyBTZWN1cmUgQm9vdCBDQSAyMSIwIAYJKoZIhvcNAQkBFhNzZWN1 +cml0eUBjZW50b3Mub3JnMB4XDTIwMDYwOTA4MTkzMloXDTM4MDExODA4MTkzMlow +RjEgMB4GA1UEAwwXQ2VudE9TIFNlY3VyZSBCb290IENBIDIxIjAgBgkqhkiG9w0B +CQEWE3NlY3VyaXR5QGNlbnRvcy5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQChatbNaQDV0RTCqff1tl92xI6gu1k8jYufW8FyzZ6uDnxoGpBT0LiU +WKuGjMQ89JgiApFzDYSLWrZg8NbTnVdz0hny4SMyspe5weUk6IToKXvEejZNFn6i +vae2vfT0/ASKsgIvUcz4sWHMK43vbfv/pVpYGLgoG5aNUkt7VhkeURwJzR3ODgDp +aL4bQ/7qEo8ASHCEvQx6klG330Z06O0kjS6GK12cPC1t5ZlimVXCNWP1jf0pMWmh +aBrZjbyY0j8R7Yns3cEovAM230chsVdyFxSYpqCLzMlmWNxiIlvcAoDIRMWEa7Da +SSAfJWH+ygAzad1PHlnCB0zAFbLAMJH1AgMBAAGjUzBRMB0GA1UdDgQWBBRwAH+Z +IJwSa+FHdOrse22WMfNNyjAfBgNVHSMEGDAWgBRwAH+ZIJwSa+FHdOrse22WMfNN +yjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAe5NcVSUd/POZs +Jkiep8ATNwXglLAeYxB55F42sXx5OOdKMBmhqWQIVJvaih/wsfKIBfdUGv2L9dH8 +IQgiU1PRYx0baSVJno3HcQTbCqLvnvckusR7IUTDAFj774MvXwS6yV6pXzxDmuh2 +t8hRktOKFeUtdlDYqg9X3Ia3GkoB5huyEbuaZTNcV4TAfU/yAERNIAgRs+fLQU70 +OgGlWsp35J8qPkZKabGf0surDa2xa6iAoFyknxruoKQ8uNSB9KB7/0JvVouNx90+ +ncykWW96GVKs8+H5WGza10FqrchtThSNCSXTtLbTXoK0Atdvu0o04XUbsCGMnlcG +zAVb3/m0 +-----END CERTIFICATE----- diff -Nru shim-15.8/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf shim-16.1/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf --- shim-15.8/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,17 @@ +[Match] +Distribution=|centos +Distribution=|fedora + +[Content] +Packages= + dos2unix + efibootmgr + efivar + python-virt-firmware + +BuildPackages= + make + gcc + elfutils-libelf-devel + openssl-devel + pesign diff -Nru shim-15.8/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-arm64.conf shim-16.1/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-arm64.conf --- shim-15.8/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-arm64.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-arm64.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,5 @@ +[Match] +Architecture=arm64 + +[Content] +Packages=grub2-efi-aa64 diff -Nru shim-15.8/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-x86-64.conf shim-16.1/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-x86-64.conf --- shim-15.8/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-x86-64.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf.d/grub-x86-64.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,5 @@ +[Match] +Architecture=x86-64 + +[Content] +Packages=grub2-efi-x64 diff -Nru shim-15.8/mkosi/mkosi.conf.d/debian/certs/shim.crt shim-16.1/mkosi/mkosi.conf.d/debian/certs/shim.crt --- shim-15.8/mkosi/mkosi.conf.d/debian/certs/shim.crt 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/debian/certs/shim.crt 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDnjCCAoagAwIBAgIRAO1UodWvh0iUjZ+JMu6cfDQwDQYJKoZIhvcNAQELBQAw +IDEeMBwGA1UEAxMVRGViaWFuIFNlY3VyZSBCb290IENBMB4XDTE2MDgxNjE4MDkx +OFoXDTQ2MDgwOTE4MDkxOFowIDEeMBwGA1UEAxMVRGViaWFuIFNlY3VyZSBCb290 +IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnZXUi5vaEKwuyoI3 +waTLSsMbQpPCeinTbt1kr4Cv6maiG2GcgwzFa7k1Jf/F++gpQ97OSz3GEk2x7yZD +lWjNBBH+wiSb3hTYhlHoOEO9sZoV5Qhr+FRQi7NLX/wU5DVQfAux4gOEqDZI5IDo +6p/6v8UYe17OHL4sgHhJNRXAIc/vZtWKlggrZi9IF7Hn7IKPB+bK4F9xJDlQCo7R +cihQpZ0h9ONhugkDZsjfTiY2CxUPYx8rr6vEKKJWZIWNplVBrjyIld3Qbdkp29jE +aLX89FeJaxTb4O/uQA1iH+pY1KPYugOmly7FaxOkkXemta0jp+sKSRRGfHbpnjK0 +ia9XeQIDAQABo4HSMIHPMEEGCCsGAQUFBwEBBDUwMzAxBggrBgEFBQcwAoYlaHR0 +cHM6Ly9kc2EuZGViaWFuLm9yZy9zZWN1cmUtYm9vdC1jYTAfBgNVHSMEGDAWgBRs +zs5+TGwNH2FJ890n38xcu0GeoTAUBglghkgBhvhCAQEBAf8EBAMCAPcwEwYDVR0l +BAwwCgYIKwYBBQUHAwMwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w +HQYDVR0OBBYEFGzOzn5MbA0fYUnz3SffzFy7QZ6hMA0GCSqGSIb3DQEBCwUAA4IB +AQB3lj5Hyc4Jz4uJzlntJg4mC7mtqSu9oeuIeQL/Md7+9WoH72ETEXAev5xOZmzh +YhKXAVdlR91Kxvf03qjxE2LMg1esPKaRFa9VJnJpLhTN3U2z0WAkLTJPGWwRXvKj +8qFfYg8wrq3xSGZkfTZEDQY0PS6vjp3DrcKR2Dfg7npfgjtnjgCKxKTfNRbCcitM +UdeTk566CA1Zl/LiKaBETeru+D4CYMoVz06aJZGEP7dax+68a4Cj2f2ybXoeYxTr +7/GwQCXV6A6B62v3y//lIQAiLC6aNWASS1tfOEaEDAacz3KTYhjuXJjWs30GJTmV +305gdrAGewiwbuNknyFWrTkP +-----END CERTIFICATE----- diff -Nru shim-15.8/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf shim-16.1/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf --- shim-15.8/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,19 @@ +[Match] +Distribution=|debian +Distribution=|ubuntu + +[Content] +Packages= + dos2unix + efibootmgr + efitools + efivar + python3-virt-firmware + sbsigntool + +BuildPackages= + build-essential + gnu-efi + libefivar-dev + libelf-dev + pesign diff -Nru shim-15.8/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-arm64.conf shim-16.1/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-arm64.conf --- shim-15.8/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-arm64.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-arm64.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,5 @@ +[Match] +Architecture=arm64 + +[Content] +Packages=grub-efi-arm64-signed diff -Nru shim-15.8/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-x86-64.conf shim-16.1/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-x86-64.conf --- shim-15.8/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-x86-64.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/grub-x86-64.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,5 @@ +[Match] +Architecture=x86-64 + +[Content] +Packages=grub-efi-amd64-signed diff -Nru shim-15.8/mkosi/mkosi.conf.d/fedora/certs/mok/redhat-test.crt shim-16.1/mkosi/mkosi.conf.d/fedora/certs/mok/redhat-test.crt --- shim-15.8/mkosi/mkosi.conf.d/fedora/certs/mok/redhat-test.crt 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/fedora/certs/mok/redhat-test.crt 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIELzCCAxegAwIBAgIJAPfZBdz9lpYhMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNV +BAYTAlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMRIwEAYDVQQHEwlDYW1icmlk +Z2UxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xIzAhBgNVBAMTGlJlZCBIYXQgVGVz +dCBDZXJ0aWZ5aW5nIENBMB4XDTEyMDcwOTE5MTI0NFoXDTEzMDcwOTE5MTI0NFow +dDELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcT +CUNhbWJyaWRnZTEWMBQGA1UEChMNUmVkIEhhdCwgSW5jLjEhMB8GA1UEAxMYUmVk +IEhhdCBUZXN0IENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAv4fxdSxUwFHTbIwWndOQUBkBgRy6r919MluiULYsHBMTcB6DVZPfeLre +4vIy2waxcpPiXBNn0y6gDlTb56yWvPR/MYdgnRdX0fBfBGQKFlIChkZWRiA+MZCW +PbQ4bZzn39CpvBnBIufhlQSxZ0leP32iBvJlCs8q5IuDJ1W79y6zgDqYl+89Dms3 +oju0Ys2juR8oUK/tHAKw+g0YPYGC0u24dwBmoBu2sV8rWTMy1hymxwIGYKIuxR6T +hPyC9hWCmXFkg9mATgns9iNIsZYqE6Hhkfg18mFCtH2NcDg83OseC5cvJ2DgKBh3 +VegwNVi/hZmkK6JucK7dNi/PScNqzwIDAQABo4HBMIG+MB0GA1UdDgQWBBQ1gM81 +12s7ZnpA32ZpHLz4c1OyPDAfBgNVHSMEGDAWgBQIoO9YAMsC+1h8ErQDJZx9TvFd +HDAPBgNVHQ8BAf8EBQMDB/+AMB8GA1UdJQQYMBYGCCsGAQUFBwMDBgorBgEEAYI3 +CgMBMAkGA1UdEwQCMAAwPwYJYIZIAYb4QgENBDIWMFRlc3RpbmcgQ2VydGlmaWNh +dGUgZm9yIFJlZCBIYXQgVGVzdCBDZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOC +AQEAfMGe9NTdhU7yqTTetnaMTSdTU4Fh82TAPvRFBTR0WA6o2Ph4Zdd95bcVwNZO +OvxEnfg3KX7mCS9mgqHI2BWg6Lkvb3cGNr4BgXxwT/Dz2QF+l5iTWeJPJrz6B1kR +2SvvhDCk8FO9IijcGN1xmjUoPnKe0DZTnPhmfnYp9+oDmmtukHA7Kt3RypwfioQx ++IahCIp9AeK+dJP29F8vxkzb5W7pufR6lftkBXjeDe/MekeBv8rBhW1A3xcZgMVU +vxJ/NLEdYAxTXqjotpOESSxdAZrZGuAZ3OnKMGj+q6tlvRQsZ0qET7OewdQtbvNb +5IJty5/t7Mu4A3T9DDwygv354g== +-----END CERTIFICATE----- diff -Nru shim-15.8/mkosi/mkosi.conf.d/fedora/certs/shim.crt shim-16.1/mkosi/mkosi.conf.d/fedora/certs/shim.crt --- shim-15.8/mkosi/mkosi.conf.d/fedora/certs/shim.crt 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/fedora/certs/shim.crt 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEWzCCA0OgAwIBAgIQIjmvBBMMRESz83ftvhr3hjANBgkqhkiG9w0BAQsFADCB +jTELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcT +CUNhbWJyaWRnZTEWMBQGA1UEChMNUmVkIEhhdCwgSW5jLjEnMCUGA1UECxMeRmVk +b3JhIFNlY3VyZSBCb290IENBIDIwMjAwNzA5MREwDwYDVQQDEwhmZWRvcmFjYTAe +Fw0yMDA3MTMxNzMxMTZaFw0zNzAxMTkwMzE0MDdaMIGNMQswCQYDVQQGEwJVUzEW +MBQGA1UECBMNTWFzc2FjaHVzZXR0czESMBAGA1UEBxMJQ2FtYnJpZGdlMRYwFAYD +VQQKEw1SZWQgSGF0LCBJbmMuMScwJQYDVQQLEx5GZWRvcmEgU2VjdXJlIEJvb3Qg +Q0EgMjAyMDA3MDkxETAPBgNVBAMTCGZlZG9yYWNhMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAq8RBrct6UIlmZfAJ3tuRK7fhex5LKMUpMaXbFVRE3/XI +srYPjxiQKtJ1thweVxvCbI2IW1VJDBmX3EbJxca6jXA4h92czXkr6XS0h8zgbbc6 +jl1Y/lNwXcqRo1KNL6wiDl2KDnX7Ub705MupRS+iBhtkl5DN1g+hY76FTfN5yQnm +xFBq4unk8qYG3pF7cVQrl1fKbTIwBXsw22+M5JUuDNGor/buO5oI1mRklIFt8q50 +89ADgl5K84/JBcUdmc5DjdGbX8+OGF3r4LYSHmje6y0KhZZnS1ZImprkQO714HJ3 +a9dyQs1/nn0K5jX1DzhP0UD1ZqM0TF6FO/DYgyKNtQIDAQABo4G0MIGxME4GCCsG +AQUFBwEBBEIwQDA+BggrBgEFBQcwAoYyaHR0cHM6Ly9mZWRvcmFwcm9qZWN0Lm9y +Zy93aWtpL0ZlYXR1cmVzL1NlY3VyZUJvb3QwHwYDVR0jBBgwFoAUsoDHrmuITg9N +Kg2HJMJer2xlwyYwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFLKAx65riE4PTSoNhyTCXq9sZcMmMA0GCSqGSIb3DQEBCwUAA4IBAQBq +6hHXlrN/76q4P93HdMlc5xT1c13EtCK38YLS/z1LVaDbwjpc2BYlAi9kNrlhB/V9 +vQDQwMGtckicgsdal0dSYfw6ip+S4BGn/Ykw4PdyQtnie0FhF7PnaYB4Hju7d1Lr +xk6AsbQttmaiwIRZ4eu/sSEqGuFzCnRimEhuRM6A+uJbZ4U5hmFua0EfMBXEUk1q +1yt/DY2sjKTqFcB5o+HBOT46cNO8v9GBlXArdcN8vOLxaXZRys3JYIa8xS0ljUOB +JAw+t853cq0Ig9gxD+NZHe3TKWHo18Kdqd3PT1thBR9nvG7uwFDThYDeaZmkYDOQ +QH0B7YJMk/PLH91foJzD +-----END CERTIFICATE----- diff -Nru shim-15.8/mkosi/mkosi.conf.d/fedora/mkosi.conf shim-16.1/mkosi/mkosi.conf.d/fedora/mkosi.conf --- shim-15.8/mkosi/mkosi.conf.d/fedora/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/fedora/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,9 @@ +[Match] +Distribution=fedora + +[Distribution] +RepositoryKeyCheck=no + +[Content] +Packages= + sbsigntools diff -Nru shim-15.8/mkosi/mkosi.conf.d/ubuntu/certs/shim.crt shim-16.1/mkosi/mkosi.conf.d/ubuntu/certs/shim.crt --- shim-15.8/mkosi/mkosi.conf.d/ubuntu/certs/shim.crt 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/ubuntu/certs/shim.crt 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENDCCAxygAwIBAgIJALlBJKAYLJJnMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD +VQQGEwJHQjEUMBIGA1UECAwLSXNsZSBvZiBNYW4xEDAOBgNVBAcMB0RvdWdsYXMx +FzAVBgNVBAoMDkNhbm9uaWNhbCBMdGQuMTQwMgYDVQQDDCtDYW5vbmljYWwgTHRk +LiBNYXN0ZXIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEyMDQxMjExMTI1MVoX +DTQyMDQxMTExMTI1MVowgYQxCzAJBgNVBAYTAkdCMRQwEgYDVQQIDAtJc2xlIG9m +IE1hbjEQMA4GA1UEBwwHRG91Z2xhczEXMBUGA1UECgwOQ2Fub25pY2FsIEx0ZC4x +NDAyBgNVBAMMK0Nhbm9uaWNhbCBMdGQuIE1hc3RlciBDZXJ0aWZpY2F0ZSBBdXRo +b3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/WzoWdO4hXa5h +7Z1WrL3e3nLz3X4tTGIPrMBtSAgRz42L+2EfJ8wRbtlVPTlU60A7sbvihTR5yvd7 +v7p6yBAtGX2tWc+m1OlOD9quUupMnpDOxpkNTmdleF350dU4Skp6j5OcfxqjhdvO ++ov3wqIhLZtUQTUQVxONbLwpBlBKfuqZqWinO8cHGzKeoBmHDnm7aJktfpNS5fbr +yZv5K+24aEm82ZVQQFvFsnGq61xX3nH5QArdW6wehC1QGlLW4fNrbpBkT1u06yDk +YRDaWvDq5ELXAcT+IR/ZucBUlUKBUnIfSWR6yGwk8QhwC02loDLRoBxXqE3jr6WO +BQU+EEOhAgMBAAGjgaYwgaMwHQYDVR0OBBYEFK2RmQvCKrH1FwSMI7ZlWiaONFpj +MB8GA1UdIwQYMBaAFK2RmQvCKrH1FwSMI7ZlWiaONFpjMA8GA1UdEwEB/wQFMAMB +Af8wCwYDVR0PBAQDAgGGMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly93d3cuY2Fu +b25pY2FsLmNvbS9zZWN1cmUtYm9vdC1tYXN0ZXItY2EuY3JsMA0GCSqGSIb3DQEB +CwUAA4IBAQA/ffZ2pbODtCt60G1SGgODxBKnUJxHkszAlHeC0q5Xs5kE9TI6xlUd +B9sSqVb62NR2IOvkw1Hbmlyckj8Yc9qUaqGZOIykiG3B/Dlx0HR2FgM+ViM11VVH +WxodQcLTEkzc/64KkpxiChcBnHPgXrH9vNa1GRF6fs0+A35m21uoyTlIUf9T4Zwx +U5EbOxB1Axe65oECgJRwTEa3lLA9Fc0fjgLgaAKP+/lHHX2iAcYHUcSazO3dz6Nd +7ZK7vtH95uwfM1FzBL48crB9CPgB/5h9y5zgaTl3JUdxiLGNJ6UuqPc/X4Bplz6p +9JkU284DDgtmxBxtvbgnd8FClL38agq8 +-----END CERTIFICATE----- diff -Nru shim-15.8/mkosi/mkosi.conf.d/ubuntu/mkosi.conf shim-16.1/mkosi/mkosi.conf.d/ubuntu/mkosi.conf --- shim-15.8/mkosi/mkosi.conf.d/ubuntu/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.conf.d/ubuntu/mkosi.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,5 @@ +[Match] +Distribution=ubuntu + +[Distribution] +Repositories=universe diff -Nru shim-15.8/mkosi/mkosi.extra/usr/bin/mkosi-test.sh shim-16.1/mkosi/mkosi.extra/usr/bin/mkosi-test.sh --- shim-15.8/mkosi/mkosi.extra/usr/bin/mkosi-test.sh 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.extra/usr/bin/mkosi-test.sh 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,17 @@ +#!/bin/bash -eux + +# Ensure secure boot is enabled and not in setup mode +cmp /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c <(printf '\6\0\0\0\1') +cmp /sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c <(printf '\6\0\0\0\0') + +# Check that we didn't accidentally install the microsoft-signed distro shim instead of the local one +if [ -d /boot/EFI ] && command -v sbverify >/dev/null 2>&1; then + sbverify --list /boot/EFI/BOOT/BOOT*.EFI | grep -q mkosi + sbverify --list /boot/EFI/BOOT/mm*.efi | grep -q mkosi + sbverify --list /boot/EFI/BOOT/grub* | grep -v -q mkosi +fi + +# Verify mok-signed UKI addon was loaded correctly +if SYSTEMD_UTF8=0 bootctl status | grep -q "+ Pick up .cmdline from addons"; then + grep -q foobarbaz /proc/cmdline +fi diff -Nru shim-15.8/mkosi/mkosi.extra/usr/lib/systemd/system/mkosi-test.service shim-16.1/mkosi/mkosi.extra/usr/lib/systemd/system/mkosi-test.service --- shim-15.8/mkosi/mkosi.extra/usr/lib/systemd/system/mkosi-test.service 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.extra/usr/lib/systemd/system/mkosi-test.service 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,12 @@ +[Unit] +Description=Smoke tests for shim +After=multi-user.target +Requires=multi-user.target +SuccessAction=exit +FailureAction=exit +SuccessActionExitStatus=123 + +[Service] +StandardOutput=journal+console +Type=oneshot +ExecStart=/usr/bin/mkosi-test.sh diff -Nru shim-15.8/mkosi/mkosi.finalize shim-16.1/mkosi/mkosi.finalize --- shim-15.8/mkosi/mkosi.finalize 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.finalize 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +touch -r "$BUILDROOT/usr" "$BUILDROOT/etc/.updated" "$BUILDROOT/var/.updated" + +mkdir -p "$OUTPUTDIR/mok/" +openssl req -new -x509 -newkey rsa:2048 -keyout "$OUTPUTDIR/mok/mkosi.key" -out "$OUTPUTDIR/mok/mkosi.crt" -days 3650 -nodes \ + -subj "/CN=mkosi MOK key" -addext "subjectAltName=DNS:mkosi.local" +chmod 0644 "$OUTPUTDIR/mok/mkosi.crt" +chmod 0600 "$OUTPUTDIR/mok/mkosi.key" + +mkdir -p "$BUILDROOT/boot/loader/addons" +ukify build \ + --stub "$BUILDROOT/usr/lib/systemd/boot/efi/addon${EFI_ARCHITECTURE}.efi.stub" \ + --cmdline="foobarbaz" \ + --output "$BUILDROOT/boot/loader/addons/test.addon.efi" \ + --secureboot-certificate "$OUTPUTDIR/mok/mkosi.crt" \ + --secureboot-private-key "$OUTPUTDIR/mok/mkosi.key" diff -Nru shim-15.8/mkosi/mkosi.postoutput shim-16.1/mkosi/mkosi.postoutput --- shim-15.8/mkosi/mkosi.postoutput 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.postoutput 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,80 @@ +#!/bin/bash +set -e +shopt -s nullglob + +ARGS=( + --secure-boot + --no-microsoft + --output "$OUTPUTDIR/ovmf_vars.fd" + --enroll-cert "$SRCDIR/mkosi/mkosi.crt" + --add-db OvmfEnrollDefaultKeys "$SRCDIR/mkosi/mkosi.crt" +) + +if [ "$ARCHITECTURE" = "x86-64" ]; then + if [ -f /usr/share/OVMF/OVMF_VARS_4M.fd ]; then + ARGS+=(--input /usr/share/OVMF/OVMF_VARS_4M.fd) + elif [ -f /usr/share/OVMF/OVMF_VARS.fd ]; then + ARGS+=(--input /usr/share/OVMF/OVMF_VARS.fd) + else + echo "No OVMF vars template found for $ARCHITECTURE" + exit 1 + fi +elif [ "$ARCHITECTURE" = "x86" ]; then + if [ -f /usr/share/OVMF/OVMF32_VARS_4M.fd ]; then + ARGS+=(--input /usr/share/OVMF/OVMF32_VARS_4M.fd) + elif [ -f /usr/share/edk2/ovmf-ia32/OVMF_VARS.fd ]; then + ARGS+=(--input /usr/share/edk2/ovmf-ia32/OVMF_VARS.fd) + else + echo "No OVMF vars template found for $ARCHITECTURE" + exit 1 + fi +elif [ "$ARCHITECTURE" = "arm64" ]; then + if [ -f /usr/share/AAVMF/AAVMF_VARS.fd ]; then + ARGS+=(--input /usr/share/AAVMF/AAVMF_VARS.fd) + else + echo "No OVMF vars template found for $ARCHITECTURE" + exit 1 + fi +elif [ "$ARCHITECTURE" = "arm" ]; then + if [ -f /usr/share/AAVMF/AAVMF32_VARS.fd ]; then + ARGS+=(--input /usr/share/AAVMF/AAVMF32_VARS.fd) + elif [ -f /usr/share/edk2/arm/QEMU_VARS.fd ]; then + ARGS+=(--input /usr/share/edk2/arm/QEMU_VARS.fd) + else + echo "No OVMF vars template found for $ARCHITECTURE" + exit 1 + fi +elif [ "$ARCHITECTURE" = "riscv64" ]; then + if [ -f /usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd ]; then + ARGS+=(--input /usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd) + elif [ -f /usr/share/edk2/riscv/RISCV_VIRT_VARS.fd ]; then + ARGS+=(--input /usr/share/edk2/riscv/RISCV_VIRT_VARS.fd) + else + echo "No OVMF vars template found for $ARCHITECTURE" + exit 1 + fi +elif [ "$ARCHITECTURE" = "loongarch64" ]; then + if [ -f /usr/share/qemu-efi-loongarch64/QEMU_VARS.fd ]; then + ARGS+=(--input /usr/share/qemu-efi-loongarch64/QEMU_VARS.fd) + elif [ -f /usr/share/edk2/loongarch64/QEMU_VARS.fd ]; then + ARGS+=(--input /usr/share/edk2/loongarch64/QEMU_VARS.fd) + else + echo "No OVMF vars template found for $ARCHITECTURE" + exit 1 + fi +else + echo "Unsupported architecture for OVMF vars template: $ARCHITECTURE" + exit 1 +fi + +if [ "$MKOSI_DEBUG" = "1" ]; then + ARGS+=(--loglevel DEBUG) +else + ARGS+=(--loglevel WARNING) +fi + +for cert in "$SRCDIR"/mkosi/mkosi.conf.d/"$DISTRIBUTION"/certs/mok/*.crt "$OUTPUTDIR"/mok/*.crt; do + ARGS+=(--add-mok 605dab50-e046-4300-abb6-3dd810dd8b23 "$cert") +done + +virt-fw-vars "${ARGS[@]}" diff -Nru shim-15.8/mkosi/mkosi.repart/00-esp.conf shim-16.1/mkosi/mkosi.repart/00-esp.conf --- shim-15.8/mkosi/mkosi.repart/00-esp.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.repart/00-esp.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,7 @@ +[Partition] +Type=esp +Format=vfat +CopyFiles=/boot:/ +CopyFiles=/efi:/ +SizeMinBytes=512M +SizeMaxBytes=512M diff -Nru shim-15.8/mkosi/mkosi.repart/10-root.conf shim-16.1/mkosi/mkosi.repart/10-root.conf --- shim-15.8/mkosi/mkosi.repart/10-root.conf 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/mkosi/mkosi.repart/10-root.conf 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,6 @@ +[Partition] +Type=root +Format=ext4 +CopyFiles=/ +SizeMinBytes=4G +SizeMaxBytes=4G diff -Nru shim-15.8/mock-variables.c shim-16.1/mock-variables.c --- shim-15.8/mock-variables.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/mock-variables.c 1970-01-01 00:00:00.000000000 +0000 @@ -163,7 +163,7 @@ 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 @@ } 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 @@ *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 @@ 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 @@ 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 @@ 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 @@ } #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 @@ 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 @@ } 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 @@ 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 @@ } #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 @@ 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 @@ 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 @@ }; 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 @@ 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 @@ 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,16 +1151,20 @@ 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]; - len -= guidstr_size; - SetMem(spacebuf, sizeof(spacebuf)-1, ' '); - spacebuf[len] = '\0'; for (size_t i = 0; filters[i]; i++) { 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 +1172,23 @@ } 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 -Nru shim-15.8/mok.c shim-16.1/mok.c --- shim-15.8/mok.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/mok.c 1970-01-01 00:00:00.000000000 +0000 @@ -6,6 +6,50 @@ #include "shim.h" +#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; +} + /* * Check if a variable exists */ @@ -34,6 +78,157 @@ 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; +} + +static UINTN +format_variable_info(UINT8 *buf, size_t bufsz, + struct mok_state_variable *msv UNUSED) +{ + typedef enum { + BS, + BS_NV, + BS_RT, + BS_RT_NV, + STOP + } variable_attr_t; + typedef struct { + uint64_t attrs; + char prefix[10]; + uint64_t max_storage_sz; + uint64_t remaining_sz; + uint64_t max_var_sz; + bool valid; + } var_set_t; + var_set_t var_sets[] = { + [BS] = { EFI_VARIABLE_BOOTSERVICE_ACCESS, + "bs", 0, 0, 0, false }, + [BS_NV] = { EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + "bs_rt", 0, 0, 0, false }, + [BS_RT] = { EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + "bs_nv", 0, 0, 0, false }, + [BS_RT_NV] = { EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + "bs_nv_rt", 0, 0, 0, false }, + [STOP] = { 0, "", 0, 0, 0, false } + }; + UINTN sz = 0; + UINTN pos = 0; + + if (EFI_MAJOR_VERSION(RT) < 2 || is_apple_firmware_vendor()) { + dprint(L"EFI %d.%d; no RT->QueryVariableInfo() %a\n", + EFI_MAJOR_VERSION(RT), EFI_MINOR_VERSION(RT), + is_apple_firmware_vendor() ? "(Apple)" : ""); + if (bufsz > 0) + buf[0] = '\0'; + return 0; + } else { + EFI_STATUS efi_status; + variable_attr_t i; + for (i = BS; i < STOP; i++) { + var_set_t *var_set = &var_sets[i]; + dprint(L"calling RT->QueryVariableInfo() for %a\n", + var_set->prefix); + efi_status = RT->QueryVariableInfo(var_set->attrs, + &var_set->max_storage_sz, + &var_set->remaining_sz, + &var_set->max_var_sz); + if (EFI_ERROR(efi_status)) { + perror(L"Could not get variable storage info: %r\n", + efi_status); + var_set->max_storage_sz = 0; + var_set->remaining_sz = 0; + var_set->max_var_sz = 0; + } else { + var_set->valid = true; + sz += strlen(var_set->prefix) + + strlen("-max_storage_sz: ") + + strlen("0x0123456701234567\n"); + sz += strlen(var_set->prefix) + + strlen("-remaining_sz: ") + + strlen("0x0123456701234567\n"); + sz += strlen(var_set->prefix) + + strlen("-max_var_sz: ") + + strlen("0x0123456701234567\n"); + } + } + sz += 1; + } + + if (!buf || bufsz < sz) { + dprint(L"buf:0x%lx bufsz:0x%lx returning 0x%lx\n", buf, bufsz, sz); + return sz; + } + + variable_attr_t i; + for (i = BS; i < STOP; i++) { + var_set_t *var_set = &var_sets[i]; + UINTN rc; + rc = AsciiSPrint((CHAR8 *)buf + pos, bufsz - pos, + "%a_max_storage_sz: 0x%lx\n", + var_set->prefix, var_set->max_storage_sz); + pos += rc; + rc = AsciiSPrint((CHAR8 *)buf + pos, bufsz - pos, + "%a_remaining_sz: 0x%lx\n", + var_set->prefix, var_set->remaining_sz); + pos += rc; + rc = AsciiSPrint((CHAR8 *)buf + pos, bufsz - pos, + "%a_max_var_sz: 0x%lx\n", + var_set->prefix, var_set->max_var_sz); + pos += rc; + } + + return pos; +} + /* * If the OS has set any of these variables we need to drop into MOK and * handle them appropriately @@ -50,6 +245,32 @@ efi_status = start_image(image_handle, MOK_MANAGER); if (EFI_ERROR(efi_status)) { + /* + * We don't do this in the unit tests because we + * don't have simulation for console_countdown() + * and similar. + */ +#ifndef SHIM_UNIT_TEST + EFI_STATUS efi_status_2; + EFI_LOADED_IMAGE *li; + efi_status_2 = BS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, + (void **)&li); + if (EFI_ERROR(efi_status_2)) + perror (L"Failed to get image: %r\n", efi_status_2); + else if (is_removable_media_path(li) && + efi_status == EFI_NOT_FOUND) { + CHAR16 *title = L"Could not find MokManager"; + CHAR16 *message = L"MokManager is missing on removable media."; + /* + * This occurs when system is booting on + * hard disk's EFI/BOOT/BOOTxxx.EFI entry + * while it should have booted on + * EFI//shimxxx.efi entry + */ + console_countdown(title, message, 10); + RT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL); + } +#endif perror(L"Failed to start MokManager: %r\n", efi_status); return efi_status; } @@ -80,12 +301,6 @@ 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 +411,172 @@ .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, + }, + /* + * Keep this entry last, or it'll be wrong. + */ + {.name = L"VariableInfo", + .name8 = "VariableInfo", + .rtname = L"VariableInfo", + .rtname8 = "VariableInfo", + .guid = &SHIM_LOCK_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + .format = format_variable_info, + }, { NULL, } }; size_t n_mok_state_variables = sizeof(mok_state_variable_data) / sizeof(mok_state_variable_data[0]); @@ -214,9 +595,6 @@ 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 EFI_STATUS get_max_var_sz(UINT32 attrs, SIZE_T *max_var_szp) { @@ -226,9 +604,10 @@ uint64_t max_var_sz = 0; *max_var_szp = 0; - if (EFI_MAJOR_VERSION(RT) < 2) { - dprint(L"EFI %d.%d; no RT->QueryVariableInfo(). Using 1024!\n", - EFI_MAJOR_VERSION(RT), EFI_MINOR_VERSION(RT)); + if (EFI_MAJOR_VERSION(RT) < 2 || is_apple_firmware_vendor()) { + dprint(L"EFI %d.%d; no RT->QueryVariableInfo()%a. Using 1024!\n", + EFI_MAJOR_VERSION(RT), EFI_MINOR_VERSION(RT), + is_apple_firmware_vendor() ? " (Apple)" : ""); max_var_sz = remaining_sz = max_storage_sz = 1024; efi_status = EFI_SUCCESS; } else { @@ -310,7 +689,7 @@ } 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 +715,13 @@ 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 +730,6 @@ 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 +771,6 @@ 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 +783,6 @@ efi_status); if (!only_first) { FreePool(namen); - FreePool(namen8); } return efi_status; } @@ -472,6 +837,9 @@ 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 +883,7 @@ 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 +948,7 @@ 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 +1033,7 @@ FullDataSize, v->name); return EFI_OUT_OF_RESOURCES; } + allocated_full_data = true; p = FullData; } } @@ -692,7 +1063,7 @@ 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 +1090,7 @@ 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 +1129,7 @@ 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 +1138,17 @@ 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 +1162,7 @@ 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 +1179,7 @@ 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 +1193,10 @@ 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 +1248,8 @@ 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 +1291,36 @@ } } } + + 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 +1412,8 @@ 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 -Nru shim-15.8/netboot.c shim-16.1/netboot.c --- shim-15.8/netboot.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/netboot.c 1970-01-01 00:00:00.000000000 +0000 @@ -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; @@ -116,6 +126,7 @@ static CHAR8 *str2ip6(CHAR8 *str) { + bool double_colon_found = false; UINT8 i = 0, j = 0, p = 0; size_t len = 0, dotcount = 0; enum { MAX_IP6_DOTS = 7 }; @@ -137,6 +148,23 @@ len = strlen(str); a = b = str; + + for (i = 0; i < len; i++) { + if (str[i] == ':') { + if (i+1 < (UINT8)len && str[i+1] == ':') { + if (double_colon_found) + return (CHAR8 *)ip; + double_colon_found = true; + } + } + else { + if (str[i] < '0' || (str[i] > '9' && str[i] < 'A') || + (str[i] > 'F' && str[i] < 'a') || str[i] > 'f') + return (CHAR8 *)ip; + } + } + + for (i = p = 0; i < len; i++, b++) { if (*b != ':') continue; @@ -147,15 +175,20 @@ if (b[1] == ':' ) break; } - a = b = (str + len); - for (j = len, p = 7; j > i; j--, a--) { - if (*a != ':') - continue; - t = *b; - *b = '\0'; - ip[p--] = str2ns(a+1); - *b = t; - b = a; + + if (i == len) { + ip[p++] = str2ns(a); + } else { + a = b = (str + len); + for (j = len, p = 7; j > i; j--, a--) { + if (*a != ':') + continue; + t = *b; + *b = '\0'; + ip[p--] = str2ns(a+1); + *b = t; + b = a; + } } return (CHAR8 *)ip; } @@ -333,7 +366,33 @@ 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 +400,8 @@ 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 +422,31 @@ 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 -Nru shim-15.8/pe-relocate.c shim-16.1/pe-relocate.c --- shim-15.8/pe-relocate.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/pe-relocate.c 1970-01-01 00:00:00.000000000 +0000 @@ -368,21 +368,28 @@ */ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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,96 @@ 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; + } +} + +static inline bool +hsi_nx_is_enforced(void) +{ + return !((hsi_status & SHIM_HSI_STATUS_HEAPX) || + (hsi_status & SHIM_HSI_STATUS_STACKX) || + (hsi_status & SHIM_HSI_STATUS_ROW)); +} + +static inline bool +hsi_api_is_present(void) +{ + return (hsi_status & SHIM_HSI_STATUS_HASMAP) || + ((hsi_status & SHIM_HSI_STATUS_HASDSTGMSD && + hsi_status & SHIM_HSI_STATUS_HASDSTSMSA)); +} + +void +set_shim_nx_policy(void) +{ + if ((hsi_status & SHIM_HSI_STATUS_NX) && + hsi_nx_is_enforced() && + hsi_api_is_present()) + { + mok_policy |= MOK_POLICY_REQUIRE_NX; + dprint("Enforcing NX policy for all images\n"); + } +} + // vim:fenc=utf-8:tw=75:noet diff -Nru shim-15.8/pe.c shim-16.1/pe.c --- shim-15.8/pe.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/pe.c 1970-01-01 00:00:00.000000000 +0000 @@ -39,6 +39,136 @@ }) #define check_size(d, ds, h, hs) check_size_line(d, ds, h, hs, __LINE__) +static EFI_STATUS +_do_sha256_sum(void *addr, UINTN size, UINT8 *digest) +{ + unsigned int sha256ctxsize; + void *sha256ctx = NULL; + + sha256ctxsize = Sha256GetContextSize(); + sha256ctx = AllocateZeroPool(sha256ctxsize); + if (sha256ctx == NULL) + return EFI_OUT_OF_RESOURCES; + + if (!Sha256Init(sha256ctx)) + return EFI_OUT_OF_RESOURCES; + + if (!Sha256Update(sha256ctx, addr, size)) + return EFI_OUT_OF_RESOURCES; + + if (!Sha256Final(sha256ctx, digest)) + return EFI_OUT_OF_RESOURCES; + + FreePool(sha256ctx); + return EFI_SUCCESS; +} + +struct shim_section_cache_entry { + EFI_HANDLE parent_image_handle; + UINT8 section_name[9]; + UINTN size; + /* + * Since this is all internal and there's no API access to it, this is + * currently always sha256 and can be updated as needed. + */ + UINT8 digest[32]; +}; + +static struct shim_section_cache_entry *section_cache = NULL; +static UINTN num_section_cache_entries = 0; + +static EFI_STATUS +cache_section(EFI_HANDLE parent_image_handle, UINT8 section_name[8], + void *addr, UINTN size) +{ + struct shim_section_cache_entry *new_section_cache = NULL; + struct shim_section_cache_entry *entry = NULL; + size_t oscsz = num_section_cache_entries * sizeof (*new_section_cache); + size_t nscsz = oscsz + sizeof (*new_section_cache); + EFI_STATUS efi_status; + + new_section_cache = AllocateZeroPool(nscsz); + if (!new_section_cache) + return EFI_OUT_OF_RESOURCES; + + if (section_cache) { + CopyMem(new_section_cache, section_cache, oscsz); + FreePool(section_cache); + } + section_cache = new_section_cache; + entry = §ion_cache[num_section_cache_entries]; + + entry->parent_image_handle = parent_image_handle; + CopyMem(entry->section_name, section_name, sizeof(entry->section_name)-1); + entry->size = size; + + efi_status = _do_sha256_sum(addr, size, entry->digest); + if (EFI_ERROR(efi_status)) { + ZeroMem(entry, sizeof (*entry)); + return efi_status; + } + num_section_cache_entries += 1; + return EFI_SUCCESS; +} + +EFI_STATUS +validate_cached_section(EFI_HANDLE parent_image_handle, + void *addr, UINTN size) +{ + struct shim_section_cache_entry *section = NULL; + EFI_STATUS efi_status; + + for (UINTN i = 0; i < num_section_cache_entries; i++) { + struct shim_section_cache_entry *this_entry = §ion_cache[i]; + UINT8 digest[32]; + + dprint(L"Handles: 0x%016llx 0x%016llx section: '%a'\n", + (unsigned long long)(uintptr_t)this_entry->parent_image_handle, + (unsigned long long)(uintptr_t)parent_image_handle, + this_entry->section_name); + + if (this_entry->size != size) + continue; + if (this_entry->parent_image_handle != parent_image_handle) + continue; + + ZeroMem(digest, sizeof(digest)); + + efi_status = _do_sha256_sum(addr, size, digest); + if (EFI_ERROR(efi_status)) + return efi_status; + + if (CompareMem(digest, this_entry->digest, sizeof(digest)) != 0) + continue; + + section = this_entry; + break; + } + if (section == NULL) + return EFI_NOT_FOUND; + + return EFI_SUCCESS; +} + +void +flush_cached_sections(EFI_HANDLE parent_image_handle) +{ + UINTN reduction = 0; + for (UINTN i = 0; i < num_section_cache_entries; i++) { + struct shim_section_cache_entry *this_entry = §ion_cache[i]; + + if (this_entry->parent_image_handle != parent_image_handle) + continue; + + reduction += 1; + CopyMem(&this_entry[1], &this_entry[0], sizeof(*this_entry) * (num_section_cache_entries - i - 1)); + } + + ZeroMem(§ion_cache[num_section_cache_entries - reduction], + sizeof(section_cache[0]) * reduction); + + num_section_cache_entries -= reduction; +} /* * Calculate the SHA1 and SHA256 hashes of a binary @@ -285,14 +415,16 @@ goto done; } -#if 1 - } -#else // we have to migrate to doing this later :/ SumOfBytesHashed += hashsize; } - /* Hash all remaining data */ - if (datasize > SumOfBytesHashed) { + /* Hash all remaining data. If SecDir->Size is > 0 this code should not + * be entered. If it is, there are still things to hash. For a file + * without a SecDir, we need to hash what remains. */ + if (datasize > SumOfBytesHashed + context->SecDir->Size) { + char padbuf[8]; + ZeroMem(padbuf, 8); + hashbase = data + SumOfBytesHashed; hashsize = datasize - SumOfBytesHashed; @@ -306,8 +438,17 @@ } SumOfBytesHashed += hashsize; + hashsize = ALIGN_VALUE(SumOfBytesHashed, 8) - SumOfBytesHashed; + + if (hashsize) { + if (!(Sha256Update(sha256ctx, padbuf, hashsize)) || + !(Sha1Update(sha1ctx, padbuf, hashsize))) { + perror(L"Unable to generate hash\n"); + efi_status = EFI_OUT_OF_RESOURCES; + goto done; + } + } } -#endif if (!(Sha256Final(sha256ctx, sha256hash)) || !(Sha1Final(sha1ctx, sha1hash))) { @@ -395,149 +536,6 @@ 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 +547,7 @@ /* * 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; @@ -561,7 +559,8 @@ */ if (secure_mode()) { efi_status = verify_buffer(data, datasize, - context, sha256hash, sha1hash); + context, sha256hash, sha1hash, + false); if (EFI_ERROR(efi_status)) { if (verbose) console_print(L"Verification failed: %r\n", efi_status); @@ -605,10 +604,11 @@ */ EFI_STATUS handle_image (void *data, unsigned int datasize, - EFI_LOADED_IMAGE *li, + EFI_LOADED_IMAGE *li, EFI_HANDLE image_handle, EFI_IMAGE_ENTRY_POINT *entry_point, EFI_PHYSICAL_ADDRESS *alloc_address, - UINTN *alloc_pages) + UINTN *alloc_pages, unsigned int *alloc_alignment, + bool parent_verified) { EFI_STATUS efi_status; char *buffer; @@ -617,7 +617,7 @@ char *base, *end; UINT32 size; PE_COFF_LOADER_IMAGE_CONTEXT context; - unsigned int alignment, alloc_size; + unsigned int alloc_size; int found_entry_point = 0; UINT8 sha1hash[SHA1_DIGEST_SIZE]; UINT8 sha256hash[SHA256_DIGEST_SIZE]; @@ -625,7 +625,7 @@ /* * 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; @@ -637,10 +637,10 @@ */ if (secure_mode ()) { efi_status = verify_buffer(data, datasize, &context, sha256hash, - sha1hash); + sha1hash, parent_verified); 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); @@ -652,29 +652,37 @@ } /* - * Calculate the hash for the TPM measurement. - * XXX: We're computing these twice in secure boot mode when the - * buffers already contain the previously computed hashes. Also, - * this is only useful for the TPM1.2 case. We should try to fix - * this in a follow-up. + * We had originally thought about making this much more granular + * and logging the child section hashes in the event log, but the + * EFI APIs give us extend-without-logging but not + * logging-without-extending, so there's no point. */ - efi_status = generate_hash(data, datasize, &context, sha256hash, - sha1hash); - if (EFI_ERROR(efi_status)) - return efi_status; + if (!parent_verified) { + /* + * Calculate the hash for the TPM measurement. + * XXX: We're computing these twice in secure boot mode when the + * buffers already contain the previously computed hashes. Also, + * this is only useful for the TPM1.2 case. We should try to fix + * this in a follow-up. + */ + efi_status = generate_hash(data, datasize, &context, sha256hash, + sha1hash); + if (EFI_ERROR(efi_status)) + return efi_status; - /* Measure the binary into the TPM */ + /* Measure the binary into the TPM */ #ifdef REQUIRE_TPM - efi_status = + efi_status = #endif - tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize, - (EFI_PHYSICAL_ADDRESS)(UINTN)context.ImageAddress, - li->FilePath, sha1hash, 4); + tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize, + (EFI_PHYSICAL_ADDRESS)(UINTN)context.ImageAddress, + li->FilePath, sha1hash, 4); #ifdef REQUIRE_TPM - if (efi_status != EFI_SUCCESS) { - return efi_status; - } + if (efi_status != EFI_SUCCESS) { + return efi_status; + } #endif + } /* The spec says, uselessly, of SectionAlignment: * ===== @@ -690,9 +698,9 @@ * * We only support one page size, so if it's zero, nerf it to 4096. */ - alignment = context.SectionAlignment; - if (!alignment) - alignment = 4096; + *alloc_alignment = context.SectionAlignment; + if (!*alloc_alignment) + *alloc_alignment = 4096; alloc_size = ALIGN_VALUE(context.ImageSize + context.SectionAlignment, PAGE_SIZE); @@ -705,7 +713,7 @@ return EFI_OUT_OF_RESOURCES; } - buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment); + buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, *alloc_alignment); dprint(L"Loading 0x%llx bytes at 0x%llx\n", (unsigned long long)context.ImageSize, (unsigned long long)(uintptr_t)buffer); @@ -871,13 +879,15 @@ } /* - * Now set the page permissions appropriately. + * Now set the page permissions appropriately and cache appropriate + * section sizes, and digests. */ Section = context.FirstSection; for (i = 0; i < context.NumberOfSections; i++, Section++) { uint64_t set_attrs = MEM_ATTR_R; uint64_t clear_attrs = MEM_ATTR_W|MEM_ATTR_X; uintptr_t addr; + uint64_t raw_length; uint64_t length; /* @@ -904,7 +914,8 @@ // platforms generally set memory attributes at page // granularity, but the section length (unlike the section // address) is not required to be aligned. - length = ALIGN_VALUE((uintptr_t)end - (uintptr_t)base + 1, PAGE_SIZE); + raw_length = (uintptr_t)end - (uintptr_t)base + 1; + length = ALIGN_VALUE(raw_length, PAGE_SIZE); if (Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) { set_attrs |= MEM_ATTR_W; @@ -915,8 +926,28 @@ clear_attrs &= ~MEM_ATTR_X; } update_mem_attrs(addr, length, set_attrs, clear_attrs); - } + /* + * We only cache CODE and INITIALIZED data sections that + * are marked readable. Also, don't cache sections on the + * second level deep... + */ + if ((Section->Characteristics & EFI_IMAGE_SCN_CNT_CODE || + Section->Characteristics & EFI_IMAGE_SCN_CNT_INITIALIZED_DATA) && + Section->Characteristics & EFI_IMAGE_SCN_MEM_READ && + !parent_verified) { + efi_status = cache_section(image_handle, Section->Name, base, raw_length); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to cache section details\n"); + BS->FreePages(*alloc_address, *alloc_pages); + return efi_status; + } + dprint(L"Cached section %d (%a) at 0x%016llx, size 0x%016llx\n", + i, Section->Name, + (unsigned long long)(uintptr_t)base, + (unsigned long long)raw_length); + } + } /* * grub needs to know its location and size in memory, so fix up @@ -931,11 +962,13 @@ if (!found_entry_point) { perror(L"Entry point is not within sections\n"); + flush_cached_sections(image_handle); BS->FreePages(*alloc_address, *alloc_pages); return EFI_UNSUPPORTED; } if (found_entry_point > 1) { perror(L"%d sections contain entry point\n", found_entry_point); + flush_cached_sections(image_handle); BS->FreePages(*alloc_address, *alloc_pages); return EFI_UNSUPPORTED; } diff -Nru shim-15.8/post-process-pe.c shim-16.1/post-process-pe.c --- shim-15.8/post-process-pe.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/post-process-pe.c 1970-01-01 00:00:00.000000000 +0000 @@ -43,6 +43,7 @@ }) static bool set_nx_compat = false; +static bool require_nx_compat = false; typedef uint8_t UINT8; typedef uint16_t UINT16; @@ -162,6 +163,7 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ {.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 @@ case 'v': verbosity = MIN(verbosity + 1, MAX_VERBOSITY); break; + case 'x': + require_nx_compat = true; + break; } } diff -Nru shim-15.8/replacements.c shim-16.1/replacements.c --- shim-15.8/replacements.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/replacements.c 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru shim-15.8/sbat.c shim-16.1/sbat.c --- shim-15.8/sbat.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/sbat.c 1970-01-01 00:00:00.000000000 +0000 @@ -537,9 +537,9 @@ */ 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 -Nru shim-15.8/sbat_var.S shim-16.1/sbat_var.S --- shim-15.8/sbat_var.S 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/sbat_var.S 1970-01-01 00:00:00.000000000 +0000 @@ -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 -Nru shim-15.8/shim.c shim-16.1/shim.c --- shim-15.8/shim.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/shim.c 1970-01-01 00:00:00.000000000 +0000 @@ -16,25 +16,8 @@ #include "shim_cert.h" #endif /* defined(ENABLE_SHIM_CERT) */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - #include -#define OID_EKU_MODSIGN "1.3.6.1.4.1.2312.16.1.2" - static EFI_SYSTEM_TABLE *systab; static EFI_HANDLE global_image_handle; static EFI_LOADED_IMAGE *shim_li; @@ -52,373 +35,11 @@ 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, - VAR_NOT_FOUND -} CHECK_STATUS; - typedef struct { UINT32 MokSize; UINT8 *Mok; } MokListNode; -static void -drain_openssl_errors(void) -{ - unsigned long err = -1; - while (err != 0) - err = ERR_get_error(); -} - -static BOOLEAN verify_x509(UINT8 *Cert, UINTN CertSize) -{ - UINTN length; - - if (!Cert || CertSize < 4) - return FALSE; - - /* - * A DER encoding x509 certificate starts with SEQUENCE(0x30), - * the number of length bytes, and the number of value bytes. - * The size of a x509 certificate is usually between 127 bytes - * and 64KB. For convenience, assume the number of value bytes - * is 2, i.e. the second byte is 0x82. - */ - if (Cert[0] != 0x30 || Cert[1] != 0x82) { - dprint(L"cert[0:1] is [%02x%02x], should be [%02x%02x]\n", - Cert[0], Cert[1], 0x30, 0x82); - return FALSE; - } - - length = Cert[2]<<8 | Cert[3]; - if (length != (CertSize - 4)) { - dprint(L"Cert length is %ld, expecting %ld\n", - length, CertSize); - return FALSE; - } - - return TRUE; -} - -static BOOLEAN verify_eku(UINT8 *Cert, UINTN CertSize) -{ - X509 *x509; - CONST UINT8 *Temp = Cert; - EXTENDED_KEY_USAGE *eku; - ASN1_OBJECT *module_signing; - - module_signing = OBJ_nid2obj(OBJ_create(OID_EKU_MODSIGN, - "modsign-eku", - "modsign-eku")); - - x509 = d2i_X509 (NULL, &Temp, (long) CertSize); - if (x509 != NULL) { - eku = X509_get_ext_d2i(x509, NID_ext_key_usage, NULL, NULL); - - if (eku) { - int i = 0; - for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) { - ASN1_OBJECT *key_usage = sk_ASN1_OBJECT_value(eku, i); - - if (OBJ_cmp(module_signing, key_usage) == 0) - return FALSE; - } - EXTENDED_KEY_USAGE_free(eku); - } - - X509_free(x509); - } - - OBJ_cleanup(); - - return TRUE; -} - -static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList, - UINTN dbsize, - WIN_CERTIFICATE_EFI_PKCS *data, - UINT8 *hash, CHAR16 *dbname, - EFI_GUID guid) -{ - EFI_SIGNATURE_DATA *Cert; - UINTN CertSize; - BOOLEAN IsFound = FALSE; - int i = 0; - - while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { - if (CompareGuid (&CertList->SignatureType, &EFI_CERT_TYPE_X509_GUID) == 0) { - Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); - CertSize = CertList->SignatureSize - sizeof(EFI_GUID); - dprint(L"trying to verify cert %d (%s)\n", i++, dbname); - if (verify_x509(Cert->SignatureData, CertSize)) { - if (verify_eku(Cert->SignatureData, CertSize)) { - drain_openssl_errors(); - IsFound = AuthenticodeVerify (data->CertData, - data->Hdr.dwLength - sizeof(data->Hdr), - Cert->SignatureData, - CertSize, - hash, SHA256_DIGEST_SIZE); - if (IsFound) { - dprint(L"AuthenticodeVerify() succeeded: %d\n", IsFound); - tpm_measure_variable(dbname, guid, CertList->SignatureSize, Cert); - drain_openssl_errors(); - return DATA_FOUND; - } else { - LogError(L"AuthenticodeVerify(): %d\n", IsFound); - } - } - } else if (verbose) { - console_print(L"Not a DER encoded x.509 Certificate"); - dprint(L"cert:\n"); - dhexdumpat(Cert->SignatureData, CertSize, 0); - } - } - - dbsize -= CertList->SignatureListSize; - CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); - } - - return DATA_NOT_FOUND; -} - -static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid, - WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash) -{ - CHECK_STATUS rc; - EFI_STATUS efi_status; - EFI_SIGNATURE_LIST *CertList; - UINTN dbsize = 0; - UINT8 *db; - - efi_status = get_variable(dbname, &db, &dbsize, guid); - if (EFI_ERROR(efi_status)) - return VAR_NOT_FOUND; - - CertList = (EFI_SIGNATURE_LIST *)db; - - rc = check_db_cert_in_ram(CertList, dbsize, data, hash, dbname, guid); - - FreePool(db); - - return rc; -} - -/* - * Check a hash against an EFI_SIGNATURE_LIST in a buffer - */ -static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList, - UINTN dbsize, UINT8 *data, - int SignatureSize, EFI_GUID CertType, - CHAR16 *dbname, EFI_GUID guid) -{ - EFI_SIGNATURE_DATA *Cert; - UINTN CertCount, Index; - BOOLEAN IsFound = FALSE; - - while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { - CertCount = (CertList->SignatureListSize -sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; - Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); - if (CompareGuid(&CertList->SignatureType, &CertType) == 0) { - for (Index = 0; Index < CertCount; Index++) { - if (CompareMem (Cert->SignatureData, data, SignatureSize) == 0) { - // - // Find the signature in database. - // - IsFound = TRUE; - tpm_measure_variable(dbname, guid, CertList->SignatureSize, Cert); - break; - } - - Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); - } - if (IsFound) { - break; - } - } - - dbsize -= CertList->SignatureListSize; - CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); - } - - if (IsFound) - return DATA_FOUND; - - return DATA_NOT_FOUND; -} - -/* - * Check a hash against an EFI_SIGNATURE_LIST in a UEFI variable - */ -static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data, - int SignatureSize, EFI_GUID CertType) -{ - EFI_STATUS efi_status; - EFI_SIGNATURE_LIST *CertList; - UINTN dbsize = 0; - UINT8 *db; - - efi_status = get_variable(dbname, &db, &dbsize, guid); - if (EFI_ERROR(efi_status)) { - return VAR_NOT_FOUND; - } - - CertList = (EFI_SIGNATURE_LIST *)db; - - CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data, - SignatureSize, CertType, - dbname, guid); - FreePool(db); - return rc; - -} - -/* - * Check whether the binary signature or hash are present in dbx or the - * built-in denylist - */ -static EFI_STATUS check_denylist (WIN_CERTIFICATE_EFI_PKCS *cert, - UINT8 *sha256hash, UINT8 *sha1hash) -{ - EFI_SIGNATURE_LIST *dbx = (EFI_SIGNATURE_LIST *)vendor_deauthorized; - - if (check_db_hash_in_ram(dbx, vendor_deauthorized_size, sha256hash, - SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID, L"dbx", - EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { - LogError(L"binary sha256hash found in vendor dbx\n"); - return EFI_SECURITY_VIOLATION; - } - if (check_db_hash_in_ram(dbx, vendor_deauthorized_size, sha1hash, - SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID, L"dbx", - EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { - LogError(L"binary sha1hash found in vendor dbx\n"); - return EFI_SECURITY_VIOLATION; - } - if (cert && - check_db_cert_in_ram(dbx, vendor_deauthorized_size, cert, sha256hash, L"dbx", - EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { - LogError(L"cert sha256hash found in vendor dbx\n"); - return EFI_SECURITY_VIOLATION; - } - if (check_db_hash(L"dbx", EFI_SECURE_BOOT_DB_GUID, sha256hash, - SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == DATA_FOUND) { - LogError(L"binary sha256hash found in system dbx\n"); - return EFI_SECURITY_VIOLATION; - } - if (check_db_hash(L"dbx", EFI_SECURE_BOOT_DB_GUID, sha1hash, - SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID) == DATA_FOUND) { - LogError(L"binary sha1hash found in system dbx\n"); - return EFI_SECURITY_VIOLATION; - } - if (cert && - check_db_cert(L"dbx", EFI_SECURE_BOOT_DB_GUID, - cert, sha256hash) == DATA_FOUND) { - LogError(L"cert sha256hash found in system dbx\n"); - return EFI_SECURITY_VIOLATION; - } - if (check_db_hash(L"MokListX", SHIM_LOCK_GUID, sha256hash, - SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == DATA_FOUND) { - LogError(L"binary sha256hash found in Mok dbx\n"); - return EFI_SECURITY_VIOLATION; - } - if (cert && - check_db_cert(L"MokListX", SHIM_LOCK_GUID, - cert, sha256hash) == DATA_FOUND) { - LogError(L"cert sha256hash found in Mok dbx\n"); - return EFI_SECURITY_VIOLATION; - } - - drain_openssl_errors(); - return EFI_SUCCESS; -} - -static void update_verification_method(verification_method_t method) -{ - if (verification_method == VERIFIED_BY_NOTHING) - verification_method = method; -} - -/* - * Check whether the binary signature or hash are present in db or MokList - */ -static EFI_STATUS check_allowlist (WIN_CERTIFICATE_EFI_PKCS *cert, - UINT8 *sha256hash, UINT8 *sha1hash) -{ - if (!ignore_db) { - if (check_db_hash(L"db", EFI_SECURE_BOOT_DB_GUID, sha256hash, SHA256_DIGEST_SIZE, - EFI_CERT_SHA256_GUID) == DATA_FOUND) { - update_verification_method(VERIFIED_BY_HASH); - return EFI_SUCCESS; - } else { - LogError(L"check_db_hash(db, sha256hash) != DATA_FOUND\n"); - } - if (check_db_hash(L"db", EFI_SECURE_BOOT_DB_GUID, sha1hash, SHA1_DIGEST_SIZE, - EFI_CERT_SHA1_GUID) == DATA_FOUND) { - verification_method = VERIFIED_BY_HASH; - update_verification_method(VERIFIED_BY_HASH); - return EFI_SUCCESS; - } else { - LogError(L"check_db_hash(db, sha1hash) != DATA_FOUND\n"); - } - if (cert && check_db_cert(L"db", EFI_SECURE_BOOT_DB_GUID, cert, sha256hash) - == DATA_FOUND) { - verification_method = VERIFIED_BY_CERT; - update_verification_method(VERIFIED_BY_CERT); - return EFI_SUCCESS; - } else if (cert) { - LogError(L"check_db_cert(db, sha256hash) != DATA_FOUND\n"); - } - } - -#if defined(VENDOR_DB_FILE) - EFI_SIGNATURE_LIST *db = (EFI_SIGNATURE_LIST *)vendor_db; - - if (check_db_hash_in_ram(db, vendor_db_size, - sha256hash, SHA256_DIGEST_SIZE, - EFI_CERT_SHA256_GUID, L"vendor_db", - EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { - verification_method = VERIFIED_BY_HASH; - update_verification_method(VERIFIED_BY_HASH); - return EFI_SUCCESS; - } else { - LogError(L"check_db_hash(vendor_db, sha256hash) != DATA_FOUND\n"); - } - if (cert && - check_db_cert_in_ram(db, vendor_db_size, - cert, sha256hash, L"vendor_db", - EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { - verification_method = VERIFIED_BY_CERT; - update_verification_method(VERIFIED_BY_CERT); - return EFI_SUCCESS; - } else if (cert) { - LogError(L"check_db_cert(vendor_db, sha256hash) != DATA_FOUND\n"); - } -#endif - - if (check_db_hash(L"MokListRT", SHIM_LOCK_GUID, sha256hash, - SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) - == DATA_FOUND) { - verification_method = VERIFIED_BY_HASH; - update_verification_method(VERIFIED_BY_HASH); - return EFI_SUCCESS; - } else { - LogError(L"check_db_hash(MokListRT, sha256hash) != DATA_FOUND\n"); - } - if (cert && check_db_cert(L"MokListRT", SHIM_LOCK_GUID, cert, sha256hash) - == DATA_FOUND) { - verification_method = VERIFIED_BY_CERT; - update_verification_method(VERIFIED_BY_CERT); - return EFI_SUCCESS; - } else if (cert) { - LogError(L"check_db_cert(MokListRT, sha256hash) != DATA_FOUND\n"); - } - - update_verification_method(VERIFIED_BY_NOTHING); - return EFI_NOT_FOUND; -} - /* * Check whether we're in Secure Boot and user mode */ @@ -458,361 +79,6 @@ return TRUE; } -static EFI_STATUS -verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig, - UINT8 *sha256hash, UINT8 *sha1hash) -{ - EFI_STATUS efi_status; - - /* - * Ensure that the binary isn't forbidden - */ - drain_openssl_errors(); - efi_status = check_denylist(sig, sha256hash, sha1hash); - if (EFI_ERROR(efi_status)) { - perror(L"Binary is forbidden: %r\n", efi_status); - PrintErrors(); - ClearErrors(); - crypterr(efi_status); - return efi_status; - } - - /* - * Check whether the binary is authorized in any of the firmware - * databases - */ - drain_openssl_errors(); - efi_status = check_allowlist(sig, sha256hash, sha1hash); - if (EFI_ERROR(efi_status)) { - if (efi_status != EFI_NOT_FOUND) { - dprint(L"check_allowlist(): %r\n", efi_status); - PrintErrors(); - ClearErrors(); - crypterr(efi_status); - } - } else { - drain_openssl_errors(); - return efi_status; - } - - efi_status = EFI_NOT_FOUND; -#if defined(ENABLE_SHIM_CERT) - /* - * Check against the shim build key - */ - drain_openssl_errors(); - if (build_cert && build_cert_size) { - dprint("verifying against shim cert\n"); - } - if (build_cert && build_cert_size && - AuthenticodeVerify(sig->CertData, - sig->Hdr.dwLength - sizeof(sig->Hdr), - build_cert, build_cert_size, sha256hash, - SHA256_DIGEST_SIZE)) { - dprint(L"AuthenticodeVerify(shim_cert) succeeded\n"); - update_verification_method(VERIFIED_BY_CERT); - tpm_measure_variable(L"Shim", SHIM_LOCK_GUID, - build_cert_size, build_cert); - efi_status = EFI_SUCCESS; - drain_openssl_errors(); - return efi_status; - } else { - dprint(L"AuthenticodeVerify(shim_cert) failed\n"); - PrintErrors(); - ClearErrors(); - crypterr(EFI_NOT_FOUND); - } -#endif /* defined(ENABLE_SHIM_CERT) */ - -#if defined(VENDOR_CERT_FILE) - /* - * And finally, check against shim's built-in key - */ - drain_openssl_errors(); - if (vendor_cert_size) { - dprint("verifying against vendor_cert\n"); - } - if (vendor_cert_size && - AuthenticodeVerify(sig->CertData, - sig->Hdr.dwLength - sizeof(sig->Hdr), - vendor_cert, vendor_cert_size, - sha256hash, SHA256_DIGEST_SIZE)) { - dprint(L"AuthenticodeVerify(vendor_cert) succeeded\n"); - update_verification_method(VERIFIED_BY_CERT); - tpm_measure_variable(L"Shim", SHIM_LOCK_GUID, - vendor_cert_size, vendor_cert); - efi_status = EFI_SUCCESS; - drain_openssl_errors(); - return efi_status; - } else { - dprint(L"AuthenticodeVerify(vendor_cert) failed\n"); - PrintErrors(); - ClearErrors(); - crypterr(EFI_NOT_FOUND); - } -#endif /* defined(VENDOR_CERT_FILE) */ - - return efi_status; -} - -/* - * Check that the signature is valid and matches the binary - */ -EFI_STATUS -verify_buffer_authenticode (char *data, int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - UINT8 *sha256hash, UINT8 *sha1hash) -{ - EFI_STATUS ret_efi_status; - size_t size = datasize; - size_t offset = 0; - unsigned int i = 0; - - if (datasize < 0) - return EFI_INVALID_PARAMETER; - - /* - * Clear OpenSSL's error log, because we get some DSO unimplemented - * errors during its intialization, and we don't want those to look - * like they're the reason for validation failures. - */ - drain_openssl_errors(); - - ret_efi_status = generate_hash(data, datasize, context, sha256hash, sha1hash); - if (EFI_ERROR(ret_efi_status)) { - dprint(L"generate_hash: %r\n", ret_efi_status); - PrintErrors(); - ClearErrors(); - crypterr(ret_efi_status); - return ret_efi_status; - } - - /* - * Ensure that the binary isn't forbidden by hash - */ - drain_openssl_errors(); - ret_efi_status = check_denylist(NULL, sha256hash, sha1hash); - if (EFI_ERROR(ret_efi_status)) { -// perror(L"Binary is forbidden\n"); -// dprint(L"Binary is forbidden: %r\n", ret_efi_status); - PrintErrors(); - ClearErrors(); - crypterr(ret_efi_status); - return ret_efi_status; - } - - /* - * Check whether the binary is authorized by hash in any of the - * firmware databases - */ - drain_openssl_errors(); - ret_efi_status = check_allowlist(NULL, sha256hash, sha1hash); - if (EFI_ERROR(ret_efi_status)) { - LogError(L"check_allowlist(): %r\n", ret_efi_status); - dprint(L"check_allowlist: %r\n", ret_efi_status); - if (ret_efi_status != EFI_NOT_FOUND) { - dprint(L"check_allowlist(): %r\n", ret_efi_status); - PrintErrors(); - ClearErrors(); - crypterr(ret_efi_status); - return ret_efi_status; - } - } else { - drain_openssl_errors(); - return ret_efi_status; - } - - if (context->SecDir->Size == 0) { - dprint(L"No signatures found\n"); - return EFI_SECURITY_VIOLATION; - } - - if (checked_add(context->SecDir->Size, context->SecDir->VirtualAddress, &offset) || - offset > size) { - perror(L"Certificate Database size is too large\n"); - return EFI_INVALID_PARAMETER; - } - - offset = 0; - ret_efi_status = EFI_NOT_FOUND; - do { - WIN_CERTIFICATE_EFI_PKCS *sig = NULL; - size_t sz; - - sig = ImageAddress(data, size, - context->SecDir->VirtualAddress + offset); - if (!sig) - break; - - if ((uint64_t)(uintptr_t)&sig[1] - > (uint64_t)(uintptr_t)data + datasize) { - perror(L"Certificate size is too large for secruity database"); - return EFI_INVALID_PARAMETER; - } - - sz = offset + offsetof(WIN_CERTIFICATE_EFI_PKCS, Hdr.dwLength) - + sizeof(sig->Hdr.dwLength); - if (sz > context->SecDir->Size) { - perror(L"Certificate size is too large for secruity database"); - return EFI_INVALID_PARAMETER; - } - - sz = sig->Hdr.dwLength; - if (sz > context->SecDir->Size - offset) { - perror(L"Certificate size is too large for secruity database"); - return EFI_INVALID_PARAMETER; - } - - if (sz < sizeof(sig->Hdr)) { - perror(L"Certificate size is too small for certificate data"); - return EFI_INVALID_PARAMETER; - } - - if (sig->Hdr.wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { - EFI_STATUS efi_status; - - dprint(L"Attempting to verify signature %d:\n", i++); - - efi_status = verify_one_signature(sig, sha256hash, sha1hash); - - /* - * If we didn't get EFI_SECURITY_VIOLATION from - * checking the hashes above, then any dbx entries are - * for a certificate, not this individual binary. - * - * So don't clobber successes with security violation - * here; that just means it isn't a success. - */ - if (ret_efi_status != EFI_SUCCESS) - ret_efi_status = efi_status; - } else { - perror(L"Unsupported certificate type %x\n", - sig->Hdr.wCertificateType); - } - offset = ALIGN_VALUE(offset + sz, 8); - } while (offset < context->SecDir->Size); - - if (ret_efi_status != EFI_SUCCESS) { - dprint(L"Binary is not authorized\n"); - PrintErrors(); - ClearErrors(); - crypterr(EFI_SECURITY_VIOLATION); - ret_efi_status = EFI_SECURITY_VIOLATION; - } - drain_openssl_errors(); - return ret_efi_status; -} - -/* - * Check that the binary is permitted to load by SBAT. - */ -EFI_STATUS -verify_buffer_sbat (char *data, int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context) -{ - int i; - EFI_IMAGE_SECTION_HEADER *Section; - char *SBATBase = NULL; - size_t SBATSize = 0; - - Section = context->FirstSection; - for (i = 0; i < context->NumberOfSections; i++, Section++) { - if ((uint64_t)(uintptr_t)&Section[1] - > (uintptr_t)(uintptr_t)data + datasize) { - perror(L"Section exceeds bounds of image\n"); - return EFI_UNSUPPORTED; - } - - if (CompareMem(Section->Name, ".sbat\0\0\0", 8) != 0) - continue; - - if (SBATBase || SBATSize) { - perror(L"Image has multiple SBAT sections\n"); - return EFI_UNSUPPORTED; - } - - if (Section->NumberOfRelocations != 0 || - Section->PointerToRelocations != 0) { - perror(L"SBAT section has relocations\n"); - return EFI_UNSUPPORTED; - } - - /* The virtual size corresponds to the size of the SBAT - * metadata and isn't necessarily a multiple of the file - * alignment. The on-disk size is a multiple of the file - * alignment and is zero padded. Make sure that the - * on-disk size is at least as large as virtual size, - * and ignore the section if it isn't. */ - if (Section->SizeOfRawData && - Section->SizeOfRawData >= Section->Misc.VirtualSize) { - uint64_t boundary; - SBATBase = ImageAddress(data, datasize, - Section->PointerToRawData); - SBATSize = Section->SizeOfRawData; - dprint(L"sbat section base:0x%lx size:0x%lx\n", - SBATBase, SBATSize); - if (checked_add((uint64_t)(uintptr_t)SBATBase, SBATSize, &boundary) || - (boundary > (uint64_t)(uintptr_t)data + datasize)) { - perror(L"Section exceeds bounds of image\n"); - return EFI_UNSUPPORTED; - } - } - } - - return verify_sbat_section(SBATBase, SBATSize); -} - -/* - * Check that the signature is valid and matches the binary and that - * the binary is permitted to load by SBAT. - */ -EFI_STATUS -verify_buffer (char *data, int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - UINT8 *sha256hash, UINT8 *sha1hash) -{ - EFI_STATUS efi_status; - - efi_status = verify_buffer_authenticode(data, datasize, context, sha256hash, sha1hash); - if (EFI_ERROR(efi_status)) - return efi_status; - - return verify_buffer_sbat(data, datasize, context); -} - -static int -is_removable_media_path(EFI_LOADED_IMAGE *li) -{ - unsigned int pathlen = 0; - CHAR16 *bootpath = NULL; - int ret = 0; - - bootpath = DevicePathToStr(li->FilePath); - - /* Check the beginning of the string and the end, to avoid - * caring about which arch this is. */ - /* I really don't know why, but sometimes bootpath gives us - * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here... - */ - if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && - StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15) && - StrnCaseCmp(bootpath, L"EFI\\BOOT\\BOOT", 13) && - StrnCaseCmp(bootpath, L"EFI\\BOOT\\/BOOT", 14)) - goto error; - - pathlen = StrLen(bootpath); - if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) - goto error; - - ret = 1; - -error: - if (bootpath) - FreePool(bootpath); - - return ret; -} - static int should_use_fallback(EFI_HANDLE image_handle) { @@ -980,84 +246,6 @@ return efi_status; } -/* - * Protocol entry point. If secure boot is enabled, verify that the provided - * buffer is signed with a trusted key. - */ -EFI_STATUS shim_verify (void *buffer, UINT32 size) -{ - EFI_STATUS efi_status = EFI_SUCCESS; - PE_COFF_LOADER_IMAGE_CONTEXT context; - UINT8 sha1hash[SHA1_DIGEST_SIZE]; - UINT8 sha256hash[SHA256_DIGEST_SIZE]; - - if ((INT32)size < 0) - return EFI_INVALID_PARAMETER; - - loader_is_participating = 1; - in_protocol = 1; - - efi_status = read_header(buffer, size, &context); - if (EFI_ERROR(efi_status)) - goto done; - - efi_status = generate_hash(buffer, size, &context, - sha256hash, sha1hash); - if (EFI_ERROR(efi_status)) - goto done; - - /* Measure the binary into the TPM */ -#ifdef REQUIRE_TPM - efi_status = -#endif - tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)buffer, size, 0, NULL, - sha1hash, 4); -#ifdef REQUIRE_TPM - if (EFI_ERROR(efi_status)) - goto done; -#endif - - if (!secure_mode()) { - efi_status = EFI_SUCCESS; - goto done; - } - - efi_status = verify_buffer(buffer, size, - &context, sha256hash, sha1hash); -done: - in_protocol = 0; - return efi_status; -} - -static EFI_STATUS shim_hash (char *data, int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - UINT8 *sha256hash, UINT8 *sha1hash) -{ - EFI_STATUS efi_status; - - if (datasize < 0) - return EFI_INVALID_PARAMETER; - - in_protocol = 1; - efi_status = generate_hash(data, datasize, context, - sha256hash, sha1hash); - in_protocol = 0; - - return efi_status; -} - -static EFI_STATUS shim_read_header(void *data, unsigned int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context) -{ - EFI_STATUS efi_status; - - in_protocol = 1; - efi_status = read_header(data, datasize, context); - in_protocol = 0; - - return efi_status; -} - VOID restore_loaded_image(VOID) { @@ -1091,7 +279,8 @@ * 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 +317,11 @@ } 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 +333,9 @@ &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 +347,7 @@ 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; @@ -1181,9 +372,10 @@ CHAR16 *PathName = NULL; void *data = NULL; int datasize = 0; + unsigned int alloc_alignment; efi_status = read_image(image_handle, ImagePath, &PathName, &data, - &datasize); + &datasize, 0); if (EFI_ERROR(efi_status)) goto done; @@ -1206,8 +398,9 @@ /* * Verify and, if appropriate, relocate and execute the executable */ - efi_status = handle_image(data, datasize, shim_li, &entry_point, - &alloc_address, &alloc_pages); + efi_status = handle_image(data, datasize, shim_li, image_handle, + &entry_point, &alloc_address, &alloc_pages, + &alloc_alignment, false); if (EFI_ERROR(efi_status)) { perror(L"Failed to load image: %r\n", efi_status); PrintErrors(); @@ -1215,7 +408,9 @@ goto restore; } - loader_is_participating = 0; +#if 0 + save_logs(); +#endif /* * The binary is trusted and relocated. Run it @@ -1257,10 +452,15 @@ 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 +486,7 @@ 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; @@ -1317,32 +517,6 @@ return EFI_SUCCESS; } -static void -init_openssl(void) -{ - OPENSSL_init(); - ERR_load_ERR_strings(); - ERR_load_BN_strings(); - ERR_load_RSA_strings(); - ERR_load_DH_strings(); - ERR_load_EVP_strings(); - ERR_load_BUF_strings(); - ERR_load_OBJ_strings(); - ERR_load_PEM_strings(); - ERR_load_X509_strings(); - ERR_load_ASN1_strings(); - ERR_load_CONF_strings(); - ERR_load_CRYPTO_strings(); - ERR_load_COMP_strings(); - ERR_load_BIO_strings(); - ERR_load_PKCS7_strings(); - ERR_load_X509V3_strings(); - ERR_load_PKCS12_strings(); - ERR_load_RAND_strings(); - ERR_load_DSO_strings(); - ERR_load_OCSP_strings(); -} - static SHIM_LOCK shim_lock_interface; static EFI_HANDLE shim_lock_handle; @@ -1373,13 +547,17 @@ 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 +583,12 @@ /* * 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 +626,7 @@ 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 +641,12 @@ 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 +692,8 @@ } 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 +701,58 @@ 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 +769,7 @@ 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 +777,7 @@ 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 +798,17 @@ 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 +878,17 @@ } /* - * 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 +899,14 @@ } } - 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 +915,86 @@ 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 +1017,6 @@ * validation of the next image. */ hook_system_services(systab); - loader_is_participating = 0; } } @@ -1746,11 +1042,12 @@ 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 +1157,7 @@ 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 +1195,8 @@ */ 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 +1228,7 @@ 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 +1237,8 @@ } init_openssl(); + get_hsi_mem_info(); + set_shim_nx_policy(); efi_status = load_unbundled_trust(global_image_handle); if (EFI_ERROR(efi_status)) { @@ -1981,6 +1282,8 @@ */ (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 -Nru shim-15.8/shim.h shim-16.1/shim.h --- shim-15.8/shim.h 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/shim.h 1970-01-01 00:00:00.000000000 +0000 @@ -55,6 +55,7 @@ #ifndef SHIM_UNIT_TEST #include #include +#include #undef uefi_call_wrapper #include #include @@ -163,7 +164,9 @@ #include "include/configtable.h" #include "include/console.h" #include "include/crypt_blowfish.h" +#include "include/dp.h" #include "include/efiauthenticated.h" +#include "include/errlog.h" #include "include/errors.h" #include "include/execute.h" #include "include/guid.h" @@ -172,12 +175,13 @@ #include "include/ip4config2.h" #include "include/ip6config.h" #include "include/load-options.h" +#include "include/loader-proto.h" +#include "include/memattrs.h" #include "include/mok.h" #include "include/netboot.h" #include "include/passwordcrypt.h" #include "include/peimage.h" #include "include/pe.h" -#include "include/replacements.h" #include "include/sbat.h" #include "include/sbat_var_defs.h" #include "include/ssp.h" @@ -187,9 +191,11 @@ #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" +#include "include/verify.h" #include "include/hexdump.h" #include "version.h" @@ -237,17 +243,11 @@ 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; @@ -274,11 +274,6 @@ BOOLEAN secure_mode (void); -EFI_STATUS -verify_buffer (char *data, int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - UINT8 *sha256hash, UINT8 *sha1hash); - #ifndef SHIM_UNIT_TEST #define perror_(file, line, func, fmt, ...) ({ \ UINTN __perror_ret = 0; \ @@ -324,4 +319,20 @@ char *translate_slashes(char *out, const char *str); +#include + +typedef struct { + EFI_LOADED_IMAGE li; + EFI_IMAGE_ENTRY_POINT entry_point; + EFI_PHYSICAL_ADDRESS alloc_address; + UINTN alloc_pages; + unsigned int alloc_alignment; + 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 -Nru shim-15.8/test-load-options.c shim-16.1/test-load-options.c --- shim-15.8/test-load-options.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/test-load-options.c 1970-01-01 00:00:00.000000000 +0000 @@ -68,9 +68,12 @@ 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 @@ } 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 @@ test(test_bds_0); test(test_bds_1); test(test_bds_2); + test(test_multi_end_dp); return status; } diff -Nru shim-15.8/test-mock-variables.c shim-16.1/test-mock-variables.c --- shim-15.8/test-mock-variables.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/test-mock-variables.c 1970-01-01 00:00:00.000000000 +0000 @@ -207,19 +207,37 @@ 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) { + mok_rt_vars[i] = ""; + 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 +254,14 @@ 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 +310,15 @@ 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) { + mok_rt_vars[i] = ""; + continue; + } mok_rt_vars[i] = mok_state_variables[i].rtname8; } diff -Nru shim-15.8/test-mok-mirror.c shim-16.1/test-mok-mirror.c --- shim-15.8/test-mok-mirror.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/test-mok-mirror.c 1970-01-01 00:00:00.000000000 +0000 @@ -8,6 +8,7 @@ #include "mock-variables.h" #include "test-data-efivars-1.h" +#include #include #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -21,21 +22,51 @@ #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 @@ } } +static EFI_STATUS +check_variables(struct test_var *vars) +{ + for (size_t i = 0; vars[i].name != NULL; i++) { + struct test_var *tv = &vars[i]; + list_t *pos = NULL; + bool found = false; + char buf[1]; + UINTN size = 0; + UINT32 attrs = 0; + bool present = false; + + list_for_each(pos, &mock_variables) { + struct mock_variable *var; + bool deleted; + bool created; + int gets = 0; + + var = list_entry(pos, struct mock_variable, list); + if (CompareGuid(&tv->guid, &var->guid) != 0 || + StrCmp(var->name, tv->name) != 0) + continue; + found = true; + assert_equal_goto(var->attrs, tv->attrs, err, + "\"%s\": wrong attrs; got %s expected %s\n", + Str2str(tv->name), + format_var_attrs(var->attrs), + format_var_attrs(tv->attrs)); + for (UINTN j = 0; j < N_TEST_VAR_OPS + && tv->ops[j] != NONE; j++) { + switch (tv->ops[j]) { + case NONE: + default: + break; + case CREATE: + if (tv->results[j] == EFI_SUCCESS) + created = true; + break; + case DELETE: + assert_goto(tv->results[j] != EFI_SUCCESS, err, + "Tried to delete absent variable \"%s\"\n", + Str2str(tv->name)); + assert_goto(created == false, err, + "Deleted variable \"%s\" was previously created.\n", + Str2str(tv->name)); + break; + case APPEND: + assert_goto(false, err, + "No append action should have been tested\n"); + break; + case REPLACE: + assert_goto(false, err, + "No replace action should have been tested\n"); + break; + case GET: + if (tv->results[j] == EFI_SUCCESS) + gets += 1; + break; + } + } + assert_goto(gets == 0 || gets == 1, err, + "Variable should not be read %d times.\n", gets); + } + if (tv->must_be_present) { + /* + * This asserts if it isn't present, and if that's + * the case, then the attributes are already + * validated in the search loop + */ + assert_goto(found == true, err, + "variable \"%s\" was not found.\n", + Str2str(tv->name)); + } + + if (tv->must_be_absent) { + /* + * deliberately does not check the attributes at + * this time. + */ + assert_goto(found == false, err, + "variable \"%s\" was found.\n", + Str2str(tv->name)); + } + } + + return EFI_SUCCESS; +err: + return EFI_INVALID_PARAMETER; +} + +static EFI_STATUS +check_config_table(struct mock_mok_variable_config_entry *test_configs, + uint8_t *config_pos) +{ + size_t i = 0; + struct mok_variable_config_entry *mok_entry = NULL; + struct mock_mok_variable_config_entry *mock_entry = NULL; + + while (config_pos) { + mock_entry = &test_configs[i]; + mok_entry = (struct mok_variable_config_entry *)config_pos; + + /* + * If the tables are different lengths, this will trigger. + */ + assert_equal_goto(mok_entry->name[0], mock_entry->name[0], err, + "mok.name[0] %ld != test.name[0] %ld\n"); + if (mock_entry->name[0] == 0) + break; + + assert_nonzero_goto(mok_entry->name[0], err, "%ld != %ld\n"); + assert_zero_goto(strncmp(mok_entry->name, mock_entry->name, + sizeof(mock_entry->name)), + err, "%ld != %ld: strcmp(\"%s\",\"%s\")\n", + mok_entry->name, mock_entry->name); + + /* + * As of 7501b6bb449f ("mok: fix potential buffer overrun in + * import_mok_state"), we should not see any mok config + * variables with data_size == 0. + */ + assert_nonzero_goto(mok_entry->data_size, err, "%ld != 0\n"); + + assert_equal_goto(mok_entry->data_size, mock_entry->data_size, + err, "%ld != %ld\n"); + assert_zero_goto(CompareMem(mok_entry->data, mock_entry->data, + mok_entry->data_size), + err, "%ld != %ld\n"); + config_pos += offsetof(struct mok_variable_config_entry, data) + + mok_entry->data_size; + i += 1; + } + + return EFI_SUCCESS; +err: + warnx("Failed on entry %zu mok.name:\"%s\" mock.name:\"%s\"", i, + mok_entry->name, mock_entry->name); + if ((mok_entry && mock_entry) && (!mok_entry->name[0] || !mock_entry->name[0])) + warnx("Entry is missing in %s variable list.", mok_entry->name[0] ? "expected" : "found"); + + return EFI_INVALID_PARAMETER; +} + static int -test_mok_mirror_0(void) +test_mok_mirror(struct test_var *vars, + struct mock_mok_variable_config_entry *configs, + EFI_STATUS expected_status) +{ + EFI_STATUS status; + EFI_GUID mok_config_guid = MOK_VARIABLE_STORE; + int ret = -1; + + status = import_mok_state(NULL); + assert_equal_goto(status, expected_status, err, + "got 0x%016lx, expected 0x%016lx\n", + expected_status); + + test_vars = vars; + + status = check_variables(vars); + if (EFI_ERROR(status)) + goto err; + + uint8_t *pos = NULL; + for (size_t i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; + + if (CompareGuid(&ct->VendorGuid, &mok_config_guid) != 0) + continue; + + pos = (void *)ct->VendorTable; + break; + } + + assert_nonzero_goto(pos, err, "%p != 0\n"); + + status = check_config_table(configs, pos); + if (EFI_ERROR(status)) + goto err; + + ret = 0; +err: + for (UINTN k = 0; k < n_mok_state_variables; k++) { + struct mok_state_variable *v = + &mok_state_variables[k]; + if (v->data_size && v->data) { + free(v->data); + v->data = NULL; + v->data_size = 0; + } + } + + test_vars = NULL; + + return ret; +} + +/* + * This tests mirroring of mok variables on fairly optimistic conditions: + * there's enough space for everything, and so we expect to see all the + * RT variables for which we have data mirrored + */ +static int +test_mok_mirror_with_enough_space(void) { const char *mok_rt_vars[n_mok_state_variables]; EFI_STATUS status; EFI_GUID guid = SHIM_LOCK_GUID; - EFI_GUID mok_config_guid = MOK_VARIABLE_STORE; int ret = -1; - struct test_var test_mok_mirror_0_vars[] = { + struct test_var test_mok_mirror_with_enough_space_vars[] = { {.guid = SHIM_LOCK_GUID, .name = L"MokList", .must_be_present = true, @@ -166,11 +398,20 @@ 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 @@ .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 @@ 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"); - - 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; - - /* - * 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_load_variables("test-data/efivars-1", mok_rt_vars, true); - 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); + mock_set_variable_post_hook = setvar_post; + mock_get_variable_post_hook = getvar_post; - /* - * 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_usage_limits(&mock_obnoxious_variable_limits, + &obnoxious_limits[0]); - 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 = test_mok_mirror(&test_mok_mirror_enospc_vars[0], + test_mok_config_table, + EFI_OUT_OF_RESOURCES); - 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; - } - } + mock_set_default_usage_limits(); - test_vars = NULL; mock_set_variable_post_hook = NULL; mock_get_variable_post_hook = NULL; return ret; @@ -391,7 +703,10 @@ 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 -Nru shim-15.8/test-sbat.c shim-16.1/test-sbat.c --- shim-15.8/test-sbat.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/test-sbat.c 1970-01-01 00:00:00.000000000 +0000 @@ -8,6 +8,7 @@ #include "sbat_var_defs.h" #endif #include "shim.h" +#include "generated_sbat_var_defs.h" #include diff -Nru shim-15.8/tpm.c shim-16.1/tpm.c --- shim-15.8/tpm.c 2024-01-22 19:18:05.000000000 +0000 +++ shim-16.1/tpm.c 1970-01-01 00:00:00.000000000 +0000 @@ -11,6 +11,7 @@ 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 @@ 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 @@ 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 @@ */ 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 @@ 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 -Nru shim-15.8/utils.c shim-16.1/utils.c --- shim-15.8/utils.c 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/utils.c 1970-01-01 00:00:00.000000000 +0000 @@ -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; +} diff -Nru shim-15.8/verify.c shim-16.1/verify.c --- shim-15.8/verify.c 1970-01-01 00:00:00.000000000 +0000 +++ shim-16.1/verify.c 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,831 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * verify.c - verification routines for UEFI Secure Boot + * Copyright Peter Jones + * Copyright Matthew Garrett + * + * Significant portions of this code are derived from Tianocore + * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel + * Corporation. + */ + +#include "shim.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define OID_EKU_MODSIGN "1.3.6.1.4.1.2312.16.1.2" + +typedef enum { + DATA_FOUND, + DATA_NOT_FOUND, + VAR_NOT_FOUND +} CHECK_STATUS; + +void +init_openssl(void) +{ + OPENSSL_init(); + ERR_load_ERR_strings(); + ERR_load_BN_strings(); + ERR_load_RSA_strings(); + ERR_load_DH_strings(); + ERR_load_EVP_strings(); + ERR_load_BUF_strings(); + ERR_load_OBJ_strings(); + ERR_load_PEM_strings(); + ERR_load_X509_strings(); + ERR_load_ASN1_strings(); + ERR_load_CONF_strings(); + ERR_load_CRYPTO_strings(); + ERR_load_COMP_strings(); + ERR_load_BIO_strings(); + ERR_load_PKCS7_strings(); + ERR_load_X509V3_strings(); + ERR_load_PKCS12_strings(); + ERR_load_RAND_strings(); + ERR_load_DSO_strings(); + ERR_load_OCSP_strings(); +} + +static void +drain_openssl_errors(void) +{ + unsigned long err = -1; + while (err != 0) + err = ERR_get_error(); +} + +static BOOLEAN +verify_x509(UINT8 *Cert, UINTN CertSize) +{ + UINTN length; + + if (!Cert || CertSize < 4) + return FALSE; + + /* + * A DER encoding x509 certificate starts with SEQUENCE(0x30), + * the number of length bytes, and the number of value bytes. + * The size of a x509 certificate is usually between 127 bytes + * and 64KB. For convenience, assume the number of value bytes + * is 2, i.e. the second byte is 0x82. + */ + if (Cert[0] != 0x30 || Cert[1] != 0x82) { + dprint(L"cert[0:1] is [%02x%02x], should be [%02x%02x]\n", + Cert[0], Cert[1], 0x30, 0x82); + return FALSE; + } + + length = Cert[2]<<8 | Cert[3]; + if (length != (CertSize - 4)) { + dprint(L"Cert length is %ld, expecting %ld\n", + length, CertSize); + return FALSE; + } + + return TRUE; +} + +static BOOLEAN +verify_eku(UINT8 *Cert, UINTN CertSize) +{ + X509 *x509; + CONST UINT8 *Temp = Cert; + EXTENDED_KEY_USAGE *eku; + ASN1_OBJECT *module_signing; + + module_signing = OBJ_nid2obj(OBJ_create(OID_EKU_MODSIGN, + "modsign-eku", + "modsign-eku")); + + x509 = d2i_X509 (NULL, &Temp, (long) CertSize); + if (x509 != NULL) { + eku = X509_get_ext_d2i(x509, NID_ext_key_usage, NULL, NULL); + + if (eku) { + int i = 0; + for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) { + ASN1_OBJECT *key_usage = sk_ASN1_OBJECT_value(eku, i); + + if (OBJ_cmp(module_signing, key_usage) == 0) + return FALSE; + } + EXTENDED_KEY_USAGE_free(eku); + } + + X509_free(x509); + } + + OBJ_cleanup(); + + return TRUE; +} + +static CHECK_STATUS +check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList, UINTN dbsize, + WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash, + CHAR16 *dbname, EFI_GUID guid) +{ + EFI_SIGNATURE_DATA *Cert; + UINTN CertSize; + BOOLEAN IsFound = FALSE; + int i = 0; + + while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { + if (CompareGuid (&CertList->SignatureType, &EFI_CERT_TYPE_X509_GUID) == 0) { + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + CertSize = CertList->SignatureSize - sizeof(EFI_GUID); + dprint(L"trying to verify cert %d (%s)\n", i++, dbname); + if (verify_x509(Cert->SignatureData, CertSize)) { + if (verify_eku(Cert->SignatureData, CertSize)) { + drain_openssl_errors(); + IsFound = AuthenticodeVerify (data->CertData, + data->Hdr.dwLength - sizeof(data->Hdr), + Cert->SignatureData, + CertSize, + hash, SHA256_DIGEST_SIZE); + if (IsFound) { + dprint(L"AuthenticodeVerify() succeeded: %d\n", IsFound); + tpm_measure_variable(dbname, guid, CertList->SignatureSize, Cert); + drain_openssl_errors(); + return DATA_FOUND; + } else { + LogError(L"AuthenticodeVerify(): %d\n", IsFound); + } + } + } else if (verbose) { + console_print(L"Not a DER encoded x.509 Certificate"); + dprint(L"cert:\n"); + dhexdumpat(Cert->SignatureData, CertSize, 0); + } + } + + dbsize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + + return DATA_NOT_FOUND; +} + +static CHECK_STATUS +check_db_cert(CHAR16 *dbname, EFI_GUID guid, WIN_CERTIFICATE_EFI_PKCS *data, + UINT8 *hash) +{ + CHECK_STATUS rc; + EFI_STATUS efi_status; + EFI_SIGNATURE_LIST *CertList; + UINTN dbsize = 0; + UINT8 *db; + + efi_status = get_variable(dbname, &db, &dbsize, guid); + if (EFI_ERROR(efi_status)) + return VAR_NOT_FOUND; + + CertList = (EFI_SIGNATURE_LIST *)db; + + rc = check_db_cert_in_ram(CertList, dbsize, data, hash, dbname, guid); + + FreePool(db); + + return rc; +} + +/* + * Check a hash against an EFI_SIGNATURE_LIST in a buffer + */ +static CHECK_STATUS +check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList, UINTN dbsize, UINT8 *data, + int SignatureSize, EFI_GUID CertType, CHAR16 *dbname, + EFI_GUID guid) +{ + EFI_SIGNATURE_DATA *Cert; + UINTN CertCount, Index; + BOOLEAN IsFound = FALSE; + + while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { + CertCount = (CertList->SignatureListSize -sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + if (CompareGuid(&CertList->SignatureType, &CertType) == 0) { + for (Index = 0; Index < CertCount; Index++) { + if (CompareMem (Cert->SignatureData, data, SignatureSize) == 0) { + // + // Find the signature in database. + // + IsFound = TRUE; + tpm_measure_variable(dbname, guid, CertList->SignatureSize, Cert); + break; + } + + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + } + if (IsFound) { + break; + } + } + + dbsize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + + if (IsFound) + return DATA_FOUND; + + return DATA_NOT_FOUND; +} + +/* + * Check a hash against an EFI_SIGNATURE_LIST in a UEFI variable + */ +static CHECK_STATUS +check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data, int SignatureSize, + EFI_GUID CertType) +{ + EFI_STATUS efi_status; + EFI_SIGNATURE_LIST *CertList; + UINTN dbsize = 0; + UINT8 *db; + + efi_status = get_variable(dbname, &db, &dbsize, guid); + if (EFI_ERROR(efi_status)) { + return VAR_NOT_FOUND; + } + + CertList = (EFI_SIGNATURE_LIST *)db; + + CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data, + SignatureSize, CertType, + dbname, guid); + FreePool(db); + return rc; +} + +/* + * Check whether the binary signature or hash are present in dbx or the + * built-in denylist + */ +static EFI_STATUS +check_denylist(WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *sha256hash, + UINT8 *sha1hash) +{ + EFI_SIGNATURE_LIST *dbx = (EFI_SIGNATURE_LIST *)vendor_deauthorized; + + if (check_db_hash_in_ram(dbx, vendor_deauthorized_size, sha256hash, + SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID, L"dbx", + EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { + LogError(L"binary sha256hash found in vendor dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (check_db_hash_in_ram(dbx, vendor_deauthorized_size, sha1hash, + SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID, L"dbx", + EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { + LogError(L"binary sha1hash found in vendor dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (cert && + check_db_cert_in_ram(dbx, vendor_deauthorized_size, cert, sha256hash, L"dbx", + EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { + LogError(L"cert sha256hash found in vendor dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (check_db_hash(L"dbx", EFI_SECURE_BOOT_DB_GUID, sha256hash, + SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == DATA_FOUND) { + LogError(L"binary sha256hash found in system dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (check_db_hash(L"dbx", EFI_SECURE_BOOT_DB_GUID, sha1hash, + SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID) == DATA_FOUND) { + LogError(L"binary sha1hash found in system dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (cert && + check_db_cert(L"dbx", EFI_SECURE_BOOT_DB_GUID, + cert, sha256hash) == DATA_FOUND) { + LogError(L"cert sha256hash found in system dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (check_db_hash(L"MokListX", SHIM_LOCK_GUID, sha256hash, + SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == DATA_FOUND) { + LogError(L"binary sha256hash found in Mok dbx\n"); + return EFI_SECURITY_VIOLATION; + } + if (cert && + check_db_cert(L"MokListX", SHIM_LOCK_GUID, + cert, sha256hash) == DATA_FOUND) { + LogError(L"cert sha256hash found in Mok dbx\n"); + return EFI_SECURITY_VIOLATION; + } + + drain_openssl_errors(); + return EFI_SUCCESS; +} + +static void +update_verification_method(verification_method_t method) +{ + if (verification_method == VERIFIED_BY_NOTHING) + verification_method = method; +} + +/* + * Check whether the binary signature or hash are present in db or MokList + */ +static EFI_STATUS +check_allowlist(WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *sha256hash, + UINT8 *sha1hash) +{ + if (!ignore_db) { + if (check_db_hash(L"db", EFI_SECURE_BOOT_DB_GUID, sha256hash, SHA256_DIGEST_SIZE, + EFI_CERT_SHA256_GUID) == DATA_FOUND) { + update_verification_method(VERIFIED_BY_HASH); + return EFI_SUCCESS; + } else { + LogError(L"check_db_hash(db, sha256hash) != DATA_FOUND\n"); + } + if (check_db_hash(L"db", EFI_SECURE_BOOT_DB_GUID, sha1hash, SHA1_DIGEST_SIZE, + EFI_CERT_SHA1_GUID) == DATA_FOUND) { + verification_method = VERIFIED_BY_HASH; + update_verification_method(VERIFIED_BY_HASH); + return EFI_SUCCESS; + } else { + LogError(L"check_db_hash(db, sha1hash) != DATA_FOUND\n"); + } + if (cert && check_db_cert(L"db", EFI_SECURE_BOOT_DB_GUID, cert, sha256hash) + == DATA_FOUND) { + verification_method = VERIFIED_BY_CERT; + update_verification_method(VERIFIED_BY_CERT); + return EFI_SUCCESS; + } else if (cert) { + LogError(L"check_db_cert(db, sha256hash) != DATA_FOUND\n"); + } + } + +#if defined(VENDOR_DB_FILE) + EFI_SIGNATURE_LIST *db = (EFI_SIGNATURE_LIST *)vendor_db; + + if (check_db_hash_in_ram(db, vendor_db_size, + sha256hash, SHA256_DIGEST_SIZE, + EFI_CERT_SHA256_GUID, L"vendor_db", + EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { + verification_method = VERIFIED_BY_HASH; + update_verification_method(VERIFIED_BY_HASH); + return EFI_SUCCESS; + } else { + LogError(L"check_db_hash(vendor_db, sha256hash) != DATA_FOUND\n"); + } + if (cert && + check_db_cert_in_ram(db, vendor_db_size, + cert, sha256hash, L"vendor_db", + EFI_SECURE_BOOT_DB_GUID) == DATA_FOUND) { + verification_method = VERIFIED_BY_CERT; + update_verification_method(VERIFIED_BY_CERT); + return EFI_SUCCESS; + } else if (cert) { + LogError(L"check_db_cert(vendor_db, sha256hash) != DATA_FOUND\n"); + } +#endif + + if (check_db_hash(L"MokListRT", SHIM_LOCK_GUID, sha256hash, + SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) + == DATA_FOUND) { + verification_method = VERIFIED_BY_HASH; + update_verification_method(VERIFIED_BY_HASH); + return EFI_SUCCESS; + } else { + LogError(L"check_db_hash(MokListRT, sha256hash) != DATA_FOUND\n"); + } + if (cert && check_db_cert(L"MokListRT", SHIM_LOCK_GUID, cert, sha256hash) + == DATA_FOUND) { + verification_method = VERIFIED_BY_CERT; + update_verification_method(VERIFIED_BY_CERT); + return EFI_SUCCESS; + } else if (cert) { + LogError(L"check_db_cert(MokListRT, sha256hash) != DATA_FOUND\n"); + } + + update_verification_method(VERIFIED_BY_NOTHING); + return EFI_NOT_FOUND; +} + +static EFI_STATUS +verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig, UINT8 *sha256hash, + UINT8 *sha1hash) +{ + EFI_STATUS efi_status; + + /* + * Ensure that the binary isn't forbidden + */ + drain_openssl_errors(); + efi_status = check_denylist(sig, sha256hash, sha1hash); + if (EFI_ERROR(efi_status)) { + perror(L"Binary is forbidden: %r\n", efi_status); + PrintErrors(); + ClearErrors(); + crypterr(efi_status); + return efi_status; + } + + /* + * Check whether the binary is authorized in any of the firmware + * databases + */ + drain_openssl_errors(); + efi_status = check_allowlist(sig, sha256hash, sha1hash); + if (EFI_ERROR(efi_status)) { + if (efi_status != EFI_NOT_FOUND) { + dprint(L"check_allowlist(): %r\n", efi_status); + PrintErrors(); + ClearErrors(); + crypterr(efi_status); + } + } else { + drain_openssl_errors(); + return efi_status; + } + + efi_status = EFI_NOT_FOUND; +#if defined(ENABLE_SHIM_CERT) + /* + * Check against the shim build key + */ + drain_openssl_errors(); + if (build_cert && build_cert_size) { + dprint("verifying against shim cert\n"); + } + if (build_cert && build_cert_size && + AuthenticodeVerify(sig->CertData, + sig->Hdr.dwLength - sizeof(sig->Hdr), + build_cert, build_cert_size, sha256hash, + SHA256_DIGEST_SIZE)) { + dprint(L"AuthenticodeVerify(shim_cert) succeeded\n"); + update_verification_method(VERIFIED_BY_CERT); + tpm_measure_variable(L"Shim", SHIM_LOCK_GUID, + build_cert_size, build_cert); + efi_status = EFI_SUCCESS; + drain_openssl_errors(); + return efi_status; + } else { + dprint(L"AuthenticodeVerify(shim_cert) failed\n"); + PrintErrors(); + ClearErrors(); + crypterr(EFI_NOT_FOUND); + } +#endif /* defined(ENABLE_SHIM_CERT) */ + +#if defined(VENDOR_CERT_FILE) + /* + * And finally, check against shim's built-in key + */ + drain_openssl_errors(); + if (vendor_cert_size) { + dprint("verifying against vendor_cert\n"); + } + if (vendor_cert_size && + AuthenticodeVerify(sig->CertData, + sig->Hdr.dwLength - sizeof(sig->Hdr), + vendor_cert, vendor_cert_size, + sha256hash, SHA256_DIGEST_SIZE)) { + dprint(L"AuthenticodeVerify(vendor_cert) succeeded\n"); + update_verification_method(VERIFIED_BY_CERT); + tpm_measure_variable(L"Shim", SHIM_LOCK_GUID, + vendor_cert_size, vendor_cert); + efi_status = EFI_SUCCESS; + drain_openssl_errors(); + return efi_status; + } else { + dprint(L"AuthenticodeVerify(vendor_cert) failed\n"); + PrintErrors(); + ClearErrors(); + crypterr(EFI_NOT_FOUND); + } +#endif /* defined(VENDOR_CERT_FILE) */ + + return efi_status; +} + +/* + * Check that the signature is valid and matches the binary + */ +static EFI_STATUS +verify_buffer_authenticode (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash, + bool parent_verified) +{ + EFI_STATUS ret_efi_status; + size_t size = datasize; + size_t offset = 0; + unsigned int i = 0; + + if (datasize < 0) + return EFI_INVALID_PARAMETER; + + /* + * Clear OpenSSL's error log, because we get some DSO unimplemented + * errors during its intialization, and we don't want those to look + * like they're the reason for validation failures. + */ + drain_openssl_errors(); + + ret_efi_status = generate_hash(data, datasize, context, sha256hash, sha1hash); + if (EFI_ERROR(ret_efi_status)) { + dprint(L"generate_hash: %r\n", ret_efi_status); + PrintErrors(); + ClearErrors(); + crypterr(ret_efi_status); + return ret_efi_status; + } + + /* + * Ensure that the binary isn't forbidden by hash + */ + drain_openssl_errors(); + ret_efi_status = check_denylist(NULL, sha256hash, sha1hash); + if (EFI_ERROR(ret_efi_status)) { +// perror(L"Binary is forbidden\n"); +// dprint(L"Binary is forbidden: %r\n", ret_efi_status); + PrintErrors(); + ClearErrors(); + crypterr(ret_efi_status); + return ret_efi_status; + } + + if (parent_verified) + return EFI_SUCCESS; + + /* + * Check whether the binary is authorized by hash in any of the + * firmware databases + */ + drain_openssl_errors(); + ret_efi_status = check_allowlist(NULL, sha256hash, sha1hash); + if (EFI_ERROR(ret_efi_status)) { + LogError(L"check_allowlist(): %r\n", ret_efi_status); + dprint(L"check_allowlist: %r\n", ret_efi_status); + if (ret_efi_status != EFI_NOT_FOUND) { + dprint(L"check_allowlist(): %r\n", ret_efi_status); + PrintErrors(); + ClearErrors(); + crypterr(ret_efi_status); + return ret_efi_status; + } + } else { + drain_openssl_errors(); + return ret_efi_status; + } + + if (context->SecDir->Size == 0) { + dprint(L"No signatures found\n"); + return EFI_SECURITY_VIOLATION; + } + + if (checked_add(context->SecDir->Size, context->SecDir->VirtualAddress, &offset) || + offset > size) { + perror(L"Certificate Database size is too large\n"); + return EFI_INVALID_PARAMETER; + } + + offset = 0; + ret_efi_status = EFI_NOT_FOUND; + do { + WIN_CERTIFICATE_EFI_PKCS *sig = NULL; + size_t sz; + + sig = ImageAddress(data, size, + context->SecDir->VirtualAddress + offset); + if (!sig) + break; + + if ((uint64_t)(uintptr_t)&sig[1] + > (uint64_t)(uintptr_t)data + datasize) { + perror(L"Certificate size is too large for secruity database"); + return EFI_INVALID_PARAMETER; + } + + sz = offset + offsetof(WIN_CERTIFICATE_EFI_PKCS, Hdr.dwLength) + + sizeof(sig->Hdr.dwLength); + if (sz > context->SecDir->Size) { + perror(L"Certificate size is too large for secruity database"); + return EFI_INVALID_PARAMETER; + } + + sz = sig->Hdr.dwLength; + if (sz > context->SecDir->Size - offset) { + perror(L"Certificate size is too large for secruity database"); + return EFI_INVALID_PARAMETER; + } + + if (sz < sizeof(sig->Hdr)) { + perror(L"Certificate size is too small for certificate data"); + return EFI_INVALID_PARAMETER; + } + + if (sig->Hdr.wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + EFI_STATUS efi_status; + + dprint(L"Attempting to verify signature %d:\n", i++); + + efi_status = verify_one_signature(sig, sha256hash, sha1hash); + + /* + * If we didn't get EFI_SECURITY_VIOLATION from + * checking the hashes above, then any dbx entries are + * for a certificate, not this individual binary. + * + * So don't clobber successes with security violation + * here; that just means it isn't a success. + */ + if (ret_efi_status != EFI_SUCCESS) + ret_efi_status = efi_status; + } else { + perror(L"Unsupported certificate type %x\n", + sig->Hdr.wCertificateType); + } + offset = ALIGN_VALUE(offset + sz, 8); + } while (offset < context->SecDir->Size); + + if (ret_efi_status != EFI_SUCCESS) { + dprint(L"Binary is not authorized\n"); + PrintErrors(); + ClearErrors(); + crypterr(EFI_SECURITY_VIOLATION); + ret_efi_status = EFI_SECURITY_VIOLATION; + } + drain_openssl_errors(); + return ret_efi_status; +} + +/* + * Check that the binary is permitted to load by SBAT. + */ +static EFI_STATUS +verify_buffer_sbat (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context) +{ + int i; + EFI_IMAGE_SECTION_HEADER *Section; + char *SBATBase = NULL; + size_t SBATSize = 0; + + Section = context->FirstSection; + for (i = 0; i < context->NumberOfSections; i++, Section++) { + if ((uint64_t)(uintptr_t)&Section[1] + > (uintptr_t)(uintptr_t)data + datasize) { + perror(L"Section exceeds bounds of image\n"); + return EFI_UNSUPPORTED; + } + + if (CompareMem(Section->Name, ".sbat\0\0\0", 8) != 0) + continue; + + if (SBATBase || SBATSize) { + perror(L"Image has multiple SBAT sections\n"); + return EFI_UNSUPPORTED; + } + + if (Section->NumberOfRelocations != 0 || + Section->PointerToRelocations != 0) { + perror(L"SBAT section has relocations\n"); + return EFI_UNSUPPORTED; + } + + /* The virtual size corresponds to the size of the SBAT + * metadata and isn't necessarily a multiple of the file + * alignment. The on-disk size is a multiple of the file + * alignment and is zero padded. Make sure that the + * on-disk size is at least as large as virtual size, + * and ignore the section if it isn't. */ + if (Section->SizeOfRawData && + Section->SizeOfRawData >= Section->Misc.VirtualSize) { + uint64_t boundary; + SBATBase = ImageAddress(data, datasize, + Section->PointerToRawData); + SBATSize = Section->SizeOfRawData; + dprint(L"sbat section base:0x%lx size:0x%lx\n", + SBATBase, SBATSize); + if (checked_add((uint64_t)(uintptr_t)SBATBase, SBATSize, &boundary) || + (boundary > (uint64_t)(uintptr_t)data + datasize)) { + perror(L"Section exceeds bounds of image\n"); + return EFI_UNSUPPORTED; + } + } + } + + return verify_sbat_section(SBATBase, SBATSize); +} + +/* + * Check that the signature is valid and matches the binary and that + * the binary is permitted to load by SBAT. + */ +EFI_STATUS +verify_buffer (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash, + bool parent_verified) +{ + EFI_STATUS efi_status; + + efi_status = verify_buffer_authenticode(data, datasize, context, + sha256hash, sha1hash, + parent_verified); + if (EFI_ERROR(efi_status)) + return efi_status; + + return verify_buffer_sbat(data, datasize, context); +} + +/* + * Protocol entry point. If secure boot is enabled, verify that the provided + * buffer is signed with a trusted key. + */ +EFI_STATUS +shim_verify(void *buffer, UINT32 size) +{ + EFI_STATUS efi_status = EFI_SUCCESS; + PE_COFF_LOADER_IMAGE_CONTEXT context; + UINT8 sha1hash[SHA1_DIGEST_SIZE]; + UINT8 sha256hash[SHA256_DIGEST_SIZE]; + + if ((INT32)size < 0) + return EFI_INVALID_PARAMETER; + + in_protocol = 1; + + efi_status = read_header(buffer, size, &context, true); + if (EFI_ERROR(efi_status)) + goto done; + + efi_status = generate_hash(buffer, size, &context, + sha256hash, sha1hash); + if (EFI_ERROR(efi_status)) + goto done; + + /* Measure the binary into the TPM */ +#ifdef REQUIRE_TPM + efi_status = +#endif + tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)buffer, size, 0, NULL, + sha1hash, 4); +#ifdef REQUIRE_TPM + if (EFI_ERROR(efi_status)) + goto done; +#endif + + if (!secure_mode()) { + efi_status = EFI_SUCCESS; + goto done; + } + + efi_status = verify_buffer(buffer, size, + &context, sha256hash, sha1hash, + false); +done: + in_protocol = 0; + return efi_status; +} + +EFI_STATUS +shim_hash(char *data, int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash) +{ + EFI_STATUS efi_status; + + if (datasize < 0) + return EFI_INVALID_PARAMETER; + + in_protocol = 1; + efi_status = generate_hash(data, datasize, context, + sha256hash, sha1hash); + in_protocol = 0; + + return efi_status; +} + +EFI_STATUS +shim_read_header(void *data, unsigned int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context) +{ + EFI_STATUS efi_status; + + in_protocol = 1; + efi_status = read_header(data, datasize, context, true); + in_protocol = 0; + + return efi_status; +} + +// vim:fenc=utf-8:tw=75:noet