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.
330 lines
14 KiB
330 lines
14 KiB
7 months ago
|
From 1d027f4d13ed1c1fbd0766db5b4a544042f1336e Mon Sep 17 00:00:00 2001
|
||
|
From: Dan Streetman <ddstreet@ieee.org>
|
||
|
Date: Wed, 7 Dec 2022 11:23:59 -0500
|
||
|
Subject: [PATCH] tpm2: move policy building out of policy session creation
|
||
|
|
||
|
This retains the use of policy sessions instead of trial sessions
|
||
|
in most cases, based on the code comment that some TPMs do not
|
||
|
implement trial sessions correctly. However, it's likely that the
|
||
|
issue was not the TPMs, but our code's incorrect use of PolicyPCR
|
||
|
inside a trial session; we are not providing expected PCR values
|
||
|
with our call to PolicyPCR inside a trial session, but the spec
|
||
|
indicates that in a trial session, the TPM *may* return error if
|
||
|
the expected PCR value(s) are not provided. That may have been the
|
||
|
source of the original confusion about trial sessions.
|
||
|
|
||
|
More details:
|
||
|
https://github.com/systemd/systemd/pull/26357#pullrequestreview-1409983694
|
||
|
|
||
|
Also, future commits will replace the use of trial sessions with
|
||
|
policy calculations, which avoids the problem entirely.
|
||
|
|
||
|
(cherry picked from commit 2cd9d57548b0dadd52523df486d33aa4cf7c3b84)
|
||
|
|
||
|
Related: RHEL-16182
|
||
|
---
|
||
|
src/shared/tpm2-util.c | 199 +++++++++++++++++++++++------------------
|
||
|
1 file changed, 112 insertions(+), 87 deletions(-)
|
||
|
|
||
|
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
|
||
|
index b4c620ec53..3960c0aed7 100644
|
||
|
--- a/src/shared/tpm2-util.c
|
||
|
+++ b/src/shared/tpm2-util.c
|
||
|
@@ -1278,6 +1278,59 @@ static int tpm2_make_encryption_session(
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+static int tpm2_make_policy_session(
|
||
|
+ Tpm2Context *c,
|
||
|
+ const Tpm2Handle *primary,
|
||
|
+ const Tpm2Handle *encryption_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;
|
||
|
+
|
||
|
+ assert(c);
|
||
|
+ assert(primary);
|
||
|
+ assert(encryption_session);
|
||
|
+ assert(ret_session);
|
||
|
+
|
||
|
+ if (!tpm2_is_encryption_session(c, encryption_session))
|
||
|
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||
|
+ "Missing encryption session");
|
||
|
+
|
||
|
+ log_debug("Starting policy session.");
|
||
|
+
|
||
|
+ _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
|
||
|
+ r = tpm2_handle_new(c, &session);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ rc = sym_Esys_StartAuthSession(
|
||
|
+ c->esys_context,
|
||
|
+ primary->esys_handle,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ encryption_session->esys_handle,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ ESYS_TR_NONE,
|
||
|
+ NULL,
|
||
|
+ session_type,
|
||
|
+ &symmetric,
|
||
|
+ TPM2_ALG_SHA256,
|
||
|
+ &session->esys_handle);
|
||
|
+ if (rc != TSS2_RC_SUCCESS)
|
||
|
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||
|
+ "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
|
||
|
+
|
||
|
+ *ret_session = TAKE_PTR(session);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static int openssl_pubkey_to_tpm2_pubkey(
|
||
|
const void *pubkey,
|
||
|
size_t pubkey_size,
|
||
|
@@ -1495,87 +1548,36 @@ static int find_signature(
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
-static int tpm2_make_policy_session(
|
||
|
+static int tpm2_build_sealing_policy(
|
||
|
Tpm2Context *c,
|
||
|
- const Tpm2Handle *primary,
|
||
|
- const Tpm2Handle *parent_session,
|
||
|
- TPM2_SE session_type,
|
||
|
+ const Tpm2Handle *session,
|
||
|
uint32_t hash_pcr_mask,
|
||
|
- uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
|
||
|
+ uint16_t pcr_bank,
|
||
|
const void *pubkey,
|
||
|
size_t pubkey_size,
|
||
|
uint32_t pubkey_pcr_mask,
|
||
|
JsonVariant *signature_json,
|
||
|
bool use_pin,
|
||
|
- Tpm2Handle **ret_session,
|
||
|
- TPM2B_DIGEST **ret_policy_digest,
|
||
|
- TPMI_ALG_HASH *ret_pcr_bank) {
|
||
|
+ TPM2B_DIGEST **ret_policy_digest) {
|
||
|
|
||
|
- static const TPMT_SYM_DEF symmetric = {
|
||
|
- .algorithm = TPM2_ALG_AES,
|
||
|
- .keyBits.aes = 128,
|
||
|
- .mode.aes = TPM2_ALG_CFB,
|
||
|
- };
|
||
|
TSS2_RC rc;
|
||
|
int r;
|
||
|
|
||
|
assert(c);
|
||
|
+ assert(session);
|
||
|
assert(pubkey || pubkey_size == 0);
|
||
|
assert(pubkey_pcr_mask == 0 || pubkey_size > 0);
|
||
|
|
||
|
- log_debug("Starting authentication session.");
|
||
|
-
|
||
|
- /* So apparently some TPM implementations don't implement trial mode correctly. To avoid issues let's
|
||
|
- * avoid it when it is easy to. At the moment we only really need trial mode for the signed PCR
|
||
|
- * policies (since only then we need to shove PCR values into the policy that don't match current
|
||
|
- * state anyway), hence if we have none of those we don't need to bother. Hence, let's patch in
|
||
|
- * TPM2_SE_POLICY even if trial mode is requested unless a pubkey PCR mask is specified that is
|
||
|
- * non-zero, i.e. signed PCR policy is requested.
|
||
|
- *
|
||
|
- * One day we should switch to calculating policy hashes client side when trial mode is requested, to
|
||
|
- * avoid this mess. */
|
||
|
- if (session_type == TPM2_SE_TRIAL && pubkey_pcr_mask == 0)
|
||
|
- session_type = TPM2_SE_POLICY;
|
||
|
+ log_debug("Building sealing policy.");
|
||
|
|
||
|
if ((hash_pcr_mask | pubkey_pcr_mask) != 0) {
|
||
|
- /* We are told to configure a PCR policy of some form, let's determine/validate the PCR bank to use. */
|
||
|
-
|
||
|
- if (pcr_bank != UINT16_MAX) {
|
||
|
- r = tpm2_pcr_mask_good(c, pcr_bank, hash_pcr_mask|pubkey_pcr_mask);
|
||
|
- if (r < 0)
|
||
|
- return r;
|
||
|
- if (r == 0)
|
||
|
- log_warning("Selected TPM2 PCRs are not initialized on this system, most likely due to a firmware issue. PCR policy is effectively not enforced. Proceeding anyway.");
|
||
|
- } else {
|
||
|
- /* No bank configured, pick automatically. Some TPM2 devices only can do SHA1. If we
|
||
|
- * detect that use that, but preferably use SHA256 */
|
||
|
- r = tpm2_get_best_pcr_bank(c, hash_pcr_mask|pubkey_pcr_mask, &pcr_bank);
|
||
|
- if (r < 0)
|
||
|
- return r;
|
||
|
- }
|
||
|
+ r = tpm2_pcr_mask_good(c, pcr_bank, hash_pcr_mask|pubkey_pcr_mask);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+ if (r == 0)
|
||
|
+ log_warning("Selected TPM2 PCRs are not initialized on this system.");
|
||
|
}
|
||
|
|
||
|
- _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
|
||
|
- r = tpm2_handle_new(c, &session);
|
||
|
- if (r < 0)
|
||
|
- return r;
|
||
|
-
|
||
|
- rc = sym_Esys_StartAuthSession(
|
||
|
- c->esys_context,
|
||
|
- primary->esys_handle,
|
||
|
- ESYS_TR_NONE,
|
||
|
- parent_session->esys_handle,
|
||
|
- ESYS_TR_NONE,
|
||
|
- ESYS_TR_NONE,
|
||
|
- NULL,
|
||
|
- session_type,
|
||
|
- &symmetric,
|
||
|
- TPM2_ALG_SHA256,
|
||
|
- &session->esys_handle);
|
||
|
- if (rc != TSS2_RC_SUCCESS)
|
||
|
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||
|
- "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
|
||
|
-
|
||
|
if (pubkey_pcr_mask != 0) {
|
||
|
_cleanup_free_ void *fp = NULL;
|
||
|
size_t fp_size = 0;
|
||
|
@@ -1756,12 +1758,6 @@ static int tpm2_make_policy_session(
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
- if (ret_session)
|
||
|
- *ret_session = TAKE_PTR(session);
|
||
|
-
|
||
|
- if (ret_pcr_bank)
|
||
|
- *ret_pcr_bank = pcr_bank;
|
||
|
-
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -1830,33 +1826,57 @@ int tpm2_seal(const char *device,
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
+ TPMI_ALG_HASH pcr_bank = 0;
|
||
|
+ if (hash_pcr_mask | pubkey_pcr_mask) {
|
||
|
+ /* Some TPM2 devices only can do SHA1. Prefer SHA256 but allow SHA1. */
|
||
|
+ r = tpm2_get_best_pcr_bank(c, hash_pcr_mask|pubkey_pcr_mask, &pcr_bank);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+ }
|
||
|
+
|
||
|
_cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
|
||
|
r = tpm2_make_primary(c, &primary, 0, &primary_alg);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
/* we cannot use the bind key before its created */
|
||
|
- _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
|
||
|
- r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, NULL, &session);
|
||
|
+ _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
|
||
|
+ r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, NULL, &encryption_session);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
- _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
|
||
|
- TPMI_ALG_HASH pcr_bank;
|
||
|
+ /* So apparently some TPM implementations don't implement trial mode correctly. To avoid issues let's
|
||
|
+ * avoid it when it is easy to. At the moment we only really need trial mode for the signed PCR
|
||
|
+ * policies (since only then we need to shove PCR values into the policy that don't match current
|
||
|
+ * state anyway), hence if we have none of those we don't need to bother. Hence, let's patch in
|
||
|
+ * TPM2_SE_POLICY even if trial mode is requested unless a pubkey PCR mask is specified that is
|
||
|
+ * non-zero, i.e. signed PCR policy is requested.
|
||
|
+ *
|
||
|
+ * One day we should switch to calculating policy hashes client side when trial mode is requested, to
|
||
|
+ * avoid this mess. */
|
||
|
+ bool trial = (pubkey_pcr_mask != 0);
|
||
|
+
|
||
|
+ _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL;
|
||
|
r = tpm2_make_policy_session(
|
||
|
c,
|
||
|
primary,
|
||
|
- session,
|
||
|
- TPM2_SE_TRIAL,
|
||
|
+ encryption_session,
|
||
|
+ trial,
|
||
|
+ &policy_session);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
|
||
|
+ r = tpm2_build_sealing_policy(
|
||
|
+ c,
|
||
|
+ policy_session,
|
||
|
hash_pcr_mask,
|
||
|
- /* pcr_bank= */ UINT16_MAX,
|
||
|
+ pcr_bank,
|
||
|
pubkey, pubkey_size,
|
||
|
pubkey_pcr_mask,
|
||
|
/* signature_json= */ NULL,
|
||
|
!!pin,
|
||
|
- /* ret_session= */ NULL,
|
||
|
- &policy_digest,
|
||
|
- &pcr_bank);
|
||
|
+ &policy_digest);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
@@ -1897,7 +1917,7 @@ int tpm2_seal(const char *device,
|
||
|
rc = sym_Esys_Create(
|
||
|
c->esys_context,
|
||
|
primary->esys_handle,
|
||
|
- session->esys_handle, /* use HMAC session to enable parameter encryption */
|
||
|
+ encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
|
||
|
ESYS_TR_NONE,
|
||
|
ESYS_TR_NONE,
|
||
|
&hmac_sensitive,
|
||
|
@@ -2066,8 +2086,8 @@ int tpm2_unseal(const char *device,
|
||
|
sym_Tss2_RC_Decode(rc));
|
||
|
}
|
||
|
|
||
|
- _cleanup_tpm2_handle_ Tpm2Handle *hmac_session = NULL;
|
||
|
- r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &hmac_session);
|
||
|
+ _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
|
||
|
+ r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &encryption_session);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
@@ -2077,17 +2097,22 @@ int tpm2_unseal(const char *device,
|
||
|
r = tpm2_make_policy_session(
|
||
|
c,
|
||
|
primary,
|
||
|
- hmac_session,
|
||
|
- TPM2_SE_POLICY,
|
||
|
+ encryption_session,
|
||
|
+ /* trial= */ false,
|
||
|
+ &policy_session);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ r = tpm2_build_sealing_policy(
|
||
|
+ c,
|
||
|
+ policy_session,
|
||
|
hash_pcr_mask,
|
||
|
pcr_bank,
|
||
|
pubkey, pubkey_size,
|
||
|
pubkey_pcr_mask,
|
||
|
signature,
|
||
|
!!pin,
|
||
|
- &policy_session,
|
||
|
- &policy_digest,
|
||
|
- /* ret_pcr_bank= */ NULL);
|
||
|
+ &policy_digest);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
@@ -2105,7 +2130,7 @@ int tpm2_unseal(const char *device,
|
||
|
c->esys_context,
|
||
|
hmac_key->esys_handle,
|
||
|
policy_session->esys_handle,
|
||
|
- hmac_session->esys_handle, /* use HMAC session to enable parameter encryption */
|
||
|
+ encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
|
||
|
ESYS_TR_NONE,
|
||
|
&unsealed);
|
||
|
if (rc == TSS2_RC_SUCCESS)
|