diff --git a/tests/api-test-2.c b/tests/api-test-2.c index e8563425..9bab1e88 100644 --- a/tests/api-test-2.c +++ b/tests/api-test-2.c @@ -489,6 +489,16 @@ static key_serial_t keyctl_unlink(key_serial_t key, key_serial_t keyring) return syscall(__NR_keyctl, KEYCTL_UNLINK, key, keyring); } +static long keyctl_update(key_serial_t id, const void *payload, size_t plen) +{ + return syscall(__NR_keyctl, KEYCTL_UPDATE, id, payload, plen); +} + +static long keyctl_read(key_serial_t id, char *buffer, size_t buflen) +{ + return syscall(__NR_keyctl, KEYCTL_READ, id, buffer, buflen); +} + static key_serial_t request_key(const char *type, const char *description, const char *callout_info, @@ -497,14 +507,20 @@ static key_serial_t request_key(const char *type, return syscall(__NR_request_key, type, description, callout_info, keyring); } -static key_serial_t _kernel_key_by_segment(struct crypt_device *_cd, int segment) +static key_serial_t _kernel_key_by_segment_and_type(struct crypt_device *_cd, int segment, + const char* type) { char key_description[1024]; if (snprintf(key_description, sizeof(key_description), "cryptsetup:%s-d%u", crypt_get_uuid(_cd), segment) < 1) return -1; - return request_key("logon", key_description, NULL, 0); + return request_key(type, key_description, NULL, 0); +} + +static key_serial_t _kernel_key_by_segment(struct crypt_device *_cd, int segment) +{ + return _kernel_key_by_segment_and_type(cd, segment, "logon"); } static int _volume_key_in_keyring(struct crypt_device *_cd, int segment) @@ -512,14 +528,20 @@ static int _volume_key_in_keyring(struct crypt_device *_cd, int segment) return _kernel_key_by_segment(_cd, segment) >= 0 ? 0 : -1; } -static int _drop_keyring_key(struct crypt_device *_cd, int segment) +static int _drop_keyring_key_from_keyring_type(struct crypt_device *_cd, int segment, + key_serial_t keyring, const char* type) { - key_serial_t kid = _kernel_key_by_segment(_cd, segment); + key_serial_t kid = _kernel_key_by_segment_and_type(_cd, segment, type); if (kid < 0) return -1; - return keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING); + return keyctl_unlink(kid, keyring); +} + +static int _drop_keyring_key(struct crypt_device *_cd, int segment) +{ + return _drop_keyring_key_from_keyring_type(cd, segment, KEY_SPEC_THREAD_KEYRING, "logon"); } #endif @@ -5120,17 +5142,21 @@ static void VolumeKeyGet(void) _cleanup_dmdevices(); } -static void KeyslotContext(void) +static void KeyslotContextAndKeyringLink(void) { +#ifdef KERNEL_KEYRING const char *cipher = "aes"; const char *cipher_mode = "xts-plain64"; struct crypt_keyslot_context *kc; uint64_t r_payload_offset; char key[128]; size_t key_size = 128; - key_serial_t kid; + key_serial_t kid, linked_kid; int suspend_status; struct crypt_active_device cad; + char key_description[1024]; + char vk_buf[1024]; + long vk_len; OK_(get_luks2_offsets(0, 0, 0, NULL, &r_payload_offset)); OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1)); @@ -5192,7 +5218,81 @@ static void KeyslotContext(void) OK_(crypt_deactivate(cd, CDEVICE_1)); crypt_keyslot_context_free(kc); - // test resume + // test linking to a keyring and setting key type + crypt_set_keyring_to_link(cd, "@u"); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0)); + NOTFAIL_((linked_kid = _kernel_key_by_segment_and_type(cd, 0, "logon")), "VK was not linked to user keyring."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + NOTFAIL_(keyctl_unlink(linked_kid, KEY_SPEC_USER_KEYRING), "VK was not linked to user keyring after deactivation."); + FAIL_(keyctl_unlink(linked_kid, KEY_SPEC_USER_KEYRING), "VK was not unlinked linked from user keyring after deactivation."); + + crypt_set_vk_keyring_type(cd, "user"); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0)); + NOTFAIL_((linked_kid = _kernel_key_by_segment_and_type(cd, 0, "user")), "VK was not linked to user keyring."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + NOTFAIL_(keyctl_unlink(linked_kid, KEY_SPEC_USER_KEYRING), "VK was not linked to user keyring after deactivation."); + FAIL_(keyctl_unlink(linked_kid, KEY_SPEC_USER_KEYRING), "VK was not unlinked linked from user keyring after deactivation."); + + crypt_set_keyring_to_link(cd, "@s"); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "VK was not linked to session keyring."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "VK was not linked to session keyring after deactivation."); + OK_(_drop_keyring_key_from_keyring_type(cd, 0, KEY_SPEC_SESSION_KEYRING, "user")); + + // test repated activation + FAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "VK was still linked to session keyring after removal."); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "VK was not linked to session keyring after repeated activation."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "VK was not linked to session keyring after deactivation."); + OK_(_drop_keyring_key_from_keyring_type(cd, 0, KEY_SPEC_SESSION_KEYRING, "user")); + FAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "failed to unlink the key from session keyring"); + + // change key type to default (logon) + crypt_set_vk_keyring_type(cd, NULL); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "logon"), "VK was not linked to session keyring after resetting key type."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "logon"), "VK was not linked to session keyring after deactivation."); + OK_(_drop_keyring_key_from_keyring_type(cd, 0, KEY_SPEC_SESSION_KEYRING, "logon")); + FAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "failed to unlink the key from session keyring"); + + // disable linking to session keyring + crypt_set_keyring_to_link(cd, NULL); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "logon"), "VK was not found in thread keyring"); + OK_(crypt_deactivate(cd, CDEVICE_1)); + FAIL_(_kernel_key_by_segment_and_type(cd, 0, "logon"), "failed to unlink the key from thread keyring"); + + // link VK to keyring and re-activate by the linked VK + crypt_set_vk_keyring_type(cd, "user"); + crypt_set_keyring_to_link(cd, "@s"); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "VK was not linked to session keyring."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + OK_(snprintf(key_description, sizeof(key_description), "%%user:cryptsetup:%s-d0", crypt_get_uuid(cd)) < 1); + OK_(crypt_keyslot_context_init_by_vk_in_keyring(cd, key_description, &kc)); + EQ_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), 0); + OK_(crypt_deactivate(cd, CDEVICE_1)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "VK was not linked to session keyring after deactivation."); + OK_(_drop_keyring_key_from_keyring_type(cd, 0, KEY_SPEC_SESSION_KEYRING, "user")); + FAIL_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), "activation via VK in keyring after dropping the key"); + + // load VK back to keyring by activating + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0)); + OK_(crypt_deactivate(cd, CDEVICE_1)); + + // activate by bad VK in keyring (test if VK digest is verified) + NOTFAIL_((linked_kid = _kernel_key_by_segment_and_type(cd, 0, "user")), "VK was not linked to session keyring after activation."); + GE_((vk_len = keyctl_read(linked_kid, vk_buf, sizeof(vk_buf))), 0); + vk_buf[0] = ~vk_buf[0]; + OK_(keyctl_update(linked_kid, vk_buf, vk_len)); + FAIL_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), 0); + OK_(_drop_keyring_key_from_keyring_type(cd, 0, KEY_SPEC_SESSION_KEYRING, "user")); + crypt_keyslot_context_free(kc); + + // After this point put resume tests only! OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &kc)); EQ_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), 0); suspend_status = crypt_suspend(cd, CDEVICE_1); @@ -5237,9 +5337,29 @@ static void KeyslotContext(void) OK_(crypt_deactivate(cd, CDEVICE_1)); crypt_keyslot_context_free(kc); + // resume by VK keyring context + crypt_set_vk_keyring_type(cd, "user"); + crypt_set_keyring_to_link(cd, "@s"); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "VK was not linked to session keyring."); + OK_(crypt_suspend(cd, CDEVICE_1)); + OK_(snprintf(key_description, sizeof(key_description), "%%user:cryptsetup:%s-d0", crypt_get_uuid(cd)) < 1); + OK_(crypt_keyslot_context_init_by_vk_in_keyring(cd, key_description, &kc)); + EQ_(crypt_resume_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc), 0); + OK_(crypt_deactivate(cd, CDEVICE_1)); + EQ_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), 0); + OK_(crypt_deactivate(cd, CDEVICE_1)); + NOTFAIL_(_kernel_key_by_segment_and_type(cd, 0, "user"), "VK was not linked to session keyring after deactivation."); + OK_(_drop_keyring_key_from_keyring_type(cd, 0, KEY_SPEC_SESSION_KEYRING, "user")); + FAIL_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), "activation via VK in keyring after dropping the key"); + crypt_keyslot_context_free(kc); + NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken."); CRYPT_FREE(cd); _cleanup_dmdevices(); +#else + printf("WARNING: cryptsetup compiled with kernel keyring service disabled, skipping test.\n"); +#endif } static int _crypt_load_check(struct crypt_device *_cd) @@ -5369,7 +5489,7 @@ int main(int argc, char *argv[]) #endif RUN_(LuksKeyslotAdd, "Adding keyslot via new API"); RUN_(VolumeKeyGet, "Getting volume key via keyslot context API"); - RUN_(KeyslotContext, "Activate via keyslot context API"); + RUN_(KeyslotContextAndKeyringLink, "Activate via keyslot context API and linking VK to a keyring"); RUN_(Luks2Repair, "LUKS2 repair"); // test disables metadata locking. Run always last! _cleanup(); diff --git a/tests/compat-test2 b/tests/compat-test2 index 8b6bb073..09a8bb86 100755 --- a/tests/compat-test2 +++ b/tests/compat-test2 @@ -287,6 +287,23 @@ function add_scsi_device() { [ -b $DEV ] || fail "Cannot find $DEV." } +# $1 key name +# $2 keyring to link VK to +# $3 key type (optional) +test_vk_link() { + KEY_TYPE=${3:-logon} + KEY_TYPE_ARG="" + if [ ! -z "$3" ]; then + KEY_TYPE_ARG="--volume-key-type $3" + fi + + 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 + keyctl search "$2" $KEY_TYPE $1 > /dev/null 2>&1 || fail "VK is not linked to the specified keyring after deactivation." + keyctl unlink "%$KEY_TYPE:$1" "$2" || fail +} + export LANG=C [ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped." @@ -1215,5 +1232,49 @@ dmsetup create $DEV_NAME --uuid CRYPT-LUKS2-3d20686f551748cb89911ad32379821b-tes $CRYPTSETUP status $DEV_NAME | grep -q "n/a" || fail $CRYPTSETUP close $DEV_NAME ||fail +if [ $HAVE_KEYRING -gt 0 -a -d /proc/sys/kernel/keys ]; then + prepare "[45] Link VK to a keyring and use custom VK type." wipe + + echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV 2> /dev/null || fail + UUID=$($CRYPTSETUP luksDump $LOOPDEV | grep 'UUID:' | awk '{print $2}') + KEY_NAME="cryptsetup:$UUID-d0" + test_and_prepare_keyring + KID=$(echo -n test | keyctl padd user my_token @s) + keyctl unlink $KID >/dev/null 2>&1 @s && SESSION_KEYRING_WORKS=1 + 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" + [[ ! -z "$SESSION_KEYRING_WORKS" ]] && test_vk_link $KEY_NAME "@s" "user" + [[ ! -z "$USER_SESSION_KEYRING_WORKS" ]] && test_vk_link $KEY_NAME "@us" + [[ ! -z "$USER_SESSION_KEYRING_WORKS" ]] && test_vk_link $KEY_NAME "@us" "user" + test_vk_link $KEY_NAME "%:$TEST_KEYRING_NAME" + test_vk_link $KEY_NAME "%:$TEST_KEYRING_NAME" "user" + # explicitly specify keyring key type + test_vk_link $KEY_NAME "%keyring:$TEST_KEYRING_NAME" + + # 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." + $CRYPTSETUP close $DEV_NAME + keyctl search @us logon $KEY_NAME > /dev/null 2>&1 || fail "VK is not linked to the specified keyring after deactivation." + keyctl unlink "%logon:$KEY_NAME" @us || fail + + # test malformed keyring descriptions and key types + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --link-vk-to-keyring "%user:$TEST_KEYRING_NAME" > /dev/null 2>&1 && fail + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --link-vk-to-keyring "$TEST_KEYRING_NAME" > /dev/null 2>&1 && fail + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --link-vk-to-keyring "%$TEST_KEYRING_NAME" > /dev/null 2>&1 && fail + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --link-vk-to-keyring ":$TEST_KEYRING_NAME" > /dev/null 2>&1 && fail + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --link-vk-to-keyring "@uuu" > /dev/null 2>&1 && fail + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --link-vk-to-keyring "@usu" > /dev/null 2>&1 && fail + + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --volume-key-type 0 > /dev/null 2>&1 && fail + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --volume-key-type blah > /dev/null 2>&1 && fail + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME --volume-key-type userlogon > /dev/null 2>&1 && fail +fi + remove_mapping exit 0