mirror of
https://git.proxmox.com/git/efi-boot-shim
synced 2025-06-14 14:48:19 +00:00
New upstream version 16.0
This commit is contained in:
parent
a075e58606
commit
b017e4554b
88
.github/workflows/pullrequest.yml
vendored
88
.github/workflows/pullrequest.yml
vendored
@ -22,66 +22,70 @@ jobs:
|
||||
efiarch: aa64
|
||||
gccarch: aarch64
|
||||
makearch: aarch64
|
||||
distro: f36
|
||||
distro: f41
|
||||
- arch: amd64
|
||||
efiarch: aa64
|
||||
gccarch: aarch64
|
||||
makearch: aarch64
|
||||
distro: f35
|
||||
distro: f40
|
||||
- arch: amd64
|
||||
efiarch: arm
|
||||
gccarch: arm
|
||||
makearch: arm
|
||||
distro: f36
|
||||
distro: f41
|
||||
- arch: amd64
|
||||
efiarch: arm
|
||||
gccarch: arm
|
||||
makearch: arm
|
||||
distro: f35
|
||||
distro: f40
|
||||
- arch: amd64
|
||||
efiarch: arm
|
||||
gccarch: arm
|
||||
makearch: arm
|
||||
distro: f34
|
||||
distro: f39
|
||||
- arch: amd64
|
||||
efiarch: x64
|
||||
gccarch: x86_64
|
||||
makearch: x86_64
|
||||
distro: f36
|
||||
distro: f41
|
||||
- arch: amd64
|
||||
efiarch: x64
|
||||
gccarch: x86_64
|
||||
makearch: x86_64
|
||||
distro: f35
|
||||
distro: f40
|
||||
- arch: amd64
|
||||
efiarch: x64
|
||||
gccarch: x86_64
|
||||
makearch: x86_64
|
||||
distro: f34
|
||||
distro: f39
|
||||
- arch: amd64
|
||||
efiarch: ia32
|
||||
gccarch: x86_64
|
||||
makearch: ia32
|
||||
distro: f36
|
||||
distro: f41
|
||||
- arch: amd64
|
||||
efiarch: ia32
|
||||
gccarch: x86_64
|
||||
makearch: ia32
|
||||
distro: f35
|
||||
distro: f40
|
||||
- arch: amd64
|
||||
efiarch: ia32
|
||||
gccarch: x86_64
|
||||
makearch: ia32
|
||||
distro: f34
|
||||
distro: f39
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger)
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
- name: Work around directory ownership issue
|
||||
id: ignore-ownership
|
||||
run: |
|
||||
git config --global --add safe.directory /__w/shim/shim
|
||||
- name: Update submodules on ${{ matrix.distro }} for ${{ matrix.efiarch }}
|
||||
id: update-submodules
|
||||
run: |
|
||||
@ -118,15 +122,15 @@ jobs:
|
||||
- arch: amd64
|
||||
efiarch: x64
|
||||
makearch: x86_64
|
||||
distro: f36
|
||||
distro: f41
|
||||
- arch: amd64
|
||||
efiarch: x64
|
||||
makearch: x86_64
|
||||
distro: f35
|
||||
distro: f40
|
||||
- arch: amd64
|
||||
efiarch: x64
|
||||
makearch: x86_64
|
||||
distro: f34
|
||||
distro: f39
|
||||
- arch: amd64
|
||||
efiarch: x64
|
||||
makearch: x86_64
|
||||
@ -138,15 +142,7 @@ jobs:
|
||||
- arch: amd64
|
||||
efiarch: ia32
|
||||
makearch: ia32
|
||||
distro: f36
|
||||
- arch: amd64
|
||||
efiarch: ia32
|
||||
makearch: ia32
|
||||
distro: f35
|
||||
- arch: amd64
|
||||
efiarch: ia32
|
||||
makearch: ia32
|
||||
distro: f34
|
||||
distro: f39
|
||||
- arch: amd64
|
||||
efiarch: ia32
|
||||
makearch: ia32
|
||||
@ -154,12 +150,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger)
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
- name: Work around directory ownership issue
|
||||
id: ignore-ownership
|
||||
run: |
|
||||
git config --global --add safe.directory /__w/shim/shim
|
||||
- name: Update submodules on ${{ matrix.distro }} for ${{ matrix.efiarch }}
|
||||
id: update-submodules
|
||||
run: |
|
||||
@ -182,3 +182,41 @@ jobs:
|
||||
make ARCH=${{ matrix.makearch }} PREFIX=/usr DESTDIR=/destdir EFIDIR=test ENABLE_SHIM_HASH=true install
|
||||
echo 'results:'
|
||||
find /destdir -type f
|
||||
|
||||
build-pull-request-intel-compile-commands-json:
|
||||
runs-on: ubuntu-20.04
|
||||
container: vathpela/efi-ci:${{ matrix.distro }}-x64
|
||||
name: ${{ matrix.distro }} ${{ matrix.efiarch }} build compile_commands.json
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
efiarch: x64
|
||||
makearch: x86_64
|
||||
distro: f41
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger)
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
- name: Work around directory ownership issue
|
||||
id: ignore-ownership
|
||||
run: |
|
||||
git config --global --add safe.directory /__w/shim/shim
|
||||
- name: Update submodules on ${{ matrix.distro }} for ${{ matrix.efiarch }}
|
||||
id: update-submodules
|
||||
run: |
|
||||
make update
|
||||
- name: Do 'make clean' on ${{ matrix.distro }} for ${{ matrix.efiarch }}
|
||||
id: clean
|
||||
run: |
|
||||
make ARCH=${{ matrix.makearch }} PREFIX=/usr DESTDIR=/destdir EFIDIR=test ENABLE_SHIM_HASH=true clean
|
||||
- name: Build compile_commands.json on ${{ matrix.distro }} for ${{ matrix.efiarch }}
|
||||
id: compile_commands
|
||||
run: |
|
||||
make ARCH=${{ matrix.makearch }} PREFIX=/usr DESTDIR=/destdir EFIDIR=test ENABLE_SHIM_HASH=true compile_commands.json
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -38,6 +38,8 @@ Make.local
|
||||
/crash-*
|
||||
/fuzz-*
|
||||
!/fuzz-*.c
|
||||
/generate_sbat_var_defs
|
||||
/generated_sbat_var_defs.h
|
||||
/leak-*
|
||||
/post-process-pe
|
||||
/random.bin
|
||||
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,4 +1,4 @@
|
||||
[submodule "gnu-efi"]
|
||||
path = gnu-efi
|
||||
url = https://github.com/rhboot/gnu-efi.git
|
||||
branch = shim-15.8
|
||||
branch = shim-16.0
|
||||
|
12
BUILDING
12
BUILDING
@ -37,15 +37,6 @@ Variables you could set to customize the build:
|
||||
debugger only on the development branch and not the OS you need to boot
|
||||
to scp in a new development build. Likewise, we look for
|
||||
SHIM_DEVEL_VERBOSE rather than SHIM_VERBOSE.
|
||||
- DISABLE_EBS_PROTECTION
|
||||
On systems where a second stage bootloader is not used, and the Linux
|
||||
Kernel is embedded in the same EFI image as shim and booted directly
|
||||
from shim, shim's ExitBootServices() hook can cause problems as the
|
||||
kernel never calls the shim's verification protocol. In this case
|
||||
calling the shim verification protocol is unnecessary and redundant as
|
||||
shim has already verified the kernel when shim loaded the kernel as the
|
||||
second stage loader. In such a case, and only in this case, you should
|
||||
use DISABLE_EBS_PROTECTION=y to build.
|
||||
- DISABLE_REMOVABLE_LOAD_OPTIONS
|
||||
Do not parse load options when invoked as boot*.efi. This prevents boot
|
||||
failures because of unexpected data in boot entries automatically generated
|
||||
@ -81,6 +72,9 @@ Variables you could set to customize the build:
|
||||
- POST_PROCESS_PE_FLAGS
|
||||
This allows you to add flags to the invocation of "post-process-pe", for
|
||||
example to disable the NX compatibility flag.
|
||||
- ENABLE_CODESIGN_EKU
|
||||
This changes the certificate validation logic to require Extended Key
|
||||
Usage 1.3.6.1.5.5.7.3.3 ("Code Signing").
|
||||
|
||||
Vendor SBAT data:
|
||||
It will sometimes be requested by reviewers that a build includes extra
|
||||
|
@ -61,7 +61,7 @@ representative at an online or offline event.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
rharwood AT redhat DOT com.
|
||||
contact AT linux-uefi DOT org.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
|
@ -32,5 +32,38 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
#define OBJ_length(o) ((o)->length)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#if defined(ENABLE_CODESIGN_EKU)
|
||||
/**
|
||||
Check input P7Data is a wrapped ContentInfo structure or not. If not construct
|
||||
a new structure to wrap P7Data.
|
||||
|
||||
Caution: This function may receive untrusted input.
|
||||
UEFI Authenticated Variable is external input, so this function will do basic
|
||||
check for PKCS#7 data structure.
|
||||
|
||||
@param[in] P7Data Pointer to the PKCS#7 message to verify.
|
||||
@param[in] P7Length Length of the PKCS#7 message in bytes.
|
||||
@param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
|
||||
return FALSE.
|
||||
@param[out] WrapData If return status of this function is TRUE:
|
||||
1) when WrapFlag is TRUE, pointer to P7Data.
|
||||
2) when WrapFlag is FALSE, pointer to a new ContentInfo
|
||||
structure. It's caller's responsibility to free this
|
||||
buffer.
|
||||
@param[out] WrapDataSize Length of ContentInfo structure in bytes.
|
||||
|
||||
@retval TRUE The operation is finished successfully.
|
||||
@retval FALSE The operation is failed due to lack of resources.
|
||||
|
||||
**/
|
||||
BOOLEAN
|
||||
WrapPkcs7Data (
|
||||
IN CONST UINT8 *P7Data,
|
||||
IN UINTN P7Length,
|
||||
OUT BOOLEAN *WrapFlag,
|
||||
OUT UINT8 **WrapData,
|
||||
OUT UINTN *WrapDataSize
|
||||
);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -2403,6 +2403,48 @@ Pkcs7Verify (
|
||||
IN UINTN DataLength
|
||||
);
|
||||
|
||||
#if defined(ENABLE_CODESIGN_EKU)
|
||||
/**
|
||||
This function receives a PKCS#7 formatted signature blob,
|
||||
looks for the EKU SEQUENCE blob, and if found then looks
|
||||
for all the required EKUs. This function was created so that
|
||||
the Surface team can cut down on the number of Certificate
|
||||
Authorities (CA's) by checking EKU's on leaf signers for
|
||||
a specific product. This prevents one product's certificate
|
||||
from signing another product's firmware or unlock blobs.
|
||||
|
||||
Note that this function does not validate the certificate chain.
|
||||
That needs to be done before using this function.
|
||||
|
||||
@param[in] Pkcs7Signature The PKCS#7 signed information content block. An array
|
||||
containing the content block with both the signature,
|
||||
the signer's certificate, and any necessary intermediate
|
||||
certificates.
|
||||
@param[in] Pkcs7SignatureSize Number of bytes in Pkcs7Signature.
|
||||
@param[in] RequiredEKUs Array of null-terminated strings listing OIDs of
|
||||
required EKUs that must be present in the signature.
|
||||
@param[in] RequiredEKUsSize Number of elements in the RequiredEKUs string array.
|
||||
@param[in] RequireAllPresent If this is TRUE, then all of the specified EKU's
|
||||
must be present in the leaf signer. If it is
|
||||
FALSE, then we will succeed if we find any
|
||||
of the specified EKU's.
|
||||
|
||||
@retval EFI_SUCCESS The required EKUs were found in the signature.
|
||||
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
||||
@retval EFI_NOT_FOUND One or more EKU's were not found in the signature.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VerifyEKUsInPkcs7Signature (
|
||||
IN CONST UINT8 *Pkcs7Signature,
|
||||
IN CONST UINT32 SignatureSize,
|
||||
IN CONST CHAR8 *RequiredEKUs[],
|
||||
IN CONST UINT32 RequiredEKUsSize,
|
||||
IN BOOLEAN RequireAllPresent
|
||||
);
|
||||
#endif
|
||||
|
||||
/**
|
||||
Extracts the attached content from a PKCS#7 signed data if existed. The input signed
|
||||
data could be wrapped in a ContentInfo structure.
|
||||
|
@ -14,6 +14,9 @@ INCLUDES = -I$(CRYPTDIR) -I$(CRYPTDIR)/Include \
|
||||
WARNFLAGS += -Wno-unused-parameter \
|
||||
-Wno-unused-but-set-variable
|
||||
|
||||
WERRFLAGS += -Wno-error=unused-but-set-variable \
|
||||
-Wno-error=unused-parameter
|
||||
|
||||
CFLAGS = $(FEATUREFLAGS) \
|
||||
$(OPTIMIZATIONS) \
|
||||
$(WARNFLAGS) \
|
||||
@ -37,6 +40,9 @@ endif
|
||||
ifeq ($(ARCH),arm)
|
||||
DEFINES += -DMDE_CPU_ARM
|
||||
endif
|
||||
ifeq ($(ENABLE_CODESIGN_EKU),1)
|
||||
DEFINES += -DENABLE_CODESIGN_EKU
|
||||
endif
|
||||
|
||||
LDFLAGS = -nostdlib -znocombreloc
|
||||
|
||||
@ -67,6 +73,10 @@ OBJS = Hash/CryptMd4Null.o \
|
||||
SysCall/BaseMemAllocation.o \
|
||||
SysCall/BaseStrings.o
|
||||
|
||||
ifeq ($(ENABLE_CODESIGN_EKU),1)
|
||||
OBJS += Pk/CryptPkcs7VerifyEku.o
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
libcryptlib.a: $(OBJS)
|
||||
|
@ -26,6 +26,9 @@ WARNFLAGS += -Wno-empty-body \
|
||||
-Wno-unused-but-set-variable \
|
||||
-Wno-unused-parameter
|
||||
|
||||
WERRFLAGS += -Wno-error=unused-but-set-variable \
|
||||
-Wno-error=unused-parameter
|
||||
|
||||
CFLAGS = $(FEATUREFLAGS) \
|
||||
$(OPTIMIZATIONS) \
|
||||
$(WARNFLAGS) \
|
||||
|
@ -29,6 +29,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
#include <openssl/pkcs7.h>
|
||||
|
||||
UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
|
||||
#if defined(ENABLE_CODESIGN_EKU)
|
||||
/* EKU CodeSign */
|
||||
CHAR8 mOidCodeSign[] = "1.3.6.1.5.5.7.3.3";
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
@ -846,6 +850,10 @@ Pkcs7Verify (
|
||||
CONST UINT8 *Temp;
|
||||
UINTN SignedDataSize;
|
||||
BOOLEAN Wrapped;
|
||||
#if defined(ENABLE_CODESIGN_EKU)
|
||||
CONST CHAR8 *Ekus[1];
|
||||
EFI_STATUS EFI_Status;
|
||||
#endif
|
||||
|
||||
//
|
||||
// Check input parameters.
|
||||
@ -859,6 +867,9 @@ Pkcs7Verify (
|
||||
DataBio = NULL;
|
||||
Cert = NULL;
|
||||
CertStore = NULL;
|
||||
#if defined(ENABLE_CODESIGN_EKU)
|
||||
Ekus[0] = mOidCodeSign;
|
||||
#endif
|
||||
|
||||
//
|
||||
// Register & Initialize necessary digest algorithms for PKCS#7 Handling
|
||||
@ -958,6 +969,13 @@ Pkcs7Verify (
|
||||
//
|
||||
X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);
|
||||
|
||||
#if defined(ENABLE_CODESIGN_EKU)
|
||||
EFI_Status = VerifyEKUsInPkcs7Signature(P7Data, P7Length, Ekus, 1, TRUE);
|
||||
if (EFI_Status != EFI_SUCCESS) {
|
||||
goto _Exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Verifies the PKCS#7 signedData structure
|
||||
//
|
||||
|
516
Cryptlib/Pk/CryptPkcs7VerifyEku.c
Normal file
516
Cryptlib/Pk/CryptPkcs7VerifyEku.c
Normal file
@ -0,0 +1,516 @@
|
||||
/** @file
|
||||
This module verifies that Enhanced Key Usages (EKU's) are present within
|
||||
a PKCS7 signature blob using OpenSSL.
|
||||
|
||||
Copyright (C) Microsoft Corporation. All Rights Reserved.
|
||||
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
**/
|
||||
|
||||
#include <Base.h>
|
||||
#include "InternalCryptLib.h"
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pkcs7.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/asn1.h>
|
||||
|
||||
/**
|
||||
This function will return the leaf signer certificate in a chain. This is
|
||||
required because certificate chains are not guaranteed to have the
|
||||
certificates in the order that they were issued.
|
||||
|
||||
A typical certificate chain looks like this:
|
||||
|
||||
|
||||
----------------------------
|
||||
| Root |
|
||||
----------------------------
|
||||
^
|
||||
|
|
||||
----------------------------
|
||||
| Policy CA | <-- Typical Trust Anchor.
|
||||
----------------------------
|
||||
^
|
||||
|
|
||||
----------------------------
|
||||
| Issuing CA |
|
||||
----------------------------
|
||||
^
|
||||
|
|
||||
-----------------------------
|
||||
/ End-Entity (leaf) signer / <-- Bottom certificate.
|
||||
----------------------------- EKU: "1.3.6.1.4.1.311.76.9.21.1"
|
||||
(Firmware Signing)
|
||||
|
||||
|
||||
@param[in] CertChain Certificate chain.
|
||||
|
||||
@param[out] SignerCert Last certificate in the chain. For PKCS7 signatures,
|
||||
this will be the end-entity (leaf) signer cert.
|
||||
|
||||
@retval EFI_SUCCESS The required EKUs were found in the signature.
|
||||
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
||||
@retval EFI_NOT_FOUND The number of signers found was not 1.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
GetSignerCertificate (
|
||||
IN CONST PKCS7 *CertChain,
|
||||
OUT X509 **SignerCert
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
STACK_OF(X509) *Signers;
|
||||
INT32 NumberSigners;
|
||||
|
||||
Status = EFI_SUCCESS;
|
||||
Signers = NULL;
|
||||
NumberSigners = 0;
|
||||
|
||||
if (CertChain == NULL || SignerCert == NULL) {
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the signers from the chain.
|
||||
//
|
||||
Signers = PKCS7_get0_signers ((PKCS7*) CertChain, NULL, PKCS7_BINARY);
|
||||
if (Signers == NULL) {
|
||||
//
|
||||
// Fail to get signers form PKCS7
|
||||
//
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// There should only be one signer in the PKCS7 stack.
|
||||
//
|
||||
NumberSigners = sk_X509_num (Signers);
|
||||
if (NumberSigners != 1) {
|
||||
//
|
||||
// The number of singers should have been 1
|
||||
//
|
||||
Status = EFI_NOT_FOUND;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
*SignerCert = sk_X509_value (Signers, 0);
|
||||
|
||||
Exit:
|
||||
//
|
||||
// Release Resources
|
||||
//
|
||||
if (Signers != NULL) {
|
||||
sk_X509_free (Signers);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Determines if the specified EKU represented in ASN1 form is present
|
||||
in a given certificate.
|
||||
|
||||
@param[in] Cert The certificate to check.
|
||||
|
||||
@param[in] Asn1ToFind The EKU to look for.
|
||||
|
||||
@retval EFI_SUCCESS We successfully identified the signing type.
|
||||
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
||||
@retval EFI_NOT_FOUND One or more EKU's were not found in the signature.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
IsEkuInCertificate (
|
||||
IN CONST X509 *Cert,
|
||||
IN ASN1_OBJECT *Asn1ToFind
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
X509 *ClonedCert;
|
||||
X509_EXTENSION *Extension;
|
||||
EXTENDED_KEY_USAGE *Eku;
|
||||
INT32 ExtensionIndex;
|
||||
INTN NumExtensions;
|
||||
ASN1_OBJECT *Asn1InCert;
|
||||
INTN Index;
|
||||
|
||||
Status = EFI_NOT_FOUND;
|
||||
ClonedCert = NULL;
|
||||
Extension = NULL;
|
||||
Eku = NULL;
|
||||
ExtensionIndex = -1;
|
||||
NumExtensions = 0;
|
||||
Asn1InCert = NULL;
|
||||
|
||||
if (Cert == NULL || Asn1ToFind == NULL) {
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Clone the certificate. This is required because the Extension API's
|
||||
// only work once per instance of an X509 object.
|
||||
//
|
||||
ClonedCert = X509_dup ((X509*)Cert);
|
||||
if (ClonedCert == NULL) {
|
||||
//
|
||||
// Fail to duplicate cert.
|
||||
//
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Look for the extended key usage.
|
||||
//
|
||||
ExtensionIndex = X509_get_ext_by_NID (ClonedCert, NID_ext_key_usage, -1);
|
||||
|
||||
if (ExtensionIndex < 0) {
|
||||
//
|
||||
// Fail to find 'NID_ext_key_usage' in Cert.
|
||||
//
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Extension = X509_get_ext (ClonedCert, ExtensionIndex);
|
||||
if (Extension == NULL) {
|
||||
//
|
||||
// Fail to get Extension form cert.
|
||||
//
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Eku = (EXTENDED_KEY_USAGE*)X509V3_EXT_d2i (Extension);
|
||||
if (Eku == NULL) {
|
||||
//
|
||||
// Fail to get Eku from extension.
|
||||
//
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
NumExtensions = sk_ASN1_OBJECT_num (Eku);
|
||||
|
||||
//
|
||||
// Now loop through the extensions, looking for the specified Eku.
|
||||
//
|
||||
for (Index = 0; Index < NumExtensions; Index++) {
|
||||
Asn1InCert = sk_ASN1_OBJECT_value (Eku, (INT32)Index);
|
||||
if (Asn1InCert == NULL) {
|
||||
//
|
||||
// Fail to get ASN object from Eku.
|
||||
//
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (OBJ_cmp(Asn1InCert, Asn1ToFind) == 0) {
|
||||
//
|
||||
// Found Eku in certificate.
|
||||
//
|
||||
Status = EFI_SUCCESS;
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
//
|
||||
// Release Resources
|
||||
//
|
||||
if (ClonedCert != NULL) {
|
||||
X509_free (ClonedCert);
|
||||
}
|
||||
|
||||
if (Eku != NULL) {
|
||||
sk_ASN1_OBJECT_pop_free (Eku, ASN1_OBJECT_free);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Determines if the specified EKUs are present in a signing certificate.
|
||||
|
||||
@param[in] SignerCert The certificate to check.
|
||||
@param[in] RequiredEKUs The EKUs to look for.
|
||||
@param[in] RequiredEKUsSize The number of EKUs
|
||||
@param[in] RequireAllPresent If TRUE, then all the specified EKUs
|
||||
must be present in the certificate.
|
||||
|
||||
@retval EFI_SUCCESS We successfully identified the signing type.
|
||||
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
||||
@retval EFI_NOT_FOUND One or more EKU's were not found in the signature.
|
||||
**/
|
||||
EFI_STATUS
|
||||
CheckEKUs(
|
||||
IN CONST X509 *SignerCert,
|
||||
IN CONST CHAR8 *RequiredEKUs[],
|
||||
IN CONST UINT32 RequiredEKUsSize,
|
||||
IN BOOLEAN RequireAllPresent
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
ASN1_OBJECT *Asn1ToFind;
|
||||
UINT32 NumEkusFound;
|
||||
UINT32 Index;
|
||||
|
||||
Status = EFI_NOT_FOUND;
|
||||
Asn1ToFind = NULL;
|
||||
NumEkusFound = 0;
|
||||
|
||||
if (SignerCert == NULL || RequiredEKUs == NULL || RequiredEKUsSize == 0) {
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
for (Index = 0; Index < RequiredEKUsSize; Index++) {
|
||||
//
|
||||
// Finding required EKU in cert.
|
||||
//
|
||||
if (Asn1ToFind != NULL) {
|
||||
ASN1_OBJECT_free(Asn1ToFind);
|
||||
Asn1ToFind = NULL;
|
||||
}
|
||||
|
||||
Asn1ToFind = OBJ_txt2obj (RequiredEKUs[Index], 0);
|
||||
if (Asn1ToFind == NULL) {
|
||||
//
|
||||
// Fail to convert required EKU to ASN1.
|
||||
//
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Status = IsEkuInCertificate (SignerCert, Asn1ToFind);
|
||||
if (Status == EFI_SUCCESS) {
|
||||
NumEkusFound++;
|
||||
if (!RequireAllPresent) {
|
||||
//
|
||||
// Found at least one, so we are done.
|
||||
//
|
||||
goto Exit;
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// Fail to find Eku in cert
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
if (Asn1ToFind != NULL) {
|
||||
ASN1_OBJECT_free(Asn1ToFind);
|
||||
}
|
||||
|
||||
if (RequireAllPresent &&
|
||||
NumEkusFound == RequiredEKUsSize) {
|
||||
//
|
||||
// Found all required EKUs in certificate.
|
||||
//
|
||||
Status = EFI_SUCCESS;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
This function receives a PKCS#7 formatted signature blob,
|
||||
looks for the EKU SEQUENCE blob, and if found then looks
|
||||
for all the required EKUs. This function was created so that
|
||||
the Surface team can cut down on the number of Certificate
|
||||
Authorities (CA's) by checking EKU's on leaf signers for
|
||||
a specific product. This prevents one product's certificate
|
||||
from signing another product's firmware or unlock blobs.
|
||||
|
||||
Note that this function does not validate the certificate chain.
|
||||
That needs to be done before using this function.
|
||||
|
||||
@param[in] Pkcs7Signature The PKCS#7 signed information content block. An array
|
||||
containing the content block with both the signature,
|
||||
the signer's certificate, and any necessary intermediate
|
||||
certificates.
|
||||
@param[in] Pkcs7SignatureSize Number of bytes in Pkcs7Signature.
|
||||
@param[in] RequiredEKUs Array of null-terminated strings listing OIDs of
|
||||
required EKUs that must be present in the signature.
|
||||
@param[in] RequiredEKUsSize Number of elements in the RequiredEKUs string array.
|
||||
@param[in] RequireAllPresent If this is TRUE, then all of the specified EKU's
|
||||
must be present in the leaf signer. If it is
|
||||
FALSE, then we will succeed if we find any
|
||||
of the specified EKU's.
|
||||
|
||||
@retval EFI_SUCCESS The required EKUs were found in the signature.
|
||||
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
||||
@retval EFI_NOT_FOUND One or more EKU's were not found in the signature.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VerifyEKUsInPkcs7Signature (
|
||||
IN CONST UINT8 *Pkcs7Signature,
|
||||
IN CONST UINT32 SignatureSize,
|
||||
IN CONST CHAR8 *RequiredEKUs[],
|
||||
IN CONST UINT32 RequiredEKUsSize,
|
||||
IN BOOLEAN RequireAllPresent
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
PKCS7 *Pkcs7;
|
||||
STACK_OF(X509) *CertChain;
|
||||
INT32 SignatureType;
|
||||
INT32 NumberCertsInSignature;
|
||||
X509 *SignerCert;
|
||||
UINT8 *SignedData;
|
||||
UINT8 *Temp;
|
||||
UINTN SignedDataSize;
|
||||
BOOLEAN IsWrapped;
|
||||
BOOLEAN Ok;
|
||||
|
||||
Status = EFI_SUCCESS;
|
||||
Pkcs7 = NULL;
|
||||
CertChain = NULL;
|
||||
SignatureType = 0;
|
||||
NumberCertsInSignature = 0;
|
||||
SignerCert = NULL;
|
||||
SignedData = NULL;
|
||||
SignedDataSize = 0;
|
||||
IsWrapped = FALSE;
|
||||
Ok = FALSE;
|
||||
|
||||
//
|
||||
//Validate the input parameters.
|
||||
//
|
||||
if (Pkcs7Signature == NULL ||
|
||||
SignatureSize == 0 ||
|
||||
RequiredEKUs == NULL ||
|
||||
RequiredEKUsSize == 0) {
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (RequiredEKUsSize == 1) {
|
||||
RequireAllPresent = TRUE;
|
||||
}
|
||||
|
||||
//
|
||||
// Wrap the PKCS7 data if needed.
|
||||
//
|
||||
Ok = WrapPkcs7Data (Pkcs7Signature,
|
||||
SignatureSize,
|
||||
&IsWrapped,
|
||||
&SignedData,
|
||||
&SignedDataSize);
|
||||
if (!Ok) {
|
||||
//
|
||||
// Fail to Wrap the PKCS7 data.
|
||||
//
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Temp = SignedData;
|
||||
|
||||
//
|
||||
// Create the PKCS7 object.
|
||||
//
|
||||
Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (INT32)SignedDataSize);
|
||||
if (Pkcs7 == NULL) {
|
||||
//
|
||||
// Fail to read PKCS7 data.
|
||||
//
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the certificate chain.
|
||||
//
|
||||
SignatureType = OBJ_obj2nid (Pkcs7->type);
|
||||
switch (SignatureType) {
|
||||
case NID_pkcs7_signed:
|
||||
if (Pkcs7->d.sign != NULL) {
|
||||
CertChain = Pkcs7->d.sign->cert;
|
||||
}
|
||||
break;
|
||||
case NID_pkcs7_signedAndEnveloped:
|
||||
if (Pkcs7->d.signed_and_enveloped != NULL) {
|
||||
CertChain = Pkcs7->d.signed_and_enveloped->cert;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Ensure we have a certificate stack
|
||||
//
|
||||
if (CertChain == NULL) {
|
||||
//
|
||||
// Fail to get the certificate stack from signature.
|
||||
//
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Find out how many certificates were in the PKCS7 signature.
|
||||
//
|
||||
NumberCertsInSignature = sk_X509_num (CertChain);
|
||||
|
||||
if (NumberCertsInSignature == 0) {
|
||||
//
|
||||
// Fail to find any certificates in signature.
|
||||
//
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the leaf signer.
|
||||
//
|
||||
Status = GetSignerCertificate (Pkcs7, &SignerCert);
|
||||
if (Status != EFI_SUCCESS || SignerCert == NULL) {
|
||||
//
|
||||
// Fail to get the end-entity leaf signer certificate.
|
||||
//
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Status = CheckEKUs (SignerCert, RequiredEKUs, RequiredEKUsSize, RequireAllPresent);
|
||||
if (Status != EFI_SUCCESS) {
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
//
|
||||
// Release Resources
|
||||
//
|
||||
// If the signature was not wrapped, then the call to WrapData() will allocate
|
||||
// the data and add a header to it
|
||||
//
|
||||
if (!IsWrapped && SignedData) {
|
||||
free (SignedData);
|
||||
}
|
||||
|
||||
if (Pkcs7 != NULL) {
|
||||
PKCS7_free (Pkcs7);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
29
Delivering_Sbat_Revocations.md
Normal file
29
Delivering_Sbat_Revocations.md
Normal file
@ -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.
|
@ -109,7 +109,7 @@ INCLUDES = -nostdinc \
|
||||
|
||||
override DEFAULT_FEATUREFLAGS = \
|
||||
-std=gnu11 \
|
||||
-ggdb \
|
||||
-ggdb -gdwarf-4 -gstrict-dwarf \
|
||||
-ffreestanding \
|
||||
$(shell $(CC) -fmacro-prefix-map=./=./ -E -x c /dev/null >/dev/null 2>&1 && echo -fmacro-prefix-map='$(TOPDIR)/=$(DEBUGSRC)') \
|
||||
-fno-stack-protector \
|
||||
@ -149,10 +149,6 @@ ifneq ($(origin REQUIRE_TPM), undefined)
|
||||
DEFINES += -DREQUIRE_TPM
|
||||
endif
|
||||
|
||||
ifneq ($(origin DISABLE_EBS_PROTECTION), undefined)
|
||||
DEFINES += -DDISABLE_EBS_PROTECTION
|
||||
endif
|
||||
|
||||
ifneq ($(origin DISABLE_REMOVABLE_LOAD_OPTIONS), undefined)
|
||||
DEFINES += -DDISABLE_REMOVABLE_LOAD_OPTIONS
|
||||
endif
|
||||
@ -201,4 +197,6 @@ ifneq ($(VERBOSE),)
|
||||
export VERBOSE
|
||||
endif
|
||||
|
||||
export DEFINES
|
||||
|
||||
# vim:filetype=make
|
||||
|
41
Makefile
41
Makefile
@ -1,7 +1,7 @@
|
||||
default : all
|
||||
|
||||
NAME = shim
|
||||
VERSION = 15.8
|
||||
VERSION = 16.0
|
||||
ifneq ($(origin RELEASE),undefined)
|
||||
DASHRELEASE ?= -$(RELEASE)
|
||||
else
|
||||
@ -38,12 +38,12 @@ CFLAGS += -DENABLE_SHIM_CERT
|
||||
else
|
||||
TARGETS += $(MMNAME) $(FBNAME)
|
||||
endif
|
||||
OBJS = shim.o globals.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o
|
||||
OBJS = shim.o globals.o memattrs.o mok.o netboot.o cert.o dp.o loader-proto.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o utils.o
|
||||
KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer
|
||||
ORIG_SOURCES = shim.c globals.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S
|
||||
MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o
|
||||
ORIG_SOURCES = shim.c globals.c memattrs.c mok.c netboot.c dp.c loader-proto.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S
|
||||
MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o dp.o
|
||||
ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h)
|
||||
FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o
|
||||
FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o utils.o
|
||||
ORIG_FALLBACK_SRCS = fallback.c
|
||||
SBATPATH = $(TOPDIR)/data/sbat.csv
|
||||
|
||||
@ -69,16 +69,24 @@ ifneq ($(origin FALLBACK_VERBOSE_WAIT), undefined)
|
||||
CFLAGS += -DFALLBACK_VERBOSE_WAIT=$(FALLBACK_VERBOSE_WAIT)
|
||||
endif
|
||||
|
||||
all: confcheck $(TARGETS)
|
||||
all: confcheck certcheck $(TARGETS)
|
||||
|
||||
confcheck:
|
||||
ifneq ($(origin EFI_PATH),undefined)
|
||||
$(error EFI_PATH is no longer supported, you must build using the supplied copy of gnu-efi)
|
||||
endif
|
||||
|
||||
certcheck:
|
||||
ifneq ($(origin VENDOR_CERT_FILE), undefined)
|
||||
@if grep -q "BEGIN" $(VENDOR_CERT_FILE); then \
|
||||
echo "$(VENDOR_CERT_FILE) is PEM-format, convert to DER!"; \
|
||||
exit 1; \
|
||||
fi
|
||||
endif
|
||||
|
||||
compile_commands.json : Makefile Make.rules Make.defaults
|
||||
make clean
|
||||
bear -- make COMPILER=clang test all
|
||||
bear -- make COMPILER=clang WARNFLAGS+="-Wno-#warnings" test all
|
||||
sed -i \
|
||||
-e 's/"-maccumulate-outgoing-args",//g' \
|
||||
$@
|
||||
@ -92,6 +100,7 @@ shim.crt:
|
||||
shim.cer: shim.crt
|
||||
$(OPENSSL) x509 -outform der -in $< -out $@
|
||||
|
||||
|
||||
.NOTPARALLEL: shim_cert.h
|
||||
shim_cert.h: shim.cer
|
||||
echo "static UINT8 shim_cert[] __attribute__((__unused__)) = {" > $@
|
||||
@ -113,8 +122,12 @@ shim.o: $(SOURCES)
|
||||
ifneq ($(origin ENABLE_SHIM_CERT),undefined)
|
||||
shim.o: shim_cert.h
|
||||
endif
|
||||
# Both of these need to be here so that when TOPDIR is unset, make isn't trying
|
||||
# to match against ./sbat_var.S, which isn't a target it will ever try to build.
|
||||
$(TOPDIR)/sbat_var.S sbat_var.S: generated_sbat_var_defs.h
|
||||
shim.o: $(wildcard $(TOPDIR)/*.h)
|
||||
|
||||
|
||||
sbat.%.csv : data/sbat.%.csv
|
||||
$(DOS2UNIX) $(D2UFLAGS) $< $@
|
||||
tail -c1 $@ | read -r _ || echo >> $@ # ensure a trailing newline
|
||||
@ -180,6 +193,13 @@ lib/lib.a: | $(TOPDIR)/lib/Makefile $(wildcard $(TOPDIR)/include/*.[ch])
|
||||
post-process-pe : $(TOPDIR)/post-process-pe.c
|
||||
$(HOSTCC) -std=gnu11 -Og -g3 -Wall -Wextra -Wno-missing-field-initializers -Werror -o $@ $<
|
||||
|
||||
generate_sbat_var_defs: $(TOPDIR)/generate_sbat_var_defs.c
|
||||
$(HOSTCC) -std=gnu11 -Og -g3 -Wall -Wextra -Wno-missing-field-initializers -Werror -o $@ $<
|
||||
|
||||
.NOTPARALLEL: generated_sbat_var_defs.h
|
||||
generated_sbat_var_defs.h: generate_sbat_var_defs
|
||||
./generate_sbat_var_defs $(TOPDIR) > $@
|
||||
|
||||
buildid : $(TOPDIR)/buildid.c
|
||||
$(HOSTCC) -I/usr/include -Og -g3 -Wall -Werror -Wextra -o $@ $< -lelf
|
||||
|
||||
@ -261,6 +281,7 @@ endif
|
||||
-j .dynamic -j .rodata -j .rel* \
|
||||
-j .rela* -j .dyn -j .reloc -j .eh_frame \
|
||||
-j .vendor_cert -j .sbat -j .sbatlevel \
|
||||
--file-alignment 0x1000 \
|
||||
$(FORMAT) $< $@
|
||||
./post-process-pe -vv $(POST_PROCESS_PE_FLAGS) $@
|
||||
|
||||
@ -280,6 +301,7 @@ endif
|
||||
-j .debug_info -j .debug_abbrev -j .debug_aranges \
|
||||
-j .debug_line -j .debug_str -j .debug_ranges \
|
||||
-j .note.gnu.build-id \
|
||||
--file-alignment 0x1000 \
|
||||
$< $@
|
||||
|
||||
ifneq ($(origin ENABLE_SBSIGN),undefined)
|
||||
@ -302,7 +324,7 @@ fuzz fuzz-clean fuzz-coverage fuzz-lto :
|
||||
EFI_INCLUDES="$(EFI_INCLUDES)" \
|
||||
fuzz-clean $@
|
||||
|
||||
test test-clean test-coverage test-lto :
|
||||
test test-clean test-coverage test-lto : generated_sbat_var_defs.h
|
||||
@make -f $(TOPDIR)/include/test.mk \
|
||||
COMPILER="$(COMPILER)" \
|
||||
CROSS_COMPILE="$(CROSS_COMPILE)" \
|
||||
@ -346,6 +368,7 @@ clean-lib-objs:
|
||||
clean-shim-objs:
|
||||
@rm -rvf $(TARGET) *.o $(SHIM_OBJS) $(MOK_OBJS) $(FALLBACK_OBJS) $(KEYS) certdb $(BOOTCSVNAME)
|
||||
@rm -vf *.debug *.so *.efi *.efi.* *.tar.* version.c buildid post-process-pe compile_commands.json
|
||||
@rm -vf generate_sbat_var_defs generated_sbat_var_defs.h
|
||||
@rm -vf Cryptlib/*.[oa] Cryptlib/*/*.[oa]
|
||||
@if [ -d .git ] ; then git clean -f -d -e 'Cryptlib/OpenSSL/*'; fi
|
||||
|
||||
@ -361,7 +384,7 @@ clean-cryptlib-objs:
|
||||
|
||||
clean: clean-shim-objs clean-fuzz-objs clean-test-objs clean-gnu-efi clean-openssl-objs clean-cryptlib-objs clean-lib-objs
|
||||
|
||||
GITTAG = $(VERSION)
|
||||
GITTAG = $(shell echo $(VERSION) | sed 's/~/-/g')
|
||||
|
||||
test-archive:
|
||||
@./make-archive $(if $(call get-config,shim.origin),--origin "$(call get-config,shim.origin)") --test "$(VERSION)"
|
||||
|
@ -142,17 +142,14 @@ static UINT32 count_keys(void *Data, UINTN DataSize)
|
||||
void *end = Data + DataSize;
|
||||
|
||||
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
|
||||
/* Use ptr arithmetics to ensure bounded access. Do not allow 0
|
||||
* SignatureListSize that will cause endless loop. */
|
||||
if ((void *)(CertList + 1) > end
|
||||
|| CertList->SignatureListSize == 0) {
|
||||
/* Use ptr arithmetics to ensure bounded access. */
|
||||
if ((void *)(CertList + 1) > end) {
|
||||
console_notify
|
||||
(L"Invalid MOK detected! Ignoring MOK List.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (CertList->SignatureListSize == 0 ||
|
||||
CertList->SignatureListSize <= CertList->SignatureSize) {
|
||||
if (CertList->SignatureListSize <= CertList->SignatureSize) {
|
||||
console_errorbox(L"Corrupted signature list");
|
||||
return 0;
|
||||
}
|
||||
|
35
MokVars.txt
35
MokVars.txt
@ -63,28 +63,51 @@ State variables:
|
||||
MokList: A list of authorized keys and hashes. An EFI_SIGNATURE_LIST
|
||||
as described in the UEFI specification. BS,NV
|
||||
|
||||
MokListRT: A copy of MokList made available to the kernel at runtime. RT
|
||||
MokListRT: A copy of MokList made available to the kernel at runtime. BS,RT
|
||||
|
||||
MokListX: A list of forbidden keys and hashes. An EFI_SIGNATURE_LIST
|
||||
as described in the UEFI specification. BS,NV
|
||||
|
||||
MokListXRT: A copy of MokListX made available to the kernel at runtime. RT
|
||||
MokListXRT: A copy of MokListX made available to the kernel at runtime. BS,RT
|
||||
|
||||
MokSBState: An 8-bit unsigned integer. If 1, shim will switch to
|
||||
insecure mode. BS,NV
|
||||
|
||||
MokSBStateRT: A copy of MokSBState made available to the kernel at runtime.
|
||||
This allows the OS to query the shim secure mode setting for its own
|
||||
verification purposes. BS,RT
|
||||
|
||||
MokDBState: An 8-bit unsigned integer. If 1, shim will not use db for
|
||||
verification. BS,NV
|
||||
|
||||
MokIgnoreDB: An 8-bit unsigned integer. This allows the OS to query whether
|
||||
or not to import DB certs for its own verification purposes.
|
||||
MokIgnoreDB: A copy of MokDBState made available to the kernel at runtime.
|
||||
This allows the OS to query whether or not to import DB certs for its own
|
||||
verification purposes. BS,RT
|
||||
|
||||
MokPWStore: A SHA-256 representation of the password set by the user
|
||||
via MokPW. The user will be prompted to enter this password in order
|
||||
to interact with MokManager.
|
||||
to interact with MokManager. BS,NV
|
||||
|
||||
MokListTrusted: An 8-bit unsigned integer. If 1, it signifies to Linux
|
||||
to trust CA keys in the MokList. BS,NV
|
||||
|
||||
MokListTrustedRT: A copy of MokListTrusted made available to the kernel
|
||||
at runtime. RT
|
||||
at runtime. BS,RT
|
||||
|
||||
HSIStatus: Status of various security features:
|
||||
heap-is-executable: 0: heap allocations are not executable by default
|
||||
1: heap allocations are executable
|
||||
stack-is-executable: 0: UEFI stack is not executable
|
||||
1: UEFI stack is executable
|
||||
ro-sections-are-writable: 0: read-only sections are not writable
|
||||
1: read-only sections are writable
|
||||
has-memory-attribute-protocol: 0: platform does not provide the EFI Memory Attribute Protocol
|
||||
1: platform does provide the EFI Memory Attribute Protocol
|
||||
has-dxe-services-table: 0: platform does not provide the DXE Services Table
|
||||
1: platform does provide the DXE Services Table
|
||||
has-get-memory-space-descriptor: 0: platform's DST does not populate GetMemorySpaceDescriptor
|
||||
1: platform's DST does populate GetMemorySpaceDescriptor
|
||||
has-set-memory-space-descriptor: 0: platform's DST does not populate SetMemorySpaceDescriptor
|
||||
1: platform's DST does populate SetMemorySpaceDescriptor
|
||||
shim-has-nx-compat-set: 0: the running shim binary does not have NX_COMPAT bit set
|
||||
1: the running shim binary does have the NX_COMPAT bit set
|
||||
|
10
README.tpm
10
README.tpm
@ -13,14 +13,20 @@ PCR7:
|
||||
- MokListX - the Mok denylist, logged as "MokListX"
|
||||
- vendor_dbx - shim's built-in vendor denylist, logged as "dbx"
|
||||
- DB - the system allowlist, logged as "db"
|
||||
- vendor_db - shim's built-in vendor allowlist, logged as "db"
|
||||
- MokList the Mok allowlist, logged as "MokList"
|
||||
- vendor_db - shim's built-in vendor allowlist, logged as "vendor_db"
|
||||
- MokListRT the runtime Mok allowlist, logged as "MokListRT"
|
||||
- vendor_cert - shim's built-in vendor allowlist, logged as "Shim"
|
||||
- shim_cert - shim's build-time generated allowlist, logged as "Shim"
|
||||
- MokSBState will be extended into PCR7 if it is set, logged as
|
||||
"MokSBState".
|
||||
- SBAT will be extended into PCR7 if it is set, logged as "SBAT"
|
||||
|
||||
Note: In the past this document called out that vendor_db was logged as
|
||||
"db", when in fact the code didn't do that. Since changing the code
|
||||
risks breaking recorded logs, the documentation is update to reflect
|
||||
reality. vendor_dbx is in fact logged as "dbx".
|
||||
|
||||
|
||||
PCR8:
|
||||
- If you're using the grub2 TPM patchset we cary in Fedora, the kernel command
|
||||
line and all grub commands (including all of grub.cfg that gets run) are
|
||||
|
119
SBAT.md
119
SBAT.md
@ -170,7 +170,7 @@ specific product's component, vendors may ask for a product-specific generation
|
||||
number to be published for one of their product's components. This avoids
|
||||
triggering an industry wide re-publishing of otherwise safe components.
|
||||
|
||||
In the example above, 1 is sbat's minimum global generation number.
|
||||
In the example above, 1 in `sbat,1` is sbat's minimum global generation number.
|
||||
|
||||
A **product-specific minimum generation number** only applies to the instance of
|
||||
that component that is signed with that product name. Another product's
|
||||
@ -184,7 +184,8 @@ entire industry that uses that component re-release, just that product's
|
||||
minimum generation number would be incremented and that product's component
|
||||
re-released along with a UEFI variable update specifying that requirement.
|
||||
|
||||
In the example above, 1 is grub.acme's product-specific minimum generation number.
|
||||
In the example above, 1 in `grub.acme,1` is grub.acme's product-specific minimum
|
||||
generation number.
|
||||
|
||||
The global and product-specific generation number name spaces are not tied to
|
||||
each other. The global number is managed externally, and the vast majority of
|
||||
@ -213,68 +214,92 @@ product. Setting a product-specific generation number for such an event
|
||||
eliminates the need for other vendors to have to re-release the binaries for
|
||||
their products with an incremented global number.
|
||||
|
||||
However, once the global number is bumped for the next upstream CVE fix there
|
||||
will be no further need to carry that product-specific generation number.
|
||||
Satisfying the check of the global number will also exclude any of the older
|
||||
product-specific binaries.
|
||||
Both generation numbers should only ever go up; they should never be reset.
|
||||
|
||||
For example: There is a global CVE disclosure and all vendors coordinate to
|
||||
release fixed components on the disclosure date. This release bumps the global
|
||||
generation number for GRUB to 4.
|
||||
#### Example: a vendor forking a global project
|
||||
|
||||
SBAT revocation data would then require a GRUB with a global generation number
|
||||
of 4.
|
||||
Let's imagine a fictional company named "Vendor C" having an active fork of the
|
||||
upstream GNU GRUB2. Therefore, Vendor C provides their own product-specific
|
||||
generation number. This is happening at the point in time, when the upstream
|
||||
product's entry starts with `grub,3`, hence why Vendor C's product ships with
|
||||
entries similar to:
|
||||
|
||||
```
|
||||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
grub,3,Free Software Foundation,[...]
|
||||
grub.vendorc,1,Vendor C,[...]
|
||||
```
|
||||
|
||||
Suddenly there is a global CVE disclosure and all vendors coordinate
|
||||
to release fixed components on the disclosure date. This release bumps the
|
||||
global generation number for GRUB from 3 to 4. Vendor C's product's binaries
|
||||
are now shipped with the entries:
|
||||
|
||||
```
|
||||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
grub,4,Free Software Foundation,[...]
|
||||
grub.vendorc,1,Vendor C,[...]
|
||||
```
|
||||
|
||||
After this, the UEFI SBAT revocation variable (named *SbatLevel*) would be
|
||||
updated to raise the mandatory minimal global generation number for GRUB to 4.
|
||||
|
||||
However, Vendor C mis-merges the patches into one of their products and does
|
||||
not become aware of the fact that this mis-merge created an additional
|
||||
vulnerability until after they have published a signed binary in that,
|
||||
vulnerable, state.
|
||||
vulnerability until after they have published a signed binary in that vulnerable
|
||||
state. Vendor C's GRUB binary can now be abused to compromise their systems.
|
||||
|
||||
Vendor C's GRUB binary can now be used to compromise anyone's system.
|
||||
To remedy this, Vendor C will release a security-fixed binary with the same
|
||||
global generation number and an updated product-specific generation number (set
|
||||
to 2):
|
||||
|
||||
To remedy this, Vendor C will release a fixed binary with the same global
|
||||
generation number and the product-specific generation number set to 1.
|
||||
```
|
||||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
grub,4,Free Software Foundation,[...]
|
||||
grub.vendorc,2,Vendor C,[...]
|
||||
```
|
||||
|
||||
SBAT revocation data would then require a GRUB with a global generation number
|
||||
of 4, as well as a product-specific generation number of 1 for the product that
|
||||
had the vulnerable binary.
|
||||
Again, in the perfect scenario, to provide the perfect security, the UEFI SBAT
|
||||
revocation variable would then be set, so that GRUB with a global generation
|
||||
number of only 4 or higher would be able to be booted, as well as Vendor C's
|
||||
products with their number of only 2 or higher. See for yourself, how this looks
|
||||
like, in the [SbatLevel_Variable.txt](./SbatLevel_Variable.txt) file.
|
||||
|
||||
If and when there is another upstream fix for a CVE that would bump the global
|
||||
number, this product-specific number can be dropped from the UEFI revocation
|
||||
variable.
|
||||
If and when there is another upstream fix for a CVE that would bump the GRUB
|
||||
global number to 5, this product-specific number can be dropped from the UEFI
|
||||
*SbatLevel* variable (because the binaries starting with upstream's `grub,4`
|
||||
entry would get denylisted anyway), but with the current consensus it's
|
||||
important to keep the product-specific number shipped with the product's binary,
|
||||
like in the case of Vendor C:
|
||||
|
||||
If this same Vendor C has a similar event after the global number is
|
||||
incremented, they would again set their product-specific or **version-specific
|
||||
number** to 1. If they have a second event on the same component, they would
|
||||
set their product-specific or version-specific number to 2.
|
||||
```
|
||||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
grub,5,Free Software Foundation,[...]
|
||||
grub.vendorc,2,Vendor C,[...]
|
||||
```
|
||||
|
||||
In such an event, a vendor would set the product-specific or version-specific
|
||||
generation number based on whether the mis-merge occurred in all of their
|
||||
branches or in just a subset of them. The goal is generally to limit end
|
||||
customer impact with as few re-releases as possible, while not creating an
|
||||
unnecessarily large UEFI revocation variable payload.
|
||||
The goal is generally to limit end user impact with as few
|
||||
re-releases as possible, while not creating an unnecessarily large UEFI
|
||||
revocation variable payload.
|
||||
|
||||
| | prior to<br>disclosure\* | after<br>disclosure | after Vendor C's<br>first update | after Vendor C's<br>second update | after next global<br>disclosure |
|
||||
|--------------------------------------------------------------------------------------|--------------------------|---------------------|----------------------------------|-----------------------------------|---------------------------------|
|
||||
| | prior to GRUB<br>first disclosure\* | after GRUB<br>first disclosure\* | after Vendor C's<br>first update | after Vendor C's<br>second update | after GRUB<br>second disclosure\* |
|
||||
|--------------------------------------------------------------------------------------|-------------------------------------|----------------------------------|----------------------------------|-----------------------------------|-----------------------------------|
|
||||
| GRUB global<br>generation number in<br>artifacts .sbat section | 3 | 4 | 4 | 4 | 5 |
|
||||
| Vendor C's product-specific<br>generation number in artifact's<br>.sbat section | 1 | 1 | 2 | 3 | 1 |
|
||||
| GRUB global<br>generation number in<br>UEFI SBAT revocation variable | 3 | 4 | 4 | 4 | 5 |
|
||||
| Vendor C's product-specific<br>generation number in<br>UEFI SBAT revocation variable | not set | not set | 2 | 3 | not set |
|
||||
| Vendor C's product<br>generation number in<br>artifact's .sbat section | 1 | 1 | 2 | 3 | 3 |
|
||||
| GRUB global<br>generation number in<br>UEFI *SbatLevel* variable | 3 | 4 | 4 | 4 | 5 |
|
||||
| Vendor C's product<br>generation number in<br>UEFI *SbatLevel* variable | not set | not set | 2 | 3 | not set |
|
||||
|
||||
\* A disclosure is the event/date where a CVE and fixes for it are made public.
|
||||
|
||||
The product-specific generation number does not reset and continues to
|
||||
monotonically increase over the course of these non-global events. Continuity of more
|
||||
specific generation numbers must be maintained in this way in order to satisfy
|
||||
checks against older revocation data.
|
||||
The product-specific generation number does not reset and continues to increase
|
||||
over the course of these non-global events. Continuity of more specific
|
||||
generation numbers must be maintained in this way in order to satisfy checks
|
||||
against older revocation data.
|
||||
|
||||
The variable payload will be stored publicly in the shim source base and
|
||||
identify the global generation associated with a product or version-specific
|
||||
one. The payload is also built into shim to additionally limit exposure.
|
||||
|
||||
At this time of writing, all version-numbers are set to 1. Presumably at some point,
|
||||
updated numbers will be published on the respective websites of the associated vendors
|
||||
and components.
|
||||
The UEFI *SbatLevel* variable payload will be stored publicly in the shim source
|
||||
base and identify the global generation associated with a product or
|
||||
version-specific one. The payload is also built into shim to additionally limit
|
||||
exposure.
|
||||
|
||||
#### Retiring Signed Releases
|
||||
|
||||
|
@ -1,6 +1,15 @@
|
||||
In order to apply SBAT based revocations on systems that will never
|
||||
run shim, code running in boot services context needs to set the
|
||||
following variable:
|
||||
This file is the single source for SbatLevel revocations the format
|
||||
follows the variable payload and should not have any leading or
|
||||
trailing whitespace on the same line.
|
||||
|
||||
Short descriptions of the revocations as well as CVE assignments (when
|
||||
available) should be provided when an entry is added.
|
||||
|
||||
On systems that run shim, shim will manage these revocations. Sytems
|
||||
that never run shim, primarily Windows, but this applies to any OS
|
||||
that supports UEFI Secure Boot under the UEFI CA without shim can
|
||||
apply SBAT based revocations by setting the following variable
|
||||
from code running in boot services context.
|
||||
|
||||
Name: SbatLevel
|
||||
Attributes: (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)
|
||||
@ -97,12 +106,22 @@ shim,4
|
||||
grub,3
|
||||
grub.debian,4
|
||||
|
||||
Since http boot shim CVE is considerably more serious than then GRUB
|
||||
ntfs CVEs shim is delivering the shim revocation without the updated
|
||||
GRUB revocation as a latest payload.
|
||||
|
||||
To revoke both the impacted shim and impacted GRUB binaries:
|
||||
Revocations for:
|
||||
- January 2024 shim CVEs
|
||||
- October 2023 grub CVEs
|
||||
- Debian/Ubuntu (peimage) CVE-2024-2312
|
||||
|
||||
sbat,1,2024<date TBD>
|
||||
sbat,1,2024040900
|
||||
shim,4
|
||||
grub,4
|
||||
grub.peimage,2
|
||||
|
||||
|
||||
Revocations for:
|
||||
- Februady 2025 GRUB CVEs
|
||||
|
||||
sbat,1,2025021800
|
||||
shim,4
|
||||
grub,5
|
||||
|
||||
|
43
dp.c
Normal file
43
dp.c
Normal file
@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
/*
|
||||
* dp.c - device path helpers
|
||||
* Copyright Peter Jones <pjones@redhat.com>
|
||||
*/
|
||||
|
||||
#include "shim.h"
|
||||
|
||||
int
|
||||
is_removable_media_path(EFI_LOADED_IMAGE *li)
|
||||
{
|
||||
unsigned int pathlen = 0;
|
||||
CHAR16 *bootpath = NULL;
|
||||
int ret = 0;
|
||||
|
||||
bootpath = DevicePathToStr(li->FilePath);
|
||||
|
||||
/* Check the beginning of the string and the end, to avoid
|
||||
* caring about which arch this is. */
|
||||
/* I really don't know why, but sometimes bootpath gives us
|
||||
* L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here...
|
||||
*/
|
||||
if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) &&
|
||||
StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15) &&
|
||||
StrnCaseCmp(bootpath, L"EFI\\BOOT\\BOOT", 13) &&
|
||||
StrnCaseCmp(bootpath, L"EFI\\BOOT\\/BOOT", 14))
|
||||
goto error;
|
||||
|
||||
pathlen = StrLen(bootpath);
|
||||
if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI"))
|
||||
goto error;
|
||||
|
||||
ret = 1;
|
||||
|
||||
error:
|
||||
if (bootpath)
|
||||
FreePool(bootpath);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// vim:fenc=utf-8:tw=75:noet
|
181
errlog.c
181
errlog.c
@ -99,4 +99,185 @@ ClearErrors(VOID)
|
||||
errs = NULL;
|
||||
}
|
||||
|
||||
static size_t
|
||||
format_error_log(UINT8 *dest, size_t dest_sz)
|
||||
{
|
||||
size_t err_log_sz = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
for (UINTN i = 0; i < nerrs; i++)
|
||||
err_log_sz += StrSize(errs[i]);
|
||||
|
||||
if (!dest || dest_sz < err_log_sz)
|
||||
return err_log_sz;
|
||||
|
||||
ZeroMem(dest, err_log_sz);
|
||||
for (UINTN i = 0; i < nerrs; i++) {
|
||||
UINTN sz = StrSize(errs[i]);
|
||||
CopyMem(&dest[pos], errs[i], sz);
|
||||
pos += sz;
|
||||
}
|
||||
|
||||
return err_log_sz;
|
||||
}
|
||||
|
||||
static UINT8 *debug_log = NULL;
|
||||
static size_t debug_log_sz = 0;
|
||||
static size_t debug_log_alloc = 0;
|
||||
|
||||
UINTN EFIAPI
|
||||
log_debug_print(const CHAR16 *fmt, ...)
|
||||
{
|
||||
ms_va_list args;
|
||||
CHAR16 *buf;
|
||||
size_t buf_sz;
|
||||
UINTN ret = 0;
|
||||
|
||||
ms_va_start(args, fmt);
|
||||
buf = VPoolPrint(fmt, args);
|
||||
if (!buf)
|
||||
return 0;
|
||||
ms_va_end(args);
|
||||
|
||||
ret = StrLen(buf);
|
||||
buf_sz = StrSize(buf);
|
||||
if (debug_log_sz + buf_sz > debug_log_alloc) {
|
||||
size_t new_alloc_sz = debug_log_alloc;
|
||||
CHAR16 *new_debug_log;
|
||||
|
||||
new_alloc_sz += buf_sz;
|
||||
new_alloc_sz = ALIGN_UP(new_alloc_sz, EFI_PAGE_SIZE);
|
||||
|
||||
new_debug_log = ReallocatePool(debug_log, debug_log_alloc, new_alloc_sz);
|
||||
if (!new_debug_log)
|
||||
return 0;
|
||||
debug_log = (UINT8 *)new_debug_log;
|
||||
debug_log_alloc = new_alloc_sz;
|
||||
}
|
||||
|
||||
CopyMem(&debug_log[debug_log_sz], buf, buf_sz);
|
||||
debug_log_sz += buf_sz;
|
||||
FreePool(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t
|
||||
format_debug_log(UINT8 *dest, size_t dest_sz)
|
||||
{
|
||||
if (!dest || dest_sz < debug_log_sz)
|
||||
return debug_log_sz;
|
||||
|
||||
ZeroMem(dest, debug_log_sz);
|
||||
CopyMem(dest, debug_log, debug_log_sz);
|
||||
return debug_log_sz;
|
||||
}
|
||||
|
||||
void
|
||||
replace_config_table(EFI_CONFIGURATION_TABLE *CT, EFI_PHYSICAL_ADDRESS new_table, UINTN new_table_pages)
|
||||
{
|
||||
EFI_GUID bogus_guid = { 0x29f2f0db, 0xd025, 0x4aa6, { 0x99, 0x58, 0xa0, 0x21, 0x8b, 0x1d, 0xec, 0x0e }};
|
||||
EFI_STATUS efi_status;
|
||||
|
||||
if (CT) {
|
||||
CopyMem(&CT->VendorGuid, &bogus_guid, sizeof(bogus_guid));
|
||||
if (CT->VendorTable &&
|
||||
CT->VendorTable == (void *)(uintptr_t)mok_config_table) {
|
||||
BS->FreePages(mok_config_table, mok_config_table_pages);
|
||||
CT->VendorTable = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
efi_status = BS->InstallConfigurationTable(&MOK_VARIABLE_STORE,
|
||||
(void *)(uintptr_t)new_table);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
console_print(L"Could not re-install MoK configuration table: %r\n", efi_status);
|
||||
} else {
|
||||
mok_config_table = new_table;
|
||||
mok_config_table_pages = new_table_pages;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
save_logs(void)
|
||||
{
|
||||
struct mok_variable_config_entry *cfg_table = NULL;
|
||||
struct mok_variable_config_entry *new_table = NULL;
|
||||
struct mok_variable_config_entry *entry = NULL;
|
||||
EFI_PHYSICAL_ADDRESS physaddr = 0;
|
||||
UINTN new_table_pages = 0;
|
||||
size_t new_table_sz;
|
||||
UINTN pos = 0;
|
||||
EFI_STATUS efi_status;
|
||||
size_t errlog_sz, dbglog_sz;
|
||||
|
||||
errlog_sz = format_error_log(NULL, 0);
|
||||
dbglog_sz = format_debug_log(NULL, 0);
|
||||
|
||||
if (errlog_sz == 0 && dbglog_sz == 0) {
|
||||
console_print(L"No console or debug log?!?!?\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) {
|
||||
EFI_CONFIGURATION_TABLE *CT;
|
||||
CT = &ST->ConfigurationTable[i];
|
||||
|
||||
if (CompareGuid(&MOK_VARIABLE_STORE, &CT->VendorGuid) == 0) {
|
||||
cfg_table = CT->VendorTable;
|
||||
break;
|
||||
}
|
||||
CT = NULL;
|
||||
}
|
||||
|
||||
entry = cfg_table;
|
||||
while (entry && entry->name[0] != 0) {
|
||||
size_t entry_sz;
|
||||
entry = (struct mok_variable_config_entry *)((uintptr_t)cfg_table + pos);
|
||||
|
||||
if (entry->name[0] != 0) {
|
||||
entry_sz = sizeof(*entry);
|
||||
entry_sz += entry->data_size;
|
||||
pos += entry_sz;
|
||||
}
|
||||
}
|
||||
|
||||
new_table_sz = pos +
|
||||
(errlog_sz ? sizeof(*entry) + errlog_sz : 0) +
|
||||
(dbglog_sz ? sizeof(*entry) + dbglog_sz : 0) +
|
||||
sizeof(*entry);
|
||||
new_table = NULL;
|
||||
new_table_pages = ALIGN_UP(new_table_sz + 4*EFI_PAGE_SIZE, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
|
||||
efi_status = BS->AllocatePages(AllocateAnyPages, EfiRuntimeServicesData, new_table_pages, &physaddr);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
perror(L"Couldn't allocate %llu pages\n", new_table_pages);
|
||||
return;
|
||||
}
|
||||
new_table = (void *)(uintptr_t)physaddr;
|
||||
if (!new_table)
|
||||
return;
|
||||
ZeroMem(new_table, new_table_pages * EFI_PAGE_SIZE);
|
||||
CopyMem(new_table, cfg_table, pos);
|
||||
|
||||
entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos);
|
||||
if (errlog_sz) {
|
||||
strcpy(entry->name, "shim-err.txt");
|
||||
entry->data_size = errlog_sz;
|
||||
format_error_log(&entry->data[0], errlog_sz);
|
||||
|
||||
pos += sizeof(*entry) + errlog_sz;
|
||||
entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos);
|
||||
}
|
||||
if (dbglog_sz) {
|
||||
strcpy(entry->name, "shim-dbg.txt");
|
||||
entry->data_size = dbglog_sz;
|
||||
format_debug_log(&entry->data[0], dbglog_sz);
|
||||
|
||||
pos += sizeof(*entry) + dbglog_sz;
|
||||
|
||||
entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos);
|
||||
}
|
||||
|
||||
replace_config_table((EFI_CONFIGURATION_TABLE *)cfg_table, physaddr, new_table_pages);
|
||||
}
|
||||
|
||||
// vim:fenc=utf-8:tw=75
|
||||
|
219
fallback.c
219
fallback.c
@ -94,89 +94,6 @@ FindSubDevicePath(EFI_DEVICE_PATH *In, UINT8 Type, UINT8 SubType,
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
get_file_size(EFI_FILE_HANDLE fh, UINTN *retsize)
|
||||
{
|
||||
EFI_STATUS efi_status;
|
||||
void *buffer = NULL;
|
||||
UINTN bs = 0;
|
||||
|
||||
/* The API here is "Call it once with bs=0, it fills in bs,
|
||||
* then allocate a buffer and ask again to get it filled. */
|
||||
efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, NULL);
|
||||
if (EFI_ERROR(efi_status) && efi_status != EFI_BUFFER_TOO_SMALL)
|
||||
return efi_status;
|
||||
if (bs == 0)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
buffer = AllocateZeroPool(bs);
|
||||
if (!buffer) {
|
||||
console_print(L"Could not allocate memory\n");
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, buffer);
|
||||
/* This checks *either* the error from the first GetInfo, if it isn't
|
||||
* the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo
|
||||
* call in *any* case. */
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
console_print(L"Could not get file info: %r\n", efi_status);
|
||||
if (buffer)
|
||||
FreePool(buffer);
|
||||
return efi_status;
|
||||
}
|
||||
EFI_FILE_INFO *fi = buffer;
|
||||
*retsize = fi->FileSize;
|
||||
FreePool(buffer);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs)
|
||||
{
|
||||
EFI_FILE_HANDLE fh2;
|
||||
EFI_STATUS efi_status;
|
||||
|
||||
efi_status = fh->Open(fh, &fh2, fullpath, EFI_FILE_READ_ONLY, 0);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
console_print(L"Couldn't open \"%s\": %r\n", fullpath, efi_status);
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
UINTN len = 0;
|
||||
CHAR16 *b = NULL;
|
||||
efi_status = get_file_size(fh2, &len);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
console_print(L"Could not get file size for \"%s\": %r\n",
|
||||
fullpath, efi_status);
|
||||
fh2->Close(fh2);
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
if (len > 1024 * PAGE_SIZE) {
|
||||
fh2->Close(fh2);
|
||||
return EFI_BAD_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
b = AllocateZeroPool(len + 2);
|
||||
if (!b) {
|
||||
console_print(L"Could not allocate memory\n");
|
||||
fh2->Close(fh2);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
efi_status = fh->Read(fh, &len, b);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
FreePool(b);
|
||||
fh2->Close(fh2);
|
||||
console_print(L"Could not read file: %r\n", efi_status);
|
||||
return efi_status;
|
||||
}
|
||||
*buffer = b;
|
||||
*bs = len;
|
||||
fh2->Close(fh2);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
make_full_path(CHAR16 *dirname, CHAR16 *filename, CHAR16 **out, UINT64 *outlen)
|
||||
{
|
||||
@ -202,8 +119,8 @@ make_full_path(CHAR16 *dirname, CHAR16 *filename, CHAR16 **out, UINT64 *outlen)
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
CHAR16 *bootorder = NULL;
|
||||
int nbootorder = 0;
|
||||
UINT16 *bootorder = NULL;
|
||||
UINTN nbootorder = 0;
|
||||
|
||||
EFI_DEVICE_PATH *first_new_option = NULL;
|
||||
VOID *first_new_option_args = NULL;
|
||||
@ -211,7 +128,8 @@ UINTN first_new_option_size = 0;
|
||||
|
||||
EFI_STATUS
|
||||
add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp,
|
||||
CHAR16 *filename, CHAR16 *label, CHAR16 *arguments)
|
||||
CHAR16 *filename, CHAR16 *label, CHAR16 *arguments,
|
||||
UINT16 **newbootentries, UINTN *nnewbootentries)
|
||||
{
|
||||
static int i = 0;
|
||||
CHAR16 varname[] = L"Boot0000";
|
||||
@ -226,9 +144,11 @@ add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp,
|
||||
|
||||
void *var = LibGetVariable(varname, &GV_GUID);
|
||||
if (!var) {
|
||||
int arg_size = StrLen(arguments) ? StrLen(arguments) * sizeof (CHAR16) +
|
||||
sizeof (CHAR16) : 0;
|
||||
int size = sizeof(UINT32) + sizeof (UINT16) +
|
||||
StrLen(label)*2 + 2 + DevicePathSize(hddp) +
|
||||
StrLen(arguments) * 2;
|
||||
arg_size;
|
||||
|
||||
CHAR8 *data, *cursor;
|
||||
cursor = data = AllocateZeroPool(size + 2);
|
||||
@ -252,7 +172,7 @@ add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp,
|
||||
if (!first_new_option) {
|
||||
first_new_option = DuplicateDevicePath(fulldp);
|
||||
first_new_option_args = StrDuplicate(arguments);
|
||||
first_new_option_size = StrLen(arguments) * sizeof (CHAR16);
|
||||
first_new_option_size = arg_size;
|
||||
}
|
||||
|
||||
efi_status = RT->SetVariable(varname, &GV_GUID,
|
||||
@ -269,24 +189,21 @@ add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp,
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
CHAR16 *newbootorder = AllocateZeroPool(sizeof (CHAR16)
|
||||
* (nbootorder + 1));
|
||||
UINT16 *newbootorder = AllocateZeroPool(sizeof (UINT16) * (*nnewbootentries + 1));
|
||||
if (!newbootorder)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
int j = 0;
|
||||
newbootorder[0] = i & 0xffff;
|
||||
if (nbootorder) {
|
||||
for (j = 0; j < nbootorder; j++)
|
||||
newbootorder[j+1] = bootorder[j];
|
||||
FreePool(bootorder);
|
||||
}
|
||||
bootorder = newbootorder;
|
||||
nbootorder += 1;
|
||||
VerbosePrint(L"nbootorder: %d\nBootOrder: ",
|
||||
nbootorder);
|
||||
for (j = 0 ; j < nbootorder ; j++)
|
||||
VerbosePrintUnprefixed(L"%04x ", bootorder[j]);
|
||||
UINTN j = 0;
|
||||
CopyMem(newbootorder, *newbootentries, sizeof (UINT16) * (*nnewbootentries));
|
||||
newbootorder[*nnewbootentries] = i & 0xffff;
|
||||
if (*newbootentries)
|
||||
FreePool(*newbootentries);
|
||||
*newbootentries = newbootorder;
|
||||
*nnewbootentries += 1;
|
||||
VerbosePrint(L"nnewbootentries: %d\nnewbootentries: ",
|
||||
*nnewbootentries);
|
||||
for (j = 0 ; j < *nnewbootentries ; j++)
|
||||
VerbosePrintUnprefixed(L"%04x ", (*newbootentries)[j]);
|
||||
VerbosePrintUnprefixed(L"\n");
|
||||
|
||||
return EFI_SUCCESS;
|
||||
@ -400,9 +317,11 @@ find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp,
|
||||
UINT16 *optnum)
|
||||
{
|
||||
unsigned int label_size = StrLen(label)*2 + 2;
|
||||
int arg_size = StrLen(arguments) ? StrLen(arguments) * sizeof (CHAR16) +
|
||||
sizeof (CHAR16) : 0;
|
||||
unsigned int size = sizeof(UINT32) + sizeof (UINT16) +
|
||||
label_size + DevicePathSize(dp) +
|
||||
StrLen(arguments) * 2;
|
||||
arg_size;
|
||||
|
||||
CHAR8 *data = AllocateZeroPool(size + 2);
|
||||
if (!data)
|
||||
@ -486,7 +405,7 @@ find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp,
|
||||
if (!first_new_option) {
|
||||
first_new_option = DuplicateDevicePath(fulldp);
|
||||
first_new_option_args = StrDuplicate(arguments);
|
||||
first_new_option_size = StrLen(arguments) * sizeof (CHAR16);
|
||||
first_new_option_size = arg_size;
|
||||
}
|
||||
|
||||
*optnum = xtoi(varname + 4);
|
||||
@ -503,13 +422,13 @@ find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp,
|
||||
EFI_STATUS
|
||||
set_boot_order(void)
|
||||
{
|
||||
CHAR16 *oldbootorder;
|
||||
UINT16 *oldbootorder;
|
||||
UINTN size;
|
||||
|
||||
oldbootorder = LibGetVariableAndSize(L"BootOrder", &GV_GUID, &size);
|
||||
if (oldbootorder) {
|
||||
int i;
|
||||
nbootorder = size / sizeof (CHAR16);
|
||||
UINTN i;
|
||||
nbootorder = size / sizeof (UINT16);
|
||||
bootorder = oldbootorder;
|
||||
|
||||
VerbosePrint(L"Original nbootorder: %d\nOriginal BootOrder: ",
|
||||
@ -523,23 +442,41 @@ set_boot_order(void)
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
update_boot_order(void)
|
||||
update_boot_order(UINT16 *newbootentries, UINTN nnewbootentries)
|
||||
{
|
||||
UINTN size;
|
||||
UINTN len = 0;
|
||||
CHAR16 *newbootorder = NULL;
|
||||
UINT16 *newbootorder = NULL;
|
||||
EFI_STATUS efi_status;
|
||||
UINTN i;
|
||||
|
||||
size = nbootorder * sizeof(CHAR16);
|
||||
VerbosePrint(L"old boot order: ");
|
||||
for (i = 0; i < nbootorder; i++)
|
||||
VerbosePrintUnprefixed(L"%04x ", bootorder[i]);
|
||||
VerbosePrintUnprefixed(L"\n");
|
||||
VerbosePrint(L"new boot entries: ");
|
||||
for (i = 0; i < nnewbootentries; i++)
|
||||
VerbosePrintUnprefixed(L"%04x ", newbootentries[i]);
|
||||
VerbosePrintUnprefixed(L"\n");
|
||||
|
||||
size = nbootorder * sizeof(UINT16) + nnewbootentries * sizeof(UINT16);
|
||||
newbootorder = AllocateZeroPool(size);
|
||||
if (!newbootorder)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
CopyMem(newbootorder, bootorder, size);
|
||||
for (i = 0 ; i < nnewbootentries; i++) {
|
||||
newbootorder[i] = newbootentries[i];
|
||||
}
|
||||
CopyMem(&newbootorder[i], bootorder, nbootorder * sizeof(UINT16));
|
||||
|
||||
VerbosePrint(L"nbootorder: %d\nBootOrder: ", size / sizeof (CHAR16));
|
||||
UINTN j;
|
||||
for (j = 0 ; j < size / sizeof (CHAR16); j++)
|
||||
VerbosePrintUnprefixed(L"%04x ", newbootorder[j]);
|
||||
if (bootorder)
|
||||
FreePool(bootorder);
|
||||
nbootorder = nnewbootentries + nbootorder;
|
||||
bootorder = newbootorder;
|
||||
|
||||
VerbosePrint(L"updated nbootorder: %d\n", nbootorder);
|
||||
VerbosePrint(L"updated bootoder: ");
|
||||
for (i = 0; i < nbootorder; i++)
|
||||
VerbosePrintUnprefixed(L"%04x ", bootorder[i]);
|
||||
VerbosePrintUnprefixed(L"\n");
|
||||
efi_status = RT->GetVariable(L"BootOrder", &GV_GUID, NULL, &len, NULL);
|
||||
if (efi_status == EFI_BUFFER_TOO_SMALL)
|
||||
@ -549,13 +486,13 @@ update_boot_order(void)
|
||||
EFI_VARIABLE_NON_VOLATILE |
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS,
|
||||
size, newbootorder);
|
||||
FreePool(newbootorder);
|
||||
size, bootorder);
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments)
|
||||
add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments,
|
||||
UINT16 **newbootentries, UINTN *nnewbootentries)
|
||||
{
|
||||
CHAR16 *fullpath = NULL;
|
||||
UINT64 pathlen = 0;
|
||||
@ -614,19 +551,19 @@ add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *argum
|
||||
arguments, &option);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
add_boot_option(dp, full_device_path, fullpath, label,
|
||||
arguments);
|
||||
arguments, newbootentries, nnewbootentries);
|
||||
goto done;
|
||||
}
|
||||
|
||||
UINT16 bootnum;
|
||||
CHAR16 *newbootorder;
|
||||
UINT16 *newbootorder;
|
||||
/* Search for the option in the current bootorder */
|
||||
for (bootnum = 0; bootnum < nbootorder; bootnum++)
|
||||
if (bootorder[bootnum] == option)
|
||||
break;
|
||||
if (bootnum == nbootorder) {
|
||||
/* Option not found, prepend option and copy the rest */
|
||||
newbootorder = AllocateZeroPool(sizeof(CHAR16)
|
||||
newbootorder = AllocateZeroPool(sizeof(UINT16)
|
||||
* (nbootorder + 1));
|
||||
if (!newbootorder) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
@ -634,30 +571,30 @@ add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *argum
|
||||
}
|
||||
newbootorder[0] = option;
|
||||
CopyMem(newbootorder + 1, bootorder,
|
||||
sizeof(CHAR16) * nbootorder);
|
||||
sizeof(UINT16) * nbootorder);
|
||||
FreePool(bootorder);
|
||||
bootorder = newbootorder;
|
||||
nbootorder += 1;
|
||||
} else {
|
||||
/* Option found, put first and slice the rest */
|
||||
newbootorder = AllocateZeroPool(
|
||||
sizeof(CHAR16) * nbootorder);
|
||||
sizeof(UINT16) * nbootorder);
|
||||
if (!newbootorder) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto done;
|
||||
}
|
||||
newbootorder[0] = option;
|
||||
CopyMem(newbootorder + 1, bootorder,
|
||||
sizeof(CHAR16) * bootnum);
|
||||
sizeof(UINT16) * bootnum);
|
||||
CopyMem(newbootorder + 1 + bootnum,
|
||||
bootorder + bootnum + 1,
|
||||
sizeof(CHAR16) * (nbootorder - bootnum - 1));
|
||||
sizeof(UINT16) * (nbootorder - bootnum - 1));
|
||||
FreePool(bootorder);
|
||||
bootorder = newbootorder;
|
||||
}
|
||||
VerbosePrint(L"New nbootorder: %d\nBootOrder: ",
|
||||
nbootorder);
|
||||
for (int i = 0 ; i < nbootorder ; i++)
|
||||
for (UINTN i = 0 ; i < nbootorder ; i++)
|
||||
VerbosePrintUnprefixed(L"%04x ", bootorder[i]);
|
||||
VerbosePrintUnprefixed(L"\n");
|
||||
|
||||
@ -672,7 +609,8 @@ done:
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
populate_stanza(CHAR16 *dirname, CHAR16 *filename UNUSED, CHAR16 *csv)
|
||||
populate_stanza(CHAR16 *dirname, CHAR16 *filename UNUSED, CHAR16 *csv,
|
||||
UINT16 **newbootentries, UINTN *nnewbootentries)
|
||||
{
|
||||
CHAR16 *file = csv;
|
||||
VerbosePrint(L"CSV data: \"%s\"\n", csv);
|
||||
@ -696,13 +634,14 @@ populate_stanza(CHAR16 *dirname, CHAR16 *filename UNUSED, CHAR16 *csv)
|
||||
/* This one is optional, so don't check if comma2 is 0 */
|
||||
VerbosePrint(L"arguments: \"%s\"\n", arguments);
|
||||
|
||||
add_to_boot_list(dirname, file, label, arguments);
|
||||
add_to_boot_list(dirname, file, label, arguments, newbootentries, nnewbootentries);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename)
|
||||
try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename,
|
||||
UINT16 **newbootentries, UINTN *nnewbootentries)
|
||||
{
|
||||
CHAR16 *fullpath = NULL;
|
||||
UINT64 pathlen = 0;
|
||||
@ -751,7 +690,7 @@ try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename)
|
||||
CHAR16 c = start[l];
|
||||
start[l] = L'\0';
|
||||
|
||||
populate_stanza(dirname, filename, start);
|
||||
populate_stanza(dirname, filename, start, newbootentries, nnewbootentries);
|
||||
|
||||
start[l] = c;
|
||||
start += l;
|
||||
@ -762,7 +701,8 @@ try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename)
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname)
|
||||
find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname,
|
||||
UINT16 **newbootentries, UINTN *nnewbootentries)
|
||||
{
|
||||
EFI_STATUS efi_status;
|
||||
void *buffer = NULL;
|
||||
@ -861,7 +801,8 @@ find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname)
|
||||
console_print(L"Couldn't open \\EFI\\%s\\%s: %r\n",
|
||||
dirname, bootarchcsv, efi_status);
|
||||
} else {
|
||||
efi_status = try_boot_csv(fh2, dirname, bootarchcsv);
|
||||
efi_status = try_boot_csv(fh2, dirname, bootarchcsv,
|
||||
newbootentries, nnewbootentries);
|
||||
fh2->Close(fh2);
|
||||
if (EFI_ERROR(efi_status))
|
||||
console_print(L"Could not process \\EFI\\%s\\%s: %r\n",
|
||||
@ -876,7 +817,8 @@ find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname)
|
||||
console_print(L"Couldn't open \\EFI\\%s\\%s: %r\n",
|
||||
dirname, bootcsv, efi_status);
|
||||
} else {
|
||||
efi_status = try_boot_csv(fh2, dirname, bootcsv);
|
||||
efi_status = try_boot_csv(fh2, dirname, bootcsv,
|
||||
newbootentries, nnewbootentries);
|
||||
fh2->Close(fh2);
|
||||
if (EFI_ERROR(efi_status))
|
||||
console_print(L"Could not process \\EFI\\%s\\%s: %r\n",
|
||||
@ -891,6 +833,8 @@ find_boot_options(EFI_HANDLE device)
|
||||
{
|
||||
EFI_STATUS efi_status;
|
||||
EFI_FILE_IO_INTERFACE *fio = NULL;
|
||||
UINT16 *newbootentries = NULL;
|
||||
UINTN nnewbootentries = 0;
|
||||
|
||||
efi_status = BS->HandleProtocol(device, &FileSystemProtocol,
|
||||
(void **) &fio);
|
||||
@ -982,7 +926,8 @@ find_boot_options(EFI_HANDLE device)
|
||||
continue;
|
||||
}
|
||||
|
||||
efi_status = find_boot_csv(fh3, fi->FileName);
|
||||
efi_status = find_boot_csv(fh3, fi->FileName,
|
||||
&newbootentries, &nnewbootentries);
|
||||
fh3->Close(fh3);
|
||||
FreePool(buffer);
|
||||
buffer = NULL;
|
||||
@ -991,8 +936,8 @@ find_boot_options(EFI_HANDLE device)
|
||||
|
||||
} while (1);
|
||||
|
||||
if (!EFI_ERROR(efi_status) && nbootorder > 0)
|
||||
efi_status = update_boot_order();
|
||||
if (!EFI_ERROR(efi_status) && (nbootorder > 0 || nnewbootentries > 0))
|
||||
efi_status = update_boot_order(newbootentries, nnewbootentries);
|
||||
|
||||
fh2->Close(fh2);
|
||||
fh->Close(fh);
|
||||
|
@ -28,7 +28,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
memcpy(data_copy, data, size);
|
||||
data_copy[size] = 0;
|
||||
|
||||
status = read_header(data_copy, size, &context);
|
||||
status = read_header(data_copy, size, &context, true);
|
||||
|
||||
free(data_copy);
|
||||
|
||||
|
173
generate_sbat_var_defs.c
Normal file
173
generate_sbat_var_defs.c
Normal file
@ -0,0 +1,173 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
/*
|
||||
* This generates the header files that produce the actual revocation
|
||||
* string payload. On the one hand this grabs the defintions from the
|
||||
* human readable SbatLevel_Variable.txt file which is nice. On the other
|
||||
* hand it's one off c code.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct sbat_revocation sbat_revocation;
|
||||
|
||||
struct sbat_revocation {
|
||||
int date;
|
||||
char *revocations;
|
||||
sbat_revocation *next;
|
||||
};
|
||||
|
||||
static sbat_revocation *revlisthead;
|
||||
|
||||
int
|
||||
readfile(char *SbatLevel_Variable)
|
||||
{
|
||||
FILE *varfilep;
|
||||
char line[1024];
|
||||
int date;
|
||||
int ret = -1;
|
||||
|
||||
unsigned int revocationsp = 0;
|
||||
|
||||
sbat_revocation *revlistlast = NULL;
|
||||
sbat_revocation *revlistentry = NULL;
|
||||
|
||||
revlisthead = NULL;
|
||||
|
||||
varfilep = fopen(SbatLevel_Variable, "r");
|
||||
if (varfilep == NULL)
|
||||
return -1;
|
||||
|
||||
while (fgets(line, sizeof(line), varfilep) != NULL) {
|
||||
if (sscanf(line, "sbat,1,%d\n", &date) && strlen(line) == 18) {
|
||||
revlistentry = calloc(1, sizeof(sbat_revocation));
|
||||
if (revlistentry == NULL)
|
||||
goto err;
|
||||
if (revlisthead == NULL)
|
||||
revlisthead = revlistentry;
|
||||
else
|
||||
revlistlast->next = revlistentry;
|
||||
|
||||
revlistlast = revlistentry;
|
||||
|
||||
revlistentry->date = date;
|
||||
while (line[0] != '\n' &&
|
||||
fgets(line, sizeof(line), varfilep) != NULL) {
|
||||
char *new = NULL;
|
||||
new = realloc(revlistentry->revocations,
|
||||
revocationsp + strlen(line) + 1);
|
||||
if (new == NULL) {
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
revlistentry->revocations = new;
|
||||
if (strlen(line) > 1) {
|
||||
line[strlen(line) - 1] = 0;
|
||||
sprintf(revlistentry->revocations +
|
||||
revocationsp,
|
||||
"%s\\n", line);
|
||||
revocationsp =
|
||||
revocationsp + strlen(line) + 2;
|
||||
}
|
||||
}
|
||||
revocationsp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
err:
|
||||
if (ret < 0 && revlisthead) {
|
||||
sbat_revocation *rle = revlisthead;
|
||||
while (rle) {
|
||||
sbat_revocation *next = rle->next;
|
||||
if (rle->revocations)
|
||||
free(rle->revocations);
|
||||
free(rle);
|
||||
rle = next;
|
||||
}
|
||||
revlisthead = NULL;
|
||||
}
|
||||
fclose(varfilep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
writefile()
|
||||
{
|
||||
int epochfound = 0;
|
||||
int epochdate = 2021030218;
|
||||
int latestdate = 0;
|
||||
|
||||
sbat_revocation *revlistentry;
|
||||
sbat_revocation *latest_revlistentry = NULL;
|
||||
|
||||
revlistentry = revlisthead;
|
||||
|
||||
while (revlistentry != NULL) {
|
||||
if (revlistentry->date == epochdate) {
|
||||
printf("#ifndef GEN_SBAT_VAR_DEFS_H_\n"
|
||||
"#define GEN_SBAT_VAR_DEFS_H_\n"
|
||||
"#ifndef ENABLE_SHIM_DEVEL\n\n"
|
||||
"#ifndef SBAT_AUTOMATIC_DATE\n"
|
||||
"#define SBAT_AUTOMATIC_DATE 2024040900\n"
|
||||
"#endif /* SBAT_AUTOMATIC_DATE */\n"
|
||||
"#if SBAT_AUTOMATIC_DATE == %d\n"
|
||||
"#define SBAT_VAR_AUTOMATIC_REVOCATIONS\n",
|
||||
revlistentry->date);
|
||||
epochfound = 1;
|
||||
} else if (epochfound == 1) {
|
||||
printf("#elif SBAT_AUTOMATIC_DATE == %d\n"
|
||||
"#define SBAT_VAR_AUTOMATIC_REVOCATIONS \"%s\"\n",
|
||||
revlistentry->date,
|
||||
revlistentry->revocations);
|
||||
}
|
||||
if (revlistentry->date > latestdate) {
|
||||
latest_revlistentry = revlistentry;
|
||||
latestdate = revlistentry->date;
|
||||
}
|
||||
revlistentry = revlistentry->next;
|
||||
}
|
||||
|
||||
if (epochfound == 0 || !latest_revlistentry)
|
||||
return -1;
|
||||
|
||||
printf("#else\n"
|
||||
"#error \"Unknown SBAT_AUTOMATIC_DATE\"\n"
|
||||
"#endif /* SBAT_AUTOMATIC_DATE == */\n\n"
|
||||
"#define SBAT_VAR_AUTOMATIC_DATE QUOTEVAL(SBAT_AUTOMATIC_DATE)\n"
|
||||
"#define SBAT_VAR_AUTOMATIC \\\n"
|
||||
" SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE \"\\n\" \\\n"
|
||||
" SBAT_VAR_AUTOMATIC_REVOCATIONS\n\n");
|
||||
|
||||
printf("#define SBAT_VAR_LATEST_DATE \"%d\"\n"
|
||||
"#define SBAT_VAR_LATEST_REVOCATIONS \"%s\"\n",
|
||||
latest_revlistentry->date,
|
||||
latest_revlistentry->revocations);
|
||||
|
||||
printf("#define SBAT_VAR_LATEST \\\n"
|
||||
" SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE \"\\n\" \\\n"
|
||||
" SBAT_VAR_LATEST_REVOCATIONS\n\n"
|
||||
"#endif /* !ENABLE_SHIM_DEVEL */\n"
|
||||
"#endif /* !GEN_SBAT_VAR_DEFS_H_ */\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char SbatLevel_Variable[2048];
|
||||
|
||||
if (argc == 2)
|
||||
snprintf(SbatLevel_Variable, 2048, "%s/SbatLevel_Variable.txt", argv[1]);
|
||||
else
|
||||
snprintf(SbatLevel_Variable, 2048, "SbatLevel_Variable.txt");
|
||||
|
||||
if (readfile(SbatLevel_Variable))
|
||||
return writefile();
|
||||
else
|
||||
return -1;
|
||||
}
|
@ -24,13 +24,18 @@ UINT8 *build_cert;
|
||||
* indicator of how an image has been verified
|
||||
*/
|
||||
verification_method_t verification_method;
|
||||
int loader_is_participating;
|
||||
|
||||
SHIM_IMAGE_LOADER shim_image_loader_interface;
|
||||
|
||||
UINT8 user_insecure_mode;
|
||||
UINTN hsi_status = 0;
|
||||
UINT8 ignore_db;
|
||||
UINT8 trust_mok_list;
|
||||
UINT8 mok_policy = 0;
|
||||
|
||||
UINT32 verbose = 0;
|
||||
|
||||
EFI_PHYSICAL_ADDRESS mok_config_table = 0;
|
||||
UINTN mok_config_table_pages = 0;
|
||||
|
||||
// vim:fenc=utf-8:tw=75:noet
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -971,5 +971,75 @@ typedef struct _EFI_SYSTEM_TABLE {
|
||||
|
||||
} EFI_SYSTEM_TABLE;
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
// Not technically EFI, but oh well.
|
||||
//
|
||||
|
||||
#define EFI_DXE_SERVICES_TABLE_SIGNATURE 0x565245535f455844ULL
|
||||
|
||||
typedef enum {
|
||||
EFI_GCD_MEMORY_TYPE_NON_EXISTENT,
|
||||
EFI_GCD_MEMORY_TYPE_RESERVED,
|
||||
EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY,
|
||||
EFI_GCD_MEMORY_TYPE_MEMORY_MAPPED_IO,
|
||||
EFI_GCD_MEMORY_TYPE_PERSISTENT,
|
||||
EFI_GCD_MEMORY_TYPE_MORE_RELIABLE,
|
||||
EFI_GCD_MEMORY_TYPE_MAXIMUM
|
||||
} EFI_GCD_MEMORY_TYPE_T;
|
||||
|
||||
#define DXE_SERVICES_TABLE_GUID \
|
||||
{ \
|
||||
0x5ad34ba, 0x6f02, 0x4214, {0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9 } \
|
||||
}
|
||||
|
||||
struct _EFI_GCD_MEMORY_SPACE_DESCRIPTOR {
|
||||
EFI_PHYSICAL_ADDRESS BaseAddress;
|
||||
UINT64 Length;
|
||||
UINT64 Capabilities;
|
||||
UINT64 Attributes;
|
||||
EFI_GCD_MEMORY_TYPE_T GcdMemoryType;
|
||||
EFI_HANDLE ImageHandle;
|
||||
EFI_HANDLE DeviceHandle;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef struct _EFI_GCD_MEMORY_SPACE_DESCRIPTOR EFI_GCD_MEMORY_SPACE_DESCRIPTOR;
|
||||
|
||||
typedef
|
||||
EFI_STATUS
|
||||
(EFIAPI *GET_MEMORY_SPACE_DESCRIPTOR) (
|
||||
IN EFI_PHYSICAL_ADDRESS BaseAddress,
|
||||
OUT EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc
|
||||
);
|
||||
|
||||
typedef
|
||||
EFI_STATUS
|
||||
(EFIAPI *SET_MEMORY_SPACE_ATTRIBUTES) (
|
||||
IN EFI_PHYSICAL_ADDRESS BaseAddress,
|
||||
IN UINT64 Length,
|
||||
IN UINT64 Attributes
|
||||
);
|
||||
|
||||
typedef struct _EFI_DXE_SERVICES_TABLE {
|
||||
EFI_TABLE_HEADER Hdr;
|
||||
VOID *AddMemorySpace;
|
||||
VOID *AllocateMemorySpace;
|
||||
VOID *FreeMemorySpace;
|
||||
VOID *RemoveMemorySpace;
|
||||
GET_MEMORY_SPACE_DESCRIPTOR GetMemorySpaceDescriptor;
|
||||
SET_MEMORY_SPACE_ATTRIBUTES SetMemorySpaceAttributes;
|
||||
VOID *GetMemorySpaceMap;
|
||||
VOID *AddIoSpace;
|
||||
VOID *AllocateIoSpace;
|
||||
VOID *FreeIoSpace;
|
||||
VOID *RemoveIoSpace;
|
||||
VOID *GetIoSpaceDescriptor;
|
||||
VOID *GetIoSpaceMap;
|
||||
VOID *Dispatch;
|
||||
VOID *Schedule;
|
||||
VOID *Trust;
|
||||
VOID *ProcessFirmwareVolume;
|
||||
VOID *SetMemorySpaceCapabilities;
|
||||
} EFI_DXE_SERVICES_TABLE;
|
||||
|
||||
#endif
|
||||
|
@ -57,12 +57,17 @@ Revision History
|
||||
#define EFI_END_OF_FILE EFIERR(31)
|
||||
#define EFI_INVALID_LANGUAGE EFIERR(32)
|
||||
#define EFI_COMPROMISED_DATA EFIERR(33)
|
||||
#define EFI_IP_ADDRESS_CONFLICT EFIERR(34)
|
||||
#define EFI_HTTP_ERROR EFIERR(35)
|
||||
|
||||
#define EFI_WARN_UNKOWN_GLYPH EFIWARN(1)
|
||||
#define EFI_WARN_UNKNOWN_GLYPH EFIWARN(1)
|
||||
#define EFI_WARN_DELETE_FAILURE EFIWARN(2)
|
||||
#define EFI_WARN_WRITE_FAILURE EFIWARN(3)
|
||||
#define EFI_WARN_BUFFER_TOO_SMALL EFIWARN(4)
|
||||
#define EFI_WARN_STALE_DATA EFIWARN(5)
|
||||
#define EFI_WARN_FILE_SYSTEM EFIWARN(6)
|
||||
#define EFI_WARN_RESET_REQUIRED EFIWARN(7)
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -79,10 +79,16 @@ extern EFI_GUID gEfiDiskIoProtocolGuid;
|
||||
#define DiskIoProtocol gEfiDiskIoProtocolGuid
|
||||
extern EFI_GUID gEfiDiskIo2ProtocolGuid;
|
||||
#define DiskIo2Protocol gEfiDiskIo2ProtocolGuid
|
||||
extern EFI_GUID gEfiDxeServicesTableGuid;
|
||||
#define DxeServicesTable gEfiDxeServicesTableGuid
|
||||
extern EFI_GUID gEfiSimpleFileSystemProtocolGuid;
|
||||
#define FileSystemProtocol gEfiSimpleFileSystemProtocolGuid
|
||||
extern EFI_GUID gEfiLoadedImageDevicePathProtocolGuid;
|
||||
#define LoadedImageDevicePathProtocol gEfiLoadedImageDevicePathProtocolGuid
|
||||
extern EFI_GUID gEfiLoadFileProtocolGuid;
|
||||
#define LoadFileProtocol gEfiLoadFileProtocolGuid
|
||||
extern EFI_GUID gEfiLoadFile2ProtocolGuid;
|
||||
#define LoadFile2Protocol gEfiLoadFile2ProtocolGuid
|
||||
extern EFI_GUID gEfiDeviceIoProtocolGuid;
|
||||
#define DeviceIoProtocol gEfiDeviceIoProtocolGuid
|
||||
extern EFI_GUID VariableStoreProtocol;
|
||||
|
@ -554,6 +554,69 @@ typedef struct _EFI_LOAD_FILE_PROTOCOL {
|
||||
typedef struct _EFI_LOAD_FILE_PROTOCOL _EFI_LOAD_FILE_INTERFACE;
|
||||
typedef EFI_LOAD_FILE_PROTOCOL EFI_LOAD_FILE_INTERFACE;
|
||||
|
||||
|
||||
//
|
||||
// Load File 2 Protocol
|
||||
//
|
||||
|
||||
#define EFI_LOAD_FILE2_PROTOCOL_GUID \
|
||||
{ \
|
||||
0x4006c0c1, 0xfcb3, 0x403e, {0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d } \
|
||||
}
|
||||
|
||||
///
|
||||
/// Protocol Guid defined by UEFI2.1.
|
||||
///
|
||||
#define LOAD_FILE2_PROTOCOL EFI_LOAD_FILE2_PROTOCOL_GUID
|
||||
|
||||
typedef struct _EFI_LOAD_FILE2_PROTOCOL EFI_LOAD_FILE2_PROTOCOL;
|
||||
|
||||
/**
|
||||
Causes the driver to load a specified file.
|
||||
|
||||
@param This Protocol instance pointer.
|
||||
@param FilePath The device specific path of the file to load.
|
||||
@param BootPolicy Should always be FALSE.
|
||||
@param BufferSize On input the size of Buffer in bytes. On output with a return
|
||||
code of EFI_SUCCESS, the amount of data transferred to
|
||||
Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
|
||||
the size of Buffer required to retrieve the requested file.
|
||||
@param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
|
||||
then no the size of the requested file is returned in
|
||||
BufferSize.
|
||||
|
||||
@retval EFI_SUCCESS The file was loaded.
|
||||
@retval EFI_UNSUPPORTED BootPolicy is TRUE.
|
||||
@retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
|
||||
BufferSize is NULL.
|
||||
@retval EFI_NO_MEDIA No medium was present to load the file.
|
||||
@retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
|
||||
@retval EFI_NO_RESPONSE The remote system did not respond.
|
||||
@retval EFI_NOT_FOUND The file was not found
|
||||
@retval EFI_ABORTED The file load process was manually canceled.
|
||||
@retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current
|
||||
directory entry. BufferSize has been updated with
|
||||
the size needed to complete the request.
|
||||
|
||||
|
||||
**/
|
||||
typedef
|
||||
EFI_STATUS
|
||||
(EFIAPI *EFI_LOAD_FILE2)(
|
||||
IN EFI_LOAD_FILE2_PROTOCOL *This,
|
||||
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
|
||||
IN BOOLEAN BootPolicy,
|
||||
IN OUT UINTN *BufferSize,
|
||||
IN VOID *Buffer OPTIONAL
|
||||
);
|
||||
|
||||
///
|
||||
/// The EFI_LOAD_FILE_PROTOCOL is a simple protocol used to obtain files from arbitrary devices.
|
||||
///
|
||||
struct _EFI_LOAD_FILE2_PROTOCOL {
|
||||
EFI_LOAD_FILE2 LoadFile;
|
||||
};
|
||||
|
||||
//
|
||||
// Device IO protocol
|
||||
//
|
||||
|
@ -102,8 +102,11 @@ EFI_GUID gEfiBlockIoProtocolGuid = EFI_BLOCK_IO_PROTOCOL_GUID
|
||||
EFI_GUID gEfiBlockIo2ProtocolGuid = EFI_BLOCK_IO2_PROTOCOL_GUID;
|
||||
EFI_GUID gEfiDiskIoProtocolGuid = EFI_DISK_IO_PROTOCOL_GUID;
|
||||
EFI_GUID gEfiDiskIo2ProtocolGuid = EFI_DISK_IO2_PROTOCOL_GUID;
|
||||
EFI_GUID gEfiDxeServicesTableGuid = DXE_SERVICES_TABLE_GUID;
|
||||
EFI_GUID gEfiSimpleFileSystemProtocolGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
|
||||
EFI_GUID gEfiLoadedImageDevicePathProtocolGuid = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
|
||||
EFI_GUID gEfiLoadFileProtocolGuid = EFI_LOAD_FILE_PROTOCOL_GUID;
|
||||
EFI_GUID gEfiLoadFile2ProtocolGuid = EFI_LOAD_FILE2_PROTOCOL_GUID;
|
||||
EFI_GUID gEfiDeviceIoProtocolGuid = EFI_DEVICE_IO_PROTOCOL_GUID;
|
||||
EFI_GUID gEfiUnicodeCollationProtocolGuid = EFI_UNICODE_COLLATION_PROTOCOL_GUID;
|
||||
EFI_GUID gEfiSerialIoProtocolGuid = EFI_SERIAL_IO_PROTOCOL_GUID;
|
||||
|
@ -54,12 +54,17 @@ struct {
|
||||
{ EFI_END_OF_FILE, L"End of File"},
|
||||
{ EFI_INVALID_LANGUAGE, L"Invalid Languages"},
|
||||
{ EFI_COMPROMISED_DATA, L"Compromised Data"},
|
||||
{ EFI_IP_ADDRESS_CONFLICT, L"IP Address Conflict"},
|
||||
{ EFI_HTTP_ERROR, L"HTTP Error"},
|
||||
|
||||
// warnings
|
||||
{ EFI_WARN_UNKNOWN_GLYPH, L"Warning Unknown Glyph"},
|
||||
{ EFI_WARN_DELETE_FAILURE, L"Warning Delete Failure"},
|
||||
{ EFI_WARN_WRITE_FAILURE, L"Warning Write Failure"},
|
||||
{ EFI_WARN_BUFFER_TOO_SMALL, L"Warning Buffer Too Small"},
|
||||
{ EFI_WARN_STALE_DATA, L"Warning Stale Data"},
|
||||
{ EFI_WARN_FILE_SYSTEM, L"Warning File System"},
|
||||
{ EFI_WARN_RESET_REQUIRED, L"Warning Reset Required"},
|
||||
{ 0, NULL}
|
||||
} ;
|
||||
|
||||
|
58
httpboot.c
58
httpboot.c
@ -55,6 +55,51 @@ convert_http_status_code (EFI_HTTP_STATUS_CODE status_code)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert an HTTP status code to an EFI status code. */
|
||||
static EFI_STATUS
|
||||
efi_status_from_http_status(EFI_HTTP_STATUS_CODE status_code)
|
||||
{
|
||||
switch (status_code) {
|
||||
case HTTP_STATUS_400_BAD_REQUEST:
|
||||
case HTTP_STATUS_411_LENGTH_REQUIRED:
|
||||
case HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE:
|
||||
case HTTP_STATUS_414_REQUEST_URI_TOO_LARGE:
|
||||
case HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE:
|
||||
case HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED:
|
||||
case HTTP_STATUS_417_EXPECTATION_FAILED:
|
||||
return EFI_INVALID_PARAMETER;
|
||||
case HTTP_STATUS_401_UNAUTHORIZED:
|
||||
case HTTP_STATUS_402_PAYMENT_REQUIRED:
|
||||
case HTTP_STATUS_403_FORBIDDEN:
|
||||
case HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED:
|
||||
return EFI_ACCESS_DENIED;
|
||||
case HTTP_STATUS_404_NOT_FOUND:
|
||||
case HTTP_STATUS_410_GONE:
|
||||
return EFI_NOT_FOUND;
|
||||
case HTTP_STATUS_405_METHOD_NOT_ALLOWED:
|
||||
case HTTP_STATUS_501_NOT_IMPLEMENTED:
|
||||
return EFI_UNSUPPORTED;
|
||||
case HTTP_STATUS_406_NOT_ACCEPTABLE:
|
||||
return EFI_NO_MEDIA;
|
||||
case HTTP_STATUS_408_REQUEST_TIME_OUT:
|
||||
case HTTP_STATUS_504_GATEWAY_TIME_OUT:
|
||||
return EFI_TIMEOUT;
|
||||
case HTTP_STATUS_409_CONFLICT:
|
||||
case HTTP_STATUS_412_PRECONDITION_FAILED:
|
||||
return EFI_MEDIA_CHANGED;
|
||||
case HTTP_STATUS_500_INTERNAL_SERVER_ERROR:
|
||||
case HTTP_STATUS_502_BAD_GATEWAY:
|
||||
return EFI_DEVICE_ERROR;
|
||||
case HTTP_STATUS_503_SERVICE_UNAVAILABLE:
|
||||
return EFI_NOT_READY;
|
||||
case HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED:
|
||||
return EFI_INCOMPATIBLE_VERSION;
|
||||
default:
|
||||
/* Use a generic HTTP error for anything else. */
|
||||
return EFI_HTTP_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static EFI_DEVICE_PATH *devpath;
|
||||
static EFI_MAC_ADDRESS mac_addr;
|
||||
static IPv4_DEVICE_PATH ip4_node;
|
||||
@ -517,7 +562,7 @@ receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size)
|
||||
EFI_HTTP_RESPONSE_DATA response;
|
||||
EFI_HTTP_STATUS_CODE http_status;
|
||||
BOOLEAN response_done;
|
||||
UINTN i, downloaded;
|
||||
UINTN i, j, downloaded;
|
||||
CHAR8 rx_buffer[9216];
|
||||
EFI_STATUS efi_status;
|
||||
EFI_STATUS event_status;
|
||||
@ -565,7 +610,7 @@ receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size)
|
||||
if (http_status != HTTP_STATUS_200_OK) {
|
||||
perror(L"HTTP Status Code: %d\n",
|
||||
convert_http_status_code(http_status));
|
||||
efi_status = EFI_ABORTED;
|
||||
efi_status = efi_status_from_http_status(http_status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -574,6 +619,15 @@ receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size)
|
||||
if (!strcasecmp(rx_message.Headers[i].FieldName,
|
||||
(CHAR8 *)"Content-Length")) {
|
||||
*buf_size = ascii_to_int(rx_message.Headers[i].FieldValue);
|
||||
for(j = 0; j < i; j++) {
|
||||
if (!strcasecmp(rx_message.Headers[i].FieldName,
|
||||
(CHAR8 *)"Content-Length")) {
|
||||
if (*buf_size != ascii_to_int(rx_message.Headers[j].FieldValue)) {
|
||||
perror(L"Content-Length is invalid\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -98,6 +98,7 @@ extern UINT32 verbose;
|
||||
#ifndef SHIM_UNIT_TEST
|
||||
#define dprint_(fmt, ...) ({ \
|
||||
UINTN __dprint_ret = 0; \
|
||||
log_debug_print((fmt), ##__VA_ARGS__); \
|
||||
if (verbose) \
|
||||
__dprint_ret = console_print((fmt), ##__VA_ARGS__); \
|
||||
__dprint_ret; \
|
||||
|
14
include/dp.h
Normal file
14
include/dp.h
Normal file
@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
/*
|
||||
* dp.h - device path helper functions
|
||||
* Copyright Peter Jones <pjones@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef DP_H_
|
||||
#define DP_H_
|
||||
|
||||
int
|
||||
is_removable_media_path(EFI_LOADED_IMAGE *li);
|
||||
|
||||
#endif /* !DP_H_ */
|
||||
// vim:fenc=utf-8:tw=75:noet
|
22
include/errlog.h
Normal file
22
include/errlog.h
Normal file
@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
/*
|
||||
* errlog.h - error logging utilities
|
||||
* Copyright Peter Jones <pjones@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef ERRLOG_H_
|
||||
#define ERRLOG_H_
|
||||
|
||||
extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func,
|
||||
const CHAR16 *fmt, ...);
|
||||
extern EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func,
|
||||
const CHAR16 *fmt, ms_va_list args);
|
||||
extern VOID LogHexdump_(const char *file, int line, const char *func,
|
||||
const void *data, size_t sz);
|
||||
extern VOID PrintErrors(VOID);
|
||||
extern VOID ClearErrors(VOID);
|
||||
extern void save_logs(void);
|
||||
extern UINTN EFIAPI log_debug_print(const CHAR16 *fmt, ...);
|
||||
|
||||
#endif /* !ERRLOG_H_ */
|
||||
// vim:fenc=utf-8:tw=75:noet
|
@ -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 */
|
||||
|
@ -21,7 +21,7 @@ fanalyzer-build-all : COMPILER=gcc
|
||||
fanalyzer-build-all : CCACHE_DISABLE=1
|
||||
fanalyzer-build-all : FEATUREFLAGS+=-fanalyzer
|
||||
fanalyzer-build-all : WERRFLAGS=-Werror=analyzer-null-dereference
|
||||
fanalyzer-build-all : IGNORE_COMPILER_ERRORS=" || :"
|
||||
fanalyzer-build-all : IGNORE_COMPILER_ERRORS= || :
|
||||
fanalyzer-build-all : all
|
||||
|
||||
fanalyzer-no-openssl : | fanalyzer-test
|
||||
|
@ -3,6 +3,16 @@
|
||||
#ifndef SHIM_GUID_H
|
||||
#define SHIM_GUID_H
|
||||
|
||||
#define LGUID_FMT L"%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
|
||||
#define GUID_FMT "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
|
||||
|
||||
#define GUID_ARGS(guid) \
|
||||
((EFI_GUID)guid).Data1, ((EFI_GUID)guid).Data2, ((EFI_GUID)guid).Data3, \
|
||||
((EFI_GUID)guid).Data4[1], ((EFI_GUID)guid).Data4[0], \
|
||||
((EFI_GUID)guid).Data4[2], ((EFI_GUID)guid).Data4[3], \
|
||||
((EFI_GUID)guid).Data4[4], ((EFI_GUID)guid).Data4[5], \
|
||||
((EFI_GUID)guid).Data4[6], ((EFI_GUID)guid).Data4[7]
|
||||
|
||||
extern EFI_GUID BDS_GUID;
|
||||
extern EFI_GUID GV_GUID;
|
||||
extern EFI_GUID SIG_DB;
|
||||
@ -36,6 +46,8 @@ extern EFI_GUID SECURITY_PROTOCOL_GUID;
|
||||
extern EFI_GUID SECURITY2_PROTOCOL_GUID;
|
||||
extern EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
|
||||
extern EFI_GUID SHIM_LOCK_GUID;
|
||||
extern EFI_GUID SHIM_IMAGE_LOADER_GUID;
|
||||
extern EFI_GUID SHIM_LOADED_IMAGE_GUID;
|
||||
extern EFI_GUID MOK_VARIABLE_STORE;
|
||||
extern EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID;
|
||||
|
||||
|
@ -89,10 +89,14 @@ vhexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt,
|
||||
if (verbose == 0)
|
||||
return;
|
||||
|
||||
if (!data || !size) {
|
||||
if (!data) {
|
||||
dprint(L"hexdump of a NULL pointer!\n");
|
||||
return;
|
||||
}
|
||||
if (!size) {
|
||||
dprint(L"hexdump of a 0 size region!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (offset < size) {
|
||||
char hexbuf[49];
|
||||
|
@ -13,6 +13,7 @@ EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li,
|
||||
EFI_STATUS parse_load_options(EFI_LOADED_IMAGE *li);
|
||||
|
||||
extern CHAR16 *second_stage;
|
||||
extern CHAR16 *optional_second_stage;
|
||||
extern void *load_options;
|
||||
extern UINT32 load_options_size;
|
||||
|
||||
|
@ -16,7 +16,6 @@ typedef enum {
|
||||
} verification_method_t;
|
||||
|
||||
extern verification_method_t verification_method;
|
||||
extern int loader_is_participating;
|
||||
|
||||
extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab);
|
||||
extern void unhook_system_services(void);
|
||||
@ -24,7 +23,14 @@ extern void unhook_system_services(void);
|
||||
extern void hook_exit(EFI_SYSTEM_TABLE *local_systab);
|
||||
extern void unhook_exit(void);
|
||||
|
||||
extern EFI_STATUS install_shim_protocols(void);
|
||||
extern void uninstall_shim_protocols(void);
|
||||
typedef struct _SHIM_IMAGE_LOADER {
|
||||
EFI_IMAGE_LOAD LoadImage;
|
||||
EFI_IMAGE_START StartImage;
|
||||
EFI_EXIT Exit;
|
||||
EFI_IMAGE_UNLOAD UnloadImage;
|
||||
} SHIM_IMAGE_LOADER;
|
||||
|
||||
extern SHIM_IMAGE_LOADER shim_image_loader_interface;
|
||||
extern void init_image_loader(void);
|
||||
|
||||
#endif /* SHIM_REPLACEMENTS_H */
|
18
include/memattrs.h
Normal file
18
include/memattrs.h
Normal file
@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
/*
|
||||
* memattrs.h - EFI and DXE memory attribute helpers
|
||||
* Copyright Peter Jones <pjones@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef SHIM_MEMATTRS_H_
|
||||
#define SHIM_MEMATTRS_H_
|
||||
|
||||
extern EFI_STATUS get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs);
|
||||
extern EFI_STATUS update_mem_attrs(uintptr_t addr, uint64_t size,
|
||||
uint64_t set_attrs, uint64_t clear_attrs);
|
||||
|
||||
extern void get_hsi_mem_info(void);
|
||||
extern char *decode_hsi_bits(UINTN hsi);
|
||||
|
||||
#endif /* !SHIM_MEMATTRS_H_ */
|
||||
// vim:fenc=utf-8:tw=75:noet
|
@ -115,6 +115,9 @@ void mock_uninstall_query_variable_info(void);
|
||||
void mock_reset_variables(void);
|
||||
void mock_reset_config_table(void);
|
||||
void mock_finalize_vars_and_configs(void);
|
||||
void mock_set_usage_limits(list_t *limit_list,
|
||||
struct mock_variable_limits *limits);
|
||||
void mock_set_default_usage_limits(void);
|
||||
|
||||
typedef enum {
|
||||
NONE = 0,
|
||||
|
@ -17,6 +17,14 @@ typedef enum {
|
||||
|
||||
struct mok_state_variable;
|
||||
typedef vendor_addend_category_t (vendor_addend_categorizer_t)(struct mok_state_variable *);
|
||||
typedef UINTN (mok_variable_format_helper_t)(UINT8 *buf, size_t sz, struct mok_state_variable *);
|
||||
|
||||
#define MOK_MIRROR_KEYDB 0x01
|
||||
#define MOK_MIRROR_DELETE_FIRST 0x02
|
||||
#define MOK_VARIABLE_MEASURE 0x04
|
||||
#define MOK_VARIABLE_LOG 0x08
|
||||
#define MOK_VARIABLE_INVERSE 0x10
|
||||
#define MOK_VARIABLE_CONFIG_ONLY 0x20
|
||||
|
||||
/*
|
||||
* MoK variables that need to have their storage validated.
|
||||
@ -81,6 +89,8 @@ struct mok_state_variable {
|
||||
* MOK_MIRROR_DELETE_FIRST delete any existing variable first
|
||||
* MOK_VARIABLE_MEASURE extend PCR 7 and log the hash change
|
||||
* MOK_VARIABLE_LOG measure into whatever .pcr says and log
|
||||
* MOK_VARIABLE_CONFIG_ONLY don't create a UEFI variable, only add
|
||||
* it to the config space variables.
|
||||
*/
|
||||
UINTN pcr; /* PCR to measure and hash to */
|
||||
|
||||
@ -89,6 +99,23 @@ struct mok_state_variable {
|
||||
* mirrored.
|
||||
*/
|
||||
UINT8 *state;
|
||||
|
||||
/*
|
||||
* If this is non-NULL, this function will be called during the
|
||||
* "import" phase to format the variable data. It'll get called
|
||||
* twice, once as:
|
||||
*
|
||||
* sz = format(NULL, 0, ptr);
|
||||
*
|
||||
* a buffer of size sz will then be allocated, and it'll be called
|
||||
* again to fill the buffer:
|
||||
*
|
||||
* format(buf, sz, ptr);
|
||||
*
|
||||
* Note that as an implementation detail data and data_size must be
|
||||
* NULL and 0 respectively for this entry.
|
||||
*/
|
||||
mok_variable_format_helper_t *format;
|
||||
};
|
||||
|
||||
extern size_t n_mok_state_variables;
|
||||
@ -100,10 +127,31 @@ struct mok_variable_config_entry {
|
||||
UINT8 data[];
|
||||
};
|
||||
|
||||
extern EFI_PHYSICAL_ADDRESS mok_config_table;
|
||||
extern UINTN mok_config_table_pages;
|
||||
|
||||
/*
|
||||
* bit definitions for MokPolicy
|
||||
*/
|
||||
#define MOK_POLICY_REQUIRE_NX 1
|
||||
|
||||
extern UINTN hsi_status;
|
||||
/* heap is executable */
|
||||
#define SHIM_HSI_STATUS_HEAPX 0x00000001ULL
|
||||
/* stack is executable */
|
||||
#define SHIM_HSI_STATUS_STACKX 0x00000002ULL
|
||||
/* read-only sections are writable */
|
||||
#define SHIM_HSI_STATUS_ROW 0x00000004ULL
|
||||
/* platform provides the EFI Memory Attribute Protocol */
|
||||
#define SHIM_HSI_STATUS_HASMAP 0x00000008ULL
|
||||
/* platform provides DXE Services Table */
|
||||
#define SHIM_HSI_STATUS_HASDST 0x00000010ULL
|
||||
/* platform has DST->GetMemorySpaceDescriptor */
|
||||
#define SHIM_HSI_STATUS_HASDSTGMSD 0x00000020ULL
|
||||
/* platform has DST->SetMemorySpaceAttributes */
|
||||
#define SHIM_HSI_STATUS_HASDSTSMSA 0x00000040ULL
|
||||
/* This shim has the NX_COMPAT bit set */
|
||||
#define SHIM_HSI_STATUS_NX 0x00000100ULL
|
||||
|
||||
#endif /* !SHIM_MOK_H_ */
|
||||
// vim:fenc=utf-8:tw=75:noet
|
||||
|
@ -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 */
|
||||
|
@ -12,7 +12,8 @@ ImageAddress (void *image, uint64_t size, uint64_t address);
|
||||
|
||||
EFI_STATUS
|
||||
read_header(void *data, unsigned int datasize,
|
||||
PE_COFF_LOADER_IMAGE_CONTEXT *context);
|
||||
PE_COFF_LOADER_IMAGE_CONTEXT *context,
|
||||
bool check_secdir);
|
||||
|
||||
EFI_STATUS verify_image(void *data, unsigned int datasize,
|
||||
EFI_LOADED_IMAGE *li,
|
||||
@ -52,5 +53,8 @@ relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
|
||||
EFI_IMAGE_SECTION_HEADER *Section,
|
||||
void *orig, void *data);
|
||||
|
||||
void
|
||||
get_shim_nx_capability(EFI_HANDLE image_handle);
|
||||
|
||||
#endif /* !PE_H_ */
|
||||
// vim:fenc=utf-8:tw=75:noet
|
||||
|
@ -824,6 +824,7 @@ typedef struct {
|
||||
EFI_IMAGE_DATA_DIRECTORY *RelocDir;
|
||||
EFI_IMAGE_DATA_DIRECTORY *SecDir;
|
||||
UINT64 NumberOfRvaAndSizes;
|
||||
UINT16 DllCharacteristics;
|
||||
EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr;
|
||||
} PE_COFF_LOADER_IMAGE_CONTEXT;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -7,7 +7,9 @@
|
||||
#define QUOTE(s) #s
|
||||
|
||||
/*
|
||||
* This is the entry for the sbat data format
|
||||
* SbatLevel Epoch and SHIM_DEVEL definitions are here
|
||||
* Actual revocations are now soley defined in
|
||||
* SbatLevel_Variable.txt
|
||||
*/
|
||||
#define SBAT_VAR_SIG "sbat,"
|
||||
#define SBAT_VAR_VERSION "1,"
|
||||
@ -22,46 +24,10 @@
|
||||
|
||||
#define SBAT_VAR_LATEST_DATE "2022050100"
|
||||
#define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n"
|
||||
#define SBAT_VAR_LATEST \
|
||||
SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \
|
||||
SBAT_VAR_LATEST_REVOCATIONS
|
||||
#else /* !ENABLE_SHIM_DEVEL */
|
||||
|
||||
/*
|
||||
* Some distros may want to apply revocations from 2022052400
|
||||
* or 2022111500 automatically. They can be selected by setting
|
||||
* SBAT_AUTOMATIC_DATE=<datestamp> at build time. Otherwise the
|
||||
* default is to apply the second to most recent revocations
|
||||
* automatically. Distros that need to manage automatic updates
|
||||
* externally from shim can choose the epoch 2021030218 emtpy
|
||||
* revocations.
|
||||
*/
|
||||
#ifndef SBAT_AUTOMATIC_DATE
|
||||
#define SBAT_AUTOMATIC_DATE 2023012900
|
||||
#endif /* SBAT_AUTOMATIC_DATE */
|
||||
#if SBAT_AUTOMATIC_DATE == 2021030218
|
||||
#define SBAT_VAR_AUTOMATIC_REVOCATIONS
|
||||
#elif SBAT_AUTOMATIC_DATE == 2022052400
|
||||
#define SBAT_VAR_AUTOMATIC_REVOCATIONS "grub,2\n"
|
||||
#elif SBAT_AUTOMATIC_DATE == 2022111500
|
||||
#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\n"
|
||||
#elif SBAT_AUTOMATIC_DATE == 2023012900
|
||||
#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\ngrub.debian,4\n"
|
||||
#else
|
||||
#error "Unknown SBAT_AUTOMATIC_DATE"
|
||||
#endif /* SBAT_AUTOMATIC_DATE == */
|
||||
#define SBAT_VAR_AUTOMATIC_DATE QUOTEVAL(SBAT_AUTOMATIC_DATE)
|
||||
#define SBAT_VAR_AUTOMATIC \
|
||||
SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE "\n" \
|
||||
SBAT_VAR_AUTOMATIC_REVOCATIONS
|
||||
|
||||
/*
|
||||
* Revocations for January 2024 shim CVEs
|
||||
*/
|
||||
#define SBAT_VAR_LATEST_DATE "2024010900"
|
||||
#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\n"
|
||||
#define SBAT_VAR_LATEST \
|
||||
SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \
|
||||
SBAT_VAR_LATEST_REVOCATIONS
|
||||
#endif /* ENABLE_SHIM_DEVEL */
|
||||
|
||||
#endif /* !SBAT_VAR_DEFS_H_ */
|
||||
|
@ -106,5 +106,16 @@ static const unsigned char test_data_efivars_1_MokListTrustedRT[] ={
|
||||
0x01
|
||||
};
|
||||
|
||||
static const unsigned char test_data_efivars_1_HSIStatus[] =
|
||||
"heap-is-executable: 0\n"
|
||||
"stack-is-executable: 0\n"
|
||||
"ro-sections-are-writable: 0\n"
|
||||
"has-memory-attribute-protocol: 0\n"
|
||||
"has-dxe-services-table: 0\n"
|
||||
"has-get-memory-space-descriptor: 0\n"
|
||||
"has-set-memory-space-attributes: 0\n"
|
||||
"shim-has-nx-compat-set: 0\n"
|
||||
;
|
||||
|
||||
#endif /* !TEST_DATA_EFIVARS_1_H_ */
|
||||
// vim:fenc=utf-8:tw=75:noet
|
||||
|
@ -85,14 +85,14 @@ extern EFI_RUNTIME_SERVICES *RT;
|
||||
static inline INT64
|
||||
guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1)
|
||||
{
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): Comparing "GUID_FMT" to "GUID_FMT"\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
GUID_ARGS(*guid0), GUID_ARGS(*guid1));
|
||||
#endif
|
||||
|
||||
if (guid0->Data1 != guid1->Data1) {
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
(INT64)guid0->Data1, (INT64)guid1->Data1,
|
||||
@ -102,7 +102,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1)
|
||||
}
|
||||
|
||||
if (guid0->Data2 != guid1->Data2) {
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
(INT64)guid0->Data2, (INT64)guid1->Data2,
|
||||
@ -112,7 +112,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1)
|
||||
}
|
||||
|
||||
if (guid0->Data3 != guid1->Data3) {
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
(INT64)guid0->Data3, (INT64)guid1->Data3,
|
||||
@ -126,7 +126,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1)
|
||||
* representation of it.
|
||||
*/
|
||||
if (guid0->Data4[1] != guid1->Data4[1]) {
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
(INT64)guid0->Data4[1], (INT64)guid1->Data4[1],
|
||||
@ -136,7 +136,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1)
|
||||
}
|
||||
|
||||
if (guid0->Data4[0] != guid1->Data4[0]) {
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
(INT64)guid0->Data4[0], (INT64)guid1->Data4[0],
|
||||
@ -147,7 +147,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1)
|
||||
|
||||
for (UINTN i = 2; i < 8; i++) {
|
||||
if (guid0->Data4[i] != guid1->Data4[i]) {
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
(INT64)guid0->Data4[i], (INT64)guid1->Data4[i],
|
||||
@ -157,7 +157,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1)
|
||||
}
|
||||
}
|
||||
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): returning 0x0\n",
|
||||
__FILE__, __LINE__-1, __func__);
|
||||
#endif
|
||||
@ -177,7 +177,7 @@ guidcmp(const EFI_GUID * const guid0, const EFI_GUID * const guid1)
|
||||
|
||||
cmp = guidcmp_helper(guida, guidb);
|
||||
ret = cmp < 0 ? -1 : (cmp > 0 ? 1 : 0);
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s():CompareGuid("GUID_FMT","GUID_FMT")->%lld (%d)\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
GUID_ARGS(*guida), GUID_ARGS(*guidb), cmp, ret);
|
||||
|
@ -76,8 +76,12 @@ libefi-test.a :
|
||||
clean
|
||||
|
||||
test-random.h:
|
||||
dd if=/dev/urandom bs=512 count=17 of=random.bin
|
||||
xxd -i random.bin test-random.h
|
||||
dd if=/dev/urandom bs=512 count=17 status=none | ( \
|
||||
echo "unsigned char random_bin[] = {" ; \
|
||||
xxd -i - ; \
|
||||
echo "};" ; \
|
||||
echo "unsigned int random_bin_len = 8704;" ; \
|
||||
) > test-random.h
|
||||
|
||||
$(wildcard test-*.c) :: %.c : test-random.h
|
||||
$(patsubst %.c,%,$(wildcard test-*.c)) :: | test-random.h
|
||||
@ -119,7 +123,7 @@ test-coverage : CFLAGS_GCOV+=--coverage
|
||||
test-coverage : $(tests)
|
||||
|
||||
test-clean :
|
||||
@rm -vf test-random.h random.bin libefi-test.a
|
||||
@rm -vf test-random.h libefi-test.a
|
||||
@rm -vf *.gcda *.gcno *.gcov vgcore.*
|
||||
|
||||
clean : test-clean
|
||||
@ -127,6 +131,5 @@ clean : test-clean
|
||||
all : test-clean test
|
||||
|
||||
.PHONY: $(tests) all test clean
|
||||
.SECONDARY: random.bin
|
||||
|
||||
# vim:ft=make
|
||||
|
9
include/utils.h
Normal file
9
include/utils.h
Normal file
@ -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_ */
|
@ -651,6 +651,7 @@ static struct {
|
||||
{ EFI_PROTOCOL_ERROR, L"Protocol Error"},
|
||||
{ EFI_INCOMPATIBLE_VERSION, L"Incompatible Version"},
|
||||
{ EFI_SECURITY_VIOLATION, L"Security Violation"},
|
||||
{ EFI_HTTP_ERROR, L"HTTP Error"},
|
||||
|
||||
// warnings
|
||||
{ EFI_WARN_UNKNOWN_GLYPH, L"Warning Unknown Glyph"},
|
||||
|
@ -35,5 +35,7 @@ EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD
|
||||
EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } };
|
||||
EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID = { 0xf4560cf6, 0x40ec, 0x4b4a, {0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89} };
|
||||
EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } };
|
||||
EFI_GUID SHIM_IMAGE_LOADER_GUID = {0x1f492041, 0xfadb, 0x4e59, {0x9e, 0x57, 0x7c, 0xaf, 0xe7, 0x3a, 0x55, 0xab } };
|
||||
EFI_GUID SHIM_LOADED_IMAGE_GUID = {0x6e6baeb8, 0x7108, 0x4179, {0x94, 0x9d, 0xa3, 0x49, 0x34, 0x15, 0xec, 0x97 } };
|
||||
EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} };
|
||||
EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID = {0x77fa9abd, 0x0359, 0x4d32, {0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b} };
|
||||
|
@ -170,7 +170,7 @@ simple_file_write_all(EFI_FILE *file, UINTN size, void *buffer)
|
||||
EFI_STATUS
|
||||
simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h)
|
||||
{
|
||||
UINTN count, i;
|
||||
UINTN count, i, j;
|
||||
EFI_HANDLE *vol_handles = NULL;
|
||||
EFI_STATUS efi_status;
|
||||
CHAR16 **entries;
|
||||
@ -184,11 +184,11 @@ simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h)
|
||||
if (!count || !vol_handles)
|
||||
return EFI_NOT_FOUND;
|
||||
|
||||
entries = AllocatePool(sizeof(CHAR16 *) * (count+1));
|
||||
entries = AllocateZeroPool(sizeof(CHAR16 *) * (count+1));
|
||||
if (!entries)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
for (i = 0, j = 0; i < count; i++) {
|
||||
char buf[4096];
|
||||
UINTN size = sizeof(buf);
|
||||
EFI_FILE_SYSTEM_INFO *fi = (void *)buf;
|
||||
@ -208,19 +208,22 @@ simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h)
|
||||
|
||||
efi_status = root->GetInfo(root, &EFI_FILE_SYSTEM_INFO_GUID,
|
||||
&size, fi);
|
||||
if (EFI_ERROR(efi_status))
|
||||
continue;
|
||||
|
||||
/* If GetInfo fails, try to form a name from DevicePath. */
|
||||
if (EFI_ERROR(efi_status)){
|
||||
name = NULL;
|
||||
} else {
|
||||
name = fi->VolumeLabel;
|
||||
}
|
||||
|
||||
if (!name || StrLen(name) == 0 || StrCmp(name, L" ") == 0)
|
||||
name = DevicePathToStr(DevicePathFromHandle(vol_handles[i]));
|
||||
|
||||
entries[i] = AllocatePool((StrLen(name) + 2) * sizeof(CHAR16));
|
||||
if (!entries[i])
|
||||
entries[j] = AllocatePool((StrLen(name) + 2) * sizeof(CHAR16));
|
||||
if (!entries[j])
|
||||
break;
|
||||
StrCpy(entries[i], name);
|
||||
StrCpy(entries[j++], name);
|
||||
}
|
||||
entries[i] = NULL;
|
||||
entries[j] = NULL;
|
||||
|
||||
val = console_select(title, entries, 0);
|
||||
|
||||
@ -285,7 +288,7 @@ simple_dir_filter(EFI_HANDLE image, CHAR16 *name, CHAR16 *filter,
|
||||
goto out;
|
||||
ptr = next = *entries;
|
||||
|
||||
for (i = 0; i < tot; i++) {
|
||||
for (i = 0; next && i < tot; i++) {
|
||||
int len = StrLen(next->FileName);
|
||||
|
||||
for (c = 0; c < filtercount; c++) {
|
||||
@ -308,7 +311,7 @@ simple_dir_filter(EFI_HANDLE image, CHAR16 *name, CHAR16 *filter,
|
||||
*count = 0;
|
||||
ptr = next = *entries;
|
||||
|
||||
for (i = 0; i < tot; i++) {
|
||||
for (i = 0; next && i < tot; i++) {
|
||||
int len = StrLen(next->FileName);
|
||||
|
||||
if (StrCmp(next->FileName, L".") == 0)
|
||||
|
@ -226,6 +226,8 @@ SetSecureVariable(const CHAR16 * const var, UINT8 *Data, UINTN len,
|
||||
}
|
||||
efi_status = CreateTimeBasedPayload(&DataSize, (UINT8 **)&Cert);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
if (Cert && Cert != (EFI_SIGNATURE_LIST *)Data)
|
||||
FreePool(Cert);
|
||||
console_print(L"Failed to create time based payload %d\n",
|
||||
efi_status);
|
||||
return efi_status;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "shim.h"
|
||||
|
||||
CHAR16 *second_stage;
|
||||
CHAR16 *optional_second_stage = NULL;
|
||||
void *load_options;
|
||||
UINT32 load_options_size;
|
||||
|
||||
@ -207,14 +208,14 @@ get_load_option_optional_data(VOID *data, UINT32 data_size,
|
||||
*/
|
||||
i += dp.len;
|
||||
}
|
||||
if (i != fplistlen)
|
||||
if (i > fplistlen)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
/*
|
||||
* if there's any space left, it's "optional data"
|
||||
* Anything left after the file path list is optional data.
|
||||
*/
|
||||
*od = cur + i;
|
||||
*ods = limit - i;
|
||||
*od = cur + fplistlen;
|
||||
*ods = limit - fplistlen;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
@ -310,8 +311,13 @@ parse_load_options(EFI_LOADED_IMAGE *li)
|
||||
UINT32 remaining_size;
|
||||
CHAR16 *loader_str = NULL;
|
||||
|
||||
dprint(L"full load options:\n");
|
||||
if (!li->LoadOptions || !li->LoadOptionsSize) {
|
||||
dprint(L"LoadOptions is empty\n");
|
||||
return EFI_SUCCESS;
|
||||
} else {
|
||||
dprint(L"full LoadOptions:\n");
|
||||
dhexdumpat(li->LoadOptions, li->LoadOptionsSize, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check since we make several assumptions about the length
|
||||
@ -442,15 +448,25 @@ parse_load_options(EFI_LOADED_IMAGE *li)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Windows bcdedit.exe puts "WINDOWS\0" (in 8-bit) in the beginning of
|
||||
* the options, so if we see that, we know it's not useful to us.
|
||||
*/
|
||||
if (li->LoadOptionsSize >= 8)
|
||||
if (CompareMem(li->LoadOptions, "WINDOWS", 8) == 0)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
loader_str = split_load_options(li->LoadOptions, li->LoadOptionsSize,
|
||||
&remaining, &remaining_size);
|
||||
|
||||
/*
|
||||
* Set up the name of the alternative loader and the LoadOptions for
|
||||
* the loader
|
||||
* the loader if it's not the empty string.
|
||||
*/
|
||||
if (loader_str) {
|
||||
if (*loader_str) {
|
||||
second_stage = loader_str;
|
||||
}
|
||||
load_options = remaining;
|
||||
load_options_size = remaining_size;
|
||||
}
|
||||
|
445
loader-proto.c
Normal file
445
loader-proto.c
Normal file
@ -0,0 +1,445 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
/*
|
||||
* loader-proto.c - shim's loader protocol
|
||||
*
|
||||
* Copyright Red Hat, Inc
|
||||
* Copyright Canonical, Ltd
|
||||
*/
|
||||
|
||||
#include "shim.h"
|
||||
|
||||
static EFI_SYSTEM_TABLE *systab;
|
||||
|
||||
EFI_SYSTEM_TABLE *
|
||||
get_active_systab(void)
|
||||
{
|
||||
if (systab)
|
||||
return systab;
|
||||
return ST;
|
||||
}
|
||||
|
||||
static typeof(systab->BootServices->LoadImage) system_load_image;
|
||||
static typeof(systab->BootServices->StartImage) system_start_image;
|
||||
static typeof(systab->BootServices->UnloadImage) system_unload_image;
|
||||
static typeof(systab->BootServices->Exit) system_exit;
|
||||
|
||||
void
|
||||
unhook_system_services(void)
|
||||
{
|
||||
if (!systab)
|
||||
return;
|
||||
|
||||
systab->BootServices->LoadImage = system_load_image;
|
||||
systab->BootServices->StartImage = system_start_image;
|
||||
systab->BootServices->Exit = system_exit;
|
||||
systab->BootServices->UnloadImage = system_unload_image;
|
||||
BS = systab->BootServices;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
EFI_HANDLE hnd;
|
||||
EFI_DEVICE_PATH *dp;
|
||||
void *buffer;
|
||||
size_t size;
|
||||
} buffer_properties_t;
|
||||
|
||||
static EFI_STATUS
|
||||
try_load_from_sfs(EFI_DEVICE_PATH *dp, buffer_properties_t *bprop)
|
||||
{
|
||||
EFI_STATUS status = EFI_SUCCESS;
|
||||
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *sfs = NULL;
|
||||
EFI_FILE_HANDLE root = NULL;
|
||||
EFI_FILE_HANDLE file = NULL;
|
||||
UINT64 tmpsz = 0;
|
||||
|
||||
bprop->buffer = NULL;
|
||||
|
||||
/* look for a handle with SFS support from the input DP */
|
||||
bprop->dp = dp;
|
||||
status = BS->LocateDevicePath(&EFI_SIMPLE_FILE_SYSTEM_GUID, &bprop->dp, &bprop->hnd);
|
||||
if (EFI_ERROR(status)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* make sure the remaining DP portion is really a file path */
|
||||
if (DevicePathType(bprop->dp) != MEDIA_DEVICE_PATH ||
|
||||
DevicePathSubType(bprop->dp) != MEDIA_FILEPATH_DP) {
|
||||
status = EFI_LOAD_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* find protocol, open the root directory, then open file */
|
||||
status = BS->HandleProtocol(bprop->hnd, &EFI_SIMPLE_FILE_SYSTEM_GUID, (void **)&sfs);
|
||||
if (EFI_ERROR(status))
|
||||
goto out;
|
||||
status = sfs->OpenVolume(sfs, &root);
|
||||
if (EFI_ERROR(status))
|
||||
goto out;
|
||||
status = root->Open(root, &file, ((FILEPATH_DEVICE_PATH *) bprop->dp)->PathName, EFI_FILE_MODE_READ, 0);
|
||||
if (EFI_ERROR(status))
|
||||
goto out;
|
||||
|
||||
/* get file size */
|
||||
status = file->SetPosition(file, -1ULL);
|
||||
if (EFI_ERROR(status))
|
||||
goto out;
|
||||
status = file->GetPosition(file, &tmpsz);
|
||||
if (EFI_ERROR(status))
|
||||
goto out;
|
||||
bprop->size = (size_t)tmpsz;
|
||||
status = file->SetPosition(file, 0);
|
||||
if (EFI_ERROR(status))
|
||||
goto out;
|
||||
|
||||
/* allocate buffer */
|
||||
bprop->buffer = AllocatePool(bprop->size);
|
||||
if (bprop->buffer == NULL) {
|
||||
status = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read file */
|
||||
status = file->Read(file, &bprop->size, bprop->buffer);
|
||||
|
||||
out:
|
||||
if (EFI_ERROR(status) && bprop->buffer)
|
||||
FreePool(bprop->buffer);
|
||||
if (file)
|
||||
file->Close(file);
|
||||
if (root)
|
||||
root->Close(root);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static EFI_STATUS
|
||||
try_load_from_lf2(EFI_DEVICE_PATH *dp, buffer_properties_t *bprop)
|
||||
{
|
||||
EFI_STATUS status = EFI_SUCCESS;
|
||||
EFI_LOAD_FILE2_PROTOCOL *lf2 = NULL;
|
||||
|
||||
bprop->buffer = NULL;
|
||||
|
||||
/* look for a handle with LF2 support from the input DP */
|
||||
bprop->dp = dp;
|
||||
status = BS->LocateDevicePath(&gEfiLoadFile2ProtocolGuid, &bprop->dp, &bprop->hnd);
|
||||
if (EFI_ERROR(status))
|
||||
goto out;
|
||||
|
||||
/* find protocol */
|
||||
status = BS->HandleProtocol(bprop->hnd, &gEfiLoadFile2ProtocolGuid, (void **) &lf2);
|
||||
if (EFI_ERROR(status))
|
||||
goto out;
|
||||
|
||||
/* get file size */
|
||||
bprop->size = 0; /* this shouldn't be read when Buffer=NULL but better be safe */
|
||||
status = lf2->LoadFile(lf2, bprop->dp, /*BootPolicy=*/false, &bprop->size, NULL);
|
||||
/*
|
||||
* NOTE: the spec is somewhat ambiguous what is the correct return
|
||||
* status code when asking for the buffer size with Buffer=NULL. I am
|
||||
* assuming EFI_SUCCESS and EFI_BUFFER_TOO_SMALL are the only
|
||||
* reasonable interpretations.
|
||||
*/
|
||||
if (EFI_ERROR(status) && status != EFI_BUFFER_TOO_SMALL) {
|
||||
status = EFI_LOAD_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* allocate buffer */
|
||||
bprop->buffer = AllocatePool(bprop->size);
|
||||
if (!bprop->buffer) {
|
||||
status = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read file */
|
||||
status = lf2->LoadFile(lf2, bprop->dp, /*BootPolicy=*/false, &bprop->size, bprop->buffer);
|
||||
if (EFI_ERROR(status))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (EFI_ERROR(status) && bprop->buffer)
|
||||
FreePool(bprop->buffer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static EFI_STATUS EFIAPI
|
||||
shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
|
||||
EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer,
|
||||
UINTN SourceSize, EFI_HANDLE *ImageHandle)
|
||||
{
|
||||
SHIM_LOADED_IMAGE *image;
|
||||
EFI_STATUS efi_status;
|
||||
buffer_properties_t bprop = { NULL, NULL, NULL, 0 };
|
||||
|
||||
if (BootPolicy)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
if (!SourceBuffer || !SourceSize) {
|
||||
if (!DevicePath) /* Both SourceBuffer and DevicePath are NULL */
|
||||
return EFI_NOT_FOUND;
|
||||
|
||||
if (try_load_from_sfs(DevicePath, &bprop) == EFI_SUCCESS)
|
||||
;
|
||||
else if (try_load_from_lf2(DevicePath, &bprop) == EFI_SUCCESS)
|
||||
;
|
||||
else
|
||||
/* no buffer given and we cannot load from this device */
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
SourceBuffer = bprop.buffer;
|
||||
SourceSize = bprop.size;
|
||||
} else {
|
||||
bprop.buffer = NULL;
|
||||
/*
|
||||
* Even if we are using a buffer, try populating the
|
||||
* device_handle and file_path fields the best we can
|
||||
*/
|
||||
|
||||
bprop.dp = DevicePath;
|
||||
|
||||
if (bprop.dp) {
|
||||
efi_status = BS->LocateDevicePath(&gEfiDevicePathProtocolGuid,
|
||||
&bprop.dp,
|
||||
&bprop.hnd);
|
||||
if (efi_status != EFI_SUCCESS) {
|
||||
/* can't seem to pull apart this DP */
|
||||
bprop.dp = DevicePath;
|
||||
bprop.hnd = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image = AllocatePool(sizeof(*image));
|
||||
if (!image) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto free_buffer;
|
||||
}
|
||||
|
||||
SetMem(image, sizeof(*image), 0);
|
||||
|
||||
image->li.Revision = 0x1000;
|
||||
image->li.ParentHandle = ParentImageHandle;
|
||||
image->li.SystemTable = systab;
|
||||
image->li.DeviceHandle = bprop.hnd;
|
||||
if (bprop.dp) {
|
||||
image->li.FilePath = DuplicateDevicePath(bprop.dp);
|
||||
if (!image->li.FilePath) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto free_image;
|
||||
}
|
||||
}
|
||||
if (DevicePath) {
|
||||
image->loaded_image_device_path = DuplicateDevicePath(DevicePath);
|
||||
if (!image->loaded_image_device_path) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto free_image;
|
||||
}
|
||||
}
|
||||
|
||||
in_protocol = 1;
|
||||
efi_status = handle_image(SourceBuffer, SourceSize, &image->li,
|
||||
&image->entry_point, &image->alloc_address,
|
||||
&image->alloc_pages);
|
||||
in_protocol = 0;
|
||||
if (EFI_ERROR(efi_status))
|
||||
goto free_image;
|
||||
|
||||
*ImageHandle = NULL;
|
||||
efi_status = BS->InstallMultipleProtocolInterfaces(ImageHandle,
|
||||
&SHIM_LOADED_IMAGE_GUID, image,
|
||||
&EFI_LOADED_IMAGE_GUID, &image->li,
|
||||
&gEfiLoadedImageDevicePathProtocolGuid,
|
||||
image->loaded_image_device_path,
|
||||
NULL);
|
||||
if (EFI_ERROR(efi_status))
|
||||
goto free_alloc;
|
||||
|
||||
if (bprop.buffer)
|
||||
FreePool(bprop.buffer);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
|
||||
free_alloc:
|
||||
BS->FreePages(image->alloc_address, image->alloc_pages);
|
||||
free_image:
|
||||
if (image->loaded_image_device_path)
|
||||
FreePool(image->loaded_image_device_path);
|
||||
if (image->li.FilePath)
|
||||
FreePool(image->li.FilePath);
|
||||
FreePool(image);
|
||||
free_buffer:
|
||||
if (bprop.buffer)
|
||||
FreePool(bprop.buffer);
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
static EFI_STATUS EFIAPI
|
||||
shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize,
|
||||
OUT CHAR16 **ExitData OPTIONAL)
|
||||
{
|
||||
SHIM_LOADED_IMAGE *image;
|
||||
EFI_STATUS efi_status;
|
||||
|
||||
efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID,
|
||||
(void **)&image);
|
||||
|
||||
/*
|
||||
* This image didn't come from shim_load_image(), so it must have come
|
||||
* from something before shim was involved.
|
||||
*/
|
||||
if (efi_status == EFI_UNSUPPORTED)
|
||||
return system_start_image(ImageHandle, ExitDataSize, ExitData);
|
||||
|
||||
if (EFI_ERROR(efi_status) || image->started)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (!setjmp(image->longjmp_buf)) {
|
||||
image->started = true;
|
||||
efi_status =
|
||||
image->entry_point(ImageHandle, image->li.SystemTable);
|
||||
} else {
|
||||
if (ExitData) {
|
||||
*ExitDataSize = image->exit_data_size;
|
||||
*ExitData = (CHAR16 *)image->exit_data;
|
||||
}
|
||||
efi_status = image->exit_status;
|
||||
}
|
||||
|
||||
//
|
||||
// We only support EFI applications, so we can unload and free the
|
||||
// image unconditionally.
|
||||
//
|
||||
BS->UninstallMultipleProtocolInterfaces(ImageHandle,
|
||||
&EFI_LOADED_IMAGE_GUID, image,
|
||||
&SHIM_LOADED_IMAGE_GUID, &image->li,
|
||||
&gEfiLoadedImageDevicePathProtocolGuid,
|
||||
image->loaded_image_device_path,
|
||||
NULL);
|
||||
|
||||
BS->FreePages(image->alloc_address, image->alloc_pages);
|
||||
if (image->li.FilePath)
|
||||
BS->FreePool(image->li.FilePath);
|
||||
if (image->loaded_image_device_path)
|
||||
BS->FreePool(image->loaded_image_device_path);
|
||||
FreePool(image);
|
||||
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
static EFI_STATUS EFIAPI
|
||||
shim_unload_image(EFI_HANDLE ImageHandle)
|
||||
{
|
||||
SHIM_LOADED_IMAGE *image;
|
||||
EFI_STATUS efi_status;
|
||||
|
||||
efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID,
|
||||
(void **)&image);
|
||||
|
||||
if (efi_status == EFI_UNSUPPORTED)
|
||||
return system_unload_image(ImageHandle);
|
||||
|
||||
BS->FreePages(image->alloc_address, image->alloc_pages);
|
||||
FreePool(image);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static EFI_STATUS EFIAPI
|
||||
shim_exit(EFI_HANDLE ImageHandle,
|
||||
EFI_STATUS ExitStatus,
|
||||
UINTN ExitDataSize,
|
||||
CHAR16 *ExitData)
|
||||
{
|
||||
EFI_STATUS efi_status;
|
||||
SHIM_LOADED_IMAGE *image;
|
||||
|
||||
efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID,
|
||||
(void **)&image);
|
||||
|
||||
/*
|
||||
* If this happens, something above us on the stack of running
|
||||
* applications called Exit(), and we're getting aborted along with
|
||||
* it.
|
||||
*/
|
||||
if (efi_status == EFI_UNSUPPORTED) {
|
||||
shim_fini();
|
||||
return system_exit(ImageHandle, ExitStatus, ExitDataSize,
|
||||
ExitData);
|
||||
}
|
||||
|
||||
if (EFI_ERROR(efi_status))
|
||||
return efi_status;
|
||||
|
||||
image->exit_status = ExitStatus;
|
||||
image->exit_data_size = ExitDataSize;
|
||||
image->exit_data = ExitData;
|
||||
|
||||
longjmp(image->longjmp_buf, 1);
|
||||
}
|
||||
|
||||
void
|
||||
init_image_loader(void)
|
||||
{
|
||||
shim_image_loader_interface.LoadImage = shim_load_image;
|
||||
shim_image_loader_interface.StartImage = shim_start_image;
|
||||
shim_image_loader_interface.Exit = shim_exit;
|
||||
shim_image_loader_interface.UnloadImage = shim_unload_image;
|
||||
}
|
||||
|
||||
void
|
||||
hook_system_services(EFI_SYSTEM_TABLE *local_systab)
|
||||
{
|
||||
systab = local_systab;
|
||||
BS = systab->BootServices;
|
||||
|
||||
/* We need to hook various calls to make this work... */
|
||||
|
||||
/*
|
||||
* We need LoadImage() hooked so that we can guarantee everything is
|
||||
* verified.
|
||||
*/
|
||||
system_load_image = systab->BootServices->LoadImage;
|
||||
systab->BootServices->LoadImage = shim_load_image;
|
||||
|
||||
/*
|
||||
* We need StartImage() hooked because the system's StartImage()
|
||||
* doesn't know about our structure layout.
|
||||
*/
|
||||
system_start_image = systab->BootServices->StartImage;
|
||||
systab->BootServices->StartImage = shim_start_image;
|
||||
|
||||
/*
|
||||
* We need Exit() hooked so that we make sure to use the right jmp_buf
|
||||
* when an application calls Exit(), but that happens in a separate
|
||||
* function.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We need UnloadImage() to match our LoadImage()
|
||||
*/
|
||||
system_unload_image = systab->BootServices->UnloadImage;
|
||||
systab->BootServices->UnloadImage = shim_unload_image;
|
||||
}
|
||||
|
||||
void
|
||||
unhook_exit(void)
|
||||
{
|
||||
systab->BootServices->Exit = system_exit;
|
||||
BS = systab->BootServices;
|
||||
}
|
||||
|
||||
void
|
||||
hook_exit(EFI_SYSTEM_TABLE *local_systab)
|
||||
{
|
||||
systab = local_systab;
|
||||
BS = local_systab->BootServices;
|
||||
|
||||
/*
|
||||
* We need to hook Exit() so that we can allow users to quit the
|
||||
* bootloader and still e.g. start a new one or run an internal
|
||||
* shell.
|
||||
*/
|
||||
system_exit = systab->BootServices->Exit;
|
||||
systab->BootServices->Exit = shim_exit;
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
set -eu
|
||||
set -o pipefail
|
||||
set -x
|
||||
|
||||
usage() {
|
||||
status="${1}"
|
||||
@ -78,10 +80,11 @@ main() {
|
||||
rm -rf "${ARCHIVE_DIR}/shim-${VERSION}" "${ARCHIVE_DIR}/shim-${VERSION}"
|
||||
mkdir -p "${ARCHIVE_DIR}/shim-${VERSION}/gnu-efi"
|
||||
cd gnu-efi || exit 1
|
||||
git fetch
|
||||
if [ "x" = "x${GNUEFI_GIT_TAG}" ] ; then
|
||||
git archive --format=tar "$(git log -1 --pretty=format:%h)" | ( cd "${ARCHIVE_DIR}/shim-${VERSION}/gnu-efi" ; tar x )
|
||||
else
|
||||
git archive --format=tar "${ORIGIN}/${GNUEFI_GIT_TAG}" | ( cd "${ARCHIVE_DIR}/shim-${VERSION}/gnu-efi" ; tar x )
|
||||
git archive --format=tar "${GNUEFI_GIT_TAG}" | ( cd "${ARCHIVE_DIR}/shim-${VERSION}/gnu-efi" ; tar x )
|
||||
fi
|
||||
cd ..
|
||||
if [ "x" = "x${SHIM_GIT_TAG}" ] ; then
|
||||
|
@ -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
|
||||
|
550
memattrs.c
Normal file
550
memattrs.c
Normal file
@ -0,0 +1,550 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
/*
|
||||
* memattrs.c - EFI and DXE memory attribute helpers
|
||||
* Copyright Peter Jones <pjones@redhat.com>
|
||||
*/
|
||||
|
||||
#include "shim.h"
|
||||
|
||||
static inline uint64_t
|
||||
shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs)
|
||||
{
|
||||
uint64_t ret = EFI_MEMORY_RP |
|
||||
EFI_MEMORY_RO |
|
||||
EFI_MEMORY_XP;
|
||||
|
||||
if (attrs & MEM_ATTR_R)
|
||||
ret &= ~EFI_MEMORY_RP;
|
||||
|
||||
if (attrs & MEM_ATTR_W)
|
||||
ret &= ~EFI_MEMORY_RO;
|
||||
|
||||
if (attrs & MEM_ATTR_X)
|
||||
ret &= ~EFI_MEMORY_XP;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs)
|
||||
{
|
||||
uint64_t ret = MEM_ATTR_R |
|
||||
MEM_ATTR_W |
|
||||
MEM_ATTR_X;
|
||||
|
||||
if (attrs & EFI_MEMORY_RP)
|
||||
ret &= ~MEM_ATTR_R;
|
||||
|
||||
if (attrs & EFI_MEMORY_RO)
|
||||
ret &= ~MEM_ATTR_W;
|
||||
|
||||
if (attrs & EFI_MEMORY_XP)
|
||||
ret &= ~MEM_ATTR_X;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
get_dxe_services_table(EFI_DXE_SERVICES_TABLE **dstp)
|
||||
{
|
||||
static EFI_DXE_SERVICES_TABLE *dst = NULL;
|
||||
|
||||
if (dst == NULL) {
|
||||
dprint(L"Looking for configuration table " LGUID_FMT L"\n", GUID_ARGS(gEfiDxeServicesTableGuid));
|
||||
|
||||
for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) {
|
||||
EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i];
|
||||
dprint(L"Testing configuration table " LGUID_FMT L"\n", GUID_ARGS(ct->VendorGuid));
|
||||
if (CompareMem(&ct->VendorGuid, &gEfiDxeServicesTableGuid, sizeof(EFI_GUID)) != 0)
|
||||
continue;
|
||||
|
||||
dst = (EFI_DXE_SERVICES_TABLE *)ct->VendorTable;
|
||||
dprint(L"Looking for DXE Services Signature 0x%16llx, found signature 0x%16llx\n",
|
||||
EFI_DXE_SERVICES_TABLE_SIGNATURE, dst->Hdr.Signature);
|
||||
if (dst->Hdr.Signature != EFI_DXE_SERVICES_TABLE_SIGNATURE)
|
||||
continue;
|
||||
|
||||
if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) {
|
||||
/*
|
||||
* purposefully not treating this as an error so that HSIStatus
|
||||
* can tell us about it later.
|
||||
*/
|
||||
dprint(L"DXE Services lacks Get/SetMemorySpace* functions\n");
|
||||
}
|
||||
|
||||
dprint(L"Setting dxe_services_table to 0x%llx\n", dst);
|
||||
*dstp = dst;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
*dstp = dst;
|
||||
return;
|
||||
}
|
||||
|
||||
dst = NULL;
|
||||
dprint(L"Couldn't find DXE services\n");
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
dxe_get_mem_attrs(uintptr_t physaddr, size_t size, uint64_t *attrs)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc;
|
||||
EFI_PHYSICAL_ADDRESS start, end, next;
|
||||
EFI_DXE_SERVICES_TABLE *dst = NULL;
|
||||
|
||||
get_dxe_services_table(&dst);
|
||||
if (!dst)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) {
|
||||
dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n",
|
||||
__func__, (unsigned long long)physaddr,
|
||||
(unsigned long long)(physaddr+size-1),
|
||||
attrs);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
start = ALIGN_DOWN(physaddr, EFI_PAGE_SIZE);
|
||||
end = ALIGN_UP(physaddr + size, EFI_PAGE_SIZE);
|
||||
|
||||
for (; start < end; start = next) {
|
||||
status = dst->GetMemorySpaceDescriptor(start, &desc);
|
||||
if (EFI_ERROR(status)) {
|
||||
dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n",
|
||||
start, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
next = desc.BaseAddress + desc.Length;
|
||||
|
||||
if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY)
|
||||
continue;
|
||||
|
||||
*attrs = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
dxe_update_mem_attrs(uintptr_t addr, size_t size,
|
||||
uint64_t set_attrs, uint64_t clear_attrs)
|
||||
{
|
||||
#if 0
|
||||
EFI_STATUS status;
|
||||
EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc;
|
||||
EFI_PHYSICAL_ADDRESS start, end, next;
|
||||
uint64_t before = 0, after = 0, dxe_set_attrs, dxe_clear_attrs;
|
||||
#endif
|
||||
EFI_DXE_SERVICES_TABLE *dst = NULL;
|
||||
|
||||
get_dxe_services_table(&dst);
|
||||
if (!dst)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(size) || size == 0) {
|
||||
perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n",
|
||||
__func__, (unsigned long long)addr,
|
||||
(unsigned long long)(addr + size - 1),
|
||||
(unsigned long long)size,
|
||||
(set_attrs & MEM_ATTR_R) ? "r" : "",
|
||||
(set_attrs & MEM_ATTR_W) ? "w" : "",
|
||||
(set_attrs & MEM_ATTR_X) ? "x" : "",
|
||||
(clear_attrs & MEM_ATTR_R) ? "r" : "",
|
||||
(clear_attrs & MEM_ATTR_W) ? "w" : "",
|
||||
(clear_attrs & MEM_ATTR_X) ? "x" : "");
|
||||
if (!IS_PAGE_ALIGNED(addr))
|
||||
perror(L" addr is not page aligned\n");
|
||||
if (!IS_PAGE_ALIGNED(size))
|
||||
perror(L" size is not page aligned\n");
|
||||
if (size == 0)
|
||||
perror(L" size is 0\n");
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* We know this only works coincidentally, so nerfing it for now
|
||||
* until we have a chance to debug more thoroughly on these niche
|
||||
* systems.
|
||||
*/
|
||||
#if 0
|
||||
start = ALIGN_DOWN(addr, EFI_PAGE_SIZE);
|
||||
end = ALIGN_UP(addr + size, EFI_PAGE_SIZE);
|
||||
|
||||
for (; start < end; start = next) {
|
||||
EFI_PHYSICAL_ADDRESS mod_start;
|
||||
UINT64 mod_size;
|
||||
|
||||
status = dst->GetMemorySpaceDescriptor(start, &desc);
|
||||
if (EFI_ERROR(status)) {
|
||||
dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n",
|
||||
start, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
next = desc.BaseAddress + desc.Length;
|
||||
|
||||
if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY)
|
||||
continue;
|
||||
|
||||
mod_start = MAX(start, desc.BaseAddress);
|
||||
mod_size = MIN(end, next) - mod_start;
|
||||
|
||||
before = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes);
|
||||
dxe_set_attrs = shim_mem_attrs_to_uefi_mem_attrs(set_attrs);
|
||||
dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, dxe_set_attrs);
|
||||
dxe_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs(clear_attrs);
|
||||
dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, dxe_clear_attrs);
|
||||
desc.Attributes |= dxe_set_attrs;
|
||||
desc.Attributes &= ~dxe_clear_attrs;
|
||||
after = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes);
|
||||
|
||||
status = dst->SetMemorySpaceAttributes(mod_start, mod_size, desc.Attributes);
|
||||
if (EFI_ERROR(status)) {
|
||||
dprint(L"Failed to update memory attrs:0x%0x addr:0x%llx size:0x%0lx status:%r\n",
|
||||
desc.Attributes, mod_start, mod_size, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n",
|
||||
(set_attrs & MEM_ATTR_R) ? "r" : "",
|
||||
(set_attrs & MEM_ATTR_W) ? "w" : "",
|
||||
(set_attrs & MEM_ATTR_X) ? "x" : "",
|
||||
(clear_attrs & MEM_ATTR_R) ? "r" : "",
|
||||
(clear_attrs & MEM_ATTR_W) ? "w" : "",
|
||||
(clear_attrs & MEM_ATTR_X) ? "x" : "",
|
||||
(unsigned long long)addr, (unsigned long long)(addr + size - 1),
|
||||
(before & MEM_ATTR_R) ? 'r' : '-',
|
||||
(before & MEM_ATTR_W) ? 'w' : '-',
|
||||
(before & MEM_ATTR_X) ? 'x' : '-',
|
||||
(after & MEM_ATTR_R) ? 'r' : '-',
|
||||
(after & MEM_ATTR_W) ? 'w' : '-',
|
||||
(after & MEM_ATTR_X) ? 'x' : '-');
|
||||
#endif
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
get_efi_mem_attr_protocol(EFI_MEMORY_ATTRIBUTE_PROTOCOL **protop)
|
||||
{
|
||||
static EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL;
|
||||
static bool has_mem_access_proto = true;
|
||||
|
||||
if (proto == NULL && has_mem_access_proto == true) {
|
||||
EFI_STATUS efi_status;
|
||||
efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID,
|
||||
(VOID **)&proto);
|
||||
if (EFI_ERROR(efi_status) || !proto) {
|
||||
has_mem_access_proto = false;
|
||||
*protop = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*protop = proto;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
efi_get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs)
|
||||
{
|
||||
EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL;
|
||||
EFI_PHYSICAL_ADDRESS physaddr = addr;
|
||||
EFI_STATUS efi_status;
|
||||
|
||||
get_efi_mem_attr_protocol(&proto);
|
||||
if (!proto)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) {
|
||||
dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n",
|
||||
__func__, (unsigned long long)physaddr,
|
||||
(unsigned long long)(physaddr+size-1),
|
||||
attrs);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
dprint(L"GetMemoryAttributes(..., 0x%llx, 0x%x, 0x%x): %r\n",
|
||||
physaddr, size, attrs, efi_status);
|
||||
} else {
|
||||
*attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs);
|
||||
}
|
||||
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
efi_update_mem_attrs(uintptr_t addr, uint64_t size,
|
||||
uint64_t set_attrs, uint64_t clear_attrs)
|
||||
{
|
||||
EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL;
|
||||
EFI_PHYSICAL_ADDRESS physaddr = addr;
|
||||
EFI_STATUS efi_status, ret;
|
||||
uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs;
|
||||
|
||||
get_efi_mem_attr_protocol(&proto);
|
||||
if (!proto)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
efi_status = efi_get_mem_attrs(addr, size, &before);
|
||||
if (EFI_ERROR(efi_status))
|
||||
dprint(L"efi_get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n",
|
||||
(unsigned long long)addr, (unsigned long long)size,
|
||||
&before, efi_status);
|
||||
|
||||
if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0) {
|
||||
perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n",
|
||||
__func__, (unsigned long long)physaddr,
|
||||
(unsigned long long)(physaddr + size - 1),
|
||||
(unsigned long long)size,
|
||||
(set_attrs & MEM_ATTR_R) ? "r" : "",
|
||||
(set_attrs & MEM_ATTR_W) ? "w" : "",
|
||||
(set_attrs & MEM_ATTR_X) ? "x" : "",
|
||||
(clear_attrs & MEM_ATTR_R) ? "r" : "",
|
||||
(clear_attrs & MEM_ATTR_W) ? "w" : "",
|
||||
(clear_attrs & MEM_ATTR_X) ? "x" : "");
|
||||
if (!IS_PAGE_ALIGNED(physaddr))
|
||||
perror(L" addr is not page aligned\n");
|
||||
if (!IS_PAGE_ALIGNED(size))
|
||||
perror(L" size is not page aligned\n");
|
||||
if (size == 0)
|
||||
perror(L" size is 0\n");
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs);
|
||||
dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs);
|
||||
uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs);
|
||||
dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs);
|
||||
efi_status = EFI_SUCCESS;
|
||||
if (uefi_set_attrs) {
|
||||
efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
dprint(L"Failed to set memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n",
|
||||
uefi_set_attrs, physaddr, size, efi_status);
|
||||
}
|
||||
}
|
||||
if (!EFI_ERROR(efi_status) && uefi_clear_attrs) {
|
||||
efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
dprint(L"Failed to clear memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n",
|
||||
uefi_clear_attrs, physaddr, size, efi_status);
|
||||
}
|
||||
}
|
||||
ret = efi_status;
|
||||
|
||||
efi_status = efi_get_mem_attrs(addr, size, &after);
|
||||
if (EFI_ERROR(efi_status))
|
||||
dprint(L"efi_get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n",
|
||||
(unsigned long long)addr, (unsigned long long)size,
|
||||
&after, efi_status);
|
||||
|
||||
dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n",
|
||||
(set_attrs & MEM_ATTR_R) ? "r" : "",
|
||||
(set_attrs & MEM_ATTR_W) ? "w" : "",
|
||||
(set_attrs & MEM_ATTR_X) ? "x" : "",
|
||||
(clear_attrs & MEM_ATTR_R) ? "r" : "",
|
||||
(clear_attrs & MEM_ATTR_W) ? "w" : "",
|
||||
(clear_attrs & MEM_ATTR_X) ? "x" : "",
|
||||
(unsigned long long)addr, (unsigned long long)(addr + size - 1),
|
||||
(before & MEM_ATTR_R) ? 'r' : '-',
|
||||
(before & MEM_ATTR_W) ? 'w' : '-',
|
||||
(before & MEM_ATTR_X) ? 'x' : '-',
|
||||
(after & MEM_ATTR_R) ? 'r' : '-',
|
||||
(after & MEM_ATTR_W) ? 'w' : '-',
|
||||
(after & MEM_ATTR_X) ? 'x' : '-');
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
update_mem_attrs(uintptr_t addr, uint64_t size,
|
||||
uint64_t set_attrs, uint64_t clear_attrs)
|
||||
{
|
||||
EFI_STATUS efi_status;
|
||||
|
||||
efi_status = efi_update_mem_attrs(addr, size, set_attrs, clear_attrs);
|
||||
if (!EFI_ERROR(efi_status))
|
||||
return efi_status;
|
||||
|
||||
if (efi_status == EFI_UNSUPPORTED)
|
||||
efi_status = dxe_update_mem_attrs(addr, size, set_attrs, clear_attrs);
|
||||
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs)
|
||||
{
|
||||
EFI_STATUS efi_status;
|
||||
|
||||
efi_status = efi_get_mem_attrs(addr, size, attrs);
|
||||
if (!EFI_ERROR(efi_status))
|
||||
return efi_status;
|
||||
|
||||
if (efi_status == EFI_UNSUPPORTED)
|
||||
efi_status = dxe_get_mem_attrs(addr, size, attrs);
|
||||
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
char *
|
||||
decode_hsi_bits(UINTN hsi)
|
||||
{
|
||||
static const struct {
|
||||
UINTN bit;
|
||||
char name[16];
|
||||
} bits[] = {
|
||||
{.bit = SHIM_HSI_STATUS_HEAPX, .name = "HEAPX"},
|
||||
{.bit = SHIM_HSI_STATUS_STACKX, .name = "STACKX"},
|
||||
{.bit = SHIM_HSI_STATUS_ROW, .name = "ROW"},
|
||||
{.bit = SHIM_HSI_STATUS_HASMAP, .name = "HASMAP"},
|
||||
{.bit = SHIM_HSI_STATUS_HASDST, .name = "HASDST"},
|
||||
{.bit = SHIM_HSI_STATUS_HASDSTGMSD, .name = "HASDSTGMSD"},
|
||||
{.bit = SHIM_HSI_STATUS_HASDSTSMSA, .name = "HASDSTSMSA"},
|
||||
{.bit = SHIM_HSI_STATUS_NX, .name = "NX"},
|
||||
{.bit = 0, .name = ""},
|
||||
};
|
||||
static int x = 0;
|
||||
static char retbufs[2][sizeof(bits)];
|
||||
char *retbuf = &retbufs[x % 2][0];
|
||||
char *prev = &retbuf[0];
|
||||
char *pos = &retbuf[0];
|
||||
|
||||
x = ( x + 1 ) % 2;
|
||||
|
||||
ZeroMem(retbuf, sizeof(bits));
|
||||
|
||||
if (hsi == 0) {
|
||||
prev = stpcpy(retbuf, "0");
|
||||
} else {
|
||||
for (UINTN i = 0; bits[i].bit != 0; i++) {
|
||||
if (hsi & bits[i].bit) {
|
||||
prev = stpcpy(pos, bits[i].name);
|
||||
pos = stpcpy(prev, "|");
|
||||
}
|
||||
}
|
||||
}
|
||||
prev[0] = '\0';
|
||||
return retbuf;
|
||||
}
|
||||
|
||||
void
|
||||
get_hsi_mem_info(void)
|
||||
{
|
||||
EFI_STATUS efi_status;
|
||||
uintptr_t addr;
|
||||
uint64_t attrs = 0;
|
||||
uint32_t *tmp_alloc;
|
||||
EFI_MEMORY_ATTRIBUTE_PROTOCOL *efiproto = NULL;
|
||||
EFI_DXE_SERVICES_TABLE *dst = NULL;
|
||||
|
||||
get_efi_mem_attr_protocol(&efiproto);
|
||||
if (efiproto) {
|
||||
dprint(L"Setting HSI from %a to %a\n",
|
||||
decode_hsi_bits(hsi_status),
|
||||
decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASMAP));
|
||||
hsi_status |= SHIM_HSI_STATUS_HASMAP;
|
||||
}
|
||||
|
||||
get_dxe_services_table(&dst);
|
||||
if (dst) {
|
||||
dprint(L"Setting HSI from %a to %a\n",
|
||||
decode_hsi_bits(hsi_status),
|
||||
decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDST));
|
||||
hsi_status |= SHIM_HSI_STATUS_HASDST;
|
||||
if (dst->GetMemorySpaceDescriptor) {
|
||||
dprint(L"Setting HSI from %a to %a\n",
|
||||
decode_hsi_bits(hsi_status),
|
||||
decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTGMSD));
|
||||
hsi_status |= SHIM_HSI_STATUS_HASDSTGMSD;
|
||||
}
|
||||
if (dst->SetMemorySpaceAttributes) {
|
||||
dprint(L"Setting HSI from %a to %a\n",
|
||||
decode_hsi_bits(hsi_status),
|
||||
decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTSMSA));
|
||||
hsi_status |= SHIM_HSI_STATUS_HASDSTSMSA;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(hsi_status & SHIM_HSI_STATUS_HASMAP) &&
|
||||
!(hsi_status & SHIM_HSI_STATUS_HASDSTGMSD &&
|
||||
hsi_status & SHIM_HSI_STATUS_HASDSTSMSA)) {
|
||||
dprint(L"No memory protocol, not testing further\n");
|
||||
return;
|
||||
}
|
||||
|
||||
addr = ((uintptr_t)&get_hsi_mem_info) & ~EFI_PAGE_MASK;
|
||||
efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (attrs & MEM_ATTR_W) {
|
||||
dprint(L"get_hsi_mem_info() is on a writable page: %a->%a\n",
|
||||
decode_hsi_bits(hsi_status),
|
||||
decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_ROW));
|
||||
hsi_status |= SHIM_HSI_STATUS_ROW;
|
||||
}
|
||||
|
||||
addr = ((uintptr_t)&addr) & ~EFI_PAGE_MASK;
|
||||
efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (attrs & MEM_ATTR_X) {
|
||||
dprint(L"Stack variable is on an executable page: %a->%a\n",
|
||||
decode_hsi_bits(hsi_status),
|
||||
decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_STACKX));
|
||||
hsi_status |= SHIM_HSI_STATUS_STACKX;
|
||||
}
|
||||
|
||||
tmp_alloc = AllocatePool(EFI_PAGE_SIZE);
|
||||
if (!tmp_alloc) {
|
||||
dprint(L"Failed to allocate heap variable.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
addr = ((uintptr_t)tmp_alloc) & ~EFI_PAGE_MASK;
|
||||
efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs);
|
||||
FreePool(tmp_alloc);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE);
|
||||
goto error;
|
||||
}
|
||||
if (attrs & MEM_ATTR_X) {
|
||||
dprint(L"Heap variable is on an executable page: %a->%a\n",
|
||||
decode_hsi_bits(hsi_status),
|
||||
decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HEAPX));
|
||||
hsi_status |= SHIM_HSI_STATUS_HEAPX;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
/*
|
||||
* In this case we can't actually tell anything, so assume
|
||||
* and report the worst case scenario.
|
||||
*/
|
||||
hsi_status = SHIM_HSI_STATUS_HEAPX |
|
||||
SHIM_HSI_STATUS_STACKX |
|
||||
SHIM_HSI_STATUS_ROW;
|
||||
dprint(L"Setting HSI to 0x%lx due to error: %r\n", decode_hsi_bits(hsi_status), efi_status);
|
||||
}
|
||||
|
||||
// vim:fenc=utf-8:tw=75:noet
|
114
mock-variables.c
114
mock-variables.c
@ -163,7 +163,7 @@ variable_cmp(const struct mock_variable * const v0,
|
||||
|
||||
ret = CompareGuid(&v0->guid, &v1->guid);
|
||||
ret <<= 8ul;
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG > 3)
|
||||
printf("%s:%d:%s(): "GUID_FMT" %s "GUID_FMT" (0x%011"PRIx64" %"PRId64")\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
GUID_ARGS(v0->guid),
|
||||
@ -177,7 +177,7 @@ variable_cmp(const struct mock_variable * const v0,
|
||||
}
|
||||
|
||||
ret = StrCmp(v0->name, v1->name);
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG > 3)
|
||||
printf("%s:%d:%s(): \"%s\" %s \"%s\" (0x%02hhx (%d)\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
Str2str(v0->name),
|
||||
@ -284,7 +284,7 @@ mock_gnvn_set_result(UINTN *size, CHAR16 *name, EFI_GUID *guid,
|
||||
*size = StrSize(result->name);
|
||||
status = EFI_BUFFER_TOO_SMALL;
|
||||
mock_gnvn_post_hook(size, name, guid, &status);
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 3
|
||||
printf("%s:%d:%s(): returning %lx\n",
|
||||
__FILE__, __LINE__-1, __func__, status);
|
||||
#endif
|
||||
@ -297,7 +297,7 @@ mock_gnvn_set_result(UINTN *size, CHAR16 *name, EFI_GUID *guid,
|
||||
|
||||
status = EFI_SUCCESS;
|
||||
mock_gnvn_post_hook(size, name, guid, &status);
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 3
|
||||
printf("%s:%d:%s(): returning %lx\n",
|
||||
__FILE__, __LINE__-1, __func__, status);
|
||||
#endif
|
||||
@ -351,15 +351,20 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid)
|
||||
struct mock_variable *var;
|
||||
|
||||
var = list_entry(pos, struct mock_variable, list);
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG)
|
||||
# if SHIM_DEBUG > 1
|
||||
printf("%s:%d:%s(): candidate var:%p &var->guid:%p &var->list:%p\n",
|
||||
__FILE__, __LINE__-1, __func__, var, &var->guid, &var->list);
|
||||
# elif SHIM_DEBUG > 0
|
||||
printf("%s:%d:%s(): candidate var:%p var->guid:" GUID_FMT"\n",
|
||||
__FILE__, __LINE__-1, __func__, var, GUID_ARGS(var->guid));
|
||||
# endif
|
||||
#endif
|
||||
if (name[0] == 0) {
|
||||
if (CompareGuid(&var->guid, guid) == 0) {
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
printf("%s:%d:%s(): found\n",
|
||||
__FILE__, __LINE__-1, __func__);
|
||||
printf("%s:%d:%s(): found guid in entry var:%p var->name:%p\n",
|
||||
__FILE__, __LINE__-1, __func__, var, var->name);
|
||||
#endif
|
||||
result = var;
|
||||
found = true;
|
||||
@ -374,14 +379,14 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid)
|
||||
continue;
|
||||
}
|
||||
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): varcmp("GUID_FMT"-%s, "GUID_FMT"-%s)\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
GUID_ARGS(goal.guid), Str2str(goal.name),
|
||||
GUID_ARGS(var->guid), Str2str(var->name));
|
||||
#endif
|
||||
if (variable_cmp(&goal, var) == 0) {
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): found\n",
|
||||
__FILE__, __LINE__-1, __func__);
|
||||
#endif
|
||||
@ -391,15 +396,15 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid)
|
||||
}
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
if (result) {
|
||||
printf("%s:%d:%s(): found:%d result:%p &result->guid:%p &result->list:%p\n"
|
||||
printf("%s:%d:%s(): found:%d result:%p &result->guid:%p &result->list:%p\n",
|
||||
__FILE__, __LINE__-1, __func__, found, result,
|
||||
&result->guid, &result->list);
|
||||
printf("%s:%d:%s(): "GUID_FMT"-%s\n",
|
||||
__FILE__, __LINE__-1, __func__, GUID_ARGS(result->guid),
|
||||
Str2str(result->name));
|
||||
} else {
|
||||
printf("%s:%d:%s(): not found\n",
|
||||
__FILE__, __LINE__-1, __func__);
|
||||
printf("%s:%d:%s(): not found (found:%d status:0x%016x)\n",
|
||||
__FILE__, __LINE__-1, __func__, found, status);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -408,13 +413,25 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid)
|
||||
status = EFI_NOT_FOUND;
|
||||
else
|
||||
status = EFI_INVALID_PARAMETER;
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): not found (found:%d status:0x%016x)\n",
|
||||
__FILE__, __LINE__-1, __func__, found, status);
|
||||
#endif
|
||||
mock_gnvn_post_hook(size, name, guid, &status);
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): not found (found:%d status:0x%016x)\n",
|
||||
__FILE__, __LINE__-1, __func__, found, status);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
status = EFI_NOT_FOUND;
|
||||
mock_gnvn_post_hook(size, name, guid, &status);
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): found (found:%d status:0x%016x)\n",
|
||||
__FILE__, __LINE__-1, __func__, found, status);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -678,7 +695,7 @@ mock_new_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size,
|
||||
}
|
||||
var = (struct mock_variable *)buf;
|
||||
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): var:%p &var->guid:%p &var->list:%p\n",
|
||||
__FILE__, __LINE__-1, __func__, var, &var->guid, &var->list);
|
||||
#endif
|
||||
@ -695,7 +712,7 @@ mock_new_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size,
|
||||
var->attrs = attrs;
|
||||
INIT_LIST_HEAD(&var->list);
|
||||
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): var: "GUID_FMT"-%s\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
GUID_ARGS(var->guid), Str2str(var->name));
|
||||
@ -772,10 +789,10 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
printf("%s:%d:%s():Setting "GUID_FMT"-%s\n",
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s():Setting "GUID_FMT"-%s size:0x%"PRIx64"\n",
|
||||
__FILE__, __LINE__ - 1, __func__,
|
||||
GUID_ARGS(*guid), Str2str(name));
|
||||
GUID_ARGS(*guid), Str2str(name), size);
|
||||
#endif
|
||||
switch (mock_variable_sort_policy) {
|
||||
case MOCK_SORT_PREPEND:
|
||||
@ -800,7 +817,7 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size,
|
||||
list_for_each_safe(pos, tmp, &mock_variables) {
|
||||
found = false;
|
||||
var = list_entry(pos, struct mock_variable, list);
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): varcmp("GUID_FMT"-%s, "GUID_FMT"-%s)\n",
|
||||
__FILE__, __LINE__-1, __func__,
|
||||
GUID_ARGS(goal.guid), Str2str(goal.name),
|
||||
@ -832,32 +849,32 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size,
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s():var_list:%p &mock_variables:%p cmp:%ld\n",
|
||||
__FILE__, __LINE__ - 1, __func__,
|
||||
var_list, &mock_variables, cmp);
|
||||
#endif
|
||||
if (cmp != 0 || (cmp == 0 && var_list == &mock_variables)) {
|
||||
size_t totalsz = size + StrSize(name);
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s():var:%p attrs:0x%lx\n",
|
||||
__FILE__, __LINE__ - 1, __func__, var, attrs);
|
||||
#endif
|
||||
status = mock_new_variable(name, guid, attrs, size, data, &var);
|
||||
status = mock_sv_adjust_usage_data(attrs, size, -totalsz);
|
||||
if (EFI_ERROR(status)) {
|
||||
mock_sv_post_hook(name, guid, attrs, size, data,
|
||||
&status, CREATE);
|
||||
return status;
|
||||
}
|
||||
mock_sv_adjust_usage_data(attrs, size, totalsz);
|
||||
status = mock_new_variable(name, guid, attrs, size, data, &var);
|
||||
mock_sv_post_hook(name, guid, attrs, size, data,
|
||||
&status, CREATE);
|
||||
if (EFI_ERROR(status)) {
|
||||
mock_sv_adjust_usage_data(attrs, 0, -totalsz);
|
||||
mock_sv_adjust_usage_data(attrs, 0, totalsz);
|
||||
return status;
|
||||
}
|
||||
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1
|
||||
printf("%s:%d:%s(): Adding "GUID_FMT"-%s %s %s\n",
|
||||
__FILE__, __LINE__ - 1, __func__,
|
||||
GUID_ARGS(var->guid), Str2str(var->name),
|
||||
@ -1001,6 +1018,20 @@ static struct mock_variable_limits default_limits[] = {
|
||||
{.attrs = 0, }
|
||||
};
|
||||
|
||||
void
|
||||
mock_set_usage_limits(list_t *limit_list,
|
||||
struct mock_variable_limits *limits)
|
||||
{
|
||||
INIT_LIST_HEAD(limit_list);
|
||||
for (size_t i = 0; limits[i].attrs != 0; i++) {
|
||||
INIT_LIST_HEAD(&limits[i].list);
|
||||
list_add_tail(&limits[i].list, limit_list);
|
||||
}
|
||||
|
||||
mock_qvi_limits = limit_list;
|
||||
mock_sv_limits = limit_list;
|
||||
}
|
||||
|
||||
void
|
||||
mock_set_default_usage_limits(void)
|
||||
{
|
||||
@ -1008,12 +1039,7 @@ mock_set_default_usage_limits(void)
|
||||
default_remaining_var_storage = 65536;
|
||||
default_max_var_size = 32768;
|
||||
|
||||
INIT_LIST_HEAD(&mock_default_variable_limits);
|
||||
for (size_t i = 0; default_limits[i].attrs != 0; i++) {
|
||||
INIT_LIST_HEAD(&default_limits[i].list);
|
||||
list_add_tail(&default_limits[i].list,
|
||||
&mock_default_variable_limits);
|
||||
}
|
||||
mock_set_usage_limits(&mock_default_variable_limits, &default_limits[0]);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1079,7 +1105,8 @@ mock_load_one_variable(int dfd, const char * const dirname, char * const name)
|
||||
|
||||
name[namelen-1] = 0;
|
||||
#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0)
|
||||
printf("loading %s-%s\n", &name[namelen], name);
|
||||
printf("%s:%d:%s(): loading %s-%s\n", __FILE__, __LINE__, __func__,
|
||||
&name[namelen], name);
|
||||
#endif
|
||||
for (size_t i = 0; i < namelen; i++)
|
||||
namebuf[i] = name[i];
|
||||
@ -1109,6 +1136,9 @@ mock_load_variables(const char *const dirname, const char *filters[],
|
||||
DIR *d;
|
||||
struct dirent *entry;
|
||||
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1
|
||||
printf("Started loading variablles from \"%s\"\n", dirname);
|
||||
#endif
|
||||
d = opendir(dirname);
|
||||
if (!d)
|
||||
err(1, "Could not open directory \"%s\"", dirname);
|
||||
@ -1121,6 +1151,11 @@ mock_load_variables(const char *const dirname, const char *filters[],
|
||||
while ((entry = readdir(d)) != NULL) {
|
||||
size_t len = strlen(entry->d_name);
|
||||
bool found = false;
|
||||
if (entry->d_type != DT_REG)
|
||||
continue;
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1
|
||||
printf("%s:%d:%s(): maybe adding entry \"%s\"\n", __FILE__, __LINE__, __func__, entry->d_name);
|
||||
#endif
|
||||
if (filters && len > guidstr_size + 1) {
|
||||
char spacebuf[len];
|
||||
|
||||
@ -1131,6 +1166,9 @@ mock_load_variables(const char *const dirname, const char *filters[],
|
||||
if (strlen(filters[i]) > len)
|
||||
continue;
|
||||
if (!strncmp(entry->d_name, filters[i], len)) {
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2
|
||||
printf("%s:%d:%s(): filter matched for \"%s\" && \"%s\"\n", __FILE__, __LINE__, __func__, entry->d_name, filters[i]);
|
||||
#endif
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -1138,9 +1176,23 @@ mock_load_variables(const char *const dirname, const char *filters[],
|
||||
}
|
||||
if ((found == false && filter_out == true) ||
|
||||
(found == true && filter_out == false)) {
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1
|
||||
printf("%s:%d:%s(): Adding \"%s\" because filter %s\n",
|
||||
__FILE__, __LINE__-1, __func__, entry->d_name,
|
||||
found ? "matched" : "did not match");
|
||||
#endif
|
||||
mock_load_one_variable(dfd, dirname, entry->d_name);
|
||||
} else {
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1
|
||||
printf("%s:%d:%s(): Skipping \"%s\" because filter %s\n",
|
||||
__FILE__, __LINE__-1, __func__, entry->d_name,
|
||||
found ? "matched" : "did not match");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1
|
||||
printf("Done loading variablles from \"%s\"\n", dirname);
|
||||
#endif
|
||||
|
||||
closedir(d);
|
||||
#if 0
|
||||
|
363
mok.c
363
mok.c
@ -34,6 +34,58 @@ static BOOLEAN check_var(CHAR16 *varname)
|
||||
efi_status_; \
|
||||
})
|
||||
|
||||
static UINTN
|
||||
format_hsi_status(UINT8 *buf, size_t sz,
|
||||
struct mok_state_variable *msv UNUSED)
|
||||
{
|
||||
const char heapx[] = "heap-is-executable: ";
|
||||
const char stackx[] = "\nstack-is-executable: ";
|
||||
const char row[] = "\nro-sections-are-writable: ";
|
||||
const char hasmap[] = "\nhas-memory-attribute-protocol: ";
|
||||
const char hasdxeservices[] = "\nhas-dxe-services-table: ";
|
||||
const char hasdsgmsd[] = "\nhas-get-memory-space-descriptor: ";
|
||||
const char hasdssmsa[] = "\nhas-set-memory-space-attributes: ";
|
||||
const char shimhasnx[] = "\nshim-has-nx-compat-set: ";
|
||||
const char finale[] = "\n";
|
||||
char *pos;
|
||||
|
||||
/*
|
||||
* sizeof includes the trailing NUL which is where our 0 or 1 value
|
||||
* fits
|
||||
*/
|
||||
UINTN ret = sizeof(heapx) + sizeof(stackx) +
|
||||
sizeof(row) + sizeof(hasmap) +
|
||||
sizeof(hasdxeservices) + sizeof(hasdsgmsd) +
|
||||
sizeof(hasdssmsa) + sizeof(shimhasnx) +
|
||||
sizeof(finale);
|
||||
|
||||
if (buf == 0 || sz < ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf[0] = 0;
|
||||
pos = (char *)buf;
|
||||
pos = stpcpy(pos, heapx);
|
||||
pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HEAPX) ? "1" : "0");
|
||||
pos = stpcpy(pos, stackx);
|
||||
pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_STACKX) ? "1" : "0");
|
||||
pos = stpcpy(pos, row);
|
||||
pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_ROW) ? "1" : "0");
|
||||
pos = stpcpy(pos, hasmap);
|
||||
pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASMAP) ? "1" : "0");
|
||||
pos = stpcpy(pos, hasdxeservices);
|
||||
pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDST) ? "1" : "0");
|
||||
pos = stpcpy(pos, hasdsgmsd);
|
||||
pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDSTGMSD) ? "1" : "0");
|
||||
pos = stpcpy(pos, hasdssmsa);
|
||||
pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDSTSMSA) ? "1" : "0");
|
||||
pos = stpcpy(pos, shimhasnx);
|
||||
pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_NX) ? "1" : "0");
|
||||
stpcpy(pos, finale);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the OS has set any of these variables we need to drop into MOK and
|
||||
* handle them appropriately
|
||||
@ -50,6 +102,32 @@ static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
|
||||
efi_status = start_image(image_handle, MOK_MANAGER);
|
||||
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
/*
|
||||
* We don't do this in the unit tests because we
|
||||
* don't have simulation for console_countdown()
|
||||
* and similar.
|
||||
*/
|
||||
#ifndef SHIM_UNIT_TEST
|
||||
EFI_STATUS efi_status_2;
|
||||
EFI_LOADED_IMAGE *li;
|
||||
efi_status_2 = BS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID,
|
||||
(void **)&li);
|
||||
if (EFI_ERROR(efi_status_2))
|
||||
perror (L"Failed to get image: %r\n", efi_status_2);
|
||||
else if (is_removable_media_path(li) &&
|
||||
efi_status == EFI_NOT_FOUND) {
|
||||
CHAR16 *title = L"Could not find MokManager";
|
||||
CHAR16 *message = L"MokManager is missing on removable media.";
|
||||
/*
|
||||
* This occurs when system is booting on
|
||||
* hard disk's EFI/BOOT/BOOTxxx.EFI entry
|
||||
* while it should have booted on
|
||||
* EFI/<os>/shimxxx.efi entry
|
||||
*/
|
||||
console_countdown(title, message, 10);
|
||||
RT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL);
|
||||
}
|
||||
#endif
|
||||
perror(L"Failed to start MokManager: %r\n", efi_status);
|
||||
return efi_status;
|
||||
}
|
||||
@ -80,12 +158,6 @@ categorize_deauthorized(struct mok_state_variable *v)
|
||||
return VENDOR_ADDEND_DB;
|
||||
}
|
||||
|
||||
#define MOK_MIRROR_KEYDB 0x01
|
||||
#define MOK_MIRROR_DELETE_FIRST 0x02
|
||||
#define MOK_VARIABLE_MEASURE 0x04
|
||||
#define MOK_VARIABLE_LOG 0x08
|
||||
#define MOK_VARIABLE_INVERSE 0x10
|
||||
|
||||
struct mok_state_variable mok_state_variable_data[] = {
|
||||
{.name = L"MokList",
|
||||
.name8 = "MokList",
|
||||
@ -196,6 +268,161 @@ struct mok_state_variable mok_state_variable_data[] = {
|
||||
.pcr = 14,
|
||||
.state = &mok_policy,
|
||||
},
|
||||
{.name = L"HSIStatus",
|
||||
.name8 = "HSIStatus",
|
||||
.rtname = L"HSIStatus",
|
||||
.rtname8 = "HSIStatus",
|
||||
.guid = &SHIM_LOCK_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
.format = format_hsi_status,
|
||||
},
|
||||
{.name = L"AuditMode",
|
||||
.name8 = "AuditMode",
|
||||
.rtname = L"AuditMode",
|
||||
.rtname8 = "AuditMode",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"BootOrder",
|
||||
.name8 = "BootOrder",
|
||||
.rtname = L"BootOrder",
|
||||
.rtname8 = "BootOrder",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"BootCurrent",
|
||||
.name8 = "BootCurrent",
|
||||
.rtname = L"BootCurrent",
|
||||
.rtname8 = "BootCurrent",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"BootNext",
|
||||
.name8 = "BootNext",
|
||||
.rtname = L"BootNext",
|
||||
.rtname8 = "BootNext",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"Boot0000",
|
||||
.name8 = "Boot0000",
|
||||
.rtname = L"Boot0000",
|
||||
.rtname8 = "Boot0000",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"Boot0001",
|
||||
.name8 = "Boot0001",
|
||||
.rtname = L"Boot0001",
|
||||
.rtname8 = "Boot0001",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"Boot0002",
|
||||
.name8 = "Boot0002",
|
||||
.rtname = L"Boot0002",
|
||||
.rtname8 = "Boot0002",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"Boot0003",
|
||||
.name8 = "Boot0003",
|
||||
.rtname = L"Boot0003",
|
||||
.rtname8 = "Boot0003",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"Boot0004",
|
||||
.name8 = "Boot0004",
|
||||
.rtname = L"Boot0004",
|
||||
.rtname8 = "Boot0004",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"Boot0005",
|
||||
.name8 = "Boot0005",
|
||||
.rtname = L"Boot0005",
|
||||
.rtname8 = "Boot0005",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"Boot0006",
|
||||
.name8 = "Boot0006",
|
||||
.rtname = L"Boot0006",
|
||||
.rtname8 = "Boot0006",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"DeployedMode",
|
||||
.name8 = "DeployedMode",
|
||||
.rtname = L"DeployedMode",
|
||||
.rtname8 = "DeployedMode",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"SecureBoot",
|
||||
.name8 = "SecureBoot",
|
||||
.rtname = L"SecureBoot",
|
||||
.rtname8 = "SecureBoot",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"SetupMode",
|
||||
.name8 = "SetupMode",
|
||||
.rtname = L"SetupMode",
|
||||
.rtname8 = "SetupMode",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"SignatureSupport",
|
||||
.name8 = "SignatureSupport",
|
||||
.rtname = L"SignatureSupport",
|
||||
.rtname8 = "SignatureSupport",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"Timeout",
|
||||
.name8 = "Timeout",
|
||||
.rtname = L"Timeout",
|
||||
.rtname8 = "Timeout",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"PK",
|
||||
.name8 = "PK",
|
||||
.rtname = L"PK",
|
||||
.rtname8 = "PK",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"KEK",
|
||||
.name8 = "KEK",
|
||||
.rtname = L"KEK",
|
||||
.rtname8 = "KEK",
|
||||
.guid = &GV_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"db",
|
||||
.name8 = "db",
|
||||
.rtname = L"db",
|
||||
.rtname8 = "db",
|
||||
.guid = &SIG_DB,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"dbx",
|
||||
.name8 = "dbx",
|
||||
.rtname = L"dbx",
|
||||
.rtname8 = "dbx",
|
||||
.guid = &SIG_DB,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{.name = L"Kernel_SkuSiStatus",
|
||||
.name8 = "Kernel_SkuSiStatus",
|
||||
.rtname = L"Kernel_SkuSiStatus",
|
||||
.rtname8 = "Kernel_SkuSiStatus",
|
||||
.guid = &SECUREBOOT_EFI_NAMESPACE_GUID,
|
||||
.flags = MOK_VARIABLE_CONFIG_ONLY,
|
||||
},
|
||||
{ NULL, }
|
||||
};
|
||||
size_t n_mok_state_variables = sizeof(mok_state_variable_data) / sizeof(mok_state_variable_data[0]);
|
||||
@ -217,6 +444,47 @@ typedef UINTN SIZE_T;
|
||||
#define EFI_MAJOR_VERSION(tablep) ((UINT16)((((tablep)->Hdr.Revision) >> 16) & 0xfffful))
|
||||
#define EFI_MINOR_VERSION(tablep) ((UINT16)(((tablep)->Hdr.Revision) & 0xfffful))
|
||||
|
||||
static BOOLEAN is_apple_firmware_vendor(void)
|
||||
{
|
||||
CHAR16 vendorbuf[6] = L"";
|
||||
CHAR16 *vendor = ST->FirmwareVendor;
|
||||
if (!vendor)
|
||||
return FALSE;
|
||||
|
||||
ZeroMem(vendorbuf, sizeof(vendorbuf));
|
||||
|
||||
/*
|
||||
* We've had a problem where ST->FirmwareVendor is only as big as
|
||||
* it needs to be (or at least less than the 200 bytes we formerly
|
||||
* defined vendorbuf as) and it's up against a page that's not
|
||||
* mapped readable, so we take a fault and reset when copying from
|
||||
* it.
|
||||
*
|
||||
* We modeled this after kernel, which has the 200 byte CHAR16
|
||||
* array and copies 198 bytes into it, so that there's a NUL
|
||||
* terminator. They solve this issue by mapping the whole 200
|
||||
* bytes unconditionally and then unmapping it after the copy, but
|
||||
* we can't take that approach because we don't necessarily have
|
||||
* page permission primitives at all.
|
||||
*
|
||||
* The 200 bytes (CHAR16 [100]) is an arbitrary number anyway, but
|
||||
* it's likely larger than any sane vendor name, and we still want
|
||||
* to do the copy into an array larger than our copied data because
|
||||
* that's how we guard against failure to terminate with a NUL.
|
||||
*
|
||||
* So right now we're only copying ten bytes, because Apple is the
|
||||
* only vendor we're testing against.
|
||||
*/
|
||||
CopyMem(vendorbuf, vendor, 10);
|
||||
|
||||
dprint(L"FirmwareVendor: \"%s\"\n", vendor);
|
||||
|
||||
if (StrnCmp(vendor, L"Apple", 5) == 0)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
get_max_var_sz(UINT32 attrs, SIZE_T *max_var_szp)
|
||||
{
|
||||
@ -226,7 +494,7 @@ get_max_var_sz(UINT32 attrs, SIZE_T *max_var_szp)
|
||||
uint64_t max_var_sz = 0;
|
||||
|
||||
*max_var_szp = 0;
|
||||
if (EFI_MAJOR_VERSION(RT) < 2) {
|
||||
if (EFI_MAJOR_VERSION(RT) < 2 || is_apple_firmware_vendor()) {
|
||||
dprint(L"EFI %d.%d; no RT->QueryVariableInfo(). Using 1024!\n",
|
||||
EFI_MAJOR_VERSION(RT), EFI_MINOR_VERSION(RT));
|
||||
max_var_sz = remaining_sz = max_storage_sz = 1024;
|
||||
@ -310,7 +578,7 @@ mirror_one_esl(CHAR16 *name, EFI_GUID *guid, UINT32 attrs,
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs,
|
||||
mirror_mok_db(CHAR16 *name, EFI_GUID *guid, UINT32 attrs,
|
||||
UINT8 *FullData, SIZE_T FullDataSize, BOOLEAN only_first)
|
||||
{
|
||||
EFI_STATUS efi_status = EFI_SUCCESS;
|
||||
@ -336,15 +604,13 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs,
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
CHAR16 *namen;
|
||||
CHAR8 *namen8;
|
||||
CHAR16 *namen = NULL;
|
||||
UINTN namelen, namesz;
|
||||
|
||||
namelen = StrLen(name);
|
||||
namesz = namelen * 2;
|
||||
if (only_first) {
|
||||
namen = name;
|
||||
namen8 = name8;
|
||||
} else {
|
||||
namelen += 18;
|
||||
namesz += 34;
|
||||
@ -353,12 +619,6 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs,
|
||||
LogError(L"Could not allocate %lu bytes", namesz);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
namen8 = AllocateZeroPool(namelen);
|
||||
if (!namen8) {
|
||||
FreePool(namen);
|
||||
LogError(L"Could not allocate %lu bytes", namelen);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
UINTN pos, i;
|
||||
@ -400,11 +660,6 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs,
|
||||
if (!only_first) {
|
||||
SPrint(namen, namelen, L"%s%lu", name, i);
|
||||
namen[namelen-1] = 0;
|
||||
/* uggggh */
|
||||
UINTN j;
|
||||
for (j = 0; j < namelen; j++)
|
||||
namen8[j] = (CHAR8)(namen[j] & 0xff);
|
||||
namen8[namelen - 1] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -417,7 +672,6 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs,
|
||||
efi_status);
|
||||
if (!only_first) {
|
||||
FreePool(namen);
|
||||
FreePool(namen8);
|
||||
}
|
||||
return efi_status;
|
||||
}
|
||||
@ -472,6 +726,9 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs,
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
if (namen && namen != name) {
|
||||
FreePool(namen);
|
||||
}
|
||||
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
perror(L"Failed to set %s: %r\n", name, efi_status);
|
||||
@ -515,6 +772,7 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
EFI_STATUS efi_status = EFI_SUCCESS;
|
||||
uint8_t *FullData = NULL;
|
||||
size_t FullDataSize = 0;
|
||||
bool allocated_full_data = false;
|
||||
vendor_addend_category_t addend_category = VENDOR_ADDEND_NONE;
|
||||
uint8_t *p = NULL;
|
||||
uint32_t attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
@ -579,6 +837,7 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
if (efi_status != EFI_BUFFER_TOO_SMALL) {
|
||||
perror(L"Could not add built-in cert to %s: %r\n",
|
||||
v->name, efi_status);
|
||||
goto err;
|
||||
return efi_status;
|
||||
}
|
||||
FullDataSize += addend_esl_sz;
|
||||
@ -663,6 +922,7 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
FullDataSize, v->name);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
allocated_full_data = true;
|
||||
p = FullData;
|
||||
}
|
||||
}
|
||||
@ -692,7 +952,7 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
perror(L"Could not add built-in cert to %s: %r\n",
|
||||
v->name, efi_status);
|
||||
return efi_status;
|
||||
goto err;
|
||||
}
|
||||
p += addend_esl_sz;
|
||||
dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
|
||||
@ -719,7 +979,7 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
perror(L"Could not add built-in cert to %s: %r\n",
|
||||
v->name, efi_status);
|
||||
return efi_status;
|
||||
goto err;
|
||||
}
|
||||
p += build_cert_esl_sz;
|
||||
dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
|
||||
@ -758,7 +1018,7 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
perror(L"Failed to allocate %lu bytes for %s\n",
|
||||
FullDataSize, v->name);
|
||||
return efi_status;
|
||||
goto err;
|
||||
}
|
||||
p = FullData + FullDataSize;
|
||||
dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
|
||||
@ -767,15 +1027,17 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
|
||||
dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n",
|
||||
FullDataSize, FullData, p, p-(uintptr_t)FullData);
|
||||
if (FullDataSize && v->flags & MOK_MIRROR_KEYDB) {
|
||||
if (FullDataSize && v->flags & MOK_MIRROR_KEYDB &&
|
||||
!(v->flags & MOK_VARIABLE_CONFIG_ONLY)) {
|
||||
dprint(L"calling mirror_mok_db(\"%s\", datasz=%lu)\n",
|
||||
v->rtname, FullDataSize);
|
||||
efi_status = mirror_mok_db(v->rtname, (CHAR8 *)v->rtname8, v->guid,
|
||||
efi_status = mirror_mok_db(v->rtname, v->guid,
|
||||
attrs, FullData, FullDataSize,
|
||||
only_first);
|
||||
dprint(L"mirror_mok_db(\"%s\", datasz=%lu) returned %r\n",
|
||||
v->rtname, FullDataSize, efi_status);
|
||||
} else if (FullDataSize && only_first) {
|
||||
} else if (FullDataSize && only_first &&
|
||||
!(v->flags & MOK_VARIABLE_CONFIG_ONLY)) {
|
||||
efi_status = SetVariable(v->rtname, v->guid, attrs,
|
||||
FullDataSize, FullData);
|
||||
}
|
||||
@ -789,7 +1051,7 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
dprint(L"tpm_measure_variable(\"%s\",%lu,0x%llx)->%r\n",
|
||||
v->name, FullDataSize, FullData, efi_status);
|
||||
return efi_status;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
@ -806,7 +1068,7 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
dprint(L"tpm_log_event(0x%llx, %lu, %lu, \"%s\")->%r\n",
|
||||
FullData, FullDataSize, v->pcr, v->name,
|
||||
efi_status);
|
||||
return efi_status;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
@ -820,6 +1082,10 @@ mirror_one_mok_variable(struct mok_state_variable *v,
|
||||
v->data_size = FullDataSize;
|
||||
dprint(L"returning %r\n", efi_status);
|
||||
return efi_status;
|
||||
err:
|
||||
if (FullData && allocated_full_data)
|
||||
FreePool(FullData);
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -871,7 +1137,8 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v,
|
||||
|
||||
dprint(L"importing mok state for \"%s\"\n", v->name);
|
||||
|
||||
if (!v->data && !v->data_size) {
|
||||
if (!v->data && !v->data_size &&
|
||||
!(v->flags & MOK_VARIABLE_CONFIG_ONLY)) {
|
||||
efi_status = get_variable_attr(v->name,
|
||||
&v->data, &v->data_size,
|
||||
*v->guid, &attrs);
|
||||
@ -913,6 +1180,36 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (v->format) {
|
||||
v->data_size = v->format(NULL, 0, v);
|
||||
if (v->data_size > 0) {
|
||||
v->data = AllocatePool(v->data_size);
|
||||
if (!v->data) {
|
||||
perror(L"Could not allocate %lu bytes for %s\n",
|
||||
v->data_size, v->name);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
}
|
||||
v->format(v->data, v->data_size, v);
|
||||
}
|
||||
|
||||
if (!v->data && !v->data_size &&
|
||||
(v->flags & MOK_VARIABLE_CONFIG_ONLY) &&
|
||||
!v->format) {
|
||||
efi_status = get_variable_attr(v->name,
|
||||
&v->data, &v->data_size,
|
||||
*v->guid, &attrs);
|
||||
if (EFI_ERROR(efi_status)) {
|
||||
dprint(L"Couldn't get variable \"%s\" for mirroring: %r\n",
|
||||
v->name, efi_status);
|
||||
if (efi_status != EFI_NOT_FOUND)
|
||||
return efi_status;
|
||||
v->data = NULL;
|
||||
v->data_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (delete == TRUE) {
|
||||
perror(L"Deleting bad variable %s\n", v->name);
|
||||
efi_status = LibDeleteVariable(v->name, v->guid);
|
||||
@ -1004,6 +1301,8 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle)
|
||||
config_table = NULL;
|
||||
} else {
|
||||
ZeroMem(config_table, npages << EFI_PAGE_SHIFT);
|
||||
mok_config_table = (EFI_PHYSICAL_ADDRESS)(uintptr_t)config_table;
|
||||
mok_config_table_pages = npages;
|
||||
}
|
||||
}
|
||||
|
||||
|
64
netboot.c
64
netboot.c
@ -16,6 +16,16 @@
|
||||
#define ntohs(x) __builtin_bswap16(x) /* supported both by GCC and clang */
|
||||
#define htons(x) ntohs(x)
|
||||
|
||||
/* TFTP error codes from RFC 1350 */
|
||||
#define TFTP_ERROR_NOT_DEFINED 0 /* Not defined, see error message (if any). */
|
||||
#define TFTP_ERROR_NOT_FOUND 1 /* File not found. */
|
||||
#define TFTP_ERROR_ACCESS 2 /* Access violation. */
|
||||
#define TFTP_ERROR_NO_SPACE 3 /* Disk full or allocation exceeded. */
|
||||
#define TFTP_ERROR_ILLEGAL_OP 4 /* Illegal TFTP operation. */
|
||||
#define TFTP_ERROR_UNKNOWN_ID 5 /* Unknown transfer ID. */
|
||||
#define TFTP_ERROR_EXISTS 6 /* File already exists. */
|
||||
#define TFTP_ERROR_NO_USER 7 /* No such user. */
|
||||
|
||||
static EFI_PXE_BASE_CODE *pxe;
|
||||
static EFI_IP_ADDRESS tftp_addr;
|
||||
static CHAR8 *full_path;
|
||||
@ -333,7 +343,33 @@ EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle UNUSED, CHAR8 *netbootname)
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, UINT64 *bufsiz)
|
||||
/* Convert a TFTP error code to an EFI status code. */
|
||||
static EFI_STATUS
|
||||
status_from_error(UINT8 error_code)
|
||||
{
|
||||
switch (error_code) {
|
||||
case TFTP_ERROR_NOT_FOUND:
|
||||
return EFI_NOT_FOUND;
|
||||
case TFTP_ERROR_ACCESS:
|
||||
case TFTP_ERROR_NO_USER:
|
||||
return EFI_ACCESS_DENIED;
|
||||
case TFTP_ERROR_NO_SPACE:
|
||||
return EFI_VOLUME_FULL;
|
||||
case TFTP_ERROR_ILLEGAL_OP:
|
||||
return EFI_PROTOCOL_ERROR;
|
||||
case TFTP_ERROR_UNKNOWN_ID:
|
||||
return EFI_INVALID_PARAMETER;
|
||||
case TFTP_ERROR_EXISTS:
|
||||
return EFI_WRITE_PROTECTED;
|
||||
case TFTP_ERROR_NOT_DEFINED:
|
||||
default:
|
||||
/* Use a generic TFTP error for anything else. */
|
||||
return EFI_TFTP_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer,
|
||||
UINT64 *bufsiz, int flags)
|
||||
{
|
||||
EFI_STATUS efi_status;
|
||||
EFI_PXE_BASE_CODE_TFTP_OPCODE read = EFI_PXE_BASE_CODE_TFTP_READ_FILE;
|
||||
@ -341,6 +377,7 @@ EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, UINT
|
||||
BOOLEAN nobuffer = FALSE;
|
||||
UINTN blksz = 512;
|
||||
|
||||
if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE)
|
||||
console_print(L"Fetching Netboot Image %a\n", full_path);
|
||||
if (*buffer == NULL) {
|
||||
*buffer = AllocatePool(4096 * 1024);
|
||||
@ -362,8 +399,31 @@ try_again:
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
if (EFI_ERROR(efi_status) && *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;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user