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.
systemd/SOURCES/0546-tpm2-cache-TPM-algorit...

187 lines
8.1 KiB

From 3fb5fadcb2c26e9e7da5de8f8bda0fa0e987c443 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
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));