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.
630 lines
25 KiB
630 lines
25 KiB
8 months ago
|
From 90d9f2996c10a2be090fd89be7409c81eabceb4c Mon Sep 17 00:00:00 2001
|
||
|
From: Dan Streetman <ddstreet@ieee.org>
|
||
|
Date: Mon, 6 Feb 2023 11:31:59 -0500
|
||
|
Subject: [PATCH] tpm2: add/rename functions to manage pcr selections
|
||
|
|
||
|
This renames some functions to match other to/from_string() naming,
|
||
|
and allows better management of TPML_PCR_SELECTION and TPMS_PCR_SELECTION
|
||
|
structs.
|
||
|
|
||
|
(cherry picked from commit c69bd0abdbd06ee89068227c67890358f5764c3d)
|
||
|
|
||
|
Related: RHEL-16182
|
||
|
---
|
||
|
src/boot/measure.c | 4 +-
|
||
|
.../cryptsetup-token-systemd-tpm2.c | 12 +-
|
||
|
src/shared/tpm2-util.c | 350 +++++++++++++++---
|
||
|
src/shared/tpm2-util.h | 31 +-
|
||
|
src/test/test-tpm2.c | 32 +-
|
||
|
5 files changed, 358 insertions(+), 71 deletions(-)
|
||
|
|
||
|
diff --git a/src/boot/measure.c b/src/boot/measure.c
|
||
|
index 65a48a01cd..86edf77c52 100644
|
||
|
--- a/src/boot/measure.c
|
||
|
+++ b/src/boot/measure.c
|
||
|
@@ -844,7 +844,9 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||
|
return log_error_errno(tpmalg, "Unsupported PCR bank");
|
||
|
|
||
|
TPML_PCR_SELECTION pcr_selection;
|
||
|
- tpm2_pcr_mask_to_selection(1 << TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, &pcr_selection);
|
||
|
+ tpm2_tpml_pcr_selection_from_mask(1 << TPM_PCR_INDEX_KERNEL_IMAGE,
|
||
|
+ tpmalg,
|
||
|
+ &pcr_selection);
|
||
|
|
||
|
rc = sym_Esys_PolicyPCR(
|
||
|
c->esys_context,
|
||
|
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
|
||
|
index e8bc091191..b5d66e389d 100644
|
||
|
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
|
||
|
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
|
||
|
@@ -205,13 +205,13 @@ _public_ void cryptsetup_token_dump(
|
||
|
if (r < 0)
|
||
|
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m");
|
||
|
|
||
|
- r = pcr_mask_to_string(hash_pcr_mask, &hash_pcrs_str);
|
||
|
- if (r < 0)
|
||
|
- return (void) crypt_log_debug_errno(cd, r, "Cannot format PCR hash mask: %m");
|
||
|
+ hash_pcrs_str = tpm2_pcr_mask_to_string(hash_pcr_mask);
|
||
|
+ if (!hash_pcrs_str)
|
||
|
+ return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
|
||
|
|
||
|
- r = pcr_mask_to_string(pubkey_pcr_mask, &pubkey_pcrs_str);
|
||
|
- if (r < 0)
|
||
|
- return (void) crypt_log_debug_errno(cd, r, "Cannot format PCR hash mask: %m");
|
||
|
+ pubkey_pcrs_str = tpm2_pcr_mask_to_string(pubkey_pcr_mask);
|
||
|
+ if (!pubkey_pcrs_str)
|
||
|
+ return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
|
||
|
|
||
|
r = crypt_dump_buffer_to_hex_string(blob, blob_size, &blob_str);
|
||
|
if (r < 0)
|
||
|
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
|
||
|
index 0cbb32f819..cf62524e34 100644
|
||
|
--- a/src/shared/tpm2-util.c
|
||
|
+++ b/src/shared/tpm2-util.c
|
||
|
@@ -458,24 +458,292 @@ static int tpm2_make_primary(
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret) {
|
||
|
+/* Utility functions for TPMS_PCR_SELECTION. */
|
||
|
+
|
||
|
+/* Convert a TPMS_PCR_SELECTION object to a mask. */
|
||
|
+void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret) {
|
||
|
+ assert(s);
|
||
|
+ assert(s->sizeofSelect <= sizeof(s->pcrSelect));
|
||
|
assert(ret);
|
||
|
|
||
|
- /* We only do 24bit here, as that's what PC TPMs are supposed to support */
|
||
|
- assert(TPM2_PCR_MASK_VALID(mask));
|
||
|
+ uint32_t mask = 0;
|
||
|
+ for (unsigned i = 0; i < s->sizeofSelect; i++)
|
||
|
+ SET_FLAG(mask, (uint32_t)s->pcrSelect[i] << (i * 8), true);
|
||
|
+ *ret = mask;
|
||
|
+}
|
||
|
|
||
|
- *ret = (TPML_PCR_SELECTION) {
|
||
|
- .count = 1,
|
||
|
- .pcrSelections[0] = {
|
||
|
- .hash = bank,
|
||
|
- .sizeofSelect = 3,
|
||
|
- .pcrSelect[0] = mask & 0xFF,
|
||
|
- .pcrSelect[1] = (mask >> 8) & 0xFF,
|
||
|
- .pcrSelect[2] = (mask >> 16) & 0xFF,
|
||
|
+/* Convert a mask and hash alg to a TPMS_PCR_SELECTION object. */
|
||
|
+void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TPMS_PCR_SELECTION *ret) {
|
||
|
+ assert(ret);
|
||
|
+
|
||
|
+ /* This is currently hardcoded at 24 PCRs, above. */
|
||
|
+ if (!TPM2_PCR_MASK_VALID(mask))
|
||
|
+ log_warning("PCR mask selections (%x) out of range, ignoring.",
|
||
|
+ mask & ~((uint32_t)TPM2_PCRS_MASK));
|
||
|
+
|
||
|
+ *ret = (TPMS_PCR_SELECTION){
|
||
|
+ .hash = hash_alg,
|
||
|
+ .sizeofSelect = TPM2_PCRS_MAX / 8,
|
||
|
+ .pcrSelect[0] = mask & 0xff,
|
||
|
+ .pcrSelect[1] = (mask >> 8) & 0xff,
|
||
|
+ .pcrSelect[2] = (mask >> 16) & 0xff,
|
||
|
+ };
|
||
|
+}
|
||
|
+
|
||
|
+/* Add all PCR selections in 'b' to 'a'. Both must have the same hash alg. */
|
||
|
+void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) {
|
||
|
+ assert(a);
|
||
|
+ assert(b);
|
||
|
+ assert(a->hash == b->hash);
|
||
|
+
|
||
|
+ uint32_t maska, maskb;
|
||
|
+ tpm2_tpms_pcr_selection_to_mask(a, &maska);
|
||
|
+ tpm2_tpms_pcr_selection_to_mask(b, &maskb);
|
||
|
+ tpm2_tpms_pcr_selection_from_mask(maska | maskb, a->hash, a);
|
||
|
+}
|
||
|
+
|
||
|
+/* Remove all PCR selections in 'b' from 'a'. Both must have the same hash alg. */
|
||
|
+void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) {
|
||
|
+ assert(a);
|
||
|
+ assert(b);
|
||
|
+ assert(a->hash == b->hash);
|
||
|
+
|
||
|
+ uint32_t maska, maskb;
|
||
|
+ tpm2_tpms_pcr_selection_to_mask(a, &maska);
|
||
|
+ tpm2_tpms_pcr_selection_to_mask(b, &maskb);
|
||
|
+ tpm2_tpms_pcr_selection_from_mask(maska & ~maskb, a->hash, a);
|
||
|
+}
|
||
|
+
|
||
|
+/* Move all PCR selections in 'b' to 'a'. Both must have the same hash alg. */
|
||
|
+void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b) {
|
||
|
+ if (a == b)
|
||
|
+ return;
|
||
|
+
|
||
|
+ tpm2_tpms_pcr_selection_add(a, b);
|
||
|
+ tpm2_tpms_pcr_selection_from_mask(0, b->hash, b);
|
||
|
+}
|
||
|
+
|
||
|
+#define FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms) \
|
||
|
+ _FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, UNIQ)
|
||
|
+#define _FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, uniq) \
|
||
|
+ FOREACH_PCR_IN_MASK(pcr, \
|
||
|
+ ({ uint32_t UNIQ_T(_mask, uniq); \
|
||
|
+ tpm2_tpms_pcr_selection_to_mask(tpms, &UNIQ_T(_mask, uniq)); \
|
||
|
+ UNIQ_T(_mask, uniq); \
|
||
|
+ }))
|
||
|
+
|
||
|
+#define FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \
|
||
|
+ UNIQ_FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml, UNIQ)
|
||
|
+#define UNIQ_FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml, uniq) \
|
||
|
+ for (TPML_PCR_SELECTION *UNIQ_T(_tpml, uniq) = (TPML_PCR_SELECTION*)(tpml); \
|
||
|
+ UNIQ_T(_tpml, uniq); UNIQ_T(_tpml, uniq) = NULL) \
|
||
|
+ _FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, UNIQ_T(_tpml, uniq))
|
||
|
+#define _FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \
|
||
|
+ for (TPMS_PCR_SELECTION *tpms = tpml->pcrSelections; \
|
||
|
+ (uint32_t)(tpms - tpml->pcrSelections) < tpml->count; \
|
||
|
+ tpms++)
|
||
|
+
|
||
|
+#define FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, tpms, tpml) \
|
||
|
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \
|
||
|
+ FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms)
|
||
|
+
|
||
|
+char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s) {
|
||
|
+ assert(s);
|
||
|
+
|
||
|
+ const char *algstr = strna(tpm2_hash_alg_to_string(s->hash));
|
||
|
+
|
||
|
+ uint32_t mask;
|
||
|
+ tpm2_tpms_pcr_selection_to_mask(s, &mask);
|
||
|
+ _cleanup_free_ char *maskstr = tpm2_pcr_mask_to_string(mask);
|
||
|
+ if (!maskstr)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ return strjoin(algstr, "(", maskstr, ")");
|
||
|
+}
|
||
|
+
|
||
|
+size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s) {
|
||
|
+ assert(s);
|
||
|
+
|
||
|
+ uint32_t mask;
|
||
|
+ tpm2_tpms_pcr_selection_to_mask(s, &mask);
|
||
|
+ return (size_t)__builtin_popcount(mask);
|
||
|
+}
|
||
|
+
|
||
|
+/* Utility functions for TPML_PCR_SELECTION. */
|
||
|
+
|
||
|
+/* Remove the (0-based) index entry from 'l', shift all following entries, and update the count. */
|
||
|
+static void tpm2_tpml_pcr_selection_remove_index(TPML_PCR_SELECTION *l, uint32_t index) {
|
||
|
+ assert(l);
|
||
|
+ assert(l->count <= sizeof(l->pcrSelections));
|
||
|
+ assert(index < l->count);
|
||
|
+
|
||
|
+ size_t s = l->count - (index + 1);
|
||
|
+ memmove(&l->pcrSelections[index], &l->pcrSelections[index + 1], s * sizeof(l->pcrSelections[0]));
|
||
|
+ l->count--;
|
||
|
+}
|
||
|
+
|
||
|
+/* Get a TPMS_PCR_SELECTION from a TPML_PCR_SELECTION for the given hash alg. Returns NULL if there is no
|
||
|
+ * entry for the hash alg. This guarantees the returned entry contains all the PCR selections for the given
|
||
|
+ * hash alg, which may require modifying the TPML_PCR_SELECTION by removing duplicate entries. */
|
||
|
+static TPMS_PCR_SELECTION *tpm2_tpml_pcr_selection_get_tpms_pcr_selection(
|
||
|
+ TPML_PCR_SELECTION *l,
|
||
|
+ TPMI_ALG_HASH hash_alg) {
|
||
|
+
|
||
|
+ assert(l);
|
||
|
+
|
||
|
+ TPMS_PCR_SELECTION *selection = NULL;
|
||
|
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, l)
|
||
|
+ if (s->hash == hash_alg) {
|
||
|
+ selection = s;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!selection)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ /* Iterate backwards through the entries, removing any other entries for the hash alg. */
|
||
|
+ for (uint32_t i = l->count - 1; i > 0; i--) {
|
||
|
+ TPMS_PCR_SELECTION *s = &l->pcrSelections[i];
|
||
|
+
|
||
|
+ if (selection == s)
|
||
|
+ break;
|
||
|
+
|
||
|
+ if (s->hash == hash_alg) {
|
||
|
+ tpm2_tpms_pcr_selection_move(selection, s);
|
||
|
+ tpm2_tpml_pcr_selection_remove_index(l, i);
|
||
|
}
|
||
|
+ }
|
||
|
+
|
||
|
+ return selection;
|
||
|
+}
|
||
|
+
|
||
|
+/* Convert a TPML_PCR_SELECTION object to a mask. Returns -ENOENT if 'hash_alg' is not in the object. */
|
||
|
+int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash_alg, uint32_t *ret) {
|
||
|
+ assert(l);
|
||
|
+ assert(ret);
|
||
|
+
|
||
|
+ /* Make a copy, as tpm2_tpml_pcr_selection_get_tpms_pcr_selection() will modify the object if there
|
||
|
+ * are multiple entries with the requested hash alg. */
|
||
|
+ TPML_PCR_SELECTION lcopy = *l;
|
||
|
+
|
||
|
+ TPMS_PCR_SELECTION *s;
|
||
|
+ s = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(&lcopy, hash_alg);
|
||
|
+ if (!s)
|
||
|
+ return SYNTHETIC_ERRNO(ENOENT);
|
||
|
+
|
||
|
+ tpm2_tpms_pcr_selection_to_mask(s, ret);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* Convert a mask and hash alg to a TPML_PCR_SELECTION object. */
|
||
|
+void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TPML_PCR_SELECTION *ret) {
|
||
|
+ assert(ret);
|
||
|
+
|
||
|
+ TPMS_PCR_SELECTION s;
|
||
|
+ tpm2_tpms_pcr_selection_from_mask(mask, hash_alg, &s);
|
||
|
+
|
||
|
+ *ret = (TPML_PCR_SELECTION){
|
||
|
+ .count = 1,
|
||
|
+ .pcrSelections[0] = s,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
+/* Combine all duplicate (same hash alg) TPMS_PCR_SELECTION entries in 'l'. */
|
||
|
+static void tpm2_tpml_pcr_selection_cleanup(TPML_PCR_SELECTION *l) {
|
||
|
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, l)
|
||
|
+ /* This removes all duplicates for s->hash. */
|
||
|
+ (void) tpm2_tpml_pcr_selection_get_tpms_pcr_selection(l, s->hash);
|
||
|
+}
|
||
|
+
|
||
|
+/* Add the PCR selections in 's' to the corresponding hash alg TPMS_PCR_SELECTION entry in 'l'. Adds a new
|
||
|
+ * TPMS_PCR_SELECTION entry for the hash alg if needed. This may modify the TPML_PCR_SELECTION by combining
|
||
|
+ * entries with the same hash alg. */
|
||
|
+void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s) {
|
||
|
+ assert(l);
|
||
|
+ assert(s);
|
||
|
+
|
||
|
+ if (tpm2_tpms_pcr_selection_is_empty(s))
|
||
|
+ return;
|
||
|
+
|
||
|
+ TPMS_PCR_SELECTION *selection = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(l, s->hash);
|
||
|
+ if (selection) {
|
||
|
+ tpm2_tpms_pcr_selection_add(selection, s);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* It's already broken if the count is higher than the array has size for. */
|
||
|
+ assert(!(l->count > sizeof(l->pcrSelections)));
|
||
|
+
|
||
|
+ /* If full, the cleanup should result in at least one available entry. */
|
||
|
+ if (l->count == sizeof(l->pcrSelections))
|
||
|
+ tpm2_tpml_pcr_selection_cleanup(l);
|
||
|
+
|
||
|
+ assert(l->count < sizeof(l->pcrSelections));
|
||
|
+ l->pcrSelections[l->count++] = *s;
|
||
|
+}
|
||
|
+
|
||
|
+/* Remove the PCR selections in 's' from the corresponding hash alg TPMS_PCR_SELECTION entry in 'l'. This
|
||
|
+ * will combine all entries for 's->hash' in 'l'. */
|
||
|
+void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s) {
|
||
|
+ assert(l);
|
||
|
+ assert(s);
|
||
|
+
|
||
|
+ if (tpm2_tpms_pcr_selection_is_empty(s))
|
||
|
+ return;
|
||
|
+
|
||
|
+ TPMS_PCR_SELECTION *selection = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(l, s->hash);
|
||
|
+ if (selection)
|
||
|
+ tpm2_tpms_pcr_selection_sub(selection, s);
|
||
|
+}
|
||
|
+
|
||
|
+/* Add all PCR selections in 'b' to 'a'. */
|
||
|
+void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b) {
|
||
|
+ assert(a);
|
||
|
+ assert(b);
|
||
|
+
|
||
|
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection_b, (TPML_PCR_SELECTION*) b)
|
||
|
+ tpm2_tpml_pcr_selection_add_tpms_pcr_selection(a, selection_b);
|
||
|
+}
|
||
|
+
|
||
|
+/* Remove all PCR selections in 'b' from 'a'. */
|
||
|
+void tpm2_tpml_pcr_selection_sub(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b) {
|
||
|
+ assert(a);
|
||
|
+ assert(b);
|
||
|
+
|
||
|
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection_b, (TPML_PCR_SELECTION*) b)
|
||
|
+ tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(a, selection_b);
|
||
|
+}
|
||
|
+
|
||
|
+char *tpm2_tpml_pcr_selection_to_string(const TPML_PCR_SELECTION *l) {
|
||
|
+ assert(l);
|
||
|
+
|
||
|
+ _cleanup_free_ char *banks = NULL;
|
||
|
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, (TPML_PCR_SELECTION*) l) {
|
||
|
+ if (tpm2_tpms_pcr_selection_is_empty(s))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ _cleanup_free_ char *str = tpm2_tpms_pcr_selection_to_string(s);
|
||
|
+ if (!str || !strextend_with_separator(&banks, ",", str))
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ return strjoin("[", strempty(banks), "]");
|
||
|
+}
|
||
|
+
|
||
|
+size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l) {
|
||
|
+ assert(l);
|
||
|
+ assert(l->count <= sizeof(l->pcrSelections));
|
||
|
+
|
||
|
+ size_t weight = 0;
|
||
|
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, l) {
|
||
|
+ size_t w = tpm2_tpms_pcr_selection_weight(s);
|
||
|
+ assert(weight <= SIZE_MAX - w);
|
||
|
+ weight += w;
|
||
|
+ }
|
||
|
+
|
||
|
+ return weight;
|
||
|
+}
|
||
|
+
|
||
|
static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
|
||
|
if (!DEBUG_LOGGING || !buffer || size == 0)
|
||
|
return;
|
||
|
@@ -562,7 +830,7 @@ static int tpm2_pcr_mask_good(
|
||
|
* actually measure into them, or only into a suboptimal bank. If so, the PCRs should be all zero or
|
||
|
* all 0xFF. Detect that, so that we can warn and maybe pick a better bank. */
|
||
|
|
||
|
- tpm2_pcr_mask_to_selection(mask, bank, &selection);
|
||
|
+ tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection);
|
||
|
|
||
|
rc = sym_Esys_PCR_Read(
|
||
|
c->esys_context,
|
||
|
@@ -1269,7 +1537,7 @@ static int tpm2_make_policy_session(
|
||
|
|
||
|
/* Put together the PCR policy we want to use */
|
||
|
TPML_PCR_SELECTION pcr_selection;
|
||
|
- tpm2_pcr_mask_to_selection(pubkey_pcr_mask, pcr_bank, &pcr_selection);
|
||
|
+ tpm2_tpml_pcr_selection_from_mask(pubkey_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
|
||
|
rc = sym_Esys_PolicyPCR(
|
||
|
c->esys_context,
|
||
|
session->esys_handle,
|
||
|
@@ -1372,7 +1640,7 @@ static int tpm2_make_policy_session(
|
||
|
log_debug("Configuring hash-based PCR policy.");
|
||
|
|
||
|
TPML_PCR_SELECTION pcr_selection;
|
||
|
- tpm2_pcr_mask_to_selection(hash_pcr_mask, pcr_bank, &pcr_selection);
|
||
|
+ tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
|
||
|
rc = sym_Esys_PolicyPCR(
|
||
|
c->esys_context,
|
||
|
session->esys_handle,
|
||
|
@@ -1989,13 +2257,28 @@ int tpm2_extend_bytes(
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
-int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
|
||
|
- const char *p = ASSERT_PTR(s);
|
||
|
+char *tpm2_pcr_mask_to_string(uint32_t mask) {
|
||
|
+ _cleanup_free_ char *s = NULL;
|
||
|
+
|
||
|
+ FOREACH_PCR_IN_MASK(n, mask)
|
||
|
+ if (strextendf_with_separator(&s, "+", "%d", n) < 0)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if (!s)
|
||
|
+ return strdup("");
|
||
|
+
|
||
|
+ return TAKE_PTR(s);
|
||
|
+}
|
||
|
+
|
||
|
+int tpm2_pcr_mask_from_string(const char *arg, uint32_t *ret_mask) {
|
||
|
uint32_t mask = 0;
|
||
|
int r;
|
||
|
|
||
|
- if (isempty(s)) {
|
||
|
- *ret = 0;
|
||
|
+ assert(arg);
|
||
|
+ assert(ret_mask);
|
||
|
+
|
||
|
+ if (isempty(arg)) {
|
||
|
+ *ret_mask = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -2004,6 +2287,7 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
|
||
|
* /etc/crypttab the "," is already used to separate options, hence a different separator is nice to
|
||
|
* avoid escaping. */
|
||
|
|
||
|
+ const char *p = arg;
|
||
|
for (;;) {
|
||
|
_cleanup_free_ char *pcr = NULL;
|
||
|
unsigned n;
|
||
|
@@ -2012,19 +2296,20 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
|
||
|
if (r == 0)
|
||
|
break;
|
||
|
if (r < 0)
|
||
|
- return log_error_errno(r, "Failed to parse PCR list: %s", s);
|
||
|
+ return log_error_errno(r, "Failed to parse PCR list: %s", arg);
|
||
|
|
||
|
r = safe_atou(pcr, &n);
|
||
|
if (r < 0)
|
||
|
return log_error_errno(r, "Failed to parse PCR number: %s", pcr);
|
||
|
if (n >= TPM2_PCRS_MAX)
|
||
|
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
|
||
|
- "PCR number out of range (valid range 0…23): %u", n);
|
||
|
+ "PCR number out of range (valid range 0…%u): %u",
|
||
|
+ TPM2_PCRS_MAX - 1, n);
|
||
|
|
||
|
- mask |= UINT32_C(1) << n;
|
||
|
+ SET_BIT(mask, n);;
|
||
|
}
|
||
|
|
||
|
- *ret = mask;
|
||
|
+ *ret_mask = mask;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -2389,7 +2674,7 @@ int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
- r = tpm2_parse_pcrs(arg, &m);
|
||
|
+ r = tpm2_pcr_mask_from_string(arg, &m);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
@@ -2445,25 +2730,6 @@ int tpm2_load_pcr_public_key(const char *path, void **ret_pubkey, size_t *ret_pu
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-int pcr_mask_to_string(uint32_t mask, char **ret) {
|
||
|
- _cleanup_free_ char *buf = NULL;
|
||
|
- int r;
|
||
|
-
|
||
|
- assert(ret);
|
||
|
-
|
||
|
- for (unsigned i = 0; i < TPM2_PCRS_MAX; i++) {
|
||
|
- if (!(mask & (UINT32_C(1) << i)))
|
||
|
- continue;
|
||
|
-
|
||
|
- r = strextendf_with_separator(&buf, "+", "%u", i);
|
||
|
- if (r < 0)
|
||
|
- return r;
|
||
|
- }
|
||
|
-
|
||
|
- *ret = TAKE_PTR(buf);
|
||
|
- return 0;
|
||
|
-}
|
||
|
-
|
||
|
#define PBKDF2_HMAC_SHA256_ITERATIONS 10000
|
||
|
|
||
|
/*
|
||
|
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
|
||
|
index 07a8a89800..c2532c61c2 100644
|
||
|
--- a/src/shared/tpm2-util.h
|
||
|
+++ b/src/shared/tpm2-util.h
|
||
|
@@ -3,6 +3,7 @@
|
||
|
|
||
|
#include <stdbool.h>
|
||
|
|
||
|
+#include "bitfield.h"
|
||
|
#include "json.h"
|
||
|
#include "macro.h"
|
||
|
#include "sha256.h"
|
||
|
@@ -23,6 +24,8 @@ static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
|
||
|
return pcr_mask <= TPM2_PCRS_MASK;
|
||
|
}
|
||
|
|
||
|
+#define FOREACH_PCR_IN_MASK(pcr, mask) BIT_FOREACH(pcr, mask)
|
||
|
+
|
||
|
#if HAVE_TPM2
|
||
|
|
||
|
#include <tss2/tss2_esys.h>
|
||
|
@@ -92,8 +95,6 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
|
||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
|
||
|
#define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep)
|
||
|
|
||
|
-void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret);
|
||
|
-
|
||
|
static inline void Esys_Freep(void *p) {
|
||
|
if (*(void**) p)
|
||
|
sym_Esys_Free(*(void**) p);
|
||
|
@@ -104,6 +105,25 @@ int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret)
|
||
|
|
||
|
int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size);
|
||
|
|
||
|
+void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret);
|
||
|
+void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret);
|
||
|
+void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
|
||
|
+void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
|
||
|
+void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b);
|
||
|
+char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s);
|
||
|
+size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s);
|
||
|
+#define tpm2_tpms_pcr_selection_is_empty(s) (tpm2_tpms_pcr_selection_weight(s) == 0)
|
||
|
+
|
||
|
+int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t *ret);
|
||
|
+void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPML_PCR_SELECTION *ret);
|
||
|
+void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
|
||
|
+void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
|
||
|
+void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b);
|
||
|
+void tpm2_tpml_pcr_selection_sub(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b);
|
||
|
+char *tpm2_tpml_pcr_selection_to_string(const TPML_PCR_SELECTION *l);
|
||
|
+size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l);
|
||
|
+#define tpm2_tpml_pcr_selection_is_empty(l) (tpm2_tpml_pcr_selection_weight(l) == 0)
|
||
|
+
|
||
|
#else /* HAVE_TPM2 */
|
||
|
typedef struct {} Tpm2Context;
|
||
|
typedef struct {} Tpm2Handle;
|
||
|
@@ -112,8 +132,6 @@ typedef struct {} Tpm2Handle;
|
||
|
int tpm2_list_devices(void);
|
||
|
int tpm2_find_device_auto(int log_level, char **ret);
|
||
|
|
||
|
-int tpm2_parse_pcrs(const char *s, uint32_t *ret);
|
||
|
-
|
||
|
int tpm2_make_pcr_json_array(uint32_t pcr_mask, JsonVariant **ret);
|
||
|
int tpm2_parse_pcr_json_array(JsonVariant *v, uint32_t *ret);
|
||
|
|
||
|
@@ -155,6 +173,9 @@ int tpm2_hash_alg_from_string(const char *alg);
|
||
|
const char *tpm2_asym_alg_to_string(uint16_t alg);
|
||
|
int tpm2_asym_alg_from_string(const char *alg);
|
||
|
|
||
|
+char *tpm2_pcr_mask_to_string(uint32_t mask);
|
||
|
+int tpm2_pcr_mask_from_string(const char *arg, uint32_t *mask);
|
||
|
+
|
||
|
typedef struct {
|
||
|
uint32_t search_pcr_mask;
|
||
|
const char *device;
|
||
|
@@ -179,8 +200,6 @@ int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask);
|
||
|
int tpm2_load_pcr_signature(const char *path, JsonVariant **ret);
|
||
|
int tpm2_load_pcr_public_key(const char *path, void **ret_pubkey, size_t *ret_pubkey_size);
|
||
|
|
||
|
-int pcr_mask_to_string(uint32_t mask, char **ret);
|
||
|
-
|
||
|
int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
|
||
|
size_t passlen,
|
||
|
const void *salt,
|
||
|
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
|
||
|
index 04e08490b3..23277449b5 100644
|
||
|
--- a/src/test/test-tpm2.c
|
||
|
+++ b/src/test/test-tpm2.c
|
||
|
@@ -3,29 +3,29 @@
|
||
|
#include "tpm2-util.h"
|
||
|
#include "tests.h"
|
||
|
|
||
|
-static void test_tpm2_parse_pcrs_one(const char *s, uint32_t mask, int ret) {
|
||
|
+static void test_tpm2_pcr_mask_from_string_one(const char *s, uint32_t mask, int ret) {
|
||
|
uint32_t m;
|
||
|
|
||
|
- assert_se(tpm2_parse_pcrs(s, &m) == ret);
|
||
|
+ assert_se(tpm2_pcr_mask_from_string(s, &m) == ret);
|
||
|
|
||
|
if (ret >= 0)
|
||
|
assert_se(m == mask);
|
||
|
}
|
||
|
|
||
|
-TEST(tpm2_parse_pcrs) {
|
||
|
- test_tpm2_parse_pcrs_one("", 0, 0);
|
||
|
- test_tpm2_parse_pcrs_one("0", 1, 0);
|
||
|
- test_tpm2_parse_pcrs_one("1", 2, 0);
|
||
|
- test_tpm2_parse_pcrs_one("0,1", 3, 0);
|
||
|
- test_tpm2_parse_pcrs_one("0+1", 3, 0);
|
||
|
- test_tpm2_parse_pcrs_one("0-1", 0, -EINVAL);
|
||
|
- test_tpm2_parse_pcrs_one("0,1,2", 7, 0);
|
||
|
- test_tpm2_parse_pcrs_one("0+1+2", 7, 0);
|
||
|
- test_tpm2_parse_pcrs_one("0+1,2", 7, 0);
|
||
|
- test_tpm2_parse_pcrs_one("0,1+2", 7, 0);
|
||
|
- test_tpm2_parse_pcrs_one("0,2", 5, 0);
|
||
|
- test_tpm2_parse_pcrs_one("0+2", 5, 0);
|
||
|
- test_tpm2_parse_pcrs_one("foo", 0, -EINVAL);
|
||
|
+TEST(tpm2_mask_from_string) {
|
||
|
+ test_tpm2_pcr_mask_from_string_one("", 0, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0", 1, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("1", 2, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0,1", 3, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0+1", 3, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0-1", 0, -EINVAL);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0,1,2", 7, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0+1+2", 7, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0+1,2", 7, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0,1+2", 7, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0,2", 5, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("0+2", 5, 0);
|
||
|
+ test_tpm2_pcr_mask_from_string_one("foo", 0, -EINVAL);
|
||
|
}
|
||
|
|
||
|
TEST(tpm2_util_pbkdf2_hmac_sha256) {
|