vishalmishra434 / rpms / openssh

Forked from rpms/openssh a month ago
Clone
Jakub Jelen def1de
diff --git a/Makefile.in b/Makefile.in
Jakub Jelen def1de
index 6f001bb3..c9424f1e 100644
Jakub Jelen def1de
--- a/Makefile.in
Jakub Jelen def1de
+++ b/Makefile.in
Jakub Jelen def1de
@@ -93,7 +93,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
Jakub Jelen def1de
 	atomicio.o dispatch.o mac.o uuencode.o misc.o utf8.o \
Jakub Jelen def1de
 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
Jakub Jelen def1de
 	msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
Jakub Jelen def1de
-	ssh-pkcs11.o smult_curve25519_ref.o \
Jakub Jelen def1de
+	ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \
Jakub Jelen def1de
 	poly1305.o chacha.o cipher-chachapoly.o \
Jakub Jelen def1de
 	ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \
Jakub Jelen def1de
 	sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \
Jakub Jelen def1de
@@ -250,6 +250,8 @@ clean:	regressclean
Jakub Jelen def1de
 	rm -f regress/unittests/match/test_match$(EXEEXT)
Jakub Jelen def1de
 	rm -f regress/unittests/utf8/*.o
Jakub Jelen def1de
 	rm -f regress/unittests/utf8/test_utf8$(EXEEXT)
Jakub Jelen def1de
+	rm -f regress/unittests/pkcs11/*.o
Jakub Jelen def1de
+	rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT)
Jakub Jelen def1de
 	rm -f regress/misc/kexfuzz/*.o
Jakub Jelen def1de
 	rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT)
Jakub Jelen def1de
 	(cd openbsd-compat && $(MAKE) clean)
Jakub Jelen def1de
@@ -280,6 +282,8 @@ distclean:	regressclean
Jakub Jelen def1de
 	rm -f regress/unittests/match/test_match
Jakub Jelen def1de
 	rm -f regress/unittests/utf8/*.o
Jakub Jelen def1de
 	rm -f regress/unittests/utf8/test_utf8
Jakub Jelen def1de
+	rm -f regress/unittests/pkcs11/*.o
Jakub Jelen def1de
+	rm -f regress/unittests/pkcs11/test_pkcs11
Jakub Jelen def1de
 	rm -f regress/misc/kexfuzz/*.o
Jakub Jelen def1de
 	rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT)
Jakub Jelen def1de
 	(cd openbsd-compat && $(MAKE) distclean)
Jakub Jelen def1de
@@ -442,6 +446,7 @@ regress-prep:
Jakub Jelen def1de
 	$(MKDIR_P) `pwd`/regress/unittests/kex
Jakub Jelen def1de
 	$(MKDIR_P) `pwd`/regress/unittests/match
Jakub Jelen def1de
 	$(MKDIR_P) `pwd`/regress/unittests/utf8
Jakub Jelen def1de
+	$(MKDIR_P) `pwd`/regress/unittests/pkcs11
Jakub Jelen def1de
 	$(MKDIR_P) `pwd`/regress/misc/kexfuzz
Jakub Jelen def1de
 	[ -f `pwd`/regress/Makefile ] || \
Jakub Jelen def1de
 	    ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile
Jakub Jelen def1de
@@ -565,6 +570,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \
Jakub Jelen def1de
 	    regress/unittests/test_helper/libtest_helper.a \
Jakub Jelen def1de
 	    -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
Jakub Jelen def1de
 
Jakub Jelen def1de
+UNITTESTS_TEST_PKCS11_OBJS=\
Jakub Jelen def1de
+	regress/unittests/pkcs11/tests.o
Jakub Jelen def1de
+
Jakub Jelen def1de
+regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \
Jakub Jelen def1de
+    ${UNITTESTS_TEST_PKCS11_OBJS} \
Jakub Jelen def1de
+    regress/unittests/test_helper/libtest_helper.a libssh.a
Jakub Jelen def1de
+	$(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \
Jakub Jelen def1de
+	    regress/unittests/test_helper/libtest_helper.a \
Jakub Jelen def1de
+	    -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
Jakub Jelen def1de
+
Jakub Jelen def1de
 MISC_KEX_FUZZ_OBJS=\
Jakub Jelen def1de
 	regress/misc/kexfuzz/kexfuzz.o
Jakub Jelen def1de
 
Jakub Jelen def1de
@@ -585,6 +600,7 @@ regress-binaries: regress/modpipe$(EXEEXT) \
Jakub Jelen def1de
 	regress/unittests/kex/test_kex$(EXEEXT) \
Jakub Jelen def1de
 	regress/unittests/match/test_match$(EXEEXT) \
Jakub Jelen def1de
 	regress/unittests/utf8/test_utf8$(EXEEXT) \
Jakub Jelen def1de
+	regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \
Jakub Jelen def1de
 	regress/misc/kexfuzz/kexfuzz$(EXEEXT)
Jakub Jelen def1de
 
Jakub Jelen def1de
 tests interop-tests t-exec unit: regress-prep regress-binaries $(TARGETS)
Jakub Jelen def1de
diff --git a/authfd.c b/authfd.c
Jakub Jelen def1de
index 95348abf..5383df92 100644
Jakub Jelen def1de
--- a/authfd.c
Jakub Jelen def1de
+++ b/authfd.c
Jakub Jelen def1de
@@ -312,6 +312,8 @@ ssh_free_identitylist(struct ssh_identitylist *idl)
Jakub Jelen def1de
 		if (idl->comments != NULL)
Jakub Jelen def1de
 			free(idl->comments[i]);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
+	free(idl->keys);
Jakub Jelen def1de
+	free(idl->comments);
Jakub Jelen def1de
 	free(idl);
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
diff --git a/configure.ac b/configure.ac
Jakub Jelen def1de
index 30be6c18..82459746 100644
Jakub Jelen def1de
--- a/configure.ac
Jakub Jelen def1de
+++ b/configure.ac
Jakub Jelen def1de
@@ -1854,12 +1854,14 @@ AC_LINK_IFELSE(
Jakub Jelen def1de
 	[AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).])
Jakub Jelen def1de
 ])
Jakub Jelen def1de
 
Jakub Jelen def1de
+SCARD_MSG="yes"
Jakub Jelen def1de
 disable_pkcs11=
Jakub Jelen def1de
 AC_ARG_ENABLE([pkcs11],
Jakub Jelen def1de
 	[  --disable-pkcs11        disable PKCS#11 support code [no]],
Jakub Jelen def1de
 	[
Jakub Jelen def1de
 		if test "x$enableval" = "xno" ; then
Jakub Jelen def1de
 			disable_pkcs11=1
Jakub Jelen def1de
+			SCARD_MSG="no"
Jakub Jelen def1de
 		fi
Jakub Jelen def1de
 	]
Jakub Jelen def1de
 )
Jakub Jelen def1de
@@ -1875,6 +1877,40 @@ if test "x$openssl" = "xyes" && test "x$disable_pkcs11" = "x"; then
Jakub Jelen def1de
 	)
Jakub Jelen def1de
 fi
Jakub Jelen def1de
 
Jakub Jelen def1de
+# Check whether we have a p11-kit, we got default provider on command line
Jakub Jelen def1de
+DEFAULT_PKCS11_PROVIDER_MSG="no"
Jakub Jelen def1de
+AC_ARG_WITH([default-pkcs11-provider],
Jakub Jelen def1de
+	[  --with-default-pkcs11-provider[[=PATH]]   Use default pkcs11 provider (p11-kit detected by default)],
Jakub Jelen def1de
+	[ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then
Jakub Jelen def1de
+		if test "x$withval" = "xyes" ; then
Jakub Jelen def1de
+			AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no])
Jakub Jelen def1de
+			if test "x$PKGCONFIG" != "xno"; then
Jakub Jelen def1de
+				AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit])
Jakub Jelen def1de
+				if "$PKGCONFIG" "p11-kit-1"; then
Jakub Jelen def1de
+					AC_MSG_RESULT([yes])
Jakub Jelen def1de
+					use_pkgconfig_for_p11kit=yes
Jakub Jelen def1de
+				else
Jakub Jelen def1de
+					AC_MSG_RESULT([no])
Jakub Jelen def1de
+				fi
Jakub Jelen def1de
+			fi
Jakub Jelen def1de
+		else
Jakub Jelen def1de
+			PKCS11_PATH="${withval}"
Jakub Jelen def1de
+		fi
Jakub Jelen def1de
+		if test "x$use_pkgconfig_for_p11kit" = "xyes"; then
Jakub Jelen def1de
+			PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1`
Jakub Jelen def1de
+		fi
Jakub Jelen def1de
+		AC_CHECK_FILE("$PKCS11_PATH",
Jakub Jelen def1de
+			[ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)])
Jakub Jelen def1de
+			  DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH"
Jakub Jelen def1de
+			],
Jakub Jelen def1de
+			[ AC_MSG_ERROR([Requested PKCS11 provided not found]) ]
Jakub Jelen def1de
+		)
Jakub Jelen def1de
+	else
Jakub Jelen def1de
+		AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider])
Jakub Jelen def1de
+	fi ]
Jakub Jelen def1de
+)
Jakub Jelen def1de
+
Jakub Jelen def1de
+
Jakub Jelen def1de
 # IRIX has a const char return value for gai_strerror()
Jakub Jelen def1de
 AC_CHECK_FUNCS([gai_strerror], [
Jakub Jelen def1de
 	AC_DEFINE([HAVE_GAI_STRERROR])
Jakub Jelen def1de
@@ -5256,6 +5292,7 @@ echo "           Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG"
Jakub Jelen def1de
 echo "                  BSD Auth support: $BSD_AUTH_MSG"
Jakub Jelen def1de
 echo "              Random number source: $RAND_MSG"
Jakub Jelen def1de
 echo "             Privsep sandbox style: $SANDBOX_STYLE"
Jakub Jelen def1de
+echo "          Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG"
Jakub Jelen def1de
 
Jakub Jelen def1de
 echo ""
Jakub Jelen def1de
 
Jakub Jelen def1de
diff --git a/regress/Makefile b/regress/Makefile
Jakub Jelen def1de
index 925edf71..94bb25e9 100644
Jakub Jelen def1de
--- a/regress/Makefile
Jakub Jelen def1de
+++ b/regress/Makefile
Jakub Jelen def1de
@@ -109,9 +109,11 @@ CLEANFILES=	*.core actual agent-key.* authorized_keys_${USERNAME} \
Jakub Jelen def1de
 		known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \
Jakub Jelen def1de
 		modpipe netcat no_identity_config \
Jakub Jelen def1de
 		pidfile putty.rsa2 ready regress.log \
Jakub Jelen def1de
-		remote_pid revoked-* rsa rsa-agent rsa-agent.pub rsa.pub \
Jakub Jelen def1de
+		remote_pid revoked-* rsa rsa-agent rsa-agent.pub \
Jakub Jelen def1de
+		rsa-agent-cert.pub rsa.pub \
Jakub Jelen def1de
 		rsa1 rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \
Jakub Jelen def1de
-		rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \
Jakub Jelen def1de
+		pkcs11*.crt pkcs11*.key \
Jakub Jelen def1de
+		pkcs11.info rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \
Jakub Jelen def1de
 		scp-ssh-wrapper.scp setuid-allowed sftp-server.log \
Jakub Jelen def1de
 		sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \
Jakub Jelen def1de
 		ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \
Jakub Jelen def1de
@@ -231,6 +233,7 @@ unit:
Jakub Jelen def1de
 		V="" ; \
Jakub Jelen def1de
 		test "x${USE_VALGRIND}" = "x" || \
Jakub Jelen def1de
 		    V=${.CURDIR}/valgrind-unit.sh ; \
Jakub Jelen def1de
+		$$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \
Jakub Jelen def1de
 		$$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \
Jakub Jelen def1de
 		$$V ${.OBJDIR}/unittests/sshkey/test_sshkey \
Jakub Jelen def1de
 			-d ${.CURDIR}/unittests/sshkey/testdata ; \
Jakub Jelen def1de
diff --git a/regress/agent-pkcs11.sh b/regress/agent-pkcs11.sh
Jakub Jelen def1de
index 5205d906..5ca49be5 100644
Jakub Jelen def1de
--- a/regress/agent-pkcs11.sh
Jakub Jelen def1de
+++ b/regress/agent-pkcs11.sh
Jakub Jelen def1de
@@ -29,6 +29,13 @@ fi
Jakub Jelen def1de
 
Jakub Jelen def1de
 test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist"
Jakub Jelen def1de
 
Jakub Jelen def1de
+# requires ssh-agent built with correct path to ssh-pkcs11-helper
Jakub Jelen def1de
+# otherwise it fails to start the helper
Jakub Jelen def1de
+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER"
Jakub Jelen def1de
+if [ $? -ne 0 ]; then
Jakub Jelen def1de
+	fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
 # setup environment for softhsm2 token
Jakub Jelen def1de
 DIR=$OBJ/SOFTHSM
Jakub Jelen def1de
 rm -rf $DIR
Jakub Jelen def1de
@@ -113,7 +120,7 @@ else
Jakub Jelen def1de
 	done
Jakub Jelen def1de
 
Jakub Jelen def1de
 	trace "remove pkcs11 keys"
Jakub Jelen def1de
-	echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1
Jakub Jelen def1de
+	${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1
Jakub Jelen def1de
 	r=$?
Jakub Jelen def1de
 	if [ $r -ne 0 ]; then
Jakub Jelen def1de
 		fail "ssh-add -e failed: exit code $r"
Jakub Jelen def1de
diff --git a/regress/pkcs11.sh b/regress/pkcs11.sh
Jakub Jelen def1de
new file mode 100644
Jakub Jelen def1de
index 00000000..19fc8169
Jakub Jelen def1de
--- /dev/null
Jakub Jelen def1de
+++ b/regress/pkcs11.sh
Jakub Jelen def1de
@@ -0,0 +1,352 @@
Jakub Jelen def1de
+#
Jakub Jelen def1de
+#  Copyright (c) 2017 Red Hat
Jakub Jelen def1de
+#
Jakub Jelen def1de
+#  Authors: Jakub Jelen <jjelen@redhat.com>
Jakub Jelen def1de
+#
Jakub Jelen def1de
+#  Permission to use, copy, modify, and distribute this software for any
Jakub Jelen def1de
+#  purpose with or without fee is hereby granted, provided that the above
Jakub Jelen def1de
+#  copyright notice and this permission notice appear in all copies.
Jakub Jelen def1de
+#
Jakub Jelen def1de
+#  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Jakub Jelen def1de
+#  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Jakub Jelen def1de
+#  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Jakub Jelen def1de
+#  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Jakub Jelen def1de
+#  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Jakub Jelen def1de
+#  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Jakub Jelen def1de
+#  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Jakub Jelen def1de
+
Jakub Jelen def1de
+tid="pkcs11 tests with soft token"
Jakub Jelen def1de
+
Jakub Jelen def1de
+try_token_libs() {
Jakub Jelen def1de
+	for _lib in "$@" ; do
Jakub Jelen def1de
+		if test -f "$_lib" ; then
Jakub Jelen def1de
+			verbose "Using token library $_lib"
Jakub Jelen def1de
+			TEST_SSH_PKCS11="$_lib"
Jakub Jelen def1de
+			return
Jakub Jelen def1de
+		fi
Jakub Jelen def1de
+	done
Jakub Jelen def1de
+	echo "skipped: Unable to find PKCS#11 token library"
Jakub Jelen def1de
+	exit 0
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+try_token_libs \
Jakub Jelen def1de
+	/usr/local/lib/softhsm/libsofthsm2.so \
Jakub Jelen def1de
+	/usr/lib64/pkcs11/libsofthsm2.so \
Jakub Jelen def1de
+	/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so
Jakub Jelen def1de
+
Jakub Jelen def1de
+TEST_SSH_PIN=1234
Jakub Jelen def1de
+TEST_SSH_SOPIN=12345678
Jakub Jelen def1de
+if [ "x$TEST_SSH_SSHPKCS11HELPER" != "x" ]; then
Jakub Jelen def1de
+	SSH_PKCS11_HELPER="${TEST_SSH_SSHPKCS11HELPER}"
Jakub Jelen def1de
+	export SSH_PKCS11_HELPER
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist"
Jakub Jelen def1de
+
Jakub Jelen def1de
+# requires ssh-agent built with correct path to ssh-pkcs11-helper
Jakub Jelen def1de
+# otherwise it fails to start the helper
Jakub Jelen def1de
+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER"
Jakub Jelen def1de
+if [ $? -ne 0 ]; then
Jakub Jelen def1de
+	fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+# setup environment for softhsm token
Jakub Jelen def1de
+DIR=$OBJ/SOFTHSM
Jakub Jelen def1de
+rm -rf $DIR
Jakub Jelen def1de
+TOKEN=$DIR/tokendir
Jakub Jelen def1de
+mkdir -p $TOKEN
Jakub Jelen def1de
+SOFTHSM2_CONF=$DIR/softhsm2.conf
Jakub Jelen def1de
+export SOFTHSM2_CONF
Jakub Jelen def1de
+cat > $SOFTHSM2_CONF << EOF
Jakub Jelen def1de
+# SoftHSM v2 configuration file
Jakub Jelen def1de
+directories.tokendir = ${TOKEN}
Jakub Jelen def1de
+objectstore.backend = file
Jakub Jelen def1de
+# ERROR, WARNING, INFO, DEBUG
Jakub Jelen def1de
+log.level = DEBUG
Jakub Jelen def1de
+# If CKF_REMOVABLE_DEVICE flag should be set
Jakub Jelen def1de
+slots.removable = false
Jakub Jelen def1de
+EOF
Jakub Jelen def1de
+out=$(softhsm2-util --init-token --free --label token-slot-0 --pin "$TEST_SSH_PIN" --so-pin "$TEST_SSH_SOPIN")
Jakub Jelen def1de
+slot=$(echo -- $out | sed 's/.* //')
Jakub Jelen def1de
+
Jakub Jelen def1de
+# prevent ssh-agent from calling ssh-askpass
Jakub Jelen def1de
+SSH_ASKPASS=/usr/bin/true
Jakub Jelen def1de
+export SSH_ASKPASS
Jakub Jelen def1de
+unset DISPLAY
Jakub Jelen def1de
+# We need interactive access to test PKCS# since it prompts for PIN
Jakub Jelen def1de
+sed -i 's/.*BatchMode.*//g' $OBJ/ssh_proxy
Jakub Jelen def1de
+
Jakub Jelen def1de
+# start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh)
Jakub Jelen def1de
+notty() {
Jakub Jelen def1de
+	perl -e 'use POSIX; POSIX::setsid();
Jakub Jelen def1de
+	    if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@"
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "generating keys"
Jakub Jelen def1de
+ID1="02"
Jakub Jelen def1de
+ID2="04"
Jakub Jelen def1de
+RSA=${DIR}/RSA
Jakub Jelen def1de
+EC=${DIR}/EC
Jakub Jelen def1de
+openssl genpkey -algorithm rsa > $RSA
Jakub Jelen def1de
+openssl pkcs8 -nocrypt -in $RSA |\
Jakub Jelen def1de
+    softhsm2-util --slot "$slot" --label "SSH RSA Key $ID1" --id $ID1 \
Jakub Jelen def1de
+	--pin "$TEST_SSH_PIN" --import /dev/stdin
Jakub Jelen def1de
+openssl genpkey \
Jakub Jelen def1de
+    -genparam \
Jakub Jelen def1de
+    -algorithm ec \
Jakub Jelen def1de
+    -pkeyopt ec_paramgen_curve:prime256v1 |\
Jakub Jelen def1de
+    openssl genpkey \
Jakub Jelen def1de
+    -paramfile /dev/stdin > $EC
Jakub Jelen def1de
+openssl pkcs8 -nocrypt -in $EC |\
Jakub Jelen def1de
+    softhsm2-util --slot "$slot" --label "SSH ECDSA Key $ID2" --id $ID2 \
Jakub Jelen def1de
+	--pin "$TEST_SSH_PIN" --import /dev/stdin
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "List the keys in the ssh-keygen with PKCS#11 URIs"
Jakub Jelen def1de
+${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys
Jakub Jelen def1de
+if [ $? -ne 0 ]; then
Jakub Jelen def1de
+	fail "keygen fails to enumerate keys on PKCS#11 token"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+grep "pkcs11:" $OBJ/token_keys > /dev/null
Jakub Jelen def1de
+if [ $? -ne 0 ]; then
Jakub Jelen def1de
+	fail "The keys from ssh-keygen do not contain PKCS#11 URI as a comment"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+tail -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "Simple connect with ssh (without PKCS#11 URI)"
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \
Jakub Jelen def1de
+    -F $OBJ/ssh_proxy somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -ne 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with pkcs11 failed (exit code $r)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "Connect with PKCS#11 URI"
Jakub Jelen def1de
+trace "  (second key should succeed)"
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Jakub Jelen def1de
+    -i "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -ne 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI failed (exit code $r)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "  (first key should fail)"
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Jakub Jelen def1de
+     -i "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -eq 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI succeeded (should fail)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "Connect with PKCS#11 URI including PIN should not prompt"
Jakub Jelen def1de
+trace "  (second key should succeed)"
Jakub Jelen def1de
+${SSH} -F $OBJ/ssh_proxy -i \
Jakub Jelen def1de
+    "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -ne 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI failed (exit code $r)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "  (first key should fail)"
Jakub Jelen def1de
+${SSH} -F $OBJ/ssh_proxy -i \
Jakub Jelen def1de
+    "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -eq 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI succeeded (should fail)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "Connect with various filtering options in PKCS#11 URI"
Jakub Jelen def1de
+trace "  (by object label, second key should succeed)"
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Jakub Jelen def1de
+    -i "pkcs11:object=SSH%20RSA%20Key%202?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -ne 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI failed (exit code $r)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "  (by object label, first key should fail)"
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Jakub Jelen def1de
+     -i "pkcs11:object=SSH%20RSA%20Key?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -eq 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI succeeded (should fail)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "  (by token label, second key should succeed)"
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Jakub Jelen def1de
+    -i "pkcs11:id=${ID2};token=SoftToken%20(token)?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -ne 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI failed (exit code $r)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "  (by wrong token label, should fail)"
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Jakub Jelen def1de
+     -i "pkcs11:token=SoftToken?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -eq 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI succeeded (should fail)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+
Jakub Jelen def1de
+
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "Test PKCS#11 URI specification in configuration files"
Jakub Jelen def1de
+echo "IdentityFile \"pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}\"" \
Jakub Jelen def1de
+    >> $OBJ/ssh_proxy
Jakub Jelen def1de
+trace "  (second key should succeed)"
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -ne 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI in config failed (exit code $r)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "  (first key should fail)"
Jakub Jelen def1de
+head -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -eq 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI in config succeeded (should fail)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "Test PKCS#11 URI specification in configuration files with bogus spaces"
Jakub Jelen def1de
+echo "IdentityFile \"    pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}    \"" \
Jakub Jelen def1de
+    >> $OBJ/ssh_proxy
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -ne 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI with bogus spaces in config failed" \
Jakub Jelen def1de
+	    "(exit code $r)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy
Jakub Jelen def1de
+
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "Combination of PKCS11Provider and PKCS11URI on commandline"
Jakub Jelen def1de
+trace "  (first key should succeed)"
Jakub Jelen def1de
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Jakub Jelen def1de
+    -i "pkcs11:id=${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -ne 5 ]; then
Jakub Jelen def1de
+	fail "ssh connect with PKCS#11 URI and provider combination" \
Jakub Jelen def1de
+	    "failed (exit code $r)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "Regress: Missing provider in PKCS11URI option"
Jakub Jelen def1de
+${SSH} -F $OBJ/ssh_proxy \
Jakub Jelen def1de
+    -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -eq 139 ]; then
Jakub Jelen def1de
+	fail "ssh connect with missing provider_id from configuration option" \
Jakub Jelen def1de
+	    "crashed (exit code $r)"
Jakub Jelen def1de
+fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+
Jakub Jelen def1de
+trace "SSH Agent can work with PKCS#11 URI"
Jakub Jelen def1de
+trace "start the agent"
Jakub Jelen def1de
+eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null
Jakub Jelen def1de
+
Jakub Jelen def1de
+r=$?
Jakub Jelen def1de
+if [ $r -ne 0 ]; then
Jakub Jelen def1de
+	fail "could not start ssh-agent: exit code $r"
Jakub Jelen def1de
+else
Jakub Jelen def1de
+	trace "add whole provider to agent"
Jakub Jelen def1de
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Jakub Jelen def1de
+	    "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 0 ]; then
Jakub Jelen def1de
+		fail "ssh-add failed with whole provider: exit code $r"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace " pkcs11 list via agent (all keys)"
Jakub Jelen def1de
+	${SSHADD} -l > /dev/null 2>&1
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 0 ]; then
Jakub Jelen def1de
+		fail "ssh-add -l failed with whole provider: exit code $r"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace " pkcs11 connect via agent (all keys)"
Jakub Jelen def1de
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 5 ]; then
Jakub Jelen def1de
+		fail "ssh connect failed with whole provider (exit code $r)"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace " remove pkcs11 keys (all keys)"
Jakub Jelen def1de
+	${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 0 ]; then
Jakub Jelen def1de
+		fail "ssh-add -d failed with whole provider: exit code $r"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace "add only first key to the agent"
Jakub Jelen def1de
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Jakub Jelen def1de
+	    "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 0 ]; then
Jakub Jelen def1de
+		fail "ssh-add failed with first key: exit code $r"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace " pkcs11 connect via agent (first key)"
Jakub Jelen def1de
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 5 ]; then
Jakub Jelen def1de
+		fail "ssh connect failed with first key (exit code $r)"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace " remove first pkcs11 key"
Jakub Jelen def1de
+	${SSHADD} -d "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" \
Jakub Jelen def1de
+	    > /dev/null 2>&1
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 0 ]; then
Jakub Jelen def1de
+		fail "ssh-add -d failed with first key: exit code $r"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace "add only second key to the agent"
Jakub Jelen def1de
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Jakub Jelen def1de
+	    "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 0 ]; then
Jakub Jelen def1de
+		fail "ssh-add failed with second key: exit code $r"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace " pkcs11 connect via agent (second key should fail)"
Jakub Jelen def1de
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -eq 5 ]; then
Jakub Jelen def1de
+		fail "ssh connect passed without key (should fail)"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace "add also the first key to the agent"
Jakub Jelen def1de
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Jakub Jelen def1de
+	    "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 0 ]; then
Jakub Jelen def1de
+		fail "ssh-add failed with first key: exit code $r"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace " remove second pkcs11 key"
Jakub Jelen def1de
+	${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \
Jakub Jelen def1de
+	    > /dev/null 2>&1
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 0 ]; then
Jakub Jelen def1de
+		fail "ssh-add -d failed with second key: exit code $r"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace " remove already-removed pkcs11 key should fail"
Jakub Jelen def1de
+	${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \
Jakub Jelen def1de
+	    > /dev/null 2>&1
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -eq 0 ]; then
Jakub Jelen def1de
+		fail "ssh-add -d passed with non-existing key (should fail)"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace " pkcs11 connect via agent (the first key should be still usable)"
Jakub Jelen def1de
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Jakub Jelen def1de
+	r=$?
Jakub Jelen def1de
+	if [ $r -ne 5 ]; then
Jakub Jelen def1de
+		fail "ssh connect failed with first key (after removing second): exit code $r"
Jakub Jelen def1de
+	fi
Jakub Jelen def1de
+
Jakub Jelen def1de
+	trace "kill agent"
Jakub Jelen def1de
+	${SSHAGENT} -k > /dev/null
Jakub Jelen def1de
+fi
Jakub Jelen def1de
diff --git a/regress/unittests/Makefile b/regress/unittests/Makefile
Jakub Jelen def1de
index e464b085..a0e5a37c 100644
Jakub Jelen def1de
--- a/regress/unittests/Makefile
Jakub Jelen def1de
+++ b/regress/unittests/Makefile
Jakub Jelen def1de
@@ -2,6 +2,6 @@
Jakub Jelen def1de
 
