]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptenroll: change class in provided PKCS#11 URI if necessary 29692/head
authorVladimir Stoiakin <VStoiakin@lavabit.com>
Tue, 24 Oct 2023 16:00:43 +0000 (19:00 +0300)
committerVladimir Stoiakin <VStoiakin@lavabit.com>
Fri, 5 Jan 2024 09:32:36 +0000 (12:32 +0300)
cryptenroll accepts only PKCS#11 URIs that match both a certificate and a private key in a token.
This patch allows users to provide a PKCS#11 URI that points to a certificate only, and makes possible to use output of some PKCS#11 tools directly.
Internally the patch changes 'type=cert' in the provided PKCS#11 URI to 'type=private' before storing in a LUKS2 header.

Fixes: #23479
man/systemd-cryptenroll.xml
src/cryptenroll/cryptenroll-pkcs11.c
src/shared/pkcs11-util.c
src/shared/pkcs11-util.h
test/units/testsuite-24.sh

index cc8b8fcd7fb73f86ccdd3ebf9dff8a2a6dc475d2..99949a81325487a02082f9b3305c9e89c61ed174 100644 (file)
       <varlistentry>
         <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
 
-        <listitem><para>Enroll a PKCS#11 security token or smartcard (e.g. a YubiKey). Expects a PKCS#11
-        smartcard URI referring to the token. Alternatively the special value <literal>auto</literal> may
-        be specified, in order to automatically determine the URI of a currently plugged in security token
-        (of which there must be exactly one). The special value <literal>list</literal> may be used to
-        enumerate all suitable PKCS#11 tokens currently plugged in.</para>
+        <listitem><para>Enroll a PKCS#11 security token or smartcard (e.g. a YubiKey). Expects a PKCS#11 URI
+        that allows to find an X.509 certificate on the token. The URI must also be suitable to find
+        a related private key after changing the type of object in it. Alternatively the special value
+        <literal>auto</literal> may be specified, in order to automatically determine the suitable URI if
+        a single security token containing a single key pair is plugged in. The special value
+        <literal>list</literal> may be used to enumerate all suitable PKCS#11 tokens currently plugged in.
+        </para>
 
         <para>The PKCS#11 token must contain an RSA or EC key pair which will be used to unlock a LUKS2 volume.
         For RSA, a randomly generated volume key is encrypted with a public key in the token, and stored in
index 7d6112e40271cb1d87ad7c334e2a1e88a53bbec8..ea969102cbe12731a7a3d7c257366d1068bb2856 100644 (file)
@@ -7,6 +7,30 @@
 #include "openssl-util.h"
 #include "pkcs11-util.h"
 
