How Shim verifies binaries in secure boot?

7,586

Solution 1

As you've correctly deduced, SHIM will attempt to load from LoadImage() and StartImage() first. The EFI will then validate that the signature matches (through use of the internal SecureBoot mechanism). If LoadImage() returns an EFI_SECURITY_VIOLATION, the system will attempt to fallback load stage2 (in this case, GRUB2) from an internal certificate.

This certificate is baked into the system at compile-time, which was done by Canonical in this case. This certificate can be extracted from SHIM using binwalk or a similar utility.

Effectively, this allows SecureBoot to have a validated signature of shim stored in cache, which then in turn permits shim to verify that GRUB was signed with the aforementioned certificate. If it was, GRUB successfully boots.

SHIM will use the system keys wherever possible - which is why LoadImage() and StartImage() are used first. Only if it does not work will SHIM attempt to load stage2 with its own internal certificate. You can see this code here (part of verify_buffer), which is called in part of the handle_image chain.

The entire verification chain looks like this:

  1. Verify system hashes and MOK list
  2. Ensure the binary is not blacklisted
  3. Attempt to check the binary through MOK/BIOS whitelist
  4. Check against internal signatures as defined by the build key and SHIM's own internal keys.

It's also important that the MOK Manager is not the MOK database itself. The latter is maintained by the EFI firmware, which takes commands for what to add/remove from the manufacturer during flashing as well as the operating system (or, in this case, shim). shim only stores a very short list of aforementioned compiled keys to allow a boot - everything else needs to be handled by the EFI firmware.

Solution 2

Kaz Wolfe's answer is pretty good, but I want to emphasize and expand on a couple of points....

The last I checked, Shim basically provided a sort of parallel Secure Boot verification feature. It's intended to be used by GRUB, which is designed to launch Linux kernels that are not EFI programs. Thus, Shim registers itself with the EFI in a way that enables follow-on programs to call Shim to verify that binaries are signed. Shim does so in either of two ways:

  • Shim's built-in key -- Most Shim binaries, including the one provided as part of Ubuntu, include a built-in Secure Boot key. Ubuntu's Shim includes Canonical's public key, which validates Ubuntu's GRUB and Linux kernel. This key is therefore stored in RAM, and is rather temporary as these things go. The main point of Shim is to enable its follow-on program (GRUB) to perform a Secure Boot type verification -- but GRUB doesn't really do a Secure Boot verification per se, as described shortly. Without Shim, Canonical would need to rely on Microsoft to sign every new release of GRUB and every new Linux kernel, which would be somewhere between impractical and impossible.
  • Machine Owner Keys (MOKs) -- MOKs are basically an extension of Shim's built-in key, but they're intended for ordinary users to manipulate. You might use MOKs if you want to launch binaries that aren't signed with Canonical's key. MOKs, like the firmware's built-in Secure Boot keys, are stored in NVRAM; but they're more easily added to NVRAM, via a program called MokManager. Getting MOKs into NVRAM is still tedious enough that most people don't bother, and many people who do have problems with it; but it's easier than taking complete control of your Secure Boot subsystem, as described in my page that you referenced (Managing EFI Boot Loaders for Linux: Controlling Secure Boot).

In most cases, MOKs are not used; if you want to dual-boot Windows and Ubuntu, you'll probably do fine with the firmware's built-in keys and the key embedded in Ubuntu's Shim binary. You'd use MOKs if you want to add another Linux distribution, compile your own kernels, use a boot loader other than GRUB, use third-party kernel modules, etc.

In addition to those two sources, there's also the Secure Boot keys built into the firmware. I don't recall if Shim uses those keys. It would implicitly use them if it uses the EFI's LoadImage() and StartImage() calls (which it does, but I haven't reviewed the context for this answer). My recollection is that its own verification code does not use the firmware's Secure Boot keys when GRUB calls back to see if a kernel is signed, but I might not be remembering that correctly.

As to how Shim integrates into the Secure Boot system, the last I checked, it didn't. IIRC, to launch its follow-on program (GRUB), Shim implements its own binary-loading code, which resembles a stripped-down version of the code in the Tianocore UEFI sample implementation. This code calls Shim's own Secure Boot verification code, which checks a binary against its built-in key and the local MOK list, to launch a binary. (It may also use the firmware's own Secure Boot keys, but I'm not positive of that.) Once GRUB is loaded, it calls Shim's binary-verification function to verify the Linux kernel, which GRUB launches in its own way (not the way the EFI launches EFI programs). Thus, Shim doesn't really integrate itself into the firmware very deeply; it just makes one or two of its functions available to follow-on programs, leaving the LoadImage() and StartImage() EFI functions unchanged.

