tpm2: tweaks to PCR mask parsing (#35835)

This commit is contained in:
Yu Watanabe
2025-01-04 12:42:54 +09:00
committed by GitHub
4 changed files with 42 additions and 32 deletions

4
TODO
View File

@@ -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.

View File

@@ -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

View File

@@ -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);

View File

@@ -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 = {