From 1aab3afcbaa68a8f606ed9c796a5a5eb32543fd6 Mon Sep 17 00:00:00 2001 From: Daniel Zatovic Date: Thu, 11 May 2023 15:52:17 +0200 Subject: [PATCH] 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 "%:". --- man/common_options.adoc | 11 +++++++++ man/cryptsetup-luksAddKey.8.adoc | 16 ++++++------ man/cryptsetup-luksResume.8.adoc | 2 +- man/cryptsetup-open.8.adoc | 2 +- src/cryptsetup.c | 21 ++++++++++++++++ src/cryptsetup_arg_list.h | 2 ++ src/utils_arg_names.h | 1 + tests/compat-test2 | 42 +++++++++++++++++++++++++++++++- 8 files changed, 86 insertions(+), 11 deletions(-) diff --git a/man/common_options.adoc b/man/common_options.adoc index 1fea45cc..82959421 100644 --- a/man/common_options.adoc +++ b/man/common_options.adoc @@ -235,6 +235,17 @@ partially predictable volume key which will compromise security. endif::[] endif::[] +ifdef::ACTION_OPEN,ACTION_LUKSRESUME,ACTION_LUKSADDKEY[] +*--volume-key-keyring* __:: +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 __ uses keyctl-compatible syntax. This can either be a +numeric key ID or a string name in the format _%:_. 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 diff --git a/man/cryptsetup-luksAddKey.8.adoc b/man/cryptsetup-luksAddKey.8.adoc index 9686a1dd..82884922 100644 --- a/man/cryptsetup-luksAddKey.8.adoc +++ b/man/cryptsetup-luksAddKey.8.adoc @@ -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. ** 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[] diff --git a/man/cryptsetup-luksResume.8.adoc b/man/cryptsetup-luksResume.8.adoc index 9d81cbc4..feb591e8 100644 --- a/man/cryptsetup-luksResume.8.adoc +++ b/man/cryptsetup-luksResume.8.adoc @@ -23,7 +23,7 @@ interactively for a passphrase if no token is usable (LUKS2 only) or ** 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[] diff --git a/man/cryptsetup-open.8.adoc b/man/cryptsetup-open.8.adoc index 78efb512..f75193d9 100644 --- a/man/cryptsetup-open.8.adoc +++ b/man/cryptsetup-open.8.adoc @@ -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 --key-file * + diff --git a/src/cryptsetup.c b/src/cryptsetup.c index 1acb5c6b..6deeb14d 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -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; diff --git a/src/cryptsetup_arg_list.h b/src/cryptsetup_arg_list.h index b74cdb93..9937da1d 100644 --- a/src/cryptsetup_arg_list.h +++ b/src/cryptsetup_arg_list.h @@ -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) diff --git a/src/utils_arg_names.h b/src/utils_arg_names.h index 8f212ed0..97d1c421 100644 --- a/src/utils_arg_names.h +++ b/src/utils_arg_names.h @@ -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" diff --git a/tests/compat-test2 b/tests/compat-test2 index 09a8bb86..0765d9ec 100755 --- a/tests/compat-test2 +++ b/tests/compat-test2 @@ -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."