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: b...@freebsd.org Reporter: kgeo...@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. _______________________________________________ freebsd-bugs@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/freebsd-bugs To unsubscribe, send any mail to "freebsd-bugs-unsubscr...@freebsd.org"