Re: How to use serial console to enter GELI password to boot kernel on a GELI encrypted ZFS pool

From: Warner Losh <imp_at_bsdimp.com>
Date: Fri, 19 Aug 2022 15:07:25 UTC
On Fri, Aug 19, 2022 at 2:20 AM Guido van Rooij <guido@gvr.org> wrote:

> On Wed, Aug 17, 2022 at 09:19:42AM -0600, Warner Losh wrote:
> >    On Wed, Aug 17, 2022 at 7:35 AM Guido van Rooij <[1]guido@gvr.org>
> >    wrote:
> >
> >      On 16 Aug 2022, at 19:09, Warner Losh <[2]imp@bsdimp.com> wrote:
> >
> >    
> >    On Tue, Aug 16, 2022 at 3:44 AM Guido van Rooij <[3]guido@gvr.org>
> >    wrote:
> >
> >      On Mon, Aug 15, 2022 at 02:20:32PM -0600, Warner Losh wrote:
> >      >Â  Â  On Mon, Aug 15, 2022 at 8:23 AM Guido van Rooij
> >      <[1][4]guido@gvr.org>
> >      >Â  Â  wrote:
> >      >
> >      >Â  Â  Â  Currently I have a system with ZFS on GELI. I use the
> >      ability in
> >      >Â  Â  Â  the EFI loader to enter the GELI password.
> >      >Â  Â  Â  Is it possible somehow to use a serial console to enter
> >      the
> >      >Â  Â  Â  password?
> >      >Â  Â  Â  My system does have a COM1 port but it isn't recognised at
> >      the early
> >      >Â  Â  Â  bot stage. There I only see:
> >      >Â  Â  Â  ÃÂ  ÃÂ  Consoles: EFI console
> >      >Â  Â  Â  ÃÂ  ÃÂ  GELI Passphrase for disk0p4:
> >      >Â  Â  Â  (Note: this is early in the boot process so there is no
> >      access to
> >      >Â  Â  Â  boot.config (or any other file in the ZFS pool) as it
> >      still on
> >      >Â  Â  Â  encrypted storage at that time).
> >      >
> >      >Â  Â  The boot loader.efi will read ESP:/efi/freebsd/loader.env for
> >      >Â  Â  environment
> >      >Â  Â  variables. You can use that to set the COM1 port since it
> >      appears your
> >      >Â  Â  EFI system doesn't do console redirection.
> >      >Â  Â  If you want it to only prompt COM1 for the password, but
> >      everything
> >      >Â  Â  else is
> >      >Â  Â  on the efi console, that's a lot harder.
> >      Hi Warner,
> >      Thanks, but somehow I still cannot get it to work properly.
> >      Content of /efi/freebsd/loader.env:
> >      boot_multicons="YES"
> >      console="efi comconsole"
> >      The boot prompt still only shows "Consoles: EFI console".
> >
> >    Yes. That's printed before we process the ESP file and switch to the
> >    new console...
> >    Â
> >
> >      When I boot I get the GELI passphrase prompt at the EFI console
> >      only. But when the kernel starts
> >      to run I do get output to the serial console, staring with:
> >      ---<<BOOT>>---
> >      Copyright (c) 1992-2021 The FreeBSD Project.
> >      So it seems the loader.env file is read correctly (it didn't output
> >      anything to the serial
> >      console before I created efi/freebsd/loader.env). But looking at the
> >      source I see in
> >      efi/loader/main.c:read_loader_env():
> >      Â  Â  Â  Â  if (fn) {
> >      Â  Â  Â  Â  Â  Â  Â  Â  printf("Â  Â  Reading loader env vars from
> >      %s\n", fn);
> >      Â  Â  Â  Â  Â  Â  Â  Â
> >      parse_loader_efi_config(boot_img->DeviceHandle, fn);
> >      Â  Â  Â  Â  }
> >      I never saw the printf appearing. I do not understand this.
> >
> >    It should have appeared on the video console of the EFI console
> >    (assuming no serial
> >    redirect is going on in that BIOS).
> >
> >    It surely did not.
> >
> >    I'd have to delve more deeply into the prompts for the GELI password
> >    than I have
> >    time to do this morning. What if you type the password blind into the
> >    serial port?
> >
> >    Tried that but nothing happened. When I
> >    enter the passphrase after typing it in via
> >    the serial port, it worked immediately so
> >    we can conclude that no single keystrokeÂ
> >    got through.
> >
> >    OK. I'll have to delve a little more deeply then...
>
> I Think I know why it does not work. The "Consoles:" line is printed in
> cons_probe()
> which is called in main():
>         setenv("console", "efi", 1);
>         cons_probe();
> So that explains why we see Consoles: EFI console
> Then we see in main():
>         for (i = 0; devsw[i] != NULL; i++)
>                 if (devsw[i]->dv_init != NULL)
>                         (devsw[i]->dv_init)();
> The way I understand it, is this the place where the GELI passphrase
> prompt originates
> from.
>

Well, not quite. We prompt for the GELI password when we first open a
device in devopen
(at least that's where we call geli_probe_and_attach())[*]. This should be
a bit later, though.
I'll have to put some debug prints in to see when it's called... But it
looks like we probe for
geli, according to the debug, at this point, so something needs to be done
to sort out this
chicken and egg problem.


> But only after that, we see the call to load /efi/freebsd/loader.env
>
> Shouldn't the dv_init() calls be moved to after the call to
> boot_howto_to_env(howto)?
>

You have discovered a wonderful chicken and egg. If we don't call dv_init
we can't do I/O
to the devices, so we can't change the console.

But, the boot_hwoto_to_env() call only sets env variables that will be used
to either set
boot args to the kernel or other env variables that are used to communicate
the console.
It doesn't actually change the console, so is the wrong thing to locate.

The likely best way to approach this is to fix loader.efi to initialize
enough of the devices
so that we can read files off the ESP early enough to set a good console
for further
interaction.

Warner

[*] this leads to a bit of a layering violation that causes a circular
dependency, but I digress...