core: introduce ConditionSecurity=measured-os

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.
This commit is contained in:
Lennart Poettering
2026-03-26 16:11:34 +01:00
parent ca03d178a0
commit cd911bec6e
25 changed files with 86 additions and 30 deletions
+10
View 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());
+4 -4
View 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;
}
+2 -2
View 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)
+8 -6
View 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(
@@ -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;
+3 -3
View 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;
}
+2
View 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;
}
+25
View 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;
+1
View 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);