diff --git a/man/rules/meson.build b/man/rules/meson.build
index 0b5e438200..911a68543e 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -1138,6 +1138,7 @@ manpages = [
'systemd-pcrfs@.service',
'systemd-pcrmachine.service',
'systemd-pcrnvdone.service',
+ 'systemd-pcrosseparator.service',
'systemd-pcrphase-initrd.service',
'systemd-pcrphase-sysinit.service',
'systemd-pcrproduct.service'],
diff --git a/man/systemd-pcrphase.service.xml b/man/systemd-pcrphase.service.xml
index 6b5ff05c3d..b1049ea916 100644
--- a/man/systemd-pcrphase.service.xml
+++ b/man/systemd-pcrphase.service.xml
@@ -21,6 +21,7 @@
systemd-pcrphase-sysinit.service
systemd-pcrphase-initrd.service
systemd-pcrmachine.service
+ systemd-pcrosseparator.service
systemd-pcrproduct.service
systemd-pcrfs-root.service
systemd-pcrfs@.service
@@ -34,6 +35,7 @@
systemd-pcrphase-sysinit.service
systemd-pcrphase-initrd.service
systemd-pcrmachine.service
+ systemd-pcrosseparator.service
systemd-pcrproduct.service
systemd-pcrfs-root.service
systemd-pcrfs@.service
@@ -53,6 +55,11 @@
(see machine-id5) into
PCR 15.
+ systemd-pcrosseparator.service is a system service that measures the word
+ os-separator into PCRs 0-7, 9, 12-14. This acts as additional separator measurement
+ separating pre-boot (i.e. firmware + bootloader) measurements from OS measurements, and in particular
+ "seals" off the firmware PCRs.
+
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.
@@ -168,7 +175,8 @@
Takes the index of the PCR to extend. If or
are specified defaults to 15, otherwise (and unless
- is specified) defaults to 11. May not be combined with
+ is specified) defaults to 11. May be specified multiple times, in order
+ to measure to multiple PCRs at the same time. May not be combined with
.
diff --git a/src/pcrextend/pcrextend.c b/src/pcrextend/pcrextend.c
index 8ec0a733c6..2a087ec8f8 100644
--- a/src/pcrextend/pcrextend.c
+++ b/src/pcrextend/pcrextend.c
@@ -29,7 +29,7 @@ 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 uint32_t arg_pcr_mask = 0;
static char *arg_nvpcr_name = NULL;
static bool arg_varlink = false;
static bool arg_early = false;
@@ -139,11 +139,16 @@ static int parse_argv(int argc, char *argv[]) {
}
case ARG_PCR:
+ if (isempty(optarg)) {
+ arg_pcr_mask = 0;
+ break;
+ }
+
r = tpm2_pcr_index_from_string(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse PCR index: %s", optarg);
- arg_pcr_index = r;
+ arg_pcr_mask |= INDEX_TO_MASK(uint32_t, r);
break;
case ARG_NVPCR:
@@ -213,7 +218,7 @@ static int parse_argv(int argc, char *argv[]) {
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)
+ if (arg_pcr_mask != 0 && arg_nvpcr_name)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--pcr= and --nvpcr= may not be combined.");
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
@@ -221,11 +226,11 @@ 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 */
- !arg_product_id ? TPM2_PCR_KERNEL_BOOT : /* → PCR 11 */
- UINT_MAX;
+ else if (arg_pcr_mask == 0 && !arg_nvpcr_name) {
+ arg_pcr_mask =
+ (arg_file_system || arg_machine_id) ? INDEX_TO_MASK(uint32_t, TPM2_PCR_SYSTEM_IDENTITY) : /* → PCR 15 */
+ !arg_product_id ? INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT) : /* → PCR 11 */
+ 0;
r = free_and_strdup_warn(&arg_nvpcr_name, arg_product_id ? "hardware" : NULL);
if (r < 0)
@@ -235,7 +240,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int determine_banks(Tpm2Context *c, unsigned target_pcr_nr) {
+static int determine_banks(Tpm2Context *c, uint32_t target_pcr_mask) {
_cleanup_strv_free_ char **l = NULL;
int r;
@@ -244,7 +249,7 @@ static int determine_banks(Tpm2Context *c, unsigned target_pcr_nr) {
if (!strv_isempty(arg_banks)) /* Explicitly configured? Then use that */
return 0;
- r = tpm2_get_good_pcr_banks_strv(c, UINT32_C(1) << target_pcr_nr, &l);
+ r = tpm2_get_good_pcr_banks_strv(c, target_pcr_mask, &l);
if (r < 0)
return log_error_errno(r, "Could not verify pcr banks: %m");
@@ -275,7 +280,7 @@ static int escape_and_truncate_data(const void *data, size_t size, char **ret) {
}
static int extend_pcr_now(
- unsigned pcr,
+ uint32_t pcr_mask,
const void *data,
size_t size,
Tpm2UserspaceEventType event) {
@@ -283,11 +288,13 @@ static int extend_pcr_now(
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
int r;
+ assert(pcr_mask != 0);
+
r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
if (r < 0)
return r;
- r = determine_banks(c, pcr);
+ r = determine_banks(c, pcr_mask);
if (r < 0)
return r;
if (strv_isempty(arg_banks)) /* Still none? */
@@ -302,18 +309,20 @@ static int extend_pcr_now(
if (escape_and_truncate_data(data, size, &safe) < 0)
return log_oom();
- log_debug("Measuring '%s' into PCR index %u, banks %s.", safe, pcr, joined_banks);
+ BIT_FOREACH(pcr, pcr_mask) {
+ log_debug("Measuring '%s' into PCR index %i, banks %s.", safe, pcr, joined_banks);
- r = tpm2_pcr_extend_bytes(c, arg_banks, pcr, &IOVEC_MAKE(data, size), /* secret= */ NULL, event, safe);
- if (r < 0)
- return log_error_errno(r, "Could not extend PCR: %m");
+ r = tpm2_pcr_extend_bytes(c, arg_banks, pcr, &IOVEC_MAKE(data, size), /* secret= */ NULL, event, safe);
+ if (r < 0)
+ return log_error_errno(r, "Could not extend PCR: %m");
- log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_TPM_PCR_EXTEND_STR),
- LOG_MESSAGE("Extended PCR index %u with '%s' (banks %s).", pcr, safe, joined_banks),
- LOG_ITEM("MEASURING=%s", safe),
- LOG_ITEM("PCR=%u", pcr),
- LOG_ITEM("BANKS=%s", joined_banks));
+ log_struct(LOG_INFO,
+ LOG_MESSAGE_ID(SD_MESSAGE_TPM_PCR_EXTEND_STR),
+ LOG_MESSAGE("Extended PCR index %i with '%s' (banks %s).", pcr, safe, joined_banks),
+ LOG_ITEM("MEASURING=%s", safe),
+ LOG_ITEM("PCR=%i", pcr),
+ LOG_ITEM("BANKS=%s", joined_banks));
+ }
return 0;
}
@@ -435,7 +444,7 @@ static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_va
if (r == -ENOENT)
return sd_varlink_error(link, "io.systemd.PCRExtend.NoSuchNvPCR", NULL);
} else
- r = extend_pcr_now(p.pcr, extend_iovec->iov_base, extend_iovec->iov_len, p.event_type);
+ r = extend_pcr_now(INDEX_TO_MASK(uint32_t, p.pcr), extend_iovec->iov_base, extend_iovec->iov_len, p.event_type);
if (r < 0)
return r;
@@ -548,7 +557,7 @@ static int run(int argc, char *argv[]) {
if (arg_nvpcr_name)
r = extend_nvpcr_now(arg_nvpcr_name, word, strlen(word), event);
else
- r = extend_pcr_now(arg_pcr_index, word, strlen(word), event);
+ r = extend_pcr_now(arg_pcr_mask, word, strlen(word), event);
if (r < 0)
return r;
diff --git a/src/pcrlock/meson.build b/src/pcrlock/meson.build
index f7fa3d18d9..ff2b0f0cb2 100644
--- a/src/pcrlock/meson.build
+++ b/src/pcrlock/meson.build
@@ -27,6 +27,7 @@ install_data('pcrlock.d/500-separator.pcrlock.d/600-0xffffffff.pcrlock', install
install_data('pcrlock.d/700-action-efi-exit-boot-services.pcrlock.d/300-present.pcrlock', install_dir : pcrlockdir / '700-action-efi-exit-boot-services.pcrlock.d')
install_data('pcrlock.d/700-action-efi-exit-boot-services.pcrlock.d/600-absent.pcrlock', install_dir : pcrlockdir / '700-action-efi-exit-boot-services.pcrlock.d')
install_data('pcrlock.d/750-enter-initrd.pcrlock', install_dir : pcrlockdir)
+install_data('pcrlock.d/750-os-separator.pcrlock', install_dir : pcrlockdir)
install_data('pcrlock.d/770-nvpcr-separator.pcrlock', install_dir : pcrlockdir)
install_data('pcrlock.d/800-leave-initrd.pcrlock', install_dir : pcrlockdir)
install_data('pcrlock.d/850-sysinit.pcrlock', install_dir : pcrlockdir)
diff --git a/src/pcrlock/pcrlock.d/750-os-separator.pcrlock b/src/pcrlock/pcrlock.d/750-os-separator.pcrlock
new file mode 100644
index 0000000000..aaeba174d1
--- /dev/null
+++ b/src/pcrlock/pcrlock.d/750-os-separator.pcrlock
@@ -0,0 +1 @@
+{"records":[{"pcr":0,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":1,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":2,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":3,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":4,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":5,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":6,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":7,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":9,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":12,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":13,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]},{"pcr":14,"digests":[{"hashAlg":"sha512","digest":"c2c4de82bbe2dd787a6bdca1014b9557313f258e18aaee2c9c3e2f9c79e526d66283db9aef5d7aeed3ffecd628ecc045716583eecd23ca73eee63f299a8dc2cc"},{"hashAlg":"sha384","digest":"5f2a9be7b55475435f46c343bb8c224db427d4fc7cdca42ac02e50d61c67a8bf0b707f7e1527a5f65175efdf8b56ba24"},{"hashAlg":"sha256","digest":"ff5b9d73dad709633ae76adf444012b57e913a12ed7403c3931145862f35f841"},{"hashAlg":"sha1","digest":"0d3751a5a6707fb8918dfbe6b0d9853ca6c313c4"}]}]}
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 05ea7c47be..fbf87d8d5a 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -6680,6 +6680,7 @@ static const char* tpm2_userspace_event_type_table[_TPM2_USERSPACE_EVENT_TYPE_MA
[TPM2_EVENT_NVPCR_SEPARATOR] = "nvpcr-separator",
[TPM2_EVENT_DM_VERITY] = "dm-verity",
[TPM2_EVENT_IMDS_USERDATA] = "imds-userdata",
+ [TPM2_EVENT_OS_SEPARATOR] = "os-separator",
};
DEFINE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType);
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 2f5d8632de..5ada96e8e1 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -149,6 +149,7 @@ typedef enum Tpm2UserspaceEventType {
TPM2_EVENT_NVPCR_SEPARATOR,
TPM2_EVENT_DM_VERITY,
TPM2_EVENT_IMDS_USERDATA,
+ TPM2_EVENT_OS_SEPARATOR,
_TPM2_USERSPACE_EVENT_TYPE_MAX,
_TPM2_USERSPACE_EVENT_TYPE_INVALID = -EINVAL,
} Tpm2UserspaceEventType;
diff --git a/test/units/TEST-70-TPM2.pcrextend.sh b/test/units/TEST-70-TPM2.pcrextend.sh
index fbb6b30a12..14808f0763 100755
--- a/test/units/TEST-70-TPM2.pcrextend.sh
+++ b/test/units/TEST-70-TPM2.pcrextend.sh
@@ -53,7 +53,6 @@ fi
(! "$SD_PCREXTEND" --bank= foo)
(! "$SD_PCREXTEND" --tpm2-device= foo)
(! "$SD_PCREXTEND" --tpm2-device=/dev/null foo)
-(! "$SD_PCREXTEND" --pcr= foo)
(! "$SD_PCREXTEND" --pcr=-1 foo)
(! "$SD_PCREXTEND" --pcr=1024 foo)
(! "$SD_PCREXTEND" --foo=bar)
diff --git a/test/units/TEST-70-TPM2.pcrlock.sh b/test/units/TEST-70-TPM2.pcrlock.sh
index d90b4bc99f..71f2ac53d7 100755
--- a/test/units/TEST-70-TPM2.pcrlock.sh
+++ b/test/units/TEST-70-TPM2.pcrlock.sh
@@ -42,6 +42,18 @@ PCRS="1+2+3+4+5+16"
# (as the PCR values simply won't match the log).
rm -f /run/log/systemd/tpm2-measure.log
+# Add the os-separator measurements, they should be the only measurements that touch pcr 0…6 done from userspace.
+RS=$'\x1e'
+cat >/run/log/systemd/tpm2-measure.log <