]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2: add common helper for checking if we are running on UKI with TPM measurements
authorLennart Poettering <lennart@poettering.net>
Mon, 17 Oct 2022 12:50:56 +0000 (14:50 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 17 Jan 2023 08:42:16 +0000 (09:42 +0100)
Let's introduce a common implementation of a function that checks
whether we are booted on a kernel with systemd-stub that has TPM PCR
measurements enabled. Do our own userspace measurements only if we
detect that.

PCRs are scarce and most likely there are projects which already make
use of them in other ways. Hence, instead of blindly stepping into their
territory let's conditionalize things so that people have to explicitly
buy into our PCR assignments before we start measuring things into them.
Specifically bind everything to an UKI that reported measurements.

This was previously already implemented in systemd-pcrphase, but with
this change we expand this to all tools that process PCR measurement
settings.

The env var to override the check is renamed to SYSTEMD_FORCE_MEASURE,
to make it more generic (since we'll use it at multiple places now).
This is not a compat break, since the original env var for that was not
included in any stable release yet.

docs/ENVIRONMENT.md
src/boot/pcrphase.c
src/cryptsetup/cryptsetup.c
src/fstab-generator/fstab-generator.c
src/gpt-auto-generator/gpt-auto-generator.c
src/shared/efi-loader.c
src/shared/efi-loader.h

index 2b751d9d533d0cdbc44db60704e45ecfd18ec266..ef0141e1a6343e90db95dac368da7f0d2549c19f 100644 (file)
@@ -506,7 +506,10 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
   journal. Note that journal files in compact mode are limited to 4G to allow use of
   32-bit offsets. Enabled by default.
 
-`systemd-pcrphase`:
+`systemd-pcrphase`, `systemd-cryptsetup`:
 
-* `$SYSTEMD_PCRPHASE_STUB_VERIFY` – Takes a boolean. If false the requested
-  measurement is done even if no EFI stub usage was reported via EFI variables.
+* `$SYSTEMD_FORCE_MEASURE=1` — If set, force measuring of resources (which are
+  marked for measurement) even if not booted on a kernel equipped with
+  systemd-stub. Normally, requested measurement of resources is conditionalized
+  on kernels that have booted with `systemd-stub`. With this environment
+  variable the test for that my be bypassed, for testing purposes.
index 70c919b0f5df1e609d8fd6b344479f19b9e81308..003e0b8ad8d9dcffb9b6de66726ba421d32657bf 100644 (file)
@@ -9,15 +9,14 @@
 #include "blockdev-util.h"
 #include "build.h"
 #include "chase-symlinks.h"
+#include "efi-loader.h"
 #include "efivars.h"
-#include "env-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "main-func.h"
 #include "mountpoint-util.h"
 #include "openssl-util.h"
 #include "parse-argument.h"
-#include "parse-util.h"
 #include "pretty-print.h"
 #include "tpm-pcr.h"
 #include "tpm2-util.h"
@@ -241,9 +240,9 @@ static int get_file_system_word(
 }
 
 static int run(int argc, char *argv[]) {
-        _cleanup_free_ char *joined = NULL, *pcr_string = NULL, *word = NULL;
         _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
-        unsigned target_pcr_nr, efi_pcr_nr;
+        _cleanup_free_ char *joined = NULL, *word = NULL;
+        unsigned target_pcr_nr;
         size_t length;
         int r;
 
@@ -334,32 +333,13 @@ static int run(int argc, char *argv[]) {
 
         length = strlen(word);
 
-        int b = getenv_bool("SYSTEMD_PCRPHASE_STUB_VERIFY");
-        if (b < 0 && b != -ENXIO)
-                log_warning_errno(b, "Unable to parse $SYSTEMD_PCRPHASE_STUB_VERIFY value, ignoring.");
-
         /* Skip logic if sd-stub is not used, after all PCR 11 might have a very different purpose then. */
-        r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
-        if (r == -ENOENT) {
-                if (b != 0) {
-                        log_info("Kernel stub did not measure kernel image into PCR %u, skipping measurement.", TPM_PCR_INDEX_KERNEL_IMAGE);
-                        return EXIT_SUCCESS;
-                } else
-                        log_notice("Kernel stub did not measure kernel image into PCR %u, but told to measure anyway, hence proceeding.", TPM_PCR_INDEX_KERNEL_IMAGE);
-        } else if (r < 0)
-                return log_error_errno(r, "Failed to read StubPcrKernelImage EFI variable: %m");
-        else {
-                /* Let's validate that the stub announced PCR 11 as we expected. */
-                r = safe_atou(pcr_string, &efi_pcr_nr);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
-                if (efi_pcr_nr != TPM_PCR_INDEX_KERNEL_IMAGE) {
-                        if (b != 0)
-                                return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", efi_pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
-                        else
-                                log_notice("Kernel stub measured kernel image into PCR %u, which is different than expected %u, but told to measure anyway, hence proceeding.", efi_pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
-                } else
-                        log_debug("Kernel stub reported same PCR %u as we want to use, proceeding.", TPM_PCR_INDEX_KERNEL_IMAGE);
+        r = efi_stub_measured();
+        if (r < 0)
+                return log_error_errno(r, "Failed to detect if we are running on a kernel image with TPM measurement enabled: %m");
+        if (r == 0) {
+                log_info("Kernel stub did not measure kernel image into PCR %u, skipping userspace measurement, too.", TPM_PCR_INDEX_KERNEL_IMAGE);
+                return EXIT_SUCCESS;
         }
 
         r = dlopen_tpm2();
index 5efce63a3801aba2e41ac800de3735f106e9c3fe..2c9d416734b0efb0fb4f27d2535565f15863de8f 100644 (file)
@@ -19,6 +19,7 @@
 #include "cryptsetup-util.h"
 #include "device-util.h"
 #include "efi-api.h"
+#include "efi-loader.h"
 #include "env-util.h"
 #include "escape.h"
 #include "fileio.h"
@@ -827,6 +828,14 @@ static int measure_volume_key(
                 return 0;
         }
 
+        r = efi_stub_measured();
+        if (r < 0)
+                return log_warning_errno(r, "Failed to detect if we are running on a kernel image with TPM measurement enabled: %m");
+        if (r == 0) {
+                log_debug("Kernel stub did not measure kernel image into the expected PCR, skipping userspace measurement, too.");
+                return 0;
+        }
+
 #if HAVE_TPM2
         r = dlopen_tpm2();
         if (r < 0)
index 4b763bc247250e4dc0772949516e34903c6fb65f..ed34e0a32fc5ea4294e8a868f2f794adaaaa72a2 100644 (file)
@@ -8,6 +8,7 @@
 #include "bus-error.h"
 #include "bus-locator.h"
 #include "chase-symlinks.h"
+#include "efi-loader.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fstab-util.h"
@@ -529,9 +530,16 @@ static int add_mount(
         }
 
         if (flags & MOUNT_PCRFS) {
-                r = generator_hook_up_pcrfs(dest, where, target_unit);
+                r = efi_stub_measured();
                 if (r < 0)
-                        return r;
+                        log_warning_errno(r, "Failed to detect if we are running on a kernel image with TPM measurement enabled, assuming not: %m");
+                else if (r == 0)
+                        log_debug("Kernel stub did not measure kernel image into PCR, skipping userspace measurement, too.");
+                else {
+                        r = generator_hook_up_pcrfs(dest, where, target_unit);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         if (!FLAGS_SET(flags, MOUNT_AUTOMOUNT)) {
index 63bb2a3cbb903ccba3dae8e83953161ad92ed006..9b113180171407b2199b9b76f991168665b12c31 100644 (file)
@@ -102,11 +102,11 @@ static int add_cryptsetup(
                  * assignment, under the assumption that people who are fine to use sd-stub with its PCR
                  * assignments are also OK with our PCR 15 use here. */
 
-                r = efi_get_variable(EFI_LOADER_VARIABLE(StubPcrKernelImage), NULL, NULL, NULL); /* we don't actually care which PCR the UKI used for itself */
-                if (r == -ENOENT)
-                        log_debug_errno(r, "Will not measure volume key of volume '%s', because not booted via systemd-stub with measurements enabled.", id);
-                else if (r < 0)
-                        log_debug_errno(r, "Failed to determine whether booted via systemd-stub with measurements enabled, ignoring: %m");
+                r = efi_stub_measured();
+                if (r < 0)
+                        log_warning_errno(r, "Failed to determine whether booted via systemd-stub with measurements enabled, ignoring: %m");
+                else if (r == 0)
+                        log_debug("Will not measure volume key of volume '%s', because not booted via systemd-stub with measurements enabled.", id);
                 else if (!strextend_with_separator(&options, ",", "tpm2-measure-pcr=yes"))
                         return log_oom();
         }
index 1340412cdae10c03d0ccb7a2dac2b93d59805aa0..621fa082baae29defe8dfd5b16d406f558fb6492 100644 (file)
@@ -2,10 +2,12 @@
 
 #include "alloc-util.h"
 #include "efi-loader.h"
+#include "env-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "stat-util.h"
 #include "strv.h"
+#include "tpm-pcr.h"
 #include "utf8.h"
 
 #if ENABLE_EFI
@@ -236,6 +238,43 @@ int efi_stub_get_features(uint64_t *ret) {
         return 0;
 }
 
+int efi_stub_measured(void) {
+        _cleanup_free_ char *pcr_string = NULL;
+        unsigned pcr_nr;
+        int r;
+
+        /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11. Or in
+         * other words, if we are running on a TPM enabled UKI.
+         *
+         * Returns == 0 and > 0 depending on the result of the test. Returns -EREMOTE if we detected a stub
+         * being used, but it measured things into a different PCR than we are configured for in
+         * userspace. (i.e. we expect PCR 11 being used for this by both sd-stub and us) */
+
+        r = getenv_bool_secure("SYSTEMD_FORCE_MEASURE"); /* Give user a chance to override the variable test,
+                                                          * for debugging purposes */
+        if (r >= 0)
+                return r;
+        if (r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $SYSTEMD_FORCE_MEASURE, ignoring: %m");
+
+        if (!is_efi_boot())
+                return 0;
+
+        r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return r;
+
+        r = safe_atou(pcr_string, &pcr_nr);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
+        if (pcr_nr != TPM_PCR_INDEX_KERNEL_IMAGE)
+                return log_debug_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
+
+        return 1;
+}
+
 int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
         _cleanup_free_ char *v = NULL;
         static struct stat cache_stat = {};
index 84968869abe2732a8cd64f5b2a4ed89774ea7542..56ccdee9c1e14c500552c70ed4e3a11d649a4c97 100644 (file)
@@ -18,6 +18,8 @@ int efi_loader_get_entries(char ***ret);
 int efi_loader_get_features(uint64_t *ret);
 int efi_stub_get_features(uint64_t *ret);
 
+int efi_stub_measured(void);
+
 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);