1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

cryptenroll: change class in provided PKCS#11 URI if necessary

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
This commit is contained in:
Vladimir Stoiakin 2023-10-24 19:00:43 +03:00
parent c01a5c0527
commit 85828ef920
5 changed files with 63 additions and 13 deletions

View File

@ -313,11 +313,13 @@
<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

View 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");

View 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),

View 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);

View File

@ -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