diff -rupN cryptsetup-2.3.3.old/man/cryptsetup.8 cryptsetup-2.3.3/man/cryptsetup.8
--- cryptsetup-2.3.3.old/man/cryptsetup.8 2022-01-13 17:19:58.082434394 +0100
+++ cryptsetup-2.3.3/man/cryptsetup.8 2022-01-13 17:20:19.860557992 +0100
@@ -803,6 +803,13 @@ are fixable. This command will only chan
any key-slot data. You may enforce LUKS version by adding \-\-type
option.
+It also repairs (upgrades) LUKS2 reencryption metadata by adding
+metadata digest that protects it against malicious changes.
+
+If LUKS2 reencryption was interrupted in the middle of writting
+reencryption segment the repair command can be used to perform
+reencryption recovery so that reencryption can continue later.
+
\fBWARNING:\fR Always create a binary backup of the original
header before calling this command.
.PP
diff -rupN cryptsetup-2.3.3.old/src/cryptsetup.c cryptsetup-2.3.3/src/cryptsetup.c
--- cryptsetup-2.3.3.old/src/cryptsetup.c 2022-01-13 17:19:58.064434292 +0100
+++ cryptsetup-2.3.3/src/cryptsetup.c 2022-01-13 17:21:29.108950976 +0100
@@ -1072,17 +1072,59 @@ static int set_keyslot_params(struct cry
return crypt_set_pbkdf_type(cd, &pbkdf);
}
-static int _do_luks2_reencrypt_recovery(struct crypt_device *cd)
+static int reencrypt_metadata_repair(struct crypt_device *cd)
+{
+ char *password;
+ size_t passwordLen;
+ int r;
+ struct crypt_params_reencrypt params = {
+ .flags = CRYPT_REENCRYPT_REPAIR_NEEDED
+ };
+
+ if (!opt_batch_mode &&
+ !yesDialog(_("Unprotected LUKS2 reencryption metadata detected. "
+ "Please verify the reencryption operation is desirable (see luksDump output)\n"
+ "and continue (upgrade metadata) only if you acknowledge the operation as genuine."),
+ _("Operation aborted.\n")))
+ return -EINVAL;
+
+ r = tools_get_key(_("Enter passphrase to protect and uppgrade reencryption metadata: "),
+ &password, &passwordLen, opt_keyfile_offset,
+ opt_keyfile_size, opt_key_file, opt_timeout,
+ _verify_passphrase(0), 0, cd);
+ if (r < 0)
+ return r;
+
+ r = crypt_reencrypt_init_by_passphrase(cd, NULL, password, passwordLen,
+ opt_key_slot, opt_key_slot, NULL, NULL, ¶ms);
+ tools_passphrase_msg(r);
+ if (r < 0)
+ goto out;
+
+ r = crypt_activate_by_passphrase(cd, NULL, opt_key_slot,
+ password, passwordLen, 0);
+ tools_passphrase_msg(r);
+ if (r >= 0)
+ r = 0;
+
+out:
+ crypt_safe_free(password);
+ return r;
+}
+
+static int luks2_reencrypt_repair(struct crypt_device *cd)
{
int r;
size_t passwordLen;
const char *msg;
char *password = NULL;
- struct crypt_params_reencrypt recovery_params = {
- .flags = CRYPT_REENCRYPT_RECOVERY
- };
+ struct crypt_params_reencrypt params = {};
+
+ crypt_reencrypt_info ri = crypt_reencrypt_status(cd, ¶ms);
+
+ if (params.flags & CRYPT_REENCRYPT_REPAIR_NEEDED)
+ return reencrypt_metadata_repair(cd);
- crypt_reencrypt_info ri = crypt_reencrypt_status(cd, NULL);
switch (ri) {
case CRYPT_REENCRYPT_NONE:
return 0;
@@ -1120,7 +1162,8 @@ static int _do_luks2_reencrypt_recovery(
}
r = crypt_reencrypt_init_by_passphrase(cd, NULL, password, passwordLen,
- opt_key_slot, opt_key_slot, NULL, NULL, &recovery_params);
+ opt_key_slot, opt_key_slot, NULL, NULL,
+ &(struct crypt_params_reencrypt){ .flags = CRYPT_REENCRYPT_RECOVERY });
if (r > 0)
r = 0;
out:
@@ -1155,8 +1198,9 @@ static int action_luksRepair(void)
if (r == 0)
r = crypt_repair(cd, luksType(opt_type), NULL);
skip_repair:
+ /* Header is ok, check if reencryption metadata needs repair/recovery. */
if (!r && crypt_get_type(cd) && !strcmp(crypt_get_type(cd), CRYPT_LUKS2))
- r = _do_luks2_reencrypt_recovery(cd);
+ r = luks2_reencrypt_repair(cd);
out:
crypt_free(cd);
return r;
diff -rupN cryptsetup-2.3.3.old/tests/luks2-reencryption-mangle-test cryptsetup-2.3.3/tests/luks2-reencryption-mangle-test
--- cryptsetup-2.3.3.old/tests/luks2-reencryption-mangle-test 2022-01-13 17:19:58.073434343 +0100
+++ cryptsetup-2.3.3/tests/luks2-reencryption-mangle-test 2022-01-13 17:20:19.861557997 +0100
@@ -172,6 +172,42 @@ EOF
[ $? -eq 0 ] || fail "Expect script failed."
}
+function img_run_reenc_fail()
+{
+local EXPECT_TIMEOUT=5
+[ -n "$VALG" ] && EXPECT_TIMEOUT=60
+# For now, we cannot run reencryption in batch mode for non-block device. Just fake the terminal here.
+expect_run - >/dev/null <<EOF
+proc abort {} { send_error "Timeout. "; exit 42 }
+set timeout $EXPECT_TIMEOUT
+eval spawn $CRYPTSETUP_RAW reencrypt $IMG $CS_PWPARAMS --disable-locks
+expect timeout abort "Are you sure? (Type 'yes' in capital letters):"
+send "YES\n"
+expect timeout abort eof
+catch wait result
+exit [lindex \$result 3]
+EOF
+local ret=$?
+[ $ret -eq 0 ] && fail "Reencryption passed (should have failed)."
+[ $ret -eq 42 ] && fail "Expect script failed."
+img_hash_unchanged
+}
+
+function img_check_fail_repair_ok()
+{
+ if [ $(id -u) == 0 ]; then
+ $CRYPTSETUP open $CS_PWPARAMS $IMG $DEV_NAME 2>/dev/null && fail
+ fi
+
+ img_run_reenc_fail
+
+ # repair metadata
+ $CRYPTSETUP repair $IMG $CS_PARAMS || fail
+
+ img_check_ok
+ img_run_reenc_ok
+}
+
function valgrind_setup()
{
bin_check valgrind
@@ -212,10 +248,10 @@ img_check_ok
img_run_reenc_ok
img_check_ok
-# Simulate old reencryption with no digest
+# Simulate old reencryption with no digest (repairable)
img_prepare
img_update_json 'del(.digests."2") | .config.requirements.mandatory = ["online-reencrypt"]'
-img_check_fail
+img_check_fail_repair_ok
# This must fail for new releases
echo "[2] Old reencryption in-progress (journal)"
@@ -236,7 +272,7 @@ img_update_json '
.digests."0".segments = ["1","2"] |
.digests."1".segments = ["0","3"] |
.config.requirements.mandatory = ["online-reencrypt"]'
-img_check_fail
+img_check_fail_repair_ok
echo "[3] Old reencryption in-progress (checksum)"
img_prepare
@@ -258,7 +294,7 @@ img_update_json '
.digests."0".segments = ["1","2"] |
.digests."1".segments = ["0","3"] |
.config.requirements.mandatory = ["online-reencrypt"]'
-img_check_fail
+img_check_fail_repair_ok
# Note: older tools cannot create this from commandline
echo "[4] Old decryption in-progress (journal)"
@@ -289,7 +325,7 @@ img_update_json '
} |
.digests."0".segments = ["1","2"] |
.config.requirements.mandatory = ["online-reencrypt"]'
-img_check_fail
+img_check_fail_repair_ok
echo "[5] Old decryption in-progress (checksum)"
img_prepare
@@ -321,7 +357,7 @@ img_update_json '
} |
.digests."0".segments = ["1","2"] |
.config.requirements.mandatory = ["online-reencrypt"]'
-img_check_fail
+img_check_fail_repair_ok
# Note - offset is set to work with the old version (with a datashift bug)
echo "[6] Old reencryption in-progress (datashift)"
@@ -344,7 +380,7 @@ img_update_json '
.digests."0".segments = ["0","2"] |
.digests."1".segments = ["1","3"] |
.config.requirements.mandatory = ["online-reencrypt"]'
-img_check_fail
+img_check_fail_repair_ok
#
# NEW metadata (with reenc digest)
@@ -360,7 +396,7 @@ img_check_ok
# Repair must validate not only metadata, but also reencryption digest.
img_prepare
img_update_json 'del(.digests."2")'
-img_check_fail
+img_check_fail_repair_ok
img_prepare '--reduce-device-size 2M'
img_update_json '.keyslots."2".area.shift_size = ((.keyslots."2".area.shift_size|tonumber / 2)|tostring)'