From: Lennart Poettering Date: Wed, 29 Apr 2020 14:37:14 +0000 (+0200) Subject: cryptsetup: automatically load luks keys off disk X-Git-Tag: v246-rc1~331^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7407f689807963d48dce08e5581b5cb4b4e56813;p=thirdparty%2Fsystemd.git cryptsetup: automatically load luks keys off disk Let's make loading of keys a bit more automatic and define a common place where key files can be placed. Specifically, whenever a volume of name "foo" is attempted, search for a key file in /etc/cryptsetup-keys.d/foo.key and /run/cryptsetup-keys.d/foo.key, unless a key file is declared explicitly. With this scheme we have a simple discovery in place that should make it more straightfoward wher to place keys, and requires no explicit configuration to be used. --- diff --git a/src/cryptsetup/cryptsetup-pkcs11.c b/src/cryptsetup/cryptsetup-pkcs11.c index 97a8c68ee8b..642a1b7d11a 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.c +++ b/src/cryptsetup/cryptsetup-pkcs11.c @@ -27,11 +27,14 @@ struct pkcs11_callback_data { size_t encrypted_key_size; void *decrypted_key; size_t decrypted_key_size; + bool free_encrypted_key; }; static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) { free(data->decrypted_key); - free(data->encrypted_key); + + if (data->free_encrypted_key) + free(data->encrypted_key); } static int pkcs11_callback( @@ -94,9 +97,11 @@ static int pkcs11_callback( int decrypt_pkcs11_key( const char *friendly_name, const char *pkcs11_uri, - const char *key_file, + const char *key_file, /* We either expect key_file and associated parameters to be set (for file keys) … */ size_t key_file_size, uint64_t key_file_offset, + const void *key_data, /* … or key_data and key_data_size (for literal keys) */ + size_t key_data_size, usec_t until, void **ret_decrypted_key, size_t *ret_decrypted_key_size) { @@ -109,15 +114,24 @@ int decrypt_pkcs11_key( assert(friendly_name); assert(pkcs11_uri); - assert(key_file); + assert(key_file || key_data); assert(ret_decrypted_key); assert(ret_decrypted_key_size); /* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */ - r = load_key_file(key_file, NULL, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size); - if (r < 0) - return r; + if (key_data) { + data.encrypted_key = (void*) key_data; + data.encrypted_key_size = key_data_size; + + data.free_encrypted_key = false; + } else { + r = load_key_file(key_file, NULL, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size); + if (r < 0) + return r; + + data.free_encrypted_key = true; + } r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data); if (r < 0) diff --git a/src/cryptsetup/cryptsetup-pkcs11.h b/src/cryptsetup/cryptsetup-pkcs11.h index 264ccb66b10..af2487e75bd 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.h +++ b/src/cryptsetup/cryptsetup-pkcs11.h @@ -14,6 +14,8 @@ int decrypt_pkcs11_key( const char *key_file, size_t key_file_size, uint64_t key_file_offset, + const void *key_data, + size_t key_data_size, usec_t until, void **ret_decrypted_key, size_t *ret_decrypted_key_size); @@ -26,6 +28,8 @@ static inline int decrypt_pkcs11_key( const char *key_file, size_t key_file_size, uint64_t key_file_offset, + const void *key_data, + size_t key_data_size, usec_t until, void **ret_decrypted_key, size_t *ret_decrypted_key_size) { diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 0222a5e2934..f6a9a21caa5 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -13,6 +13,7 @@ #include "ask-password-api.h" #include "crypt-util.h" #include "cryptsetup-pkcs11.h" +#include "cryptsetup-util.h" #include "device-util.h" #include "escape.h" #include "fileio.h" @@ -21,6 +22,7 @@ #include "hexdecoct.h" #include "log.h" #include "main-func.h" +#include "memory-util.h" #include "mount-util.h" #include "nulstr-util.h" #include "parse-util.h" @@ -458,6 +460,8 @@ static int attach_tcrypt( struct crypt_device *cd, const char *name, const char *key_file, + const void *key_data, + size_t key_data_size, char **passwords, uint32_t flags) { @@ -471,7 +475,7 @@ static int attach_tcrypt( assert(cd); assert(name); - assert(key_file || (passwords && passwords[0])); + assert(key_file || key_data || !strv_isempty(passwords)); if (arg_pkcs11_uri) /* Ask for a regular password */ @@ -487,23 +491,36 @@ static int attach_tcrypt( if (arg_tcrypt_veracrypt) params.flags |= CRYPT_TCRYPT_VERA_MODES; - if (key_file) { - r = read_one_line_file(key_file, &passphrase); - if (r < 0) { - log_error_errno(r, "Failed to read password file '%s': %m", key_file); - return -EAGAIN; /* log with the actual error, but return EAGAIN */ - } + if (key_data) { + params.passphrase = key_data; + params.passphrase_size = key_data_size; + } else { + if (key_file) { + r = read_one_line_file(key_file, &passphrase); + if (r < 0) { + log_error_errno(r, "Failed to read password file '%s': %m", key_file); + return -EAGAIN; /* log with the actual error, but return EAGAIN */ + } - params.passphrase = passphrase; - } else - params.passphrase = passwords[0]; - params.passphrase_size = strlen(params.passphrase); + params.passphrase = passphrase; + } else + params.passphrase = passwords[0]; + + params.passphrase_size = strlen(params.passphrase); + } r = crypt_load(cd, CRYPT_TCRYPT, ¶ms); if (r < 0) { - if (key_file && r == -EPERM) { - log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file); - return -EAGAIN; /* log the actual error, but return EAGAIN */ + if (r == -EPERM) { + if (key_data) { + log_error_errno(r, "Failed to activate using discovered key. (Key not correct?)"); + return -EAGAIN; /* log the actual error, but return EAGAIN */ + } + + if (key_file) { + log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file); + return -EAGAIN; /* log the actual error, but return EAGAIN */ + } } return log_error_errno(r, "Failed to load tcrypt superblock on device %s: %m", crypt_get_device_name(cd)); @@ -520,6 +537,8 @@ static int attach_luks_or_plain( struct crypt_device *cd, const char *name, const char *key_file, + const void *key_data, + size_t key_data_size, char **passwords, uint32_t flags, usec_t until) { @@ -589,7 +608,7 @@ static int attach_luks_or_plain( _cleanup_free_ char *friendly = NULL; size_t decrypted_key_size = 0; - if (!key_file) + if (!key_file && !key_data) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "PKCS#11 mode selected but no key file specified, refusing."); friendly = friendly_disk_name(crypt_get_device_name(cd), name); @@ -602,8 +621,8 @@ static int attach_luks_or_plain( r = decrypt_pkcs11_key( friendly, arg_pkcs11_uri, - key_file, - arg_keyfile_size, arg_keyfile_offset, + key_file, arg_keyfile_size, arg_keyfile_offset, + key_data, key_data_size, until, &decrypted_key, &decrypted_key_size); if (r >= 0) @@ -686,6 +705,18 @@ static int attach_luks_or_plain( if (r < 0) return log_error_errno(r, "Failed to activate with PKCS#11 acquired key: %m"); + } else if (key_data) { + if (pass_volume_key) + r = crypt_activate_by_volume_key(cd, name, key_data, key_data_size, flags); + else + r = crypt_activate_by_passphrase(cd, name, arg_key_slot, key_data, key_data_size, flags); + if (r == -EPERM) { + log_error_errno(r, "Failed to activate. (Key incorrect?)"); + return -EAGAIN; /* Log actual error, but return EAGAIN */ + } + if (r < 0) + return log_error_errno(r, "Failed to activate: %m"); + } else if (key_file) { r = crypt_activate_by_keyfile_device_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags); if (r == -EPERM) { @@ -805,12 +836,17 @@ static int run(int argc, char *argv[]) { crypt_status_info status; _cleanup_(remove_and_erasep) const char *destroy_key_file = NULL; const char *key_file = NULL; + _cleanup_(erase_and_freep) void *key_data = NULL; + size_t key_data_size = 0; /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */ if (argc < 4) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments."); + if (!filename_is_valid(argv[2])) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]); + if (argc >= 5 && !STR_IN_SET(argv[4], "", "-", "none")) { if (path_is_absolute(argv[4])) key_file = argv[4]; @@ -827,7 +863,22 @@ static int run(int argc, char *argv[]) { /* A delicious drop of snake oil */ (void) mlockall(MCL_FUTURE); - if (key_file && arg_keyfile_erase) + if (!key_file) { + const char *fn; + + /* If a key file is not explicitly specified, search for a key in a well defined + * search path, and load it. */ + + fn = strjoina(argv[2], ".key"); + r = load_key_file(fn, + STRV_MAKE("/etc/cryptsetup-keys.d", "/run/cryptsetup-keys.d"), + 0, 0, /* Note we leave arg_keyfile_offset/arg_keyfile_size as something that only applies to arg_keyfile! */ + &key_data, &key_data_size); + if (r < 0) + return r; + if (r > 0) + log_debug("Automatically discovered key for volume '%s'.", argv[2]); + } else if (arg_keyfile_erase) destroy_key_file = key_file; /* let's get this baby erased when we leave */ if (arg_header) { @@ -876,7 +927,7 @@ static int run(int argc, char *argv[]) { } /* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */ - if (!key_file) { + if (!key_file && !key_data) { r = crypt_activate_by_token(cd, argv[2], CRYPT_ANY_TOKEN, NULL, flags); if (r >= 0) { log_debug("Volume %s activated with LUKS token id %i.", argv[2], r); @@ -890,7 +941,7 @@ static int run(int argc, char *argv[]) { for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { _cleanup_strv_free_erase_ char **passwords = NULL; - if (!key_file && !arg_pkcs11_uri) { + if (!key_file && !key_data && !arg_pkcs11_uri) { r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords); if (r == -EAGAIN) continue; @@ -899,9 +950,9 @@ static int run(int argc, char *argv[]) { } if (streq_ptr(arg_type, CRYPT_TCRYPT)) - r = attach_tcrypt(cd, argv[2], key_file, passwords, flags); + r = attach_tcrypt(cd, argv[2], key_file, key_data, key_data_size, passwords, flags); else - r = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags, until); + r = attach_luks_or_plain(cd, argv[2], key_file, key_data, key_data_size, passwords, flags, until); if (r >= 0) break; if (r != -EAGAIN) @@ -909,6 +960,8 @@ static int run(int argc, char *argv[]) { /* Passphrase not correct? Let's try again! */ key_file = NULL; + key_data = erase_and_free(key_data); + key_data_size = 0; arg_pkcs11_uri = NULL; } @@ -917,6 +970,9 @@ static int run(int argc, char *argv[]) { } else if (streq(argv[1], "detach")) { + if (!filename_is_valid(argv[2])) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]); + r = crypt_init_by_name(&cd, argv[2]); if (r == -ENODEV) { log_info("Volume %s already inactive.", argv[2]);