You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
612 lines
25 KiB
612 lines
25 KiB
8 months ago
|
From 1f5a825144e492e54b68373d035bd2889bffc81b Mon Sep 17 00:00:00 2001
|
||
|
From: Dan Streetman <ddstreet@ieee.org>
|
||
|
Date: Tue, 11 Jul 2023 21:23:36 -0400
|
||
|
Subject: [PATCH] tpm2: add Tpm2PCRValue struct and associated functions
|
||
|
|
||
|
Add a new struct that can represent a PCR index, hash, and value all
|
||
|
together. This replaces code (e.g. the tpm2_pcr_read() parameters) that
|
||
|
required using both a TPML_PCR_SELECTION as well as array of TPM2B_DIGEST
|
||
|
entries, which was difficult to correlate the selection hash/index to each
|
||
|
digest.
|
||
|
|
||
|
(cherry picked from commit 323eb4803a29a9cc255aa16ef7cab3a00429b146)
|
||
|
|
||
|
Related: RHEL-16182
|
||
|
---
|
||
|
.../cryptsetup-token-systemd-tpm2.c | 2 +-
|
||
|
src/cryptsetup/cryptsetup.c | 2 +-
|
||
|
src/shared/tpm2-util.c | 419 ++++++++++++++++--
|
||
|
src/shared/tpm2-util.h | 25 +-
|
||
|
4 files changed, 399 insertions(+), 49 deletions(-)
|
||
|
|
||
|
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
|
||
|
index aab3a4b4c0..94d568c17f 100644
|
||
|
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
|
||
|
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
|
||
|
@@ -278,7 +278,7 @@ _public_ int cryptsetup_token_validate(
|
||
|
}
|
||
|
|
||
|
u = json_variant_unsigned(e);
|
||
|
- if (!TPM2_PCR_VALID(u)) {
|
||
|
+ if (!TPM2_PCR_INDEX_VALID(u)) {
|
||
|
crypt_log_debug(cd, "TPM2 PCR number out of range.");
|
||
|
return 1;
|
||
|
}
|
||
|
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
|
||
|
index 96341207b3..866141ac44 100644
|
||
|
--- a/src/cryptsetup/cryptsetup.c
|
||
|
+++ b/src/cryptsetup/cryptsetup.c
|
||
|
@@ -426,7 +426,7 @@ static int parse_one_option(const char *option) {
|
||
|
}
|
||
|
|
||
|
pcr = r ? TPM_PCR_INDEX_VOLUME_KEY : UINT_MAX;
|
||
|
- } else if (!TPM2_PCR_VALID(pcr)) {
|
||
|
+ } else if (!TPM2_PCR_INDEX_VALID(pcr)) {
|
||
|
log_warning("Selected TPM index for measurement %u outside of allowed range 0…%u, ignoring.", pcr, TPM2_PCRS_MAX-1);
|
||
|
return 0;
|
||
|
}
|
||
|
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
|
||
|
index 9c0cad47c6..cef251e69c 100644
|
||
|
--- a/src/shared/tpm2-util.c
|
||
|
+++ b/src/shared/tpm2-util.c
|
||
|
@@ -19,6 +19,7 @@
|
||
|
#include "parse-util.h"
|
||
|
#include "random-util.h"
|
||
|
#include "sha256.h"
|
||
|
+#include "sort-util.h"
|
||
|
#include "stat-util.h"
|
||
|
#include "string-table.h"
|
||
|
#include "time-util.h"
|
||
|
@@ -1516,6 +1517,318 @@ size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l) {
|
||
|
return weight;
|
||
|
}
|
||
|
|
||
|
+bool TPM2_PCR_VALUE_VALID(const Tpm2PCRValue *pcr_value) {
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(pcr_value);
|
||
|
+
|
||
|
+ if (!TPM2_PCR_INDEX_VALID(pcr_value->index)) {
|
||
|
+ log_debug("PCR index %u invalid.", pcr_value->index);
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* If it contains a value, the value size must match the hash size. */
|
||
|
+ if (pcr_value->value.size > 0) {
|
||
|
+ r = tpm2_hash_alg_to_size(pcr_value->hash);
|
||
|
+ if (r < 0)
|
||
|
+ return false;
|
||
|
+
|
||
|
+ if ((int) pcr_value->value.size != r) {
|
||
|
+ log_debug("PCR hash 0x%" PRIx16 " expected size %d does not match actual size %" PRIu16 ".",
|
||
|
+ pcr_value->hash, r, pcr_value->value.size);
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return true;
|
||
|
+}
|
||
|
+
|
||
|
+/* Verify all entries are valid, and consistent with each other. The requirements for consistency are:
|
||
|
+ *
|
||
|
+ * 1) all entries must be sorted in ascending order (e.g. using tpm2_sort_pcr_values())
|
||
|
+ * 2) all entries must be unique, i.e. there cannot be 2 entries with the same hash and index
|
||
|
+ */
|
||
|
+bool TPM2_PCR_VALUES_VALID(const Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
|
||
|
+ assert(pcr_values || n_pcr_values == 0);
|
||
|
+
|
||
|
+ for (size_t i = 0; i < n_pcr_values; i++) {
|
||
|
+ const Tpm2PCRValue *v = &pcr_values[i];
|
||
|
+
|
||
|
+ if (!TPM2_PCR_VALUE_VALID(v))
|
||
|
+ return false;
|
||
|
+
|
||
|
+ if (i == 0)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ const Tpm2PCRValue *l = &pcr_values[i - 1];
|
||
|
+
|
||
|
+ /* Hashes must be sorted in ascending order */
|
||
|
+ if (v->hash < l->hash) {
|
||
|
+ log_debug("PCR values not in ascending order, hash %" PRIu16 " is after %" PRIu16 ".",
|
||
|
+ v->hash, l->hash);
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (v->hash == l->hash) {
|
||
|
+ /* Indexes (for the same hash) must be sorted in ascending order */
|
||
|
+ if (v->index < l->index) {
|
||
|
+ log_debug("PCR values not in ascending order, hash %" PRIu16 " index %u is after %u.",
|
||
|
+ v->hash, v->index, l->index);
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Indexes (for the same hash) must not be duplicates */
|
||
|
+ if (v->index == l->index) {
|
||
|
+ log_debug("PCR values contain duplicates for hash %" PRIu16 " index %u.",
|
||
|
+ v->hash, v->index);
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return true;
|
||
|
+}
|
||
|
+
|
||
|
+static int cmp_pcr_values(const Tpm2PCRValue *a, const Tpm2PCRValue *b) {
|
||
|
+ assert(a);
|
||
|
+ assert(b);
|
||
|
+
|
||
|
+ return CMP(a->hash, b->hash) ?: CMP(a->index, b->index);
|
||
|
+}
|
||
|
+
|
||
|
+/* Sort the array of Tpm2PCRValue entries in-place. This sorts first in ascending order of hash algorithm
|
||
|
+ * (sorting simply by the TPM2 hash algorithm number), and then sorting by pcr index. */
|
||
|
+void tpm2_sort_pcr_values(Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
|
||
|
+ typesafe_qsort(pcr_values, n_pcr_values, cmp_pcr_values);
|
||
|
+}
|
||
|
+
|
||
|
+int tpm2_pcr_values_from_mask(uint32_t mask, TPMI_ALG_HASH hash, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) {
|
||
|
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
|
||
|
+ size_t n_pcr_values = 0;
|
||
|
+
|
||
|
+ assert(ret_pcr_values);
|
||
|
+ assert(ret_n_pcr_values);
|
||
|
+
|
||
|
+ FOREACH_PCR_IN_MASK(index, mask)
|
||
|
+ if (!GREEDY_REALLOC_APPEND(
|
||
|
+ pcr_values,
|
||
|
+ n_pcr_values,
|
||
|
+ &TPM2_PCR_VALUE_MAKE(index, hash, {}),
|
||
|
+ 1))
|
||
|
+ return log_oom_debug();
|
||
|
+
|
||
|
+ *ret_pcr_values = TAKE_PTR(pcr_values);
|
||
|
+ *ret_n_pcr_values = n_pcr_values;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+int tpm2_pcr_values_to_mask(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPMI_ALG_HASH hash, uint32_t *ret_mask) {
|
||
|
+ uint32_t mask = 0;
|
||
|
+
|
||
|
+ assert(pcr_values || n_pcr_values == 0);
|
||
|
+ assert(ret_mask);
|
||
|
+
|
||
|
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
|
||
|
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PCR values.");
|
||
|
+
|
||
|
+ for (size_t i = 0; i < n_pcr_values; i++)
|
||
|
+ if (pcr_values[i].hash == hash)
|
||
|
+ SET_BIT(mask, pcr_values[i].index);
|
||
|
+
|
||
|
+ *ret_mask = mask;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+int tpm2_tpml_pcr_selection_from_pcr_values(
|
||
|
+ const Tpm2PCRValue *pcr_values,
|
||
|
+ size_t n_pcr_values,
|
||
|
+ TPML_PCR_SELECTION *ret_selection,
|
||
|
+ TPM2B_DIGEST **ret_values,
|
||
|
+ size_t *ret_n_values) {
|
||
|
+
|
||
|
+ TPML_PCR_SELECTION selection = {};
|
||
|
+ _cleanup_free_ TPM2B_DIGEST *values = NULL;
|
||
|
+ size_t n_values = 0;
|
||
|
+
|
||
|
+ assert(pcr_values || n_pcr_values == 0);
|
||
|
+
|
||
|
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
|
||
|
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "PCR values are not valid.");
|
||
|
+
|
||
|
+ for (size_t i = 0; i < n_pcr_values; i++) {
|
||
|
+ unsigned index = pcr_values[i].index;
|
||
|
+ TPMI_ALG_HASH hash = pcr_values[i].hash;
|
||
|
+ const TPM2B_DIGEST *digest = &pcr_values[i].value;
|
||
|
+
|
||
|
+ tpm2_tpml_pcr_selection_add_mask(&selection, hash, INDEX_TO_MASK(uint32_t, index));
|
||
|
+
|
||
|
+ if (!GREEDY_REALLOC_APPEND(values, n_values, digest, 1))
|
||
|
+ return log_oom_debug();
|
||
|
+ }
|
||
|
+
|
||
|
+ if (ret_selection)
|
||
|
+ *ret_selection = selection;
|
||
|
+ if (ret_values)
|
||
|
+ *ret_values = TAKE_PTR(values);
|
||
|
+ if (ret_n_values)
|
||
|
+ *ret_n_values = n_values;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* Count the number of different hash algorithms for all the entries. */
|
||
|
+int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, size_t *ret_count) {
|
||
|
+ TPML_PCR_SELECTION selection;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(pcr_values);
|
||
|
+ assert(ret_count);
|
||
|
+
|
||
|
+ r = tpm2_tpml_pcr_selection_from_pcr_values(
|
||
|
+ pcr_values,
|
||
|
+ n_pcr_values,
|
||
|
+ &selection,
|
||
|
+ /* ret_values= */ NULL,
|
||
|
+ /* ret_n_values= */ NULL);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ *ret_count = selection.count;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* Parse a string argument into a Tpm2PCRValue object.
|
||
|
+ *
|
||
|
+ * The format is <index>[:hash[=value]] where index is the index number (or name) of the PCR, e.g. 0 (or
|
||
|
+ * platform-code), hash is the name of the hash algorithm (e.g. sha256) and value is the hex hash digest
|
||
|
+ * value, optionally with a leading 0x. This does not check for validity of the fields. */
|
||
|
+int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value) {
|
||
|
+ Tpm2PCRValue pcr_value = {};
|
||
|
+ const char *p = arg;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(arg);
|
||
|
+ assert(ret_pcr_value);
|
||
|
+
|
||
|
+ _cleanup_free_ char *index = NULL;
|
||
|
+ r = extract_first_word(&p, &index, ":", /* flags= */ 0);
|
||
|
+ if (r < 1)
|
||
|
+ return log_error_errno(r, "Could not parse pcr value '%s': %m", p);
|
||
|
+
|
||
|
+ r = pcr_index_from_string(index);
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(r, "Invalid pcr index '%s': %m", index);
|
||
|
+ pcr_value.index = (unsigned) r;
|
||
|
+
|
||
|
+ if (!isempty(p)) {
|
||
|
+ _cleanup_free_ char *hash = NULL;
|
||
|
+ r = extract_first_word(&p, &hash, "=", /* flags= */ 0);
|
||
|
+ if (r < 1)
|
||
|
+ return log_error_errno(r, "Could not parse pcr hash algorithm '%s': %m", p);
|
||
|
+
|
||
|
+ r = tpm2_hash_alg_from_string(hash);
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(r, "Invalid pcr hash algorithm '%s': %m", hash);
|
||
|
+ pcr_value.hash = (TPMI_ALG_HASH) r;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!isempty(p)) {
|
||
|
+ /* Remove leading 0x if present */
|
||
|
+ p = startswith_no_case(p, "0x") ?: p;
|
||
|
+
|
||
|
+ _cleanup_free_ void *buf = NULL;
|
||
|
+ size_t buf_size = 0;
|
||
|
+ r = unhexmem(p, strlen(p), &buf, &buf_size);
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(r, "Invalid pcr hash value '%s': %m", p);
|
||
|
+
|
||
|
+ pcr_value.value.size = buf_size;
|
||
|
+ assert(sizeof(pcr_value.value.buffer) >= pcr_value.value.size);
|
||
|
+ memcpy(pcr_value.value.buffer, buf, pcr_value.value.size);
|
||
|
+ }
|
||
|
+
|
||
|
+ *ret_pcr_value = pcr_value;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* Return a string for the PCR value. The format is described in tpm2_pcr_value_from_string(). Note that if
|
||
|
+ * the hash algorithm is not recognized, neither hash name nor hash digest value is included in the
|
||
|
+ * string. This does not check for validity. */
|
||
|
+char *tpm2_pcr_value_to_string(const Tpm2PCRValue *pcr_value) {
|
||
|
+ _cleanup_free_ char *index = NULL, *value = NULL;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ r = asprintf(&index, "%u", pcr_value->index);
|
||
|
+ if (r < 0)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ const char *hash = tpm2_hash_alg_to_string(pcr_value->hash);
|
||
|
+
|
||
|
+ if (hash && pcr_value->value.size > 0) {
|
||
|
+ value = hexmem(pcr_value->value.buffer, pcr_value->value.size);
|
||
|
+ if (!value)
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ return strjoin(index, hash ? ":" : "", hash ?: "", value ? "=" : "", value ?: "");
|
||
|
+}
|
||
|
+
|
||
|
+/* Parse a string argument into an array of Tpm2PCRValue objects.
|
||
|
+ *
|
||
|
+ * The format is zero or more entries separated by ',' or '+'. The format of each entry is described in
|
||
|
+ * tpm2_pcr_value_from_string(). This does not check for validity of the entries. */
|
||
|
+int tpm2_pcr_values_from_string(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) {
|
||
|
+ const char *p = arg;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(arg);
|
||
|
+ assert(ret_pcr_values);
|
||
|
+ assert(ret_n_pcr_values);
|
||
|
+
|
||
|
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
|
||
|
+ size_t n_pcr_values = 0;
|
||
|
+
|
||
|
+ for (;;) {
|
||
|
+ _cleanup_free_ char *pcr_arg = NULL;
|
||
|
+ r = extract_first_word(&p, &pcr_arg, ",+", /* flags= */ 0);
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(r, "Could not parse pcr values '%s': %m", p);
|
||
|
+ if (r == 0)
|
||
|
+ break;
|
||
|
+
|
||
|
+ Tpm2PCRValue pcr_value;
|
||
|
+ r = tpm2_pcr_value_from_string(pcr_arg, &pcr_value);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ if (!GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, &pcr_value, 1))
|
||
|
+ return log_oom();
|
||
|
+ }
|
||
|
+
|
||
|
+ *ret_pcr_values = TAKE_PTR(pcr_values);
|
||
|
+ *ret_n_pcr_values = n_pcr_values;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* Return a string representing the array of PCR values. The format is as described in
|
||
|
+ * tpm2_pcr_values_from_string(). This does not check for validity. */
|
||
|
+char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
|
||
|
+ _cleanup_free_ char *s = NULL;
|
||
|
+
|
||
|
+ for (size_t i = 0; i < n_pcr_values; i++) {
|
||
|
+ _cleanup_free_ char *pcrstr = tpm2_pcr_value_to_string(&pcr_values[i]);
|
||
|
+ if (!pcrstr || !strextend_with_separator(&s, "+", pcrstr))
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ return s ? TAKE_PTR(s) : strdup("");
|
||
|
+}
|
||
|
+
|
||
|
static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) {
|
||
|
if (!DEBUG_LOGGING || !l)
|
||
|
return;
|
||
|
@@ -1524,6 +1837,14 @@ static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const
|
||
|
log_debug("%s: %s", msg ?: "PCR selection", strna(s));
|
||
|
}
|
||
|
|
||
|
+static void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg) {
|
||
|
+ if (!DEBUG_LOGGING || !pcr_value)
|
||
|
+ return;
|
||
|
+
|
||
|
+ _cleanup_free_ char *s = tpm2_pcr_value_to_string(pcr_value);
|
||
|
+ log_debug("%s: %s", msg ?: "PCR value", strna(s));
|
||
|
+}
|
||
|
+
|
||
|
static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
|
||
|
if (!DEBUG_LOGGING || !buffer || size == 0)
|
||
|
return;
|
||
|
@@ -1919,22 +2240,27 @@ int tpm2_create_loaded(
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+/* Read hash values from the specified PCR selection. Provides a Tpm2PCRValue array that contains all
|
||
|
+ * requested PCR values, in the order provided by the TPM. Normally, the provided pcr values will match
|
||
|
+ * exactly what is in the provided selection, but the TPM may ignore some selected PCRs (for example, if an
|
||
|
+ * unimplemented PCR index is requested), in which case those PCRs will be absent from the provided pcr
|
||
|
+ * values. */
|
||
|
static int tpm2_pcr_read(
|
||
|
Tpm2Context *c,
|
||
|
const TPML_PCR_SELECTION *pcr_selection,
|
||
|
- TPML_PCR_SELECTION *ret_pcr_selection,
|
||
|
- TPM2B_DIGEST **ret_pcr_values,
|
||
|
+ Tpm2PCRValue **ret_pcr_values,
|
||
|
size_t *ret_n_pcr_values) {
|
||
|
|
||
|
- _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
|
||
|
- TPML_PCR_SELECTION remaining, total_read = {};
|
||
|
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
|
||
|
size_t n_pcr_values = 0;
|
||
|
TSS2_RC rc;
|
||
|
|
||
|
assert(c);
|
||
|
assert(pcr_selection);
|
||
|
+ assert(ret_pcr_values);
|
||
|
+ assert(ret_n_pcr_values);
|
||
|
|
||
|
- remaining = *pcr_selection;
|
||
|
+ TPML_PCR_SELECTION remaining = *pcr_selection;
|
||
|
while (!tpm2_tpml_pcr_selection_is_empty(&remaining)) {
|
||
|
_cleanup_(Esys_Freep) TPML_PCR_SELECTION *current_read = NULL;
|
||
|
_cleanup_(Esys_Freep) TPML_DIGEST *current_values = NULL;
|
||
|
@@ -1955,44 +2281,39 @@ static int tpm2_pcr_read(
|
||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||
|
"Failed to read TPM2 PCRs: %s", sym_Tss2_RC_Decode(rc));
|
||
|
|
||
|
+ tpm2_log_debug_tpml_pcr_selection(current_read, "Read PCR selection");
|
||
|
+
|
||
|
if (tpm2_tpml_pcr_selection_is_empty(current_read)) {
|
||
|
log_warning("TPM2 refused to read possibly unimplemented PCRs, ignoring.");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
- tpm2_tpml_pcr_selection_sub(&remaining, current_read);
|
||
|
- tpm2_tpml_pcr_selection_add(&total_read, current_read);
|
||
|
+ unsigned i = 0;
|
||
|
+ FOREACH_PCR_IN_TPML_PCR_SELECTION(index, tpms, current_read) {
|
||
|
+ assert(i < current_values->count);
|
||
|
+ Tpm2PCRValue pcr_value = {
|
||
|
+ .index = index,
|
||
|
+ .hash = tpms->hash,
|
||
|
+ .value = current_values->digests[i++],
|
||
|
+ };
|
||
|
|
||
|
- if (!GREEDY_REALLOC(pcr_values, n_pcr_values + current_values->count))
|
||
|
- return log_oom();
|
||
|
+ tpm2_log_debug_pcr_value(&pcr_value, /* msg= */ NULL);
|
||
|
|
||
|
- memcpy_safe(&pcr_values[n_pcr_values], current_values->digests,
|
||
|
- current_values->count * sizeof(TPM2B_DIGEST));
|
||
|
- n_pcr_values += current_values->count;
|
||
|
+ if (!GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, &pcr_value, 1))
|
||
|
+ return log_oom();
|
||
|
+ }
|
||
|
+ assert(i == current_values->count);
|
||
|
|
||
|
- if (DEBUG_LOGGING) {
|
||
|
- unsigned i = 0;
|
||
|
- FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, current_read) {
|
||
|
- assert(i < current_values->count);
|
||
|
+ tpm2_tpml_pcr_selection_sub(&remaining, current_read);
|
||
|
+ }
|
||
|
|
||
|
- TPM2B_DIGEST *d = ¤t_values->digests[i];
|
||
|
- i++;
|
||
|
+ tpm2_sort_pcr_values(pcr_values, n_pcr_values);
|
||
|
|
||
|
- TPML_PCR_SELECTION l;
|
||
|
- tpm2_tpml_pcr_selection_from_mask(INDEX_TO_MASK(uint32_t, pcr), s->hash, &l);
|
||
|
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
|
||
|
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PCR values read from TPM are not valid.");
|
||
|
|
||
|
- _cleanup_free_ char *desc = tpm2_tpml_pcr_selection_to_string(&l);
|
||
|
- tpm2_log_debug_digest(d, strna(desc));
|
||
|
- }
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- if (ret_pcr_selection)
|
||
|
- *ret_pcr_selection = total_read;
|
||
|
- if (ret_pcr_values)
|
||
|
- *ret_pcr_values = TAKE_PTR(pcr_values);
|
||
|
- if (ret_n_pcr_values)
|
||
|
- *ret_n_pcr_values = n_pcr_values;
|
||
|
+ *ret_pcr_values = TAKE_PTR(pcr_values);
|
||
|
+ *ret_n_pcr_values = n_pcr_values;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
@@ -2002,9 +2323,7 @@ static int tpm2_pcr_mask_good(
|
||
|
TPMI_ALG_HASH bank,
|
||
|
uint32_t mask) {
|
||
|
|
||
|
- _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
|
||
|
TPML_PCR_SELECTION selection;
|
||
|
- size_t n_pcr_values = 0;
|
||
|
int r;
|
||
|
|
||
|
assert(c);
|
||
|
@@ -2015,22 +2334,18 @@ static int tpm2_pcr_mask_good(
|
||
|
|
||
|
tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection);
|
||
|
|
||
|
- r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &n_pcr_values);
|
||
|
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
|
||
|
+ size_t n_pcr_values;
|
||
|
+ r = tpm2_pcr_read(c, &selection, &pcr_values, &n_pcr_values);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
/* If at least one of the selected PCR values is something other than all 0x00 or all 0xFF we are happy. */
|
||
|
- unsigned i = 0;
|
||
|
- FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, &selection) {
|
||
|
- assert(i < n_pcr_values);
|
||
|
-
|
||
|
- if (!memeqbyte(0x00, pcr_values[i].buffer, pcr_values[i].size) &&
|
||
|
- !memeqbyte(0xFF, pcr_values[i].buffer, pcr_values[i].size))
|
||
|
+ for (unsigned i = 0; i < n_pcr_values; i++)
|
||
|
+ if (!memeqbyte(0x00, pcr_values[i].value.buffer, pcr_values[i].value.size) &&
|
||
|
+ !memeqbyte(0xFF, pcr_values[i].value.buffer, pcr_values[i].value.size))
|
||
|
return true;
|
||
|
|
||
|
- i++;
|
||
|
- }
|
||
|
-
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
@@ -3254,14 +3569,26 @@ int tpm2_seal(const char *device,
|
||
|
|
||
|
TPML_PCR_SELECTION hash_pcr_selection = {};
|
||
|
_cleanup_free_ TPM2B_DIGEST *hash_pcr_values = NULL;
|
||
|
- size_t n_hash_pcr_values = 0;
|
||
|
+ size_t n_hash_pcr_values;
|
||
|
if (hash_pcr_mask) {
|
||
|
/* For now, we just read the current values from the system; we need to be able to specify
|
||
|
* expected values, eventually. */
|
||
|
tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, pcr_bank, &hash_pcr_selection);
|
||
|
- r = tpm2_pcr_read(c, &hash_pcr_selection, &hash_pcr_selection, &hash_pcr_values, &n_hash_pcr_values);
|
||
|
+
|
||
|
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
|
||
|
+ size_t n_pcr_values;
|
||
|
+ r = tpm2_pcr_read(c, &hash_pcr_selection, &pcr_values, &n_pcr_values);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
+
|
||
|
+ r = tpm2_tpml_pcr_selection_from_pcr_values(
|
||
|
+ pcr_values,
|
||
|
+ n_pcr_values,
|
||
|
+ &hash_pcr_selection,
|
||
|
+ &hash_pcr_values,
|
||
|
+ &n_hash_pcr_values);
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(r, "Could not get PCR selection from values: %m");
|
||
|
}
|
||
|
|
||
|
TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
|
||
|
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
|
||
|
index c024245915..2e25866401 100644
|
||
|
--- a/src/shared/tpm2-util.h
|
||
|
+++ b/src/shared/tpm2-util.h
|
||
|
@@ -17,7 +17,7 @@ typedef enum TPM2Flags {
|
||
|
* TPM2 on a Client PC must have at least 24 PCRs. This hardcodes our expectation of 24. */
|
||
|
#define TPM2_PCRS_MAX 24U
|
||
|
#define TPM2_PCRS_MASK ((UINT32_C(1) << TPM2_PCRS_MAX) - 1)
|
||
|
-static inline bool TPM2_PCR_VALID(unsigned pcr) {
|
||
|
+static inline bool TPM2_PCR_INDEX_VALID(unsigned pcr) {
|
||
|
return pcr < TPM2_PCRS_MAX;
|
||
|
}
|
||
|
static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
|
||
|
@@ -88,6 +88,26 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
|
||
|
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
|
||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
|
||
|
|
||
|
+typedef struct {
|
||
|
+ unsigned index;
|
||
|
+ TPMI_ALG_HASH hash;
|
||
|
+ TPM2B_DIGEST value;
|
||
|
+} Tpm2PCRValue;
|
||
|
+
|
||
|
+#define TPM2_PCR_VALUE_MAKE(i, h, v) (Tpm2PCRValue) { .index = (i), .hash = (h), .value = ((TPM2B_DIGEST) v), }
|
||
|
+bool TPM2_PCR_VALUE_VALID(const Tpm2PCRValue *pcr_value);
|
||
|
+int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value);
|
||
|
+char *tpm2_pcr_value_to_string(const Tpm2PCRValue *pcr_value);
|
||
|
+
|
||
|
+bool TPM2_PCR_VALUES_VALID(const Tpm2PCRValue *pcr_values, size_t n_pcr_values);
|
||
|
+void tpm2_sort_pcr_values(Tpm2PCRValue *pcr_values, size_t n_pcr_values);
|
||
|
+int tpm2_pcr_values_from_mask(uint32_t mask, TPMI_ALG_HASH hash, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
|
||
|
+int tpm2_pcr_values_to_mask(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPMI_ALG_HASH hash, uint32_t *ret_mask);
|
||
|
+int tpm2_pcr_values_from_string(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
|
||
|
+char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_values);
|
||
|
+int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, size_t *ret_count);
|
||
|
+int tpm2_tpml_pcr_selection_from_pcr_values(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPML_PCR_SELECTION *ret_selection, TPM2B_DIGEST **ret_values, size_t *ret_n_values);
|
||
|
+
|
||
|
int tpm2_create_primary(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_PUBLIC *template, const TPM2B_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, Tpm2Handle **ret_handle);
|
||
|
int tpm2_create(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private);
|
||
|
int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private, Tpm2Handle **ret_handle);
|
||
|
@@ -130,6 +150,9 @@ size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l);
|
||
|
#else /* HAVE_TPM2 */
|
||
|
typedef struct {} Tpm2Context;
|
||
|
typedef struct {} Tpm2Handle;
|
||
|
+typedef struct {} Tpm2PCRValue;
|
||
|
+
|
||
|
+#define TPM2_PCR_VALUE_MAKE(i, h, v) (Tpm2PCRValue) {}
|
||
|
#endif /* HAVE_TPM2 */
|
||
|
|
||
|
int tpm2_list_devices(void);
|