Merge pull request #28891 from poettering/pcrlock

new pcrlock tool for generating signed PCR policies for PCR 0, 1, 4, …
This commit is contained in:
Luca Boccassi 2023-11-03 16:07:43 +00:00 committed by GitHub
commit 8d04721507
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 8548 additions and 118 deletions

95
TODO
View file

@ -147,6 +147,23 @@ Features:
root=nvme:<trtype>:<traddr>:<trsvcid>:<nqn>:<partition> to boot directly from
nvme-oF
* pcrlock:
- make signed PCR work together with pcrlock
- add kernel-install plugin that automatically creates UKI .pcrlock file when
UKI is installed, and removes it when it is removed again
- automatically install PE measurement of sd-boot on "bootctl install"
- write generated pcrlock signature files to the ESP as credential, one for
each installed OS & pick up generated pcrlock signature file in sd-stub,
pass it via initrd to OS
- pre-calc sysext + kernel cmdline measurements
- pre-calc cryptsetup root key measurement
- maybe make systemd-repart generate .pcrlock for old and new GPT header in
/run?
- Add support for more than 8 branches per PCR OR
- add "systemd-pcrlock lock-kernel-current" or so which synthesizes .pcrlock
policy from currently booted kernel/event log, to close gap for first boot
for pre-built images
* add a new systemd-project@.service that is very similar to user@.service but
uses DynamicUser=1 and no PAMName= to invoke an unprivileged somewhat
light-weight service manager. Use HOME=/var/lib/systemd/projects/%i as home
@ -299,54 +316,6 @@ Features:
* systemd-mount should only consider modern file systems when mounting, similar
to systemd-dissect
* new "systemd-pcrlock" component for dealing with PCR4. Design idea:
1. define /{etc,usr,var/lib}/pcrlock.d/<component>/<version>.pcrlock
2. these files contain list of hashes that will be measured when component is
run, per PCR
3. each component involved in the boot that is deterministically measured can
place one or more of these files in those dirs (shim, sd-boot,
sd-stub/UKI, cryptsetup, pcrphase, pcrfs, …)
4. since each component has its own dir, with multiple files in them, package
such as kernels (of which there can be multiple installed at the same
time) can be grouped together: only one of them is measured at a time.
5. whenever a new component is added or an old one removed, or the PCR lock
shall be relaxed or tightened the systemd-pcrlock tool is invoked.
6. tool iterates through all these files, orders them alphabetically by
component, then matches them up with current measurements (as per uefi
event log), identifying by hash, accepting that the "beginning" of the
measurements might not be recognizable.
7. Then calculates expected PCR values starting with the "unrecognized
head" from the event log, then continuing with all of components
defined via the .pcrlock files (but dropping out the "recognized tail"
from the uefi event log). (This might mean combinatorial explosion, if
there are multiple shims, multiple sd-boot, and so on.)
8. Generates a public/private key pair on the TPM
9. Generates a counter object in the TPM, with a policy that allows only
one-by-one increase with signature policy by the public/private key pair.
10. now signs policies of all expected PCR values with the generated keypair,
using all combinations of components defined in the .pcrlock files
restricting it to the counter + 1.
11. locks down the keypair with a signed policy with its own public key
12. generates JSON file of all these policies with their signatures, drops
them as singleton in ESP
13. increases the counter by one.
14. after boot sd-stub picks JSON up from ESP, passes it to userspace via
.extra
15. JSON contained policies can now be used to unlock disk as well as the
public/key itself for signing further policies, as well as increment for
the counter
16. whenever any of the components above is added/removed new JSON file with
signatures for counter + 1 is generated, dropped in ESP, then counter
increased. (i.e. this means the "recognized tail" of the event log is
deterministically swapped out)
17. when firmware update is expected, relaxed signed policy is generated for
next boot only valid if counter is increased (this means the
"unrecognized head" for the event log can change without losing access)
18. on every boot checks if releaxed policy is in effect, if so, new strict
policy is generated and counter increased.
Net result: Removes downgrade attack surface + Locks OS to firmware + Allows
downgrades within bounds
* add another PE section ".fname" or so that encodes the intended filename for
PE file, and validate that when loading add-ons and similar before using
it. This is particularly relevant when we load multiple add-ons and want to
@ -469,30 +438,6 @@ Features:
* SIGRTMIN+18 and memory pressure handling should still be added to: hostnamed,
localed, oomd, timedated.
* in order to make binding to PCR 4 realistic:
- generate one keypair "U" and store it in a tpm2 nvindex.
- Generate another keypair "P" and store it in a second tpm2 nvindex.
- allocate a persistent counter object "C" in the tpm2
- Enroll all user objects (i.e. luks volumes, creds, …) to a tpm2 policy
signed by U.
- Lock both U and P down with a tpm2 policy signed by P (yes, P can only be
used if a signature by P itself can be provided)
- For regular reboots generate a signature for a restrictive PCR4 + counter C
based policy with key P. Place signature in EFI var, so it can be found on
next boot
- For reboots where a firmware update is expected generate a signature with a
more open policy against just counter C. Place signature in same EFI var.
- Increase C whenever switching between these two signature types.
- During early boot, use the signature from the EFI var to unlock U and P.
Use it to generate a signature for unlocking user objects given the current
PCR 4 value, store that away into /run somewhere, for user during the whole
later boot.
- When booting up automatically update the mentioned efi var so that it
contains the restrictive signature. But also generate a signature ahead of
time that could be used in case during the current boot we later detect we might
need to reboot for a firmware update. Store that in /run somewhere, so that
it can be placed in the EFI var, if needed.
* repart/gpt-auto/DDIs: maybe introduce a concept of "extension" partitions,
that have a new type uuid and can "extend" earlier partitions, to work around
the fact that systemd-repart can only grow the last partition defined. During
@ -1027,12 +972,6 @@ Features:
set up the directory so that it can only be accessed if host and app are in
order.
* TPM2: extend unlock policy to protect against version downgrades in signed
policies: policy probably must take some nvram based generation counter into
account that can only monotonically increase and can be used to invalidate
old PCR signatures. Otherwise people could downgrade to old signed PCR sets
whenever they want.
* update HACKING.md to suggest developing systemd with the ideas from:
https://0pointer.net/blog/testing-my-system-code-in-usr-without-modifying-usr.html
https://0pointer.net/blog/running-an-container-off-the-host-usr.html

View file

@ -808,6 +808,22 @@
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry>
<varlistentry>
<term><option>tpm2-pcrlock=</option></term>
<listitem><para>Takes an absolute path to a TPM2 pcrlock policy file, as produced by the
<citerefentry><refentrytitle>systemd-pcrlock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
tool. This permits locking LUKS2 volumes to a local policy of allowed PCR values with
variants. See
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for details on enrolling TPM2 pcrlock policies. If this option is not specified but it is attempted
to unlock a LUKS2 volume with a TPM2 pcrlock enrollment a suitable signature file
<filename>pcrlock.json</filename> is searched for in <filename>/run/systemd/</filename> and
<filename>/var/lib/systemd/</filename> (in this order).</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>tpm2-measure-pcr=</option></term>

View file

