diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index f31ed2e7ea..8c64daa3c8 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -260,7 +260,10 @@ int load_volume_key_tpm2( &decrypted_key); if (IN_SET(r, -EACCES, -ENOLCK)) return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed"); - if (r != -EPERM) + /* Stop unless we should keep iterating to next token because the tried one + * does not match boot state. For now without -EUCLEAN because currently the + * only error it reports won't be solved by moving to another token. */ + if (!ERRNO_IS_NEG_TPM2_TOKEN_MISMATCH(r)) break; token++; /* try a different token next time */ diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c index d8d233fabb..20876cc71b 100644 --- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c @@ -23,13 +23,22 @@ _public_ const char *cryptsetup_token_version(void) { return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" PROJECT_VERSION_FULL " (" GIT_VERSION ")"; } -static int log_debug_open_error(struct crypt_device *cd, int r) { +static int log_debug_open_error(struct crypt_device *cd, int token, int r) { if (r == -EAGAIN) return crypt_log_debug_errno(cd, r, "TPM2 device not found."); - if (r == -ENOSTR) - return crypt_log_debug_errno(cd, r, "No matching TPM2 token data found."); + if (r == -ENOSTR) { + /* Remap to -EPERM so libcryptsetup's CRYPT_ANY_TOKEN loop keeps iterating. */ + (void) crypt_log_debug_errno(cd, r, "Token %d: no matching TPM2 token data found.", token); + return -EPERM; + } + if (IN_SET(r, -EREMCHG, -EREMOTE)) { + /* Remap as above. Note: For now without -EUCLEAN because currently the only error it + * reports won't be solved by moving to another token. */ + (void) crypt_log_debug_errno(cd, r, "Token %d: TPM policy does not match current system state, skipping.", token); + return -EPERM; + } - return crypt_log_debug_errno(cd, r, TOKEN_NAME " open failed: %m."); + return crypt_log_debug_errno(cd, r, "Token %d: open failed: %m.", token); } _public_ int cryptsetup_token_open_pin( @@ -78,8 +87,11 @@ _public_ int cryptsetup_token_open_pin( params = *(systemd_tpm2_plugin_params *)usrptr; r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL); - if (r < 0) - return crypt_log_debug_errno(cd, r, "Failed to parse token JSON data: %m"); + if (r < 0) { + /* Remap to -EPERM so libcryptsetup keeps iterating past a broken token. */ + (void) crypt_log_debug_errno(cd, r, "Token %d: failed to parse JSON data: %m", token); + return -EPERM; + } struct iovec *blobs = NULL, *policy_hash = NULL; size_t n_blobs = 0, n_policy_hash = 0; @@ -104,10 +116,13 @@ _public_ int cryptsetup_token_open_pin( &pcrlock_nv, &flags); if (r < 0) - return log_debug_open_error(cd, r); + return log_debug_open_error(cd, token, r); - 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); + if (params.search_pcr_mask != UINT32_MAX && hash_pcr_mask != params.search_pcr_mask) { + /* Remap to -EPERM so libcryptsetup keeps iterating to the next token. */ + crypt_log_debug(cd, "Token %d: PCR mask doesn't match expectation (%" PRIu32 " vs. %" PRIu32 ")", token, hash_pcr_mask, params.search_pcr_mask); + return -EPERM; + } r = acquire_luks2_key( params.device, @@ -130,12 +145,12 @@ _public_ int cryptsetup_token_open_pin( flags, &decrypted_key); if (r < 0) - return log_debug_open_error(cd, r); + return log_debug_open_error(cd, token, r); /* Before using this key as passphrase we base64 encode it, for compat with homed */ base64_encoded_size = base64mem(decrypted_key.iov_base, decrypted_key.iov_len, &base64_encoded); if (base64_encoded_size < 0) - return log_debug_open_error(cd, base64_encoded_size); + return log_debug_open_error(cd, token, base64_encoded_size); /* free'd automatically by libcryptsetup */ *ret_password = TAKE_PTR(base64_encoded); diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 5f02e64da8..36058b249d 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -2181,7 +2181,10 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( &decrypted_key); 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) + /* Stop unless we should keep iterating to next token because the tried one + * does not match boot state. For now without -EUCLEAN because currently the + * only error it reports won't be solved by moving to another token. */ + if (!ERRNO_IS_NEG_TPM2_TOKEN_MISMATCH(r)) break; token++; /* try a different token next time */ diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 88098acac8..7fca284d69 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -49,6 +49,14 @@ static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) { int dlopen_tpm2(int log_level); +/* 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) + +/* 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, or even a different TPM. The caller should keep trying other tokens. */ +#define ERRNO_IS_NEG_TPM2_TOKEN_MISMATCH(r) IN_SET(r, -EREMCHG, -ENOANO, -EPERM, -ENOSTR, -EREMOTE) + #if HAVE_TPM2 #ifndef SYSTEMD_CFLAGS_MARKER_TPM2 # error "missing tpm2_cflags in meson dependency." @@ -378,9 +386,6 @@ int tpm2_get_or_create_ek(Tpm2Context *c, const Tpm2Handle *session, TPM2B_PUBLI int tpm2_seal(Tpm2Context *c, uint32_t seal_key_handle, const TPM2B_DIGEST policy_hash[], size_t n_policy, const char *pin, struct iovec *ret_secret, struct iovec **ret_blobs, size_t *ret_n_blobs, uint16_t *ret_primary_alg, struct iovec *ret_srk); int tpm2_unseal(Tpm2Context *c, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, const char *pubkey_policy_ref, uint32_t pubkey_pcr_mask, sd_json_variant *signature, const char *pin, const Tpm2PCRLockPolicy *pcrlock_policy, uint16_t primary_alg, const struct iovec blobs[], size_t n_blobs, const struct iovec known_policy_hash[], size_t n_known_policy_hash, const struct iovec *srk, struct iovec *ret_secret); -/* tpm2_unseal() returns a bunch of different errors for various flavours of PCR issues, let's group them */ -#define ERRNO_IS_NEG_TPM2_UNSEAL_BAD_PCR(r) IN_SET(r, -EREMCHG, -ENOANO, -EUCLEAN, -EPERM) - #if HAVE_OPENSSL int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret); int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret);