Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 29 Nov 2021 17:12:35 +0100
From:      Stanislaw Adaszewski <s.adaszewski@gmail.com>
To:        Warner Losh <imp@bsdimp.com>
Cc:        FreeBSD Hackers <freebsd-hackers@freebsd.org>
Subject:   Re: TPM2 Support in bootloader / kernel in order to retrieve GELI passphrase
Message-ID:  <CADxsEsQ0hrPSfEXE4Dt28AWt9mWHu-qieYo0jpnMie=VeRZHnA@mail.gmail.com>
In-Reply-To: <CADxsEsSiWF2W9axytK_QXyS-_rEABuOoCJCAku19kF%2B-GxAmBw@mail.gmail.com>
References:  <CADxsEsRbt6xj1TOHVMMC3jhT%2BCfqZqX479JvdNyM31eAQh1%2BtA@mail.gmail.com> <CANCZdfphx9ZwL4j1deR9LLMBTatqVH%2B_PtkGp8ReQtWzp6T24Q@mail.gmail.com> <CADxsEsQKWrt5w%2B-Xo11QnDb_z5j%2BXkxC5q09%2BZnbMX1VLwAwYA@mail.gmail.com> <CADxsEsSiWF2W9axytK_QXyS-_rEABuOoCJCAku19kF%2B-GxAmBw@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
Made a handful of other changes:

1) Prevent interaction (exit) if autoboot fails (stand/common/interp.c)

2) Wipe env vars and GELI keys before panic()'ing the kernel in case the
passphrase marker does not match - probably does not really help much
but I guess the idea to narrow the time window any secrets are in memory
a good rule of thumb,

3) Call Tpm2NvReadLock() right after retrieving the passphrase from TPM
in the bootloader. Therefore, it cannot be read again from the TPM until th=
e
next restart. Thus, it remains unavailable for example in the booted OS.