+static int uri_set_private_class(const char *uri, char **ret_uri) {
+        _cleanup_(sym_p11_kit_uri_freep) P11KitUri *p11kit_uri = NULL;
+        _cleanup_free_ char *private_uri = NULL;
+        int r;
+
+        r = uri_from_string(uri, &p11kit_uri);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", uri);
+
+        if (sym_p11_kit_uri_get_attribute(p11kit_uri, CKA_CLASS)) {
+                CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
+                CK_ATTRIBUTE attribute = { CKA_CLASS, &class, sizeof(class) };
+
+                if (sym_p11_kit_uri_set_attribute(p11kit_uri, &attribute) != P11_KIT_URI_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set class for URI '%s': %m", uri);
+
+                if (sym_p11_kit_uri_format(p11kit_uri, P11_KIT_URI_FOR_ANY, &private_uri) != P11_KIT_URI_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to format PKCS#11 URI: %m");
+        }
+
+        *ret_uri = TAKE_PTR(private_uri);
+        return 0;
+}
+
 int enroll_pkcs11(
                 struct crypt_device *cd,
                 const void *volume_key,
@@ -16,13 +40,13 @@ int enroll_pkcs11(
         _cleanup_(erase_and_freep) void *decrypted_key = NULL;
         _cleanup_(erase_and_freep) char *base64_encoded = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-        _cleanup_free_ char *keyslot_as_string = NULL;
+        _cleanup_free_ char *keyslot_as_string = NULL, *private_uri = NULL;
         size_t decrypted_key_size, saved_key_size;
         _cleanup_free_ void *saved_key = NULL;
         _cleanup_(X509_freep) X509 *cert = NULL;
         ssize_t base64_encoded_size;
         const char *node;
-        int keyslot, r;
+        int r;
 
         assert_se(cd);
         assert_se(volume_key);
@@ -49,7 +73,7 @@ int enroll_pkcs11(
         if (r < 0)
                 return log_error_errno(r, "Failed to set minimal PBKDF: %m");
 
-        keyslot = crypt_keyslot_add_by_volume_key(
+        int keyslot = crypt_keyslot_add_by_volume_key(
                         cd,
                         CRYPT_ANY_SLOT,
                         volume_key,
@@ -62,12 +86,18 @@ int enroll_pkcs11(
         if (asprintf(&keyslot_as_string, "%i", keyslot) < 0)
                 return log_oom();
 
+        /* Change 'type=cert' in the provided URI to 'type=private' before storing in a LUKS2 header.
+           This allows users to use output of some PKCS#11 tools directly without modifications. */
+        r = uri_set_private_class(uri, &private_uri);
+        if (r < 0)
+                return r;
+
         r = json_build(&v,
-                       JSON_BUILD_OBJECT(
-                                       JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-pkcs11")),
-                                       JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
-                                       JSON_BUILD_PAIR("pkcs11-uri", JSON_BUILD_STRING(uri)),
-                                       JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(saved_key, saved_key_size))));
+                JSON_BUILD_OBJECT(
+                        JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-pkcs11")),
+                        JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
+                        JSON_BUILD_PAIR("pkcs11-uri", JSON_BUILD_STRING(private_uri ?: uri)),
+                        JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(saved_key, saved_key_size))));
         if (r < 0)
                 return log_error_errno(r, "Failed to prepare PKCS#11 JSON token object: %m");
 
index c3d97b80f9e4c30e870d53a0056a56f83eb14732..093d143ce95aeca41b8d745455d54f8b90b4e20e 100644 (file)
@@ -50,6 +50,8 @@ const char *(*sym_p11_kit_strerror)(CK_RV rv);
 int (*sym_p11_kit_uri_format)(P11KitUri *uri, P11KitUriType uri_type, char **string);
 void (*sym_p11_kit_uri_free)(P11KitUri *uri);
 CK_ATTRIBUTE_PTR (*sym_p11_kit_uri_get_attributes)(P11KitUri *uri, CK_ULONG *n_attrs);
+CK_ATTRIBUTE_PTR (*sym_p11_kit_uri_get_attribute)(P11KitUri *uri, CK_ATTRIBUTE_TYPE attr_type);
+int (*sym_p11_kit_uri_set_attribute)(P11KitUri *uri, CK_ATTRIBUTE_PTR attr);
 CK_INFO_PTR (*sym_p11_kit_uri_get_module_info)(P11KitUri *uri);
 CK_SLOT_INFO_PTR (*sym_p11_kit_uri_get_slot_info)(P11KitUri *uri);
 CK_TOKEN_INFO_PTR (*sym_p11_kit_uri_get_token_info)(P11KitUri *uri);
@@ -69,6 +71,8 @@ int dlopen_p11kit(void) {
                         DLSYM_ARG(p11_kit_uri_format),
                         DLSYM_ARG(p11_kit_uri_free),
                         DLSYM_ARG(p11_kit_uri_get_attributes),
+                        DLSYM_ARG(p11_kit_uri_get_attribute),
+                        DLSYM_ARG(p11_kit_uri_set_attribute),
                         DLSYM_ARG(p11_kit_uri_get_module_info),
                         DLSYM_ARG(p11_kit_uri_get_slot_info),
                         DLSYM_ARG(p11_kit_uri_get_token_info),
index 2ff6997823e1b4a486fbac75ab5eebcb3bc885f9..d901bbea91fce2acc271e2f1f67de0b7e135d106 100644 (file)
@@ -26,6 +26,8 @@ extern const char *(*sym_p11_kit_strerror)(CK_RV rv);
 extern int (*sym_p11_kit_uri_format)(P11KitUri *uri, P11KitUriType uri_type, char **string);
 extern void (*sym_p11_kit_uri_free)(P11KitUri *uri);
 extern CK_ATTRIBUTE_PTR (*sym_p11_kit_uri_get_attributes)(P11KitUri *uri, CK_ULONG *n_attrs);
+extern CK_ATTRIBUTE_PTR (*sym_p11_kit_uri_get_attribute)(P11KitUri *uri, CK_ATTRIBUTE_TYPE attr_type);
+extern int (*sym_p11_kit_uri_set_attribute)(P11KitUri *uri, CK_ATTRIBUTE_PTR attr);
 extern CK_INFO_PTR (*sym_p11_kit_uri_get_module_info)(P11KitUri *uri);
 extern CK_SLOT_INFO_PTR (*sym_p11_kit_uri_get_slot_info)(P11KitUri *uri);
 extern CK_TOKEN_INFO_PTR (*sym_p11_kit_uri_get_token_info)(P11KitUri *uri);
index eeec411e9c8f4fb49af5da00ac9045ade1b92c85..4d2a71843350620647faef15d45efddee7bd1d66 100755 (executable)
@@ -231,14 +231,26 @@ cryptsetup_start_and_check empty_nokey
 if [[ -r /etc/softhsm2.conf ]]; then
     # Test unlocking with a PKCS#11 token
     export SOFTHSM2_CONF="/etc/softhsm2.conf"
+
     PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
     cryptsetup_start_and_check empty_pkcs11_auto
     cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
     cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
     PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
     cryptsetup_start_and_check empty_pkcs11_auto
     cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
     cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
 fi
 
 cryptsetup_start_and_check detached