systemd/0532-tpm2-verify-symmetric-...

273 lines
11 KiB
Diff

From 2c78f1e768c75bbea7076fa9242ba484c8f472b5 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 17 Feb 2023 12:59:18 -0500
Subject: [PATCH] tpm2: verify symmetric parms in tpm2_context_new()
This adds tpm2_get_capability_algs(), tpm2_supports_alg(), and
tpm2_test_parms(). These functions allow verifying that the TPM supports
specific algs and parameters.
When creating a new context, this checks if the TPM supports the symmetric algs
we use. If the TPM does not support the symmetric algs and parameters we
require, we log and return error.
(cherry picked from commit a47060bb34c912ea9909fcf617f7b553488b5daf)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 119 ++++++++++++++++++++++++++++++++++++-----
src/shared/tpm2-util.h | 4 ++
src/test/test-tpm2.c | 38 +++++++++++++
3 files changed, 149 insertions(+), 12 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 460ebe62c7..91f66aaaf4 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -49,6 +49,7 @@ static TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySe
static TSS2_RC (*sym_Esys_ReadPublic)(ESYS_CONTEXT *esysContext, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_PUBLIC **outPublic, TPM2B_NAME **name, TPM2B_NAME **qualifiedName) = NULL;
static TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
static TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
+static TSS2_RC (*sym_Esys_TestParms)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPMT_PUBLIC_PARMS *parameters) = NULL;
static TSS2_RC (*sym_Esys_TR_Deserialize)(ESYS_CONTEXT *esys_context, uint8_t const *buffer, size_t buffer_size, ESYS_TR *esys_handle) = NULL;
static TSS2_RC (*sym_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext, TPM2_HANDLE tpm_handle, ESYS_TR optionalSession1, ESYS_TR optionalSession2, ESYS_TR optionalSession3, ESYS_TR *object) = NULL;
static TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name) = NULL;
@@ -95,6 +96,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_ReadPublic),
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
+ DLSYM_ARG(Esys_TestParms),
DLSYM_ARG(Esys_TR_Deserialize),
DLSYM_ARG(Esys_TR_FromTPMPublic),
DLSYM_ARG(Esys_TR_GetName),
@@ -218,6 +220,87 @@ static int tpm2_cache_capabilities(Tpm2Context *c) {
#define tpm2_capability_pcrs(c) ((c)->capability_pcrs)
+/* 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;
+
+ 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;
+ }
+
+ if (ret)
+ *ret = algorithms.algProperties[0].algProperties;
+
+ return 1;
+}
+
+/* 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) {
+ return tpm2_get_capability_alg(c, alg, NULL);
+}
+
+/* Returns 1 if the TPM supports the parms, or 0 if the TPM does not support the parms. */
+bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms) {
+ TSS2_RC rc;
+
+ assert(c);
+ assert(parms);
+
+ TPMT_PUBLIC_PARMS parameters = {
+ .type = alg,
+ .parameters = *parms,
+ };
+
+ rc = sym_Esys_TestParms(c->esys_context, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &parameters);
+ if (rc != TSS2_RC_SUCCESS)
+ /* The spec says if the parms are not supported the TPM returns "...the appropriate
+ * unmarshaling error if a parameter is not valid". Since the spec (currently) defines 15
+ * unmarshaling errors, instead of checking for them all here, let's just assume any error
+ * indicates unsupported parms, and log the specific error text. */
+ log_debug("TPM does not support tested parms: %s", sym_Tss2_RC_Decode(rc));
+
+ return rc == TSS2_RC_SUCCESS;
+}
+
+static inline bool tpm2_supports_tpmt_sym_def_object(Tpm2Context *c, const TPMT_SYM_DEF_OBJECT *parameters) {
+ assert(c);
+ assert(parameters);
+
+ TPMU_PUBLIC_PARMS parms = {
+ .symDetail.sym = *parameters,
+ };
+
+ return tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms);
+}
+
+static inline bool tpm2_supports_tpmt_sym_def(Tpm2Context *c, const TPMT_SYM_DEF *parameters) {
+ assert(c);
+ assert(parameters);
+
+ /* Unfortunately, TPMT_SYM_DEF and TPMT_SYM_DEF_OBEJECT are separately defined, even though they are
+ * functionally identical. */
+ TPMT_SYM_DEF_OBJECT object = {
+ .algorithm = parameters->algorithm,
+ .keyBits = parameters->keyBits,
+ .mode = parameters->mode,
+ };
+
+ return tpm2_supports_tpmt_sym_def_object(c, &object);
+}
+
static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
if (!c)
return NULL;
@@ -233,6 +316,12 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
DEFINE_TRIVIAL_REF_UNREF_FUNC(Tpm2Context, tpm2_context, tpm2_context_free);
+static const TPMT_SYM_DEF SESSION_TEMPLATE_SYM_AES_128_CFB = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB, /* The spec requires sessions to use CFB. */
+};
+
int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
_cleanup_tpm2_context_ Tpm2Context *context = NULL;
TSS2_RC rc;
@@ -342,6 +431,22 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
if (r < 0)
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)
+ 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)
+ 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))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES-128-CFB.");
+
*ret_context = TAKE_PTR(context);
return 0;
@@ -1570,11 +1675,6 @@ static int tpm2_make_encryption_session(
const Tpm2Handle *bind_key,
Tpm2Handle **ret_session) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
TPMA_SESSION_CONTINUESESSION;
TSS2_RC rc;
@@ -1602,7 +1702,7 @@ static int tpm2_make_encryption_session(
ESYS_TR_NONE,
NULL,
TPM2_SE_HMAC,
- &symmetric,
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
TPM2_ALG_SHA256,
&session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
@@ -1631,11 +1731,6 @@ static int tpm2_make_policy_session(
bool trial,
Tpm2Handle **ret_session) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
TPM2_SE session_type = trial ? TPM2_SE_TRIAL : TPM2_SE_POLICY;
TSS2_RC rc;
int r;
@@ -1665,7 +1760,7 @@ static int tpm2_make_policy_session(
ESYS_TR_NONE,
NULL,
session_type,
- &symmetric,
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
TPM2_ALG_SHA256,
&session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 5e5d9e2604..764104ed58 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -92,6 +92,10 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
#define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep)
+int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
+
+bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms);
+
int tpm2_get_good_pcr_banks(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 0b123c25a7..130a968273 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -713,6 +713,44 @@ TEST(calculate_policy_pcr) {
assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
}
+TEST(tpm_required_tests) {
+ int r;
+
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(NULL, &c);
+ if (r < 0) {
+ log_tests_skipped("Could not find TPM");
+ return;
+ }
+
+ TPMU_PUBLIC_PARMS parms = {
+ .symDetail.sym = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ },
+ };
+
+ /* Test with invalid parms */
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_CFB, &parms));
+
+ TPMU_PUBLIC_PARMS invalid_parms = parms;
+ invalid_parms.symDetail.sym.keyBits.aes = 1;
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &invalid_parms));
+
+ /* Test with valid parms */
+ 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);
+
+ /* 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);
+}
+
#endif /* HAVE_TPM2 */
DEFINE_TEST_MAIN(LOG_DEBUG);