From 3fb5fadcb2c26e9e7da5de8f8bda0fa0e987c443 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Wed, 14 Jun 2023 13:17:21 -0400 Subject: [PATCH] tpm2: cache TPM algorithms Cache the supported algorithms when creating a new context. (cherry picked from commit cbc92a3172609238db572b86fa7da5e543e6a4dd) Related: RHEL-16182 --- src/shared/tpm2-util.c | 84 +++++++++++++++++++++++++++--------------- src/shared/tpm2-util.h | 4 +- src/test/test-tpm2.c | 10 ++--- 3 files changed, 62 insertions(+), 36 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index d38e260f9a..c05c636745 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -199,6 +199,44 @@ static int tpm2_cache_capabilities(Tpm2Context *c) { assert(c); + /* Cache the algorithms. The spec indicates supported algorithms can only be modified during runtime + * by the SetAlgorithmSet() command. Unfortunately, the spec doesn't require a TPM reinitialization + * after changing the algorithm set (unless the PCR algorithms are changed). However, the spec also + * indicates the TPM behavior after SetAlgorithmSet() is "vendor-dependent", giving the example of + * flushing sessions and objects, erasing policies, etc. So, if the algorithm set is programatically + * changed while we are performing some operation, it's reasonable to assume it will break us even if + * we don't cache the algorithms, thus they should be "safe" to cache. */ + TPM2_ALG_ID current_alg = TPM2_ALG_FIRST; + for (;;) { + r = tpm2_get_capability( + c, + TPM2_CAP_ALGS, + (uint32_t) current_alg, /* The spec states to cast TPM2_ALG_ID to uint32_t. */ + TPM2_MAX_CAP_ALGS, + &capability); + if (r < 0) + return r; + + TPML_ALG_PROPERTY algorithms = capability.algorithms; + + /* We should never get 0; the TPM must support some algorithms, and it must not set 'more' if + * there are no more. */ + assert(algorithms.count > 0); + + if (!GREEDY_REALLOC_APPEND( + c->capability_algorithms, + c->n_capability_algorithms, + algorithms.algProperties, + algorithms.count)) + return log_oom(); + + if (r == 0) + break; + + /* Set current_alg to alg id after last alg id the TPM provided */ + current_alg = algorithms.algProperties[algorithms.count - 1].alg + 1; + } + /* Cache the command capabilities. The spec isn't actually clear if commands can be added/removed * while running, but that would be crazy, so let's hope it is not possbile. */ TPM2_CC current_cc = TPM2_CC_FIRST; @@ -255,35 +293,26 @@ static int tpm2_cache_capabilities(Tpm2Context *c) { return 0; } -/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID. - * - * Returns 1 if the TPM supports the algorithm and the TPMA_ALGORITHM is provided, or 0 if the TPM does not - * support the algorithm, or < 0 for any errors. */ -static int tpm2_get_capability_alg(Tpm2Context *c, TPM2_ALG_ID alg, TPMA_ALGORITHM *ret) { - TPMU_CAPABILITIES capability; - int r; - +/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID. Returns true if the TPM supports the algorithm and the + * TPMA_ALGORITHM is provided, otherwise false. */ +static bool tpm2_get_capability_alg(Tpm2Context *c, TPM2_ALG_ID alg, TPMA_ALGORITHM *ret) { assert(c); - /* The spec explicitly states the TPM2_ALG_ID should be cast to uint32_t. */ - r = tpm2_get_capability(c, TPM2_CAP_ALGS, (uint32_t) alg, 1, &capability); - if (r < 0) - return r; - - TPML_ALG_PROPERTY algorithms = capability.algorithms; - if (algorithms.count == 0 || algorithms.algProperties[0].alg != alg) { - log_debug("TPM does not support alg 0x%02" PRIx16 ".", alg); - return 0; - } + FOREACH_ARRAY(alg_prop, c->capability_algorithms, c->n_capability_algorithms) + if (alg_prop->alg == alg) { + if (ret) + *ret = alg_prop->algProperties; + return true; + } + log_debug("TPM does not support alg 0x%02" PRIx16 ".", alg); if (ret) - *ret = algorithms.algProperties[0].algProperties; + *ret = 0; - return 1; + return false; } -/* Returns 1 if the TPM supports the alg, 0 if the TPM does not support the alg, or < 0 for any error. */ -int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) { +bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) { return tpm2_get_capability_alg(c, alg, NULL); } @@ -473,6 +502,7 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) { c->tcti_context = mfree(c->tcti_context); c->tcti_dl = safe_dlclose(c->tcti_dl); + c->capability_algorithms = mfree(c->capability_algorithms); c->capability_commands = mfree(c->capability_commands); return mfree(c); @@ -598,16 +628,10 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) { return r; /* We require AES and CFB support for session encryption. */ - r = tpm2_supports_alg(context, TPM2_ALG_AES); - if (r < 0) - return r; - if (r == 0) + if (!tpm2_supports_alg(context, TPM2_ALG_AES)) return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES."); - r = tpm2_supports_alg(context, TPM2_ALG_CFB); - if (r < 0) - return r; - if (r == 0) + if (!tpm2_supports_alg(context, TPM2_ALG_CFB)) return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support CFB."); if (!tpm2_supports_tpmt_sym_def(context, &SESSION_TEMPLATE_SYM_AES_128_CFB)) diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 1ca1a2e503..64a2fd3677 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -62,6 +62,8 @@ typedef struct { ESYS_CONTEXT *esys_context; /* Some selected cached capabilities of the TPM */ + TPMS_ALG_PROPERTY *capability_algorithms; + size_t n_capability_algorithms; TPMA_CC *capability_commands; size_t n_capability_commands; TPML_PCR_SELECTION capability_pcrs; @@ -86,7 +88,7 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle); Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle); DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free); -int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg); +bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg); bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command); bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms); diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index dfc8b98e08..8fd859b83d 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -651,13 +651,13 @@ TEST(tpm_required_tests) { assert_se(tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms)); /* Test invalid algs */ - assert_se(tpm2_supports_alg(c, TPM2_ALG_ERROR) == 0); - assert_se(tpm2_supports_alg(c, TPM2_ALG_LAST + 1) == 0); + assert_se(!tpm2_supports_alg(c, TPM2_ALG_ERROR)); + assert_se(!tpm2_supports_alg(c, TPM2_ALG_LAST + 1)); /* Test valid algs */ - assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA) == 1); - assert_se(tpm2_supports_alg(c, TPM2_ALG_AES) == 1); - assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB) == 1); + assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA)); + assert_se(tpm2_supports_alg(c, TPM2_ALG_AES)); + assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB)); /* Test invalid commands */ assert_se(!tpm2_supports_command(c, TPM2_CC_FIRST - 1));