On Sun, 28 Nov 2021 at 12:19, Stanislaw Adaszewski
<s.adaszewski@gmail.com> wrote:
>
> I have made further modifications:
>
> 1) As per suggestion - use SYSINIT() and EVENTHANDLER_REGISTER()
> in a separate file (tpm2cpm.c) rather than hardcoding in init_main.c.
>
> 2) Make the functionality configurable / optional both for the kernel
> (option TPM2_PASSPHRASE) and the bootloader
> (WITH_LOADER_TPM2_PASSPHRASE). The one for the bootloader
> is enabled by default and the one for the kernel is enabled by default
> in the GENERIC amd64 kernel.
>
> Best regards,
>
> --
> Stanislaw
>
> On Sat, 27 Nov 2021 at 21:35, Stanislaw Adaszewski
> <s.adaszewski@gmail.com> wrote:
> >
> > Hi Warner,
> >
> >
> > Thanks a lot for the quick reaction - that helps. Accordingly,
> > I have taken several actions (below). If you have any more tips
> > how to push this forward, please let me know. Like I don't know
> > is there a person formally responsible for this kind of
> > contributions? Let's say when you are happy with the general
> > architecture of the solution and the quality of the code
> > (it still requires some polishing) - is that enough to pull
> > the changes into the codebase? How does that work?
> > Thank you in advance!
> >
> >
> > 1. I have rebased my changeset on top of the tip of the
> > FreeBSD's main branch [1]
> >
> >
> > 2. I have changed the /.passphrase_marker convention to hold
> > (instead of the passphrase) a human-readable lower-case digest
> > of SHA256(salt | passphrase) where salt is a new (optional)
> > parameter which can be passed using another EFI variable:
> > KernGeomEliPassphraseFromTpm2Salt.
> >
> > I think it is more for the peace of mind than anything else,
> > as anyone having access to /.passphrase_marker would normally
> > have to be the root user. Nevertheless it is perhaps nicer not
> > to keep raw passphrases laying around in files.
> >
> > We still pass the passphrase to the kernel via a kernel variable
> > kern.geom.eli.passphrase.from_tpm2.passphrase which is unset
> > before the system becomes interactive. I could be easily
> > passing the hash computed by the bootloader instead - what do
> > you think?
> >
> > One thing to keep in mind is that another kernel variable -
> > kern.geom.eli.passphrase has been passed around like this
> > as well and it is being unset precisely in init_main.c.
> >
> > But even more importantly, one has to keep in mind that
> > geli_export_key_buffer() passes all GELI keys to the kernel
> > anyway, so access to the encrypted drives is already possible
> > by that alone. Not to mention that the root user can simply
> > read the driver's master key with a simple geli show.
> >
> >
> > 3) As an explanation, also to @Ka Ho Ng, the /.passphrase_marker
> > serves only as a tag to allow to reliably pair a boot filesystem
> > to the keyphrase retrieved from the TPM. If we were to just
> > retrieve the passphrase and pass it to any boot environment then
> > one would simply boot another OS with another root password and
> > could read all our secrets. The same goes for the root filesystem
> > that is mounted in turn by the kernel. If one would for example
> > remove the boot drive - that would cause the kernel to drop out
> > to interactive mountfrom> prompt and then one could for example
> > boot from another drive. That is unacceptable. Kernel is by default
> > very "boot happy" - it tries really hard to boot SOMETHING rather
> > than accept a strict specification of what is allowed to boot.
> >
> >
> > 4) The code is fully functional and I have tested it quite a bit
> > on a Zotac CI622 mini-PC with the latest BIOS update which enables
> > the TPM functionality on the Intel 10110U process in there. If you
> > have a TPM-capable BIOS and CPU, I would encourage you to try, like
> > this you will understand better how it works and that the design is
> > necessary like it is with the /.passphrase_marker. I am not 100% sure
> > if there are no ways left to fool the system to boot or run some
> > arbitrary code. Such attacks would generally consist of causing some
> > kind of errors on the "trusted" UFS-on-GELI or ZFS-on-GELI systems and
> > making the system drop out into some kind of interactive prompts. I
> > hope that does not happen. For example if one removes the drive during =
init.
> > I would certainly expect that no process drops out to interactive promp=
ts
> > on a system with a root password set bit this kind of scares is
> > a tradeoff of not using full VERIEXEC. It is however very convenient
> > to say - just trust everything on the XXX-on-GELI since this was encryp=
ted
> > and therefore tampering-proof. More tests are necessary but also - VERI=
EXEC
> > can be enabled in addition to that to ensure that such weird scenarios =
do not
> > happen.
> >
> >
> > 5) @Ka Ho Ng what you said is taking place clearly, the code relies on =
a PCR
> > policy of user's choice and uses that to read the passphrase.
> >
> >
> > 6) Regarding ZFS encryption I am not sure if that is supported in the E=
FI
> > bootloader - at first glance I would say that it isn't. With that said,
> > the code can be used to further modify the loader to read any kind of
> > values stored in the TPM and put them in kernel variables for later use
> > in the boot process. Just a big WARNING, kenv seems to let even lusers =
to read
> > the variables. So whatever one would do with them, it would have to be =
done
> > quickly and the variables would need to be discarded before letting
> > the lusers log in.
> >
> >
> > [1] https://github.com/sadaszewski/freebsd-src/compare/main...main-cher=
ry-pick-tpm-support-in-stand
> >
> >
> > Kind regards,
> >
> > --
> > Stanislaw
> >
> > On Sat, 27 Nov 2021 at 18:00, Warner Losh <imp@bsdimp.com> wrote:
> > >
> > >
> > >
> > > On Sat, Nov 27, 2021, 7:36 AM Stanislaw Adaszewski <s.adaszewski@gmai=
l.com> wrote:
> > >>
> > >> Dear All,
> > >>
> > >> Could you please guide me so that we can together integrate
> > >> the following piece of work into the FreeBSD base system?
> > >> Thank you for your time and consideration.
> > >
> > >
> > > See below for some advice.
> > >
> > >> I have created the following bundle of work [1]. The referenced
> > >> patch implements on top of releng/13.0, the support for TPM2
> > >> in the EFI bootloader and in the kernel in order to allow for
> > >> storage and retrievel of a GELI passphrase in a TPM2 module,
> > >> secured with a PCR policy.
> > >>
> > >> The way the bootloader behavior is modified is the following:
> > >>
> > >> 1) before calling efipart_inithandles() an attempt to retrieve the
> > >> passphrase from a TPM2 module might be performed -
> > >> how this is achieved is described later on.
> > >> 2) if a passphrase is indeed retrieved, then after determining
> > >> currdev, the currdev is checked for the presence of a
> > >> /.passphrase_marker file which must contain the same passphrase
> > >> as retrieved from the TPM. This is supposed to ensure that we
> > >> do not end up booting an environment not on the device we just
> > >> unlocked with the passphrase.
> > >> 3a) If all is go, the autoboot_delay is set to -1 in order to preven=
t
> > >> any interaction and continue the boot process of the "safe" environm=
ent,
> > >> a 'kern.geom.eli.passphrase.from_tpm2.passphrase' variable is set
> > >> to the passphrase from TPM in order for kernel use later, as well as=
 a
