[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC] efi/boot: Unified Xen executable for UEFI Secure Boot support



On Thursday, August 6, 2020 9:57 AM, Jan Beulich <jbeulich@xxxxxxxx> wrote:
> On 05.08.2020 19:20, Trammell Hudson wrote:
> > This preliminary patch adds support for bundling the Xen hypervisor, 
> > xen.cfg,
> > the Linux kernel, initrd and XSM into a single "unified" EFI executable that
> > can be signed by sbsigntool for verification by UEFI Secure Boot. [...]
>
> Looks reasonable for a PoC, thanks, but needs cleaning up I think.

Thanks for the comments.  It is very much a WIP and hopefully we can clean
it up for merging.

> A couple of specific remarks / questions:
>
> [...]
> > -   /* PE32+ Subsystem type */
> > -   if (pe->FileHeader.Machine != PE_HEADER_MACHINE_X64
> > -   && pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM64
> > -   && pe->FileHeader.Machine != PE_HEADER_MACHINE_I386)
> > -          return NULL;
>
> I don't think i386 should be recognized here, and of the two other
> ones only the one matching the current target architecture should
> be.

That's a good point.  There isn't much point in proceeding if Xen
can't boot the executable anyway...


> > -   if ( pe->FileHeader.NumberOfSections > 96 )
> > -          return NULL;
>
> What's this 96 about?

Not sure, to be honest.  The PE parsing code is directly copied
from systemd-boot (including the bit about ARM and i386):

https://github.com/systemd/systemd/blob/07d5ed536ec0a76b08229c7a80b910cb9acaf6b1/src/boot/efi/pe.c#L83

> Overall I think it might help if this PE parsing code (if UEFI
> doesn't offer a protocol to do it for us) was put into its own
> source file.

I tried to putting it into a separate file and ran into link issues,
seems that it needs to be mentioned in both arch/x86/Makefile and
arch/x86/pe/Makefile, so this was a "just make it work" for the PoC.
Now that it is working, I'll go back to see if I can figure out the
makefile magic.

> I also wonder if it wouldn't better be optional
> (i.e. depend on a Kconfig option).

My preference would be to have it always on so that any Xen
executable can be unified and signed by the end user, rather than
requiring the user to do a separate build from source. For instance,
the Qubes install DVD has a normal xen.efi, but I can generate my own
signed version for my system by unifying it with the kernel and
initrd.

> > -   if ( efi_rs->GetVariable(L"SecureBoot", (EFI_GUID *)&global_guid, NULL, 
> > &size, buf) != EFI_SUCCESS )
> > -          return false;
> > -   return buf[0] != 0;
>
> I.e. "SecureBoot=N" still means "enabled"?

Maybe? UEFI 2.8, section 3.3 "Global Variables" says for SecureBoot:

"Whether the platform firmware is operating in Secure boot mode (1) or not (0). 
All other values
are reserved. Should be treated as read-only."

> [...]
> "secure" depending merely on an env var, how is this logic compatible
> with the shim model? You need to keep the other approach working.

Oops. Yes, I broke the shim model when booting in non-unified way with
SecureBoot enabled.

> Also, considering kernel and initrd are embedded, is there really a
> strict need for a config file? It would seem to me that you could
> boot the system fine without.

The config file is still necessary for Xen options (console, etc) as
well as the kernel command line.

> [...]
> Once you know whether you're dealing with a "unified" image, you
> shouldn't have a need to make logic dependent upon read_section()
> finding a particular section: Either you find all of them (and
> don't even try to interpret respective config file settings), or
> you read everything from disk.

Another option that might be better would be to have a "special"
file name -- if the config file has a leading "." then read_file()
could do the PE section search instead of going to the disk.
That way the config file could have some things on disk, and
some things unified.

For instance, microcode doesn't (always) need to be signed, since
it is already signed and encrypted by Intel.  Many OEM's leave
the ucode regions of flash unprotected by bootguard, which allows
the end user to update the microcode without requiring an OEM
signature.  This does potentially leave open some rollback
attacks, so I'm not 100% positive it is a good idea (although the
firmware volume should still be measured into the TPM and things
like SGX include the microcode version in the attestation).


> > --- /dev/null
> > +++ b/xen/scripts/unify-xen
> > @@ -0,0 +1,68 @@
> > +#!/bin/bash
> > +# Merge a Linux kernel, initrd and commandline into xen.efi to produce a 
> > single signed
> > +# EFI executable.
> > +#
> > +# turn off "expressions don't expand in single quotes"
> > +# and "can't follow non-constant sources"
> > +# shellcheck disable=SC2016 disable=SC1090
>
> What are these three lines about?

Those are hold-overs from the safeboot script that I borrowed this from.
When linting the script with shellcheck, it complains about sourcing
files based on environment variables, and some code that has '$' in
single quotes.

This script is definitely NOT ready for inclusion and is just an example
of how to use objcopy to build the unified image.  A more full-featured
unify script could take the xen.cfg file and use it to locate the kernel,
initrd, xsm, ucode, etc instead of hard coded command line arguments.

I'm surprised that systemd-boot doesn't have a proper script; their
tutorials show how to run a raw objcopy command, which is about as
user unfriendly as possible...

> [...]
> > +# Xen goes up to a pad at 00400000
>
> "pad at 00400000"?

$ objdump -h xen.efi

xen.efi:     file format pei-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
[...]
  8 .pad          00400000  ffff82d040c00000  ffff82d040c00000  00000000  2**2
                  ALLOC

There is this pad at the end of the image; I wasn't sure if it was important,
so I had the script deposit the extra sections after it.  Hopefully there is
someway to automatically figure out the correct address for the additional
segments.

> > +XEN="$1"
> > +CONFIG="$2"
> > +KERNEL="$3"
> > +RAMDISK="$4"
>
> What about ucode and xsm policy?

Yeah... Did I mention this is a total hack?

It looks like microcode is handled in efi_arch_cfg_file_late(),
so it will need to be updated.  If the read_file() change is made,
then it would automatically do the right thing.

>
> > +objcopy \
> > -   --add-section .kernel="$KERNEL" \
> > -   --add-section .ramdisk="$RAMDISK" \
> > -   --add-section .config="$CONFIG" \
> > -   --change-section-vma .config=0xffff82d041000000 \
> > -   --change-section-vma .kernel=0xffff82d041010000 \
> > -   --change-section-vma .ramdisk=0xffff82d042000000 \
>
> Of course these hard coded numbers will be eliminated in the
> long run?

Ideally.  We could try to parse out the address based on the objdump output,
although oddly systemd-boot has hardcoded ones as well.

[...]
> > -   warn "$OUTDIR/linux.efi: signature failed! Try $try."
> >     +done
>
> Why the retries? (Also leftover "linux.efi" in the warning?)

The sbsign could be moved into a separate script or example.

The retries in the safeboot script are because this happens after
the dm-verity hashes have been computed, which takes a while,
so it is a better user experience to give them another chance if
they mis-type their signing key password (or haven't yet plugged in
the yubikey).

Thanks again for the comments!  I'm really hopeful that we can have
Xen interoperating with UEFI SecureBoot sometime soon!

--
Trammell



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.