Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 29 Mar 2021 06:50:34 +0000
From:      bugzilla-noreply@freebsd.org
To:        bugs@FreeBSD.org
Subject:   [Bug 254637] [PATCH] Read kern.geom.eli.passphrase from UEFI variable for unattended boot without passphrase on disk
Message-ID:  <bug-254637-227@https.bugs.freebsd.org/bugzilla/>

next in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D254637

            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@FreeBSD.org
          Reporter: kgeorge@tcpsoft.com

Created attachment 223677
  --> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=3D223677&action=
=3Dedit
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 encrypti=
on
of the boot disk, the options to decrypt the boot disk at boot time are eit=
her
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 (consid=
er
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 lea=
st
on ZFS root, /boot is part of the zpool and thus encrypted by GELI regardle=
ss.=20
While the /boot option does work with legacy BIOS in some configurations, t=
his
is going away on modern systems.

The FreeBSD boot system already looks for the variable kern.geom.eli.passph=
rase
in the boot runtime environment and, when present, this passphrase is used
before prompting the user when it encounters a GELI-encrypted partition.  S=
o 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 NVR=
AM.=20
These are stored separate from any boot media (like on the motherboard).  T=
his
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 ha=
s a
(mostly unused, as far as I can tell) UEFI variable namespace
(cfee69ad-a0de-47a9-93a8-f63106f8ae99).  We can store kern.geom.eli.passphr=
ase
there and read it into the boot environment _before_ disks are probed, maki=
ng
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.passphr=
ase
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 =3D efi_autoload;
            archsw.arch_getdev =3D 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 =3D sizeof(geom_eli_passphrase);
    +       bzero(geom_eli_passphrase, geom_eli_bufsz);
    +       rv =3D efi_freebsd_getenv("kern.geom.eli.passphrase",
geom_eli_passphrase,
    +           &geom_eli_bufsz);
    +       if (rv =3D=3D 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 rebo=
ot.=20
Your system should boot without prompting (assuming your passphrase is corr=
ect,
of course).

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

[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-td6308=
485.html.
 boot1.efi, which used to be the first stage, is being phased out.

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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-254637-227>