Allow activation, resume and luksAddKey using VK stored in keyring.

Add --volume-key-keyring option, which takes a name of a key in keyring,
which will be used as a VK during device activation. The key can be
specified in keyctl-compatible syntax "%<key_type>:<key_name>".
This commit is contained in:
Daniel Zatovic
2023-05-11 15:52:17 +02:00
parent d0ef2d84be
commit 1aab3afcba
8 changed files with 86 additions and 11 deletions

View File

@@ -235,6 +235,17 @@ partially predictable volume key which will compromise security.
endif::[]
endif::[]
ifdef::ACTION_OPEN,ACTION_LUKSRESUME,ACTION_LUKSADDKEY[]
*--volume-key-keyring* _<key description>_::
Use a volume key stored in a keyring.
This allows one to open _luks_ and device types without giving a passphrase.
The volume key has to be of user type, in order to validate the digest.
+
The _<key description>_ uses keyctl-compatible syntax. This can either be a
numeric key ID or a string name in the format _%<key type>:<key name>_. See
also *KEY IDENTIFIERS* section of *keyctl*(1).
endif::[]
ifdef::ACTION_LUKSDUMP[]
*--dump-json-metadata*::
For _luksDump_ (LUKS2 only) this option prints content of LUKS2 header

View File

@@ -19,9 +19,9 @@ cryptsetup-luksAddKey - add a new passphrase
Adds a keyslot protected by a new passphrase. An existing passphrase
must be supplied interactively, via --key-file or LUKS2 token (plugin).
Alternatively to existing passphrase user may pass directly volume key
(via --volume-key-file). The new passphrase to be added can be specified
interactively, read from the file given as the positional argument (also
via --new-keyfile parameter) or via LUKS2 token.
(via --volume-key-file or --volume-key-keyring). The new passphrase to be added
can be specified interactively, read from the file given as the positional
argument (also via --new-keyfile parameter) or via LUKS2 token.
*NOTE:* with --unbound option the action creates new unbound LUKS2
keyslot. The keyslot cannot be used for device activation. If you don't
@@ -34,11 +34,11 @@ algorithm is always the same for all keyslots.
*<options>* can be [--key-file, --keyfile-offset, --keyfile-size,
--new-keyfile, --new-keyfile-offset, --new-keyfile-size, --key-slot,
--new-key-slot, --volume-key-file, --force-password, --hash, --header,
--disable-locks, --iter-time, --pbkdf, --pbkdf-force-iterations,
--pbkdf-memory, --pbkdf-parallel, --unbound, --type, --keyslot-cipher,
--keyslot-key-size, --key-size, --timeout, --token-id, --token-type,
--token-only, --new-token-id, --verify-passphrase].
--new-key-slot, --volume-key-file, --volume-key-keyring, --force-password,
--hash, --header, --disable-locks, --iter-time, --pbkdf,
--pbkdf-force-iterations, --pbkdf-memory, --pbkdf-parallel, --unbound, --type,
--keyslot-cipher, --keyslot-key-size, --key-size, --timeout, --token-id,
--token-type, --token-only, --new-token-id, --verify-passphrase].
include::man/common_options.adoc[]

View File

@@ -23,7 +23,7 @@ interactively for a passphrase if no token is usable (LUKS2 only) or
*<options>* can be [--key-file, --keyfile-size, --keyfile-offset,
--key-slot, --header, --disable-keyring, --disable-locks, --token-id,
--token-only, --token-type, --disable-external-tokens, --type, --tries,
--timeout, --verify-passphrase].
--timeout, --verify-passphrase, --volume-key-keyring].
include::man/common_options.adoc[]
include::man/common_footer.adoc[]

View File

@@ -74,7 +74,7 @@ matching PIN protected token.
--volume-key-file, --token-id, --token-only, --token-type,
--disable-external-tokens, --disable-keyring, --disable-locks, --type,
--refresh, --serialize-memory-hard-pbkdf, --unbound, --tries, --timeout,
--verify-passphrase, --persistent].
--verify-passphrase, --persistent, --volume-key-keyring].
=== loopAES
*open --type loopaes <device> <name> --key-file <keyfile>* +

View File

