mirror of
https://github.com/systemd/systemd.git
synced 2026-08-12 22:17:19 +00:00
NV indexes created in the storage hierarchy can be undefined and
redefined with TPM owner auth. Because of this, NvPCRs need some way
to prevent them from being redfined in a way that allows spoof
measurements to be replayed.
The current approach requires knowledge of a secret ("anchor secret")
in order to derive the initial NvPCR measurement and to derive a
measurement to an existing PCR (9). The credential is protected by the
TPM with a PCR policy. Without access to the credential, it's not
possible to replay measurements to a newly defined NvPCR without
breaking the binding with the measurement in PCR 9. However, this
approach has a couple of issues:
- The credential is currently only protected by PCR11. As it's not
protected by the rest of the boot chain, it's possible to boot other
operating systems in order to replay the PCR11 measurements and
recover the secret. Note that as the NvPCR anchoring happens in early
boot, the credential is stored in the ESP.
- Someone with privileged access to a system can just create a new
credential containing a known secret and store this in /var/lib and
the ESP. The NvPCRs are anchored with this known secret on subsequent
boots, and therefore the measurements can no longer be trusted.
Imagine the scenario where privileged access is theoretically possible
as a result of some vulnerability. After upgrading the system to fix
this vulnerability, the system should be able to attest that it is
now in a good state. However, if an adversary were able to use their
priviliges to replace the credential, they are able to obtain
persistence and the NvPCR measurements are no longer trustworthy.
This PR changes things to take a different approach. Instead of
requiring knowledge of a secret, the NvPCRs are now created in a way
that requires a policy to be satisfied for writing. The write policy has
2 branches:
- TPM2_PolicyNvWritten(true), which can be satisified without any
further authorization if the NvPCR has already been extended.
- TPM2_PolicyAuthorize(pcrPubKey, SHA256("nvpcr-init")) which can be
satisfied with a signed PCR policy, and must be used to perform the
initial extend to a NvPCR.
The intention here is that the signed PCR policy that can be used to
authorize the initial extend to the NvPCR can only be satisfied during
early boot. During later boot phases, this signed PCR policy must not be
valid. This means that if a NvPCR is undefined and redefined, it won't
be possible to satisfy its write policy in order to able to perform the
initial extend.
In order to anchor the NvPCRs and prevent them from being undefined and
then redefined with a different policy that does allow them to be
extended, the names of the NvPCRs are measured to PCR9. Verifiers must
check that the names of attested NvPCRs match the measurements in PCR9.
This uses the PCR signing key from the currently booted UKI to create
the NvPCRs. If this changes between boots, then tpm2-setup automatically
recreates new NvPCRs with an updated write policy to reflect this. I've
tried to be careful to not undefine arbitrary NV indexes in this case,
so it checks that the existing NV index looks like a NvPCR (ie, it has
the expected attributes) before undefining it.
I did originally try to preserve the old behaviour for existing systems,
but it makes things a lot more complicated. As the new implementation
already creates new NvPCRs when the PCR signing key changes, I ended up
just automatically upgrading the old NvPCRs as well. Again, I check here
that any existing NV index looks like an old style NvPCR (ie, it has the
expected attributes) before undefining it.
I did notice that the initial NvPCR measurement isn't going into the
log. I don't know if that was an intentional choice, but I've preserved
that behaviour in this PR.
This also adds a new option to ukify (--sign-initrd-pcrs) which creates
signed policies (one per PCR bank) that can only be satisfied from the
initrd. These policies are used for initializing the NvPCRs, but can also
be used for protecting TPM2 keyslots enrolled with systemd-cryptenroll
(by using the --tpm2-public-key-policyref=initrd option).
There is one outstanding issue. The NvPCR definitions support different
algorithms, but the use of PolicyAuthorize means that they can only support
SHA-256 for now. This is because the signed policy algorithm must match
the name algorithm, and some additional work is required to support
signed PCR policies for algorithms other than SHA256. I've left a note in
tpm2_nvpcr_initialize that details what's required, and I'll take a look
at that in a subsequent PR.
92 lines
3.8 KiB
XML
92 lines
3.8 KiB
XML
<?xml version="1.0"?>
|
|
<!--*-nxml-*-->
|
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
|
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
|
<refentry id="systemd-tpm2-setup.service" conditional='ENABLE_BOOTLOADER'
|
|
xmlns:xi="http://www.w3.org/2001/XInclude">
|
|
|
|
<refentryinfo>
|
|
<title>systemd-tpm2-setup.service</title>
|
|
<productname>systemd</productname>
|
|
</refentryinfo>
|
|
|
|
<refmeta>
|
|
<refentrytitle>systemd-tpm2-setup.service</refentrytitle>
|
|
<manvolnum>8</manvolnum>
|
|
</refmeta>
|
|
|
|
<refnamediv>
|
|
<refname>systemd-tpm2-setup.service</refname>
|
|
<refname>systemd-tpm2-setup-early.service</refname>
|
|
<refname>systemd-tpm2-setup</refname>
|
|
<refpurpose>Set up the TPM2 Storage Root Key (SRK), Endorsement Key (EK), and initialize NvPCRs at boot</refpurpose>
|
|
</refnamediv>
|
|
|
|
<refsynopsisdiv>
|
|
<para><filename>systemd-tpm2-setup.service</filename></para>
|
|
<para><filename>/usr/lib/systemd/systemd-tpm2-setup</filename></para>
|
|
</refsynopsisdiv>
|
|
|
|
<refsect1>
|
|
<title>Description</title>
|
|
|
|
<para><filename>systemd-tpm2-setup.service</filename> and
|
|
<filename>systemd-tpm2-setup-early.service</filename> are services that generate the Storage Root Key
|
|
(SRK) and Endorsement Key (EK) if they have not been generated yet, and stores them in the TPM. If
|
|
NvPCRs (additional PCR registers in TPM NV Indexes) are defined, these are initialized with the
|
|
anchoring secret.</para>
|
|
|
|
<para>The services will store the public key of the SRK key pair in a PEM file in
|
|
<filename>/run/systemd/tpm2-srk-public-key.pem</filename> and
|
|
<filename>/var/lib/systemd/tpm2-srk-public-key.pem</filename>. They will also store it in TPM2B_PUBLIC
|
|
format in <filename>/run/systemd/tpm2-srk-public-key.tpm2_public</filename> and
|
|
<filename>/var/lib/systemd/tpm2-srk-public-key.tpm2b_public</filename>.</para>
|
|
|
|
<para><filename>systemd-tpm2-setup-early.service</filename> runs very early at boot (possibly in the
|
|
initrd), and writes the SRK public key to <filename>/run/systemd/tpm2-srk-public-key.*</filename> (as
|
|
<filename>/var/</filename> is generally not accessible this early yet), while
|
|
<filename>systemd-tpm2-setup.service</filename> runs during a later boot phase and saves the public key
|
|
to <filename>/var/lib/systemd/tpm2-srk-public-key.*</filename>.</para>
|
|
</refsect1>
|
|
|
|
<refsect1>
|
|
<title>Files</title>
|
|
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term><filename>/run/systemd/tpm2-srk-public-key.pem</filename></term>
|
|
<term><filename>/run/systemd/tpm2-srk-public-key.tpm2b_public</filename></term>
|
|
|
|
<listitem><para>The SRK public key in PEM and TPM2B_PUBLIC format, written during early boot.</para>
|
|
|
|
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><filename>/var/lib/systemd/tpm2-srk-public-key.pem</filename></term>
|
|
<term><filename>/var/lib/systemd/tpm2-srk-public-key.tpm2_public</filename></term>
|
|
|
|
<listitem><para>The SRK public key in PEM and TPM2B_PUBLIC format, written during later boot (once
|
|
<filename>/var/</filename> is available).</para>
|
|
|
|
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><filename>/usr/lib/nvpcr/*.nvpcr</filename></term>
|
|
|
|
<listitem><para>Definition files for NvPCRs.</para>
|
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
</refsect1>
|
|
|
|
<refsect1>
|
|
<title>See Also</title>
|
|
<para><simplelist type="inline">
|
|
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
|
</simplelist></para>
|
|
</refsect1>
|
|
</refentry>
|