systemd/0515-tpm2-move-policy-build...

330 lines
14 KiB
Diff

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)