Split logic for uploading keys in kernel key service.

We can not link internal VK kernel key in custom user
keyring. There are two reasons for it:

The internal VK kernel key description can not be
acquired via API and it may change over time
(LUKS2 reencryption).

With recent SED OPAL support volume key becomes a 'blob'
containing up to two keys (dm-crypt key for SWE and key
for unlocking SED OPAL locking range). The internal
kernel key contains only dm-crypt (if required) but
custom user keyring needs to be provided with whole
volume key (blob).

Added user specified key description for the linked key
in custom user keyring. The linked key can be reached by
the specified description after successful activation (resume).
This commit is contained in:
Ondrej Kozina
2023-09-13 13:41:44 +02:00
committed by Milan Broz
parent 7ae109dccd
commit 51a1e218cf
15 changed files with 541 additions and 274 deletions

View File

@@ -3073,14 +3073,27 @@ void *crypt_safe_realloc(void *data, size_t size);
void crypt_safe_memzero(void *data, size_t size);
/**
* Link the volume key to the specified keyring.
* Link the volume key to the specified kernel keyring.
*
* @param cd crypt device handle
* @param keyring_to_link_vk the ID of the keyring in which volume key should
* be linked, if @e NULL is specified, linking will be disabled (the key will
* be linked just to the thread keyring, which is destroyed on process exit)
* @param key_description the key description of volume key linked in desired keyring.
* @param key_type the key type used for the volume key. Currently only "user" and "logon" types are
* supported. if @e NULL is specified the default "user" type is applied.
* @param keyring_to_link_vk the keyring description of the keyring in which volume key should
* be linked, if @e NULL is specified, linking will be disabled.
*
* @note keyring_to_link_vk may be passed in various string formats:
* It can be kernel key numeric id of existing keyring written as a string,
* keyring name prefixed optionally be either "%:" or "%keyring:" substrings or keyctl
* special values for keyrings "@t", "@p", "@s" and so on. See keyctl(1) man page,
* section KEY IDENTIFIERS for more information. All other prefixes starting "%<type>:"
* are ignored.
*
* @note key_description "%<type>:" prefixes are ignored. Type is applied based on key_type parameter
* value.
*/
int crypt_set_keyring_to_link(struct crypt_device *cd, const char *keyring_to_link_vk);
int crypt_set_keyring_to_link(struct crypt_device *cd, const char *key_description,
const char *key_type_desc, const char *keyring_to_link_vk);
/**
* Set the type of volume key stored in keyring.

View File

@@ -545,7 +545,7 @@ static char *_uf(char *buf, size_t buf_size, const char *s, unsigned u)
}
/* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt */
static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags, key_type_t key_type)
static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags)
{
int r, max_size, null_cipher = 0, num_options = 0, keystr_len = 0;
char *params = NULL, *hexkey = NULL;
@@ -602,8 +602,8 @@ static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags, ke
hexkey = crypt_safe_alloc(keystr_len);
if (!hexkey)
goto out;
r = snprintf(hexkey, keystr_len, ":%zu:%s:%s", tgt->u.crypt.vk->keylength,
key_type_name(key_type), tgt->u.crypt.vk->key_description);
r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", tgt->u.crypt.vk->keylength,
tgt->u.crypt.vk->key_description);
if (r < 0 || r >= keystr_len)
goto out;
} else
@@ -1247,14 +1247,14 @@ static void _destroy_dm_targets_params(struct crypt_dm_active_device *dmd)
} while (t);
}
static int _create_dm_targets_params(struct crypt_dm_active_device *dmd, key_type_t key_type)
static int _create_dm_targets_params(struct crypt_dm_active_device *dmd)
{
int r;
struct dm_target *tgt = &dmd->segment;
do {
if (tgt->type == DM_CRYPT)
tgt->params = get_dm_crypt_params(tgt, dmd->flags, key_type);
tgt->params = get_dm_crypt_params(tgt, dmd->flags);
else if (tgt->type == DM_VERITY)
tgt->params = get_dm_verity_params(tgt, dmd->flags);
else if (tgt->type == DM_INTEGRITY)
@@ -1312,7 +1312,7 @@ static int _dm_create_device(struct crypt_device *cd, const char *name, const ch
if ((dmd->flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt))
goto out;
r = _create_dm_targets_params(dmd, key_type_by_name(crypt_get_vk_keyring_type(cd)));
r = _create_dm_targets_params(dmd);
if (r)
goto out;
@@ -1424,7 +1424,7 @@ static int _dm_reload_device(struct crypt_device *cd, const char *name,
if ((dmd->flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt))
goto out;
r = _create_dm_targets_params(dmd, key_type_by_name(crypt_get_vk_keyring_type(cd)));
r = _create_dm_targets_params(dmd);
if (r)
goto out;
@@ -1907,7 +1907,7 @@ static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags,
int r;
size_t key_size;
struct device *data_device = NULL;
char *cipher = NULL, *integrity = NULL, *key_type = NULL;
char *cipher = NULL, *integrity = NULL;
struct volume_key *vk = NULL;
tgt->type = DM_CRYPT;
@@ -2026,16 +2026,12 @@ static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags,
/* :<key_size>:<key_type>:<key_description> */
key_desc = NULL;
endp = strpbrk(key_ + 1, ":");
key_type = endp + 1;
if (endp)
key_desc = strpbrk(endp + 1, ":");
if (!key_desc) {
r = -ENOMEM;
goto err;
}
/* replace colon with zero character */
key_desc[0] = '\0';
key_desc++;
crypt_volume_key_set_description(vk, key_desc);
} else {
@@ -2061,8 +2057,6 @@ static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags,
tgt->data_device = data_device;
if (vk)
tgt->u.crypt.vk = vk;
if (key_type)
crypt_set_vk_keyring_type(cd, key_type);
return 0;
err:
free(cipher);
@@ -3001,8 +2995,8 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
}
if (vk->key_description) {
r = snprintf(msg, msg_size, "key set :%zu:%s:%s", vk->keylength,
crypt_get_vk_keyring_type(cd), vk->key_description);
r = snprintf(msg, msg_size, "key set :%zu:logon:%s", vk->keylength,
vk->key_description);
} else {
key = crypt_bytes_to_hex(vk->keylength, vk->key);
if (!key) {

View File

@@ -2865,7 +2865,7 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
while (tgt) {
if (tgt->type == DM_CRYPT)
crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description,
key_type_by_name(crypt_get_vk_keyring_type(cd)));
LOGON_KEY);
tgt = tgt->next;
}
}
@@ -2901,7 +2901,7 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
while (tgt) {
if (tgt->type == DM_CRYPT)
crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description,
key_type_by_name(crypt_get_vk_keyring_type(cd)));
LOGON_KEY);
tgt = tgt->next;
}
}

