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);
|