tpm2-util: make -EUCLEAN from tpm2_unseal() mean "PCRs kept changing"

So far tpm2_unseal() used -EUCLEAN together with the errors that
signal that the PCR state does not match the policy.  That is
misleading: -EUCLEAN (and TPM2_RC_PCR_CHANGED) is only ever generated
when the TPM's global PCR update counter moves while we are building the
policy or unsealing, i.e. when something on the system extends a PCR
concurrentlyi (usually IMA extending PCR 10).  It says nothing about
whether the policy itself matches.

Until now the retry loop only recognized those conditions while retries
were left (i > 0), and once the budget was exhausted the last iteration
fell through to the generic paths, reporting either an unrelated policy
error or -ENOTRECOVERABLE.  Handle the last iteration explicitly instead,
and return -EUCLEAN with a message that names the actual problem.

Drop -EUCLEAN from ERRNO_IS_NEG_TPM2_UNSEAL_BAD_PCR(), it is not a "bad
PCR state" error.

Signed-off-by: Alberto Planas <aplanas@suse.com>
This commit is contained in:
Alberto Planas
2026-09-22 21:31:48 +02:00
committed by Luca Boccassi
parent c99af7968e
commit 2d54e6b56e
2 changed files with 20 additions and 7 deletions
+16 -5
View File
@@ -7197,13 +7197,17 @@ int tpm2_unseal(Tpm2Context *c,
* -ENOLCK → TPM is in dictionary lockout mode
* -EREMCHG → submitted policy doesn't match NV index stored policy (in case of PolicyAuthorizeNV)
* -ENOANO → none of the PolicyOR branches of a policy matched current state
* -EUCLEAN → PCR state doesn't match expectations
* -EUCLEAN → a PCR kept being extended while we were unsealing
* -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. */
* Of these EREMCHG, ENOANO and EPERM mean that PCR state is not matching expectations. EUCLEAN on
* the other hand says nothing about the correctness of the policy: it means the TPM's global PCR
* update counter kept moving while we built the policy or unsealed, i.e. something on this system
* extends a PCR continuously (the usual suspect is PCR 10 when IMA is enabled directly, or
* indirectly via secure boot). We make at most RETRY_UNSEAL_MAX + 1 attempts before reporting
* it. */
TSS2_RC rc;
int r;
@@ -7361,7 +7365,10 @@ int tpm2_unseal(Tpm2Context *c,
!!pin,
(shard == 1 || !iovec_is_set(pubkey)) ? pcrlock_policy : NULL,
&policy_digest);
if (r == -EUCLEAN && i > 0) {
if (r == -EUCLEAN) {
if (i == 0)
return log_debug_errno(r, "PCR values kept changing while building the TPM2 policy, giving up after %u unsealing attempts.", RETRY_UNSEAL_MAX + 1);
log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
retry = true;
break;
@@ -7403,7 +7410,11 @@ int tpm2_unseal(Tpm2Context *c,
encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
&unsealed);
if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
if (rc == TPM2_RC_PCR_CHANGED) {
if (i == 0)
return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
"PCR values kept changing while unsealing, giving up after %u unsealing attempts.", RETRY_UNSEAL_MAX + 1);
log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
retry = true;
break;
+4 -2
View File
@@ -51,8 +51,10 @@ static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
int dlopen_tpm2(int log_level) _dlopen_loader_;
/* tpm2_unseal() returns a bunch of different errors for various flavours of PCR issues, let's group them.
* Kept outside the HAVE_TPM2 guard as callers switch on these errnos even in builds without TPM2. */
#define ERRNO_IS_NEG_TPM2_UNSEAL_BAD_PCR(r) IN_SET(r, -EREMCHG, -ENOANO, -EUCLEAN, -EPERM)
* Kept outside the HAVE_TPM2 guard as callers switch on these errnos even in builds without TPM2. Note that
* -EUCLEAN is not part of this: when returned by tpm2_unseal() it means a PCR was continuously extended
* while unsealing, which says nothing about whether the PCR state matches the policy. */
#define ERRNO_IS_NEG_TPM2_UNSEAL_BAD_PCR(r) IN_SET(r, -EREMCHG, -ENOANO, -EPERM)
/* Errors that mean the tried TPM2 token does not match the boot state, be it due to wrong PCR state, a
* different PCR signing key/policy, a different TPM, or an unusable NV index. The caller should keep trying