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::[]
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[] ifdef::ACTION_LUKSDUMP[]
*--dump-json-metadata*:: *--dump-json-metadata*::
For _luksDump_ (LUKS2 only) this option prints content of LUKS2 header 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 Adds a keyslot protected by a new passphrase. An existing passphrase
must be supplied interactively, via --key-file or LUKS2 token (plugin). must be supplied interactively, via --key-file or LUKS2 token (plugin).
Alternatively to existing passphrase user may pass directly volume key Alternatively to existing passphrase user may pass directly volume key
(via --volume-key-file). The new passphrase to be added can be specified (via --volume-key-file or --volume-key-keyring). The new passphrase to be added
interactively, read from the file given as the positional argument (also can be specified interactively, read from the file given as the positional
via --new-keyfile parameter) or via LUKS2 token. argument (also via --new-keyfile parameter) or via LUKS2 token.
*NOTE:* with --unbound option the action creates new unbound LUKS2 *NOTE:* with --unbound option the action creates new unbound LUKS2
keyslot. The keyslot cannot be used for device activation. If you don't 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, *<options>* can be [--key-file, --keyfile-offset, --keyfile-size,
--new-keyfile, --new-keyfile-offset, --new-keyfile-size, --key-slot, --new-keyfile, --new-keyfile-offset, --new-keyfile-size, --key-slot,
--new-key-slot, --volume-key-file, --force-password, --hash, --header, --new-key-slot, --volume-key-file, --volume-key-keyring, --force-password,
--disable-locks, --iter-time, --pbkdf, --pbkdf-force-iterations, --hash, --header, --disable-locks, --iter-time, --pbkdf,
--pbkdf-memory, --pbkdf-parallel, --unbound, --type, --keyslot-cipher, --pbkdf-force-iterations, --pbkdf-memory, --pbkdf-parallel, --unbound, --type,
--keyslot-key-size, --key-size, --timeout, --token-id, --token-type, --keyslot-cipher, --keyslot-key-size, --key-size, --timeout, --token-id,
--token-only, --new-token-id, --verify-passphrase]. --token-type, --token-only, --new-token-id, --verify-passphrase].
include::man/common_options.adoc[] 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, *<options>* can be [--key-file, --keyfile-size, --keyfile-offset,
--key-slot, --header, --disable-keyring, --disable-locks, --token-id, --key-slot, --header, --disable-keyring, --disable-locks, --token-id,
--token-only, --token-type, --disable-external-tokens, --type, --tries, --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_options.adoc[]
include::man/common_footer.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, --volume-key-file, --token-id, --token-only, --token-type,
--disable-external-tokens, --disable-keyring, --disable-locks, --type, --disable-external-tokens, --disable-keyring, --disable-locks, --type,
--refresh, --serialize-memory-hard-pbkdf, --unbound, --tries, --timeout, --refresh, --serialize-memory-hard-pbkdf, --unbound, --tries, --timeout,
--verify-passphrase, --persistent]. --verify-passphrase, --persistent, --volume-key-keyring].
=== loopAES === loopAES
*open --type loopaes <device> <name> --key-file <keyfile>* + *open --type loopaes <device> <name> --key-file <keyfile>* +

View File

