]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
units: add a tpm2.target synchronization point and small generator that pulls in
authorLennart Poettering <lennart@poettering.net>
Fri, 24 Nov 2023 17:01:56 +0000 (18:01 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 3 Jan 2024 12:49:02 +0000 (13:49 +0100)
Distributions apparently only compile a subset of TPM2 drivers into the
kernel. For those not compiled it but provided as kmod we need a
synchronization point: we must wait before the first TPM2 interaction
until the driver is available and accessible.

This adds a tpm2.target unit as such a synchronization point. It's
ordered after /dev/tpmrm0, and is pulled in by a generator whenever we
detect that the kernel reported a TPM2 to exist but we have no device
for it yet.

This should solve the issue, but might create problems: if there are TPM
devices supported by firmware that we don't have Linux drivers for we'll
hang for a bit. Hence let's add a kernel cmdline switch to disable (or
alternatively force) this logic.

Fixes: #30164
19 files changed:
man/rules/meson.build
man/systemd-tpm2-generator.xml [new file with mode: 0644]
man/systemd.special.xml
rules.d/99-systemd.rules.in
src/basic/special.h
src/tpm2-setup/meson.build
src/tpm2-setup/tpm2-generator.c [new file with mode: 0644]
units/meson.build
units/systemd-pcrextend.socket
units/systemd-pcrextend@.service.in
units/systemd-pcrfs-root.service.in
units/systemd-pcrfs@.service.in
units/systemd-pcrmachine.service.in
units/systemd-pcrphase-initrd.service.in
units/systemd-pcrphase-sysinit.service.in
units/systemd-pcrphase.service.in
units/systemd-tpm2-setup-early.service.in
units/systemd-tpm2-setup.service.in
units/tpm2.target [new file with mode: 0644]

index e99b77eb50051b458f657a1814fd90c86de417b0..c99f79eba8afcfedca4c8dae07d80b4bd2f6dbe7 100644 (file)
@@ -1100,6 +1100,7 @@ manpages = [
    'systemd-tmpfiles-setup-dev.service',
    'systemd-tmpfiles-setup.service'],
   ''],
+ ['systemd-tpm2-generator', '8', [], ''],
  ['systemd-tpm2-setup.service',
   '8',
   ['systemd-tpm2-setup', 'systemd-tpm2-setup-early.service'],
diff --git a/man/systemd-tpm2-generator.xml b/man/systemd-tpm2-generator.xml
new file mode 100644 (file)
index 0000000..51950ee
--- /dev/null
@@ -0,0 +1,60 @@
+<?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" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+<refentry id="systemd-tpm2-generator">
+
+  <refentryinfo>
+    <title>systemd-tpm2-generator</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-tpm2-generator</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-tpm2-generator</refname>
+    <refpurpose>Generator for inserting TPM2 synchronization point in the boot process</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>/usr/lib/systemd/system-generators/systemd-tpm2-generator</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>systemd-tpm2-generator</filename> is a generator that adds a <varname>Wants=</varname>
+    dependency from <filename>sysinit.target</filename> to <filename>tpm2.target</filename> when it detects
+    that the firmware discovered a TPM2 device but the OS kernel so far did
+    not. <filename>tpm2.target</filename> is supposed to act as synchronization point for all services that
+    require TPM2 device access. See
+    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+    details.</para>
+
+    <para>The <option>systemd.tpm2_wait=</option> kernel command line option may be used to override
+    behaviour of the generator. It accepts a boolean value: if true then <filename>tpm2.target</filename>
+    will be added as synchronization point even if the firmware has not detected a TPM2 device. If false, the
+    target will not be inserted even if firmware reported a device but the OS kernel doesn't expose a device
+    for it yet. The latter might be useful in environments where a suitable TPM2 driver for the available
+    hardware is not available.</para>
+
+    <para><filename>systemd-tpm2-generator</filename> implements
+    <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</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.special</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+    </simplelist></para>
+  </refsect1>
+</refentry>
index ff0f73f191890ed2fe522c5b722097c136fb1288..988b7175ba8dd90ebd44074efa2ab896f7fc3cf9 100644 (file)
@@ -92,6 +92,7 @@
     <filename>time-set.target</filename>,
     <filename>time-sync.target</filename>,
     <filename>timers.target</filename>,
+    <filename>tpm2.target</filename>,
     <filename>umount.target</filename>,
     <filename>usb-gadget.target</filename>,
     <!-- slices --><filename>-.slice</filename>,
             <xi:include href="version-info.xml" xpointer="v242"/>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><filename>tpm2.target</filename></term>
+          <listitem>
+            <para>This target is started automatically if a TPM2 device is discovered, either by the OS or by
+            the firmware. It acts as synchronization point for services that require TPM2 device access. The
+            target unit is enqueued by
+            <citerefentry><refentrytitle>systemd-tpm2-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            if it detects that the firmware has discovered a TPM2 device but the OS kernel has not activated
+            a driver for it yet. It is also pulled in whenever
+            <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            discovers a TPM2 device. The target unit is ordered after the <filename>/dev/tpmrm0</filename>
+            device node, so that it only becomes active once the TPM2 device is actually accessible. Early
+            boot programs that intend to access the TPM2 device should hence order themselves after this
+            target unit, but not pull it in.</para>
+
+            <xi:include href="version-info.xml" xpointer="v256"/>
+          </listitem>
+        </varlistentry>
       </variablelist>
     </refsect2>
 
index 455a2368eb7aff0f949422be69be8a234bf06b10..33624f88a5ffe5a2586545405480e457ac88405f 100644 (file)
@@ -85,4 +85,7 @@ SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="system
 SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount"
 SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount"
 
+# Pull in tpm2.target whenever /dev/tpmrm* shows up
+SUBSYSTEM=="tpmrm", KERNEL=="tpmrm[0-9]*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="tpm2.target"
+
 LABEL="systemd_end"
index a625e75bedf7d26cd4e182b585f0dbcafd1395b8..27d2c26e48e028a1f1febde2ce2f9121ed5c11e8 100644 (file)
@@ -47,6 +47,7 @@
 #define SPECIAL_TIME_SYNC_TARGET "time-sync.target"       /* LSB's $time */
 #define SPECIAL_TIME_SET_TARGET "time-set.target"
 #define SPECIAL_BASIC_TARGET "basic.target"
+#define SPECIAL_TPM2_TARGET "tpm2.target"
 
 /* LSB compatibility */
 #define SPECIAL_NETWORK_TARGET "network.target"           /* LSB's $network */
index c85721c98ee07ed4a330f1d91ca5c46a1b99feb0..77fad97b7fa1714d8f28aa66bb267fbf1c9b4456 100644 (file)
@@ -13,4 +13,10 @@ executables += [
                         libopenssl,
                 ],
         },
