mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-15 04:40:05 +01:00
Allow reencryption metadata repair from cryptsetup.
This commit is contained in:
committed by
Milan Broz
parent
ea47937187
commit
00feca3ce0
@@ -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
|
||||
|
||||
@@ -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, ¶ms);
|
||||
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, ¶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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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)'
|
||||
|
||||
Reference in New Issue
Block a user