View File

@@ -62,6 +62,7 @@ struct crypt_device {
bool link_vk_to_keyring;
int32_t keyring_to_link_vk;
const char *user_key_name;
key_type_t keyring_key_type;
uint64_t data_offset;
@@ -3993,6 +3994,26 @@ static int resume_luks1_by_volume_key(struct crypt_device *cd,
return r;
}
static int crypt_volume_key_load_in_user_keyring(struct crypt_device *cd, struct volume_key *vk)
{
int r;
const char *type_name = key_type_name(cd->keyring_key_type);
if (!vk || !cd || !type_name || !cd->link_vk_to_keyring)
return -EINVAL;
log_dbg(cd, "Linking volume key (%zu bytes, type %s, name %s) to the specified keyring",
vk->keylength, type_name, cd->user_key_name);
r = keyring_add_key_to_custom_keyring(cd->keyring_key_type, cd->user_key_name, vk->key, vk->keylength, cd->keyring_to_link_vk);
if (r) {
log_err(cd, _("Failed to link key to the specified keyring."));
log_dbg(cd, "The keyring_link_key_to_keyring function failed (error %d).", r);
}
return r;
}
static int resume_luks2_by_volume_key(struct crypt_device *cd,
int digest,
struct volume_key *vk,
@@ -4043,6 +4064,14 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd,
r = LUKS2_volume_key_load_in_keyring_by_digest(cd, p_crypt, digest);
if (r < 0)
goto out;
/* upload volume key in custom keyring if requested */
if (cd->link_vk_to_keyring) {
r = crypt_volume_key_load_in_user_keyring(cd, vk);
if (r < 0)
/* FIXME: We should unlink VK from custom keyring on error */
goto out;
}
}
if (p_opal) {
@@ -4703,12 +4732,13 @@ static int _open_and_activate(struct crypt_device *cd,
if (r < 0)
goto out;
/* copy volume key digest id in crypt subkey */
crypt_volume_key_set_id(crypt_key, crypt_volume_key_get_id(vk));
p_crypt = crypt_key;
p_opal = opal_key ?: vk;
} else {
} else
p_crypt = vk;
p_opal = NULL;
}
if (!crypt_use_keyring_for_vk(cd) || !p_crypt)
use_keyring = false;
@@ -4722,6 +4752,14 @@ static int _open_and_activate(struct crypt_device *cd,
if (r < 0)
goto out;
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
/* upload the volume key in custom user keyring if requested */
if (cd->link_vk_to_keyring) {
r = crypt_volume_key_load_in_user_keyring(cd, vk);
if (r < 0)
/* FIXME: We should unlink VK from custom keyring on error */
goto out;
}
}
if (name)
@@ -5314,6 +5352,14 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd,
if (r < 0)
goto out;
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
/* upload the volume key in custom user keyring if requested */
if (cd->link_vk_to_keyring) {
r = crypt_volume_key_load_in_user_keyring(cd, vk);
if (r < 0)
/* FIXME: We should unlink VK from custom keyring on error */
goto out;
}
}
} else {
p_crypt = vk;
@@ -7184,9 +7230,8 @@ int crypt_volume_key_keyring(struct crypt_device *cd __attribute__((unused)), in
int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk)
{
int r;
const char *type_name = key_type_name(cd->keyring_key_type);
if (!vk || !cd || !type_name)
if (!vk || !cd)
return -EINVAL;
if (!vk->key_description) {
@@ -7194,24 +7239,15 @@ int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key
return -EINVAL;
}
log_dbg(cd, "Loading key (%zu bytes, type %s) in thread keyring.", vk->keylength, type_name);
log_dbg(cd, "Loading key (%zu bytes, type logon, name %s) in thread keyring.", vk->keylength, vk->key_description);
r = keyring_add_key_in_thread_keyring(cd->keyring_key_type, vk->key_description, vk->key, vk->keylength);
r = keyring_add_key_in_thread_keyring(LOGON_KEY, vk->key_description, vk->key, vk->keylength);
if (r) {
log_dbg(cd, "keyring_add_key_in_thread_keyring failed (error %d)", r);
log_err(cd, _("Failed to load key in kernel keyring."));
} else
crypt_set_key_in_keyring(cd, 1);
if (!r && cd->link_vk_to_keyring) {
log_dbg(cd, "Linking volume key to the specified keyring");
r = keyring_link_key_to_keyring(cd->keyring_key_type, vk->key_description, cd->keyring_to_link_vk);
if (r) {
log_err(cd, _("Failed to link key to the specified keyring."));
log_dbg(cd, "The keyring_link_key_to_keyring function failed (error %d).", r);
}
}
return r;
}
@@ -7247,11 +7283,21 @@ void crypt_drop_keyring_key_by_description(struct crypt_device *cd, const char *
crypt_set_key_in_keyring(cd, 0);
}
int crypt_set_keyring_to_link(struct crypt_device *cd, const char *keyring_to_link_vk)
int crypt_set_keyring_to_link(struct crypt_device *cd, const char *key_description,
const char *key_type_description, const char *keyring_to_link_vk)
{
key_type_t key_type = USER_KEY;
const char *name = NULL;
int32_t id = 0;
if (!cd)
if (!cd || (!key_description && keyring_to_link_vk) ||
(key_description && !keyring_to_link_vk))
return -EINVAL;
if (key_type_description)
key_type = key_type_by_name(key_type_description);
if (key_type != LOGON_KEY && key_type != USER_KEY)
return -EINVAL;
if (keyring_to_link_vk) {
@@ -7260,8 +7306,14 @@ int crypt_set_keyring_to_link(struct crypt_device *cd, const char *keyring_to_li
log_err(cd, _("Could not find keyring described by \"%s\"."), keyring_to_link_vk);
return -EINVAL;
}
if (!(name = strdup(key_description)))
return -ENOMEM;
}
cd->keyring_key_type = key_type;
free(CONST_CAST(void*)cd->user_key_name);
cd->user_key_name = name;
cd->keyring_to_link_vk = id;
cd->link_vk_to_keyring = id != 0;

View File

@@ -20,9 +20,11 @@
*/
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -32,11 +34,6 @@
#include "libcryptsetup_macros.h"
#include "utils_keyring.h"
#ifndef HAVE_KEY_SERIAL_T
#define HAVE_KEY_SERIAL_T
typedef int32_t key_serial_t;
#endif
#ifdef KERNEL_KEYRING
static const struct {
@@ -213,7 +210,11 @@ int keyring_check(void)
return syscall(__NR_request_key, "logon", "dummy", NULL, 0) == -1l && errno != ENOSYS;
}
int keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
int keyring_add_key_in_keyring(key_type_t ktype,
const char *key_desc,
const void *key,
size_t key_size,
key_serial_t keyring)
{
key_serial_t kid;
const char *type_name = key_type_name(ktype);
@@ -221,27 +222,22 @@ int keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, co
if (!type_name || !key_desc)
return -EINVAL;
kid = add_key(type_name, key_desc, key, key_size, KEY_SPEC_THREAD_KEYRING);
kid = add_key(type_name, key_desc, key, key_size, keyring);
if (kid < 0)
return -errno;
return 0;
}
int keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
{
return keyring_add_key_in_keyring(ktype, key_desc, key, key_size, KEY_SPEC_THREAD_KEYRING);
}
/* currently used in client utilities only */
int keyring_add_key_in_user_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
{
const char *type_name = key_type_name(ktype);
key_serial_t kid;
if (!type_name || !key_desc)
return -EINVAL;
kid = add_key(type_name, key_desc, key, key_size, KEY_SPEC_USER_KEYRING);
if (kid < 0)
return -errno;
return 0;
return keyring_add_key_in_keyring(ktype, key_desc, key, key_size, KEY_SPEC_USER_KEYRING);
}
int keyring_find_and_get_key_by_name(const char *key_name,
@@ -325,32 +321,6 @@ int keyring_get_user_key(const char *key_desc,
return 0;
}
static int keyring_link_key_to_keyring_key_type(const char *type_name, const char *key_desc,
key_serial_t keyring_to_link)
{
long r;
key_serial_t kid;
if (!type_name || !key_desc)
return -EINVAL;
do {
kid = request_key(type_name, key_desc, NULL, 0);
} while (kid < 0 && errno == EINTR);
if (kid < 0)
return -errno;
/* see https://mjg59.dreamwidth.org/37333.html */
if (keyring_to_link == KEY_SPEC_USER_KEYRING || keyring_to_link == KEY_SPEC_USER_SESSION_KEYRING)
keyctl_setperm(kid, KEY_POS_ALL | KEY_USR_ALL);
r = keyctl_link(kid, keyring_to_link);
if (r < 0)
return -errno;
return 0;
}
static int keyring_revoke_and_unlink_key_type(const char *type_name, const char *key_desc)
{
key_serial_t kid;
@@ -444,13 +414,26 @@ out:
return id;
}
static bool numbered(const char *str)
{
char *endp;
errno = 0;
(void) strtol(str, &endp, 0);
if (errno == ERANGE)
return false;
return *endp == '\0' ? true : false;
}
int32_t keyring_find_keyring_id_by_name(const char *keyring_name)
{
assert(keyring_name);
/* "%:" is abbreviation for the type keyring */
if ((keyring_name[0] == '@' && keyring_name[1] != 'a') ||
strstr(keyring_name, "%:") || strstr(keyring_name, "%keyring:"))
strstr(keyring_name, "%:") || strstr(keyring_name, "%keyring:") ||
numbered(keyring_name))
return keyring_find_key_id_by_name(keyring_name);
return 0;
@@ -467,9 +450,28 @@ key_type_t key_type_by_name(const char *name)
return INVALID_KEY;
}
int keyring_link_key_to_keyring(key_type_t ktype, const char *key_desc, key_serial_t keyring_to_link)
int keyring_add_key_to_custom_keyring(key_type_t ktype,
const char *key_desc,
const void *key,
size_t key_size,
key_serial_t keyring_to_link)
{
return keyring_link_key_to_keyring_key_type(key_type_name(ktype), key_desc, keyring_to_link);
key_serial_t kid;
const char *type_name = key_type_name(ktype);
if (!type_name || !key_desc)
return -EINVAL;
kid = add_key(type_name, key_desc, key, key_size, keyring_to_link);
if (kid < 0)
return -errno;
/* FIXME: could we delegate it to the caller? */
// see https://mjg59.dreamwidth.org/37333.html
if (keyring_to_link == KEY_SPEC_USER_KEYRING || keyring_to_link == KEY_SPEC_USER_SESSION_KEYRING)
keyctl_setperm(kid, KEY_POS_ALL | KEY_USR_ALL);
return 0;
}
int keyring_revoke_and_unlink_key(key_type_t ktype, const char *key_desc)
@@ -495,6 +497,13 @@ int keyring_add_key_in_user_keyring(key_type_t ktype, const char *key_desc, cons
return -ENOTSUP;
}
int keyring_find_and_get_key_by_name(const char *key_name,
char **key,
size_t *key_size)
{
return -ENOTSUP;
}
int keyring_read_by_id(const char *key_desc, char **passphrase, size_t *passphrase_len)
{
return -ENOTSUP;
@@ -525,7 +534,11 @@ key_type_t key_type_by_name(const char *name)
return INVALID_KEY;
}
int keyring_link_key_to_keyring(key_type_t ktype, const char *key_desc, key_serial_t keyring_to_link)
int keyring_add_key_to_custom_keyring(key_type_t ktype,
const char *key_desc,
const void *key,
size_t key_size,
key_serial_t keyring_to_link)
{
return -ENOTSUP;
}

View File

@@ -25,6 +25,11 @@
#include <stddef.h>
#include <stdint.h>
#ifndef HAVE_KEY_SERIAL_T
#define HAVE_KEY_SERIAL_T
typedef int32_t key_serial_t;
#endif
typedef enum { LOGON_KEY = 0, USER_KEY, BIG_KEY, TRUSTED_KEY, ENCRYPTED_KEY, INVALID_KEY } key_type_t;
const char *key_type_name(key_type_t ktype);
@@ -54,7 +59,15 @@ int keyring_add_key_in_user_keyring(
const void *key,
size_t key_size);
int keyring_add_key_in_keyring(
key_type_t ktype,
const char *key_desc,
const void *key,
size_t key_size,
key_serial_t keyring_id);
int keyring_revoke_and_unlink_key(key_type_t ktype, const char *key_desc);
int keyring_link_key_to_keyring(key_type_t ktype, const char *key_desc, int keyring_to_link);
int keyring_add_key_to_custom_keyring(key_type_t ktype, const char *key_desc, const void *key,
size_t key_size, key_serial_t keyring_to_link);
#endif