diff options
author | Antonio Alvarez Feijoo <antonio.feijoo@suse.com> | 2022-12-07 16:52:27 +0100 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2022-12-08 10:37:28 +0100 |
commit | 0254e4d66af7aa893b31b2326335ded5dde48b51 (patch) | |
tree | dd8cab4cf1cc976fe9bb20b434ae94a7907ea12a /src/shared/tpm2-util.c | |
parent | 15f330cf8745e68abc429765dafdd8d04ebf7154 (diff) | |
download | systemd-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.c | 81 |
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); |