Add --keep-key parameter for LUKS2 reencryption.

One of missing features when comparing to legacy
reencryption code.
This commit is contained in:
Ondrej Kozina
2021-04-26 13:15:51 +02:00
parent 98cd52c8d7
commit 74ad0d71b9
4 changed files with 92 additions and 35 deletions

View File

@@ -1024,6 +1024,12 @@ Using /dev/random can block a long time, potentially
forever, if not enough entropy can be harvested by
the kernel.
.TP
.B "\-\-keep-key"
Do not change volume key in \fIreencrypt\fR action.
With LUKS2 it'll reuse key and change other parameters provided it is
requested. With LUKS1 it just reencrypts the LUKS header and keyslots.
.TP
.B "\-\-key\-slot, \-S <0\-N>"
For LUKS operations that add key material, this options allows you
to specify which key slot is selected for the new key.

View File

@@ -3160,9 +3160,9 @@ static int init_passphrase(struct keyslot_passwords *kp, size_t keyslot_password
return r;
}
static int _check_luks2_keyslots(struct crypt_device *cd)
static int _check_luks2_keyslots(struct crypt_device *cd, bool new_vk)
{
int i, max = crypt_keyslot_max(CRYPT_LUKS2), active = 0, unbound = 0;
int i, new_vk_slot = (new_vk ? 1 : 0), max = crypt_keyslot_max(CRYPT_LUKS2), active = 0, unbound = 0;
if (max < 0)
return max;
@@ -3184,14 +3184,17 @@ static int _check_luks2_keyslots(struct crypt_device *cd)
}
}
/* at least one keyslot for reencryption plus new volume key */
if (active + unbound > max - 2) {
/* at least one keyslot for reencryption plus new volume key (if needed) */
if (active + unbound + new_vk_slot + 1 > max) {
log_err(_("Not enough free keyslots for reencryption."));
return -EINVAL;
}
if (!new_vk)
return 0;
if ((ARG_INT32(OPT_KEY_SLOT_ID) == CRYPT_ANY_SLOT) &&
(2 * active + unbound > max - 1)) {
(2 * active + unbound + 1 > max)) {
log_err(_("Not enough free keyslots for reencryption."));
return -EINVAL;
}
@@ -3200,13 +3203,14 @@ static int _check_luks2_keyslots(struct crypt_device *cd)
}
static int fill_keyslot_passwords(struct crypt_device *cd,
struct keyslot_passwords *kp, size_t kp_size)
struct keyslot_passwords *kp, size_t kp_size,
bool new_vk)
{
char msg[128];
crypt_keyslot_info ki;
int i, r = 0;
if (ARG_INT32(OPT_KEY_SLOT_ID) == CRYPT_ANY_SLOT && ARG_SET(OPT_KEY_FILE_ID)) {
if (new_vk && ARG_INT32(OPT_KEY_SLOT_ID) == CRYPT_ANY_SLOT && ARG_SET(OPT_KEY_FILE_ID)) {
for (i = 0; (size_t)i < kp_size; i++) {
ki = crypt_keyslot_status(cd, i);
if (ki == CRYPT_SLOT_INVALID)
@@ -3224,6 +3228,9 @@ static int fill_keyslot_passwords(struct crypt_device *cd,
if (snprintf(msg, sizeof(msg), _("Enter passphrase for key slot %d: "), i) < 0)
return -EINVAL;
r = init_passphrase(kp, kp_size, cd, msg, i);
/* no need to initialize all keyslots with --keep-key */
if (r >= 0 && !new_vk)
break;
if (r == -ENOENT)
r = 0;
if (r < 0)
@@ -3255,11 +3262,12 @@ static int assign_tokens(struct crypt_device *cd, int keyslot_old, int keyslot_n
static int action_reencrypt_luks2(struct crypt_device *cd)
{
bool new_vk_size, new_sector_size, new_vk;
size_t i, vk_size, kp_size;
int r, keyslot_old = CRYPT_ANY_SLOT, keyslot_new = CRYPT_ANY_SLOT, key_size;
char dm_name[PATH_MAX], cipher [MAX_CIPHER_LEN], mode[MAX_CIPHER_LEN], *vk = NULL;
const char *active_name = NULL;
struct keyslot_passwords *kp;
const char *active_name = NULL, *new_cipher = NULL;
struct keyslot_passwords *kp = NULL;
struct crypt_params_luks2 luks2_params = {};
struct crypt_params_reencrypt params = {
.mode = CRYPT_REENCRYPT_REENCRYPT,
@@ -3274,28 +3282,36 @@ static int action_reencrypt_luks2(struct crypt_device *cd)
_set_reencryption_flags(&params.flags);
if (!ARG_SET(OPT_CIPHER_ID) && crypt_is_cipher_null(crypt_get_cipher(cd))) {
/* cipher */
if (ARG_SET(OPT_CIPHER_ID))
new_cipher = ARG_STR(OPT_CIPHER_ID);
else if (!ARG_SET(OPT_CIPHER_ID) && crypt_is_cipher_null(crypt_get_cipher(cd))) {
log_std(_("Switching data encryption cipher to %s.\n"), DEFAULT_CIPHER(LUKS1));
ARG_SET_STR(OPT_CIPHER_ID, strdup(DEFAULT_CIPHER(LUKS1)));
new_cipher = DEFAULT_CIPHER(LUKS1);
}
if (!ARG_SET(OPT_CIPHER_ID)) {
if (!new_cipher) {
strncpy(cipher, crypt_get_cipher(cd), MAX_CIPHER_LEN - 1);
strncpy(mode, crypt_get_cipher_mode(cd), MAX_CIPHER_LEN - 1);
cipher[MAX_CIPHER_LEN-1] = '\0';
mode[MAX_CIPHER_LEN-1] = '\0';
} else if ((r = crypt_parse_name_and_mode(ARG_STR(OPT_CIPHER_ID), cipher, NULL, mode))) {
} else {
if ((r = crypt_parse_name_and_mode(new_cipher, cipher, NULL, mode))) {
log_err(_("No known cipher specification pattern detected."));
return r;
}
/* the segment cipher is identical with existing one */
if (!strcmp(cipher, crypt_get_cipher(cd)) && !strcmp(mode, crypt_get_cipher_mode(cd)))
new_cipher = NULL;
}
/* sector size */
luks2_params.sector_size = ARG_UINT32(OPT_SECTOR_SIZE_ID) ?: (uint32_t)crypt_get_sector_size(cd);
new_sector_size = luks2_params.sector_size != (uint32_t)crypt_get_sector_size(cd);
r = _check_luks2_keyslots(cd);
if (r)
return r;
if (ARG_SET(OPT_KEY_SIZE_ID) || ARG_SET(OPT_CIPHER_ID))
/* key size */
if (ARG_SET(OPT_KEY_SIZE_ID) || new_cipher)
key_size = get_adjusted_key_size(mode, DEFAULT_LUKS1_KEYBITS, 0);
else
key_size = crypt_get_volume_key_size(cd);
@@ -3304,28 +3320,58 @@ static int action_reencrypt_luks2(struct crypt_device *cd)
return -EINVAL;
vk_size = key_size;
new_vk_size = key_size != crypt_get_volume_key_size(cd);
/* volume key */
new_vk = !ARG_SET(OPT_KEEP_KEY_ID);
if (new_vk && ARG_SET(OPT_MASTER_KEY_FILE_ID)) {
r = tools_read_mk(ARG_STR(OPT_MASTER_KEY_FILE_ID), &vk, key_size);
if (r < 0)
goto out;
if (!crypt_activate_by_volume_key(cd, NULL, vk, key_size, 0)) {
/* passed key was valid volume key */
new_vk = false;
crypt_safe_free(vk);
vk = NULL;
}
}
if (!new_vk && !new_vk_size && !new_cipher && !new_sector_size) {
log_err(_("No data segment parameters changed. Reencryption aborted."));
r = -EINVAL;
goto out;
}
r = _check_luks2_keyslots(cd, new_vk);
if (r)
return r;
r = crypt_keyslot_max(CRYPT_LUKS2);
if (r < 0)
return r;
kp_size = r;
kp = init_keyslot_passwords(kp_size);
kp = init_keyslot_passwords(kp_size);
if (!kp)
return -ENOMEM;
r = fill_keyslot_passwords(cd, kp, kp_size);
r = fill_keyslot_passwords(cd, kp, kp_size, new_vk);
if (r)
goto out;
if (ARG_SET(OPT_MASTER_KEY_FILE_ID)) {
r = tools_read_mk(ARG_STR(OPT_MASTER_KEY_FILE_ID), &vk, key_size);
if (r < 0)
goto out;
}
r = -ENOENT;
for (i = 0; i < kp_size; i++) {
if (!new_vk) {
if (kp[i].password) {
r = keyslot_old = kp[i].new = i;
break;
}
continue;
}
if (kp[i].password && keyslot_new < 0) {
r = set_keyslot_params(cd, i);
if (r < 0)
@@ -3389,14 +3435,16 @@ static int action_reencrypt_luks2(struct crypt_device *cd)
cipher, mode, &params);
out:
crypt_safe_free(vk);
if (kp) {
for (i = 0; i < kp_size; i++) {
crypt_safe_free(kp[i].password);
if (r < 0 && kp[i].new >= 0 &&
if (r < 0 && kp[i].new >= 0 && kp[i].new != (int)i &&
crypt_reencrypt_status(cd, NULL) == CRYPT_REENCRYPT_NONE &&
crypt_keyslot_destroy(cd, kp[i].new))
log_dbg("Failed to remove keyslot %d with unbound key.", kp[i].new);
}
free(kp);
}
return r;
}

View File

@@ -83,6 +83,8 @@ ARG(OPT_IV_LARGE_SECTORS, '\0', POPT_ARG_NONE, N_("Use IV counted in sector size
ARG(OPT_JSON_FILE, '\0', POPT_ARG_STRING, N_("Read or write the json from or to a file"), NULL, CRYPT_ARG_STRING, {}, {})
ARG(OPT_KEEP_KEY, '\0', POPT_ARG_NONE, N_("Do not change volume key."), NULL, CRYPT_ARG_BOOL, {}, OPT_KEEP_KEY_ACTIONS)
ARG(OPT_KEY_DESCRIPTION, '\0', POPT_ARG_STRING, N_("Key description"), NULL, CRYPT_ARG_STRING, {}, {})
ARG(OPT_KEY_FILE, 'd', POPT_ARG_STRING, N_("Read the key from a file"), NULL, CRYPT_ARG_STRING, {}, {})

View File

@@ -58,6 +58,7 @@
#define OPT_DEFERRED_ACTIONS { CLOSE_ACTION }
#define OPT_HOTZONE_SIZE_ACTIONS { REENCRYPT_ACTION }
#define OPT_INTEGRITY_ACTIONS { FORMAT_ACTION }
#define OPT_KEEP_KEY_ACTIONS { REENCRYPT_ACTION }
#define OPT_KEY_SIZE_ACTIONS { OPEN_ACTION, BENCHMARK_ACTION, FORMAT_ACTION, REENCRYPT_ACTION, ADDKEY_ACTION }
#define OPT_KEY_SLOT_ACTIONS { OPEN_ACTION, REENCRYPT_ACTION, CONFIG_ACTION, FORMAT_ACTION, ADDKEY_ACTION, CHANGEKEY_ACTION, CONVERTKEY_ACTION, LUKSDUMP_ACTION, TOKEN_ACTION }
#define OPT_LABEL_ACTIONS { CONFIG_ACTION, FORMAT_ACTION }