kentpeacock / rpms / openssh

Forked from rpms/openssh 2 years ago
Clone
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/configure.ac.pkcs11-uri openssh-8.6p1/configure.ac
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/configure.ac.pkcs11-uri	2021-05-06 11:35:55.101653187 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/configure.ac	2021-05-06 11:35:55.111653265 +0200
Dmitry Belyavskiy 9dff9c
@@ -1974,12 +1974,14 @@ AC_LINK_IFELSE(
Petr Šabata 81d24c
 	[AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).])
Petr Šabata 81d24c
 ])
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+SCARD_MSG="yes"
Petr Šabata 81d24c
 disable_pkcs11=
Petr Šabata 81d24c
 AC_ARG_ENABLE([pkcs11],
Petr Šabata 81d24c
 	[  --disable-pkcs11        disable PKCS#11 support code [no]],
Petr Šabata 81d24c
 	[
Petr Šabata 81d24c
 		if test "x$enableval" = "xno" ; then
Petr Šabata 81d24c
 			disable_pkcs11=1
Petr Šabata 81d24c
+			SCARD_MSG="no"
Petr Šabata 81d24c
 		fi
Petr Šabata 81d24c
 	]
Petr Šabata 81d24c
 )
Dmitry Belyavskiy 9dff9c
@@ -2008,6 +2010,40 @@ AC_SEARCH_LIBS([dlopen], [dl])
Petr Šabata 81d24c
 AC_CHECK_FUNCS([dlopen])
Petr Šabata 81d24c
 AC_CHECK_DECL([RTLD_NOW], [], [], [#include <dlfcn.h>])
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+# Check whether we have a p11-kit, we got default provider on command line
Petr Šabata 81d24c
+DEFAULT_PKCS11_PROVIDER_MSG="no"
Petr Šabata 81d24c
+AC_ARG_WITH([default-pkcs11-provider],
Petr Šabata 81d24c
+	[  --with-default-pkcs11-provider[[=PATH]]   Use default pkcs11 provider (p11-kit detected by default)],
Petr Šabata 81d24c
+	[ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then
Petr Šabata 81d24c
+		if test "x$withval" = "xyes" ; then
Petr Šabata 81d24c
+			AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no])
Petr Šabata 81d24c
+			if test "x$PKGCONFIG" != "xno"; then
Petr Šabata 81d24c
+				AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit])
Petr Šabata 81d24c
+				if "$PKGCONFIG" "p11-kit-1"; then
Petr Šabata 81d24c
+					AC_MSG_RESULT([yes])
Petr Šabata 81d24c
+					use_pkgconfig_for_p11kit=yes
Petr Šabata 81d24c
+				else
Petr Šabata 81d24c
+					AC_MSG_RESULT([no])
Petr Šabata 81d24c
+				fi
Petr Šabata 81d24c
+			fi
Petr Šabata 81d24c
+		else
Petr Šabata 81d24c
+			PKCS11_PATH="${withval}"
Petr Šabata 81d24c
+		fi
Petr Šabata 81d24c
+		if test "x$use_pkgconfig_for_p11kit" = "xyes"; then
Petr Šabata 81d24c
+			PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1`
Petr Šabata 81d24c
+		fi
Petr Šabata 81d24c
+		AC_CHECK_FILE("$PKCS11_PATH",
Petr Šabata 81d24c
+			[ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)])
Petr Šabata 81d24c
+			  DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH"
Petr Šabata 81d24c
+			],
Petr Šabata 81d24c
+			[ AC_MSG_ERROR([Requested PKCS11 provided not found]) ]
Petr Šabata 81d24c
+		)
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider])
Petr Šabata 81d24c
+	fi ]
Petr Šabata 81d24c
+)
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 # IRIX has a const char return value for gai_strerror()
Petr Šabata 81d24c
 AC_CHECK_FUNCS([gai_strerror], [
Petr Šabata 81d24c
 	AC_DEFINE([HAVE_GAI_STRERROR])
Dmitry Belyavskiy 9dff9c
@@ -5564,6 +5600,7 @@ echo "                  BSD Auth support
Petr Šabata 81d24c
 echo "              Random number source: $RAND_MSG"
Petr Šabata 81d24c
 echo "             Privsep sandbox style: $SANDBOX_STYLE"
Petr Šabata 81d24c
 echo "                   PKCS#11 support: $enable_pkcs11"
Petr Šabata 81d24c
+echo "          Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG"
Petr Šabata 81d24c
 echo "                  U2F/FIDO support: $enable_sk"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 echo ""
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/Makefile.in.pkcs11-uri openssh-8.6p1/Makefile.in
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/Makefile.in.pkcs11-uri	2021-05-06 11:35:55.054652818 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/Makefile.in	2021-05-06 11:58:16.895135904 +0200
Dmitry Belyavskiy 9dff9c
@@ -103,7 +103,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
Dmitry Belyavskiy 9dff9c
 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-ecdsa-sk.o \
Dmitry Belyavskiy 9dff9c
 	ssh-ed25519-sk.o ssh-rsa.o dh.o \
Dmitry Belyavskiy 9dff9c
 	msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
Dmitry Belyavskiy 9dff9c
-	ssh-pkcs11.o smult_curve25519_ref.o \
Dmitry Belyavskiy 9dff9c
+	ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \
Dmitry Belyavskiy 9dff9c
 	poly1305.o chacha.o cipher-chachapoly.o cipher-chachapoly-libcrypto.o \
Dmitry Belyavskiy 9dff9c
 	ssh-ed25519.o digest-openssl.o digest-libc.o \
Dmitry Belyavskiy 9dff9c
 	hmac.o sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \
Dmitry Belyavskiy 9dff9c
@@ -300,6 +300,8 @@ clean:	regressclean
Dmitry Belyavskiy 9dff9c
 	rm -f regress/unittests/sshsig/test_sshsig$(EXEEXT)
Dmitry Belyavskiy 9dff9c
 	rm -f regress/unittests/utf8/*.o
Dmitry Belyavskiy 9dff9c
 	rm -f regress/unittests/utf8/test_utf8$(EXEEXT)
Dmitry Belyavskiy 9dff9c
+	rm -f regress/unittests/pkcs11/*.o
Dmitry Belyavskiy 9dff9c
+	rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT)
Dmitry Belyavskiy 9dff9c
 	rm -f regress/misc/sk-dummy/*.o
Dmitry Belyavskiy 9dff9c
 	rm -f regress/misc/sk-dummy/*.lo
Dmitry Belyavskiy 9dff9c
 	rm -f regress/misc/sk-dummy/sk-dummy.so
Dmitry Belyavskiy 9dff9c
@@ -337,6 +339,8 @@ distclean:	regressclean
Dmitry Belyavskiy 9dff9c
 	rm -f regress/unittests/sshsig/test_sshsig
Dmitry Belyavskiy 9dff9c
 	rm -f regress/unittests/utf8/*.o
Dmitry Belyavskiy 9dff9c
 	rm -f regress/unittests/utf8/test_utf8
Dmitry Belyavskiy 9dff9c
+	rm -f regress/unittests/pkcs11/*.o
Dmitry Belyavskiy 9dff9c
+	rm -f regress/unittests/pkcs11/test_pkcs11
Dmitry Belyavskiy 9dff9c
 	(cd openbsd-compat && $(MAKE) distclean)
Dmitry Belyavskiy 9dff9c
 	if test -d pkg ; then \
Dmitry Belyavskiy 9dff9c
 		rm -fr pkg ; \
Dmitry Belyavskiy 9dff9c
@@ -511,6 +515,7 @@ regress-prep:
Dmitry Belyavskiy 9dff9c
 	$(MKDIR_P) `pwd`/regress/unittests/sshkey
Dmitry Belyavskiy 9dff9c
 	$(MKDIR_P) `pwd`/regress/unittests/sshsig
Dmitry Belyavskiy 9dff9c
 	$(MKDIR_P) `pwd`/regress/unittests/utf8
Dmitry Belyavskiy 9dff9c
+	$(MKDIR_P) `pwd`/regress/unittests/pkcs11
Dmitry Belyavskiy 9dff9c
 	$(MKDIR_P) `pwd`/regress/misc/sk-dummy
Dmitry Belyavskiy 9dff9c
 	[ -f `pwd`/regress/Makefile ] || \
Dmitry Belyavskiy 9dff9c
 	    ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile
Dmitry Belyavskiy 9dff9c
@@ -674,6 +679,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT
Dmitry Belyavskiy 9dff9c
 	    regress/unittests/test_helper/libtest_helper.a \
Dmitry Belyavskiy 9dff9c
 	    -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
+UNITTESTS_TEST_PKCS11_OBJS=\
Dmitry Belyavskiy 9dff9c
+	regress/unittests/pkcs11/tests.o
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \
Dmitry Belyavskiy 9dff9c
+    ${UNITTESTS_TEST_PKCS11_OBJS} \
Dmitry Belyavskiy 9dff9c
+    regress/unittests/test_helper/libtest_helper.a libssh.a
Dmitry Belyavskiy 9dff9c
+	$(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \
Dmitry Belyavskiy 9dff9c
+	    regress/unittests/test_helper/libtest_helper.a \
Dmitry Belyavskiy 9dff9c
+	    -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
 # These all need to be compiled -fPIC, so they are treated differently.
Dmitry Belyavskiy 9dff9c
 SK_DUMMY_OBJS=\
Dmitry Belyavskiy 9dff9c
 	regress/misc/sk-dummy/sk-dummy.lo \
Dmitry Belyavskiy 9dff9c
@@ -709,6 +724,7 @@ regress-unit-binaries: regress-prep $(RE
Dmitry Belyavskiy 9dff9c
 	regress/unittests/sshkey/test_sshkey$(EXEEXT) \
Dmitry Belyavskiy 9dff9c
 	regress/unittests/sshsig/test_sshsig$(EXEEXT) \
Dmitry Belyavskiy 9dff9c
 	regress/unittests/utf8/test_utf8$(EXEEXT) \
Dmitry Belyavskiy 9dff9c
+	regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
 tests:	file-tests t-exec interop-tests unit
Dmitry Belyavskiy 9dff9c
 	echo all tests passed
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/regress/agent-pkcs11.sh.pkcs11-uri openssh-8.6p1/regress/agent-pkcs11.sh
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/regress/agent-pkcs11.sh.pkcs11-uri	2021-04-16 05:55:25.000000000 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/regress/agent-pkcs11.sh	2021-05-06 11:35:55.112653273 +0200
Dmitry Belyavskiy 9dff9c
@@ -113,7 +113,7 @@ else
Dmitry Belyavskiy 9dff9c
 	done
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
 	trace "remove pkcs11 keys"
Dmitry Belyavskiy 9dff9c
-	echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1
Dmitry Belyavskiy 9dff9c
+	${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1
Dmitry Belyavskiy 9dff9c
 	r=$?
Dmitry Belyavskiy 9dff9c
 	if [ $r -ne 0 ]; then
Dmitry Belyavskiy 9dff9c
 		fail "ssh-add -e failed: exit code $r"
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/regress/Makefile.pkcs11-uri openssh-8.6p1/regress/Makefile
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/regress/Makefile.pkcs11-uri	2021-04-16 05:55:25.000000000 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/regress/Makefile	2021-05-06 11:59:24.465658383 +0200
Dmitry Belyavskiy 9dff9c
@@ -119,7 +119,8 @@ CLEANFILES=	*.core actual agent-key.* au
Petr Šabata 81d24c
 		known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \
Petr Šabata 81d24c
 		modpipe netcat no_identity_config \
Petr Šabata 81d24c
 		pidfile putty.rsa2 ready regress.log remote_pid \
Petr Šabata 81d24c
-		revoked-* rsa rsa-agent rsa-agent.pub rsa.pub rsa_ssh2_cr.prv \
Petr Šabata 81d24c
+		revoked-* rsa rsa-agent rsa-agent.pub rsa-agent-cert.pub \
Petr Šabata 81d24c
+		rsa.pub rsa_ssh2_cr.prv pkcs11*.crt pkcs11*.key pkcs11.info \
Petr Šabata 81d24c
 		rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \
Petr Šabata 81d24c
 		scp-ssh-wrapper.scp setuid-allowed sftp-server.log \
Petr Šabata 81d24c
 		sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \
Dmitry Belyavskiy 9dff9c
@@ -249,6 +250,7 @@ unit:
Petr Šabata 81d24c
 		V="" ; \
Petr Šabata 81d24c
 		test "x${USE_VALGRIND}" = "x" || \
Petr Šabata 81d24c
 		    V=${.CURDIR}/valgrind-unit.sh ; \
Dmitry Belyavskiy 9dff9c
+		 $$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \
Dmitry Belyavskiy 9dff9c
 		 $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \
Dmitry Belyavskiy 9dff9c
 		 $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \
Petr Šabata 81d24c
 			-d ${.CURDIR}/unittests/sshkey/testdata ; \
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/regress/pkcs11.sh.pkcs11-uri openssh-8.6p1/regress/pkcs11.sh
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/regress/pkcs11.sh.pkcs11-uri	2021-05-06 11:35:55.112653273 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/regress/pkcs11.sh	2021-05-06 11:35:55.112653273 +0200
Petr Šabata 81d24c
@@ -0,0 +1,349 @@
Petr Šabata 81d24c
+#
Petr Šabata 81d24c
+#  Copyright (c) 2017 Red Hat
Petr Šabata 81d24c
+#
Petr Šabata 81d24c
+#  Authors: Jakub Jelen <jjelen@redhat.com>
Petr Šabata 81d24c
+#
Petr Šabata 81d24c
+#  Permission to use, copy, modify, and distribute this software for any
Petr Šabata 81d24c
+#  purpose with or without fee is hereby granted, provided that the above
Petr Šabata 81d24c
+#  copyright notice and this permission notice appear in all copies.
Petr Šabata 81d24c
+#
Petr Šabata 81d24c
+#  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Petr Šabata 81d24c
+#  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Petr Šabata 81d24c
+#  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Petr Šabata 81d24c
+#  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Petr Šabata 81d24c
+#  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Petr Šabata 81d24c
+#  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Petr Šabata 81d24c
+#  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+tid="pkcs11 tests with soft token"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+try_token_libs() {
Petr Šabata 81d24c
+	for _lib in "$@" ; do
Petr Šabata 81d24c
+		if test -f "$_lib" ; then
Petr Šabata 81d24c
+			verbose "Using token library $_lib"
Petr Šabata 81d24c
+			TEST_SSH_PKCS11="$_lib"
Petr Šabata 81d24c
+			return
Petr Šabata 81d24c
+		fi
Petr Šabata 81d24c
+	done
Petr Šabata 81d24c
+	echo "skipped: Unable to find PKCS#11 token library"
Petr Šabata 81d24c
+	exit 0
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+try_token_libs \
Petr Šabata 81d24c
+	/usr/local/lib/softhsm/libsofthsm2.so \
Petr Šabata 81d24c
+	/usr/lib64/pkcs11/libsofthsm2.so \
Petr Šabata 81d24c
+	/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+TEST_SSH_PIN=1234
Petr Šabata 81d24c
+TEST_SSH_SOPIN=12345678
Petr Šabata 81d24c
+if [ "x$TEST_SSH_SSHPKCS11HELPER" != "x" ]; then
Petr Šabata 81d24c
+	SSH_PKCS11_HELPER="${TEST_SSH_SSHPKCS11HELPER}"
Petr Šabata 81d24c
+	export SSH_PKCS11_HELPER
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# setup environment for softhsm token
Petr Šabata 81d24c
+DIR=$OBJ/SOFTHSM
Petr Šabata 81d24c
+rm -rf $DIR
Petr Šabata 81d24c
+TOKEN=$DIR/tokendir
Petr Šabata 81d24c
+mkdir -p $TOKEN
Petr Šabata 81d24c
+SOFTHSM2_CONF=$DIR/softhsm2.conf
Petr Šabata 81d24c
+export SOFTHSM2_CONF
Petr Šabata 81d24c
+cat > $SOFTHSM2_CONF << EOF
Petr Šabata 81d24c
+# SoftHSM v2 configuration file
Petr Šabata 81d24c
+directories.tokendir = ${TOKEN}
Petr Šabata 81d24c
+objectstore.backend = file
Petr Šabata 81d24c
+# ERROR, WARNING, INFO, DEBUG
Petr Šabata 81d24c
+log.level = DEBUG
Petr Šabata 81d24c
+# If CKF_REMOVABLE_DEVICE flag should be set
Petr Šabata 81d24c
+slots.removable = false
Petr Šabata 81d24c
+EOF
Petr Šabata 81d24c
+out=$(softhsm2-util --init-token --free --label token-slot-0 --pin "$TEST_SSH_PIN" --so-pin "$TEST_SSH_SOPIN")
Petr Šabata 81d24c
+slot=$(echo -- $out | sed 's/.* //')
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# prevent ssh-agent from calling ssh-askpass
Petr Šabata 81d24c
+SSH_ASKPASS=/usr/bin/true
Petr Šabata 81d24c
+export SSH_ASKPASS
Petr Šabata 81d24c
+unset DISPLAY
Petr Šabata 81d24c
+# We need interactive access to test PKCS# since it prompts for PIN
Petr Šabata 81d24c
+sed -i 's/.*BatchMode.*//g' $OBJ/ssh_proxy
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh)
Petr Šabata 81d24c
+notty() {
Petr Šabata 81d24c
+	perl -e 'use POSIX; POSIX::setsid();
Petr Šabata 81d24c
+	    if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@"
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "generating keys"
Petr Šabata 81d24c
+ID1="02"
Petr Šabata 81d24c
+ID2="04"
Petr Šabata 81d24c
+RSA=${DIR}/RSA
Petr Šabata 81d24c
+EC=${DIR}/EC
Petr Šabata 81d24c
+openssl genpkey -algorithm rsa > $RSA
Petr Šabata 81d24c
+openssl pkcs8 -nocrypt -in $RSA |\
Petr Šabata 81d24c
+    softhsm2-util --slot "$slot" --label "SSH RSA Key $ID1" --id $ID1 \
Petr Šabata 81d24c
+	--pin "$TEST_SSH_PIN" --import /dev/stdin
Petr Šabata 81d24c
+openssl genpkey \
Petr Šabata 81d24c
+    -genparam \
Petr Šabata 81d24c
+    -algorithm ec \
Petr Šabata 81d24c
+    -pkeyopt ec_paramgen_curve:prime256v1 |\
Petr Šabata 81d24c
+    openssl genpkey \
Petr Šabata 81d24c
+    -paramfile /dev/stdin > $EC
Petr Šabata 81d24c
+openssl pkcs8 -nocrypt -in $EC |\
Petr Šabata 81d24c
+    softhsm2-util --slot "$slot" --label "SSH ECDSA Key $ID2" --id $ID2 \
Petr Šabata 81d24c
+	--pin "$TEST_SSH_PIN" --import /dev/stdin
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "List the keys in the ssh-keygen with PKCS#11 URIs"
Petr Šabata 81d24c
+${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys
Petr Šabata 81d24c
+if [ $? -ne 0 ]; then
Petr Šabata 81d24c
+	fail "FAIL: keygen fails to enumerate keys on PKCS#11 token"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+grep "pkcs11:" $OBJ/token_keys > /dev/null
Petr Šabata 81d24c
+if [ $? -ne 0 ]; then
Petr Šabata 81d24c
+	fail "FAIL: The keys from ssh-keygen do not contain PKCS#11 URI as a comment"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# Set the ECDSA key to authorized keys
Petr Šabata 81d24c
+grep "ECDSA" $OBJ/token_keys > $OBJ/authorized_keys_$USER
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Simple connect with ssh (without PKCS#11 URI)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \
Petr Šabata 81d24c
+    -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with pkcs11 failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Connect with PKCS#11 URI"
Petr Šabata 81d24c
+trace "  (ECDSA key should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -i "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (RSA key should fail)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+     -i "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Connect with PKCS#11 URI including PIN should not prompt"
Petr Šabata 81d24c
+trace "  (ECDSA key should succeed)"
Petr Šabata 81d24c
+${SSH} -F $OBJ/ssh_proxy -i \
Petr Šabata 81d24c
+    "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (RSA key should fail)"
Petr Šabata 81d24c
+${SSH} -F $OBJ/ssh_proxy -i \
Petr Šabata 81d24c
+    "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Connect with various filtering options in PKCS#11 URI"
Petr Šabata 81d24c
+trace "  (by object label, ECDSA should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -i "pkcs11:object=SSH%20ECDSA%20Key%2004?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (by object label, RSA key should fail)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+     -i "pkcs11:object=SSH%20RSA%20Key%2002?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (by token label, ECDSA key should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -i "pkcs11:id=%${ID2};token=token-slot-0?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (by wrong token label, should fail)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+     -i "pkcs11:token=token-slot-99?module-path=${TEST_SSH_PKCS11}" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Test PKCS#11 URI specification in configuration files"
Petr Šabata 81d24c
+echo "IdentityFile \"pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}\"" \
Petr Šabata 81d24c
+    >> $OBJ/ssh_proxy
Petr Šabata 81d24c
+trace "  (ECDSA key should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI in config failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+# Set the RSA key as authorized
Petr Šabata 81d24c
+grep "RSA" $OBJ/token_keys > $OBJ/authorized_keys_$USER
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "  (RSA key should fail)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI in config succeeded (should fail)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Test PKCS#11 URI specification in configuration files with bogus spaces"
Petr Šabata 81d24c
+echo "IdentityFile \"    pkcs11:?module-path=${TEST_SSH_PKCS11}    \"" \
Petr Šabata 81d24c
+    >> $OBJ/ssh_proxy
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI with bogus spaces in config failed" \
Petr Šabata 81d24c
+	    "(exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Combination of PKCS11Provider and PKCS11URI on commandline"
Petr Šabata 81d24c
+trace "  (RSA key should succeed)"
Petr Šabata 81d24c
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -i "pkcs11:id=%${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with PKCS#11 URI and provider combination" \
Petr Šabata 81d24c
+	    "failed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "Regress: Missing provider in PKCS11URI option"
Petr Šabata 81d24c
+${SSH} -F $OBJ/ssh_proxy \
Petr Šabata 81d24c
+    -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -eq 139 ]; then
Petr Šabata 81d24c
+	fail "FAIL: ssh connect with missing provider_id from configuration option" \
Petr Šabata 81d24c
+	    "crashed (exit code $r)"
Petr Šabata 81d24c
+fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+trace "SSH Agent can work with PKCS#11 URI"
Petr Šabata 81d24c
+trace "start the agent"
Petr Šabata 81d24c
+eval `${SSHAGENT} -s` >  /dev/null
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+r=$?
Petr Šabata 81d24c
+if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+	fail "could not start ssh-agent: exit code $r"
Petr Šabata 81d24c
+else
Petr Šabata 81d24c
+	trace "add whole provider to agent"
Petr Šabata 81d24c
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Petr Šabata 81d24c
+	    "pkcs11:?module-path=${TEST_SSH_PKCS11}" #> /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add failed with whole provider: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 list via agent (all keys)"
Petr Šabata 81d24c
+	${SSHADD} -l > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add -l failed with whole provider: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 connect via agent (all keys)"
Petr Šabata 81d24c
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh connect failed with whole provider (exit code $r)"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " remove pkcs11 keys (all keys)"
Petr Šabata 81d24c
+	${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add -d failed with whole provider: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace "add only RSA key to the agent"
Petr Šabata 81d24c
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Petr Šabata 81d24c
+	    "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL ssh-add failed with RSA key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 connect via agent (RSA key)"
Petr Šabata 81d24c
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh connect failed with RSA key (exit code $r)"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " remove RSA pkcs11 key"
Petr Šabata 81d24c
+	${SSHADD} -d "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" \
Petr Šabata 81d24c
+	    > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add -d failed with RSA key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace "add only ECDSA key to the agent"
Petr Šabata 81d24c
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Petr Šabata 81d24c
+	    "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add failed with second key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 connect via agent (ECDSA key should fail)"
Petr Šabata 81d24c
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -eq 5 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh connect passed with ECDSA key (should fail)"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace "add also the RSA key to the agent"
Petr Šabata 81d24c
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
Petr Šabata 81d24c
+	    "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add failed with first key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " remove ECDSA pkcs11 key"
Petr Šabata 81d24c
+	${SSHADD} -d "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" \
Petr Šabata 81d24c
+	    > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 0 ]; then
Petr Šabata 81d24c
+		fail "ssh-add -d failed with ECDSA key: exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " remove already-removed pkcs11 key should fail"
Petr Šabata 81d24c
+	${SSHADD} -d "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" \
Petr Šabata 81d24c
+	    > /dev/null 2>&1
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -eq 0 ]; then
Petr Šabata 81d24c
+		fail "FAIL: ssh-add -d passed with non-existing key (should fail)"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace " pkcs11 connect via agent (the RSA key should be still usable)"
Petr Šabata 81d24c
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
Petr Šabata 81d24c
+	r=$?
Petr Šabata 81d24c
+	if [ $r -ne 5 ]; then
Petr Šabata 81d24c
+		fail "ssh connect failed with RSA key (after removing ECDSA): exit code $r"
Petr Šabata 81d24c
+	fi
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	trace "kill agent"
Petr Šabata 81d24c
+	${SSHAGENT} -k > /dev/null
Petr Šabata 81d24c
+fi
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/regress/unittests/Makefile.pkcs11-uri openssh-8.6p1/regress/unittests/Makefile
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/regress/unittests/Makefile.pkcs11-uri	2021-04-16 05:55:25.000000000 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/regress/unittests/Makefile	2021-05-06 11:35:55.112653273 +0200
Petr Šabata 81d24c
@@ -2,6 +2,6 @@
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 REGRESS_FAIL_EARLY?=	yes
Petr Šabata 81d24c
 SUBDIR=	test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion
Petr Šabata 81d24c
-SUBDIR+=authopt misc sshsig
Petr Šabata 81d24c
+SUBDIR+=authopt misc sshsig pkcs11
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 .include <bsd.subdir.mk>
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/regress/unittests/pkcs11/tests.c.pkcs11-uri openssh-8.6p1/regress/unittests/pkcs11/tests.c
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/regress/unittests/pkcs11/tests.c.pkcs11-uri	2021-05-06 11:35:55.112653273 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/regress/unittests/pkcs11/tests.c	2021-05-06 11:35:55.112653273 +0200
Petr Šabata 81d24c
@@ -0,0 +1,337 @@
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * Copyright (c) 2017 Red Hat
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Authors: Jakub Jelen <jjelen@redhat.com>
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * Permission to use, copy, modify, and distribute this software for any
Petr Šabata 81d24c
+ * purpose with or without fee is hereby granted, provided that the above
Petr Šabata 81d24c
+ * copyright notice and this permission notice appear in all copies.
Petr Šabata 81d24c
+ *
Petr Šabata 81d24c
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Petr Šabata 81d24c
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Petr Šabata 81d24c
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Petr Šabata 81d24c
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Petr Šabata 81d24c
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Petr Šabata 81d24c
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Petr Šabata 81d24c
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "includes.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include <locale.h>
Petr Šabata 81d24c
+#include <string.h>
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "../test_helper/test_helper.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#include "sshbuf.h"
Petr Šabata 81d24c
+#include "ssh-pkcs11-uri.h"
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+#define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL)
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/* prototypes are not public -- specify them here internally for tests */
Petr Šabata 81d24c
+struct sshbuf *percent_encode(const char *, size_t, char *);
Petr Šabata 81d24c
+int percent_decode(char *, char **);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+compare_uri(struct pkcs11_uri *a, struct pkcs11_uri *b)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	ASSERT_PTR_NE(a, NULL);
Petr Šabata 81d24c
+	ASSERT_PTR_NE(b, NULL);
Petr Šabata 81d24c
+	ASSERT_SIZE_T_EQ(a->id_len, b->id_len);
Petr Šabata 81d24c
+	ASSERT_MEM_EQ(a->id, b->id, a->id_len);
Petr Šabata 81d24c
+	if (b->object != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->object, b->object);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->object, b->object);
Petr Šabata 81d24c
+	if (b->module_path != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->module_path, b->module_path);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->module_path, b->module_path);
Petr Šabata 81d24c
+	if (b->token != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->token, b->token);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->token, b->token);
Petr Šabata 81d24c
+	if (b->manuf != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->manuf, b->manuf);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->manuf, b->manuf);
Petr Šabata 81d24c
+	if (b->lib_manuf != NULL)
Petr Šabata 81d24c
+		ASSERT_STRING_EQ(a->lib_manuf, b->lib_manuf);
Petr Šabata 81d24c
+	else /* both should be null */
Petr Šabata 81d24c
+		ASSERT_PTR_EQ(a->lib_manuf, b->lib_manuf);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_parse_rv(char *uri, struct pkcs11_uri *expect, int expect_rv)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *buf = NULL, *str;
Petr Šabata 81d24c
+	struct pkcs11_uri *pkcs11uri = NULL;
Petr Šabata 81d24c
+	int rv;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (expect_rv == 0)
Petr Šabata 81d24c
+		str = "Valid";
Petr Šabata 81d24c
+	else
Petr Šabata 81d24c
+		str = "Invalid";
Petr Šabata 81d24c
+	asprintf(&buf, "%s PKCS#11 URI parsing: %s", str, uri);
Petr Šabata 81d24c
+	TEST_START(buf);
Petr Šabata 81d24c
+	free(buf);
Petr Šabata 81d24c
+	pkcs11uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	rv = pkcs11_uri_parse(uri, pkcs11uri);
Petr Šabata 81d24c
+	ASSERT_INT_EQ(rv, expect_rv);
Petr Šabata 81d24c
+	if (rv == 0) /* in case of failure result is undefined */
Petr Šabata 81d24c
+		compare_uri(pkcs11uri, expect);
Petr Šabata 81d24c
+	pkcs11_uri_cleanup(pkcs11uri);
Petr Šabata 81d24c
+	free(expect);
Petr Šabata 81d24c
+	TEST_DONE();
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_parse(char *uri, struct pkcs11_uri *expect)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	check_parse_rv(uri, expect, 0);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+struct pkcs11_uri *
Petr Šabata 81d24c
+compose_uri(unsigned char *id, size_t id_len, char *token, char *lib_manuf,
Petr Šabata 81d24c
+    char *manuf, char *module_path, char *object, char *pin)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct pkcs11_uri *uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	if (id_len > 0) {
Petr Šabata 81d24c
+		uri->id_len = id_len;
Petr Šabata 81d24c
+		uri->id = id;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	uri->module_path = module_path;
Petr Šabata 81d24c
+	uri->token = token;
Petr Šabata 81d24c
+	uri->lib_manuf = lib_manuf;
Petr Šabata 81d24c
+	uri->manuf = manuf;
Petr Šabata 81d24c
+	uri->object = object;
Petr Šabata 81d24c
+	uri->pin = pin;
Petr Šabata 81d24c
+	return uri;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_parse_valid(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* path arguments */
Petr Šabata 81d24c
+	check_parse("pkcs11:id=%01",
Petr Šabata 81d24c
+	    compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:id=%00%01",
Petr Šabata 81d24c
+	    compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:token=SSH%20Keys",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:library-manufacturer=OpenSC",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:manufacturer=piv_II",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:object=SIGN%20Key",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key", NULL));
Petr Šabata 81d24c
+	/* query arguments */
Petr Šabata 81d24c
+	check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:?pin-value=123456",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, "123456"));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* combinations */
Petr Šabata 81d24c
+	/* ID SHOULD be percent encoded */
Petr Šabata 81d24c
+	check_parse("pkcs11:token=SSH%20Key;id=0",
Petr Šabata 81d24c
+	    compose_uri("0", 1, "SSH Key", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_parse(
Petr Šabata 81d24c
+	    "pkcs11:manufacturer=CAC?module-path=/usr/lib64/p11-kit-proxy.so",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, "CAC",
Petr Šabata 81d24c
+	    "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Petr Šabata 81d24c
+	check_parse(
Petr Šabata 81d24c
+	    "pkcs11:object=RSA%20Key?module-path=/usr/lib64/pkcs11/opencryptoki.so",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL,
Petr Šabata 81d24c
+	    "/usr/lib64/pkcs11/opencryptoki.so", "RSA Key", NULL));
Petr Šabata 81d24c
+	check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so&pin-value=123456",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, "123456"));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* empty path component matches everything */
Petr Šabata 81d24c
+	check_parse("pkcs11:", EMPTY_URI);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* empty string is a valid to match against (and different from NULL) */
Petr Šabata 81d24c
+	check_parse("pkcs11:token=",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, "", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	/* Percent character needs to be percent-encoded */
Petr Šabata 81d24c
+	check_parse("pkcs11:token=%25",
Petr Šabata 81d24c
+	     compose_uri(NULL, 0, "%", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_parse_invalid(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* Invalid percent encoding */
Petr Šabata 81d24c
+	check_parse_rv("pkcs11:id=%0", EMPTY_URI, -1);
Petr Šabata 81d24c
+	/* Invalid percent encoding */
Petr Šabata 81d24c
+	check_parse_rv("pkcs11:id=%ZZ", EMPTY_URI, -1);
Petr Šabata 81d24c
+	/* Space MUST be percent encoded -- XXX not enforced yet */
Petr Šabata 81d24c
+	check_parse("pkcs11:token=SSH Keys",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	/* MUST NOT contain duplicate attributes of the same name */
Petr Šabata 81d24c
+	check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1);
Petr Šabata 81d24c
+	/* MUST NOT contain duplicate attributes of the same name */
Petr Šabata 81d24c
+	check_parse_rv("pkcs11:?pin-value=111111&pin-value=123456", EMPTY_URI, -1);
Petr Šabata 81d24c
+	/* Unrecognized attribute in path are ignored with log message */
Petr Šabata 81d24c
+	check_parse("pkcs11:key_name=SSH", EMPTY_URI);
Petr Šabata 81d24c
+	/* Unrecognized attribute in query SHOULD be ignored */
Petr Šabata 81d24c
+	check_parse("pkcs11:?key_name=SSH", EMPTY_URI);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_gen(char *expect, struct pkcs11_uri *uri)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *buf = NULL, *uri_str;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	asprintf(&buf, "Valid PKCS#11 URI generation: %s", expect);
Petr Šabata 81d24c
+	TEST_START(buf);
Petr Šabata 81d24c
+	free(buf);
Petr Šabata 81d24c
+	uri_str = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+	ASSERT_PTR_NE(uri_str, NULL);
Petr Šabata 81d24c
+	ASSERT_STRING_EQ(uri_str, expect);
Petr Šabata 81d24c
+	free(uri_str);
Petr Šabata 81d24c
+	TEST_DONE();
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_generate_valid(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* path arguments */
Petr Šabata 81d24c
+	check_gen("pkcs11:id=%01",
Petr Šabata 81d24c
+	    compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:id=%00%01",
Petr Šabata 81d24c
+	    compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:token=SSH%20Keys", /* space must be percent encoded */
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	/* library-manufacturer is not implmented now */
Petr Šabata 81d24c
+	/*check_gen("pkcs11:library-manufacturer=OpenSC",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));*/
Petr Šabata 81d24c
+	check_gen("pkcs11:manufacturer=piv_II",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:object=RSA%20Key",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key", NULL));
Petr Šabata 81d24c
+	/* query arguments */
Petr Šabata 81d24c
+	check_gen("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* combinations */
Petr Šabata 81d24c
+	check_gen("pkcs11:id=%02;token=SSH%20Keys",
Petr Šabata 81d24c
+	    compose_uri("\x02", 1, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:id=%EE%02?module-path=/usr/lib64/p11-kit-proxy.so",
Petr Šabata 81d24c
+	    compose_uri("\xEE\x02", 2, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL));
Petr Šabata 81d24c
+	check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II",
Petr Šabata 81d24c
+	    compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key", NULL));
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* empty path component matches everything */
Petr Šabata 81d24c
+	check_gen("pkcs11:", EMPTY_URI);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_encode(char *source, size_t len, char *allow_list, char *expect)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *buf = NULL;
Petr Šabata 81d24c
+	struct sshbuf *b;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	asprintf(&buf, "percent_encode: expected %s", expect);
Petr Šabata 81d24c
+	TEST_START(buf);
Petr Šabata 81d24c
+	free(buf);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	b = percent_encode(source, len, allow_list);
Petr Šabata 81d24c
+	ASSERT_STRING_EQ(sshbuf_ptr(b), expect);
Petr Šabata 81d24c
+	sshbuf_free(b);
Petr Šabata 81d24c
+	TEST_DONE();
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_percent_encode_multibyte(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* SHOULD be encoded as octets according to the UTF-8 character encoding */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* multi-byte characters are "for free" */
Petr Šabata 81d24c
+	check_encode("$", 1, "", "%24");
Petr Šabata 81d24c
+	check_encode("¢", 2, "", "%C2%A2");
Petr Šabata 81d24c
+	check_encode("€", 3, "", "%E2%82%AC");
Petr Šabata 81d24c
+	check_encode("𐍈", 4, "", "%F0%90%8D%88");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* CK_UTF8CHAR is unsigned char (1 byte) */
Petr Šabata 81d24c
+	/* labels SHOULD be normalized to NFC [UAX15] */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_percent_encode(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* Without allow list encodes everything (for CKA_ID) */
Petr Šabata 81d24c
+	check_encode("A*", 2, "", "%41%2A");
Petr Šabata 81d24c
+	check_encode("\x00", 1, "", "%00");
Petr Šabata 81d24c
+	check_encode("\x7F", 1, "", "%7F");
Petr Šabata 81d24c
+	check_encode("\x80", 1, "", "%80");
Petr Šabata 81d24c
+	check_encode("\xff", 1, "", "%FF");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Default allow list encodes anything but safe letters */
Petr Šabata 81d24c
+	check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "test%000alpha");
Petr Šabata 81d24c
+	check_encode(" ", 1, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "%20"); /* Space MUST be percent encoded */
Petr Šabata 81d24c
+	check_encode("/", 1, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */
Petr Šabata 81d24c
+	check_encode("?", 1, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */
Petr Šabata 81d24c
+	check_encode("#", 1, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "%23"); /* '#' MUST be always percent encoded */
Petr Šabata 81d24c
+	check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST,
Petr Šabata 81d24c
+	    "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* Components in query can have '/' unencoded (useful for paths) */
Petr Šabata 81d24c
+	check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/",
Petr Šabata 81d24c
+	    "/path/to.file");
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+check_decode(char *source, char *expect, int expect_len)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *buf = NULL, *out = NULL;
Petr Šabata 81d24c
+	int rv;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	asprintf(&buf, "percent_decode: %s", source);
Petr Šabata 81d24c
+	TEST_START(buf);
Petr Šabata 81d24c
+	free(buf);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	rv = percent_decode(source, &out;;
Petr Šabata 81d24c
+	ASSERT_INT_EQ(rv, expect_len);
Petr Šabata 81d24c
+	if (rv >= 0)
Petr Šabata 81d24c
+		ASSERT_MEM_EQ(out, expect, expect_len);
Petr Šabata 81d24c
+	free(out);
Petr Šabata 81d24c
+	TEST_DONE();
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+test_percent_decode(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	/* simple valid cases */
Petr Šabata 81d24c
+	check_decode("%00", "\x00", 1);
Petr Šabata 81d24c
+	check_decode("%FF", "\xFF", 1);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* normal strings shold be kept intact */
Petr Šabata 81d24c
+	check_decode("strings are left", "strings are left", 16);
Petr Šabata 81d24c
+	check_decode("10%25 of trees", "10% of trees", 12);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* make sure no more than 2 bytes are parsed */
Petr Šabata 81d24c
+	check_decode("%222", "\x22" "2", 2);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* invalid expects failure */
Petr Šabata 81d24c
+	check_decode("%0", "", -1);
Petr Šabata 81d24c
+	check_decode("%Z", "", -1);
Petr Šabata 81d24c
+	check_decode("%FG", "", -1);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+void
Petr Šabata 81d24c
+tests(void)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	test_percent_encode();
Petr Šabata 81d24c
+	test_percent_encode_multibyte();
Petr Šabata 81d24c
+	test_percent_decode();
Petr Šabata 81d24c
+	test_parse_valid();
Petr Šabata 81d24c
+	test_parse_invalid();
Petr Šabata 81d24c
+	test_generate_valid();
Petr Šabata 81d24c
+}
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh-add.c.pkcs11-uri openssh-8.6p1/ssh-add.c
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh-add.c.pkcs11-uri	2021-04-16 05:55:25.000000000 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh-add.c	2021-05-06 11:35:55.112653273 +0200
Dmitry Belyavskiy 9dff9c
@@ -68,6 +68,7 @@
Petr Šabata 81d24c
 #include "digest.h"
Petr Šabata 81d24c
 #include "ssh-sk.h"
Petr Šabata 81d24c
 #include "sk-api.h"
Petr Šabata 81d24c
+#include "ssh-pkcs11-uri.h"
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /* argv0 */
Petr Šabata 81d24c
 extern char *__progname;
Dmitry Belyavskiy 9dff9c
@@ -229,6 +230,32 @@ delete_all(int agent_fd, int qflag)
Petr Šabata 81d24c
 	return ret;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+static int update_card(int, int, const char *, int, char *);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri, int qflag)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *pin = NULL;
Petr Šabata 81d24c
+	struct pkcs11_uri *uri;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* dry-run parse to make sure the URI is valid and to report errors */
Petr Šabata 81d24c
+	uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0)
Petr Šabata 81d24c
+		fatal("Failed to parse PKCS#11 URI");
Petr Šabata 81d24c
+	if (uri->pin != NULL) {
Petr Šabata 81d24c
+		pin = strdup(uri->pin);
Petr Šabata 81d24c
+		if (pin == NULL) {
Petr Šabata 81d24c
+			fatal("Failed to dupplicate string");
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		/* pin is freed in the update_card() */
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	return update_card(agent_fd, adding, pkcs11_uri, qflag, pin);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 add_file(int agent_fd, const char *filename, int key_only, int qflag,
Petr Šabata 81d24c
     const char *skprovider)
Dmitry Belyavskiy 9dff9c
@@ -445,12 +472,11 @@ add_file(int agent_fd, const char *filen
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
-update_card(int agent_fd, int add, const char *id, int qflag)
Petr Šabata 81d24c
+update_card(int agent_fd, int add, const char *id, int qflag, char *pin)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	char *pin = NULL;
Petr Šabata 81d24c
 	int r, ret = -1;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (add) {
Petr Šabata 81d24c
+	if (add && pin == NULL) {
Petr Šabata 81d24c
 		if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
Petr Šabata 81d24c
 		    RP_ALLOW_STDIN)) == NULL)
Petr Šabata 81d24c
 			return -1;
Dmitry Belyavskiy 9dff9c
@@ -630,6 +656,13 @@ static int
Petr Šabata 81d24c
 do_file(int agent_fd, int deleting, int key_only, char *file, int qflag,
Petr Šabata 81d24c
     const char *skprovider)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
+#ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+	if (strlen(file) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+	    strncmp(file, PKCS11_URI_SCHEME,
Petr Šabata 81d24c
+	    strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+		return update_pkcs11_uri(agent_fd, !deleting, file, qflag);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
 	if (deleting) {
Petr Šabata 81d24c
 		if (delete_file(agent_fd, file, key_only, qflag) == -1)
Petr Šabata 81d24c
 			return -1;
Dmitry Belyavskiy 9dff9c
@@ -813,7 +846,7 @@ main(int argc, char **argv)
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	if (pkcs11provider != NULL) {
Petr Šabata 81d24c
 		if (update_card(agent_fd, !deleting, pkcs11provider,
Petr Šabata 81d24c
-		    qflag) == -1)
Petr Šabata 81d24c
+		    qflag, NULL) == -1)
Petr Šabata 81d24c
 			ret = 1;
Petr Šabata 81d24c
 		goto done;
Petr Šabata 81d24c
 	}
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh-agent.c.pkcs11-uri openssh-8.6p1/ssh-agent.c
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh-agent.c.pkcs11-uri	2021-04-16 05:55:25.000000000 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh-agent.c	2021-05-06 11:35:55.113653281 +0200
Dmitry Belyavskiy 9dff9c
@@ -847,10 +847,72 @@ no_identities(SocketEntry *e)
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 #ifdef ENABLE_PKCS11
Petr Šabata 81d24c
+static char *
Petr Šabata 81d24c
+sanitize_pkcs11_provider(const char *provider)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct pkcs11_uri *uri = NULL;
Petr Šabata 81d24c
+	char *sane_uri, *module_path = NULL; /* default path */
Petr Šabata 81d24c
+	char canonical_provider[PATH_MAX];
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (provider == NULL)
Petr Šabata 81d24c
+		return NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (strlen(provider) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+	    strncmp(provider, PKCS11_URI_SCHEME,
Petr Šabata 81d24c
+	    strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+		/* PKCS#11 URI */
Petr Šabata 81d24c
+		uri = pkcs11_uri_init();
Petr Šabata 81d24c
+		if (uri == NULL) {
Petr Šabata 81d24c
+			error("Failed to init PKCS#11 URI");
Petr Šabata 81d24c
+			return NULL;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		if (pkcs11_uri_parse(provider, uri) != 0) {
Petr Šabata 81d24c
+			error("Failed to parse PKCS#11 URI");
Petr Šabata 81d24c
+			return NULL;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		/* validate also provider from URI */
Petr Šabata 81d24c
+		if (uri->module_path)
Petr Šabata 81d24c
+			module_path = strdup(uri->module_path);
Petr Šabata 81d24c
+	} else
Petr Šabata 81d24c
+		module_path = strdup(provider); /* simple path */
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (module_path != NULL) { /* do not validate default NULL path in URI */
Petr Šabata 81d24c
+		if (realpath(module_path, canonical_provider) == NULL) {
Petr Šabata 81d24c
+			verbose("failed PKCS#11 provider \"%.100s\": realpath: %s",
Petr Šabata 81d24c
+			    module_path, strerror(errno));
Petr Šabata 81d24c
+			free(module_path);
Petr Šabata 81d24c
+			pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+			return NULL;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		free(module_path);
Petr Šabata 81d24c
+		if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) {
Petr Šabata 81d24c
+			verbose("refusing PKCS#11 provider \"%.100s\": "
Petr Šabata 81d24c
+			    "not allowed", canonical_provider);
Petr Šabata 81d24c
+			pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+			return NULL;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+		/* copy verified and sanitized provider path back to the uri */
Petr Šabata 81d24c
+		if (uri) {
Petr Šabata 81d24c
+			free(uri->module_path);
Petr Šabata 81d24c
+			uri->module_path = xstrdup(canonical_provider);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (uri) {
Petr Šabata 81d24c
+		sane_uri = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+		pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+		return sane_uri;
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		return xstrdup(canonical_provider); /* simple path */
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
 process_add_smartcard_key(SocketEntry *e)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
Petr Šabata 81d24c
+	char *provider = NULL, *pin = NULL, *sane_uri = NULL;
Petr Šabata 81d24c
 	char **comments = NULL;
Petr Šabata 81d24c
 	int r, i, count = 0, success = 0, confirm = 0;
DistroBaker d029bb
 	u_int seconds = 0;
Dmitry Belyavskiy 9dff9c
@@ -869,33 +931,28 @@ process_add_smartcard_key(SocketEntry *e
DistroBaker d029bb
 		error_f("failed to parse constraints");
DistroBaker d029bb
 		goto send;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if (realpath(provider, canonical_provider) == NULL) {
Petr Šabata 81d24c
-		verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
Petr Šabata 81d24c
-		    provider, strerror(errno));
Petr Šabata 81d24c
-		goto send;
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
-	if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) {
Petr Šabata 81d24c
-		verbose("refusing PKCS#11 add of \"%.100s\": "
Petr Šabata 81d24c
-		    "provider not allowed", canonical_provider);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	sane_uri = sanitize_pkcs11_provider(provider);
Petr Šabata 81d24c
+	if (sane_uri == NULL)
Petr Šabata 81d24c
 		goto send;
Petr Šabata 81d24c
-	}
DistroBaker d029bb
-	debug_f("add %.100s", canonical_provider);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	if (lifetime && !death)
Petr Šabata 81d24c
 		death = monotime() + lifetime;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments);
DistroBaker d029bb
+	debug_f("add %.100s", sane_uri);
Petr Šabata 81d24c
+	count = pkcs11_add_provider(sane_uri, pin, &keys, &comments);
Petr Šabata 81d24c
 	for (i = 0; i < count; i++) {
Petr Šabata 81d24c
 		k = keys[i];
Petr Šabata 81d24c
 		if (lookup_identity(k) == NULL) {
Petr Šabata 81d24c
 			id = xcalloc(1, sizeof(Identity));
Petr Šabata 81d24c
 			id->key = k;
Petr Šabata 81d24c
 			keys[i] = NULL; /* transferred */
Petr Šabata 81d24c
-			id->provider = xstrdup(canonical_provider);
Petr Šabata 81d24c
+			id->provider = xstrdup(sane_uri);
Petr Šabata 81d24c
 			if (*comments[i] != '\0') {
Petr Šabata 81d24c
 				id->comment = comments[i];
Petr Šabata 81d24c
 				comments[i] = NULL; /* transferred */
Petr Šabata 81d24c
 			} else {
Petr Šabata 81d24c
-				id->comment = xstrdup(canonical_provider);
Petr Šabata 81d24c
+				id->comment = xstrdup(sane_uri);
Petr Šabata 81d24c
 			}
Petr Šabata 81d24c
 			id->death = death;
Petr Šabata 81d24c
 			id->confirm = confirm;
Dmitry Belyavskiy 9dff9c
@@ -910,6 +967,7 @@ process_add_smartcard_key(SocketEntry *e
Petr Šabata 81d24c
 send:
Petr Šabata 81d24c
 	free(pin);
Petr Šabata 81d24c
 	free(provider);
Petr Šabata 81d24c
+	free(sane_uri);
Petr Šabata 81d24c
 	free(keys);
Petr Šabata 81d24c
 	free(comments);
Petr Šabata 81d24c
 	send_status(e, success);
Dmitry Belyavskiy 9dff9c
@@ -918,7 +976,7 @@ send:
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
 process_remove_smartcard_key(SocketEntry *e)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
Petr Šabata 81d24c
+	char *provider = NULL, *pin = NULL, *sane_uri = NULL;
Petr Šabata 81d24c
 	int r, success = 0;
Petr Šabata 81d24c
 	Identity *id, *nxt;
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
@@ -930,30 +988,29 @@ process_remove_smartcard_key(SocketEntry
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	free(pin);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (realpath(provider, canonical_provider) == NULL) {
Petr Šabata 81d24c
-		verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
Petr Šabata 81d24c
-		    provider, strerror(errno));
Petr Šabata 81d24c
+	sane_uri = sanitize_pkcs11_provider(provider);
Petr Šabata 81d24c
+	if (sane_uri == NULL)
Petr Šabata 81d24c
 		goto send;
Petr Šabata 81d24c
-	}
Petr Šabata 81d24c
 
DistroBaker d029bb
-	debug_f("remove %.100s", canonical_provider);
DistroBaker d029bb
+	debug_f("remove %.100s", sane_uri);
Petr Šabata 81d24c
 	for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
Petr Šabata 81d24c
 		nxt = TAILQ_NEXT(id, next);
Petr Šabata 81d24c
 		/* Skip file--based keys */
Petr Šabata 81d24c
 		if (id->provider == NULL)
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
-		if (!strcmp(canonical_provider, id->provider)) {
Petr Šabata 81d24c
+		if (!strcmp(sane_uri, id->provider)) {
Petr Šabata 81d24c
 			TAILQ_REMOVE(&idtab->idlist, id, next);
Petr Šabata 81d24c
 			free_identity(id);
Petr Šabata 81d24c
 			idtab->nentries--;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if (pkcs11_del_provider(canonical_provider) == 0)
Petr Šabata 81d24c
+	if (pkcs11_del_provider(sane_uri) == 0)
Petr Šabata 81d24c
 		success = 1;
Petr Šabata 81d24c
 	else
DistroBaker d029bb
 		error_f("pkcs11_del_provider failed");
Petr Šabata 81d24c
 send:
Petr Šabata 81d24c
 	free(provider);
Petr Šabata 81d24c
+	free(sane_uri);
Petr Šabata 81d24c
 	send_status(e, success);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 #endif /* ENABLE_PKCS11 */
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh_config.5.pkcs11-uri openssh-8.6p1/ssh_config.5
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh_config.5.pkcs11-uri	2021-05-06 11:35:55.061652873 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh_config.5	2021-05-06 11:35:55.116653304 +0200
Dmitry Belyavskiy 9dff9c
@@ -1063,6 +1063,21 @@ may also be used in conjunction with
Dmitry Belyavskiy 9dff9c
 .Cm CertificateFile
Dmitry Belyavskiy 9dff9c
 in order to provide any certificate also needed for authentication with
Dmitry Belyavskiy 9dff9c
 the identity.
Dmitry Belyavskiy 9dff9c
+.Pp
Dmitry Belyavskiy 9dff9c
+The authentication identity can be also specified in a form of PKCS#11 URI
Dmitry Belyavskiy 9dff9c
+starting with a string
Dmitry Belyavskiy 9dff9c
+.Cm pkcs11: .
Dmitry Belyavskiy 9dff9c
+There is supported a subset of the PKCS#11 URI as defined
Dmitry Belyavskiy 9dff9c
+in RFC 7512 (implemented path arguments
Dmitry Belyavskiy 9dff9c
+.Cm id ,
Dmitry Belyavskiy 9dff9c
+.Cm manufacturer ,
Dmitry Belyavskiy 9dff9c
+.Cm object ,
Dmitry Belyavskiy 9dff9c
+.Cm token
Dmitry Belyavskiy 9dff9c
+and query arguments
Dmitry Belyavskiy 9dff9c
+.Cm module-path
Dmitry Belyavskiy 9dff9c
+and
Dmitry Belyavskiy 9dff9c
+.Cm pin-value
Dmitry Belyavskiy 9dff9c
+). The URI can not be in quotes.
Dmitry Belyavskiy 9dff9c
 .It Cm IgnoreUnknown
Dmitry Belyavskiy 9dff9c
 Specifies a pattern-list of unknown options to be ignored if they are
Dmitry Belyavskiy 9dff9c
 encountered in configuration parsing.
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh.c.pkcs11-uri openssh-8.6p1/ssh.c
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh.c.pkcs11-uri	2021-05-06 11:35:55.060652865 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh.c	2021-05-06 12:00:07.129988275 +0200
Dmitry Belyavskiy 9dff9c
@@ -843,6 +843,14 @@ main(int ac, char **av)
Dmitry Belyavskiy 9dff9c
 			options.gss_deleg_creds = 1;
Dmitry Belyavskiy 9dff9c
 			break;
Dmitry Belyavskiy 9dff9c
 		case 'i':
Dmitry Belyavskiy 9dff9c
+#ifdef ENABLE_PKCS11
Dmitry Belyavskiy 9dff9c
+			if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) &&
Dmitry Belyavskiy 9dff9c
+			    strncmp(optarg, PKCS11_URI_SCHEME,
Dmitry Belyavskiy 9dff9c
+			    strlen(PKCS11_URI_SCHEME)) == 0) {
Dmitry Belyavskiy 9dff9c
+				add_identity_file(&options, NULL, optarg, 1);
Dmitry Belyavskiy 9dff9c
+				break;
Petr Šabata 81d24c
+			}
Dmitry Belyavskiy 9dff9c
+#endif
Dmitry Belyavskiy 9dff9c
 			p = tilde_expand_filename(optarg, getuid());
Dmitry Belyavskiy 9dff9c
 			if (stat(p, &st) == -1)
Dmitry Belyavskiy 9dff9c
 				fprintf(stderr, "Warning: Identity file %s "
Dmitry Belyavskiy 9dff9c
@@ -1695,6 +1703,7 @@ main(int ac, char **av)
Dmitry Belyavskiy 9dff9c
 #ifdef ENABLE_PKCS11
Dmitry Belyavskiy 9dff9c
 	(void)pkcs11_del_provider(options.pkcs11_provider);
Dmitry Belyavskiy 9dff9c
 #endif
Dmitry Belyavskiy 9dff9c
+	pkcs11_terminate();
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
  skip_connect:
Dmitry Belyavskiy 9dff9c
 	exit_status = ssh_session2(ssh, cinfo);
Dmitry Belyavskiy 9dff9c
@@ -2211,6 +2220,45 @@ ssh_session2(struct ssh *ssh, const stru
Dmitry Belyavskiy 9dff9c
 	    options.escape_char : SSH_ESCAPECHAR_NONE, id);
Dmitry Belyavskiy 9dff9c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+#ifdef ENABLE_PKCS11
Dmitry Belyavskiy 9dff9c
+static void
Dmitry Belyavskiy 9dff9c
+load_pkcs11_identity(char *pkcs11_uri, char *identity_files[],
Dmitry Belyavskiy 9dff9c
+    struct sshkey *identity_keys[], int *n_ids)
Dmitry Belyavskiy 9dff9c
+{
Dmitry Belyavskiy 9dff9c
+	int nkeys, i;
Dmitry Belyavskiy 9dff9c
+	struct sshkey **keys;
Dmitry Belyavskiy 9dff9c
+	struct pkcs11_uri *uri;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	debug("identity file '%s' from pkcs#11", pkcs11_uri);
Dmitry Belyavskiy 9dff9c
+	uri = pkcs11_uri_init();
Dmitry Belyavskiy 9dff9c
+	if (uri == NULL)
Dmitry Belyavskiy 9dff9c
+		fatal("Failed to init PKCS#11 URI");
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	if (pkcs11_uri_parse(pkcs11_uri, uri) != 0)
Dmitry Belyavskiy 9dff9c
+	fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri);
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	/* we need to merge URI and provider together */
Dmitry Belyavskiy 9dff9c
+	if (options.pkcs11_provider != NULL && uri->module_path == NULL)
Dmitry Belyavskiy 9dff9c
+		uri->module_path = strdup(options.pkcs11_provider);
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	if (options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
Dmitry Belyavskiy 9dff9c
+	    (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys, NULL)) > 0) {
Dmitry Belyavskiy 9dff9c
+		for (i = 0; i < nkeys; i++) {
Dmitry Belyavskiy 9dff9c
+			if (*n_ids >= SSH_MAX_IDENTITY_FILES) {
Dmitry Belyavskiy 9dff9c
+				sshkey_free(keys[i]);
Dmitry Belyavskiy 9dff9c
+				continue;
Dmitry Belyavskiy 9dff9c
+			}
Dmitry Belyavskiy 9dff9c
+			identity_keys[*n_ids] = keys[i];
Dmitry Belyavskiy 9dff9c
+			identity_files[*n_ids] = pkcs11_uri_get(uri);
Dmitry Belyavskiy 9dff9c
+			(*n_ids)++;
Dmitry Belyavskiy 9dff9c
+		}
Dmitry Belyavskiy 9dff9c
+		free(keys);
Dmitry Belyavskiy 9dff9c
+	}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	pkcs11_uri_cleanup(uri);
Dmitry Belyavskiy 9dff9c
+}
Dmitry Belyavskiy 9dff9c
+#endif /* ENABLE_PKCS11 */
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
 /* Loads all IdentityFile and CertificateFile keys */
Dmitry Belyavskiy 9dff9c
 static void
Dmitry Belyavskiy 9dff9c
 load_public_identity_files(const struct ssh_conn_info *cinfo)
Dmitry Belyavskiy 9dff9c
@@ -2225,11 +2273,6 @@ load_public_identity_files(const struct
Dmitry Belyavskiy 9dff9c
 	char *certificate_files[SSH_MAX_CERTIFICATE_FILES];
Dmitry Belyavskiy 9dff9c
 	struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
Dmitry Belyavskiy 9dff9c
 	int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES];
Dmitry Belyavskiy 9dff9c
-#ifdef ENABLE_PKCS11
Dmitry Belyavskiy 9dff9c
-	struct sshkey **keys = NULL;
Dmitry Belyavskiy 9dff9c
-	char **comments = NULL;
Dmitry Belyavskiy 9dff9c
-	int nkeys;
Dmitry Belyavskiy 9dff9c
-#endif /* PKCS11 */
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
 	n_ids = n_certs = 0;
Dmitry Belyavskiy 9dff9c
 	memset(identity_files, 0, sizeof(identity_files));
Dmitry Belyavskiy 9dff9c
@@ -2242,33 +2285,46 @@ load_public_identity_files(const struct
Dmitry Belyavskiy 9dff9c
 	    sizeof(certificate_file_userprovided));
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
 #ifdef ENABLE_PKCS11
Dmitry Belyavskiy 9dff9c
-	if (options.pkcs11_provider != NULL &&
Dmitry Belyavskiy 9dff9c
-	    options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
Dmitry Belyavskiy 9dff9c
-	    (pkcs11_init(!options.batch_mode) == 0) &&
Dmitry Belyavskiy 9dff9c
-	    (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL,
Dmitry Belyavskiy 9dff9c
-	    &keys, &comments)) > 0) {
Dmitry Belyavskiy 9dff9c
-		for (i = 0; i < nkeys; i++) {
Dmitry Belyavskiy 9dff9c
-			if (n_ids >= SSH_MAX_IDENTITY_FILES) {
Dmitry Belyavskiy 9dff9c
-				sshkey_free(keys[i]);
Dmitry Belyavskiy 9dff9c
-				free(comments[i]);
Dmitry Belyavskiy 9dff9c
-				continue;
Dmitry Belyavskiy 9dff9c
-			}
Dmitry Belyavskiy 9dff9c
-			identity_keys[n_ids] = keys[i];
Dmitry Belyavskiy 9dff9c
-			identity_files[n_ids] = comments[i]; /* transferred */
Dmitry Belyavskiy 9dff9c
-			n_ids++;
Dmitry Belyavskiy 9dff9c
-		}
Dmitry Belyavskiy 9dff9c
-		free(keys);
Dmitry Belyavskiy 9dff9c
-		free(comments);
Dmitry Belyavskiy 9dff9c
+	/* handle fallback from PKCS11Provider option */
Dmitry Belyavskiy 9dff9c
+	pkcs11_init(!options.batch_mode);
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	if (options.pkcs11_provider != NULL) {
Dmitry Belyavskiy 9dff9c
+		struct pkcs11_uri *uri;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		uri = pkcs11_uri_init();
Dmitry Belyavskiy 9dff9c
+		if (uri == NULL)
Dmitry Belyavskiy 9dff9c
+			fatal("Failed to init PKCS#11 URI");
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		/* Construct simple PKCS#11 URI to simplify access */
Dmitry Belyavskiy 9dff9c
+		uri->module_path = strdup(options.pkcs11_provider);
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		/* Add it as any other IdentityFile */
Dmitry Belyavskiy 9dff9c
+		cp = pkcs11_uri_get(uri);
Dmitry Belyavskiy 9dff9c
+		add_identity_file(&options, NULL, cp, 1);
Dmitry Belyavskiy 9dff9c
+		free(cp);
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		pkcs11_uri_cleanup(uri);
Dmitry Belyavskiy 9dff9c
 	}
Dmitry Belyavskiy 9dff9c
 #endif /* ENABLE_PKCS11 */
Dmitry Belyavskiy 9dff9c
 	for (i = 0; i < options.num_identity_files; i++) {
Dmitry Belyavskiy 9dff9c
+		char *name = options.identity_files[i];
Dmitry Belyavskiy 9dff9c
 		if (n_ids >= SSH_MAX_IDENTITY_FILES ||
Dmitry Belyavskiy 9dff9c
-		    strcasecmp(options.identity_files[i], "none") == 0) {
Dmitry Belyavskiy 9dff9c
+		    strcasecmp(name, "none") == 0) {
Dmitry Belyavskiy 9dff9c
 			free(options.identity_files[i]);
Dmitry Belyavskiy 9dff9c
 			options.identity_files[i] = NULL;
Dmitry Belyavskiy 9dff9c
 			continue;
Dmitry Belyavskiy 9dff9c
 		}
Dmitry Belyavskiy 9dff9c
-		cp = tilde_expand_filename(options.identity_files[i], getuid());
Dmitry Belyavskiy 9dff9c
+#ifdef ENABLE_PKCS11
Dmitry Belyavskiy 9dff9c
+		if (strlen(name) >= strlen(PKCS11_URI_SCHEME) &&
Dmitry Belyavskiy 9dff9c
+		    strncmp(name, PKCS11_URI_SCHEME,
Dmitry Belyavskiy 9dff9c
+		    strlen(PKCS11_URI_SCHEME)) == 0) {
Dmitry Belyavskiy 9dff9c
+			load_pkcs11_identity(name, identity_files,
Dmitry Belyavskiy 9dff9c
+			    identity_keys, &n_ids);
Dmitry Belyavskiy 9dff9c
+			free(options.identity_files[i]);
Dmitry Belyavskiy 9dff9c
+			continue;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+#endif /* ENABLE_PKCS11 */
Dmitry Belyavskiy 9dff9c
+		cp = tilde_expand_filename(name, getuid());
Dmitry Belyavskiy 9dff9c
 		filename = default_client_percent_dollar_expand(cp, cinfo);
Dmitry Belyavskiy 9dff9c
 		free(cp);
Dmitry Belyavskiy 9dff9c
 		check_load(sshkey_load_public(filename, &public, NULL),
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh-keygen.c.pkcs11-uri openssh-8.6p1/ssh-keygen.c
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh-keygen.c.pkcs11-uri	2021-04-16 05:55:25.000000000 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh-keygen.c	2021-05-06 11:35:55.114653289 +0200
Dmitry Belyavskiy 9dff9c
@@ -860,8 +860,11 @@ do_download(struct passwd *pw)
Dmitry Belyavskiy 9dff9c
 			free(fp);
Dmitry Belyavskiy 9dff9c
 		} else {
Dmitry Belyavskiy 9dff9c
 			(void) sshkey_write(keys[i], stdout); /* XXX check */
Dmitry Belyavskiy 9dff9c
-			fprintf(stdout, "%s%s\n",
Dmitry Belyavskiy 9dff9c
-			    *(comments[i]) == '\0' ? "" : " ", comments[i]);
Dmitry Belyavskiy 9dff9c
+			if (*(comments[i]) != '\0') {
Dmitry Belyavskiy 9dff9c
+				fprintf(stdout, " %s", comments[i]);
Dmitry Belyavskiy 9dff9c
+			}
Dmitry Belyavskiy 9dff9c
+			(void) pkcs11_uri_write(keys[i], stdout);
Dmitry Belyavskiy 9dff9c
+			fprintf(stdout, "\n");
Dmitry Belyavskiy 9dff9c
 		}
Dmitry Belyavskiy 9dff9c
 		free(comments[i]);
Dmitry Belyavskiy 9dff9c
 		sshkey_free(keys[i]);
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh-pkcs11-client.c.pkcs11-uri openssh-8.6p1/ssh-pkcs11-client.c
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh-pkcs11-client.c.pkcs11-uri	2021-04-16 05:55:25.000000000 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh-pkcs11-client.c	2021-05-06 11:35:55.114653289 +0200
Dmitry Belyavskiy 9dff9c
@@ -323,6 +323,8 @@ pkcs11_add_provider(char *name, char *pi
Dmitry Belyavskiy 9dff9c
 	u_int nkeys, i;
Dmitry Belyavskiy 9dff9c
 	struct sshbuf *msg;
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
+	debug_f("called, name = %s", name);
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
 	if (fd < 0 && pkcs11_start_helper() < 0)
Dmitry Belyavskiy 9dff9c
 		return (-1);
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
@@ -342,6 +344,7 @@ pkcs11_add_provider(char *name, char *pi
Dmitry Belyavskiy 9dff9c
 		*keysp = xcalloc(nkeys, sizeof(struct sshkey *));
Dmitry Belyavskiy 9dff9c
 		if (labelsp)
Dmitry Belyavskiy 9dff9c
 			*labelsp = xcalloc(nkeys, sizeof(char *));
Dmitry Belyavskiy 9dff9c
+		debug_f("nkeys = %u", nkeys);
Dmitry Belyavskiy 9dff9c
 		for (i = 0; i < nkeys; i++) {
Dmitry Belyavskiy 9dff9c
 			/* XXX clean up properly instead of fatal() */
Dmitry Belyavskiy 9dff9c
 			if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 ||
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh-pkcs11.c.pkcs11-uri openssh-8.6p1/ssh-pkcs11.c
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh-pkcs11.c.pkcs11-uri	2021-04-16 05:55:25.000000000 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh-pkcs11.c	2021-05-06 11:35:55.115653297 +0200
Dmitry Belyavskiy 9dff9c
@@ -55,8 +55,8 @@ struct pkcs11_slotinfo {
Petr Šabata 81d24c
 	int			logged_in;
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-struct pkcs11_provider {
Petr Šabata 81d24c
-	char			*name;
Petr Šabata 81d24c
+struct pkcs11_module {
Petr Šabata 81d24c
+	char			*module_path;
Petr Šabata 81d24c
 	void			*handle;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*function_list;
Petr Šabata 81d24c
 	CK_INFO			info;
Dmitry Belyavskiy 9dff9c
@@ -65,6 +65,13 @@ struct pkcs11_provider {
Petr Šabata 81d24c
 	struct pkcs11_slotinfo	*slotinfo;
Petr Šabata 81d24c
 	int			valid;
Petr Šabata 81d24c
 	int			refcount;
Petr Šabata 81d24c
+};
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+struct pkcs11_provider {
Petr Šabata 81d24c
+	char			*name;
Petr Šabata 81d24c
+	struct pkcs11_module	*module; /* can be shared between various providers */
Petr Šabata 81d24c
+	int			refcount;
Petr Šabata 81d24c
+	int			valid;
Petr Šabata 81d24c
 	TAILQ_ENTRY(pkcs11_provider) next;
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
@@ -75,6 +82,7 @@ struct pkcs11_key {
Petr Šabata 81d24c
 	CK_ULONG		slotidx;
Petr Šabata 81d24c
 	char			*keyid;
Petr Šabata 81d24c
 	int			keyid_len;
Petr Šabata 81d24c
+	char			*label;
Petr Šabata 81d24c
 };
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 int pkcs11_interactive = 0;
DistroBaker d029bb
@@ -106,26 +114,61 @@ pkcs11_init(int interactive)
Petr Šabata 81d24c
  * this is called when a provider gets unregistered.
Petr Šabata 81d24c
  */
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
-pkcs11_provider_finalize(struct pkcs11_provider *p)
Petr Šabata 81d24c
+pkcs11_module_finalize(struct pkcs11_module *m)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	CK_RV rv;
Petr Šabata 81d24c
 	CK_ULONG i;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	debug("pkcs11_provider_finalize: %p refcount %d valid %d",
Petr Šabata 81d24c
-	    p, p->refcount, p->valid);
Petr Šabata 81d24c
-	if (!p->valid)
DistroBaker d029bb
+	debug_f("%p refcount %d valid %d", m, m->refcount, m->valid);
Petr Šabata 81d24c
+	if (!m->valid)
Petr Šabata 81d24c
 		return;
Petr Šabata 81d24c
-	for (i = 0; i < p->nslots; i++) {
Petr Šabata 81d24c
-		if (p->slotinfo[i].session &&
Petr Šabata 81d24c
-		    (rv = p->function_list->C_CloseSession(
Petr Šabata 81d24c
-		    p->slotinfo[i].session)) != CKR_OK)
Petr Šabata 81d24c
+	for (i = 0; i < m->nslots; i++) {
Petr Šabata 81d24c
+		if (m->slotinfo[i].session &&
Petr Šabata 81d24c
+		    (rv = m->function_list->C_CloseSession(
Petr Šabata 81d24c
+		    m->slotinfo[i].session)) != CKR_OK)
Petr Šabata 81d24c
 			error("C_CloseSession failed: %lu", rv);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
Petr Šabata 81d24c
+	if ((rv = m->function_list->C_Finalize(NULL)) != CKR_OK)
Petr Šabata 81d24c
 		error("C_Finalize failed: %lu", rv);
Petr Šabata 81d24c
+	m->valid = 0;
Petr Šabata 81d24c
+	m->function_list = NULL;
Petr Šabata 81d24c
+	dlclose(m->handle);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * remove a reference to the pkcs11 module.
Petr Šabata 81d24c
+ * called when a provider is unregistered.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+pkcs11_module_unref(struct pkcs11_module *m)
Petr Šabata 81d24c
+{
DistroBaker d029bb
+	debug_f("%p refcount %d", m, m->refcount);
Petr Šabata 81d24c
+	if (--m->refcount <= 0) {
Petr Šabata 81d24c
+		pkcs11_module_finalize(m);
Petr Šabata 81d24c
+		if (m->valid)
DistroBaker d029bb
+			error_f("%p still valid", m);
Petr Šabata 81d24c
+		free(m->slotlist);
Petr Šabata 81d24c
+		free(m->slotinfo);
Petr Šabata 81d24c
+		free(m->module_path);
Petr Šabata 81d24c
+		free(m);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * finalize a provider shared libarary, it's no longer usable.
Petr Šabata 81d24c
+ * however, there might still be keys referencing this provider,
Petr Šabata 81d24c
+ * so the actuall freeing of memory is handled by pkcs11_provider_unref().
Petr Šabata 81d24c
+ * this is called when a provider gets unregistered.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+static void
Petr Šabata 81d24c
+pkcs11_provider_finalize(struct pkcs11_provider *p)
Petr Šabata 81d24c
+{
DistroBaker d029bb
+	debug_f("%p refcount %d valid %d", p, p->refcount, p->valid);
Petr Šabata 81d24c
+	if (!p->valid)
Petr Šabata 81d24c
+		return;
Petr Šabata 81d24c
+	pkcs11_module_unref(p->module);
Petr Šabata 81d24c
+	p->module = NULL;
Petr Šabata 81d24c
 	p->valid = 0;
Petr Šabata 81d24c
-	p->function_list = NULL;
Petr Šabata 81d24c
-	dlclose(p->handle);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 /*
Dmitry Belyavskiy 9dff9c
@@ -135,13 +178,11 @@ pkcs11_provider_finalize(struct pkcs11_p
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
 pkcs11_provider_unref(struct pkcs11_provider *p)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
DistroBaker d029bb
+	debug_f("%p refcount %d", p, p->refcount);
Petr Šabata 81d24c
 	if (--p->refcount <= 0) {
Petr Šabata 81d24c
-		if (p->valid)
Petr Šabata 81d24c
-			error("pkcs11_provider_unref: %p still valid", p);
Petr Šabata 81d24c
 		free(p->name);
Petr Šabata 81d24c
-		free(p->slotlist);
Petr Šabata 81d24c
-		free(p->slotinfo);
Petr Šabata 81d24c
+		if (p->module)
Petr Šabata 81d24c
+			pkcs11_module_unref(p->module);
Petr Šabata 81d24c
 		free(p);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 }
Dmitry Belyavskiy 9dff9c
@@ -159,6 +200,20 @@ pkcs11_terminate(void)
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/* lookup provider by module path */
Petr Šabata 81d24c
+static struct pkcs11_module *
Petr Šabata 81d24c
+pkcs11_provider_lookup_module(char *module_path)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	struct pkcs11_provider *p;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	TAILQ_FOREACH(p, &pkcs11_providers, next) {
Petr Šabata 81d24c
+		debug("check %p %s (%s)", p, p->name, p->module->module_path);
Petr Šabata 81d24c
+		if (!strcmp(module_path, p->module->module_path))
Petr Šabata 81d24c
+			return (p->module);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	return (NULL);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* lookup provider by name */
Petr Šabata 81d24c
 static struct pkcs11_provider *
Petr Šabata 81d24c
 pkcs11_provider_lookup(char *provider_id)
Dmitry Belyavskiy 9dff9c
@@ -173,19 +228,55 @@ pkcs11_provider_lookup(char *provider_id
Petr Šabata 81d24c
 	return (NULL);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+int pkcs11_del_provider_by_uri(struct pkcs11_uri *);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* unregister provider by name */
Petr Šabata 81d24c
 int
Petr Šabata 81d24c
 pkcs11_del_provider(char *provider_id)
Dmitry Belyavskiy 9dff9c
 {
Petr Šabata 81d24c
+	int rv;
Petr Šabata 81d24c
+	struct pkcs11_uri *uri;
Petr Šabata 81d24c
+
DistroBaker d029bb
+	debug_f("called, provider_id = %s", provider_id);
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+      if (provider_id == NULL)
Dmitry Belyavskiy 9dff9c
+          return 0;
Dmitry Belyavskiy 9dff9c
+
Petr Šabata 81d24c
+	uri = pkcs11_uri_init();
Petr Šabata 81d24c
+	if (uri == NULL)
Petr Šabata 81d24c
+		fatal("Failed to init PKCS#11 URI");
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) &&
Petr Šabata 81d24c
+	    strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) {
Petr Šabata 81d24c
+		if (pkcs11_uri_parse(provider_id, uri) != 0)
Petr Šabata 81d24c
+			fatal("Failed to parse PKCS#11 URI");
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		uri->module_path = strdup(provider_id);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	rv = pkcs11_del_provider_by_uri(uri);
Petr Šabata 81d24c
+	pkcs11_uri_cleanup(uri);
Petr Šabata 81d24c
+	return rv;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/* unregister provider by PKCS#11 URI */
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+pkcs11_del_provider_by_uri(struct pkcs11_uri *uri)
Dmitry Belyavskiy 9dff9c
+{
Petr Šabata 81d24c
 	struct pkcs11_provider *p;
Petr Šabata 81d24c
+	int rv = -1;
Petr Šabata 81d24c
+	char *provider_uri = pkcs11_uri_get(uri);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
DistroBaker d029bb
+	debug3_f("called with provider %s", provider_uri);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) {
Petr Šabata 81d24c
 		TAILQ_REMOVE(&pkcs11_providers, p, next);
Petr Šabata 81d24c
 		pkcs11_provider_finalize(p);
Petr Šabata 81d24c
 		pkcs11_provider_unref(p);
Petr Šabata 81d24c
-		return (0);
Petr Šabata 81d24c
+		rv = 0;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	return (-1);
Petr Šabata 81d24c
+	free(provider_uri);
Petr Šabata 81d24c
+	return rv;
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 static RSA_METHOD *rsa_method;
Dmitry Belyavskiy 9dff9c
@@ -195,6 +283,55 @@ static EC_KEY_METHOD *ec_key_method;
Petr Šabata 81d24c
 static int ec_key_idx = 0;
Petr Šabata 81d24c
 #endif
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * This can't be in the ssh-pkcs11-uri, becase we can not depend on
Petr Šabata 81d24c
+ * PKCS#11 structures in ssh-agent (using client-helper communication)
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+int
Petr Šabata 81d24c
+pkcs11_uri_write(const struct sshkey *key, FILE *f)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	char *p = NULL;
Petr Šabata 81d24c
+	struct pkcs11_uri uri;
Petr Šabata 81d24c
+	struct pkcs11_key *k11;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* sanity - is it a RSA key with associated app_data? */
Petr Šabata 81d24c
+	switch (key->type) {
Petr Šabata 81d24c
+	case KEY_RSA:
Petr Šabata 81d24c
+		k11 = RSA_get_ex_data(key->rsa, rsa_idx);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+#ifdef HAVE_EC_KEY_METHOD_NEW
Petr Šabata 81d24c
+	case KEY_ECDSA:
Petr Šabata 81d24c
+		k11 = EC_KEY_get_ex_data(key->ecdsa, ec_key_idx);
Petr Šabata 81d24c
+		break;
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+	default:
Petr Šabata 81d24c
+		error("Unknown key type %d", key->type);
Petr Šabata 81d24c
+		return -1;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (k11 == NULL) {
Petr Šabata 81d24c
+		error("Failed to get ex_data for key type %d", key->type);
Petr Šabata 81d24c
+		return (-1);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* omit type -- we are looking for private-public or private-certificate pairs */
Petr Šabata 81d24c
+	uri.id = k11->keyid;
Petr Šabata 81d24c
+	uri.id_len = k11->keyid_len;
Petr Šabata 81d24c
+	uri.token = k11->provider->module->slotinfo[k11->slotidx].token.label;
Petr Šabata 81d24c
+	uri.object = k11->label;
Petr Šabata 81d24c
+	uri.module_path = k11->provider->module->module_path;
Petr Šabata 81d24c
+	uri.lib_manuf = k11->provider->module->info.manufacturerID;
Petr Šabata 81d24c
+	uri.manuf = k11->provider->module->slotinfo[k11->slotidx].token.manufacturerID;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	p = pkcs11_uri_get(&uri);
Petr Šabata 81d24c
+	/* do not cleanup -- we do not allocate here, only reference */
Petr Šabata 81d24c
+	if (p == NULL)
Petr Šabata 81d24c
+		return -1;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	fprintf(f, " %s", p);
Petr Šabata 81d24c
+	free(p);
Petr Šabata 81d24c
+	return 0;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 /* release a wrapped object */
Petr Šabata 81d24c
 static void
Petr Šabata 81d24c
 pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
Dmitry Belyavskiy 9dff9c
@@ -208,6 +345,7 @@ pkcs11_k11_free(void *parent, void *ptr,
Petr Šabata 81d24c
 	if (k11->provider)
Petr Šabata 81d24c
 		pkcs11_provider_unref(k11->provider);
Petr Šabata 81d24c
 	free(k11->keyid);
Petr Šabata 81d24c
+	free(k11->label);
Petr Šabata 81d24c
 	free(k11);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
@@ -222,8 +360,8 @@ pkcs11_find(struct pkcs11_provider *p, C
Petr Šabata 81d24c
 	CK_RV			rv;
Petr Šabata 81d24c
 	int			ret = -1;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
 	if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv);
Petr Šabata 81d24c
 		return (-1);
Dmitry Belyavskiy 9dff9c
@@ -262,12 +400,12 @@ pkcs11_login_slot(struct pkcs11_provider
Petr Šabata 81d24c
 	else {
Petr Šabata 81d24c
 		snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ",
Petr Šabata 81d24c
 		    si->token.label);
Petr Šabata 81d24c
-		if ((pin = read_passphrase(prompt, RP_ALLOW_EOF)) == NULL) {
Petr Šabata 81d24c
+		if ((pin = read_passphrase(prompt, RP_ALLOW_EOF|RP_ALLOW_STDIN)) == NULL) {
DistroBaker d029bb
 			debug_f("no pin specified");
Petr Šabata 81d24c
 			return (-1);	/* bail out */
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	rv = provider->function_list->C_Login(si->session, type, (u_char *)pin,
Petr Šabata 81d24c
+	rv = provider->module->function_list->C_Login(si->session, type, (u_char *)pin,
Petr Šabata 81d24c
 	    (pin != NULL) ? strlen(pin) : 0);
Petr Šabata 81d24c
 	if (pin != NULL)
Petr Šabata 81d24c
 		freezero(pin, strlen(pin));
Dmitry Belyavskiy 9dff9c
@@ -297,13 +435,14 @@ pkcs11_login_slot(struct pkcs11_provider
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	if (k11 == NULL || k11->provider == NULL || !k11->provider->valid) {
Petr Šabata 81d24c
+	if (k11 == NULL || k11->provider == NULL || !k11->provider->valid ||
Petr Šabata 81d24c
+	    k11->provider->module == NULL || !k11->provider->module->valid) {
Petr Šabata 81d24c
 		error("no pkcs11 (valid) provider found");
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	return pkcs11_login_slot(k11->provider,
Petr Šabata 81d24c
-	    &k11->provider->slotinfo[k11->slotidx], type);
Petr Šabata 81d24c
+	    &k11->provider->module->slotinfo[k11->slotidx], type);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
@@ -319,13 +458,14 @@ pkcs11_check_obj_bool_attrib(struct pkcs
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	*val = 0;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (!k11->provider || !k11->provider->valid) {
Petr Šabata 81d24c
+	if (!k11->provider || !k11->provider->valid ||
Petr Šabata 81d24c
+	    !k11->provider->module || !k11->provider->module->valid) {
Petr Šabata 81d24c
 		error("no pkcs11 (valid) provider found");
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = k11->provider->function_list;
Petr Šabata 81d24c
-	si = &k11->provider->slotinfo[k11->slotidx];
Petr Šabata 81d24c
+	f = k11->provider->module->function_list;
Petr Šabata 81d24c
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	attr.type = type;
Petr Šabata 81d24c
 	attr.pValue = &flag;
Dmitry Belyavskiy 9dff9c
@@ -356,13 +496,14 @@ pkcs11_get_key(struct pkcs11_key *k11, C
Petr Šabata 81d24c
 	int			 always_auth = 0;
Petr Šabata 81d24c
 	int			 did_login = 0;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (!k11->provider || !k11->provider->valid) {
Petr Šabata 81d24c
+	if (!k11->provider || !k11->provider->valid ||
Petr Šabata 81d24c
+	    !k11->provider->module || !k11->provider->module->valid) {
Petr Šabata 81d24c
 		error("no pkcs11 (valid) provider found");
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = k11->provider->function_list;
Petr Šabata 81d24c
-	si = &k11->provider->slotinfo[k11->slotidx];
Petr Šabata 81d24c
+	f = k11->provider->module->function_list;
Petr Šabata 81d24c
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
Petr Šabata 81d24c
 		if (pkcs11_login(k11, CKU_USER) < 0) {
Dmitry Belyavskiy 9dff9c
@@ -439,8 +580,8 @@ pkcs11_rsa_private_encrypt(int flen, con
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = k11->provider->function_list;
Petr Šabata 81d24c
-	si = &k11->provider->slotinfo[k11->slotidx];
Petr Šabata 81d24c
+	f = k11->provider->module->function_list;
Petr Šabata 81d24c
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Petr Šabata 81d24c
 	tlen = RSA_size(rsa);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* XXX handle CKR_BUFFER_TOO_SMALL */
Dmitry Belyavskiy 9dff9c
@@ -484,7 +625,7 @@ pkcs11_rsa_start_wrapper(void)
Petr Šabata 81d24c
 /* redirect private key operations for rsa key to pkcs11 token */
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Petr Šabata 81d24c
-    CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
Petr Šabata 81d24c
+    CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, RSA *rsa)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct pkcs11_key	*k11;
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
@@ -502,6 +643,12 @@ pkcs11_rsa_wrap(struct pkcs11_provider *
Petr Šabata 81d24c
 		memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	if (label_attrib->ulValueLen > 0 ) {
Petr Šabata 81d24c
+		k11->label = xmalloc(label_attrib->ulValueLen+1);
Petr Šabata 81d24c
+		memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen);
Petr Šabata 81d24c
+		k11->label[label_attrib->ulValueLen] = 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	RSA_set_method(rsa, rsa_method);
Petr Šabata 81d24c
 	RSA_set_ex_data(rsa, rsa_idx, k11);
Petr Šabata 81d24c
 	return (0);
Dmitry Belyavskiy 9dff9c
@@ -532,8 +679,8 @@ ecdsa_do_sign(const unsigned char *dgst,
Petr Šabata 81d24c
 		return (NULL);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = k11->provider->function_list;
Petr Šabata 81d24c
-	si = &k11->provider->slotinfo[k11->slotidx];
Petr Šabata 81d24c
+	f = k11->provider->module->function_list;
Petr Šabata 81d24c
+	si = &k11->provider->module->slotinfo[k11->slotidx];
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	siglen = ECDSA_size(ec);
Petr Šabata 81d24c
 	sig = xmalloc(siglen);
Dmitry Belyavskiy 9dff9c
@@ -598,7 +745,7 @@ pkcs11_ecdsa_start_wrapper(void)
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
Petr Šabata 81d24c
-    CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
Petr Šabata 81d24c
+    CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ec)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct pkcs11_key	*k11;
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
@@ -614,6 +761,12 @@ pkcs11_ecdsa_wrap(struct pkcs11_provider
Petr Šabata 81d24c
 	k11->keyid = xmalloc(k11->keyid_len);
Petr Šabata 81d24c
 	memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
+	if (label_attrib->ulValueLen > 0 ) {
Petr Šabata 81d24c
+		k11->label = xmalloc(label_attrib->ulValueLen+1);
Petr Šabata 81d24c
+		memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen);
Petr Šabata 81d24c
+		k11->label[label_attrib->ulValueLen] = 0;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	EC_KEY_set_method(ec, ec_key_method);
Petr Šabata 81d24c
 	EC_KEY_set_ex_data(ec, ec_key_idx, k11);
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
@@ -650,8 +803,8 @@ pkcs11_open_session(struct pkcs11_provid
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	session;
Petr Šabata 81d24c
 	int			login_required, ret;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
-	si = &p->slotinfo[slotidx];
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
+	si = &p->module->slotinfo[slotidx];
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	login_required = si->token.flags & CKF_LOGIN_REQUIRED;
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
@@ -661,9 +814,9 @@ pkcs11_open_session(struct pkcs11_provid
Petr Šabata 81d24c
 		error("pin required");
Petr Šabata 81d24c
 		return (-SSH_PKCS11_ERR_PIN_REQUIRED);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
Petr Šabata 81d24c
+	if ((rv = f->C_OpenSession(p->module->slotlist[slotidx], CKF_RW_SESSION|
Petr Šabata 81d24c
 	    CKF_SERIAL_SESSION, NULL, NULL, &session)) != CKR_OK) {
Petr Šabata 81d24c
-		error("C_OpenSession failed: %lu", rv);
Petr Šabata 81d24c
+		error("C_OpenSession failed for slot %lu: %lu", slotidx, rv);
Petr Šabata 81d24c
 		return (-1);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	if (login_required && pin != NULL && strlen(pin) != 0) {
Dmitry Belyavskiy 9dff9c
@@ -699,7 +852,8 @@ static struct sshkey *
Petr Šabata 81d24c
 pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
     CK_OBJECT_HANDLE *obj)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 key_attr[3];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 key_attr[4];
Petr Šabata 81d24c
+	int			 nattr = 4;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Dmitry Belyavskiy 9dff9c
@@ -713,14 +867,15 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	memset(&key_attr, 0, sizeof(key_attr));
Petr Šabata 81d24c
 	key_attr[0].type = CKA_ID;
Petr Šabata 81d24c
-	key_attr[1].type = CKA_EC_POINT;
Petr Šabata 81d24c
-	key_attr[2].type = CKA_EC_PARAMS;
Petr Šabata 81d24c
+	key_attr[1].type = CKA_LABEL;
Petr Šabata 81d24c
+	key_attr[2].type = CKA_EC_POINT;
Petr Šabata 81d24c
+	key_attr[3].type = CKA_EC_PARAMS;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* figure out size of the attributes */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		return (NULL);
Dmitry Belyavskiy 9dff9c
@@ -731,19 +886,19 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
DistroBaker fba82b
 	 * ensure that none of the others are zero length.
Petr Šabata 81d24c
 	 * XXX assumes CKA_ID is always first.
Petr Šabata 81d24c
 	 */
DistroBaker fba82b
-	if (key_attr[1].ulValueLen == 0 ||
Petr Šabata 81d24c
-	    key_attr[2].ulValueLen == 0) {
DistroBaker fba82b
+	if (key_attr[2].ulValueLen == 0 ||
Petr Šabata 81d24c
+	    key_attr[3].ulValueLen == 0) {
Petr Šabata 81d24c
 		error("invalid attribute length");
Petr Šabata 81d24c
 		return (NULL);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* allocate buffers for attributes */
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		if (key_attr[i].ulValueLen > 0)
Petr Šabata 81d24c
 			key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* retrieve ID, public point and curve parameters of EC key */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Dmitry Belyavskiy 9dff9c
@@ -755,8 +910,8 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	attrp = key_attr[2].pValue;
Petr Šabata 81d24c
-	group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
Petr Šabata 81d24c
+	attrp = key_attr[3].pValue;
Petr Šabata 81d24c
+	group = d2i_ECPKParameters(NULL, &attrp, key_attr[3].ulValueLen);
Petr Šabata 81d24c
 	if (group == NULL) {
Petr Šabata 81d24c
 		ossl_error("d2i_ECPKParameters failed");
Petr Šabata 81d24c
 		goto fail;
Dmitry Belyavskiy 9dff9c
@@ -767,13 +922,13 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (key_attr[1].ulValueLen <= 2) {
Petr Šabata 81d24c
+	if (key_attr[2].ulValueLen <= 2) {
Petr Šabata 81d24c
 		error("CKA_EC_POINT too small");
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	attrp = key_attr[1].pValue;
Petr Šabata 81d24c
-	octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen);
Petr Šabata 81d24c
+	attrp = key_attr[2].pValue;
Petr Šabata 81d24c
+	octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[2].ulValueLen);
Petr Šabata 81d24c
 	if (octet == NULL) {
Petr Šabata 81d24c
 		ossl_error("d2i_ASN1_OCTET_STRING failed");
Petr Šabata 81d24c
 		goto fail;
Dmitry Belyavskiy 9dff9c
@@ -790,7 +945,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
Petr Šabata 81d24c
+	if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], ec))
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	key = sshkey_new(KEY_UNSPEC);
Dmitry Belyavskiy 9dff9c
@@ -806,7 +961,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
Petr Šabata 81d24c
 	ec = NULL;	/* now owned by key */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 fail:
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		free(key_attr[i].pValue);
Petr Šabata 81d24c
 	if (ec)
Petr Šabata 81d24c
 		EC_KEY_free(ec);
Dmitry Belyavskiy 9dff9c
@@ -823,7 +978,8 @@ static struct sshkey *
Petr Šabata 81d24c
 pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
     CK_OBJECT_HANDLE *obj)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 key_attr[3];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 key_attr[4];
Petr Šabata 81d24c
+	int			 nattr = 4;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Dmitry Belyavskiy 9dff9c
@@ -834,14 +990,15 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	memset(&key_attr, 0, sizeof(key_attr));
Petr Šabata 81d24c
 	key_attr[0].type = CKA_ID;
Petr Šabata 81d24c
-	key_attr[1].type = CKA_MODULUS;
Petr Šabata 81d24c
-	key_attr[2].type = CKA_PUBLIC_EXPONENT;
Petr Šabata 81d24c
+	key_attr[1].type = CKA_LABEL;
Petr Šabata 81d24c
+	key_attr[2].type = CKA_MODULUS;
Petr Šabata 81d24c
+	key_attr[3].type = CKA_PUBLIC_EXPONENT;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* figure out size of the attributes */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		return (NULL);
Dmitry Belyavskiy 9dff9c
@@ -852,19 +1009,19 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
DistroBaker fba82b
 	 * ensure that none of the others are zero length.
Petr Šabata 81d24c
 	 * XXX assumes CKA_ID is always first.
Petr Šabata 81d24c
 	 */
DistroBaker fba82b
-	if (key_attr[1].ulValueLen == 0 ||
Petr Šabata 81d24c
-	    key_attr[2].ulValueLen == 0) {
DistroBaker fba82b
+	if (key_attr[2].ulValueLen == 0 ||
Petr Šabata 81d24c
+	    key_attr[3].ulValueLen == 0) {
Petr Šabata 81d24c
 		error("invalid attribute length");
Petr Šabata 81d24c
 		return (NULL);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* allocate buffers for attributes */
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		if (key_attr[i].ulValueLen > 0)
Petr Šabata 81d24c
 			key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* retrieve ID, modulus and public exponent of RSA key */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Dmitry Belyavskiy 9dff9c
@@ -876,8 +1033,8 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
Petr Šabata 81d24c
-	rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
Petr Šabata 81d24c
+	rsa_n = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
Petr Šabata 81d24c
+	rsa_e = BN_bin2bn(key_attr[3].pValue, key_attr[3].ulValueLen, NULL);
Petr Šabata 81d24c
 	if (rsa_n == NULL || rsa_e == NULL) {
Petr Šabata 81d24c
 		error("BN_bin2bn failed");
Petr Šabata 81d24c
 		goto fail;
Dmitry Belyavskiy 9dff9c
@@ -886,7 +1043,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
DistroBaker d029bb
 		fatal_f("set key");
Petr Šabata 81d24c
 	rsa_n = rsa_e = NULL; /* transferred */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
Petr Šabata 81d24c
+	if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], rsa))
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	key = sshkey_new(KEY_UNSPEC);
Dmitry Belyavskiy 9dff9c
@@ -901,7 +1058,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
Petr Šabata 81d24c
 	rsa = NULL;	/* now owned by key */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 fail:
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		free(key_attr[i].pValue);
Petr Šabata 81d24c
 	RSA_free(rsa);
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
@@ -912,7 +1069,8 @@ static int
Petr Šabata 81d24c
 pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
     CK_OBJECT_HANDLE *obj, struct sshkey **keyp, char **labelp)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 cert_attr[3];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 cert_attr[4];
Petr Šabata 81d24c
+	int			 nattr = 4;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Dmitry Belyavskiy 9dff9c
@@ -936,14 +1094,15 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	memset(&cert_attr, 0, sizeof(cert_attr));
Petr Šabata 81d24c
 	cert_attr[0].type = CKA_ID;
Petr Šabata 81d24c
-	cert_attr[1].type = CKA_SUBJECT;
Petr Šabata 81d24c
-	cert_attr[2].type = CKA_VALUE;
Petr Šabata 81d24c
+	cert_attr[1].type = CKA_LABEL;
Petr Šabata 81d24c
+	cert_attr[2].type = CKA_SUBJECT;
Petr Šabata 81d24c
+	cert_attr[3].type = CKA_VALUE;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* figure out size of the attributes */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		return -1;
Dmitry Belyavskiy 9dff9c
@@ -955,18 +1114,19 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
Petr Šabata 81d24c
 	 * XXX assumes CKA_ID is always first.
Petr Šabata 81d24c
 	 */
Petr Šabata 81d24c
 	if (cert_attr[1].ulValueLen == 0 ||
Petr Šabata 81d24c
-	    cert_attr[2].ulValueLen == 0) {
Petr Šabata 81d24c
+	    cert_attr[2].ulValueLen == 0 ||
Petr Šabata 81d24c
+	    cert_attr[3].ulValueLen == 0) {
Petr Šabata 81d24c
 		error("invalid attribute length");
Petr Šabata 81d24c
 		return -1;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* allocate buffers for attributes */
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		if (cert_attr[i].ulValueLen > 0)
Petr Šabata 81d24c
 			cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* retrieve ID, subject and value of certificate */
Petr Šabata 81d24c
-	rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
Petr Šabata 81d24c
+	rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetAttributeValue failed: %lu", rv);
Petr Šabata 81d24c
 		goto out;
Dmitry Belyavskiy 9dff9c
@@ -980,8 +1140,8 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
Petr Šabata 81d24c
 		subject = xstrdup("invalid subject");
Petr Šabata 81d24c
 	X509_NAME_free(x509_name);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	cp = cert_attr[2].pValue;
Petr Šabata 81d24c
-	if ((x509 = d2i_X509(NULL, &cp, cert_attr[2].ulValueLen)) == NULL) {
Petr Šabata 81d24c
+	cp = cert_attr[3].pValue;
Petr Šabata 81d24c
+	if ((x509 = d2i_X509(NULL, &cp, cert_attr[3].ulValueLen)) == NULL) {
Petr Šabata 81d24c
 		error("d2i_x509 failed");
Petr Šabata 81d24c
 		goto out;
Petr Šabata 81d24c
 	}
Dmitry Belyavskiy 9dff9c
@@ -1001,7 +1161,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
Petr Šabata 81d24c
 			goto out;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-		if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
Petr Šabata 81d24c
+		if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], rsa))
Petr Šabata 81d24c
 			goto out;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 		key = sshkey_new(KEY_UNSPEC);
Dmitry Belyavskiy 9dff9c
@@ -1031,7 +1191,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
Petr Šabata 81d24c
 			goto out;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-		if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
Petr Šabata 81d24c
+		if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], ec))
Petr Šabata 81d24c
 			goto out;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 		key = sshkey_new(KEY_UNSPEC);
Dmitry Belyavskiy 9dff9c
@@ -1051,7 +1211,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
Petr Šabata 81d24c
 		goto out;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
  out:
Petr Šabata 81d24c
-	for (i = 0; i < 3; i++)
Petr Šabata 81d24c
+	for (i = 0; i < nattr; i++)
Petr Šabata 81d24c
 		free(cert_attr[i].pValue);
Petr Šabata 81d24c
 	X509_free(x509);
Petr Šabata 81d24c
 	RSA_free(rsa);
Dmitry Belyavskiy 9dff9c
@@ -1102,11 +1262,12 @@ note_key(struct pkcs11_provider *p, CK_U
Petr Šabata 81d24c
  */
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
-    struct sshkey ***keysp, char ***labelsp, int *nkeys)
Petr Šabata 81d24c
+    struct sshkey ***keysp, char ***labelsp, int *nkeys, struct pkcs11_uri *uri)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct sshkey		*key = NULL;
Petr Šabata 81d24c
 	CK_OBJECT_CLASS		 key_class;
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 key_attr[1];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 key_attr[3];
Petr Šabata 81d24c
+	int			 nattr = 1;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Dmitry Belyavskiy 9dff9c
@@ -1123,10 +1284,23 @@ pkcs11_fetch_certs(struct pkcs11_provide
Petr Šabata 81d24c
 	key_attr[0].pValue = &key_class;
Petr Šabata 81d24c
 	key_attr[0].ulValueLen = sizeof(key_class);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	if (uri->id != NULL) {
Petr Šabata 81d24c
+		key_attr[nattr].type = CKA_ID;
Petr Šabata 81d24c
+		key_attr[nattr].pValue = uri->id;
Petr Šabata 81d24c
+		key_attr[nattr].ulValueLen = uri->id_len;
Petr Šabata 81d24c
+		nattr++;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (uri->object != NULL) {
Petr Šabata 81d24c
+		key_attr[nattr].type = CKA_LABEL;
Petr Šabata 81d24c
+		key_attr[nattr].pValue = uri->object;
Petr Šabata 81d24c
+		key_attr[nattr].ulValueLen = strlen(uri->object);
Petr Šabata 81d24c
+		nattr++;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	rv = f->C_FindObjectsInit(session, key_attr, 1);
Petr Šabata 81d24c
+	rv = f->C_FindObjectsInit(session, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_FindObjectsInit failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Dmitry Belyavskiy 9dff9c
@@ -1207,11 +1381,12 @@ fail:
Petr Šabata 81d24c
  */
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
 pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
Petr Šabata 81d24c
-    struct sshkey ***keysp, char ***labelsp, int *nkeys)
Petr Šabata 81d24c
+    struct sshkey ***keysp, char ***labelsp, int *nkeys, struct pkcs11_uri *uri)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
 	struct sshkey		*key = NULL;
Petr Šabata 81d24c
 	CK_OBJECT_CLASS		 key_class;
Petr Šabata 81d24c
-	CK_ATTRIBUTE		 key_attr[2];
Petr Šabata 81d24c
+	CK_ATTRIBUTE		 key_attr[3];
Petr Šabata 81d24c
+	int			 nattr = 1;
Petr Šabata 81d24c
 	CK_SESSION_HANDLE	 session;
Petr Šabata 81d24c
 	CK_FUNCTION_LIST	*f = NULL;
Petr Šabata 81d24c
 	CK_RV			 rv;
Dmitry Belyavskiy 9dff9c
@@ -1227,10 +1402,23 @@ pkcs11_fetch_keys(struct pkcs11_provider
Petr Šabata 81d24c
 	key_attr[0].pValue = &key_class;
Petr Šabata 81d24c
 	key_attr[0].ulValueLen = sizeof(key_class);
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	session = p->slotinfo[slotidx].session;
Petr Šabata 81d24c
-	f = p->function_list;
Petr Šabata 81d24c
+	if (uri->id != NULL) {
Petr Šabata 81d24c
+		key_attr[nattr].type = CKA_ID;
Petr Šabata 81d24c
+		key_attr[nattr].pValue = uri->id;
Petr Šabata 81d24c
+		key_attr[nattr].ulValueLen = uri->id_len;
Petr Šabata 81d24c
+		nattr++;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (uri->object != NULL) {
Petr Šabata 81d24c
+		key_attr[nattr].type = CKA_LABEL;
Petr Šabata 81d24c
+		key_attr[nattr].pValue = uri->object;
Petr Šabata 81d24c
+		key_attr[nattr].ulValueLen = strlen(uri->object);
Petr Šabata 81d24c
+		nattr++;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	session = p->module->slotinfo[slotidx].session;
Petr Šabata 81d24c
+	f = p->module->function_list;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	rv = f->C_FindObjectsInit(session, key_attr, 1);
Petr Šabata 81d24c
+	rv = f->C_FindObjectsInit(session, key_attr, nattr);
Petr Šabata 81d24c
 	if (rv != CKR_OK) {
Petr Šabata 81d24c
 		error("C_FindObjectsInit failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Dmitry Belyavskiy 9dff9c
@@ -1499,16 +1687,10 @@ pkcs11_ecdsa_generate_private_key(struct
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 #endif /* WITH_PKCS11_KEYGEN */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-/*
Petr Šabata 81d24c
- * register a new provider, fails if provider already exists. if
Petr Šabata 81d24c
- * keyp is provided, fetch keys.
Petr Šabata 81d24c
- */
Petr Šabata 81d24c
 static int
Petr Šabata 81d24c
-pkcs11_register_provider(char *provider_id, char *pin,
Petr Šabata 81d24c
-    struct sshkey ***keyp, char ***labelsp,
Petr Šabata 81d24c
-    struct pkcs11_provider **providerp, CK_ULONG user)
Petr Šabata 81d24c
+pkcs11_initialize_provider(struct pkcs11_uri *uri, struct pkcs11_provider **providerp)
Petr Šabata 81d24c
 {
Petr Šabata 81d24c
-	int nkeys, need_finalize = 0;
Petr Šabata 81d24c
+	int need_finalize = 0;
Petr Šabata 81d24c
 	int ret = -1;
Petr Šabata 81d24c
 	struct pkcs11_provider *p = NULL;
Petr Šabata 81d24c
 	void *handle = NULL;
Dmitry Belyavskiy 9dff9c
@@ -1517,164 +1699,298 @@ pkcs11_register_provider(char *provider_
Petr Šabata 81d24c
 	CK_FUNCTION_LIST *f = NULL;
Petr Šabata 81d24c
 	CK_TOKEN_INFO *token;
Petr Šabata 81d24c
 	CK_ULONG i;
Petr Šabata 81d24c
+	char *provider_module = NULL;
Petr Šabata 81d24c
+	struct pkcs11_module *m = NULL;
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
-	if (providerp == NULL)
Petr Šabata 81d24c
+	/* if no provider specified, fallback to p11-kit */
Petr Šabata 81d24c
+	if (uri->module_path == NULL) {
Petr Šabata 81d24c
+#ifdef PKCS11_DEFAULT_PROVIDER
Petr Šabata 81d24c
+		provider_module = strdup(PKCS11_DEFAULT_PROVIDER);
Petr Šabata 81d24c
+#else
Dmitry Belyavskiy 9dff9c
+		error_f("No module path provided");
Dmitry Belyavskiy 9dff9c
 		goto fail;
Dmitry Belyavskiy 9dff9c
-	*providerp = NULL;
Petr Šabata 81d24c
+#endif
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		provider_module = strdup(uri->module_path);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
 
Dmitry Belyavskiy 9dff9c
-	if (keyp != NULL)
Dmitry Belyavskiy 9dff9c
-		*keyp = NULL;
Dmitry Belyavskiy 9dff9c
-	if (labelsp != NULL)
Dmitry Belyavskiy 9dff9c
-		*labelsp = NULL;
Dmitry Belyavskiy 9dff9c
+	p = xcalloc(1, sizeof(*p));
Dmitry Belyavskiy 9dff9c
+	p->name = pkcs11_uri_get(uri);
Dmitry Belyavskiy 9dff9c
 
Petr Šabata 81d24c
-	if (pkcs11_provider_lookup(provider_id) != NULL) {
DistroBaker d029bb
-		debug_f("provider already registered: %s", provider_id);
Petr Šabata 81d24c
-		goto fail;
Petr Šabata 81d24c
+	if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL
Petr Šabata 81d24c
+	   && m->valid) {
DistroBaker d029bb
+		debug_f("provider module already initialized: %s", provider_module);
Petr Šabata 81d24c
+		free(provider_module);
Petr Šabata 81d24c
+		/* Skip the initialization of PKCS#11 module */
Petr Šabata 81d24c
+		m->refcount++;
Petr Šabata 81d24c
+		p->module = m;
Petr Šabata 81d24c
+		p->valid = 1;
Petr Šabata 81d24c
+		TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
Petr Šabata 81d24c
+		p->refcount++;	/* add to provider list */
Petr Šabata 81d24c
+		*providerp = p;
Petr Šabata 81d24c
+		return 0;
Petr Šabata 81d24c
+	} else {
Petr Šabata 81d24c
+		m = xcalloc(1, sizeof(*m));
Petr Šabata 81d24c
+		p->module = m;
Petr Šabata 81d24c
+		m->refcount++;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
 	/* open shared pkcs11-library */
Petr Šabata 81d24c
-	if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
Petr Šabata 81d24c
-		error("dlopen %s failed: %s", provider_id, dlerror());
Petr Šabata 81d24c
+	if ((handle = dlopen(provider_module, RTLD_NOW)) == NULL) {
Petr Šabata 81d24c
+		error("dlopen %s failed: %s", provider_module, dlerror());
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
Petr Šabata 81d24c
 		error("dlsym(C_GetFunctionList) failed: %s", dlerror());
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	p = xcalloc(1, sizeof(*p));
Petr Šabata 81d24c
-	p->name = xstrdup(provider_id);
Petr Šabata 81d24c
-	p->handle = handle;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	p->module->handle = handle;
Petr Šabata 81d24c
 	/* setup the pkcs11 callbacks */
Petr Šabata 81d24c
 	if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetFunctionList for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	p->function_list = f;
Petr Šabata 81d24c
+	m->function_list = f;
Petr Šabata 81d24c
 	if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_Initialize for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
 	need_finalize = 1;
Petr Šabata 81d24c
-	if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
Petr Šabata 81d24c
+	if ((rv = f->C_GetInfo(&m->info)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetInfo for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
Petr Šabata 81d24c
-	rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
Petr Šabata 81d24c
+	rmspace(m->info.manufacturerID, sizeof(m->info.manufacturerID));
Petr Šabata 81d24c
+	if (uri->lib_manuf != NULL &&
Petr Šabata 81d24c
+	    strcmp(uri->lib_manuf, m->info.manufacturerID)) {
DistroBaker d029bb
+		debug_f("Skipping provider %s not matching library_manufacturer",
DistroBaker d029bb
+		    m->info.manufacturerID);
Petr Šabata 81d24c
+		goto fail;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	rmspace(m->info.libraryDescription, sizeof(m->info.libraryDescription));
Petr Šabata 81d24c
 	debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d"
Petr Šabata 81d24c
 	    " libraryDescription <%s> libraryVersion %d.%d",
Petr Šabata 81d24c
-	    provider_id,
Petr Šabata 81d24c
-	    p->info.manufacturerID,
Petr Šabata 81d24c
-	    p->info.cryptokiVersion.major,
Petr Šabata 81d24c
-	    p->info.cryptokiVersion.minor,
Petr Šabata 81d24c
-	    p->info.libraryDescription,
Petr Šabata 81d24c
-	    p->info.libraryVersion.major,
Petr Šabata 81d24c
-	    p->info.libraryVersion.minor);
Petr Šabata 81d24c
-	if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
Petr Šabata 81d24c
+	    provider_module,
Petr Šabata 81d24c
+	    m->info.manufacturerID,
Petr Šabata 81d24c
+	    m->info.cryptokiVersion.major,
Petr Šabata 81d24c
+	    m->info.cryptokiVersion.minor,
Petr Šabata 81d24c
+	    m->info.libraryDescription,
Petr Šabata 81d24c
+	    m->info.libraryVersion.major,
Petr Šabata 81d24c
+	    m->info.libraryVersion.minor);
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &m->nslots)) != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetSlotList failed: %lu", rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if (p->nslots == 0) {
DistroBaker d029bb
-		debug_f("provider %s returned no slots", provider_id);
Dmitry Belyavskiy 9dff9c
+	if (m->nslots == 0) {
DistroBaker d029bb
+		debug_f("provider %s returned no slots", provider_module);
Petr Šabata 81d24c
 		ret = -SSH_PKCS11_ERR_NO_SLOTS;
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
Petr Šabata 81d24c
-	if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots))
Petr Šabata 81d24c
+	m->slotlist = xcalloc(m->nslots, sizeof(CK_SLOT_ID));
Petr Šabata 81d24c
+	if ((rv = f->C_GetSlotList(CK_TRUE, m->slotlist, &m->nslots))
Petr Šabata 81d24c
 	    != CKR_OK) {
Petr Šabata 81d24c
 		error("C_GetSlotList for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
 		goto fail;
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
Petr Šabata 81d24c
 	p->valid = 1;
Petr Šabata 81d24c
-	nkeys = 0;
Petr Šabata 81d24c
-	for (i = 0; i < p->nslots; i++) {
Petr Šabata 81d24c
-		token = &p->slotinfo[i].token;
Petr Šabata 81d24c
-		if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
Petr Šabata 81d24c
+	m->slotinfo = xcalloc(m->nslots, sizeof(struct pkcs11_slotinfo));
Petr Šabata 81d24c
+	m->valid = 1;
Petr Šabata 81d24c
+	for (i = 0; i < m->nslots; i++) {
Petr Šabata 81d24c
+		token = &m->slotinfo[i].token;
Petr Šabata 81d24c
+		if ((rv = f->C_GetTokenInfo(m->slotlist[i], token))
Petr Šabata 81d24c
 		    != CKR_OK) {
Petr Šabata 81d24c
 			error("C_GetTokenInfo for provider %s slot %lu "
DistroBaker d029bb
-			    "failed: %lu", provider_id, (u_long)i, rv);
Dmitry Belyavskiy 9dff9c
-			continue;
Dmitry Belyavskiy 9dff9c
-		}
Dmitry Belyavskiy 9dff9c
-		if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
Dmitry Belyavskiy 9dff9c
-			debug2_f("ignoring uninitialised token in "
Dmitry Belyavskiy 9dff9c
-			    "provider %s slot %lu", provider_id, (u_long)i);
DistroBaker d029bb
+			    "failed: %lu", provider_module, (u_long)i, rv);
Petr Šabata 81d24c
+			token->flags = 0;
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
 		}
Dmitry Belyavskiy 9dff9c
 		rmspace(token->label, sizeof(token->label));
Dmitry Belyavskiy 9dff9c
 		rmspace(token->manufacturerID, sizeof(token->manufacturerID));
Dmitry Belyavskiy 9dff9c
 		rmspace(token->model, sizeof(token->model));
Dmitry Belyavskiy 9dff9c
 		rmspace(token->serialNumber, sizeof(token->serialNumber));
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	m->module_path = provider_module;
Petr Šabata 81d24c
+	provider_module = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	/* insert unconditionally -- remove if there will be no keys later */
Petr Šabata 81d24c
+	TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
Petr Šabata 81d24c
+	p->refcount++;	/* add to provider list */
Petr Šabata 81d24c
+	*providerp = p;
Petr Šabata 81d24c
+	return 0;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+fail:
Petr Šabata 81d24c
+	if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
Petr Šabata 81d24c
+		error("C_Finalize for provider %s failed: %lu",
Petr Šabata 81d24c
+		    provider_module, rv);
Petr Šabata 81d24c
+	free(provider_module);
Petr Šabata 81d24c
+	if (m) {
Petr Šabata 81d24c
+		free(m->slotlist);
Petr Šabata 81d24c
+		free(m);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (p) {
Petr Šabata 81d24c
+		free(p->name);
Petr Šabata 81d24c
+		free(p);
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	if (handle)
Petr Šabata 81d24c
+		dlclose(handle);
Petr Šabata 81d24c
+	return ret;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+/*
Petr Šabata 81d24c
+ * register a new provider, fails if provider already exists. if
Petr Šabata 81d24c
+ * keyp is provided, fetch keys.
Petr Šabata 81d24c
+ */
Petr Šabata 81d24c
+static int
Petr Šabata 81d24c
+pkcs11_register_provider_by_uri(struct pkcs11_uri *uri, char *pin,
Petr Šabata 81d24c
+    struct sshkey ***keyp, char ***labelsp, struct pkcs11_provider **providerp,
Petr Šabata 81d24c
+    CK_ULONG user)
Petr Šabata 81d24c
+{
Petr Šabata 81d24c
+	int nkeys;
Petr Šabata 81d24c
+	int ret = -1;
Petr Šabata 81d24c
+	struct pkcs11_provider *p = NULL;
Petr Šabata 81d24c
+	CK_ULONG i;
Petr Šabata 81d24c
+	CK_TOKEN_INFO *token;
Petr Šabata 81d24c
+	char *provider_uri = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (providerp == NULL)
Petr Šabata 81d24c
+		goto fail;
Petr Šabata 81d24c
+	*providerp = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if (keyp != NULL)
Petr Šabata 81d24c
+		*keyp = NULL;
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	if ((ret = pkcs11_initialize_provider(uri, &p)) != 0) {
Petr Šabata 81d24c
+		goto fail;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+	provider_uri = pkcs11_uri_get(uri);
Petr Šabata 81d24c
+	if (pin == NULL && uri->pin != NULL) {
Petr Šabata 81d24c
+		pin = uri->pin;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+	nkeys = 0;
Petr Šabata 81d24c
+	for (i = 0; i < p->module->nslots; i++) {
Petr Šabata 81d24c
+		token = &p->module->slotinfo[i].token;
Dmitry Belyavskiy 9dff9c
+		if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
Dmitry Belyavskiy 9dff9c
+			debug2_f("ignoring uninitialised token in "
DistroBaker d029bb
+			    "provider %s slot %lu", provider_uri, (u_long)i);
Petr Šabata 81d24c
+			continue;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		if (uri->token != NULL &&
Petr Šabata 81d24c
+		    strcmp(token->label, uri->token) != 0) {
DistroBaker d029bb
+			debug2_f("ignoring token not matching label (%s) "
DistroBaker d029bb
+			    "specified by PKCS#11 URI in slot %lu",
Petr Šabata 81d24c
+			    token->label, (unsigned long)i);
Petr Šabata 81d24c
+			continue;
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		if (uri->manuf != NULL &&
Petr Šabata 81d24c
+		    strcmp(token->manufacturerID, uri->manuf) != 0) {
DistroBaker d029bb
+			debug2_f("ignoring token not matching requrested "
Petr Šabata 81d24c
+			    "manufacturerID (%s) specified by PKCS#11 URI in "
DistroBaker d029bb
+			    "slot %lu", token->manufacturerID, (unsigned long)i);
Dmitry Belyavskiy 9dff9c
+			continue;
Dmitry Belyavskiy 9dff9c
+		}
Petr Šabata 81d24c
 		debug("provider %s slot %lu: label <%s> manufacturerID <%s> "
Petr Šabata 81d24c
 		    "model <%s> serial <%s> flags 0x%lx",
Petr Šabata 81d24c
-		    provider_id, (unsigned long)i,
Petr Šabata 81d24c
+		    provider_uri, (unsigned long)i,
Petr Šabata 81d24c
 		    token->label, token->manufacturerID, token->model,
Petr Šabata 81d24c
 		    token->serialNumber, token->flags);
Petr Šabata 81d24c
 		/*
Petr Šabata 81d24c
-		 * open session, login with pin and retrieve public
Petr Šabata 81d24c
-		 * keys (if keyp is provided)
Petr Šabata 81d24c
+		 * open session if not yet openend, login with pin and
Petr Šabata 81d24c
+		 * retrieve public keys (if keyp is provided)
Petr Šabata 81d24c
 		 */
Petr Šabata 81d24c
-		if ((ret = pkcs11_open_session(p, i, pin, user)) != 0 ||
Petr Šabata 81d24c
+		if ((p->module->slotinfo[i].session != 0 ||
Petr Šabata 81d24c
+		    (ret = pkcs11_open_session(p, i, pin, user)) != 0) && /* ??? */
Petr Šabata 81d24c
 		    keyp == NULL)
Petr Šabata 81d24c
 			continue;
Petr Šabata 81d24c
-		pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
Petr Šabata 81d24c
-		pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
Petr Šabata 81d24c
-		if (nkeys == 0 && !p->slotinfo[i].logged_in &&
Petr Šabata 81d24c
+		pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+		pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+		if (nkeys == 0 && !p->module->slotinfo[i].logged_in &&
Petr Šabata 81d24c
 		    pkcs11_interactive) {
Petr Šabata 81d24c
 			/*
Petr Šabata 81d24c
 			 * Some tokens require login before they will
Petr Šabata 81d24c
 			 * expose keys.
Petr Šabata 81d24c
 			 */
Petr Šabata 81d24c
-			if (pkcs11_login_slot(p, &p->slotinfo[i],
DistroBaker d029bb
+			debug3_f("Trying to login as there were no keys found");
Petr Šabata 81d24c
+			if (pkcs11_login_slot(p, &p->module->slotinfo[i],
Petr Šabata 81d24c
 			    CKU_USER) < 0) {
Petr Šabata 81d24c
 				error("login failed");
Petr Šabata 81d24c
 				continue;
Petr Šabata 81d24c
 			}
Petr Šabata 81d24c
-			pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
Petr Šabata 81d24c
-			pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
Petr Šabata 81d24c
+			pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+			pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+		if (nkeys == 0 && uri->object != NULL) {
DistroBaker d029bb
+			debug3_f("No keys found. Retrying without label (%s) ",
DistroBaker d029bb
+			    uri->object);
Petr Šabata 81d24c
+			/* Try once more without the label filter */
Petr Šabata 81d24c
+			char *label = uri->object;
Petr Šabata 81d24c
+			uri->object = NULL; /* XXX clone uri? */
Petr Šabata 81d24c
+			pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+			pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys, uri);
Petr Šabata 81d24c
+			uri->object = label;
Petr Šabata 81d24c
 		}
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
+	pin = NULL; /* Will be cleaned up with URI */
Petr Šabata 81d24c
 
Petr Šabata 81d24c
 	/* now owned by caller */
Petr Šabata 81d24c
 	*providerp = p;
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-	TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
Petr Šabata 81d24c
-	p->refcount++;	/* add to provider list */
Petr Šabata 81d24c
-
Petr Šabata 81d24c
+	free(provider_uri);
Petr Šabata 81d24c
 	return (nkeys);
Petr Šabata 81d24c
 fail:
Petr Šabata 81d24c
-	if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
Petr Šabata 81d24c
-		error("C_Finalize for provider %s failed: %lu",
Petr Šabata 81d24c
-		    provider_id, rv);
Petr Šabata 81d24c
 	if (p) {
Petr Šabata 81d24c
-		free(p->name);
Petr Šabata 81d24c
-		free(p->slotlist);
Petr Šabata 81d24c
-		free(p->slotinfo);
Petr Šabata 81d24c
-		free(p);
Petr Šabata 81d24c
+ 		TAILQ_REMOVE(&pkcs11_providers, p, next);
Petr Šabata 81d24c
+		pkcs11_provider_unref(p);
Petr Šabata 81d24c
 	}
Petr Šabata 81d24c
-	if (handle)
Petr Šabata 81d24c
-		dlclose(handle);
Petr Šabata 81d24c
 	if (ret > 0)
Petr Šabata 81d24c
 		ret = -1;
Petr Šabata 81d24c
 	return (ret);
Petr Šabata 81d24c
 }
Petr Šabata 81d24c
 
Petr Šabata 81d24c
-/*
Petr Šabata 81d24c
- * register a new provider and get number of keys hold by the token,
Petr Šabata 81d24c
- * fails if provider already exists
Petr Šabata 81d24c
- */
Petr Šabata 81d24c
+static int
Petr Šabata 81d24c
+pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
Petr Šabata 81d24c
+    char ***labelsp, struct pkcs11_provider **providerp, CK_ULONG user)
Petr Šabata 81d24c
+{
Dmitry Belyavskiy 9dff9c
+	struct pkcs11_uri *uri = NULL;
Dmitry Belyavskiy 9dff9c
+	int r;
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	debug_f("called, provider_id = %s", provider_id);
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	uri = pkcs11_uri_init();
Dmitry Belyavskiy 9dff9c
+	if (uri == NULL)
Dmitry Belyavskiy 9dff9c
+		fatal("failed to init PKCS#11 URI");
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) &&
Dmitry Belyavskiy 9dff9c
+	    strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) {
Dmitry Belyavskiy 9dff9c
+		if (pkcs11_uri_parse(provider_id, uri) != 0)
Dmitry Belyavskiy 9dff9c
+			fatal("Failed to parse PKCS#11 URI");
Dmitry Belyavskiy 9dff9c
+	} else {
Dmitry Belyavskiy 9dff9c
+		uri->module_path = strdup(provider_id);
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	r = pkcs11_register_provider_by_uri(uri, pin, keyp, labelsp, providerp, user);
Dmitry Belyavskiy 9dff9c
+	pkcs11_uri_cleanup(uri);
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	return r;
Dmitry Belyavskiy 9dff9c
+}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
 int
Dmitry Belyavskiy 9dff9c
-pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp,
Dmitry Belyavskiy 9dff9c
-    char ***labelsp)
Dmitry Belyavskiy 9dff9c
+pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin,
Dmitry Belyavskiy 9dff9c
+    struct sshkey ***keyp, char ***labelsp)
Dmitry Belyavskiy 9dff9c
 {
Dmitry Belyavskiy 9dff9c
 	struct pkcs11_provider *p = NULL;
Dmitry Belyavskiy 9dff9c
 	int nkeys;
Dmitry Belyavskiy 9dff9c
+	char *provider_uri = pkcs11_uri_get(uri);
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	debug_f("called, provider_uri = %s", provider_uri);
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
-	nkeys = pkcs11_register_provider(provider_id, pin, keyp, labelsp,
Dmitry Belyavskiy 9dff9c
-	    &p, CKU_USER);
Dmitry Belyavskiy 9dff9c
+	nkeys = pkcs11_register_provider_by_uri(uri, pin, keyp, labelsp, &p, CKU_USER);
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
 	/* no keys found or some other error, de-register provider */
Dmitry Belyavskiy 9dff9c
 	if (nkeys <= 0 && p != NULL) {
Dmitry Belyavskiy 9dff9c
@@ -1683,7 +1999,37 @@ pkcs11_add_provider(char *provider_id, c
Dmitry Belyavskiy 9dff9c
 		pkcs11_provider_unref(p);
Dmitry Belyavskiy 9dff9c
 	}
Dmitry Belyavskiy 9dff9c
 	if (nkeys == 0)
Dmitry Belyavskiy 9dff9c
-		debug_f("provider %s returned no keys", provider_id);
Dmitry Belyavskiy 9dff9c
+		debug_f("provider %s returned no keys", provider_uri);
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	free(provider_uri);
Dmitry Belyavskiy 9dff9c
+	return nkeys;
Dmitry Belyavskiy 9dff9c
+}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+/*
Dmitry Belyavskiy 9dff9c
+ * register a new provider and get number of keys hold by the token,
Dmitry Belyavskiy 9dff9c
+ * fails if provider already exists
Dmitry Belyavskiy 9dff9c
+ */
Dmitry Belyavskiy 9dff9c
+int
Dmitry Belyavskiy 9dff9c
+pkcs11_add_provider(char *provider_id, char *pin,
Dmitry Belyavskiy 9dff9c
+    struct sshkey ***keyp, char ***labelsp)
Dmitry Belyavskiy 9dff9c
+{
Dmitry Belyavskiy 9dff9c
+	struct pkcs11_uri *uri;
Dmitry Belyavskiy 9dff9c
+	int nkeys;
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	uri = pkcs11_uri_init();
Dmitry Belyavskiy 9dff9c
+	if (uri == NULL)
Dmitry Belyavskiy 9dff9c
+		fatal("Failed to init PKCS#11 URI");
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) &&
Dmitry Belyavskiy 9dff9c
+	    strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) {
Dmitry Belyavskiy 9dff9c
+		if (pkcs11_uri_parse(provider_id, uri) != 0)
Dmitry Belyavskiy 9dff9c
+			fatal("Failed to parse PKCS#11 URI");
Dmitry Belyavskiy 9dff9c
+	} else {
Dmitry Belyavskiy 9dff9c
+		uri->module_path = strdup(provider_id);
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	nkeys = pkcs11_add_provider_by_uri(uri, pin, keyp, labelsp);
Dmitry Belyavskiy 9dff9c
+	pkcs11_uri_cleanup(uri);
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
 	return (nkeys);
Dmitry Belyavskiy 9dff9c
 }
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh-pkcs11.h.pkcs11-uri openssh-8.6p1/ssh-pkcs11.h
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh-pkcs11.h.pkcs11-uri	2021-04-16 05:55:25.000000000 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh-pkcs11.h	2021-05-06 11:35:55.115653297 +0200
Dmitry Belyavskiy 9dff9c
@@ -22,10 +22,14 @@
Dmitry Belyavskiy 9dff9c
 #define	SSH_PKCS11_ERR_PIN_REQUIRED		4
Dmitry Belyavskiy 9dff9c
 #define	SSH_PKCS11_ERR_PIN_LOCKED		5
Dmitry Belyavskiy 9dff9c
 
Dmitry Belyavskiy 9dff9c
+#include "ssh-pkcs11-uri.h"
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
 int	pkcs11_init(int);
Dmitry Belyavskiy 9dff9c
 void	pkcs11_terminate(void);
Dmitry Belyavskiy 9dff9c
 int	pkcs11_add_provider(char *, char *, struct sshkey ***, char ***);
Dmitry Belyavskiy 9dff9c
+int	pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***, char ***);
Dmitry Belyavskiy 9dff9c
 int	pkcs11_del_provider(char *);
Dmitry Belyavskiy 9dff9c
+int	pkcs11_uri_write(const struct sshkey *, FILE *);
Dmitry Belyavskiy 9dff9c
 #ifdef WITH_PKCS11_KEYGEN
Dmitry Belyavskiy 9dff9c
 struct sshkey *
Dmitry Belyavskiy 9dff9c
 	pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int,
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh-pkcs11-uri.c.pkcs11-uri openssh-8.6p1/ssh-pkcs11-uri.c
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh-pkcs11-uri.c.pkcs11-uri	2021-05-06 11:35:55.114653289 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh-pkcs11-uri.c	2021-05-06 11:35:55.114653289 +0200
Dmitry Belyavskiy 9dff9c
@@ -0,0 +1,419 @@
Dmitry Belyavskiy 9dff9c
+/*
Dmitry Belyavskiy 9dff9c
+ * Copyright (c) 2017 Red Hat
Dmitry Belyavskiy 9dff9c
+ *
Dmitry Belyavskiy 9dff9c
+ * Authors: Jakub Jelen <jjelen@redhat.com>
Dmitry Belyavskiy 9dff9c
+ *
Dmitry Belyavskiy 9dff9c
+ * Permission to use, copy, modify, and distribute this software for any
Dmitry Belyavskiy 9dff9c
+ * purpose with or without fee is hereby granted, provided that the above
Dmitry Belyavskiy 9dff9c
+ * copyright notice and this permission notice appear in all copies.
Dmitry Belyavskiy 9dff9c
+ *
Dmitry Belyavskiy 9dff9c
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Dmitry Belyavskiy 9dff9c
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Dmitry Belyavskiy 9dff9c
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Dmitry Belyavskiy 9dff9c
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Dmitry Belyavskiy 9dff9c
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Dmitry Belyavskiy 9dff9c
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Dmitry Belyavskiy 9dff9c
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Dmitry Belyavskiy 9dff9c
+ */
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+#include "includes.h"
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+#ifdef ENABLE_PKCS11
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+#include <stdio.h>
Dmitry Belyavskiy 9dff9c
+#include <string.h>
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+#include "sshkey.h"
Dmitry Belyavskiy 9dff9c
+#include "sshbuf.h"
Dmitry Belyavskiy 9dff9c
+#include "log.h"
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+#define CRYPTOKI_COMPAT
Dmitry Belyavskiy 9dff9c
+#include "pkcs11.h"
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+#include "ssh-pkcs11-uri.h"
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_PATH_SEPARATOR ";"
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_QUERY_SEPARATOR "&"
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_VALUE_SEPARATOR "="
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_ID "id"
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_TOKEN "token"
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_OBJECT "object"
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_LIB_MANUF "library-manufacturer"
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_MANUF "manufacturer"
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_MODULE_PATH "module-path"
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_PIN_VALUE "pin-value"
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+/* Keyword tokens. */
Dmitry Belyavskiy 9dff9c
+typedef enum {
Dmitry Belyavskiy 9dff9c
+	pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath,
Dmitry Belyavskiy 9dff9c
+	pPinValue, pBadOption
Dmitry Belyavskiy 9dff9c
+} pkcs11uriOpCodes;
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+/* Textual representation of the tokens. */
Dmitry Belyavskiy 9dff9c
+static struct {
Dmitry Belyavskiy 9dff9c
+	const char *name;
Dmitry Belyavskiy 9dff9c
+	pkcs11uriOpCodes opcode;
Dmitry Belyavskiy 9dff9c
+} keywords[] = {
Dmitry Belyavskiy 9dff9c
+	{ PKCS11_URI_ID, pId },
Dmitry Belyavskiy 9dff9c
+	{ PKCS11_URI_TOKEN, pToken },
Dmitry Belyavskiy 9dff9c
+	{ PKCS11_URI_OBJECT, pObject },
Dmitry Belyavskiy 9dff9c
+	{ PKCS11_URI_LIB_MANUF, pLibraryManufacturer },
Dmitry Belyavskiy 9dff9c
+	{ PKCS11_URI_MANUF, pManufacturer },
Dmitry Belyavskiy 9dff9c
+	{ PKCS11_URI_MODULE_PATH, pModulePath },
Dmitry Belyavskiy 9dff9c
+	{ PKCS11_URI_PIN_VALUE, pPinValue },
Dmitry Belyavskiy 9dff9c
+	{ NULL, pBadOption }
Dmitry Belyavskiy 9dff9c
+};
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+static pkcs11uriOpCodes
Dmitry Belyavskiy 9dff9c
+parse_token(const char *cp)
Dmitry Belyavskiy 9dff9c
+{
Dmitry Belyavskiy 9dff9c
+	u_int i;
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	for (i = 0; keywords[i].name; i++)
Dmitry Belyavskiy 9dff9c
+		if (strncasecmp(cp, keywords[i].name,
Dmitry Belyavskiy 9dff9c
+		    strlen(keywords[i].name)) == 0)
Dmitry Belyavskiy 9dff9c
+			return keywords[i].opcode;
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	return pBadOption;
Dmitry Belyavskiy 9dff9c
+}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+int
Dmitry Belyavskiy 9dff9c
+percent_decode(char *data, char **outp)
Dmitry Belyavskiy 9dff9c
+{
Dmitry Belyavskiy 9dff9c
+	char tmp[3];
Dmitry Belyavskiy 9dff9c
+	char *out, *tmp_end;
Dmitry Belyavskiy 9dff9c
+	char *p = data;
Dmitry Belyavskiy 9dff9c
+	long value;
Dmitry Belyavskiy 9dff9c
+	size_t outlen = 0;
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	out = malloc(strlen(data)+1); /* upper bound */
Dmitry Belyavskiy 9dff9c
+	if (out == NULL)
Dmitry Belyavskiy 9dff9c
+		return -1;
Dmitry Belyavskiy 9dff9c
+	while (*p != '\0') {
Dmitry Belyavskiy 9dff9c
+		switch (*p) {
Dmitry Belyavskiy 9dff9c
+		case '%':
Dmitry Belyavskiy 9dff9c
+			p++;
Dmitry Belyavskiy 9dff9c
+			if (*p == '\0')
Dmitry Belyavskiy 9dff9c
+				goto fail;
Dmitry Belyavskiy 9dff9c
+			tmp[0] = *p++;
Dmitry Belyavskiy 9dff9c
+			if (*p == '\0')
Dmitry Belyavskiy 9dff9c
+				goto fail;
Dmitry Belyavskiy 9dff9c
+			tmp[1] = *p++;
Dmitry Belyavskiy 9dff9c
+			tmp[2] = '\0';
Dmitry Belyavskiy 9dff9c
+			tmp_end = NULL;
Dmitry Belyavskiy 9dff9c
+			value = strtol(tmp, &tmp_end, 16);
Dmitry Belyavskiy 9dff9c
+			if (tmp_end != tmp+2)
Dmitry Belyavskiy 9dff9c
+				goto fail;
Dmitry Belyavskiy 9dff9c
+			else
Dmitry Belyavskiy 9dff9c
+				out[outlen++] = (char) value;
Dmitry Belyavskiy 9dff9c
+			break;
Dmitry Belyavskiy 9dff9c
+		default:
Dmitry Belyavskiy 9dff9c
+			out[outlen++] = *p++;
Dmitry Belyavskiy 9dff9c
+			break;
Dmitry Belyavskiy 9dff9c
+		}
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	/* zero terminate */
Dmitry Belyavskiy 9dff9c
+	out[outlen] = '\0';
Dmitry Belyavskiy 9dff9c
+	*outp = out;
Dmitry Belyavskiy 9dff9c
+	return outlen;
Dmitry Belyavskiy 9dff9c
+fail:
Dmitry Belyavskiy 9dff9c
+	free(out);
Dmitry Belyavskiy 9dff9c
+	return -1;
Dmitry Belyavskiy 9dff9c
+}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+struct sshbuf *
Dmitry Belyavskiy 9dff9c
+percent_encode(const char *data, size_t length, const char *allow_list)
Dmitry Belyavskiy 9dff9c
+{
Dmitry Belyavskiy 9dff9c
+	struct sshbuf *b = NULL;
Dmitry Belyavskiy 9dff9c
+	char tmp[4], *cp;
Dmitry Belyavskiy 9dff9c
+	size_t i;
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	if ((b = sshbuf_new()) == NULL)
Dmitry Belyavskiy 9dff9c
+		return NULL;
Dmitry Belyavskiy 9dff9c
+	for (i = 0; i < length; i++) {
Dmitry Belyavskiy 9dff9c
+		cp = strchr(allow_list, data[i]);
Dmitry Belyavskiy 9dff9c
+		/* if c is specified as '\0' pointer to terminator is returned !! */
Dmitry Belyavskiy 9dff9c
+		if (cp != NULL && *cp != '\0') {
Dmitry Belyavskiy 9dff9c
+			if (sshbuf_put(b, &data[i], 1) != 0)
Dmitry Belyavskiy 9dff9c
+				goto err;
Dmitry Belyavskiy 9dff9c
+		} else
Dmitry Belyavskiy 9dff9c
+			if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3
Dmitry Belyavskiy 9dff9c
+			    || sshbuf_put(b, tmp, 3) != 0)
Dmitry Belyavskiy 9dff9c
+				goto err;
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+	if (sshbuf_put(b, "\0", 1) == 0)
Dmitry Belyavskiy 9dff9c
+		return b;
Dmitry Belyavskiy 9dff9c
+err:
Dmitry Belyavskiy 9dff9c
+	sshbuf_free(b);
Dmitry Belyavskiy 9dff9c
+	return NULL;
Dmitry Belyavskiy 9dff9c
+}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+char *
Dmitry Belyavskiy 9dff9c
+pkcs11_uri_append(char *part, const char *separator, const char *key,
Dmitry Belyavskiy 9dff9c
+    struct sshbuf *value)
Dmitry Belyavskiy 9dff9c
+{
Dmitry Belyavskiy 9dff9c
+	char *new_part;
Dmitry Belyavskiy 9dff9c
+	size_t size = 0;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	if (value == NULL)
Dmitry Belyavskiy 9dff9c
+		return NULL;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	size = asprintf(&new_part,
Dmitry Belyavskiy 9dff9c
+	    "%s%s%s"  PKCS11_URI_VALUE_SEPARATOR "%s",
Dmitry Belyavskiy 9dff9c
+	    (part != NULL ? part : ""),
Dmitry Belyavskiy 9dff9c
+	    (part != NULL ? separator : ""),
Dmitry Belyavskiy 9dff9c
+	    key, sshbuf_ptr(value));
Dmitry Belyavskiy 9dff9c
+	sshbuf_free(value);
Dmitry Belyavskiy 9dff9c
+	free(part);
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	if (size <= 0)
Dmitry Belyavskiy 9dff9c
+		return NULL;
Dmitry Belyavskiy 9dff9c
+	return new_part;
Dmitry Belyavskiy 9dff9c
+}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+char *
Dmitry Belyavskiy 9dff9c
+pkcs11_uri_get(struct pkcs11_uri *uri)
Dmitry Belyavskiy 9dff9c
+{
Dmitry Belyavskiy 9dff9c
+	size_t size = 0;
Dmitry Belyavskiy 9dff9c
+	char *p = NULL, *path = NULL, *query = NULL;
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	/* compose a percent-encoded ID */
Dmitry Belyavskiy 9dff9c
+	if (uri->id_len > 0) {
Dmitry Belyavskiy 9dff9c
+		struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, "");
Dmitry Belyavskiy 9dff9c
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Dmitry Belyavskiy 9dff9c
+		    PKCS11_URI_ID, key_id);
Dmitry Belyavskiy 9dff9c
+		if (path == NULL)
Dmitry Belyavskiy 9dff9c
+			goto err;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	/* Write object label */
Dmitry Belyavskiy 9dff9c
+	if (uri->object) {
Dmitry Belyavskiy 9dff9c
+		struct sshbuf *label = percent_encode(uri->object, strlen(uri->object),
Dmitry Belyavskiy 9dff9c
+		    PKCS11_URI_WHITELIST);
Dmitry Belyavskiy 9dff9c
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Dmitry Belyavskiy 9dff9c
+		    PKCS11_URI_OBJECT, label);
Dmitry Belyavskiy 9dff9c
+		if (path == NULL)
Dmitry Belyavskiy 9dff9c
+			goto err;
Dmitry Belyavskiy 9dff9c
+	}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	/* Write token label */
Dmitry Belyavskiy 9dff9c
+	if (uri->token) {
Dmitry Belyavskiy 9dff9c
+		struct sshbuf *label = percent_encode(uri->token, strlen(uri->token),
Dmitry Belyavskiy 9dff9c
+		    PKCS11_URI_WHITELIST);
Dmitry Belyavskiy 9dff9c
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Dmitry Belyavskiy 9dff9c
+		    PKCS11_URI_TOKEN, label);
Dmitry Belyavskiy 9dff9c
+		if (path == NULL)
Dmitry Belyavskiy 9dff9c
+			goto err;
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	/* Write manufacturer */
Dmitry Belyavskiy 9dff9c
+	if (uri->manuf) {
Dmitry Belyavskiy 9dff9c
+		struct sshbuf *manuf = percent_encode(uri->manuf,
Dmitry Belyavskiy 9dff9c
+		    strlen(uri->manuf), PKCS11_URI_WHITELIST);
Dmitry Belyavskiy 9dff9c
+		path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
Dmitry Belyavskiy 9dff9c
+		    PKCS11_URI_MANUF, manuf);
Dmitry Belyavskiy 9dff9c
+		if (path == NULL)
Dmitry Belyavskiy 9dff9c
+			goto err;
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	/* Write module_path */
Dmitry Belyavskiy 9dff9c
+	if (uri->module_path) {
Dmitry Belyavskiy 9dff9c
+		struct sshbuf *module = percent_encode(uri->module_path,
Dmitry Belyavskiy 9dff9c
+		    strlen(uri->module_path), PKCS11_URI_WHITELIST "/");
Dmitry Belyavskiy 9dff9c
+		query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR,
Dmitry Belyavskiy 9dff9c
+		    PKCS11_URI_MODULE_PATH, module);
Dmitry Belyavskiy 9dff9c
+		if (query == NULL)
Dmitry Belyavskiy 9dff9c
+			goto err;
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s",
Dmitry Belyavskiy 9dff9c
+	    path != NULL ? path : "",
Dmitry Belyavskiy 9dff9c
+	    query != NULL ? "?" : "",
Dmitry Belyavskiy 9dff9c
+	    query != NULL ? query : "");
Dmitry Belyavskiy 9dff9c
+err:
Dmitry Belyavskiy 9dff9c
+	free(query);
Dmitry Belyavskiy 9dff9c
+	free(path);
Dmitry Belyavskiy 9dff9c
+	if (size <= 0)
Dmitry Belyavskiy 9dff9c
+		return NULL;
Dmitry Belyavskiy 9dff9c
+	return p;
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+struct pkcs11_uri *
Dmitry Belyavskiy 9dff9c
+pkcs11_uri_init()
Dmitry Belyavskiy 9dff9c
+{
Dmitry Belyavskiy 9dff9c
+	struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri));
Dmitry Belyavskiy 9dff9c
+	return d;
Dmitry Belyavskiy 9dff9c
+}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+void
Dmitry Belyavskiy 9dff9c
+pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11)
Dmitry Belyavskiy 9dff9c
+{
Dmitry Belyavskiy 9dff9c
+	if (pkcs11 == NULL) {
Dmitry Belyavskiy 9dff9c
+		return;
Dmitry Belyavskiy 9dff9c
+	}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	free(pkcs11->id);
Dmitry Belyavskiy 9dff9c
+	free(pkcs11->module_path);
Dmitry Belyavskiy 9dff9c
+	free(pkcs11->token);
Dmitry Belyavskiy 9dff9c
+	free(pkcs11->object);
Dmitry Belyavskiy 9dff9c
+	free(pkcs11->lib_manuf);
Dmitry Belyavskiy 9dff9c
+	free(pkcs11->manuf);
Dmitry Belyavskiy 9dff9c
+	if (pkcs11->pin)
Dmitry Belyavskiy 9dff9c
+		freezero(pkcs11->pin, strlen(pkcs11->pin));
Dmitry Belyavskiy 9dff9c
+	free(pkcs11);
Petr Šabata 81d24c
+}
Petr Šabata 81d24c
+
Petr Šabata 81d24c
+int
Dmitry Belyavskiy 9dff9c
+pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11)
Petr Šabata 81d24c
+{
Dmitry Belyavskiy 9dff9c
+	char *saveptr1, *saveptr2, *str1, *str2, *tok;
Dmitry Belyavskiy 9dff9c
+	int rv = 0, len;
Dmitry Belyavskiy 9dff9c
+	char *p = NULL;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	size_t scheme_len = strlen(PKCS11_URI_SCHEME);
Dmitry Belyavskiy 9dff9c
+	if (strlen(uri) < scheme_len || /* empty URI matches everything */
Dmitry Belyavskiy 9dff9c
+	    strncmp(uri, PKCS11_URI_SCHEME, scheme_len) != 0) {
Dmitry Belyavskiy 9dff9c
+		error_f("The '%s' does not look like PKCS#11 URI", uri);
Dmitry Belyavskiy 9dff9c
+		return -1;
Dmitry Belyavskiy 9dff9c
+	}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	if (pkcs11 == NULL) {
Dmitry Belyavskiy 9dff9c
+		error_f("Bad arguments. The pkcs11 can't be null");
Dmitry Belyavskiy 9dff9c
+		return -1;
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	/* skip URI schema name */
Dmitry Belyavskiy 9dff9c
+	p = strdup(uri);
Dmitry Belyavskiy 9dff9c
+	str1 = p;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	/* everything before ? */
Dmitry Belyavskiy 9dff9c
+	tok = strtok_r(str1, "?", &saveptr1);
Dmitry Belyavskiy 9dff9c
+	if (tok == NULL) {
Dmitry Belyavskiy 9dff9c
+		error_f("pk11-path expected, got EOF");
Dmitry Belyavskiy 9dff9c
+		rv = -1;
Dmitry Belyavskiy 9dff9c
+		goto out;
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	/* skip URI schema name:
Dmitry Belyavskiy 9dff9c
+	 * the scheme ensures that there is at least something before "?"
Dmitry Belyavskiy 9dff9c
+	 * allowing empty pk11-path. Resulting token at worst pointing to
Dmitry Belyavskiy 9dff9c
+	 * \0 byte */
Dmitry Belyavskiy 9dff9c
+	tok = tok + scheme_len;
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+	/* parse pk11-path */
Dmitry Belyavskiy 9dff9c
+	for (str2 = tok; ; str2 = NULL) {
Dmitry Belyavskiy 9dff9c
+		char **charptr, *arg = NULL;
Dmitry Belyavskiy 9dff9c
+		pkcs11uriOpCodes opcode;
Dmitry Belyavskiy 9dff9c
+		tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2);
Dmitry Belyavskiy 9dff9c
+		if (tok == NULL)
Dmitry Belyavskiy 9dff9c
+			break;
Dmitry Belyavskiy 9dff9c
+		opcode = parse_token(tok);
Dmitry Belyavskiy 9dff9c
+		if (opcode != pBadOption)
Dmitry Belyavskiy 9dff9c
+			arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+		switch (opcode) {
Dmitry Belyavskiy 9dff9c
+		case pId:
Dmitry Belyavskiy 9dff9c
+			/* CKA_ID */
Dmitry Belyavskiy 9dff9c
+			if (pkcs11->id != NULL) {
Dmitry Belyavskiy 9dff9c
+				verbose_f("The id already set in the PKCS#11 URI");
Dmitry Belyavskiy 9dff9c
+				rv = -1;
Dmitry Belyavskiy 9dff9c
+				goto out;
Dmitry Belyavskiy 9dff9c
+			}
Dmitry Belyavskiy 9dff9c
+			len = percent_decode(arg, &pkcs11->id);
Dmitry Belyavskiy 9dff9c
+			if (len <= 0) {
Dmitry Belyavskiy 9dff9c
+				verbose_f("Failed to percent-decode CKA_ID: %s", arg);
Dmitry Belyavskiy 9dff9c
+				rv = -1;
Dmitry Belyavskiy 9dff9c
+				goto out;
Dmitry Belyavskiy 9dff9c
+			} else
Dmitry Belyavskiy 9dff9c
+				pkcs11->id_len = len;
Dmitry Belyavskiy 9dff9c
+			debug3_f("Setting CKA_ID = %s from PKCS#11 URI", arg);
Dmitry Belyavskiy 9dff9c
+			break;
Dmitry Belyavskiy 9dff9c
+		case pToken:
Dmitry Belyavskiy 9dff9c
+			/* CK_TOKEN_INFO -> label */
Dmitry Belyavskiy 9dff9c
+			charptr = &pkcs11->token;
Dmitry Belyavskiy 9dff9c
+ parse_string:
Dmitry Belyavskiy 9dff9c
+			if (*charptr != NULL) {
Dmitry Belyavskiy 9dff9c
+				verbose_f("The %s already set in the PKCS#11 URI",
Dmitry Belyavskiy 9dff9c
+				    keywords[opcode].name);
Dmitry Belyavskiy 9dff9c
+				rv = -1;
Dmitry Belyavskiy 9dff9c
+				goto out;
Petr Šabata 81d24c
+			}
Dmitry Belyavskiy 9dff9c
+			percent_decode(arg, charptr);
Dmitry Belyavskiy 9dff9c
+			debug3_f("Setting %s = %s from PKCS#11 URI",
Dmitry Belyavskiy 9dff9c
+			    keywords[opcode].name, *charptr);
Dmitry Belyavskiy 9dff9c
+			break;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		case pObject:
Dmitry Belyavskiy 9dff9c
+			/* CK_TOKEN_INFO -> manufacturerID */
Dmitry Belyavskiy 9dff9c
+			charptr = &pkcs11->object;
Dmitry Belyavskiy 9dff9c
+			goto parse_string;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		case pManufacturer:
Dmitry Belyavskiy 9dff9c
+			/* CK_TOKEN_INFO -> manufacturerID */
Dmitry Belyavskiy 9dff9c
+			charptr = &pkcs11->manuf;
Dmitry Belyavskiy 9dff9c
+			goto parse_string;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		case pLibraryManufacturer:
Dmitry Belyavskiy 9dff9c
+			/* CK_INFO -> manufacturerID */
Dmitry Belyavskiy 9dff9c
+			charptr = &pkcs11->lib_manuf;
Dmitry Belyavskiy 9dff9c
+			goto parse_string;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		default:
Dmitry Belyavskiy 9dff9c
+			/* Unrecognized attribute in the URI path SHOULD be error */
Dmitry Belyavskiy 9dff9c
+			verbose_f("Unknown part of path in PKCS#11 URI: %s", tok);
Petr Šabata 81d24c
+		}
Petr Šabata 81d24c
+	}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+	tok = strtok_r(NULL, "?", &saveptr1);
Dmitry Belyavskiy 9dff9c
+	if (tok == NULL) {
Dmitry Belyavskiy 9dff9c
+		goto out;
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+	/* parse pk11-query (optional) */
Dmitry Belyavskiy 9dff9c
+	for (str2 = tok; ; str2 = NULL) {
Dmitry Belyavskiy 9dff9c
+		char *arg;
Dmitry Belyavskiy 9dff9c
+		pkcs11uriOpCodes opcode;
Dmitry Belyavskiy 9dff9c
+		tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2);
Dmitry Belyavskiy 9dff9c
+		if (tok == NULL)
Dmitry Belyavskiy 9dff9c
+			break;
Dmitry Belyavskiy 9dff9c
+		opcode = parse_token(tok);
Dmitry Belyavskiy 9dff9c
+		if (opcode != pBadOption)
Dmitry Belyavskiy 9dff9c
+			arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		switch (opcode) {
Dmitry Belyavskiy 9dff9c
+		case pModulePath:
Dmitry Belyavskiy 9dff9c
+			/* module-path is PKCS11Provider */
Dmitry Belyavskiy 9dff9c
+			if (pkcs11->module_path != NULL) {
Dmitry Belyavskiy 9dff9c
+				verbose_f("Multiple module-path attributes are"
Dmitry Belyavskiy 9dff9c
+				    "not supported the PKCS#11 URI");
Dmitry Belyavskiy 9dff9c
+				rv = -1;
Dmitry Belyavskiy 9dff9c
+				goto out;
Dmitry Belyavskiy 9dff9c
+			}
Dmitry Belyavskiy 9dff9c
+			percent_decode(arg, &pkcs11->module_path);
Dmitry Belyavskiy 9dff9c
+			debug3_f("Setting PKCS11Provider = %s from PKCS#11 URI",
Dmitry Belyavskiy 9dff9c
+			    pkcs11->module_path);
Dmitry Belyavskiy 9dff9c
+			break;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		case pPinValue:
Dmitry Belyavskiy 9dff9c
+			/* pin-value */
Dmitry Belyavskiy 9dff9c
+			if (pkcs11->pin != NULL) {
Dmitry Belyavskiy 9dff9c
+				verbose_f("Multiple pin-value attributes are"
Dmitry Belyavskiy 9dff9c
+				    "not supported the PKCS#11 URI");
Dmitry Belyavskiy 9dff9c
+				rv = -1;
Dmitry Belyavskiy 9dff9c
+				goto out;
Dmitry Belyavskiy 9dff9c
+			}
Dmitry Belyavskiy 9dff9c
+			percent_decode(arg, &pkcs11->pin);
Dmitry Belyavskiy 9dff9c
+			debug3_f("Setting PIN from PKCS#11 URI");
Dmitry Belyavskiy 9dff9c
+			break;
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+		default:
Dmitry Belyavskiy 9dff9c
+			/* Unrecognized attribute in the URI query SHOULD be ignored */
Dmitry Belyavskiy 9dff9c
+			verbose_f("Unknown part of query in PKCS#11 URI: %s", tok);
Dmitry Belyavskiy 9dff9c
+		}
Dmitry Belyavskiy 9dff9c
+	}
Dmitry Belyavskiy 9dff9c
+out:
Dmitry Belyavskiy 9dff9c
+	free(p);
Dmitry Belyavskiy 9dff9c
+	return rv;
Dmitry Belyavskiy 9dff9c
+}
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+#endif /* ENABLE_PKCS11 */
Dmitry Belyavskiy 9dff9c
diff -up openssh-8.6p1/ssh-pkcs11-uri.h.pkcs11-uri openssh-8.6p1/ssh-pkcs11-uri.h
Dmitry Belyavskiy 9dff9c
--- openssh-8.6p1/ssh-pkcs11-uri.h.pkcs11-uri	2021-05-06 11:35:55.114653289 +0200
Dmitry Belyavskiy 9dff9c
+++ openssh-8.6p1/ssh-pkcs11-uri.h	2021-05-06 11:35:55.114653289 +0200
Dmitry Belyavskiy 9dff9c
@@ -0,0 +1,42 @@
Dmitry Belyavskiy 9dff9c
+/*
Dmitry Belyavskiy 9dff9c
+ * Copyright (c) 2017 Red Hat
Dmitry Belyavskiy 9dff9c
+ *
Dmitry Belyavskiy 9dff9c
+ * Authors: Jakub Jelen <jjelen@redhat.com>
Dmitry Belyavskiy 9dff9c
+ *
Dmitry Belyavskiy 9dff9c
+ * Permission to use, copy, modify, and distribute this software for any
Dmitry Belyavskiy 9dff9c
+ * purpose with or without fee is hereby granted, provided that the above
Dmitry Belyavskiy 9dff9c
+ * copyright notice and this permission notice appear in all copies.
Dmitry Belyavskiy 9dff9c
+ *
Dmitry Belyavskiy 9dff9c
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
Dmitry Belyavskiy 9dff9c
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
Dmitry Belyavskiy 9dff9c
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Dmitry Belyavskiy 9dff9c
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Dmitry Belyavskiy 9dff9c
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
Dmitry Belyavskiy 9dff9c
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Dmitry Belyavskiy 9dff9c
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Dmitry Belyavskiy 9dff9c
+ */
Petr Šabata 81d24c
+
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_SCHEME "pkcs11:"
Dmitry Belyavskiy 9dff9c
+#define PKCS11_URI_WHITELIST	"abcdefghijklmnopqrstuvwxyz" \
Dmitry Belyavskiy 9dff9c
+				"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
Dmitry Belyavskiy 9dff9c
+				"0123456789_-.()"
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+struct pkcs11_uri {
Dmitry Belyavskiy 9dff9c
+	/* path */
Dmitry Belyavskiy 9dff9c
+	char *id;
Dmitry Belyavskiy 9dff9c
+	size_t id_len;
Dmitry Belyavskiy 9dff9c
+	char *token;
Dmitry Belyavskiy 9dff9c
+	char *object;
Dmitry Belyavskiy 9dff9c
+	char *lib_manuf;
Dmitry Belyavskiy 9dff9c
+	char *manuf;
Dmitry Belyavskiy 9dff9c
+	/* query */
Dmitry Belyavskiy 9dff9c
+	char *module_path;
Dmitry Belyavskiy 9dff9c
+	char *pin; /* Only parsed, but not printed */
Dmitry Belyavskiy 9dff9c
+};
Dmitry Belyavskiy 9dff9c
+
Dmitry Belyavskiy 9dff9c
+struct	 pkcs11_uri *pkcs11_uri_init();
Dmitry Belyavskiy 9dff9c
+void	 pkcs11_uri_cleanup(struct pkcs11_uri *);
Dmitry Belyavskiy 9dff9c
+int	 pkcs11_uri_parse(const char *, struct pkcs11_uri *);
Dmitry Belyavskiy 9dff9c
+struct	 pkcs11_uri *pkcs11_uri_init();
Dmitry Belyavskiy 9dff9c
+char	*pkcs11_uri_get(struct pkcs11_uri *uri);
Petr Šabata 81d24c
+