> > >> kern.geom.eli.passphrase.from_tpm2.was_retrieved'=3D'1' variable.
> > >> 3b) If the passphrase marker does not match, the bootloader cleans u=
p
> > >> GELI keys, the TPM passphrase and kern.geom.eli.passphrase and exits=
.
> > >
> > >
> > > I worry about information disclosure having the pass phrase available=
 on the running system with this setup. Can you explain why that design poi=
nt was selected? Usually there is something signed with a private key that =
the public key can be used to verify instead of a direct comparison like th=
is to prevent disclosure of key material. I've not looked at the code yet, =
so it may already do this...
> > >
> > >> The way the kernel behavior is modified is the following:
> > >>
> > >> 1) In init_main.c, after vfs_mountroot() a check is added
> > >> 2a) If kern.geom.eli.passphrase.from_tpm2.was_retrieved is not
> > >> set to 1, then we do nothing and continue the boot process
> > >> 2b) If the was_retrieved variable is set to '1' then we check for th=
e
> > >> same passphrase marker as the bootloader, its content compared
> > >> against the 'kern.geom.eli.passphrase.from_tpm2.passphrase'
> > >> variable.
> > >> 3a) If all is go, the passphrase variable is unset and the boot proc=
ess
> > >> continues,
> > >> 3c) If the passphrase marker does not match, we panic.
> > >
> > >
> > > I'm sure that main_init should not know about geom or geli. This is u=
sually done with a handler of some sort so the mountroot code can just call=
 the generic handler. Can your code be restructured such that this is possi=
ble?  The reason I ask is that ZFS supports encryption too and it would be =
nice to use that instead of geli.
> > >
> > >> The configuration of the bootloader for this procedure looks the fol=
lowing:
> > >>
> > >> 1) We set an efivar KernGeomEliPassphraseFromTpm2NvIndex
> > >> to contain the TPM2 NV Index we store our passphrase in, e.g. 0x1000=
001
> > >> 2) We set an efivar KernGeomEfiPassphraseFromTpm2PolicyPcr
> > >> to contain the PCR policy used to secure the passphrase, e.g.
> > >> sha256:0,2,4,7
> > >> 3a) If both are set, the bootloader will attempt to retrieve the pas=
sphrase
> > >> and behave in the modified way described above
> > >> 3b) Otherwise, it behaves as the vanilla version and will ask for GE=
LI
> > >> passphrases if necessary
> > >>
> > >> The configuration of the TPM and the passphrase marker looks the fol=
lowing:
> > >>
> > >> 1) echo -n "passphrase" >/.passphrase_marker
> > >> 2) chmod 600 /.passphrase_marker
> > >> 3) tpm2_createpolicy -L policy.digest --policy-pcr -l sha256:0,2,4,7
> > >> 4) tpm2_nvdefine -Q 0x1000001 -s `wc -c /.passphrase_marker` -L
> > >> policy.digest -A "policyread|policywrite"
> > >> 5) tpm2_nvwrite -Q 0x1000001 -i /.passphrase_marker -P pcr:sha256:0,=
2,4,7
> > >>
> > >> [1] https://github.com/sadaszewski/freebsd-src/compare/releng/13.0..=
.tpm-support-in-stand
> > >
> > >
> > > This sounds cool. Any chance you can rebase this to the tip of the ma=
in branch? All code goes into FreeBSD that way and 13.0 is about a year old=
 already.
> > >
> > > Thanks for sharing this. Despite some reservations expressed above, I=
 think this has potential to be quite cool.
> > >
> > > Warner
> > >
> > >>
> > >> Kind regards,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CADxsEsQ0hrPSfEXE4Dt28AWt9mWHu-qieYo0jpnMie=VeRZHnA>