diff --git a/TODO b/TODO index 60a190427d..5cefbc48aa 100644 --- a/TODO +++ b/TODO @@ -328,10 +328,6 @@ Features: probably should measure the dm-verity root hash from the kernel side, but DDI meta info from userspace. -* rework tpm2_parse_pcr_argument_to_mask() to refuse literal hash value - specifications. They are currently parsed but ignored. We should refuse them - however, to not confuse people. - * use name_to_handle_at() with AT_HANDLE_FID instead of .st_ino (inode number) for identifying inodes, for example in copy.c when finding hard links, or loop-util.c for tracking backing files, and other places. diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 890d77dcc2..29d9dbbe2e 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -1845,8 +1845,11 @@ int tpm2_pcr_values_from_mask(uint32_t mask, TPMI_ALG_HASH hash, Tpm2PCRValue ** 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; +int tpm2_pcr_values_to_mask( + const Tpm2PCRValue *pcr_values, + size_t n_pcr_values, + TPMI_ALG_HASH hash, + uint32_t *ret_mask) { assert(pcr_values || n_pcr_values == 0); assert(ret_mask); @@ -1854,12 +1857,12 @@ int tpm2_pcr_values_to_mask(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, if (!tpm2_pcr_values_valid(pcr_values, n_pcr_values)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PCR values."); + uint32_t mask = 0; FOREACH_ARRAY(v, pcr_values, n_pcr_values) - if (v->hash == hash) + if (hash == 0 || v->hash == hash) SET_BIT(mask, v->index); *ret_mask = mask; - return 0; } @@ -8087,46 +8090,48 @@ int tpm2_parse_pcr_argument_append(const char *arg, Tpm2PCRValue **pcr_values, s #endif } -/* Same as tpm2_parse_pcr_argument() but converts the pcr values to a pcr mask. If more than one hash - * algorithm is included in the pcr values array this results in error. This retains the previous behavior of - * tpm2_parse_pcr_argument() of clearing the mask if 'arg' is empty, replacing the mask if it is set to - * UINT32_MAX, and or-ing the mask otherwise. */ -int tpm2_parse_pcr_argument_to_mask(const char *arg, uint32_t *ret_mask) { +int tpm2_parse_pcr_argument_to_mask(const char *arg, uint32_t *mask) { #if HAVE_TPM2 - _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; - size_t n_pcr_values; int r; - assert(arg); - assert(ret_mask); + /* Same as tpm2_parse_pcr_argument() but converts the pcr values to a pcr mask. If a hash algorithm or + * hash value is specified an error is generated (after all we only return the mask here, nothing + * else). This retains the previous behavior of tpm2_parse_pcr_argument() of clearing the mask if + * 'arg' is empty, replacing the mask if it is set to UINT32_MAX, and or-ing the mask otherwise. */ + assert(arg); + assert(mask); + + _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; + size_t n_pcr_values; r = tpm2_parse_pcr_argument(arg, &pcr_values, &n_pcr_values); if (r < 0) return r; if (n_pcr_values == 0) { /* This retains the previous behavior of clearing the mask if the arg is empty */ - *ret_mask = 0; + *mask = 0; return 0; } - size_t hash_count; - r = tpm2_pcr_values_hash_count(pcr_values, n_pcr_values, &hash_count); - if (r < 0) - return log_error_errno(r, "Could not get hash count from pcr values: %m"); - - if (hash_count > 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR hash banks selected."); + FOREACH_ARRAY(v, pcr_values, n_pcr_values) { + if (v->hash != 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Not expecting hash algorithm specification in PCR mask value, refusing: %s", arg); + if (v->value.size != 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Not expecting hash value specification in PCR mask value, refusing: %s", arg); + } uint32_t new_mask; - r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, pcr_values[0].hash, &new_mask); + r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, /* algorithm= */ 0, &new_mask); if (r < 0) return log_error_errno(r, "Could not get pcr values mask: %m"); - if (*ret_mask == UINT32_MAX) - *ret_mask = new_mask; + if (*mask == UINT32_MAX) + *mask = new_mask; else - *ret_mask |= new_mask; + *mask |= new_mask; return 0; #else diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 76b4dd3cc1..4a44c43f0f 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -479,7 +479,7 @@ static inline bool tpm2_is_fully_supported(void) { int verb_has_tpm2_generic(bool quiet); int tpm2_parse_pcr_argument(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values); -int tpm2_parse_pcr_argument_append(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values); +int tpm2_parse_pcr_argument_append(const char *arg, Tpm2PCRValue **pcr_values, size_t *n_pcr_values); int tpm2_parse_pcr_argument_to_mask(const char *arg, uint32_t *mask); int tpm2_load_pcr_signature(const char *path, sd_json_variant **ret); diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index e4f1f861b4..31ef6192b2 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -556,8 +556,13 @@ static void check_parse_pcr_argument( assert_se(tpm2_pcr_values_to_mask(expected_values, n_expected_values, expected_values[0].hash, &expected_mask) == 0); - assert_se(tpm2_parse_pcr_argument_to_mask(arg, &mask) == 0); - assert_se(mask == expected_mask); + _cleanup_free_ Tpm2PCRValue *arg_pcr_values = NULL; + size_t n_arg_pcr_values = 0; + assert_se(tpm2_parse_pcr_argument(arg, &arg_pcr_values, &n_arg_pcr_values) >= 0); + uint32_t mask2 = UINT32_MAX; + assert_se(tpm2_pcr_values_to_mask(arg_pcr_values, n_arg_pcr_values, /* algorithm= */ 0, &mask2) >= 0); + + assert_se((mask == UINT32_MAX ? mask2 : (mask|mask2)) == expected_mask); } size_t old_n_values = n_values; @@ -701,6 +706,10 @@ TEST(parse_pcr_argument) { check_parse_pcr_argument_to_mask("sysexts+17+23", 0x822000); check_parse_pcr_argument_to_mask("6+boot-loader-code,44", -EINVAL); check_parse_pcr_argument_to_mask("debug+24", -EINVAL); + check_parse_pcr_argument_to_mask("5:sha1=f013d66c7f6817d08b7eb2a93e6d0440c1f3e7f8", -EINVAL); + check_parse_pcr_argument_to_mask("0:sha256=f013d66c7f6817d08b7eb2a93e6d0440c1f3e7f8", -EINVAL); + check_parse_pcr_argument_to_mask("5:sha1=f013d66c7f6817d08b7eb2a93e6d0440c1f3e7f8,3", -EINVAL); + check_parse_pcr_argument_to_mask("3,0:sha256=f013d66c7f6817d08b7eb2a93e6d0440c1f3e7f8", -EINVAL); } static const TPMT_PUBLIC test_rsa_template = {