]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptsetup: read PKCS#11 key and token info from LUKS2 metadata
authorLennart Poettering <lennart@poettering.net>
Wed, 25 Nov 2020 10:10:29 +0000 (11:10 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 17 Dec 2020 18:59:24 +0000 (19:59 +0100)
Optionally, embedd PKCS#11 token URI and encrypted key in LUKS2 JSON
metadata header. That way it becomes very easy to unlock properly set up
PKCS#11-enabled LUKS2 volumes, a simple /etc/crypttab line like the
following suffices:

    mytest /dev/disk/by-partuuid/41c1df55-e628-4dbb-8492-bc69d81e172e - pkcs11-uri=auto

Such a line declares that unlocking via PKCS#11 shall be attempted, and
the token URI and the encrypted key shall be read from the LUKS2 header.
An external key file for the encrypted PKCS#11 key is hence no longer
necessary, nor is specifying the precise URI to use.

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

index b645ff28e01bcb55e8ac23990f262b820989aa00..93cf7c64b3a828d0ba3839e5c44be32a5ebba02a 100644 (file)
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
+#include "hexdecoct.h"
+#include "json.h"
 #include "macro.h"
 #include "memory-util.h"
+#include "parse-util.h"
 #include "pkcs11-util.h"
 #include "random-util.h"
 #include "stat-util.h"
@@ -156,3 +159,80 @@ int decrypt_pkcs11_key(
 
         return 0;
 }
+
+int find_pkcs11_auto_data(
+                struct crypt_device *cd,
+                char **ret_uri,
+                void **ret_encrypted_key,
+                size_t *ret_encrypted_key_size,
+                int *ret_keyslot) {
+
+        _cleanup_free_ char *uri = NULL;
+        _cleanup_free_ void *key = NULL;
+        int r, keyslot = -1;
+        size_t key_size = 0;
+
+        assert(cd);
+        assert(ret_uri);
+        assert(ret_encrypted_key);
+        assert(ret_encrypted_key_size);
+        assert(ret_keyslot);
+
+        /* Loads PKCS#11 metadata from LUKS2 JSON token headers. */
+
+        for (int token = 0; token < LUKS2_TOKENS_MAX; token++) {
+                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+                JsonVariant *w;
+
+                r = cryptsetup_get_token_as_json(cd, token, "systemd-pkcs11", &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");
+
+                if (uri)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
+                                               "Multiple PKCS#11 tokens enrolled, cannot automatically determine token.");
+
+                w = json_variant_by_key(v, "pkcs11-uri");
+                if (!w || !json_variant_is_string(w))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "PKCS#11 token data lacks 'pkcs11-uri' field.");
+
+                uri = strdup(json_variant_string(w));
+                if (!uri)
+                        return log_oom();
+
+                if (!pkcs11_uri_valid(uri))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "PKCS#11 token data contains invalid PKCS#11 URI.");
+
+                w = json_variant_by_key(v, "pkcs11-key");
+                if (!w || !json_variant_is_string(w))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "PKCS#11 token data lacks 'pkcs11-key' field.");
+
+                assert(!key);
+                assert(key_size == 0);
+                r = unbase64mem(json_variant_string(w), (size_t) -1, &key, &key_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to decode base64 encoded key.");
+
+                assert(keyslot < 0);
+                keyslot = cryptsetup_get_keyslot_from_token(v);
+                if (keyslot < 0)
+                        return log_error_errno(keyslot, "Failed to extract keyslot index from PKCS#11 JSON data: %m");
+        }
+
+        if (!uri)
+                return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
+                                       "No valid PKCS#11 token data found.");
+
+        log_info("Automatically discovered security PKCS#11 token '%s' unlocks volume.", uri);
+
+        *ret_uri = TAKE_PTR(uri);
+        *ret_encrypted_key = TAKE_PTR(key);
+        *ret_encrypted_key_size = key_size;
+        *ret_keyslot = keyslot;
+        return 0;
+}
index 522ed28bd3c64a452ddacac534feb2123027f163..4cd82e0215fa31c77141ddd9d384a8fc85b1ea42 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <sys/types.h>
 
+#include "cryptsetup-util.h"
 #include "log.h"
 #include "time-util.h"
 
@@ -21,6 +22,13 @@ int decrypt_pkcs11_key(
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size);
 
+int find_pkcs11_auto_data(
+                struct crypt_device *cd,
+                char **ret_uri,
+                void **ret_encrypted_key,
+                size_t *ret_encrypted_key_size,
+                int *ret_keyslot);
+
 #else
 
 static inline int decrypt_pkcs11_key(
@@ -40,4 +48,15 @@ static inline int decrypt_pkcs11_key(
                                "PKCS#11 Token support not available.");
 }
 
+static inline int find_pkcs11_auto_data(
+                struct crypt_device *cd,
+                char **ret_uri,
+                void **ret_encrypted_key,
+                size_t *ret_encrypted_key_size,
+                int *ret_keyslot) {
+
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                               "PKCS#11 Token support not available.");
+}
+
 #endif
index 4ac76f1826af3f3c5c47996f7238913a65131134..10ba44a5593a96cef1bc09e8b1211bbfdcd45d6d 100644 (file)
@@ -64,6 +64,7 @@ static uint64_t arg_offset = 0;
 static uint64_t arg_skip = 0;
 static usec_t arg_timeout = USEC_INFINITY;
 static char *arg_pkcs11_uri = NULL;
