Support permanent device decryption using cryptsetup-reencrypt --decrypt.

This commit is contained in:
Milan Broz
2015-01-27 14:09:29 +01:00
parent 8157e47ad4
commit 6d51e8ab69
3 changed files with 162 additions and 36 deletions

View File

@@ -2,7 +2,7 @@
* cryptsetup-reencrypt - crypt utility for offline re-encryption
*
* Copyright (C) 2012, Red Hat, Inc. All rights reserved.
* Copyright (C) 2012-2014, Milan Broz All rights reserved.
* Copyright (C) 2012-2015, Milan Broz All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -48,6 +48,7 @@ static int opt_key_slot = CRYPT_ANY_SLOT;
static int opt_key_size = 0;
static int opt_new = 0;
static int opt_keep_key = 0;
static int opt_decrypt = 0;
static const char *opt_reduce_size_str = NULL;
static uint64_t opt_reduce_size = 0;
@@ -62,12 +63,14 @@ struct reenc_ctx {
char *device;
char *device_uuid;
uint64_t device_size; /* overrided by parameter */
uint64_t device_size_real;
uint64_t device_size_new_real;
uint64_t device_size_org_real;
uint64_t device_offset;
uint64_t device_shift;
int in_progress:1;
enum { FORWARD = 0, BACKWARD = 1 } reencrypt_direction;
enum { REENCRYPT = 0, ENCRYPT = 1, DECRYPT = 2 } reencrypt_mode;
char header_file_org[PATH_MAX];
char header_file_new[PATH_MAX];
@@ -265,9 +268,9 @@ static int write_log(struct reenc_ctx *rc)
memset(rc->log_buf, 0, SECTOR_SIZE);
snprintf(rc->log_buf, SECTOR_SIZE, "# LUKS reencryption log, DO NOT EDIT OR DELETE.\n"
"version = %d\nUUID = %s\ndirection = %d\n"
"version = %d\nUUID = %s\ndirection = %d\nmode = %d\n"
"offset = %" PRIu64 "\nshift = %" PRIu64 "\n# EOF\n",
1, rc->device_uuid, rc->reencrypt_direction,
2, rc->device_uuid, rc->reencrypt_direction, rc->reencrypt_mode,
rc->device_offset, rc->device_shift);
if (lseek(rc->log_fd, 0, SEEK_SET) == -1)
@@ -293,7 +296,7 @@ static int parse_line_log(struct reenc_ctx *rc, const char *line)
return 0;
if (sscanf(line, "version = %d", &i) == 1) {
if (i != 1) {
if (i < 1 || i > 2) {
log_dbg("Log: Unexpected version = %i", i);
return -EINVAL;
}
@@ -311,6 +314,13 @@ static int parse_line_log(struct reenc_ctx *rc, const char *line)
} else if (sscanf(line, "shift = %" PRIu64, &u64) == 1) {
log_dbg("Log: shift = %" PRIu64, u64);
rc->device_shift = u64;
} else if (sscanf(line, "mode = %d", &i) == 1) { /* added in v2 */
log_dbg("Log: mode = %i", i);
rc->reencrypt_mode = i;
if (rc->reencrypt_mode != REENCRYPT &&
rc->reencrypt_mode != ENCRYPT &&
rc->reencrypt_mode != DECRYPT)
return -EINVAL;
} else
return -EINVAL;
@@ -473,6 +483,10 @@ static int backup_luks_headers(struct reenc_ctx *rc)
goto out;
log_verbose(_("LUKS header backup of device %s created.\n"), rc->device);
/* For decrypt, new header will be fake one, so we are done here. */
if (rc->reencrypt_mode == DECRYPT)
goto out;
if ((r = create_empty_header(rc->header_file_new, rc->header_file_org,
crypt_get_data_offset(cd))))
goto out;
@@ -525,10 +539,13 @@ static int backup_fake_header(struct reenc_ctx *rc)
struct crypt_device *cd_new = NULL;
struct crypt_params_luks1 params = {0};
char cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
const char *header_file_fake;
int r;
log_dbg("Creating fake (cipher_null) header for original device.");
log_dbg("Creating fake (cipher_null) header for %s device.",
(rc->reencrypt_mode == DECRYPT) ? "new" : "original");
header_file_fake = (rc->reencrypt_mode == DECRYPT) ? rc->header_file_new : rc->header_file_org;
if (!opt_key_size)
opt_key_size = DEFAULT_LUKS1_KEYBITS;
@@ -541,7 +558,7 @@ static int backup_fake_header(struct reenc_ctx *rc)
}
}
r = create_empty_header(rc->header_file_org, NULL, 0);
r = create_empty_header(header_file_fake, NULL, MAX_BCK_SECTORS);
if (r < 0)
return r;
@@ -549,7 +566,7 @@ static int backup_fake_header(struct reenc_ctx *rc)
params.data_alignment = 0;
params.data_device = rc->device;
r = crypt_init(&cd_new, rc->header_file_org);
r = crypt_init(&cd_new, header_file_fake);
if (r < 0)
return r;
@@ -563,6 +580,10 @@ static int backup_fake_header(struct reenc_ctx *rc)
if (r < 0)
goto out;
/* The real header is backup header created in backup_luks_headers() */
if (rc->reencrypt_mode == DECRYPT)
goto out;
r = create_empty_header(rc->header_file_new, rc->header_file_org, 0);
if (r < 0)
goto out;
@@ -794,6 +815,41 @@ static int copy_data_backward(struct reenc_ctx *rc, int fd_old, int fd_new,
return quit ? -EAGAIN : 0;
}
static void zero_rest_of_device(int fd, size_t block_size, void *buf,
uint64_t *bytes, uint64_t offset)
{
ssize_t s1, s2;
log_dbg("Zeroing rest of device.");
if (lseek64(fd, offset, SEEK_SET) < 0) {
log_dbg(_("Cannot seek to device offset.\n"));
return;
}
memset(buf, 0, block_size);
s1 = block_size;
while (!quit && *bytes) {
if (*bytes < s1)
s1 = *bytes;
s2 = write(fd, buf, s1);
if (s2 < 0) {
log_dbg("Write error, expecting %zu, got %zd.",
block_size, s2);
return;
}
if (opt_fsync && fsync(fd) < 0) {
log_dbg("Write error, fsync.");
return;
}
*bytes -= s2;
}
}
static int copy_data(struct reenc_ctx *rc)
{
size_t block_size = opt_bsize * 1024 * 1024;
@@ -816,13 +872,22 @@ static int copy_data(struct reenc_ctx *rc)
goto out;
}
/* Check size */
if (ioctl(fd_new, BLKGETSIZE64, &rc->device_size_real) < 0) {
if (ioctl(fd_old, BLKGETSIZE64, &rc->device_size_org_real) < 0) {
log_err(_("Cannot get device size.\n"));
goto out;
}
rc->device_size = opt_device_size ?: rc->device_size_real;
if (ioctl(fd_new, BLKGETSIZE64, &rc->device_size_new_real) < 0) {
log_err(_("Cannot get device size.\n"));
goto out;
}
if (opt_device_size)
rc->device_size = opt_device_size;
else if (rc->reencrypt_mode == DECRYPT)
rc->device_size = rc->device_size_org_real;
else
rc->device_size = rc->device_size_new_real;
if (posix_memalign((void *)&buf, alignment(fd_new), block_size)) {
log_err(_("Allocation of aligned memory failed.\n"));
@@ -838,9 +903,18 @@ static int copy_data(struct reenc_ctx *rc)
else
r = copy_data_backward(rc, fd_old, fd_new, block_size, buf, &bytes);
set_int_block(1);
print_progress(rc, bytes, 1);
/* Zero (wipe) rest of now plain-only device when decrypting.
* (To not leave any sign of encryption here.) */
if (!r && rc->reencrypt_mode == DECRYPT &&
rc->device_size_new_real > rc->device_size_org_real) {
bytes = rc->device_size_new_real - rc->device_size_org_real;
zero_rest_of_device(fd_new, block_size, buf, &bytes, rc->device_size_org_real);
}
set_int_block(1);
if (r == -EAGAIN)
log_err(_("Interrupted by a signal.\n"));
else if (r < 0)
@@ -981,7 +1055,7 @@ static int initialize_passphrase(struct reenc_ctx *rc, const char *device)
log_dbg("Passhrases initialization.");
if (opt_new && !rc->in_progress) {
if (rc->reencrypt_mode == ENCRYPT && !rc->in_progress) {
r = init_passphrase1(rc, cd, _("Enter new passphrase: "), opt_key_slot, 0);
return r > 0 ? 0 : r;
}
@@ -1001,7 +1075,9 @@ static int initialize_passphrase(struct reenc_ctx *rc, const char *device)
if (opt_key_file) {
r = init_keyfile(rc, cd, opt_key_slot);
} else if (rc->in_progress || opt_key_slot != CRYPT_ANY_SLOT) {
} else if (rc->in_progress ||
opt_key_slot != CRYPT_ANY_SLOT ||
rc->reencrypt_mode == DECRYPT) {
r = init_passphrase1(rc, cd, msg, opt_key_slot, 1);
} else for (i = 0; i < MAX_SLOT; i++) {
ki = crypt_keyslot_status(cd, i);
@@ -1068,6 +1144,13 @@ static int initialize_context(struct reenc_ctx *rc, const char *device)
rc->reencrypt_direction = BACKWARD;
rc->device_offset = (uint64_t)~0;
}
if (opt_new)
rc->reencrypt_mode = ENCRYPT;
else if (opt_decrypt)
rc->reencrypt_mode = DECRYPT;
else
rc->reencrypt_mode = REENCRYPT;
}
return 0;
@@ -1109,14 +1192,23 @@ static int run_reencrypt(const char *device)
log_dbg("Running reencryption.");
if (!rc.in_progress) {
if (opt_new) {
if ((r = initialize_passphrase(&rc, rc.device)) ||
if ((r = initialize_passphrase(&rc, rc.device)))
goto out;
if (rc.reencrypt_mode == ENCRYPT) {
/* Create fake header for exising device */
if ((r = backup_fake_header(&rc)))
goto out;
} else {
if ((r = backup_luks_headers(&rc)))
goto out;
/* Create fake header for decrypted device */
if (rc.reencrypt_mode == DECRYPT &&
(r = backup_fake_header(&rc)))
goto out;
} else if ((r = initialize_passphrase(&rc, rc.device)) ||
(r = backup_luks_headers(&rc)) ||
(r = device_check(&rc, MAKE_UNUSABLE)))
goto out;
goto out;
if ((r = device_check(&rc, MAKE_UNUSABLE)))
goto out;
}
} else {
if ((r = initialize_passphrase(&rc, rc.header_file_new)))
goto out;
@@ -1132,7 +1224,9 @@ static int run_reencrypt(const char *device)
} else
log_dbg("Keeping existing key, skipping data area reencryption.");
r = restore_luks_header(&rc);
// FIXME: fix error path above to not skip this
if (rc.reencrypt_mode != DECRYPT)
r = restore_luks_header(&rc);
out:
destroy_context(&rc);
return r;
@@ -1184,7 +1278,8 @@ int main(int argc, const char **argv)
{ "keyfile-size", 'l', POPT_ARG_LONG, &opt_keyfile_size, 0, N_("Limits the read from keyfile"), N_("bytes") },
{ "reduce-device-size",'\0', POPT_ARG_STRING, &opt_reduce_size_str, 0, N_("Reduce data device size (move data offset). DANGEROUS!"), N_("bytes") },
{ "device-size", '\0', POPT_ARG_STRING, &opt_device_size_str, 0, N_("Use only specified device size (ignore rest of device). DANGEROUS!"), N_("bytes") },
{ "new", 'N', POPT_ARG_NONE,&opt_new, 0, N_("Create new header on not encrypted device."), NULL },
{ "new", 'N', POPT_ARG_NONE, &opt_new, 0, N_("Create new header on not encrypted device."), NULL },
{ "decrypt", '\0', POPT_ARG_NONE, &opt_decrypt, 0, N_("Permanently decrypt device (remove encryption)."), NULL },
POPT_TABLEEND
};
poptContext popt_context;
@@ -1279,6 +1374,14 @@ int main(int argc, const char **argv)
usage(popt_context, EXIT_FAILURE, _("Option --keep-key can be used only with --hash or --iter-time."),
poptGetInvocationName(popt_context));
if (opt_new && opt_decrypt)
usage(popt_context, EXIT_FAILURE, _("Option --new cannot be used together with --decrypt."),
poptGetInvocationName(popt_context));
if (opt_decrypt && (opt_cipher || opt_hash || opt_reduce_size || opt_keep_key || opt_device_size))
usage(popt_context, EXIT_FAILURE, _("Option --decrypt is incompatible with specified parameters."),
poptGetInvocationName(popt_context));
if (opt_debug) {
opt_verbose = 1;
crypt_set_debug_level(-1);