From f740d8db6c1df2bedb50c12494a87477b83ea5e0 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 24 Oct 2024 11:21:59 -0400 Subject: [PATCH 1/2] Add code to prevent locking the token by mistake For tokens that properly report the status of the PIN authentication counter via token flags, check them out and refuse to attempt login if the token is on its last try. A token should never be on its last try and finding this flags set is an indication that someone may have hardocded an in correct pin in the configuration or an URI. Proceeding would have a high chance of ending up blocking the token. Fixes: #455 Signed-off-by: Simo Sorce --- src/session.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/session.c b/src/session.c index 42cbaab7..69c3a1f3 100644 --- a/src/session.c +++ b/src/session.c @@ -439,6 +439,35 @@ static int p11prov_session_prompt_for_pin(struct p11prov_slot *slot, return ret; } +static CK_RV check_pin_flags_ok(P11PROV_CTX *ctx, CK_SLOT_ID slotid) +{ + CK_TOKEN_INFO token; + CK_RV ret; + + ret = p11prov_GetTokenInfo(ctx, slotid, &token); + if (ret != CKR_OK) { + return ret; + } + + if (token.flags & CKF_USER_PIN_FINAL_TRY) { + ret = CKR_CANCEL; + P11PROV_raise(ctx, ret, + "Only one auth attempt left on token. " + "Canceling login attempt to avoid locking the token. " + "Manual user login required to reset counter."); + } else if (token.flags & CKF_USER_PIN_LOCKED) { + ret = CKR_PIN_LOCKED; + P11PROV_raise(ctx, ret, "PIN marked as locked, canceling login"); + } else if (token.flags & CKF_USER_PIN_TO_BE_CHANGED) { + ret = CKR_PIN_EXPIRED; + P11PROV_raise(ctx, ret, "PIN marked as expired, canceling login"); + } else { + ret = CKR_OK; + } + + return ret; +} + /* returns a locked login_session if _session is not NULL */ static CK_RV token_login(P11PROV_SESSION *session, P11PROV_URI *uri, OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg, @@ -522,6 +551,11 @@ static CK_RV token_login(P11PROV_SESSION *session, P11PROV_URI *uri, } } + ret = check_pin_flags_ok(session->provctx, session->slotid); + if (ret != CKR_OK) { + goto done; + } + P11PROV_debug("Attempt Login on session %lu", session->session); /* Supports only USER login sessions for now */ ret = p11prov_Login(session->provctx, session->session, user_type, pin, From 9e36e1fdeca1e23b7dc337b006b15dc8c62dd375 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Fri, 25 Oct 2024 16:03:59 -0400 Subject: [PATCH 2/2] Test pin locking prevention Only kryoptic seem to correctly enforce pin lockout and return the correct flags. Softhsm seem to expose CKF_PIN_COUNT_LOW at some point but never lock the token. Softoken seem not support pin counting or locking at all. Signed-off-by: Simo Sorce --- tests/meson.build | 1 + tests/setup.sh | 3 +- tests/tpinlock | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100755 tests/tpinlock diff --git a/tests/meson.build b/tests/meson.build index 7e7f00bd..03119db0 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -140,6 +140,7 @@ tests = { 'uri': {'suites': ['softokn', 'softhsm', 'kryoptic']}, 'ecxc': {'suites': ['softhsm', 'kryoptic']}, 'cms': {'suites': ['softokn', 'kryoptic']}, + 'pinlock': {'suites': ['kryoptic']}, } test_wrapper = find_program('test-wrapper') diff --git a/tests/setup.sh b/tests/setup.sh index 56977359..bc5cec1b 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -391,7 +391,8 @@ sed -e "s|@libtoollibs@|${LIBSPATH}|g" \ title LINE "Export test variables to ${TMPPDIR}/testvars" cat >> "${TMPPDIR}/testvars" < +# SPDX-License-Identifier: Apache-2.0 + +source "${TESTSSRCDIR}/helpers.sh" + +title PARA "Test PIN lock prevention" + +ORIG_OPENSSL_CONF=${OPENSSL_CONF} +sed "s/^pkcs11-module-token-pin.*$/##nopin/" "${OPENSSL_CONF}" > "${OPENSSL_CONF}.nopin" +OPENSSL_CONF=${OPENSSL_CONF}.nopin + +BADPIN="bad" +export BADPINURI="${PRIURI}?pin-value=${BADPIN}" +export GOODPINURI="${PRIURI}?pin-value=${PINVALUE}" + +TOOLDEFARGS=("--module=${P11LIB}" "--token-label=${TOKENLABEL}") + +FAIL=0 +pkcs11-tool "${TOOLDEFARGS[@]}" -T | grep "PIN initialized" && FAIL=1 +if [ $FAIL -eq 0 ]; then + echo "Failed to detect PIN status" + exit 1 +fi + +# Kryoptic allows for 10 tries by default +for i in {1..10}; do + echo "Login attempt: $i" + pkcs11-tool "${TOOLDEFARGS[@]}" -l -I -p "${BADPIN}" && false + DETECT=0 + pkcs11-tool "${TOOLDEFARGS[@]}" -T | grep "final user PIN try" && DETECT=1 + if [ $DETECT -eq 1 ]; then + break + fi +done +FAIL=0 +pkcs11-tool "${TOOLDEFARGS[@]}" -T | grep "final user PIN try" && FAIL=1 +if [ $FAIL -eq 0 ]; then + echo "Failed to reach "final try" status" + exit 1 +fi + +# Now we test one operation with a bad pin. +# It should fail but not lock the token +title LINE "Try op with bad pin and fail" +FAIL=0 +ossl ' +pkeyutl -sign -inkey "${BADPINURI}" + -in ${TMPPDIR}/sha256.bin + -out ${TMPPDIR}/pinlock-sig.bin' || FAIL=1 +if [ $FAIL -eq 0 ]; then + echo "Operation should have failed, pin lock prevention not working" + exit 1 +fi + +# Now we test one operation with a good pin. +# It should fail because the token is on last try +title LINE "Try op with good pin and fail" +FAIL=0 +ossl ' +pkeyutl -sign -inkey "${GOODPINURI}" + -in ${TMPPDIR}/sha256.bin + -out ${TMPPDIR}/pinlock-sig.bin' || FAIL=1 +if [ $FAIL -eq 0 ]; then + echo "Operation should have failed, pin lock prevention not working" + exit 1 +fi + + +# Now reset the token counter with a good try +pkcs11-tool "${TOOLDEFARGS[@]}" -l -T -p "${PINVALUE}" + +# Now we test one operation with a good pin. +# It should succeed +title LINE "Try op with good pin and succeed" +ossl ' +pkeyutl -sign -inkey "${GOODPINURI}" + -in ${TMPPDIR}/sha256.bin + -out ${TMPPDIR}/pinlock-sig.bin' + +OPENSSL_CONF=${ORIG_OPENSSL_CONF}