]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2: cache TPM algorithms
authorDan Streetman <ddstreet@ieee.org>
Wed, 14 Jun 2023 17:17:21 +0000 (13:17 -0400)
committerDan Streetman <ddstreet@ieee.org>
Wed, 5 Jul 2023 21:33:55 +0000 (17:33 -0400)
Cache the supported algorithms when creating a new context.

src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/test/test-tpm2.c

index cd71073a4de71442440b9288f50e3c07ab9bbf8f..284c0f0fbaf83a62dbfc8c4bfefc2562ea174feb 100644 (file)
@@ -204,6 +204,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;
@@ -260,35 +298,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);
 }
 
@@ -478,6 +507,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);
@@ -603,16 +633,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))
index 93b0943742c72cd6f53ae886c7cc9256c50293ae..c34239854bf6d6cba67c2470c8402ce2efa68a69 100644 (file)
@@ -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);
index 2bfe5677ab0a6d8ebe9fa0945c3b7d035e4c53c9..1220e9e3102dc962224a5982efe48faa4d873097 100644 (file)
@@ -699,13 +699,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));