+
+        generator_template + {
+                'name' : 'systemd-tpm2-generator',
+                'sources' : files('tpm2-generator.c'),
+        },
+
 ]
diff --git a/src/tpm2-setup/tpm2-generator.c b/src/tpm2-setup/tpm2-generator.c
new file mode 100644 (file)
index 0000000..1ba8a7f
--- /dev/null
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "generator.h"
+#include "proc-cmdline.h"
+#include "special.h"
+#include "tpm2-util.h"
+#include "parse-util.h"
+
+/* A small generator that enqueues tpm2.target as synchronization point if the TPM2 device hasn't shown up
+ * yet, but the firmware reports it to exist. This is supposed to deal with systems where the TPM2 driver
+ * support is built as kmod and must be loaded before it's ready to be used. The tpm2.target is only enqueued
+ * if firmware says there is a TPM2 device, our userspace support for TPM2 is fully available but the TPM2
+ * device hasn't shown up in /dev/ yet. */
+
+static const char *arg_dest = NULL;
+static int arg_tpm2_wait = -1; /* tri-state: negative → don't know */
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+        int r;
+
+        assert(key);
+
+        if (proc_cmdline_key_streq(key, "systemd.tpm2-wait")) {
+                r = value ? parse_boolean(value) : 1;
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse 'systemd.tpm2-wait' kernel command line argument, ignoring: %s", value);
+                else
+                        arg_tpm2_wait = r;
+        }
+
+        return 0;
+}
+
+static int generate_tpm_target_symlink(void) {
+        int r;
+
+        if (arg_tpm2_wait == 0) {
+                log_debug("Not generating tpm2.target synchronization point, as this was explicitly turned off via kernel command line.");
+                return 0;
+        }
+
+        if (arg_tpm2_wait < 0) {
+                Tpm2Support support = tpm2_support();
+
+                if (FLAGS_SET(support, TPM2_SUPPORT_DRIVER)) {
+                        log_debug("Not generating tpm2.target synchronization point, as TPM2 device is already present.");
+                        return 0;
+                }
+
+                if (!FLAGS_SET(support, TPM2_SUPPORT_FIRMWARE)) {
+                        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);
+        if (r < 0)
+                return log_error_errno(r, "Failed to hook in tpm2.target: %m");
+
+        return 0;
+}
+
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+        int r;
+
+        assert_se(arg_dest = dest);
+
+        r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
+        if (r < 0)
+                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+
+        return generate_tpm_target_symlink();
+}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
index 9d3604951d56bf6a1b6d1e450520bdb25759ae6c..40cc3d10d35ff9f6621b77be051de44b1a008445 100644 (file)
@@ -700,6 +700,7 @@ units = [
           'file' : 'tmp.mount',
           'symlinks' : ['local-fs.target.wants/'],
         },
+        { 'file' : 'tpm2.target' },
         { 'file' : 'umount.target' },
         { 'file' : 'usb-gadget.target' },
         { 'file' : 'user-runtime-dir@.service.in' },
