diff --git a/openssh-7.3p1-openssl-1.1.0.patch b/openssh-7.3p1-openssl-1.1.0.patch index 4d7855e..dadde28 100644 --- a/openssh-7.3p1-openssl-1.1.0.patch +++ b/openssh-7.3p1-openssl-1.1.0.patch @@ -2498,9 +2498,9 @@ diff -up openssh/ssh-pkcs11.c.openssl openssh/ssh-pkcs11.c int (*orig_finish)(RSA *rsa); - RSA_METHOD rsa_method; + RSA_METHOD *rsa_method; - char *label; char *keyid; int keyid_len; + char *label; @@ -183,6 +183,7 @@ pkcs11_rsa_finish(RSA *rsa) pkcs11_provider_unref(k11->provider); free(k11->keyid); @@ -2544,8 +2544,8 @@ diff -up openssh/ssh-pkcs11.c.openssl openssh/ssh-pkcs11.c CK_FUNCTION_LIST *f; + const BIGNUM *n, *e; - f = p->function_list; - session = p->slotinfo[slotidx].session; + f = p->module->function_list; + session = p->module->slotinfo[slotidx].session; @@ -512,10 +521,16 @@ pkcs11_fetch_keys_filter(struct pkcs11_p if ((rsa = RSA_new()) == NULL) { error("RSA_new failed"); diff --git a/openssh-7.6p1-pkcs11-ecdsa.patch b/openssh-7.6p1-pkcs11-ecdsa.patch index ff36699..b7fd2cf 100644 --- a/openssh-7.6p1-pkcs11-ecdsa.patch +++ b/openssh-7.6p1-pkcs11-ecdsa.patch @@ -155,7 +155,7 @@ diff -up openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.c + CK_ULONG key_type; int (*orig_finish)(RSA *rsa); RSA_METHOD rsa_method; - char *label; + char *keyid; @@ -75,6 +85,9 @@ struct pkcs11_key { }; @@ -217,8 +217,8 @@ diff -up openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.c key_filter[0].pValue = &private_key_class; @@ -326,33 +372,8 @@ pkcs11_rsa_private_encrypt(int flen, con } - f = k11->provider->function_list; - si = &k11->provider->slotinfo[k11->slotidx]; + f = k11->provider->module->function_list; + si = &k11->provider->module->slotinfo[k11->slotidx]; - if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { - if (!pkcs11_interactive) { - error("need pin entry%s", (si->token.flags & @@ -300,8 +300,8 @@ diff -up openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.c + error("no pkcs11 (valid) provider for ecdsa %p", ecdsa); + return NULL; + } -+ f = k11->provider->function_list; -+ si = &k11->provider->slotinfo[k11->slotidx]; ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; + if(pkcs11_login(k11, f, si)) { + return NULL; + } @@ -595,7 +595,7 @@ diff -up openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.c cp = attribs[3].pValue; if ((x509 = X509_new()) == NULL) { @@ -639,13 +879,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - X509_free(x509); + X509_free(x509); EVP_PKEY_free(evp); } - if (rsa && rsa->n && rsa->e && @@ -666,13 +666,13 @@ diff -up openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11 if (!strcmp(ki->providername, name)) { TAILQ_REMOVE(&pkcs11_keylist, ki, next); free(ki->providername); -- key_free(ki->key); +- sshkey_free(ki->key); + pkcs11_del_key(ki->key); free(ki); } } @@ -164,6 +174,20 @@ process_del(void) - buffer_free(&msg); + sshbuf_free(msg); } +#ifdef ENABLE_PKCS11_ECDSA @@ -693,7 +693,7 @@ diff -up openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11 process_sign(void) { @@ -180,14 +204,38 @@ process_sign(void) - if ((key = key_from_blob(blob, blen)) != NULL) { + else { if ((found = lookup_key(key)) != NULL) { #ifdef WITH_OPENSSL - int ret; @@ -790,5 +790,5 @@ diff -up openssh-7.6p1/ssh-pkcs11.c.old openssh-7.6p1/ssh-pkcs11.c + break; + } } - if (x509) - X509_free(x509); + X509_free(x509); + EVP_PKEY_free(evp); diff --git a/openssh-7.6p1-pkcs11-uri.patch b/openssh-7.6p1-pkcs11-uri.patch index 266063f..0114eb7 100644 --- a/openssh-7.6p1-pkcs11-uri.patch +++ b/openssh-7.6p1-pkcs11-uri.patch @@ -1,74 +1,8 @@ -diff -up openssh-7.6p1/configure.ac.pkcs11-uri openssh-7.6p1/configure.ac ---- openssh-7.6p1/configure.ac.pkcs11-uri 2018-02-16 12:40:58.320180022 +0100 -+++ openssh-7.6p1/configure.ac 2018-02-16 12:43:04.352962754 +0100 -@@ -1956,12 +1956,14 @@ AC_LINK_IFELSE( - [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) - ]) - -+SCARD_MSG="yes" - disable_pkcs11= - AC_ARG_ENABLE([pkcs11], - [ --disable-pkcs11 disable PKCS#11 support code [no]], - [ - if test "x$enableval" = "xno" ; then - disable_pkcs11=1 -+ SCARD_MSG="no" - fi - ] - ) -@@ -1974,6 +1976,40 @@ if test "x$openssl" = "xyes" && test "x$ - ) - fi - -+# Check whether we have a p11-kit, we got default provider on command line -+DEFAULT_PKCS11_PROVIDER_MSG="no" -+AC_ARG_WITH([default-pkcs11-provider], -+ [ --with-default-pkcs11-provider[[=PATH]] Use default pkcs11 provider (p11-kit detected by default)], -+ [ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then -+ if test "x$withval" = "xyes" ; then -+ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) -+ if test "x$PKGCONFIG" != "xno"; then -+ AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit]) -+ if "$PKGCONFIG" "p11-kit-1"; then -+ AC_MSG_RESULT([yes]) -+ use_pkgconfig_for_p11kit=yes -+ else -+ AC_MSG_RESULT([no]) -+ fi -+ fi -+ else -+ PKCS11_PATH="${withval}" -+ fi -+ if test "x$use_pkgconfig_for_p11kit" = "xyes"; then -+ PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1` -+ fi -+ AC_CHECK_FILE("$PKCS11_PATH", -+ [ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)]) -+ DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH" -+ ], -+ [ AC_MSG_ERROR([Requested PKCS11 provided not found]) ] -+ ) -+ else -+ AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider]) -+ fi ] -+) -+ -+ - # IRIX has a const char return value for gai_strerror() - AC_CHECK_FUNCS([gai_strerror], [ - AC_DEFINE([HAVE_GAI_STRERROR]) -@@ -5280,6 +5316,7 @@ echo " BSD Auth support - echo " Random number source: $RAND_MSG" - echo " Privsep sandbox style: $SANDBOX_STYLE" - echo " Vendor patch level: $SSH_VENDOR_PATCHLEVEL" -+echo " Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG" - - echo "" - -diff -up openssh-7.6p1/Makefile.in.pkcs11-uri openssh-7.6p1/Makefile.in ---- openssh-7.6p1/Makefile.in.pkcs11-uri 2018-02-16 12:40:58.238179513 +0100 -+++ openssh-7.6p1/Makefile.in 2018-02-16 12:42:37.366794883 +0100 -@@ -94,7 +94,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ +diff --git a/Makefile.in b/Makefile.in +index ac959c1f..f8ed1781 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -93,7 +93,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ kexgssc.o \ msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ @@ -76,8 +10,8 @@ diff -up openssh-7.6p1/Makefile.in.pkcs11-uri openssh-7.6p1/Makefile.in + ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \ poly1305.o chacha.o cipher-chachapoly.o \ ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ - sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \ -@@ -268,6 +268,8 @@ clean: regressclean + sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \ +@@ -248,6 +248,8 @@ clean: regressclean rm -f regress/unittests/match/test_match$(EXEEXT) rm -f regress/unittests/utf8/*.o rm -f regress/unittests/utf8/test_utf8$(EXEEXT) @@ -86,7 +20,7 @@ diff -up openssh-7.6p1/Makefile.in.pkcs11-uri openssh-7.6p1/Makefile.in rm -f regress/misc/kexfuzz/*.o rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) (cd openbsd-compat && $(MAKE) clean) -@@ -296,6 +298,8 @@ distclean: regressclean +@@ -276,6 +278,8 @@ distclean: regressclean rm -f regress/unittests/match/test_match rm -f regress/unittests/utf8/*.o rm -f regress/unittests/utf8/test_utf8 @@ -95,16 +29,15 @@ diff -up openssh-7.6p1/Makefile.in.pkcs11-uri openssh-7.6p1/Makefile.in rm -f regress/unittests/misc/kexfuzz (cd openbsd-compat && $(MAKE) distclean) if test -d pkg ; then \ -@@ -484,6 +488,8 @@ regress-prep: - mkdir -p `pwd`/regress/unittests/match - [ -d `pwd`/regress/unittests/utf8 ] || \ - mkdir -p `pwd`/regress/unittests/utf8 -+ [ -d `pwd`/regress/unittests/pkcs11 ] || \ -+ mkdir -p `pwd`/regress/unittests/pkcs11 - [ -d `pwd`/regress/misc/kexfuzz ] || \ - mkdir -p `pwd`/regress/misc/kexfuzz +@@ -437,6 +441,7 @@ regress-prep: + $(MKDIR_P) `pwd`/regress/unittests/kex + $(MKDIR_P) `pwd`/regress/unittests/match + $(MKDIR_P) `pwd`/regress/unittests/utf8 ++ $(MKDIR_P) `pwd`/regress/unittests/pkcs11 + $(MKDIR_P) `pwd`/regress/misc/kexfuzz [ -f `pwd`/regress/Makefile ] || \ -@@ -503,6 +509,11 @@ regress/netcat$(EXEEXT): $(srcdir)/regre + ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile +@@ -455,6 +460,11 @@ regress/netcat$(EXEEXT): $(srcdir)/regress/netcat.c $(REGRESSLIBS) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/netcat.c \ $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) @@ -116,7 +49,7 @@ diff -up openssh-7.6p1/Makefile.in.pkcs11-uri openssh-7.6p1/Makefile.in regress/check-perm$(EXEEXT): $(srcdir)/regress/check-perm.c $(REGRESSLIBS) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/check-perm.c \ $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) -@@ -604,6 +615,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT +@@ -556,6 +566,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \ regress/unittests/test_helper/libtest_helper.a \ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) @@ -133,7 +66,7 @@ diff -up openssh-7.6p1/Makefile.in.pkcs11-uri openssh-7.6p1/Makefile.in MISC_KEX_FUZZ_OBJS=\ regress/misc/kexfuzz/kexfuzz.o -@@ -614,6 +635,7 @@ regress/misc/kexfuzz/kexfuzz$(EXEEXT): $ +@@ -566,6 +586,7 @@ regress/misc/kexfuzz/kexfuzz$(EXEEXT): ${MISC_KEX_FUZZ_OBJS} libssh.a regress-binaries: regress/modpipe$(EXEEXT) \ regress/setuid-allowed$(EXEEXT) \ regress/netcat$(EXEEXT) \ @@ -141,28 +74,110 @@ diff -up openssh-7.6p1/Makefile.in.pkcs11-uri openssh-7.6p1/Makefile.in regress/check-perm$(EXEEXT) \ regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \ regress/unittests/sshkey/test_sshkey$(EXEEXT) \ -@@ -623,6 +645,7 @@ regress-binaries: regress/modpipe$(EXEEX +@@ -575,6 +596,7 @@ regress-binaries: regress/modpipe$(EXEEXT) \ regress/unittests/kex/test_kex$(EXEEXT) \ regress/unittests/match/test_match$(EXEEXT) \ regress/unittests/utf8/test_utf8$(EXEEXT) \ + regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \ regress/misc/kexfuzz/kexfuzz$(EXEEXT) - tests interop-tests t-exec unit: regress-prep regress-binaries $(TARGETS) -diff -up openssh-7.6p1/readconf.c.pkcs11-uri openssh-7.6p1/readconf.c ---- openssh-7.6p1/readconf.c.pkcs11-uri 2018-02-16 12:40:58.303179916 +0100 -+++ openssh-7.6p1/readconf.c 2018-02-16 12:40:58.344180171 +0100 -@@ -1088,7 +1088,8 @@ parse_time: + REGRESSTMP = "$(PWD)/regress" +diff --git a/authfd.c b/authfd.c +index 1eff7ba9..35153f47 100644 +--- a/authfd.c ++++ b/authfd.c +@@ -312,6 +312,8 @@ ssh_free_identitylist(struct ssh_identitylist *idl) + if (idl->comments != NULL) + free(idl->comments[i]); + } ++ free(idl->keys); ++ free(idl->comments); + free(idl); + } + +diff --git a/configure.ac b/configure.ac +index d7bcaf01..171a8597 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1895,12 +1895,14 @@ AC_LINK_IFELSE( + [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) + ]) + ++SCARD_MSG="yes" + disable_pkcs11= + AC_ARG_ENABLE([pkcs11], + [ --disable-pkcs11 disable PKCS#11 support code [no]], + [ + if test "x$enableval" = "xno" ; then + disable_pkcs11=1 ++ SCARD_MSG="no" + fi + ] + ) +@@ -1916,6 +1918,40 @@ if test "x$openssl" = "xyes" && test "x$disable_pkcs11" = "x"; then + ) + fi + ++# Check whether we have a p11-kit, we got default provider on command line ++DEFAULT_PKCS11_PROVIDER_MSG="no" ++AC_ARG_WITH([default-pkcs11-provider], ++ [ --with-default-pkcs11-provider[[=PATH]] Use default pkcs11 provider (p11-kit detected by default)], ++ [ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then ++ if test "x$withval" = "xyes" ; then ++ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) ++ if test "x$PKGCONFIG" != "xno"; then ++ AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit]) ++ if "$PKGCONFIG" "p11-kit-1"; then ++ AC_MSG_RESULT([yes]) ++ use_pkgconfig_for_p11kit=yes ++ else ++ AC_MSG_RESULT([no]) ++ fi ++ fi ++ else ++ PKCS11_PATH="${withval}" ++ fi ++ if test "x$use_pkgconfig_for_p11kit" = "xyes"; then ++ PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1` ++ fi ++ AC_CHECK_FILE("$PKCS11_PATH", ++ [ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)]) ++ DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH" ++ ], ++ [ AC_MSG_ERROR([Requested PKCS11 provided not found]) ] ++ ) ++ else ++ AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider]) ++ fi ] ++) ++ ++ + # IRIX has a const char return value for gai_strerror() + AC_CHECK_FUNCS([gai_strerror], [ + AC_DEFINE([HAVE_GAI_STRERROR]) +@@ -5226,6 +5262,7 @@ echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" + echo " Random number source: $RAND_MSG" + echo " Privsep sandbox style: $SANDBOX_STYLE" + echo " Vendor patch level: $SSH_VENDOR_PATCHLEVEL" ++echo " Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG" + + echo "" + +diff --git a/readconf.c b/readconf.c +index 88051db5..c1e7ce93 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -1016,7 +1016,8 @@ parse_time: break; case oIdentityFile: - arg = strdelim(&s); + /* Can't use strdelim() becase it would break on equal signs */ -+ arg = xstrdup(s); ++ arg = s; if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (*activep) { -@@ -1099,7 +1100,7 @@ parse_time: +@@ -1027,7 +1028,7 @@ parse_time: add_identity_file(options, NULL, arg, flags & SSHCONF_USERCONF); } @@ -171,9 +186,36 @@ diff -up openssh-7.6p1/readconf.c.pkcs11-uri openssh-7.6p1/readconf.c case oCertificateFile: arg = strdelim(&s); -diff -up openssh-7.6p1/regress/agent-pkcs11.sh.pkcs11-uri openssh-7.6p1/regress/agent-pkcs11.sh ---- openssh-7.6p1/regress/agent-pkcs11.sh.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/regress/agent-pkcs11.sh 2018-02-16 12:40:58.344180171 +0100 +diff --git a/regress/Makefile b/regress/Makefile +index d15898ad..9c15afa4 100644 +--- a/regress/Makefile ++++ b/regress/Makefile +@@ -108,9 +110,11 @@ CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \ + known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \ + modpipe netcat no_identity_config \ + pidfile putty.rsa2 ready regress.log \ +- remote_pid revoked-* rsa rsa-agent rsa-agent.pub rsa.pub \ ++ remote_pid revoked-* rsa rsa-agent rsa-agent.pub \ ++ rsa-agent-cert.pub rsa.pub \ + rsa1 rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \ +- rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ ++ soft-pkcs11.so soft-pkcs11.o pkcs11*.crt pkcs11*.key \ ++ pkcs11.info rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ + scp-ssh-wrapper.scp setuid-allowed sftp-server.log \ + sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ + ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ +@@ -225,6 +229,7 @@ unit: + V="" ; \ + test "x${USE_VALGRIND}" = "x" || \ + V=${.CURDIR}/valgrind-unit.sh ; \ ++ $$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \ + $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ + $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ + -d ${.CURDIR}/unittests/sshkey/testdata ; \ +diff --git a/regress/agent-pkcs11.sh b/regress/agent-pkcs11.sh +index db3018b8..4991d0bc 100644 +--- a/regress/agent-pkcs11.sh ++++ b/regress/agent-pkcs11.sh @@ -4,10 +4,17 @@ tid="pkcs11 agent test" @@ -211,9 +253,11 @@ diff -up openssh-7.6p1/regress/agent-pkcs11.sh.pkcs11-uri openssh-7.6p1/regress/ r=$? if [ $r -ne 0 ]; then fail "ssh-add -e failed: exit code $r" -diff -up openssh-7.6p1/regress/locl.h.pkcs11-uri openssh-7.6p1/regress/locl.h ---- openssh-7.6p1/regress/locl.h.pkcs11-uri 2018-02-16 12:40:58.345180177 +0100 -+++ openssh-7.6p1/regress/locl.h 2018-02-16 12:40:58.344180171 +0100 +diff --git a/regress/locl.h b/regress/locl.h +new file mode 100644 +index 00000000..afefe27d +--- /dev/null ++++ b/regress/locl.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2004, Stockholms universitet @@ -294,44 +338,12 @@ diff -up openssh-7.6p1/regress/locl.h.pkcs11-uri openssh-7.6p1/regress/locl.h + } \ + } \ +} -diff -up openssh-7.6p1/regress/Makefile.pkcs11-uri openssh-7.6p1/regress/Makefile ---- openssh-7.6p1/regress/Makefile.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/regress/Makefile 2018-02-16 12:44:29.780494023 +0100 -@@ -41,6 +41,8 @@ LTESTS= connect \ - keygen-convert \ - keygen-moduli \ - key-options \ -+ pkcs11 \ -+ agent-pkcs11 \ - scp \ - sftp \ - sftp-chroot \ -@@ -105,9 +107,11 @@ CLEANFILES= *.core actual agent-key.* au - known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \ - modpipe netcat no_identity_config \ - pidfile putty.rsa2 ready regress.log \ -- remote_pid revoked-* rsa rsa-agent rsa-agent.pub rsa.pub \ -+ remote_pid revoked-* rsa rsa-agent rsa-agent.pub \ -+ rsa-agent-cert.pub rsa.pub \ - rsa1 rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \ -- rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ -+ soft-pkcs11.so soft-pkcs11.o pkcs11*.crt pkcs11*.key \ -+ pkcs11.info rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ - scp-ssh-wrapper.scp setuid-allowed sftp-server.log \ - sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ - ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ -@@ -222,6 +226,7 @@ unit: - V="" ; \ - test "x${USE_VALGRIND}" = "x" || \ - V=${.CURDIR}/valgrind-unit.sh ; \ -+ $$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \ - $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ - $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ - -d ${.CURDIR}/unittests/sshkey/testdata ; \ -diff -up openssh-7.6p1/regress/pkcs11.sh.pkcs11-uri openssh-7.6p1/regress/pkcs11.sh ---- openssh-7.6p1/regress/pkcs11.sh.pkcs11-uri 2018-02-16 12:40:58.345180177 +0100 -+++ openssh-7.6p1/regress/pkcs11.sh 2018-02-16 12:40:58.345180177 +0100 -@@ -0,0 +1,285 @@ +diff --git a/regress/pkcs11.sh b/regress/pkcs11.sh +new file mode 100644 +index 00000000..cf98e379 +--- /dev/null ++++ b/regress/pkcs11.sh +@@ -0,0 +1,300 @@ +# +# Copyright (c) 2017 Red Hat +# @@ -596,6 +608,14 @@ diff -up openssh-7.6p1/regress/pkcs11.sh.pkcs11-uri openssh-7.6p1/regress/pkcs11 + fail "ssh connect passed without key (should fail)" + fi + ++ trace "add also the first key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with first key: exit code $r" ++ fi ++ + trace " remove second pkcs11 key" + ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ + > /dev/null 2>&1 @@ -605,21 +625,30 @@ diff -up openssh-7.6p1/regress/pkcs11.sh.pkcs11-uri openssh-7.6p1/regress/pkcs11 + fi + + trace " remove already-removed pkcs11 key should fail" -+ ${SSHADD} -d "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" \ ++ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ + > /dev/null 2>&1 + r=$? + if [ $r -eq 0 ]; then + fail "ssh-add -d passed with non-existing key (should fail)" + fi + ++ trace " pkcs11 connect via agent (the first key should be still usable)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with first key (after removing second): exit code $r" ++ fi ++ + trace "kill agent" + ${SSHAGENT} -k > /dev/null +fi + +rm -rf $OBJ/.tokens $OBJ/token_keys -diff -up openssh-7.6p1/regress/soft-pkcs11.c.pkcs11-uri openssh-7.6p1/regress/soft-pkcs11.c ---- openssh-7.6p1/regress/soft-pkcs11.c.pkcs11-uri 2018-02-16 12:40:58.345180177 +0100 -+++ openssh-7.6p1/regress/soft-pkcs11.c 2018-02-16 12:40:58.345180177 +0100 +diff --git a/regress/soft-pkcs11.c b/regress/soft-pkcs11.c +new file mode 100644 +index 00000000..8b4981bd +--- /dev/null ++++ b/regress/soft-pkcs11.c @@ -0,0 +1,2058 @@ +/* + * Copyright (c) 2004-2006, Stockholms universitet @@ -2679,20 +2708,23 @@ diff -up openssh-7.6p1/regress/soft-pkcs11.c.pkcs11-uri openssh-7.6p1/regress/so + (void *)func_not_supported, /* C_CancelFunction */ + (void *)func_not_supported /* C_WaitForSlotEvent */ +}; -diff -up openssh-7.6p1/regress/unittests/Makefile.pkcs11-uri openssh-7.6p1/regress/unittests/Makefile ---- openssh-7.6p1/regress/unittests/Makefile.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/regress/unittests/Makefile 2018-02-16 12:40:58.345180177 +0100 -@@ -1,6 +1,6 @@ - # $OpenBSD: Makefile,v 1.9 2017/03/14 01:20:29 dtucker Exp $ +diff --git a/regress/unittests/Makefile b/regress/unittests/Makefile +index e464b085..a0e5a37c 100644 +--- a/regress/unittests/Makefile ++++ b/regress/unittests/Makefile +@@ -2,6 +2,6 @@ REGRESS_FAIL_EARLY?= yes --SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion -+SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion pkcs11 + SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion +-SUBDIR+=authopt ++SUBDIR+=pkcs11 authopt .include -diff -up openssh-7.6p1/regress/unittests/pkcs11/Makefile.pkcs11-uri openssh-7.6p1/regress/unittests/pkcs11/Makefile ---- openssh-7.6p1/regress/unittests/pkcs11/Makefile.pkcs11-uri 2018-02-16 12:40:58.345180177 +0100 -+++ openssh-7.6p1/regress/unittests/pkcs11/Makefile 2018-02-16 12:40:58.345180177 +0100 +diff --git a/regress/unittests/pkcs11/Makefile b/regress/unittests/pkcs11/Makefile +new file mode 100644 +index 00000000..481b13d0 +--- /dev/null ++++ b/regress/unittests/pkcs11/Makefile @@ -0,0 +1,9 @@ + +PROG=test_pkcs11 @@ -2703,9 +2735,11 @@ diff -up openssh-7.6p1/regress/unittests/pkcs11/Makefile.pkcs11-uri openssh-7.6p + env ${TEST_ENV} ./${PROG} + +.include -diff -up openssh-7.6p1/regress/unittests/pkcs11/tests.c.pkcs11-uri openssh-7.6p1/regress/unittests/pkcs11/tests.c ---- openssh-7.6p1/regress/unittests/pkcs11/tests.c.pkcs11-uri 2018-02-16 12:40:58.345180177 +0100 -+++ openssh-7.6p1/regress/unittests/pkcs11/tests.c 2018-02-16 12:40:58.345180177 +0100 +diff --git a/regress/unittests/pkcs11/tests.c b/regress/unittests/pkcs11/tests.c +new file mode 100644 +index 00000000..e83aca54 +--- /dev/null ++++ b/regress/unittests/pkcs11/tests.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2017 Red Hat @@ -3036,9 +3070,10 @@ diff -up openssh-7.6p1/regress/unittests/pkcs11/tests.c.pkcs11-uri openssh-7.6p1 + test_parse_invalid(); + test_generate_valid(); +} -diff -up openssh-7.6p1/ssh-add.c.pkcs11-uri openssh-7.6p1/ssh-add.c ---- openssh-7.6p1/ssh-add.c.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/ssh-add.c 2018-02-16 12:40:58.346180183 +0100 +diff --git a/ssh-add.c b/ssh-add.c +index adcc4599..e4fd5623 100644 +--- a/ssh-add.c ++++ b/ssh-add.c @@ -64,6 +64,7 @@ #include "misc.h" #include "ssherr.h" @@ -3047,7 +3082,7 @@ diff -up openssh-7.6p1/ssh-add.c.pkcs11-uri openssh-7.6p1/ssh-add.c /* argv0 */ extern char *__progname; -@@ -183,6 +184,24 @@ delete_all(int agent_fd) +@@ -188,6 +189,24 @@ delete_all(int agent_fd) return ret; } @@ -3072,7 +3107,7 @@ diff -up openssh-7.6p1/ssh-add.c.pkcs11-uri openssh-7.6p1/ssh-add.c static int add_file(int agent_fd, const char *filename, int key_only, int qflag) { -@@ -434,6 +453,13 @@ lock_agent(int agent_fd, int lock) +@@ -480,6 +499,13 @@ lock_agent(int agent_fd, int lock) static int do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) { @@ -3086,10 +3121,11 @@ diff -up openssh-7.6p1/ssh-add.c.pkcs11-uri openssh-7.6p1/ssh-add.c if (deleting) { if (delete_file(agent_fd, file, key_only, qflag) == -1) return -1; -diff -up openssh-7.6p1/ssh-agent.c.pkcs11-uri openssh-7.6p1/ssh-agent.c ---- openssh-7.6p1/ssh-agent.c.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/ssh-agent.c 2018-02-16 12:46:35.647278145 +0100 -@@ -526,10 +526,70 @@ no_identities(SocketEntry *e) +diff --git a/ssh-agent.c b/ssh-agent.c +index 2a4578b0..f6c86240 100644 +--- a/ssh-agent.c ++++ b/ssh-agent.c +@@ -546,10 +546,70 @@ no_identities(SocketEntry *e) } #ifdef ENABLE_PKCS11 @@ -3156,12 +3192,12 @@ diff -up openssh-7.6p1/ssh-agent.c.pkcs11-uri openssh-7.6p1/ssh-agent.c static void process_add_smartcard_key(SocketEntry *e) { -- char *provider = NULL, *pin, canonical_provider[PATH_MAX]; -+ char *provider = NULL, *pin, *sane_uri = NULL; +- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; ++ char *provider = NULL, *pin = NULL, *sane_uri = NULL; int r, i, count = 0, success = 0, confirm = 0; u_int seconds; time_t death = 0; -@@ -559,28 +619,23 @@ process_add_smartcard_key(SocketEntry *e +@@ -585,28 +645,23 @@ process_add_smartcard_key(SocketEntry *e) goto send; } } @@ -3198,7 +3234,7 @@ diff -up openssh-7.6p1/ssh-agent.c.pkcs11-uri openssh-7.6p1/ssh-agent.c id->death = death; id->confirm = confirm; TAILQ_INSERT_TAIL(&idtab->idlist, id, next); -@@ -594,6 +649,7 @@ process_add_smartcard_key(SocketEntry *e +@@ -620,6 +675,7 @@ process_add_smartcard_key(SocketEntry *e) send: free(pin); free(provider); @@ -3206,7 +3242,7 @@ diff -up openssh-7.6p1/ssh-agent.c.pkcs11-uri openssh-7.6p1/ssh-agent.c free(keys); send_status(e, success); } -@@ -601,7 +657,7 @@ send: +@@ -627,7 +683,7 @@ send: static void process_remove_smartcard_key(SocketEntry *e) { @@ -3215,17 +3251,17 @@ diff -up openssh-7.6p1/ssh-agent.c.pkcs11-uri openssh-7.6p1/ssh-agent.c int r, success = 0; Identity *id, *nxt; -@@ -610,30 +666,30 @@ process_remove_smartcard_key(SocketEntry - fatal("%s: buffer error: %s", __func__, ssh_err(r)); +@@ -638,30 +694,29 @@ process_remove_smartcard_key(SocketEntry *e) + } free(pin); - if (realpath(provider, canonical_provider) == NULL) { - verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", - provider, strerror(errno)); + sane_uri = sanitize_pkcs11_provider(provider); -+ if (sane_uri == NULL) { ++ if (sane_uri == NULL) goto send; - } +- } - debug("%s: remove %.100s", __func__, canonical_provider); + debug("%s: remove %.100s", __func__, sane_uri); @@ -3252,195 +3288,40 @@ diff -up openssh-7.6p1/ssh-agent.c.pkcs11-uri openssh-7.6p1/ssh-agent.c send_status(e, success); } #endif /* ENABLE_PKCS11 */ -diff -up openssh-7.6p1/ssh_config.5.pkcs11-uri openssh-7.6p1/ssh_config.5 ---- openssh-7.6p1/ssh_config.5.pkcs11-uri 2018-02-16 12:40:58.329180078 +0100 -+++ openssh-7.6p1/ssh_config.5 2018-02-16 12:40:58.348180196 +0100 -@@ -970,6 +970,19 @@ may also be used in conjunction with - .Cm CertificateFile - in order to provide any certificate also needed for authentication with - the identity. -+.Pp -+The authentication identity can be also specified in a form of PKCS#11 URI -+starting with a string -+.Cm pkcs11: . -+There is supported a subset of the PKCS#11 URI as defined -+in RFC 7512 (implemented path arguments -+.Cm id , -+.Cm manufacturer , -+.Cm object , -+.Cm token -+and query argument -+.Cm module-path -+). The URI can not be in quotes. - .It Cm IgnoreUnknown - Specifies a pattern-list of unknown options to be ignored if they are - encountered in configuration parsing. -diff -up openssh-7.6p1/ssh.c.pkcs11-uri openssh-7.6p1/ssh.c ---- openssh-7.6p1/ssh.c.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/ssh.c 2018-02-16 12:52:59.480695053 +0100 -@@ -718,6 +718,15 @@ main(int ac, char **av) - options.gss_deleg_creds = 1; - break; - case 'i': -+#ifdef ENABLE_PKCS11 -+ if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(optarg, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ p = strdup(optarg); -+ add_identity_file(&options, NULL, p, 1); -+ break; -+ } -+#endif - p = tilde_expand_filename(optarg, original_real_uid); - if (stat(p, &st) < 0) - fprintf(stderr, "Warning: Identity file %s " -@@ -1879,6 +1888,46 @@ ssh_session2(struct ssh *ssh) - options.escape_char : SSH_ESCAPECHAR_NONE, id); - } +diff --git a/ssh-keygen.c b/ssh-keygen.c +index d1ffc30c..00e38049 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -820,6 +820,7 @@ do_download(struct passwd *pw) + free(fp); + } else { + (void) sshkey_write(keys[i], stdout); /* XXX check */ ++ (void) pkcs11_uri_write(keys[i], stdout); + fprintf(stdout, "\n"); + } + sshkey_free(keys[i]); +diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c +index a023f5f4..882e8381 100644 +--- a/ssh-pkcs11-client.c ++++ b/ssh-pkcs11-client.c +@@ -117,6 +117,7 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + return (-1); + key.type = KEY_RSA; + key.rsa = rsa; ++ key.ecdsa_nid = 0; + if (key_to_blob(&key, &blob, &blen) == 0) + return -1; + buffer_init(&msg); +@@ -195,6 +196,8 @@ pkcs11_add_provider(char *name, char *pin, Key ***keysp) + u_int blen; + Buffer msg; -+#ifdef ENABLE_PKCS11 -+static void -+load_pkcs11_identity(char *pkcs11_uri, char *identity_files[], -+ struct sshkey *identity_keys[], int *n_ids) -+{ -+ int nkeys, i; -+ struct sshkey **keys; -+ struct pkcs11_uri *uri; -+ -+ debug("identity file '%s' from pkcs#11", pkcs11_uri); -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ if (pkcs11_uri_parse(pkcs11_uri, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri); -+ -+ /* we need to merge URI and provider together */ -+ if (options.pkcs11_provider != NULL && uri->module_path == NULL) -+ uri->module_path = strdup(options.pkcs11_provider); -+ -+ if (pkcs11_init(!options.batch_mode) == 0 && -+ options.num_identity_files < SSH_MAX_IDENTITY_FILES && -+ (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys)) > 0) { -+ for (i = 0; i < nkeys; i++) { -+ if (*n_ids >= SSH_MAX_IDENTITY_FILES) { -+ key_free(keys[i]); -+ continue; -+ } -+ identity_keys[*n_ids] = keys[i]; -+ identity_files[*n_ids] = pkcs11_uri_get(uri); -+ (*n_ids)++; -+ } -+ free(keys); -+ } -+ -+ pkcs11_uri_cleanup(uri); -+} -+#endif /* ENABLE_PKCS11 */ -+ - /* Loads all IdentityFile and CertificateFile keys */ - static void - load_public_identity_files(void) -@@ -1893,10 +1942,6 @@ load_public_identity_files(void) - struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; - char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; - struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; --#ifdef ENABLE_PKCS11 -- struct sshkey **keys; -- int nkeys; --#endif /* PKCS11 */ - - n_ids = n_certs = 0; - memset(identity_files, 0, sizeof(identity_files)); -@@ -1905,22 +1950,21 @@ load_public_identity_files(void) - memset(certificates, 0, sizeof(certificates)); - - #ifdef ENABLE_PKCS11 -- if (options.pkcs11_provider != NULL && -- options.num_identity_files < SSH_MAX_IDENTITY_FILES && -- (pkcs11_init(!options.batch_mode) == 0) && -- (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, -- &keys)) > 0) { -- for (i = 0; i < nkeys; i++) { -- if (n_ids >= SSH_MAX_IDENTITY_FILES) { -- key_free(keys[i]); -- continue; -- } -- identity_keys[n_ids] = keys[i]; -- identity_files[n_ids] = -- xstrdup(options.pkcs11_provider); /* XXX */ -- n_ids++; -- } -- free(keys); -+ /* handle fallback from PKCS11Provider option */ -+ if (options.pkcs11_provider != NULL) { -+ struct pkcs11_uri *uri; -+ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ /* Construct simple PKCS#11 URI to simplify access */ -+ uri->module_path = strdup(options.pkcs11_provider); -+ -+ /* Add it as any other IdentityFile */ -+ add_identity_file(&options, NULL, pkcs11_uri_get(uri), 1); -+ -+ pkcs11_uri_cleanup(uri); - } - #endif /* ENABLE_PKCS11 */ - if ((pw = getpwuid(original_real_uid)) == NULL) -@@ -1931,14 +1975,23 @@ load_public_identity_files(void) - fatal("load_public_identity_files: gethostname: %s", - strerror(errno)); - for (i = 0; i < options.num_identity_files; i++) { -+ char *name = options.identity_files[i]; - if (n_ids >= SSH_MAX_IDENTITY_FILES || -- strcasecmp(options.identity_files[i], "none") == 0) { -++ strcasecmp(name, "none") == 0) { - free(options.identity_files[i]); - options.identity_files[i] = NULL; - continue; - } -- cp = tilde_expand_filename(options.identity_files[i], -- original_real_uid); -+#ifdef ENABLE_PKCS11 -+ if (strlen(name) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(name, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ load_pkcs11_identity(name, identity_files, -+ identity_keys, &n_ids); -+ continue; -+ } -+#endif /* ENABLE_PKCS11 */ -+ cp = tilde_expand_filename(name, original_real_uid); - filename = percent_expand(cp, "d", pwdir, - "u", pwname, "l", thishost, "h", host, - "r", options.user, (char *)NULL); -diff -up openssh-7.6p1/ssh-keygen.c.pkcs11-uri openssh-7.6p1/ssh-keygen.c ---- openssh-7.6p1/ssh-keygen.c.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/ssh-keygen.c 2018-02-16 12:40:58.346180183 +0100 -@@ -811,6 +811,7 @@ do_download(struct passwd *pw) - free(fp); - } else { - (void) sshkey_write(keys[i], stdout); /* XXX check */ -+ (void) pkcs11_uri_write(keys[i], stdout); - fprintf(stdout, "\n"); - } - sshkey_free(keys[i]); -diff -up openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-uri openssh-7.6p1/ssh-pkcs11-client.c ---- openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/ssh-pkcs11-client.c 2018-02-16 12:40:58.346180183 +0100 -@@ -192,6 +192,8 @@ pkcs11_add_provider(char *name, char *pi - u_int blen; - Buffer msg; - -+ debug("%s: called, name = %s", __func__, name); ++ debug("%s: called, name = %s", __func__, name); + if (fd < 0 && pkcs11_start_helper() < 0) return (-1); -@@ -205,6 +207,7 @@ pkcs11_add_provider(char *name, char *pi +@@ -208,6 +211,7 @@ pkcs11_add_provider(char *name, char *pin, Key ***keysp) if (recv_msg(&msg) == SSH2_AGENT_IDENTITIES_ANSWER) { nkeys = buffer_get_int(&msg); *keysp = xcalloc(nkeys, sizeof(Key *)); @@ -3448,708 +3329,135 @@ diff -up openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-uri openssh-7.6p1/ssh-pkcs11-c for (i = 0; i < nkeys; i++) { blob = buffer_get_string(&msg, &blen); free(buffer_get_string(&msg, NULL)); -diff -up openssh-7.6p1/ssh-pkcs11.c.pkcs11-uri openssh-7.6p1/ssh-pkcs11.c ---- openssh-7.6p1/ssh-pkcs11.c.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/ssh-pkcs11.c 2018-02-16 12:50:33.921768717 +0100 -@@ -50,6 +50,7 @@ struct pkcs11_slotinfo { - - struct pkcs11_provider { - char *name; -+ char *module_path; - void *handle; - CK_FUNCTION_LIST *function_list; - CK_INFO info; -@@ -68,12 +70,57 @@ struct pkcs11_key { - CK_ULONG slotidx; - int (*orig_finish)(RSA *rsa); - RSA_METHOD rsa_method; -+ char *label; - char *keyid; - int keyid_len; - }; - - int pkcs11_interactive = 0; - +diff --git a/ssh-pkcs11-uri.c b/ssh-pkcs11-uri.c +new file mode 100644 +index 00000000..da15c164 +--- /dev/null ++++ b/ssh-pkcs11-uri.c +@@ -0,0 +1,399 @@ +/* -+ * This can't be in the ssh-pkcs11-uri, becase we can not depend on -+ * PKCS#11 structures in ssh-agent (using client-helper communication) ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ -+int -+pkcs11_uri_write(const struct sshkey *key, FILE *f) -+{ -+ char *p = NULL; -+ struct pkcs11_uri uri; -+ struct pkcs11_key *k11; + -+ /* sanity - is it a RSA key with associated app_data? */ -+ switch (key->type) { -+ case KEY_RSA: -+ if ((k11 = RSA_get_app_data(key->rsa)) == NULL) -+ return -1; -+ break; -+ case KEY_ECDSA: -+ if ((k11 = EC_KEY_get_ex_data(key->ecdsa, pkcs11_key_idx)) == NULL) -+ return -1; -+ break; -+ default: -+ return -1; -+ } ++#include "includes.h" + -+ /* omit type -- we are looking for private-public or private-certificate pairs */ -+ uri.id = k11->keyid; -+ uri.id_len = k11->keyid_len; -+ uri.token = k11->provider->slotinfo[k11->slotidx].token.label; -+ uri.object = k11->label; -+ uri.module_path = k11->provider->module_path; -+ uri.lib_manuf = k11->provider->info.manufacturerID; -+ uri.manuf = k11->provider->slotinfo[k11->slotidx].token.manufacturerID; ++#ifdef ENABLE_PKCS11 + -+ p = pkcs11_uri_get(&uri); -+ /* do not cleanup -- we do not allocate here, only reference */ -+ if (p == NULL) -+ return -1; ++#include ++#include + -+ fprintf(f, " %s", p); -+ free(p); -+ return 0; -+} ++#include "sshkey.h" ++#include "log.h" + - int - pkcs11_init(int interactive) - { -@@ -124,6 +161,8 @@ pkcs11_provider_unref(struct pkcs11_prov - error("pkcs11_provider_unref: %p still valid", p); - free(p->slotlist); - free(p->slotinfo); -+ free(p->name); -+ free(p->module_path); - free(p); - } - } -@@ -155,19 +194,52 @@ pkcs11_provider_lookup(char *provider_id - return (NULL); - } - -+int pkcs11_del_provider_by_uri(struct pkcs11_uri *); ++#define CRYPTOKI_COMPAT ++#include "pkcs11.h" + - /* unregister provider by name */ - int - pkcs11_del_provider(char *provider_id) - { -+ int rv; -+ struct pkcs11_uri *uri; ++#include "ssh-pkcs11-uri.h" + -+ debug("%s: called, provider_id = %s", __func__, provider_id); ++#define PKCS11_URI_PATH_SEPARATOR ";" ++#define PKCS11_URI_QUERY_SEPARATOR "&" ++#define PKCS11_URI_VALUE_SEPARATOR "=" ++#define PKCS11_URI_ID "id" ++#define PKCS11_URI_TOKEN "token" ++#define PKCS11_URI_OBJECT "object" ++#define PKCS11_URI_LIB_MANUF "library-manufacturer" ++#define PKCS11_URI_MANUF "manufacturer" ++#define PKCS11_URI_MODULE_PATH "module-path" + -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); ++/* Keyword tokens. */ ++typedef enum { ++ pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath, ++ pBadOption ++} pkcs11uriOpCodes; + -+ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { -+ if (pkcs11_uri_parse(provider_id, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI"); -+ } else { -+ uri->module_path = strdup(provider_id); -+ } ++/* Textual representation of the tokens. */ ++static struct { ++ const char *name; ++ pkcs11uriOpCodes opcode; ++} keywords[] = { ++ { PKCS11_URI_ID, pId }, ++ { PKCS11_URI_TOKEN, pToken }, ++ { PKCS11_URI_OBJECT, pObject }, ++ { PKCS11_URI_LIB_MANUF, pLibraryManufacturer }, ++ { PKCS11_URI_MANUF, pManufacturer }, ++ { PKCS11_URI_MODULE_PATH, pModulePath }, ++ { NULL, pBadOption } ++}; + -+ rv = pkcs11_del_provider_by_uri(uri); -+ pkcs11_uri_cleanup(uri); -+ return rv; ++static pkcs11uriOpCodes ++parse_token(const char *cp) ++{ ++ u_int i; ++ ++ for (i = 0; keywords[i].name; i++) ++ if (strncasecmp(cp, keywords[i].name, ++ strlen(keywords[i].name)) == 0) ++ return keywords[i].opcode; ++ ++ return pBadOption; +} + -+/* unregister provider by PKCS#11 URI */ +int -+pkcs11_del_provider_by_uri(struct pkcs11_uri *uri) ++percent_decode(char *data, char **outp) +{ - struct pkcs11_provider *p; -+ int rv = -1; -+ char *provider_uri = pkcs11_uri_get(uri); ++ char tmp[3]; ++ char *out, *tmp_end; ++ char *p = data; ++ long value; ++ size_t outlen = 0; + -+ debug3("%s(%s): called", __func__, provider_uri); - -- if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { -+ if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) { - TAILQ_REMOVE(&pkcs11_providers, p, next); - pkcs11_provider_finalize(p); - pkcs11_provider_unref(p); -- return (0); -+ rv = 0; - } -- return (-1); -+ free(provider_uri); -+ return rv; - } - - /* openssl callback for freeing an RSA key */ -@@ -183,6 +255,7 @@ pkcs11_rsa_finish(RSA *rsa) - if (k11->provider) - pkcs11_provider_unref(k11->provider); - free(k11->keyid); -+ free(k11->label); - free(k11); - } - return (rv); -@@ -311,7 +384,7 @@ pkcs11_rsa_private_decrypt(int flen, con - /* redirect private key operations for rsa key to pkcs11 token */ - static int - pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, -- CK_ATTRIBUTE *keyid_attrib, RSA *rsa) -+ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, RSA *rsa) - { - struct pkcs11_key *k11; - const RSA_METHOD *def = RSA_get_default_method(); -@@ -326,6 +399,11 @@ pkcs11_rsa_wrap(struct pkcs11_provider * - k11->keyid = xmalloc(k11->keyid_len); - memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); - } -+ if (label_attrib->ulValueLen > 0 ) { -+ k11->label = xmalloc(label_attrib->ulValueLen+1); -+ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); -+ k11->label[label_attrib->ulValueLen] = 0; -+ } - k11->orig_finish = def->finish; - memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); - k11->rsa_method.name = "pkcs11"; -@@ -373,7 +451,7 @@ pkcs11_open_session(struct pkcs11_provid - if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| - CKF_SERIAL_SESSION, NULL, NULL, &session)) - != CKR_OK) { -- error("C_OpenSession failed: %lu", rv); -+ error("C_OpenSession failed for slot %lu: %lu", slotidx, rv); - return (-1); - } - if (login_required && pin) { -@@ -397,38 +475,62 @@ pkcs11_open_session(struct pkcs11_provid - * keysp points to an (possibly empty) array with *nkeys keys. - */ - static int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG, -- CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *) -+ CK_ATTRIBUTE [], size_t, CK_ATTRIBUTE [3], struct sshkey ***, int *) - __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE)))); - - static int - pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, -- struct sshkey ***keysp, int *nkeys) -+ struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) - { -+ size_t filter_size = 1; - CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; - CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; - CK_ATTRIBUTE pubkey_filter[] = { -- { CKA_CLASS, NULL, sizeof(pubkey_class) } -+ { CKA_CLASS, NULL, sizeof(pubkey_class) }, -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 } - }; - CK_ATTRIBUTE cert_filter[] = { -- { CKA_CLASS, NULL, sizeof(cert_class) } -+ { CKA_CLASS, NULL, sizeof(cert_class) }, -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 } - }; - CK_ATTRIBUTE pubkey_attribs[] = { - { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 }, - { CKA_MODULUS, NULL, 0 }, - { CKA_PUBLIC_EXPONENT, NULL, 0 } - }; - CK_ATTRIBUTE cert_attribs[] = { - { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 }, - { CKA_SUBJECT, NULL, 0 }, - { CKA_VALUE, NULL, 0 } - }; - pubkey_filter[0].pValue = &pubkey_class; - cert_filter[0].pValue = &cert_class; - -- if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs, -- keysp, nkeys) < 0 || -- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs, -- keysp, nkeys) < 0) -+ if (uri->id != NULL) { -+ pubkey_filter[filter_size].pValue = uri->id; -+ pubkey_filter[filter_size].ulValueLen = uri->id_len; -+ cert_filter[filter_size].pValue = uri->id; -+ cert_filter[filter_size].ulValueLen = uri->id_len; -+ filter_size++; -+ } -+ if (uri->object != NULL) { -+ pubkey_filter[filter_size].pValue = uri->object; -+ pubkey_filter[filter_size].ulValueLen = strlen(uri->object); -+ pubkey_filter[filter_size].type = CKA_LABEL; -+ cert_filter[filter_size].pValue = uri->object; -+ cert_filter[filter_size].ulValueLen = strlen(uri->object); -+ cert_filter[filter_size].type = CKA_LABEL; -+ filter_size++; -+ } -+ -+ if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, -+ pubkey_attribs, keysp, nkeys) < 0 || -+ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, -+ cert_attribs, keysp, nkeys) < 0) - return (-1); - return (0); - } -@@ -446,14 +548,15 @@ pkcs11_key_included(struct sshkey ***key - - static int - pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, -- CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3], -+ CK_ATTRIBUTE filter[], size_t filter_size, CK_ATTRIBUTE attribs[4], - struct sshkey ***keysp, int *nkeys) - { - struct sshkey *key; - RSA *rsa; - X509 *x509; -- EVP_PKEY *evp; -+ EVP_PKEY *evp = NULL; - int i; -+ int nattribs = 4; - const u_char *cp; - CK_RV rv; - CK_OBJECT_HANDLE obj; -@@ -464,13 +567,13 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - f = p->function_list; - session = p->slotinfo[slotidx].session; - /* setup a filter the looks for public keys */ -- if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) { -+ if ((rv = f->C_FindObjectsInit(session, filter, filter_size)) != CKR_OK) { - error("C_FindObjectsInit failed: %lu", rv); - return (-1); - } - while (1) { - /* XXX 3 attributes in attribs[] */ -- for (i = 0; i < 3; i++) { -+ for (i = 0; i < nattribs; i++) { - attribs[i].pValue = NULL; - attribs[i].ulValueLen = 0; - } -@@ -478,22 +581,22 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - || nfound == 0) - break; - /* found a key, so figure out size of the attributes */ -- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) -+ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) - != CKR_OK) { - error("C_GetAttributeValue failed: %lu", rv); - continue; - } - /* -- * Allow CKA_ID (always first attribute) to be empty, but -- * ensure that none of the others are zero length. -+ * Allow CKA_ID (always first attribute) and CKA_LABEL (second) -+ * to be empty, but ensure that none of the others are zero length. - * XXX assumes CKA_ID is always first. - */ -- if (attribs[1].ulValueLen == 0 || -- attribs[2].ulValueLen == 0) { -+ if (attribs[2].ulValueLen == 0 || -+ attribs[3].ulValueLen == 0) { - continue; - } - /* allocate buffers for attributes */ -- for (i = 0; i < 3; i++) { -+ for (i = 0; i < nattribs; i++) { - if (attribs[i].ulValueLen > 0) { - attribs[i].pValue = xmalloc( - attribs[i].ulValueLen); -@@ -501,27 +604,27 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } - - /* -- * retrieve ID, modulus and public exponent of RSA key, -- * or ID, subject and value for certificates. -+ * retrieve ID, label, modulus and public exponent of RSA key, -+ * or ID, label, subject and value for certificates. - */ - rsa = NULL; -- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) -+ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) - != CKR_OK) { - error("C_GetAttributeValue failed: %lu", rv); -- } else if (attribs[1].type == CKA_MODULUS ) { -+ } else if (attribs[2].type == CKA_MODULUS ) { - if ((rsa = RSA_new()) == NULL) { - error("RSA_new failed"); - } else { -- rsa->n = BN_bin2bn(attribs[1].pValue, -- attribs[1].ulValueLen, NULL); -- rsa->e = BN_bin2bn(attribs[2].pValue, -+ rsa->n = BN_bin2bn(attribs[2].pValue, - attribs[2].ulValueLen, NULL); -+ rsa->e = BN_bin2bn(attribs[3].pValue, -+ attribs[3].ulValueLen, NULL); - } - } else { -- cp = attribs[2].pValue; -+ cp = attribs[3].pValue; - if ((x509 = X509_new()) == NULL) { - error("X509_new failed"); -- } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen) -+ } else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen) - == NULL) { - error("d2i_X509 failed"); - } else if ((evp = X509_get_pubkey(x509)) == NULL || -@@ -534,9 +637,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } - if (x509) - X509_free(x509); -+ EVP_PKEY_free(evp); - } - if (rsa && rsa->n && rsa->e && -- pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { -+ pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { - if ((key = sshkey_new(KEY_UNSPEC)) == NULL) - fatal("sshkey_new failed"); - key->rsa = rsa; -@@ -555,7 +659,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } else if (rsa) { - RSA_free(rsa); - } -- for (i = 0; i < 3; i++) -+ for (i = 0; i < nattribs; i++) - free(attribs[i].pValue); - } - if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) -@@ -567,6 +671,31 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - int - pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) - { -+ int rv; -+ struct pkcs11_uri *uri; -+ -+ debug("%s: called, provider_id = %s", __func__, provider_id); -+ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { -+ if (pkcs11_uri_parse(provider_id, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI"); -+ } else { -+ uri->module_path = strdup(provider_id); ++ out = malloc(strlen(data)+1); /* upper bound */ ++ if (out == NULL) ++ return -1; ++ while (*p != '\0') { ++ switch (*p) { ++ case '%': ++ p++; ++ if (*p == '\0') ++ goto fail; ++ tmp[0] = *p++; ++ if (*p == '\0') ++ goto fail; ++ tmp[1] = *p++; ++ tmp[2] = '\0'; ++ tmp_end = NULL; ++ value = strtol(tmp, &tmp_end, 16); ++ if (tmp_end != tmp+2) ++ goto fail; ++ else ++ out[outlen++] = (char) value; ++ break; ++ default: ++ out[outlen++] = *p++; ++ break; ++ } + } + -+ rv = pkcs11_add_provider_by_uri(uri, pin, keyp); -+ pkcs11_uri_cleanup(uri); -+ return rv; ++ /* zero terminate */ ++ out[outlen] = '\0'; ++ *outp = out; ++ return outlen; ++fail: ++ free(out); ++ return -1; +} + -+int -+pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, struct sshkey ***keyp) -+{ - int nkeys, need_finalize = 0; - struct pkcs11_provider *p = NULL; - void *handle = NULL; -@@ -575,11 +704,26 @@ pkcs11_add_provider(char *provider_id, c - CK_FUNCTION_LIST *f = NULL; - CK_TOKEN_INFO *token; - CK_ULONG i; -+ char *provider_id = NULL; -+ char *provider_uri = pkcs11_uri_get(uri); -+ -+ debug("%s: called, provider_uri = %s", __func__, provider_uri); -+ -+ /* if no provider specified, fallback to p11-kit */ -+ if (uri->module_path == NULL) { -+#ifdef PKCS11_DEFAULT_PROVIDER -+ provider_id = strdup(PKCS11_DEFAULT_PROVIDER); -+#else -+ error("%s: No module path provided", __func__); -+ goto fail; -+#endif -+ } else -+ provider_id = strdup(uri->module_path); - - *keyp = NULL; -- if (pkcs11_provider_lookup(provider_id) != NULL) { -+ if (pkcs11_provider_lookup(provider_uri) != NULL) { - debug("%s: provider already registered: %s", -- __func__, provider_id); -+ __func__, provider_uri); - goto fail; - } - /* open shared pkcs11-libarary */ -@@ -592,31 +736,38 @@ pkcs11_add_provider(char *provider_id, c - goto fail; - } - p = xcalloc(1, sizeof(*p)); -- p->name = xstrdup(provider_id); -+ p->name = provider_uri; -+ p->module_path = provider_id; - p->handle = handle; - /* setup the pkcs11 callbacks */ - if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { - error("C_GetFunctionList for provider %s failed: %lu", -- provider_id, rv); -+ provider_uri, rv); - goto fail; - } - p->function_list = f; - if ((rv = f->C_Initialize(NULL)) != CKR_OK) { - error("C_Initialize for provider %s failed: %lu", -- provider_id, rv); -+ provider_uri, rv); - goto fail; - } - need_finalize = 1; - if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { - error("C_GetInfo for provider %s failed: %lu", -- provider_id, rv); -+ provider_uri, rv); - goto fail; - } - rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); -+ if (uri->lib_manuf != NULL && -+ strcmp(uri->lib_manuf, p->info.manufacturerID)) { -+ debug("%s: Skipping provider %s not matching library_manufacturer", -+ __func__, p->info.manufacturerID); -+ goto fail; -+ } - rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); - debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d" - " libraryDescription <%s> libraryVersion %d.%d", -- provider_id, -+ provider_uri, - p->info.manufacturerID, - p->info.cryptokiVersion.major, - p->info.cryptokiVersion.minor, -@@ -629,14 +780,14 @@ pkcs11_add_provider(char *provider_id, c - } - if (p->nslots == 0) { - debug("%s: provider %s returned no slots", __func__, -- provider_id); -+ provider_uri); - goto fail; - } - p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); - if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) - != CKR_OK) { - error("C_GetSlotList for provider %s failed: %lu", -- provider_id, rv); -+ provider_uri, rv); - goto fail; - } - p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); -@@ -647,39 +798,54 @@ pkcs11_add_provider(char *provider_id, c - if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) - != CKR_OK) { - error("C_GetTokenInfo for provider %s slot %lu " -- "failed: %lu", provider_id, (unsigned long)i, rv); -+ "failed: %lu", provider_uri, (unsigned long)i, rv); - continue; - } - if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { - debug2("%s: ignoring uninitialised token in " - "provider %s slot %lu", __func__, -- provider_id, (unsigned long)i); -+ provider_uri, (unsigned long)i); - continue; - } - rmspace(token->label, sizeof(token->label)); - rmspace(token->manufacturerID, sizeof(token->manufacturerID)); - rmspace(token->model, sizeof(token->model)); - rmspace(token->serialNumber, sizeof(token->serialNumber)); -+ if (uri->token != NULL && -+ strcmp(token->label, uri->token) != 0) { -+ debug2("%s: ignoring token not matching label (%s) " -+ "specified by PKCS#11 URI in slot %lu", __func__, -+ token->label, (unsigned long)i); -+ continue; -+ } -+ if (uri->manuf != NULL && -+ strcmp(token->manufacturerID, uri->manuf) != 0) { -+ debug2("%s: ignoring token not matching requrested " -+ "manufacturerID (%s) specified by PKCS#11 URI in " -+ "slot %lu", __func__, -+ token->manufacturerID, (unsigned long)i); -+ continue; -+ } - debug("provider %s slot %lu: label <%s> manufacturerID <%s> " - "model <%s> serial <%s> flags 0x%lx", -- provider_id, (unsigned long)i, -+ provider_uri, (unsigned long)i, - token->label, token->manufacturerID, token->model, - token->serialNumber, token->flags); - /* open session, login with pin and retrieve public keys */ - if (pkcs11_open_session(p, i, pin) == 0) -- pkcs11_fetch_keys(p, i, keyp, &nkeys); -+ pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); - } - if (nkeys > 0) { - TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); - p->refcount++; /* add to provider list */ - return (nkeys); - } -- debug("%s: provider %s returned no keys", __func__, provider_id); -+ debug("%s: provider %s returned no keys", __func__, provider_uri); - /* don't add the provider, since it does not have any keys */ - fail: - if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) - error("C_Finalize for provider %s failed: %lu", -- provider_id, rv); -+ provider_uri, rv); - if (p) { - free(p->slotlist); - free(p->slotinfo); -@@ -687,6 +853,8 @@ fail: - } - if (handle) - dlclose(handle); -+ free(provider_id); -+ free(provider_uri); - return (-1); - } - -diff -up openssh-7.6p1/ssh-pkcs11.h.pkcs11-uri openssh-7.6p1/ssh-pkcs11.h ---- openssh-7.6p1/ssh-pkcs11.h.pkcs11-uri 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/ssh-pkcs11.h 2018-02-16 12:40:58.347180190 +0100 -@@ -14,10 +14,15 @@ - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -+ -+#include "ssh-pkcs11-uri.h" -+ - int pkcs11_init(int); - void pkcs11_terminate(void); - int pkcs11_add_provider(char *, char *, struct sshkey ***); -+int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); - int pkcs11_del_provider(char *); -+int pkcs11_uri_write(const struct sshkey *, FILE *); - - #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) - #undef ENABLE_PKCS11 -diff -up openssh-7.6p1/ssh-pkcs11-uri.c.pkcs11-uri openssh-7.6p1/ssh-pkcs11-uri.c ---- openssh-7.6p1/ssh-pkcs11-uri.c.pkcs11-uri 2018-02-16 12:40:58.347180190 +0100 -+++ openssh-7.6p1/ssh-pkcs11-uri.c 2018-02-16 12:40:58.347180190 +0100 -@@ -0,0 +1,399 @@ -+/* -+ * Copyright (c) 2017 Red Hat -+ * -+ * Authors: Jakub Jelen -+ * -+ * Permission to use, copy, modify, and distribute this software for any -+ * purpose with or without fee is hereby granted, provided that the above -+ * copyright notice and this permission notice appear in all copies. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ */ -+ -+#include "includes.h" -+ -+#ifdef ENABLE_PKCS11 -+ -+#include -+#include -+ -+#include "sshkey.h" -+#include "log.h" -+ -+#define CRYPTOKI_COMPAT -+#include "pkcs11.h" -+ -+#include "ssh-pkcs11-uri.h" -+ -+#define PKCS11_URI_PATH_SEPARATOR ";" -+#define PKCS11_URI_QUERY_SEPARATOR "&" -+#define PKCS11_URI_VALUE_SEPARATOR "=" -+#define PKCS11_URI_ID "id" -+#define PKCS11_URI_TOKEN "token" -+#define PKCS11_URI_OBJECT "object" -+#define PKCS11_URI_LIB_MANUF "library-manufacturer" -+#define PKCS11_URI_MANUF "manufacturer" -+#define PKCS11_URI_MODULE_PATH "module-path" -+ -+/* Keyword tokens. */ -+typedef enum { -+ pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath, -+ pBadOption -+} pkcs11uriOpCodes; -+ -+/* Textual representation of the tokens. */ -+static struct { -+ const char *name; -+ pkcs11uriOpCodes opcode; -+} keywords[] = { -+ { PKCS11_URI_ID, pId }, -+ { PKCS11_URI_TOKEN, pToken }, -+ { PKCS11_URI_OBJECT, pObject }, -+ { PKCS11_URI_LIB_MANUF, pLibraryManufacturer }, -+ { PKCS11_URI_MANUF, pManufacturer }, -+ { PKCS11_URI_MODULE_PATH, pModulePath }, -+ { NULL, pBadOption } -+}; -+ -+static pkcs11uriOpCodes -+parse_token(const char *cp) -+{ -+ u_int i; -+ -+ for (i = 0; keywords[i].name; i++) -+ if (strncasecmp(cp, keywords[i].name, -+ strlen(keywords[i].name)) == 0) -+ return keywords[i].opcode; -+ -+ return pBadOption; -+} -+ -+int -+percent_decode(char *data, char **outp) -+{ -+ char tmp[3]; -+ char *out, *tmp_end; -+ char *p = data; -+ long value; -+ size_t outlen = 0; -+ -+ out = malloc(strlen(data)+1); /* upper bound */ -+ if (out == NULL) -+ return -1; -+ while (*p != '\0') { -+ switch (*p) { -+ case '%': -+ p++; -+ if (*p == '\0') -+ goto fail; -+ tmp[0] = *p++; -+ if (*p == '\0') -+ goto fail; -+ tmp[1] = *p++; -+ tmp[2] = '\0'; -+ tmp_end = NULL; -+ value = strtol(tmp, &tmp_end, 16); -+ if (tmp_end != tmp+2) -+ goto fail; -+ else -+ out[outlen++] = (char) value; -+ break; -+ default: -+ out[outlen++] = *p++; -+ break; -+ } -+ } -+ -+ /* zero terminate */ -+ out[outlen] = '\0'; -+ *outp = out; -+ return outlen; -+fail: -+ free(out); -+ return -1; -+} -+ -+struct sshbuf * -+percent_encode(const char *data, size_t length, const char *whitelist) ++struct sshbuf * ++percent_encode(const char *data, size_t length, const char *whitelist) +{ + struct sshbuf *b = NULL; + char tmp[4], *cp; @@ -4412,7 +3720,7 @@ diff -up openssh-7.6p1/ssh-pkcs11-uri.c.pkcs11-uri openssh-7.6p1/ssh-pkcs11-uri. + rv = -1; + } + percent_decode(tok + key_len, &pkcs11->module_path); -+ debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI\n", ++ debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI", + __func__, pkcs11->module_path); + /* } else if ( pin-value ) { */ + } else { @@ -4426,9 +3734,11 @@ diff -up openssh-7.6p1/ssh-pkcs11-uri.c.pkcs11-uri openssh-7.6p1/ssh-pkcs11-uri. +} + +#endif /* ENABLE_PKCS11 */ -diff -up openssh-7.6p1/ssh-pkcs11-uri.h.pkcs11-uri openssh-7.6p1/ssh-pkcs11-uri.h ---- openssh-7.6p1/ssh-pkcs11-uri.h.pkcs11-uri 2018-02-16 12:40:58.347180190 +0100 -+++ openssh-7.6p1/ssh-pkcs11-uri.h 2018-02-16 12:40:58.347180190 +0100 +diff --git a/ssh-pkcs11-uri.h b/ssh-pkcs11-uri.h +new file mode 100644 +index 00000000..609c4df1 +--- /dev/null ++++ b/ssh-pkcs11-uri.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 Red Hat @@ -4448,83 +3758,592 @@ diff -up openssh-7.6p1/ssh-pkcs11-uri.h.pkcs11-uri openssh-7.6p1/ssh-pkcs11-uri. + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + -+#define PKCS11_URI_SCHEME "pkcs11:" -+#define PKCS11_URI_WHITELIST "abcdefghijklmnopqrstuvwxyz" \ -+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ -+ "0123456789_-.()" ++#define PKCS11_URI_SCHEME "pkcs11:" ++#define PKCS11_URI_WHITELIST "abcdefghijklmnopqrstuvwxyz" \ ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ ++ "0123456789_-.()" ++ ++struct pkcs11_uri { ++ /* path */ ++ char *id; ++ size_t id_len; ++ char *token; ++ char *object; ++ char *lib_manuf; ++ char *manuf; ++ /* query */ ++ char *module_path; ++}; ++ ++struct pkcs11_uri *pkcs11_uri_init(); ++void pkcs11_uri_cleanup(struct pkcs11_uri *); ++int pkcs11_uri_parse(const char *, struct pkcs11_uri *); ++struct pkcs11_uri *pkcs11_uri_init(); ++char * pkcs11_uri_get(struct pkcs11_uri *uri); ++ +diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c +index 88c9d6e2..a29b4451 100644 +--- a/ssh-pkcs11.c ++++ b/ssh-pkcs11.c +@@ -48,8 +48,8 @@ struct pkcs11_slotinfo { + int logged_in; + }; + +-struct pkcs11_provider { +- char *name; ++struct pkcs11_module { ++ char *module_path; + void *handle; + CK_FUNCTION_LIST *function_list; + CK_INFO info; +@@ -58,6 +58,13 @@ struct pkcs11_provider { + struct pkcs11_slotinfo *slotinfo; + int valid; + int refcount; ++}; ++ ++struct pkcs11_provider { ++ char *name; ++ struct pkcs11_module *module; /* can be shared between various providers */ ++ int refcount; ++ int valid; + TAILQ_ENTRY(pkcs11_provider) next; + }; + +@@ -70,10 +77,46 @@ struct pkcs11_key { + RSA_METHOD rsa_method; + char *keyid; + int keyid_len; ++ char *label; + }; + + int pkcs11_interactive = 0; + ++/* ++ * This can't be in the ssh-pkcs11-uri, becase we can not depend on ++ * PKCS#11 structures in ssh-agent (using client-helper communication) ++ */ ++int ++pkcs11_uri_write(const struct sshkey *key, FILE *f) ++{ ++ char *p = NULL; ++ struct pkcs11_uri uri; ++ struct pkcs11_key *k11; ++ ++ /* sanity - is it a RSA key with associated app_data? */ ++ if (key->type != KEY_RSA || ++ (k11 = RSA_get_app_data(key->rsa)) == NULL) ++ return -1; ++ ++ /* omit type -- we are looking for private-public or private-certificate pairs */ ++ uri.id = k11->keyid; ++ uri.id_len = k11->keyid_len; ++ uri.token = k11->provider->module->slotinfo[k11->slotidx].token.label; ++ uri.object = k11->label; ++ uri.module_path = k11->provider->module->module_path; ++ uri.lib_manuf = k11->provider->module->info.manufacturerID; ++ uri.manuf = k11->provider->module->slotinfo[k11->slotidx].token.manufacturerID; ++ ++ p = pkcs11_uri_get(&uri); ++ /* do not cleanup -- we do not allocate here, only reference */ ++ if (p == NULL) ++ return -1; ++ ++ fprintf(f, " %s", p); ++ free(p); ++ return 0; ++} ++ + int + pkcs11_init(int interactive) + { +@@ -89,26 +132,63 @@ pkcs11_init(int interactive) + * this is called when a provider gets unregistered. + */ + static void +-pkcs11_provider_finalize(struct pkcs11_provider *p) ++pkcs11_module_finalize(struct pkcs11_module *m) + { + CK_RV rv; + CK_ULONG i; + +- debug("pkcs11_provider_finalize: %p refcount %d valid %d", +- p, p->refcount, p->valid); +- if (!p->valid) ++ debug("%s: %p refcount %d valid %d", __func__, ++ m, m->refcount, m->valid); ++ if (!m->valid) + return; +- for (i = 0; i < p->nslots; i++) { +- if (p->slotinfo[i].session && +- (rv = p->function_list->C_CloseSession( +- p->slotinfo[i].session)) != CKR_OK) ++ for (i = 0; i < m->nslots; i++) { ++ if (m->slotinfo[i].session && ++ (rv = m->function_list->C_CloseSession( ++ m->slotinfo[i].session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + } +- if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) ++ if ((rv = m->function_list->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize failed: %lu", rv); ++ m->valid = 0; ++ m->function_list = NULL; ++ dlclose(m->handle); ++} ++ ++/* ++ * remove a reference to the pkcs11 module. ++ * called when a provider is unregistered. ++ */ ++static void ++pkcs11_module_unref(struct pkcs11_module *m) ++{ ++ debug("%s: %p refcount %d", __func__, m, m->refcount); ++ if (--m->refcount <= 0) { ++ pkcs11_module_finalize(m); ++ if (m->valid) ++ error("%s: %p still valid", __func__, m); ++ free(m->slotlist); ++ free(m->slotinfo); ++ free(m->module_path); ++ free(m); ++ } ++} ++ ++/* ++ * finalize a provider shared libarary, it's no longer usable. ++ * however, there might still be keys referencing this provider, ++ * so the actuall freeing of memory is handled by pkcs11_provider_unref(). ++ * this is called when a provider gets unregistered. ++ */ ++static void ++pkcs11_provider_finalize(struct pkcs11_provider *p) ++{ ++ debug("%s: %p refcount %d valid %d", __func__, ++ p, p->refcount, p->valid); ++ if (!p->valid) ++ return; ++ pkcs11_module_unref(p->module); ++ p->module = NULL; + p->valid = 0; +- p->function_list = NULL; +- dlclose(p->handle); + } + + /* +@@ -118,12 +198,11 @@ pkcs11_provider_finalize(struct pkcs11_provider *p) + static void + pkcs11_provider_unref(struct pkcs11_provider *p) + { +- debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); ++ debug("%s: %p refcount %d", __func__, p, p->refcount); + if (--p->refcount <= 0) { +- if (p->valid) +- error("pkcs11_provider_unref: %p still valid", p); +- free(p->slotlist); +- free(p->slotinfo); ++ if (p->module) ++ pkcs11_module_unref(p->module); ++ free(p->name); + free(p); + } + } +@@ -141,6 +220,20 @@ pkcs11_terminate(void) + } + } + ++/* lookup provider by module path */ ++static struct pkcs11_module * ++pkcs11_provider_lookup_module(char *module_path) ++{ ++ struct pkcs11_provider *p; ++ ++ TAILQ_FOREACH(p, &pkcs11_providers, next) { ++ debug("check %p %s (%s)", p, p->name, p->module->module_path); ++ if (!strcmp(module_path, p->module->module_path)) ++ return (p->module); ++ } ++ return (NULL); ++} ++ + /* lookup provider by name */ + static struct pkcs11_provider * + pkcs11_provider_lookup(char *provider_id) +@@ -155,19 +248,52 @@ pkcs11_provider_lookup(char *provider_id) + return (NULL); + } + ++int pkcs11_del_provider_by_uri(struct pkcs11_uri *); ++ + /* unregister provider by name */ + int + pkcs11_del_provider(char *provider_id) ++{ ++ int rv; ++ struct pkcs11_uri *uri; ++ ++ debug("%s: called, provider_id = %s", __func__, provider_id); ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { ++ if (pkcs11_uri_parse(provider_id, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ } else { ++ uri->module_path = strdup(provider_id); ++ } ++ ++ rv = pkcs11_del_provider_by_uri(uri); ++ pkcs11_uri_cleanup(uri); ++ return rv; ++} ++ ++/* unregister provider by PKCS#11 URI */ ++int ++pkcs11_del_provider_by_uri(struct pkcs11_uri *uri) + { + struct pkcs11_provider *p; ++ int rv = -1; ++ char *provider_uri = pkcs11_uri_get(uri); + +- if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { ++ debug3("%s(%s): called", __func__, provider_uri); ++ ++ if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); +- return (0); ++ rv = 0; + } +- return (-1); ++ free(provider_uri); ++ return rv; + } + + /* openssl callback for freeing an RSA key */ +@@ -183,6 +309,7 @@ pkcs11_rsa_finish(RSA *rsa) + if (k11->provider) + pkcs11_provider_unref(k11->provider); + free(k11->keyid); ++ free(k11->label); + free(k11); + } + return (rv); +@@ -199,8 +327,8 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, + CK_RV rv; + int ret = -1; + +- f = p->function_list; +- session = p->slotinfo[slotidx].session; ++ f = p->module->function_list; ++ session = p->module->slotinfo[slotidx].session; + if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { + error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); + return (-1); +@@ -247,12 +375,13 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + error("RSA_get_app_data failed for rsa %p", rsa); + return (-1); + } +- if (!k11->provider || !k11->provider->valid) { ++ if (!k11->provider || !k11->provider->valid || !k11->provider->module ++ || !k11->provider->module->valid) { + error("no pkcs11 (valid) provider for rsa %p", rsa); + return (-1); + } +- f = k11->provider->function_list; +- si = &k11->provider->slotinfo[k11->slotidx]; ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; + if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { + if (!pkcs11_interactive) { + error("need pin entry%s", (si->token.flags & +@@ -311,7 +440,7 @@ pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + /* redirect private key operations for rsa key to pkcs11 token */ + static int + pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, +- CK_ATTRIBUTE *keyid_attrib, RSA *rsa) ++ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, RSA *rsa) + { + struct pkcs11_key *k11; + const RSA_METHOD *def = RSA_get_default_method(); +@@ -326,6 +455,11 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + } ++ if (label_attrib->ulValueLen > 0 ) { ++ k11->label = xmalloc(label_attrib->ulValueLen+1); ++ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); ++ k11->label[label_attrib->ulValueLen] = 0; ++ } + k11->orig_finish = def->finish; + memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); + k11->rsa_method.name = "pkcs11"; +@@ -372,16 +506,16 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) + CK_SESSION_HANDLE session; + int login_required; + +- f = p->function_list; +- login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; ++ f = p->module->function_list; ++ login_required = p->module->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; + if (pin && login_required && !strlen(pin)) { + error("pin required"); + return (-1); + } +- if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| ++ if ((rv = f->C_OpenSession(p->module->slotlist[slotidx], CKF_RW_SESSION| + CKF_SERIAL_SESSION, NULL, NULL, &session)) + != CKR_OK) { +- error("C_OpenSession failed: %lu", rv); ++ error("C_OpenSession failed for slot %lu: %lu", slotidx, rv); + return (-1); + } + if (login_required && pin) { +@@ -393,9 +527,9 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) + error("C_CloseSession failed: %lu", rv); + return (-1); + } +- p->slotinfo[slotidx].logged_in = 1; ++ p->module->slotinfo[slotidx].logged_in = 1; + } +- p->slotinfo[slotidx].session = session; ++ p->module->slotinfo[slotidx].session = session; + return (0); + } + +@@ -405,38 +539,62 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) + * keysp points to an (possibly empty) array with *nkeys keys. + */ + static int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG, +- CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *) ++ CK_ATTRIBUTE [], size_t, CK_ATTRIBUTE [3], struct sshkey ***, int *) + __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE)))); + + static int + pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, +- struct sshkey ***keysp, int *nkeys) ++ struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) + { ++ size_t filter_size = 1; + CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; + CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; + CK_ATTRIBUTE pubkey_filter[] = { +- { CKA_CLASS, NULL, sizeof(pubkey_class) } ++ { CKA_CLASS, NULL, sizeof(pubkey_class) }, ++ { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 } + }; + CK_ATTRIBUTE cert_filter[] = { +- { CKA_CLASS, NULL, sizeof(cert_class) } ++ { CKA_CLASS, NULL, sizeof(cert_class) }, ++ { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 } + }; + CK_ATTRIBUTE pubkey_attribs[] = { + { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 }, + { CKA_MODULUS, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 } + }; + CK_ATTRIBUTE cert_attribs[] = { + { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + pubkey_filter[0].pValue = &pubkey_class; + cert_filter[0].pValue = &cert_class; + +- if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs, +- keysp, nkeys) < 0 || +- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs, +- keysp, nkeys) < 0) ++ if (uri->id != NULL) { ++ pubkey_filter[filter_size].pValue = uri->id; ++ pubkey_filter[filter_size].ulValueLen = uri->id_len; ++ cert_filter[filter_size].pValue = uri->id; ++ cert_filter[filter_size].ulValueLen = uri->id_len; ++ filter_size++; ++ } ++ if (uri->object != NULL) { ++ pubkey_filter[filter_size].pValue = uri->object; ++ pubkey_filter[filter_size].ulValueLen = strlen(uri->object); ++ pubkey_filter[filter_size].type = CKA_LABEL; ++ cert_filter[filter_size].pValue = uri->object; ++ cert_filter[filter_size].ulValueLen = strlen(uri->object); ++ cert_filter[filter_size].type = CKA_LABEL; ++ filter_size++; ++ } ++ ++ if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, ++ pubkey_attribs, keysp, nkeys) < 0 || ++ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, ++ cert_attribs, keysp, nkeys) < 0) + return (-1); + return (0); + } +@@ -454,14 +612,15 @@ pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) + + static int + pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, +- CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3], ++ CK_ATTRIBUTE filter[], size_t filter_size, CK_ATTRIBUTE attribs[4], + struct sshkey ***keysp, int *nkeys) + { + struct sshkey *key; + RSA *rsa; + X509 *x509; +- EVP_PKEY *evp; ++ EVP_PKEY *evp = NULL; + int i; ++ int nattribs = 4; + const u_char *cp; + CK_RV rv; + CK_OBJECT_HANDLE obj; +@@ -470,16 +629,15 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f; + +- f = p->function_list; +- session = p->slotinfo[slotidx].session; ++ f = p->module->function_list; ++ session = p->module->slotinfo[slotidx].session; + /* setup a filter the looks for public keys */ +- if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) { ++ if ((rv = f->C_FindObjectsInit(session, filter, filter_size)) != CKR_OK) { + error("C_FindObjectsInit failed: %lu", rv); + return (-1); + } + while (1) { +- /* XXX 3 attributes in attribs[] */ +- for (i = 0; i < 3; i++) { ++ for (i = 0; i < nattribs; i++) { + attribs[i].pValue = NULL; + attribs[i].ulValueLen = 0; + } +@@ -487,22 +645,22 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + || nfound == 0) + break; + /* found a key, so figure out size of the attributes */ +- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) ++ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) + != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + continue; + } + /* +- * Allow CKA_ID (always first attribute) to be empty, but +- * ensure that none of the others are zero length. ++ * Allow CKA_ID (always first attribute) and CKA_LABEL (second) ++ * to be empty, but ensure that none of the others are zero length. + * XXX assumes CKA_ID is always first. + */ +- if (attribs[1].ulValueLen == 0 || +- attribs[2].ulValueLen == 0) { ++ if (attribs[2].ulValueLen == 0 || ++ attribs[3].ulValueLen == 0) { + continue; + } + /* allocate buffers for attributes */ +- for (i = 0; i < 3; i++) { ++ for (i = 0; i < nattribs; i++) { + if (attribs[i].ulValueLen > 0) { + attribs[i].pValue = xmalloc( + attribs[i].ulValueLen); +@@ -510,27 +668,27 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + } + + /* +- * retrieve ID, modulus and public exponent of RSA key, +- * or ID, subject and value for certificates. ++ * retrieve ID, label, modulus and public exponent of RSA key, ++ * or ID, label, subject and value for certificates. + */ + rsa = NULL; +- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) ++ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) + != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); +- } else if (attribs[1].type == CKA_MODULUS ) { ++ } else if (attribs[2].type == CKA_MODULUS ) { + if ((rsa = RSA_new()) == NULL) { + error("RSA_new failed"); + } else { +- rsa->n = BN_bin2bn(attribs[1].pValue, +- attribs[1].ulValueLen, NULL); +- rsa->e = BN_bin2bn(attribs[2].pValue, ++ rsa->n = BN_bin2bn(attribs[2].pValue, + attribs[2].ulValueLen, NULL); ++ rsa->e = BN_bin2bn(attribs[3].pValue, ++ attribs[3].ulValueLen, NULL); + } + } else { +- cp = attribs[2].pValue; ++ cp = attribs[3].pValue; + if ((x509 = X509_new()) == NULL) { + error("X509_new failed"); +- } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen) ++ } else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen) + == NULL) { + error("d2i_X509 failed"); + } else if ((evp = X509_get_pubkey(x509)) == NULL || +@@ -546,9 +704,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + error("RSAPublicKey_dup"); + } + X509_free(x509); ++ EVP_PKEY_free(evp); + } + if (rsa && rsa->n && rsa->e && +- pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { ++ pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + key->rsa = rsa; +@@ -569,7 +728,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + } else if (rsa) { + RSA_free(rsa); + } +- for (i = 0; i < 3; i++) ++ for (i = 0; i < nattribs; i++) + free(attribs[i].pValue); + } + if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) +@@ -581,126 +740,238 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + int + pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) + { +- int nkeys, need_finalize = 0; +- struct pkcs11_provider *p = NULL; ++ int rv; ++ struct pkcs11_uri *uri; ++ ++ debug("%s: called, provider_id = %s", __func__, provider_id); ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); + -+struct pkcs11_uri { -+ /* path */ -+ char *id; -+ size_t id_len; -+ char *token; -+ char *object; -+ char *lib_manuf; -+ char *manuf; -+ /* query */ -+ char *module_path; -+}; ++ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { ++ if (pkcs11_uri_parse(provider_id, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ } else { ++ uri->module_path = strdup(provider_id); ++ } + -+struct pkcs11_uri *pkcs11_uri_init(); -+void pkcs11_uri_cleanup(struct pkcs11_uri *); -+int pkcs11_uri_parse(const char *, struct pkcs11_uri *); -+struct pkcs11_uri *pkcs11_uri_init(); -+char * pkcs11_uri_get(struct pkcs11_uri *uri); ++ rv = pkcs11_add_provider_by_uri(uri, pin, keyp); ++ pkcs11_uri_cleanup(uri); ++ return rv; ++} + -commit 8cd98e76449fc112685882009c95e49eb555094a -Author: Jakub Jelen -Date: Wed Feb 28 16:58:07 2018 +0100 - - Allow to load multiple keys from a single pkcs11_module - -diff --git a/ssh-pkcs11-uri.c b/ssh-pkcs11-uri.c -index 35ed6a09..da15c164 100644 ---- a/ssh-pkcs11-uri.c -+++ b/ssh-pkcs11-uri.c -@@ -383,7 +383,7 @@ pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11) - rv = -1; - } - percent_decode(tok + key_len, &pkcs11->module_path); -- debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI\n", -+ debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI", - __func__, pkcs11->module_path); - /* } else if ( pin-value ) { */ - } else { -diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c -index f54d905e..c9a79176 100644 ---- a/ssh-pkcs11.c -+++ b/ssh-pkcs11.c -@@ -187,8 +187,8 @@ pkcs11_provider_lookup(char *provider_id) - struct pkcs11_provider *p; - - TAILQ_FOREACH(p, &pkcs11_providers, next) { -- debug("check %p %s", p, p->name); -- if (!strcmp(provider_id, p->name)) -+ debug("check %p %s", p, p->module_path); -+ if (!strcmp(provider_id, p->module_path)) - return (p); - } - return (NULL); -@@ -232,7 +232,7 @@ pkcs11_del_provider_by_uri(struct pkcs11_uri *uri) - - debug3("%s(%s): called", __func__, provider_uri); - -- if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) { -+ if ((p = pkcs11_provider_lookup(uri->module_path)) != NULL) { - TAILQ_REMOVE(&pkcs11_providers, p, next); - pkcs11_provider_finalize(p); - pkcs11_provider_unref(p); -@@ -707,42 +707,42 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) - return rv; - } - --int --pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, struct sshkey ***keyp) +struct pkcs11_provider * +pkcs11_provider_initialize(struct pkcs11_uri *uri) - { -- int nkeys, need_finalize = 0; -- struct pkcs11_provider *p = NULL; ++{ + int need_finalize = 0; void *handle = NULL; CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); @@ -4532,38 +4351,44 @@ index f54d905e..c9a79176 100644 CK_FUNCTION_LIST *f = NULL; CK_TOKEN_INFO *token; CK_ULONG i; -- char *provider_id = NULL; -- char *provider_uri = pkcs11_uri_get(uri); -- -- debug("%s: called, provider_uri = %s", __func__, provider_uri); + char *provider_module = NULL; + struct pkcs11_provider *p; ++ struct pkcs11_module *m; - /* if no provider specified, fallback to p11-kit */ - if (uri->module_path == NULL) { - #ifdef PKCS11_DEFAULT_PROVIDER -- provider_id = strdup(PKCS11_DEFAULT_PROVIDER); +- *keyp = NULL; +- if (pkcs11_provider_lookup(provider_id) != NULL) { +- debug("%s: provider already registered: %s", +- __func__, provider_id); ++ /* if no provider specified, fallback to p11-kit */ ++ if (uri->module_path == NULL) { ++#ifdef PKCS11_DEFAULT_PROVIDER + provider_module = strdup(PKCS11_DEFAULT_PROVIDER); - #else - error("%s: No module path provided", __func__); ++#else ++ error("%s: No module path provided", __func__); goto fail; - #endif - } else -- provider_id = strdup(uri->module_path); ++#endif ++ } else + provider_module = strdup(uri->module_path); - -- *keyp = NULL; -- if (pkcs11_provider_lookup(provider_uri) != NULL) { -- debug("%s: provider already registered: %s", -- __func__, provider_uri); -- goto fail; -+ if ((p = pkcs11_provider_lookup(provider_module)) != NULL) { -+ debug("%s: provider already initialized: %s", ++ ++ p = xcalloc(1, sizeof(*p)); ++ p->name = pkcs11_uri_get(uri); ++ ++ if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL ++ && m->valid) { ++ debug("%s: provider module already initialized: %s", + __func__, provider_module); -+ p->refcount++; -+ /* Skip the initialization -- rereading the slot list would -+ * be more complicated job */ ++ free(provider_module); ++ /* Skip the initialization of PKCS#11 module */ ++ m->refcount++; ++ p->module = m; ++ p->valid = 1; ++ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); ++ p->refcount++; /* add to provider list */ + return p; ++ } else { ++ m = xcalloc(1, sizeof(*m)); ++ p->module = m; ++ m->refcount++; } + /* open shared pkcs11-libarary */ @@ -4574,85 +4399,107 @@ index f54d905e..c9a79176 100644 goto fail; } if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { -@@ -750,25 +750,25 @@ pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, struct sshkey ***k + error("dlsym(C_GetFunctionList) failed: %s", dlerror()); goto fail; } - p = xcalloc(1, sizeof(*p)); -- p->name = provider_uri; -- p->module_path = provider_id; -+ p->name = pkcs11_uri_get(uri);; -+ p->module_path = provider_module; - p->handle = handle; +- p = xcalloc(1, sizeof(*p)); +- p->name = xstrdup(provider_id); +- p->handle = handle; ++ m->handle = handle; /* setup the pkcs11 callbacks */ if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { error("C_GetFunctionList for provider %s failed: %lu", -- provider_uri, rv); +- provider_id, rv); + provider_module, rv); goto fail; } - p->function_list = f; +- p->function_list = f; ++ m->function_list = f; if ((rv = f->C_Initialize(NULL)) != CKR_OK) { error("C_Initialize for provider %s failed: %lu", -- provider_uri, rv); +- provider_id, rv); + provider_module, rv); goto fail; } need_finalize = 1; - if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { +- if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { ++ if ((rv = f->C_GetInfo(&m->info)) != CKR_OK) { error("C_GetInfo for provider %s failed: %lu", -- provider_uri, rv); +- provider_id, rv); + provider_module, rv); goto fail; } - rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); -@@ -781,50 +781,91 @@ pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, struct sshkey ***k - rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); +- rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); +- rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); ++ rmspace(m->info.manufacturerID, sizeof(m->info.manufacturerID)); ++ if (uri->lib_manuf != NULL && ++ strcmp(uri->lib_manuf, m->info.manufacturerID)) { ++ debug("%s: Skipping provider %s not matching library_manufacturer", ++ __func__, m->info.manufacturerID); ++ goto fail; ++ } ++ rmspace(m->info.libraryDescription, sizeof(m->info.libraryDescription)); debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d" " libraryDescription <%s> libraryVersion %d.%d", -- provider_uri, +- provider_id, +- p->info.manufacturerID, +- p->info.cryptokiVersion.major, +- p->info.cryptokiVersion.minor, +- p->info.libraryDescription, +- p->info.libraryVersion.major, +- p->info.libraryVersion.minor); +- if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { + provider_module, - p->info.manufacturerID, - p->info.cryptokiVersion.major, - p->info.cryptokiVersion.minor, - p->info.libraryDescription, - p->info.libraryVersion.major, - p->info.libraryVersion.minor); -+ - if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { ++ m->info.manufacturerID, ++ m->info.cryptokiVersion.major, ++ m->info.cryptokiVersion.minor, ++ m->info.libraryDescription, ++ m->info.libraryVersion.major, ++ m->info.libraryVersion.minor); ++ ++ if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &m->nslots)) != CKR_OK) { error("C_GetSlotList failed: %lu", rv); goto fail; } - if (p->nslots == 0) { +- if (p->nslots == 0) { ++ if (m->nslots == 0) { debug("%s: provider %s returned no slots", __func__, -- provider_uri); +- provider_id); + provider_module); goto fail; } - p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); - if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) +- p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); +- if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) ++ m->slotlist = xcalloc(m->nslots, sizeof(CK_SLOT_ID)); ++ if ((rv = f->C_GetSlotList(CK_TRUE, m->slotlist, &m->nslots)) != CKR_OK) { error("C_GetSlotList for provider %s failed: %lu", -- provider_uri, rv); +- provider_id, rv); + provider_module, rv); goto fail; } - p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); +- p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); ++ m->slotinfo = xcalloc(m->nslots, sizeof(struct pkcs11_slotinfo)); ++ m->valid = 1; p->valid = 1; - nkeys = 0; +- for (i = 0; i < p->nslots; i++) { +- token = &p->slotinfo[i].token; +- if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) + - for (i = 0; i < p->nslots; i++) { - token = &p->slotinfo[i].token; - if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) ++ for (i = 0; i < m->nslots; i++) { ++ token = &m->slotinfo[i].token; ++ if ((rv = f->C_GetTokenInfo(m->slotlist[i], token)) != CKR_OK) { error("C_GetTokenInfo for provider %s slot %lu " -- "failed: %lu", provider_uri, (unsigned long)i, rv); +- "failed: %lu", provider_id, (unsigned long)i, rv); + "failed: %lu", provider_module, (unsigned long)i, rv); continue; } if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { - debug2("%s: ignoring uninitialised token in " - "provider %s slot %lu", __func__, -- provider_uri, (unsigned long)i); +- provider_id, (unsigned long)i); continue; } rmspace(token->label, sizeof(token->label)); @@ -4660,6 +4507,8 @@ index f54d905e..c9a79176 100644 rmspace(token->model, sizeof(token->model)); rmspace(token->serialNumber, sizeof(token->serialNumber)); + } ++ m->module_path = provider_module; ++ provider_module = NULL; + + /* insert unconditionally -- remove if there will be no keys later */ + TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); @@ -4670,6 +4519,7 @@ index f54d905e..c9a79176 100644 + if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize for provider %s failed: %lu", + provider_module, rv); ++ free(provider_module); + if (handle) + dlclose(handle); + return NULL; @@ -4694,40 +4544,57 @@ index f54d905e..c9a79176 100644 + } + + nkeys = 0; -+ for (i = 0; i < p->nslots; i++) { -+ token = &p->slotinfo[i].token; ++ for (i = 0; i < p->module->nslots; i++) { ++ token = &p->module->slotinfo[i].token; + if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { + debug2("%s: ignoring uninitialised token in " + "provider %s slot %lu", __func__, + provider_uri, (unsigned long)i); + continue; + } - if (uri->token != NULL && - strcmp(token->label, uri->token) != 0) { - debug2("%s: ignoring token not matching label (%s) " -@@ -845,29 +887,21 @@ pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, struct sshkey ***k - provider_uri, (unsigned long)i, ++ if (uri->token != NULL && ++ strcmp(token->label, uri->token) != 0) { ++ debug2("%s: ignoring token not matching label (%s) " ++ "specified by PKCS#11 URI in slot %lu", __func__, ++ token->label, (unsigned long)i); ++ continue; ++ } ++ if (uri->manuf != NULL && ++ strcmp(token->manufacturerID, uri->manuf) != 0) { ++ debug2("%s: ignoring token not matching requrested " ++ "manufacturerID (%s) specified by PKCS#11 URI in " ++ "slot %lu", __func__, ++ token->manufacturerID, (unsigned long)i); ++ continue; ++ } + debug("provider %s slot %lu: label <%s> manufacturerID <%s> " + "model <%s> serial <%s> flags 0x%lx", +- provider_id, (unsigned long)i, ++ provider_uri, (unsigned long)i, token->label, token->manufacturerID, token->model, token->serialNumber, token->flags); - /* open session, login with pin and retrieve public keys */ - if (pkcs11_open_session(p, i, pin) == 0) +- pkcs11_fetch_keys(p, i, keyp, &nkeys); + /* open session if not yet opened, login with pin + * and retrieve public keys */ -+ if ((p->slotinfo[i].session != 0) || ++ if ((p->module->slotinfo[i].session != 0) || + pkcs11_open_session(p, i, pin) == 0) - pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); ++ pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); } if (nkeys > 0) { - TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); - p->refcount++; /* add to provider list */ ++ free(provider_uri); return (nkeys); } - debug("%s: provider %s returned no keys", __func__, provider_uri); +- debug("%s: provider %s returned no keys", __func__, provider_id); ++ debug("%s: provider %s returned no keys", __func__, provider_uri); /* don't add the provider, since it does not have any keys */ fail: - if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) - error("C_Finalize for provider %s failed: %lu", -- provider_uri, rv); +- provider_id, rv); if (p) { - free(p->slotlist); - free(p->slotinfo); @@ -4736,30 +4603,195 @@ index f54d905e..c9a79176 100644 } - if (handle) - dlclose(handle); -- free(provider_id); - free(provider_uri); ++ free(provider_uri); return (-1); } + +diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h +index 0ced74f2..c63a88f6 100644 +--- a/ssh-pkcs11.h ++++ b/ssh-pkcs11.h +@@ -14,10 +14,15 @@ + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ ++ ++#include "ssh-pkcs11-uri.h" ++ + int pkcs11_init(int); + void pkcs11_terminate(void); + int pkcs11_add_provider(char *, char *, struct sshkey ***); ++int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); + int pkcs11_del_provider(char *); ++int pkcs11_uri_write(const struct sshkey *, FILE *); + + #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) + #undef ENABLE_PKCS11 diff --git a/ssh.c b/ssh.c -index 120e1ec4..92bb6a20 100644 +index d3619fe2..180eb2e0 100644 --- a/ssh.c +++ b/ssh.c -@@ -2017,8 +2017,7 @@ load_pkcs11_identity(char *pkcs11_uri, char *identity_files[], - if (options.pkcs11_provider != NULL && uri->module_path == NULL) - uri->module_path = strdup(options.pkcs11_provider); +@@ -769,6 +769,14 @@ main(int ac, char **av) + options.gss_deleg_creds = 1; + break; + case 'i': ++#ifdef ENABLE_PKCS11 ++ if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(optarg, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ add_identity_file(&options, NULL, optarg, 1); ++ break; ++ } ++#endif + p = tilde_expand_filename(optarg, original_real_uid); + if (stat(p, &st) < 0) + fprintf(stderr, "Warning: Identity file %s " +@@ -1999,6 +2007,45 @@ ssh_session2(struct ssh *ssh, struct passwd *pw) + options.escape_char : SSH_ESCAPECHAR_NONE, id); + } + ++#ifdef ENABLE_PKCS11 ++static void ++load_pkcs11_identity(char *pkcs11_uri, char *identity_files[], ++ struct sshkey *identity_keys[], int *n_ids) ++{ ++ int nkeys, i; ++ struct sshkey **keys; ++ struct pkcs11_uri *uri; ++ ++ debug("identity file '%s' from pkcs#11", pkcs11_uri); ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (pkcs11_uri_parse(pkcs11_uri, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri); ++ ++ /* we need to merge URI and provider together */ ++ if (options.pkcs11_provider != NULL && uri->module_path == NULL) ++ uri->module_path = strdup(options.pkcs11_provider); ++ ++ if (options.num_identity_files < SSH_MAX_IDENTITY_FILES && ++ (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys)) > 0) { ++ for (i = 0; i < nkeys; i++) { ++ if (*n_ids >= SSH_MAX_IDENTITY_FILES) { ++ key_free(keys[i]); ++ continue; ++ } ++ identity_keys[*n_ids] = keys[i]; ++ identity_files[*n_ids] = pkcs11_uri_get(uri); ++ (*n_ids)++; ++ } ++ free(keys); ++ } ++ ++ pkcs11_uri_cleanup(uri); ++} ++#endif /* ENABLE_PKCS11 */ ++ + /* Loads all IdentityFile and CertificateFile keys */ + static void + load_public_identity_files(struct passwd *pw) +@@ -2011,10 +2058,6 @@ load_public_identity_files(struct passwd *pw) + struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; + char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; + struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; +-#ifdef ENABLE_PKCS11 +- struct sshkey **keys; +- int nkeys; +-#endif /* PKCS11 */ -- if (pkcs11_init(!options.batch_mode) == 0 && -- options.num_identity_files < SSH_MAX_IDENTITY_FILES && -+ if (options.num_identity_files < SSH_MAX_IDENTITY_FILES && - (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys)) > 0) { - for (i = 0; i < nkeys; i++) { - if (*n_ids >= SSH_MAX_IDENTITY_FILES) { -@@ -2057,6 +2056,8 @@ load_public_identity_files(struct passwd *pw) + n_ids = n_certs = 0; + memset(identity_files, 0, sizeof(identity_files)); +@@ -2023,35 +2066,48 @@ load_public_identity_files(struct passwd *pw) + memset(certificates, 0, sizeof(certificates)); #ifdef ENABLE_PKCS11 - /* handle fallback from PKCS11Provider option */ +- if (options.pkcs11_provider != NULL && +- options.num_identity_files < SSH_MAX_IDENTITY_FILES && +- (pkcs11_init(!options.batch_mode) == 0) && +- (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, +- &keys)) > 0) { +- for (i = 0; i < nkeys; i++) { +- if (n_ids >= SSH_MAX_IDENTITY_FILES) { +- key_free(keys[i]); +- continue; +- } +- identity_keys[n_ids] = keys[i]; +- identity_files[n_ids] = +- xstrdup(options.pkcs11_provider); /* XXX */ +- n_ids++; +- } +- free(keys); ++ /* handle fallback from PKCS11Provider option */ + pkcs11_init(!options.batch_mode); + - if (options.pkcs11_provider != NULL) { - struct pkcs11_uri *uri; - ++ if (options.pkcs11_provider != NULL) { ++ struct pkcs11_uri *uri; ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ /* Construct simple PKCS#11 URI to simplify access */ ++ uri->module_path = strdup(options.pkcs11_provider); ++ ++ /* Add it as any other IdentityFile */ ++ cp = pkcs11_uri_get(uri); ++ add_identity_file(&options, NULL, cp, 1); ++ free(cp); ++ ++ pkcs11_uri_cleanup(uri); + } + #endif /* ENABLE_PKCS11 */ + if ((pw = getpwuid(original_real_uid)) == NULL) + fatal("load_public_identity_files: getpwuid failed"); + for (i = 0; i < options.num_identity_files; i++) { ++ char *name = options.identity_files[i]; + if (n_ids >= SSH_MAX_IDENTITY_FILES || +- strcasecmp(options.identity_files[i], "none") == 0) { ++ strcasecmp(name, "none") == 0) { + free(options.identity_files[i]); + options.identity_files[i] = NULL; + continue; + } +- cp = tilde_expand_filename(options.identity_files[i], +- original_real_uid); ++#ifdef ENABLE_PKCS11 ++ if (strlen(name) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(name, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ load_pkcs11_identity(name, identity_files, ++ identity_keys, &n_ids); ++ free(options.identity_files[i]); ++ continue; ++ } ++#endif /* ENABLE_PKCS11 */ ++ cp = tilde_expand_filename(name, original_real_uid); + filename = percent_expand(cp, "d", pw->pw_dir, + "u", pw->pw_name, "l", thishost, "h", host, + "r", options.user, (char *)NULL); +diff --git a/ssh_config.5 b/ssh_config.5 +index 71705cab..e0266609 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -919,6 +919,19 @@ may also be used in conjunction with + .Cm CertificateFile + in order to provide any certificate also needed for authentication with + the identity. ++.Pp ++The authentication identity can be also specified in a form of PKCS#11 URI ++starting with a string ++.Cm pkcs11: . ++There is supported a subset of the PKCS#11 URI as defined ++in RFC 7512 (implemented path arguments ++.Cm id , ++.Cm manufacturer , ++.Cm object , ++.Cm token ++and query argument ++.Cm module-path ++). The URI can not be in quotes. + .It Cm IgnoreUnknown + Specifies a pattern-list of unknown options to be ignored if they are + encountered in configuration parsing.