Commit a22a24bc authored by Milan Broz's avatar Milan Broz

Support detached header for cryptsetup-reencrypt.

This patch allows encryption/decryption of the whole device,
IOW add encryption later with detached header.

This operation can be dangerous, there is no fixed bindings between
the specific LUKS header and data device (encrypted data device
contains no magic signatures).
parent b7c24658
......@@ -35,14 +35,14 @@ To start (or continue) re-encryption for <device> use:
\fIcryptsetup-reencrypt\fR <device>
\fB<options>\fR can be [\-\-batch-mode, \-\-block-size, \-\-cipher | \-\-keep-key,
\-\-debug, \-\-device-size, \-\-hash, \-\-iter-time | \-\-pbkdf\-force\-iterations,
\-\-debug, \-\-device-size, \-\-hash, \-\-header, \-\-iter-time | \-\-pbkdf\-force\-iterations,
\-\-key-file, \-\-key-size, \-\-key-slot, \-\-keyfile-offset, \-\-keyfile-size,
\-\-tries, \-\-pbkdf, \-\-pbkdf\-memory, \-\-pbkdf\-parallel, \-\-progress-frequency,
\-\-use-directio, \-\-use-random | \-\-use-urandom, \-\-use-fsync, \-\-uuid,
\-\-verbose, \-\-write-log]
To encrypt data on (not yet encrypted) device, use \fI\-\-new\fR with combination
with \fI\-\-reduce-device-size\fR.
with \fI\-\-reduce-device-size\fR or with \fI\-\-header\fR option for detached header.
To remove encryption from device, use \fI\-\-decrypt\fR.
......@@ -89,7 +89,17 @@ Specifies the hash used in the LUKS1 key setup scheme and volume key digest.
for new LUKS1 device header.
\fBNOTE:\fR with LUKS2 format this option is only relevant when new keyslot pbkdf algorithm
is set to PBKDF2 (see \fI\-\-pbkdf).
is set to PBKDF2 (see \fI\-\-pbkdf\fR).
.TP
.B "\-\-header\fR \fI<LUKS header file>\fR"
Use a detached (separated) metadata device or file where the
LUKS header is stored. This option allows one to store ciphertext
and LUKS header on different devices.
\fBWARNING:\fR There is no check whether the ciphertext device specified
actually belongs to the header given.
If used with \fI\-\-new\fR option, the header file will created (or overwritten).
Use with care.
.TP
.B "\-\-iter-time, \-i \fI<milliseconds>\fR"
The number of milliseconds to spend with PBKDF2 passphrase processing for the
......
......@@ -55,6 +55,7 @@ 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_header_device = NULL;
static const char *opt_reduce_size_str = NULL;
static uint64_t opt_reduce_size = 0;
......@@ -68,6 +69,7 @@ static const char **action_argv;
#define MAX_TOKEN 32
struct reenc_ctx {
char *device;
char *device_header;
char *device_uuid;
const char *type;
uint64_t device_size; /* overridden by parameter */
......@@ -147,6 +149,11 @@ static const char *luksType(const char *type)
return NULL;
}
static const char *hdr_device(const struct reenc_ctx *rc)
{
return rc->device_header ?: rc->device;
}
static int set_reencrypt_requirement(const struct reenc_ctx *rc)
{
uint32_t reqs;
......@@ -154,7 +161,7 @@ static int set_reencrypt_requirement(const struct reenc_ctx *rc)
struct crypt_device *cd = NULL;
struct crypt_params_integrity ip = { 0 };
if (crypt_init(&cd, rc->device) ||
if (crypt_init(&cd, hdr_device(rc)) ||
crypt_load(cd, CRYPT_LUKS2, NULL) ||
crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &reqs))
goto out;
......@@ -179,7 +186,7 @@ out:
}
/* Depends on the first two fields of LUKS1 header format, magic and version */
static int device_check(struct reenc_ctx *rc, header_magic set_magic)
static int device_check(struct reenc_ctx *rc, const char *device, header_magic set_magic)
{
char *buf = NULL;
int r, devfd;
......@@ -187,14 +194,14 @@ static int device_check(struct reenc_ctx *rc, header_magic set_magic)
uint16_t version;
size_t buf_size = pagesize();
devfd = open(rc->device, O_RDWR | O_EXCL | O_DIRECT);
devfd = open(device, O_RDWR | O_EXCL | O_DIRECT);
if (devfd == -1) {
if (errno == EBUSY) {
log_err(_("Cannot exclusively open %s, device in use.\n"),
rc->device);
device);
return -EBUSY;
}
log_err(_("Cannot open device %s.\n"), rc->device);
log_err(_("Cannot open device %s.\n"), device);
return -EINVAL;
}
......@@ -211,7 +218,7 @@ static int device_check(struct reenc_ctx *rc, header_magic set_magic)
s = read(devfd, buf, buf_size);
if (s < 0 || s != (ssize_t)buf_size) {
log_err(_("Cannot read device %s.\n"), rc->device);
log_err(_("Cannot read device %s.\n"), device);
r = -EIO;
goto out;
}
......@@ -222,11 +229,11 @@ static int device_check(struct reenc_ctx *rc, header_magic set_magic)
if (set_magic == MAKE_UNUSABLE && !memcmp(buf, MAGIC, MAGIC_L) &&
version == 1) {
log_verbose(_("Marking LUKS1 device %s unusable.\n"), rc->device);
log_verbose(_("Marking LUKS1 device %s unusable.\n"), device);
memcpy(buf, NOMAGIC, MAGIC_L);
r = 0;
} else if (set_magic == MAKE_UNUSABLE && version == 2) {
log_verbose(_("Setting LUKS2 offline reencrypt flag on device %s.\n"), rc->device);
log_verbose(_("Setting LUKS2 offline reencrypt flag on device %s.\n"), device);
r = set_reencrypt_requirement(rc);
if (!r)
rc->stained = 1;
......@@ -243,14 +250,14 @@ static int device_check(struct reenc_ctx *rc, header_magic set_magic)
goto out;
s = write(devfd, buf, buf_size);
if (s < 0 || s != (ssize_t)buf_size) {
log_err(_("Cannot write device %s.\n"), rc->device);
log_err(_("Cannot write device %s.\n"), device);
r = -EIO;
}
if (s > 0 && set_magic == MAKE_UNUSABLE)
rc->stained = 1;
}
if (r)
log_dbg("LUKS signature check failed for %s.", rc->device);
log_dbg("LUKS signature check failed for %s.", device);
out:
if (buf)
memset(buf, 0, buf_size);
......@@ -284,7 +291,7 @@ static int create_empty_header(const char *new_file, const char *old_file,
* if requesting key size change, try to use offset
* here can be enough space to fit new key.
*/
if (opt_key_size)
if (opt_key_size && data_sector)
size = data_sector * SECTOR_SIZE;
/* if reducing size, be sure we have enough space */
......@@ -663,9 +670,9 @@ static int backup_luks_headers(struct reenc_ctx *rc)
size_t old_key_size;
int r;
log_dbg("Creating LUKS header backup for device %s.", rc->device);
log_dbg("Creating LUKS header backup for device %s.", hdr_device(rc));
if ((r = crypt_init(&cd, rc->device)) ||
if ((r = crypt_init(&cd, hdr_device(rc))) ||
(r = crypt_load(cd, CRYPT_LUKS, NULL)))
goto out;
......@@ -834,21 +841,42 @@ static void remove_headers(struct reenc_ctx *rc)
static int restore_luks_header(struct reenc_ctx *rc)
{
struct stat st;
struct crypt_device *cd = NULL;
int r;
int fd, r;
log_dbg("Restoring header for %s from %s.", rc->device, rc->header_file_new);
log_dbg("Restoring header for %s from %s.", hdr_device(rc), rc->header_file_new);
r = crypt_init(&cd, rc->device);
/*
* For new encryption and new detached header in file just move it.
* For existing file try to ensure we have prealocated space for restore.
*/
if (opt_new && rc->device_header) {
r = stat(rc->device_header, &st);
if (r == -1) {
r = rename(rc->header_file_new, rc->device_header);
goto out;
} else if ((st.st_mode & S_IFMT) == S_IFREG &&
stat(rc->header_file_new, &st) != -1) {
fd = open(rc->device_header, O_WRONLY);
if (fd != -1) {
posix_fallocate(fd, 0, st.st_size);
close(fd);
}
}
}
r = crypt_init(&cd, hdr_device(rc));
if (r == 0) {
r = crypt_header_restore(cd, rc->type, rc->header_file_new);
}
crypt_free(cd);
out:
if (r)
log_err(_("Cannot restore %s header on device %s.\n"), rc->device, isLUKS2(rc->type) ? "LUKS2" : "LUKS1");
log_err(_("Cannot restore %s header on device %s.\n"), hdr_device(rc), isLUKS2(rc->type) ? "LUKS2" : "LUKS1");
else {
log_verbose(_("%s header on device %s restored.\n"), rc->device, isLUKS2(rc->type) ? "LUKS2" : "LUKS1");
log_verbose(_("%s header on device %s restored.\n"), hdr_device(rc), isLUKS2(rc->type) ? "LUKS2" : "LUKS1");
rc->stained = 0;
}
return r;
......@@ -1140,7 +1168,7 @@ static int initialize_uuid(struct reenc_ctx *rc)
}
/* Try to load LUKS from device */
if ((r = crypt_init(&cd, rc->device)))
if ((r = crypt_init(&cd, hdr_device(rc))))
return r;
crypt_set_log_callback(cd, _quiet_log, NULL);
r = crypt_load(cd, CRYPT_LUKS, NULL);
......@@ -1148,7 +1176,7 @@ static int initialize_uuid(struct reenc_ctx *rc)
rc->device_uuid = strdup(crypt_get_uuid(cd));
else
/* Reencryption already in progress - magic header? */
r = device_check(rc, CHECK_UNUSABLE);
r = device_check(rc, hdr_device(rc), CHECK_UNUSABLE);
if (!r)
rc->type = isLUKS2(crypt_get_type(cd)) ? CRYPT_LUKS2 : CRYPT_LUKS1;
......@@ -1309,7 +1337,10 @@ static int initialize_context(struct reenc_ctx *rc, const char *device)
if (!(rc->device = strndup(device, PATH_MAX)))
return -ENOMEM;
if (device_check(rc, CHECK_OPEN) < 0)
if (opt_header_device && !(rc->device_header = strndup(opt_header_device, PATH_MAX)))
return -ENOMEM;
if (device_check(rc, rc->device, CHECK_OPEN) < 0)
return -EINVAL;
if (initialize_uuid(rc)) {
......@@ -1397,6 +1428,7 @@ static void destroy_context(struct reenc_ctx *rc)
crypt_safe_free(rc->p[i].password);
free(rc->device);
free(rc->device_header);
free(rc->device_uuid);
}
......@@ -1405,10 +1437,10 @@ static int luks2_change_pbkdf_params(struct reenc_ctx *rc)
int i, r;
struct crypt_device *cd;
if ((r = initialize_passphrase(rc, rc->device)))
if ((r = initialize_passphrase(rc, hdr_device(rc))))
return r;
if (crypt_init(&cd, rc->device) ||
if (crypt_init(&cd, hdr_device(rc)) ||
crypt_load(cd, CRYPT_LUKS2, NULL))
return -EINVAL;
......@@ -1461,7 +1493,7 @@ static int run_reencrypt(const char *device)
log_dbg("Running reencryption.");
if (!rc.in_progress) {
if ((r = initialize_passphrase(&rc, rc.device)))
if ((r = initialize_passphrase(&rc, hdr_device(&rc))))
goto out;
if (rc.reencrypt_mode == ENCRYPT) {
......@@ -1475,7 +1507,7 @@ static int run_reencrypt(const char *device)
if (rc.reencrypt_mode == DECRYPT &&
(r = backup_fake_header(&rc)))
goto out;
if ((r = device_check(&rc, MAKE_UNUSABLE)))
if ((r = device_check(&rc, hdr_device(&rc), MAKE_UNUSABLE)))
goto out;
}
} else {
......@@ -1558,6 +1590,7 @@ int main(int argc, const char **argv)
{ "pbkdf-memory", '\0', POPT_ARG_LONG, &opt_pbkdf_memory, 0, N_("PBKDF memory cost limit"), N_("kilobytes") },
{ "pbkdf-parallel", '\0', POPT_ARG_LONG, &opt_pbkdf_parallel, 0, N_("PBKDF parallel cost "), N_("threads") },
{ "pbkdf-force-iterations",'\0',POPT_ARG_LONG, &opt_pbkdf_iterations, 0, N_("PBKDF iterations cost (forced, disables benchmark)"), NULL },
{ "header", '\0', POPT_ARG_STRING, &opt_header_device, 0, N_("Device or file with separated LUKS header."), NULL },
POPT_TABLEEND
};
poptContext popt_context;
......@@ -1656,8 +1689,8 @@ int main(int argc, const char **argv)
usage(popt_context, EXIT_FAILURE, _("Reduce size must be multiple of 512 bytes sector."),
poptGetInvocationName(popt_context));
if (opt_new && !opt_reduce_size)
usage(popt_context, EXIT_FAILURE, _("Option --new must be used together with --reduce-device-size."),
if (opt_new && (!opt_reduce_size && !opt_header_device))
usage(popt_context, EXIT_FAILURE, _("Option --new must be used together with --reduce-device-size or --header."),
poptGetInvocationName(popt_context));
if (opt_keep_key && ((!opt_hash && !opt_iteration_time && !opt_pbkdf_iterations) || opt_cipher || opt_new))
......
......@@ -7,6 +7,7 @@ FAST_PBKDF="--pbkdf-force-iterations 1000"
DEV_NAME=reenc9768
DEV_NAME2=reenc1273
IMG=reenc-data
IMG_HDR=$IMG.hdr
ORIG_IMG=reenc-data-orig
KEY1=key1
PWD1="93R4P4pIqAH8"
......@@ -27,7 +28,7 @@ function remove_mapping()
[ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2
[ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
[ ! -z "$LOOPDEV1" ] && losetup -d $LOOPDEV1 >/dev/null 2>&1
rm -f $IMG $ORIG_IMG $KEY1 >/dev/null 2>&1
rm -f $IMG $IMG_HDR $ORIG_IMG $KEY1 >/dev/null 2>&1
umount $MNT_DIR > /dev/null 2>&1
rmdir $MNT_DIR > /dev/null 2>&1
LOOPDEV1=""
......@@ -62,9 +63,11 @@ function add_scsi_device() {
[ -b $SCSI_DEV ] || fail "Cannot find $SCSI_DEV."
}
function open_crypt()
function open_crypt() # $1 pwd, $2 hdr
{
if [ -n "$1" ] ; then
if [ -n "$2" ] ; then
echo "$1" | $CRYPTSETUP luksOpen $LOOPDEV1 $DEV_NAME --header $2 || fail
elif [ -n "$1" ] ; then
echo "$1" | $CRYPTSETUP luksOpen $LOOPDEV1 $DEV_NAME || fail
else
$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV1 $DEV_NAME || fail
......@@ -104,9 +107,9 @@ function check_hash_dev() # $1 dev, $2 hash
[ $HASH != "$2" ] && fail "HASH differs ($HASH)"
}
function check_hash() # $1 pwd, $2 hash
function check_hash() # $1 pwd, $2 hash, $3 hdr
{
open_crypt $1
open_crypt $1 $3
check_hash_dev /dev/mapper/$DEV_NAME $2
$CRYPTSETUP remove $DEV_NAME || fail
}
......@@ -336,5 +339,21 @@ check_hash $PWD1 $HASH1
echo $PWD1 | $REENC $LOOPDEV1 -q --decrypt
check_hash_dev $LOOPDEV1 $HASH4
echo "[11] Detached header - adding encryption/reencryption/decryption"
prepare 8192
check_hash_dev $IMG $HASH4
echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --header $IMG_HDR --new
check_hash $PWD1 $HASH4 $IMG_HDR
echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --header $IMG_HDR
check_hash $PWD1 $HASH4 $IMG_HDR
echo $PWD1 | $REENC $LOOPDEV1 -q --header $IMG_HDR --decrypt
check_hash_dev $IMG $HASH4
# existing header of zero size
cat /dev/null >$IMG_HDR
echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --header $IMG_HDR --new
check_hash $PWD1 $HASH4 $IMG_HDR
$CRYPTSETUP isLuks $LOOPDEV1 && fail
$CRYPTSETUP isLuks $IMG_HDR || fail
remove_mapping
exit 0
......@@ -8,6 +8,7 @@ DEFAULT_ARGON="argon2i"
DEV_NAME=reenc9768
DEV_NAME2=reenc1273
IMG=reenc-data
IMG_HDR=$IMG.hdr
ORIG_IMG=reenc-data-orig
KEY1=key1
PWD1="93R4P4pIqAH8"
......@@ -43,7 +44,7 @@ function remove_mapping()
[ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2
[ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
[ ! -z "$LOOPDEV1" ] && losetup -d $LOOPDEV1 >/dev/null 2>&1
rm -f $IMG $ORIG_IMG $KEY1 >/dev/null 2>&1
rm -f $IMG $IMG_HDR $ORIG_IMG $KEY1 >/dev/null 2>&1
umount $MNT_DIR > /dev/null 2>&1
rmdir $MNT_DIR > /dev/null 2>&1
LOOPDEV1=""
......@@ -78,9 +79,11 @@ function add_scsi_device() {
[ -b $SCSI_DEV ] || fail "Cannot find $SCSI_DEV."
}
function open_crypt()
function open_crypt() # $1 pwd, $2 hdr
{
if [ -n "$1" ] ; then
if [ -n "$2" ] ; then
echo "$1" | $CRYPTSETUP luksOpen $LOOPDEV1 $DEV_NAME --header $2 || fail
elif [ -n "$1" ] ; then
echo "$1" | $CRYPTSETUP luksOpen $LOOPDEV1 $DEV_NAME || fail
else
$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV1 $DEV_NAME || fail
......@@ -120,9 +123,9 @@ function check_hash_dev() # $1 dev, $2 hash
[ $HASH != "$2" ] && fail "HASH differs ($HASH)"
}
function check_hash() # $1 pwd, $2 hash
function check_hash() # $1 pwd, $2 hash, $hdr
{
open_crypt $1
open_crypt $1 $3
check_hash_dev /dev/mapper/$DEV_NAME $2
$CRYPTSETUP remove $DEV_NAME || fail
}
......@@ -386,5 +389,22 @@ if [ -n "$ALLOW_DISCARDS" ]; then
$CRYPTSETUP luksDump $LOOPDEV1 | grep -m1 Flags: | grep allow-discards > /dev/null || fail
fi
echo "[13] Detached header - adding encryption/reencryption/decryption"
prepare 8192
check_hash_dev $IMG $HASH4
echo $PWD1 | $REENC --type luks2 $LOOPDEV1 -q $FAST_PBKDF --header $IMG_HDR --new
check_hash $PWD1 $HASH4 $IMG_HDR
echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --header $IMG_HDR
check_hash $PWD1 $HASH4 $IMG_HDR
echo $PWD1 | $REENC $LOOPDEV1 -q --header $IMG_HDR --decrypt
check_hash_dev $IMG $HASH4
# existing header of zero size
cat /dev/null >$IMG_HDR
echo $PWD1 | $REENC --type luks2 $LOOPDEV1 -q $FAST_PBKDF --header $IMG_HDR --new
check_hash $PWD1 $HASH4 $IMG_HDR
$CRYPTSETUP isLuks $LOOPDEV1 && fail
$CRYPTSETUP isLuks $IMG_HDR || fail
$CRYPTSETUP luksDump $IMG_HDR | grep -q "0: luks2" || fail
remove_mapping
exit 0
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment