From 48cb1ad9c3fde47dc40b4345fa2efb2012596768 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 19 Jun 2025 12:26:45 +0200 Subject: [PATCH 1/2] tpm2-util: recognize DA lockout mode and PIN failures properly When we create the policy session the previously passed PIN will be checked. This means we'll see PIN errors here, in case the PIN is wrong or if DA lockout mode has been triggered. Recognize these two errors, and bubble up recognizable errors. --- src/shared/tpm2-util.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 1c1ce441a2..d5106dbb54 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -3134,6 +3134,12 @@ int tpm2_make_policy_session( &SESSION_TEMPLATE_SYM_AES_128_CFB, TPM2_ALG_SHA256, &session->esys_handle); + if ((rc & ~TPM2_RC_N_MASK) == TPM2_RC_AUTH_FAIL) + return log_debug_errno(SYNTHETIC_ERRNO(EILSEQ), + "Authorization failure from TPM: %s", sym_Tss2_RC_Decode(rc)); + if (rc == TPM2_RC_LOCKOUT) + return log_debug_errno(SYNTHETIC_ERRNO(ENOLCK), + "TPM2 device is in dictionary attack lockout mode."); if (rc != TSS2_RC_SUCCESS) return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc)); @@ -5518,6 +5524,7 @@ int tpm2_unseal(Tpm2Context *c, * -EUCLEAN → PCR state doesn't match expectations * -EPERM → stored policy does not match TPM state * -ENOTRECOVERABLE → all other kinds of TPM errors + * -EILSEQ → bad PIN * * Of these all four of EREMCHG, ENOANO, EUCLEAN, EPERM can all mean that PCR state is not matching * expectations. */ @@ -5657,7 +5664,7 @@ int tpm2_unseal(Tpm2Context *c, encryption_session, &policy_session); if (r < 0) - return r; + return r; /* Will return EILSEQ on auth failure (i.e. bad PIN) */ /* If both public PCR key and pcrlock policies are requested, then generate the * public PCR policy for the first shared, and the pcrlock policy for the 2nd */ From df21ceca25a9b86ab9763be9c8e68aa3107178f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 19 Jun 2025 12:28:32 +0200 Subject: [PATCH 2/2] cryptsetup: handle DA lockout/bad PIN properly Now that we have recognizable errors, let's print clear error messages when we try to unlock a TPM slot. And in case of the token plugin propagate this as ENOANO so that libcryptsetup recognizes this as bad PIN. (ENOANO is documented as the error to return in that case) Fixes: #32260 --- src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c | 4 ++++ src/shared/cryptsetup-tpm2.c | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c index 708d8a7645..ab7d01f137 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c @@ -111,6 +111,10 @@ int acquire_luks2_key( ret_decrypted_key); if (r == -EREMOTE) return log_error_errno(r, "TPM key integrity check failed. Key enrolled in superblock most likely does not belong to this TPM."); + if (r == -EILSEQ) + return log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Bad PIN."); /* cryptsetup docs say we should return ENOANO on bad PIN */ + if (r == -ENOLCK) + return log_error_errno(r, "TPM is in dictionary attack lock-out mode."); if (ERRNO_IS_NEG_TPM2_UNSEAL_BAD_PCR(r)) return log_error_errno(r, "TPM policy does not match current system state. Either system has been tempered with or policy out-of-date: %m"); if (r < 0) diff --git a/src/shared/cryptsetup-tpm2.c b/src/shared/cryptsetup-tpm2.c index b62b0f8b33..bd27d3e6d6 100644 --- a/src/shared/cryptsetup-tpm2.c +++ b/src/shared/cryptsetup-tpm2.c @@ -227,14 +227,14 @@ int acquire_tpm2_key( return log_error_errno(r, "TPM key integrity check failed. Key enrolled in superblock most likely does not belong to this TPM."); if (ERRNO_IS_NEG_TPM2_UNSEAL_BAD_PCR(r)) return log_error_errno(r, "TPM policy does not match current system state. Either system has been tempered with or policy out-of-date: %m"); - if (r < 0) { - log_error_errno(r, "Failed to unseal secret using TPM2: %m"); - - /* We get this error in case there is an authentication policy mismatch. This should - * not happen, but this avoids confusing behavior, just in case. */ - if (r != -ENOLCK) - continue; + if (r == -ENOLCK) + return log_error_errno(r, "TPM is in dictionary attack lock-out mode."); + if (r == -EILSEQ) { + log_warning_errno(r, "Bad PIN."); + continue; } + if (r < 0) + return log_error_errno(r, "Failed to unseal secret using TPM2: %m"); return r; }