Allow reencryption metadata repair from cryptsetup.

This commit is contained in:
Ondrej Kozina
2022-01-11 17:18:40 +01:00
committed by Milan Broz
parent ea47937187
commit 00feca3ce0
3 changed files with 102 additions and 16 deletions

View File

@@ -834,6 +834,13 @@ are fixable. This command will only change the LUKS header, not
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

View File

@@ -1024,17 +1024,59 @@ static int action_benchmark(void)
return r;
}
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 (!ARG_SET(OPT_BATCH_MODE_ID) &&
!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, ARG_UINT64(OPT_KEYFILE_OFFSET_ID),
ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID), ARG_UINT32(OPT_TIMEOUT_ID),
verify_passphrase(0), 0, cd);
if (r < 0)
return r;
r = crypt_reencrypt_init_by_passphrase(cd, NULL, password, passwordLen,
ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_KEY_SLOT_ID), NULL, NULL, &params);
tools_passphrase_msg(r);
if (r < 0)
goto out;
r = crypt_activate_by_passphrase(cd, NULL, ARG_INT32(OPT_KEY_SLOT_ID),
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, &params);
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;
@@ -1072,7 +1114,8 @@ static int _do_luks2_reencrypt_recovery(struct crypt_device *cd)
}
r = crypt_reencrypt_init_by_passphrase(cd, NULL, password, passwordLen,
ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_KEY_SLOT_ID), NULL, NULL, &recovery_params);
ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_KEY_SLOT_ID), NULL, NULL,
&(struct crypt_params_reencrypt){ .flags = CRYPT_REENCRYPT_RECOVERY });
if (r > 0)
r = 0;
out:
@@ -1113,9 +1156,9 @@ static int action_luksRepair(void)
else
r = crypt_repair(cd, luksType(device_type), NULL);
out:
/* Header is ok, check if possible interrupted reencryption need repairs. */
/* Header is ok, check if reencryption metadata needs repair/recovery. */
if (!r && isLUKS2(crypt_get_type(cd)))
r = _do_luks2_reencrypt_recovery(cd);
r = luks2_reencrypt_repair(cd);
crypt_free(cd);
return r;

View File

@@ -170,6 +170,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
@@ -210,10 +246,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)"
@@ -234,7 +270,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
@@ -256,7 +292,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)"
@@ -287,7 +323,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
@@ -319,7 +355,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)"
@@ -342,7 +378,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)
@@ -358,7 +394,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)'