optionally run a software TPM at boot as fallback for TPM less machines (#41016)
In various scenarios it's useful to be able to run a software TPM as fallback on a machine if it doesn't natively have a hardware for it, in order to provide somewhat systematic interfacing for legacy machines. This adds the infrastructure for it. Relevant parts: 1. On EFI systems sd-stub will now generate a random secret on first boot and store it in a persistent NV variable, which is marked inaccessible to the OS. It then derives a per-OS secret from that which is passed to the OS via the initrd logic. This is generally useful, but in particular is intended to secure the software TPM at least a bit: it provides better security than nothing (i.e. the only protection in place is that the firmwrae protections work, but this is also what shim relies on, hence maybe not too bad), and allows swtpm to encrypt its storage with something. 2. systemd-tpm2-generator is extended to optionally start an swtpm if no tpm hw is detected. Because this is of course a major downgrade in security, this has to be requested explicitly at boot via a kernel cmdline switch. 3. This optionally mounts the ESP from the initrd. This is general infrastructure, and has been requested before, but is particularly interesting in the context of software TPMs: the state needs to be stored somewhere, and that before the rootfs can be unlocked. 4. This introduces a special new separator measurement for PCRs 0-7 that isolates all measurements from pre-os/uefi world from those done by the OS. I added this for three reasons: - in the swtpm case we'll not have any pre-os/uefi measurements, and we need to be able to determine cleanly that this is the case. this hence is supposed to play a similar role as the usual firmware separator measurement, that however cleanly fixates to the PCRs even the case where the firmware measurements don't exist at all - this is a very comprehensive fix for #40567 - not all firmwares generate the firmware separator at all, but it is essential to seal off firmware variables from OS generated ones. This can fill the void to some degree. 6. This introduces a new kernel cmdline switch systemd.tpm2-measured-os=1, which allows force enabling all our measurement logic, even if UKI TPM measurements are not done. This is supposed to be used for the swtpm case so that one gets all the measurements even without having the early boot verified boot chain in place. Benefits of all this: systems that care about TPMs have a (lower security) compat glue in place that allows supporting legacy hw the same way as modern hw in many ways, so that remote attestation and other uses can reasonably work with the same codepaths. Also see: https://github.com/lxc/incus-os/pull/667 regarding prior similar work. Fixes: #40567
This commit is contained in:
@@ -133,6 +133,14 @@ Features:
|
||||
* add service file setting to force the fwmark (a la SO_MARK) to some value, so
|
||||
that we can allowlist certain services for imds this way.
|
||||
|
||||
* lock down swtpm a bit to make it harder to extract keys from it as it is
|
||||
running. i.e. make ptracing + termination hard from the outside. also run
|
||||
swtpm as unpriv user (not trivial, probably requires patch swtpm, as it needs
|
||||
to allocate vtpm device), to lock it down from the inside.
|
||||
|
||||
* once swtpm's sd_notify() support has landed in the distributions, remove the
|
||||
invocation in tpm2-swtpm.c and let swtpm handle it.
|
||||
|
||||
* make systemd work nicely without /bin/sh, logins and associated shell tools around
|
||||
- make sure debug shell service (sushell) has a nice failure mode, prints a message and reboots
|
||||
- varlink interface for "systemctl start" and friends
|
||||
@@ -156,13 +164,6 @@ Features:
|
||||
|
||||
* on first login of a user, measure its identity to some nvpcr
|
||||
|
||||
* optionally spawn an swtpm instance if a system doesn't have a native tpm, do
|
||||
it via the tpm generator
|
||||
|
||||
* add a secret key logic to sd-stub, that uses early-boot efi variables for
|
||||
storing, that can be used as fallback logic for tpm-less systems for disk
|
||||
encryption, and swtpm state encryption.
|
||||
|
||||
* sd-lldp: pick up 802.3 maximum frame size/mtu, to be able to detect jumbo
|
||||
frame capable networks
|
||||
|
||||
@@ -562,7 +563,7 @@ Features:
|
||||
service into the early boot, waiting for the DMI and network device to show
|
||||
up.
|
||||
|
||||
* Add UKI profile conditioning so that profles are only available if secure
|
||||
* Add UKI profile conditioning so that profiles are only available if secure
|
||||
boot is turned off, or only on. similar, add conditions on TPM availability,
|
||||
network boot, and other conditions.
|
||||
|
||||
|
||||
@@ -782,6 +782,28 @@
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.tpm2_software_fallback=</varname></term>
|
||||
|
||||
<listitem><para>Controls whether to start a fallback software TPM service in case a hardware TPM is
|
||||
not available, implemented by
|
||||
<citerefentry><refentrytitle>systemd-tpm2-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.tpm2_measured_os=</varname></term>
|
||||
|
||||
<listitem><para>Controls whether to execute various boot and runtime TPM PCR measurements. Takes a
|
||||
boolean argument. If not specified explicitly this behaviour is enabled automatically in case
|
||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> is
|
||||
used and it succeeded in doing pre-boot measurements of the booted UKI, and otherwise
|
||||
disabled.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.factory_reset=</varname></term>
|
||||
|
||||
|
||||
@@ -1138,6 +1138,7 @@ manpages = [
|
||||
'systemd-pcrfs@.service',
|
||||
'systemd-pcrmachine.service',
|
||||
'systemd-pcrnvdone.service',
|
||||
'systemd-pcrosseparator.service',
|
||||
'systemd-pcrphase-initrd.service',
|
||||
'systemd-pcrphase-sysinit.service',
|
||||
'systemd-pcrproduct.service'],
|
||||
@@ -1237,6 +1238,10 @@ manpages = [
|
||||
'8',
|
||||
['systemd-tpm2-setup', 'systemd-tpm2-setup-early.service'],
|
||||
'ENABLE_BOOTLOADER'],
|
||||
['systemd-tpm2-swtpm.service',
|
||||
'8',
|
||||
['systemd-tpm2-swtpm'],
|
||||
'ENABLE_BOOTLOADER'],
|
||||
['systemd-tty-ask-password-agent', '1', [], ''],
|
||||
['systemd-udev-settle.service', '8', [], ''],
|
||||
['systemd-udevd.service',
|
||||
|
||||
@@ -75,6 +75,13 @@
|
||||
discovery based on the boot loader reported ESP which is also enabled if no <literal>root=</literal>
|
||||
parameter is specified at all. (The latter relies on <command>systemd-udevd.service</command>'s
|
||||
<filename>/dev/gpt-auto-root</filename> block device symlink generation).</para></listitem>
|
||||
|
||||
<listitem><para>It will also generate a unit file mounting the EFI System Partition (ESP) to
|
||||
<filename>/sysefi/</filename>, if applicable. Unlike the file systems mentioned above this mount is not
|
||||
activated by default however, but can be pulled in by services requiring ESP access from within the
|
||||
initrd. Note that this mount point is initrd-specific and does not make use of autofs. The ESP is
|
||||
typically mounted at a different place and via autofs once the system transitions out of the
|
||||
initrd.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
@@ -179,7 +186,7 @@
|
||||
<entry><constant>SD_GPT_ESP</constant></entry>
|
||||
<entry><constant>c12a7328-f81f-11d2-ba4b-00a0c93ec93b</constant></entry>
|
||||
<entry>EFI System Partition (ESP)</entry>
|
||||
<entry><filename>/efi/</filename> or <filename>/boot/</filename></entry>
|
||||
<entry><filename>/efi/</filename> or <filename>/boot/</filename> once the system transitioned out of the initrd, <filename>/sysefi/</filename> before</entry>
|
||||
<entry>The first partition with this type UUID located on the same disk as the root partition is mounted to <filename>/boot/</filename> or <filename>/efi/</filename>, see below.</entry>
|
||||
</row>
|
||||
<row>
|
||||
@@ -259,7 +266,9 @@
|
||||
|
||||
<para>The ESP is mounted to <filename>/boot/</filename> if that directory exists and is not used for
|
||||
XBOOTLDR, and otherwise to <filename>/efi/</filename>. Same as for <filename>/boot/</filename>, an
|
||||
automount unit is used. The mount point will be created if necessary.</para>
|
||||
automount unit is used. The mount point will be created if necessary. These apply once the system
|
||||
transitioned out of the initrd phase. Before that, if components in the initrd require ESP access, it
|
||||
will be mounted to <filename>/sysefi/</filename>.</para>
|
||||
|
||||
<para>No configuration is created for mount points that are configured in <citerefentry
|
||||
project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry> or when
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<refname>systemd-pcrphase-sysinit.service</refname>
|
||||
<refname>systemd-pcrphase-initrd.service</refname>
|
||||
<refname>systemd-pcrmachine.service</refname>
|
||||
<refname>systemd-pcrosseparator.service</refname>
|
||||
<refname>systemd-pcrproduct.service</refname>
|
||||
<refname>systemd-pcrfs-root.service</refname>
|
||||
<refname>systemd-pcrfs@.service</refname>
|
||||
@@ -34,6 +35,7 @@
|
||||
<para><filename>systemd-pcrphase-sysinit.service</filename></para>
|
||||
<para><filename>systemd-pcrphase-initrd.service</filename></para>
|
||||
<para><filename>systemd-pcrmachine.service</filename></para>
|
||||
<para><filename>systemd-pcrosseparator.service</filename></para>
|
||||
<para><filename>systemd-pcrproduct.service</filename></para>
|
||||
<para><filename>systemd-pcrfs-root.service</filename></para>
|
||||
<para><filename>systemd-pcrfs@.service</filename></para>
|
||||
@@ -53,6 +55,11 @@
|
||||
(see <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>) into
|
||||
PCR 15.</para>
|
||||
|
||||
<para><filename>systemd-pcrosseparator.service</filename> is a system service that measures the word
|
||||
<literal>os-separator</literal> into PCRs 0-7, 9, 12-14. This acts as additional separator measurement
|
||||
separating pre-boot (i.e. firmware + bootloader) measurements from OS measurements, and in particular
|
||||
"seals" off the firmware PCRs.</para>
|
||||
|
||||
<para><filename>systemd-pcrproduct.service</filename> is a system service that measures the firmware
|
||||
product UUID (as provided by one of SMBIOS, Devicetree, …) into a NvPCR named
|
||||
<literal>hardware</literal>.</para>
|
||||
@@ -168,7 +175,8 @@
|
||||
|
||||
<listitem><para>Takes the index of the PCR to extend. If <option>--machine-id</option> or
|
||||
<option>--file-system=</option> are specified defaults to 15, otherwise (and unless
|
||||
<option>--product-id</option> is specified) defaults to 11. May not be combined with
|
||||
<option>--product-id</option> is specified) defaults to 11. May be specified multiple times, in order
|
||||
to measure to multiple PCRs at the same time. May not be combined with
|
||||
<option>--nvpcr=</option>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
|
||||
@@ -45,6 +45,14 @@
|
||||
for it yet. The latter might be useful in environments where a suitable TPM2 driver for the available
|
||||
hardware is not available.</para>
|
||||
|
||||
<para>The <option>systemd.tpm2_software_fallback=</option> kernel command line option (which takes a
|
||||
boolean argument, defaulting to false) may be used to enable an automatic software TPM fallback in case a
|
||||
hardware TPM is not detected and
|
||||
<citerefentry><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry> is
|
||||
available. This pulls in the
|
||||
<citerefentry><refentrytitle>systemd-tpm2-swtpm.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
service.</para>
|
||||
|
||||
<para><command>systemd-tpm2-generator</command> implements
|
||||
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
</refsect1>
|
||||
@@ -55,6 +63,7 @@
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-tpm2-swtpm.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
<refentry id="systemd-tpm2-swtpm.service" conditional='ENABLE_BOOTLOADER HAVE_TPM2'
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-tpm2-swtpm.service</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-tpm2-swtpm.service</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-tpm2-swtpm.service</refname>
|
||||
<refname>systemd-tpm2-swtpm</refname>
|
||||
<refpurpose>Provide a fallback software TPM</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-tpm2-swtpm.service</filename></para>
|
||||
<para><filename>/usr/lib/systemd/systemd-tpm2-swtpm</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>The <filename>systemd-tpm2-swtpm.service</filename> provides fallback software TPM functionality,
|
||||
intended for use in environments where a discrete or firmware TPM ("hardware TPM") is not available. It is
|
||||
pulled into the boot process by
|
||||
<citerefentry><refentrytitle>systemd-tpm2-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
if a hardware TPM is not available, and the system is configured to provide a software TPM in that case.</para>
|
||||
|
||||
<para>Note that a software TPM provides only very weak security properties compared to a hardware TPM,
|
||||
and hence should only be used as a fallback mechanism if a hardware TPM is not available but TPM
|
||||
semantics are desired. This service ultimately wraps
|
||||
<citerefentry><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
|
||||
|
||||
<para>If the boot secret <filename>/.extra/boot-secret</filename> (in the initrd) or
|
||||
<filename>/run/systemd/stub/boot-secret</filename> (on the host) is available the software TPM NVRAM
|
||||
storage is encrypted with this key. See
|
||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
|
||||
details.</para>
|
||||
|
||||
<para>The TPM NVRAM storage is placed on the EFI System Partition as it needs to be accessible during
|
||||
very early boot-up, in particular before the root file system is decrypted and mounted.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-tpm2-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
@@ -1605,6 +1605,10 @@
|
||||
<entry>measured-uki</entry>
|
||||
<entry>Unified Kernel Image with PCR 11 Measurements, as per <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>. <xi:include href="version-info.xml" xpointer="v255"/></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>measured-os</entry>
|
||||
<entry>OS PCR measurements enabled. This is typically equivalent to <varname>measured-uki</varname>, however may also be set explicitly via the <varname>systemd.tpm2_measured_os=</varname> kernel command line switch, see <citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details. The various system services doing boot and runtime measurements are conditioned on this flag. <xi:include href="version-info.xml" xpointer="v261"/></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
@@ -123,6 +123,7 @@ Packages=
|
||||
sed
|
||||
socat
|
||||
strace
|
||||
swtpm
|
||||
tar
|
||||
tree
|
||||
util-linux
|
||||
|
||||
@@ -63,6 +63,7 @@ Packages=
|
||||
softhsm
|
||||
squashfs-tools
|
||||
stress-ng
|
||||
swtpm-tools
|
||||
tpm2-tools
|
||||
veritysetup
|
||||
vim-common
|
||||
|
||||
@@ -71,6 +71,7 @@ Packages=
|
||||
softhsm2
|
||||
squashfs-tools
|
||||
stress-ng
|
||||
swtpm-tools
|
||||
tgt
|
||||
tpm2-tools
|
||||
tzdata
|
||||
|
||||
@@ -16,4 +16,5 @@ Packages=
|
||||
findutils
|
||||
grep
|
||||
sed
|
||||
swtpm
|
||||
tar
|
||||
|
||||
@@ -7,6 +7,7 @@ Distribution=|fedora
|
||||
[Content]
|
||||
PrepareScripts=%D/mkosi/mkosi.conf.d/centos-fedora/systemd.prepare
|
||||
Packages=
|
||||
swtpm-tools
|
||||
tpm2-tools
|
||||
|
||||
VolatilePackages=
|
||||
|
||||
@@ -9,6 +9,7 @@ PrepareScripts=%D/mkosi/mkosi.conf.d/debian-ubuntu/systemd.prepare
|
||||
Packages=
|
||||
btrfs-progs
|
||||
tpm2-tools
|
||||
swtpm-tools
|
||||
|
||||
VolatilePackages=
|
||||
libsystemd-shared
|
||||
|
||||
@@ -56,7 +56,7 @@ int verb_nvpcrs(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
int r;
|
||||
|
||||
bool have_tpm2 = tpm2_is_fully_supported();
|
||||
bool have_tpm2 = tpm2_is_mostly_supported();
|
||||
|
||||
if (!have_tpm2)
|
||||
log_notice("System lacks full TPM2 support, not showing NvPCR state.");
|
||||
|
||||
@@ -101,8 +101,8 @@ int verb_pcrs(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
const char *alg = NULL;
|
||||
int r;
|
||||
|
||||
if (!tpm2_is_fully_supported())
|
||||
log_notice("System lacks full TPM2 support, not showing PCR state.");
|
||||
if (!tpm2_is_mostly_supported())
|
||||
log_notice("System lacks sufficient TPM2 support, not showing PCR state.");
|
||||
else {
|
||||
r = get_pcr_alg(&alg);
|
||||
if (r < 0)
|
||||
|
||||
+1
-1
@@ -1706,7 +1706,7 @@ int write_data_file_atomic_at(
|
||||
return r;
|
||||
}
|
||||
|
||||
r = fchmod_umask(fd, 0644);
|
||||
r = fchmod_umask(fd, FLAGS_SET(flags, WRITE_DATA_FILE_MODE_0400) ? 0400 : 0644);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -166,6 +166,7 @@ int fopen_mode_to_flags(const char *mode);
|
||||
|
||||
typedef enum WriteDataFileFlags {
|
||||
WRITE_DATA_FILE_MKDIR_0755 = 1 << 0,
|
||||
WRITE_DATA_FILE_MODE_0400 = 1 << 1,
|
||||
} WriteDataFileFlags;
|
||||
|
||||
int write_data_file_atomic_at(int dir_fd, const char *path, const struct iovec *iovec, WriteDataFileFlags flags);
|
||||
|
||||
@@ -477,6 +477,16 @@ int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
printf(" Measured UKI: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
|
||||
}
|
||||
|
||||
k = efi_measured_os(LOG_DEBUG);
|
||||
if (k > 0)
|
||||
printf(" Measured OS: %syes%s\n", ansi_highlight_green(), ansi_normal());
|
||||
else if (k == 0)
|
||||
printf(" Measured OS: no\n");
|
||||
else {
|
||||
errno = -k;
|
||||
printf(" Measured OS: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
|
||||
}
|
||||
|
||||
k = efi_get_reboot_to_firmware();
|
||||
if (k > 0)
|
||||
printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
|
||||
|
||||
@@ -1030,11 +1030,11 @@ static int measure_volume_key(
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_measured_uki(LOG_WARNING);
|
||||
r = efi_measured_os(LOG_WARNING);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_debug("Kernel stub did not measure kernel image into the expected PCR, skipping userspace volume key measurement, too.");
|
||||
log_debug("OS measurements not explicitly requested and kernel stub did not measure kernel image into the expected PCR, skipping userspace volume key measurement, too.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1109,11 +1109,11 @@ static int measure_keyslot(
|
||||
}
|
||||
|
||||
#if HAVE_TPM2
|
||||
r = efi_measured_uki(LOG_WARNING);
|
||||
r = efi_measured_os(LOG_WARNING);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_debug("Kernel stub did not measure kernel image into the expected PCR, skipping userspace key slot measurement, too.");
|
||||
log_debug("OS measurements not explicitly requested and kernel stub did not measure kernel image into the expected PCR, skipping userspace key slot measurement, too.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -672,9 +672,9 @@ static int add_mount(
|
||||
}
|
||||
|
||||
if (flags & MOUNT_PCRFS) {
|
||||
r = efi_measured_uki(LOG_WARNING);
|
||||
r = efi_measured_os(LOG_WARNING);
|
||||
if (r == 0)
|
||||
log_debug("Kernel stub did not measure kernel image into PCR, skipping userspace measurement, too.");
|
||||
log_debug("OS measurements not explicitly requested and kernel stub did not measure kernel image into PCR, skipping userspace measurement, too.");
|
||||
else if (r > 0) {
|
||||
r = generator_hook_up_pcrfs(dest, where, target_unit);
|
||||
if (r < 0)
|
||||
|
||||
@@ -115,11 +115,13 @@ static int add_cryptsetup(
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = efi_measured_uki(LOG_WARNING);
|
||||
if (r > 0)
|
||||
r = efi_measured_os(LOG_WARNING);
|
||||
if (r > 0) {
|
||||
/* Enable TPM2 based unlocking automatically, if we have a TPM. See #30176. */
|
||||
if (!strextend_with_separator(&options, ",", "tpm2-device=auto"))
|
||||
return log_oom();
|
||||
} else if (r == 0)
|
||||
log_debug("Will not enable TPM based unlocking of volume '%s', OS measurements are not explicitly requested and not booted via systemd-stub with measurements enabled.", id);
|
||||
|
||||
if (FLAGS_SET(flags, MOUNT_MEASURE)) {
|
||||
/* We only measure the root volume key into PCR 15 if we are booted with sd-stub (i.e. in a
|
||||
@@ -130,7 +132,7 @@ static int add_cryptsetup(
|
||||
if (!strextend_with_separator(&options, ",", "tpm2-measure-pcr=yes,tpm2-measure-keyslot-nvpcr=yes"))
|
||||
return log_oom();
|
||||
if (r == 0)
|
||||
log_debug("Will not measure volume key of volume '%s', not booted via systemd-stub with measurements enabled.", id);
|
||||
log_debug("Will not measure volume key of volume '%s', as OS measurements are not explicitly requested and not booted via systemd-stub with measurements enabled.", id);
|
||||
}
|
||||
|
||||
r = generator_write_cryptsetup_service_section(f, id, what, NULL, options);
|
||||
@@ -240,11 +242,11 @@ static int add_veritysetup(
|
||||
return log_oom();
|
||||
|
||||
if (FLAGS_SET(flags, MOUNT_MEASURE)) {
|
||||
r = efi_measured_uki(LOG_WARNING);
|
||||
r = efi_measured_os(LOG_WARNING);
|
||||
if (r > 0 && !strextend_with_separator(&options, ",", "tpm2-measure-nvpcr=yes"))
|
||||
return log_oom();
|
||||
if (r == 0)
|
||||
log_debug("Will not measure root hash/signature of volume '%s', not booted via systemd-stub with measurements enabled.", id);
|
||||
else if (r == 0)
|
||||
log_debug("Will not measure root hash/signature of volume '%s', OS measurements not explicitly requested and not booted via systemd-stub with measurements enabled.", id);
|
||||
}
|
||||
|
||||
r = generator_write_veritysetup_service_section(
|
||||
@@ -307,7 +309,8 @@ static int add_mount(
|
||||
MountPointFlags flags,
|
||||
const char *options,
|
||||
const char *description,
|
||||
const char *post) {
|
||||
const char *post,
|
||||
const char *conflicts) {
|
||||
|
||||
_cleanup_free_ char *unit = NULL, *crypto_what = NULL, *opts_filtered = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
@@ -377,6 +380,12 @@ static int add_mount(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (conflicts)
|
||||
fprintf(f,
|
||||
"Conflicts=%1$s\n"
|
||||
"Before=%1$s\n",
|
||||
conflicts);
|
||||
|
||||
fprintf(f,
|
||||
"\n"
|
||||
"[Mount]\n"
|
||||
@@ -493,7 +502,8 @@ static int add_partition_mount(
|
||||
(STR_IN_SET(id, "root", "var") ? MOUNT_MEASURE : 0), /* by default measure rootfs and /var, since they contain the "identity" of the system */
|
||||
options,
|
||||
description,
|
||||
SPECIAL_LOCAL_FS_TARGET);
|
||||
SPECIAL_LOCAL_FS_TARGET,
|
||||
/* conflicts= */ NULL);
|
||||
}
|
||||
|
||||
static int add_partition_swap(DissectedPartition *p) {
|
||||
@@ -582,7 +592,8 @@ static int add_automount(
|
||||
flags,
|
||||
options,
|
||||
description,
|
||||
/* post= */ NULL);
|
||||
/* post= */ NULL,
|
||||
/* conflicts= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -919,7 +930,8 @@ static int add_root_mount(void) {
|
||||
MOUNT_MEASURE,
|
||||
options,
|
||||
"Root Partition",
|
||||
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
|
||||
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET,
|
||||
/* conflicts= */ NULL);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
@@ -995,7 +1007,8 @@ static int add_usr_mount(void) {
|
||||
/* flags= */ 0,
|
||||
options,
|
||||
"/usr/ Partition",
|
||||
in_initrd() ? SPECIAL_INITRD_USR_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
|
||||
in_initrd() ? SPECIAL_INITRD_USR_FS_TARGET : SPECIAL_LOCAL_FS_TARGET,
|
||||
/* conflicts= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1009,7 +1022,8 @@ static int add_usr_mount(void) {
|
||||
MOUNT_VALIDATEFS,
|
||||
"bind",
|
||||
"/usr/ Partition (Final)",
|
||||
SPECIAL_INITRD_FS_TARGET);
|
||||
SPECIAL_INITRD_FS_TARGET,
|
||||
/* conflicts= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -1017,6 +1031,49 @@ static int add_usr_mount(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_early_esp_mount(void) {
|
||||
int r;
|
||||
|
||||
/* Early ESP discovery is a bit different than the other mounts here: it's purely about the initrd,
|
||||
* and goes away during the transition to the host (where it might likely be mounted again, but then
|
||||
* via autofs, hence lazily). Moreover, the location is fixed → /sysefi/, i.e. we do not bother with
|
||||
* XBOOTLDR vs. ESP for this. Also, the mount is not pulled in by default, but is expected to be
|
||||
* pulled in by the component that uses it.
|
||||
*
|
||||
* The initial usecase for this is software TPM that needs a place to store its state before the root
|
||||
* file system can be mounted.
|
||||
*
|
||||
* Or in other words: this is much simpler, more focussed on a short-lived boot-time operation than
|
||||
* the regular logic during later boot. */
|
||||
|
||||
if (!in_initrd())
|
||||
return 0;
|
||||
|
||||
if (!is_efi_boot())
|
||||
return 0;
|
||||
|
||||
_cleanup_free_ char *options = NULL;
|
||||
r = partition_pick_mount_options(
|
||||
PARTITION_ESP,
|
||||
"vfat",
|
||||
/* rw= */ true,
|
||||
/* discard= */ false,
|
||||
&options,
|
||||
/* ret_ms_flags= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to pick ESP mount options: %m");
|
||||
|
||||
return add_mount("esp",
|
||||
"/dev/disk/by-designator/esp",
|
||||
"/sysefi/",
|
||||
"vfat",
|
||||
MOUNT_RW,
|
||||
options,
|
||||
"EFI System Partition (Early)",
|
||||
/* post= */ NULL,
|
||||
/* conflicts= */ "initrd-switch-root.target");
|
||||
}
|
||||
|
||||
static int process_loader_partitions(DissectedPartition *esp, DissectedPartition *xbootldr) {
|
||||
sd_id128_t loader_uuid;
|
||||
int r;
|
||||
@@ -1190,7 +1247,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
||||
return 0;
|
||||
|
||||
/* Disable root disk logic if there's a root= value specified (unless it happens to be
|
||||
* "gpt-auto" or "gpt-auto-force") */
|
||||
* "gpt-auto", "gpt-auto-force", "dissect", "dissect-force") */
|
||||
|
||||
arg_auto_root = parse_gpt_auto_root("root=", value);
|
||||
assert(arg_auto_root >= 0);
|
||||
@@ -1244,9 +1301,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
/* Disable root disk logic if there's a root= value specified (unless it happens to be
|
||||
* "gpt-auto" or "gpt-auto-force") */
|
||||
|
||||
arg_auto_usr = parse_gpt_auto_root("mount.usr=", value);
|
||||
assert(arg_auto_usr >= 0);
|
||||
|
||||
@@ -1325,6 +1379,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
r = 0;
|
||||
RET_GATHER(r, add_root_mount());
|
||||
RET_GATHER(r, add_usr_mount());
|
||||
RET_GATHER(r, add_early_esp_mount());
|
||||
RET_GATHER(r, add_mounts());
|
||||
|
||||
return r;
|
||||
|
||||
@@ -86,7 +86,7 @@ static int add_dissected_swap_cryptsetup(void) {
|
||||
r = generator_write_cryptsetup_service_section(
|
||||
f, "swap", DISSECTED_SWAP_LUKS_DEVICE,
|
||||
/* key_file= */ NULL,
|
||||
efi_measured_uki(LOG_DEBUG) > 0 ? "tpm2-device=auto" : NULL);
|
||||
efi_measured_os(LOG_DEBUG) > 0 ? "tpm2-device=auto" : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
+37
-28
@@ -29,7 +29,7 @@ static char **arg_banks = NULL;
|
||||
static char *arg_file_system = NULL;
|
||||
static bool arg_machine_id = false;
|
||||
static bool arg_product_id = false;
|
||||
static unsigned arg_pcr_index = UINT_MAX;
|
||||
static uint32_t arg_pcr_mask = 0;
|
||||
static char *arg_nvpcr_name = NULL;
|
||||
static bool arg_varlink = false;
|
||||
static bool arg_early = false;
|
||||
@@ -139,11 +139,16 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
case ARG_PCR:
|
||||
if (isempty(optarg)) {
|
||||
arg_pcr_mask = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
r = tpm2_pcr_index_from_string(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse PCR index: %s", optarg);
|
||||
|
||||
arg_pcr_index = r;
|
||||
arg_pcr_mask |= INDEX_TO_MASK(uint32_t, r);
|
||||
break;
|
||||
|
||||
case ARG_NVPCR:
|
||||
@@ -213,7 +218,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (!!arg_file_system + arg_machine_id + arg_product_id > 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--file-system=, --machine-id, --product-id may not be combined.");
|
||||
|
||||
if (arg_pcr_index != UINT_MAX && arg_nvpcr_name)
|
||||
if (arg_pcr_mask != 0 && arg_nvpcr_name)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--pcr= and --nvpcr= may not be combined.");
|
||||
|
||||
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
|
||||
@@ -221,11 +226,11 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
|
||||
if (r > 0)
|
||||
arg_varlink = true;
|
||||
else if (arg_pcr_index == UINT_MAX && !arg_nvpcr_name) {
|
||||
arg_pcr_index =
|
||||
(arg_file_system || arg_machine_id) ? TPM2_PCR_SYSTEM_IDENTITY : /* → PCR 15 */
|
||||
!arg_product_id ? TPM2_PCR_KERNEL_BOOT : /* → PCR 11 */
|
||||
UINT_MAX;
|
||||
else if (arg_pcr_mask == 0 && !arg_nvpcr_name) {
|
||||
arg_pcr_mask =
|
||||
(arg_file_system || arg_machine_id) ? INDEX_TO_MASK(uint32_t, TPM2_PCR_SYSTEM_IDENTITY) : /* → PCR 15 */
|
||||
!arg_product_id ? INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT) : /* → PCR 11 */
|
||||
0;
|
||||
|
||||
r = free_and_strdup_warn(&arg_nvpcr_name, arg_product_id ? "hardware" : NULL);
|
||||
if (r < 0)
|
||||
@@ -235,7 +240,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int determine_banks(Tpm2Context *c, unsigned target_pcr_nr) {
|
||||
static int determine_banks(Tpm2Context *c, uint32_t target_pcr_mask) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
@@ -244,7 +249,7 @@ static int determine_banks(Tpm2Context *c, unsigned target_pcr_nr) {
|
||||
if (!strv_isempty(arg_banks)) /* Explicitly configured? Then use that */
|
||||
return 0;
|
||||
|
||||
r = tpm2_get_good_pcr_banks_strv(c, UINT32_C(1) << target_pcr_nr, &l);
|
||||
r = tpm2_get_good_pcr_banks_strv(c, target_pcr_mask, &l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not verify pcr banks: %m");
|
||||
|
||||
@@ -275,7 +280,7 @@ static int escape_and_truncate_data(const void *data, size_t size, char **ret) {
|
||||
}
|
||||
|
||||
static int extend_pcr_now(
|
||||
unsigned pcr,
|
||||
uint32_t pcr_mask,
|
||||
const void *data,
|
||||
size_t size,
|
||||
Tpm2UserspaceEventType event) {
|
||||
@@ -283,11 +288,13 @@ static int extend_pcr_now(
|
||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
||||
int r;
|
||||
|
||||
assert(pcr_mask != 0);
|
||||
|
||||
r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = determine_banks(c, pcr);
|
||||
r = determine_banks(c, pcr_mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (strv_isempty(arg_banks)) /* Still none? */
|
||||
@@ -302,18 +309,20 @@ static int extend_pcr_now(
|
||||
if (escape_and_truncate_data(data, size, &safe) < 0)
|
||||
return log_oom();
|
||||
|
||||
log_debug("Measuring '%s' into PCR index %u, banks %s.", safe, pcr, joined_banks);
|
||||
BIT_FOREACH(pcr, pcr_mask) {
|
||||
log_debug("Measuring '%s' into PCR index %i, banks %s.", safe, pcr, joined_banks);
|
||||
|
||||
r = tpm2_pcr_extend_bytes(c, arg_banks, pcr, &IOVEC_MAKE(data, size), /* secret= */ NULL, event, safe);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not extend PCR: %m");
|
||||
r = tpm2_pcr_extend_bytes(c, arg_banks, pcr, &IOVEC_MAKE(data, size), /* secret= */ NULL, event, safe);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not extend PCR: %m");
|
||||
|
||||
log_struct(LOG_INFO,
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_TPM_PCR_EXTEND_STR),
|
||||
LOG_MESSAGE("Extended PCR index %u with '%s' (banks %s).", pcr, safe, joined_banks),
|
||||
LOG_ITEM("MEASURING=%s", safe),
|
||||
LOG_ITEM("PCR=%u", pcr),
|
||||
LOG_ITEM("BANKS=%s", joined_banks));
|
||||
log_struct(LOG_INFO,
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_TPM_PCR_EXTEND_STR),
|
||||
LOG_MESSAGE("Extended PCR index %i with '%s' (banks %s).", pcr, safe, joined_banks),
|
||||
LOG_ITEM("MEASURING=%s", safe),
|
||||
LOG_ITEM("PCR=%i", pcr),
|
||||
LOG_ITEM("BANKS=%s", joined_banks));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -435,7 +444,7 @@ static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_va
|
||||
if (r == -ENOENT)
|
||||
return sd_varlink_error(link, "io.systemd.PCRExtend.NoSuchNvPCR", NULL);
|
||||
} else
|
||||
r = extend_pcr_now(p.pcr, extend_iovec->iov_base, extend_iovec->iov_len, p.event_type);
|
||||
r = extend_pcr_now(INDEX_TO_MASK(uint32_t, p.pcr), extend_iovec->iov_base, extend_iovec->iov_len, p.event_type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -531,24 +540,24 @@ static int run(int argc, char *argv[]) {
|
||||
if (arg_event_type >= 0)
|
||||
event = arg_event_type;
|
||||
|
||||
if (arg_graceful && !tpm2_is_fully_supported()) {
|
||||
if (arg_graceful && !tpm2_is_mostly_supported()) {
|
||||
log_notice("No complete TPM2 support detected, exiting gracefully.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Skip logic if sd-stub is not used, after all PCR 11 might have a very different purpose then. */
|
||||
r = efi_measured_uki(LOG_ERR);
|
||||
/* Skip logic if measured OS functionality is not enabled. */
|
||||
r = efi_measured_os(LOG_ERR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_info("Kernel stub did not measure kernel image into PCR %i, skipping userspace measurement, too.", TPM2_PCR_KERNEL_BOOT);
|
||||
log_info("OS measurements not explicitly requested and kernel stub did not measure kernel image into PCR %i, skipping userspace measurement, too.", TPM2_PCR_KERNEL_BOOT);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (arg_nvpcr_name)
|
||||
r = extend_nvpcr_now(arg_nvpcr_name, word, strlen(word), event);
|
||||
else
|
||||
r = extend_pcr_now(arg_pcr_index, word, strlen(word), event);
|
||||
r = extend_pcr_now(arg_pcr_mask, word, strlen(word), event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ install_data('pcrlock.d/500-separator.pcrlock.d/600-0xffffffff.pcrlock', install
|
||||
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/750-os-separator.pcrlock', install_dir : pcrlockdir)
|
||||
install_data('pcrlock.d/770-nvpcr-separator.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)
|
||||
|
||||
@@ -899,6 +899,10 @@ static int event_log_load_firmware(EventLog *el) {
|
||||
path = tpm2_firmware_log_path();
|
||||
|
||||
r = read_full_file(path, (char**) &buf, &bufsize);
|
||||
if (r == -ENOENT) {
|
||||
log_notice("No '%s' file, assuming TPM without firmware support.", path);
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open TPM2 event log '%s': %m", path);
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -741,6 +741,8 @@ static int condition_test_security(Condition *c, char **env) {
|
||||
return detect_confidential_virtualization() > 0;
|
||||
if (streq(c->parameter, "measured-uki"))
|
||||
return efi_measured_uki(LOG_DEBUG);
|
||||
if (streq(c->parameter, "measured-os"))
|
||||
return efi_measured_os(LOG_DEBUG);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -894,14 +894,17 @@ int encrypt_credential_and_warn(
|
||||
* container tpm2_support will detect this, and will return a different flag combination of
|
||||
* TPM2_SUPPORT_FULL, effectively skipping the use of TPM2 when inside one. */
|
||||
|
||||
try_tpm2 = tpm2_is_fully_supported();
|
||||
try_tpm2 = tpm2_is_mostly_supported();
|
||||
if (!try_tpm2)
|
||||
log_debug("System lacks TPM2 support or running in a container, not attempting to use TPM2.");
|
||||
} else
|
||||
try_tpm2 = CRED_KEY_REQUIRES_TPM2(with_key);
|
||||
|
||||
if (try_tpm2) {
|
||||
if (CRED_KEY_WANTS_TPM2_PK(with_key) || CRED_KEY_REQUIRES_TPM2_PK(with_key)) {
|
||||
/* If the firmware does not support TPMs, then UKI measurements are not going to work, hence
|
||||
* PCR 11 public key stuff cannot work. Because of that, if PK is only wanted (but not
|
||||
* required) we won't try it. */
|
||||
if ((CRED_KEY_WANTS_TPM2_PK(with_key) && tpm2_is_fully_supported()) || CRED_KEY_REQUIRES_TPM2_PK(with_key)) {
|
||||
|
||||
/* Load public key for PCR policies, if one is specified, or explicitly requested */
|
||||
|
||||
@@ -926,6 +929,8 @@ int encrypt_credential_and_warn(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not find best pcr bank: %m");
|
||||
|
||||
log_debug("Selected literal PCR mask: 0x%x, PK PCR mask: 0x%x", tpm2_hash_pcr_mask, tpm2_pubkey_pcr_mask);
|
||||
|
||||
TPML_PCR_SELECTION tpm2_hash_pcr_selection;
|
||||
tpm2_tpml_pcr_selection_from_mask(tpm2_hash_pcr_mask, tpm2_pcr_bank, &tpm2_hash_pcr_selection);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "log.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@@ -312,6 +313,30 @@ int efi_measured_uki(int log_level) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int efi_measured_os(int log_level) {
|
||||
#if ENABLE_EFI
|
||||
static int cached = -1;
|
||||
int r;
|
||||
|
||||
/* Returns if we shall enable our measurement machinery */
|
||||
|
||||
if (cached >= 0)
|
||||
return cached;
|
||||
|
||||
bool b;
|
||||
r = proc_cmdline_get_bool("systemd.tpm2_measured_os", /* flags= */ 0, &b);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse systemd.tpm2_measured_os= kernel command line argument, ignoring: %m");
|
||||
else if (r > 0)
|
||||
return (cached = b);
|
||||
|
||||
/* If nothing is explicitly configured, just assume that if we booted with a measured UKI we also want a measured OS */
|
||||
return (cached = efi_measured_uki(log_level));
|
||||
#else
|
||||
return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), "Compiled without support for EFI");
|
||||
#endif
|
||||
}
|
||||
|
||||
int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
|
||||
#if ENABLE_EFI
|
||||
_cleanup_free_ char *v = NULL;
|
||||
|
||||
@@ -15,6 +15,7 @@ int efi_loader_get_features(uint64_t *ret);
|
||||
int efi_stub_get_features(uint64_t *ret);
|
||||
|
||||
int efi_measured_uki(int log_level);
|
||||
int efi_measured_os(int log_level);
|
||||
|
||||
int efi_loader_get_config_timeout_one_shot(usec_t *ret);
|
||||
int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat);
|
||||
|
||||
@@ -2899,6 +2899,8 @@ int tpm2_get_best_pcr_bank(
|
||||
log_debug("Boot loader didn't set the LoaderTpm2ActivePcrBanks EFI variable or EFI support is unavailable, we have to guess the used PCR banks.");
|
||||
} else if (efi_banks == UINT32_MAX)
|
||||
log_debug("Boot loader set the LoaderTpm2ActivePcrBanks EFI variable to indicate that the GetActivePcrBanks() API is not available in the firmware. We have to guess the used PCR banks.");
|
||||
else if (efi_banks == 0)
|
||||
log_debug("Boot loader set the LoaderTpm2ActivePcrBanks EFI variable to zero to indicate that TPM support is not available in the firmware. We'll have to guess the used PCR banks.");
|
||||
else {
|
||||
if (BIT_SET(efi_banks, TPM2_ALG_SHA256))
|
||||
*ret = TPM2_ALG_SHA256;
|
||||
@@ -3008,6 +3010,8 @@ int tpm2_get_good_pcr_banks(
|
||||
log_debug("Boot loader didn't set the LoaderTpm2ActivePcrBanks EFI variable or EFI support is unavailable, we have to guess the used PCR banks.");
|
||||
} else if (efi_banks == UINT32_MAX)
|
||||
log_debug("Boot loader set the LoaderTpm2ActivePcrBanks EFI variable to indicate that the GetActivePcrBanks() API is not available in the firmware. We have to guess the used PCR banks.");
|
||||
else if (efi_banks == 0)
|
||||
log_debug("Boot loader set the LoaderTpm2ActivePcrBanks EFI variable to zero to indicate that TPM support is not available in the firmware. We'll have to guess the used PCR banks.");
|
||||
else {
|
||||
FOREACH_ARRAY(hash, tpm2_hash_algorithms, TPM2_N_HASH_ALGORITHMS) {
|
||||
if (!BIT_SET(efi_banks, *hash))
|
||||
@@ -6676,6 +6680,7 @@ static const char* tpm2_userspace_event_type_table[_TPM2_USERSPACE_EVENT_TYPE_MA
|
||||
[TPM2_EVENT_NVPCR_SEPARATOR] = "nvpcr-separator",
|
||||
[TPM2_EVENT_DM_VERITY] = "dm-verity",
|
||||
[TPM2_EVENT_IMDS_USERDATA] = "imds-userdata",
|
||||
[TPM2_EVENT_OS_SEPARATOR] = "os-separator",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType);
|
||||
|
||||
@@ -149,6 +149,7 @@ typedef enum Tpm2UserspaceEventType {
|
||||
TPM2_EVENT_NVPCR_SEPARATOR,
|
||||
TPM2_EVENT_DM_VERITY,
|
||||
TPM2_EVENT_IMDS_USERDATA,
|
||||
TPM2_EVENT_OS_SEPARATOR,
|
||||
_TPM2_USERSPACE_EVENT_TYPE_MAX,
|
||||
_TPM2_USERSPACE_EVENT_TYPE_INVALID = -EINVAL,
|
||||
} Tpm2UserspaceEventType;
|
||||
@@ -496,6 +497,7 @@ typedef enum Tpm2Support {
|
||||
|
||||
/* Combined flags for generic (i.e. not tool-specific) support */
|
||||
TPM2_SUPPORT_FULL = TPM2_SUPPORT_API|TPM2_SUPPORT_LIBTSS2_ALL,
|
||||
TPM2_SUPPORT_SOFTWARE = TPM2_SUPPORT_FULL & ~TPM2_SUPPORT_FIRMWARE, /* Same, just without PC firmware support */
|
||||
} Tpm2Support;
|
||||
|
||||
Tpm2Support tpm2_support_full(Tpm2Support mask);
|
||||
@@ -505,6 +507,9 @@ static inline Tpm2Support tpm2_support(void) {
|
||||
static inline bool tpm2_is_fully_supported(void) {
|
||||
return tpm2_support() == TPM2_SUPPORT_FULL;
|
||||
}
|
||||
static inline bool tpm2_is_mostly_supported(void) {
|
||||
return (tpm2_support() & TPM2_SUPPORT_SOFTWARE) == TPM2_SUPPORT_SOFTWARE;
|
||||
}
|
||||
|
||||
int verb_has_tpm2_generic(bool quiet);
|
||||
|
||||
|
||||
@@ -22,6 +22,15 @@ executables += [
|
||||
'HAVE_TPM2',
|
||||
],
|
||||
},
|
||||
libexec_template + {
|
||||
'name' : 'systemd-tpm2-swtpm',
|
||||
'sources' : files('tpm2-swtpm.c'),
|
||||
'conditions' : [
|
||||
'ENABLE_BOOTLOADER',
|
||||
'HAVE_OPENSSL',
|
||||
'HAVE_TPM2',
|
||||
],
|
||||
},
|
||||
generator_template + {
|
||||
'name' : 'systemd-tpm2-generator',
|
||||
'sources' : files('tpm2-generator.c'),
|
||||
|
||||
+104
-10
@@ -1,8 +1,14 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "dropin.h"
|
||||
#include "efivars.h"
|
||||
#include "generator.h"
|
||||
#include "initrd-util.h"
|
||||
#include "log.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "special.h"
|
||||
#include "tpm2-util.h"
|
||||
@@ -15,6 +21,7 @@
|
||||
|
||||
static const char *arg_dest = NULL;
|
||||
static int arg_tpm2_wait = -1; /* tri-state: negative → don't know */
|
||||
static bool arg_tpm2_software_fallback = false;
|
||||
|
||||
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
|
||||
int r;
|
||||
@@ -27,12 +34,19 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
||||
log_warning_errno(r, "Failed to parse 'systemd.tpm2_wait=' kernel command line argument, ignoring: %s", value);
|
||||
else
|
||||
arg_tpm2_wait = r;
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "systemd.tpm2_software_fallback")) {
|
||||
r = value ? parse_boolean(value) : 1;
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse 'systemd.tpm2_software_fallback=' kernel command line argument, ignoring: %s", value);
|
||||
else
|
||||
arg_tpm2_software_fallback = r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_tpm_target_symlink(void) {
|
||||
static int generate_tpm_target_symlink(Tpm2Support support, bool software_fallback_enabled) {
|
||||
int r;
|
||||
|
||||
if (arg_tpm2_wait == 0) {
|
||||
@@ -40,9 +54,7 @@ static int generate_tpm_target_symlink(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg_tpm2_wait < 0) {
|
||||
Tpm2Support support = tpm2_support();
|
||||
|
||||
if (arg_tpm2_wait < 0 && !software_fallback_enabled) {
|
||||
if (FLAGS_SET(support, TPM2_SUPPORT_DRIVER)) {
|
||||
log_debug("Not generating tpm2.target synchronization point, as TPM2 device is already present.");
|
||||
return 0;
|
||||
@@ -52,11 +64,6 @@ static int generate_tpm_target_symlink(void) {
|
||||
log_debug("Not generating tpm2.target synchronization point, as firmware reports no TPM2 present.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(support, TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_LIBRARIES)) {
|
||||
log_debug("Not generating tpm2.target synchronization point, as userspace support for TPM2 is not complete.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
r = generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_TPM2_TARGET);
|
||||
@@ -66,6 +73,93 @@ static int generate_tpm_target_symlink(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_swtpm_symlink(Tpm2Support support) {
|
||||
int r;
|
||||
|
||||
if (!arg_tpm2_software_fallback)
|
||||
return 0;
|
||||
|
||||
if (FLAGS_SET(support, TPM2_SUPPORT_DRIVER) || FLAGS_SET(support, TPM2_SUPPORT_FIRMWARE)) {
|
||||
log_debug("Not generating software TPM units, as a TPM2 device is otherwise available.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!is_efi_boot()) { /* We need the ESP to store the TPM state. */
|
||||
log_warning("TPM software fallback requested but not booted in EFI mode, not pulling in software TPM unit.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = find_executable("swtpm", /* ret_filename= */ NULL);
|
||||
if (r == -ENOENT) {
|
||||
log_warning("TPM software fallback requested but swtpm not available, not pulling in software TPM unit.");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if 'swtpm' is available: %m");
|
||||
|
||||
r = generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/systemd-tpm2-swtpm.service");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to hook in systemd-tpm2-swtpm.service: %m");
|
||||
|
||||
if (in_initrd())
|
||||
/* Order + pull in the early ESP mount so that swtpm has a place to store its data. */
|
||||
r = write_drop_in(
|
||||
arg_dest,
|
||||
"systemd-tpm2-swtpm.service",
|
||||
50, "esp",
|
||||
"# Automatically generated by systemd-tpm2-generator\n\n"
|
||||
"[Unit]\n"
|
||||
"Wants=sysefi.mount\n"
|
||||
"After=sysefi.mount\n");
|
||||
else
|
||||
/* Order (but not pull in) the regular ESP automount so that swtpm has a place to store its
|
||||
* data. Note that it might be mounted to two different places depending on the existence of
|
||||
* XBOOTLDR, hence order after both. */
|
||||
r = write_drop_in(
|
||||
arg_dest,
|
||||
"systemd-tpm2-swtpm.service",
|
||||
50, "esp",
|
||||
"# Automatically generated by systemd-tpm2-generator\n\n"
|
||||
"[Unit]\n"
|
||||
"After=boot.automount efi.automount\n");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to hook ESP mount before systemd-tpm2-swtpm.service: %m");
|
||||
|
||||
return 1; /* Tell caller we now created swtpm units */
|
||||
}
|
||||
|
||||
static int generate_now(void) {
|
||||
int r;
|
||||
|
||||
/* Let's shortcut things before we check for TPM2 support if no one cares anyway */
|
||||
if (arg_tpm2_wait == 0 && !arg_tpm2_software_fallback) {
|
||||
log_debug("Not generating tpm2.target synchronization point or activating software TPM, as turned off via kernel command line.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We are supposed to sync on TPM or do a software fallback, let's first and unconditionally validate this makes sense at
|
||||
* all, i.e. if we have a suitable kernel+userspace. */
|
||||
Tpm2Support support = tpm2_support();
|
||||
if (!FLAGS_SET(support, TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_LIBRARIES)) {
|
||||
|
||||
/* Raise log level if things were explicitly configured */
|
||||
log_full((arg_tpm2_wait > 0 ||
|
||||
arg_tpm2_software_fallback) ? LOG_NOTICE : LOG_DEBUG,
|
||||
"Not generating tpm2.target synchronization point or activating software TPM, as userspace support for TPM2 is not complete.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = generate_swtpm_symlink(support);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = generate_tpm_target_symlink(support, /* software_fallback_enabled= */ r > 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run(const char *dest, const char *dest_early, const char *dest_late) {
|
||||
int r;
|
||||
|
||||
@@ -75,7 +169,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
|
||||
|
||||
return generate_tpm_target_symlink();
|
||||
return generate_now();
|
||||
}
|
||||
|
||||
DEFINE_MAIN_GENERATOR_FUNCTION(run);
|
||||
|
||||
@@ -516,7 +516,7 @@ static int run(int argc, char *argv[]) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (arg_graceful && !tpm2_is_fully_supported()) {
|
||||
if (arg_graceful && !tpm2_is_mostly_supported()) {
|
||||
log_notice("No complete TPM2 support detected, exiting gracefully.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-daemon.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase.h"
|
||||
#include "errno-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "find-esp.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "hmac.h"
|
||||
#include "initrd-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "path-lookup.h"
|
||||
#include "path-util.h"
|
||||
#include "sha256.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "swtpm-util.h"
|
||||
|
||||
#define BOOT_SECRET_SIZE 32U
|
||||
|
||||
static int load_boot_secret(struct iovec *ret) {
|
||||
_cleanup_(iovec_done_erase) struct iovec buf = {};
|
||||
int r;
|
||||
|
||||
const char *bs = in_initrd() ? "/.extra/boot-secret" : "/run/systemd/stub/boot-secret";
|
||||
r = read_full_file_full(
|
||||
AT_FDCWD,
|
||||
bs,
|
||||
UINT64_MAX,
|
||||
BOOT_SECRET_SIZE,
|
||||
READ_FULL_FILE_SECURE|READ_FULL_FILE_VERIFY_REGULAR,
|
||||
/* bind_name= */ NULL,
|
||||
(char**) &buf.iov_base,
|
||||
&buf.iov_len);
|
||||
if (r == -ENOENT) {
|
||||
log_warning_errno(r, "Boot secret (%s) not found, not encrypting software TPM state!", bs);
|
||||
*ret = (struct iovec) {};
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read '%s': %m", bs);
|
||||
|
||||
if (buf.iov_len < BOOT_SECRET_SIZE)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Boot secret too short, refusing.");
|
||||
|
||||
*ret = TAKE_STRUCT(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int prepare_secret(const char *runtime_dir, char **ret) {
|
||||
int r;
|
||||
|
||||
assert(runtime_dir);
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(iovec_done_erase) struct iovec boot_secret = {};
|
||||
r = load_boot_secret(&boot_secret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Derive a suitable swtpm specific secret */
|
||||
static const char tag[] = "systemd swtpm tag v1";
|
||||
uint8_t secret[SHA256_DIGEST_SIZE];
|
||||
CLEANUP_ERASE(secret);
|
||||
hmac_sha256(boot_secret.iov_base,
|
||||
boot_secret.iov_len,
|
||||
tag,
|
||||
strlen(tag),
|
||||
secret);
|
||||
|
||||
_cleanup_free_ char *p = path_join(runtime_dir, "secret");
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
assert_cc(sizeof(secret) >= 16); /* swtpm only wants a 16 byte key */
|
||||
_cleanup_(erase_and_freep) char *h = hexmem(secret, 16);
|
||||
if (!h)
|
||||
return log_oom();
|
||||
|
||||
r = write_data_file_atomic_at(XAT_FDROOT, p, &IOVEC_MAKE_STRING(h), WRITE_DATA_FILE_MODE_0400);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write secret file: %m");
|
||||
|
||||
*ret = TAKE_PTR(p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setup_swtpm(const char *state_dir, int state_fd, const char *secret) {
|
||||
int r;
|
||||
|
||||
assert(state_dir);
|
||||
assert(state_fd >= 0);
|
||||
|
||||
/* Sets up the state directory via swtpm_setup */
|
||||
|
||||
if (in_initrd()) {
|
||||
/* In the initrd remove previous transient state */
|
||||
r = RET_NERRNO(unlinkat(state_fd, "tpm2-00.volatilestate", /* flags= */ 0));
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to remove 'tpm2-00.volatilestate': %m");
|
||||
}
|
||||
|
||||
r = dir_is_empty_at(state_fd, /* path= */ NULL, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if TPM state directory is empty: %m");
|
||||
if (r == 0) {
|
||||
log_debug("TPM state directory is already populated, not manufacturing a TPM.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!in_initrd())
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ESTALE), "swtpm TPM state directory has not been initialized in the initrd, refusing.");
|
||||
|
||||
log_debug("TPM state directory is unpopulated, manufacturing a TPM.");
|
||||
|
||||
return manufacture_swtpm(state_dir, secret);
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
_cleanup_free_ char *runtime_dir = NULL;
|
||||
r = runtime_directory(RUNTIME_SCOPE_SYSTEM, "systemd/swtpm", &runtime_dir);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to determine runtime directory: %m");
|
||||
|
||||
_cleanup_free_ char *swtpm = NULL;
|
||||
r = find_executable("swtpm", &swtpm);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to find 'swtpm' binary: %m");
|
||||
|
||||
_cleanup_free_ char *_esp = NULL;
|
||||
const char *esp;
|
||||
if (in_initrd())
|
||||
/* The early ESP support uses only a single mount point, we do not need to search for it. */
|
||||
esp = "/sysefi";
|
||||
else {
|
||||
r = find_esp_and_warn(
|
||||
/* root= */ NULL,
|
||||
/* path= */ NULL,
|
||||
/* unprivileged_mode= */ false,
|
||||
&_esp);
|
||||
if (r == -ENOKEY) /* This one find_esp_and_warn() doesn't actually log about. */
|
||||
return log_error_errno(r, "No ESP discovered.");
|
||||
if (r < 0)
|
||||
return r;
|
||||
esp = _esp;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *state_dir = NULL;
|
||||
_cleanup_close_ int state_fd = -EBADF;
|
||||
r = chase("/loader/swtpm",
|
||||
esp, CHASE_PREFIX_ROOT|CHASE_TRIGGER_AUTOFS|CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY,
|
||||
&state_dir,
|
||||
&state_fd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open swtpm state directory in ESP: %m");
|
||||
|
||||
_cleanup_(unlink_and_freep) char *secret = NULL;
|
||||
r = prepare_secret(runtime_dir, &secret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = setup_swtpm(state_dir, state_fd, secret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_strv_free_ char **args =
|
||||
strv_new(swtpm,
|
||||
"chardev",
|
||||
"--vtpm-proxy",
|
||||
"--tpm2",
|
||||
/* Make sure that in the initrd swtpm never sends TPM2_Shutdown() for us, we want to
|
||||
* be able to stop the daemon after all temporarily during the initrd→host
|
||||
* transition. */
|
||||
in_initrd() ? "--flags=startup-clear,disable-auto-shutdown" : "--flags=startup-clear");
|
||||
if (!args)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(&args, "--ctrl=type=unixio,path=%s/socket", runtime_dir) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (secret && strv_extendf(&args, "--key=file=%s,format=hex,mode=aes-cbc,remove=true", secret) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(&args, "--tpmstate=dir=%s,mode=0600", state_dir) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *cmd = quote_command_line(args, SHELL_ESCAPE_EMPTY);
|
||||
|
||||
log_debug("Chain-loading: %s", strnull(cmd));
|
||||
}
|
||||
|
||||
/* Ideally swtpm could send this itself, but for now let's accept it like this. */
|
||||
// FIXME: remove this once swtpm 0.11 is released and hit all relevant distros. Then bump version
|
||||
// requirements.
|
||||
(void) sd_notify(/* unset_environment= */ true, "READY=1");
|
||||
|
||||
/* NB: if the execve() succeeds it's swtpm's job to actually unlink the secret file */
|
||||
execv(swtpm, args);
|
||||
return log_error_errno(errno, "Failed to chainload swtpm: %m");
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
@@ -53,7 +53,6 @@ fi
|
||||
(! "$SD_PCREXTEND" --bank= foo)
|
||||
(! "$SD_PCREXTEND" --tpm2-device= foo)
|
||||
(! "$SD_PCREXTEND" --tpm2-device=/dev/null foo)
|
||||
(! "$SD_PCREXTEND" --pcr= foo)
|
||||
(! "$SD_PCREXTEND" --pcr=-1 foo)
|
||||
(! "$SD_PCREXTEND" --pcr=1024 foo)
|
||||
(! "$SD_PCREXTEND" --foo=bar)
|
||||
|
||||
@@ -42,6 +42,18 @@ PCRS="1+2+3+4+5+16"
|
||||
# (as the PCR values simply won't match the log).
|
||||
rm -f /run/log/systemd/tpm2-measure.log
|
||||
|
||||
# Add the os-separator measurements, they should be the only measurements that touch pcr 0…6 done from userspace.
|
||||
RS=$'\x1e'
|
||||
cat >/run/log/systemd/tpm2-measure.log <<EOF
|
||||
${RS}{"pcr":0,"digests":[{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"}],"content_type":"systemd","content":{"string":"os-separator","bootId":"5270738c127841a4b592f0fdc2839929","timestamp":2659711,"eventType":"os-separator"}}
|
||||
${RS}{"pcr":1,"digests":[{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"}],"content_type":"systemd","content":{"string":"os-separator","bootId":"5270738c127841a4b592f0fdc2839929","timestamp":2659775,"eventType":"os-separator"}}
|
||||
${RS}{"pcr":2,"digests":[{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"}],"content_type":"systemd","content":{"string":"os-separator","bootId":"5270738c127841a4b592f0fdc2839929","timestamp":2659805,"eventType":"os-separator"}}
|
||||
${RS}{"pcr":3,"digests":[{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"}],"content_type":"systemd","content":{"string":"os-separator","bootId":"5270738c127841a4b592f0fdc2839929","timestamp":2659829,"eventType":"os-separator"}}
|
||||
${RS}{"pcr":4,"digests":[{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"}],"content_type":"systemd","content":{"string":"os-separator","bootId":"5270738c127841a4b592f0fdc2839929","timestamp":2659851,"eventType":"os-separator"}}
|
||||
${RS}{"pcr":5,"digests":[{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"}],"content_type":"systemd","content":{"string":"os-separator","bootId":"5270738c127841a4b592f0fdc2839929","timestamp":2660099,"eventType":"os-separator"}}
|
||||
${RS}{"pcr":6,"digests":[{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"}],"content_type":"systemd","content":{"string":"os-separator","bootId":"5270738c127841a4b592f0fdc2839929","timestamp":2660139,"eventType":"os-separator"}}
|
||||
EOF
|
||||
|
||||
# Reset TPM PCR 16 ("debug") explicitly, so that we can use it in a known good state
|
||||
tpm2_pcrreset 16
|
||||
|
||||
|
||||
@@ -584,6 +584,11 @@ units = [
|
||||
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
|
||||
'symlinks' : ['sysinit.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-pcrosseparator.service.in',
|
||||
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
|
||||
'symlinks' : ['sysinit.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-pcrphase-factory-reset.service.in',
|
||||
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
|
||||
@@ -633,6 +638,10 @@ units = [
|
||||
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
|
||||
'symlinks' : ['sysinit.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-tpm2-swtpm.service.in',
|
||||
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-pcrlock-make-policy.service.in',
|
||||
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
|
||||
|
||||
@@ -13,7 +13,7 @@ Documentation=man:systemd-pcrextend(8)
|
||||
DefaultDependencies=no
|
||||
After=tpm2.target
|
||||
Before=sockets.target
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
|
||||
[Socket]
|
||||
ListenStream=/run/systemd/io.systemd.PCRExtend
|
||||
|
||||
@@ -15,7 +15,7 @@ Conflicts=shutdown.target
|
||||
After=tpm2.target systemd-pcrmachine.service
|
||||
Before=shutdown.target
|
||||
ConditionPathExists=!/etc/initrd-release
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
FailureAction=reboot-force
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -16,7 +16,7 @@ Conflicts=shutdown.target
|
||||
After=%i.mount tpm2.target systemd-pcrfs-root.service
|
||||
Before=shutdown.target
|
||||
ConditionPathExists=!/etc/initrd-release
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
FailureAction=reboot-force
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -15,7 +15,7 @@ Conflicts=shutdown.target
|
||||
After=tpm2.target
|
||||
Before=sysinit.target shutdown.target
|
||||
ConditionPathExists=!/etc/initrd-release
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
FailureAction=reboot-force
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -13,8 +13,8 @@ Documentation=man:systemd-pcrnvdone.service(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
After=systemd-tpm2-setup-early.service systemd-tpm2-setup.service
|
||||
Before=sysinit.target shutdown.target
|
||||
ConditionSecurity=measured-uki
|
||||
Before=sysinit.target cryptsetup-pre.target cryptsetup.target shutdown.target
|
||||
ConditionSecurity=measured-os
|
||||
ConditionPathExists=!/etc/initrd-release
|
||||
FailureAction=reboot-force
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# 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=TPM PCR OS Separator
|
||||
Documentation=man:systemd-pcrosseparator.service(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target initrd-switch-root.target
|
||||
After=tpm2.target
|
||||
Before=sysinit.target shutdown.target initrd-switch-root.target cryptsetup-pre.target cryptsetup.target
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
ConditionSecurity=measured-os
|
||||
FailureAction=reboot-force
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart={{LIBEXECDIR}}/systemd-pcrextend --graceful \
|
||||
--pcr=0 --pcr=1 --pcr=2 --pcr=3 --pcr=4 --pcr=5 \
|
||||
--pcr=6 --pcr=7 --pcr=9 --pcr=12 --pcr=13 --pcr=14 \
|
||||
--event-type=os-separator \
|
||||
os-separator
|
||||
@@ -14,7 +14,7 @@ DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
After=tpm2.target
|
||||
Before=shutdown.target factory-reset.target
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
FailureAction=reboot-force
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -15,7 +15,7 @@ Conflicts=shutdown.target initrd-switch-root.target
|
||||
After=tpm2.target
|
||||
Before=sysinit.target cryptsetup-pre.target cryptsetup.target shutdown.target initrd-switch-root.target systemd-sysext.service
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
FailureAction=reboot-force
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -15,7 +15,7 @@ Conflicts=shutdown.target
|
||||
After=tpm2.target
|
||||
Before=shutdown.target
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
FailureAction=reboot-force
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -15,7 +15,7 @@ Conflicts=shutdown.target
|
||||
After=sysinit.target tpm2.target
|
||||
Before=basic.target shutdown.target
|
||||
ConditionPathExists=!/etc/initrd-release
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
FailureAction=reboot-force
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -13,7 +13,7 @@ Documentation=man:systemd-pcrphase.service(8)
|
||||
After=remote-fs.target remote-cryptsetup.target tpm2.target
|
||||
Before=systemd-user-sessions.service
|
||||
ConditionPathExists=!/etc/initrd-release
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
FailureAction=reboot-force
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -12,11 +12,11 @@ Description=TPM NvPCR Product ID Measurement
|
||||
Documentation=man:systemd-pcrproduct.service(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
After=tpm2.target
|
||||
After=tpm2.target systemd-pcrnvdone.service
|
||||
Before=sysinit.target shutdown.target
|
||||
RequiresMountsFor=/var/lib/systemd/nvpcr
|
||||
ConditionPathExists=!/etc/initrd-release
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
|
||||
@@ -22,7 +22,7 @@ ConditionPathExists=/sys/class/tpm/tpm0/ppi/request
|
||||
# derive here from the fact that UKIs are used. Because if they do they are OK
|
||||
# with our SRK initialization and our PCR measurements, and hence should also
|
||||
# be OK with our TPM resets.
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
|
||||
@@ -12,9 +12,9 @@ Description=Early TPM SRK Setup
|
||||
Documentation=man:systemd-tpm2-setup.service(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
After=tpm2.target systemd-pcrphase-initrd.service
|
||||
After=tpm2.target systemd-pcrphase-initrd.service systemd-pcrosseparator.service
|
||||
Before=sysinit.target shutdown.target
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
ConditionPathExists=!/run/systemd/tpm2-srk-public-key.pem
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -15,7 +15,7 @@ Conflicts=shutdown.target
|
||||
After=tpm2.target systemd-tpm2-setup-early.service systemd-remount-fs.service
|
||||
Before=sysinit.target shutdown.target
|
||||
RequiresMountsFor=/var/lib/systemd
|
||||
ConditionSecurity=measured-uki
|
||||
ConditionSecurity=measured-os
|
||||
ConditionPathExists=!/etc/initrd-release
|
||||
|
||||
[Service]
|
||||
|
||||
@@ -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=Fallback Software TPM
|
||||
Documentation=man:systemd-tpm2-swtpm.service(8)
|
||||
DefaultDependencies=no
|
||||
After=systemd-sysusers.service
|
||||
Wants=modprobe@tpm_vtpm_proxy.service
|
||||
After=modprobe@tpm_vtpm_proxy.service
|
||||
Before=tpm2.target sysinit.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
RuntimeDirectory=systemd/swtpm
|
||||
ExecStart={{LIBEXECDIR}}/systemd-tpm2-swtpm
|
||||
# Write out volatile state (so that we can read it back after the initrd transition
|
||||
ExecStop=swtpm_ioctl --unix %t/systemd/swtpm/socket -v
|
||||
# Initiate graceful shutdown
|
||||
ExecStop=swtpm_ioctl --unix %t/systemd/swtpm/socket -s
|
||||
Reference in New Issue
Block a user