tpm2: add common helper for checking if we are running on UKI with TPM measurements

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.
This commit is contained in:
Lennart Poettering 2022-10-17 14:50:56 +02:00
parent 04959faa63
commit 6c51b49ce0
7 changed files with 80 additions and 39 deletions

View 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.

View 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();

View 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)

View 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)) {

View 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();
}

View 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 = {};

View 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);