Adventures with the UEFI shim

Peter Jones pjones at redhat.com
Sun Oct 11 19:49:53 BST 2020


On Thu, Oct 08, 2020 at 11:42:33AM -0400, Paul Moore wrote:
> On Wed, Oct 7, 2020 at 3:17 PM Peter Jones <pjones at redhat.com> wrote:
> > On Thu, Oct 01, 2020 at 03:16:08PM -0400, Paul Moore wrote:
> 
> ...
> 
> > > While most shim use cases involve using shim to load GRUB which in
> > > turn loads the kernel/OS, I'm looking to use shim to boot the
> > > kernel/OS directly.  Specifically, I want to use shim to boot a
> > > combined kernel+initrd+cmdline EFI application using the systemd-boot
> > > EFI stub.  Unfortunately I've been running into problems with shim's
> > > ExitBootServices hook.  Looking at the code, it would appear that shim
> > > expects (and requires) that some piece of the bootloader chain calls
> > > into shim's verification protocol to authorize the kernel prior to
> > > calling ExitBootServices.  This would normally be handled by GRUB, or
> > > any other bootloader in the chain, but if there is no bootloader
> > > beyond shim things fall apart.
> > >
> > > If this was anything but shim, the fix would be a small and
> > > straightforward patch to replacements.c:exit_boot_services() but since
> > > our project would eventually like to get a shim signed by Microsoft we
> > > need to find a solution that is suitable for the rhboot/shim-review
> > > crowd.
> >
> > Let me start by saying that I don't think there's a good answer here,
> > for a lot of reasons.  The biggest one is that systemd-boot is LGPL and
> > linux is GPL licensed, and Microsoft has stated repeatedly that they're
> > not going to sign images that are GPL licensed.  That's a part of why
> > shim exists in the first place.
> 
> I think there may be a misunderstanding here, I'm not expecting
> Microsoft to sign anything other than the UEFI shim.  The only
> difference from the typical shim->GRUB->kernel solution is that I'm
> thinking of skipping GRUB and booting the kernel+initrd+cmdline (with
> the systemd-boot UEFI stub/wrapper to properly setup the
> initrd/cmdline).

Yeah, I think I was misunderstanding you.  My initial read was that
everything was being packed in as one binary, but that now seems wrong.
So let me ask explicitly just to avoid further confusion: are you
actually saying you'll have one binary that's shim and second one that's
the systemd-boot+kernel+initramfs?

> > If it weren't for that, I would say:
> > - put a pubkey in shim like normal
> > - sign your kernel with it
> > - make systemd-boot call the shim lock protocol on the embedded kernel
> 
> Yes, I'm already planning on steps 1 and 2 (those are needed pretty
> much regardless of the design), I was just wondering if the UEFI shim
> folks would be open to working on solution that doesn't require the
> "loader_is_participating" check in shim's ExitBootServices hook.
> Conceptually if shim has already verified the signature on the next
> stage of the boot process that would seem to imply a certain level of
> trust in that binary.  Would you be open to a mechanism for post-shim
> loaders to signal shim that they do not need additional verification
> via the shim protocol?

I think that sounds like a reasonable trade-off, yes, though I do think
we'd want to see the code during review, just to be sure.

> > So I guess the questions are:
> >
> > - What's your reasoning for wanting to go the systemd-boot packed
> >   binary route?  There may be more options, depending on which parts of
> >   that design are critical to you.
> 
> I want to be able to verify not just the kernel, but the initrd and
> kernel command line as well.  If I bundle the kernel+initrd+cmdline
> together into one EFI application using the systemd-boot EFI stub (or
> similar), I can sign the resulting EFI binary and load it directly.
> 
> I am open to other solutions that provide similar verification of the
> kernel+initrd+cmdline.
> 
> > - If you can share, what does your timeline for needing something signed
> >   look like?
> 
> It's fuzzy.  First and foremost I need to find a solution that meets
> our needs, and is acceptable to Microsoft and the rhboot/shim-review
> crowd.  My hope is that we can find a solution that can be integrated
> directly into shim, since that seems like the quickest path forward
> (least amount of changes).  If there is no acceptable way around the
> "loader_is_participating" issue, then my plan B looks to be to create
> a small second stage loader[1] which simply verifies a EFI binary via
> the shim protocol and then hands off execution.
> 
> Parallel to the technical discussions I've been working on making sure
> we have everything in place for the actual Microsoft signing.  Oddly
> enough that has proved to be the easier of the two tasks.  Go figure.

