summaryrefslogtreecommitdiff
path: root/src/shared/tpm2-util.c
diff options
context:
space:
mode:
authorAntonio Alvarez Feijoo <antonio.feijoo@suse.com>2022-12-07 16:52:27 +0100
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2022-12-08 10:37:28 +0100
commit0254e4d66af7aa893b31b2326335ded5dde48b51 (patch)
treedd8cab4cf1cc976fe9bb20b434ae94a7907ea12a /src/shared/tpm2-util.c
parent15f330cf8745e68abc429765dafdd8d04ebf7154 (diff)
downloadsystemd-0254e4d66af7aa893b31b2326335ded5dde48b51.tar.gz
cryptsetup: retry TPM2 unseal operation if it fails with TPM2_RC_PCR_CHANGED
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
Diffstat (limited to 'src/shared/tpm2-util.c')
-rw-r--r--src/shared/tpm2-util.c81
1 files changed, 46 insertions, 35 deletions
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 327caa439f..ba8dfb041d 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1565,6 +1565,8 @@ finish:
return r;
}
+#define RETRY_UNSEAL_MAX 30u
+
int tpm2_unseal(const char *device,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
@@ -1676,44 +1678,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);