Jakub Jelen def1de
 REGRESS_FAIL_EARLY?=	yes
Jakub Jelen def1de
 SUBDIR=	test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion
Jakub Jelen def1de
-SUBDIR+=authopt
Jakub Jelen def1de
+SUBDIR+=pkcs11 authopt
Jakub Jelen def1de
 
Jakub Jelen def1de
 .include <bsd.subdir.mk>
Jakub Jelen def1de
diff --git a/regress/unittests/pkcs11/tests.c b/regress/unittests/pkcs11/tests.c
Jakub Jelen def1de
new file mode 100644
Jakub Jelen def1de
index 00000000..b637cb13
Jakub Jelen def1de
--- /dev/null
Jakub Jelen def1de
+++ b/regress/unittests/pkcs11/tests.c
Jakub Jelen def1de
@@ -0,0 +1,337 @@
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * Copyright (c) 2017 Red Hat
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * Authors: Jakub Jelen <jjelen@redhat.com>
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * Permission to use, copy, modify, and distribute this software for any
Jakub Jelen def1de
+ * purpose with or without fee is hereby granted, provided that the above
Jakub Jelen def1de
+ * copyright notice and this permission notice appear in all copies.
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Jakub Jelen def1de
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Jakub Jelen def1de
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Jakub Jelen def1de
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Jakub Jelen def1de
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Jakub Jelen def1de
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Jakub Jelen def1de
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "includes.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include <locale.h>
Jakub Jelen def1de
+#include <string.h>
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "../test_helper/test_helper.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "sshbuf.h"
Jakub Jelen def1de
+#include "ssh-pkcs11-uri.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL)
Jakub Jelen def1de
+
Jakub Jelen def1de
+/* prototypes are not public -- specify them here internally for tests */
Jakub Jelen def1de
+struct sshbuf *percent_encode(const char *, size_t, char *);
Jakub Jelen def1de
+int percent_decode(char *, char **);
Jakub Jelen def1de
+
Jakub Jelen def1de
+void
Jakub Jelen def1de
+compare_uri(struct pkcs11_uri *a, struct pkcs11_uri *b)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	ASSERT_PTR_NE(a, NULL);
Jakub Jelen def1de
+	ASSERT_PTR_NE(b, NULL);
Jakub Jelen def1de
+	ASSERT_SIZE_T_EQ(a->id_len, b->id_len);
Jakub Jelen def1de
+	ASSERT_MEM_EQ(a->id, b->id, a->id_len);
Jakub Jelen def1de
+	if (b->object != NULL)
Jakub Jelen def1de
+		ASSERT_STRING_EQ(a->object, b->object);
Jakub Jelen def1de
+	else /* both should be null */
Jakub Jelen def1de
+		ASSERT_PTR_EQ(a->object, b->object);
Jakub Jelen def1de
+	if (b->module_path != NULL)
Jakub Jelen def1de
+		ASSERT_STRING_EQ(a->module_path, b->module_path);
Jakub Jelen def1de
+	else /* both should be null */
Jakub Jelen def1de
+		ASSERT_PTR_EQ(a->module_path, b->module_path);
Jakub Jelen def1de
+	if (b->token != NULL)
Jakub Jelen def1de
+		ASSERT_STRING_EQ(a->token, b->token);
Jakub Jelen def1de
+	else /* both should be null */
Jakub Jelen def1de
+		ASSERT_PTR_EQ(a->token, b->token);
Jakub Jelen def1de
+	if (b->manuf != NULL)
Jakub Jelen def1de
+		ASSERT_STRING_EQ(a->manuf, b->manuf);
Jakub Jelen def1de
+	else /* both should be null */
Jakub Jelen def1de
+		ASSERT_PTR_EQ(a->manuf, b->manuf);
Jakub Jelen def1de
+	if (b->lib_manuf != NULL)
Jakub Jelen def1de
+		ASSERT_STRING_EQ(a->lib_manuf, b->lib_manuf);
Jakub Jelen def1de
+	else /* both should be null */
Jakub Jelen def1de
+		ASSERT_PTR_EQ(a->lib_manuf, b->lib_manuf);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+void
Jakub Jelen def1de
+check_parse_rv(char *uri, struct pkcs11_uri *expect, int expect_rv)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	char *buf = NULL, *str;
Jakub Jelen def1de
+	struct pkcs11_uri *pkcs11uri = NULL;
Jakub Jelen def1de
+	int rv;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (expect_rv == 0)
Jakub Jelen def1de
+		str = "Valid";
Jakub Jelen def1de
+	else
Jakub Jelen def1de
+		str = "Invalid";
Jakub Jelen def1de
+	asprintf(&buf, "%s PKCS#11 URI parsing: %s", str, uri);
Jakub Jelen def1de
+	TEST_START(buf);
Jakub Jelen def1de
+	free(buf);
Jakub Jelen def1de
+	pkcs11uri = pkcs11_uri_init();
Jakub Jelen def1de
+	rv = pkcs11_uri_parse(uri, pkcs11uri);
Jakub Jelen def1de
+	ASSERT_INT_EQ(rv, expect_rv);
Jakub Jelen def1de
+	if (rv == 0) /* in case of failure result is undefined */
Jakub Jelen def1de
+		compare_uri(pkcs11uri, expect);
Jakub Jelen def1de
+	pkcs11_uri_cleanup(pkcs11uri);
Jakub Jelen def1de
+	free(expect);
Jakub Jelen def1de
+	TEST_DONE();
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+void
Jakub Jelen def1de
+check_parse(char *uri, struct pkcs11_uri *expect)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	check_parse_rv(uri, expect, 0);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+struct pkcs11_uri *
Jakub Jelen def1de
+compose_uri(unsigned char *id, size_t id_len, char *token, char *lib_manuf,
Jakub Jelen def1de
+    char *manuf, char *module_path, char *object, char *pin)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct pkcs11_uri *uri = pkcs11_uri_init();
Jakub Jelen def1de
+	if (id_len > 0) {
Jakub Jelen def1de
+		uri->id_len = id_len;
Jakub Jelen def1de
+		uri->id = id;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	uri->module_path = module_path;
Jakub Jelen def1de
+	uri->token = token;
Jakub Jelen def1de
+	uri->lib_manuf = lib_manuf;
Jakub Jelen def1de
+	uri->manuf = manuf;
Jakub Jelen def1de
+	uri->object = object;
Jakub Jelen def1de
+	uri->pin = pin;
Jakub Jelen def1de
+	return uri;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+static void
Jakub Jelen def1de
+test_parse_valid(void)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	/* path arguments */
Jakub Jelen def1de
+	check_parse("pkcs11:id=%01",
Jakub Jelen def1de
+	    compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	check_parse("pkcs11:id=%00%01",
Jakub Jelen def1de
+	    compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	check_parse("pkcs11:token=SSH%20Keys",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	check_parse("pkcs11:library-manufacturer=OpenSC",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	check_parse("pkcs11:manufacturer=piv_II",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL));
Jakub Jelen def1de
+	check_parse("pkcs11:object=SIGN%20Key",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key", NULL));
Jakub Jelen def1de
+	/* query arguments */
Jakub Jelen def1de
+	check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Jakub Jelen def1de
+	check_parse("pkcs11:?pin-value=123456",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, "123456"));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* combinations */
Jakub Jelen def1de
+	/* ID SHOULD be percent encoded */
Jakub Jelen def1de
+	check_parse("pkcs11:token=SSH%20Key;id=0",
Jakub Jelen def1de
+	    compose_uri("0", 1, "SSH Key", NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	check_parse(
Jakub Jelen def1de
+	    "pkcs11:manufacturer=CAC?module-path=/usr/lib64/p11-kit-proxy.so",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, "CAC",
Jakub Jelen def1de
+	    "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Jakub Jelen def1de
+	check_parse(
Jakub Jelen def1de
+	    "pkcs11:object=RSA%20Key?module-path=/usr/lib64/pkcs11/opencryptoki.so",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, NULL,
Jakub Jelen def1de
+	    "/usr/lib64/pkcs11/opencryptoki.so", "RSA Key", NULL));
Jakub Jelen def1de
+	check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so&pin-value=123456",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, "123456"));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* empty path component matches everything */
Jakub Jelen def1de
+	check_parse("pkcs11:", EMPTY_URI);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* empty string is a valid to match against (and different from NULL) */
Jakub Jelen def1de
+	check_parse("pkcs11:token=",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, "", NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	/* Percent character needs to be percent-encoded */
Jakub Jelen def1de
+	check_parse("pkcs11:token=%25",
Jakub Jelen def1de
+	     compose_uri(NULL, 0, "%", NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+static void
Jakub Jelen def1de
+test_parse_invalid(void)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	/* Invalid percent encoding */
Jakub Jelen def1de
+	check_parse_rv("pkcs11:id=%0", EMPTY_URI, -1);
Jakub Jelen def1de
+	/* Invalid percent encoding */
Jakub Jelen def1de
+	check_parse_rv("pkcs11:id=%ZZ", EMPTY_URI, -1);
Jakub Jelen def1de
+	/* Space MUST be percent encoded -- XXX not enforced yet */
Jakub Jelen def1de
+	check_parse("pkcs11:token=SSH Keys",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	/* MUST NOT contain duplicate attributes of the same name */
Jakub Jelen def1de
+	check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1);
Jakub Jelen def1de
+	/* MUST NOT contain duplicate attributes of the same name */
Jakub Jelen def1de
+	check_parse_rv("pkcs11:?pin-value=111111&pin-value=123456", EMPTY_URI, -1);
Jakub Jelen def1de
+	/* Unrecognized attribute in path are ignored with log message */
Jakub Jelen def1de
+	check_parse("pkcs11:key_name=SSH", EMPTY_URI);
Jakub Jelen def1de
+	/* Unrecognized attribute in query SHOULD be ignored */
Jakub Jelen def1de
+	check_parse("pkcs11:?key_name=SSH", EMPTY_URI);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+void
Jakub Jelen def1de
+check_gen(char *expect, struct pkcs11_uri *uri)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	char *buf = NULL, *uri_str;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	asprintf(&buf, "Valid PKCS#11 URI generation: %s", expect);
Jakub Jelen def1de
+	TEST_START(buf);
Jakub Jelen def1de
+	free(buf);
Jakub Jelen def1de
+	uri_str = pkcs11_uri_get(uri);
Jakub Jelen def1de
+	ASSERT_PTR_NE(uri_str, NULL);
Jakub Jelen def1de
+	ASSERT_STRING_EQ(uri_str, expect);
Jakub Jelen def1de
+	free(uri_str);
Jakub Jelen def1de
+	TEST_DONE();
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+static void
Jakub Jelen def1de
+test_generate_valid(void)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	/* path arguments */
Jakub Jelen def1de
+	check_gen("pkcs11:id=%01",
Jakub Jelen def1de
+	    compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	check_gen("pkcs11:id=%00%01",
Jakub Jelen def1de
+	    compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	check_gen("pkcs11:token=SSH%20Keys", /* space must be percent encoded */
Jakub Jelen def1de
+	    compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	/* library-manufacturer is not implmented now */
Jakub Jelen def1de
+	/*check_gen("pkcs11:library-manufacturer=OpenSC",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));*/
Jakub Jelen def1de
+	check_gen("pkcs11:manufacturer=piv_II",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL));
Jakub Jelen def1de
+	check_gen("pkcs11:object=RSA%20Key",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key", NULL));
Jakub Jelen def1de
+	/* query arguments */
Jakub Jelen def1de
+	check_gen("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* combinations */
Jakub Jelen def1de
+	check_gen("pkcs11:id=%02;token=SSH%20Keys",
Jakub Jelen def1de
+	    compose_uri("\x02", 1, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Jakub Jelen def1de
+	check_gen("pkcs11:id=%EE%02?module-path=/usr/lib64/p11-kit-proxy.so",
Jakub Jelen def1de
+	    compose_uri("\xEE\x02", 2, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Jakub Jelen def1de
+	check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II",
Jakub Jelen def1de
+	    compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key", NULL));
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* empty path component matches everything */
Jakub Jelen def1de
+	check_gen("pkcs11:", EMPTY_URI);
Jakub Jelen def1de
+
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+void
Jakub Jelen def1de
+check_encode(char *source, size_t len, char *whitelist, char *expect)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	char *buf = NULL;
Jakub Jelen def1de
+	struct sshbuf *b;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	asprintf(&buf, "percent_encode: expected %s", expect);
Jakub Jelen def1de
+	TEST_START(buf);
Jakub Jelen def1de
+	free(buf);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	b = percent_encode(source, len, whitelist);
Jakub Jelen def1de
+	ASSERT_STRING_EQ(sshbuf_ptr(b), expect);
Jakub Jelen def1de
+	sshbuf_free(b);
Jakub Jelen def1de
+	TEST_DONE();
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+static void
Jakub Jelen def1de
+test_percent_encode_multibyte(void)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	/* SHOULD be encoded as octets according to the UTF-8 character encoding */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* multi-byte characters are "for free" */
Jakub Jelen def1de
+	check_encode("$", 1, "", "%24");
Jakub Jelen def1de
+	check_encode("¢", 2, "", "%C2%A2");
Jakub Jelen def1de
+	check_encode("€", 3, "", "%E2%82%AC");
Jakub Jelen def1de
+	check_encode("𐍈", 4, "", "%F0%90%8D%88");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* CK_UTF8CHAR is unsigned char (1 byte) */
Jakub Jelen def1de
+	/* labels SHOULD be normalized to NFC [UAX15] */
Jakub Jelen def1de
+
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+static void
Jakub Jelen def1de
+test_percent_encode(void)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	/* Without whitelist encodes everything (for CKA_ID) */
Jakub Jelen def1de
+	check_encode("A*", 2, "", "%41%2A");
Jakub Jelen def1de
+	check_encode("\x00", 1, "", "%00");
Jakub Jelen def1de
+	check_encode("\x7F", 1, "", "%7F");
Jakub Jelen def1de
+	check_encode("\x80", 1, "", "%80");
Jakub Jelen def1de
+	check_encode("\xff", 1, "", "%FF");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Default whitelist encodes anything but safe letters */
Jakub Jelen def1de
+	check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST,
Jakub Jelen def1de
+	    "test%000alpha");
Jakub Jelen def1de
+	check_encode(" ", 1, PKCS11_URI_WHITELIST,
Jakub Jelen def1de
+	    "%20"); /* Space MUST be percent encoded */
Jakub Jelen def1de
+	check_encode("/", 1, PKCS11_URI_WHITELIST,
Jakub Jelen def1de
+	    "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */
Jakub Jelen def1de
+	check_encode("?", 1, PKCS11_URI_WHITELIST,
Jakub Jelen def1de
+	    "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */
Jakub Jelen def1de
+	check_encode("#", 1, PKCS11_URI_WHITELIST,
Jakub Jelen def1de
+	    "%23"); /* '#' MUST be always percent encoded */
Jakub Jelen def1de
+	check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST,
Jakub Jelen def1de
+	    "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Components in query can have '/' unencoded (useful for paths) */
Jakub Jelen def1de
+	check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/",
Jakub Jelen def1de
+	    "/path/to.file");
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+void
Jakub Jelen def1de
+check_decode(char *source, char *expect, int expect_len)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	char *buf = NULL, *out = NULL;
Jakub Jelen def1de
+	int rv;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	asprintf(&buf, "percent_decode: %s", source);
Jakub Jelen def1de
+	TEST_START(buf);
Jakub Jelen def1de
+	free(buf);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	rv = percent_decode(source, &out;;
Jakub Jelen def1de
+	ASSERT_INT_EQ(rv, expect_len);
Jakub Jelen def1de
+	if (rv >= 0)
Jakub Jelen def1de
+		ASSERT_MEM_EQ(out, expect, expect_len);
Jakub Jelen def1de
+	free(out);
Jakub Jelen def1de
+	TEST_DONE();
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+static void
Jakub Jelen def1de
+test_percent_decode(void)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	/* simple valid cases */
Jakub Jelen def1de
+	check_decode("%00", "\x00", 1);
Jakub Jelen def1de
+	check_decode("%FF", "\xFF", 1);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* normal strings shold be kept intact */
Jakub Jelen def1de
+	check_decode("strings are left", "strings are left", 16);
Jakub Jelen def1de
+	check_decode("10%25 of trees", "10% of trees", 12);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* make sure no more than 2 bytes are parsed */
Jakub Jelen def1de
+	check_decode("%222", "\x22" "2", 2);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* invalid expects failure */
Jakub Jelen def1de
+	check_decode("%0", "", -1);
Jakub Jelen def1de
+	check_decode("%Z", "", -1);
Jakub Jelen def1de
+	check_decode("%FG", "", -1);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+void
Jakub Jelen def1de
+tests(void)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	test_percent_encode();
Jakub Jelen def1de
+	test_percent_encode_multibyte();
Jakub Jelen def1de
+	test_percent_decode();
Jakub Jelen def1de
+	test_parse_valid();
Jakub Jelen def1de
+	test_parse_invalid();
Jakub Jelen def1de
+	test_generate_valid();
Jakub Jelen def1de
+}
Jakub Jelen def1de
diff --git a/ssh-add.c b/ssh-add.c
Jakub Jelen def1de
index ac9c808d..f039e00e 100644
Jakub Jelen def1de
--- a/ssh-add.c
Jakub Jelen def1de
+++ b/ssh-add.c
Jakub Jelen def1de
@@ -64,6 +64,7 @@
Jakub Jelen def1de
 #include "misc.h"
Jakub Jelen def1de
 #include "ssherr.h"
Jakub Jelen def1de
 #include "digest.h"
Jakub Jelen def1de
+#include "ssh-pkcs11-uri.h"
Jakub Jelen def1de
 
Jakub Jelen def1de
 /* argv0 */
Jakub Jelen def1de
 extern char *__progname;
Jakub Jelen def1de
@@ -188,6 +189,24 @@ delete_all(int agent_fd, int qflag)
Jakub Jelen def1de
 	return ret;
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
+#ifdef ENABLE_PKCS11
Jakub Jelen def1de
+static int update_card(int, int, const char *, int);
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri, int qflag)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct pkcs11_uri *uri;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* dry-run parse to make sure the URI is valid and to report errors */
Jakub Jelen def1de
+	uri = pkcs11_uri_init();
Jakub Jelen def1de
+	if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0)
Jakub Jelen def1de
+		fatal("Failed to parse PKCS#11 URI");
Jakub Jelen def1de
+	pkcs11_uri_cleanup(uri);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return update_card(agent_fd, adding, pkcs11_uri, qflag);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+
Jakub Jelen def1de
 static int
Jakub Jelen def1de
 add_file(int agent_fd, const char *filename, int key_only, int qflag)
Jakub Jelen def1de
 {
Jakub Jelen def1de
@@ -529,6 +548,13 @@ lock_agent(int agent_fd, int lock)
Jakub Jelen def1de
 static int
Jakub Jelen def1de
 do_file(int agent_fd, int deleting, int key_only, char *file, int qflag)
Jakub Jelen def1de
 {
Jakub Jelen def1de
+#ifdef ENABLE_PKCS11
Jakub Jelen def1de
+	if (strlen(file) >= strlen(PKCS11_URI_SCHEME) &&
Jakub Jelen def1de
+	    strncmp(file, PKCS11_URI_SCHEME,
Jakub Jelen def1de
+	    strlen(PKCS11_URI_SCHEME)) == 0) {
Jakub Jelen def1de
+		return update_pkcs11_uri(agent_fd, !deleting, file, qflag);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 	if (deleting) {
Jakub Jelen def1de
 		if (delete_file(agent_fd, file, key_only, qflag) == -1)
Jakub Jelen def1de
 			return -1;
Jakub Jelen def1de
diff --git a/ssh-agent.c b/ssh-agent.c
Jakub Jelen def1de
index d06ecfd9..9c1b328f 100644
Jakub Jelen def1de
--- a/ssh-agent.c
Jakub Jelen def1de
+++ b/ssh-agent.c
Jakub Jelen def1de
@@ -548,10 +548,72 @@ no_identities(SocketEntry *e)
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
 #ifdef ENABLE_PKCS11
Jakub Jelen def1de
+static char *
Jakub Jelen def1de
+sanitize_pkcs11_provider(const char *provider)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct pkcs11_uri *uri = NULL;
Jakub Jelen def1de
+	char *sane_uri, *module_path = NULL; /* default path */
Jakub Jelen def1de
+	char canonical_provider[PATH_MAX];
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (provider == NULL)
Jakub Jelen def1de
+		return NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (strlen(provider) >= strlen(PKCS11_URI_SCHEME) &&
Jakub Jelen def1de
+	    strncmp(provider, PKCS11_URI_SCHEME,
Jakub Jelen def1de
+	    strlen(PKCS11_URI_SCHEME)) == 0) {
Jakub Jelen def1de
+		/* PKCS#11 URI */
Jakub Jelen def1de
+		uri = pkcs11_uri_init();
Jakub Jelen def1de
+		if (uri == NULL) {
Jakub Jelen def1de
+			error("Failed to init PCKS#11 URI");
Jakub Jelen def1de
+			return NULL;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Jakub Jelen def1de
+		if (pkcs11_uri_parse(provider, uri) != 0) {
Jakub Jelen def1de
+			error("Failed to parse PKCS#11 URI");
Jakub Jelen def1de
+			return NULL;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+		/* validate also provider from URI */
Jakub Jelen def1de
+		if (uri->module_path)
Jakub Jelen def1de
+			module_path = strdup(uri->module_path);
Jakub Jelen def1de
+	} else
Jakub Jelen def1de
+		module_path = strdup(provider); /* simple path */
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (module_path != NULL) { /* do not validate default NULL path in URI */
Jakub Jelen def1de
+		if (realpath(module_path, canonical_provider) == NULL) {
Jakub Jelen def1de
+			verbose("failed PKCS#11 provider \"%.100s\": realpath: %s",
Jakub Jelen def1de
+			    module_path, strerror(errno));
Jakub Jelen def1de
+			free(module_path);
Jakub Jelen def1de
+			pkcs11_uri_cleanup(uri);
Jakub Jelen def1de
+			return NULL;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+		free(module_path);
Jakub Jelen def1de
+		if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) {
Jakub Jelen def1de
+			verbose("refusing PKCS#11 provider \"%.100s\": "
Jakub Jelen def1de
+			    "not whitelisted", canonical_provider);
Jakub Jelen def1de
+			pkcs11_uri_cleanup(uri);
Jakub Jelen def1de
+			return NULL;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+
Jakub Jelen def1de
+		/* copy verified and sanitized provider path back to the uri */
Jakub Jelen def1de
+		if (uri) {
Jakub Jelen def1de
+			free(uri->module_path);
Jakub Jelen def1de
+			uri->module_path = xstrdup(canonical_provider);
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (uri) {
Jakub Jelen def1de
+		sane_uri = pkcs11_uri_get(uri);
Jakub Jelen def1de
+		pkcs11_uri_cleanup(uri);
Jakub Jelen def1de
+		return sane_uri;
Jakub Jelen def1de
+	} else {
Jakub Jelen def1de
+		return xstrdup(canonical_provider); /* simple path */
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 static void
Jakub Jelen def1de
 process_add_smartcard_key(SocketEntry *e)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
Jakub Jelen def1de
+	char *provider = NULL, *pin = NULL, *sane_uri = NULL;
Jakub Jelen def1de
 	int r, i, count = 0, success = 0, confirm = 0;
Jakub Jelen def1de
 	u_int seconds;
Jakub Jelen def1de
 	time_t death = 0;
Jakub Jelen def1de
@@ -587,28 +649,23 @@ process_add_smartcard_key(SocketEntry *e)
Jakub Jelen def1de
 			goto send;
Jakub Jelen def1de
 		}
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	if (realpath(provider, canonical_provider) == NULL) {
Jakub Jelen def1de
-		verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
Jakub Jelen def1de
-		    provider, strerror(errno));
Jakub Jelen def1de
-		goto send;
Jakub Jelen def1de
-	}
Jakub Jelen def1de
-	if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) {
Jakub Jelen def1de
-		verbose("refusing PKCS#11 add of \"%.100s\": "
Jakub Jelen def1de
-		    "provider not whitelisted", canonical_provider);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	sane_uri = sanitize_pkcs11_provider(provider);
Jakub Jelen def1de
+	if (sane_uri == NULL)
Jakub Jelen def1de
 		goto send;
Jakub Jelen def1de
-	}
Jakub Jelen def1de
-	debug("%s: add %.100s", __func__, canonical_provider);
Jakub Jelen def1de
+
Jakub Jelen def1de
 	if (lifetime && !death)
Jakub Jelen def1de
 		death = monotime() + lifetime;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	count = pkcs11_add_provider(canonical_provider, pin, &keys);
Jakub Jelen def1de
+	debug("%s: add %.100s", __func__, sane_uri);
Jakub Jelen def1de
+	count = pkcs11_add_provider(sane_uri, pin, &keys);
Jakub Jelen def1de
 	for (i = 0; i < count; i++) {
Jakub Jelen def1de
 		k = keys[i];
Jakub Jelen def1de
 		if (lookup_identity(k) == NULL) {
Jakub Jelen def1de
 			id = xcalloc(1, sizeof(Identity));
Jakub Jelen def1de
 			id->key = k;
Jakub Jelen def1de
-			id->provider = xstrdup(canonical_provider);
Jakub Jelen def1de
-			id->comment = xstrdup(canonical_provider); /* XXX */
Jakub Jelen def1de
+			id->provider = xstrdup(sane_uri);
Jakub Jelen def1de
+			id->comment = xstrdup(sane_uri);
Jakub Jelen def1de
 			id->death = death;
Jakub Jelen def1de
 			id->confirm = confirm;
Jakub Jelen def1de
 			TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
Jakub Jelen def1de
@@ -622,6 +679,7 @@ process_add_smartcard_key(SocketEntry *e)
Jakub Jelen def1de
 send:
Jakub Jelen def1de
 	free(pin);
Jakub Jelen def1de
 	free(provider);
Jakub Jelen def1de
+	free(sane_uri);
Jakub Jelen def1de
 	free(keys);
Jakub Jelen def1de
 	send_status(e, success);
Jakub Jelen def1de
 }
Jakub Jelen def1de
@@ -629,7 +687,7 @@ send:
Jakub Jelen def1de
 static void
Jakub Jelen def1de
 process_remove_smartcard_key(SocketEntry *e)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
Jakub Jelen def1de
+	char *provider = NULL, *pin = NULL, *sane_uri = NULL;
Jakub Jelen def1de
 	int r, success = 0;
Jakub Jelen def1de
 	Identity *id, *nxt;
Jakub Jelen def1de
 
Jakub Jelen def1de
@@ -640,30 +698,29 @@ process_remove_smartcard_key(SocketEntry *e)
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 	free(pin);
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (realpath(provider, canonical_provider) == NULL) {
Jakub Jelen def1de
-		verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
Jakub Jelen def1de
-		    provider, strerror(errno));
Jakub Jelen def1de
+	sane_uri = sanitize_pkcs11_provider(provider);
Jakub Jelen def1de
+	if (sane_uri == NULL)
Jakub Jelen def1de
 		goto send;
Jakub Jelen def1de
-	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	debug("%s: remove %.100s", __func__, canonical_provider);
Jakub Jelen def1de
+	debug("%s: remove %.100s", __func__, sane_uri);
Jakub Jelen def1de
 	for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
Jakub Jelen def1de
 		nxt = TAILQ_NEXT(id, next);
Jakub Jelen def1de
 		/* Skip file--based keys */
Jakub Jelen def1de
 		if (id->provider == NULL)
Jakub Jelen def1de
 			continue;
Jakub Jelen def1de
-		if (!strcmp(canonical_provider, id->provider)) {
Jakub Jelen def1de
+		if (!strcmp(sane_uri, id->provider)) {
Jakub Jelen def1de
 			TAILQ_REMOVE(&idtab->idlist, id, next);
Jakub Jelen def1de
 			free_identity(id);
Jakub Jelen def1de
 			idtab->nentries--;
Jakub Jelen def1de
 		}
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	if (pkcs11_del_provider(canonical_provider) == 0)
Jakub Jelen def1de
+	if (pkcs11_del_provider(sane_uri) == 0)
Jakub Jelen def1de
 		success = 1;
Jakub Jelen def1de
 	else
Jakub Jelen def1de
 		error("%s: pkcs11_del_provider failed", __func__);
Jakub Jelen def1de
 send:
Jakub Jelen def1de
 	free(provider);
Jakub Jelen def1de
+	free(sane_uri);
Jakub Jelen def1de
 	send_status(e, success);
Jakub Jelen def1de
 }
Jakub Jelen def1de
 #endif /* ENABLE_PKCS11 */
Jakub Jelen def1de
diff --git a/ssh-keygen.c b/ssh-keygen.c
Jakub Jelen def1de
index 3898b281..91c43b14 100644
Jakub Jelen def1de
--- a/ssh-keygen.c
Jakub Jelen def1de
+++ b/ssh-keygen.c
Jakub Jelen def1de
@@ -798,6 +798,7 @@ do_download(struct passwd *pw)
Jakub Jelen def1de
 			free(fp);
Jakub Jelen def1de
 		} else {
Jakub Jelen def1de
 			(void) sshkey_write(keys[i], stdout); /* XXX check */
Jakub Jelen def1de
+			(void) pkcs11_uri_write(keys[i], stdout);
Jakub Jelen def1de
 			fprintf(stdout, "\n");
Jakub Jelen def1de
 		}
Jakub Jelen def1de
 		sshkey_free(keys[i]);
Jakub Jelen def1de
diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c
Jakub Jelen def1de
index e7860de8..7b2a9115 100644
Jakub Jelen def1de
--- a/ssh-pkcs11-client.c
Jakub Jelen def1de
+++ b/ssh-pkcs11-client.c
Jakub Jelen def1de
@@ -321,6 +321,8 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp)
Jakub Jelen def1de
 	u_int nkeys, i;
Jakub Jelen def1de
 	struct sshbuf *msg;
Jakub Jelen def1de
 
Jakub Jelen def1de
+	debug("%s: called, name = %s", __func__, name);
Jakub Jelen def1de
+
Jakub Jelen def1de
 	if (fd < 0 && pkcs11_start_helper() < 0)
Jakub Jelen def1de
 		return (-1);
Jakub Jelen def1de
 
Jakub Jelen def1de
@@ -338,6 +340,7 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp)
Jakub Jelen def1de
 		if ((r = sshbuf_get_u32(msg, &nkeys)) != 0)
Jakub Jelen def1de
 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
Jakub Jelen def1de
 		*keysp = xcalloc(nkeys, sizeof(struct sshkey *));
Jakub Jelen def1de
+		debug("%s: nkeys = %u", __func__, nkeys);
Jakub Jelen def1de
 		for (i = 0; i < nkeys; i++) {
Jakub Jelen def1de
 			/* XXX clean up properly instead of fatal() */
Jakub Jelen def1de
 			if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 ||
Jakub Jelen def1de
diff --git a/ssh-pkcs11-uri.c b/ssh-pkcs11-uri.c
Jakub Jelen def1de
new file mode 100644
Jakub Jelen def1de
index 00000000..e1a7b4e0
Jakub Jelen def1de
--- /dev/null
Jakub Jelen def1de
+++ b/ssh-pkcs11-uri.c
Jakub Jelen def1de
@@ -0,0 +1,421 @@
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * Copyright (c) 2017 Red Hat
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * Authors: Jakub Jelen <jjelen@redhat.com>
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * Permission to use, copy, modify, and distribute this software for any
Jakub Jelen def1de
+ * purpose with or without fee is hereby granted, provided that the above
Jakub Jelen def1de
+ * copyright notice and this permission notice appear in all copies.
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Jakub Jelen def1de
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Jakub Jelen def1de
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Jakub Jelen def1de
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Jakub Jelen def1de
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Jakub Jelen def1de
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Jakub Jelen def1de
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "includes.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#ifdef ENABLE_PKCS11
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include <stdio.h>
Jakub Jelen def1de
+#include <string.h>
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "sshkey.h"
Jakub Jelen def1de
+#include "sshbuf.h"
Jakub Jelen def1de
+#include "log.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#define CRYPTOKI_COMPAT
Jakub Jelen def1de
+#include "pkcs11.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#include "ssh-pkcs11-uri.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
+#define PKCS11_URI_PATH_SEPARATOR ";"
Jakub Jelen def1de
+#define PKCS11_URI_QUERY_SEPARATOR "&"
Jakub Jelen def1de
+#define PKCS11_URI_VALUE_SEPARATOR "="
Jakub Jelen def1de
+#define PKCS11_URI_ID "id"
Jakub Jelen def1de
+#define PKCS11_URI_TOKEN "token"
Jakub Jelen def1de
+#define PKCS11_URI_OBJECT "object"
Jakub Jelen def1de
+#define PKCS11_URI_LIB_MANUF "library-manufacturer"
Jakub Jelen def1de
+#define PKCS11_URI_MANUF "manufacturer"
Jakub Jelen def1de
+#define PKCS11_URI_MODULE_PATH "module-path"
Jakub Jelen def1de
+#define PKCS11_URI_PIN_VALUE "pin-value"
Jakub Jelen def1de
+
Jakub Jelen def1de
+/* Keyword tokens. */
Jakub Jelen def1de
+typedef enum {
Jakub Jelen def1de
+	pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath,
Jakub Jelen def1de
+	pPinValue, pBadOption
Jakub Jelen def1de
+} pkcs11uriOpCodes;
Jakub Jelen def1de
+
Jakub Jelen def1de
+/* Textual representation of the tokens. */
Jakub Jelen def1de
+static struct {
Jakub Jelen def1de
+	const char *name;
Jakub Jelen def1de
+	pkcs11uriOpCodes opcode;
Jakub Jelen def1de
+} keywords[] = {
Jakub Jelen def1de
+	{ PKCS11_URI_ID, pId },
Jakub Jelen def1de
+	{ PKCS11_URI_TOKEN, pToken },
Jakub Jelen def1de
+	{ PKCS11_URI_OBJECT, pObject },
Jakub Jelen def1de
+	{ PKCS11_URI_LIB_MANUF, pLibraryManufacturer },
Jakub Jelen def1de
+	{ PKCS11_URI_MANUF, pManufacturer },
Jakub Jelen def1de
+	{ PKCS11_URI_MODULE_PATH, pModulePath },
Jakub Jelen def1de
+	{ PKCS11_URI_PIN_VALUE, pPinValue },
Jakub Jelen def1de
+	{ NULL, pBadOption }
Jakub Jelen def1de
+};
Jakub Jelen def1de
+
Jakub Jelen def1de
+static pkcs11uriOpCodes
Jakub Jelen def1de
+parse_token(const char *cp)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	u_int i;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	for (i = 0; keywords[i].name; i++)
Jakub Jelen def1de
+		if (strncasecmp(cp, keywords[i].name,
Jakub Jelen def1de
+		    strlen(keywords[i].name)) == 0)
Jakub Jelen def1de
+			return keywords[i].opcode;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return pBadOption;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+percent_decode(char *data, char **outp)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	char tmp[3];
Jakub Jelen def1de
+	char *out, *tmp_end;
Jakub Jelen def1de
+	char *p = data;
Jakub Jelen def1de
+	long value;
Jakub Jelen def1de
+	size_t outlen = 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	out = malloc(strlen(data)+1); /* upper bound */
Jakub Jelen def1de
+	if (out == NULL)
Jakub Jelen def1de
+		return -1;
Jakub Jelen def1de
+	while (*p != '\0') {
Jakub Jelen def1de
+		switch (*p) {
Jakub Jelen def1de
+		case '%':
Jakub Jelen def1de
+			p++;
Jakub Jelen def1de
+			if (*p == '\0')
Jakub Jelen def1de
+				goto fail;
Jakub Jelen def1de
+			tmp[0] = *p++;
Jakub Jelen def1de
+			if (*p == '\0')
Jakub Jelen def1de
+				goto fail;
Jakub Jelen def1de
+			tmp[1] = *p++;
Jakub Jelen def1de
+			tmp[2] = '\0';
Jakub Jelen def1de
+			tmp_end = NULL;
Jakub Jelen def1de
+			value = strtol(tmp, &tmp_end, 16);
Jakub Jelen def1de
+			if (tmp_end != tmp+2)
Jakub Jelen def1de
+				goto fail;
Jakub Jelen def1de
+			else
Jakub Jelen def1de
+				out[outlen++] = (char) value;
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+		default:
Jakub Jelen def1de
+			out[outlen++] = *p++;
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* zero terminate */
Jakub Jelen def1de
+	out[outlen] = '\0';
Jakub Jelen def1de
+	*outp = out;
Jakub Jelen def1de
+	return outlen;
Jakub Jelen def1de
+fail:
Jakub Jelen def1de
+	free(out);
Jakub Jelen def1de
+	return -1;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+struct sshbuf *
Jakub Jelen def1de
+percent_encode(const char *data, size_t length, const char *whitelist)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct sshbuf *b = NULL;
Jakub Jelen def1de
+	char tmp[4], *cp;
Jakub Jelen def1de
+	size_t i;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((b = sshbuf_new()) == NULL)
Jakub Jelen def1de
+		return NULL;
Jakub Jelen def1de
+	for (i = 0; i < length; i++) {
Jakub Jelen def1de
+		cp = strchr(whitelist, data[i]);
Jakub Jelen def1de
+		/* if c is specified as '\0' pointer to terminator is returned !! */
Jakub Jelen def1de
+		if (cp != NULL && *cp != '\0') {
Jakub Jelen def1de
+			if (sshbuf_put(b, &data[i], 1) != 0)
Jakub Jelen def1de
+				goto err;
Jakub Jelen def1de
+		} else
Jakub Jelen def1de
+			if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3
Jakub Jelen def1de
+			    || sshbuf_put(b, tmp, 3) != 0)
Jakub Jelen def1de
+				goto err;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if (sshbuf_put(b, "\0", 1) == 0)
Jakub Jelen def1de
+		return b;
Jakub Jelen def1de
+err:
Jakub Jelen def1de
+	sshbuf_free(b);
Jakub Jelen def1de
+	return NULL;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+char *
Jakub Jelen def1de
+pkcs11_uri_append(char *part, const char *separator, const char *key,
Jakub Jelen def1de
+    struct sshbuf *value)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	char *new_part;
Jakub Jelen def1de
+	size_t size = 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (value == NULL)
Jakub Jelen def1de
+		return NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	size = asprintf(&new_part,
Jakub Jelen def1de
+	    "%s%s%s"  PKCS11_URI_VALUE_SEPARATOR "%s",
Jakub Jelen def1de
+	    (part != NULL ? part : ""),
Jakub Jelen def1de
+	    (part != NULL ? separator : ""),
Jakub Jelen def1de
+	    key, sshbuf_ptr(value));
Jakub Jelen def1de
+	sshbuf_free(value);
Jakub Jelen def1de
+	free(part);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (size <= 0)
Jakub Jelen def1de
+		return NULL;
Jakub Jelen def1de
+	return new_part;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+char *
Jakub Jelen def1de
+pkcs11_uri_get(struct pkcs11_uri *uri)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	size_t size = 0;
Jakub Jelen def1de
+	char *p = NULL, *path = NULL, *query = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* compose a percent-encoded ID */
Jakub Jelen def1de
+	if (uri->id_len > 0) {
Jakub Jelen def1de
+		struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, "");
Jakub Jelen def1de
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Jakub Jelen def1de
+		    PKCS11_URI_ID, key_id);
Jakub Jelen def1de
+		if (path == NULL)
Jakub Jelen def1de
+			goto err;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Write object label */
Jakub Jelen def1de
+	if (uri->object) {
Jakub Jelen def1de
+		struct sshbuf *label = percent_encode(uri->object, strlen(uri->object),
Jakub Jelen def1de
+		    PKCS11_URI_WHITELIST);
Jakub Jelen def1de
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Jakub Jelen def1de
+		    PKCS11_URI_OBJECT, label);
Jakub Jelen def1de
+		if (path == NULL)
Jakub Jelen def1de
+			goto err;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Write token label */
Jakub Jelen def1de
+	if (uri->token) {
Jakub Jelen def1de
+		struct sshbuf *label = percent_encode(uri->token, strlen(uri->token),
Jakub Jelen def1de
+		    PKCS11_URI_WHITELIST);
Jakub Jelen def1de
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Jakub Jelen def1de
+		    PKCS11_URI_TOKEN, label);
Jakub Jelen def1de
+		if (path == NULL)
Jakub Jelen def1de
+			goto err;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Write manufacturer */
Jakub Jelen def1de
+	if (uri->manuf) {
Jakub Jelen def1de
+		struct sshbuf *manuf = percent_encode(uri->manuf,
Jakub Jelen def1de
+		    strlen(uri->manuf), PKCS11_URI_WHITELIST);
Jakub Jelen def1de
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Jakub Jelen def1de
+		    PKCS11_URI_MANUF, manuf);
Jakub Jelen def1de
+		if (path == NULL)
Jakub Jelen def1de
+			goto err;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* Write module_path */
Jakub Jelen def1de
+	if (uri->module_path) {
Jakub Jelen def1de
+		struct sshbuf *module = percent_encode(uri->module_path,
Jakub Jelen def1de
+		    strlen(uri->module_path), PKCS11_URI_WHITELIST "/");
Jakub Jelen def1de
+		query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR,
Jakub Jelen def1de
+		    PKCS11_URI_MODULE_PATH, module);
Jakub Jelen def1de
+		if (query == NULL)
Jakub Jelen def1de
+			goto err;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s",
Jakub Jelen def1de
+	    path != NULL ? path : "",
Jakub Jelen def1de
+	    query != NULL ? "?" : "",
Jakub Jelen def1de
+	    query != NULL ? query : "");
Jakub Jelen def1de
+err:
Jakub Jelen def1de
+	free(query);
Jakub Jelen def1de
+	free(path);
Jakub Jelen def1de
+	if (size <= 0)
Jakub Jelen def1de
+		return NULL;
Jakub Jelen def1de
+	return p;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+struct pkcs11_uri *
Jakub Jelen def1de
+pkcs11_uri_init()
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri));
Jakub Jelen def1de
+	return d;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+void
Jakub Jelen def1de
+pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	free(pkcs11->id);
Jakub Jelen def1de
+	free(pkcs11->module_path);
Jakub Jelen def1de
+	free(pkcs11->token);
Jakub Jelen def1de
+	free(pkcs11->object);
Jakub Jelen def1de
+	free(pkcs11->lib_manuf);
Jakub Jelen def1de
+	free(pkcs11->manuf);
Jakub Jelen def1de
+	if (pkcs11->pin)
Jakub Jelen def1de
+		freezero(pkcs11->pin, strlen(pkcs11->pin));
Jakub Jelen def1de
+	free(pkcs11);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+int
Jakub Jelen def1de
+pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	char *saveptr1, *saveptr2, *str1, *str2, *tok;
Jakub Jelen def1de
+	int rv = 0, len;
Jakub Jelen def1de
+	char *p = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	size_t scheme_len = strlen(PKCS11_URI_SCHEME);
Jakub Jelen def1de
+	if (strlen(uri) < scheme_len || /* empty URI matches everything */
Jakub Jelen def1de
+	    strncmp(uri, PKCS11_URI_SCHEME, scheme_len) != 0) {
Jakub Jelen def1de
+		error("%s: The '%s' does not look like PKCS#11 URI",
Jakub Jelen def1de
+		    __func__, uri);
Jakub Jelen def1de
+		return -1;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (pkcs11 == NULL) {
Jakub Jelen def1de
+		error("%s: Bad arguments. The pkcs11 can't be null", __func__);
Jakub Jelen def1de
+		return -1;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* skip URI schema name */
Jakub Jelen def1de
+	p = strdup(uri);
Jakub Jelen def1de
+	str1 = p;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* everything before ? */
Jakub Jelen def1de
+	tok = strtok_r(str1, "?", &saveptr1);
Jakub Jelen def1de
+	if (tok == NULL) {
Jakub Jelen def1de
+		error("%s: pk11-path expected, got EOF", __func__);
Jakub Jelen def1de
+		rv = -1;
Jakub Jelen def1de
+		goto out;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* skip URI schema name:
Jakub Jelen def1de
+	 * the scheme ensures that there is at least something before "?"
Jakub Jelen def1de
+	 * allowing empty pk11-path. Resulting token at worst pointing to
Jakub Jelen def1de
+	 * \0 byte */
Jakub Jelen def1de
+	tok = tok + scheme_len;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* parse pk11-path */
Jakub Jelen def1de
+	for (str2 = tok; ; str2 = NULL) {
Jakub Jelen def1de
+		char **charptr, *arg = NULL;
Jakub Jelen def1de
+		pkcs11uriOpCodes opcode;
Jakub Jelen def1de
+		tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2);
Jakub Jelen def1de
+		if (tok == NULL)
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+		opcode = parse_token(tok);
Jakub Jelen def1de
+		if (opcode != pBadOption)
Jakub Jelen def1de
+			arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
Jakub Jelen def1de
+
Jakub Jelen def1de
+		switch (opcode) {
Jakub Jelen def1de
+		case pId:
Jakub Jelen def1de
+			/* CKA_ID */
Jakub Jelen def1de
+			if (pkcs11->id != NULL) {
Jakub Jelen def1de
+				verbose("%s: The id already set in the PKCS#11 URI",
Jakub Jelen def1de
+					__func__);
Jakub Jelen def1de
+				rv = -1;
Jakub Jelen def1de
+				goto out;
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			len = percent_decode(arg, &pkcs11->id);
Jakub Jelen def1de
+			if (len <= 0) {
Jakub Jelen def1de
+				verbose("%s: Failed to percent-decode CKA_ID: %s",
Jakub Jelen def1de
+				    __func__, arg);
Jakub Jelen def1de
+				rv = -1;
Jakub Jelen def1de
+				goto out;
Jakub Jelen def1de
+			} else
Jakub Jelen def1de
+				pkcs11->id_len = len;
Jakub Jelen def1de
+			debug3("%s: Setting CKA_ID = %s from PKCS#11 URI",
Jakub Jelen def1de
+			    __func__, arg);
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+		case pToken:
Jakub Jelen def1de
+			/* CK_TOKEN_INFO -> label */
Jakub Jelen def1de
+			charptr = &pkcs11->token;
Jakub Jelen def1de
+ parse_string:
Jakub Jelen def1de
+			if (*charptr != NULL) {
Jakub Jelen def1de
+				verbose("%s: The %s already set in the PKCS#11 URI",
Jakub Jelen def1de
+				    keywords[opcode].name, __func__);
Jakub Jelen def1de
+				rv = -1;
Jakub Jelen def1de
+				goto out;
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			percent_decode(arg, charptr);
Jakub Jelen def1de
+			debug3("%s: Setting %s = %s from PKCS#11 URI",
Jakub Jelen def1de
+			    __func__, keywords[opcode].name, *charptr);
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+
Jakub Jelen def1de
+		case pObject:
Jakub Jelen def1de
+			/* CK_TOKEN_INFO -> manufacturerID */
Jakub Jelen def1de
+			charptr = &pkcs11->object;
Jakub Jelen def1de
+			goto parse_string;
Jakub Jelen def1de
+
Jakub Jelen def1de
+		case pManufacturer:
Jakub Jelen def1de
+			/* CK_TOKEN_INFO -> manufacturerID */
Jakub Jelen def1de
+			charptr = &pkcs11->manuf;
Jakub Jelen def1de
+			goto parse_string;
Jakub Jelen def1de
+
Jakub Jelen def1de
+		case pLibraryManufacturer:
Jakub Jelen def1de
+			/* CK_INFO -> manufacturerID */
Jakub Jelen def1de
+			charptr = &pkcs11->lib_manuf;
Jakub Jelen def1de
+			goto parse_string;
Jakub Jelen def1de
+
Jakub Jelen def1de
+		default:
Jakub Jelen def1de
+			/* Unrecognized attribute in the URI path SHOULD be error */
Jakub Jelen def1de
+			verbose("%s: Unknown part of path in PKCS#11 URI: %s",
Jakub Jelen def1de
+			    __func__, tok);
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	tok = strtok_r(NULL, "?", &saveptr1);
Jakub Jelen def1de
+	if (tok == NULL) {
Jakub Jelen def1de
+		goto out;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	/* parse pk11-query (optional) */
Jakub Jelen def1de
+	for (str2 = tok; ; str2 = NULL) {
Jakub Jelen def1de
+		char *arg;
Jakub Jelen def1de
+		pkcs11uriOpCodes opcode;
Jakub Jelen def1de
+		tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2);
Jakub Jelen def1de
+		if (tok == NULL)
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+		opcode = parse_token(tok);
Jakub Jelen def1de
+		if (opcode != pBadOption)
Jakub Jelen def1de
+			arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
Jakub Jelen def1de
+
Jakub Jelen def1de
+		switch (opcode) {
Jakub Jelen def1de
+		case pModulePath:
Jakub Jelen def1de
+			/* module-path is PKCS11Provider */
Jakub Jelen def1de
+			if (pkcs11->module_path != NULL) {
Jakub Jelen def1de
+				verbose("%s: Multiple module-path attributes are"
Jakub Jelen def1de
+				    "not supported the PKCS#11 URI", __func__);
Jakub Jelen def1de
+				rv = -1;
Jakub Jelen def1de
+				goto out;
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			percent_decode(arg, &pkcs11->module_path);
Jakub Jelen def1de
+			debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI",
Jakub Jelen def1de
+			    __func__, pkcs11->module_path);
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+
Jakub Jelen def1de
+		case pPinValue:
Jakub Jelen def1de
+			/* pin-value */
Jakub Jelen def1de
+			if (pkcs11->pin != NULL) {
Jakub Jelen def1de
+				verbose("%s: Multiple pin-value attributes are"
Jakub Jelen def1de
+				    "not supported the PKCS#11 URI", __func__);
Jakub Jelen def1de
+				rv = -1;
Jakub Jelen def1de
+				goto out;
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			percent_decode(arg, &pkcs11->pin);
Jakub Jelen def1de
+			debug3("%s: Setting PIN from PKCS#11 URI", __func__);
Jakub Jelen def1de
+			break;
Jakub Jelen def1de
+
Jakub Jelen def1de
+		default:
Jakub Jelen def1de
+			/* Unrecognized attribute in the URI query SHOULD be ignored */
Jakub Jelen def1de
+			verbose("%s: Unknown part of query in PKCS#11 URI: %s",
Jakub Jelen def1de
+			    __func__, tok);
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+out:
Jakub Jelen def1de
+	free(p);
Jakub Jelen def1de
+	return rv;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+#endif /* ENABLE_PKCS11 */
Jakub Jelen def1de
diff --git a/ssh-pkcs11-uri.h b/ssh-pkcs11-uri.h
Jakub Jelen def1de
new file mode 100644
Jakub Jelen def1de
index 00000000..942a5a5a
Jakub Jelen def1de
--- /dev/null
Jakub Jelen def1de
+++ b/ssh-pkcs11-uri.h
Jakub Jelen def1de
@@ -0,0 +1,42 @@
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * Copyright (c) 2017 Red Hat
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * Authors: Jakub Jelen <jjelen@redhat.com>
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * Permission to use, copy, modify, and distribute this software for any
Jakub Jelen def1de
+ * purpose with or without fee is hereby granted, provided that the above
Jakub Jelen def1de
+ * copyright notice and this permission notice appear in all copies.
Jakub Jelen def1de
+ *
Jakub Jelen def1de
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Jakub Jelen def1de
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Jakub Jelen def1de
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Jakub Jelen def1de
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Jakub Jelen def1de
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Jakub Jelen def1de
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Jakub Jelen def1de
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+
Jakub Jelen def1de
+#define PKCS11_URI_SCHEME "pkcs11:"
Jakub Jelen def1de
+#define PKCS11_URI_WHITELIST	"abcdefghijklmnopqrstuvwxyz" \
Jakub Jelen def1de
+				"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
Jakub Jelen def1de
+				"0123456789_-.()"
Jakub Jelen def1de
+
Jakub Jelen def1de
+struct pkcs11_uri {
Jakub Jelen def1de
+	/* path */
Jakub Jelen def1de
+	char *id;
Jakub Jelen def1de
+	size_t id_len;
Jakub Jelen def1de
+	char *token;
Jakub Jelen def1de
+	char *object;
Jakub Jelen def1de
+	char *lib_manuf;
Jakub Jelen def1de
+	char *manuf;
Jakub Jelen def1de
+	/* query */
Jakub Jelen def1de
+	char *module_path;
Jakub Jelen def1de
+	char *pin; /* Only parsed, but not printed */
Jakub Jelen def1de
+};
Jakub Jelen def1de
+
Jakub Jelen def1de
+struct	 pkcs11_uri *pkcs11_uri_init();
Jakub Jelen def1de
+void	 pkcs11_uri_cleanup(struct pkcs11_uri *);
Jakub Jelen def1de
+int	 pkcs11_uri_parse(const char *, struct pkcs11_uri *);
Jakub Jelen def1de
+struct	 pkcs11_uri *pkcs11_uri_init();
Jakub Jelen def1de
+char	*pkcs11_uri_get(struct pkcs11_uri *uri);
Jakub Jelen def1de
+
Jakub Jelen def1de
diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c
Jakub Jelen def1de
index 70f06bff..59332945 100644
Jakub Jelen def1de
--- a/ssh-pkcs11.c
Jakub Jelen def1de
+++ b/ssh-pkcs11.c
Jakub Jelen def1de
@@ -54,8 +54,8 @@ struct pkcs11_slotinfo {
Jakub Jelen def1de
 	int			logged_in;
Jakub Jelen def1de
 };
Jakub Jelen def1de
 
Jakub Jelen def1de
-struct pkcs11_provider {
Jakub Jelen def1de
-	char			*name;
Jakub Jelen def1de
+struct pkcs11_module {
Jakub Jelen def1de
+	char			*module_path;
Jakub Jelen def1de
 	void			*handle;
Jakub Jelen def1de
 	CK_FUNCTION_LIST	*function_list;
Jakub Jelen def1de
 	CK_INFO			info;
Jakub Jelen def1de
@@ -64,6 +64,13 @@ struct pkcs11_provider {
Jakub Jelen def1de
 	struct pkcs11_slotinfo	*slotinfo;
Jakub Jelen def1de
 	int			valid;
Jakub Jelen def1de
 	int			refcount;
Jakub Jelen def1de
+};
Jakub Jelen def1de
+
Jakub Jelen def1de
+struct pkcs11_provider {
Jakub Jelen def1de
+	char			*name;
Jakub Jelen def1de
+	struct pkcs11_module	*module; /* can be shared between various providers */
Jakub Jelen def1de
+	int			refcount;
Jakub Jelen def1de
+	int			valid;
Jakub Jelen def1de
 	TAILQ_ENTRY(pkcs11_provider) next;
Jakub Jelen def1de
 };
Jakub Jelen def1de
 
Jakub Jelen def1de
@@ -74,6 +81,7 @@ struct pkcs11_key {
Jakub Jelen def1de
 	CK_ULONG		slotidx;
Jakub Jelen def1de
 	char			*keyid;
Jakub Jelen def1de
 	int			keyid_len;
Jakub Jelen def1de
+	char			*label;
Jakub Jelen def1de
 };
Jakub Jelen def1de
 
Jakub Jelen def1de
 int pkcs11_interactive = 0;
Jakub Jelen def1de
@@ -106,26 +114,63 @@ pkcs11_init(int interactive)
Jakub Jelen def1de
  * this is called when a provider gets unregistered.
Jakub Jelen def1de
  */
Jakub Jelen def1de
 static void
Jakub Jelen def1de
-pkcs11_provider_finalize(struct pkcs11_provider *p)
Jakub Jelen def1de
+pkcs11_module_finalize(struct pkcs11_module *m)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	CK_RV rv;
Jakub Jelen def1de
 	CK_ULONG i;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	debug("pkcs11_provider_finalize: %p refcount %d valid %d",
Jakub Jelen def1de
-	    p, p->refcount, p->valid);
Jakub Jelen def1de
-	if (!p->valid)
Jakub Jelen def1de
+	debug("%s: %p refcount %d valid %d", __func__,
Jakub Jelen def1de
+	    m, m->refcount, m->valid);
Jakub Jelen def1de
+	if (!m->valid)
Jakub Jelen def1de
 		return;
Jakub Jelen def1de
-	for (i = 0; i < p->nslots; i++) {
Jakub Jelen def1de
-		if (p->slotinfo[i].session &&
Jakub Jelen def1de
-		    (rv = p->function_list->C_CloseSession(
Jakub Jelen def1de
-		    p->slotinfo[i].session)) != CKR_OK)
Jakub Jelen def1de
+	for (i = 0; i < m->nslots; i++) {
Jakub Jelen def1de
+		if (m->slotinfo[i].session &&
Jakub Jelen def1de
+		    (rv = m->function_list->C_CloseSession(
Jakub Jelen def1de
+		    m->slotinfo[i].session)) != CKR_OK)
Jakub Jelen def1de
 			error("C_CloseSession failed: %lu", rv);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
Jakub Jelen def1de
+	if ((rv = m->function_list->C_Finalize(NULL)) != CKR_OK)
Jakub Jelen def1de
 		error("C_Finalize failed: %lu", rv);
Jakub Jelen def1de
+	m->valid = 0;
Jakub Jelen def1de
+	m->function_list = NULL;
Jakub Jelen def1de
+	dlclose(m->handle);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * remove a reference to the pkcs11 module.
Jakub Jelen def1de
+ * called when a provider is unregistered.
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+static void
Jakub Jelen def1de
+pkcs11_module_unref(struct pkcs11_module *m)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	debug("%s: %p refcount %d", __func__, m, m->refcount);
Jakub Jelen def1de
+	if (--m->refcount <= 0) {
Jakub Jelen def1de
+		pkcs11_module_finalize(m);
Jakub Jelen def1de
+		if (m->valid)
Jakub Jelen def1de
+			error("%s: %p still valid", __func__, m);
Jakub Jelen def1de
+		free(m->slotlist);
Jakub Jelen def1de
+		free(m->slotinfo);
Jakub Jelen def1de
+		free(m->module_path);
Jakub Jelen def1de
+		free(m);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * finalize a provider shared libarary, it's no longer usable.
Jakub Jelen def1de
+ * however, there might still be keys referencing this provider,
Jakub Jelen def1de
+ * so the actuall freeing of memory is handled by pkcs11_provider_unref().
Jakub Jelen def1de
+ * this is called when a provider gets unregistered.
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+static void
Jakub Jelen def1de
+pkcs11_provider_finalize(struct pkcs11_provider *p)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	debug("%s: %p refcount %d valid %d", __func__,
Jakub Jelen def1de
+	    p, p->refcount, p->valid);
Jakub Jelen def1de
+	if (!p->valid)
Jakub Jelen def1de
+		return;
Jakub Jelen def1de
+	pkcs11_module_unref(p->module);
Jakub Jelen def1de
+	p->module = NULL;
Jakub Jelen def1de
 	p->valid = 0;
Jakub Jelen def1de
-	p->function_list = NULL;
Jakub Jelen def1de
-	dlclose(p->handle);
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
 /*
Jakub Jelen def1de
@@ -135,13 +180,11 @@ pkcs11_provider_finalize(struct pkcs11_provider *p)
Jakub Jelen def1de
 static void
Jakub Jelen def1de
 pkcs11_provider_unref(struct pkcs11_provider *p)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
Jakub Jelen def1de
+	debug("%s: %p refcount %d", __func__, p, p->refcount);
Jakub Jelen def1de
 	if (--p->refcount <= 0) {
Jakub Jelen def1de
-		if (p->valid)
Jakub Jelen def1de
-			error("pkcs11_provider_unref: %p still valid", p);
Jakub Jelen def1de
 		free(p->name);
Jakub Jelen def1de
-		free(p->slotlist);
Jakub Jelen def1de
-		free(p->slotinfo);
Jakub Jelen def1de
+		if (p->module)
Jakub Jelen def1de
+			pkcs11_module_unref(p->module);
Jakub Jelen def1de
 		free(p);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 }
Jakub Jelen def1de
@@ -159,6 +202,20 @@ pkcs11_terminate(void)
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
+/* lookup provider by module path */
Jakub Jelen def1de
+static struct pkcs11_module *
Jakub Jelen def1de
+pkcs11_provider_lookup_module(char *module_path)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct pkcs11_provider *p;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	TAILQ_FOREACH(p, &pkcs11_providers, next) {
Jakub Jelen def1de
+		debug("check %p %s (%s)", p, p->name, p->module->module_path);
Jakub Jelen def1de
+		if (!strcmp(module_path, p->module->module_path))
Jakub Jelen def1de
+			return (p->module);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	return (NULL);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 /* lookup provider by name */
Jakub Jelen def1de
 static struct pkcs11_provider *
Jakub Jelen def1de
 pkcs11_provider_lookup(char *provider_id)
Jakub Jelen def1de
@@ -173,19 +230,52 @@ pkcs11_provider_lookup(char *provider_id)
Jakub Jelen def1de
 	return (NULL);
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
+int pkcs11_del_provider_by_uri(struct pkcs11_uri *);
Jakub Jelen def1de
+
Jakub Jelen def1de
 /* unregister provider by name */
Jakub Jelen def1de
 int
Jakub Jelen def1de
 pkcs11_del_provider(char *provider_id)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	int rv;
Jakub Jelen def1de
+	struct pkcs11_uri *uri;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	debug("%s: called, provider_id = %s", __func__, provider_id);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	uri = pkcs11_uri_init();
Jakub Jelen def1de
+	if (uri == NULL)
Jakub Jelen def1de
+		fatal("Failed to init PCKS#11 URI");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) &&
Jakub Jelen def1de
+	    strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) {
Jakub Jelen def1de
+		if (pkcs11_uri_parse(provider_id, uri) != 0)
Jakub Jelen def1de
+			fatal("Failed to parse PKCS#11 URI");
Jakub Jelen def1de
+	} else {
Jakub Jelen def1de
+		uri->module_path = strdup(provider_id);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	rv = pkcs11_del_provider_by_uri(uri);
Jakub Jelen def1de
+	pkcs11_uri_cleanup(uri);
Jakub Jelen def1de
+	return rv;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+/* unregister provider by PKCS#11 URI */
Jakub Jelen def1de
+int
Jakub Jelen def1de
+pkcs11_del_provider_by_uri(struct pkcs11_uri *uri)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	struct pkcs11_provider *p;
Jakub Jelen def1de
+	int rv = -1;
Jakub Jelen def1de
+	char *provider_uri = pkcs11_uri_get(uri);
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
Jakub Jelen def1de
+	debug3("%s(%s): called", __func__, provider_uri);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) {
Jakub Jelen def1de
 		TAILQ_REMOVE(&pkcs11_providers, p, next);
Jakub Jelen def1de
 		pkcs11_provider_finalize(p);
Jakub Jelen def1de
 		pkcs11_provider_unref(p);
Jakub Jelen def1de
-		return (0);
Jakub Jelen def1de
+		rv = 0;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	return (-1);
Jakub Jelen def1de
+	free(provider_uri);
Jakub Jelen def1de
+	return rv;
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
 static RSA_METHOD *rsa_method;
Jakub Jelen def1de
@@ -195,6 +285,55 @@ static EC_KEY_METHOD *ec_key_method;
Jakub Jelen def1de
 static int ec_key_idx = 0;
Jakub Jelen def1de
 #endif
Jakub Jelen def1de
 
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * This can't be in the ssh-pkcs11-uri, becase we can not depend on
Jakub Jelen def1de
+ * PKCS#11 structures in ssh-agent (using client-helper communication)
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+int
Jakub Jelen def1de
+pkcs11_uri_write(const struct sshkey *key, FILE *f)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	char *p = NULL;
Jakub Jelen def1de
+	struct pkcs11_uri uri;
Jakub Jelen def1de
+	struct pkcs11_key *k11;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* sanity - is it a RSA key with associated app_data? */
Jakub Jelen def1de
+	switch (key->type) {
Jakub Jelen def1de
+	case KEY_RSA:
Jakub Jelen def1de
+		k11 = RSA_get_ex_data(key->rsa, rsa_idx);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+#ifdef HAVE_EC_KEY_METHOD_NEW
Jakub Jelen def1de
+	case KEY_ECDSA:
Jakub Jelen def1de
+		k11 = EC_KEY_get_ex_data(key->ecdsa, ec_key_idx);
Jakub Jelen def1de
+		break;
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+	default:
Jakub Jelen def1de
+		error("Unknown key type %d", key->type);
Jakub Jelen def1de
+		return -1;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if (k11 == NULL) {
Jakub Jelen def1de
+		error("Failed to get ex_data for key type %d", key->type);
Jakub Jelen def1de
+		return (-1);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* omit type -- we are looking for private-public or private-certificate pairs */
Jakub Jelen def1de
+	uri.id = k11->keyid;
Jakub Jelen def1de
+	uri.id_len = k11->keyid_len;
Jakub Jelen def1de
+	uri.token = k11->provider->module->slotinfo[k11->slotidx].token.label;
Jakub Jelen def1de
+	uri.object = k11->label;
Jakub Jelen def1de
+	uri.module_path = k11->provider->module->module_path;
Jakub Jelen def1de
+	uri.lib_manuf = k11->provider->module->info.manufacturerID;
Jakub Jelen def1de
+	uri.manuf = k11->provider->module->slotinfo[k11->slotidx].token.manufacturerID;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	p = pkcs11_uri_get(&uri);
Jakub Jelen def1de
+	/* do not cleanup -- we do not allocate here, only reference */
Jakub Jelen def1de
+	if (p == NULL)
Jakub Jelen def1de
+		return -1;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	fprintf(f, " %s", p);
Jakub Jelen def1de
+	free(p);
Jakub Jelen def1de
+	return 0;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 /* release a wrapped object */
Jakub Jelen def1de
 static void
Jakub Jelen def1de
 pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
Jakub Jelen def1de
@@ -208,6 +347,7 @@ pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
Jakub Jelen def1de
         if (k11->provider)
Jakub Jelen def1de
                 pkcs11_provider_unref(k11->provider);
Jakub Jelen def1de
         free(k11->keyid);
Jakub Jelen def1de
+	free(k11->label);
Jakub Jelen def1de
         free(k11);
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
@@ -222,8 +362,8 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
Jakub Jelen def1de
 	CK_RV			rv;
Jakub Jelen def1de
 	int			ret = -1;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	f = p->function_list;
Jakub Jelen def1de
-	session = p->slotinfo[slotidx].session;
Jakub Jelen def1de
+	f = p->module->function_list;
Jakub Jelen def1de
+	session = p->module->slotinfo[slotidx].session;
Jakub Jelen def1de
 	if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) {
Jakub Jelen def1de
 		error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv);
Jakub Jelen def1de
 		return (-1);
Jakub Jelen def1de
@@ -252,8 +392,8 @@ pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type)
Jakub Jelen def1de
 		return (-1);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	f = k11->provider->function_list;
Jakub Jelen def1de
-	si = &k11->provider->slotinfo[k11->slotidx];
Jakub Jelen def1de
+	f = k11->provider->module->function_list;
Jakub Jelen def1de
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if (!pkcs11_interactive) {
Jakub Jelen def1de
 		error("need pin entry%s",
Jakub Jelen def1de
@@ -300,8 +440,8 @@ pkcs11_check_obj_bool_attrib(struct pkcs11_key *k11, CK_OBJECT_HANDLE obj,
Jakub Jelen def1de
 		return (-1);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	f = k11->provider->function_list;
Jakub Jelen def1de
-	si = &k11->provider->slotinfo[k11->slotidx];
Jakub Jelen def1de
+	f = k11->provider->module->function_list;
Jakub Jelen def1de
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Jakub Jelen def1de
 
Jakub Jelen def1de
 	attr.type = type;
Jakub Jelen def1de
 	attr.pValue = &flag;
Jakub Jelen def1de
@@ -332,13 +472,14 @@ pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
Jakub Jelen def1de
 	int			 always_auth = 0;
Jakub Jelen def1de
 	int			 did_login = 0;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (!k11->provider || !k11->provider->valid) {
Jakub Jelen def1de
+	if (!k11->provider || !k11->provider->valid || !k11->provider->module
Jakub Jelen def1de
+	    || !k11->provider->module->valid) {
Jakub Jelen def1de
 		error("no pkcs11 (valid) provider found");
Jakub Jelen def1de
 		return (-1);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	f = k11->provider->function_list;
Jakub Jelen def1de
-	si = &k11->provider->slotinfo[k11->slotidx];
Jakub Jelen def1de
+	f = k11->provider->module->function_list;
Jakub Jelen def1de
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
Jakub Jelen def1de
 		if (pkcs11_login(k11, CKU_USER) < 0) {
Jakub Jelen def1de
@@ -415,8 +556,8 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
Jakub Jelen def1de
 		return (-1);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	f = k11->provider->function_list;
Jakub Jelen def1de
-	si = &k11->provider->slotinfo[k11->slotidx];
Jakub Jelen def1de
+	f = k11->provider->module->function_list;
Jakub Jelen def1de
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Jakub Jelen def1de
 	tlen = RSA_size(rsa);
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* XXX handle CKR_BUFFER_TOO_SMALL */
Jakub Jelen def1de
@@ -460,7 +601,7 @@ pkcs11_rsa_start_wrapper(void)
Jakub Jelen def1de
 /* redirect private key operations for rsa key to pkcs11 token */
Jakub Jelen def1de
 static int
Jakub Jelen def1de
 pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Jakub Jelen def1de
-    CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
Jakub Jelen def1de
+    CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, RSA *rsa)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	struct pkcs11_key	*k11;
Jakub Jelen def1de
 
Jakub Jelen def1de
@@ -478,6 +619,12 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Jakub Jelen def1de
 		memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
+	if (label_attrib->ulValueLen > 0 ) {
Jakub Jelen def1de
+		k11->label = xmalloc(label_attrib->ulValueLen+1);
Jakub Jelen def1de
+		memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen);
Jakub Jelen def1de
+		k11->label[label_attrib->ulValueLen] = 0;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
 	RSA_set_method(rsa, rsa_method);
Jakub Jelen def1de
 	RSA_set_ex_data(rsa, rsa_idx, k11);
Jakub Jelen def1de
 	return (0);
Jakub Jelen def1de
@@ -508,8 +655,8 @@ ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
Jakub Jelen def1de
 		return (NULL);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	f = k11->provider->function_list;
Jakub Jelen def1de
-	si = &k11->provider->slotinfo[k11->slotidx];
Jakub Jelen def1de
+	f = k11->provider->module->function_list;
Jakub Jelen def1de
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Jakub Jelen def1de
 
Jakub Jelen def1de
 	siglen = ECDSA_size(ec);
Jakub Jelen def1de
 	sig = xmalloc(siglen);
Jakub Jelen def1de
@@ -574,7 +721,7 @@ pkcs11_ecdsa_start_wrapper(void)
Jakub Jelen def1de
 
Jakub Jelen def1de
 static int
Jakub Jelen def1de
 pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Jakub Jelen def1de
-    CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
Jakub Jelen def1de
+    CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ec)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	struct pkcs11_key	*k11;
Jakub Jelen def1de
 
Jakub Jelen def1de
@@ -590,6 +737,12 @@ pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Jakub Jelen def1de
 	k11->keyid = xmalloc(k11->keyid_len);
Jakub Jelen def1de
 	memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
Jakub Jelen def1de
 
Jakub Jelen def1de
+	if (label_attrib->ulValueLen > 0 ) {
Jakub Jelen def1de
+		k11->label = xmalloc(label_attrib->ulValueLen+1);
Jakub Jelen def1de
+		memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen);
Jakub Jelen def1de
+		k11->label[label_attrib->ulValueLen] = 0;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
 	EC_KEY_set_method(ec, ec_key_method);
Jakub Jelen def1de
 	EC_KEY_set_ex_data(ec, ec_key_idx, k11);
Jakub Jelen def1de
 
Jakub Jelen def1de
@@ -624,47 +777,26 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
Jakub Jelen def1de
 	CK_FUNCTION_LIST	*f;
Jakub Jelen def1de
 	CK_RV			rv;
Jakub Jelen def1de
 	CK_SESSION_HANDLE	session;
Jakub Jelen def1de
-	int			login_required, have_pinpad, ret;
Jakub Jelen def1de
-	char			prompt[1024], *xpin = NULL;
Jakub Jelen def1de
+	int			login_required, ret;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	f = p->function_list;
Jakub Jelen def1de
-	si = &p->slotinfo[slotidx];
Jakub Jelen def1de
+	f = p->module->function_list;
Jakub Jelen def1de
+	si = &p->module->slotinfo[slotidx];
Jakub Jelen def1de
 
Jakub Jelen def1de
-	have_pinpad = si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH;
Jakub Jelen def1de
 	login_required = si->token.flags & CKF_LOGIN_REQUIRED;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* fail early before opening session */
Jakub Jelen def1de
-	if (login_required && !have_pinpad && !pkcs11_interactive &&
Jakub Jelen def1de
+	if (login_required && !pkcs11_interactive &&
Jakub Jelen def1de
 	    (pin == NULL || strlen(pin) == 0)) {
Jakub Jelen def1de
 		error("pin required");
Jakub Jelen def1de
 		return (-SSH_PKCS11_ERR_PIN_REQUIRED);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
Jakub Jelen def1de
+	if ((rv = f->C_OpenSession(p->module->slotlist[slotidx], CKF_RW_SESSION|
Jakub Jelen def1de
 	    CKF_SERIAL_SESSION, NULL, NULL, &session)) != CKR_OK) {
Jakub Jelen def1de
-		error("C_OpenSession failed: %lu", rv);
Jakub Jelen def1de
+		error("C_OpenSession failed for slot %lu: %lu", slotidx, rv);
Jakub Jelen def1de
 		return (-1);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	if (login_required) {
Jakub Jelen def1de
-		if (have_pinpad && (pin == NULL || strlen(pin) == 0)) {
Jakub Jelen def1de
-			/* defer PIN entry to the reader keypad */
Jakub Jelen def1de
-			rv = f->C_Login(session, CKU_USER, NULL_PTR, 0);
Jakub Jelen def1de
-		} else {
Jakub Jelen def1de
-			if (pkcs11_interactive) {
Jakub Jelen def1de
-				snprintf(prompt, sizeof(prompt),
Jakub Jelen def1de
-				    "Enter PIN for '%s': ", si->token.label);
Jakub Jelen def1de
-				if ((xpin = read_passphrase(prompt,
Jakub Jelen def1de
-				    RP_ALLOW_EOF)) == NULL) {
Jakub Jelen def1de
-					debug("%s: no pin specified",
Jakub Jelen def1de
-					    __func__);
Jakub Jelen def1de
-					return (-SSH_PKCS11_ERR_PIN_REQUIRED);
Jakub Jelen def1de
-				}
Jakub Jelen def1de
-				pin = xpin;
Jakub Jelen def1de
-			}
Jakub Jelen def1de
-			rv = f->C_Login(session, CKU_USER,
Jakub Jelen def1de
-			    (u_char *)pin, strlen(pin));
Jakub Jelen def1de
-			if (xpin != NULL)
Jakub Jelen def1de
-				freezero(xpin, strlen(xpin));
Jakub Jelen def1de
-		}
Jakub Jelen def1de
+	if (login_required && pin != NULL && strlen(pin) != 0) {
Jakub Jelen def1de
+		rv = f->C_Login(session, user, (u_char *)pin, strlen(pin));
Jakub Jelen def1de
 		if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
Jakub Jelen def1de
 			error("C_Login failed: %lu", rv);
Jakub Jelen def1de
 			ret = (rv == CKR_PIN_LOCKED) ?
Jakub Jelen def1de
@@ -696,7 +828,8 @@ static struct sshkey *
Jakub Jelen def1de
 pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
     CK_OBJECT_HANDLE *obj)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	CK_ATTRIBUTE		 key_attr[3];
Jakub Jelen def1de
+	CK_ATTRIBUTE		 key_attr[4];
Jakub Jelen def1de
+	int			 nattr = 4;
Jakub Jelen def1de
 	CK_SESSION_HANDLE	 session;
Jakub Jelen def1de
 	CK_FUNCTION_LIST	*f = NULL;
Jakub Jelen def1de
 	CK_RV			 rv;
Jakub Jelen def1de
@@ -710,14 +843,15 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 
Jakub Jelen def1de
 	memset(&key_attr, 0, sizeof(key_attr));
Jakub Jelen def1de
 	key_attr[0].type = CKA_ID;
Jakub Jelen def1de
-	key_attr[1].type = CKA_EC_POINT;
Jakub Jelen def1de
-	key_attr[2].type = CKA_EC_PARAMS;
Jakub Jelen def1de
+	key_attr[1].type = CKA_LABEL;
Jakub Jelen def1de
+	key_attr[2].type = CKA_EC_POINT;
Jakub Jelen def1de
+	key_attr[3].type = CKA_EC_PARAMS;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	session = p->slotinfo[slotidx].session;
Jakub Jelen def1de
-	f = p->function_list;
Jakub Jelen def1de
+	session = p->module->slotinfo[slotidx].session;
Jakub Jelen def1de
+	f = p->module->function_list;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* figure out size of the attributes */
Jakub Jelen def1de
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Jakub Jelen def1de
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Jakub Jelen def1de
 	if (rv != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetAttributeValue failed: %lu", rv);
Jakub Jelen def1de
 		return (NULL);
Jakub Jelen def1de
@@ -729,18 +863,19 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 	 * XXX assumes CKA_ID is always first.
Jakub Jelen def1de
 	 */
Jakub Jelen def1de
 	if (key_attr[1].ulValueLen == 0 ||
Jakub Jelen def1de
-	    key_attr[2].ulValueLen == 0) {
Jakub Jelen def1de
+	    key_attr[2].ulValueLen == 0 ||
Jakub Jelen def1de
+	    key_attr[3].ulValueLen == 0) {
Jakub Jelen def1de
 		error("invalid attribute length");
Jakub Jelen def1de
 		return (NULL);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* allocate buffers for attributes */
Jakub Jelen def1de
-	for (i = 0; i < 3; i++)
Jakub Jelen def1de
+	for (i = 0; i < nattr; i++)
Jakub Jelen def1de
 		if (key_attr[i].ulValueLen > 0)
Jakub Jelen def1de
 			key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* retrieve ID, public point and curve parameters of EC key */
Jakub Jelen def1de
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Jakub Jelen def1de
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Jakub Jelen def1de
 	if (rv != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetAttributeValue failed: %lu", rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
@@ -752,8 +887,8 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	attrp = key_attr[2].pValue;
Jakub Jelen def1de
-	group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
Jakub Jelen def1de
+	attrp = key_attr[3].pValue;
Jakub Jelen def1de
+	group = d2i_ECPKParameters(NULL, &attrp, key_attr[3].ulValueLen);
Jakub Jelen def1de
 	if (group == NULL) {
Jakub Jelen def1de
 		ossl_error("d2i_ECPKParameters failed");
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
@@ -764,13 +899,13 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (key_attr[1].ulValueLen <= 2) {
Jakub Jelen def1de
+	if (key_attr[2].ulValueLen <= 2) {
Jakub Jelen def1de
 		error("CKA_EC_POINT too small");
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	attrp = key_attr[1].pValue;
Jakub Jelen def1de
-	octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen);
Jakub Jelen def1de
+	attrp = key_attr[2].pValue;
Jakub Jelen def1de
+	octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[2].ulValueLen);
Jakub Jelen def1de
 	if (octet == NULL) {
Jakub Jelen def1de
 		ossl_error("d2i_ASN1_OCTET_STRING failed");
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
@@ -787,7 +922,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
Jakub Jelen def1de
+	if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], ec))
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	key = sshkey_new(KEY_UNSPEC);
Jakub Jelen def1de
@@ -803,7 +938,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 	ec = NULL;	/* now owned by key */
Jakub Jelen def1de
 
Jakub Jelen def1de
 fail:
Jakub Jelen def1de
-	for (i = 0; i < 3; i++)
Jakub Jelen def1de
+	for (i = 0; i < nattr; i++)
Jakub Jelen def1de
 		free(key_attr[i].pValue);
Jakub Jelen def1de
 	if (ec)
Jakub Jelen def1de
 		EC_KEY_free(ec);
Jakub Jelen def1de
@@ -820,7 +955,8 @@ static struct sshkey *
Jakub Jelen def1de
 pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
     CK_OBJECT_HANDLE *obj)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	CK_ATTRIBUTE		 key_attr[3];
Jakub Jelen def1de
+	CK_ATTRIBUTE		 key_attr[4];
Jakub Jelen def1de
+	int			 nattr = 4;
Jakub Jelen def1de
 	CK_SESSION_HANDLE	 session;
Jakub Jelen def1de
 	CK_FUNCTION_LIST	*f = NULL;
Jakub Jelen def1de
 	CK_RV			 rv;
Jakub Jelen def1de
@@ -831,14 +967,15 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 
Jakub Jelen def1de
 	memset(&key_attr, 0, sizeof(key_attr));
Jakub Jelen def1de
 	key_attr[0].type = CKA_ID;
Jakub Jelen def1de
-	key_attr[1].type = CKA_MODULUS;
Jakub Jelen def1de
-	key_attr[2].type = CKA_PUBLIC_EXPONENT;
Jakub Jelen def1de
+	key_attr[1].type = CKA_LABEL;
Jakub Jelen def1de
+	key_attr[2].type = CKA_MODULUS;
Jakub Jelen def1de
+	key_attr[3].type = CKA_PUBLIC_EXPONENT;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	session = p->slotinfo[slotidx].session;
Jakub Jelen def1de
-	f = p->function_list;
Jakub Jelen def1de
+	session = p->module->slotinfo[slotidx].session;
Jakub Jelen def1de
+	f = p->module->function_list;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* figure out size of the attributes */
Jakub Jelen def1de
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Jakub Jelen def1de
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Jakub Jelen def1de
 	if (rv != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetAttributeValue failed: %lu", rv);
Jakub Jelen def1de
 		return (NULL);
Jakub Jelen def1de
@@ -850,18 +987,19 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 	 * XXX assumes CKA_ID is always first.
Jakub Jelen def1de
 	 */
Jakub Jelen def1de
 	if (key_attr[1].ulValueLen == 0 ||
Jakub Jelen def1de
-	    key_attr[2].ulValueLen == 0) {
Jakub Jelen def1de
+	    key_attr[2].ulValueLen == 0 ||
Jakub Jelen def1de
+	    key_attr[3].ulValueLen == 0) {
Jakub Jelen def1de
 		error("invalid attribute length");
Jakub Jelen def1de
 		return (NULL);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* allocate buffers for attributes */
Jakub Jelen def1de
-	for (i = 0; i < 3; i++)
Jakub Jelen def1de
+	for (i = 0; i < nattr; i++)
Jakub Jelen def1de
 		if (key_attr[i].ulValueLen > 0)
Jakub Jelen def1de
 			key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* retrieve ID, modulus and public exponent of RSA key */
Jakub Jelen def1de
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Jakub Jelen def1de
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Jakub Jelen def1de
 	if (rv != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetAttributeValue failed: %lu", rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
@@ -873,8 +1011,8 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
Jakub Jelen def1de
-	rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
Jakub Jelen def1de
+	rsa_n = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
Jakub Jelen def1de
+	rsa_e = BN_bin2bn(key_attr[3].pValue, key_attr[3].ulValueLen, NULL);
Jakub Jelen def1de
 	if (rsa_n == NULL || rsa_e == NULL) {
Jakub Jelen def1de
 		error("BN_bin2bn failed");
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
@@ -883,7 +1021,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 		fatal("%s: set key", __func__);
Jakub Jelen def1de
 	rsa_n = rsa_e = NULL; /* transferred */
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
Jakub Jelen def1de
+	if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], rsa))
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	key = sshkey_new(KEY_UNSPEC);
Jakub Jelen def1de
@@ -898,7 +1036,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 	rsa = NULL;	/* now owned by key */
Jakub Jelen def1de
 
Jakub Jelen def1de
 fail:
Jakub Jelen def1de
-	for (i = 0; i < 3; i++)
Jakub Jelen def1de
+	for (i = 0; i < nattr; i++)
Jakub Jelen def1de
 		free(key_attr[i].pValue);
Jakub Jelen def1de
 	RSA_free(rsa);
Jakub Jelen def1de
 
Jakub Jelen def1de
@@ -909,7 +1047,8 @@ static struct sshkey *
Jakub Jelen def1de
 pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
     CK_OBJECT_HANDLE *obj)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	CK_ATTRIBUTE		 cert_attr[3];
Jakub Jelen def1de
+	CK_ATTRIBUTE		 cert_attr[4];
Jakub Jelen def1de
+	int			 nattr = 4;
Jakub Jelen def1de
 	CK_SESSION_HANDLE	 session;
Jakub Jelen def1de
 	CK_FUNCTION_LIST	*f = NULL;
Jakub Jelen def1de
 	CK_RV			 rv;
Jakub Jelen def1de
@@ -926,14 +1065,15 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 
Jakub Jelen def1de
 	memset(&cert_attr, 0, sizeof(cert_attr));
Jakub Jelen def1de
 	cert_attr[0].type = CKA_ID;
Jakub Jelen def1de
-	cert_attr[1].type = CKA_SUBJECT;
Jakub Jelen def1de
-	cert_attr[2].type = CKA_VALUE;
Jakub Jelen def1de
+	cert_attr[1].type = CKA_LABEL;
Jakub Jelen def1de
+	cert_attr[2].type = CKA_SUBJECT;
Jakub Jelen def1de
+	cert_attr[3].type = CKA_VALUE;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	session = p->slotinfo[slotidx].session;
Jakub Jelen def1de
-	f = p->function_list;
Jakub Jelen def1de
+	session = p->module->slotinfo[slotidx].session;
Jakub Jelen def1de
+	f = p->module->function_list;
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* figure out size of the attributes */
Jakub Jelen def1de
-	rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
Jakub Jelen def1de
+	rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr);
Jakub Jelen def1de
 	if (rv != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetAttributeValue failed: %lu", rv);
Jakub Jelen def1de
 		return (NULL);
Jakub Jelen def1de
@@ -945,18 +1085,19 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 	 * XXX assumes CKA_ID is always first.
Jakub Jelen def1de
 	 */
Jakub Jelen def1de
 	if (cert_attr[1].ulValueLen == 0 ||
Jakub Jelen def1de
-	    cert_attr[2].ulValueLen == 0) {
Jakub Jelen def1de
+	    cert_attr[2].ulValueLen == 0 ||
Jakub Jelen def1de
+	    cert_attr[3].ulValueLen == 0) {
Jakub Jelen def1de
 		error("invalid attribute length");
Jakub Jelen def1de
 		return (NULL);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* allocate buffers for attributes */
Jakub Jelen def1de
-	for (i = 0; i < 3; i++)
Jakub Jelen def1de
+	for (i = 0; i < nattr; i++)
Jakub Jelen def1de
 		if (cert_attr[i].ulValueLen > 0)
Jakub Jelen def1de
 			cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* retrieve ID, subject and value of certificate */
Jakub Jelen def1de
-	rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
Jakub Jelen def1de
+	rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr);
Jakub Jelen def1de
 	if (rv != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetAttributeValue failed: %lu", rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
@@ -968,8 +1109,8 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	cp = cert_attr[2].pValue;
Jakub Jelen def1de
-	if (d2i_X509(&x509, &cp, cert_attr[2].ulValueLen) == NULL) {
Jakub Jelen def1de
+	cp = cert_attr[3].pValue;
Jakub Jelen def1de
+	if (d2i_X509(&x509, &cp, cert_attr[3].ulValueLen) == NULL) {
Jakub Jelen def1de
 		error("d2i_x509 failed");
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
@@ -990,7 +1131,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 			goto fail;
Jakub Jelen def1de
 		}
Jakub Jelen def1de
 
Jakub Jelen def1de
-		if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
Jakub Jelen def1de
+		if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], rsa))
Jakub Jelen def1de
 			goto fail;
Jakub Jelen def1de
 
Jakub Jelen def1de
 		key = sshkey_new(KEY_UNSPEC);
Jakub Jelen def1de
@@ -1020,7 +1161,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 			goto fail;
Jakub Jelen def1de
 		}
Jakub Jelen def1de
 
Jakub Jelen def1de
-		if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
Jakub Jelen def1de
+		if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], ec))
Jakub Jelen def1de
 			goto fail;
Jakub Jelen def1de
 
Jakub Jelen def1de
 		key = sshkey_new(KEY_UNSPEC);
Jakub Jelen def1de
@@ -1039,7 +1180,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 		error("unknown certificate key type");
Jakub Jelen def1de
 
Jakub Jelen def1de
 fail:
Jakub Jelen def1de
-	for (i = 0; i < 3; i++)
Jakub Jelen def1de
+	for (i = 0; i < nattr; i++)
Jakub Jelen def1de
 		free(cert_attr[i].pValue);
Jakub Jelen def1de
 	X509_free(x509);
Jakub Jelen def1de
 	RSA_free(rsa);
Jakub Jelen def1de
@@ -1066,11 +1207,12 @@ have_rsa_key(const RSA *rsa)
Jakub Jelen def1de
  */
Jakub Jelen def1de
 static int
Jakub Jelen def1de
 pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
-    struct sshkey ***keysp, int *nkeys)
Jakub Jelen def1de
+    struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	struct sshkey		*key = NULL;
Jakub Jelen def1de
 	CK_OBJECT_CLASS		 key_class;
Jakub Jelen def1de
-	CK_ATTRIBUTE		 key_attr[1];
Jakub Jelen def1de
+	CK_ATTRIBUTE		 key_attr[3];
Jakub Jelen def1de
+	int			 nattr = 1;
Jakub Jelen def1de
 	CK_SESSION_HANDLE	 session;
Jakub Jelen def1de
 	CK_FUNCTION_LIST	*f = NULL;
Jakub Jelen def1de
 	CK_RV			 rv;
Jakub Jelen def1de
@@ -1086,10 +1228,23 @@ pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 	key_attr[0].pValue = &key_class;
Jakub Jelen def1de
 	key_attr[0].ulValueLen = sizeof(key_class);
Jakub Jelen def1de
 
Jakub Jelen def1de
-	session = p->slotinfo[slotidx].session;
Jakub Jelen def1de
-	f = p->function_list;
Jakub Jelen def1de
+	if (uri->id != NULL) {
Jakub Jelen def1de
+		key_attr[nattr].type = CKA_ID;
Jakub Jelen def1de
+		key_attr[nattr].pValue = uri->id;
Jakub Jelen def1de
+		key_attr[nattr].ulValueLen = uri->id_len;
Jakub Jelen def1de
+		nattr++;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if (uri->object != NULL) {
Jakub Jelen def1de
+		key_attr[nattr].type = CKA_LABEL;
Jakub Jelen def1de
+		key_attr[nattr].pValue = uri->object;
Jakub Jelen def1de
+		key_attr[nattr].ulValueLen = strlen(uri->object);
Jakub Jelen def1de
+		nattr++;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	rv = f->C_FindObjectsInit(session, key_attr, 1);
Jakub Jelen def1de
+	session = p->module->slotinfo[slotidx].session;
Jakub Jelen def1de
+	f = p->module->function_list;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	rv = f->C_FindObjectsInit(session, key_attr, nattr);
Jakub Jelen def1de
 	if (rv != CKR_OK) {
Jakub Jelen def1de
 		error("C_FindObjectsInit failed: %lu", rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
@@ -1163,11 +1318,12 @@ fail:
Jakub Jelen def1de
  */
Jakub Jelen def1de
 static int
Jakub Jelen def1de
 pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
-    struct sshkey ***keysp, int *nkeys)
Jakub Jelen def1de
+    struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri)
Jakub Jelen def1de
 {
Jakub Jelen def1de
 	struct sshkey		*key = NULL;
Jakub Jelen def1de
 	CK_OBJECT_CLASS		 key_class;
Jakub Jelen def1de
-	CK_ATTRIBUTE		 key_attr[1];
Jakub Jelen def1de
+	CK_ATTRIBUTE		 key_attr[3];
Jakub Jelen def1de
+	int			 nattr = 1;
Jakub Jelen def1de
 	CK_SESSION_HANDLE	 session;
Jakub Jelen def1de
 	CK_FUNCTION_LIST	*f = NULL;
Jakub Jelen def1de
 	CK_RV			 rv;
Jakub Jelen def1de
@@ -1183,10 +1339,23 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 	key_attr[0].pValue = &key_class;
Jakub Jelen def1de
 	key_attr[0].ulValueLen = sizeof(key_class);
Jakub Jelen def1de
 
Jakub Jelen def1de
-	session = p->slotinfo[slotidx].session;
Jakub Jelen def1de
-	f = p->function_list;
Jakub Jelen def1de
+	if (uri->id != NULL) {
Jakub Jelen def1de
+		key_attr[nattr].type = CKA_ID;
Jakub Jelen def1de
+		key_attr[nattr].pValue = uri->id;
Jakub Jelen def1de
+		key_attr[nattr].ulValueLen = uri->id_len;
Jakub Jelen def1de
+		nattr++;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if (uri->object != NULL) {
Jakub Jelen def1de
+		key_attr[nattr].type = CKA_LABEL;
Jakub Jelen def1de
+		key_attr[nattr].pValue = uri->object;
Jakub Jelen def1de
+		key_attr[nattr].ulValueLen = strlen(uri->object);
Jakub Jelen def1de
+		nattr++;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	rv = f->C_FindObjectsInit(session, key_attr, 1);
Jakub Jelen def1de
+	session = p->module->slotinfo[slotidx].session;
Jakub Jelen def1de
+	f = p->module->function_list;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	rv = f->C_FindObjectsInit(session, key_attr, nattr);
Jakub Jelen def1de
 	if (rv != CKR_OK) {
Jakub Jelen def1de
 		error("C_FindObjectsInit failed: %lu", rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
@@ -1443,15 +1612,10 @@ pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
Jakub Jelen def1de
 }
Jakub Jelen def1de
 #endif /* WITH_PKCS11_KEYGEN */
Jakub Jelen def1de
 
Jakub Jelen def1de
-/*
Jakub Jelen def1de
- * register a new provider, fails if provider already exists. if
Jakub Jelen def1de
- * keyp is provided, fetch keys.
Jakub Jelen def1de
- */
Jakub Jelen def1de
 static int
Jakub Jelen def1de
-pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
Jakub Jelen def1de
-    struct pkcs11_provider **providerp, CK_ULONG user)
Jakub Jelen def1de
+pkcs11_initialize_provider(struct pkcs11_uri *uri, struct pkcs11_provider **providerp)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	int nkeys, need_finalize = 0;
Jakub Jelen def1de
+	int need_finalize = 0;
Jakub Jelen def1de
 	int ret = -1;
Jakub Jelen def1de
 	struct pkcs11_provider *p = NULL;
Jakub Jelen def1de
 	void *handle = NULL;
Jakub Jelen def1de
@@ -1460,148 +1624,285 @@ pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
Jakub Jelen def1de
 	CK_FUNCTION_LIST *f = NULL;
Jakub Jelen def1de
 	CK_TOKEN_INFO *token;
Jakub Jelen def1de
 	CK_ULONG i;
Jakub Jelen def1de
-
Jakub Jelen def1de
-	if (providerp == NULL)
Jakub Jelen def1de
+	char *provider_module = NULL;
Jakub Jelen def1de
+	struct pkcs11_module *m;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* if no provider specified, fallback to p11-kit */
Jakub Jelen def1de
+	if (uri->module_path == NULL) {
Jakub Jelen def1de
+#ifdef PKCS11_DEFAULT_PROVIDER
Jakub Jelen def1de
+		provider_module = strdup(PKCS11_DEFAULT_PROVIDER);
Jakub Jelen def1de
+#else
Jakub Jelen def1de
+		error("%s: No module path provided", __func__);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
-	*providerp = NULL;
Jakub Jelen def1de
-
Jakub Jelen def1de
-	if (keyp != NULL)
Jakub Jelen def1de
-		*keyp = NULL;
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
+	} else {
Jakub Jelen def1de
+		provider_module = strdup(uri->module_path);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
 
Jakub Jelen def1de
-	if (pkcs11_provider_lookup(provider_id) != NULL) {
Jakub Jelen def1de
-		debug("%s: provider already registered: %s",
Jakub Jelen def1de
-		    __func__, provider_id);
Jakub Jelen def1de
-		goto fail;
Jakub Jelen def1de
+	p = xcalloc(1, sizeof(*p));
Jakub Jelen def1de
+	p->name = pkcs11_uri_get(uri);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL
Jakub Jelen def1de
+	   && m->valid) {
Jakub Jelen def1de
+		debug("%s: provider module already initialized: %s",
Jakub Jelen def1de
+		    __func__, provider_module);
Jakub Jelen def1de
+		free(provider_module);
Jakub Jelen def1de
+		/* Skip the initialization of PKCS#11 module */
Jakub Jelen def1de
+		m->refcount++;
Jakub Jelen def1de
+		p->module = m;
Jakub Jelen def1de
+		p->valid = 1;
Jakub Jelen def1de
+		TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
Jakub Jelen def1de
+		p->refcount++;	/* add to provider list */
Jakub Jelen def1de
+		*providerp = p;
Jakub Jelen def1de
+		return 0;
Jakub Jelen def1de
+	} else {
Jakub Jelen def1de
+		m = xcalloc(1, sizeof(*m));
Jakub Jelen def1de
+		p->module = m;
Jakub Jelen def1de
+		m->refcount++;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
+
Jakub Jelen def1de
 	/* open shared pkcs11-library */
Jakub Jelen def1de
-	if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
Jakub Jelen def1de
-		error("dlopen %s failed: %s", provider_id, dlerror());
Jakub Jelen def1de
+	if ((handle = dlopen(provider_module, RTLD_NOW)) == NULL) {
Jakub Jelen def1de
+		error("dlopen %s failed: %s", provider_module, dlerror());
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 	if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
Jakub Jelen def1de
 		error("dlsym(C_GetFunctionList) failed: %s", dlerror());
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	p = xcalloc(1, sizeof(*p));
Jakub Jelen def1de
-	p->name = xstrdup(provider_id);
Jakub Jelen def1de
-	p->handle = handle;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	p->module->handle = handle;
Jakub Jelen def1de
 	/* setup the pkcs11 callbacks */
Jakub Jelen def1de
 	if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetFunctionList for provider %s failed: %lu",
Jakub Jelen def1de
-		    provider_id, rv);
Jakub Jelen def1de
+		    provider_module, rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	p->function_list = f;
Jakub Jelen def1de
+	m->function_list = f;
Jakub Jelen def1de
 	if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
Jakub Jelen def1de
 		error("C_Initialize for provider %s failed: %lu",
Jakub Jelen def1de
-		    provider_id, rv);
Jakub Jelen def1de
+		    provider_module, rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 	need_finalize = 1;
Jakub Jelen def1de
-	if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
Jakub Jelen def1de
+	if ((rv = f->C_GetInfo(&m->info)) != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetInfo for provider %s failed: %lu",
Jakub Jelen def1de
-		    provider_id, rv);
Jakub Jelen def1de
+		    provider_module, rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
Jakub Jelen def1de
-	rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
Jakub Jelen def1de
+	rmspace(m->info.manufacturerID, sizeof(m->info.manufacturerID));
Jakub Jelen def1de
+	if (uri->lib_manuf != NULL &&
Jakub Jelen def1de
+	    strcmp(uri->lib_manuf, m->info.manufacturerID)) {
Jakub Jelen def1de
+		debug("%s: Skipping provider %s not matching library_manufacturer",
Jakub Jelen def1de
+		    __func__, m->info.manufacturerID);
Jakub Jelen def1de
+		goto fail;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	rmspace(m->info.libraryDescription, sizeof(m->info.libraryDescription));
Jakub Jelen def1de
 	debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d"
Jakub Jelen def1de
 	    " libraryDescription <%s> libraryVersion %d.%d",
Jakub Jelen def1de
-	    provider_id,
Jakub Jelen def1de
-	    p->info.manufacturerID,
Jakub Jelen def1de
-	    p->info.cryptokiVersion.major,
Jakub Jelen def1de
-	    p->info.cryptokiVersion.minor,
Jakub Jelen def1de
-	    p->info.libraryDescription,
Jakub Jelen def1de
-	    p->info.libraryVersion.major,
Jakub Jelen def1de
-	    p->info.libraryVersion.minor);
Jakub Jelen def1de
-	if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
Jakub Jelen def1de
+	    provider_module,
Jakub Jelen def1de
+	    m->info.manufacturerID,
Jakub Jelen def1de
+	    m->info.cryptokiVersion.major,
Jakub Jelen def1de
+	    m->info.cryptokiVersion.minor,
Jakub Jelen def1de
+	    m->info.libraryDescription,
Jakub Jelen def1de
+	    m->info.libraryVersion.major,
Jakub Jelen def1de
+	    m->info.libraryVersion.minor);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &m->nslots)) != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetSlotList failed: %lu", rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	if (p->nslots == 0) {
Jakub Jelen def1de
+	if (m->nslots == 0) {
Jakub Jelen def1de
 		error("%s: provider %s returned no slots", __func__,
Jakub Jelen def1de
-		    provider_id);
Jakub Jelen def1de
+		    provider_module);
Jakub Jelen def1de
 		ret = -SSH_PKCS11_ERR_NO_SLOTS;
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
Jakub Jelen def1de
-	if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots))
Jakub Jelen def1de
+	m->slotlist = xcalloc(m->nslots, sizeof(CK_SLOT_ID));
Jakub Jelen def1de
+	if ((rv = f->C_GetSlotList(CK_TRUE, m->slotlist, &m->nslots))
Jakub Jelen def1de
 	    != CKR_OK) {
Jakub Jelen def1de
 		error("C_GetSlotList for provider %s failed: %lu",
Jakub Jelen def1de
-		    provider_id, rv);
Jakub Jelen def1de
+		    provider_module, rv);
Jakub Jelen def1de
 		goto fail;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
Jakub Jelen def1de
 	p->valid = 1;
Jakub Jelen def1de
-	nkeys = 0;
Jakub Jelen def1de
-	for (i = 0; i < p->nslots; i++) {
Jakub Jelen def1de
-		token = &p->slotinfo[i].token;
Jakub Jelen def1de
-		if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
Jakub Jelen def1de
+	m->slotinfo = xcalloc(m->nslots, sizeof(struct pkcs11_slotinfo));
Jakub Jelen def1de
+	m->valid = 1;
Jakub Jelen def1de
+	for (i = 0; i < m->nslots; i++) {
Jakub Jelen def1de
+		token = &m->slotinfo[i].token;
Jakub Jelen def1de
+		if ((rv = f->C_GetTokenInfo(m->slotlist[i], token))
Jakub Jelen def1de
 		    != CKR_OK) {
Jakub Jelen def1de
 			error("C_GetTokenInfo for provider %s slot %lu "
Jakub Jelen def1de
-			    "failed: %lu", provider_id, (unsigned long)i, rv);
Jakub Jelen def1de
+			    "failed: %lu", provider_module, (unsigned long)i, rv);
Jakub Jelen def1de
+			token->flags = 0;
Jakub Jelen def1de
 			continue;
Jakub Jelen def1de
 		}
Jakub Jelen def1de
+		rmspace(token->label, sizeof(token->label));
Jakub Jelen def1de
+		rmspace(token->manufacturerID, sizeof(token->manufacturerID));
Jakub Jelen def1de
+		rmspace(token->model, sizeof(token->model));
Jakub Jelen def1de
+		rmspace(token->serialNumber, sizeof(token->serialNumber));
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	m->module_path = provider_module;
Jakub Jelen def1de
+	provider_module = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* insert unconditionally -- remove if there will be no keys later */
Jakub Jelen def1de
+	TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
Jakub Jelen def1de
+	p->refcount++;	/* add to provider list */
Jakub Jelen def1de
+	*providerp = p;
Jakub Jelen def1de
+	return 0;
Jakub Jelen def1de
+
Jakub Jelen def1de
+fail:
Jakub Jelen def1de
+	if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
Jakub Jelen def1de
+		error("C_Finalize for provider %s failed: %lu",
Jakub Jelen def1de
+		    provider_module, rv);
Jakub Jelen def1de
+	free(provider_module);
Jakub Jelen def1de
+	if (m) {
Jakub Jelen def1de
+		free(m->slotlist);
Jakub Jelen def1de
+		free(m);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if (p) {
Jakub Jelen def1de
+		free(p->name);
Jakub Jelen def1de
+		free(p);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+	if (handle)
Jakub Jelen def1de
+		dlclose(handle);
Jakub Jelen def1de
+	return ret;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * register a new provider, fails if provider already exists. if
Jakub Jelen def1de
+ * keyp is provided, fetch keys.
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+static int
Jakub Jelen def1de
+pkcs11_register_provider_by_uri(struct pkcs11_uri *uri, char *pin,
Jakub Jelen def1de
+    struct sshkey ***keyp, struct pkcs11_provider **providerp, CK_ULONG user)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	int nkeys;
Jakub Jelen def1de
+	int ret = -1;
Jakub Jelen def1de
+	struct pkcs11_provider *p = NULL;
Jakub Jelen def1de
+	CK_ULONG i;
Jakub Jelen def1de
+	CK_TOKEN_INFO *token;
Jakub Jelen def1de
+	char *provider_uri = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (providerp == NULL)
Jakub Jelen def1de
+		goto fail;
Jakub Jelen def1de
+	*providerp = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (keyp != NULL)
Jakub Jelen def1de
+		*keyp = NULL;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if ((ret = pkcs11_initialize_provider(uri, &p)) != 0) {
Jakub Jelen def1de
+		goto fail;
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	provider_uri = pkcs11_uri_get(uri);
Jakub Jelen def1de
+	nkeys = 0;
Jakub Jelen def1de
+	for (i = 0; i < p->module->nslots; i++) {
Jakub Jelen def1de
+		token = &p->module->slotinfo[i].token;
Jakub Jelen def1de
 		if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
Jakub Jelen def1de
 			debug2("%s: ignoring uninitialised token in "
Jakub Jelen def1de
 			    "provider %s slot %lu", __func__,
Jakub Jelen def1de
-			    provider_id, (unsigned long)i);
Jakub Jelen def1de
+			    provider_uri, (unsigned long)i);
Jakub Jelen def1de
+			continue;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+		if (uri->token != NULL &&
Jakub Jelen def1de
+		    strcmp(token->label, uri->token) != 0) {
Jakub Jelen def1de
+			debug2("%s: ignoring token not matching label (%s) "
Jakub Jelen def1de
+			    "specified by PKCS#11 URI in slot %lu", __func__,
Jakub Jelen def1de
+			    token->label, (unsigned long)i);
Jakub Jelen def1de
+			continue;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+		if (uri->manuf != NULL &&
Jakub Jelen def1de
+		    strcmp(token->manufacturerID, uri->manuf) != 0) {
Jakub Jelen def1de
+			debug2("%s: ignoring token not matching requrested "
Jakub Jelen def1de
+			    "manufacturerID (%s) specified by PKCS#11 URI in "
Jakub Jelen def1de
+			    "slot %lu", __func__,
Jakub Jelen def1de
+			    token->manufacturerID, (unsigned long)i);
Jakub Jelen def1de
 			continue;
Jakub Jelen def1de
 		}
Jakub Jelen def1de
-		rmspace(token->label, sizeof(token->label));
Jakub Jelen def1de
-		rmspace(token->manufacturerID, sizeof(token->manufacturerID));
Jakub Jelen def1de
-		rmspace(token->model, sizeof(token->model));
Jakub Jelen def1de
-		rmspace(token->serialNumber, sizeof(token->serialNumber));
Jakub Jelen def1de
 		debug("provider %s slot %lu: label <%s> manufacturerID <%s> "
Jakub Jelen def1de
 		    "model <%s> serial <%s> flags 0x%lx",
Jakub Jelen def1de
-		    provider_id, (unsigned long)i,
Jakub Jelen def1de
+		    provider_uri, (unsigned long)i,
Jakub Jelen def1de
 		    token->label, token->manufacturerID, token->model,
Jakub Jelen def1de
 		    token->serialNumber, token->flags);
Jakub Jelen def1de
+		if (pin == NULL && uri->pin != NULL) {
Jakub Jelen def1de
+			pin = uri->pin;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
 		/*
Jakub Jelen def1de
-		 * open session, login with pin and retrieve public
Jakub Jelen def1de
-		 * keys (if keyp is provided)
Jakub Jelen def1de
+		 * open session if not yet openend, login with pin and
Jakub Jelen def1de
+		 * retrieve public keys (if keyp is provided)
Jakub Jelen def1de
 		 */
Jakub Jelen def1de
-		if ((ret = pkcs11_open_session(p, i, pin, user)) == 0) {
Jakub Jelen def1de
+		if (p->module->slotinfo[i].session != 0 ||
Jakub Jelen def1de
+		    (ret = pkcs11_open_session(p, i, pin, user)) == 0) {
Jakub Jelen def1de
 			if (keyp == NULL)
Jakub Jelen def1de
 				continue;
Jakub Jelen def1de
-			pkcs11_fetch_keys(p, i, keyp, &nkeys);
Jakub Jelen def1de
-			pkcs11_fetch_certs(p, i, keyp, &nkeys);
Jakub Jelen def1de
+			pkcs11_fetch_keys(p, i, keyp, &nkeys, uri);
Jakub Jelen def1de
+			pkcs11_fetch_certs(p, i, keyp, &nkeys, uri);
Jakub Jelen def1de
+			if (nkeys == 0 && uri->object != NULL) {
Jakub Jelen def1de
+				debug3("%s: No keys found. Retrying without label (%s) ",
Jakub Jelen def1de
+				    __func__, token->label);
Jakub Jelen def1de
+				/* Try once more without the label filter */
Jakub Jelen def1de
+				char *label = uri->object;
Jakub Jelen def1de
+				uri->object = NULL; /* XXX clone uri? */
Jakub Jelen def1de
+				pkcs11_fetch_keys(p, i, keyp, &nkeys, uri);
Jakub Jelen def1de
+				pkcs11_fetch_certs(p, i, keyp, &nkeys, uri);
Jakub Jelen def1de
+				uri->object = label;
Jakub Jelen def1de
+			}
Jakub Jelen def1de
 		}
Jakub Jelen def1de
+		pin = NULL; /* Will be cleaned up with URI */
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* now owned by caller */
Jakub Jelen def1de
 	*providerp = p;
Jakub Jelen def1de
 
Jakub Jelen def1de
-	TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
Jakub Jelen def1de
-	p->refcount++;	/* add to provider list */
Jakub Jelen def1de
-
Jakub Jelen def1de
+	free(provider_uri);
Jakub Jelen def1de
 	return (nkeys);
Jakub Jelen def1de
 fail:
Jakub Jelen def1de
-	if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
Jakub Jelen def1de
-		error("C_Finalize for provider %s failed: %lu",
Jakub Jelen def1de
-		    provider_id, rv);
Jakub Jelen def1de
 	if (p) {
Jakub Jelen def1de
-		free(p->name);
Jakub Jelen def1de
-		free(p->slotlist);
Jakub Jelen def1de
-		free(p->slotinfo);
Jakub Jelen def1de
-		free(p);
Jakub Jelen def1de
+ 		TAILQ_REMOVE(&pkcs11_providers, p, next);
Jakub Jelen def1de
+		pkcs11_provider_unref(p);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
-	if (handle)
Jakub Jelen def1de
-		dlclose(handle);
Jakub Jelen def1de
 	return (ret);
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
-/*
Jakub Jelen def1de
- * register a new provider and get number of keys hold by the token,
Jakub Jelen def1de
- * fails if provider already exists
Jakub Jelen def1de
- */
Jakub Jelen def1de
+static int
Jakub Jelen def1de
+pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
Jakub Jelen def1de
+    struct pkcs11_provider **providerp, CK_ULONG user)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct pkcs11_uri *uri = NULL;
Jakub Jelen def1de
+	int r;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	debug("%s: called, provider_id = %s", __func__, provider_id);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	uri = pkcs11_uri_init();
Jakub Jelen def1de
+	if (uri == NULL)
Jakub Jelen def1de
+		fatal("failed to init PKCS#11 URI");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) &&
Jakub Jelen def1de
+	    strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) {
Jakub Jelen def1de
+		if (pkcs11_uri_parse(provider_id, uri) != 0)
Jakub Jelen def1de
+			fatal("Failed to parse PKCS#11 URI");
Jakub Jelen def1de
+	} else {
Jakub Jelen def1de
+		uri->module_path = strdup(provider_id);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	r = pkcs11_register_provider_by_uri(uri, pin, keyp, providerp, user);
Jakub Jelen def1de
+	pkcs11_uri_cleanup(uri);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	return r;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
 int
Jakub Jelen def1de
-pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
Jakub Jelen def1de
+pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin,
Jakub Jelen def1de
+    struct sshkey ***keyp)
Jakub Jelen def1de
 {
Jakub Jelen def1de
-	struct pkcs11_provider *p = NULL;
Jakub Jelen def1de
 	int nkeys;
Jakub Jelen def1de
+	struct pkcs11_provider *p = NULL;
Jakub Jelen def1de
+	char *provider_uri = pkcs11_uri_get(uri);
Jakub Jelen def1de
 
Jakub Jelen def1de
-	nkeys = pkcs11_register_provider(provider_id, pin, keyp, &p, CKU_USER);
Jakub Jelen def1de
+	debug("%s: called, provider_uri = %s", __func__, provider_uri);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	nkeys = pkcs11_register_provider_by_uri(uri, pin, keyp, &p, CKU_USER);
Jakub Jelen def1de
 
Jakub Jelen def1de
 	/* no keys found or some other error, de-register provider */
Jakub Jelen def1de
 	if (nkeys <= 0 && p != NULL) {
Jakub Jelen def1de
@@ -1611,7 +1912,36 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 	if (nkeys == 0)
Jakub Jelen def1de
 		debug("%s: provider %s returned no keys", __func__,
Jakub Jelen def1de
-		    provider_id);
Jakub Jelen def1de
+		    provider_uri);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	free(provider_uri);
Jakub Jelen def1de
+	return nkeys;
Jakub Jelen def1de
+}
Jakub Jelen def1de
+
Jakub Jelen def1de
+/*
Jakub Jelen def1de
+ * register a new provider and get number of keys hold by the token,
Jakub Jelen def1de
+ * fails if provider already exists
Jakub Jelen def1de
+ */
Jakub Jelen def1de
+int
Jakub Jelen def1de
+pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	struct pkcs11_uri *uri;
Jakub Jelen def1de
+	int nkeys;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	uri = pkcs11_uri_init();
Jakub Jelen def1de
+	if (uri == NULL)
Jakub Jelen def1de
+		fatal("Failed to init PCKS#11 URI");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) &&
Jakub Jelen def1de
+	    strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) {
Jakub Jelen def1de
+		if (pkcs11_uri_parse(provider_id, uri) != 0)
Jakub Jelen def1de
+			fatal("Failed to parse PKCS#11 URI");
Jakub Jelen def1de
+	} else {
Jakub Jelen def1de
+		uri->module_path = strdup(provider_id);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	nkeys = pkcs11_add_provider_by_uri(uri, pin, keyp);
Jakub Jelen def1de
+	pkcs11_uri_cleanup(uri);
Jakub Jelen def1de
 
Jakub Jelen def1de
 	return (nkeys);
Jakub Jelen def1de
 }
Jakub Jelen def1de
@@ -1633,8 +1963,7 @@ pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
Jakub Jelen def1de
 		debug("%s: provider \"%s\" available", __func__, provider_id);
Jakub Jelen def1de
-	else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, &p,
Jakub Jelen def1de
-	    CKU_SO)) < 0) {
Jakub Jelen def1de
+	else if ((rv = pkcs11_register_provider(provider_id, pin, NULL, &p, CKU_SO)) != 0) {
Jakub Jelen def1de
 		debug("%s: could not register provider %s", __func__,
Jakub Jelen def1de
 		    provider_id);
Jakub Jelen def1de
 		goto out;
Jakub Jelen def1de
@@ -1705,8 +2034,7 @@ pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
Jakub Jelen def1de
 
Jakub Jelen def1de
 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
Jakub Jelen def1de
 		debug("%s: using provider \"%s\"", __func__, provider_id);
Jakub Jelen def1de
-	} else if (pkcs11_register_provider(provider_id, pin, NULL, &p,
Jakub Jelen def1de
-	    CKU_SO) < 0) {
Jakub Jelen def1de
+	} else if ((rv = pkcs11_register_provider(provider_id, pin, NULL, &p, CKU_SO)) != 0) {
Jakub Jelen def1de
 		debug("%s: could not register provider %s", __func__,
Jakub Jelen def1de
 		    provider_id);
Jakub Jelen def1de
 		goto out;
Jakub Jelen def1de
diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h
Jakub Jelen def1de
index b9038450..5a855338 100644
Jakub Jelen def1de
--- a/ssh-pkcs11.h
Jakub Jelen def1de
+++ b/ssh-pkcs11.h
Jakub Jelen def1de
@@ -22,10 +22,14 @@
Jakub Jelen def1de
 #define	SSH_PKCS11_ERR_PIN_REQUIRED		4
Jakub Jelen def1de
 #define	SSH_PKCS11_ERR_PIN_LOCKED		5
Jakub Jelen def1de
 
Jakub Jelen def1de
+#include "ssh-pkcs11-uri.h"
Jakub Jelen def1de
+
Jakub Jelen def1de
 int	pkcs11_init(int);
Jakub Jelen def1de
 void	pkcs11_terminate(void);
Jakub Jelen def1de
 int	pkcs11_add_provider(char *, char *, struct sshkey ***);
Jakub Jelen def1de
+int	pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***);
Jakub Jelen def1de
 int	pkcs11_del_provider(char *);
Jakub Jelen def1de
+int	pkcs11_uri_write(const struct sshkey *, FILE *);
Jakub Jelen def1de
 #ifdef WITH_PKCS11_KEYGEN
Jakub Jelen def1de
 struct sshkey *
Jakub Jelen def1de
 	pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int,
Jakub Jelen def1de
diff --git a/ssh.c b/ssh.c
Jakub Jelen def1de
index 91e7c351..47f4f299 100644
Jakub Jelen def1de
--- a/ssh.c
Jakub Jelen def1de
+++ b/ssh.c
Jakub Jelen def1de
@@ -772,6 +772,14 @@ main(int ac, char **av)
Jakub Jelen def1de
 			options.gss_deleg_creds = 1;
Jakub Jelen def1de
 			break;
Jakub Jelen def1de
 		case 'i':
Jakub Jelen def1de
+#ifdef ENABLE_PKCS11
Jakub Jelen def1de
+			if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) &&
Jakub Jelen def1de
+			    strncmp(optarg, PKCS11_URI_SCHEME,
Jakub Jelen def1de
+			    strlen(PKCS11_URI_SCHEME)) == 0) {
Jakub Jelen def1de
+				add_identity_file(&options, NULL, optarg, 1);
Jakub Jelen def1de
+				break;
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+#endif
Jakub Jelen def1de
 			p = tilde_expand_filename(optarg, getuid());
Jakub Jelen def1de
 			if (stat(p, &st) < 0)
Jakub Jelen def1de
 				fprintf(stderr, "Warning: Identity file %s "
Jakub Jelen def1de
@@ -1521,6 +1529,7 @@ main(int ac, char **av)
Jakub Jelen def1de
 		free(options.certificate_files[i]);
Jakub Jelen def1de
 		options.certificate_files[i] = NULL;
Jakub Jelen def1de
 	}
Jakub Jelen def1de
+	pkcs11_terminate();
Jakub Jelen def1de
 
Jakub Jelen def1de
  skip_connect:
Jakub Jelen def1de
 	exit_status = ssh_session2(ssh, pw);
Jakub Jelen def1de
@@ -1994,6 +2003,45 @@ ssh_session2(struct ssh *ssh, struct passwd *pw)
Jakub Jelen def1de
 	    options.escape_char : SSH_ESCAPECHAR_NONE, id);
Jakub Jelen def1de
 }
Jakub Jelen def1de
 
Jakub Jelen def1de
+#ifdef ENABLE_PKCS11
Jakub Jelen def1de
+static void
Jakub Jelen def1de
+load_pkcs11_identity(char *pkcs11_uri, char *identity_files[],
Jakub Jelen def1de
+    struct sshkey *identity_keys[], int *n_ids)
Jakub Jelen def1de
+{
Jakub Jelen def1de
+	int nkeys, i;
Jakub Jelen def1de
+	struct sshkey **keys;
Jakub Jelen def1de
+	struct pkcs11_uri *uri;
Jakub Jelen def1de
+
Jakub Jelen def1de
+	debug("identity file '%s' from pkcs#11", pkcs11_uri);
Jakub Jelen def1de
+	uri = pkcs11_uri_init();
Jakub Jelen def1de
+	if (uri == NULL)
Jakub Jelen def1de
+		fatal("Failed to init PCKS#11 URI");
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (pkcs11_uri_parse(pkcs11_uri, uri) != 0)
Jakub Jelen def1de
+	fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	/* we need to merge URI and provider together */
Jakub Jelen def1de
+	if (options.pkcs11_provider != NULL && uri->module_path == NULL)
Jakub Jelen def1de
+		uri->module_path = strdup(options.pkcs11_provider);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
Jakub Jelen def1de
+	    (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys)) > 0) {
Jakub Jelen def1de
+		for (i = 0; i < nkeys; i++) {
Jakub Jelen def1de
+			if (*n_ids >= SSH_MAX_IDENTITY_FILES) {
Jakub Jelen def1de
+				sshkey_free(keys[i]);
Jakub Jelen def1de
+				continue;
Jakub Jelen def1de
+			}
Jakub Jelen def1de
+			identity_keys[*n_ids] = keys[i];
Jakub Jelen def1de
+			identity_files[*n_ids] = pkcs11_uri_get(uri);
Jakub Jelen def1de
+			(*n_ids)++;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+		free(keys);
Jakub Jelen def1de
+	}
Jakub Jelen def1de
+
Jakub Jelen def1de
+	pkcs11_uri_cleanup(uri);
Jakub Jelen def1de
+}
Jakub Jelen def1de
+#endif /* ENABLE_PKCS11 */
Jakub Jelen def1de
+
Jakub Jelen def1de
 /* Loads all IdentityFile and CertificateFile keys */
Jakub Jelen def1de
 static void
Jakub Jelen def1de
 load_public_identity_files(struct passwd *pw)
Jakub Jelen def1de
@@ -2008,10 +2056,6 @@ load_public_identity_files(struct passwd *pw)
Jakub Jelen def1de
 	char *certificate_files[SSH_MAX_CERTIFICATE_FILES];
Jakub Jelen def1de
 	struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
Jakub Jelen def1de
 	int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES];
Jakub Jelen def1de
-#ifdef ENABLE_PKCS11
Jakub Jelen def1de
-	struct sshkey **keys;
Jakub Jelen def1de
-	int nkeys;
Jakub Jelen def1de
-#endif /* PKCS11 */
Jakub Jelen def1de
 
Jakub Jelen def1de
 	n_ids = n_certs = 0;
Jakub Jelen def1de
 	memset(identity_files, 0, sizeof(identity_files));
Jakub Jelen def1de
@@ -2024,32 +2068,46 @@ load_public_identity_files(struct passwd *pw)
Jakub Jelen def1de
 	    sizeof(certificate_file_userprovided));
Jakub Jelen def1de
 
Jakub Jelen def1de
 #ifdef ENABLE_PKCS11
Jakub Jelen def1de
-	if (options.pkcs11_provider != NULL &&
Jakub Jelen def1de
-	    options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
Jakub Jelen def1de
-	    (pkcs11_init(!options.batch_mode) == 0) &&
Jakub Jelen def1de
-	    (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL,
Jakub Jelen def1de
-	    &keys)) > 0) {
Jakub Jelen def1de
-		for (i = 0; i < nkeys; i++) {
Jakub Jelen def1de
-			if (n_ids >= SSH_MAX_IDENTITY_FILES) {
Jakub Jelen def1de
-				sshkey_free(keys[i]);
Jakub Jelen def1de
-				continue;
Jakub Jelen def1de
-			}
Jakub Jelen def1de
-			identity_keys[n_ids] = keys[i];
Jakub Jelen def1de
-			identity_files[n_ids] =
Jakub Jelen def1de
-			    xstrdup(options.pkcs11_provider); /* XXX */
Jakub Jelen def1de
-			n_ids++;
Jakub Jelen def1de
-		}
Jakub Jelen def1de
-		free(keys);
Jakub Jelen def1de
+	/* handle fallback from PKCS11Provider option */
Jakub Jelen def1de
+	pkcs11_init(!options.batch_mode);
Jakub Jelen def1de
+
Jakub Jelen def1de
+	if (options.pkcs11_provider != NULL) {
Jakub Jelen def1de
+		struct pkcs11_uri *uri;
Jakub Jelen def1de
+
Jakub Jelen def1de
+		uri = pkcs11_uri_init();
Jakub Jelen def1de
+		if (uri == NULL)
Jakub Jelen def1de
+			fatal("Failed to init PCKS#11 URI");
Jakub Jelen def1de
+
Jakub Jelen def1de
+		/* Construct simple PKCS#11 URI to simplify access */
Jakub Jelen def1de
+		uri->module_path = strdup(options.pkcs11_provider);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		/* Add it as any other IdentityFile */
Jakub Jelen def1de
+		cp = pkcs11_uri_get(uri);
Jakub Jelen def1de
+		add_identity_file(&options, NULL, cp, 1);
Jakub Jelen def1de
+		free(cp);
Jakub Jelen def1de
+
Jakub Jelen def1de
+		pkcs11_uri_cleanup(uri);
Jakub Jelen def1de
 	}
Jakub Jelen def1de
 #endif /* ENABLE_PKCS11 */
Jakub Jelen def1de
 	for (i = 0; i < options.num_identity_files; i++) {
Jakub Jelen def1de
+		char *name = options.identity_files[i];
Jakub Jelen def1de
 		if (n_ids >= SSH_MAX_IDENTITY_FILES ||
Jakub Jelen def1de
-		    strcasecmp(options.identity_files[i], "none") == 0) {
Jakub Jelen def1de
+		    strcasecmp(name, "none") == 0) {
Jakub Jelen def1de
 			free(options.identity_files[i]);
Jakub Jelen def1de
 			options.identity_files[i] = NULL;
Jakub Jelen def1de
 			continue;
Jakub Jelen def1de
 		}
Jakub Jelen def1de
-		cp = tilde_expand_filename(options.identity_files[i], getuid());
Jakub Jelen def1de
+#ifdef ENABLE_PKCS11
Jakub Jelen def1de
+		if (strlen(name) >= strlen(PKCS11_URI_SCHEME) &&
Jakub Jelen def1de
+		    strncmp(name, PKCS11_URI_SCHEME,
Jakub Jelen def1de
+		    strlen(PKCS11_URI_SCHEME)) == 0) {
Jakub Jelen def1de
+			load_pkcs11_identity(name, identity_files,
Jakub Jelen def1de
+			    identity_keys, &n_ids);
Jakub Jelen def1de
+			free(options.identity_files[i]);
Jakub Jelen def1de
+			continue;
Jakub Jelen def1de
+		}
Jakub Jelen def1de
+#endif /* ENABLE_PKCS11 */
Jakub Jelen def1de
+		cp = tilde_expand_filename(name, getuid());
Jakub Jelen def1de
 		filename = percent_expand(cp, "d", pw->pw_dir,
Jakub Jelen def1de
 		    "u", pw->pw_name, "l", thishost, "h", host,
Jakub Jelen def1de
 		    "r", options.user, (char *)NULL);
Jakub Jelen def1de
diff --git a/ssh_config.5 b/ssh_config.5
Jakub Jelen def1de
index 41262963..a211034e 100644
Jakub Jelen def1de
--- a/ssh_config.5
Jakub Jelen def1de
+++ b/ssh_config.5
Jakub Jelen def1de
@@ -952,6 +952,21 @@ may also be used in conjunction with
Jakub Jelen def1de
 .Cm CertificateFile
Jakub Jelen def1de
 in order to provide any certificate also needed for authentication with
Jakub Jelen def1de
 the identity.
Jakub Jelen def1de
+.Pp
Jakub Jelen def1de
+The authentication identity can be also specified in a form of PKCS#11 URI
Jakub Jelen def1de
+starting with a string
Jakub Jelen def1de
+.Cm pkcs11: .
Jakub Jelen def1de
+There is supported a subset of the PKCS#11 URI as defined
Jakub Jelen def1de
+in RFC 7512 (implemented path arguments
Jakub Jelen def1de
+.Cm id ,
Jakub Jelen def1de
+.Cm manufacturer ,
Jakub Jelen def1de
+.Cm object ,
Jakub Jelen def1de
+.Cm token
Jakub Jelen def1de
+and query arguments
Jakub Jelen def1de
+.Cm module-path
Jakub Jelen def1de
+and
Jakub Jelen def1de
+.Cm pin-value
Jakub Jelen def1de
+). The URI can not be in quotes.
Jakub Jelen def1de
 .It Cm IgnoreUnknown
Jakub Jelen def1de
 Specifies a pattern-list of unknown options to be ignored if they are
Jakub Jelen def1de
 encountered in configuration parsing.