Allow linking multiple VKs (also in reencryption).

If the device is in reencryption, it has two active volume keys. Linking
the VK to keyring is not supported for such devices, because the API
only counts with one key. This commit modifies the API
crypt_set_keyring_to_link to allow passing multiple keyring key names.
This commit is contained in:
Daniel Zatovic
2023-11-22 17:41:11 +01:00
committed by Daniel Zaťovič
parent e0eb4dad95
commit 5814b39cdd
5 changed files with 237 additions and 60 deletions

View File

@@ -382,6 +382,7 @@ bool LUKS2_segments_dynamic_size(struct luks2_hdr *hdr);
int LUKS2_reencrypt_digest_new(struct luks2_hdr *hdr);
int LUKS2_reencrypt_digest_old(struct luks2_hdr *hdr);
unsigned LUKS2_reencrypt_vks_count(struct luks2_hdr *hdr);
int LUKS2_reencrypt_data_offset(struct luks2_hdr *hdr, bool blockwise);
/*

View File

@@ -183,6 +183,21 @@ int LUKS2_reencrypt_digest_old(struct luks2_hdr *hdr)
return reencrypt_digest(hdr, 0);
}
unsigned LUKS2_reencrypt_vks_count(struct luks2_hdr *hdr)
{
int digest_old, digest_new;
unsigned vks_count = 0;
if ((digest_new = LUKS2_reencrypt_digest_new(hdr)) >= 0)
vks_count++;
if ((digest_old = LUKS2_reencrypt_digest_old(hdr)) >= 0) {
if (digest_old != digest_new)
vks_count++;
}
return vks_count;
}
/* none, checksums, journal or shift */
static const char *reencrypt_resilience_type(struct luks2_hdr *hdr)
{

View File

@@ -62,7 +62,8 @@ struct crypt_device {
bool link_vk_to_keyring;
int32_t keyring_to_link_vk;
const char *user_key_name;
const char *user_key_name1;
const char *user_key_name2;
key_type_t keyring_key_type;
uint64_t data_offset;
@@ -3906,7 +3907,8 @@ void crypt_free(struct crypt_device *cd)
free(CONST_CAST(void*)cd->pbkdf.type);
free(CONST_CAST(void*)cd->pbkdf.hash);
free(CONST_CAST(void*)cd->user_key_name);
free(CONST_CAST(void*)cd->user_key_name1);
free(CONST_CAST(void*)cd->user_key_name2);
/* Some structures can contain keys (TCRYPT), wipe it */
crypt_safe_memzero(cd, sizeof(*cd));
@@ -4112,29 +4114,6 @@ static int resume_luks1_by_volume_key(struct crypt_device *cd,
return r;
}
static key_serial_t crypt_volume_key_load_in_user_keyring(struct crypt_device *cd, struct volume_key *vk)
{
key_serial_t kid;
const char *type_name;
assert(cd);
assert(cd->link_vk_to_keyring);
if (!vk || !(type_name = key_type_name(cd->keyring_key_type)))
return -EINVAL;
log_dbg(cd, "Linking volume key (type %s, name %s) to the specified keyring",
type_name, cd->user_key_name);
kid = keyring_add_key_to_custom_keyring(cd->keyring_key_type, cd->user_key_name, vk->key, vk->keylength, cd->keyring_to_link_vk);
if (kid <= 0) {
log_err(cd, _("Failed to link key to the specified keyring."));
log_dbg(cd, "The keyring_link_key_to_keyring function failed (error %d).", errno);
}
return kid;
}
static void crypt_unlink_key_from_custom_keyring(struct crypt_device *cd, key_serial_t kid)
{
assert(cd);
@@ -4150,6 +4129,58 @@ static void crypt_unlink_key_from_custom_keyring(struct crypt_device *cd, key_se
log_err(cd, _("Failed to unlink volume key from user specified keyring."));
}
static key_serial_t crypt_single_volume_key_load_in_user_keyring(struct crypt_device *cd, struct volume_key *vk, const char *user_key_name)
{
key_serial_t kid;
const char *type_name;
assert(cd);
assert(cd->link_vk_to_keyring);
if (!vk || !(type_name = key_type_name(cd->keyring_key_type)))
return -EINVAL;
log_dbg(cd, "Linking volume key (type %s, name %s) to the specified keyring",
type_name, user_key_name);
kid = keyring_add_key_to_custom_keyring(cd->keyring_key_type, user_key_name, vk->key, vk->keylength, cd->keyring_to_link_vk);
if (kid <= 0) {
log_dbg(cd, "The keyring_link_key_to_keyring function failed (error %d).", errno);
}
return kid;
}
static int crypt_volume_key_load_in_user_keyring(struct crypt_device *cd, struct volume_key *vk, key_serial_t *kid1_out, key_serial_t *kid2_out)
{
key_serial_t kid1, kid2 = 0;
assert(cd);
assert(cd->link_vk_to_keyring);
assert(cd->user_key_name1);
if (!vk || !key_type_name(cd->keyring_key_type))
return -EINVAL;
kid1 = crypt_single_volume_key_load_in_user_keyring(cd, vk, cd->user_key_name1);
if (kid1 <= 0)
return -EINVAL;
vk = vk->next;
if (vk) {
assert(cd->user_key_name2);
kid2 = crypt_single_volume_key_load_in_user_keyring(cd, vk, cd->user_key_name2);
if (kid2 <= 0) {
crypt_unlink_key_from_custom_keyring(cd, kid1);
return -EINVAL;
}
}
*kid2_out = kid2;
*kid1_out = kid1;
return 0;
}
static int resume_luks2_by_volume_key(struct crypt_device *cd,
int digest,
struct volume_key *vk,
@@ -4158,10 +4189,10 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd,
bool use_keyring;
int r, enc_type;
uint32_t opal_segment_number;
key_serial_t user_vk_kid = 0;
struct volume_key *p_crypt = vk, *p_opal = NULL, *zerokey = NULL, *crypt_key = NULL, *opal_key = NULL;
char *iname = NULL;
struct crypt_lock_handle *opal_lh = NULL;
key_serial_t kid1 = 0, kid2 = 0;
assert(digest >= 0);
assert(vk && crypt_volume_key_get_id(vk) == digest);
@@ -4208,10 +4239,9 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd,
/* upload volume key in custom keyring if requested */
if (cd->link_vk_to_keyring) {
user_vk_kid = crypt_volume_key_load_in_user_keyring(cd, vk);
if (user_vk_kid <= 0) {
r = crypt_volume_key_load_in_user_keyring(cd, vk, &kid1, &kid2);
if (r < 0) {
log_err(cd, _("Failed to link volume key in user defined keyring."));
r = -EINVAL;
goto out;
}
}
@@ -4254,8 +4284,10 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd,
out:
if (r < 0) {
crypt_drop_keyring_key(cd, p_crypt);
if (user_vk_kid > 0 && cd->link_vk_to_keyring)
crypt_unlink_key_from_custom_keyring(cd, user_vk_kid);
if (cd->link_vk_to_keyring && kid1)
crypt_unlink_key_from_custom_keyring(cd, kid1);
if (cd->link_vk_to_keyring && kid2)
crypt_unlink_key_from_custom_keyring(cd, kid2);
}
if (r < 0 && p_opal)
@@ -4884,8 +4916,8 @@ static int _open_and_activate(struct crypt_device *cd,
{
bool use_keyring;
int r;
key_serial_t user_vk_kid = 0;
struct volume_key *p_crypt = NULL, *p_opal = NULL, *crypt_key = NULL, *opal_key = NULL, *vk = NULL;
key_serial_t kid1 = 0, kid2 = 0;
r = LUKS2_keyslot_open(cd, keyslot,
(flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) ?
@@ -4929,10 +4961,9 @@ static int _open_and_activate(struct crypt_device *cd,
/* upload the volume key in custom user keyring if requested */
if (cd->link_vk_to_keyring) {
user_vk_kid = crypt_volume_key_load_in_user_keyring(cd, vk);
if (user_vk_kid <= 0) {
r = crypt_volume_key_load_in_user_keyring(cd, vk, &kid1, &kid2);
if (r < 0) {
log_err(cd, _("Failed to link volume key in user defined keyring."));
r = -EINVAL;
goto out;
}
}
@@ -4943,8 +4974,10 @@ static int _open_and_activate(struct crypt_device *cd,
out:
if (r < 0) {
crypt_drop_keyring_key(cd, p_crypt);
if (user_vk_kid > 0 && cd->link_vk_to_keyring)
crypt_unlink_key_from_custom_keyring(cd, user_vk_kid);
if (cd->link_vk_to_keyring && kid1)
crypt_unlink_key_from_custom_keyring(cd, kid1);
if (cd->link_vk_to_keyring && kid2)
crypt_unlink_key_from_custom_keyring(cd, kid2);
}
crypt_free_volume_key(vk);
crypt_free_volume_key(crypt_key);
@@ -5031,6 +5064,7 @@ static int _open_and_activate_reencrypt_device(struct crypt_device *cd,
struct volume_key *vks = NULL;
int r = 0;
struct crypt_lock_handle *reencrypt_lock = NULL;
key_serial_t kid1 = 0, kid2 = 0;
if (crypt_use_keyring_for_vk(cd))
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
@@ -5091,6 +5125,14 @@ static int _open_and_activate_reencrypt_device(struct crypt_device *cd,
log_dbg(cd, "Entering clean reencryption state mode.");
if (cd->link_vk_to_keyring) {
r = crypt_volume_key_load_in_user_keyring(cd, vks, &kid1, &kid2);
if (r < 0) {
log_err(cd, _("Failed to link volume keys in user defined keyring."));
goto out;
}
}
if (r >= 0)
r = LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size,
!(flags & CRYPT_ACTIVATE_SHARED),
@@ -5100,8 +5142,14 @@ static int _open_and_activate_reencrypt_device(struct crypt_device *cd,
r = LUKS2_activate_multi(cd, name, vks, device_size >> SECTOR_SHIFT, flags);
out:
LUKS2_reencrypt_unlock(cd, reencrypt_lock);
if (r < 0)
if (r < 0) {
crypt_drop_keyring_key(cd, vks);
if (cd->link_vk_to_keyring && kid1)
crypt_unlink_key_from_custom_keyring(cd, kid1);
if (cd->link_vk_to_keyring && kid2)
crypt_unlink_key_from_custom_keyring(cd, kid2);
}
crypt_free_volume_key(vks);
return r < 0 ? r : keyslot;
@@ -5406,7 +5454,7 @@ const char *name,
size_t passphrase_size;
const char *passphrase = NULL;
int unlocked_keyslot, r = -EINVAL;
key_serial_t user_vk_kid = 0;
key_serial_t kid1 = 0, kid2 = 0;
UNUSED(additional_keyslot);
UNUSED(additional_kc);
@@ -5546,10 +5594,9 @@ const char *name,
/* upload the volume key in custom user keyring if requested */
if (cd->link_vk_to_keyring) {
user_vk_kid = crypt_volume_key_load_in_user_keyring(cd, vk);
if (user_vk_kid <= 0) {
r = crypt_volume_key_load_in_user_keyring(cd, vk, &kid1, &kid2);
if (r < 0) {
log_err(cd, _("Failed to link volume key in user defined keyring."));
r = -EINVAL;
goto out;
}
}
@@ -5567,8 +5614,10 @@ const char *name,
out:
if (r < 0) {
crypt_drop_keyring_key(cd, p_crypt);
if (user_vk_kid > 0 && cd->link_vk_to_keyring)
crypt_unlink_key_from_custom_keyring(cd, user_vk_kid);
if (cd->link_vk_to_keyring && kid1)
crypt_unlink_key_from_custom_keyring(cd, kid1);
if (cd->link_vk_to_keyring && kid2)
crypt_unlink_key_from_custom_keyring(cd, kid2);
}
crypt_free_volume_key(vk);
@@ -7567,35 +7616,58 @@ int crypt_set_keyring_to_link(struct crypt_device *cd, const char *key_descripti
const char *key_type_desc, const char *keyring_to_link_vk)
{
key_type_t key_type = USER_KEY;
const char *name = NULL;
const char *name1 = NULL, *name2 = NULL;
int32_t id = 0;
int r, ri;
struct luks2_hdr *hdr;
unsigned user_descriptions_count, vks_count = 1;
UNUSED(old_key_description);
if (!cd || (!key_description && keyring_to_link_vk) ||
(key_description && !keyring_to_link_vk))
if (!cd || ((!key_description && !old_key_description) && (keyring_to_link_vk || key_type_desc)) ||
((key_description || old_key_description) && !keyring_to_link_vk))
return -EINVAL;
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
/* if only one key description is supplied, force it to be the first one */
if (!key_description && old_key_description)
return -EINVAL;
if ((r = _onlyLUKS2(cd, 0, CRYPT_REQUIREMENT_OPAL | CRYPT_REQUIREMENT_ONLINE_REENCRYPT)))
return r;
if (key_type_desc)
key_type = key_type_by_name(key_type_desc);
if (key_type != LOGON_KEY && key_type != USER_KEY)
return -EINVAL;
ri = crypt_reencrypt_status(cd, NULL);
if (ri > CRYPT_REENCRYPT_NONE && ri < CRYPT_REENCRYPT_INVALID)
vks_count = LUKS2_reencrypt_vks_count(hdr);
user_descriptions_count = (key_description ? 1 : 0) + (old_key_description ? 1 : 0);
if (user_descriptions_count != 0 && vks_count > user_descriptions_count)
return -EAGAIN;
if (keyring_to_link_vk) {
id = keyring_find_keyring_id_by_name(keyring_to_link_vk);
if (id == 0) {
log_err(cd, _("Could not find keyring described by \"%s\"."), keyring_to_link_vk);
return -EINVAL;
}
if (!(name = strdup(key_description)))
if (key_description && !(name1 = strdup(key_description)))
return -ENOMEM;
if (old_key_description && !(name2 = strdup(old_key_description))) {
free(CONST_CAST(void*)name1);
return -ENOMEM;
}
}
cd->keyring_key_type = key_type;
free(CONST_CAST(void*)cd->user_key_name);
cd->user_key_name = name;
free(CONST_CAST(void*)cd->user_key_name1);
free(CONST_CAST(void*)cd->user_key_name2);
cd->user_key_name1 = name1;
cd->user_key_name2 = name2;
cd->keyring_to_link_vk = id;
cd->link_vk_to_keyring = id != 0;

View File

@@ -30,6 +30,7 @@ struct crypt_device;
#define MAX_CIPHER_LEN 32
#define MAX_CIPHER_LEN_STR "31"
#define MAX_KEYFILES 32
#define MAX_KEYRING_LINKS 2
#define MAX_CAPI_ONE_LEN 2 * MAX_CIPHER_LEN
#define MAX_CAPI_ONE_LEN_STR "63" /* for sscanf length + '\0' */
#define MAX_CAPI_LEN 144 /* should be enough to fit whole capi string */

View File

@@ -28,9 +28,11 @@
#include "utils_luks.h"
static char *keyfiles[MAX_KEYFILES];
static char *keyring_links[MAX_KEYRING_LINKS];
static char *keyfile_stdin = NULL;
static int keyfiles_count = 0;
static int keyring_links_count = 0;
int64_t data_shift = 0;
const char *device_type = "luks";
@@ -57,6 +59,8 @@ void tools_cleanup(void)
while (keyfiles_count)
free(keyfiles[--keyfiles_count]);
while (keyring_links_count)
free(keyring_links[--keyring_links_count]);
total_keyfiles = 0;
}
@@ -1710,12 +1714,14 @@ static int parse_vk_description(const char *key_description, char **ret_key_desc
return r;
}
static int parse_vk_and_keyring_description(
static int parse_single_vk_and_keyring_description(
struct crypt_device *cd,
char *keyring_key_description)
char *keyring_key_description, char **keyring_part_out, char
**key_part_out, char **type_part_out)
{
int r = -EINVAL;
char *endp, *sep, *keyring_part = NULL, *key_part, *type_part = NULL;
char *endp, *sep, *key_part, *type_part = NULL;
char *key_part_copy = NULL, *type_part_copy = NULL, *keyring_part = NULL;
if (!cd || !keyring_key_description)
return -EINVAL;
@@ -1759,12 +1765,81 @@ static int parse_vk_and_keyring_description(
goto out;
}
r = crypt_set_keyring_to_link(cd, key_part, NULL, type_part, keyring_part);
if (!(key_part_copy = strdup(key_part))) {
r = -ENOMEM;
goto out;
}
if (type_part && !(type_part_copy = strdup(type_part)))
r = -ENOMEM;
out:
if (r < 0) {
free(keyring_part);
free(key_part_copy);
free(type_part_copy);
} else {
*keyring_part_out = keyring_part;
*key_part_out = key_part_copy;
*type_part_out = type_part_copy;
}
return r;
}
static int parse_vk_and_keyring_description(
struct crypt_device *cd,
char **keyring_key_descriptions,
int keyring_links_count)
{
int r = 0;
char *keyring_part_out1 = NULL, *key_part_out1 = NULL, *type_part_out1 = NULL;
char *keyring_part_out2 = NULL, *key_part_out2 = NULL, *type_part_out2 = NULL;
if (keyring_links_count > 0) {
r = parse_single_vk_and_keyring_description(cd,
keyring_key_descriptions[0],
&keyring_part_out1, &key_part_out1,
&type_part_out1);
if (r < 0)
goto out;
}
if (keyring_links_count > 1) {
r = parse_single_vk_and_keyring_description(cd,
keyring_key_descriptions[1],
&keyring_part_out2, &key_part_out2,
&type_part_out2);
if (r < 0)
goto out;
if ((type_part_out1 && type_part_out2) && strcmp(type_part_out1, type_part_out2)) {
log_err(_("Key types have to be the same for both volume keys."));
r = -EINVAL;
goto out;
}
if ((keyring_part_out1 && keyring_part_out2) && strcmp(keyring_part_out1, keyring_part_out2)) {
log_err(_("Both volume keys have to be linked to the same keyring."));
r = -EINVAL;
goto out;
}
}
if (keyring_links_count > 0) {
r = crypt_set_keyring_to_link(cd, key_part_out1, key_part_out2,
type_part_out1, keyring_part_out1);
if (r == -EAGAIN)
log_err(_("You need to supply more key names."));
}
out:
if (r == -EINVAL)
log_err(_("Invalid --link-vk-to-keyring value."));
free(keyring_part_out1);
free(key_part_out1);
free(type_part_out1);
free(keyring_part_out2);
free(key_part_out2);
free(type_part_out2);
free(keyring_part);
return r;
}
@@ -1828,7 +1903,7 @@ static int action_open_luks(void)
}
if (ARG_SET(OPT_LINK_VK_TO_KEYRING_ID)) {
r = parse_vk_and_keyring_description(cd, ARG_STR(OPT_LINK_VK_TO_KEYRING_ID));
r = parse_vk_and_keyring_description(cd, keyring_links, keyring_links_count);
if (r < 0)
goto out;
}
@@ -2730,7 +2805,7 @@ static int action_luksResume(void)
return r;
if (ARG_SET(OPT_LINK_VK_TO_KEYRING_ID)) {
r = parse_vk_and_keyring_description(cd, ARG_STR(OPT_LINK_VK_TO_KEYRING_ID));
r = parse_vk_and_keyring_description(cd, keyring_links, keyring_links_count);
if (r < 0)
goto out;
}
@@ -3658,6 +3733,7 @@ static void basic_options_cb(poptContext popt_context,
const char *arg,
void *data __attribute__((unused)))
{
char buf[128];
tools_parse_arg_value(popt_context, tool_core_args[key->val].type, tool_core_args + key->val, arg, key->val, needs_size_conversion);
/* special cases additional handling */
@@ -3709,6 +3785,18 @@ static void basic_options_cb(poptContext popt_context,
_("Key size must be a multiple of 8 bits"),
poptGetInvocationName(popt_context));
break;
case OPT_LINK_VK_TO_KEYRING_ID:
if (keyring_links_count < MAX_KEYRING_LINKS)
keyring_links[keyring_links_count++] = strdup(ARG_STR(OPT_LINK_VK_TO_KEYRING_ID));
else {
if (snprintf(buf, sizeof(buf), _("At most %d keyring link specifications can be supplied."), MAX_KEYRING_LINKS) < 0)
buf[0] = '\0';
usage(popt_context, EXIT_FAILURE,
buf,
poptGetInvocationName(popt_context));
}
break;
case OPT_REDUCE_DEVICE_SIZE_ID:
if (ARG_UINT64(OPT_REDUCE_DEVICE_SIZE_ID) > 1024 * 1024 * 1024)
usage(popt_context, EXIT_FAILURE, _("Maximum device reduce size is 1 GiB."),