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/0493-cryptsetup-retry-TPM2-...

140 lines
6.4 KiB

From 7f8a43eff0d800f21e9f873010637d08da13da67 Mon Sep 17 00:00:00 2001
From: Antonio Alvarez Feijoo <antonio.feijoo@suse.com>
Date: Wed, 7 Dec 2022 16:52:27 +0100
Subject: [PATCH] cryptsetup: retry TPM2 unseal operation if it fails with
TPM2_RC_PCR_CHANGED
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Quoting "Trusted Platform Module Library - Part 3: Commands (Rev. 01.59)":
"pcrUpdateCounter this parameter is updated by TPM2_PolicyPCR(). This value
may only be set once during a policy. Each time TPM2_PolicyPCR() executes, it
checks to see if policySession->pcrUpdateCounter has its default state,
indicating that this is the first TPM2_PolicyPCR(). If it has its default value,
then policySession->pcrUpdateCounter is set to the current value of
pcrUpdateCounter. If policySession->pcrUpdateCounter does not have its default
value and its value is not the same as pcrUpdateCounter, the TPM shall return
TPM_RC_PCR_CHANGED.
If this parameter and pcrUpdateCounter are not the same, it indicates that PCR
have changed since checked by the previous TPM2_PolicyPCR(). Since they have
changed, the previous PCR validation is no longer valid."
The TPM will return TPM_RC_PCR_CHANGED if any PCR value changes (no matter
which) between validating the PCRs binded to the enrollment and unsealing the
HMAC key, so this patch adds a retry mechanism in this case.
Fixes #24906
(cherry picked from commit 0254e4d66af7aa893b31b2326335ded5dde48b51)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 81 ++++++++++++++++++++++++------------------
1 file changed, 46 insertions(+), 35 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index aca7f22e54..d1a4e9cd11 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1608,6 +1608,8 @@ finish:
return r;
}
+#define RETRY_UNSEAL_MAX 30u
+
int tpm2_unseal(const char *device,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
@@ -1719,44 +1721,53 @@ int tpm2_unseal(const char *device,
if (r < 0)
goto finish;
- r = tpm2_make_policy_session(
- c.esys_context,
- primary,
- hmac_session,
- TPM2_SE_POLICY,
- hash_pcr_mask,
- pcr_bank,
- pubkey, pubkey_size,
- pubkey_pcr_mask,
- signature,
- !!pin,
- &session,
- &policy_digest,
- /* ret_pcr_bank= */ NULL);
- if (r < 0)
- goto finish;
+ for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
+ r = tpm2_make_policy_session(
+ c.esys_context,
+ primary,
+ hmac_session,
+ TPM2_SE_POLICY,
+ hash_pcr_mask,
+ pcr_bank,
+ pubkey, pubkey_size,
+ pubkey_pcr_mask,
+ signature,
+ !!pin,
+ &session,
+ &policy_digest,
+ /* ret_pcr_bank= */ NULL);
+ if (r < 0)
+ goto finish;
- /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
- * wait until the TPM2 tells us to go away. */
- if (known_policy_hash_size > 0 &&
- memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
- return log_error_errno(SYNTHETIC_ERRNO(EPERM),
- "Current policy digest does not match stored policy digest, cancelling "
- "TPM2 authentication attempt.");
+ /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
+ * wait until the TPM2 tells us to go away. */
+ if (known_policy_hash_size > 0 &&
+ memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Current policy digest does not match stored policy digest, cancelling "
+ "TPM2 authentication attempt.");
- log_debug("Unsealing HMAC key.");
+ log_debug("Unsealing HMAC key.");
- rc = sym_Esys_Unseal(
- c.esys_context,
- hmac_key,
- session,
- hmac_session, /* use HMAC session to enable parameter encryption */
- ESYS_TR_NONE,
- &unsealed);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
+ rc = sym_Esys_Unseal(
+ c.esys_context,
+ hmac_key,
+ session,
+ hmac_session, /* use HMAC session to enable parameter encryption */
+ ESYS_TR_NONE,
+ &unsealed);
+ if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
+ log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
+ session = tpm2_flush_context_verbose(c.esys_context, session);
+ continue;
+ }
+ if (rc != TSS2_RC_SUCCESS) {
+ r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
+ goto finish;
+ }
+
+ break;
}
secret = memdup(unsealed->buffer, unsealed->size);