mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-13 03:40:05 +01:00
Add support for suspend/resume with LUKS2 OPAL2 devices.
This commit is contained in:
194
lib/setup.c
194
lib/setup.c
@@ -3830,11 +3830,13 @@ static char *crypt_get_device_key_description(struct crypt_device *cd, const cha
|
||||
int crypt_suspend(struct crypt_device *cd,
|
||||
const char *name)
|
||||
{
|
||||
char *key_desc;
|
||||
bool dm_opal_uuid;
|
||||
crypt_status_info ci;
|
||||
int r;
|
||||
struct crypt_dm_active_device dmd;
|
||||
uint32_t dmflags = DM_SUSPEND_WIPE_KEY;
|
||||
uint32_t opal_segment_number = 1, dmflags = DM_SUSPEND_WIPE_KEY;
|
||||
struct dm_target *tgt = &dmd.segment;
|
||||
char *key_desc = NULL;
|
||||
|
||||
if (!cd || !name)
|
||||
return -EINVAL;
|
||||
@@ -3877,6 +3879,13 @@ int crypt_suspend(struct crypt_device *cd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check if active device has LUKS2-OPAL dm uuid prefix */
|
||||
dm_opal_uuid = !crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2_HW_OPAL);
|
||||
|
||||
if (!dm_opal_uuid && isLUKS2(cd->type) &&
|
||||
LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT))
|
||||
goto out;
|
||||
|
||||
if (cd->type && (r = crypt_uuid_cmp(dmd.uuid, LUKS_UUID(cd))) < 0) {
|
||||
log_dbg(cd, "LUKS device header uuid: %s mismatches DM returned uuid %s",
|
||||
LUKS_UUID(cd), dmd.uuid);
|
||||
@@ -3895,45 +3904,59 @@ int crypt_suspend(struct crypt_device *cd,
|
||||
|
||||
key_desc = crypt_get_device_key_description(cd, name);
|
||||
|
||||
/* we can't simply wipe wrapped keys */
|
||||
if (crypt_cipher_wrapped_key(crypt_get_cipher(cd), crypt_get_cipher_mode(cd)))
|
||||
if (dm_opal_uuid && crypt_data_device(cd)) {
|
||||
if (isLUKS2(cd->type)) {
|
||||
r = LUKS2_get_opal_segment_number(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, &opal_segment_number);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
} else {
|
||||
/* Guess OPAL range number for LUKS2-OPAL device with missing header */
|
||||
r = crypt_dev_get_partition_number(device_path(crypt_data_device(cd)));
|
||||
if (r > 0)
|
||||
opal_segment_number = r;
|
||||
}
|
||||
}
|
||||
|
||||
/* we can't simply wipe wrapped keys. HW OPAL only encryption does not use dm-crypt target */
|
||||
if (crypt_cipher_wrapped_key(crypt_get_cipher(cd), crypt_get_cipher_mode(cd)) ||
|
||||
(dm_opal_uuid && tgt->type == DM_LINEAR))
|
||||
dmflags &= ~DM_SUSPEND_WIPE_KEY;
|
||||
|
||||
r = dm_suspend_device(cd, name, dmflags);
|
||||
if (r == -ENOTSUP)
|
||||
log_err(cd, _("Suspend is not supported for device %s."), name);
|
||||
else if (r)
|
||||
log_err(cd, _("Error during suspending device %s."), name);
|
||||
else
|
||||
crypt_drop_keyring_key_by_description(cd, key_desc, LOGON_KEY);
|
||||
free(key_desc);
|
||||
if (r) {
|
||||
if (r == -ENOTSUP)
|
||||
log_err(cd, _("Suspend is not supported for device %s."), name);
|
||||
else
|
||||
log_err(cd, _("Error during suspending device %s."), name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
crypt_drop_keyring_key_by_description(cd, key_desc, LOGON_KEY);
|
||||
|
||||
if (dm_opal_uuid && (!crypt_data_device(cd) || opal_lock(cd, crypt_data_device(cd), opal_segment_number)))
|
||||
log_err(cd, _("Device %s was suspended but hardware OPAL device cannot be locked."), name);
|
||||
out:
|
||||
free(key_desc);
|
||||
dm_targets_free(cd, &dmd);
|
||||
free(CONST_CAST(void*)dmd.uuid);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* key must be properly verified */
|
||||
static int resume_by_volume_key(struct crypt_device *cd,
|
||||
static int resume_luks1_by_volume_key(struct crypt_device *cd,
|
||||
struct volume_key *vk,
|
||||
const char *name)
|
||||
{
|
||||
int digest, r;
|
||||
int r;
|
||||
struct volume_key *zerokey = NULL;
|
||||
|
||||
assert(vk && crypt_volume_key_get_id(vk) == 0);
|
||||
assert(name);
|
||||
|
||||
if (crypt_is_cipher_null(crypt_get_cipher_spec(cd))) {
|
||||
zerokey = crypt_alloc_volume_key(0, NULL);
|
||||
if (!zerokey)
|
||||
return -ENOMEM;
|
||||
vk = zerokey;
|
||||
} else if (crypt_use_keyring_for_vk(cd)) {
|
||||
/* LUKS2 path only */
|
||||
digest = LUKS2_digest_by_segment(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT);
|
||||
if (digest < 0)
|
||||
return -EINVAL;
|
||||
r = LUKS2_volume_key_load_in_keyring_by_digest(cd, vk, digest);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dm_resume_and_reinstate_key(cd, name, vk);
|
||||
@@ -3943,14 +3966,113 @@ static int resume_by_volume_key(struct crypt_device *cd,
|
||||
else if (r)
|
||||
log_err(cd, _("Error during resuming device %s."), name);
|
||||
|
||||
if (r < 0)
|
||||
crypt_drop_keyring_key(cd, vk);
|
||||
|
||||
crypt_free_volume_key(zerokey);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int resume_luks2_by_volume_key(struct crypt_device *cd,
|
||||
int digest,
|
||||
struct volume_key *vk,
|
||||
const char *name)
|
||||
{
|
||||
bool use_keyring;
|
||||
int r, enc_type;
|
||||
uint32_t opal_segment_number;
|
||||
struct volume_key *p_crypt = vk, *p_opal = NULL, *zerokey = NULL, *crypt_key = NULL, *opal_key = NULL;
|
||||
|
||||
assert(digest >= 0);
|
||||
assert(vk && crypt_volume_key_get_id(vk) == digest);
|
||||
assert(name);
|
||||
|
||||
enc_type = crypt_get_hw_encryption_type(cd);
|
||||
if (enc_type < 0)
|
||||
return enc_type;
|
||||
|
||||
use_keyring = crypt_use_keyring_for_vk(cd);
|
||||
|
||||
if (enc_type == CRYPT_OPAL_HW_ONLY || enc_type == CRYPT_SW_AND_OPAL_HW) {
|
||||
r = LUKS2_get_opal_segment_number(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT,
|
||||
&opal_segment_number);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = LUKS2_split_crypt_and_opal_keys(cd, &cd->u.luks2.hdr,
|
||||
vk, &crypt_key,
|
||||
&opal_key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p_crypt = crypt_key;
|
||||
p_opal = opal_key ?: vk;
|
||||
}
|
||||
|
||||
if (enc_type != CRYPT_OPAL_HW_ONLY && crypt_is_cipher_null(crypt_get_cipher_spec(cd))) {
|
||||
zerokey = crypt_alloc_volume_key(0, NULL);
|
||||
if (!zerokey) {
|
||||
r = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
p_crypt = zerokey;
|
||||
use_keyring = false;
|
||||
}
|
||||
|
||||
if (use_keyring && p_crypt) {
|
||||
r = LUKS2_volume_key_load_in_keyring_by_digest(cd, p_crypt, digest);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p_opal) {
|
||||
r = opal_unlock(cd, crypt_data_device(cd), opal_segment_number, p_opal);
|
||||
if (r < 0) {
|
||||
p_opal = NULL; /* do not lock on error path */
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (enc_type == CRYPT_OPAL_HW_ONLY)
|
||||
r = dm_resume_device(cd, name, 0);
|
||||
else
|
||||
r = dm_resume_and_reinstate_key(cd, name, p_crypt);
|
||||
|
||||
if (r == -ENOTSUP)
|
||||
log_err(cd, _("Resume is not supported for device %s."), name);
|
||||
else if (r)
|
||||
log_err(cd, _("Error during resuming device %s."), name);
|
||||
|
||||
out:
|
||||
if (r < 0)
|
||||
crypt_drop_keyring_key(cd, p_crypt);
|
||||
|
||||
if (r < 0 && p_opal)
|
||||
opal_lock(cd, crypt_data_device(cd), opal_segment_number);
|
||||
|
||||
crypt_free_volume_key(zerokey);
|
||||
crypt_free_volume_key(opal_key);
|
||||
crypt_free_volume_key(crypt_key);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* key must be properly verified */
|
||||
static int resume_by_volume_key(struct crypt_device *cd,
|
||||
struct volume_key *vk,
|
||||
const char *name)
|
||||
{
|
||||
assert(cd);
|
||||
|
||||
if (isLUKS2(cd->type))
|
||||
return resume_luks2_by_volume_key(cd,
|
||||
LUKS2_digest_by_segment(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT),
|
||||
vk, name);
|
||||
|
||||
if (isLUKS1(cd->type))
|
||||
return resume_luks1_by_volume_key(cd, vk, name);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int crypt_resume_by_passphrase(struct crypt_device *cd,
|
||||
const char *name,
|
||||
int keyslot,
|
||||
@@ -3979,10 +4101,13 @@ int crypt_resume_by_passphrase(struct crypt_device *cd,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (isLUKS1(cd->type))
|
||||
if (isLUKS1(cd->type)) {
|
||||
r = LUKS_open_key_with_hdr(keyslot, passphrase, passphrase_size,
|
||||
&cd->u.luks1.hdr, &vk, cd);
|
||||
else
|
||||
if (r >= 0)
|
||||
crypt_volume_key_set_id(vk, 0);
|
||||
|
||||
} else
|
||||
r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT, passphrase, passphrase_size, &vk);
|
||||
|
||||
if (r < 0)
|
||||
@@ -4033,10 +4158,12 @@ int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd,
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isLUKS1(cd->type))
|
||||
if (isLUKS1(cd->type)) {
|
||||
r = LUKS_open_key_with_hdr(keyslot, passphrase_read, passphrase_size_read,
|
||||
&cd->u.luks1.hdr, &vk, cd);
|
||||
else
|
||||
if (r >= 0)
|
||||
crypt_volume_key_set_id(vk, 0);
|
||||
} else
|
||||
r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT,
|
||||
passphrase_read, passphrase_size_read, &vk);
|
||||
|
||||
@@ -4102,11 +4229,14 @@ int crypt_resume_by_volume_key(struct crypt_device *cd,
|
||||
if (!vk)
|
||||
return -ENOMEM;
|
||||
|
||||
if (isLUKS1(cd->type))
|
||||
if (isLUKS1(cd->type)) {
|
||||
r = LUKS_verify_volume_key(&cd->u.luks1.hdr, vk);
|
||||
else if (isLUKS2(cd->type))
|
||||
if (r >= 0)
|
||||
crypt_volume_key_set_id(vk, 0);
|
||||
} else if (isLUKS2(cd->type)) {
|
||||
r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk);
|
||||
else
|
||||
crypt_volume_key_set_id(vk, r);
|
||||
} else
|
||||
r = -EINVAL;
|
||||
if (r == -EPERM || r == -ENOENT)
|
||||
log_err(cd, _("Volume key does not match the volume."));
|
||||
|
||||
@@ -21,6 +21,7 @@ function fail()
|
||||
function remove_mapping()
|
||||
{
|
||||
[ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $DEV_NAME
|
||||
[ -b /dev/mapper/$DEV_NAME-dif ] && dmsetup remove --retry $DEV_NAME-dif
|
||||
}
|
||||
|
||||
function skip()
|
||||
@@ -34,25 +35,47 @@ function skip()
|
||||
[ -z "$OPAL2_DEV" ] && skip "WARNING: Variable OPAL2_DEV must be defined (partition or block dev), test skipped."
|
||||
[ -z "$OPAL2_ADMIN_PIN" ] && skip "WARNING: Variable OPAL2_ADMIN_PIN must be defined, test skipped."
|
||||
|
||||
echo "[1] OPAL2 simple test"
|
||||
echo "[1] OPAL2 HW only"
|
||||
echo -e "$PWD1\n$OPAL2_ADMIN_PIN" | $CRYPTSETUP luksFormat --type luks2 --hw-opal-only -q $FAST_PBKDF_OPT $OPAL2_DEV || fail
|
||||
echo $PWD1 | $CRYPTSETUP open $OPAL2_DEV $DEV_NAME || fail
|
||||
$CRYPTSETUP luksSuspend $DEV_NAME || fail
|
||||
dd if=$OPAL2_DEV of=/dev/zero bs=1M skip=16 count=1 iflag=direct >/dev/null 2>&1 && fail
|
||||
echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME || fail
|
||||
dd if=/dev/mapper/$DEV_NAME of=/dev/zero bs=1M count=1 iflag=direct >/dev/null 2>&1 || fail
|
||||
$CRYPTSETUP close $DEV_NAME || fail
|
||||
dd if=$OPAL2_DEV of=/dev/zero bs=1M skip=16 count=1 iflag=direct >/dev/null 2>&1 && fail
|
||||
echo $OPAL2_ADMIN_PIN | $CRYPTSETUP luksErase $OPAL2_DEV -q || fail
|
||||
|
||||
echo "[2] OPAL2 + dmcrypt"
|
||||
echo -e "$PWD1\n$OPAL2_ADMIN_PIN" | $CRYPTSETUP luksFormat --type luks2 --hw-opal -q $FAST_PBKDF_OPT $OPAL2_DEV || fail
|
||||
echo $PWD1 | $CRYPTSETUP open $OPAL2_DEV $DEV_NAME || fail
|
||||
$CRYPTSETUP luksSuspend $DEV_NAME || fail
|
||||
dd if=$OPAL2_DEV of=/dev/zero bs=1M skip=16 count=1 iflag=direct >/dev/null 2>&1 && fail
|
||||
echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME || fail
|
||||
dd if=/dev/mapper/$DEV_NAME of=/dev/zero bs=1M count=1 iflag=direct >/dev/null 2>&1 || fail
|
||||
$CRYPTSETUP close $DEV_NAME || fail
|
||||
dd if=$OPAL2_DEV of=/dev/zero bs=1M skip=16 count=1 iflag=direct >/dev/null 2>&1 && fail
|
||||
echo $OPAL2_ADMIN_PIN | $CRYPTSETUP luksErase $OPAL2_DEV -q || fail
|
||||
|
||||
echo "[3] OPAL2 + auth encryption"
|
||||
echo -e "$PWD1\n$OPAL2_ADMIN_PIN" | $CRYPTSETUP luksFormat --type luks2 --hw-opal -q $FAST_PBKDF_OPT $OPAL2_DEV -c aes-gcm-random --integrity aead --integrity-no-wipe || fail
|
||||
echo $PWD1 | $CRYPTSETUP open $OPAL2_DEV $DEV_NAME || fail
|
||||
dd if=/dev/zero of=/dev/mapper/$DEV_NAME bs=1M count=1 oflag=direct >/dev/null 2>&1 || fail
|
||||
$CRYPTSETUP luksSuspend $DEV_NAME || fail
|
||||
dd if=$OPAL2_DEV of=/dev/zero bs=1M skip=16 count=1 iflag=direct >/dev/null 2>&1 && fail
|
||||
echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME || fail
|
||||
dd if=/dev/mapper/$DEV_NAME of=/dev/zero bs=1M count=1 iflag=direct >/dev/null 2>&1 || fail
|
||||
$CRYPTSETUP close $DEV_NAME || fail
|
||||
dd if=$OPAL2_DEV of=/dev/zero bs=1M skip=16 count=1 iflag=direct >/dev/null 2>&1 && fail
|
||||
echo $OPAL2_ADMIN_PIN | $CRYPTSETUP luksErase $OPAL2_DEV -q || fail
|
||||
|
||||
echo -e "$PWD1\n$OPAL2_ADMIN_PIN" | $CRYPTSETUP luksFormat --type luks2 --hw-opal -q $FAST_PBKDF_OPT $OPAL2_DEV -s 280 -c aes-ccm-random --integrity aead --integrity-no-wipe || fail
|
||||
echo $PWD1 | $CRYPTSETUP open $OPAL2_DEV $DEV_NAME || fail
|
||||
dd if=/dev/zero of=/dev/mapper/$DEV_NAME bs=1M count=1 oflag=direct >/dev/null 2>&1 || fail
|
||||
$CRYPTSETUP luksSuspend $DEV_NAME || fail
|
||||
dd if=$OPAL2_DEV of=/dev/zero bs=1M skip=16 count=1 iflag=direct >/dev/null 2>&1 && fail
|
||||
echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME || fail
|
||||
dd if=/dev/mapper/$DEV_NAME of=/dev/zero bs=1M count=1 iflag=direct >/dev/null 2>&1 || fail
|
||||
$CRYPTSETUP close $DEV_NAME || fail
|
||||
dd if=$OPAL2_DEV of=/dev/zero bs=1M skip=16 count=1 iflag=direct >/dev/null 2>&1 && fail
|
||||
echo $OPAL2_ADMIN_PIN | $CRYPTSETUP luksErase $OPAL2_DEV -q || fail
|
||||
|
||||
Reference in New Issue
Block a user