]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: introduce ConditionSecurity=measured-os
authorLennart Poettering <lennart@amutable.com>
Mon, 9 Mar 2026 17:53:09 +0000 (18:53 +0100)
committerLennart Poettering <lennart@amutable.com>
Thu, 26 Mar 2026 15:11:34 +0000 (16:11 +0100)
So far we always conditioned our TPM magic on the UKI having detected
TPM support in the firmware. This is a bit limiting when we want to
support a software TPM that is not visible to the firmware. Hence let's
split this up, and add a separate control that can be set via the kernel
command line.  However, as before, let's by default inherit the firmare
TPM discovery state into it, to retain the current behaviour unless
overriden.

With this in place, boot with "systemd.tpm2_measured_os=1
systemd.tpm2_software_fallback=1" on the kernel cmdline to get the swtpm
fallback and then a measured OS based on it.

25 files changed:
man/kernel-command-line.xml
man/systemd.unit.xml
src/bootctl/bootctl-status.c
src/cryptsetup/cryptsetup.c
src/fstab-generator/fstab-generator.c
src/gpt-auto-generator/gpt-auto-generator.c
src/hibernate-resume/hibernate-resume-generator.c
src/pcrextend/pcrextend.c
src/shared/condition.c
src/shared/efi-loader.c
src/shared/efi-loader.h
units/systemd-pcrextend.socket
units/systemd-pcrfs-root.service.in
units/systemd-pcrfs@.service.in
units/systemd-pcrmachine.service.in
units/systemd-pcrnvdone.service.in
units/systemd-pcrphase-factory-reset.service.in
units/systemd-pcrphase-initrd.service.in
units/systemd-pcrphase-storage-target-mode.service.in
units/systemd-pcrphase-sysinit.service.in
units/systemd-pcrphase.service.in
units/systemd-pcrproduct.service.in
units/systemd-tpm2-clear.service.in
units/systemd-tpm2-setup-early.service.in
units/systemd-tpm2-setup.service.in

index 1292bbfbee1398e7fb6fad49ba524293215e45fe..088ce24154042200a4f1b674f41e1c4a6b5e41ec 100644 (file)
         <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>
 
index 37022ecc1c3aa1a5160d1953ccec02befeded6af..8bbff2f210a7f755f70dd54839a51740bba6e549 100644 (file)
                   <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>
index 4184e3d4249aac12a35f339143f388c92c7e740f..178bffb36522c867a4572e1033971712dd82f5b9 100644 (file)
@@ -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());
index bda9a8cc84a970c2d2d4dd2f670d18e4974a0c18..64cd3813b872858e13c96691d4138d5f5420a19f 100644 (file)
@@ -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;
         }
 
index d60db7e9c1de11a62d6e70b6670e3b1137dd1fcb..1bfebf3c4089f6c4983bdfcfd65b6f83d3dd8596 100644 (file)
@@ -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)
index a87b5140805873d8872fa2e752a563ebaabed963..6716a8d1aaf7c5547866538cb80bbe12291201cc 100644 (file)
@@ -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(
index 79c7d41bb453df4db0a1bb2f5f3773422effbe7b..998c8e84d95043cad27489b1226fda50fe39a77b 100644 (file)
@@ -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;
 
index c0b111a0964e6796ce3de48881c4363c9bed800d..8ec0a733c68aa697f043c835999cdcc964e5e3c3 100644 (file)
@@ -536,12 +536,12 @@ static int run(int argc, char *argv[]) {
                 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;
         }
 
index 903662edf1a8f5efc07b8c2985ea406a91e7a57e..dd720c55bd9e536a9ad03e1176feb276c2cc6f19 100644 (file)
@@ -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;
 }
index 1f4fc665c03b86f8fda351b14fdfabca4bc42a14..ce10a44d34ccca9fce700e51ae716c1f42bdbab0 100644 (file)
@@ -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;
index 5b614cd0a7ee0289bd2844aee0d2d9763b01330a..abf8bdc49ef043c2ca8b58aeccbb84f11496a4b8 100644 (file)
@@ -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);
index d429150eda0d784b03623b6bdea73a63c1910343..0f4ab11e2fd3c5c413840f75e9a4dba99835dc9a 100644 (file)
@@ -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
index f774c4c8bf6bf7b6d0bdf94cecaa2d0e1d1f4ad6..88551d7ed0893a67409859fccc994632de73539c 100644 (file)
@@ -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]
index 3d18fe4d30e1635e039a17db6a199b205d5db6ce..38cc41976f66c24d4dfd1b4675d7d688181d4cf3 100644 (file)
@@ -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]
index ea2561ef79e3f0dcba1226fd2ed18c380c69e435..d97afa696554d1fe5b8d1fdbb9b06aa628ca66af 100644 (file)
@@ -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]
index e0dd9a882098817131d5c778b3d4e25073ac5b74..7593dedfed189422b4b9479eb365395bb86540a6 100644 (file)
@@ -14,7 +14,7 @@ DefaultDependencies=no
 Conflicts=shutdown.target
 After=systemd-tpm2-setup-early.service systemd-tpm2-setup.service
 Before=sysinit.target shutdown.target
-ConditionSecurity=measured-uki
+ConditionSecurity=measured-os
 ConditionPathExists=!/etc/initrd-release
 FailureAction=reboot-force
 
index 5dbcb0f53f160bd3717d8f9449e99218e3d9f08c..2efd8830d321022c904d869d51b01e4623a737a1 100644 (file)
@@ -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]
index 5aba32128c012a0c44bc755ce0444f7482102a38..cbb833147018d419f48cb74bc0327d44fa623405 100644 (file)
@@ -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]
index 52b53e5b819a8f3bc3009c6409389757e51f3a69..b4330c560f5bbd12da5d2de920dc3306e536b026 100644 (file)
@@ -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]
index 4a01279159d9381300bc92fb89762ea523b85b3b..aa4d36409813a5bc47c1ba418052aede80f8b38a 100644 (file)
@@ -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]
index 43459a2fccba0b9af3ec3eb1fffe11494a3efd26..b2f925d40f46b3ac97b2229cc84d5f681b6e4007 100644 (file)
@@ -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]
index 09e446c2a01b0cc800675427a5cdeecbad8bdfec..2562dea18fe4e61ad44e82eecc5e9518e88686de 100644 (file)
@@ -16,7 +16,7 @@ After=tpm2.target
 Before=sysinit.target shutdown.target
 RequiresMountsFor=/var/lib/systemd/nvpcr
 ConditionPathExists=!/etc/initrd-release
-ConditionSecurity=measured-uki
+ConditionSecurity=measured-os
 
 [Service]
 Type=oneshot
index a47d99ac8e70d018fccaf914eec85b17a4a4b03e..501846180c974ea07c633e73be88d2b84be815ce 100644 (file)
@@ -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
index ce1ee94cc4906ccfcc75bc9f65ab2d3ebe12fa75..6b7ef34b8c00fc28b99e6c45911e73ea24993533 100644 (file)
@@ -14,7 +14,7 @@ DefaultDependencies=no
 Conflicts=shutdown.target
 After=tpm2.target systemd-pcrphase-initrd.service
 Before=sysinit.target shutdown.target
-ConditionSecurity=measured-uki
+ConditionSecurity=measured-os
 ConditionPathExists=!/run/systemd/tpm2-srk-public-key.pem
 
 [Service]
index dff516832d3c663e2c8615280533074e33cace37..4593211c1ef8b3e1cc245e743548b62962e3c47e 100644 (file)
@@ -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]