That said, EFI does provide ways to replace or supplement normal EFI system calls, and some tools do use these methods. For instance, the PreLoader program, which was a tool to do something similar to what Shim does, integrated itself more deeply into the firmware; it used EFI system calls designed to patch broken or obsolete functions to modify StartImage() so that it would check both the usual UEFI Secure Boot keys and the MOK. PreLoader has pretty well fallen by the wayside; its developers and Shim's developers have cooperated to focus on Shim rather than PreLoader as the standard Linux Secure Boot tool. AFAIK, Shim has not adopted PreLoader's deeper UEFI integration; however, it's been a while since I looked at the code very closely, so I may be out of date on this. That said....

My own rEFInd boot manager uses code that I took from the PreLoader program in order to "glue" Shim's binary verification code into the UEFI's normal verification subsystem. Thus, with rEFInd in the picture, any attempt to launch an EFI program using LoadImage() and StartImage() calls the Shim authentication code first and, if that fails, calls the standard UEFI Secure Boot authentication second. The gummiboot/systemd-boot boot manager does something similar. Both programs do this because they launch the Linux kernel by way of its EFI stub loader, which means that they rely on the EFI LoadImage() and StartImage() calls. This contrasts with GRUB, which is a full boot loader that launches the Linux kernel in its own way, so GRUB doesn't need these EFI system calls to recognize the Shim's key or the local MOK list.

I hope this helps clarify things, but I'm not sure it will. The details of how all this work are pretty messy, and it's been a while since I've dealt with them in any detail, so my own thoughts aren't as organized as they might be.

Share:
7,586

Related videos on Youtube

direprobs
Author by

direprobs

Updated on September 18, 2022

Comments

  • direprobs
    direprobs over 1 year

    UEFI shim loader

    shim is a trivial EFI application that, when run, attempts to open and execute another application. It will initially attempt to do this via the standard EFI LoadImage() and StartImage() calls. If these fail (because secure boot is enabled and the binary is not signed with an appropriate key, for instance) it will then validate the binary against a built-in certificate. If this succeeds and if the binary or signing key are not blacklisted then shim will relocate and execute the binary.

    I've been reading to understand how the verification procedure happens when the secure boot option is enabled:

    Difference between vmlinuz *-generic and *-generic.efi.signed

    How does Secure Boot actually work?

    Managing EFI Boot Loaders for Linux: Controlling Secure Boot

    I could tell now that the procedure goes like this:

    Shim is first run by the machine's firmware. Now shim has to run the bootloader. What I don't understand is how shim verifies the binaries? For example, the above quoted paragraph states that shim attempts to start the other application via the standard EFI LoadImage() and StartImage() calls and if this fails shim tries to verify the binary from a builtin certificate. This built-in certificate then belongs to shim? Essentailly is that why shim is called Machine Owner Key Manager (MOK)? Because it has its own database of keys to verify binaries.

    Put it simply, the machine's firmware has its own database of keys in NVRAM to verify binaries, and shim has it own database of keys to verify binaries?

    After the bootloader has been verified and executed, where does the bootloader look to locate the keys of the signed kernel that it need to boot, from the firmware's database of keys for example?

  • direprobs
    direprobs over 6 years
    "only if it does not work will SHIM attempt to load stage2 with its own internal certificate." Is it right to say then that shim fools UEFI by providing its certificate on behalf of another binary? However, although UEFI has been fooled, SHIM will perform its own key check. In other words, there are two checks (1) UFEI key verification (first) , when this fails (2) SHIM provides its own certificate to UEFI and performs its own key verification not using the UEFI's keys, instead the keys built into SHIM statically.
  • Kaz Wolfe
    Kaz Wolfe over 6 years
    I honestly am not able to make that good sense of what SHIM is doing. As far as I can tell though, SHIM does fool UEFI by using its own certificate, and then just arbitrarily executing whatever it finds (that is signed). Or, at least, that's how I currently understand it.
  • direprobs
    direprobs over 6 years
    Fair enough, if secure boot is meant to make us secure, it's complexity is just unwanted. Luckily, my firmware does allow me to add and manipulate keys as I desire, I still however disable secure boot as some kernel modules for some reasons don't have their own keys.
  • direprobs
    direprobs over 6 years
    Also surprisingly when I checked db keys there's a Canonical signature in there. Perhaps this Canonical key is used to verify Grub? And from what I understand from your answer is that, UFEI's system calls can possibly be patched?
  • Rod Smith
    Rod Smith over 6 years
    Canonical's key is included in some computers' firmware, but this is rare. (I've seen it myself only on an ASUS P8 H77-I motherboard.) Such a computer can launch Ubuntu's GRUB directly, without Shim, with Secure Boot enabled. You can sign third-party kernel modules yourself -- see my answer to this question for one approach. Yes, many EFI system calls can be patched by EFI programs.
  • direprobs
    direprobs over 6 years
    @Rob Smith I have Asus though.