diff options
Diffstat (limited to 'src/cryptsetup')
-rw-r--r-- | src/cryptsetup/cryptsetup-generator.c | 25 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-pkcs11.c | 6 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-pkcs11.h | 4 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c | 12 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c | 6 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c | 65 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tokens/luks2-fido2.c | 4 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c | 4 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c | 44 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h | 19 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tpm2.c | 314 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tpm2.h | 126 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup.c | 288 | ||||
-rw-r--r-- | src/cryptsetup/meson.build | 4 |
14 files changed, 294 insertions, 627 deletions
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 904e4cd..4db25d3 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -229,7 +229,8 @@ static int generate_device_umount(const char *name, static int print_dependencies(FILE *f, const char* device_path, const char* timeout_value, bool canfail) { int r; - assert(!canfail || timeout_value); + assert(f); + assert(device_path); if (STR_IN_SET(device_path, "-", "none")) /* None, nothing to do */ @@ -263,11 +264,13 @@ static int print_dependencies(FILE *f, const char* device_path, const char* time fprintf(f, "After=%1$s\n", unit); if (canfail) { fprintf(f, "Wants=%1$s\n", unit); - r = write_drop_in_format(arg_dest, unit, 90, "device-timeout", - "# Automatically generated by systemd-cryptsetup-generator \n\n" - "[Unit]\nJobRunningTimeoutSec=%s", timeout_value); - if (r < 0) - return log_error_errno(r, "Failed to write device drop-in: %m"); + if (timeout_value) { + r = write_drop_in_format(arg_dest, unit, 90, "device-timeout", + "# Automatically generated by systemd-cryptsetup-generator \n\n" + "[Unit]\nJobRunningTimeoutSec=%s", timeout_value); + if (r < 0) + return log_error_errno(r, "Failed to write device drop-in: %m"); + } } else fprintf(f, "Requires=%1$s\n", unit); } else { @@ -276,7 +279,7 @@ static int print_dependencies(FILE *f, const char* device_path, const char* time if (!escaped_path) return log_oom(); - fprintf(f, "RequiresMountsFor=%s\n", escaped_path); + fprintf(f, "%s=%s\n", canfail ? "WantsMountsFor" : "RequiresMountsFor", escaped_path); } return 0; @@ -486,7 +489,7 @@ static int create_disk( if (key_file && !keydev) { r = print_dependencies(f, key_file, keyfile_timeout_value, - /* canfail= */ keyfile_can_timeout > 0); + /* canfail= */ keyfile_can_timeout > 0 || nofail); if (r < 0) return r; } @@ -494,8 +497,8 @@ static int create_disk( /* Check if a header option was specified */ if (detached_header > 0 && !headerdev) { r = print_dependencies(f, header_path, - NULL, - /* canfail= */ false); /* header is always necessary */ + /* timeout_value= */ NULL, + /* canfail= */ nofail); if (r < 0) return r; } @@ -578,6 +581,8 @@ static crypto_device* crypt_device_free(crypto_device *d) { free(d->uuid); free(d->keyfile); free(d->keydev); + free(d->headerdev); + free(d->datadev); free(d->name); free(d->options); return mfree(d); diff --git a/src/cryptsetup/cryptsetup-pkcs11.c b/src/cryptsetup/cryptsetup-pkcs11.c index f991389..4b2b5bb 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.c +++ b/src/cryptsetup/cryptsetup-pkcs11.c @@ -34,14 +34,14 @@ int decrypt_pkcs11_key( const void *key_data, /* … or key_data and key_data_size (for literal keys) */ size_t key_data_size, usec_t until, - bool headless, + AskPasswordFlags askpw_flags, void **ret_decrypted_key, size_t *ret_decrypted_key_size) { _cleanup_(pkcs11_crypt_device_callback_data_release) pkcs11_crypt_device_callback_data data = { .friendly_name = friendly_name, + .askpw_flags = askpw_flags, .until = until, - .headless = headless, }; int r; @@ -154,7 +154,7 @@ int find_pkcs11_auto_data( assert(!key); assert(key_size == 0); - r = unbase64mem(json_variant_string(w), SIZE_MAX, &key, &key_size); + r = unbase64mem(json_variant_string(w), &key, &key_size); if (r < 0) return log_error_errno(r, "Failed to decode base64 encoded key."); } diff --git a/src/cryptsetup/cryptsetup-pkcs11.h b/src/cryptsetup/cryptsetup-pkcs11.h index 256c09a..22e6992 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.h +++ b/src/cryptsetup/cryptsetup-pkcs11.h @@ -19,7 +19,7 @@ int decrypt_pkcs11_key( const void *key_data, size_t key_data_size, usec_t until, - bool headless, + AskPasswordFlags askpw_flags, void **ret_decrypted_key, size_t *ret_decrypted_key_size); @@ -42,7 +42,7 @@ static inline int decrypt_pkcs11_key( const void *key_data, size_t key_data_size, usec_t until, - bool headless, + AskPasswordFlags askpw_flags, void **ret_decrypted_key, size_t *ret_decrypted_key_size) { diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c index fdb3b17..1efb7c5 100644 --- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c +++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c @@ -18,7 +18,7 @@ /* for libcryptsetup debug purpose */ _public_ const char *cryptsetup_token_version(void) { - return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")"; + return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" PROJECT_VERSION_FULL " (" GIT_VERSION ")"; } _public_ int cryptsetup_token_open_pin( @@ -34,7 +34,7 @@ _public_ int cryptsetup_token_open_pin( const char *json; _cleanup_(erase_and_freep) char *pin_string = NULL; - assert(!pin || pin_size); + assert(pin || pin_size == 0); assert(token >= 0); /* This must not fail at this moment (internal error) */ @@ -87,7 +87,7 @@ _public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) { */ _public_ void cryptsetup_token_dump( struct crypt_device *cd /* is always LUKS2 context */, - const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) { + const char *json /* validated 'systemd-fido2' token if cryptsetup_token_validate is defined */) { int r; Fido2EnrollFlags required; @@ -154,7 +154,7 @@ _public_ void cryptsetup_token_dump( */ _public_ int cryptsetup_token_validate( struct crypt_device *cd, /* is always LUKS2 context */ - const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-tpm2' */) { + const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-fido2' */) { int r; JsonVariant *w; @@ -172,7 +172,7 @@ _public_ int cryptsetup_token_validate( return 1; } - r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL); + r = unbase64mem(json_variant_string(w), NULL, NULL); if (r < 0) return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'fido2-credential' field: %m"); @@ -182,7 +182,7 @@ _public_ int cryptsetup_token_validate( return 1; } - r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL); + r = unbase64mem(json_variant_string(w), NULL, NULL); if (r < 0) return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded salt: %m."); diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c index 2ac8a27..a9898ba 100644 --- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c +++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c @@ -18,7 +18,7 @@ /* for libcryptsetup debug purpose */ _public_ const char *cryptsetup_token_version(void) { - return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")"; + return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" PROJECT_VERSION_FULL " (" GIT_VERSION ")"; } _public_ int cryptsetup_token_open_pin( @@ -33,7 +33,7 @@ _public_ int cryptsetup_token_open_pin( const char *json; int r; - assert(!pin || pin_size); + assert(pin || pin_size == 0); assert(token >= 0); /* This must not fail at this moment (internal error) */ @@ -136,7 +136,7 @@ _public_ int cryptsetup_token_validate( return 1; } - r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL); + r = unbase64mem(json_variant_string(w), NULL, NULL); if (r < 0) return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m."); diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c index 6fee831..8b4754a 100644 --- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c @@ -20,7 +20,7 @@ /* for libcryptsetup debug purpose */ _public_ const char *cryptsetup_token_version(void) { - return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")"; + 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) { @@ -42,9 +42,8 @@ _public_ int cryptsetup_token_open_pin( void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) { _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL; - _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL, *salt = NULL, *srk_buf = NULL; - size_t blob_size, policy_hash_size, decrypted_key_size, pubkey_size, salt_size = 0, srk_buf_size = 0; - _cleanup_(erase_and_freep) void *decrypted_key = NULL; + _cleanup_(iovec_done) struct iovec blob = {}, pubkey = {}, policy_hash = {}, salt = {}, srk = {}, pcrlock_nv = {}; + _cleanup_(iovec_done_erase) struct iovec decrypted_key = {}; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; uint32_t hash_pcr_mask, pubkey_pcr_mask; systemd_tpm2_plugin_params params = { @@ -57,7 +56,7 @@ _public_ int cryptsetup_token_open_pin( int r; assert(token >= 0); - assert(!pin || pin_size > 0); + assert(pin || pin_size == 0); assert(ret_password); assert(ret_password_len); @@ -79,21 +78,17 @@ _public_ int cryptsetup_token_open_pin( r = tpm2_parse_luks2_json( v, - NULL, + /* ret_keyslot= */ NULL, &hash_pcr_mask, &pcr_bank, &pubkey, - &pubkey_size, &pubkey_pcr_mask, &primary_alg, &blob, - &blob_size, &policy_hash, - &policy_hash_size, &salt, - &salt_size, - &srk_buf, - &srk_buf_size, + &srk, + &pcrlock_nv, &flags); if (r < 0) return log_debug_open_error(cd, r); @@ -105,28 +100,24 @@ _public_ int cryptsetup_token_open_pin( params.device, hash_pcr_mask, pcr_bank, - pubkey, pubkey_size, + &pubkey, pubkey_pcr_mask, params.signature_path, pin_string, params.pcrlock_path, primary_alg, - blob, - blob_size, - policy_hash, - policy_hash_size, - salt, - salt_size, - srk_buf, - srk_buf_size, + &blob, + &policy_hash, + &salt, + &srk, + &pcrlock_nv, flags, - &decrypted_key, - &decrypted_key_size); + &decrypted_key); if (r < 0) return log_debug_open_error(cd, r); /* Before using this key as passphrase we base64 encode it, for compat with homed */ - base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded); + 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); @@ -177,9 +168,8 @@ _public_ void cryptsetup_token_dump( const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) { _cleanup_free_ char *hash_pcrs_str = NULL, *pubkey_pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL, *pubkey_str = NULL; - _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL, *salt = NULL, *srk_buf = NULL; + _cleanup_(iovec_done) struct iovec blob = {}, pubkey = {}, policy_hash = {}, salt = {}, srk = {}, pcrlock_nv = {}; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; - size_t blob_size, policy_hash_size, pubkey_size, salt_size = 0, srk_buf_size = 0; uint32_t hash_pcr_mask, pubkey_pcr_mask; uint16_t pcr_bank, primary_alg; TPM2Flags flags = 0; @@ -197,17 +187,13 @@ _public_ void cryptsetup_token_dump( &hash_pcr_mask, &pcr_bank, &pubkey, - &pubkey_size, &pubkey_pcr_mask, &primary_alg, &blob, - &blob_size, &policy_hash, - &policy_hash_size, &salt, - &salt_size, - &srk_buf, - &srk_buf_size, + &srk, + &pcrlock_nv, &flags); if (r < 0) return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m"); @@ -220,15 +206,15 @@ _public_ void cryptsetup_token_dump( if (!pubkey_pcrs_str) return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m"); - r = crypt_dump_buffer_to_hex_string(blob, blob_size, &blob_str); + r = crypt_dump_buffer_to_hex_string(blob.iov_base, blob.iov_len, &blob_str); if (r < 0) return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m"); - r = crypt_dump_buffer_to_hex_string(pubkey, pubkey_size, &pubkey_str); + r = crypt_dump_buffer_to_hex_string(pubkey.iov_base, pubkey.iov_len, &pubkey_str); if (r < 0) return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m"); - r = crypt_dump_buffer_to_hex_string(policy_hash, policy_hash_size, &policy_hash_str); + r = crypt_dump_buffer_to_hex_string(policy_hash.iov_base, policy_hash.iov_len, &policy_hash_str); if (r < 0) return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m"); @@ -241,8 +227,9 @@ _public_ void cryptsetup_token_dump( crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str); crypt_log(cd, "\ttpm2-pin: %s\n", true_false(flags & TPM2_FLAGS_USE_PIN)); crypt_log(cd, "\ttpm2-pcrlock: %s\n", true_false(flags & TPM2_FLAGS_USE_PCRLOCK)); - crypt_log(cd, "\ttpm2-salt: %s\n", true_false(salt)); - crypt_log(cd, "\ttpm2-srk: %s\n", true_false(srk_buf)); + crypt_log(cd, "\ttpm2-salt: %s\n", true_false(iovec_is_set(&salt))); + crypt_log(cd, "\ttpm2-srk: %s\n", true_false(iovec_is_set(&srk))); + crypt_log(cd, "\ttpm2-pcrlock-nv: %s\n", true_false(iovec_is_set(&pcrlock_nv))); } /* @@ -326,7 +313,7 @@ _public_ int cryptsetup_token_validate( return 1; } - r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL); + r = unbase64mem(json_variant_string(w), NULL, NULL); if (r < 0) return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-blob' field: %m"); @@ -336,7 +323,7 @@ _public_ int cryptsetup_token_validate( return 1; } - r = unhexmem(json_variant_string(w), SIZE_MAX, NULL, NULL); + r = unhexmem(json_variant_string(w), NULL, NULL); if (r < 0) return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-policy-hash' field: %m"); diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c index a1c85e6..5b38613 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c +++ b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c @@ -104,7 +104,7 @@ int parse_luks2_fido2_data( if (!w) return -EINVAL; - r = unbase64mem(json_variant_string(w), SIZE_MAX, &cid, &cid_size); + r = unbase64mem(json_variant_string(w), &cid, &cid_size); if (r < 0) return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-credentials' field: %m"); @@ -112,7 +112,7 @@ int parse_luks2_fido2_data( if (!w) return -EINVAL; - r = unbase64mem(json_variant_string(w), SIZE_MAX, &salt, &salt_size); + r = unbase64mem(json_variant_string(w), &salt, &salt_size); if (r < 0) return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-salt' field: %m"); diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c b/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c index 178fc7a..ac5100f 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c +++ b/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c @@ -157,7 +157,7 @@ static int acquire_luks2_key_systemd( assert(params); data.friendly_name = params->friendly_name; - data.headless = params->headless; + data.askpw_credential = params->askpw_credential; data.askpw_flags = params->askpw_flags; data.until = params->until; @@ -260,7 +260,7 @@ int parse_luks2_pkcs11_data( if (!w) return -EINVAL; - r = unbase64mem(json_variant_string(w), SIZE_MAX, &key, &key_size); + r = unbase64mem(json_variant_string(w), &key, &key_size); if (r < 0) return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m."); diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c index 846679f..08f901c 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c @@ -17,33 +17,27 @@ int acquire_luks2_key( const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, - const void *pubkey, - size_t pubkey_size, + const struct iovec *pubkey, uint32_t pubkey_pcr_mask, const char *signature_path, const char *pin, const char *pcrlock_path, uint16_t primary_alg, - const void *key_data, - size_t key_data_size, - const void *policy_hash, - size_t policy_hash_size, - const void *salt, - size_t salt_size, - const void *srk_buf, - size_t srk_buf_size, + const struct iovec *blob, + const struct iovec *policy_hash, + const struct iovec *salt, + const struct iovec *srk, + const struct iovec *pcrlock_nv, TPM2Flags flags, - void **ret_decrypted_key, - size_t *ret_decrypted_key_size) { + struct iovec *ret_decrypted_key) { _cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL; _cleanup_free_ char *auto_device = NULL; _cleanup_(erase_and_freep) char *b64_salted_pin = NULL; int r; - assert(salt || salt_size == 0); + assert(iovec_is_valid(salt)); assert(ret_decrypted_key); - assert(ret_decrypted_key_size); if (!device) { r = tpm2_find_device_auto(&auto_device); @@ -58,10 +52,10 @@ int acquire_luks2_key( if ((flags & TPM2_FLAGS_USE_PIN) && !pin) return -ENOANO; - if (pin && salt_size > 0) { + if (pin && iovec_is_set(salt)) { uint8_t salted_pin[SHA256_DIGEST_SIZE] = {}; CLEANUP_ERASE(salted_pin); - r = tpm2_util_pbkdf2_hmac_sha256(pin, strlen(pin), salt, salt_size, salted_pin); + r = tpm2_util_pbkdf2_hmac_sha256(pin, strlen(pin), salt->iov_base, salt->iov_len, salted_pin); if (r < 0) return log_error_errno(r, "Failed to perform PBKDF2: %m"); @@ -82,6 +76,14 @@ int acquire_luks2_key( r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy); if (r < 0) return r; + if (r == 0) { + /* Not found? Then search among passed credentials */ + r = tpm2_pcrlock_policy_from_credentials(srk, pcrlock_nv, &pcrlock_policy); + if (r < 0) + return r; + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Couldn't find pcrlock policy for volume."); + } } _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; @@ -92,16 +94,16 @@ int acquire_luks2_key( r = tpm2_unseal(tpm2_context, hash_pcr_mask, pcr_bank, - pubkey, pubkey_size, + pubkey, pubkey_pcr_mask, signature_json, pin, FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL, primary_alg, - key_data, key_data_size, - policy_hash, policy_hash_size, - srk_buf, srk_buf_size, - ret_decrypted_key, ret_decrypted_key_size); + blob, + policy_hash, + srk, + ret_decrypted_key); if (r < 0) return log_error_errno(r, "Failed to unseal secret using TPM2: %m"); diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h index 8408bab..c3a01df 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h @@ -10,21 +10,16 @@ int acquire_luks2_key( const char *device, uint32_t pcr_mask, uint16_t pcr_bank, - const void *pubkey, - size_t pubkey_size, + const struct iovec *pubkey, uint32_t pubkey_pcr_mask, const char *signature_path, const char *pin, const char *pcrlock_path, uint16_t primary_alg, - const void *key_data, - size_t key_data_size, - const void *policy_hash, - size_t policy_hash_size, - const void *salt, - size_t salt_size, - const void *srk_buf, - size_t srk_buf_size, + const struct iovec *key_data, + const struct iovec *policy_hash, + const struct iovec *salt, + const struct iovec *srk, + const struct iovec *pcrlock_nv, TPM2Flags flags, - void **ret_decrypted_key, - size_t *ret_decrypted_key_size); + struct iovec *decrypted_key); diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c deleted file mode 100644 index e7a38d4..0000000 --- a/src/cryptsetup/cryptsetup-tpm2.c +++ /dev/null @@ -1,314 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "alloc-util.h" -#include "ask-password-api.h" -#include "cryptsetup-tpm2.h" -#include "env-util.h" -#include "fileio.h" -#include "hexdecoct.h" -#include "json.h" -#include "parse-util.h" -#include "random-util.h" -#include "sha256.h" -#include "tpm2-util.h" - -static int get_pin(usec_t until, AskPasswordFlags ask_password_flags, bool headless, char **ret_pin_str) { - _cleanup_(erase_and_freep) char *pin_str = NULL; - _cleanup_strv_free_erase_ char **pin = NULL; - int r; - - assert(ret_pin_str); - - r = getenv_steal_erase("PIN", &pin_str); - if (r < 0) - return log_error_errno(r, "Failed to acquire PIN from environment: %m"); - if (!r) { - if (headless) - return log_error_errno( - SYNTHETIC_ERRNO(ENOPKG), - "PIN querying disabled via 'headless' option. " - "Use the '$PIN' environment variable."); - - pin = strv_free_erase(pin); - r = ask_password_auto( - "Please enter TPM2 PIN:", - "drive-harddisk", - NULL, - "tpm2-pin", - "cryptsetup.tpm2-pin", - until, - ask_password_flags, - &pin); - if (r < 0) - return log_error_errno(r, "Failed to ask for user pin: %m"); - assert(strv_length(pin) == 1); - - pin_str = strdup(pin[0]); - if (!pin_str) - return log_oom(); - } - - *ret_pin_str = TAKE_PTR(pin_str); - - return r; -} - -int acquire_tpm2_key( - const char *volume_name, - const char *device, - uint32_t hash_pcr_mask, - uint16_t pcr_bank, - const void *pubkey, - size_t pubkey_size, - uint32_t pubkey_pcr_mask, - const char *signature_path, - const char *pcrlock_path, - uint16_t primary_alg, - const char *key_file, - size_t key_file_size, - uint64_t key_file_offset, - const void *key_data, - size_t key_data_size, - const void *policy_hash, - size_t policy_hash_size, - const void *salt, - size_t salt_size, - const void *srk_buf, - size_t srk_buf_size, - TPM2Flags flags, - usec_t until, - bool headless, - AskPasswordFlags ask_password_flags, - void **ret_decrypted_key, - size_t *ret_decrypted_key_size) { - - _cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL; - _cleanup_free_ void *loaded_blob = NULL; - _cleanup_free_ char *auto_device = NULL; - size_t blob_size; - const void *blob; - int r; - - assert(salt || salt_size == 0); - - if (!device) { - r = tpm2_find_device_auto(&auto_device); - if (r == -ENODEV) - return -EAGAIN; /* Tell the caller to wait for a TPM2 device to show up */ - if (r < 0) - return log_error_errno(r, "Could not find TPM2 device: %m"); - - device = auto_device; - } - - if (key_data) { - blob = key_data; - blob_size = key_data_size; - } else { - _cleanup_free_ char *bindname = NULL; - - /* If we read the salt via AF_UNIX, make this client recognizable */ - if (asprintf(&bindname, "@%" PRIx64"/cryptsetup-tpm2/%s", random_u64(), volume_name) < 0) - return log_oom(); - - r = read_full_file_full( - AT_FDCWD, key_file, - key_file_offset == 0 ? UINT64_MAX : key_file_offset, - key_file_size == 0 ? SIZE_MAX : key_file_size, - READ_FULL_FILE_CONNECT_SOCKET, - bindname, - (char**) &loaded_blob, &blob_size); - if (r < 0) - return r; - - blob = loaded_blob; - } - - if (pubkey_pcr_mask != 0) { - r = tpm2_load_pcr_signature(signature_path, &signature_json); - if (r < 0) - return log_error_errno(r, "Failed to load pcr signature: %m"); - } - - _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {}; - - if (FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK)) { - r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy); - if (r < 0) - return r; - } - - _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; - r = tpm2_context_new_or_warn(device, &tpm2_context); - if (r < 0) - return r; - - if (!(flags & TPM2_FLAGS_USE_PIN)) { - r = tpm2_unseal(tpm2_context, - hash_pcr_mask, - pcr_bank, - pubkey, pubkey_size, - pubkey_pcr_mask, - signature_json, - /* pin= */ NULL, - FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL, - primary_alg, - blob, - blob_size, - policy_hash, - policy_hash_size, - srk_buf, - srk_buf_size, - ret_decrypted_key, - ret_decrypted_key_size); - if (r < 0) - return log_error_errno(r, "Failed to unseal secret using TPM2: %m"); - - return r; - } - - for (int i = 5;; i--) { - _cleanup_(erase_and_freep) char *pin_str = NULL, *b64_salted_pin = NULL; - - if (i <= 0) - return -EACCES; - - r = get_pin(until, ask_password_flags, headless, &pin_str); - if (r < 0) - return r; - - if (salt_size > 0) { - uint8_t salted_pin[SHA256_DIGEST_SIZE] = {}; - CLEANUP_ERASE(salted_pin); - - r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), salt, salt_size, salted_pin); - if (r < 0) - return log_error_errno(r, "Failed to perform PBKDF2: %m"); - - r = base64mem(salted_pin, sizeof(salted_pin), &b64_salted_pin); - if (r < 0) - return log_error_errno(r, "Failed to base64 encode salted pin: %m"); - } else - /* no salting needed, backwards compat with non-salted pins */ - b64_salted_pin = TAKE_PTR(pin_str); - - r = tpm2_unseal(tpm2_context, - hash_pcr_mask, - pcr_bank, - pubkey, pubkey_size, - pubkey_pcr_mask, - signature_json, - b64_salted_pin, - pcrlock_path ? &pcrlock_policy : NULL, - primary_alg, - blob, - blob_size, - policy_hash, - policy_hash_size, - srk_buf, - srk_buf_size, - ret_decrypted_key, - ret_decrypted_key_size); - 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 (!IN_SET(r, -EPERM, -ENOLCK)) - continue; - } - - return r; - } -} - -int find_tpm2_auto_data( - struct crypt_device *cd, - uint32_t search_pcr_mask, - int start_token, - uint32_t *ret_hash_pcr_mask, - uint16_t *ret_pcr_bank, - void **ret_pubkey, - size_t *ret_pubkey_size, - uint32_t *ret_pubkey_pcr_mask, - uint16_t *ret_primary_alg, - void **ret_blob, - size_t *ret_blob_size, - void **ret_policy_hash, - size_t *ret_policy_hash_size, - void **ret_salt, - size_t *ret_salt_size, - void **ret_srk_buf, - size_t *ret_srk_buf_size, - TPM2Flags *ret_flags, - int *ret_keyslot, - int *ret_token) { - - int r, token; - - assert(cd); - - for (token = start_token; token < sym_crypt_token_max(CRYPT_LUKS2); token++) { - _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL, *salt = NULL, *srk_buf = NULL; - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; - size_t blob_size, policy_hash_size, pubkey_size, salt_size = 0, srk_buf_size = 0; - uint32_t hash_pcr_mask, pubkey_pcr_mask; - uint16_t pcr_bank, primary_alg; - TPM2Flags flags; - int keyslot; - - r = cryptsetup_get_token_as_json(cd, token, "systemd-tpm2", &v); - if (IN_SET(r, -ENOENT, -EINVAL, -EMEDIUMTYPE)) - continue; - if (r < 0) - return log_error_errno(r, "Failed to read JSON token data off disk: %m"); - - r = tpm2_parse_luks2_json( - v, - &keyslot, - &hash_pcr_mask, - &pcr_bank, - &pubkey, &pubkey_size, - &pubkey_pcr_mask, - &primary_alg, - &blob, &blob_size, - &policy_hash, &policy_hash_size, - &salt, &salt_size, - &srk_buf, &srk_buf_size, - &flags); - if (r == -EUCLEAN) /* Gracefully handle issues in JSON fields not owned by us */ - continue; - if (r < 0) - return log_error_errno(r, "Failed to parse TPM2 JSON data: %m"); - - if (search_pcr_mask == UINT32_MAX || - search_pcr_mask == hash_pcr_mask) { - - if (start_token <= 0) - log_info("Automatically discovered security TPM2 token unlocks volume."); - - *ret_hash_pcr_mask = hash_pcr_mask; - *ret_pcr_bank = pcr_bank; - *ret_pubkey = TAKE_PTR(pubkey); - *ret_pubkey_size = pubkey_size; - *ret_pubkey_pcr_mask = pubkey_pcr_mask; - *ret_primary_alg = primary_alg; - *ret_blob = TAKE_PTR(blob); - *ret_blob_size = blob_size; - *ret_policy_hash = TAKE_PTR(policy_hash); - *ret_policy_hash_size = policy_hash_size; - *ret_salt = TAKE_PTR(salt); - *ret_salt_size = salt_size; - *ret_keyslot = keyslot; - *ret_token = token; - *ret_srk_buf = TAKE_PTR(srk_buf); - *ret_srk_buf_size = srk_buf_size; - *ret_flags = flags; - return 0; - } - - /* PCR mask doesn't match what is configured, ignore this entry, let's see next */ - } - - return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No valid TPM2 token data found."); -} diff --git a/src/cryptsetup/cryptsetup-tpm2.h b/src/cryptsetup/cryptsetup-tpm2.h deleted file mode 100644 index a50a943..0000000 --- a/src/cryptsetup/cryptsetup-tpm2.h +++ /dev/null @@ -1,126 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <sys/types.h> - -#include "ask-password-api.h" -#include "cryptsetup-util.h" -#include "log.h" -#include "time-util.h" -#include "tpm2-util.h" - -#if HAVE_TPM2 - -int acquire_tpm2_key( - const char *volume_name, - const char *device, - uint32_t hash_pcr_mask, - uint16_t pcr_bank, - const void *pubkey, - size_t pubkey_size, - uint32_t pubkey_pcr_mask, - const char *signature_path, - const char *pcrlock_path, - uint16_t primary_alg, - const char *key_file, - size_t key_file_size, - uint64_t key_file_offset, - const void *key_data, - size_t key_data_size, - const void *policy_hash, - size_t policy_hash_size, - const void *salt, - size_t salt_size, - const void *srk_buf, - size_t salt_srk_buf_size, - TPM2Flags flags, - usec_t until, - bool headless, - AskPasswordFlags ask_password_flags, - void **ret_decrypted_key, - size_t *ret_decrypted_key_size); - -int find_tpm2_auto_data( - struct crypt_device *cd, - uint32_t search_pcr_mask, - int start_token, - uint32_t *ret_hash_pcr_mask, - uint16_t *ret_pcr_bank, - void **ret_pubkey, - size_t *ret_pubkey_size, - uint32_t *ret_pubkey_pcr_mask, - uint16_t *ret_primary_alg, - void **ret_blob, - size_t *ret_blob_size, - void **ret_policy_hash, - size_t *ret_policy_hash_size, - void **ret_salt, - size_t *ret_salt_size, - void **ret_srk_buf, - size_t *ret_srk_size, - TPM2Flags *ret_flags, - int *ret_keyslot, - int *ret_token); - -#else - -static inline int acquire_tpm2_key( - const char *volume_name, - const char *device, - uint32_t hash_pcr_mask, - uint16_t pcr_bank, - const void *pubkey, - size_t pubkey_size, - uint32_t pubkey_pcr_mask, - const char *signature_path, - const char *pcrlock_path, - uint16_t primary_alg, - const char *key_file, - size_t key_file_size, - uint64_t key_file_offset, - const void *key_data, - size_t key_data_size, - const void *policy_hash, - size_t policy_hash_size, - const void *salt, - size_t salt_size, - const void *srk_buf, - size_t salt_srk_buf_size, - TPM2Flags flags, - usec_t until, - bool headless, - AskPasswordFlags ask_password_flags, - void **ret_decrypted_key, - size_t *ret_decrypted_key_size) { - - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "TPM2 support not available."); -} - -static inline int find_tpm2_auto_data( - struct crypt_device *cd, - uint32_t search_pcr_mask, - int start_token, - uint32_t *ret_hash_pcr_mask, - uint16_t *ret_pcr_bank, - void **ret_pubkey, - size_t *ret_pubkey_size, - uint32_t *ret_pubkey_pcr_mask, - uint16_t *ret_primary_alg, - void **ret_blob, - size_t *ret_blob_size, - void **ret_policy_hash, - size_t *ret_policy_hash_size, - void **ret_salt, - size_t *ret_salt_size, - void **ret_srk_buf, - size_t *ret_srk_size, - TPM2Flags *ret_flags, - int *ret_keyslot, - int *ret_token) { - - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "TPM2 support not available."); -} - -#endif diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 1822beb..85897ae 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -101,10 +101,12 @@ static uint32_t arg_tpm2_pcr_mask = UINT32_MAX; static char *arg_tpm2_signature = NULL; static bool arg_tpm2_pin = false; static char *arg_tpm2_pcrlock = NULL; -static bool arg_headless = false; static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC; static unsigned arg_tpm2_measure_pcr = UINT_MAX; /* This and the following field is about measuring the unlocked volume key to the local TPM */ static char **arg_tpm2_measure_banks = NULL; +static char *arg_link_keyring = NULL; +static char *arg_link_key_type = NULL; +static char *arg_link_key_description = NULL; STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep); STATIC_DESTRUCTOR_REGISTER(arg_hash, freep); @@ -118,6 +120,9 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_measure_banks, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep); +STATIC_DESTRUCTOR_REGISTER(arg_link_keyring, freep); +STATIC_DESTRUCTOR_REGISTER(arg_link_key_type, freep); +STATIC_DESTRUCTOR_REGISTER(arg_link_key_description, freep); static const char* const passphrase_type_table[_PASSPHRASE_TYPE_MAX] = { [PASSPHRASE_REGULAR] = "passphrase", @@ -338,7 +343,7 @@ static int parse_one_option(const char *option) { arg_pkcs11_uri_auto = true; } else { if (!pkcs11_uri_valid(val)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "pkcs11-uri= parameter expects a PKCS#11 URI, refusing"); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "pkcs11-uri= parameter expects a PKCS#11 URI, refusing."); r = free_and_strdup(&arg_pkcs11_uri, val); if (r < 0) @@ -368,7 +373,7 @@ static int parse_one_option(const char *option) { _cleanup_free_ void *cid = NULL; size_t cid_size; - r = unbase64mem(val, SIZE_MAX, &cid, &cid_size); + r = unbase64mem(val, &cid, &cid_size); if (r < 0) return log_error_errno(r, "Failed to decode FIDO2 CID data: %m"); @@ -498,9 +503,9 @@ static int parse_one_option(const char *option) { return 0; } - arg_headless = r; + SET_FLAG(arg_ask_password_flags, ASK_PASSWORD_HEADLESS, r); } else if (streq(option, "headless")) - arg_headless = true; + arg_ask_password_flags |= ASK_PASSWORD_HEADLESS; else if ((val = startswith(option, "token-timeout="))) { @@ -508,6 +513,56 @@ static int parse_one_option(const char *option) { if (r < 0) log_warning_errno(r, "Failed to parse %s, ignoring: %m", option); + } else if ((val = startswith(option, "link-volume-key="))) { +#ifdef HAVE_CRYPT_SET_KEYRING_TO_LINK + const char *sep, *c; + _cleanup_free_ char *keyring = NULL, *key_type = NULL, *key_description = NULL; + + /* Stick with cryptsetup --link-vk-to-keyring format + * <keyring_description>::%<key_type>:<key_description>, + * where %<key_type> is optional and defaults to 'user'. + */ + if (!(sep = strstr(val, "::"))) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse link-volume-key= option value: %s", val); + + /* cryptsetup (cli) supports <keyring_description> passed in various formats: + * - well-known keyrings prefixed with '@' (@u user, @s session, etc) + * - text descriptions prefixed with "%:" or "%keyring:". + * - text description with no prefix. + * - numeric keyring id (ignored in current patch set). */ + if (IN_SET(*val, '@', '%')) + keyring = strndup(val, sep - val); + else + /* add type prefix if missing (crypt_set_keyring_to_link() expects it) */ + keyring = strnappend("%:", val, sep - val); + if (!keyring) + return log_oom(); + + sep += 2; + + /* %<key_type> is optional (and defaults to 'user') */ + if (*sep == '%') { + /* must be separated by colon */ + if (!(c = strchr(sep, ':'))) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse link-volume-key= option value: %s", val); + + key_type = strndup(sep + 1, c - sep - 1); + if (!key_type) + return log_oom(); + + sep = c + 1; + } + + key_description = strdup(sep); + if (!key_description) + return log_oom(); + + free_and_replace(arg_link_keyring, keyring); + free_and_replace(arg_link_key_type, key_type); + free_and_replace(arg_link_key_description, key_description); +#else + log_error("Build lacks libcryptsetup support for linking volume keys in user specified kernel keyrings upon device activation, ignoring: %s", option); +#endif } else if (!streq(option, "x-initrd.attach")) log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option); @@ -742,17 +797,16 @@ static int get_password( PassphraseType passphrase_type, char ***ret) { - _cleanup_free_ char *friendly = NULL, *text = NULL, *disk_path = NULL; + _cleanup_free_ char *friendly = NULL, *text = NULL, *disk_path = NULL, *id = NULL; _cleanup_strv_free_erase_ char **passwords = NULL; - char *id; - int r = 0; AskPasswordFlags flags = arg_ask_password_flags | ASK_PASSWORD_PUSH_CACHE; + int r; assert(vol); assert(src); assert(ret); - if (arg_headless) + if (FLAGS_SET(arg_ask_password_flags, ASK_PASSWORD_HEADLESS)) return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "Password querying disabled via 'headless' option."); friendly = friendly_disk_name(src, vol); @@ -766,11 +820,23 @@ static int get_password( if (!disk_path) return log_oom(); - id = strjoina("cryptsetup:", disk_path); + id = strjoin("cryptsetup:", disk_path); + if (!id) + return log_oom(); + + AskPasswordRequest req = { + .message = text, + .icon = "drive-harddisk", + .id = id, + .keyring = "cryptsetup", + .credential = "cryptsetup.passphrase", + }; - r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", "cryptsetup.passphrase", until, - flags | (accept_cached*ASK_PASSWORD_ACCEPT_CACHED), - &passwords); + r = ask_password_auto( + &req, + until, + flags | (accept_cached*ASK_PASSWORD_ACCEPT_CACHED), + &passwords); if (r < 0) return log_error_errno(r, "Failed to query password: %m"); @@ -779,12 +845,19 @@ static int get_password( assert(strv_length(passwords) == 1); + text = mfree(text); if (asprintf(&text, "Please enter %s for disk %s (verification):", passphrase_type_to_string(passphrase_type), friendly) < 0) return log_oom(); - id = strjoina("cryptsetup-verification:", disk_path); + free(id); + id = strjoin("cryptsetup-verification:", disk_path); + if (!id) + return log_oom(); - r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", "cryptsetup.passphrase", until, flags, &passwords2); + req.message = text; + req.id = id; + + r = ask_password_auto(&req, until, flags, &passwords2); if (r < 0) return log_error_errno(r, "Failed to query verification password: %m"); @@ -1192,12 +1265,11 @@ static int crypt_activate_by_token_pin_ask_password( const char *name, const char *type, usec_t until, - bool headless, void *userdata, uint32_t activation_flags, const char *message, - const char *key_name, - const char *credential_name) { + const char *keyring, + const char *credential) { #if HAVE_LIBCRYPTSETUP_PLUGINS AskPasswordFlags flags = arg_ask_password_flags | ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED; @@ -1222,12 +1294,20 @@ static int crypt_activate_by_token_pin_ask_password( return r; } - if (headless) + if (FLAGS_SET(arg_ask_password_flags, ASK_PASSWORD_HEADLESS)) return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable."); for (;;) { pins = strv_free_erase(pins); - r = ask_password_auto(message, "drive-harddisk", /* id= */ NULL, key_name, credential_name, until, flags, &pins); + + AskPasswordRequest req = { + .message = message, + .icon = "drive-harddisk", + .keyring = keyring, + .credential = credential, + }; + + r = ask_password_auto(&req, until, flags, &pins); if (r < 0) return r; @@ -1251,7 +1331,6 @@ static int attach_luks2_by_fido2_via_plugin( struct crypt_device *cd, const char *name, usec_t until, - bool headless, void *userdata, uint32_t activation_flags) { @@ -1260,7 +1339,6 @@ static int attach_luks2_by_fido2_via_plugin( name, "systemd-fido2", until, - headless, userdata, activation_flags, "Please enter security token PIN:", @@ -1315,7 +1393,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2( for (;;) { if (use_libcryptsetup_plugin && !arg_fido2_cid) { - r = attach_luks2_by_fido2_via_plugin(cd, name, until, arg_headless, arg_fido2_device, flags); + r = attach_luks2_by_fido2_via_plugin(cd, name, until, arg_fido2_device, flags); if (IN_SET(r, -ENOTUNIQ, -ENXIO, -ENOENT)) return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking."); @@ -1331,10 +1409,11 @@ static int attach_luks_or_plain_or_bitlk_by_fido2( key_file, arg_keyfile_size, arg_keyfile_offset, key_data, key_data_size, until, - arg_headless, required, - &decrypted_key, &decrypted_key_size, - arg_ask_password_flags); + "cryptsetup.fido2-pin", + arg_ask_password_flags, + &decrypted_key, + &decrypted_key_size); else r = acquire_fido2_key_auto( cd, @@ -1342,9 +1421,10 @@ static int attach_luks_or_plain_or_bitlk_by_fido2( friendly, arg_fido2_device, until, - arg_headless, - &decrypted_key, &decrypted_key_size, - arg_ask_password_flags); + "cryptsetup.fido2-pin", + arg_ask_password_flags, + &decrypted_key, + &decrypted_key_size); if (r >= 0) break; } @@ -1405,7 +1485,7 @@ static int attach_luks2_by_pkcs11_via_plugin( const char *name, const char *friendly_name, usec_t until, - bool headless, + const char *askpw_credential, uint32_t flags) { #if HAVE_LIBCRYPTSETUP_PLUGINS @@ -1417,7 +1497,7 @@ static int attach_luks2_by_pkcs11_via_plugin( systemd_pkcs11_plugin_params params = { .friendly_name = friendly_name, .until = until, - .headless = headless, + .askpw_credential = askpw_credential, .askpw_flags = arg_ask_password_flags, }; @@ -1481,7 +1561,13 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11( for (;;) { if (use_libcryptsetup_plugin && arg_pkcs11_uri_auto) - r = attach_luks2_by_pkcs11_via_plugin(cd, name, friendly, until, arg_headless, flags); + r = attach_luks2_by_pkcs11_via_plugin( + cd, + name, + friendly, + until, + "cryptsetup.pkcs11-pin", + flags); else { r = decrypt_pkcs11_key( name, @@ -1490,7 +1576,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11( key_file, arg_keyfile_size, arg_keyfile_offset, key_data, key_data_size, until, - arg_headless, + arg_ask_password_flags, &decrypted_key, &decrypted_key_size); if (r >= 0) break; @@ -1615,7 +1701,6 @@ static int attach_luks2_by_tpm2_via_plugin( struct crypt_device *cd, const char *name, usec_t until, - bool headless, uint32_t flags) { #if HAVE_LIBCRYPTSETUP_PLUGINS @@ -1635,7 +1720,6 @@ static int attach_luks2_by_tpm2_via_plugin( name, "systemd-tpm2", until, - headless, ¶ms, flags, "Please enter TPM2 PIN:", @@ -1650,18 +1734,16 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( struct crypt_device *cd, const char *name, const char *key_file, - const void *key_data, - size_t key_data_size, + const struct iovec *key_data, usec_t until, uint32_t flags, bool pass_volume_key) { _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; - _cleanup_(erase_and_freep) void *decrypted_key = NULL; + _cleanup_(iovec_done_erase) struct iovec decrypted_key = {}; _cleanup_(sd_event_unrefp) sd_event *event = NULL; _cleanup_free_ char *friendly = NULL; int keyslot = arg_key_slot, r; - size_t decrypted_key_size; assert(cd); assert(name); @@ -1672,7 +1754,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( return log_oom(); for (;;) { - if (key_file || key_data) { + if (key_file || iovec_is_set(key_data)) { /* If key data is specified, use that */ r = acquire_tpm2_key( @@ -1680,21 +1762,22 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( arg_tpm2_device, arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask, UINT16_MAX, - /* pubkey= */ NULL, /* pubkey_size= */ 0, + /* pubkey= */ NULL, /* pubkey_pcr_mask= */ 0, /* signature_path= */ NULL, /* pcrlock_path= */ NULL, /* primary_alg= */ 0, key_file, arg_keyfile_size, arg_keyfile_offset, - key_data, key_data_size, - /* policy_hash= */ NULL, /* policy_hash_size= */ 0, /* we don't know the policy hash */ - /* salt= */ NULL, /* salt_size= */ 0, - /* srk_buf= */ NULL, /* srk_buf_size= */ 0, + key_data, + /* policy_hash= */ NULL, /* we don't know the policy hash */ + /* salt= */ NULL, + /* srk= */ NULL, + /* pcrlock_nv= */ NULL, arg_tpm2_pin ? TPM2_FLAGS_USE_PIN : 0, until, - arg_headless, + "cryptsetup.tpm2-pin", arg_ask_password_flags, - &decrypted_key, &decrypted_key_size); + &decrypted_key); if (r >= 0) break; if (IN_SET(r, -EACCES, -ENOLCK)) @@ -1707,7 +1790,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( return -EAGAIN; /* Mangle error code: let's make any form of TPM2 failure non-fatal. */ } } else { - r = attach_luks2_by_tpm2_via_plugin(cd, name, until, arg_headless, flags); + r = attach_luks2_by_tpm2_via_plugin(cd, name, until, flags); if (r >= 0) return 0; /* EAGAIN means: no tpm2 chip found @@ -1725,8 +1808,6 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( } if (r == -EOPNOTSUPP) { /* Plugin not available, let's process TPM2 stuff right here instead */ - _cleanup_free_ void *blob = NULL, *policy_hash = NULL; - size_t blob_size, policy_hash_size; bool found_some = false; int token = 0; /* first token to look at */ @@ -1735,8 +1816,8 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( * works. */ for (;;) { - _cleanup_free_ void *pubkey = NULL, *salt = NULL, *srk_buf = NULL; - size_t pubkey_size = 0, salt_size = 0, srk_buf_size = 0; + _cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {}; + _cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {}; uint32_t hash_pcr_mask, pubkey_pcr_mask; uint16_t pcr_bank, primary_alg; TPM2Flags tpm2_flags; @@ -1747,13 +1828,14 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( token, /* search for the token with this index, or any later index than this */ &hash_pcr_mask, &pcr_bank, - &pubkey, &pubkey_size, + &pubkey, &pubkey_pcr_mask, &primary_alg, - &blob, &blob_size, - &policy_hash, &policy_hash_size, - &salt, &salt_size, - &srk_buf, &srk_buf_size, + &blob, + &policy_hash, + &salt, + &srk, + &pcrlock_nv, &tpm2_flags, &keyslot, &token); @@ -1778,21 +1860,22 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( arg_tpm2_device, hash_pcr_mask, pcr_bank, - pubkey, pubkey_size, + &pubkey, pubkey_pcr_mask, arg_tpm2_signature, arg_tpm2_pcrlock, primary_alg, /* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */ - blob, blob_size, - policy_hash, policy_hash_size, - salt, salt_size, - srk_buf, srk_buf_size, + &blob, + &policy_hash, + &salt, + &srk, + &pcrlock_nv, tpm2_flags, until, - arg_headless, + "cryptsetup.tpm2-pin", arg_ask_password_flags, - &decrypted_key, &decrypted_key_size); + &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) @@ -1837,17 +1920,16 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( log_debug("Got one or more potentially relevant udev events, rescanning for TPM2..."); } - assert(decrypted_key); if (pass_volume_key) - r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags); + r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key.iov_base, decrypted_key.iov_len, flags); else { _cleanup_(erase_and_freep) char *base64_encoded = NULL; ssize_t base64_encoded_size; /* Before using this key as passphrase we base64 encode it, for compat with homed */ - base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded); + base64_encoded_size = base64mem(decrypted_key.iov_base, decrypted_key.iov_len, &base64_encoded); if (base64_encoded_size < 0) return log_oom(); @@ -2045,7 +2127,7 @@ static int attach_luks_or_plain_or_bitlk( crypt_get_device_name(cd)); if (arg_tpm2_device || arg_tpm2_device_auto) - return attach_luks_or_plain_or_bitlk_by_tpm2(cd, name, key_file, key_data, key_data_size, until, flags, pass_volume_key); + return attach_luks_or_plain_or_bitlk_by_tpm2(cd, name, key_file, &IOVEC_MAKE(key_data, key_data_size), until, flags, pass_volume_key); if (arg_fido2_device || arg_fido2_device_auto) return attach_luks_or_plain_or_bitlk_by_fido2(cd, name, key_file, key_data, key_data_size, until, flags, pass_volume_key); if (arg_pkcs11_uri || arg_pkcs11_uri_auto) @@ -2289,6 +2371,15 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to load LUKS superblock on device %s: %m", crypt_get_device_name(cd)); +/* since cryptsetup 2.7.0 (Jan 2024) */ +#if HAVE_CRYPT_SET_KEYRING_TO_LINK + if (arg_link_key_description) { + r = crypt_set_keyring_to_link(cd, arg_link_key_description, NULL, arg_link_key_type, arg_link_keyring); + if (r < 0) + log_warning_errno(r, "Failed to set keyring or key description to link volume key in, ignoring: %m"); + } +#endif + if (arg_header) { r = crypt_set_data_device(cd, source); if (r < 0) @@ -2302,7 +2393,6 @@ static int run(int argc, char *argv[]) { volume, /* type= */ NULL, until, - arg_headless, /* userdata= */ NULL, flags, "Please enter LUKS2 token PIN:", @@ -2326,8 +2416,10 @@ static int run(int argc, char *argv[]) { } #endif + bool use_cached_passphrase = true; + _cleanup_strv_free_erase_ char **passwords = NULL; for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { - _cleanup_strv_free_erase_ char **passwords = NULL; + log_debug("Beginning attempt %u to unlock.", tries); /* When we were able to acquire multiple keys, let's always process them in this order: * @@ -2338,7 +2430,9 @@ static int run(int argc, char *argv[]) { * 5. We enquire the user for a password */ - if (!key_file && !key_data && !arg_pkcs11_uri && !arg_pkcs11_uri_auto && !arg_fido2_device && !arg_fido2_device_auto && !arg_tpm2_device && !arg_tpm2_device_auto) { + if (!passwords && !key_file && !key_data && !arg_pkcs11_uri && !arg_pkcs11_uri_auto && !arg_fido2_device && !arg_fido2_device_auto && !arg_tpm2_device && !arg_tpm2_device_auto) { + + /* If we have nothing to try anymore, then acquire a new password */ if (arg_try_empty_password) { /* Hmm, let's try an empty password now, but only once */ @@ -2358,7 +2452,8 @@ static int run(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No passphrase or recovery key registered."); } - r = get_password(volume, source, until, tries == 0 && !arg_verify, passphrase_type, &passwords); + r = get_password(volume, source, until, use_cached_passphrase && !arg_verify, passphrase_type, &passwords); + use_cached_passphrase = false; if (r == -EAGAIN) continue; if (r < 0) @@ -2375,17 +2470,44 @@ static int run(int argc, char *argv[]) { if (r != -EAGAIN) return r; - /* Key not correct? Let's try again! */ + /* Key not correct? Let's try again, but let's invalidate one of the passed fields, + * so that we fallback to the next best thing. */ - key_file = NULL; - key_data = erase_and_free(key_data); - key_data_size = 0; - arg_pkcs11_uri = mfree(arg_pkcs11_uri); - arg_pkcs11_uri_auto = false; - arg_fido2_device = mfree(arg_fido2_device); - arg_fido2_device_auto = false; - arg_tpm2_device = mfree(arg_tpm2_device); - arg_tpm2_device_auto = false; + if (arg_tpm2_device || arg_tpm2_device_auto) { + arg_tpm2_device = mfree(arg_tpm2_device); + arg_tpm2_device_auto = false; + continue; + } + + if (arg_fido2_device || arg_fido2_device_auto) { + arg_fido2_device = mfree(arg_fido2_device); + arg_fido2_device_auto = false; + continue; + } + + if (arg_pkcs11_uri || arg_pkcs11_uri_auto) { + arg_pkcs11_uri = mfree(arg_pkcs11_uri); + arg_pkcs11_uri_auto = false; + continue; + } + + if (key_data) { + key_data = erase_and_free(key_data); + key_data_size = 0; + continue; + } + + if (key_file) { + key_file = NULL; + continue; + } + + if (passwords) { + passwords = strv_free_erase(passwords); + continue; + } + + log_debug("Prepared for next attempt to unlock."); } if (arg_tries != 0 && tries >= arg_tries) @@ -2395,7 +2517,7 @@ static int run(int argc, char *argv[]) { const char *volume = ASSERT_PTR(argv[optind + 1]); if (argc - optind >= 3) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach does not accept more than one argument."); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "detach does not accept more than one argument."); if (!filename_is_valid(volume)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume); diff --git a/src/cryptsetup/meson.build b/src/cryptsetup/meson.build index 90e2be7..9ccc098 100644 --- a/src/cryptsetup/meson.build +++ b/src/cryptsetup/meson.build @@ -11,10 +11,6 @@ if conf.get('HAVE_P11KIT') == 1 systemd_cryptsetup_sources += files('cryptsetup-pkcs11.c') endif -if conf.get('HAVE_TPM2') == 1 - systemd_cryptsetup_sources += files('cryptsetup-tpm2.c') -endif - executables += [ executable_template + { 'name' : 'systemd-cryptsetup', |