diff --git a/man/systemd-pcrphase.service.xml b/man/systemd-pcrphase.service.xml
index a00eda08bb..b95007dfcb 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 68d90d4e4a..641b1e8cbe 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 e1e80a93ee..15e0da822a 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 7dd612b8fb..88bd23c10d 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 49da185271..da74ca06a5 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 564a3e46ad..667d783cea 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 36082486d3..0221a5fba1 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 0000000000..ddeb7f99e5
--- /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 ba2dfcab06..bd788f6d0b 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 0000000000..a70fff19df
--- /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