mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-16 13:20:11 +01:00
Add --keep-key parameter for LUKS2 reencryption.
One of missing features when comparing to legacy reencryption code.
This commit is contained in:
@@ -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.
|
||||
|
||||
118
src/cryptsetup.c
118
src/cryptsetup.c
@@ -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(¶ms.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))) {
|
||||
log_err(_("No known cipher specification pattern detected."));
|
||||
return r;
|
||||
} 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, ¶ms);
|
||||
out:
|
||||
crypt_safe_free(vk);
|
||||
for (i = 0; i < kp_size; i++) {
|
||||
crypt_safe_free(kp[i].password);
|
||||
if (r < 0 && kp[i].new >= 0 &&
|
||||
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);
|
||||
if (kp) {
|
||||
for (i = 0; i < kp_size; i++) {
|
||||
crypt_safe_free(kp[i].password);
|
||||
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);
|
||||
}
|
||||
free(kp);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -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, {}, {})
|
||||
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user