kentpeacock / rpms / openssh

Forked from rpms/openssh 2 years ago
Clone
Petr Šabata 81d24c
commit ed3eaf7d68c083b6015ca3425b75932999dafaad
Petr Šabata 81d24c
Author: Jakub Jelen <jjelen@redhat.com>
Petr Šabata 81d24c
Date:   Wed Apr 24 17:23:21 2019 +0200
Petr Šabata 81d24c
Petr Šabata 81d24c
    PKCS#11 URI from Fedora
Petr Šabata 81d24c
    
Petr Šabata 81d24c
     * Print PKCS#11 URIs from ssh-keygen
Petr Šabata 81d24c
     * Accept PKCS#11 URIs in -i argument to ssh
Petr Šabata 81d24c
     * Allow PKCS#11 URI specification in ssh_config
Petr Šabata 81d24c
     * Fallback to p11-kit-proxy
Petr Šabata 81d24c
     * PKCS#11 URI support for ssh-add and ssh-agent
Petr Šabata 81d24c
      * internal representation is URI
Petr Šabata 81d24c
     * Allow to specify pin-value in URI to avoid interactive prompts
Petr Šabata 81d24c
    
Petr Šabata 81d24c
    Currently recognized and used parts of PKCS#11 URI:
Petr Šabata 81d24c
     * path (optional)
Petr Šabata 81d24c
      * token
Petr Šabata 81d24c
      * id
Petr Šabata 81d24c
      * manufacturer
Petr Šabata 81d24c
      * (library-manufacturer)
Petr Šabata 81d24c
     * query (optional)
Petr Šabata 81d24c
      * module-path
Petr Šabata 81d24c
      * pin-value
Petr Šabata 81d24c
    
Petr Šabata 81d24c
    Unit test for PKCS#11 URIs
Petr Šabata 81d24c
    
Petr Šabata 81d24c
     * test PKCS#11 URI parser, generator
Petr Šabata 81d24c
     * test percent_encodeer and decoder
Petr Šabata 81d24c
    
Petr Šabata 81d24c
    Regression tests for PKCS#11 URI support
Petr Šabata 81d24c
    
Petr Šabata 81d24c
     * soft-pkcs11.so  from people.su.se/~lha/soft-pkcs11
Petr Šabata 81d24c
      * Return correct CKR for unknown attributes
Petr Šabata 81d24c
      * Adjust and build it with regress tests (allowing agent-pkcs11 test)
Petr Šabata 81d24c
     * Test PKCS#11 URIs support with soft-pkcs11
Petr Šabata 81d24c
      * Direct usage from commandline (URI, provider and combination)
Petr Šabata 81d24c
      * Usage from configuration files
Petr Šabata 81d24c
      * Usage in ssh-agent (add, sign, remove)
Petr Šabata 81d24c
      * Make sure it is built with correct paths
Petr Šabata 81d24c
Petr Šabata 81d24c
diff --git a/Makefile.in b/Makefile.in
Petr Šabata 81d24c
index e7549470..4511f82a 100644
Petr Šabata 81d24c
--- a/Makefile.in
Petr Šabata 81d24c
+++ b/Makefile.in
Petr Šabata 81d24c
@@ -102,7 +102,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
Petr Šabata 81d24c
 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-ecdsa-sk.o \
Petr Šabata 81d24c
 	ssh-ed25519-sk.o ssh-rsa.o dh.o \
Petr Šabata 81d24c
 	msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
Petr Šabata 81d24c
-	ssh-pkcs11.o smult_curve25519_ref.o \
Petr Šabata 81d24c
+	ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \
Petr Šabata 81d24c
 	poly1305.o chacha.o cipher-chachapoly.o cipher-chachapoly-libcrypto.o \
Petr Šabata 81d24c
 	ssh-ed25519.o digest-openssl.o digest-libc.o \
Petr Šabata 81d24c
 	hmac.o sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \
Petr Šabata 81d24c
@@ -289,6 +289,8 @@ clean:	regressclean
Petr Šabata 81d24c
 	rm -f regress/unittests/match/test_match$(EXEEXT)
