140 lines
6.4 KiB
Diff
140 lines
6.4 KiB
Diff
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);
|