diff --git a/src/cryptsetup.h b/src/cryptsetup.h index 0c7ea31e..ca322f56 100644 --- a/src/cryptsetup.h +++ b/src/cryptsetup.h @@ -112,7 +112,8 @@ int tools_write_json_file(const char *file, const char *json); typedef enum { PRB_FILTER_NONE = 0, - PRB_FILTER_LUKS + PRB_FILTER_LUKS, + PRB_ONLY_LUKS } tools_probe_filter_info; int tools_detect_signatures(const char *device, tools_probe_filter_info filter, size_t *count, bool batch_mode); diff --git a/src/utils_blockdev.c b/src/utils_blockdev.c index cfac2e09..272d7114 100644 --- a/src/utils_blockdev.c +++ b/src/utils_blockdev.c @@ -220,11 +220,22 @@ int tools_detect_signatures(const char *device, tools_probe_filter_info filter, return -EINVAL; } - blk_set_chains_for_full_print(h); - - if (filter == PRB_FILTER_LUKS && blk_superblocks_filter_luks(h)) { - r = -EINVAL; - goto out; + switch (filter) { + case PRB_FILTER_LUKS: + if (blk_superblocks_filter_luks(h)) { + r = -EINVAL; + goto out; + } + /* fall-through */ + case PRB_FILTER_NONE: + blk_set_chains_for_full_print(h); + break; + case PRB_ONLY_LUKS: + blk_set_chains_for_fast_detection(h); + if (blk_superblocks_only_luks(h)) { + r = -EINVAL; + goto out; + } } while ((pr = blk_probe(h)) < PRB_EMPTY) { diff --git a/src/utils_reencrypt.c b/src/utils_reencrypt.c index 4566ff07..a3293118 100644 --- a/src/utils_reencrypt.c +++ b/src/utils_reencrypt.c @@ -252,7 +252,6 @@ static enum device_status_info load_luks(struct crypt_device **r_cd, const char if (crypt_init_data_device(&cd, uuid_or_device(header_device ?: data_device), data_device)) return DEVICE_INVALID; - /* TODO: LUKS2 load may fail when header is damaged and blkid reports ambiguous/other signatures */ if ((r = crypt_load(cd, CRYPT_LUKS, NULL))) { crypt_free(cd); @@ -924,10 +923,27 @@ static int reencrypt_luks2_resume(struct crypt_device *cd) return r; } +static int check_broken_luks_signature(const char *device) +{ + int r; + size_t count; + + r = tools_detect_signatures(device, PRB_ONLY_LUKS, &count, ARG_SET(OPT_BATCH_MODE_ID)); + if (r < 0) + return -EINVAL; + if (count) { + log_err(_("Device %s contains broken LUKS metadata. Aborting operation."), device); + return -EINVAL; + } + + return 0; +} + static int _encrypt(struct crypt_device *cd, const char *type, enum device_status_info dev_st, int action_argc, const char **action_argv) { - const char *data_device; + const char *device_ptr; enum device_status_info data_dev_st; + struct stat st; struct crypt_device *encrypt_cd = NULL; int r = -EINVAL; @@ -937,24 +953,37 @@ static int _encrypt(struct crypt_device *cd, const char *type, enum device_statu return -EINVAL; } + if (dev_st == DEVICE_NOT_LUKS && + (!ARG_SET(OPT_HEADER_ID) || !stat(ARG_STR(OPT_HEADER_ID), &st))) { + device_ptr = ARG_SET(OPT_HEADER_ID) ? ARG_STR(OPT_HEADER_ID) : action_argv[0]; + r = check_broken_luks_signature(device_ptr); + if (r < 0) + return r; + } + /* check data device type/state */ if (ARG_SET(OPT_HEADER_ID)) { - data_device = cd ? crypt_get_device_name(cd) : action_argv[0]; - data_dev_st = check_luks_device(data_device); + device_ptr = cd ? crypt_get_device_name(cd) : action_argv[0]; + data_dev_st = check_luks_device(device_ptr); + if (data_dev_st == DEVICE_INVALID) return -EINVAL; if (data_dev_st == DEVICE_LUKS2 || data_dev_st == DEVICE_LUKS1) { log_err(_("Device %s is already LUKS device. Aborting operation."), - data_device); + device_ptr); return -EINVAL; } if (data_dev_st == DEVICE_LUKS2_REENCRYPT || data_dev_st == DEVICE_LUKS1_UNUSABLE) { log_err(_("Device %s is already in LUKS reencryption. Aborting operation."), - data_device); + device_ptr); return -EINVAL; } + + r = check_broken_luks_signature(device_ptr); + if (r < 0) + return r; } if (!type) diff --git a/tests/luks2-reencryption-test b/tests/luks2-reencryption-test index 1d45cb82..bc0fee36 100755 --- a/tests/luks2-reencryption-test +++ b/tests/luks2-reencryption-test @@ -18,6 +18,7 @@ DEV_NAME=reenc9768 DEV_NAME2=reenc97682 IMG=reenc-data IMG_HDR=$IMG.hdr +HEADER_LUKS2_PV=blkid-luks2-pv.img KEY1=key1 VKEY1=vkey1 PWD1="93R4P4pIqAH8" @@ -98,7 +99,7 @@ function remove_mapping() [ -b /dev/mapper/$OVRDEV-err ] && dmsetup remove --retry $OVRDEV-err 2>/dev/null [ -n "$LOOPDEV" ] && losetup -d $LOOPDEV unset LOOPDEV - rm -f $IMG $IMG_HDR $KEY1 $VKEY1 $DEVBIG $DEV_LINK >/dev/null 2>&1 + rm -f $IMG $IMG_HDR $KEY1 $VKEY1 $DEVBIG $DEV_LINK $HEADER_LUKS2_PV >/dev/null 2>&1 rmmod scsi_debug >/dev/null 2>&1 scsi_debug_teardown $DEV } @@ -1708,5 +1709,20 @@ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $IMG_HDR $FAST_PBKD echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only $FAST_PBKDF_ARGON || fail echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --header $IMG_HDR $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail +echo "[30] Prevent nested encryption of broken LUKS device" +rm -f $IMG_HDR +xz -dk $HEADER_LUKS2_PV.xz +wipe_dev $DEV + +# broken header +echo $PWD1 | $CRYPTSETUP reencrypt -q --header $HEADER_LUKS2_PV $DEV $FAST_PBKDF --encrypt --type luks2 2>/dev/null && fail +$CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail +# broken device +echo $PWD1 | $CRYPTSETUP reencrypt -q $HEADER_LUKS2_PV $FAST_PBKDF --encrypt --force-offline-reencrypt --type luks2 --reduce-device-size 8m 2>/dev/null && fail +$CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail +# broken data device only +echo $PWD1 | $CRYPTSETUP reencrypt -q --header $IMG_HDR $HEADER_LUKS2_PV $FAST_PBKDF --encrypt --force-offline-reencrypt --type luks2 2>/dev/null && fail +test -f $IMG_HDR && fail + remove_mapping exit 0 diff --git a/tests/reencryption-compat-test b/tests/reencryption-compat-test index 3651c1be..f21d5c8f 100755 --- a/tests/reencryption-compat-test +++ b/tests/reencryption-compat-test @@ -10,6 +10,7 @@ DEV_NAME=reenc9768 DEV_NAME2=reenc1273 IMG=reenc-data IMG_HDR=$IMG.hdr +HEADER_LUKS2_PV=blkid-luks2-pv.img ORIG_IMG=reenc-data-orig KEY1=key1 PWD1="93R4P4pIqAH8" @@ -30,7 +31,7 @@ function remove_mapping() [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove --retry $DEV_NAME2 [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $DEV_NAME [ ! -z "$LOOPDEV1" ] && losetup -d $LOOPDEV1 >/dev/null 2>&1 - rm -f $IMG $IMG_HDR $ORIG_IMG $KEY1 >/dev/null 2>&1 + rm -f $IMG $IMG_HDR $ORIG_IMG $KEY1 $HEADER_LUKS2_PV >/dev/null 2>&1 umount $MNT_DIR > /dev/null 2>&1 rmdir $MNT_DIR > /dev/null 2>&1 LOOPDEV1="" @@ -416,5 +417,20 @@ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 --header $IMG_HDR $FAST_PBKD echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --new --type luks1 --header $IMG_HDR 2>/dev/null && fail echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --new --type luks2 --header $IMG_HDR 2>/dev/null && fail +echo "[13] Prevent nested encryption of broken LUKS device" +rm -f $IMG_HDR +wipe_dev $LOOPDEV1 +xz -dk $HEADER_LUKS2_PV.xz + +# broken header +echo $PWD1 | $REENC --header $HEADER_LUKS2_PV $LOOPDEV1 -q $FAST_PBKDF --new --type luks1 2>/dev/null && fail +$CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail +# broken device +echo $PWD1 | $REENC $HEADER_LUKS2_PV -q $FAST_PBKDF --new --type luks1 --reduce-device-size 1024S 2>/dev/null && fail +$CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail +# broken data device only +echo $PWD1 | $REENC --header $IMG_HDR $HEADER_LUKS2_PV -q $FAST_PBKDF --new --type luks1 2>/dev/null && fail +test -f $IMG_HDR && fail + remove_mapping exit 0