]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptsetup: automatically load luks keys off disk
authorLennart Poettering <lennart@poettering.net>
Wed, 29 Apr 2020 14:37:14 +0000 (16:37 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 19 May 2020 15:28:25 +0000 (17:28 +0200)
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.

src/cryptsetup/cryptsetup-pkcs11.c
src/cryptsetup/cryptsetup-pkcs11.h
src/cryptsetup/cryptsetup.c

index 97a8c68ee8b229ee8bb028f8ca76fadec8b238ad..642a1b7d11a8843732d9e781d4e9e91830cc1d44 100644 (file)
@@ -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)
index 264ccb66b107d279a63ec9f04cbfe0fcacbafb5c..af2487e75bdfc5d8c38dc0297af42f015d70363d 100644 (file)
@@ -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) {
index 0222a5e29343c3029b9111c7ab3eb3e54bf3455d..f6a9a21caa54d88aa33f1432dce419426c4212ac 100644 (file)
@@ -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, &params);
         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]);