@ -1004,6 +1004,16 @@ manpages = [
['systemd-nspawn', '1', [], ''],
['systemd-oomd.service', '8', ['systemd-oomd'], 'ENABLE_OOMD'],
['systemd-path', '1', [], ''],
['systemd-pcrlock',
'8',
['systemd-pcrlock-file-system.service',
'systemd-pcrlock-firmware-code.service',
'systemd-pcrlock-firmware-config.service',
'systemd-pcrlock-machine-id.service',
'systemd-pcrlock-make-policy.service',
'systemd-pcrlock-secureboot-authority.service',
'systemd-pcrlock-secureboot-policy.service'],
'ENABLE_BOOTLOADER'],
['systemd-pcrphase.service',
'8',
['systemd-pcrextend',
@ -1139,6 +1149,7 @@ manpages = [
['systemd.nspawn', '5', [], ''],
['systemd.offline-updates', '7', [], ''],
['systemd.path', '5', [], ''],
['systemd.pcrlock', '5', ['systemd.pcrlock.d'], ''],
['systemd.preset', '5', [], ''],
['systemd.resource-control', '5', [], ''],
['systemd.scope', '5', [], ''],

View file

@ -519,6 +519,20 @@
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--tpm2-pcrlock=</option><arg>PATH</arg></term>
<listitem><para>Configures a TPM2 pcrlock policy to bind encryption to. Expects a path to a pcrlock
policy file as generated by the
<citerefentry><refentrytitle>systemd-pcrlock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
tool. If a TPM2 device is enrolled and this option is not used but a file
<filename>pcrlock.json</filename> is found in <filename>/run/systemd/</filename> or
<filename>/var/lib/systemd/</filename> it is automatically used. Assign an empty string to turn this
behaviour off.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--wipe-slot=</option><arg rep="repeat">SLOT</arg></term>

559
man/systemd-pcrlock.xml Normal file
View file

@ -0,0 +1,559 @@
<?xml version="1.0"?>
<!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-pcrlock" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='ENABLE_BOOTLOADER'>
<refentryinfo>
<title>systemd-pcrlock</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-pcrlock</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-pcrlock</refname>
<refname>systemd-pcrlock-file-system.service</refname>
<refname>systemd-pcrlock-firmware-code.service</refname>
<refname>systemd-pcrlock-firmware-config.service</refname>
<refname>systemd-pcrlock-machine-id.service</refname>
<refname>systemd-pcrlock-make-policy.service</refname>
<refname>systemd-pcrlock-secureboot-authority.service</refname>
<refname>systemd-pcrlock-secureboot-policy.service</refname>
<refpurpose>Analyze and predict TPM2 PCR states and generate an access policy from the prediction</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>/usr/lib/systemd/systemd-pcrlock <arg choice="opt" rep="repeat">OPTIONS</arg></command>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>Note: this command is experimental for now. While it is likely to become a regular component of
systemd, it might still change in behaviour and interface.</para>
<para><command>systemd-pcrlock</command> is a tool that may be used to analyze and predict TPM2 PCR
measurements, and generate TPM2 access policies from the prediction which it stores in a TPM2 NV index
(i.e. in the TPM2 non-volatile memory). This may then be used to restrict access to TPM2 objects (such as
disk encryption keys) to system boot-ups in which only specific, trusted components are used.</para>
<para><command>systemd-pcrlock</command> uses as input for its analysis and prediction:</para>
<itemizedlist>
<listitem><para>The UEFI firmware TPM2 event log
(i.e. <filename>/sys/kernel/security/tpm0/binary_bios_measurements</filename>) of the current
boot.</para></listitem>
<listitem><para>The userspace TPM2 event log
(i.e. <filename>/run/log/systemd/tpm2-measure.log</filename>) of the current
boot.</para></listitem>
<listitem><para>The current PCR state of the TPM2 chip.</para></listitem>
<listitem><para>Boot component definition files (<filename>*.pcrlock</filename> and
<filename>*.pcrlock.d/*.pcrlock</filename>, see
<citerefentry><refentrytitle>systemd.pcrlock</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
that each define expected measurements for one component of the boot process, permitting alternative
variants for each. (Variants may be used used to bless multiple kernel versions or boot loader versions
at the same time.)</para></listitem>
</itemizedlist>
<para>It uses these inputs to generate a combined event log, validating it against the PCR states. It
then attempts to recognize event log records and matches them against the defined components. For each PCR
where this can be done comprehensively (i.e. where all listed records and all defined components have
been matched) this may then be used to predict future PCR measurements, taking the alternative variants
defined for each component into account. This prediction may then be converted into a TPM2 access policy
(consisting of TPM2 <function>PolicyPCR</function> and <function>PolicyOR</function> items), which is
then stored in an NV index in the TPM2. This may be used to then lock secrets (such as disk encryption
keys) to these policies (via a TPM2 <function>PolicyAuthorizeNV</function> policy).</para>
<para>Use tools such as
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
or <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry> to
bind disk encryption to such a <command>systemd-pcrlock</command> TPM2 policy. Specifically, see the
<option>--tpm2-pcrlock=</option> switches of these tools.</para>
<para>The access policy logic requires a TPM2 device that implements the
<literal>PolicyAuthorizeNV</literal> command, i.e. implements TPM 2.0 version 1.38 or newer.</para>
</refsect1>
<refsect1>
<title>Commands</title>
<para>The following commands are understood:</para>
<variablelist>
<varlistentry>
<term><command>log</command></term>
<listitem><para>This reads the combined TPM2 event log, validates it, matches it against the current
PCR values, and outputs both in tabular form. Combine with <option>--json=</option> to generate
output in JSON format.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><command>cel</command></term>
<listitem><para>This reads the combined TPM2 event log and writes it to STDOUT in <ulink
url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Common Event Log
Format (CEL-JSON)</ulink> format.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><command>list-components</command></term>
<listitem><para>Shows a list of component definitions and their variants, i.e. the
<filename>*.pcrlock</filename> files discovered in <filename>/var/lib/pcrlock.d/</filename>,
<filename>/usr/lib/pcrlock.d/</filename>, and the other supported directories. See
<citerefentry><refentrytitle>systemd.pcrlock</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details on these files and the full list of directories searched.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><command>predict</command></term>
<listitem><para>Predicts the PCR state on future boots. This will analyze the TPM2 event log as
described above, recognize components, and then generate all possible resulting PCR values for all
combinations of component variants. Note that no prediction is made for PCRs whose value does not
match the event log records, for which unrecognized measurements are discovered or for which
components are defined that cannot be found in the event log. This is a safety measure to ensure that
any generated access policy can be fulfilled correctly on current and future boots.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>make-policy</command></term>
<listitem><para>This predicts the PCR state for future boots, much like the
<command>predict</command> command above. It then uses this data to generate a TPM2 access policy
which it stores in a TPM2 NV index. The prediction and information about the used TPM2 and its NV
index are written to <filename>/var/lib/systemd/pcrlock.json</filename>.</para>
<para>The NV index is allocated on first invocation, and updated on subsequent invocations.</para>
<para>The NV index contents may be changed (and thus the policy stored in it updated) by providing an
access PIN. This PIN is normally generated automatically and stored in encrypted form (with an access
policy binding it to the NV index itself) in the aforementioned JSON policy file. This PIN may be
chosen by the user, via the <option>--recovery-pin=</option> switch. If specified it may be used as
alternative path of access to update the policy.</para>
<para>If the new prediction matches the old this command terminates quickly and executes no further
operation. (Unless <option>--force</option> is specified, see below.)</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>remove-policy</command></term>
<listitem><para>Removes a previously generated policy. Deletes the
<filename>/var/lib/systemd/pcrlock.json</filename> file, and deallocates the NV index.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-firmware-code</command></term>
<term><command>unlock-firmware-code</command></term>
<listitem><para>Generates/removes <filename>.pcrlock</filename> files based on the TPM2 event log of
the current boot covering all records for PCRs 0 ("platform-code") and 2 ("external-code").</para>
<para>This operation allows locking the boot process to the current version of the firmware of the
system and its extension cards. This operation should only be used if the system vendor does not
provide suitable pcrlock data ahead of time.</para>
<para>Note that this data only matches the current version of the firmware. If a firmware update is
applied this data will be out-of-date and any access policy generated from it will no longer pass. It
is thus recommended to invoke <command>unlock-firmware-code</command> before doing a firmware update,
followed by <command>make-policy</command> to refresh the policy.</para>
<para><command>systemd-pcrlock lock-firmware-code</command> is invoked automatically at boot via the
<filename>systemd-pcrlock-firmware-code.service</filename> unit, if enabled. This ensures that an
access policy managed by <command>systemd-pcrlock</command> is automatically locked to the new
firmware version whenever the policy has been relaxed temporarily, in order to cover for firmware
updates, as described above.</para>
<para>The files are only generated from the event log if the event log matches the current TPM2 PCR
state.</para>
<para>This writes/removes the files
<filename>/var/lib/pcrlock.d/250-firmware-code-early.pcrlock.d/generated.pcrlock</filename> and
<filename>/var/lib/pcrlock.d/550-firmware-code-late.pcrlock.d/generated.pcrlock</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-firmware-config</command></term>
<term><command>unlock-firmware-config</command></term>
<listitem><para>This is similar to
<command>lock-firmware-code</command>/<command>unlock-firmware-code</command> but locks down the
firmware configuration, i.e. PCRs 1 ("platform-config") and 3 ("external-config").</para>
<para>This functionality should be used with care as in most scenarios a minor firmware configuration
change should not invalidate access policies to TPM2 objects. Also note that some systems measure
unstable and unpredictable information (e.g. current CPU voltages, temperatures, as part of SMBIOS
data) to these PCRs, which means this form of lockdown cannot be used reliably on such systems. Use
this functionality only if the system and hardware is well known and does not suffer by these
limitations, for example in virtualized environments.</para>
<para>Use <command>unlock-firmware-config</command> before making firmware configuration changes. If
the <filename>systemd-pcrlock-firmware-config.service</filename> unit is enabled it will
automatically generate a pcrlock file from the new measurements.</para>
<para>This writes/removes the files
<filename>/var/lib/pcrlock.d/250-firmware-config-early.pcrlock.d/generated.pcrlock</filename> and
<filename>/var/lib/pcrlock.d/550-firmware-config-late.pcrlock.d/generated.pcrlock</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-secureboot-policy</command></term>
<term><command>unlock-secureboot-policy</command></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on the SecureBoot policy
currently enforced. This looks at the SecureBoot, PK, KEK, db, dbx, dbt, dbr EFI variables and
predicts their measurements to PCR 7 ("secure-boot-policy") on the next boot.</para>
<para>Use <command>unlock-firmware-config</command> before applying SecureBoot policy updates. If
the <filename>systemd-pcrlock-secureboot-policy.service</filename> unit is enabled it will
automatically generate a pcrlock file from the policy discovered.</para>
<para>This writes/removes the file
<filename>/var/lib/pcrlock.d/230-secureboot-policy.pcrlock.d/generated.pcrlock</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-secureboot-authority</command></term>
<term><command>unlock-secureboot-authority</command></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on the SecureBoot
authorities used to validate the boot path. SecureBoot authorities are the specific SecureBoot
database entries that where used to validate the UEFI PE binaries executed at boot. This looks at the
event log of the current boot, and uses relevant measurements on PCR 7
("secure-boot-policy").</para>
<para>This writes/removes the file
<filename>/var/lib/pcrlock.d/620-secureboot-authority.pcrlock.d/generated.pcrlock</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-gpt</command> <arg choice="opt"><replaceable>DEVICE</replaceable></arg></term>
<term><command>unlock-gpt</command></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on the GPT partition
table of the specified disk. If no disk is specified automatically determines the block device
backing the root file system. This locks the state of the disk partitioning of the booted medium,
which firmware measures to PCR 5 ("boot-loader-config").</para>
<para>This writes/removes the file
<filename>/var/lib/pcrlock.d/600-gpt.pcrlock.d/generated.pcrlock</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-pe</command> <arg choice="opt"><replaceable>BINARY</replaceable></arg></term>
<term><command>unlock-pe</command></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on the specified PE
binary. This is useful for predicting measurements the firmware makes to PCR 4 ("boot-loader-code")
if the specified binary is part of the UEFI boot process. Use this on boot loader binaries and
suchlike. Use <command>lock-uki</command> (see below) for PE binaries that are unified kernel images
(UKIs).</para>
<para>Expects a path to the PE binary as argument. If not specified, reads the binary from STDIN
instead.</para>
<para>The pcrlock file to write must be specified via the <option>--pcrlock=</option> switch.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-uki</command> <arg choice="opt"><replaceable>UKI</replaceable></arg></term>
<term><command>unlock-uki</command></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on the specified UKI PE
binary. This is useful for predicting measurements the firmware makes to PCR 4 ("boot-loader-code"),
and <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>
makes to PCR 11 ("kernel-boot"), if the specified UKI is booted. This is a superset of
<command>lock-pe</command>.</para>
<para>Expects a path to the UKI PE binary as argument. If not specified, reads the binary from STDIN
instead.</para>
<para>The pcrlock file to write must be specified via the <option>--pcrlock=</option> switch.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-machine-id</command></term>
<term><command>unlock-machine-id</command></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on
<filename>/etc/machine-id</filename>. This is useful for predicting measurements
<citerefentry><refentrytitle>systemd-pcrmachine.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes to PCR 15 ("system-identity").</para>
<para>This writes/removes the file
<filename>/var/lib/pcrlock.d/820-machine-id.pcrlock</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-file-system</command> <arg choice="opt"><replaceable>PATH</replaceable></arg></term>
<term><command>unlock-file-system</command> <arg choice="opt"><replaceable>PATH</replaceable></arg></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on file system
identity. This is useful for predicting measurements
<citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes to PCR 15 ("system-identity") for the root and <filename>/var/</filename> file systems.</para>
<para>This writes/removes the files
<filename>/var/lib/pcrlock.d/830-root-file-system.pcrlock</filename> and
<filename>/var/lib/pcrlock.d/840-file-system-<replaceable>path</replaceable>.pcrlock</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-kernel-cmdline</command> <arg choice="opt"><replaceable>FILE</replaceable></arg></term>
<term><command>unlock-kernel-cmdline</command></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on
<filename>/proc/cmdline</filename> (or the specified file if given). This is useful for predicting
measurements the Linux kernel makes to PCR 9 ("kernel-initrd").</para>
<para>This writes/removes the file
<filename>/var/lib/pcrlock.d/710-kernel-cmdline.pcrlock/generated.pcrlock</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-kernel-initrd</command> <replaceable>FILE</replaceable></term>
<term><command>unlock-kernel-initrd</command></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on a kernel initrd cpio
archive. This is useful for predicting measurements the Linux kernel makes to PCR 9
("kernel-initrd"). Do not use for <command>systemd-stub</command> UKIs, as the initrd is combined
dynamically from various sources and hence does not take a single input, like this command.</para>
<para>This writes/removes the file
<filename>/var/lib/pcrlock.d/720-kernel-initrd.pcrlock/generated.pcrlock</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lock-raw</command> <arg choice="opt"><replaceable>FILE</replaceable></arg></term>
<term><command>unlock-raw</command></term>
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on raw binary data. The
data is either read from the specified file or from STDIN (if none is specified). This requires that
<option>--pcrs=</option> is specified. The generated pcrlock file is written to the file specified
via <option>--pcrlock=</option> or to STDOUT (if none is specified).</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood:</para>
<variablelist>
<varlistentry>
<term><option>--raw-description</option></term>
<listitem><para>When displaying the TPM2 event log do not attempt to decode the records to provide a
friendly event log description string. Instead, show the binary payload data in escaped form.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--pcr=</option></term>
<listitem><para>Specifies the PCR number to use. May be specified more than once to select multiple
PCRs.</para>
<para>This is used by <command>lock-raw</command> and <command>lock-pe</command> to select the
PCR to lock against.</para>
<para>If used with <command>predict</command> and <command>make-policy</command> this will override
which PCRs to include in the prediction and policy. If unspecified this defaults to PCRs 0-5, 7,
11-15. Note that these commands will not include any PCRs in the prediction/policy (even if specified
explicitly) if there are measurements in the event log that do not match the current PCR value, or
there are unrecognized measurements in the event log, or components define measurements not seen in
the event log.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--nv-index=</option></term>
<listitem><para>Specifies to NV index to store the policy in. Honoured by
<command>make-policy</command>. If not specified the command will automatically pick a free NV
index.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--components=</option></term>
<listitem><para>Takes a path to read <filename>*.pcrlock</filename> and
<filename>*.pcrlock.d/*.pcrlock</filename> files from. May be used more than once to specify multiple
such directories. If not specified defaults to <filename>/etc/pcrlock.d/</filename>,
<filename>/run/pcrlock.d/</filename>, <filename>/var/lib/pcrlock.d/</filename>,
<filename>/usr/local/pcrlock.d/</filename>, <filename>/usr/lib/pcrlock.d/</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--location=</option></term>
<listitem><para>Takes either a string or a colon-separated pair of strings. Configures up to which
point in the sorted list of defined components to analyze/predict PCRs to. Typically, the
<command>systemd-pcrlock</command> tool is invoked from a fully booted system after boot-up and
before shutdown. This means various components that are defined for shutdown have not been measured
yet, and should not be searched for. This option allows to restrict which components are considered
for analysis (taking only components before some point into account, ignoring components after
them). The expected string is ordered against the filenames of the components defined. Any components
with a lexicographically later name are ignored. This logic applies to the <command>log</command>,
<command>predict</command>, and <command>make-policy</command> verbs. If a colon-separated pair of
strings are specified then they select which phases of the boot to include in the
prediction/policy. The first string defines where the first prediction shall be made, and the second
string defines where the last prediction shall be made. All such predictions are then combined into
one set.</para>
<para>If used with <command>list-components</command> the selected location range will be highlighted
in the component list.</para>
<para>Defaults to <literal>760-:940-</literal>, which means the policies generated by default will
basically cover the whole runtime of the OS userspace, from the initrd (as <literal>760-</literal>
closely follows <filename>750-enter-initrd.pcrlock</filename>) until (and including) the main runtime
of the system (as <literal>940-</literal> is closely followed by
<filename>950-shutdown.pcrlock</filename>). See
<citerefentry><refentrytitle>systemd.pcrlock</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for a full list of well-known components, that illustrate where this range is placed by
default.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--recovery-pin=</option></term>
<listitem><para>Takes a boolean. Defaults to false. Honoured by <command>make-policy</command>. If
true, will query the user for a PIN to unlock the TPM2 NV index with. If no policy was created before
this PIN is used to protect the newly allocated NV index. If a policy has been created before the PIN
is used to unlock write access to the NV index. If this option is not used a PIN is automatically
generated. Regardless if user supplied or automatically generated, it is stored in encrypted form in
the policy metadata file. The recovery PIN may be used to regain write access to an NV index in case
the access policy became out of date.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--pcrlock=</option></term>
<listitem><para>Takes a file system path as argument. If specified overrides where to write the
generated pcrlock data to. Honoured by the various <command>lock-*</command> commands. If not
specified, a default path is generally used, as documented above.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--policy=</option></term>
<listitem><para>Takes a file system path as argument. If specified overrides where to write pcrlock
policy metadata to. If not specified defaults to
<filename>/var/lib/systemd/pcrlock.json</filename>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--force</option></term>
<listitem><para>If specified with <command>make-policy</command>, the predicted policy will be
written to the NV index even if it is detected to be the same as the previously stored
one.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="json" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
<refsect1>
<title>Exit status</title>
<para>On success, 0 is returned, a non-zero failure code otherwise.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.pcrlock</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-pcrmachine.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View file

@ -388,6 +388,16 @@
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--tpm2-pcrlock=</option><arg>PATH</arg></term>
<listitem><para>Configures a TPM2 pcrlock policy to bind encryption to. See
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for details on this option.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--split=</option><arg>BOOL</arg></term>

298
man/systemd.pcrlock.xml Normal file
View file

@ -0,0 +1,298 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="systemd.pcrlock"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd.pcrlock</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd.pcrlock</refentrytitle>
<manvolnum>5</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd.pcrlock</refname>
<refname>systemd.pcrlock.d</refname>
<refpurpose>PCR measurement prediction files</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><literallayout>
<filename>/etc/pcrlock.d/*.pcrlock</filename>
<filename>/etc/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
<filename>/run/pcrlock.d/*.pcrlock</filename>
<filename>/run/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
<filename>/var/lib/pcrlock.d/*.pcrlock</filename>
<filename>/var/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
<filename>/usr/local/pcrlock.d/*.pcrlock</filename>
<filename>/usr/local/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
<filename>/usr/lib/pcrlock.d/*.pcrlock</filename>
<filename>/usr/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></literallayout></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>*.pcrlock</filename> files define expected TPM2 PCR measurements of components involved
in the boot
process. <citerefentry><refentrytitle>systemd-pcrlock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
uses such pcrlock files to analyze and predict TPM2 PCR measurements. The pcrlock files are JSON arrays
that follow a subset of the <ulink
url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Common Event Log Format
(CEL-JSON)</ulink> specification. Specifically the <literal>recnum</literal>, <literal>content</literal>,
and <literal>content_type</literal> record fields are not used and ignored if present. Each pcrlock file
defines one set of expected, ordered PCR measurements of a specific component of the boot.</para>
<para>*.pcrlock files may be placed in various <filename>.d/</filename> drop-in directores (see above for
a full list). All matching files discovered in these directories are sorted alphabetically by their file
name (without taking the actual directory they were found in into account): pcrlock files with
alphabetically earlier names are expected to cover measurements done before those with alphabetically
later names. In order to make positioning pcrlock files in the boot process convenient the files are
expected (by convention, this is not enforced) to be named
<literal><replaceable>NNN</replaceable>-<replaceable>component</replaceable>.pcrlock</literal> (where
<replaceable>NNN</replaceable> is a three-digit decimal number), for example
<filename>750-enter-initrd.pcrlock</filename>.</para>
<para>For various components of the boot process more than one alternative pcrlock file shall be
supported (i.e. "variants"). For example to cover multiple kernels installed in parallel in the access
policy, or multiple versions of the boot loader. This can be done by placing
<filename>*.pcrlock.d/*.pcrlock</filename> in the drop-in dirs, i.e. a common directory for a specific
component, that contains one or more pcrlock files each covering one <emphasis>variant</emphasis> of the
component. Example: <filename>650-kernel.pcrlock.d/6.5.5-200.fc38.x86_64.pcrlock</filename> and
<filename>650-kernel.pcrlock.d/6.5.7-100.fc38.x86_64.pcrlock</filename></para>
<para>Use <command>systemd-pcrlock list-components</command> to list all pcrlock files currently
installed.</para>
<para>Use the various <command>lock-*</command> commands of <command>systemd-pcrlock</command> to
automatically generate suitable pcrlock files for various types of resources.</para>
</refsect1>
<refsect1>
<title>Well-known Components</title>
<para>Components of the boot process may be defined freely by the administrator or OS vendor. The
following components are well-known however, and are defined by systemd. The list below is useful for
ordering local pcrlock files properly against these components of the boot.</para>
<variablelist>
<varlistentry>
<term><filename>240-secureboot-policy.pcrlock</filename></term>
<listitem><para>The SecureBoot policy, as recorded to PCR 7. May be generated via
<command>systemd-pcrlock lock-secureboot-policy</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>250-firmware-code-early.pcrlock</filename></term>
<listitem><para>Firmware code measurements, as recorded to PCR 0 and 2, up to the separator
measurement (see <filename>400-secureboot-separator.pcrlock.</filename> below). May be generated via
<command>systemd-pcrlock lock-firmware-code</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>250-firmware-config-early.pcrlock</filename></term>
<listitem><para>Firmware configuration measurements, as recorded to PCR 1 and 3, up to the separator
measurement (see <filename>400-secureboot-separator.pcrlock.</filename> below). May be generated via
<command>systemd-pcrlock lock-firmware-config</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>350-action-efi-application.pcrlock</filename></term>
<listitem><para>The EFI "Application" measurement done once by the firmware. Statically defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>400-secureboot-separator.pcrlock</filename></term>
<listitem><para>The EFI "separator" measurement on PCR 7 done once by the firmware to indicate where
firmware control transitions into boot loader/OS control. Statically defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>500-separator.pcrlock</filename></term>
<listitem><para>The EFI "separator" measurements on PCRs 0-6 done once by the firmware to indicate
where firmware control transitions into boot loader/OS control. Statically defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>550-firmware-code-late.pcrlock</filename></term>
<listitem><para>Firmware code measurements, as recorded to PCR 0 and 2, after the separator
measurement (see <filename>400-secureboot-separator.pcrlock.</filename> above). May be generated via
<command>systemd-pcrlock lock-firmware-code</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>550-firmware-config-late.pcrlock</filename></term>
<listitem><para>Firmware configuration measurements, as recorded to PCR 1 and 3, after the separator
measurement (see <filename>400-secureboot-separator.pcrlock.</filename> above). May be generated via
<command>systemd-pcrlock lock-firmware-config</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>600-gpt.pcrlock</filename></term>
<listitem><para>The GPT partition table of the booted medium, as recorded to PCR 5 by the
firmware. May be generated via <command>systemd-pcrlock lock-gpt</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>620-secureboot-authority.pcrlock</filename></term>
<listitem><para>The SecureBoot authority, as recorded to PCR 7. May be generated via
<command>systemd-pcrlock lock-secureboot-authority</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>700-action-efi-exit-boot-services.pcrlock</filename></term>
<listitem><para>The EFI action generated when <function>ExitBootServices()</function> is generated,
i.e. the UEFI environment is left and the OS takes over. Covers the PCR 5 measurement. Statically
defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>710-kernel-cmdline.pcrlock</filename></term>
<listitem><para>The kernel command line, as measured by the Linux kernel to PCR 9. May be generated
via <command>systemd-pcrlock lock-kernel-cmdline</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>720-kernel-initrd.pcrlock</filename></term>
<listitem><para>The kernel initrd, as measured by the Linux kernel to PCR 9. May be generated
via <command>systemd-pcrlock lock-kernel-initrd</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>750-enter-initrd.pcrlock</filename></term>
<listitem><para>The measurement to PCR 11
<citerefentry><refentrytitle>systemd-pcrphase-initrd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes when the initrd initializes. Statically defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>800-leave-initrd.pcrlock</filename></term>
<listitem><para>The measurement to PCR 11
<citerefentry><refentrytitle>systemd-pcrphase-initrd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes when the initrd finishes. Statically defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>820-machine-id.pcrlock</filename></term>
<listitem><para>The measurement to PCR 15
<citerefentry><refentrytitle>systemd-pcrmachine.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes at boot, covering <filename>/etc/machine-id</filename> contents. May be generated via
<command>systemd-pcrlock lock-machine-id</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>830-root-file-system.pcrlock</filename></term>
<listitem><para>The measurement to PCR 15
<citerefentry><refentrytitle>systemd-pcrfs-root.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes at boot, covering the root file system identity. May be generated
via <command>systemd-pcrlock lock-file-system</command>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>850-sysinit.pcrlock</filename></term>
<listitem><para>The measurement to PCR 11
<citerefentry><refentrytitle>systemd-pcrphase-sysinit.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes when the main userspace did basic initialization and will now proceed to start regular system
services. Statically defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>900-ready.pcrlock</filename></term>
<listitem><para>The measurement to PCR 11
<citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes when the system fully booted up. Statically defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>950-shutdown.pcrlock</filename></term>
<listitem><para>The measurement to PCR 11
<citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes when the system begins shutdown. Statically defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><filename>990-final.pcrlock</filename></term>
<listitem><para>The measurement to PCR 11
<citerefentry><refentrytitle>systemd-pcrphase-sysinit.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
makes when the system is close to finishing shutdown. Statically defined.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-pcrlock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View file

@ -174,6 +174,7 @@ profiledir = libexecdir / 'portable' / 'profile'
repartdefinitionsdir = libexecdir / 'repart/definitions'
ntpservicelistdir = prefixdir / 'lib/systemd/ntp-units.d'
credstoredir = prefixdir / 'lib/credstore'
pcrlockdir = prefixdir / 'lib/pcrlock.d'
configfiledir = get_option('configfiledir')
if configfiledir == ''
@ -2176,6 +2177,7 @@ subdir('src/oom')
subdir('src/partition')
subdir('src/path')
subdir('src/pcrextend')
subdir('src/pcrlock')
subdir('src/portable')
subdir('src/pstore')
subdir('src/quotacheck')

View file

@ -13,12 +13,14 @@
#include "efivars-fundamental.h"
#include "time-util.h"
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
#define EFI_VENDOR_LOADER_STR SD_ID128_MAKE_UUID_STR(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
#define EFI_VENDOR_GLOBAL_STR SD_ID128_MAKE_UUID_STR(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
#define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
#define EFI_VENDOR_SYSTEMD_STR SD_ID128_MAKE_UUID_STR(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
#define EFI_VENDOR_LOADER_STR SD_ID128_MAKE_UUID_STR(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
#define EFI_VENDOR_GLOBAL_STR SD_ID128_MAKE_UUID_STR(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
#define EFI_VENDOR_DATABASE SD_ID128_MAKE(d7,19,b2,cb,3d,3a,45,96,a3,bc,da,d0,0e,67,65,6f)
#define EFI_VENDOR_DATABASE_STR SD_ID128_MAKE_UUID_STR(d7,19,b2,cb,3d,3a,45,96,a3,bc,da,d0,0e,67,65,6f)
#define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
#define EFI_VENDOR_SYSTEMD_STR SD_ID128_MAKE_UUID_STR(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
#define EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001)
#define EFI_VARIABLE_BOOTSERVICE_ACCESS UINT32_C(0x00000002)

View file

@ -139,7 +139,8 @@ int enroll_tpm2(struct crypt_device *cd,
const char *pubkey_path,
uint32_t pubkey_pcr_mask,
const char *signature_path,
bool use_pin) {
bool use_pin,
const char *pcrlock_path) {
_cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL;
@ -207,6 +208,15 @@ int enroll_tpm2(struct crypt_device *cd,
return log_debug_errno(r, "Failed to read TPM PCR signature: %m");
}
_cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
if (pcrlock_path) {
r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
if (r < 0)
return r;
flags |= TPM2_FLAGS_USE_PCRLOCK;
}
_cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
r = tpm2_context_new(device, &tpm2_context);
if (r < 0)
@ -248,6 +258,7 @@ int enroll_tpm2(struct crypt_device *cd,
n_hash_pcr_values,
pubkey ? &public : NULL,
use_pin,
pcrlock_path ? &pcrlock_policy : NULL,
&policy);
if (r < 0)
return r;
@ -288,6 +299,7 @@ int enroll_tpm2(struct crypt_device *cd,
pubkey_pcr_mask,
signature_json,
pin_str,
pcrlock_path ? &pcrlock_policy : NULL,
/* primary_alg= */ 0,
blob, blob_size,
policy.buffer, policy.size,

View file

@ -8,9 +8,9 @@
#include "tpm2-util.h"
#if HAVE_TPM2
int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin);
int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path);
#else
static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin) {
static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"TPM2 key enrollment not supported.");
}

View file

@ -43,6 +43,7 @@ static bool arg_tpm2_pin = false;
static char *arg_tpm2_public_key = NULL;
static uint32_t arg_tpm2_public_key_pcr_mask = 0;
static char *arg_tpm2_signature = NULL;
static char *arg_tpm2_pcrlock = NULL;
static char *arg_node = NULL;
static int *arg_wipe_slots = NULL;
static size_t arg_n_wipe_slots = 0;
@ -65,6 +66,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep);
STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
STATIC_DESTRUCTOR_REGISTER(arg_wipe_slots, freep);
@ -144,6 +146,8 @@ static int help(void) {
" --tpm2-signature=PATH\n"
" Validate public key enrollment works with JSON signature\n"
" file\n"
" --tpm2-pcrlock=PATH\n"
" Specify pcrlock policy to lock against\n"
" --tpm2-with-pin=BOOL\n"
" Whether to require entering a PIN to unlock the volume\n"
"\nSee the %2$s for details.\n",
@ -173,6 +177,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TPM2_PUBLIC_KEY_PCRS,
ARG_TPM2_SIGNATURE,
ARG_TPM2_PIN,
ARG_TPM2_PCRLOCK,
ARG_WIPE_SLOT,
ARG_FIDO2_WITH_PIN,
ARG_FIDO2_WITH_UP,
@ -200,11 +205,12 @@ static int parse_argv(int argc, char *argv[]) {
{ "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
{ "tpm2-signature", required_argument, NULL, ARG_TPM2_SIGNATURE },
{ "tpm2-with-pin", required_argument, NULL, ARG_TPM2_PIN },
{ "tpm2-pcrlock", required_argument, NULL, ARG_TPM2_PCRLOCK },
{ "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT },
{}
};
bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true;
bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true;
int c, r;
assert(argc >= 0);
@ -412,6 +418,14 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TPM2_PCRLOCK:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_pcrlock);
if (r < 0)
return r;
auto_pcrlock = false;
break;
case ARG_WIPE_SLOT: {
const char *p = optarg;
@ -500,12 +514,23 @@ static int parse_argv(int argc, char *argv[]) {
}
}
if (auto_pcrlock) {
assert(!arg_tpm2_pcrlock);
r = tpm2_pcrlock_search_file(NULL, NULL, &arg_tpm2_pcrlock);
if (r < 0) {
if (r != -ENOENT)
log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m");
} else
log_info("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock);
}
if (auto_public_key_pcr_mask) {
assert(arg_tpm2_public_key_pcr_mask == 0);
arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT);
}
if (auto_hash_pcr_values) {
if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 by default if no pcrlock policy is around (which is a better replacement) */
assert(arg_tpm2_n_hash_pcr_values == 0);
if (!GREEDY_REALLOC_APPEND(
@ -690,7 +715,7 @@ static int run(int argc, char *argv[]) {
break;
case ENROLL_TPM2:
slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin);
slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin, arg_tpm2_pcrlock);
break;
case _ENROLL_TYPE_INVALID:

View file

@ -109,6 +109,7 @@ _public_ int cryptsetup_token_open_pin(
pubkey_pcr_mask,
params.signature_path,
pin_string,
params.pcrlock_path,
primary_alg,
blob,
blob_size,
@ -239,6 +240,7 @@ _public_ void cryptsetup_token_dump(
crypt_log(cd, "\ttpm2-blob: %s\n", blob_str);
crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
crypt_log(cd, "\ttpm2-pin: %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
crypt_log(cd, "\ttpm2-pcrlock: %s\n", true_false(flags & TPM2_FLAGS_USE_PCRLOCK));
crypt_log(cd, "\ttpm2-salt: %s\n", true_false(salt));
crypt_log(cd, "\ttpm2-srk: %s\n", true_false(srk_buf));
}

View file

@ -22,6 +22,7 @@ int acquire_luks2_key(
uint32_t pubkey_pcr_mask,
const char *signature_path,
const char *pin,
const char *pcrlock_path,
uint16_t primary_alg,
const void *key_data,
size_t key_data_size,
@ -76,6 +77,13 @@ int acquire_luks2_key(
return log_error_errno(r, "Failed to load PCR signature: %m");
}
_cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
if (FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK)) {
r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
if (r < 0)
return r;
}
_cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
r = tpm2_context_new(device, &tpm2_context);
if (r < 0)
@ -88,6 +96,7 @@ int acquire_luks2_key(
pubkey_pcr_mask,
signature_json,
pin,
FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL,
primary_alg,
key_data, key_data_size,
policy_hash, policy_hash_size,

View file

@ -14,6 +14,7 @@ int acquire_luks2_key(
size_t pubkey_size,
uint32_t pubkey_pcr_mask,
const char *signature_path,
const char *pcrlock_path,
const char *pin,
uint16_t primary_alg,
const void *key_data,

View file

@ -62,6 +62,7 @@ int acquire_tpm2_key(
size_t pubkey_size,
uint32_t pubkey_pcr_mask,
const char *signature_path,
const char *pcrlock_path,
uint16_t primary_alg,
const char *key_file,
size_t key_file_size,
@ -129,6 +130,14 @@ int acquire_tpm2_key(
return log_error_errno(r, "Failed to load pcr signature: %m");
}
_cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
if (FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK)) {
r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
if (r < 0)
return r;
}
_cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
r = tpm2_context_new(device, &tpm2_context);
if (r < 0)
@ -142,6 +151,7 @@ int acquire_tpm2_key(
pubkey_pcr_mask,
signature_json,
/* pin= */ NULL,
FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL,
primary_alg,
blob,
blob_size,
@ -189,6 +199,7 @@ int acquire_tpm2_key(
pubkey_pcr_mask,
signature_json,
b64_salted_pin,
pcrlock_path ? &pcrlock_policy : NULL,
primary_alg,
blob,
blob_size,

View file

@ -20,6 +20,7 @@ int acquire_tpm2_key(
size_t pubkey_size,
uint32_t pubkey_pcr_mask,
const char *signature_path,
const char *pcrlock_path,
uint16_t primary_alg,
const char *key_file,
size_t key_file_size,
@ -72,6 +73,7 @@ static inline int acquire_tpm2_key(
size_t pubkey_size,
uint32_t pubkey_pcr_mask,
const char *signature_path,
const char *pcrlock_path,
uint16_t primary_alg,
const char *key_file,
size_t key_file_size,

View file

@ -100,6 +100,7 @@ static bool arg_tpm2_device_auto = false;
static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
static char *arg_tpm2_signature = NULL;
static bool arg_tpm2_pin = false;
static char *arg_tpm2_pcrlock = NULL;
static bool arg_headless = false;
static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC;
static unsigned arg_tpm2_measure_pcr = UINT_MAX; /* This and the following field is about measuring the unlocked volume key to the local TPM */
@ -116,6 +117,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_fido2_rp_id, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_measure_banks, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep);
static const char* const passphrase_type_table[_PASSPHRASE_TYPE_MAX] = {
[PASSPHRASE_REGULAR] = "passphrase",
@ -424,6 +426,16 @@ static int parse_one_option(const char *option) {
arg_tpm2_pin = r;
} else if ((val = startswith(option, "tpm2-pcrlock="))) {
if (!path_is_absolute(val))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"TPM2 pcrlock policy path \"%s\" is not absolute, refusing.", val);
r = free_and_strdup(&arg_tpm2_pcrlock, val);
if (r < 0)
return log_oom();
} else if ((val = startswith(option, "tpm2-measure-pcr="))) {
unsigned pcr;
@ -1605,6 +1617,7 @@ static int attach_luks2_by_tpm2_via_plugin(
.search_pcr_mask = arg_tpm2_pcr_mask,
.device = arg_tpm2_device,
.signature_path = arg_tpm2_signature,
.pcrlock_path = arg_tpm2_pcrlock,
};
if (!libcryptsetup_plugins_support())
@ -1664,6 +1677,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
/* pubkey= */ NULL, /* pubkey_size= */ 0,
/* pubkey_pcr_mask= */ 0,
/* signature_path= */ NULL,
/* pcrlock_path= */ NULL,
/* primary_alg= */ 0,
key_file, arg_keyfile_size, arg_keyfile_offset,
key_data, key_data_size,
@ -1761,6 +1775,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
pubkey, pubkey_size,
pubkey_pcr_mask,
arg_tpm2_signature,
arg_tpm2_pcrlock,
primary_alg,
/* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
blob, blob_size,

View file

@ -152,6 +152,7 @@ static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL;
static size_t arg_tpm2_n_hash_pcr_values = 0;
static char *arg_tpm2_public_key = NULL;
static uint32_t arg_tpm2_public_key_pcr_mask = 0;
static char *arg_tpm2_pcrlock = NULL;
static bool arg_split = false;
static GptPartitionType *arg_filter_partitions = NULL;
static size_t arg_n_filter_partitions = 0;
@ -175,6 +176,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep);
STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_copy_from, strv_freep);
@ -3821,8 +3823,21 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
return log_error_errno(r, "Could not get hash mask: %m");
}
_cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
if (arg_tpm2_pcrlock) {
r = tpm2_pcrlock_policy_load(arg_tpm2_pcrlock, &pcrlock_policy);
if (r < 0)
return r;
}
TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
r = tpm2_calculate_sealing_policy(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, pubkey ? &public : NULL, /* use_pin= */ false, &policy);
r = tpm2_calculate_sealing_policy(
arg_tpm2_hash_pcr_values,
arg_tpm2_n_hash_pcr_values,
pubkey ? &public : NULL,
/* use_pin= */ false,
arg_tpm2_pcrlock ? &pcrlock_policy : NULL,
&policy);
if (r < 0)
return log_error_errno(r, "Could not calculate sealing policy digest: %m");
@ -6435,6 +6450,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TPM2_PCRS,
ARG_TPM2_PUBLIC_KEY,
ARG_TPM2_PUBLIC_KEY_PCRS,
ARG_TPM2_PCRLOCK,
ARG_SPLIT,
ARG_INCLUDE_PARTITIONS,
ARG_EXCLUDE_PARTITIONS,
@ -6472,6 +6488,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
{ "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY },
{ "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
{ "tpm2-pcrlock", required_argument, NULL, ARG_TPM2_PCRLOCK },
{ "split", required_argument, NULL, ARG_SPLIT },
{ "include-partitions", required_argument, NULL, ARG_INCLUDE_PARTITIONS },
{ "exclude-partitions", required_argument, NULL, ARG_EXCLUDE_PARTITIONS },
@ -6485,7 +6502,7 @@ static int parse_argv(int argc, char *argv[]) {
{}
};
bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true;
bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true;
int c, r;
assert(argc >= 0);
@ -6728,6 +6745,14 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TPM2_PCRLOCK:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_pcrlock);
if (r < 0)
return r;
auto_pcrlock = false;
break;
case ARG_SPLIT:
r = parse_boolean_argument("--split=", optarg, NULL);
if (r < 0)
@ -6935,12 +6960,23 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"A path to an image file must be specified when --split is used.");
if (auto_pcrlock) {
assert(!arg_tpm2_pcrlock);
r = tpm2_pcrlock_search_file(NULL, NULL, &arg_tpm2_pcrlock);
if (r < 0) {
if (r != -ENOENT)
log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m");
} else
log_info("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock);
}
if (auto_public_key_pcr_mask) {
assert(arg_tpm2_public_key_pcr_mask == 0);
arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT);
}
if (auto_hash_pcr_values) {
if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 if no pcr policy is specified. */
assert(arg_tpm2_n_hash_pcr_values == 0);
if (!GREEDY_REALLOC_APPEND(

37
src/pcrlock/meson.build Normal file
View file

@ -0,0 +1,37 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
executables += [
libexec_template + {
'name' : 'systemd-pcrlock',
'conditions' : [
'HAVE_OPENSSL',
'HAVE_TPM2'
],
'sources' : files(
'pcrlock.c',
'pcrlock-firmware.c',
'pehash.c',
),
'dependencies' : [
libm,
libopenssl,
tpm2,
],
},
]
if conf.get('HAVE_OPENSSL') == 1 and conf.get('HAVE_TPM2') == 1
install_data('pcrlock.d/350-action-efi-application.pcrlock', install_dir : pcrlockdir)
install_data('pcrlock.d/400-secureboot-separator.pcrlock.d/300-0x00000000.pcrlock', install_dir : pcrlockdir / '400-secureboot-separator.pcrlock.d')
install_data('pcrlock.d/400-secureboot-separator.pcrlock.d/600-0xffffffff.pcrlock', install_dir : pcrlockdir / '400-secureboot-separator.pcrlock.d')
install_data('pcrlock.d/500-separator.pcrlock.d/300-0x00000000.pcrlock', install_dir : pcrlockdir / '500-separator.pcrlock.d')
install_data('pcrlock.d/500-separator.pcrlock.d/600-0xffffffff.pcrlock', install_dir : pcrlockdir / '500-separator.pcrlock.d')
install_data('pcrlock.d/700-action-efi-exit-boot-services.pcrlock.d/300-present.pcrlock', install_dir : pcrlockdir / '700-action-efi-exit-boot-services.pcrlock.d')
install_data('pcrlock.d/700-action-efi-exit-boot-services.pcrlock.d/600-absent.pcrlock', install_dir : pcrlockdir / '700-action-efi-exit-boot-services.pcrlock.d')
install_data('pcrlock.d/750-enter-initrd.pcrlock', install_dir : pcrlockdir)
install_data('pcrlock.d/800-leave-initrd.pcrlock', install_dir : pcrlockdir)
install_data('pcrlock.d/850-sysinit.pcrlock', install_dir : pcrlockdir)
install_data('pcrlock.d/900-ready.pcrlock', install_dir : pcrlockdir)
install_data('pcrlock.d/950-shutdown.pcrlock', install_dir : pcrlockdir)
install_data('pcrlock.d/990-final.pcrlock', install_dir : pcrlockdir)
endif

View file

@ -0,0 +1,168 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <openssl/evp.h>
#include "pcrlock-firmware.h"
#include "unaligned.h"
static int tcg_pcr_event2_digests_size(
const TCG_EfiSpecIdEventAlgorithmSize *algorithms,
size_t n_algorithms,
size_t *ret) {
size_t m = 0;
assert(algorithms || n_algorithms == 0);
assert(ret);
FOREACH_ARRAY(a, algorithms, n_algorithms) {
if (a->digestSize > UINT32_MAX - offsetof(TPMT_HA, digest) - m)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Accumulated hash size too large");
m += offsetof(TPMT_HA, digest) + a->digestSize;
}
*ret = m;
return 0;
}
int validate_firmware_event(
const TCG_PCR_EVENT2 *event,
size_t left,
const TCG_EfiSpecIdEventAlgorithmSize *algorithms,
size_t n_algorithms,
const TCG_PCR_EVENT2 **ret_next_event,
size_t *ret_left,
const void **ret_payload,
size_t *ret_payload_size) {
size_t digests_size;
int r;
assert(event);
assert(algorithms || n_algorithms == 0);
assert(ret_next_event);
assert(ret_left);
if (left == 0) {
*ret_next_event = NULL;
*ret_left = 0;
return 0;
}
r = tcg_pcr_event2_digests_size(algorithms, n_algorithms, &digests_size);
if (r < 0)
return r;
if (left < (uint64_t) offsetof(TCG_PCR_EVENT2, digests.digests) + (uint64_t) digests_size + sizeof(uint32_t))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event header too short.");
if (event->digests.count != n_algorithms)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Number of digests in event doesn't match log.");
uint32_t eventSize = unaligned_read_ne32((const uint8_t*) &event->digests.digests + digests_size);
uint64_t size = (uint64_t) offsetof(TCG_PCR_EVENT2, digests.digests) + (uint64_t) digests_size + sizeof(uint32_t) + eventSize;
if (size > left)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event header too short.");
*ret_next_event = (const TCG_PCR_EVENT2*) ((const uint8_t*) event + size);
*ret_left = left - size;
if (ret_payload)
*ret_payload = (const uint8_t*) &event->digests.digests + digests_size + sizeof(uint32_t);
if (ret_payload_size)
*ret_payload_size = eventSize;
return 1;
}
int validate_firmware_header(
const void *start,
size_t size,
const TCG_EfiSpecIdEventAlgorithmSize **ret_algorithms,
size_t *ret_n_algorithms,
const TCG_PCR_EVENT2 **ret_first,
size_t *ret_left) {
assert(start || size == 0);
assert(ret_algorithms);
assert(ret_n_algorithms);
assert(ret_first);
assert(ret_left);
if (size < offsetof(TCG_PCClientPCREvent, event))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log too short for TCG_PCClientPCREvent.");
const TCG_PCClientPCREvent *h = start;
if (size < (uint64_t) offsetof(TCG_PCClientPCREvent, event) + (uint64_t) h->eventDataSize)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log too short for TCG_PCClientPCREvent events data.");
if (h->pcrIndex != 0)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected PCR index %" PRIu32, h->pcrIndex);
if (h->eventType != EV_NO_ACTION)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected event type 0x%" PRIx32, h->eventType);
if (!memeqzero(h->digest, sizeof(h->digest)))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected non-zero digest.");
if (h->eventDataSize < offsetof(TCG_EfiSpecIDEvent, digestSizes))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header too short for TCG_EfiSpecIdEvent.");
const TCG_EfiSpecIDEvent *id = (const TCG_EfiSpecIDEvent*) h->event;
/* Signature as per "TCG PC Client Specific Platform Firmware Profile Specification"
* (https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/),
* section 10.4.5.1 "Specification ID Version Event" (at least in version 1.05 Revision 23 of the
* spec) */
if (memcmp(id->signature,
(const uint8_t[]) { 0x53, 0x70, 0x65, 0x63, 0x20, 0x49, 0x44, 0x20, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x33, 0x00 },
sizeof(id->signature)) != 0)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing TPM2 event log signature.");
if (id->numberOfAlgorithms <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Number of advertised hash algorithms is zero.");
if (id->numberOfAlgorithms > UINT32_MAX / sizeof(TCG_EfiSpecIdEventAlgorithmSize))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Number of advertised hash algorithms too large.");
log_debug("TPM PC Client Platform Firmware Profile: family %u.%u, revision %u.%u",
id->specVersionMajor, id->specVersionMinor,
id->specErrata / 100, id->specErrata % 100);
if (h->eventDataSize < (uint64_t) offsetof(TCG_EfiSpecIDEvent, digestSizes) + (uint64_t) (id->numberOfAlgorithms * sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + 1U)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header doesn't fit all algorithms.");
uint8_t vendorInfoSize = *((const uint8_t*) id + offsetof(TCG_EfiSpecIDEvent, digestSizes) + (id->numberOfAlgorithms * sizeof(TCG_EfiSpecIdEventAlgorithmSize)));
if (h->eventDataSize != offsetof(TCG_EfiSpecIDEvent, digestSizes) + (id->numberOfAlgorithms * sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + 1U + vendorInfoSize)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header doesn't fit vendor info.");
for (size_t i = 0; i < id->numberOfAlgorithms; i++) {
const EVP_MD *implementation;
const char *a;
a = tpm2_hash_alg_to_string(id->digestSizes[i].algorithmId);
if (!a) {
log_notice("Event log advertises unknown hash algorithm 0x%4x, can't validate.", id->digestSizes[i].algorithmId);
continue;
}
implementation = EVP_get_digestbyname(a);
if (!implementation) {
log_notice("Event log advertises hash algorithm '%s' we don't implement, can't validate.", a);
continue;
}
if (EVP_MD_size(implementation) != id->digestSizes[i].digestSize)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Advertised digest size for '%s' is wrong, refusing.", a);
}
*ret_algorithms = id->digestSizes;
*ret_n_algorithms = id->numberOfAlgorithms;
size_t offset = offsetof(TCG_PCClientPCREvent, event) + h->eventDataSize;
*ret_first = (TCG_PCR_EVENT2*) ((const uint8_t*) h + offset);
*ret_left = size - offset;
return 0;
}

View file

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/types.h>
#include "tpm2-event-log.h"
#include "tpm2-util.h"
int validate_firmware_event(
const TCG_PCR_EVENT2 *event,
size_t left,
const TCG_EfiSpecIdEventAlgorithmSize *algorithms,
size_t n_algorithms,
const TCG_PCR_EVENT2 **ret_next_event,
size_t *ret_left,
const void **ret_payload,
size_t *ret_payload_size);
int validate_firmware_header(
const void *start,
size_t size,
const TCG_EfiSpecIdEventAlgorithmSize **ret_algorithms,
size_t *ret_n_algorithms,
const TCG_PCR_EVENT2 **ret_first,
size_t *ret_left);

4992
src/pcrlock/pcrlock.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
{"records":[{"pcr":4,"digests":[{"hashAlg":"sha1","digest":"cd0fdb4531a6ec41be2753ba042637d6e5f7f256"},{"hashAlg":"sha256","digest":"3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba"},{"hashAlg":"sha384","digest":"77a0dab2312b4e1e57a84d865a21e5b2ee8d677a21012ada819d0a98988078d3d740f6346bfe0abaa938ca20439a8d71"},{"hashAlg":"sha512","digest":"03020279c5ea3676d6630c82a9931343225e8eab81529b65c786aeb6a445d3852a34dd193178f938b6b47345a72d4b647df309c971f7c02f0ede296a136a1086"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":7,"digests":[{"hashAlg":"sha1","digest":"9069ca78e7450a285173431b3e52c5c25299e473"},{"hashAlg":"sha256","digest":"df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"},{"hashAlg":"sha384","digest":"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0"},{"hashAlg":"sha512","digest":"ec2d57691d9b2d40182ac565032054b7d784ba96b18bcb5be0bb4e70e3fb041eff582c8af66ee50256539f2181d7f9e53627c0189da7e75a4d5ef10ea93b20b3"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":7,"digests":[{"hashAlg":"sha1","digest":"d9be6524a5f5047db5866813acf3277892a7a30a"},{"hashAlg":"sha256","digest":"ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"},{"hashAlg":"sha384","digest":"4a06b879c7eedbe01c945d46b5bd785b59203dce81ea6a1206c28091ca285365f760d9167778f0dc1763d4854aafd40a"},{"hashAlg":"sha512","digest":"ea71bb243b0b2db729b9eb88e3c55a3f490fbff23457825051224a1fe6e6d3f480590cfa3a4a6b12c622d6ac366feb03cd17004ed004cb3f0d52731626946679"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":0,"digests":[{"hashAlg":"sha1","digest":"9069ca78e7450a285173431b3e52c5c25299e473"},{"hashAlg":"sha256","digest":"df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"},{"hashAlg":"sha384","digest":"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0"},{"hashAlg":"sha512","digest":"ec2d57691d9b2d40182ac565032054b7d784ba96b18bcb5be0bb4e70e3fb041eff582c8af66ee50256539f2181d7f9e53627c0189da7e75a4d5ef10ea93b20b3"}]},{"pcr":1,"digests":[{"hashAlg":"sha1","digest":"9069ca78e7450a285173431b3e52c5c25299e473"},{"hashAlg":"sha256","digest":"df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"},{"hashAlg":"sha384","digest":"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0"},{"hashAlg":"sha512","digest":"ec2d57691d9b2d40182ac565032054b7d784ba96b18bcb5be0bb4e70e3fb041eff582c8af66ee50256539f2181d7f9e53627c0189da7e75a4d5ef10ea93b20b3"}]},{"pcr":2,"digests":[{"hashAlg":"sha1","digest":"9069ca78e7450a285173431b3e52c5c25299e473"},{"hashAlg":"sha256","digest":"df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"},{"hashAlg":"sha384","digest":"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0"},{"hashAlg":"sha512","digest":"ec2d57691d9b2d40182ac565032054b7d784ba96b18bcb5be0bb4e70e3fb041eff582c8af66ee50256539f2181d7f9e53627c0189da7e75a4d5ef10ea93b20b3"}]},{"pcr":3,"digests":[{"hashAlg":"sha1","digest":"9069ca78e7450a285173431b3e52c5c25299e473"},{"hashAlg":"sha256","digest":"df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"},{"hashAlg":"sha384","digest":"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0"},{"hashAlg":"sha512","digest":"ec2d57691d9b2d40182ac565032054b7d784ba96b18bcb5be0bb4e70e3fb041eff582c8af66ee50256539f2181d7f9e53627c0189da7e75a4d5ef10ea93b20b3"}]},{"pcr":4,"digests":[{"hashAlg":"sha1","digest":"9069ca78e7450a285173431b3e52c5c25299e473"},{"hashAlg":"sha256","digest":"df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"},{"hashAlg":"sha384","digest":"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0"},{"hashAlg":"sha512","digest":"ec2d57691d9b2d40182ac565032054b7d784ba96b18bcb5be0bb4e70e3fb041eff582c8af66ee50256539f2181d7f9e53627c0189da7e75a4d5ef10ea93b20b3"}]},{"pcr":5,"digests":[{"hashAlg":"sha1","digest":"9069ca78e7450a285173431b3e52c5c25299e473"},{"hashAlg":"sha256","digest":"df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"},{"hashAlg":"sha384","digest":"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0"},{"hashAlg":"sha512","digest":"ec2d57691d9b2d40182ac565032054b7d784ba96b18bcb5be0bb4e70e3fb041eff582c8af66ee50256539f2181d7f9e53627c0189da7e75a4d5ef10ea93b20b3"}]},{"pcr":6,"digests":[{"hashAlg":"sha1","digest":"9069ca78e7450a285173431b3e52c5c25299e473"},{"hashAlg":"sha256","digest":"df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"},{"hashAlg":"sha384","digest":"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0"},{"hashAlg":"sha512","digest":"ec2d57691d9b2d40182ac565032054b7d784ba96b18bcb5be0bb4e70e3fb041eff582c8af66ee50256539f2181d7f9e53627c0189da7e75a4d5ef10ea93b20b3"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":0,"digests":[{"hashAlg":"sha1","digest":"d9be6524a5f5047db5866813acf3277892a7a30a"},{"hashAlg":"sha256","digest":"ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"},{"hashAlg":"sha384","digest":"4a06b879c7eedbe01c945d46b5bd785b59203dce81ea6a1206c28091ca285365f760d9167778f0dc1763d4854aafd40a"},{"hashAlg":"sha512","digest":"ea71bb243b0b2db729b9eb88e3c55a3f490fbff23457825051224a1fe6e6d3f480590cfa3a4a6b12c622d6ac366feb03cd17004ed004cb3f0d52731626946679"}]},{"pcr":1,"digests":[{"hashAlg":"sha1","digest":"d9be6524a5f5047db5866813acf3277892a7a30a"},{"hashAlg":"sha256","digest":"ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"},{"hashAlg":"sha384","digest":"4a06b879c7eedbe01c945d46b5bd785b59203dce81ea6a1206c28091ca285365f760d9167778f0dc1763d4854aafd40a"},{"hashAlg":"sha512","digest":"ea71bb243b0b2db729b9eb88e3c55a3f490fbff23457825051224a1fe6e6d3f480590cfa3a4a6b12c622d6ac366feb03cd17004ed004cb3f0d52731626946679"}]},{"pcr":2,"digests":[{"hashAlg":"sha1","digest":"d9be6524a5f5047db5866813acf3277892a7a30a"},{"hashAlg":"sha256","digest":"ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"},{"hashAlg":"sha384","digest":"4a06b879c7eedbe01c945d46b5bd785b59203dce81ea6a1206c28091ca285365f760d9167778f0dc1763d4854aafd40a"},{"hashAlg":"sha512","digest":"ea71bb243b0b2db729b9eb88e3c55a3f490fbff23457825051224a1fe6e6d3f480590cfa3a4a6b12c622d6ac366feb03cd17004ed004cb3f0d52731626946679"}]},{"pcr":3,"digests":[{"hashAlg":"sha1","digest":"d9be6524a5f5047db5866813acf3277892a7a30a"},{"hashAlg":"sha256","digest":"ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"},{"hashAlg":"sha384","digest":"4a06b879c7eedbe01c945d46b5bd785b59203dce81ea6a1206c28091ca285365f760d9167778f0dc1763d4854aafd40a"},{"hashAlg":"sha512","digest":"ea71bb243b0b2db729b9eb88e3c55a3f490fbff23457825051224a1fe6e6d3f480590cfa3a4a6b12c622d6ac366feb03cd17004ed004cb3f0d52731626946679"}]},{"pcr":4,"digests":[{"hashAlg":"sha1","digest":"d9be6524a5f5047db5866813acf3277892a7a30a"},{"hashAlg":"sha256","digest":"ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"},{"hashAlg":"sha384","digest":"4a06b879c7eedbe01c945d46b5bd785b59203dce81ea6a1206c28091ca285365f760d9167778f0dc1763d4854aafd40a"},{"hashAlg":"sha512","digest":"ea71bb243b0b2db729b9eb88e3c55a3f490fbff23457825051224a1fe6e6d3f480590cfa3a4a6b12c622d6ac366feb03cd17004ed004cb3f0d52731626946679"}]},{"pcr":5,"digests":[{"hashAlg":"sha1","digest":"d9be6524a5f5047db5866813acf3277892a7a30a"},{"hashAlg":"sha256","digest":"ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"},{"hashAlg":"sha384","digest":"4a06b879c7eedbe01c945d46b5bd785b59203dce81ea6a1206c28091ca285365f760d9167778f0dc1763d4854aafd40a"},{"hashAlg":"sha512","digest":"ea71bb243b0b2db729b9eb88e3c55a3f490fbff23457825051224a1fe6e6d3f480590cfa3a4a6b12c622d6ac366feb03cd17004ed004cb3f0d52731626946679"}]},{"pcr":6,"digests":[{"hashAlg":"sha1","digest":"d9be6524a5f5047db5866813acf3277892a7a30a"},{"hashAlg":"sha256","digest":"ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"},{"hashAlg":"sha384","digest":"4a06b879c7eedbe01c945d46b5bd785b59203dce81ea6a1206c28091ca285365f760d9167778f0dc1763d4854aafd40a"},{"hashAlg":"sha512","digest":"ea71bb243b0b2db729b9eb88e3c55a3f490fbff23457825051224a1fe6e6d3f480590cfa3a4a6b12c622d6ac366feb03cd17004ed004cb3f0d52731626946679"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":5,"digests":[{"hashAlg":"sha1","digest":"443a6b7b82b7af564f2e393cd9d5a388b7fa4a98"},{"hashAlg":"sha256","digest":"d8043d6b7b85ad358eb3b6ae6a873ab7ef23a26352c5dc4faa5aeedacf5eb41b"},{"hashAlg":"sha384","digest":"214b0bef1379756011344877743fdc2a5382bac6e70362d624ccf3f654407c1b4badf7d8f9295dd3dabdef65b27677e0"},{"hashAlg":"sha512","digest":"0fed3a4c9552021436534d27f3adb481e22b50b29e4b37a63f518540a651a174f149b69f500b0bdb2cb3bf4e0e21e0781451090af33e88f6bee4cbebd15c1668"}]},{"pcr":5,"digests":[{"hashAlg":"sha1","digest":"475545ddc978d7bfd036facc7e2e987f48189f0d"},{"hashAlg":"sha256","digest":"b54f7542cbd872a81a9d9dea839b2b8d747c7ebd5ea6615c40f42f44a6dbeba0"},{"hashAlg":"sha384","digest":"0a2e01c85deae718a530ad8c6d20a84009babe6c8989269e950d8cf440c6e997695e64d455c4174a652cd080f6230b74"},{"hashAlg":"sha512","digest":"1bb30cdbd6da78fe2a8a161ef51176e22d64dce305b40b47243673af64a2b16fca6182116433e3891be94773f6d7d411275721d5bf7d40ea51a274d5c891637c"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"b1b01d5f73f321eb70e76f8a0e241ac0a3fa4a6e"},{"hashAlg":"sha256","digest":"51e6b92f405d1f98d96e3de343d61d420ad6923b25de21d766f9298192f14fed"},{"hashAlg":"sha384","digest":"687eef3a3a8c716439b5ed583657e8668401630c321f2f35d19b953ddf20b68a96474d0c2e5f0e1757bfa5ba70b9fc32"},{"hashAlg":"sha512","digest":"ab0ddfdabe43f1d06b3e58fbe17439a0f7f552e9e228d85665d485ececf7e733bae4cd7e0a17e5456e2ee7e412f5a0f37de05a782cce781e173ee26958de7f30"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"865e1ff2cc5b8db815313b23fe3d8b561212f5d1"},{"hashAlg":"sha256","digest":"3be261aff7db92bf507eae947f4003ffa2bcad0bffe3524601d62d0bc8be7135"},{"hashAlg":"sha384","digest":"9c0743b7a2e1ee06c70b7137b763cd2205c26ced274149959b05bd5a51bfa96b4fedaa4f87398b5c88986d1ff0879910"},{"hashAlg":"sha512","digest":"01b8ca86b9f8fac967f383380aff7cdffd2ef0c496574517c25398f7c74aa611821dd469ba021b2aa9b9a7232865708ca45c79368f2e7fffda3dd6b308264008"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"aeabcf402223916e804cce79778a55d5a9276983"},{"hashAlg":"sha256","digest":"730bb5a583ba880c277e656d2dc8aba1a314a11b14d25b05153d2bab82567a48"},{"hashAlg":"sha384","digest":"955cc8939f81d862b3119aabe612fd36bf91668bb62397f5e4126085d79ba6d7cbfa4e3a2345747f0b476ce4b1cbc2c9"},{"hashAlg":"sha512","digest":"a9eb62cdd1cd8292b6325a8ee3770d6f1b613426a749e17ffba8f90bdd6c41806468fb79d01276de7cc791877dfebae165d4ed07585154acf96652c6db92acc1"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"75c0533730caf1f78561c0883fb87bc8d98ef04b"},{"hashAlg":"sha256","digest":"b24d6d33736ecd5604a4b17bc9c6481039fac362bb7df044ef1c10a2bfd21db6"},{"hashAlg":"sha384","digest":"23ed5781da39fe6dc17f79478aeeb9eb2bca1d776061da188e10f9c85f7933fb39cfdba50f39af8aed24e5b45b80d006"},{"hashAlg":"sha512","digest":"ca6616f94a209e53f6fdc526b473172eb4b2157cf4809c31e36ad52db614ed352e68407be53c238ba17a561c4fde43f4a859aa8711f9781a0c934296d4d7571b"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"53669f193b2174641c72654b5c3e5b67950334ae"},{"hashAlg":"sha256","digest":"08434ba9cdf55a02284e2913400586cd289878e0f055f7bb0b07ce392caeb989"},{"hashAlg":"sha384","digest":"186e2d6603b9755221b7ef894dd52b1154b48ef4786aec06ab6f7709e639715e89bd59fa80736bb45f0ca88583c212c1"},{"hashAlg":"sha512","digest":"9e5549deb36fc48768cb80e03bc91c36cf549ff5921e05bab5b68faefda7fac8c8a0755db783cbf1c1b98c80dc22ef06ff3f4a0a16704749f5cd4acf40e42a94"}]}]}

View file

@ -0,0 +1 @@
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"d594c2cc0a53025004791399d80e20852af4c988"},{"hashAlg":"sha256","digest":"2443630b4620165c8b173e7265e17526fe2787ae594364dd6d839ad58f2fc007"},{"hashAlg":"sha384","digest":"90697eec39ed47f2b7ed278aa6fe6a1c073fcc7f3af54299fb95ac8a18c771acbac71e25b5a5639554943bfdfab76737"},{"hashAlg":"sha512","digest":"b3d9598ca0aa5da28be1c97a45d53cc5c72a80e61c439c8bf3e89c5c0661f49df8fa34019a21cd5e31261ae3a3a87ef4592d8010aad6a5ecdc9dbaae38cd1470"}]}]}

246
src/pcrlock/pehash.c Normal file
View file

@ -0,0 +1,246 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/stat.h>
#include <unistd.h>
#include "alloc-util.h"
#include "hexdecoct.h"
#include "pe-binary.h"
#include "pehash.h"
#include "sort-util.h"
#include "stat-util.h"
#include "string-table.h"
/* Implements:
*
* https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/authenticode_pe.docx
* Section "Calculating the PE Image Hash"
*/
#define IMAGE_DATA_DIRECTORY_INDEX_CERTIFICATION_TABLE 4U
static int hash_file(int fd, EVP_MD_CTX *md_ctx, uint64_t offset, uint64_t size) {
uint8_t buffer[64*1024];
log_debug("Hashing %" PRIu64 " @ %" PRIu64 " → %" PRIu64, size, offset, offset + size);
while (size > 0) {
size_t m = MIN(size, sizeof(buffer));
ssize_t n;
n = pread(fd, buffer, m, offset);
if (n < 0)
return log_debug_errno(errno, "Failed to read file for hashing: %m");
if ((size_t) n != m)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while hashing.");
if (EVP_DigestUpdate(md_ctx, buffer, m) != 1)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to hash data.");
offset += m;
size -= m;
}
return 0;
}
static int section_offset_cmp(const IMAGE_SECTION_HEADER *a, const IMAGE_SECTION_HEADER *b) {
return CMP(ASSERT_PTR(a)->PointerToRawData, ASSERT_PTR(b)->PointerToRawData);
}
int pe_hash(int fd,
const EVP_MD *md,
void **ret_hash,
size_t *ret_hash_size) {
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mdctx = NULL;
_cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
_cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
_cleanup_free_ PeHeader *pe_header = NULL;
const IMAGE_DATA_DIRECTORY *certificate_table;
struct stat st;
uint64_t p, q;
int r;
assert(fd >= 0);
assert(md);
assert(ret_hash_size);
assert(ret_hash);
if (fstat(fd, &st) < 0)
return log_debug_errno(errno, "Failed to stat file: %m");
r = stat_verify_regular(&st);
if (r < 0)
return log_debug_errno(r, "Not a regular file: %m");
r = pe_load_headers(fd, &dos_header, &pe_header);
if (r < 0)
return r;
r = pe_load_sections(fd, dos_header, pe_header, &sections);
if (r < 0)
return r;
certificate_table = pe_header_get_data_directory(pe_header, IMAGE_DATA_DIRECTORY_INDEX_CERTIFICATION_TABLE);
if (!certificate_table)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "File lacks certificate table.");
mdctx = EVP_MD_CTX_new();
if (!mdctx)
return log_oom_debug();
if (EVP_DigestInit_ex(mdctx, md, NULL) != 1)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to allocate message digest.");
/* Everything from beginning of file to CheckSum field in PE header */
p = (uint64_t) dos_header->e_lfanew +
offsetof(PeHeader, optional.CheckSum);
r = hash_file(fd, mdctx, 0, p);
if (r < 0)
return r;
p += sizeof(le32_t);
/* Everything between the CheckSum field and the Image Data Directory Entry for the Certification Table */
q = (uint64_t) dos_header->e_lfanew +
PE_HEADER_OPTIONAL_FIELD_OFFSET(pe_header, DataDirectory[IMAGE_DATA_DIRECTORY_INDEX_CERTIFICATION_TABLE]);
r = hash_file(fd, mdctx, p, q - p);
if (r < 0)
return r;
q += sizeof(IMAGE_DATA_DIRECTORY);
/* The rest of the header + the section table */
p = pe_header->optional.SizeOfHeaders;
if (p < q)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "SizeOfHeaders too short.");
r = hash_file(fd, mdctx, q, p - q);
if (r < 0)
return r;
/* Sort by location in file */
typesafe_qsort(sections, pe_header->pe.NumberOfSections, section_offset_cmp);
FOREACH_ARRAY(section, sections, pe_header->pe.NumberOfSections) {
r = hash_file(fd, mdctx, section->PointerToRawData, section->SizeOfRawData);
if (r < 0)
return r;
p += section->SizeOfRawData;
}
if ((uint64_t) st.st_size > p) {
if (st.st_size - p < certificate_table->Size)
return log_debug_errno(errno, "No space for certificate table, refusing.");
r = hash_file(fd, mdctx, p, st.st_size - p - certificate_table->Size);
if (r < 0)
return r;
}
int hsz = EVP_MD_CTX_size(mdctx);
if (hsz < 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get hash size.");
unsigned hash_size = (unsigned) hsz;
_cleanup_free_ void *hash = malloc(hsz);
if (!hash)
return log_oom_debug();
if (EVP_DigestFinal_ex(mdctx, hash, &hash_size) != 1)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to finalize hash function.");
assert(hash_size == (unsigned) hsz);
*ret_hash = TAKE_PTR(hash);
*ret_hash_size = hash_size;
return 0;
}
typedef void* SectionHashArray[_UNIFIED_SECTION_MAX];
static void section_hash_array_done(SectionHashArray *array) {
assert(array);
for (size_t i = 0; i < _UNIFIED_SECTION_MAX; i++)
free((*array)[i]);
}
int uki_hash(int fd,
const EVP_MD *md,
void* ret_hashes[static _UNIFIED_SECTION_MAX],
size_t *ret_hash_size) {
_cleanup_(section_hash_array_done) SectionHashArray hashes = {};
_cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
_cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
_cleanup_free_ PeHeader *pe_header = NULL;
int r;
assert(fd >= 0);
assert(ret_hashes);
assert(ret_hash_size);
r = pe_load_headers(fd, &dos_header, &pe_header);
if (r < 0)
return r;
r = pe_load_sections(fd, dos_header, pe_header, &sections);
if (r < 0)
return r;
int hsz = EVP_MD_size(md);
if (hsz < 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get hash size.");
FOREACH_ARRAY(section, sections, pe_header->pe.NumberOfSections) {
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mdctx = NULL;
_cleanup_free_ char *n = NULL;
ssize_t i;
n = memdup_suffix0(section->Name, sizeof(section->Name));
if (!n)
return log_oom_debug();
i = string_table_lookup(unified_sections, _UNIFIED_SECTION_MAX, n);
if (i < 0)
continue;
if (hashes[i])
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate section");
mdctx = EVP_MD_CTX_new();
if (!mdctx)
return log_oom_debug();
if (EVP_DigestInit_ex(mdctx, md, NULL) != 1)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to allocate message digest.");
r = hash_file(fd, mdctx, section->PointerToRawData, section->VirtualSize);
if (r < 0)
return r;
hashes[i] = malloc(hsz);
if (!hashes[i])
return log_oom_debug();
unsigned hash_size = (unsigned) hsz;
if (EVP_DigestFinal_ex(mdctx, hashes[i], &hash_size) != 1)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to finalize hash function.");
assert(hash_size == (unsigned) hsz);
if (DEBUG_LOGGING) {
_cleanup_free_ char *hs = NULL;
hs = hexmem(hashes[i], hsz);
log_debug("Section %s with %s is %s.", n, EVP_MD_name(md), strna(hs));
}
}
memcpy(ret_hashes, hashes, sizeof(hashes));
zero(hashes);
*ret_hash_size = (unsigned) hsz;
return 0;
}

11
src/pcrlock/pehash.h Normal file
View file

@ -0,0 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/types.h>
#include "openssl-util.h"
#include "uki.h"
int pe_hash(int fd, const EVP_MD *md, void **ret_hash, size_t *ret_hash_size);
int uki_hash(int fd, const EVP_MD *md, void *ret_hashes[static _UNIFIED_SECTION_MAX], size_t *ret_hash_size);

View file

@ -856,6 +856,7 @@ int encrypt_credential_and_warn(
tpm2_n_hash_pcr_values,
pubkey ? &public : NULL,
/* use_pin= */ false,
/* pcrlock_policy= */ NULL,
&tpm2_policy);
if (r < 0)
return log_error_errno(r, "Could not calculate sealing policy digest: %m");
@ -1219,6 +1220,7 @@ int decrypt_credential_and_warn(
z ? le64toh(z->pcr_mask) : 0,
signature_json,
/* pin= */ NULL,
/* pcrlock_policy= */ NULL,
le16toh(t->primary_alg),
t->policy_hash_and_blob,
le32toh(t->blob_size),

View file

@ -161,6 +161,7 @@ shared_sources = files(
'tmpfile-util-label.c',
'tomoyo-util.c',
'tpm2-util.c',
'tpm2-event-log.c',
'udev-util.c',
'user-record-nss.c',
'user-record-show.c',

View file

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "tpm2-event-log.h"
#include "sort-util.h"
typedef struct tpm2_log_event_type_info {
uint32_t event_type;
const char *name;
} tpm2_log_event_type_info;
static tpm2_log_event_type_info tpm2_log_event_type_table[] = {
/* Unfortunately the types are defined all over the place, hence we are not using a dense table
* here.
*
* Keep this sorted by event type, so that we can do bisection! */
{ EV_PREBOOT_CERT, "preboot-cert" },
{ EV_POST_CODE, "post-code" },
{ EV_NO_ACTION, "no-action" },
{ EV_SEPARATOR, "separator" },
{ EV_ACTION, "action" },
{ EV_EVENT_TAG, "event-tag" },
{ EV_S_CRTM_CONTENTS, "s-crtm-contents" },
{ EV_S_CRTM_VERSION, "s-crtm-version" },
{ EV_CPU_MICROCODE, "cpu-microcode" },
{ EV_PLATFORM_CONFIG_FLAGS, "platform-config-flags" },
{ EV_TABLE_OF_DEVICES, "table-of-devices" },
{ EV_COMPACT_HASH, "compact-hash" },
{ EV_IPL, "ipl" },
{ EV_IPL_PARTITION_DATA, "ipl-partition-data" },
{ EV_NONHOST_CODE, "nonhost-code" },
{ EV_NONHOST_CONFIG, "nonhost-config" },
{ EV_NONHOST_INFO, "nonhost-info" },
{ EV_OMIT_BOOT_DEVICE_EVENTS, "omit-boot-device-events" },
/* omitting EV_EFI_EVENT_BASE, since its not an event, but just a base value for other events */
{ EV_EFI_VARIABLE_DRIVER_CONFIG, "efi-variable-driver-config" },
{ EV_EFI_VARIABLE_BOOT, "efi-variable-boot" },
{ EV_EFI_BOOT_SERVICES_APPLICATION, "efi-boot-services-application" },
{ EV_EFI_BOOT_SERVICES_DRIVER, "efi-boot-services-driver" },
{ EV_EFI_RUNTIME_SERVICES_DRIVER, "efi-runtime-services-driver" },
{ EV_EFI_GPT_EVENT, "efi-gpt-event" },
{ EV_EFI_ACTION, "efi-action" },
{ EV_EFI_PLATFORM_FIRMWARE_BLOB, "efi-platform-firmware-blob" },
{ EV_EFI_HANDOFF_TABLES, "efi-handoff-tables" },
{ EV_EFI_PLATFORM_FIRMWARE_BLOB2, "efi-platform-firmware-blob2" },
{ EV_EFI_HANDOFF_TABLES2, "efi-handoff-tables" },
{ EV_EFI_VARIABLE_BOOT2, "efi-variable-boot2" },
{ EV_EFI_HCRTM_EVENT, "efi-hcrtm-event" },
{ EV_EFI_VARIABLE_AUTHORITY, "efi-variable-authority" },
{ EV_EFI_SPDM_FIRMWARE_BLOB, "efi-spdm-firmware-blob" },
{ EV_EFI_SPDM_FIRMWARE_CONFIG, "efi-spdm-firmware-config" },
};
static int tpm2_log_event_type_info_cmp(const tpm2_log_event_type_info *a, const tpm2_log_event_type_info *b) {
return CMP(ASSERT_PTR(a)->event_type, ASSERT_PTR(b)->event_type);
}
const char *tpm2_log_event_type_to_string(uint32_t type) {
tpm2_log_event_type_info *found, key = {
.event_type = type,
};
found = typesafe_bsearch(&key, tpm2_log_event_type_table, ELEMENTSOF(tpm2_log_event_type_table), tpm2_log_event_type_info_cmp);
return found ? found->name : NULL;
}

139
src/shared/tpm2-event-log.h Normal file
View file

@ -0,0 +1,139 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <uchar.h>
#include "tpm2-util.h"
/* Definitions as per "TCG PC Client Specific Platform Firmware Profile Specification"
* (https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/),
* section 10.4.1 "Event Types" (at least in version 1.05 Revision 23 of the spec) */
#ifndef EV_PREBOOT_CERT
#define EV_PREBOOT_CERT UINT32_C(0x00000000)
#define EV_POST_CODE UINT32_C(0x00000001)
#define EV_NO_ACTION UINT32_C(0x00000003)
#define EV_SEPARATOR UINT32_C(0x00000004)
#define EV_ACTION UINT32_C(0x00000005)
#define EV_EVENT_TAG UINT32_C(0x00000006)
#define EV_S_CRTM_CONTENTS UINT32_C(0x00000007)
#define EV_S_CRTM_VERSION UINT32_C(0x00000008)
#define EV_CPU_MICROCODE UINT32_C(0x00000009)
#define EV_PLATFORM_CONFIG_FLAGS UINT32_C(0x0000000a)
#define EV_TABLE_OF_DEVICES UINT32_C(0x0000000b)
#define EV_COMPACT_HASH UINT32_C(0x0000000c)
#define EV_IPL UINT32_C(0x0000000d)
#define EV_IPL_PARTITION_DATA UINT32_C(0x0000000e)
#define EV_NONHOST_CODE UINT32_C(0x0000000f)
#define EV_NONHOST_CONFIG UINT32_C(0x00000010)
#define EV_NONHOST_INFO UINT32_C(0x00000011)
#define EV_OMIT_BOOT_DEVICE_EVENTS UINT32_C(0x00000012)
#define EV_EFI_EVENT_BASE UINT32_C(0x80000000)
#define EV_EFI_VARIABLE_DRIVER_CONFIG UINT32_C(0x80000001)
#define EV_EFI_VARIABLE_BOOT UINT32_C(0x80000002)
#define EV_EFI_BOOT_SERVICES_APPLICATION UINT32_C(0x80000003)
#define EV_EFI_BOOT_SERVICES_DRIVER UINT32_C(0x80000004)
#define EV_EFI_RUNTIME_SERVICES_DRIVER UINT32_C(0x80000005)
#define EV_EFI_GPT_EVENT UINT32_C(0x80000006)
#define EV_EFI_ACTION UINT32_C(0x80000007)
#define EV_EFI_PLATFORM_FIRMWARE_BLOB UINT32_C(0x80000008)
#define EV_EFI_HANDOFF_TABLES UINT32_C(0x80000009)
#define EV_EFI_PLATFORM_FIRMWARE_BLOB2 UINT32_C(0x8000000A)
#define EV_EFI_HANDOFF_TABLES2 UINT32_C(0x8000000B)
#define EV_EFI_VARIABLE_BOOT2 UINT32_C(0x8000000C)
#define EV_EFI_HCRTM_EVENT UINT32_C(0x80000010)
#define EV_EFI_VARIABLE_AUTHORITY UINT32_C(0x800000E0)
#define EV_EFI_SPDM_FIRMWARE_BLOB UINT32_C(0x800000E1)
#define EV_EFI_SPDM_FIRMWARE_CONFIG UINT32_C(0x800000E2)
#endif
/* Defined in drivers/firmware/efi/libstub/efistub.h in the Linux kernel sources */
#ifndef INITRD_EVENT_TAG_ID
#define INITRD_EVENT_TAG_ID UINT32_C(0x8F3B22EC)
#endif
#ifndef LOAD_OPTIONS_EVENT_TAG_ID
#define LOAD_OPTIONS_EVENT_TAG_ID UINT32_C(0x8F3B22ED)
#endif
const char *tpm2_log_event_type_to_string(uint32_t type) _const_;
#if HAVE_TPM2
/* UEFI event log data structures */
typedef struct _packed_ TCG_PCClientPCREvent {
uint32_t pcrIndex;
uint32_t eventType;
uint8_t digest[20];
uint32_t eventDataSize;
uint32_t event[];
} TCG_PCClientPCREvent;
typedef struct _packed_ packed_TPMT_HA {
uint16_t hashAlg;
TPMU_HA digest;
} packed_TPMT_HA;
typedef struct _packed_ packed_TPML_DIGEST_VALUES {
uint32_t count;
packed_TPMT_HA digests[];
} packed_TPML_DIGEST_VALUES;
typedef struct _packed_ TCG_PCR_EVENT2 {
uint32_t pcrIndex;
uint32_t eventType;
packed_TPML_DIGEST_VALUES digests;
/* … */
} TCG_PCR_EVENT2;
typedef struct _packed_ TCG_EfiSpecIdEventAlgorithmSize {
uint16_t algorithmId;
uint16_t digestSize;
} TCG_EfiSpecIdEventAlgorithmSize;
typedef struct _packed_ tdTCG_EfiSpecIdEvent {
uint8_t signature[16];
uint32_t platformClass;
uint8_t specVersionMinor;
uint8_t specVersionMajor;
uint8_t specErrata;
uint8_t uintnSize;
uint32_t numberOfAlgorithms;
TCG_EfiSpecIdEventAlgorithmSize digestSizes[];
/* … */
} TCG_EfiSpecIDEvent;
typedef struct _packed_ UEFI_VARIABLE_DATA {
uint8_t variableName[16];
uint64_t unicodeNameLength;
uint64_t variableDataLength;
char16_t unicodeName[];
/* … */
} UEFI_VARIABLE_DATA;
typedef struct _packed_ TCG_PCClientTaggedEvent{
uint32_t taggedEventID;
uint32_t taggedEventDataSize;
uint8_t taggedEventData[];
} TCG_PCClientTaggedEvent;
typedef struct _packed_ packed_EFI_DEVICE_PATH {
uint8_t type;
uint8_t subType;
uint16_t length;
uint8_t path[];
} packed_EFI_DEVICE_PATH;
typedef struct _packed_ UEFI_IMAGE_LOAD_EVENT {
uint64_t imageLocationInMemory;
uint64_t imageLengthInMemory;
uint64_t imageLinkTimeAddress;
uint64_t lengthOfDevicePath;
packed_EFI_DEVICE_PATH devicePath[];
} UEFI_IMAGE_LOAD_EVENT;
typedef struct _packed_ UEFI_PLATFORM_FIRMWARE_BLOB {
uint64_t blobBase;
uint64_t blobLength;
} UEFI_PLATFORM_FIRMWARE_BLOB;
#endif

File diff suppressed because it is too large Load diff

View file

@ -8,11 +8,13 @@
#include "json.h"
#include "macro.h"
#include "openssl-util.h"
#include "ordered-set.h"
#include "sha256.h"
#include "tpm2-pcr.h"
typedef enum TPM2Flags {
TPM2_FLAGS_USE_PIN = 1 << 0,
TPM2_FLAGS_USE_PIN = 1 << 0,
TPM2_FLAGS_USE_PCRLOCK = 1 << 1,
} TPM2Flags;
/* As per https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a
@ -37,6 +39,8 @@ static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
#define FOREACH_PCR_IN_MASK(pcr, mask) BIT_FOREACH(pcr, mask)
#define TPM2_N_HASH_ALGORITHMS 4U
#if HAVE_TPM2
#include <tss2/tss2_esys.h>
@ -111,9 +115,14 @@ char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_val
int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, size_t *ret_count);
int tpm2_tpml_pcr_selection_from_pcr_values(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPML_PCR_SELECTION *ret_selection, TPM2B_DIGEST **ret_values, size_t *ret_n_values);
int tpm2_make_encryption_session(Tpm2Context *c, const Tpm2Handle *primary, const Tpm2Handle *bind_key, Tpm2Handle **ret_session);
int tpm2_create_primary(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_PUBLIC *template, const TPM2B_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, Tpm2Handle **ret_handle);
int tpm2_create(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private);
int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private, Tpm2Handle **ret_handle);
int tpm2_load(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPM2B_PUBLIC *public, const TPM2B_PRIVATE *private, Tpm2Handle **ret_handle);
int tpm2_marshal_nv_public(const TPM2B_NV_PUBLIC *nv_public, void **ret, size_t *ret_size);
int tpm2_unmarshal_nv_public(const void *data, size_t size, TPM2B_NV_PUBLIC *ret_nv_public);
bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command);
@ -126,6 +135,7 @@ int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret)
int tpm2_get_best_pcr_bank(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH *ret);
const char *tpm2_userspace_log_path(void);
const char *tpm2_firmware_log_path(void);
typedef enum Tpm2UserspaceEventType {
TPM2_EVENT_PHASE,
@ -184,22 +194,85 @@ void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg);
void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg);
void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg);
typedef struct Tpm2PCRPredictionResult {
TPM2B_DIGEST hash[TPM2_N_HASH_ALGORITHMS]; /* a hash for each potential algorithm */
} Tpm2PCRPredictionResult;
TPM2B_DIGEST *tpm2_pcr_prediction_result_get_hash(Tpm2PCRPredictionResult *result, uint16_t alg);
/* A structure encapsulating a full set of PCR predictions with alternatives. This can be converted into a
* series of PolicyOR + PolicyPCR items for the TPM. */
typedef struct Tpm2PCRPrediction {
uint32_t pcrs; /* A mask of pcrs included */
OrderedSet* results[TPM2_PCRS_MAX]; /* set of Tpm2PCRPredictionResult objects, one for each PCR */
} Tpm2PCRPrediction;
void tpm2_pcr_prediction_done(Tpm2PCRPrediction *p);
extern const struct hash_ops tpm2_pcr_prediction_result_hash_ops;
bool tpm2_pcr_prediction_equal(Tpm2PCRPrediction *a, Tpm2PCRPrediction *b, uint16_t algorithm);
int tpm2_pcr_prediction_to_json(const Tpm2PCRPrediction *prediction, uint16_t algorithm, JsonVariant **ret);
int tpm2_pcr_prediction_from_json(Tpm2PCRPrediction *prediction, uint16_t algorithm, JsonVariant *aj);
/* As structure encapsulating all metadata stored for a pcrlock policy on disk */
typedef struct Tpm2PCRLockPolicy {
/* The below is the fixed metadata encoding information about the NV index we store the
* PolicyAuthorizeNV policy in, as well as a pinned SRK, and the encrypted PIN to use for writing to
* the NV Index. */
uint16_t algorithm;
uint32_t nv_index;
struct iovec nv_handle;
struct iovec nv_public;
struct iovec srk_handle;
struct iovec pin_public;
struct iovec pin_private;
/* The below contains the current prediction whose resulting policy is stored in the NV
* index. Once in JSON and once in parsed form. When the policy is updated the fields below are
* changed, the fields above remain fixed. */
JsonVariant *prediction_json;
Tpm2PCRPrediction prediction;
} Tpm2PCRLockPolicy;
void tpm2_pcrlock_policy_done(Tpm2PCRLockPolicy *data);
int tpm2_pcrlock_search_file(const char *path, FILE **ret_file, char **ret_path);
int tpm2_pcrlock_policy_load(const char *path, Tpm2PCRLockPolicy *ret_policy);
int tpm2_index_to_handle(Tpm2Context *c, TPM2_HANDLE index, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle);
int tpm2_index_from_handle(Tpm2Context *c, const Tpm2Handle *handle, TPM2_HANDLE *ret_index);
int tpm2_pcr_read(Tpm2Context *c, const TPML_PCR_SELECTION *pcr_selection, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
int tpm2_pcr_read_missing_values(Tpm2Context *c, Tpm2PCRValue *pcr_values, size_t n_pcr_values);
int tpm2_get_pin_auth(TPMI_ALG_HASH hash, const char *pin, TPM2B_AUTH *ret_auth);
int tpm2_set_auth(Tpm2Context *c, const Tpm2Handle *handle, const char *pin);
int tpm2_set_auth_binary(Tpm2Context *c, const Tpm2Handle *handle, const TPM2B_AUTH *auth);
int tpm2_make_policy_session(Tpm2Context *c, const Tpm2Handle *primary, const Tpm2Handle *encryption_session, Tpm2Handle **ret_session);
int tpm2_policy_auth_value(Tpm2Context *c, const Tpm2Handle *session, TPM2B_DIGEST **ret_policy_digest);
int tpm2_policy_authorize_nv(Tpm2Context *c, const Tpm2Handle *session, const Tpm2Handle *nv_handle, TPM2B_DIGEST **ret_policy_digest);
int tpm2_policy_pcr(Tpm2Context *c, const Tpm2Handle *session, const TPML_PCR_SELECTION *pcr_selection, TPM2B_DIGEST **ret_policy_digest);
int tpm2_policy_or(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_DIGEST *branches, size_t n_branches, TPM2B_DIGEST **ret_policy_digest);
int tpm2_policy_super_pcr(Tpm2Context *c, const Tpm2Handle *session, const Tpm2PCRPrediction *prediction, uint16_t algorithm);
int tpm2_calculate_pubkey_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
int tpm2_calculate_nv_index_name(const TPMS_NV_PUBLIC *nvpublic, TPM2B_NAME *ret_name);
int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest);
int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest);
int tpm2_calculate_policy_authorize_nv(const TPM2B_NV_PUBLIC *public, TPM2B_DIGEST *digest);
int tpm2_calculate_policy_pcr(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPM2B_DIGEST *digest);
int tpm2_calculate_sealing_policy(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, const TPM2B_PUBLIC *public, bool use_pin, TPM2B_DIGEST *digest);
int tpm2_calculate_policy_or(const TPM2B_DIGEST *branches, size_t n_branches, TPM2B_DIGEST *digest);
int tpm2_calculate_policy_super_pcr(Tpm2PCRPrediction *prediction, uint16_t algorithm, TPM2B_DIGEST *pcr_policy);
int tpm2_calculate_sealing_policy(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, const TPM2B_PUBLIC *public, bool use_pin, const Tpm2PCRLockPolicy *policy, TPM2B_DIGEST *digest);
int tpm2_get_or_create_srk(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLIC **ret_public, TPM2B_NAME **ret_name, TPM2B_NAME **ret_qname, Tpm2Handle **ret_handle);
int tpm2_seal(Tpm2Context *c, uint32_t seal_key_handle, const TPM2B_DIGEST *policy, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
int tpm2_unseal(Tpm2Context *c, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
int tpm2_unseal(Tpm2Context *c, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, const Tpm2PCRLockPolicy *pcrlock_policy, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
#if HAVE_OPENSSL
int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret);
@ -209,6 +282,16 @@ int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret)
int tpm2_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret);
int tpm2_tpm2b_public_to_fingerprint(const TPM2B_PUBLIC *public, void **ret_fingerprint, size_t *ret_fingerprint_size);
int tpm2_define_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE requested_nv_index, const TPM2B_DIGEST *write_policy, const char *pin, const TPM2B_AUTH *auth, TPM2_HANDLE *ret_nv_index, Tpm2Handle **ret_nv_handle, TPM2B_NV_PUBLIC *ret_nv_public);
int tpm2_write_policy_nv_index(Tpm2Context *c, const Tpm2Handle *policy_session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, const TPM2B_DIGEST *policy_digest);
int tpm2_undefine_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle);
int tpm2_seal_data(Tpm2Context *c, const struct iovec *data, const Tpm2Handle *primary_handle, const Tpm2Handle *encryption_session, const TPM2B_DIGEST *policy, struct iovec *ret_public, struct iovec *ret_private);
int tpm2_unseal_data(Tpm2Context *c, const struct iovec *public, const struct iovec *private, const Tpm2Handle *primary_handle, const Tpm2Handle *policy_session, const Tpm2Handle *encryption_session, struct iovec *ret_data);
int tpm2_serialize(Tpm2Context *c, const Tpm2Handle *handle, void **ret_serialized, size_t *ret_serialized_size);
int tpm2_deserialize(Tpm2Context *c, const void *serialized, size_t serialized_size, Tpm2Handle **ret_handle);
/* The tpm2-tss library has many structs that are simply a combination of an array (or object) and
* size. These macros allow easily initializing or assigning instances of such structs from an existing
* buffer/object and size, while also checking the size for safety with the struct buffer/object size. If the
@ -271,6 +354,11 @@ typedef struct {} Tpm2Handle;
typedef struct {} Tpm2PCRValue;
#define TPM2_PCR_VALUE_MAKE(i, h, v) (Tpm2PCRValue) {}
static inline int tpm2_pcrlock_search_file(const char *path, FILE **ret_file, char **ret_path) {
return -ENOENT;
}
#endif /* HAVE_TPM2 */
int tpm2_list_devices(void);
@ -322,10 +410,13 @@ int tpm2_asym_alg_from_string(const char *alg) _pure_;
char *tpm2_pcr_mask_to_string(uint32_t mask);
extern const uint16_t tpm2_hash_algorithms[];
typedef struct {
uint32_t search_pcr_mask;
const char *device;
const char *signature_path;
const char *pcrlock_path;
} systemd_tpm2_plugin_params;
typedef enum Tpm2Support {

View file

@ -1043,6 +1043,7 @@ static void check_seal_unseal_for_handle(Tpm2Context *c, TPM2_HANDLE handle) {
/* pubkey_pcr_mask= */ 0,
/* signature= */ NULL,
/* pin= */ NULL,
/* pcrlock_policy= */ NULL,
/* primary_alg= */ 0,
blob, blob_size,
/* policy_hash= */ NULL, /* policy_hash_size= */ 0,

View file

@ -0,0 +1,137 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
export SYSTEMD_LOG_LEVEL=debug
export PAGER=
SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
SD_PCRLOCK="/usr/lib/systemd/systemd-pcrlock"
if [[ ! -x "${SD_PCREXTEND:?}" ]] || [[ ! -x "${SD_PCRLOCK:?}" ]] ; then
echo "$SD_PCREXTEND or $SD_PCRLOCK not found, skipping pcrlock tests"
exit 0
fi
at_exit() {
if [[ $? -ne 0 ]]; then
# Dump the event log on fail, to make debugging a bit easier
[[ -e /run/log/systemd/tpm2-measure.log ]] && jq --seq --slurp </run/log/systemd/tpm2-measure.log
fi
return 0
}
trap at_exit EXIT
# Temporarily override sd-pcrextend's sanity checks
export SYSTEMD_FORCE_MEASURE=1
# The PCRs we are going to lock to. We exclude the various PCRs we touched
# above where no event log record was written, because we cannot analyze
# things without event log. We include debug PCR 16, see below.
PCRS="1+2+3+4+5+16"
# Remove the old measurement log, as it contains all kinds of nonsense from the
# previous test, which will fail our consistency checks. Removing the file also
# means we'll fail consistency check, but at least we'll fail them consistently
# (as the PCR values simply won't match the log).
rm -f /run/log/systemd/tpm2-measure.log
SYSTEMD_COLORS=256 "$SD_PCRLOCK"
"$SD_PCRLOCK" cel --no-pager --json=pretty
"$SD_PCRLOCK" log --pcr="$PCRS"
"$SD_PCRLOCK" log --json=pretty --pcr="$PCRS"
"$SD_PCRLOCK" list-components
"$SD_PCRLOCK" list-components --location=250-
"$SD_PCRLOCK" list-components --location=250-:350-
"$SD_PCRLOCK" lock-firmware-config
"$SD_PCRLOCK" lock-gpt
"$SD_PCRLOCK" lock-machine-id
"$SD_PCRLOCK" lock-file-system
"$SD_PCRLOCK" lock-file-system /
"$SD_PCRLOCK" predict --pcr="$PCRS"
"$SD_PCRLOCK" predict --pcr="0x1+0x3+4"
"$SD_PCRLOCK" predict --json=pretty --pcr="$PCRS"
SD_STUB="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)"
if [[ -n "$SD_STUB" ]]; then
"$SD_PCRLOCK" lock-pe "$SD_STUB"
"$SD_PCRLOCK" lock-pe <"$SD_STUB"
"$SD_PCRLOCK" lock-uki "$SD_STUB"
"$SD_PCRLOCK" lock-uki <"$SD_STUB"
fi
PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes
# Repeat immediately (this call will have to reuse the nvindex, rather than create it)
"$SD_PCRLOCK" make-policy --pcr="$PCRS"
"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
img="/tmp/pcrlock.img"
truncate -s 20M "$img"
echo -n hoho >/tmp/pcrlockpwd
chmod 0600 /tmp/pcrlockpwd
cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/pcrlockpwd
systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --tpm2-public-key= --wipe-slot=tpm2 "$img"
systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json
systemd-cryptsetup detach pcrlock
# Measure something into PCR 16 (the "debug" PCR), which should make the activation fail
"$SD_PCREXTEND" --pcr=16 test70
"$SD_PCRLOCK" cel --json=pretty
(! systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless )
# Now add a component for it, rebuild policy and it should work (we'll rebuild
# once like that, but don't provide the recovery pin. This should fail, since
# the PCR is hosed after all. But then we'll use recovery pin, and it should
# work.
echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16
(! "$SD_PCRLOCK" make-policy --pcr="$PCRS")
PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes
systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json
systemd-cryptsetup detach pcrlock
# And now let's do it the clean way, and generate the right policy ahead of time.
echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16
"$SD_PCRLOCK" make-policy --pcr="$PCRS"
"$SD_PCREXTEND" --pcr=16 test70-take-two
"$SD_PCRLOCK" cel --json=pretty
systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json
systemd-cryptsetup detach pcrlock
"$SD_PCRLOCK" remove-policy
"$SD_PCRLOCK" unlock-firmware-config
"$SD_PCRLOCK" unlock-gpt
"$SD_PCRLOCK" unlock-machine-id
"$SD_PCRLOCK" unlock-file-system
"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock
"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock
(! "$SD_PCRLOCK" "")
(! "$SD_PCRLOCK" predict --pcr=-1)
(! "$SD_PCRLOCK" predict --pcr=foo)
(! "$SD_PCRLOCK" predict --pcr=1+1)
(! "$SD_PCRLOCK" predict --pcr=1+++++1)
(! "$SD_PCRLOCK" make-policy --nv-index=0)
(! "$SD_PCRLOCK" make-policy --nv-index=foo)
(! "$SD_PCRLOCK" list-components --location=:)
(! "$SD_PCRLOCK" lock-gpt "")
(! "$SD_PCRLOCK" lock-gpt /dev/sr0)
(! "$SD_PCRLOCK" lock-pe /dev/full)
(! "$SD_PCRLOCK" lock-pe /bin/true)
(! "$SD_PCRLOCK" lock-uki /dev/full)
(! "$SD_PCRLOCK" lock-uki /bin/true)
(! "$SD_PCRLOCK" lock-file-system "")
rm "$img" /tmp/pcrlockpwd

View file

@ -477,6 +477,34 @@ units = [
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
'symlinks' : ['sysinit.target.wants/'],
},
{
'file' : 'systemd-pcrlock-make-policy.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
},
{
'file' : 'systemd-pcrlock-secureboot-policy.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
},
{
'file' : 'systemd-pcrlock-secureboot-authority.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
},
{
'file' : 'systemd-pcrlock-file-system.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
},
{
'file' : 'systemd-pcrlock-machine-id.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
},
{
'file' : 'systemd-pcrlock-firmware-code.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
},
{
'file' : 'systemd-pcrlock-firmware-config.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
},
{
'file' : 'systemd-portabled.service.in',
'conditions' : ['ENABLE_PORTABLED'],

View file

@ -0,0 +1,25 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Lock File Systems to TPM2 PCR Policy
Documentation=man:systemd-pcrlock(8)
DefaultDependencies=no
Conflicts=shutdown.target
Before=sysinit.target shutdown.target systemd-pcrlock-make-policy.service
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-pcrlock lock-file-system
[Install]
WantedBy=sysinit.target

View file

@ -0,0 +1,26 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Lock Firmware Code to TPM2 PCR Policy
Documentation=man:systemd-pcrlock(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-tpm2-setup.service
Before=sysinit.target shutdown.target systemd-pcrlock-make-policy.service
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-pcrlock lock-firmware-code
[Install]
WantedBy=sysinit.target

View file

@ -0,0 +1,26 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Lock Firmware Configuration to TPM2 PCR Policy
Documentation=man:systemd-pcrlock(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-tpm2-setup.service
Before=sysinit.target shutdown.target systemd-pcrlock-make-policy.service
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-pcrlock lock-firmware-config
[Install]
WantedBy=sysinit.target

View file

@ -0,0 +1,25 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Lock Machine ID to TPM2 PCR Policy
Documentation=man:systemd-pcrlock(8)
DefaultDependencies=no
Conflicts=shutdown.target
Before=sysinit.target shutdown.target systemd-pcrlock-make-policy.service
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-pcrlock lock-machine-id
[Install]
WantedBy=sysinit.target

View file

@ -0,0 +1,26 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Make TPM2 PCR Policy
Documentation=man:systemd-pcrlock(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-tpm2-setup.service
Before=sysinit.target shutdown.target
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-pcrlock make-policy --location=770
[Install]
WantedBy=sysinit.target

View file

@ -0,0 +1,26 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Lock UEFI SecureBoot Authority to TPM2 PCR Policy
Documentation=man:systemd-pcrlock(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-tpm2-setup.service
Before=sysinit.target shutdown.target systemd-pcrlock-make-policy.service
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-pcrlock lock-secureboot-authority
[Install]
WantedBy=sysinit.target

View file

@ -0,0 +1,26 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Lock UEFI SecureBoot Policy to TPM2 PCR Policy
Documentation=man:systemd-pcrlock(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-tpm2-setup.service
Before=sysinit.target shutdown.target systemd-pcrlock-make-policy.service
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-pcrlock lock-secureboot-policy
[Install]
WantedBy=sysinit.target