index 6d7b8ff84c6c766d1af654fcf0de1852ab1137c8..7d156c14483923331b070db98418a21df92da910 100644 (file)
@@ -11,6 +11,7 @@
 Description=TPM2 PCR Extension (Varlink)
 Documentation=man:systemd-pcrextend(8)
 DefaultDependencies=no
+After=tpm2.target
 Before=sockets.target
 ConditionSecurity=measured-uki
 
index 2305b1cd4c1d81df018d8b68d190640152200f03..6020a21c40c7d04882068e13bd9343b825e2fcbc 100644 (file)
@@ -11,6 +11,7 @@
 Description=TPM2 PCR Extension (Varlink)
 Documentation=man:systemd-pcrphase.service(8)
 DefaultDependencies=no
+After=tpm2.target
 Conflicts=shutdown.target initrd-switch-root.target
 Before=shutdown.target initrd-switch-root.target
 
index 11dc7471946b866c2f88c3880fc05714a61427c2..582e1d83e505e609d74a30401ebe5383fdc0c2cc 100644 (file)
@@ -12,7 +12,7 @@ Description=TPM2 PCR Root File System Measurement
 Documentation=man:systemd-pcrfs-root.service(8)
 DefaultDependencies=no
 Conflicts=shutdown.target
-After=systemd-pcrmachine.service
+After=tpm2.target systemd-pcrmachine.service
 Before=shutdown.target
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index fbaec4b999ea71c04bcd7e546bd4ca354c05663b..262a82fb04ce41134a9f5702f1fc63b5d90578db 100644 (file)
@@ -13,7 +13,7 @@ Documentation=man:systemd-pcrfs@.service(8)
 DefaultDependencies=no
 BindsTo=%i.mount
 Conflicts=shutdown.target
-After=%i.mount systemd-pcrfs-root.service
+After=%i.mount tpm2.target systemd-pcrfs-root.service
 Before=shutdown.target
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index fb7d3ce601fd5c7239d02544347e9843a9164ffb..cbd1005104f2f26bcfe24602c2769f30baf884a2 100644 (file)
@@ -12,6 +12,7 @@ Description=TPM2 PCR Machine ID Measurement
 Documentation=man:systemd-pcrmachine.service(8)
 DefaultDependencies=no
 Conflicts=shutdown.target
+After=tpm2.target
 Before=sysinit.target shutdown.target
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index b337d602bad01d592ea1b39da561fc8704b6efe4..3b18b4f29d60c39f81176dd780e9b88683cb5356 100644 (file)
@@ -12,6 +12,7 @@ Description=TPM2 PCR Barrier (initrd)
 Documentation=man:systemd-pcrphase-initrd.service(8)
 DefaultDependencies=no
 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
index 08f73973bee81b5351aa82f1693aa02f415d8661..d938e02401bb70ae457087547eb7ec9afc46e731 100644 (file)
@@ -12,7 +12,7 @@ Description=TPM2 PCR Barrier (Initialization)
 Documentation=man:systemd-pcrphase-sysinit.service(8)
 DefaultDependencies=no
 Conflicts=shutdown.target
-After=sysinit.target
+After=sysinit.target tpm2.target
 Before=basic.target shutdown.target
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index c94ad756d40008d284de239091d239ebfcea99b2..26b9e5c5389114cb26f1cadb20a448d90d1459c0 100644 (file)
@@ -10,7 +10,7 @@
 [Unit]
 Description=TPM2 PCR Barrier (User)
 Documentation=man:systemd-pcrphase.service(8)
-After=remote-fs.target remote-cryptsetup.target
+After=remote-fs.target remote-cryptsetup.target tpm2.target
 Before=systemd-user-sessions.service
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index c1597ea3f9fd6f730b9fbc5cf97546d805b7337c..628f8166782b8d0845979928a236c12d8b851a54 100644 (file)
@@ -15,6 +15,7 @@ Conflicts=shutdown.target
 Before=sysinit.target shutdown.target
 ConditionSecurity=measured-uki
 ConditionPathExists=!/run/systemd/tpm2-srk-public-key.pem
+After=tpm2.target
 
 [Service]
 Type=oneshot
index 6c99f3af0a6b258a3df6562cc57c22825df64b6d..4830afa24356c8e5300f1e9ec074ac87ed38d1a6 100644 (file)
@@ -17,6 +17,7 @@ Before=sysinit.target shutdown.target
 RequiresMountsFor=/var/lib/systemd/tpm2-srk-public-key.pem
 ConditionSecurity=measured-uki
 ConditionPathExists=!/etc/initrd-release
+After=tpm2.target
 
 [Service]
 Type=oneshot
diff --git a/units/tpm2.target b/units/tpm2.target
new file mode 100644 (file)
index 0000000..ba51d57
--- /dev/null
@@ -0,0 +1,16 @@
+#  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=Trusted Platform Module
+Documentation=man:systemd.special(7)
+
+# Make this a synchronization point on the first TPM device found
+After=dev-tpmrm0.device
+Wants=dev-tpmrm0.device