cryptsetup: read PKCS#11 key and token info from LUKS2 metadata

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.
This commit is contained in:
Lennart Poettering 2020-11-25 11:10:29 +01:00
parent d3ad474f0c
commit b997d1115b
4 changed files with 142 additions and 13 deletions

View File

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

View 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

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

View 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