[Bug 254637] [PATCH] Read kern.geom.eli.passphrase from UEFI variable for unattended boot without passphrase on disk

bugzilla-noreply at freebsd.org bugzilla-noreply at freebsd.org
Mon Mar 29 06:50:34 UTC 2021


https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=254637

            Bug ID: 254637
           Summary: [PATCH] Read kern.geom.eli.passphrase from UEFI
                    variable for unattended boot without passphrase on
                    disk
           Product: Base System
           Version: 12.2-RELEASE
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Many People
          Priority: ---
         Component: kern
          Assignee: bugs at FreeBSD.org
          Reporter: kgeorge at tcpsoft.com

Created attachment 223677
  --> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=223677&action=edit
Patch to loader.efi to read kern.geom.eli.passphrase from UEFI environment

TL;DR this patch reads kern.geom.eli.passphrase from a namespaced UEFI
"environment variable" (NVRAM firmware variable), setting it in the boot
environment for unattended boot of encrypted boot disks, without storing
anything on-disk in the clear.

GELI is used in FreeBSD to encrypt disks / partitions.  When doing encryption
of the boot disk, the options to decrypt the boot disk at boot time are either
enter a passphrase, or (if applicable) keep an unencrypted /boot partition on
the disk with keys.  However, in addition to not being a great idea (consider
the threat model of having to RMA a drive, for example), the latter option does
not work with UEFI: the UEFI loader.efi [0] stage probes devices and when it
probes a GELI partition, it prompts for a passphrase.  So storing on disk in
/boot is not an option with UEFI (and not really great anyway), and, at least
on ZFS root, /boot is part of the zpool and thus encrypted by GELI regardless. 
While the /boot option does work with legacy BIOS in some configurations, this
is going away on modern systems.

The FreeBSD boot system already looks for the variable kern.geom.eli.passphrase
in the boot runtime environment and, when present, this passphrase is used
before prompting the user when it encounters a GELI-encrypted partition.  So if
there was some way to get kern.geom.eli.passphrase populated it would solve
this problem ...

UEFI has the ability to store arbitrary, namespaced variables in system NVRAM. 
These are stored separate from any boot media (like on the motherboard).  This
is good for at least two reasons: 1) we can read them early in the boot
process, separate from any disk block and 2) because they're separate from the
disks, the keys are never written in the clear to disk.  FreeBSD already has a
(mostly unused, as far as I can tell) UEFI variable namespace
(cfee69ad-a0de-47a9-93a8-f63106f8ae99).  We can store kern.geom.eli.passphrase
there and read it into the boot environment _before_ disks are probed, making
the rest of the boot infrastructure try it before prompting.  The attached
patch does just that.

kern.geom.eli.passphrase can be written and read by userspace tool efivar:

# write

% cat passphrase | head -1 | tr -d $'\n' | doas efivar --name
cfee69ad-a0de-47a9-93a8-f63106f8ae99-kern.geom.eli.passphrase -w

# read

% doas efivar -n cfee69ad-a0de-47a9-93a8-f63106f8ae99-kern.geom.eli.passphrase
cfee69ad-a0de-47a9-93a8-f63106f8ae99-kern.geom.eli.passphrase
0000: 74 65 73 74 31 32 33 34

      ^^^^^^^^^^^^^^^^^^^^^^^ test1234

efivar requires root to read/write.

The patch, which is fairly simple (but took a while to figure out exactly
where/how to patch), is inline and attached:

    diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
    index a5213a51d88b..ecce7a2e4bba 100644
    --- a/stand/efi/loader/main.c
    +++ b/stand/efi/loader/main.c
    @@ -869,6 +869,8 @@ main(int argc, CHAR16 *argv[])
            char boot_info[4096];
            char buf[32];
            bool uefi_boot_mgr;
    +       char geom_eli_passphrase[256];
    +       UINTN geom_eli_bufsz;

            archsw.arch_autoload = efi_autoload;
            archsw.arch_getdev = efi_getdev;
    @@ -902,6 +904,22 @@ main(int argc, CHAR16 *argv[])
             */
            bcache_init(32768, 512);

    +       /*
    +        * Read kern.geom.eli.passphrase from the EFI environment under the
    +        * FreeBSD EFI GUID namespace (efi_freebsd_getenv).  Read before
scanning
    +        * block IO media so that it's available when probing.
    +        */
    +       geom_eli_bufsz = sizeof(geom_eli_passphrase);
    +       bzero(geom_eli_passphrase, geom_eli_bufsz);
    +       rv = efi_freebsd_getenv("kern.geom.eli.passphrase",
geom_eli_passphrase,
    +           &geom_eli_bufsz);
    +       if (rv == EFI_SUCCESS) {
    +               printf("kern.geom.eli.phassphrase read from EFI env\n");
    +               env_setenv("kern.geom.eli.passphrase", EV_VOLATILE,
    +                   &geom_eli_passphrase, env_noset, env_nounset);
    +               bzero(geom_eli_passphrase, geom_eli_bufsz);
    +       }
    +
            /*
             * Scan the BLOCK IO MEDIA handles then
             * march through the device switch probing for things.

To build:

    % cd /usr/src
    % patch -p1 < /path/to/patch.patch
    % cd /usr/src/stand/efi/loader
    % make
    % make install # changes /boot/loader.efi

To install:

    % mount -t msdosfs /dev/gpt/efiboot0 /mnt/efi
    % cp /boot/loader.efi /mnt/efi/efi/boot/BOOTx64.efi

To test:

    On any UEFI + GELI boot system, set the passphrase like above, and reboot. 
Your system should boot without prompting (assuming your passphrase is correct,
of course).

I've tested this / I'm using this on my own machine: 12.2-RELEASE amd64, with
UEFI + GELI + zfsboot.  Patch is made against 12.2 branch from the git mirror.

[0] loader.efi is the first and last stage loader now, according to
http://freebsd.1045724.x6.nabble.com/UEFI-loader-efi-and-boot-config-td6308485.html.
 boot1.efi, which used to be the first stage, is being phased out.

-- 
You are receiving this mail because:
You are the assignee for the bug.


More information about the freebsd-bugs mailing list