@@ -1616,6 +1616,7 @@ static int action_open_luks(void)
char *password = NULL;
size_t passwordLen;
struct stat st;
struct crypt_keyslot_context *kc = NULL;
if (ARG_SET(OPT_REFRESH_ID)) {
activated_name = action_argc > 1 ? action_argv[1] : action_argv[0];
@@ -1683,6 +1684,13 @@ static int action_open_luks(void)
goto out;
r = crypt_activate_by_volume_key(cd, activated_name,
key, keysize, activate_flags);
} else if (ARG_SET(OPT_VOLUME_KEY_KEYRING_ID)) {
r = crypt_keyslot_context_init_by_vk_in_keyring(cd, ARG_STR(OPT_VOLUME_KEY_KEYRING_ID), &kc);
if (r)
goto out;
r = crypt_activate_by_keyslot_context(cd, activated_name, CRYPT_ANY_SLOT, kc, activate_flags);
if (r)
goto out;
} else {
r = crypt_activate_by_token_pin(cd, activated_name, ARG_STR(OPT_TOKEN_TYPE_ID),
ARG_INT32(OPT_TOKEN_ID_ID), NULL, 0, NULL, activate_flags);
@@ -1719,6 +1727,7 @@ out:
(crypt_get_active_device(cd, activated_name, &cad) ||
crypt_persistent_flags_set(cd, CRYPT_FLAGS_ACTIVATION, cad.flags & activate_flags)))
log_err(_("Device activated but cannot make flags persistent."));
crypt_keyslot_context_free(kc);
crypt_safe_free(key);
crypt_safe_free(password);
@@ -2082,6 +2091,8 @@ static int action_luksAddKey(void)
ARG_UINT32(OPT_KEYFILE_SIZE_ID),
ARG_UINT64(OPT_KEYFILE_OFFSET_ID),
&kc);
else if (ARG_SET(OPT_VOLUME_KEY_KEYRING_ID))
r = crypt_keyslot_context_init_by_vk_in_keyring(cd, ARG_STR(OPT_VOLUME_KEY_KEYRING_ID), &kc);
else if (ARG_SET(OPT_TOKEN_ID_ID) || ARG_SET(OPT_TOKEN_TYPE_ID) || ARG_SET(OPT_TOKEN_ONLY_ID)) {
r = crypt_keyslot_context_init_by_token(cd,
ARG_INT32(OPT_TOKEN_ID_ID),
@@ -2516,6 +2527,7 @@ static int action_luksResume(void)
int r, tries;
struct crypt_active_device cad;
const char *req_type = luksType(device_type);
struct crypt_keyslot_context *kc = NULL;
if (req_type && !isLUKS(req_type))
return -EINVAL;
@@ -2573,6 +2585,14 @@ static int action_luksResume(void)
if (r >= 0 || quit || ARG_SET(OPT_TOKEN_ONLY_ID))
goto out;
if (ARG_SET(OPT_VOLUME_KEY_KEYRING_ID)) {
r = crypt_keyslot_context_init_by_vk_in_keyring(cd, ARG_STR(OPT_VOLUME_KEY_KEYRING_ID), &kc);
if (r)
goto out;
r = crypt_resume_by_keyslot_context(cd, action_argv[0], CRYPT_ANY_SLOT, kc);
goto out;
}
tries = set_tries_tty();
do {
r = tools_get_key(NULL, &password, &passwordLen,
@@ -2591,6 +2611,7 @@ static int action_luksResume(void)
password = NULL;
} while ((r == -EPERM || r == -ERANGE) && (--tries > 0));
out:
crypt_keyslot_context_free(kc);
crypt_safe_free(password);
crypt_free(cd);
return r;

View File

@@ -119,6 +119,8 @@ ARG(OPT_LUKS2_METADATA_SIZE, '\0', POPT_ARG_STRING, N_("LUKS2 header metadata ar
ARG(OPT_VOLUME_KEY_FILE, '\0', POPT_ARG_STRING, N_("Use the volume key from file."), NULL, CRYPT_ARG_STRING, {}, {})
ARG(OPT_VOLUME_KEY_KEYRING, '\0', POPT_ARG_STRING, N_("Use the specified keyring key as a volume key."), NULL, CRYPT_ARG_STRING, {}, {})
ARG(OPT_VOLUME_KEY_TYPE, '\0', POPT_ARG_STRING, N_("Specify the type of the volume key stored in keyring"), NULL, CRYPT_ARG_STRING, {}, {})
ARG(OPT_NEW_KEYFILE, '\0', POPT_ARG_STRING, N_("Read the key for a new slot from a file"), NULL, CRYPT_ARG_STRING, {}, OPT_NEW_KEYFILE_ACTIONS)

View File

@@ -110,6 +110,7 @@
#define OPT_LUKS2_METADATA_SIZE "luks2-metadata-size"
#define OPT_MASTER_KEY_FILE "master-key-file"
#define OPT_VOLUME_KEY_FILE "volume-key-file"
#define OPT_VOLUME_KEY_KEYRING "volume-key-keyring"
#define OPT_VOLUME_KEY_TYPE "volume-key-type"
#define OPT_NEW "new"
#define OPT_NEW_KEY_SLOT "new-key-slot"

View File

@@ -304,6 +304,40 @@ test_vk_link() {
keyctl unlink "%$KEY_TYPE:$1" "$2" || fail
}
# $1 key name
# $2 keyring to link VK to
# $3 key type (optional)
test_vk_link_and_reactivate() {
KEY_TYPE=${3:-logon}
KEY_TYPE_ARG=""
if [ ! -z "$3" ]; then
KEY_TYPE_ARG="--volume-key-type $3"
fi
LINKED_KEY_NAME="%$KEY_TYPE:$1"
echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --link-vk-to-keyring "$2" $KEY_TYPE_ARG || fail
keyctl search "$2" $KEY_TYPE $1 > /dev/null 2>&1 || fail "VK is not linked to the specified keyring after activation."
$CRYPTSETUP close $DEV_NAME || fail
keyctl search "$2" $KEY_TYPE $1 > /dev/null 2>&1 || fail "VK is not linked to the specified keyring after deactivation."
$CRYPTSETUP open $LOOPDEV $DEV_NAME --volume-key-keyring $LINKED_KEY_NAME || fail "Failed to unlock volume via a VK in keyring."
$CRYPTSETUP luksSuspend $DEV_NAME || fail "Failed to suspend device."
$CRYPTSETUP luksResume $DEV_NAME --volume-key-keyring $LINKED_KEY_NAME || fail "Failed to resume via a VK in keyring."
echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase 2>/dev/null || fail
echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase 2>/dev/null && fail
echo $PWD2 | $CRYPTSETUP luksAddKey $FAST_PBKDF2 --volume-key-keyring $LINKED_KEY_NAME $LOOPDEV --new-key-slot 1 || fail "Failed to add passphrase by VK in keyring."
echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase 2>/dev/null || fail
$CRYPTSETUP luksKillSlot -q $LOOPDEV 1 2>/dev/null || fail
$CRYPTSETUP close $DEV_NAME || fail
# zero-out the key in keyring
keyctl pipe $LINKED_KEY_NAME | tr -c '\0' '\0' | keyctl pupdate $LINKED_KEY_NAME
$CRYPTSETUP open $LOOPDEV $DEV_NAME --volume-key-keyring $LINKED_KEY_NAME > /dev/null 2>&1 && fail "Unlocked volume via a bad VK in keyring."
keyctl search "$2" $KEY_TYPE $1 > /dev/null 2>&1 || fail "VK is not linked to the specified keyring after bad activation."
keyctl unlink "%$KEY_TYPE:$1" "$2" || fail
}
export LANG=C
[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
@@ -1244,7 +1278,6 @@ if [ $HAVE_KEYRING -gt 0 -a -d /proc/sys/kernel/keys ]; then
KID=$(echo -n test | keyctl padd user my_token @us)
keyctl unlink $KID >/dev/null 2>&1 @us && USER_SESSION_KEYRING_WORKS=1
test_vk_link $KEY_NAME "@u"
test_vk_link $KEY_NAME "@u" "user"
[[ ! -z "$SESSION_KEYRING_WORKS" ]] && test_vk_link $KEY_NAME "@s"
@@ -1256,6 +1289,13 @@ if [ $HAVE_KEYRING -gt 0 -a -d /proc/sys/kernel/keys ]; then
# explicitly specify keyring key type
test_vk_link $KEY_NAME "%keyring:$TEST_KEYRING_NAME"
test_vk_link_and_reactivate $KEY_NAME "@u" "user"
[[ ! -z "$SESSION_KEYRING_WORKS" ]] && test_vk_link_and_reactivate $KEY_NAME "@s" "user"
[[ ! -z "$USER_SESSION_KEYRING_WORKS" ]] && test_vk_link_and_reactivate $KEY_NAME "@us" "user"
test_vk_link_and_reactivate $KEY_NAME "%:$TEST_KEYRING_NAME" "user"
# explicitly specify keyring key type
test_vk_link_and_reactivate $KEY_NAME "%keyring:$TEST_KEYRING_NAME" "user"
# test numeric keyring name -5 is user session (@us) keyring
echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --link-vk-to-keyring -5 --volume-key-type logon || fail
keyctl search @us logon $KEY_NAME > /dev/null 2>&1 || fail "VK is not linked to the specified keyring after activation."