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 committed Dec 2, 2022
1 parent f0c24a0 commit f6a077f
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 18 deletions.
44 changes: 26 additions & 18 deletions src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
Expand Up @@ -96,24 +96,32 @@ _public_ int cryptsetup_token_open_pin(
if (params.search_pcr_mask != UINT32_MAX && hash_pcr_mask != params.search_pcr_mask)
return crypt_log_debug_errno(cd, ENXIO, "PCR mask doesn't match expectation (%" PRIu32 " vs. %" PRIu32 ")", hash_pcr_mask, params.search_pcr_mask);

r = acquire_luks2_key(
params.device,
hash_pcr_mask,
pcr_bank,
pubkey, pubkey_size,
pubkey_pcr_mask,
params.signature_path,
pin_string,
primary_alg,
blob,
blob_size,
policy_hash,
policy_hash_size,
flags,
&decrypted_key,
&decrypted_key_size);
if (r < 0)
return log_debug_open_error(cd, r);
for (unsigned i = TPM2_PCRS_MAX;; i--) {
r = acquire_luks2_key(
params.device,
hash_pcr_mask,
pcr_bank,
pubkey, pubkey_size,
pubkey_pcr_mask,
params.signature_path,
pin_string,
primary_alg,
blob,
blob_size,
policy_hash,
policy_hash_size,
flags,
&decrypted_key,
&decrypted_key_size);
if (r >= 0)
break;
if (r == -ERESTART && i > 0) {
crypt_log_debug(cd, "Attempt to acquire the TPM2 key again.");
continue;
}
if (r < 0)
return log_debug_open_error(cd, r);
}

/* Before using this key as passphrase we base64 encode it, for compat with homed */
r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
Expand Down
11 changes: 11 additions & 0 deletions src/cryptsetup/cryptsetup.c
Expand Up @@ -1439,6 +1439,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_free_ char *friendly = NULL;
int keyslot = arg_key_slot, r;
unsigned restart_count = 0;
size_t decrypted_key_size;

assert(cd);
Expand Down Expand Up @@ -1472,6 +1473,11 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
&decrypted_key, &decrypted_key_size);
if (r >= 0)
break;
if (r == -ERESTART && restart_count < TPM2_PCRS_MAX) {
restart_count++;
log_debug_errno(r, "Attempt to acquire the TPM2 key again.");
continue;
}
if (IN_SET(r, -EACCES, -ENOLCK))
return log_error_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed, falling back to traditional unlocking.");
if (ERRNO_IS_NOT_SUPPORTED(r)) /* TPM2 support not compiled in? */
Expand Down Expand Up @@ -1561,6 +1567,11 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
arg_headless,
arg_ask_password_flags,
&decrypted_key, &decrypted_key_size);
if (r == -ERESTART && restart_count < TPM2_PCRS_MAX) {
restart_count++;
log_debug_errno(r, "Attempt to acquire the TPM2 key again.");
continue;
}
if (IN_SET(r, -EACCES, -ENOLCK))
return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed, falling back to traditional unlocking.");
if (r != -EPERM)
Expand Down
5 changes: 5 additions & 0 deletions src/shared/tpm2-util.c
Expand Up @@ -1710,6 +1710,11 @@ int tpm2_unseal(const char *device,
hmac_session, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
&unsealed);
if (rc == TPM2_RC_PCR_CHANGED) {
r = log_warning_errno(SYNTHETIC_ERRNO(ERESTART),
"Any PCR value changed after the TPM2 policy session was created.");
goto finish;
}
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));
Expand Down

0 comments on commit f6a077f

Please sign in to comment.