diff --git a/src/cryptsetup/cryptsetup-pkcs11.c b/src/cryptsetup/cryptsetup-pkcs11.c index b645ff28e0..93cf7c64b3 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.c +++ b/src/cryptsetup/cryptsetup-pkcs11.c @@ -14,8 +14,11 @@ #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; +} diff --git a/src/cryptsetup/cryptsetup-pkcs11.h b/src/cryptsetup/cryptsetup-pkcs11.h index 522ed28bd3..4cd82e0215 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.h +++ b/src/cryptsetup/cryptsetup-pkcs11.h @@ -3,6 +3,7 @@ #include +#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 diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 4ac76f1826..10ba44a559 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -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) diff --git a/src/shared/cryptsetup-util.h b/src/shared/cryptsetup-util.h index 26f5dd3c89..fa2d2f65f3 100644 --- a/src/shared/cryptsetup-util.h +++ b/src/shared/cryptsetup-util.h @@ -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