From: Lennart Poettering Date: Tue, 4 Jun 2024 10:01:10 +0000 (+0200) Subject: pcrextend: automatically measure SMBIOS product ID at boot X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0196abbd10331f89bd5ca7abc39225852dff7406;p=thirdparty%2Fsystemd.git pcrextend: automatically measure SMBIOS product ID at boot Now that PCRs are not that expensive anymore, let's use them to measure the SMBIOS product ID to one. --- diff --git a/man/systemd-pcrphase.service.xml b/man/systemd-pcrphase.service.xml index a00eda08bb1..b95007dfcba 100644 --- a/man/systemd-pcrphase.service.xml +++ b/man/systemd-pcrphase.service.xml @@ -21,10 +21,11 @@ systemd-pcrphase-sysinit.service systemd-pcrphase-initrd.service systemd-pcrmachine.service + systemd-pcrproduct.service systemd-pcrfs-root.service systemd-pcrfs@.service systemd-pcrextend - Measure boot phase into TPM2 PCR 11, machine ID and file system identity into PCR 15 + Measure boot phases, machine ID, product UUID and file system identity into TPM PCRs and NvPCRs @@ -49,6 +50,10 @@ (see machine-id5) into PCR 15. + systemd-pcrproduct.service is a system service that measures the firmware + product UUID (as provided by one of SMBIOS, Devicetree, …) into a NvPCR named + hardware. + systemd-pcrfs-root.service and systemd-pcrfs@.service are services that measure file system identity information (i.e. mount point, file system type, label and UUID, partition label and UUID) into PCR 15. systemd-pcrfs-root.service does so for @@ -156,8 +161,9 @@ Takes the index of the PCR to extend. If or - are specified defaults to 15, otherwise defaults to 11. May not be - combined with . + are specified defaults to 15, otherwise (and unless + is specified) defaults to 11. May not be combined with + . @@ -167,7 +173,8 @@ Takes a name of an NvPCR to extend. NvPCRs are additional PCRs implemented via TPM NV indexes. The name should be a short string such as hardware or - disk-encryption. May not be combined with . + disk-encryption. If is specified defaults + hardware. May not be combined with . @@ -214,6 +221,15 @@ + + + + Instead of measuring a word specified on the command line into PCR 11, measure the + firmware's product UUID into an NvPCR named hardware. + + + + diff --git a/src/pcrextend/pcrextend.c b/src/pcrextend/pcrextend.c index 68d90d4e4a3..641b1e8cbe6 100644 --- a/src/pcrextend/pcrextend.c +++ b/src/pcrextend/pcrextend.c @@ -27,6 +27,7 @@ static char *arg_tpm2_device = NULL; static char **arg_banks = NULL; static char *arg_file_system = NULL; static bool arg_machine_id = false; +static bool arg_product_id = false; static unsigned arg_pcr_index = UINT_MAX; static char *arg_nvpcr_name = NULL; static bool arg_varlink = false; @@ -50,6 +51,7 @@ static int help(int argc, char *argv[], void *userdata) { printf("%1$s [OPTIONS...] WORD\n" "%1$s [OPTIONS...] --file-system=PATH\n" "%1$s [OPTIONS...] --machine-id\n" + "%1$s [OPTIONS...] --product-id\n" "\n%5$sExtend a TPM2 PCR with boot phase, machine ID, or file system ID.%6$s\n" "\n%3$sOptions:%4$s\n" " -h --help Show this help\n" @@ -61,6 +63,7 @@ static int help(int argc, char *argv[], void *userdata) { " --graceful Exit gracefully if no TPM2 device is found\n" " --file-system=PATH Measure UUID/labels of file system into PCR 15\n" " --machine-id Measure machine ID into PCR 15\n" + " --product-id Measure SMBIOS product ID into NvPCR 'hardware'\n" " --early Run in early boot mode, without access to /var/\n" "\nSee the %2$s for details.\n", program_invocation_short_name, @@ -83,6 +86,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_GRACEFUL, ARG_FILE_SYSTEM, ARG_MACHINE_ID, + ARG_PRODUCT_ID, ARG_EARLY, }; @@ -96,6 +100,7 @@ static int parse_argv(int argc, char *argv[]) { { "graceful", no_argument, NULL, ARG_GRACEFUL }, { "file-system", required_argument, NULL, ARG_FILE_SYSTEM }, { "machine-id", no_argument, NULL, ARG_MACHINE_ID }, + { "product-id", no_argument, NULL, ARG_PRODUCT_ID }, { "early", no_argument, NULL, ARG_EARLY }, {} }; @@ -176,6 +181,10 @@ static int parse_argv(int argc, char *argv[]) { arg_machine_id = true; break; + case ARG_PRODUCT_ID: + arg_product_id = true; + break; + case ARG_EARLY: arg_early = true; break; @@ -187,8 +196,8 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached(); } - if (!!arg_file_system + arg_machine_id > 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--file-system=, --machine-id may not be combined."); + if (!!arg_file_system + arg_machine_id + arg_product_id > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--file-system=, --machine-id, --product-id may not be combined."); if (arg_pcr_index != UINT_MAX && arg_nvpcr_name) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--pcr= and --nvpcr= may not be combined."); @@ -198,10 +207,16 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m"); if (r > 0) arg_varlink = true; - else if (arg_pcr_index == UINT_MAX && !arg_nvpcr_name) - arg_pcr_index = (arg_file_system || arg_machine_id) ? - TPM2_PCR_SYSTEM_IDENTITY : /* → PCR 15 */ - TPM2_PCR_KERNEL_BOOT; /* → PCR 11 */ + else if (arg_pcr_index == UINT_MAX && !arg_nvpcr_name) { + arg_pcr_index = + (arg_file_system || arg_machine_id) ? TPM2_PCR_SYSTEM_IDENTITY : /* → PCR 15 */ + !arg_product_id ? TPM2_PCR_KERNEL_BOOT : /* → PCR 11 */ + UINT_MAX; + + r = free_and_strdup_warn(&arg_nvpcr_name, arg_product_id ? "hardware" : NULL); + if (r < 0) + return r; + } return 1; } @@ -463,6 +478,17 @@ static int run(int argc, char *argv[]) { return r; event = TPM2_EVENT_MACHINE_ID; + + } else if (arg_product_id) { + + if (optind != argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected no argument."); + + r = pcrextend_product_id_word(&word); + if (r < 0) + return r; + + event = TPM2_EVENT_PRODUCT_ID; } else { if (optind+1 != argc) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected a single argument."); diff --git a/src/shared/pcrextend-util.c b/src/shared/pcrextend-util.c index e1e80a93eeb..15e0da822a6 100644 --- a/src/shared/pcrextend-util.c +++ b/src/shared/pcrextend-util.c @@ -10,6 +10,7 @@ #include "errno-util.h" #include "escape.h" #include "fd-util.h" +#include "id128-util.h" #include "log.h" #include "mountpoint-util.h" #include "pcrextend-util.h" @@ -158,3 +159,24 @@ int pcrextend_machine_id_word(char **ret) { *ret = TAKE_PTR(word); return 0; } + +int pcrextend_product_id_word(char **ret) { + _cleanup_free_ char *word = NULL; + sd_id128_t pid; + int r; + + assert(ret); + + r = id128_get_product(&pid); + if (IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* No product UUID field, or an all-zero or all-0xFF UUID */ + word = strdup("product-id:missing"); + else if (r < 0) + return log_error_errno(r, "Failed to acquire product ID: %m"); + else + word = strjoin("product-id:", SD_ID128_TO_STRING(pid)); + if (!word) + return log_oom(); + + *ret = TAKE_PTR(word); + return 0; +} diff --git a/src/shared/pcrextend-util.h b/src/shared/pcrextend-util.h index 7dd612b8fb8..88bd23c10d9 100644 --- a/src/shared/pcrextend-util.h +++ b/src/shared/pcrextend-util.h @@ -3,3 +3,4 @@ int pcrextend_file_system_word(const char *path, char **ret, char **ret_normalized_path); int pcrextend_machine_id_word(char **ret); +int pcrextend_product_id_word(char **ret); diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 49da1852719..da74ca06a5a 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -6424,6 +6424,7 @@ static const char* tpm2_userspace_event_type_table[_TPM2_USERSPACE_EVENT_TYPE_MA [TPM2_EVENT_FILESYSTEM] = "filesystem", [TPM2_EVENT_VOLUME_KEY] = "volume-key", [TPM2_EVENT_MACHINE_ID] = "machine-id", + [TPM2_EVENT_PRODUCT_ID] = "product-id", }; DEFINE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType); diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 564a3e46ad3..667d783cea5 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -142,6 +142,7 @@ typedef enum Tpm2UserspaceEventType { TPM2_EVENT_FILESYSTEM, TPM2_EVENT_VOLUME_KEY, TPM2_EVENT_MACHINE_ID, + TPM2_EVENT_PRODUCT_ID, _TPM2_USERSPACE_EVENT_TYPE_MAX, _TPM2_USERSPACE_EVENT_TYPE_INVALID = -EINVAL, } Tpm2UserspaceEventType; diff --git a/src/tpm2-setup/meson.build b/src/tpm2-setup/meson.build index 36082486d30..0221a5fba16 100644 --- a/src/tpm2-setup/meson.build +++ b/src/tpm2-setup/meson.build @@ -35,7 +35,7 @@ executables += [ ] if conf.get('ENABLE_BOOTLOADER') == 1 and conf.get('HAVE_OPENSSL') == 1 and conf.get('HAVE_TPM2') == 1 - nvpcrs = [] + nvpcrs = [ 'hardware' ] foreach n : nvpcrs custom_target( input : 'nvpcr/' + n + '.nvpcr.in', diff --git a/src/tpm2-setup/nvpcr/hardware.nvpcr.in b/src/tpm2-setup/nvpcr/hardware.nvpcr.in new file mode 100644 index 00000000000..ddeb7f99e50 --- /dev/null +++ b/src/tpm2-setup/nvpcr/hardware.nvpcr.in @@ -0,0 +1,5 @@ +{ + "name" : "hardware", + "algorithm" : "sha256", + "nvIndex" : {{TPM2_NVPCR_BASE + 0}} +} diff --git a/units/meson.build b/units/meson.build index ba2dfcab065..bd788f6d0b6 100644 --- a/units/meson.build +++ b/units/meson.build @@ -556,6 +556,11 @@ units = [ 'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'], 'symlinks' : ['factory-reset.target.wants/'], }, + { + 'file' : 'systemd-pcrproduct.service.in', + 'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'], + 'symlinks' : ['sysinit.target.wants/'], + }, { 'file' : 'systemd-pcrphase-initrd.service.in', 'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2', 'ENABLE_INITRD'], diff --git a/units/systemd-pcrproduct.service.in b/units/systemd-pcrproduct.service.in new file mode 100644 index 00000000000..a70fff19df2 --- /dev/null +++ b/units/systemd-pcrproduct.service.in @@ -0,0 +1,23 @@ +# 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=TPM PCR Product ID Measurement +Documentation=man:systemd-pcrproduct.service(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=tpm2.target +Before=sysinit.target shutdown.target +ConditionPathExists=!/etc/initrd-release +ConditionSecurity=measured-uki + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart={{LIBEXECDIR}}/systemd-pcrextend --graceful --product-id