Petr Šabata 81d24c
 	rm -f regress/unittests/utf8/*.o
Petr Šabata 81d24c
 	rm -f regress/unittests/utf8/test_utf8$(EXEEXT)
Petr Šabata 81d24c
+	rm -f regress/unittests/pkcs11/*.o
Petr Šabata 81d24c
+	rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT)
Petr Šabata 81d24c
 	rm -f regress/misc/kexfuzz/*.o
Petr Šabata 81d24c
 	rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT)
Petr Šabata 81d24c
 	rm -f regress/misc/sk-dummy/*.o
Petr Šabata 81d24c
@@ -322,6 +324,8 @@ distclean:	regressclean
Petr Šabata 81d24c
 	rm -f regress/unittests/match/test_match
Petr Šabata 81d24c
 	rm -f regress/unittests/utf8/*.o
Petr Šabata 81d24c
 	rm -f regress/unittests/utf8/test_utf8
Petr Šabata 81d24c
+	rm -f regress/unittests/pkcs11/*.o
Petr Šabata 81d24c
+	rm -f regress/unittests/pkcs11/test_pkcs11
Petr Šabata 81d24c
 	rm -f regress/misc/kexfuzz/*.o
Petr Šabata 81d24c
 	rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT)
Petr Šabata 81d24c
 	(cd openbsd-compat && $(MAKE) distclean)
Petr Šabata 81d24c
@@ -490,6 +494,7 @@ regress-prep:
Petr Šabata 81d24c
 	$(MKDIR_P) `pwd`/regress/unittests/kex
Petr Šabata 81d24c
 	$(MKDIR_P) `pwd`/regress/unittests/match
Petr Šabata 81d24c
 	$(MKDIR_P) `pwd`/regress/unittests/utf8
Petr Šabata 81d24c
+	$(MKDIR_P) `pwd`/regress/unittests/pkcs11
Petr Šabata 81d24c
 	$(MKDIR_P) `pwd`/regress/misc/kexfuzz
Petr Šabata 81d24c
 	$(MKDIR_P) `pwd`/regress/misc/sk-dummy
Petr Šabata 81d24c
 	[ -f `pwd`/regress/Makefile ] || \
Petr Šabata 81d24c
@@ -617,6 +622,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \
Petr Šabata 81d24c
 	    regress/unittests/test_helper/libtest_helper.a \
Petr Šabata 81d24c
 	    -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+UNITTESTS_TEST_PKCS11_OBJS=\
Petr Šabata 81d24c
+	regress/unittests/pkcs11/tests.o
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \
Petr Šabata 81d24c
+    ${UNITTESTS_TEST_PKCS11_OBJS} \
Petr Šabata 81d24c
+    regress/unittests/test_helper/libtest_helper.a libssh.a
Petr Šabata 81d24c
+	$(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \
Petr Šabata 81d24c
+	    regress/unittests/test_helper/libtest_helper.a \
Petr Šabata 81d24c
+	    -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 MISC_KEX_FUZZ_OBJS=\
Petr Šabata 81d24c
 	regress/misc/kexfuzz/kexfuzz.o \
Petr Šabata 81d24c
 	$(SKOBJS)
Petr Šabata 81d24c
@@ -655,6 +670,7 @@ regress-unit-binaries: regress-prep $(REGRESSLIBS) \
Petr Šabata 81d24c
 	regress/unittests/kex/test_kex$(EXEEXT) \
Petr Šabata 81d24c
 	regress/unittests/match/test_match$(EXEEXT) \
Petr Šabata 81d24c
 	regress/unittests/utf8/test_utf8$(EXEEXT) \
Petr Šabata 81d24c
+	regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \
Petr Šabata 81d24c
 	regress/misc/kexfuzz/kexfuzz$(EXEEXT)
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 tests:	file-tests t-exec interop-tests unit
Petr Šabata 81d24c
diff --git a/configure.ac b/configure.ac
Petr Šabata 81d24c
index b689db4b..98d3ce4f 100644
Petr Šabata 81d24c
--- a/configure.ac
Petr Šabata 81d24c
+++ b/configure.ac
Petr Šabata 81d24c
@@ -1911,12 +1911,14 @@ AC_LINK_IFELSE(
Petr Šabata 81d24c
 	[AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).])
Petr Šabata 81d24c
 ])
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+SCARD_MSG="yes"
Petr Šabata 81d24c
 disable_pkcs11=
Petr Šabata 81d24c
 AC_ARG_ENABLE([pkcs11],
Petr Šabata 81d24c
 	[  --disable-pkcs11        disable PKCS#11 support code [no]],
Petr Šabata 81d24c
 	[
Petr Šabata 81d24c
 		if test "x$enableval" = "xno" ; then
Petr Šabata 81d24c
 			disable_pkcs11=1
Petr Šabata 81d24c
+			SCARD_MSG="no"
Petr Šabata 81d24c
 		fi
Petr Šabata 81d24c
 	]
Petr Šabata 81d24c
 )
Petr Šabata 81d24c
@@ -1945,6 +1947,40 @@ AC_SEARCH_LIBS([dlopen], [dl])
Petr Šabata 81d24c
 AC_CHECK_FUNCS([dlopen])
Petr Šabata 81d24c
 AC_CHECK_DECL([RTLD_NOW], [], [], [#include <dlfcn.h>])
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+# Check whether we have a p11-kit, we got default provider on command line
Petr Šabata 81d24c
+DEFAULT_PKCS11_PROVIDER_MSG="no"
Petr Šabata 81d24c
+AC_ARG_WITH([default-pkcs11-provider],
Petr Šabata 81d24c
+	[  --with-default-pkcs11-provider[[=PATH]]   Use default pkcs11 provider (p11-kit detected by default)],
Petr Šabata 81d24c
+	[ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then
Petr Šabata 81d24c
+		if test "x$withval" = "xyes" ; then
Petr Šabata 81d24c
+			AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no])
Petr Šabata 81d24c
+			if test "x$PKGCONFIG" != "xno"; then
Petr Šabata 81d24c
+				AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit])
Petr Šabata 81d24c
+				if "$PKGCONFIG" "p11-kit-1"; then
Petr Šabata 81d24c
+					AC_MSG_RESULT([yes])
Petr Šabata 81d24c
+					use_pkgconfig_for_p11kit=yes
Petr Šabata 81d24c
+				else
Petr Šabata 81d24c
+					AC_MSG_RESULT([no])
Petr Šabata 81d24c
+				fi
Petr Šabata 81d24c
+			fi
Petr Šabata 81d24c
+		else
Petr Šabata 81d24c
+			PKCS11_PATH="${withval}"
Petr Šabata 81d24c
+		fi
Petr Šabata 81d24c
+		if test "x$use_pkgconfig_for_p11kit" = "xyes"; then
Petr Šabata 81d24c
+			PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1`
Petr Šabata 81d24c
+		fi
Petr Šabata 81d24c
+		AC_CHECK_FILE("$PKCS11_PATH",
Petr Šabata 81d24c
+			[ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)])
Petr Šabata 81d24c
+			  DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH"
Petr Šabata 81d24c
+			],
Petr Šabata 81d24c
+			[ AC_MSG_ERROR([Requested PKCS11 provided not found]) ]
Petr Šabata 81d24c
+		)
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider])
Petr Šabata 81d24c
+	fi ]
Petr Šabata 81d24c
+)
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 # IRIX has a const char return value for gai_strerror()
Petr Šabata 81d24c
 AC_CHECK_FUNCS([gai_strerror], [
Petr Šabata 81d24c
 	AC_DEFINE([HAVE_GAI_STRERROR])
Petr Šabata 81d24c
@@ -5401,6 +5437,7 @@ echo "                  BSD Auth support: $BSD_AUTH_MSG"
Petr Šabata 81d24c
 echo "              Random number source: $RAND_MSG"
Petr Šabata 81d24c
 echo "             Privsep sandbox style: $SANDBOX_STYLE"
Petr Šabata 81d24c
 echo "                   PKCS#11 support: $enable_pkcs11"
Petr Šabata 81d24c
+echo "          Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG"
Petr Šabata 81d24c
 echo "                  U2F/FIDO support: $enable_sk"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 echo ""
Petr Šabata 81d24c
diff --git a/regress/Makefile b/regress/Makefile
Petr Šabata 81d24c
index 774c10d4..6bf3b627 100644
Petr Šabata 81d24c
--- a/regress/Makefile
Petr Šabata 81d24c
+++ b/regress/Makefile
Petr Šabata 81d24c
@@ -116,7 +116,8 @@ CLEANFILES=	*.core actual agent-key.* authorized_keys_${USERNAME} \
Petr Šabata 81d24c
 		known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \
Petr Šabata 81d24c
 		modpipe netcat no_identity_config \
Petr Šabata 81d24c
 		pidfile putty.rsa2 ready regress.log remote_pid \
Petr Šabata 81d24c
-		revoked-* rsa rsa-agent rsa-agent.pub rsa.pub rsa_ssh2_cr.prv \
Petr Šabata 81d24c
+		revoked-* rsa rsa-agent rsa-agent.pub rsa-agent-cert.pub \
Petr Šabata 81d24c
+		rsa.pub rsa_ssh2_cr.prv pkcs11*.crt pkcs11*.key pkcs11.info \
Petr Šabata 81d24c
 		rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \
Petr Šabata 81d24c
 		scp-ssh-wrapper.scp setuid-allowed sftp-server.log \
Petr Šabata 81d24c
 		sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \
Petr Šabata 81d24c
@@ -246,6 +247,7 @@ unit:
Petr Šabata 81d24c
 		V="" ; \
Petr Šabata 81d24c
 		test "x${USE_VALGRIND}" = "x" || \
Petr Šabata 81d24c
 		    V=${.CURDIR}/valgrind-unit.sh ; \
Petr Šabata 81d24c
+		$$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \
Petr Šabata 81d24c
 		$$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \
Petr Šabata 81d24c
 		$$V ${.OBJDIR}/unittests/sshkey/test_sshkey \
Petr Šabata 81d24c
 			-d ${.CURDIR}/unittests/sshkey/testdata ; \
Petr Šabata 81d24c
diff --git a/regress/agent-pkcs11.sh b/regress/agent-pkcs11.sh
Petr Šabata 81d24c
index fbbaea51..5d75d69f 100644
Petr Šabata 81d24c
--- a/regress/agent-pkcs11.sh
Petr Šabata 81d24c
+++ b/regress/agent-pkcs11.sh
Petr Šabata 81d24c
@@ -113,7 +113,7 @@ else
Petr Šabata 81d24c
 	done
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	trace "remove pkcs11 keys"
Petr Šabata 81d24c
-	echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1
Petr Šabata 81d24c
+	${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1
Petr Šabata 81d24c
 	r=$?
Petr Šabata 81d24c
 	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
 		fail "ssh-add -e failed: exit code $r"
Petr Šabata 81d24c
diff --git a/regress/pkcs11.sh b/regress/pkcs11.sh
Petr Šabata 81d24c
new file mode 100644
Petr Šabata 81d24c
index 00000000..a91aee94
Petr Šabata 81d24c
--- /dev/null
Petr Šabata 81d24c
+++ b/regress/pkcs11.sh
Petr Šabata 81d24c
@@ -0,0 +1,349 @@
Petr Šabata 81d24c
+#
Petr Šabata 81d24c
+#  Copyright (c) 2017 Red Hat
Petr Šabata 81d24c
+#
Petr Šabata 81d24c
+#  Authors: Jakub Jelen <jjelen@redhat.com>
Petr Šabata 81d24c
+#
Petr Šabata 81d24c
+#  Permission to use, copy, modify, and distribute this software for any
Petr Šabata 81d24c
+#  purpose with or without fee is hereby granted, provided that the above
Petr Šabata 81d24c
+#  copyright notice and this permission notice appear in all copies.
Petr Šabata 81d24c
+#
Petr Šabata 81d24c
+#  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Petr Šabata 81d24c
+#  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Petr Šabata 81d24c
+#  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Petr Šabata 81d24c
+#  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Petr Šabata 81d24c
+#  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Petr Šabata 81d24c
+#  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Petr Šabata 81d24c
+#  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+tid="pkcs11 tests with soft token"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+try_token_libs() {
Petr Šabata 81d24c
+	for _lib in "$@" ; do
Petr Šabata 81d24c
+		if test -f "$_lib" ; then
Petr Šabata 81d24c
+			verbose "Using token library $_lib"
Petr Šabata 81d24c
+			TEST_SSH_PKCS11="$_lib"
Petr Šabata 81d24c
+			return
Petr Šabata 81d24c
+		fi
Petr Šabata 81d24c
+	done
Petr Šabata 81d24c
+	echo "skipped: Unable to find PKCS#11 token library"
Petr Šabata 81d24c
+	exit 0
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+try_token_libs \
Petr Šabata 81d24c
+	/usr/local/lib/softhsm/libsofthsm2.so \
Petr Šabata 81d24c
+	/usr/lib64/pkcs11/libsofthsm2.so \
Petr Šabata 81d24c
+	/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+TEST_SSH_PIN=1234
Petr Šabata 81d24c
+TEST_SSH_SOPIN=12345678
Petr Šabata 81d24c
+if [ "x$TEST_SSH_SSHPKCS11HELPER" != "x" ]; then
Petr Šabata 81d24c
+	SSH_PKCS11_HELPER="${TEST_SSH_SSHPKCS11HELPER}"
Petr Šabata 81d24c
+	export SSH_PKCS11_HELPER
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# setup environment for softhsm token
Petr Šabata 81d24c
+DIR=$OBJ/SOFTHSM
Petr Šabata 81d24c
+rm -rf $DIR
Petr Šabata 81d24c
+TOKEN=$DIR/tokendir
Petr Šabata 81d24c
+mkdir -p $TOKEN
Petr Šabata 81d24c
+SOFTHSM2_CONF=$DIR/softhsm2.conf
Petr Šabata 81d24c
+export SOFTHSM2_CONF
Petr Šabata 81d24c
+cat > $SOFTHSM2_CONF << EOF
Petr Šabata 81d24c
+# SoftHSM v2 configuration file
Petr Šabata 81d24c
+directories.tokendir = ${TOKEN}
Petr Šabata 81d24c
+objectstore.backend = file
Petr Šabata 81d24c
+# ERROR, WARNING, INFO, DEBUG
Petr Šabata 81d24c
+log.level = DEBUG
Petr Šabata 81d24c
+# If CKF_REMOVABLE_DEVICE flag should be set
Petr Šabata 81d24c
+slots.removable = false
Petr Šabata 81d24c
+EOF
Petr Šabata 81d24c
+out=$(softhsm2-util --init-token --free --label token-slot-0 --pin "$TEST_SSH_PIN" --so-pin "$TEST_SSH_SOPIN")
Petr Šabata 81d24c
+slot=$(echo -- $out | sed 's/.* //')
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# prevent ssh-agent from calling ssh-askpass
Petr Šabata 81d24c
+SSH_ASKPASS=/usr/bin/true
Petr Šabata 81d24c
+export SSH_ASKPASS
Petr Šabata 81d24c
+unset DISPLAY
Petr Šabata 81d24c
+# We need interactive access to test PKCS# since it prompts for PIN
Petr Šabata 81d24c
+sed -i 's/.*BatchMode.*//g' $OBJ/ssh_proxy
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh)
Petr Šabata 81d24c
+notty() {
Petr Šabata 81d24c
+	perl -e 'use POSIX; POSIX::setsid();
Petr Šabata 81d24c
+	    if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@"
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "generating keys"
Petr Šabata 81d24c
+ID1="02"
Petr Šabata 81d24c
+ID2="04"
Petr Šabata 81d24c
+RSA=${DIR}/RSA
Petr Šabata 81d24c
+EC=${DIR}/EC
Petr Šabata 81d24c
+openssl genpkey -algorithm rsa > $RSA
Petr Šabata 81d24c
+openssl pkcs8 -nocrypt -in $RSA |\
Petr Šabata 81d24c
+    softhsm2-util --slot "$slot" --label "SSH RSA Key $ID1" --id $ID1 \
Petr Šabata 81d24c
+	--pin "$TEST_SSH_PIN" --import /dev/stdin
Petr Šabata 81d24c
+openssl genpkey \
Petr Šabata 81d24c
+    -genparam \
Petr Šabata 81d24c
+    -algorithm ec \
Petr Šabata 81d24c
+    -pkeyopt ec_paramgen_curve:prime256v1 |\
Petr Šabata 81d24c
+    openssl genpkey \
Petr Šabata 81d24c
+    -paramfile /dev/stdin > $EC
Petr Šabata 81d24c
+openssl pkcs8 -nocrypt -in $EC |\
Petr Šabata 81d24c
+    softhsm2-util --slot "$slot" --label "SSH ECDSA Key $ID2" --id $ID2 \
Petr Šabata 81d24c
+	--pin "$TEST_SSH_PIN" --import /dev/stdin
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "List the keys in the ssh-keygen with PKCS#11 URIs"
Petr Šabata 81d24c
+${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys
Petr Šabata 81d24c
+if [ $? -ne 0 ]; then
Petr Šabata 81d24c
+	fail "FAIL: keygen fails to enumerate keys on PKCS#11 token"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+grep "pkcs11:" $OBJ/token_keys > /dev/null
Petr Šabata 81d24c
+if [ $? -ne 0 ]; then
Petr Šabata 81d24c
+	fail "FAIL: The keys from ssh-keygen do not contain PKCS#11 URI as a comment"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# Set the ECDSA key to authorized keys
Petr Šabata 81d24c
+grep "ECDSA" $OBJ/token_keys > $OBJ/authorized_keys_$USER
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Simple connect with ssh (without PKCS#11 URI)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \
Petr Šabata 81d24c
+    -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with pkcs11 failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Connect with PKCS#11 URI"
Petr Šabata 81d24c
+trace "  (ECDSA key should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -i "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (RSA key should fail)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+     -i "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Connect with PKCS#11 URI including PIN should not prompt"
Petr Šabata 81d24c
+trace "  (ECDSA key should succeed)"
Petr Šabata 81d24c
+${SSH} -F $OBJ/ssh_proxy -i \
Petr Šabata 81d24c
+    "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (RSA key should fail)"
Petr Šabata 81d24c
+${SSH} -F $OBJ/ssh_proxy -i \
Petr Šabata 81d24c
+    "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Connect with various filtering options in PKCS#11 URI"
Petr Šabata 81d24c
+trace "  (by object label, ECDSA should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -i "pkcs11:object=SSH%20ECDSA%20Key%2004?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (by object label, RSA key should fail)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+     -i "pkcs11:object=SSH%20RSA%20Key%2002?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (by token label, ECDSA key should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -i "pkcs11:id=%${ID2};token=token-slot-0?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (by wrong token label, should fail)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+     -i "pkcs11:token=token-slot-99?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Test PKCS#11 URI specification in configuration files"
Petr Šabata 81d24c
+echo "IdentityFile \"pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}\"" \
Petr Šabata 81d24c
+    >> $OBJ/ssh_proxy
Petr Šabata 81d24c
+trace "  (ECDSA key should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI in config failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# Set the RSA key as authorized
Petr Šabata 81d24c
+grep "RSA" $OBJ/token_keys > $OBJ/authorized_keys_$USER
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (RSA key should fail)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI in config succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Test PKCS#11 URI specification in configuration files with bogus spaces"
Petr Šabata 81d24c
+echo "IdentityFile \"    pkcs11:?module-path=${TEST_SSH_PKCS11}    \"" \
Petr Šabata 81d24c
+    >> $OBJ/ssh_proxy
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI with bogus spaces in config failed" \
Petr Šabata 81d24c
+	    "(exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Combination of PKCS11Provider and PKCS11URI on commandline"
Petr Šabata 81d24c
+trace "  (RSA key should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -i "pkcs11:id=%${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI and provider combination" \
Petr Šabata 81d24c
+	    "failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Regress: Missing provider in PKCS11URI option"
Petr Šabata 81d24c
+${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 139 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with missing provider_id from configuration option" \
Petr Šabata 81d24c
+	    "crashed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "SSH Agent can work with PKCS#11 URI"
Petr Šabata 81d24c
+trace "start the agent"
Petr Šabata 81d24c
+eval `${SSHAGENT} -s` >  /dev/null
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+	fail "could not start ssh-agent: exit code $r"
Petr Šabata 81d24c
+else
Petr Šabata 81d24c
+	trace "add whole provider to agent"
Petr Šabata 81d24c
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Petr Šabata 81d24c
+	    "pkcs11:?module-path=${TEST_SSH_PKCS11}" #> /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add failed with whole provider: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 list via agent (all keys)"
Petr Šabata 81d24c
+	${SSHADD} -l > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add -l failed with whole provider: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 connect via agent (all keys)"
Petr Šabata 81d24c
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh connect failed with whole provider (exit code $r)"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " remove pkcs11 keys (all keys)"
Petr Šabata 81d24c
+	${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add -d failed with whole provider: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace "add only RSA key to the agent"
Petr Šabata 81d24c
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Petr Šabata 81d24c
+	    "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL ssh-add failed with RSA key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 connect via agent (RSA key)"
Petr Šabata 81d24c
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh connect failed with RSA key (exit code $r)"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " remove RSA pkcs11 key"
Petr Šabata 81d24c
+	${SSHADD} -d "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" \
Petr Šabata 81d24c
+	    > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add -d failed with RSA key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace "add only ECDSA key to the agent"
Petr Šabata 81d24c
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Petr Šabata 81d24c
+	    "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add failed with second key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 connect via agent (ECDSA key should fail)"
Petr Šabata 81d24c
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh connect passed with ECDSA key (should fail)"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace "add also the RSA key to the agent"
Petr Šabata 81d24c
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Petr Šabata 81d24c
+	    "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add failed with first key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " remove ECDSA pkcs11 key"
Petr Šabata 81d24c
+	${SSHADD} -d "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" \
Petr Šabata 81d24c
+	    > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "ssh-add -d failed with ECDSA key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " remove already-removed pkcs11 key should fail"
Petr Šabata 81d24c
+	${SSHADD} -d "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" \
Petr Šabata 81d24c
+	    > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -eq 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add -d passed with non-existing key (should fail)"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 connect via agent (the RSA key should be still usable)"
Petr Šabata 81d24c
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+		fail "ssh connect failed with RSA key (after removing ECDSA): exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace "kill agent"
Petr Šabata 81d24c
+	${SSHAGENT} -k > /dev/null
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
diff --git a/regress/unittests/Makefile b/regress/unittests/Makefile
Petr Šabata 81d24c
index 4e56e110..2690ebeb 100644
Petr Šabata 81d24c
--- a/regress/unittests/Makefile
Petr Šabata 81d24c
+++ b/regress/unittests/Makefile
Petr Šabata 81d24c
@@ -2,6 +2,6 @@
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 REGRESS_FAIL_EARLY?=	yes
Petr Šabata 81d24c
 SUBDIR=	test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion
Petr Šabata 81d24c
-SUBDIR+=authopt misc sshsig
Petr Šabata 81d24c
+SUBDIR+=authopt misc sshsig pkcs11
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 .include <bsd.subdir.mk>
Petr Šabata 81d24c
diff --git a/regress/unittests/pkcs11/tests.c b/regress/unittests/pkcs11/tests.c
Petr Šabata 81d24c
new file mode 100644
Petr Šabata 81d24c
index 00000000..b637cb13
Petr Šabata 81d24c
--- /dev/null
Petr Šabata 81d24c
+++ b/regress/unittests/pkcs11/tests.c
Petr Šabata 81d24c
@@ -0,0 +1,337 @@
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * Copyright (c) 2017 Red Hat
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Authors: Jakub Jelen <jjelen@redhat.com>
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Permission to use, copy, modify, and distribute this software for any
Petr Šabata 81d24c
+ * purpose with or without fee is hereby granted, provided that the above
Petr Šabata 81d24c
+ * copyright notice and this permission notice appear in all copies.
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Petr Šabata 81d24c
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Petr Šabata 81d24c
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Petr Šabata 81d24c
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Petr Šabata 81d24c
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Petr Šabata 81d24c
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Petr Šabata 81d24c
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "includes.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include <locale.h>
Petr Šabata 81d24c
+#include <string.h>
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "../test_helper/test_helper.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "sshbuf.h"
Petr Šabata 81d24c
+#include "ssh-pkcs11-uri.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL)
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/* prototypes are not public -- specify them here internally for tests */
Petr Šabata 81d24c
+struct sshbuf *percent_encode(const char *, size_t, char *);
Petr Šabata 81d24c
+int percent_decode(char *, char **);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+compare_uri(struct pkcs11_uri *a, struct pkcs11_uri *b)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	ASSERT_PTR_NE(a, NULL);
Petr Šabata 81d24c
+	ASSERT_PTR_NE(b, NULL);
Petr Šabata 81d24c
+	ASSERT_SIZE_T_EQ(a->id_len, b->id_len);
Petr Šabata 81d24c
+	ASSERT_MEM_EQ(a->id, b->id, a->id_len);
Petr Šabata 81d24c
+	if (b->object != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->object, b->object);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->object, b->object);
Petr Šabata 81d24c
+	if (b->module_path != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->module_path, b->module_path);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->module_path, b->module_path);
Petr Šabata 81d24c
+	if (b->token != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->token, b->token);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->token, b->token);
Petr Šabata 81d24c
+	if (b->manuf != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->manuf, b->manuf);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->manuf, b->manuf);
Petr Šabata 81d24c
+	if (b->lib_manuf != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->lib_manuf, b->lib_manuf);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->lib_manuf, b->lib_manuf);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_parse_rv(char *uri, struct pkcs11_uri *expect, int expect_rv)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *buf = NULL, *str;
Petr Šabata 81d24c
+	struct pkcs11_uri *pkcs11uri = NULL;
Petr Šabata 81d24c
+	int rv;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (expect_rv == 0)
Petr Šabata 81d24c
+		str = "Valid";
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		str = "Invalid";
Petr Šabata 81d24c
+	asprintf(&buf, "%s PKCS#11 URI parsing: %s", str, uri);
Petr Šabata 81d24c
+	TEST_START(buf);
Petr Šabata 81d24c
+	free(buf);
Petr Šabata 81d24c
+	pkcs11uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	rv = pkcs11_uri_parse(uri, pkcs11uri);
Petr Šabata 81d24c
+	ASSERT_INT_EQ(rv, expect_rv);
Petr Šabata 81d24c
+	if (rv == 0) /* in case of failure result is undefined */
Petr Šabata 81d24c
+		compare_uri(pkcs11uri, expect);
Petr Šabata 81d24c
+	pkcs11_uri_cleanup(pkcs11uri);
Petr Šabata 81d24c
+	free(expect);
Petr Šabata 81d24c
+	TEST_DONE();
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_parse(char *uri, struct pkcs11_uri *expect)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	check_parse_rv(uri, expect, 0);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+struct pkcs11_uri *
Petr Šabata 81d24c
+compose_uri(unsigned char *id, size_t id_len, char *token, char *lib_manuf,
Petr Šabata 81d24c
+    char *manuf, char *module_path, char *object, char *pin)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct pkcs11_uri *uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	if (id_len > 0) {
Petr Šabata 81d24c
+		uri->id_len = id_len;
Petr Šabata 81d24c
+		uri->id = id;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	uri->module_path = module_path;
Petr Šabata 81d24c
+	uri->token = token;
Petr Šabata 81d24c
+	uri->lib_manuf = lib_manuf;
Petr Šabata 81d24c
+	uri->manuf = manuf;
Petr Šabata 81d24c
+	uri->object = object;
Petr Šabata 81d24c
+	uri->pin = pin;
Petr Šabata 81d24c
+	return uri;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_parse_valid(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* path arguments */
Petr Šabata 81d24c
+	check_parse("pkcs11:id=%01",
Petr Šabata 81d24c
+	    compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:id=%00%01",
Petr Šabata 81d24c
+	    compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:token=SSH%20Keys",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:library-manufacturer=OpenSC",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:manufacturer=piv_II",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:object=SIGN%20Key",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key", NULL));
Petr Šabata 81d24c
+	/* query arguments */
Petr Šabata 81d24c
+	check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:?pin-value=123456",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, "123456"));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* combinations */
Petr Šabata 81d24c
+	/* ID SHOULD be percent encoded */
Petr Šabata 81d24c
+	check_parse("pkcs11:token=SSH%20Key;id=0",
Petr Šabata 81d24c
+	    compose_uri("0", 1, "SSH Key", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse(
Petr Šabata 81d24c
+	    "pkcs11:manufacturer=CAC?module-path=/usr/lib64/p11-kit-proxy.so",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, "CAC",
Petr Šabata 81d24c
+	    "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Petr Šabata 81d24c
+	check_parse(
Petr Šabata 81d24c
+	    "pkcs11:object=RSA%20Key?module-path=/usr/lib64/pkcs11/opencryptoki.so",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL,
Petr Šabata 81d24c
+	    "/usr/lib64/pkcs11/opencryptoki.so", "RSA Key", NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so&pin-value=123456",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, "123456"));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* empty path component matches everything */
Petr Šabata 81d24c
+	check_parse("pkcs11:", EMPTY_URI);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* empty string is a valid to match against (and different from NULL) */
Petr Šabata 81d24c
+	check_parse("pkcs11:token=",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, "", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	/* Percent character needs to be percent-encoded */
Petr Šabata 81d24c
+	check_parse("pkcs11:token=%25",
Petr Šabata 81d24c
+	     compose_uri(NULL, 0, "%", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_parse_invalid(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* Invalid percent encoding */
Petr Šabata 81d24c
+	check_parse_rv("pkcs11:id=%0", EMPTY_URI, -1);
Petr Šabata 81d24c
+	/* Invalid percent encoding */
Petr Šabata 81d24c
+	check_parse_rv("pkcs11:id=%ZZ", EMPTY_URI, -1);
Petr Šabata 81d24c
+	/* Space MUST be percent encoded -- XXX not enforced yet */
Petr Šabata 81d24c
+	check_parse("pkcs11:token=SSH Keys",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	/* MUST NOT contain duplicate attributes of the same name */
Petr Šabata 81d24c
+	check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1);
Petr Šabata 81d24c
+	/* MUST NOT contain duplicate attributes of the same name */
Petr Šabata 81d24c
+	check_parse_rv("pkcs11:?pin-value=111111&pin-value=123456", EMPTY_URI, -1);
Petr Šabata 81d24c
+	/* Unrecognized attribute in path are ignored with log message */
Petr Šabata 81d24c
+	check_parse("pkcs11:key_name=SSH", EMPTY_URI);
Petr Šabata 81d24c
+	/* Unrecognized attribute in query SHOULD be ignored */
Petr Šabata 81d24c
+	check_parse("pkcs11:?key_name=SSH", EMPTY_URI);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_gen(char *expect, struct pkcs11_uri *uri)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *buf = NULL, *uri_str;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	asprintf(&buf, "Valid PKCS#11 URI generation: %s", expect);
Petr Šabata 81d24c
+	TEST_START(buf);
Petr Šabata 81d24c
+	free(buf);
Petr Šabata 81d24c
+	uri_str = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+	ASSERT_PTR_NE(uri_str, NULL);
Petr Šabata 81d24c
+	ASSERT_STRING_EQ(uri_str, expect);
Petr Šabata 81d24c
+	free(uri_str);
Petr Šabata 81d24c
+	TEST_DONE();
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_generate_valid(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* path arguments */
Petr Šabata 81d24c
+	check_gen("pkcs11:id=%01",
Petr Šabata 81d24c
+	    compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:id=%00%01",
Petr Šabata 81d24c
+	    compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:token=SSH%20Keys", /* space must be percent encoded */
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	/* library-manufacturer is not implmented now */
Petr Šabata 81d24c
+	/*check_gen("pkcs11:library-manufacturer=OpenSC",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));*/
Petr Šabata 81d24c
+	check_gen("pkcs11:manufacturer=piv_II",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:object=RSA%20Key",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key", NULL));
Petr Šabata 81d24c
+	/* query arguments */
Petr Šabata 81d24c
+	check_gen("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* combinations */
Petr Šabata 81d24c
+	check_gen("pkcs11:id=%02;token=SSH%20Keys",
Petr Šabata 81d24c
+	    compose_uri("\x02", 1, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:id=%EE%02?module-path=/usr/lib64/p11-kit-proxy.so",
Petr Šabata 81d24c
+	    compose_uri("\xEE\x02", 2, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key", NULL));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* empty path component matches everything */
Petr Šabata 81d24c
+	check_gen("pkcs11:", EMPTY_URI);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_encode(char *source, size_t len, char *allow_list, char *expect)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *buf = NULL;
Petr Šabata 81d24c
+	struct sshbuf *b;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	asprintf(&buf, "percent_encode: expected %s", expect);
Petr Šabata 81d24c
+	TEST_START(buf);
Petr Šabata 81d24c
+	free(buf);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	b = percent_encode(source, len, allow_list);
Petr Šabata 81d24c
+	ASSERT_STRING_EQ(sshbuf_ptr(b), expect);
Petr Šabata 81d24c
+	sshbuf_free(b);
Petr Šabata 81d24c
+	TEST_DONE();
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_percent_encode_multibyte(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* SHOULD be encoded as octets according to the UTF-8 character encoding */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* multi-byte characters are "for free" */
Petr Šabata 81d24c
+	check_encode("$", 1, "", "%24");
Petr Šabata 81d24c
+	check_encode("¢", 2, "", "%C2%A2");
Petr Šabata 81d24c
+	check_encode("€", 3, "", "%E2%82%AC");
Petr Šabata 81d24c
+	check_encode("𐍈", 4, "", "%F0%90%8D%88");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* CK_UTF8CHAR is unsigned char (1 byte) */
Petr Šabata 81d24c
+	/* labels SHOULD be normalized to NFC [UAX15] */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_percent_encode(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* Without allow list encodes everything (for CKA_ID) */
Petr Šabata 81d24c
+	check_encode("A*", 2, "", "%41%2A");
Petr Šabata 81d24c
+	check_encode("\x00", 1, "", "%00");
Petr Šabata 81d24c
+	check_encode("\x7F", 1, "", "%7F");
Petr Šabata 81d24c
+	check_encode("\x80", 1, "", "%80");
Petr Šabata 81d24c
+	check_encode("\xff", 1, "", "%FF");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Default allow list encodes anything but safe letters */
Petr Šabata 81d24c
+	check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "test%000alpha");
Petr Šabata 81d24c
+	check_encode(" ", 1, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "%20"); /* Space MUST be percent encoded */
Petr Šabata 81d24c
+	check_encode("/", 1, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */
Petr Šabata 81d24c
+	check_encode("?", 1, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */
Petr Šabata 81d24c
+	check_encode("#", 1, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "%23"); /* '#' MUST be always percent encoded */
Petr Šabata 81d24c
+	check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Components in query can have '/' unencoded (useful for paths) */
Petr Šabata 81d24c
+	check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/",
Petr Šabata 81d24c
+	    "/path/to.file");
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_decode(char *source, char *expect, int expect_len)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *buf = NULL, *out = NULL;
Petr Šabata 81d24c
+	int rv;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	asprintf(&buf, "percent_decode: %s", source);
Petr Šabata 81d24c
+	TEST_START(buf);
Petr Šabata 81d24c
+	free(buf);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	rv = percent_decode(source, &out;;
Petr Šabata 81d24c
+	ASSERT_INT_EQ(rv, expect_len);
Petr Šabata 81d24c
+	if (rv >= 0)
Petr Šabata 81d24c
+		ASSERT_MEM_EQ(out, expect, expect_len);
Petr Šabata 81d24c
+	free(out);
Petr Šabata 81d24c
+	TEST_DONE();
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_percent_decode(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* simple valid cases */
Petr Šabata 81d24c
+	check_decode("%00", "\x00", 1);
Petr Šabata 81d24c
+	check_decode("%FF", "\xFF", 1);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* normal strings shold be kept intact */
Petr Šabata 81d24c
+	check_decode("strings are left", "strings are left", 16);
Petr Šabata 81d24c
+	check_decode("10%25 of trees", "10% of trees", 12);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* make sure no more than 2 bytes are parsed */
Petr Šabata 81d24c
+	check_decode("%222", "\x22" "2", 2);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* invalid expects failure */
Petr Šabata 81d24c
+	check_decode("%0", "", -1);
Petr Šabata 81d24c
+	check_decode("%Z", "", -1);
Petr Šabata 81d24c
+	check_decode("%FG", "", -1);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+tests(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	test_percent_encode();
Petr Šabata 81d24c
+	test_percent_encode_multibyte();
Petr Šabata 81d24c
+	test_percent_decode();
Petr Šabata 81d24c
+	test_parse_valid();
Petr Šabata 81d24c
+	test_parse_invalid();
Petr Šabata 81d24c
+	test_generate_valid();
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
diff --git a/ssh-add.c b/ssh-add.c
Petr Šabata 81d24c
index 8057eb1f..0c470e32 100644
Petr Šabata 81d24c
--- a/ssh-add.c
Petr Šabata 81d24c
+++ b/ssh-add.c
Petr Šabata 81d24c
@@ -67,6 +67,7 @@
Petr Šabata 81d24c
 #include "digest.h"
Petr Šabata 81d24c
 #include "ssh-sk.h"
Petr Šabata 81d24c
 #include "sk-api.h"
Petr Šabata 81d24c
+#include "ssh-pkcs11-uri.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /* argv0 */
Petr Šabata 81d24c
 extern char *__progname;
Petr Šabata 81d24c
@@ -193,6 +194,32 @@ delete_all(int agent_fd, int qflag)
Petr Šabata 81d24c
 	return ret;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+static int update_card(int, int, const char *, int, char *);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri, int qflag)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *pin = NULL;
Petr Šabata 81d24c
+	struct pkcs11_uri *uri;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* dry-run parse to make sure the URI is valid and to report errors */
Petr Šabata 81d24c
+	uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0)
Petr Šabata 81d24c
+		fatal("Failed to parse PKCS#11 URI");
Petr Šabata 81d24c
+	if (uri->pin != NULL) {
Petr Šabata 81d24c
+		pin = strdup(uri->pin);
Petr Šabata 81d24c
+		if (pin == NULL) {
Petr Šabata 81d24c
+			fatal("Failed to dupplicate string");
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		/* pin is freed in the update_card() */
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return update_card(agent_fd, adding, pkcs11_uri, qflag, pin);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 add_file(int agent_fd, const char *filename, int key_only, int qflag,
Petr Šabata 81d24c
     const char *skprovider)
Petr Šabata 81d24c
@@ -402,12 +429,11 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
-update_card(int agent_fd, int add, const char *id, int qflag)
Petr Šabata 81d24c
+update_card(int agent_fd, int add, const char *id, int qflag, char *pin)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	char *pin = NULL;
Petr Šabata 81d24c
 	int r, ret = -1;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (add) {
Petr Šabata 81d24c
+	if (add && pin == NULL) {
Petr Šabata 81d24c
 		if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
Petr Šabata 81d24c
 		    RP_ALLOW_STDIN)) == NULL)
Petr Šabata 81d24c
 			return -1;
Petr Šabata 81d24c
@@ -591,6 +617,13 @@ static int
Petr Šabata 81d24c
 do_file(int agent_fd, int deleting, int key_only, char *file, int qflag,
Petr Šabata 81d24c
     const char *skprovider)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
+#ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+	if (strlen(file) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+	    strncmp(file, PKCS11_URI_SCHEME,
Petr Šabata 81d24c
+	    strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+		return update_pkcs11_uri(agent_fd, !deleting, file, qflag);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 	if (deleting) {
Petr Šabata 81d24c
 		if (delete_file(agent_fd, file, key_only, qflag) == -1)
Petr Šabata 81d24c
 			return -1;
Petr Šabata 81d24c
@@ -773,7 +806,7 @@ main(int argc, char **argv)
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	if (pkcs11provider != NULL) {
Petr Šabata 81d24c
 		if (update_card(agent_fd, !deleting, pkcs11provider,
Petr Šabata 81d24c
-		    qflag) == -1)
Petr Šabata 81d24c
+		    qflag, NULL) == -1)
Petr Šabata 81d24c
 			ret = 1;
Petr Šabata 81d24c
 		goto done;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
diff --git a/ssh-agent.c b/ssh-agent.c
Petr Šabata 81d24c
index 7eb6f0dc..27d8e4af 100644
Petr Šabata 81d24c
--- a/ssh-agent.c
Petr Šabata 81d24c
+++ b/ssh-agent.c
Petr Šabata 81d24c
@@ -641,10 +641,72 @@ no_identities(SocketEntry *e)
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+static char *
Petr Šabata 81d24c
+sanitize_pkcs11_provider(const char *provider)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct pkcs11_uri *uri = NULL;
Petr Šabata 81d24c
+	char *sane_uri, *module_path = NULL; /* default path */
Petr Šabata 81d24c
+	char canonical_provider[PATH_MAX];
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (provider == NULL)
Petr Šabata 81d24c
+		return NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (strlen(provider) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+	    strncmp(provider, PKCS11_URI_SCHEME,
Petr Šabata 81d24c
+	    strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+		/* PKCS#11 URI */
Petr Šabata 81d24c
+		uri = pkcs11_uri_init();
Petr Šabata 81d24c
+		if (uri == NULL) {
Petr Šabata 81d24c
+			error("Failed to init PKCS#11 URI");
Petr Šabata 81d24c
+			return NULL;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (pkcs11_uri_parse(provider, uri) != 0) {
Petr Šabata 81d24c
+			error("Failed to parse PKCS#11 URI");
Petr Šabata 81d24c
+			return NULL;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		/* validate also provider from URI */
Petr Šabata 81d24c
+		if (uri->module_path)
Petr Šabata 81d24c
+			module_path = strdup(uri->module_path);
Petr Šabata 81d24c
+	} else
Petr Šabata 81d24c
+		module_path = strdup(provider); /* simple path */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (module_path != NULL) { /* do not validate default NULL path in URI */
Petr Šabata 81d24c
+		if (realpath(module_path, canonical_provider) == NULL) {
Petr Šabata 81d24c
+			verbose("failed PKCS#11 provider \"%.100s\": realpath: %s",
Petr Šabata 81d24c
+			    module_path, strerror(errno));
Petr Šabata 81d24c
+			free(module_path);
Petr Šabata 81d24c
+			pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+			return NULL;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		free(module_path);
Petr Šabata 81d24c
+		if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) {
Petr Šabata 81d24c
+			verbose("refusing PKCS#11 provider \"%.100s\": "
Petr Šabata 81d24c
+			    "not allowed", canonical_provider);
Petr Šabata 81d24c
+			pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+			return NULL;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		/* copy verified and sanitized provider path back to the uri */
Petr Šabata 81d24c
+		if (uri) {
Petr Šabata 81d24c
+			free(uri->module_path);
Petr Šabata 81d24c
+			uri->module_path = xstrdup(canonical_provider);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (uri) {
Petr Šabata 81d24c
+		sane_uri = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+		pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+		return sane_uri;
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		return xstrdup(canonical_provider); /* simple path */
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
 process_add_smartcard_key(SocketEntry *e)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
Petr Šabata 81d24c
+	char *provider = NULL, *pin = NULL, *sane_uri = NULL;
Petr Šabata 81d24c
 	char **comments = NULL;
Petr Šabata 81d24c
 	int r, i, count = 0, success = 0, confirm = 0;
Petr Šabata 81d24c
 	u_int seconds;
Petr Šabata 81d24c
@@ -681,33 +743,28 @@ process_add_smartcard_key(SocketEntry *e)
Petr Šabata 81d24c
 			goto send;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if (realpath(provider, canonical_provider) == NULL) {
Petr Šabata 81d24c
-		verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
Petr Šabata 81d24c
-		    provider, strerror(errno));
Petr Šabata 81d24c
-		goto send;
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
-	if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) {
Petr Šabata 81d24c
-		verbose("refusing PKCS#11 add of \"%.100s\": "
Petr Šabata 81d24c
-		    "provider not allowed", canonical_provider);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	sane_uri = sanitize_pkcs11_provider(provider);
Petr Šabata 81d24c
+	if (sane_uri == NULL)
Petr Šabata 81d24c
 		goto send;
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
-	debug("%s: add %.100s", __func__, canonical_provider);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	if (lifetime && !death)
Petr Šabata 81d24c
 		death = monotime() + lifetime;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments);
Petr Šabata 81d24c
+	debug("%s: add %.100s", __func__, sane_uri);
Petr Šabata 81d24c
+	count = pkcs11_add_provider(sane_uri, pin, &keys, &comments);
Petr Šabata 81d24c
 	for (i = 0; i < count; i++) {
Petr Šabata 81d24c
 		k = keys[i];
Petr Šabata 81d24c
 		if (lookup_identity(k) == NULL) {
Petr Šabata 81d24c
 			id = xcalloc(1, sizeof(Identity));
Petr Šabata 81d24c
 			id->key = k;
Petr Šabata 81d24c
 			keys[i] = NULL; /* transferred */
Petr Šabata 81d24c
-			id->provider = xstrdup(canonical_provider);
Petr Šabata 81d24c
+			id->provider = xstrdup(sane_uri);
Petr Šabata 81d24c
 			if (*comments[i] != '\0') {
Petr Šabata 81d24c
 				id->comment = comments[i];
Petr Šabata 81d24c
 				comments[i] = NULL; /* transferred */
Petr Šabata 81d24c
 			} else {
Petr Šabata 81d24c
-				id->comment = xstrdup(canonical_provider);
Petr Šabata 81d24c
+				id->comment = xstrdup(sane_uri);
Petr Šabata 81d24c
 			}
Petr Šabata 81d24c
 			id->death = death;
Petr Šabata 81d24c
 			id->confirm = confirm;
Petr Šabata 81d24c
@@ -721,6 +778,7 @@ process_add_smartcard_key(SocketEntry *e)
Petr Šabata 81d24c
 send:
Petr Šabata 81d24c
 	free(pin);
Petr Šabata 81d24c
 	free(provider);
Petr Šabata 81d24c
+	free(sane_uri);
Petr Šabata 81d24c
 	free(keys);
Petr Šabata 81d24c
 	free(comments);
Petr Šabata 81d24c
 	send_status(e, success);
Petr Šabata 81d24c
@@ -729,7 +787,7 @@ send:
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
 process_remove_smartcard_key(SocketEntry *e)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
Petr Šabata 81d24c
+	char *provider = NULL, *pin = NULL, *sane_uri = NULL;
Petr Šabata 81d24c
 	int r, success = 0;
Petr Šabata 81d24c
 	Identity *id, *nxt;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -740,30 +798,29 @@ process_remove_smartcard_key(SocketEntry *e)
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	free(pin);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (realpath(provider, canonical_provider) == NULL) {
Petr Šabata 81d24c
-		verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
Petr Šabata 81d24c
-		    provider, strerror(errno));
Petr Šabata 81d24c
+	sane_uri = sanitize_pkcs11_provider(provider);
Petr Šabata 81d24c
+	if (sane_uri == NULL)
Petr Šabata 81d24c
 		goto send;
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	debug("%s: remove %.100s", __func__, canonical_provider);
Petr Šabata 81d24c
+	debug("%s: remove %.100s", __func__, sane_uri);
Petr Šabata 81d24c
 	for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
Petr Šabata 81d24c
 		nxt = TAILQ_NEXT(id, next);
Petr Šabata 81d24c
 		/* Skip file--based keys */
Petr Šabata 81d24c
 		if (id->provider == NULL)
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
-		if (!strcmp(canonical_provider, id->provider)) {
Petr Šabata 81d24c
+		if (!strcmp(sane_uri, id->provider)) {
Petr Šabata 81d24c
 			TAILQ_REMOVE(&idtab->idlist, id, next);
Petr Šabata 81d24c
 			free_identity(id);
Petr Šabata 81d24c
 			idtab->nentries--;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if (pkcs11_del_provider(canonical_provider) == 0)
Petr Šabata 81d24c
+	if (pkcs11_del_provider(sane_uri) == 0)
Petr Šabata 81d24c
 		success = 1;
Petr Šabata 81d24c
 	else
Petr Šabata 81d24c
 		error("%s: pkcs11_del_provider failed", __func__);
Petr Šabata 81d24c
 send:
Petr Šabata 81d24c
 	free(provider);
Petr Šabata 81d24c
+	free(sane_uri);
Petr Šabata 81d24c
 	send_status(e, success);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 #endif /* ENABLE_PKCS11 */
Petr Šabata 81d24c
diff --git a/ssh-keygen.c b/ssh-keygen.c
Petr Šabata 81d24c
index 0d6ed1ff..182f4f2b 100644
Petr Šabata 81d24c
--- a/ssh-keygen.c
Petr Šabata 81d24c
+++ b/ssh-keygen.c
Petr Šabata 81d24c
@@ -855,8 +855,11 @@ do_download(struct passwd *pw)
Petr Šabata 81d24c
 			free(fp);
Petr Šabata 81d24c
 		} else {
Petr Šabata 81d24c
 			(void) sshkey_write(keys[i], stdout); /* XXX check */
Petr Šabata 81d24c
-			fprintf(stdout, "%s%s\n",
Petr Šabata 81d24c
-			    *(comments[i]) == '\0' ? "" : " ", comments[i]);
Petr Šabata 81d24c
+			if (*(comments[i]) != '\0') {
Petr Šabata 81d24c
+				fprintf(stdout, " %s", comments[i]);
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			(void) pkcs11_uri_write(keys[i], stdout);
Petr Šabata 81d24c
+			fprintf(stdout, "\n");
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 		free(comments[i]);
Petr Šabata 81d24c
 		sshkey_free(keys[i]);
Petr Šabata 81d24c
diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c
Petr Šabata 81d24c
index 8a0ffef5..ead8a562 100644
Petr Šabata 81d24c
--- a/ssh-pkcs11-client.c
Petr Šabata 81d24c
+++ b/ssh-pkcs11-client.c
Petr Šabata 81d24c
@@ -323,6 +323,8 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp,
Petr Šabata 81d24c
 	u_int nkeys, i;
Petr Šabata 81d24c
 	struct sshbuf *msg;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	debug("%s: called, name = %s", __func__, name);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	if (fd < 0 && pkcs11_start_helper() < 0)
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -342,6 +344,7 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp,
Petr Šabata 81d24c
 		*keysp = xcalloc(nkeys, sizeof(struct sshkey *));
Petr Šabata 81d24c
 		if (labelsp)
Petr Šabata 81d24c
 			*labelsp = xcalloc(nkeys, sizeof(char *));
Petr Šabata 81d24c
+		debug("%s: nkeys = %u", __func__, nkeys);
Petr Šabata 81d24c
 		for (i = 0; i < nkeys; i++) {
Petr Šabata 81d24c
 			/* XXX clean up properly instead of fatal() */
Petr Šabata 81d24c
 			if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 ||
Petr Šabata 81d24c
diff --git a/ssh-pkcs11-uri.c b/ssh-pkcs11-uri.c
Petr Šabata 81d24c
new file mode 100644
Petr Šabata 81d24c
index 00000000..e1a7b4e0
Petr Šabata 81d24c
--- /dev/null
Petr Šabata 81d24c
+++ b/ssh-pkcs11-uri.c
Petr Šabata 81d24c
@@ -0,0 +1,425 @@
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * Copyright (c) 2017 Red Hat
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Authors: Jakub Jelen <jjelen@redhat.com>
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Permission to use, copy, modify, and distribute this software for any
Petr Šabata 81d24c
+ * purpose with or without fee is hereby granted, provided that the above
Petr Šabata 81d24c
+ * copyright notice and this permission notice appear in all copies.
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Petr Šabata 81d24c
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Petr Šabata 81d24c
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Petr Šabata 81d24c
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Petr Šabata 81d24c
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Petr Šabata 81d24c
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Petr Šabata 81d24c
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "includes.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include <stdio.h>
Petr Šabata 81d24c
+#include <string.h>
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "sshkey.h"
Petr Šabata 81d24c
+#include "sshbuf.h"
Petr Šabata 81d24c
+#include "log.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#define CRYPTOKI_COMPAT
Petr Šabata 81d24c
+#include "pkcs11.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "ssh-pkcs11-uri.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#define PKCS11_URI_PATH_SEPARATOR ";"
Petr Šabata 81d24c
+#define PKCS11_URI_QUERY_SEPARATOR "&"
Petr Šabata 81d24c
+#define PKCS11_URI_VALUE_SEPARATOR "="
Petr Šabata 81d24c
+#define PKCS11_URI_ID "id"
Petr Šabata 81d24c
+#define PKCS11_URI_TOKEN "token"
Petr Šabata 81d24c
+#define PKCS11_URI_OBJECT "object"
Petr Šabata 81d24c
+#define PKCS11_URI_LIB_MANUF "library-manufacturer"
Petr Šabata 81d24c
+#define PKCS11_URI_MANUF "manufacturer"
Petr Šabata 81d24c
+#define PKCS11_URI_MODULE_PATH "module-path"
Petr Šabata 81d24c
+#define PKCS11_URI_PIN_VALUE "pin-value"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/* Keyword tokens. */
Petr Šabata 81d24c
+typedef enum {
Petr Šabata 81d24c
+	pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath,
Petr Šabata 81d24c
+	pPinValue, pBadOption
Petr Šabata 81d24c
+} pkcs11uriOpCodes;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/* Textual representation of the tokens. */
Petr Šabata 81d24c
+static struct {
Petr Šabata 81d24c
+	const char *name;
Petr Šabata 81d24c
+	pkcs11uriOpCodes opcode;
Petr Šabata 81d24c
+} keywords[] = {
Petr Šabata 81d24c
+	{ PKCS11_URI_ID, pId },
Petr Šabata 81d24c
+	{ PKCS11_URI_TOKEN, pToken },
Petr Šabata 81d24c
+	{ PKCS11_URI_OBJECT, pObject },
Petr Šabata 81d24c
+	{ PKCS11_URI_LIB_MANUF, pLibraryManufacturer },
Petr Šabata 81d24c
+	{ PKCS11_URI_MANUF, pManufacturer },
Petr Šabata 81d24c
+	{ PKCS11_URI_MODULE_PATH, pModulePath },
Petr Šabata 81d24c
+	{ PKCS11_URI_PIN_VALUE, pPinValue },
Petr Šabata 81d24c
+	{ NULL, pBadOption }
Petr Šabata 81d24c
+};
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static pkcs11uriOpCodes
Petr Šabata 81d24c
+parse_token(const char *cp)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	u_int i;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	for (i = 0; keywords[i].name; i++)
Petr Šabata 81d24c
+		if (strncasecmp(cp, keywords[i].name,
Petr Šabata 81d24c
+		    strlen(keywords[i].name)) == 0)
Petr Šabata 81d24c
+			return keywords[i].opcode;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return pBadOption;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+percent_decode(char *data, char **outp)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char tmp[3];
Petr Šabata 81d24c
+	char *out, *tmp_end;
Petr Šabata 81d24c
+	char *p = data;
Petr Šabata 81d24c
+	long value;
Petr Šabata 81d24c
+	size_t outlen = 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	out = malloc(strlen(data)+1); /* upper bound */
Petr Šabata 81d24c
+	if (out == NULL)
Petr Šabata 81d24c
+		return -1;
Petr Šabata 81d24c
+	while (*p != '\0') {
Petr Šabata 81d24c
+		switch (*p) {
Petr Šabata 81d24c
+		case '%':
Petr Šabata 81d24c
+			p++;
Petr Šabata 81d24c
+			if (*p == '\0')
Petr Šabata 81d24c
+				goto fail;
Petr Šabata 81d24c
+			tmp[0] = *p++;
Petr Šabata 81d24c
+			if (*p == '\0')
Petr Šabata 81d24c
+				goto fail;
Petr Šabata 81d24c
+			tmp[1] = *p++;
Petr Šabata 81d24c
+			tmp[2] = '\0';
Petr Šabata 81d24c
+			tmp_end = NULL;
Petr Šabata 81d24c
+			value = strtol(tmp, &tmp_end, 16);
Petr Šabata 81d24c
+			if (tmp_end != tmp+2)
Petr Šabata 81d24c
+				goto fail;
Petr Šabata 81d24c
+			else
Petr Šabata 81d24c
+				out[outlen++] = (char) value;
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+		default:
Petr Šabata 81d24c
+			out[outlen++] = *p++;
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* zero terminate */
Petr Šabata 81d24c
+	out[outlen] = '\0';
Petr Šabata 81d24c
+	*outp = out;
Petr Šabata 81d24c
+	return outlen;
Petr Šabata 81d24c
+fail:
Petr Šabata 81d24c
+	free(out);
Petr Šabata 81d24c
+	return -1;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+struct sshbuf *
Petr Šabata 81d24c
+percent_encode(const char *data, size_t length, const char *allow_list)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct sshbuf *b = NULL;
Petr Šabata 81d24c
+	char tmp[4], *cp;
Petr Šabata 81d24c
+	size_t i;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((b = sshbuf_new()) == NULL)
Petr Šabata 81d24c
+		return NULL;
Petr Šabata 81d24c
+	for (i = 0; i < length; i++) {
Petr Šabata 81d24c
+		cp = strchr(allow_list, data[i]);
Petr Šabata 81d24c
+		/* if c is specified as '\0' pointer to terminator is returned !! */
Petr Šabata 81d24c
+		if (cp != NULL && *cp != '\0') {
Petr Šabata 81d24c
+			if (sshbuf_put(b, &data[i], 1) != 0)
Petr Šabata 81d24c
+				goto err;
Petr Šabata 81d24c
+		} else
Petr Šabata 81d24c
+			if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3
Petr Šabata 81d24c
+			    || sshbuf_put(b, tmp, 3) != 0)
Petr Šabata 81d24c
+				goto err;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (sshbuf_put(b, "\0", 1) == 0)
Petr Šabata 81d24c
+		return b;
Petr Šabata 81d24c
+err:
Petr Šabata 81d24c
+	sshbuf_free(b);
Petr Šabata 81d24c
+	return NULL;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+char *
Petr Šabata 81d24c
+pkcs11_uri_append(char *part, const char *separator, const char *key,
Petr Šabata 81d24c
+    struct sshbuf *value)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *new_part;
Petr Šabata 81d24c
+	size_t size = 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (value == NULL)
Petr Šabata 81d24c
+		return NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	size = asprintf(&new_part,
Petr Šabata 81d24c
+	    "%s%s%s"  PKCS11_URI_VALUE_SEPARATOR "%s",
Petr Šabata 81d24c
+	    (part != NULL ? part : ""),
Petr Šabata 81d24c
+	    (part != NULL ? separator : ""),
Petr Šabata 81d24c
+	    key, sshbuf_ptr(value));
Petr Šabata 81d24c
+	sshbuf_free(value);
Petr Šabata 81d24c
+	free(part);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (size <= 0)
Petr Šabata 81d24c
+		return NULL;
Petr Šabata 81d24c
+	return new_part;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+char *
Petr Šabata 81d24c
+pkcs11_uri_get(struct pkcs11_uri *uri)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	size_t size = 0;
Petr Šabata 81d24c
+	char *p = NULL, *path = NULL, *query = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* compose a percent-encoded ID */
Petr Šabata 81d24c
+	if (uri->id_len > 0) {
Petr Šabata 81d24c
+		struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, "");
Petr Šabata 81d24c
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Petr Šabata 81d24c
+		    PKCS11_URI_ID, key_id);
Petr Šabata 81d24c
+		if (path == NULL)
Petr Šabata 81d24c
+			goto err;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Write object label */
Petr Šabata 81d24c
+	if (uri->object) {
Petr Šabata 81d24c
+		struct sshbuf *label = percent_encode(uri->object, strlen(uri->object),
Petr Šabata 81d24c
+		    PKCS11_URI_WHITELIST);
Petr Šabata 81d24c
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Petr Šabata 81d24c
+		    PKCS11_URI_OBJECT, label);
Petr Šabata 81d24c
+		if (path == NULL)
Petr Šabata 81d24c
+			goto err;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Write token label */
Petr Šabata 81d24c
+	if (uri->token) {
Petr Šabata 81d24c
+		struct sshbuf *label = percent_encode(uri->token, strlen(uri->token),
Petr Šabata 81d24c
+		    PKCS11_URI_WHITELIST);
Petr Šabata 81d24c
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Petr Šabata 81d24c
+		    PKCS11_URI_TOKEN, label);
Petr Šabata 81d24c
+		if (path == NULL)
Petr Šabata 81d24c
+			goto err;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Write manufacturer */
Petr Šabata 81d24c
+	if (uri->manuf) {
Petr Šabata 81d24c
+		struct sshbuf *manuf = percent_encode(uri->manuf,
Petr Šabata 81d24c
+		    strlen(uri->manuf), PKCS11_URI_WHITELIST);
Petr Šabata 81d24c
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Petr Šabata 81d24c
+		    PKCS11_URI_MANUF, manuf);
Petr Šabata 81d24c
+		if (path == NULL)
Petr Šabata 81d24c
+			goto err;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Write module_path */
Petr Šabata 81d24c
+	if (uri->module_path) {
Petr Šabata 81d24c
+		struct sshbuf *module = percent_encode(uri->module_path,
Petr Šabata 81d24c
+		    strlen(uri->module_path), PKCS11_URI_WHITELIST "/");
Petr Šabata 81d24c
+		query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR,
Petr Šabata 81d24c
+		    PKCS11_URI_MODULE_PATH, module);
Petr Šabata 81d24c
+		if (query == NULL)
Petr Šabata 81d24c
+			goto err;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s",
Petr Šabata 81d24c
+	    path != NULL ? path : "",
Petr Šabata 81d24c
+	    query != NULL ? "?" : "",
Petr Šabata 81d24c
+	    query != NULL ? query : "");
Petr Šabata 81d24c
+err:
Petr Šabata 81d24c
+	free(query);
Petr Šabata 81d24c
+	free(path);
Petr Šabata 81d24c
+	if (size <= 0)
Petr Šabata 81d24c
+		return NULL;
Petr Šabata 81d24c
+	return p;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+struct pkcs11_uri *
Petr Šabata 81d24c
+pkcs11_uri_init()
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri));
Petr Šabata 81d24c
+	return d;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	if (pkcs11 == NULL) {
Petr Šabata 81d24c
+		return;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	free(pkcs11->id);
Petr Šabata 81d24c
+	free(pkcs11->module_path);
Petr Šabata 81d24c
+	free(pkcs11->token);
Petr Šabata 81d24c
+	free(pkcs11->object);
Petr Šabata 81d24c
+	free(pkcs11->lib_manuf);
Petr Šabata 81d24c
+	free(pkcs11->manuf);
Petr Šabata 81d24c
+	if (pkcs11->pin)
Petr Šabata 81d24c
+		freezero(pkcs11->pin, strlen(pkcs11->pin));
Petr Šabata 81d24c
+	free(pkcs11);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *saveptr1, *saveptr2, *str1, *str2, *tok;
Petr Šabata 81d24c
+	int rv = 0, len;
Petr Šabata 81d24c
+	char *p = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	size_t scheme_len = strlen(PKCS11_URI_SCHEME);
Petr Šabata 81d24c
+	if (strlen(uri) < scheme_len || /* empty URI matches everything */
Petr Šabata 81d24c
+	    strncmp(uri, PKCS11_URI_SCHEME, scheme_len) != 0) {
Petr Šabata 81d24c
+		error("%s: The '%s' does not look like PKCS#11 URI",
Petr Šabata 81d24c
+		    __func__, uri);
Petr Šabata 81d24c
+		return -1;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (pkcs11 == NULL) {
Petr Šabata 81d24c
+		error("%s: Bad arguments. The pkcs11 can't be null", __func__);
Petr Šabata 81d24c
+		return -1;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* skip URI schema name */
Petr Šabata 81d24c
+	p = strdup(uri);
Petr Šabata 81d24c
+	str1 = p;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* everything before ? */
Petr Šabata 81d24c
+	tok = strtok_r(str1, "?", &saveptr1);
Petr Šabata 81d24c
+	if (tok == NULL) {
Petr Šabata 81d24c
+		error("%s: pk11-path expected, got EOF", __func__);
Petr Šabata 81d24c
+		rv = -1;
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* skip URI schema name:
Petr Šabata 81d24c
+	 * the scheme ensures that there is at least something before "?"
Petr Šabata 81d24c
+	 * allowing empty pk11-path. Resulting token at worst pointing to
Petr Šabata 81d24c
+	 * \0 byte */
Petr Šabata 81d24c
+	tok = tok + scheme_len;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* parse pk11-path */
Petr Šabata 81d24c
+	for (str2 = tok; ; str2 = NULL) {
Petr Šabata 81d24c
+		char **charptr, *arg = NULL;
Petr Šabata 81d24c
+		pkcs11uriOpCodes opcode;
Petr Šabata 81d24c
+		tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2);
Petr Šabata 81d24c
+		if (tok == NULL)
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+		opcode = parse_token(tok);
Petr Šabata 81d24c
+		if (opcode != pBadOption)
Petr Šabata 81d24c
+			arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		switch (opcode) {
Petr Šabata 81d24c
+		case pId:
Petr Šabata 81d24c
+			/* CKA_ID */
Petr Šabata 81d24c
+			if (pkcs11->id != NULL) {
Petr Šabata 81d24c
+				verbose("%s: The id already set in the PKCS#11 URI",
Petr Šabata 81d24c
+					__func__);
Petr Šabata 81d24c
+				rv = -1;
Petr Šabata 81d24c
+				goto out;
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			len = percent_decode(arg, &pkcs11->id);
Petr Šabata 81d24c
+			if (len <= 0) {
Petr Šabata 81d24c
+				verbose("%s: Failed to percent-decode CKA_ID: %s",
Petr Šabata 81d24c
+				    __func__, arg);
Petr Šabata 81d24c
+				rv = -1;
Petr Šabata 81d24c
+				goto out;
Petr Šabata 81d24c
+			} else
Petr Šabata 81d24c
+				pkcs11->id_len = len;
Petr Šabata 81d24c
+			debug3("%s: Setting CKA_ID = %s from PKCS#11 URI",
Petr Šabata 81d24c
+			    __func__, arg);
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+		case pToken:
Petr Šabata 81d24c
+			/* CK_TOKEN_INFO -> label */
Petr Šabata 81d24c
+			charptr = &pkcs11->token;
Petr Šabata 81d24c
+ parse_string:
Petr Šabata 81d24c
+			if (*charptr != NULL) {
Petr Šabata 81d24c
+				verbose("%s: The %s already set in the PKCS#11 URI",
Petr Šabata 81d24c
+				    keywords[opcode].name, __func__);
Petr Šabata 81d24c
+				rv = -1;
Petr Šabata 81d24c
+				goto out;
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			percent_decode(arg, charptr);
Petr Šabata 81d24c
+			debug3("%s: Setting %s = %s from PKCS#11 URI",
Petr Šabata 81d24c
+			    __func__, keywords[opcode].name, *charptr);
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		case pObject:
Petr Šabata 81d24c
+			/* CK_TOKEN_INFO -> manufacturerID */
Petr Šabata 81d24c
+			charptr = &pkcs11->object;
Petr Šabata 81d24c
+			goto parse_string;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		case pManufacturer:
Petr Šabata 81d24c
+			/* CK_TOKEN_INFO -> manufacturerID */
Petr Šabata 81d24c
+			charptr = &pkcs11->manuf;
Petr Šabata 81d24c
+			goto parse_string;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		case pLibraryManufacturer:
Petr Šabata 81d24c
+			/* CK_INFO -> manufacturerID */
Petr Šabata 81d24c
+			charptr = &pkcs11->lib_manuf;
Petr Šabata 81d24c
+			goto parse_string;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		default:
Petr Šabata 81d24c
+			/* Unrecognized attribute in the URI path SHOULD be error */
Petr Šabata 81d24c
+			verbose("%s: Unknown part of path in PKCS#11 URI: %s",
Petr Šabata 81d24c
+			    __func__, tok);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	tok = strtok_r(NULL, "?", &saveptr1);
Petr Šabata 81d24c
+	if (tok == NULL) {
Petr Šabata 81d24c
+		goto out;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	/* parse pk11-query (optional) */
Petr Šabata 81d24c
+	for (str2 = tok; ; str2 = NULL) {
Petr Šabata 81d24c
+		char *arg;
Petr Šabata 81d24c
+		pkcs11uriOpCodes opcode;
Petr Šabata 81d24c
+		tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2);
Petr Šabata 81d24c
+		if (tok == NULL)
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+		opcode = parse_token(tok);
Petr Šabata 81d24c
+		if (opcode != pBadOption)
Petr Šabata 81d24c
+			arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		switch (opcode) {
Petr Šabata 81d24c
+		case pModulePath:
Petr Šabata 81d24c
+			/* module-path is PKCS11Provider */
Petr Šabata 81d24c
+			if (pkcs11->module_path != NULL) {
Petr Šabata 81d24c
+				verbose("%s: Multiple module-path attributes are"
Petr Šabata 81d24c
+				    "not supported the PKCS#11 URI", __func__);
Petr Šabata 81d24c
+				rv = -1;
Petr Šabata 81d24c
+				goto out;
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			percent_decode(arg, &pkcs11->module_path);
Petr Šabata 81d24c
+			debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI",
Petr Šabata 81d24c
+			    __func__, pkcs11->module_path);
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		case pPinValue:
Petr Šabata 81d24c
+			/* pin-value */
Petr Šabata 81d24c
+			if (pkcs11->pin != NULL) {
Petr Šabata 81d24c
+				verbose("%s: Multiple pin-value attributes are"
Petr Šabata 81d24c
+				    "not supported the PKCS#11 URI", __func__);
Petr Šabata 81d24c
+				rv = -1;
Petr Šabata 81d24c
+				goto out;
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			percent_decode(arg, &pkcs11->pin);
Petr Šabata 81d24c
+			debug3("%s: Setting PIN from PKCS#11 URI", __func__);
Petr Šabata 81d24c
+			break;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		default:
Petr Šabata 81d24c
+			/* Unrecognized attribute in the URI query SHOULD be ignored */
Petr Šabata 81d24c
+			verbose("%s: Unknown part of query in PKCS#11 URI: %s",
Petr Šabata 81d24c
+			    __func__, tok);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+out:
Petr Šabata 81d24c
+	free(p);
Petr Šabata 81d24c
+	return rv;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#endif /* ENABLE_PKCS11 */
Petr Šabata 81d24c
diff --git a/ssh-pkcs11-uri.h b/ssh-pkcs11-uri.h
Petr Šabata 81d24c
new file mode 100644
Petr Šabata 81d24c
index 00000000..942a5a5a
Petr Šabata 81d24c
--- /dev/null
Petr Šabata 81d24c
+++ b/ssh-pkcs11-uri.h
Petr Šabata 81d24c
@@ -0,0 +1,42 @@
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * Copyright (c) 2017 Red Hat
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Authors: Jakub Jelen <jjelen@redhat.com>
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Permission to use, copy, modify, and distribute this software for any
Petr Šabata 81d24c
+ * purpose with or without fee is hereby granted, provided that the above
Petr Šabata 81d24c
+ * copyright notice and this permission notice appear in all copies.
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Petr Šabata 81d24c
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Petr Šabata 81d24c
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Petr Šabata 81d24c
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Petr Šabata 81d24c
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Petr Šabata 81d24c
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Petr Šabata 81d24c
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#define PKCS11_URI_SCHEME "pkcs11:"
Petr Šabata 81d24c
+#define PKCS11_URI_WHITELIST	"abcdefghijklmnopqrstuvwxyz" \
Petr Šabata 81d24c
+				"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
Petr Šabata 81d24c
+				"0123456789_-.()"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+struct pkcs11_uri {
Petr Šabata 81d24c
+	/* path */
Petr Šabata 81d24c
+	char *id;
Petr Šabata 81d24c
+	size_t id_len;
Petr Šabata 81d24c
+	char *token;
Petr Šabata 81d24c
+	char *object;
Petr Šabata 81d24c
+	char *lib_manuf;
Petr Šabata 81d24c
+	char *manuf;
Petr Šabata 81d24c
+	/* query */
Petr Šabata 81d24c
+	char *module_path;
Petr Šabata 81d24c
+	char *pin; /* Only parsed, but not printed */
Petr Šabata 81d24c
+};
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+struct	 pkcs11_uri *pkcs11_uri_init();
Petr Šabata 81d24c
+void	 pkcs11_uri_cleanup(struct pkcs11_uri *);
Petr Šabata 81d24c
+int	 pkcs11_uri_parse(const char *, struct pkcs11_uri *);
Petr Šabata 81d24c
+struct	 pkcs11_uri *pkcs11_uri_init();
Petr Šabata 81d24c
+char	*pkcs11_uri_get(struct pkcs11_uri *uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c
Petr Šabata 81d24c
index a302c79c..879fe917 100644
Petr Šabata 81d24c
--- a/ssh-pkcs11.c
Petr Šabata 81d24c
+++ b/ssh-pkcs11.c
Petr Šabata 81d24c
@@ -54,8 +54,8 @@ struct pkcs11_slotinfo {
Petr Šabata 81d24c
 	int			logged_in;
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-struct pkcs11_provider {
Petr Šabata 81d24c
-	char			*name;
Petr Šabata 81d24c
+struct pkcs11_module {
Petr Šabata 81d24c
+	char			*module_path;
Petr Šabata 81d24c
 	void			*handle;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*function_list;
Petr Šabata 81d24c
 	CK_INFO			info;
Petr Šabata 81d24c
@@ -64,6 +64,13 @@ struct pkcs11_provider {
Petr Šabata 81d24c
 	struct pkcs11_slotinfo	*slotinfo;
Petr Šabata 81d24c
 	int			valid;
Petr Šabata 81d24c
 	int			refcount;
Petr Šabata 81d24c
+};
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+struct pkcs11_provider {
Petr Šabata 81d24c
+	char			*name;
Petr Šabata 81d24c
+	struct pkcs11_module	*module; /* can be shared between various providers */
Petr Šabata 81d24c
+	int			refcount;
Petr Šabata 81d24c
+	int			valid;
Petr Šabata 81d24c
 	TAILQ_ENTRY(pkcs11_provider) next;
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -74,6 +81,7 @@ struct pkcs11_key {
Petr Šabata 81d24c
 	CK_ULONG		slotidx;
Petr Šabata 81d24c
 	char			*keyid;
Petr Šabata 81d24c
 	int			keyid_len;
Petr Šabata 81d24c
+	char			*label;
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 int pkcs11_interactive = 0;
Petr Šabata 81d24c
@@ -106,26 +114,63 @@ pkcs11_init(int interactive)
Petr Šabata 81d24c
  * this is called when a provider gets unregistered.
Petr Šabata 81d24c
  */
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
-pkcs11_provider_finalize(struct pkcs11_provider *p)
Petr Šabata 81d24c
+pkcs11_module_finalize(struct pkcs11_module *m)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	CK_RV rv;
Petr Šabata 81d24c
 	CK_ULONG i;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	debug("pkcs11_provider_finalize: %p refcount %d valid %d",
Petr Šabata 81d24c
-	    p, p->refcount, p->valid);
Petr Šabata 81d24c
-	if (!p->valid)
Petr Šabata 81d24c
+	debug("%s: %p refcount %d valid %d", __func__,
Petr Šabata 81d24c
+	    m, m->refcount, m->valid);
Petr Šabata 81d24c
+	if (!m->valid)
Petr Šabata 81d24c
 		return;
Petr Šabata 81d24c
-	for (i = 0; i < p->nslots; i++) {
Petr Šabata 81d24c
-		if (p->slotinfo[i].session &&
Petr Šabata 81d24c
-		    (rv = p->function_list->C_CloseSession(
Petr Šabata 81d24c
-		    p->slotinfo[i].session)) != CKR_OK)
Petr Šabata 81d24c
+	for (i = 0; i < m->nslots; i++) {
Petr Šabata 81d24c
+		if (m->slotinfo[i].session &&
Petr Šabata 81d24c
+		    (rv = m->function_list->C_CloseSession(
Petr Šabata 81d24c
+		    m->slotinfo[i].session)) != CKR_OK)
Petr Šabata 81d24c
 			error("C_CloseSession failed: %lu", rv);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
Petr Šabata 81d24c
+	if ((rv = m->function_list->C_Finalize(NULL)) != CKR_OK)
Petr Šabata 81d24c
 		error("C_Finalize failed: %lu", rv);
Petr Šabata 81d24c
+	m->valid = 0;
Petr Šabata 81d24c
+	m->function_list = NULL;
Petr Šabata 81d24c
+	dlclose(m->handle);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * remove a reference to the pkcs11 module.
Petr Šabata 81d24c
+ * called when a provider is unregistered.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+pkcs11_module_unref(struct pkcs11_module *m)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	debug("%s: %p refcount %d", __func__, m, m->refcount);
Petr Šabata 81d24c
+	if (--m->refcount <= 0) {
Petr Šabata 81d24c
+		pkcs11_module_finalize(m);
Petr Šabata 81d24c
+		if (m->valid)
Petr Šabata 81d24c
+			error("%s: %p still valid", __func__, m);
Petr Šabata 81d24c
+		free(m->slotlist);
Petr Šabata 81d24c
+		free(m->slotinfo);
Petr Šabata 81d24c
+		free(m->module_path);
Petr Šabata 81d24c
+		free(m);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * finalize a provider shared libarary, it's no longer usable.
Petr Šabata 81d24c
+ * however, there might still be keys referencing this provider,
Petr Šabata 81d24c
+ * so the actuall freeing of memory is handled by pkcs11_provider_unref().
Petr Šabata 81d24c
+ * this is called when a provider gets unregistered.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+pkcs11_provider_finalize(struct pkcs11_provider *p)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	debug("%s: %p refcount %d valid %d", __func__,
Petr Šabata 81d24c
+	    p, p->refcount, p->valid);
Petr Šabata 81d24c
+	if (!p->valid)
Petr Šabata 81d24c
+		return;
Petr Šabata 81d24c
+	pkcs11_module_unref(p->module);
Petr Šabata 81d24c
+	p->module = NULL;
Petr Šabata 81d24c
 	p->valid = 0;
Petr Šabata 81d24c
-	p->function_list = NULL;
Petr Šabata 81d24c
-	dlclose(p->handle);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /*
Petr Šabata 81d24c
@@ -135,13 +180,11 @@ pkcs11_provider_finalize(struct pkcs11_provider *p)
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
 pkcs11_provider_unref(struct pkcs11_provider *p)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
Petr Šabata 81d24c
+	debug("%s: %p refcount %d", __func__, p, p->refcount);
Petr Šabata 81d24c
 	if (--p->refcount <= 0) {
Petr Šabata 81d24c
-		if (p->valid)
Petr Šabata 81d24c
-			error("pkcs11_provider_unref: %p still valid", p);
Petr Šabata 81d24c
 		free(p->name);
Petr Šabata 81d24c
-		free(p->slotlist);
Petr Šabata 81d24c
-		free(p->slotinfo);
Petr Šabata 81d24c
+		if (p->module)
Petr Šabata 81d24c
+			pkcs11_module_unref(p->module);
Petr Šabata 81d24c
 		free(p);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
@@ -159,6 +202,20 @@ pkcs11_terminate(void)
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/* lookup provider by module path */
Petr Šabata 81d24c
+static struct pkcs11_module *
Petr Šabata 81d24c
+pkcs11_provider_lookup_module(char *module_path)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct pkcs11_provider *p;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	TAILQ_FOREACH(p, &pkcs11_providers, next) {
Petr Šabata 81d24c
+		debug("check %p %s (%s)", p, p->name, p->module->module_path);
Petr Šabata 81d24c
+		if (!strcmp(module_path, p->module->module_path))
Petr Šabata 81d24c
+			return (p->module);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	return (NULL);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* lookup provider by name */
Petr Šabata 81d24c
 static struct pkcs11_provider *
Petr Šabata 81d24c
 pkcs11_provider_lookup(char *provider_id)
Petr Šabata 81d24c
@@ -173,19 +230,52 @@ pkcs11_provider_lookup(char *provider_id)
Petr Šabata 81d24c
 	return (NULL);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+int pkcs11_del_provider_by_uri(struct pkcs11_uri *);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* unregister provider by name */
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
 pkcs11_del_provider(char *provider_id)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	int rv;
Petr Šabata 81d24c
+	struct pkcs11_uri *uri;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	debug("%s: called, provider_id = %s", __func__, provider_id);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	if (uri == NULL)
Petr Šabata 81d24c
+		fatal("Failed to init PKCS#11 URI");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+	    strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+		if (pkcs11_uri_parse(provider_id, uri) != 0)
Petr Šabata 81d24c
+			fatal("Failed to parse PKCS#11 URI");
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		uri->module_path = strdup(provider_id);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	rv = pkcs11_del_provider_by_uri(uri);
Petr Šabata 81d24c
+	pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+	return rv;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/* unregister provider by PKCS#11 URI */
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+pkcs11_del_provider_by_uri(struct pkcs11_uri *uri)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct pkcs11_provider *p;
Petr Šabata 81d24c
+	int rv = -1;
Petr Šabata 81d24c
+	char *provider_uri = pkcs11_uri_get(uri);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
Petr Šabata 81d24c
+	debug3("%s(%s): called", __func__, provider_uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) {
Petr Šabata 81d24c
 		TAILQ_REMOVE(&pkcs11_providers, p, next);
Petr Šabata 81d24c
 		pkcs11_provider_finalize(p);
Petr Šabata 81d24c
 		pkcs11_provider_unref(p);
Petr Šabata 81d24c
-		return (0);
Petr Šabata 81d24c
+		rv = 0;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	return (-1);
Petr Šabata 81d24c
+	free(provider_uri);
Petr Šabata 81d24c
+	return rv;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 static RSA_METHOD *rsa_method;
Petr Šabata 81d24c
@@ -195,6 +285,55 @@ static EC_KEY_METHOD *ec_key_method;
Petr Šabata 81d24c
 static int ec_key_idx = 0;
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * This can't be in the ssh-pkcs11-uri, becase we can not depend on
Petr Šabata 81d24c
+ * PKCS#11 structures in ssh-agent (using client-helper communication)
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+pkcs11_uri_write(const struct sshkey *key, FILE *f)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *p = NULL;
Petr Šabata 81d24c
+	struct pkcs11_uri uri;
Petr Šabata 81d24c
+	struct pkcs11_key *k11;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* sanity - is it a RSA key with associated app_data? */
Petr Šabata 81d24c
+	switch (key->type) {
Petr Šabata 81d24c
+	case KEY_RSA:
Petr Šabata 81d24c
+		k11 = RSA_get_ex_data(key->rsa, rsa_idx);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+#ifdef HAVE_EC_KEY_METHOD_NEW
Petr Šabata 81d24c
+	case KEY_ECDSA:
Petr Šabata 81d24c
+		k11 = EC_KEY_get_ex_data(key->ecdsa, ec_key_idx);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+	default:
Petr Šabata 81d24c
+		error("Unknown key type %d", key->type);
Petr Šabata 81d24c
+		return -1;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (k11 == NULL) {
Petr Šabata 81d24c
+		error("Failed to get ex_data for key type %d", key->type);
Petr Šabata 81d24c
+		return (-1);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* omit type -- we are looking for private-public or private-certificate pairs */
Petr Šabata 81d24c
+	uri.id = k11->keyid;
Petr Šabata 81d24c
+	uri.id_len = k11->keyid_len;
Petr Šabata 81d24c
+	uri.token = k11->provider->module->slotinfo[k11->slotidx].token.label;
Petr Šabata 81d24c
+	uri.object = k11->label;
Petr Šabata 81d24c
+	uri.module_path = k11->provider->module->module_path;
Petr Šabata 81d24c
+	uri.lib_manuf = k11->provider->module->info.manufacturerID;
Petr Šabata 81d24c
+	uri.manuf = k11->provider->module->slotinfo[k11->slotidx].token.manufacturerID;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	p = pkcs11_uri_get(&uri);
Petr Šabata 81d24c
+	/* do not cleanup -- we do not allocate here, only reference */
Petr Šabata 81d24c
+	if (p == NULL)
Petr Šabata 81d24c
+		return -1;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	fprintf(f, " %s", p);
Petr Šabata 81d24c
+	free(p);
Petr Šabata 81d24c
+	return 0;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* release a wrapped object */
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
 pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
Petr Šabata 81d24c
@@ -208,6 +347,7 @@ pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
Petr Šabata 81d24c
 	if (k11->provider)
Petr Šabata 81d24c
 		pkcs11_provider_unref(k11->provider);
Petr Šabata 81d24c
 	free(k11->keyid);
Petr Šabata 81d24c
+	free(k11->label);
Petr Šabata 81d24c
 	free(k11);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -222,8 +362,8 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
Petr Šabata 81d24c
 	CK_RV			rv;
Petr Šabata 81d24c
 	int			ret = -1;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
 	if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv);
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
@@ -262,12 +402,12 @@ pkcs11_login_slot(struct pkcs11_provider *provider, struct pkcs11_slotinfo *si,
Petr Šabata 81d24c
 	else {
Petr Šabata 81d24c
 		snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ",
Petr Šabata 81d24c
 		    si->token.label);
Petr Šabata 81d24c
-		if ((pin = read_passphrase(prompt, RP_ALLOW_EOF)) == NULL) {
Petr Šabata 81d24c
+		if ((pin = read_passphrase(prompt, RP_ALLOW_EOF|RP_ALLOW_STDIN)) == NULL) {
Petr Šabata 81d24c
 			debug("%s: no pin specified", __func__);
Petr Šabata 81d24c
 			return (-1);	/* bail out */
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	rv = provider->function_list->C_Login(si->session, type, (u_char *)pin,
Petr Šabata 81d24c
+	rv = provider->module->function_list->C_Login(si->session, type, (u_char *)pin,
Petr Šabata 81d24c
 	    (pin != NULL) ? strlen(pin) : 0);
Petr Šabata 81d24c
 	if (pin != NULL)
Petr Šabata 81d24c
 		freezero(pin, strlen(pin));
Petr Šabata 81d24c
@@ -282,13 +422,14 @@ pkcs11_login_slot(struct pkcs11_provider *provider, struct pkcs11_slotinfo *si,
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	if (k11 == NULL || k11->provider == NULL || !k11->provider->valid) {
Petr Šabata 81d24c
+	if (k11 == NULL || k11->provider == NULL || !k11->provider->valid ||
Petr Šabata 81d24c
+	    k11->provider->module == NULL || !k11->provider->module->valid) {
Petr Šabata 81d24c
 		error("no pkcs11 (valid) provider found");
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	return pkcs11_login_slot(k11->provider,
Petr Šabata 81d24c
-	    &k11->provider->slotinfo[k11->slotidx], type);
Petr Šabata 81d24c
+	    &k11->provider->module->slotinfo[k11->slotidx], type);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -304,13 +445,14 @@ pkcs11_check_obj_bool_attrib(struct pkcs11_key *k11, CK_OBJECT_HANDLE obj,
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	*val = 0;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (!k11->provider || !k11->provider->valid) {
Petr Šabata 81d24c
+	if (!k11->provider || !k11->provider->valid ||
Petr Šabata 81d24c
+	    !k11->provider->module || !k11->provider->module->valid) {
Petr Šabata 81d24c
 		error("no pkcs11 (valid) provider found");
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = k11->provider->function_list;
Petr Šabata 81d24c
-	si = &k11->provider->slotinfo[k11->slotidx];
Petr Šabata 81d24c
+	f = k11->provider->module->function_list;
Petr Šabata 81d24c
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	attr.type = type;
Petr Šabata 81d24c
 	attr.pValue = &flag;
Petr Šabata 81d24c
@@ -341,13 +483,14 @@ pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
Petr Šabata 81d24c
 	int			 always_auth = 0;
Petr Šabata 81d24c
 	int			 did_login = 0;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (!k11->provider || !k11->provider->valid) {
Petr Šabata 81d24c
+	if (!k11->provider || !k11->provider->valid ||
Petr Šabata 81d24c
+	    !k11->provider->module || !k11->provider->module->valid) {
Petr Šabata 81d24c
 		error("no pkcs11 (valid) provider found");
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = k11->provider->function_list;
Petr Šabata 81d24c
-	si = &k11->provider->slotinfo[k11->slotidx];
Petr Šabata 81d24c
+	f = k11->provider->module->function_list;
Petr Šabata 81d24c
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
Petr Šabata 81d24c
 		if (pkcs11_login(k11, CKU_USER) < 0) {
Petr Šabata 81d24c
@@ -424,8 +567,8 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = k11->provider->function_list;
Petr Šabata 81d24c
-	si = &k11->provider->slotinfo[k11->slotidx];
Petr Šabata 81d24c
+	f = k11->provider->module->function_list;
Petr Šabata 81d24c
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Petr Šabata 81d24c
 	tlen = RSA_size(rsa);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* XXX handle CKR_BUFFER_TOO_SMALL */
Petr Šabata 81d24c
@@ -469,7 +612,7 @@ pkcs11_rsa_start_wrapper(void)
Petr Šabata 81d24c
 /* redirect private key operations for rsa key to pkcs11 token */
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Petr Šabata 81d24c
-    CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
Petr Šabata 81d24c
+    CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, RSA *rsa)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct pkcs11_key	*k11;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -487,6 +630,12 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Petr Šabata 81d24c
 		memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	if (label_attrib->ulValueLen > 0 ) {
Petr Šabata 81d24c
+		k11->label = xmalloc(label_attrib->ulValueLen+1);
Petr Šabata 81d24c
+		memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen);
Petr Šabata 81d24c
+		k11->label[label_attrib->ulValueLen] = 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	RSA_set_method(rsa, rsa_method);
Petr Šabata 81d24c
 	RSA_set_ex_data(rsa, rsa_idx, k11);
Petr Šabata 81d24c
 	return (0);
Petr Šabata 81d24c
@@ -517,8 +666,8 @@ ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
Petr Šabata 81d24c
 		return (NULL);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = k11->provider->function_list;
Petr Šabata 81d24c
-	si = &k11->provider->slotinfo[k11->slotidx];
Petr Šabata 81d24c
+	f = k11->provider->module->function_list;
Petr Šabata 81d24c
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	siglen = ECDSA_size(ec);
Petr Šabata 81d24c
 	sig = xmalloc(siglen);
Petr Šabata 81d24c
@@ -583,7 +732,7 @@ pkcs11_ecdsa_start_wrapper(void)
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Petr Šabata 81d24c
-    CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
Petr Šabata 81d24c
+    CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ec)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct pkcs11_key	*k11;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -599,6 +748,12 @@ pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Petr Šabata 81d24c
 	k11->keyid = xmalloc(k11->keyid_len);
Petr Šabata 81d24c
 	memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	if (label_attrib->ulValueLen > 0 ) {
Petr Šabata 81d24c
+		k11->label = xmalloc(label_attrib->ulValueLen+1);
Petr Šabata 81d24c
+		memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen);
Petr Šabata 81d24c
+		k11->label[label_attrib->ulValueLen] = 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	EC_KEY_set_method(ec, ec_key_method);
Petr Šabata 81d24c
 	EC_KEY_set_ex_data(ec, ec_key_idx, k11);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -635,8 +790,8 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	session;
Petr Šabata 81d24c
 	int			login_required, ret;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
-	si = &p->slotinfo[slotidx];
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
+	si = &p->module->slotinfo[slotidx];
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	login_required = si->token.flags & CKF_LOGIN_REQUIRED;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -646,9 +801,9 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
Petr Šabata 81d24c
 		error("pin required");
Petr Šabata 81d24c
 		return (-SSH_PKCS11_ERR_PIN_REQUIRED);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
Petr Šabata 81d24c
+	if ((rv = f->C_OpenSession(p->module->slotlist[slotidx], CKF_RW_SESSION|
Petr Šabata 81d24c
 	    CKF_SERIAL_SESSION, NULL, NULL, &session)) != CKR_OK) {
Petr Šabata 81d24c
-		error("C_OpenSession failed: %lu", rv);
Petr Šabata 81d24c
+		error("C_OpenSession failed for slot %lu: %lu", slotidx, rv);
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	if (login_required && pin != NULL && strlen(pin) != 0) {
Petr Šabata 81d24c
@@ -684,7 +839,8 @@ static struct sshkey *
Petr Šabata 81d24c
 pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
     CK_OBJECT_HANDLE *obj)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 key_attr[3];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 key_attr[4];
Petr Šabata 81d24c
+	int			 nattr = 4;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Petr Šabata 81d24c
@@ -698,14 +854,15 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	memset(&key_attr, 0, sizeof(key_attr));
Petr Šabata 81d24c
 	key_attr[0].type = CKA_ID;
Petr Šabata 81d24c
-	key_attr[1].type = CKA_EC_POINT;
Petr Šabata 81d24c
-	key_attr[2].type = CKA_EC_PARAMS;
Petr Šabata 81d24c
+	key_attr[1].type = CKA_LABEL;
Petr Šabata 81d24c
+	key_attr[2].type = CKA_EC_POINT;
Petr Šabata 81d24c
+	key_attr[3].type = CKA_EC_PARAMS;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* figure out size of the attributes */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		return (NULL);
Petr Šabata 81d24c
@@ -717,18 +874,19 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 	 * XXX assumes CKA_ID is always first.
Petr Šabata 81d24c
 	 */
Petr Šabata 81d24c
 	if (key_attr[1].ulValueLen == 0 ||
Petr Šabata 81d24c
-	    key_attr[2].ulValueLen == 0) {
Petr Šabata 81d24c
+	    key_attr[2].ulValueLen == 0 ||
Petr Šabata 81d24c
+	    key_attr[3].ulValueLen == 0) {
Petr Šabata 81d24c
 		error("invalid attribute length");
Petr Šabata 81d24c
 		return (NULL);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* allocate buffers for attributes */
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		if (key_attr[i].ulValueLen > 0)
Petr Šabata 81d24c
 			key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* retrieve ID, public point and curve parameters of EC key */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
@@ -740,8 +898,8 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	attrp = key_attr[2].pValue;
Petr Šabata 81d24c
-	group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
Petr Šabata 81d24c
+	attrp = key_attr[3].pValue;
Petr Šabata 81d24c
+	group = d2i_ECPKParameters(NULL, &attrp, key_attr[3].ulValueLen);
Petr Šabata 81d24c
 	if (group == NULL) {
Petr Šabata 81d24c
 		ossl_error("d2i_ECPKParameters failed");
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
@@ -752,13 +910,13 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (key_attr[1].ulValueLen <= 2) {
Petr Šabata 81d24c
+	if (key_attr[2].ulValueLen <= 2) {
Petr Šabata 81d24c
 		error("CKA_EC_POINT too small");
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	attrp = key_attr[1].pValue;
Petr Šabata 81d24c
-	octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen);
Petr Šabata 81d24c
+	attrp = key_attr[2].pValue;
Petr Šabata 81d24c
+	octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[2].ulValueLen);
Petr Šabata 81d24c
 	if (octet == NULL) {
Petr Šabata 81d24c
 		ossl_error("d2i_ASN1_OCTET_STRING failed");
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
@@ -775,7 +933,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
Petr Šabata 81d24c
+	if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], ec))
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	key = sshkey_new(KEY_UNSPEC);
Petr Šabata 81d24c
@@ -791,7 +949,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 	ec = NULL;	/* now owned by key */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 fail:
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		free(key_attr[i].pValue);
Petr Šabata 81d24c
 	if (ec)
Petr Šabata 81d24c
 		EC_KEY_free(ec);
Petr Šabata 81d24c
@@ -808,7 +966,8 @@ static struct sshkey *
Petr Šabata 81d24c
 pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
     CK_OBJECT_HANDLE *obj)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 key_attr[3];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 key_attr[4];
Petr Šabata 81d24c
+	int			 nattr = 4;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Petr Šabata 81d24c
@@ -819,14 +978,15 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	memset(&key_attr, 0, sizeof(key_attr));
Petr Šabata 81d24c
 	key_attr[0].type = CKA_ID;
Petr Šabata 81d24c
-	key_attr[1].type = CKA_MODULUS;
Petr Šabata 81d24c
-	key_attr[2].type = CKA_PUBLIC_EXPONENT;
Petr Šabata 81d24c
+	key_attr[1].type = CKA_LABEL;
Petr Šabata 81d24c
+	key_attr[2].type = CKA_MODULUS;
Petr Šabata 81d24c
+	key_attr[3].type = CKA_PUBLIC_EXPONENT;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* figure out size of the attributes */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		return (NULL);
Petr Šabata 81d24c
@@ -838,18 +998,19 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 	 * XXX assumes CKA_ID is always first.
Petr Šabata 81d24c
 	 */
Petr Šabata 81d24c
 	if (key_attr[1].ulValueLen == 0 ||
Petr Šabata 81d24c
-	    key_attr[2].ulValueLen == 0) {
Petr Šabata 81d24c
+	    key_attr[2].ulValueLen == 0 ||
Petr Šabata 81d24c
+	    key_attr[3].ulValueLen == 0) {
Petr Šabata 81d24c
 		error("invalid attribute length");
Petr Šabata 81d24c
 		return (NULL);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* allocate buffers for attributes */
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		if (key_attr[i].ulValueLen > 0)
Petr Šabata 81d24c
 			key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* retrieve ID, modulus and public exponent of RSA key */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
@@ -861,8 +1022,8 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
Petr Šabata 81d24c
-	rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
Petr Šabata 81d24c
+	rsa_n = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
Petr Šabata 81d24c
+	rsa_e = BN_bin2bn(key_attr[3].pValue, key_attr[3].ulValueLen, NULL);
Petr Šabata 81d24c
 	if (rsa_n == NULL || rsa_e == NULL) {
Petr Šabata 81d24c
 		error("BN_bin2bn failed");
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
@@ -871,7 +1032,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 		fatal("%s: set key", __func__);
Petr Šabata 81d24c
 	rsa_n = rsa_e = NULL; /* transferred */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
Petr Šabata 81d24c
+	if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], rsa))
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	key = sshkey_new(KEY_UNSPEC);
Petr Šabata 81d24c
@@ -886,7 +1047,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 	rsa = NULL;	/* now owned by key */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 fail:
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		free(key_attr[i].pValue);
Petr Šabata 81d24c
 	RSA_free(rsa);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
@@ -897,7 +1058,8 @@ static int
Petr Šabata 81d24c
 pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
     CK_OBJECT_HANDLE *obj, struct sshkey **keyp, char **labelp)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 cert_attr[3];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 cert_attr[4];
Petr Šabata 81d24c
+	int			 nattr = 4;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Petr Šabata 81d24c
@@ -921,14 +1083,15 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	memset(&cert_attr, 0, sizeof(cert_attr));
Petr Šabata 81d24c
 	cert_attr[0].type = CKA_ID;
Petr Šabata 81d24c
-	cert_attr[1].type = CKA_SUBJECT;
Petr Šabata 81d24c
-	cert_attr[2].type = CKA_VALUE;
Petr Šabata 81d24c
+	cert_attr[1].type = CKA_LABEL;
Petr Šabata 81d24c
+	cert_attr[2].type = CKA_SUBJECT;
Petr Šabata 81d24c
+	cert_attr[3].type = CKA_VALUE;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* figure out size of the attributes */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		return -1;
Petr Šabata 81d24c
@@ -940,18 +1103,19 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 	 * XXX assumes CKA_ID is always first.
Petr Šabata 81d24c
 	 */
Petr Šabata 81d24c
 	if (cert_attr[1].ulValueLen == 0 ||
Petr Šabata 81d24c
-	    cert_attr[2].ulValueLen == 0) {
Petr Šabata 81d24c
+	    cert_attr[2].ulValueLen == 0 ||
Petr Šabata 81d24c
+	    cert_attr[3].ulValueLen == 0) {
Petr Šabata 81d24c
 		error("invalid attribute length");
Petr Šabata 81d24c
 		return -1;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* allocate buffers for attributes */
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		if (cert_attr[i].ulValueLen > 0)
Petr Šabata 81d24c
 			cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* retrieve ID, subject and value of certificate */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		goto out;
Petr Šabata 81d24c
@@ -965,8 +1129,8 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 		subject = xstrdup("invalid subject");
Petr Šabata 81d24c
 	X509_NAME_free(x509_name);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	cp = cert_attr[2].pValue;
Petr Šabata 81d24c
-	if ((x509 = d2i_X509(NULL, &cp, cert_attr[2].ulValueLen)) == NULL) {
Petr Šabata 81d24c
+	cp = cert_attr[3].pValue;
Petr Šabata 81d24c
+	if ((x509 = d2i_X509(NULL, &cp, cert_attr[3].ulValueLen)) == NULL) {
Petr Šabata 81d24c
 		error("d2i_x509 failed");
Petr Šabata 81d24c
 		goto out;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
@@ -986,7 +1150,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 			goto out;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-		if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
Petr Šabata 81d24c
+		if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], rsa))
Petr Šabata 81d24c
 			goto out;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 		key = sshkey_new(KEY_UNSPEC);
Petr Šabata 81d24c
@@ -1016,7 +1180,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 			goto out;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-		if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
Petr Šabata 81d24c
+		if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], ec))
Petr Šabata 81d24c
 			goto out;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 		key = sshkey_new(KEY_UNSPEC);
Petr Šabata 81d24c
@@ -1036,7 +1200,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 		goto out;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
  out:
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		free(cert_attr[i].pValue);
Petr Šabata 81d24c
 	X509_free(x509);
Petr Šabata 81d24c
 	RSA_free(rsa);
Petr Šabata 81d24c
@@ -1071,11 +1235,12 @@ have_rsa_key(const RSA *rsa)
Petr Šabata 81d24c
  */
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
-    struct sshkey ***keysp, char ***labelsp, int *nkeys)
Petr Šabata 81d24c
+    struct sshkey ***keysp, char ***labelsp, int *nkeys, struct pkcs11_uri *uri)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct sshkey		*key = NULL;
Petr Šabata 81d24c
 	CK_OBJECT_CLASS		 key_class;
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 key_attr[1];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 key_attr[3];
Petr Šabata 81d24c
+	int			 nattr = 1;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Petr Šabata 81d24c
@@ -1092,10 +1257,23 @@ pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 	key_attr[0].pValue = &key_class;
Petr Šabata 81d24c
 	key_attr[0].ulValueLen = sizeof(key_class);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	if (uri->id != NULL) {
Petr Šabata 81d24c
+		key_attr[nattr].type = CKA_ID;
Petr Šabata 81d24c
+		key_attr[nattr].pValue = uri->id;
Petr Šabata 81d24c
+		key_attr[nattr].ulValueLen = uri->id_len;
Petr Šabata 81d24c
+		nattr++;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (uri->object != NULL) {
Petr Šabata 81d24c
+		key_attr[nattr].type = CKA_LABEL;
Petr Šabata 81d24c
+		key_attr[nattr].pValue = uri->object;
Petr Šabata 81d24c
+		key_attr[nattr].ulValueLen = strlen(uri->object);
Petr Šabata 81d24c
+		nattr++;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	rv = f->C_FindObjectsInit(session, key_attr, 1);
Petr Šabata 81d24c
+	rv = f->C_FindObjectsInit(session, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_FindObjectsInit failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
@@ -1175,11 +1353,12 @@ fail:
Petr Šabata 81d24c
  */
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
-    struct sshkey ***keysp, char ***labelsp, int *nkeys)
Petr Šabata 81d24c
+    struct sshkey ***keysp, char ***labelsp, int *nkeys, struct pkcs11_uri *uri)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct sshkey		*key = NULL;
Petr Šabata 81d24c
 	CK_OBJECT_CLASS		 key_class;
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 key_attr[2];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 key_attr[3];
Petr Šabata 81d24c
+	int			 nattr = 1;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Petr Šabata 81d24c
@@ -1195,10 +1374,23 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 	key_attr[0].pValue = &key_class;
Petr Šabata 81d24c
 	key_attr[0].ulValueLen = sizeof(key_class);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	if (uri->id != NULL) {
Petr Šabata 81d24c
+		key_attr[nattr].type = CKA_ID;
Petr Šabata 81d24c
+		key_attr[nattr].pValue = uri->id;
Petr Šabata 81d24c
+		key_attr[nattr].ulValueLen = uri->id_len;
Petr Šabata 81d24c
+		nattr++;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (uri->object != NULL) {
Petr Šabata 81d24c
+		key_attr[nattr].type = CKA_LABEL;
Petr Šabata 81d24c
+		key_attr[nattr].pValue = uri->object;
Petr Šabata 81d24c
+		key_attr[nattr].ulValueLen = strlen(uri->object);
Petr Šabata 81d24c
+		nattr++;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	rv = f->C_FindObjectsInit(session, key_attr, 1);
Petr Šabata 81d24c
+	rv = f->C_FindObjectsInit(session, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_FindObjectsInit failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
@@ -1466,16 +1658,10 @@ pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 #endif /* WITH_PKCS11_KEYGEN */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-/*
Petr Šabata 81d24c
- * register a new provider, fails if provider already exists. if
Petr Šabata 81d24c
- * keyp is provided, fetch keys.
Petr Šabata 81d24c
- */
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
-pkcs11_register_provider(char *provider_id, char *pin,
Petr Šabata 81d24c
-    struct sshkey ***keyp, char ***labelsp,
Petr Šabata 81d24c
-    struct pkcs11_provider **providerp, CK_ULONG user)
Petr Šabata 81d24c
+pkcs11_initialize_provider(struct pkcs11_uri *uri, struct pkcs11_provider **providerp)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	int nkeys, need_finalize = 0;
Petr Šabata 81d24c
+	int need_finalize = 0;
Petr Šabata 81d24c
 	int ret = -1;
Petr Šabata 81d24c
 	struct pkcs11_provider *p = NULL;
Petr Šabata 81d24c
 	void *handle = NULL;
Petr Šabata 81d24c
@@ -1484,167 +1670,303 @@ pkcs11_register_provider(char *provider_id, char *pin,
Petr Šabata 81d24c
 	CK_FUNCTION_LIST *f = NULL;
Petr Šabata 81d24c
 	CK_TOKEN_INFO *token;
Petr Šabata 81d24c
 	CK_ULONG i;
Petr Šabata 81d24c
-
Petr Šabata 81d24c
-	if (providerp == NULL)
Petr Šabata 81d24c
+	char *provider_module = NULL;
Petr Šabata 81d24c
+	struct pkcs11_module *m = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* if no provider specified, fallback to p11-kit */
Petr Šabata 81d24c
+	if (uri->module_path == NULL) {
Petr Šabata 81d24c
+#ifdef PKCS11_DEFAULT_PROVIDER
Petr Šabata 81d24c
+		provider_module = strdup(PKCS11_DEFAULT_PROVIDER);
Petr Šabata 81d24c
+#else
Petr Šabata 81d24c
+		error("%s: No module path provided", __func__);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
-	*providerp = NULL;
Petr Šabata 81d24c
-
Petr Šabata 81d24c
-	if (keyp != NULL)
Petr Šabata 81d24c
-		*keyp = NULL;
Petr Šabata 81d24c
-	if (labelsp != NULL)
Petr Šabata 81d24c
-		*labelsp = NULL;
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		provider_module = strdup(uri->module_path);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (pkcs11_provider_lookup(provider_id) != NULL) {
Petr Šabata 81d24c
-		debug("%s: provider already registered: %s",
Petr Šabata 81d24c
-		    __func__, provider_id);
Petr Šabata 81d24c
-		goto fail;
Petr Šabata 81d24c
+	p = xcalloc(1, sizeof(*p));
Petr Šabata 81d24c
+	p->name = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL
Petr Šabata 81d24c
+	   && m->valid) {
Petr Šabata 81d24c
+		debug("%s: provider module already initialized: %s",
Petr Šabata 81d24c
+		    __func__, provider_module);
Petr Šabata 81d24c
+		free(provider_module);
Petr Šabata 81d24c
+		/* Skip the initialization of PKCS#11 module */
Petr Šabata 81d24c
+		m->refcount++;
Petr Šabata 81d24c
+		p->module = m;
Petr Šabata 81d24c
+		p->valid = 1;
Petr Šabata 81d24c
+		TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
Petr Šabata 81d24c
+		p->refcount++;	/* add to provider list */
Petr Šabata 81d24c
+		*providerp = p;
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		m = xcalloc(1, sizeof(*m));
Petr Šabata 81d24c
+		p->module = m;
Petr Šabata 81d24c
+		m->refcount++;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	/* open shared pkcs11-library */
Petr Šabata 81d24c
-	if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
Petr Šabata 81d24c
-		error("dlopen %s failed: %s", provider_id, dlerror());
Petr Šabata 81d24c
+	if ((handle = dlopen(provider_module, RTLD_NOW)) == NULL) {
Petr Šabata 81d24c
+		error("dlopen %s failed: %s", provider_module, dlerror());
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
Petr Šabata 81d24c
 		error("dlsym(C_GetFunctionList) failed: %s", dlerror());
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	p = xcalloc(1, sizeof(*p));
Petr Šabata 81d24c
-	p->name = xstrdup(provider_id);
Petr Šabata 81d24c
-	p->handle = handle;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	p->module->handle = handle;
Petr Šabata 81d24c
 	/* setup the pkcs11 callbacks */
Petr Šabata 81d24c
 	if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetFunctionList for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	p->function_list = f;
Petr Šabata 81d24c
+	m->function_list = f;
Petr Šabata 81d24c
 	if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_Initialize for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	need_finalize = 1;
Petr Šabata 81d24c
-	if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
Petr Šabata 81d24c
+	if ((rv = f->C_GetInfo(&m->info)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetInfo for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
Petr Šabata 81d24c
-	rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
Petr Šabata 81d24c
+	rmspace(m->info.manufacturerID, sizeof(m->info.manufacturerID));
Petr Šabata 81d24c
+	if (uri->lib_manuf != NULL &&
Petr Šabata 81d24c
+	    strcmp(uri->lib_manuf, m->info.manufacturerID)) {
Petr Šabata 81d24c
+		debug("%s: Skipping provider %s not matching library_manufacturer",
Petr Šabata 81d24c
+		    __func__, m->info.manufacturerID);
Petr Šabata 81d24c
+		goto fail;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	rmspace(m->info.libraryDescription, sizeof(m->info.libraryDescription));
Petr Šabata 81d24c
 	debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d"
Petr Šabata 81d24c
 	    " libraryDescription <%s> libraryVersion %d.%d",
Petr Šabata 81d24c
-	    provider_id,
Petr Šabata 81d24c
-	    p->info.manufacturerID,
Petr Šabata 81d24c
-	    p->info.cryptokiVersion.major,
Petr Šabata 81d24c
-	    p->info.cryptokiVersion.minor,
Petr Šabata 81d24c
-	    p->info.libraryDescription,
Petr Šabata 81d24c
-	    p->info.libraryVersion.major,
Petr Šabata 81d24c
-	    p->info.libraryVersion.minor);
Petr Šabata 81d24c
-	if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
Petr Šabata 81d24c
+	    provider_module,
Petr Šabata 81d24c
+	    m->info.manufacturerID,
Petr Šabata 81d24c
+	    m->info.cryptokiVersion.major,
Petr Šabata 81d24c
+	    m->info.cryptokiVersion.minor,
Petr Šabata 81d24c
+	    m->info.libraryDescription,
Petr Šabata 81d24c
+	    m->info.libraryVersion.major,
Petr Šabata 81d24c
+	    m->info.libraryVersion.minor);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &m->nslots)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetSlotList failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if (p->nslots == 0) {
Petr Šabata 81d24c
+	if (m->nslots == 0) {
Petr Šabata 81d24c
 		debug("%s: provider %s returned no slots", __func__,
Petr Šabata 81d24c
-		    provider_id);
Petr Šabata 81d24c
+		    provider_module);
Petr Šabata 81d24c
 		ret = -SSH_PKCS11_ERR_NO_SLOTS;
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
Petr Šabata 81d24c
-	if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots))
Petr Šabata 81d24c
+	m->slotlist = xcalloc(m->nslots, sizeof(CK_SLOT_ID));
Petr Šabata 81d24c
+	if ((rv = f->C_GetSlotList(CK_TRUE, m->slotlist, &m->nslots))
Petr Šabata 81d24c
 	    != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetSlotList for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
Petr Šabata 81d24c
 	p->valid = 1;
Petr Šabata 81d24c
-	nkeys = 0;
Petr Šabata 81d24c
-	for (i = 0; i < p->nslots; i++) {
Petr Šabata 81d24c
-		token = &p->slotinfo[i].token;
Petr Šabata 81d24c
-		if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
Petr Šabata 81d24c
+	m->slotinfo = xcalloc(m->nslots, sizeof(struct pkcs11_slotinfo));
Petr Šabata 81d24c
+	m->valid = 1;
Petr Šabata 81d24c
+	for (i = 0; i < m->nslots; i++) {
Petr Šabata 81d24c
+		token = &m->slotinfo[i].token;
Petr Šabata 81d24c
+		if ((rv = f->C_GetTokenInfo(m->slotlist[i], token))
Petr Šabata 81d24c
 		    != CKR_OK) {
Petr Šabata 81d24c
 			error("C_GetTokenInfo for provider %s slot %lu "
Petr Šabata 81d24c
-			    "failed: %lu", provider_id, (unsigned long)i, rv);
Petr Šabata 81d24c
+			    "failed: %lu", provider_module, (unsigned long)i, rv);
Petr Šabata 81d24c
+			token->flags = 0;
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
+		rmspace(token->label, sizeof(token->label));
Petr Šabata 81d24c
+		rmspace(token->manufacturerID, sizeof(token->manufacturerID));
Petr Šabata 81d24c
+		rmspace(token->model, sizeof(token->model));
Petr Šabata 81d24c
+		rmspace(token->serialNumber, sizeof(token->serialNumber));
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	m->module_path = provider_module;
Petr Šabata 81d24c
+	provider_module = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* insert unconditionally -- remove if there will be no keys later */
Petr Šabata 81d24c
+	TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
Petr Šabata 81d24c
+	p->refcount++;	/* add to provider list */
Petr Šabata 81d24c
+	*providerp = p;
Petr Šabata 81d24c
+	return 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+fail:
Petr Šabata 81d24c
+	if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
Petr Šabata 81d24c
+		error("C_Finalize for provider %s failed: %lu",
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
+	free(provider_module);
Petr Šabata 81d24c
+	if (m) {
Petr Šabata 81d24c
+		free(m->slotlist);
Petr Šabata 81d24c
+		free(m);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (p) {
Petr Šabata 81d24c
+		free(p->name);
Petr Šabata 81d24c
+		free(p);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (handle)
Petr Šabata 81d24c
+		dlclose(handle);
Petr Šabata 81d24c
+	return ret;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * register a new provider, fails if provider already exists. if
Petr Šabata 81d24c
+ * keyp is provided, fetch keys.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+static int
Petr Šabata 81d24c
+pkcs11_register_provider_by_uri(struct pkcs11_uri *uri, char *pin,
Petr Šabata 81d24c
+    struct sshkey ***keyp, char ***labelsp, struct pkcs11_provider **providerp,
Petr Šabata 81d24c
+    CK_ULONG user)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	int nkeys;
Petr Šabata 81d24c
+	int ret = -1;
Petr Šabata 81d24c
+	struct pkcs11_provider *p = NULL;
Petr Šabata 81d24c
+	CK_ULONG i;
Petr Šabata 81d24c
+	CK_TOKEN_INFO *token;
Petr Šabata 81d24c
+	char *provider_uri = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (providerp == NULL)
Petr Šabata 81d24c
+		goto fail;
Petr Šabata 81d24c
+	*providerp = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (keyp != NULL)
Petr Šabata 81d24c
+		*keyp = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((ret = pkcs11_initialize_provider(uri, &p)) != 0) {
Petr Šabata 81d24c
+		goto fail;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	provider_uri = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+	if (pin == NULL && uri->pin != NULL) {
Petr Šabata 81d24c
+		pin = uri->pin;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	nkeys = 0;
Petr Šabata 81d24c
+	for (i = 0; i < p->module->nslots; i++) {
Petr Šabata 81d24c
+		token = &p->module->slotinfo[i].token;
Petr Šabata 81d24c
 		if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
Petr Šabata 81d24c
 			debug2("%s: ignoring uninitialised token in "
Petr Šabata 81d24c
 			    "provider %s slot %lu", __func__,
Petr Šabata 81d24c
-			    provider_id, (unsigned long)i);
Petr Šabata 81d24c
+			    provider_uri, (unsigned long)i);
Petr Šabata 81d24c
+			continue;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		if (uri->token != NULL &&
Petr Šabata 81d24c
+		    strcmp(token->label, uri->token) != 0) {
Petr Šabata 81d24c
+			debug2("%s: ignoring token not matching label (%s) "
Petr Šabata 81d24c
+			    "specified by PKCS#11 URI in slot %lu", __func__,
Petr Šabata 81d24c
+			    token->label, (unsigned long)i);
Petr Šabata 81d24c
+			continue;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		if (uri->manuf != NULL &&
Petr Šabata 81d24c
+		    strcmp(token->manufacturerID, uri->manuf) != 0) {
Petr Šabata 81d24c
+			debug2("%s: ignoring token not matching requrested "
Petr Šabata 81d24c
+			    "manufacturerID (%s) specified by PKCS#11 URI in "
Petr Šabata 81d24c
+			    "slot %lu", __func__,
Petr Šabata 81d24c
+			    token->manufacturerID, (unsigned long)i);
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
-		rmspace(token->label, sizeof(token->label));
Petr Šabata 81d24c
-		rmspace(token->manufacturerID, sizeof(token->manufacturerID));
Petr Šabata 81d24c
-		rmspace(token->model, sizeof(token->model));
Petr Šabata 81d24c
-		rmspace(token->serialNumber, sizeof(token->serialNumber));
Petr Šabata 81d24c
 		debug("provider %s slot %lu: label <%s> manufacturerID <%s> "
Petr Šabata 81d24c
 		    "model <%s> serial <%s> flags 0x%lx",
Petr Šabata 81d24c
-		    provider_id, (unsigned long)i,
Petr Šabata 81d24c
+		    provider_uri, (unsigned long)i,
Petr Šabata 81d24c
 		    token->label, token->manufacturerID, token->model,
Petr Šabata 81d24c
 		    token->serialNumber, token->flags);
Petr Šabata 81d24c
 		/*
Petr Šabata 81d24c
-		 * open session, login with pin and retrieve public
Petr Šabata 81d24c
-		 * keys (if keyp is provided)
Petr Šabata 81d24c
+		 * open session if not yet openend, login with pin and
Petr Šabata 81d24c
+		 * retrieve public keys (if keyp is provided)
Petr Šabata 81d24c
 		 */
Petr Šabata 81d24c
-		if ((ret = pkcs11_open_session(p, i, pin, user)) != 0 ||
Petr Šabata 81d24c
+		if ((p->module->slotinfo[i].session != 0 ||
Petr Šabata 81d24c
+		    (ret = pkcs11_open_session(p, i, pin, user)) != 0) && /* ??? */
Petr Šabata 81d24c
 		    keyp == NULL)
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
-		pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
Petr Šabata 81d24c
-		pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
Petr Šabata 81d24c
-		if (nkeys == 0 && !p->slotinfo[i].logged_in &&
Petr Šabata 81d24c
+		pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+		pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+		if (nkeys == 0 && !p->module->slotinfo[i].logged_in &&
Petr Šabata 81d24c
 		    pkcs11_interactive) {
Petr Šabata 81d24c
 			/*
Petr Šabata 81d24c
 			 * Some tokens require login before they will
Petr Šabata 81d24c
 			 * expose keys.
Petr Šabata 81d24c
 			 */
Petr Šabata 81d24c
-			if (pkcs11_login_slot(p, &p->slotinfo[i],
Petr Šabata 81d24c
+			debug3("%s: Trying to login as there were no keys found",
Petr Šabata 81d24c
+			    __func__);
Petr Šabata 81d24c
+			if (pkcs11_login_slot(p, &p->module->slotinfo[i],
Petr Šabata 81d24c
 			    CKU_USER) < 0) {
Petr Šabata 81d24c
 				error("login failed");
Petr Šabata 81d24c
 				continue;
Petr Šabata 81d24c
 			}
Petr Šabata 81d24c
-			pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
Petr Šabata 81d24c
-			pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
Petr Šabata 81d24c
+			pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+			pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		if (nkeys == 0 && uri->object != NULL) {
Petr Šabata 81d24c
+			debug3("%s: No keys found. Retrying without label (%s) ",
Petr Šabata 81d24c
+			    __func__, uri->object);
Petr Šabata 81d24c
+			/* Try once more without the label filter */
Petr Šabata 81d24c
+			char *label = uri->object;
Petr Šabata 81d24c
+			uri->object = NULL; /* XXX clone uri? */
Petr Šabata 81d24c
+			pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+			pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+			uri->object = label;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
+	pin = NULL; /* Will be cleaned up with URI */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* now owned by caller */
Petr Šabata 81d24c
 	*providerp = p;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
Petr Šabata 81d24c
-	p->refcount++;	/* add to provider list */
Petr Šabata 81d24c
-
Petr Šabata 81d24c
+	free(provider_uri);
Petr Šabata 81d24c
 	return (nkeys);
Petr Šabata 81d24c
 fail:
Petr Šabata 81d24c
-	if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
Petr Šabata 81d24c
-		error("C_Finalize for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
 	if (p) {
Petr Šabata 81d24c
-		free(p->name);
Petr Šabata 81d24c
-		free(p->slotlist);
Petr Šabata 81d24c
-		free(p->slotinfo);
Petr Šabata 81d24c
-		free(p);
Petr Šabata 81d24c
+ 		TAILQ_REMOVE(&pkcs11_providers, p, next);
Petr Šabata 81d24c
+		pkcs11_provider_unref(p);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if (handle)
Petr Šabata 81d24c
-		dlclose(handle);
Petr Šabata 81d24c
 	if (ret > 0)
Petr Šabata 81d24c
 		ret = -1;
Petr Šabata 81d24c
 	return (ret);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-/*
Petr Šabata 81d24c
- * register a new provider and get number of keys hold by the token,
Petr Šabata 81d24c
- * fails if provider already exists
Petr Šabata 81d24c
- */
Petr Šabata 81d24c
+static int
Petr Šabata 81d24c
+pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
Petr Šabata 81d24c
+    char ***labelsp, struct pkcs11_provider **providerp, CK_ULONG user)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct pkcs11_uri *uri = NULL;
Petr Šabata 81d24c
+	int r;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	debug("%s: called, provider_id = %s", __func__, provider_id);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	if (uri == NULL)
Petr Šabata 81d24c
+		fatal("failed to init PKCS#11 URI");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+	    strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+		if (pkcs11_uri_parse(provider_id, uri) != 0)
Petr Šabata 81d24c
+			fatal("Failed to parse PKCS#11 URI");
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		uri->module_path = strdup(provider_id);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	r = pkcs11_register_provider_by_uri(uri, pin, keyp, labelsp, providerp, user);
Petr Šabata 81d24c
+	pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return r;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
-pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp,
Petr Šabata 81d24c
-    char ***labelsp)
Petr Šabata 81d24c
+pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin,
Petr Šabata 81d24c
+    struct sshkey ***keyp, char ***labelsp)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	struct pkcs11_provider *p = NULL;
Petr Šabata 81d24c
 	int nkeys;
Petr Šabata 81d24c
+	struct pkcs11_provider *p = NULL;
Petr Šabata 81d24c
+	char *provider_uri = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	debug("%s: called, provider_uri = %s", __func__, provider_uri);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	nkeys = pkcs11_register_provider(provider_id, pin, keyp, labelsp,
Petr Šabata 81d24c
-	    &p, CKU_USER);
Petr Šabata 81d24c
+	nkeys = pkcs11_register_provider_by_uri(uri, pin, keyp, labelsp, &p, CKU_USER);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* no keys found or some other error, de-register provider */
Petr Šabata 81d24c
 	if (nkeys <= 0 && p != NULL) {
Petr Šabata 81d24c
@@ -1652,7 +1974,37 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp,
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	if (nkeys == 0)
Petr Šabata 81d24c
 		debug("%s: provider %s returned no keys", __func__,
Petr Šabata 81d24c
-		    provider_id);
Petr Šabata 81d24c
+		    provider_uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	free(provider_uri);
Petr Šabata 81d24c
+	return nkeys;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * register a new provider and get number of keys hold by the token,
Petr Šabata 81d24c
+ * fails if provider already exists
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+pkcs11_add_provider(char *provider_id, char *pin,
Petr Šabata 81d24c
+    struct sshkey ***keyp, char ***labelsp)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct pkcs11_uri *uri;
Petr Šabata 81d24c
+	int nkeys;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	if (uri == NULL)
Petr Šabata 81d24c
+		fatal("Failed to init PKCS#11 URI");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+	    strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+		if (pkcs11_uri_parse(provider_id, uri) != 0)
Petr Šabata 81d24c
+			fatal("Failed to parse PKCS#11 URI");
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		uri->module_path = strdup(provider_id);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	nkeys = pkcs11_add_provider_by_uri(uri, pin, keyp, labelsp);
Petr Šabata 81d24c
+	pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	return (nkeys);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
@@ -1674,7 +2026,7 @@ pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
Petr Šabata 81d24c
 		debug("%s: provider \"%s\" available", __func__, provider_id);
Petr Šabata 81d24c
-	else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, NULL,
Petr Šabata 81d24c
+	else if ((rv = pkcs11_register_provider(provider_id, pin, NULL, NULL,
Petr Šabata 81d24c
 	    &p, CKU_SO)) < 0) {
Petr Šabata 81d24c
 		debug("%s: could not register provider %s", __func__,
Petr Šabata 81d24c
 		    provider_id);
Petr Šabata 81d24c
@@ -1746,8 +2098,8 @@ pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
Petr Šabata 81d24c
 		debug("%s: using provider \"%s\"", __func__, provider_id);
Petr Šabata 81d24c
-	} else if (pkcs11_register_provider(provider_id, pin, NULL, NULL, &p,
Petr Šabata 81d24c
-	    CKU_SO) < 0) {
Petr Šabata 81d24c
+	} else if ((rv = pkcs11_register_provider(provider_id, pin, NULL, NULL,
Petr Šabata 81d24c
+	    &p, CKU_SO)) < 0) {
Petr Šabata 81d24c
 		debug("%s: could not register provider %s", __func__,
Petr Šabata 81d24c
 		    provider_id);
Petr Šabata 81d24c
 		goto out;
Petr Šabata 81d24c
diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h
Petr Šabata 81d24c
index 81f1d7c5..feaf74de 100644
Petr Šabata 81d24c
--- a/ssh-pkcs11.h
Petr Šabata 81d24c
+++ b/ssh-pkcs11.h
Petr Šabata 81d24c
@@ -22,10 +22,14 @@
Petr Šabata 81d24c
 #define	SSH_PKCS11_ERR_PIN_REQUIRED		4
Petr Šabata 81d24c
 #define	SSH_PKCS11_ERR_PIN_LOCKED		5
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#include "ssh-pkcs11-uri.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 int	pkcs11_init(int);
Petr Šabata 81d24c
 void	pkcs11_terminate(void);
Petr Šabata 81d24c
 int	pkcs11_add_provider(char *, char *, struct sshkey ***, char ***);
Petr Šabata 81d24c
+int	pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***, char ***);
Petr Šabata 81d24c
 int	pkcs11_del_provider(char *);
Petr Šabata 81d24c
+int	pkcs11_uri_write(const struct sshkey *, FILE *);
Petr Šabata 81d24c
 #ifdef WITH_PKCS11_KEYGEN
Petr Šabata 81d24c
 struct sshkey *
Petr Šabata 81d24c
 	pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int,
Petr Šabata 81d24c
diff --git a/ssh.c b/ssh.c
Petr Šabata 81d24c
index 15aee569..976844cb 100644
Petr Šabata 81d24c
--- a/ssh.c
Petr Šabata 81d24c
+++ b/ssh.c
Petr Šabata 81d24c
@@ -795,6 +795,14 @@ main(int ac, char **av)
Petr Šabata 81d24c
 			options.gss_deleg_creds = 1;
Petr Šabata 81d24c
 			break;
Petr Šabata 81d24c
 		case 'i':
Petr Šabata 81d24c
+#ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+			if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+			    strncmp(optarg, PKCS11_URI_SCHEME,
Petr Šabata 81d24c
+			    strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+				add_identity_file(&options, NULL, optarg, 1);
Petr Šabata 81d24c
+				break;
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 			p = tilde_expand_filename(optarg, getuid());
Petr Šabata 81d24c
 			if (stat(p, &st) == -1)
Petr Šabata 81d24c
 				fprintf(stderr, "Warning: Identity file %s "
Petr Šabata 81d24c
@@ -1603,6 +1611,7 @@ main(int ac, char **av)
Petr Šabata 81d24c
 		free(options.certificate_files[i]);
Petr Šabata 81d24c
 		options.certificate_files[i] = NULL;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
+	pkcs11_terminate();
Petr Šabata 81d24c
 
Petr Šabata 81d24c
  skip_connect:
Petr Šabata 81d24c
 	exit_status = ssh_session2(ssh, pw);
Petr Šabata 81d24c
@@ -2076,6 +2085,45 @@ ssh_session2(struct ssh *ssh, struct passwd *pw)
Petr Šabata 81d24c
 	    options.escape_char : SSH_ESCAPECHAR_NONE, id);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+load_pkcs11_identity(char *pkcs11_uri, char *identity_files[],
Petr Šabata 81d24c
+    struct sshkey *identity_keys[], int *n_ids)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	int nkeys, i;
Petr Šabata 81d24c
+	struct sshkey **keys;
Petr Šabata 81d24c
+	struct pkcs11_uri *uri;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	debug("identity file '%s' from pkcs#11", pkcs11_uri);
Petr Šabata 81d24c
+	uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	if (uri == NULL)
Petr Šabata 81d24c
+		fatal("Failed to init PKCS#11 URI");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (pkcs11_uri_parse(pkcs11_uri, uri) != 0)
Petr Šabata 81d24c
+	fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* we need to merge URI and provider together */
Petr Šabata 81d24c
+	if (options.pkcs11_provider != NULL && uri->module_path == NULL)
Petr Šabata 81d24c
+		uri->module_path = strdup(options.pkcs11_provider);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
Petr Šabata 81d24c
+	    (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys, NULL)) > 0) {
Petr Šabata 81d24c
+		for (i = 0; i < nkeys; i++) {
Petr Šabata 81d24c
+			if (*n_ids >= SSH_MAX_IDENTITY_FILES) {
Petr Šabata 81d24c
+				sshkey_free(keys[i]);
Petr Šabata 81d24c
+				continue;
Petr Šabata 81d24c
+			}
Petr Šabata 81d24c
+			identity_keys[*n_ids] = keys[i];
Petr Šabata 81d24c
+			identity_files[*n_ids] = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+			(*n_ids)++;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		free(keys);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+#endif /* ENABLE_PKCS11 */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* Loads all IdentityFile and CertificateFile keys */
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
 load_public_identity_files(struct passwd *pw)
Petr Šabata 81d24c
@@ -2090,11 +2138,6 @@ load_public_identity_files(struct passwd *pw)
Petr Šabata 81d24c
 	char *certificate_files[SSH_MAX_CERTIFICATE_FILES];
Petr Šabata 81d24c
 	struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
Petr Šabata 81d24c
 	int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES];
Petr Šabata 81d24c
-#ifdef ENABLE_PKCS11
Petr Šabata 81d24c
-	struct sshkey **keys = NULL;
Petr Šabata 81d24c
-	char **comments = NULL;
Petr Šabata 81d24c
-	int nkeys;
Petr Šabata 81d24c
-#endif /* PKCS11 */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	n_ids = n_certs = 0;
Petr Šabata 81d24c
 	memset(identity_files, 0, sizeof(identity_files));
Petr Šabata 81d24c
@@ -2107,33 +2150,46 @@ load_public_identity_files(struct passwd *pw)
Petr Šabata 81d24c
 	    sizeof(certificate_file_userprovided));
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #ifdef ENABLE_PKCS11
Petr Šabata 81d24c
-	if (options.pkcs11_provider != NULL &&
Petr Šabata 81d24c
-	    options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
Petr Šabata 81d24c
-	    (pkcs11_init(!options.batch_mode) == 0) &&
Petr Šabata 81d24c
-	    (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL,
Petr Šabata 81d24c
-	    &keys, &comments)) > 0) {
Petr Šabata 81d24c
-		for (i = 0; i < nkeys; i++) {
Petr Šabata 81d24c
-			if (n_ids >= SSH_MAX_IDENTITY_FILES) {
Petr Šabata 81d24c
-				sshkey_free(keys[i]);
Petr Šabata 81d24c
-				free(comments[i]);
Petr Šabata 81d24c
-				continue;
Petr Šabata 81d24c
-			}
Petr Šabata 81d24c
-			identity_keys[n_ids] = keys[i];
Petr Šabata 81d24c
-			identity_files[n_ids] = comments[i]; /* transferred */
Petr Šabata 81d24c
-			n_ids++;
Petr Šabata 81d24c
-		}
Petr Šabata 81d24c
-		free(keys);
Petr Šabata 81d24c
-		free(comments);
Petr Šabata 81d24c
+	/* handle fallback from PKCS11Provider option */
Petr Šabata 81d24c
+	pkcs11_init(!options.batch_mode);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (options.pkcs11_provider != NULL) {
Petr Šabata 81d24c
+		struct pkcs11_uri *uri;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		uri = pkcs11_uri_init();
Petr Šabata 81d24c
+		if (uri == NULL)
Petr Šabata 81d24c
+			fatal("Failed to init PKCS#11 URI");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		/* Construct simple PKCS#11 URI to simplify access */
Petr Šabata 81d24c
+		uri->module_path = strdup(options.pkcs11_provider);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		/* Add it as any other IdentityFile */
Petr Šabata 81d24c
+		cp = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+		add_identity_file(&options, NULL, cp, 1);
Petr Šabata 81d24c
+		free(cp);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 #endif /* ENABLE_PKCS11 */
Petr Šabata 81d24c
 	for (i = 0; i < options.num_identity_files; i++) {
Petr Šabata 81d24c
+		char *name = options.identity_files[i];
Petr Šabata 81d24c
 		if (n_ids >= SSH_MAX_IDENTITY_FILES ||
Petr Šabata 81d24c
-		    strcasecmp(options.identity_files[i], "none") == 0) {
Petr Šabata 81d24c
+		    strcasecmp(name, "none") == 0) {
Petr Šabata 81d24c
 			free(options.identity_files[i]);
Petr Šabata 81d24c
 			options.identity_files[i] = NULL;
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
-		cp = tilde_expand_filename(options.identity_files[i], getuid());
Petr Šabata 81d24c
+#ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+		if (strlen(name) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+		    strncmp(name, PKCS11_URI_SCHEME,
Petr Šabata 81d24c
+		    strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+			load_pkcs11_identity(name, identity_files,
Petr Šabata 81d24c
+			    identity_keys, &n_ids);
Petr Šabata 81d24c
+			free(options.identity_files[i]);
Petr Šabata 81d24c
+			continue;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+#endif /* ENABLE_PKCS11 */
Petr Šabata 81d24c
+		cp = tilde_expand_filename(name, getuid());
Petr Šabata 81d24c
 		filename = default_client_percent_dollar_expand(cp,
Petr Šabata 81d24c
 		    pw->pw_dir, host, options.user, pw->pw_name);
Petr Šabata 81d24c
 		free(cp);
Petr Šabata 81d24c
diff --git a/ssh_config.5 b/ssh_config.5
Petr Šabata 81d24c
index 06a32d31..4b2763bd 100644
Petr Šabata 81d24c
--- a/ssh_config.5
Petr Šabata 81d24c
+++ b/ssh_config.5
Petr Šabata 81d24c
@@ -986,6 +986,21 @@ may also be used in conjunction with
Petr Šabata 81d24c
 .Cm CertificateFile
Petr Šabata 81d24c
 in order to provide any certificate also needed for authentication with
Petr Šabata 81d24c
 the identity.
Petr Šabata 81d24c
+.Pp
Petr Šabata 81d24c
+The authentication identity can be also specified in a form of PKCS#11 URI
Petr Šabata 81d24c
+starting with a string
Petr Šabata 81d24c
+.Cm pkcs11: .
Petr Šabata 81d24c
+There is supported a subset of the PKCS#11 URI as defined
Petr Šabata 81d24c
+in RFC 7512 (implemented path arguments
Petr Šabata 81d24c
+.Cm id ,
Petr Šabata 81d24c
+.Cm manufacturer ,
Petr Šabata 81d24c
+.Cm object ,
Petr Šabata 81d24c
+.Cm token
Petr Šabata 81d24c
+and query arguments
Petr Šabata 81d24c
+.Cm module-path
Petr Šabata 81d24c
+and
Petr Šabata 81d24c
+.Cm pin-value
Petr Šabata 81d24c
+). The URI can not be in quotes.
Petr Šabata 81d24c
 .It Cm IgnoreUnknown
Petr Šabata 81d24c
 Specifies a pattern-list of unknown options to be ignored if they are
Petr Šabata 81d24c
 encountered in configuration parsing.