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

From: Guido van Rooij <guido_at_gvr.org>
Date: Sun, 21 Aug 2022 11:11:22 UTC
On Fri, Aug 19, 2022 at 09:07:25AM -0600, Warner Losh wrote:
>    On Fri, Aug 19, 2022 at 2:20 AM Guido van Rooij <[1]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][2]guido@gvr.org>
>      >Â  Â  wrote:
>      >
>      >Â  Â  Â  On 16 Aug 2022, at 19:09, Warner Losh
>      <[2][3]imp@bsdimp.com> wrote:
>      >
>      >    
>      >Â  Â  On Tue, Aug 16, 2022 at 3:44 AM Guido van Rooij
>      <[3][4]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][5]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...
> 

Hi Warner,

We know for sure that the filesystem the EFI loader is read from is not
encrypted and thus we should be able to read the /efi/freebsd/loader.env.

So how about doing the dv_init() loop twice: The first time without trying
to unlock encrypted geom's. We can then safely read /efi/freebsd/loader.env.
The second dv_init() loop, we only trying to unlock the encrypted ones.

-Guido