@@ -1616,6 +1616,7 @@ static int action_open_luks(void)
char *password = NULL; char *password = NULL;
size_t passwordLen; size_t passwordLen;
struct stat st; struct stat st;
struct crypt_keyslot_context *kc = NULL;
if (ARG_SET(OPT_REFRESH_ID)) { if (ARG_SET(OPT_REFRESH_ID)) {
activated_name = action_argc > 1 ? action_argv[1] : action_argv[0]; activated_name = action_argc > 1 ? action_argv[1] : action_argv[0];
@@ -1683,6 +1684,13 @@ static int action_open_luks(void)
goto out; goto out;
r = crypt_activate_by_volume_key(cd, activated_name, r = crypt_activate_by_volume_key(cd, activated_name,
key, keysize, activate_flags); 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 { } else {
r = crypt_activate_by_token_pin(cd, activated_name, ARG_STR(OPT_TOKEN_TYPE_ID), 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); 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_get_active_device(cd, activated_name, &cad) ||
crypt_persistent_flags_set(cd, CRYPT_FLAGS_ACTIVATION, cad.flags & activate_flags))) crypt_persistent_flags_set(cd, CRYPT_FLAGS_ACTIVATION, cad.flags & activate_flags)))
log_err(_("Device activated but cannot make flags persistent.")); log_err(_("Device activated but cannot make flags persistent."));
crypt_keyslot_context_free(kc);
crypt_safe_free(key); crypt_safe_free(key);
crypt_safe_free(password); crypt_safe_free(password);
@@ -2082,6 +2091,8 @@ static int action_luksAddKey(void)
ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID),
ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT64(OPT_KEYFILE_OFFSET_ID),
&kc); &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)) { 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, r = crypt_keyslot_context_init_by_token(cd,
ARG_INT32(OPT_TOKEN_ID_ID), ARG_INT32(OPT_TOKEN_ID_ID),
@@ -2516,6 +2527,7 @@ static int action_luksResume(void)
int r, tries; int r, tries;
struct crypt_active_device cad; struct crypt_active_device cad;
const char *req_type = luksType(device_type); const char *req_type = luksType(device_type);
struct crypt_keyslot_context *kc = NULL;
if (req_type && !isLUKS(req_type)) if (req_type && !isLUKS(req_type))
return -EINVAL; return -EINVAL;
@@ -2573,6 +2585,14 @@ static int action_luksResume(void)
if (r >= 0 || quit || ARG_SET(OPT_TOKEN_ONLY_ID)) if (r >= 0 || quit || ARG_SET(OPT_TOKEN_ONLY_ID))
goto out; 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(); tries = set_tries_tty();
do { do {
r = tools_get_key(NULL, &password, &passwordLen, r = tools_get_key(NULL, &password, &passwordLen,
@@ -2591,6 +2611,7 @@ static int action_luksResume(void)
password = NULL; password = NULL;
} while ((r == -EPERM || r == -ERANGE) && (--tries > 0)); } while ((r == -EPERM || r == -ERANGE) && (--tries > 0));
out: out:
crypt_keyslot_context_free(kc);
crypt_safe_free(password); crypt_safe_free(password);
crypt_free(cd); crypt_free(cd);
return r; 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_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_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) 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_LUKS2_METADATA_SIZE "luks2-metadata-size"
#define OPT_MASTER_KEY_FILE "master-key-file" #define OPT_MASTER_KEY_FILE "master-key-file"
#define OPT_VOLUME_KEY_FILE "volume-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_VOLUME_KEY_TYPE "volume-key-type"
#define OPT_NEW "new" #define OPT_NEW "new"
#define OPT_NEW_KEY_SLOT "new-key-slot" #define OPT_NEW_KEY_SLOT "new-key-slot"

View File

@@ -304,6 +304,40 @@ test_vk_link() {
keyctl unlink "%$KEY_TYPE:$1" "$2" || fail 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 export LANG=C
[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped." [ ! -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) KID=$(echo -n test | keyctl padd user my_token @us)
keyctl unlink $KID >/dev/null 2>&1 @us && USER_SESSION_KEYRING_WORKS=1 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"
test_vk_link $KEY_NAME "@u" "user" test_vk_link $KEY_NAME "@u" "user"
[[ ! -z "$SESSION_KEYRING_WORKS" ]] && test_vk_link $KEY_NAME "@s" [[ ! -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 # explicitly specify keyring key type
test_vk_link $KEY_NAME "%keyring:$TEST_KEYRING_NAME" 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 # 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 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." keyctl search @us logon $KEY_NAME > /dev/null 2>&1 || fail "VK is not linked to the specified keyring after activation."