Skip to content

Commit

Permalink
cryptsetup: retry TPM2 unseal operation if it fails with TPM2_RC_PCR_…
Browse files Browse the repository at this point in the history
…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 systemd#24906
  • Loading branch information
aafeijoo-suse authored and keszybz committed Dec 8, 2022
1 parent 15f330c commit 0254e4d
Showing 1 changed file with 46 additions and 35 deletions.
81 changes: 46 additions & 35 deletions src/shared/tpm2-util.c
Expand Up @@ -1565,6 +1565,8 @@ int tpm2_seal(const char *device,
return r;
}

#define RETRY_UNSEAL_MAX 30u

int tpm2_unseal(const char *device,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 0254e4d

Please sign in to comment.