diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 7a20965658..cda3f94ab1 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -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; diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 3723281f42..8c0b136b08 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -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