I think there are a couple of things we could do.  If I recall
correctly, systemd-boot packs the kernel into a section called ".linux"?
(For now I'll assume that's at least the right idea.)  

My initial thought is that we could change shim to cache the hashes of
loaded objects on a per-section basis, and systemd-boot call
shim->verify_buffer() the normal way, but on only that section.  Then
we'd make verify_buffer consult the cache and see if the addresses
passed in match a section we've already validated, and if so, hash it
and compare.  If it matches, we call that success.

That being said, since this method generates top-level images that
differ on a per-vendor/per-product basis, we probably need all the work
regarding https://github.com/rhboot/shim/issues/223 to land before we
could deploy such a scheme.

> [1] You may ask, why not just use GRUB?  Beyond the simple-is-good
> argument,

That's a fair point, though I would suggest putting some time into
auditing the systemd-boot loader code; I don't know for sure how much
they've done.

> we also want some additional TPM PCR extensions beyond what
> is commonly supplied by shim+GRUB.  The existing PCR7 measurements are
> close, but we want more stability than PCR7 currently provides; we
> want PCR stability across firmware changes and db/dbx might change
> across firmware updates.  As we are not planning to use GRUB, we are
> planning on using PCR8 or PCR9 to measure the SecureBoot variable, PK,
> KEK, and EFI binary (kernel+initrd+cmdline) signing certificate.  The
> other PCR will likely contain just a measurement of the EFI binary
> itself.  In a perfect world we would also work with shim to see if we
> could get these extensions into shim as a build time option, but it's
> not worth discussing that if we can't find a shim-based solution to
> the "loader_is_participating" issue we are facing.

That actually sounds like something we should investigate for upstream
as well, whether it's optional or not.  Anything to increase the PCR
agility is a win in my book, so long as it doesn't actually /harm/ some
other use case.  That said, obviously we don't want to break existing
installations, so we do have to be careful with changes there in
upstream, and make sure downstreams have a path forward.

At any rate, the TPM is mostly tangential to the security boundary shim
is there to help establish and protect, and currently shim is really
only measuring things because the tpm doesn't work if we don't. So
changes to tpm validation may be a good or a bad idea, but aren't
generally something we'll care about in review.

> I realize this will complicate the rhboot/shim-review process, but my
> goal is to make this loader as small as possible to ease review (and
> likely "borrow" a lot of code from shim itself as I have no specific
> licensing requirements on my end).
> 
> > > As an aside, how do people do dev/test of the UEFI shim when UEFI
> > > Secure Boot is enabled?  I originally thought I could use a Microsoft
> > > signed shim to boot my development shim which would finally boot the
> > > kernel/OS, but I'm running afoul of the nested EFI service hooks.  Any
> > > advice you can provide would be appreciated.
> >
> > We use Qemu with OVMF (see the "edk2" packages in fedora for example
> > builds) and enroll our own certs and self-sign everything.  The biggest
> > "gotcha" there is to be sure you have one cert that's in 'db' and signs
> > shim, and a completely different cert that shim trusts, which signs
> > anything shim might be loading.  That said, my statement here is
> > obviously not written with your attempt to use systemd-boot in mind.
> 
> You wouldn't happen to have a doc/guide written up on this, would you?
>  Bonus points if it uses QEMU directly and doesn't require libvirt.
> The past two days I started playing around with using QEMU+OVMF to
> ease UEFI development, but I haven't properly tried to get UEFI Secure
> Boot working yet.

I use a qemu qcow2 disk image that's already got an OS installed in it,
which I set up in libvirt the first time, and then deploy with
https://pjones.fedorapeople.org/deploy-qemu.sh .  I think the only
meaningful thing libvirt is doing for me is file paths:

/usr/share/OVMF/OVMF_CODE.fd for the firmware code comes from edk2-ovmf,
/var/lib/libvirt/qemu/nvram/efi-test-x64-sb_VARS.fd is copied from
edk2-ovmf's /usr/share/OVMF/OVMF_VARS.secboot.fd , and
/var/lib/libvirt/images/efi-test-x64.qcow2 is my disk image.  You can
set the Secure Boot security databases up by dropping your certs them as
DER file onto /boot/efi in the booted image, then rebooting into the
firmware menu and adding them there.

Hope this helps.

-- 
        Peter




More information about the Efi mailing list