diff --git a/src/cryptsetup.c b/src/cryptsetup.c index 5616d7d3..6fd70bf2 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -89,27 +89,49 @@ static int _set_keyslot_encryption_params(struct crypt_device *cd) return crypt_keyslot_set_encryption(cd, ARG_STR(OPT_KEYSLOT_CIPHER_ID), ARG_UINT32(OPT_KEYSLOT_KEY_SIZE_ID) / 8); } -static int _try_token_pin_unlock(struct crypt_device *cd, - int token_id, - const char *activated_name, - const char *token_type, - uint32_t activate_flags, - int tries, - bool activation) +static int _try_token_unlock(struct crypt_device *cd, + int keyslot, + int token_id, + const char *activated_name, + const char *token_type, + uint32_t activate_flags, + int tries, + bool activation, + bool token_only) { + int r; + struct crypt_keyslot_context *kc; size_t pin_len; char msg[64], *pin = NULL; - int r; assert(tries >= 1); assert(token_id >= 0 || token_id == CRYPT_ANY_TOKEN); + assert(keyslot >= 0 || keyslot == CRYPT_ANY_SLOT); + + r = crypt_keyslot_context_init_by_token(cd, token_id, token_type, NULL, 0, NULL, &kc); + if (r < 0) + return r; + + if (activation) + r = crypt_activate_by_keyslot_context(cd, activated_name, keyslot, kc, activate_flags); + else + r = crypt_resume_by_keyslot_context(cd, activated_name, keyslot, kc); + + tools_keyslot_msg(r, UNLOCKED); + tools_token_error_msg(r, token_type, token_id, false); + + /* Token requires PIN (-ENOANO). Ask for it if there is evident preference for tokens */ + if (r != -ENOANO || (!token_only && !token_type && token_id == CRYPT_ANY_TOKEN)) + goto out; if (token_id == CRYPT_ANY_TOKEN) r = snprintf(msg, sizeof(msg), _("Enter token PIN: ")); else r = snprintf(msg, sizeof(msg), _("Enter token %d PIN: "), token_id); - if (r < 0 || (size_t)r >= sizeof(msg)) - return -EINVAL; + if (r < 0 || (size_t)r >= sizeof(msg)) { + r = -EINVAL; + goto out; + } do { r = tools_get_key(msg, &pin, &pin_len, 0, 0, NULL, @@ -117,20 +139,26 @@ static int _try_token_pin_unlock(struct crypt_device *cd, if (r < 0) break; + r = crypt_keyslot_context_set_pin(cd, pin, pin_len, kc); + if (r < 0) { + crypt_safe_free(pin); + break; + } + if (activation) - r = crypt_activate_by_token_pin(cd, activated_name, token_type, - token_id, pin, pin_len, NULL, - activate_flags); + r = crypt_activate_by_keyslot_context(cd, activated_name, keyslot, + kc, activate_flags); else - r = crypt_resume_by_token_pin(cd, activated_name, token_type, - token_id, pin, pin_len, NULL); + r = crypt_resume_by_keyslot_context(cd, activated_name, keyslot, kc); + crypt_safe_free(pin); pin = NULL; tools_keyslot_msg(r, UNLOCKED); - tools_token_error_msg(r, ARG_STR(OPT_TOKEN_TYPE_ID), token_id, true); + tools_token_error_msg(r, token_type, token_id, true); check_signal(&r); } while (r == -ENOANO && (--tries > 0)); - +out: + crypt_keyslot_context_free(kc); return r; } @@ -859,16 +887,9 @@ static int action_resize(void) } /* try load VK in kernel keyring using token */ - r = crypt_activate_by_token_pin(cd, NULL, ARG_STR(OPT_TOKEN_TYPE_ID), - ARG_INT32(OPT_TOKEN_ID_ID), NULL, 0, NULL, - CRYPT_ACTIVATE_KEYRING_KEY); - tools_keyslot_msg(r, UNLOCKED); - tools_token_error_msg(r, ARG_STR(OPT_TOKEN_TYPE_ID), ARG_INT32(OPT_TOKEN_ID_ID), false); - - /* Token requires PIN. Ask if there is evident preference for tokens */ - if (r == -ENOANO && (ARG_SET(OPT_TOKEN_ONLY_ID) || ARG_SET(OPT_TOKEN_TYPE_ID) || - ARG_SET(OPT_TOKEN_ID_ID))) - r = _try_token_pin_unlock(cd, ARG_INT32(OPT_TOKEN_ID_ID), NULL, ARG_STR(OPT_TOKEN_TYPE_ID), CRYPT_ACTIVATE_KEYRING_KEY, 1, true); + r = _try_token_unlock(cd, ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_TOKEN_ID_ID), + NULL, ARG_STR(OPT_TOKEN_TYPE_ID), CRYPT_ACTIVATE_KEYRING_KEY, + 1, true, ARG_SET(OPT_TOKEN_ONLY_ID)); if (r >= 0 || quit || ARG_SET(OPT_TOKEN_ONLY_ID)) goto out; @@ -1795,15 +1816,10 @@ static int action_open_luks(void) 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); - tools_keyslot_msg(r, UNLOCKED); - tools_token_error_msg(r, ARG_STR(OPT_TOKEN_TYPE_ID), ARG_INT32(OPT_TOKEN_ID_ID), false); - - /* Token requires PIN. Ask if there is evident preference for tokens */ - if (r == -ENOANO && (ARG_SET(OPT_TOKEN_ONLY_ID) || ARG_SET(OPT_TOKEN_TYPE_ID) || - ARG_SET(OPT_TOKEN_ID_ID))) - r = _try_token_pin_unlock(cd, ARG_INT32(OPT_TOKEN_ID_ID), activated_name, ARG_STR(OPT_TOKEN_TYPE_ID), activate_flags, set_tries_tty(), true); + r = _try_token_unlock(cd, ARG_INT32(OPT_KEY_SLOT_ID), + ARG_INT32(OPT_TOKEN_ID_ID), activated_name, + ARG_STR(OPT_TOKEN_TYPE_ID), activate_flags, + set_tries_tty(), true, ARG_SET(OPT_TOKEN_ONLY_ID)); if (r >= 0 || r == -EEXIST || quit || ARG_SET(OPT_TOKEN_ONLY_ID)) goto out; @@ -2673,15 +2689,9 @@ static int action_luksResume(void) } /* try to resume LUKS2 device by token first */ - r = crypt_resume_by_token_pin(cd, action_argv[0], ARG_STR(OPT_TOKEN_TYPE_ID), - ARG_INT32(OPT_TOKEN_ID_ID), NULL, 0, NULL); - tools_keyslot_msg(r, UNLOCKED); - tools_token_error_msg(r, ARG_STR(OPT_TOKEN_TYPE_ID), ARG_INT32(OPT_TOKEN_ID_ID), false); - - /* Token requires PIN. Ask if there is evident preference for tokens */ - if (r == -ENOANO && (ARG_SET(OPT_TOKEN_ONLY_ID) || ARG_SET(OPT_TOKEN_TYPE_ID) || - ARG_SET(OPT_TOKEN_ID_ID))) - r = _try_token_pin_unlock(cd, ARG_INT32(OPT_TOKEN_ID_ID), action_argv[0], ARG_STR(OPT_TOKEN_TYPE_ID), 0, set_tries_tty(), false); + r = _try_token_unlock(cd, ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_TOKEN_ID_ID), + action_argv[0], ARG_STR(OPT_TOKEN_TYPE_ID), 0, + set_tries_tty(), false, ARG_SET(OPT_TOKEN_ONLY_ID)); if (r >= 0 || quit || ARG_SET(OPT_TOKEN_ONLY_ID)) goto out; diff --git a/tests/compat-test2 b/tests/compat-test2 index 6a7d4fa7..223d8c32 100755 --- a/tests/compat-test2 +++ b/tests/compat-test2 @@ -3,6 +3,7 @@ PS4='$LINENO:' [ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +CRYPTSETUP_RAW=$CRYPTSETUP CRYPTSETUP_VALGRIND=../.libs/cryptsetup CRYPTSETUP_LIB_VALGRIND=../.libs @@ -342,6 +343,36 @@ test_vk_link_and_reactivate() { keyctl unlink $KEYCTL_KEY_NAME "$2" || fail } +function expect_run() +{ + export INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" + expect "$@" +} + +# expected unlocked keyslot id +# command arguments +function expect_unlocked_keyslot() +{ + command -v expect >/dev/null || { + echo "WARNING: expect tool missing, interactive test will be skipped." + return 0 + } + + EXPECT_TIMEOUT=60 + EXPECT_KEY=$1 + + expect_run - >/dev/null </dev/null && fail $CRYPTSETUP token unassign --token-id 0 -S44 $LOOPDEV 2>/dev/null && fail $CRYPTSETUP token unassign --token-id 44 -S0 $LOOPDEV 2>/dev/null && fail + + $CRYPTSETUP token remove $LOOPDEV --token-id 0 || fail + $CRYPTSETUP token add $LOOPDEV --key-description $TEST_TOKEN0 -S0 --token-id 0 || fail + + # token 8 assigned to keyslot 0 and 5. Unlocks only 5 + echo "$PWD2" | $CRYPTSETUP luksAddKey -q -S5 $FAST_PBKDF_OPT --token-id 0 $LOOPDEV || fail + echo -n "{\"type\":\"luks2-keyring\",\"keyslots\":[\"0\",\"5\"],\"key_description\":\"$TEST_TOKEN1\"}" | $CRYPTSETUP token import $LOOPDEV --token-id 8 || fail + load_key user $TEST_TOKEN1 "$PWD2" "$TEST_KEYRING" || fail "Cannot load 32 byte user key type" + + # token 3 assigned to keyslot 1 (wrong passphrase) + echo "$PWD3" | $CRYPTSETUP luksAddKey -q -S1 $FAST_PBKDF_OPT --token-id 0 $LOOPDEV || fail + $CRYPTSETUP token add $LOOPDEV --key-description $TEST_TOKEN2 -S1 --token-id 3 || fail + load_key user $TEST_TOKEN2 "blabla" "$TEST_KEYRING" || fail "Cannot load 32 byte user key type" + + # specific token, specific keyslot + $CRYPTSETUP open --test-passphrase --token-id 0 -S0 $LOOPDEV --token-only <&- || fail + # specific keyslot unlocked by any token + $CRYPTSETUP open --test-passphrase -S0 $LOOPDEV --token-only <&- || fail + + # token 0 unusable for keyslot 5 + $CRYPTSETUP open --test-passphrase --token-id 0 -S5 $LOOPDEV --token-only <&- >/dev/null && fail + # backup interactive prompt should work + echo $PWD2 | $CRYPTSETUP open --test-passphrase --token-id 0 -S5 $LOOPDEV || fail + + $CRYPTSETUP open --test-passphrase -S5 --token-id 8 $LOOPDEV <&- || fail + $CRYPTSETUP open --test-passphrase -S5 $LOOPDEV <&- || fail + + expect_unlocked_keyslot 5 "open -v --test-passphrase --token-id 8 -S5 $LOOPDEV" || fail + expect_unlocked_keyslot 5 "open -v --test-passphrase --token-id 8 $LOOPDEV" || fail + + $CRYPTSETUP open --test-passphrase -S0 --token-id 8 $LOOPDEV --token-only >/dev/null && fail + [ $? -ne 2 ] && fail "open should return EPERM exit code." + $CRYPTSETUP open --test-passphrase -S1 $LOOPDEV --token-only && fail + [ $? -ne 2 ] && fail "open should return EPERM exit code." fi echo -n "$IMPORT_TOKEN" | $CRYPTSETUP token import $LOOPDEV --token-id 10 || fail echo -n "$IMPORT_TOKEN" | $CRYPTSETUP token import $LOOPDEV --token-id 11 --json-file - || fail