mirror of
https://github.com/systemd/systemd
synced 2024-07-21 10:17:21 +00:00
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:
commit
8d04721507
95
TODO
95
TODO
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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', [], ''],
|
||||
|
|
|
@ -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
559
man/systemd-pcrlock.xml
Normal 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>
|
|
@ -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
298
man/systemd.pcrlock.xml
Normal 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>
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
37
src/pcrlock/meson.build
Normal 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
|
168
src/pcrlock/pcrlock-firmware.c
Normal file
168
src/pcrlock/pcrlock-firmware.c
Normal 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;
|
||||
}
|
25
src/pcrlock/pcrlock-firmware.h
Normal file
25
src/pcrlock/pcrlock-firmware.h
Normal 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
4992
src/pcrlock/pcrlock.c
Normal file
File diff suppressed because it is too large
Load diff
1
src/pcrlock/pcrlock.d/350-action-efi-application.pcrlock
Normal file
1
src/pcrlock/pcrlock.d/350-action-efi-application.pcrlock
Normal file
|
@ -0,0 +1 @@
|
|||
{"records":[{"pcr":4,"digests":[{"hashAlg":"sha1","digest":"cd0fdb4531a6ec41be2753ba042637d6e5f7f256"},{"hashAlg":"sha256","digest":"3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba"},{"hashAlg":"sha384","digest":"77a0dab2312b4e1e57a84d865a21e5b2ee8d677a21012ada819d0a98988078d3d740f6346bfe0abaa938ca20439a8d71"},{"hashAlg":"sha512","digest":"03020279c5ea3676d6630c82a9931343225e8eab81529b65c786aeb6a445d3852a34dd193178f938b6b47345a72d4b647df309c971f7c02f0ede296a136a1086"}]}]}
|
|
@ -0,0 +1 @@
|
|||
{"records":[{"pcr":7,"digests":[{"hashAlg":"sha1","digest":"9069ca78e7450a285173431b3e52c5c25299e473"},{"hashAlg":"sha256","digest":"df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"},{"hashAlg":"sha384","digest":"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0"},{"hashAlg":"sha512","digest":"ec2d57691d9b2d40182ac565032054b7d784ba96b18bcb5be0bb4e70e3fb041eff582c8af66ee50256539f2181d7f9e53627c0189da7e75a4d5ef10ea93b20b3"}]}]}
|
|
@ -0,0 +1 @@
|
|||
{"records":[{"pcr":7,"digests":[{"hashAlg":"sha1","digest":"d9be6524a5f5047db5866813acf3277892a7a30a"},{"hashAlg":"sha256","digest":"ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"},{"hashAlg":"sha384","digest":"4a06b879c7eedbe01c945d46b5bd785b59203dce81ea6a1206c28091ca285365f760d9167778f0dc1763d4854aafd40a"},{"hashAlg":"sha512","digest":"ea71bb243b0b2db729b9eb88e3c55a3f490fbff23457825051224a1fe6e6d3f480590cfa3a4a6b12c622d6ac366feb03cd17004ed004cb3f0d52731626946679"}]}]}
|
|
@ -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"}]}]}
|
|
@ -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"}]}]}
|
|
@ -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"}]}]}
|
|
@ -0,0 +1 @@
|
|||
{"records":[]}
|
1
src/pcrlock/pcrlock.d/750-enter-initrd.pcrlock
Normal file
1
src/pcrlock/pcrlock.d/750-enter-initrd.pcrlock
Normal file
|
@ -0,0 +1 @@
|
|||
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"b1b01d5f73f321eb70e76f8a0e241ac0a3fa4a6e"},{"hashAlg":"sha256","digest":"51e6b92f405d1f98d96e3de343d61d420ad6923b25de21d766f9298192f14fed"},{"hashAlg":"sha384","digest":"687eef3a3a8c716439b5ed583657e8668401630c321f2f35d19b953ddf20b68a96474d0c2e5f0e1757bfa5ba70b9fc32"},{"hashAlg":"sha512","digest":"ab0ddfdabe43f1d06b3e58fbe17439a0f7f552e9e228d85665d485ececf7e733bae4cd7e0a17e5456e2ee7e412f5a0f37de05a782cce781e173ee26958de7f30"}]}]}
|
1
src/pcrlock/pcrlock.d/800-leave-initrd.pcrlock
Normal file
1
src/pcrlock/pcrlock.d/800-leave-initrd.pcrlock
Normal file
|
@ -0,0 +1 @@
|
|||
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"865e1ff2cc5b8db815313b23fe3d8b561212f5d1"},{"hashAlg":"sha256","digest":"3be261aff7db92bf507eae947f4003ffa2bcad0bffe3524601d62d0bc8be7135"},{"hashAlg":"sha384","digest":"9c0743b7a2e1ee06c70b7137b763cd2205c26ced274149959b05bd5a51bfa96b4fedaa4f87398b5c88986d1ff0879910"},{"hashAlg":"sha512","digest":"01b8ca86b9f8fac967f383380aff7cdffd2ef0c496574517c25398f7c74aa611821dd469ba021b2aa9b9a7232865708ca45c79368f2e7fffda3dd6b308264008"}]}]}
|
1
src/pcrlock/pcrlock.d/850-sysinit.pcrlock
Normal file
1
src/pcrlock/pcrlock.d/850-sysinit.pcrlock
Normal file
|
@ -0,0 +1 @@
|
|||
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"aeabcf402223916e804cce79778a55d5a9276983"},{"hashAlg":"sha256","digest":"730bb5a583ba880c277e656d2dc8aba1a314a11b14d25b05153d2bab82567a48"},{"hashAlg":"sha384","digest":"955cc8939f81d862b3119aabe612fd36bf91668bb62397f5e4126085d79ba6d7cbfa4e3a2345747f0b476ce4b1cbc2c9"},{"hashAlg":"sha512","digest":"a9eb62cdd1cd8292b6325a8ee3770d6f1b613426a749e17ffba8f90bdd6c41806468fb79d01276de7cc791877dfebae165d4ed07585154acf96652c6db92acc1"}]}]}
|
1
src/pcrlock/pcrlock.d/900-ready.pcrlock
Normal file
1
src/pcrlock/pcrlock.d/900-ready.pcrlock
Normal file
|
@ -0,0 +1 @@
|
|||
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"75c0533730caf1f78561c0883fb87bc8d98ef04b"},{"hashAlg":"sha256","digest":"b24d6d33736ecd5604a4b17bc9c6481039fac362bb7df044ef1c10a2bfd21db6"},{"hashAlg":"sha384","digest":"23ed5781da39fe6dc17f79478aeeb9eb2bca1d776061da188e10f9c85f7933fb39cfdba50f39af8aed24e5b45b80d006"},{"hashAlg":"sha512","digest":"ca6616f94a209e53f6fdc526b473172eb4b2157cf4809c31e36ad52db614ed352e68407be53c238ba17a561c4fde43f4a859aa8711f9781a0c934296d4d7571b"}]}]}
|
1
src/pcrlock/pcrlock.d/950-shutdown.pcrlock
Normal file
1
src/pcrlock/pcrlock.d/950-shutdown.pcrlock
Normal file
|
@ -0,0 +1 @@
|
|||
{"records":[{"pcr":11,"digests":[{"hashAlg":"sha1","digest":"53669f193b2174641c72654b5c3e5b67950334ae"},{"hashAlg":"sha256","digest":"08434ba9cdf55a02284e2913400586cd289878e0f055f7bb0b07ce392caeb989"},{"hashAlg":"sha384","digest":"186e2d6603b9755221b7ef894dd52b1154b48ef4786aec06ab6f7709e639715e89bd59fa80736bb45f0ca88583c212c1"},{"hashAlg":"sha512","digest":"9e5549deb36fc48768cb80e03bc91c36cf549ff5921e05bab5b68faefda7fac8c8a0755db783cbf1c1b98c80dc22ef06ff3f4a0a16704749f5cd4acf40e42a94"}]}]}
|
1
src/pcrlock/pcrlock.d/990-final.pcrlock
Normal file
1
src/pcrlock/pcrlock.d/990-final.pcrlock
Normal 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
246
src/pcrlock/pehash.c
Normal 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, §ions);
|
||||
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, §ions);
|
||||
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
11
src/pcrlock/pehash.h
Normal 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);
|
|
@ -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),
|
||||
|
|
|
@ -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',
|
||||
|
|
67
src/shared/tpm2-event-log.c
Normal file
67
src/shared/tpm2-event-log.c
Normal 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
139
src/shared/tpm2-event-log.h
Normal 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
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
137
test/units/testsuite-70.pcrlock.sh
Executable file
137
test/units/testsuite-70.pcrlock.sh
Executable 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
|
|
@ -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'],
|
||||
|
|
25
units/systemd-pcrlock-file-system.service.in
Normal file
25
units/systemd-pcrlock-file-system.service.in
Normal 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
|
26
units/systemd-pcrlock-firmware-code.service.in
Normal file
26
units/systemd-pcrlock-firmware-code.service.in
Normal 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
|
26
units/systemd-pcrlock-firmware-config.service.in
Normal file
26
units/systemd-pcrlock-firmware-config.service.in
Normal 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
|
25
units/systemd-pcrlock-machine-id.service.in
Normal file
25
units/systemd-pcrlock-machine-id.service.in
Normal 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
|
26
units/systemd-pcrlock-make-policy.service.in
Normal file
26
units/systemd-pcrlock-make-policy.service.in
Normal 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
|
26
units/systemd-pcrlock-secureboot-authority.service.in
Normal file
26
units/systemd-pcrlock-secureboot-authority.service.in
Normal 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
|
26
units/systemd-pcrlock-secureboot-policy.service.in
Normal file
26
units/systemd-pcrlock-secureboot-policy.service.in
Normal 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
|
Loading…
Reference in a new issue