Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 30 Nov 2021 13:32:17 +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:  <CADxsEsTDfzJS0e0M8Cqyd_P%2BjRwiU5NJg8VOEd0mgOV=kLuqgQ@mail.gmail.com>
In-Reply-To: <CADxsEsQ0hrPSfEXE4Dt28AWt9mWHu-qieYo0jpnMie=VeRZHnA@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> <CADxsEsQ0hrPSfEXE4Dt28AWt9mWHu-qieYo0jpnMie=VeRZHnA@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
I have submitted a PR: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D=
260138

On Mon, 29 Nov 2021 at 17:12, Stanislaw Adaszewski
<s.adaszewski@gmail.com> wrote:
>
> 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 =
the
> 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 an=
d
> > > making the system drop out into some kind of interactive prompts. I
> > > hope that does not happen. For example if one removes the drive durin=
g init.
> > > I would certainly expect that no process drops out to interactive pro=
mpts
> > > 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 encr=
ypted
> > > and therefore tampering-proof. More tests are necessary but also - VE=
RIEXEC
> > > can be enabled in addition to that to ensure that such weird scenario=
s do not
> > > happen.
> > >
> > >
> > > 5) @Ka Ho Ng what you said is taking place clearly, the code relies o=
n 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=
 EFI
> > > bootloader - at first glance I would say that it isn't. With that sai=
d,
> > > 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 u=
se
> > > in the boot process. Just a big WARNING, kenv seems to let even luser=
s to read
> > > the variables. So whatever one would do with them, it would have to b=
e 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-ch=
erry-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@gm=
ail.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 prev=
ent
> > > >> any interaction and continue the boot process of the "safe" enviro=
nment,
> > > >> 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=
 up
> > > >> GELI keys, the TPM passphrase and kern.geom.eli.passphrase and exi=
ts.
> > > >
> > > >
> > > > I worry about information disclosure having the pass phrase availab=
le on the running system with this setup. Can you explain why that design p=
oint was selected? Usually there is something signed with a private key tha=
t the public key can be used to verify instead of a direct comparison like =
this 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 =
the
> > > >> 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 pr=
ocess
> > > >> 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=
 usually done with a handler of some sort so the mountroot code can just ca=
ll the generic handler. Can your code be restructured such that this is pos=
sible?  The reason I ask is that ZFS supports encryption too and it would b=
e nice to use that instead of geli.
> > > >
> > > >> The configuration of the bootloader for this procedure looks the f=
ollowing:
> > > >>
> > > >> 1) We set an efivar KernGeomEliPassphraseFromTpm2NvIndex
> > > >> to contain the TPM2 NV Index we store our passphrase in, e.g. 0x10=
00001
> > > >> 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 p=
assphrase
> > > >> and behave in the modified way described above
> > > >> 3b) Otherwise, it behaves as the vanilla version and will ask for =
GELI
> > > >> passphrases if necessary
> > > >>
> > > >> The configuration of the TPM and the passphrase marker looks the f=
ollowing:
> > > >>
> > > >> 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 =
main branch? All code goes into FreeBSD that way and 13.0 is about a year o=
ld 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?CADxsEsTDfzJS0e0M8Cqyd_P%2BjRwiU5NJg8VOEd0mgOV=kLuqgQ>