+static bool arg_pkcs11_uri_auto = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
@@ -262,12 +263,19 @@ static int parse_one_option(const char *option) {
 
         } else if ((val = startswith(option, "pkcs11-uri="))) {
 
-                if (!pkcs11_uri_valid(val))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "pkcs11-uri= parameter expects a PKCS#11 URI, refusing");
+                if (streq(val, "auto")) {
+                        arg_pkcs11_uri = mfree(arg_pkcs11_uri);
+                        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");
 
-                r = free_and_strdup(&arg_pkcs11_uri, val);
-                if (r < 0)
-                        return log_oom();
+                        r = free_and_strdup(&arg_pkcs11_uri, val);
+                        if (r < 0)
+                                return log_oom();
+
+                        arg_pkcs11_uri_auto = false;
+                }
 
         } else if ((val = startswith(option, "try-empty-password="))) {
 
@@ -498,7 +506,7 @@ static int attach_tcrypt(
         assert(name);
         assert(key_file || key_data || !strv_isempty(passwords));
 
-        if (arg_pkcs11_uri)
+        if (arg_pkcs11_uri || arg_pkcs11_uri_auto)
                 /* Ask for a regular password */
                 return log_error_errno(SYNTHETIC_ERRNO(EAGAIN),
                                        "Sorry, but tcrypt devices are currently not supported in conjunction with pkcs11 support.");
@@ -655,12 +663,29 @@ static int attach_luks_or_plain_or_bitlk(
                  crypt_get_volume_key_size(cd)*8,
                  crypt_get_device_name(cd));
 
-        if (arg_pkcs11_uri) {
+        if (arg_pkcs11_uri || arg_pkcs11_uri_auto) {
                 _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
+                _cleanup_free_ char *friendly = NULL, *discovered_uri = NULL;
+                size_t decrypted_key_size = 0, discovered_key_size = 0;
                 _cleanup_(erase_and_freep) void *decrypted_key = NULL;
                 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
-                _cleanup_free_ char *friendly = NULL;
-                size_t decrypted_key_size = 0;
+                _cleanup_free_ void *discovered_key = NULL;
+                int keyslot = arg_key_slot;
+                const char *uri;
+
+                if (arg_pkcs11_uri_auto) {
+                        r = find_pkcs11_auto_data(cd, &discovered_uri, &discovered_key, &discovered_key_size, &keyslot);
+                        if (IN_SET(r, -ENOTUNIQ, -ENXIO))
+                                return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+                                                       "Automatic PKCS#11 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
+                        if (r < 0)
+                                return r;
+
+                        key_data = discovered_key;
+                        key_data_size = discovered_key_size;
+                        uri = discovered_uri;
+                } else
+                        uri = arg_pkcs11_uri;
 
                 if (!key_file && !key_data)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "PKCS#11 mode selected but no key file specified, refusing.");
@@ -675,7 +700,7 @@ static int attach_luks_or_plain_or_bitlk(
                         r = decrypt_pkcs11_key(
                                         name,
                                         friendly,
-                                        arg_pkcs11_uri,
+                                        uri,
                                         key_file, arg_keyfile_size, arg_keyfile_offset,
                                         key_data, key_data_size,
                                         until,
@@ -700,7 +725,7 @@ static int attach_luks_or_plain_or_bitlk(
                                         return r;
 
                                 log_notice("Security token %s not present for unlocking volume %s, please plug it in.",
-                                           arg_pkcs11_uri, friendly);
+                                           uri, friendly);
 
                                 /* Let's immediately rescan in case the token appeared in the time we needed
                                  * to create and configure the monitor */
@@ -739,7 +764,7 @@ static int attach_luks_or_plain_or_bitlk(
                         if (r < 0)
                                 return log_oom();
 
-                        r = crypt_activate_by_passphrase(cd, name, arg_key_slot, base64_encoded, strlen(base64_encoded), flags);
+                        r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, strlen(base64_encoded), flags);
                 }
                 if (r == -EPERM) {
                         log_error_errno(r, "Failed to activate with PKCS#11 decrypted key. (Key incorrect?)");
@@ -1030,7 +1055,7 @@ static int run(int argc, char *argv[]) {
                          *    5. We enquire the user for a password
                          */
 
-                        if (!key_file && !key_data && !arg_pkcs11_uri) {
+                        if (!key_file && !key_data && !arg_pkcs11_uri && !arg_pkcs11_uri_auto) {
 
                                 if (arg_try_empty_password) {
                                         /* Hmm, let's try an empty password now, but only once */
@@ -1068,6 +1093,7 @@ static int run(int argc, char *argv[]) {
                         key_data = erase_and_free(key_data);
                         key_data_size = 0;
                         arg_pkcs11_uri = mfree(arg_pkcs11_uri);
+                        arg_pkcs11_uri_auto = false;
                 }
 
                 if (arg_tries != 0 && tries >= arg_tries)
index 26f5dd3c89a4a2328c23b464e8df0b7cc9f26872..fa2d2f65f3c45dc00d267866f27ff0ee8f33603f 100644 (file)
@@ -43,4 +43,8 @@ int cryptsetup_get_token_as_json(struct crypt_device *cd, int idx, const char *v
 int cryptsetup_get_keyslot_from_token(JsonVariant *v);
 int cryptsetup_add_token_json(struct crypt_device *cd, JsonVariant *v);
 
+/* Stolen from cryptsetup's sources. We use to iterate through all tokens defined for a volume. Ideally, we'd
+ * be able to query this via some API, but there appears to be none currently in libcryptsetup. */
+#define LUKS2_TOKENS_MAX 32
+
 #endif