Shim verification flow

Shim verification flow #

This document outlines how the shim boot loader verifies an EFI application and what sources it uses for both trusted and revoked keys.

Introduction #

The shim boot loader is a simple EFI application that chain loads either another boot loader or a Linux kernel. Its purpose is to enable Linux distributions and users to sign their own kernels and boot loaders with keys not present in the UEFI Secure Boot databases without disabling Secure Boot. While the shim application itself must be signed by a key the UEFI Secure Boot trusts, i.e. a Microsoft key, shim will consider user controlled keys when chain loading.

Sources #

The framework shim uses for its trust decisions mirrors that of UEFI Secure Boot. When verifying an EFI application shim considers two aspects.

  1. The Microsoft Authenticode SHA-256 and SHA-1 hashes of the application, and
  2. each signature embedded in the Authenticode PE/COFF section.

For both aspects, the items in question must not be in the list of revoked hashes or certificates and must be in the list of trusted hashes or certificates. If either is not the case the application is not loaded.

Sources of the list of trusted aspects are, in order

  1. a compiled-in list supplied either via the VENDOR_DB_FILE (EFI_SIGNATURE_LIST) or VENDOR_DB_CERT (single certificate) defines during build,
  2. the UEFI Secure Boot database read from the “db” variable,
  3. the contents of the “.db” section of all EFI applications whose names start with “shim_signature” that are in the same folder as the shim binary on the ESP,
  4. the EFI_SIGNATURE_LIST found in the EFI variable with name “MokList”, and
  5. an optional, auto-generated certificate “build_cert” if the ENABLE_SHIM_CERT define is set.

The applications in (3) are only considered if they themselves are valid for chain loading, even though they do not need to include code.

The “build_cert” in (5) is generated during the build process and only used to sign the MokManager and Fallback auxiliary binaries described in the next section.

Sources of revoked hashes and certificates are

  1. a compiled-in list supplied via the VENDOR_DBX_FILE define,
  2. the UEFI Secure Boot database read from the “dbx” variable, and
  3. the EFI_SIGNATURE_LIST found in the EFI variable with name “MokListX”.

Each application first needs to pass the revocation check and then must be recognized as trusted. After that it is verified against the finer grained SBAT revocation mechanism that has been documented elsewhere.

All certificates used for verifying applications need to have Code Signing in their Extended Key Usage certificate extension set. Aside from that, no further verification of the certificates themselves is done, including their signature or expiration time.

A pseudocode implementation of the verification flow is given below. The init function is called once at startup to read all external certificates and to copy the trusted, Boot Services accessible variables to the untrusted, runtime accessible ones. These are explained in the next section. Afterwards, any application that is chain loaded through shim has to pass the verify function.

def init():
    MokListRT = MokList + build_cert
    MokListXRT = MokListX

    global user_trusted = []
    for ext in glob("./shim_signature*"):
        if load_external and verify(ext, False):
            user_trusted = user_trusted + ext.db

def verify(file, include_external=True):
    # Check whether the file hash is revoked
    if file.hash in VENDOR_DBX_FILE:
        return False
    if file.hash in dbx:
        return False
    if file.hash in MokListXRT:
        return False

    # Check whether the file hash is trusted
    if not MokIgnoreDB and file.hash in db:
        return True
    if file.hash in VENDOR_DB_FILE:
        return True
    if file.hash in MokListRT:
        return True

    # File hash not trusted, see if a signature is trusted
    revocations = VENDOR_DBX_FILE + dbx + MokListXRT:
    trusted = VENDOR_DB_FILE + MokListRT + VENDOR_DB_CERT

    if not MokIgnoreDB:
        trusted = trusted + db
    if include_external:
        trusted = trusted + user_trusted

    not_revoked = trusted - revocations

    for sig in file.signatures:
        # Check whether the signtature was done by a non-revoked certificate
        for cert in not_revoked:
            if valid(sig, file.hash, cert):
                return True

    return False

Trusting and Revoking Keys #

All sources of trusted and revoked keys except for MokList and MokListX are read-only. The build-in keys cannot be changed as the shim application itself is signed and the additional “shim_signature*” files can only add certificates or hashes if their hash or signing certificate is itself trusted.

The MokList, MokListX and MokIgnoreDB EFI variable are only accessible to Boot Services, i.e. EFI applications that run before ExitBootServices() is called. This means they cannot be written (or read) by the operating system. shim ships with a companion application called MokManager that runs as a Boot Service and will add or remove hashes and certificates to and from both MokList and MokListX. This is done by first requesting a change from the operating system by writing EFI variables documented in the shim source code together with a user chosen password. After rebooting, MokManager will confirm the change by requiring the user to supply the password via the UEFI text console. By design this process cannot be automated.

MokManager is verified using the same flow as any other chain loaded application.