Bugzilla – Attachment 3119 Details for
Bug 2817
Add support for PKCS#11 URIs (RFC 7512)
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
PKCS#11 URI (RFC7512) support v2
openssh_pkcs11.patch (text/plain), 125.89 KB, created by
Jakub Jelen
on 2018-02-16 02:14:42 AEDT
(
hide
)
Description:
PKCS#11 URI (RFC7512) support v2
Filename:
MIME Type:
Creator:
Jakub Jelen
Created:
2018-02-16 02:14:42 AEDT
Size:
125.89 KB
patch
obsolete
>diff --git a/Makefile.in b/Makefile.in >index 3e19a6a2..7d60587d 100644 >--- a/Makefile.in >+++ b/Makefile.in >@@ -85,7 +85,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ > atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o utf8.o \ > monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ > msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ >- ssh-pkcs11.o smult_curve25519_ref.o \ >+ 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 \ >@@ -240,6 +240,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) >+ rm -f regress/unittests/pkcs11/*.o >+ rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT) > rm -f regress/misc/kexfuzz/*.o > rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) > (cd openbsd-compat && $(MAKE) clean) >@@ -268,6 +270,8 @@ distclean: regressclean > rm -f regress/unittests/match/test_match > rm -f regress/unittests/utf8/*.o > rm -f regress/unittests/utf8/test_utf8 >+ rm -f regress/unittests/pkcs11/*.o >+ rm -f regress/unittests/pkcs11/test_pkcs11 > rm -f regress/unittests/misc/kexfuzz > (cd openbsd-compat && $(MAKE) distclean) > if test -d pkg ; then \ >@@ -429,6 +433,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 ] || \ > ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile >@@ -447,6 +452,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) > >+regress/soft-pkcs11.so: $(srcdir)/regress/soft-pkcs11.c $(REGRESSLIBS) >+ $(CC) $(CFLAGS) $(CPPFLAGS) -fpic -c $(srcdir)/regress/soft-pkcs11.c \ >+ -o $(srcdir)/regress/soft-pkcs11.o >+ $(CC) -shared -o $@ $(srcdir)/regress/soft-pkcs11.o >+ > 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) >@@ -548,6 +558,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \ > regress/unittests/test_helper/libtest_helper.a \ > -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) > >+UNITTESTS_TEST_PKCS11_OBJS=\ >+ regress/unittests/pkcs11/tests.o >+ >+regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \ >+ ${UNITTESTS_TEST_PKCS11_OBJS} \ >+ regress/unittests/test_helper/libtest_helper.a libssh.a >+ $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \ >+ regress/unittests/test_helper/libtest_helper.a \ >+ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) >+ > MISC_KEX_FUZZ_OBJS=\ > regress/misc/kexfuzz/kexfuzz.o > >@@ -558,6 +578,7 @@ regress/misc/kexfuzz/kexfuzz$(EXEEXT): ${MISC_KEX_FUZZ_OBJS} libssh.a > regress-binaries: regress/modpipe$(EXEEXT) \ > regress/setuid-allowed$(EXEEXT) \ > regress/netcat$(EXEEXT) \ >+ regress/soft-pkcs11.so \ > regress/check-perm$(EXEEXT) \ > regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \ > regress/unittests/sshkey/test_sshkey$(EXEEXT) \ >@@ -567,6 +588,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 --git a/configure.ac b/configure.ac >index a22bd3ee..6bc063c3 100644 >--- a/configure.ac >+++ b/configure.ac >@@ -1848,12 +1848,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 > ] > ) >@@ -1866,6 +1868,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]) >@@ -5141,6 +5177,7 @@ echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" > echo " BSD Auth support: $BSD_AUTH_MSG" > echo " Random number source: $RAND_MSG" > echo " Privsep sandbox style: $SANDBOX_STYLE" >+echo " Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG" > > echo "" > >diff --git a/readconf.c b/readconf.c >index 10b57bd4..f50853e8 100644 >--- a/readconf.c >+++ b/readconf.c >@@ -1015,7 +1015,8 @@ parse_time: > break; > > case oIdentityFile: >- arg = strdelim(&s); >+ /* Can't use strdelim() becase it would break on equal signs */ >+ arg = xstrdup(s); > if (!arg || *arg == '\0') > fatal("%.200s line %d: Missing argument.", filename, linenum); > if (*activep) { >@@ -1026,7 +1027,7 @@ parse_time: > add_identity_file(options, NULL, > arg, flags & SSHCONF_USERCONF); > } >- break; >+ return 0; > > case oCertificateFile: > arg = strdelim(&s); >diff --git a/regress/Makefile b/regress/Makefile >index d15898ad..9c15afa4 100644 >--- a/regress/Makefile >+++ b/regress/Makefile >@@ -42,6 +42,8 @@ LTESTS= connect \ > keygen-convert \ > keygen-moduli \ > key-options \ >+ pkcs11 \ >+ agent-pkcs11 \ > scp \ > scp-uri \ > sftp \ >@@ -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" > > TEST_SSH_PIN="" >-TEST_SSH_PKCS11=/usr/local/lib/soft-pkcs11.so.0.0 >+TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so > > test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" > >+# requires ssh-agent built with correct path to ssh-pkcs11-helper >+# otherwise it fails to start the helper >+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" >+if [ $? -ne 0 ]; then >+ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" >+fi >+ > # setup environment for soft-pkcs11 token > SOFTPKCS11RC=$OBJ/pkcs11.info > export SOFTPKCS11RC >@@ -23,7 +30,7 @@ notty() { > } > > trace "start agent" >-eval `${SSHAGENT} -s` > /dev/null >+eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null > r=$? > if [ $r -ne 0 ]; then > fail "could not start ssh-agent: exit code $r" >@@ -60,7 +67,7 @@ else > fi > > trace "remove pkcs11 keys" >- echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 >+ ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 > r=$? > if [ $r -ne 0 ]; then > fail "ssh-add -e failed: exit code $r" >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 >+ * (Stockholm University, Stockholm Sweden) >+ * All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * 3. Neither the name of the university nor the names of its contributors >+ * may be used to endorse or promote products derived from this software >+ * without specific prior written permission. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE >+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE >+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE >+ * POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+/* $Id: locl.h,v 1.5 2005/08/28 15:30:31 lha Exp $ */ >+ >+#ifdef HAVE_CONFIG_H >+#include <config.h> >+#endif >+ >+#include <openssl/err.h> >+#include <openssl/evp.h> >+#include <openssl/pem.h> >+#include <openssl/rand.h> >+#include <openssl/x509.h> >+#include "../libcrypto-compat.h" >+ >+#include <ctype.h> >+#include <errno.h> >+#include <pwd.h> >+#include <stdarg.h> >+#define _GNU_SOURCE >+#include <stdio.h> >+#include <stdlib.h> >+#include <string.h> >+#include <unistd.h> >+ >+#include "../pkcs11.h" >+ >+#define OPENSSL_ASN1_MALLOC_ENCODE(T, B, BL, S, R) \ >+{ \ >+ unsigned char *p; \ >+ (BL) = i2d_##T((S), NULL); \ >+ if ((BL) <= 0) { \ >+ (R) = EINVAL; \ >+ } else { \ >+ (B) = malloc((BL)); \ >+ if ((B) == NULL) { \ >+ (R) = ENOMEM; \ >+ } else { \ >+ p = (B); \ >+ (R) = 0; \ >+ (BL) = i2d_##T((S), &p); \ >+ if ((BL) <= 0) { \ >+ free((B)); \ >+ (R) = EINVAL; \ >+ } \ >+ } \ >+ } \ >+} >diff --git a/regress/pkcs11.sh b/regress/pkcs11.sh >new file mode 100644 >index 00000000..e07220ea >--- /dev/null >+++ b/regress/pkcs11.sh >@@ -0,0 +1,285 @@ >+# >+# Copyright (c) 2017 Red Hat >+# >+# Authors: Jakub Jelen <jjelen@redhat.com> >+# >+# 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. >+ >+tid="pkcs11 tests with soft token" >+ >+TEST_SSH_PIN="" >+TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so >+ >+test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" >+ >+# requires ssh-agent built with correct path to ssh-pkcs11-helper >+# otherwise it fails to start the helper >+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" >+if [ $? -ne 0 ]; then >+ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" >+fi >+ >+# setup environment for soft-pkcs11 token >+SOFTPKCS11RC=$OBJ/pkcs11.info >+rm -f $SOFTPKCS11RC >+export SOFTPKCS11RC >+# prevent ssh-agent from calling ssh-askpass >+SSH_ASKPASS=/usr/bin/true >+export SSH_ASKPASS >+unset DISPLAY >+ >+# start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh) >+notty() { >+ perl -e 'use POSIX; POSIX::setsid(); >+ if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@" >+} >+ >+create_key() { >+ ID=$1 >+ LABEL=$2 >+ rm -f $OBJ/pkcs11-${ID}.key $OBJ/pkcs11-${ID}.crt >+ openssl genrsa -out $OBJ/pkcs11-${ID}.key 2048 > /dev/null 2>&1 >+ chmod 600 $OBJ/pkcs11-${ID}.key >+ openssl req -key $OBJ/pkcs11-${ID}.key -new -x509 \ >+ -out $OBJ/pkcs11-${ID}.crt -text -subj '/CN=pkcs11 test' >/dev/null >+ printf "${ID}\t${LABEL}\t$OBJ/pkcs11-${ID}.crt\t$OBJ/pkcs11-${ID}.key\n" \ >+ >> $SOFTPKCS11RC >+} >+ >+trace "Create a key pairs on soft token" >+ID1="02" >+ID2="04" >+create_key "$ID1" "SSH RSA Key" >+create_key "$ID2" "SSH RSA Key 2" >+ >+trace "List the keys in the ssh-keygen with PKCS#11 URIs" >+${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys >+if [ $? -ne 0 ]; then >+ fail "keygen fails to enumerate keys on PKCS#11 token" >+fi >+grep "pkcs11:" $OBJ/token_keys > /dev/null >+if [ $? -ne 0 ]; then >+ fail "The keys from ssh-keygen do not contain PKCS#11 URI as a comment" >+fi >+tail -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER >+ >+ >+trace "Simple connect with ssh (without PKCS#11 URI)" >+echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \ >+ -F $OBJ/ssh_proxy somehost exit 5 >+r=$? >+if [ $r -ne 5 ]; then >+ fail "ssh connect with pkcs11 failed (exit code $r)" >+fi >+ >+ >+trace "Connect with PKCS#11 URI" >+trace " (second key should succeed)" >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ >+ -i "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 >+r=$? >+if [ $r -ne 5 ]; then >+ fail "ssh connect with PKCS#11 URI failed (exit code $r)" >+fi >+ >+trace " (first key should fail)" >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ >+ -i "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 >+r=$? >+if [ $r -eq 5 ]; then >+ fail "ssh connect with PKCS#11 URI succeeded (should fail)" >+fi >+ >+trace "Connect with various filtering options in PKCS#11 URI" >+trace " (by object label, second key should succeed)" >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ >+ -i "pkcs11:object=SSH%20RSA%20Key%202?module-path=${TEST_SSH_PKCS11}" somehost exit 5 >+r=$? >+if [ $r -ne 5 ]; then >+ fail "ssh connect with PKCS#11 URI failed (exit code $r)" >+fi >+ >+trace " (by object label, first key should fail)" >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ >+ -i "pkcs11:object=SSH%20RSA%20Key?module-path=${TEST_SSH_PKCS11}" somehost exit 5 >+r=$? >+if [ $r -eq 5 ]; then >+ fail "ssh connect with PKCS#11 URI succeeded (should fail)" >+fi >+ >+trace " (by token label, second key should succeed)" >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ >+ -i "pkcs11:id=${ID2};token=SoftToken%20(token)?module-path=${TEST_SSH_PKCS11}" somehost exit 5 >+r=$? >+if [ $r -ne 5 ]; then >+ fail "ssh connect with PKCS#11 URI failed (exit code $r)" >+fi >+ >+trace " (by wrong token label, should fail)" >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ >+ -i "pkcs11:token=SoftToken?module-path=${TEST_SSH_PKCS11}" somehost exit 5 >+r=$? >+if [ $r -eq 5 ]; then >+ fail "ssh connect with PKCS#11 URI succeeded (should fail)" >+fi >+ >+ >+ >+ >+trace "Test PKCS#11 URI specification in configuration files" >+echo "IdentityFile pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ >+ >> $OBJ/ssh_proxy >+trace " (second key should succeed)" >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 >+r=$? >+if [ $r -ne 5 ]; then >+ fail "ssh connect with PKCS#11 URI in config failed (exit code $r)" >+fi >+ >+trace " (first key should fail)" >+head -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 >+r=$? >+if [ $r -eq 5 ]; then >+ fail "ssh connect with PKCS#11 URI in config succeeded (should fail)" >+fi >+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy >+ >+trace "Test PKCS#11 URI specification in configuration files with bogus spaces" >+echo "IdentityFile pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11} " \ >+ >> $OBJ/ssh_proxy >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 >+r=$? >+if [ $r -ne 5 ]; then >+ fail "ssh connect with PKCS#11 URI with bogus spaces in config failed" \ >+ "(exit code $r)" >+fi >+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy >+ >+ >+trace "Combination of PKCS11Provider and PKCS11URI on commandline" >+trace " (first key should succeed)" >+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ >+ -i "pkcs11:id=${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5 >+r=$? >+if [ $r -ne 5 ]; then >+ fail "ssh connect with PKCS#11 URI and provider combination" \ >+ "failed (exit code $r)" >+fi >+ >+trace "Regress: Missing provider in PKCS11URI option" >+${SSH} -F $OBJ/ssh_proxy \ >+ -o IdentityFile='pkcs11:token=segfault' somehost exit 5 >+r=$? >+if [ $r -eq 139 ]; then >+ fail "ssh connect with missing provider_id from configuration option" \ >+ "crashed (exit code $r)" >+fi >+ >+ >+trace "SSH Agent can work with PKCS#11 URI" >+trace "start the agent" >+eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null >+ >+r=$? >+if [ $r -ne 0 ]; then >+ fail "could not start ssh-agent: exit code $r" >+else >+ trace "add whole provider to agent" >+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ >+ "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 >+ r=$? >+ if [ $r -ne 0 ]; then >+ fail "ssh-add failed with whole provider: exit code $r" >+ fi >+ >+ trace " pkcs11 list via agent (all keys)" >+ ${SSHADD} -l > /dev/null 2>&1 >+ r=$? >+ if [ $r -ne 0 ]; then >+ fail "ssh-add -l failed with whole provider: exit code $r" >+ fi >+ >+ trace " pkcs11 connect via agent (all keys)" >+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 >+ r=$? >+ if [ $r -ne 5 ]; then >+ fail "ssh connect failed with whole provider (exit code $r)" >+ fi >+ >+ trace " remove pkcs11 keys (all keys)" >+ ${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 >+ r=$? >+ if [ $r -ne 0 ]; then >+ fail "ssh-add -d failed with whole provider: exit code $r" >+ fi >+ >+ trace "add only 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 " pkcs11 connect via agent (first key)" >+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 >+ r=$? >+ if [ $r -ne 5 ]; then >+ fail "ssh connect failed with first key (exit code $r)" >+ fi >+ >+ trace " remove first pkcs11 key" >+ ${SSHADD} -d "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" \ >+ > /dev/null 2>&1 >+ r=$? >+ if [ $r -ne 0 ]; then >+ fail "ssh-add -d failed with first key: exit code $r" >+ fi >+ >+ trace "add only second key to the agent" >+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ >+ "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 >+ r=$? >+ if [ $r -ne 0 ]; then >+ fail "ssh-add failed with second key: exit code $r" >+ fi >+ >+ trace " pkcs11 connect via agent (second key should fail)" >+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 >+ r=$? >+ if [ $r -eq 5 ]; then >+ fail "ssh connect passed without key (should fail)" >+ fi >+ >+ trace " remove second pkcs11 key" >+ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ >+ > /dev/null 2>&1 >+ r=$? >+ if [ $r -ne 0 ]; then >+ fail "ssh-add -d failed with second key: exit code $r" >+ fi >+ >+ trace " remove already-removed pkcs11 key should fail" >+ ${SSHADD} -d "pkcs11:id=${ID1}?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 "kill agent" >+ ${SSHAGENT} -k > /dev/null >+fi >+ >+rm -rf $OBJ/.tokens $OBJ/token_keys >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 >+ * (Stockholm University, Stockholm Sweden) >+ * All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * 3. Neither the name of the university nor the names of its contributors >+ * may be used to endorse or promote products derived from this software >+ * without specific prior written permission. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE >+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE >+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE >+ * POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "locl.h" >+ >+/* RCSID("$Id: main.c,v 1.24 2006/01/11 12:42:53 lha Exp $"); */ >+ >+#define OBJECT_ID_MASK 0xfff >+#define HANDLE_OBJECT_ID(h) ((h) & OBJECT_ID_MASK) >+#define OBJECT_ID(obj) HANDLE_OBJECT_ID((obj)->object_handle) >+ >+#if OPENSSL_VERSION_NUMBER < 0x10100000L >+ #define RSA_PKCS1_SSLeay RSA_PKCS1_OpenSSL >+#endif >+ >+struct st_attr { >+ CK_ATTRIBUTE attribute; >+ int secret; >+}; >+ >+struct st_object { >+ CK_OBJECT_HANDLE object_handle; >+ struct st_attr *attrs; >+ int num_attributes; >+ enum { >+ STO_T_CERTIFICATE, >+ STO_T_PRIVATE_KEY, >+ STO_T_PUBLIC_KEY >+ } type; >+ union { >+ X509 *cert; >+ EVP_PKEY *public_key; >+ struct { >+ const char *file; >+ EVP_PKEY *key; >+ X509 *cert; >+ } private_key; >+ } u; >+}; >+ >+static struct soft_token { >+ CK_VOID_PTR application; >+ CK_NOTIFY notify; >+ struct { >+ struct st_object **objs; >+ int num_objs; >+ } object; >+ struct { >+ int hardware_slot; >+ int app_error_fatal; >+ int login_done; >+ } flags; >+ int open_sessions; >+ struct session_state { >+ CK_SESSION_HANDLE session_handle; >+ >+ struct { >+ CK_ATTRIBUTE *attributes; >+ CK_ULONG num_attributes; >+ int next_object; >+ } find; >+ >+ int encrypt_object; >+ CK_MECHANISM_PTR encrypt_mechanism; >+ int decrypt_object; >+ CK_MECHANISM_PTR decrypt_mechanism; >+ int sign_object; >+ CK_MECHANISM_PTR sign_mechanism; >+ int verify_object; >+ CK_MECHANISM_PTR verify_mechanism; >+ int digest_object; >+ } state[10]; >+#define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0])) >+ FILE *logfile; >+} soft_token; >+ >+static void >+application_error(const char *fmt, ...) >+{ >+ va_list ap; >+ va_start(ap, fmt); >+ vprintf(fmt, ap); >+ va_end(ap); >+ if (soft_token.flags.app_error_fatal) >+ abort(); >+} >+ >+static void >+st_logf(const char *fmt, ...) >+{ >+ va_list ap; >+ if (soft_token.logfile == NULL) >+ return; >+ va_start(ap, fmt); >+ vfprintf(soft_token.logfile, fmt, ap); >+ va_end(ap); >+ fflush(soft_token.logfile); >+} >+ >+static void >+snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...) >+{ >+ int len; >+ va_list ap; >+ len = vsnprintf(str, size, fmt, ap); >+ va_end(ap); >+ if (len < 0 || len > (int) size) >+ return; >+ while(len < (int) size) >+ str[len++] = fillchar; >+} >+ >+#ifndef TEST_APP >+#define printf error_use_st_logf >+#endif >+ >+#define VERIFY_SESSION_HANDLE(s, state) \ >+{ \ >+ CK_RV ret; \ >+ ret = verify_session_handle(s, state); \ >+ if (ret != CKR_OK) { \ >+ /* return CKR_OK */; \ >+ } \ >+} >+ >+static CK_RV >+verify_session_handle(CK_SESSION_HANDLE hSession, >+ struct session_state **state) >+{ >+ size_t i; >+ >+ for (i = 0; i < MAX_NUM_SESSION; i++){ >+ if (soft_token.state[i].session_handle == hSession) >+ break; >+ } >+ if (i == MAX_NUM_SESSION) { >+ application_error("use of invalid handle: 0x%08lx\n", >+ (unsigned long)hSession); >+ return CKR_SESSION_HANDLE_INVALID; >+ } >+ if (state) >+ *state = &soft_token.state[i]; >+ return CKR_OK; >+} >+ >+static CK_RV >+object_handle_to_object(CK_OBJECT_HANDLE handle, >+ struct st_object **object) >+{ >+ int i = HANDLE_OBJECT_ID(handle); >+ >+ *object = NULL; >+ if (i >= soft_token.object.num_objs) >+ return CKR_ARGUMENTS_BAD; >+ if (soft_token.object.objs[i] == NULL) >+ return CKR_ARGUMENTS_BAD; >+ if (soft_token.object.objs[i]->object_handle != handle) >+ return CKR_ARGUMENTS_BAD; >+ *object = soft_token.object.objs[i]; >+ return CKR_OK; >+} >+ >+static int >+attributes_match(const struct st_object *obj, >+ const CK_ATTRIBUTE *attributes, >+ CK_ULONG num_attributes) >+{ >+ CK_ULONG i; >+ int j; >+ st_logf("attributes_match: %ld\n", (unsigned long)OBJECT_ID(obj)); >+ >+ for (i = 0; i < num_attributes; i++) { >+ int match = 0; >+ for (j = 0; j < obj->num_attributes; j++) { >+ if (attributes[i].type == obj->attrs[j].attribute.type && >+ attributes[i].ulValueLen == obj->attrs[j].attribute.ulValueLen && >+ memcmp(attributes[i].pValue, obj->attrs[j].attribute.pValue, >+ attributes[i].ulValueLen) == 0) { >+ match = 1; >+ break; >+ } >+ } >+ if (match == 0) { >+ st_logf("type %d attribute have no match\n", attributes[i].type); >+ return 0; >+ } >+ } >+ st_logf("attribute matches\n"); >+ return 1; >+} >+ >+static void >+print_attributes(const CK_ATTRIBUTE *attributes, >+ CK_ULONG num_attributes) >+{ >+ CK_ULONG i; >+ >+ st_logf("find objects: attrs: %lu\n", (unsigned long)num_attributes); >+ >+ for (i = 0; i < num_attributes; i++) { >+ st_logf(" type: "); >+ switch (attributes[i].type) { >+ case CKA_TOKEN: { >+ CK_BBOOL *ck_true; >+ if (attributes[i].ulValueLen != sizeof(CK_BBOOL)) { >+ application_error("token attribute wrong length\n"); >+ break; >+ } >+ ck_true = attributes[i].pValue; >+ st_logf("token: %s", *ck_true ? "TRUE" : "FALSE"); >+ break; >+ } >+ case CKA_CLASS: { >+ CK_OBJECT_CLASS *class; >+ if (attributes[i].ulValueLen != sizeof(CK_ULONG)) { >+ application_error("class attribute wrong length\n"); >+ break; >+ } >+ class = attributes[i].pValue; >+ st_logf("class "); >+ switch (*class) { >+ case CKO_CERTIFICATE: >+ st_logf("certificate"); >+ break; >+ case CKO_PUBLIC_KEY: >+ st_logf("public key"); >+ break; >+ case CKO_PRIVATE_KEY: >+ st_logf("private key"); >+ break; >+ case CKO_SECRET_KEY: >+ st_logf("secret key"); >+ break; >+ case CKO_DOMAIN_PARAMETERS: >+ st_logf("domain parameters"); >+ break; >+ default: >+ st_logf("[class %lx]", (long unsigned)*class); >+ break; >+ } >+ break; >+ } >+ case CKA_PRIVATE: >+ st_logf("private"); >+ break; >+ case CKA_LABEL: >+ st_logf("label"); >+ break; >+ case CKA_APPLICATION: >+ st_logf("application"); >+ break; >+ case CKA_VALUE: >+ st_logf("value"); >+ break; >+ case CKA_ID: >+ st_logf("id"); >+ break; >+ default: >+ st_logf("[unknown 0x%08lx]", (unsigned long)attributes[i].type); >+ break; >+ } >+ st_logf("\n"); >+ } >+} >+ >+static struct st_object * >+add_st_object(void) >+{ >+ struct st_object *o, **objs; >+ int i; >+ >+ o = malloc(sizeof(*o)); >+ if (o == NULL) >+ return NULL; >+ memset(o, 0, sizeof(*o)); >+ o->attrs = NULL; >+ o->num_attributes = 0; >+ >+ for (i = 0; i < soft_token.object.num_objs; i++) { >+ if (soft_token.object.objs == NULL) { >+ soft_token.object.objs[i] = o; >+ break; >+ } >+ } >+ if (i == soft_token.object.num_objs) { >+ objs = realloc(soft_token.object.objs, >+ (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0])); >+ if (objs == NULL) { >+ free(o); >+ return NULL; >+ } >+ soft_token.object.objs = objs; >+ soft_token.object.objs[soft_token.object.num_objs++] = o; >+ } >+ soft_token.object.objs[i]->object_handle = >+ (random() & (~OBJECT_ID_MASK)) | i; >+ >+ return o; >+} >+ >+static CK_RV >+add_object_attribute(struct st_object *o, >+ int secret, >+ CK_ATTRIBUTE_TYPE type, >+ CK_VOID_PTR pValue, >+ CK_ULONG ulValueLen) >+{ >+ struct st_attr *a; >+ int i; >+ >+ i = o->num_attributes; >+ a = realloc(o->attrs, (i + 1) * sizeof(o->attrs[0])); >+ if (a == NULL) >+ return CKR_DEVICE_MEMORY; >+ o->attrs = a; >+ o->attrs[i].secret = secret; >+ o->attrs[i].attribute.type = type; >+ o->attrs[i].attribute.pValue = malloc(ulValueLen); >+ if (o->attrs[i].attribute.pValue == NULL && ulValueLen != 0) >+ return CKR_DEVICE_MEMORY; >+ memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen); >+ o->attrs[i].attribute.ulValueLen = ulValueLen; >+ o->num_attributes++; >+ >+ return CKR_OK; >+} >+ >+static CK_RV >+add_pubkey_info(struct st_object *o, CK_KEY_TYPE key_type, EVP_PKEY *key) >+{ >+ switch (key_type) { >+ case CKK_RSA: { >+ CK_BYTE *modulus = NULL; >+ size_t modulus_len = 0; >+ CK_ULONG modulus_bits = 0; >+ CK_BYTE *exponent = NULL; >+ size_t exponent_len = 0; >+ RSA* rsa = NULL; >+ const BIGNUM *n = NULL, *e = NULL; >+ >+ rsa = EVP_PKEY_get0_RSA(key); >+ RSA_get0_key(rsa, &n, &e, NULL); >+ >+ modulus_bits = BN_num_bits(n); >+ >+ modulus_len = BN_num_bytes(n); >+ modulus = malloc(modulus_len); >+ BN_bn2bin(n, modulus); >+ >+ exponent_len = BN_num_bytes(e); >+ exponent = malloc(exponent_len); >+ BN_bn2bin(e, exponent); >+ >+ add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len); >+ add_object_attribute(o, 0, CKA_MODULUS_BITS, >+ &modulus_bits, sizeof(modulus_bits)); >+ add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT, >+ exponent, exponent_len); >+ >+ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); >+ >+ free(modulus); >+ free(exponent); >+ } >+ default: >+ /* XXX */ >+ break; >+ } >+ return CKR_OK; >+} >+ >+ >+static int >+pem_callback(char *buf, int num, int w, void *key) >+{ >+ return -1; >+} >+ >+ >+static CK_RV >+add_certificate(char *label, >+ const char *cert_file, >+ const char *private_key_file, >+ char *id, >+ int anchor) >+{ >+ struct st_object *o = NULL; >+ CK_BBOOL bool_true = CK_TRUE; >+ CK_BBOOL bool_false = CK_FALSE; >+ CK_OBJECT_CLASS c; >+ CK_CERTIFICATE_TYPE cert_type = CKC_X_509; >+ CK_KEY_TYPE key_type; >+ CK_MECHANISM_TYPE mech_type; >+ void *cert_data = NULL; >+ size_t cert_length; >+ void *subject_data = NULL; >+ size_t subject_length; >+ void *issuer_data = NULL; >+ size_t issuer_length; >+ void *serial_data = NULL; >+ size_t serial_length; >+ CK_RV ret = CKR_GENERAL_ERROR; >+ X509 *cert; >+ EVP_PKEY *public_key; >+ >+ size_t id_len = strlen(id); >+ >+ { >+ FILE *f; >+ >+ f = fopen(cert_file, "r"); >+ if (f == NULL) { >+ st_logf("failed to open file %s\n", cert_file); >+ return CKR_GENERAL_ERROR; >+ } >+ >+ cert = PEM_read_X509(f, NULL, NULL, NULL); >+ fclose(f); >+ if (cert == NULL) { >+ st_logf("failed reading PEM cert\n"); >+ return CKR_GENERAL_ERROR; >+ } >+ >+ OPENSSL_ASN1_MALLOC_ENCODE(X509, cert_data, cert_length, cert, ret); >+ if (ret) >+ goto out; >+ >+ OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, issuer_data, issuer_length, >+ X509_get_issuer_name(cert), ret); >+ if (ret) >+ goto out; >+ >+ OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, subject_data, subject_length, >+ X509_get_subject_name(cert), ret); >+ if (ret) >+ goto out; >+ >+ OPENSSL_ASN1_MALLOC_ENCODE(ASN1_INTEGER, serial_data, serial_length, >+ X509_get_serialNumber(cert), ret); >+ if (ret) >+ goto out; >+ >+ } >+ >+ st_logf("done parsing, adding to internal structure\n"); >+ >+ o = add_st_object(); >+ if (o == NULL) { >+ ret = CKR_DEVICE_MEMORY; >+ goto out; >+ } >+ o->type = STO_T_CERTIFICATE; >+ o->u.cert = cert; >+ public_key = X509_get_pubkey(o->u.cert); >+ >+ switch (EVP_PKEY_base_id(public_key)) { >+ case EVP_PKEY_RSA: >+ key_type = CKK_RSA; >+ break; >+ case EVP_PKEY_DSA: >+ key_type = CKK_DSA; >+ break; >+ default: >+ /* XXX */ >+ break; >+ } >+ >+ c = CKO_CERTIFICATE; >+ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); >+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); >+ >+ add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type)); >+ add_object_attribute(o, 0, CKA_ID, id, id_len); >+ >+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); >+ add_object_attribute(o, 0, CKA_ISSUER, issuer_data, issuer_length); >+ add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data, serial_length); >+ add_object_attribute(o, 0, CKA_VALUE, cert_data, cert_length); >+ if (anchor) >+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); >+ else >+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_false, sizeof(bool_false)); >+ >+ st_logf("add cert ok: %lx\n", (unsigned long)OBJECT_ID(o)); >+ >+ o = add_st_object(); >+ if (o == NULL) { >+ ret = CKR_DEVICE_MEMORY; >+ goto out; >+ } >+ o->type = STO_T_PUBLIC_KEY; >+ o->u.public_key = public_key; >+ >+ c = CKO_PUBLIC_KEY; >+ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); >+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); >+ >+ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); >+ add_object_attribute(o, 0, CKA_ID, id, id_len); >+ add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ >+ add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ >+ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); >+ mech_type = CKM_RSA_X_509; >+ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); >+ >+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); >+ add_object_attribute(o, 0, CKA_ENCRYPT, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_VERIFY, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_VERIFY_RECOVER, &bool_false, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_WRAP, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); >+ >+ add_pubkey_info(o, key_type, public_key); >+ >+ st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o)); >+ >+ if (private_key_file) { >+ CK_FLAGS flags; >+ FILE *f; >+ >+ o = add_st_object(); >+ if (o == NULL) { >+ ret = CKR_DEVICE_MEMORY; >+ goto out; >+ } >+ o->type = STO_T_PRIVATE_KEY; >+ o->u.private_key.file = strdup(private_key_file); >+ o->u.private_key.key = NULL; >+ >+ o->u.private_key.cert = cert; >+ >+ c = CKO_PRIVATE_KEY; >+ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); >+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_true, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); >+ >+ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); >+ add_object_attribute(o, 0, CKA_ID, id, id_len); >+ add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ >+ add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ >+ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); >+ mech_type = CKM_RSA_X_509; >+ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); >+ >+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); >+ add_object_attribute(o, 0, CKA_SENSITIVE, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_SECONDARY_AUTH, &bool_false, sizeof(bool_true)); >+ flags = 0; >+ add_object_attribute(o, 0, CKA_AUTH_PIN_FLAGS, &flags, sizeof(flags)); >+ >+ add_object_attribute(o, 0, CKA_DECRYPT, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_SIGN, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_SIGN_RECOVER, &bool_false, sizeof(bool_false)); >+ add_object_attribute(o, 0, CKA_UNWRAP, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_EXTRACTABLE, &bool_true, sizeof(bool_true)); >+ add_object_attribute(o, 0, CKA_NEVER_EXTRACTABLE, &bool_false, sizeof(bool_false)); >+ >+ add_pubkey_info(o, key_type, public_key); >+ >+ f = fopen(private_key_file, "r"); >+ if (f == NULL) { >+ st_logf("failed to open private key\n"); >+ return CKR_GENERAL_ERROR; >+ } >+ >+ o->u.private_key.key = PEM_read_PrivateKey(f, NULL, pem_callback, NULL); >+ fclose(f); >+ if (o->u.private_key.key == NULL) { >+ st_logf("failed to read private key a startup\n"); >+ /* don't bother with this failure for now, >+ fix it at C_Login time */; >+ } else { >+ /* XXX verify keytype */ >+ >+ if (key_type == CKK_RSA) { >+ RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); >+ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); >+ } >+ >+ if (X509_check_private_key(cert, o->u.private_key.key) != 1) { >+ EVP_PKEY_free(o->u.private_key.key); >+ o->u.private_key.key = NULL; >+ st_logf("private key doesn't verify\n"); >+ } else { >+ st_logf("private key usable\n"); >+ soft_token.flags.login_done = 1; >+ } >+ } >+ } >+ >+ ret = CKR_OK; >+ out: >+ if (ret != CKR_OK) { >+ st_logf("something went wrong when adding cert!\n"); >+ >+ /* XXX wack o */; >+ } >+ free(cert_data); >+ free(serial_data); >+ free(issuer_data); >+ free(subject_data); >+ >+ return ret; >+} >+ >+static void >+find_object_final(struct session_state *state) >+{ >+ if (state->find.attributes) { >+ CK_ULONG i; >+ >+ for (i = 0; i < state->find.num_attributes; i++) { >+ if (state->find.attributes[i].pValue) >+ free(state->find.attributes[i].pValue); >+ } >+ free(state->find.attributes); >+ state->find.attributes = NULL; >+ state->find.num_attributes = 0; >+ state->find.next_object = -1; >+ } >+} >+ >+static void >+reset_crypto_state(struct session_state *state) >+{ >+ state->encrypt_object = -1; >+ if (state->encrypt_mechanism) >+ free(state->encrypt_mechanism); >+ state->encrypt_mechanism = NULL_PTR; >+ state->decrypt_object = -1; >+ if (state->decrypt_mechanism) >+ free(state->decrypt_mechanism); >+ state->decrypt_mechanism = NULL_PTR; >+ state->sign_object = -1; >+ if (state->sign_mechanism) >+ free(state->sign_mechanism); >+ state->sign_mechanism = NULL_PTR; >+ state->verify_object = -1; >+ if (state->verify_mechanism) >+ free(state->verify_mechanism); >+ state->verify_mechanism = NULL_PTR; >+ state->digest_object = -1; >+} >+ >+static void >+close_session(struct session_state *state) >+{ >+ if (state->find.attributes) { >+ application_error("application didn't do C_FindObjectsFinal\n"); >+ find_object_final(state); >+ } >+ >+ state->session_handle = CK_INVALID_HANDLE; >+ soft_token.application = NULL_PTR; >+ soft_token.notify = NULL_PTR; >+ reset_crypto_state(state); >+} >+ >+static const char * >+has_session(void) >+{ >+ return soft_token.open_sessions > 0 ? "yes" : "no"; >+} >+ >+static void >+read_conf_file(const char *fn) >+{ >+ char buf[1024], *cert, *key, *id, *label, *s, *p; >+ int anchor; >+ FILE *f; >+ >+ f = fopen(fn, "r"); >+ if (f == NULL) { >+ st_logf("can't open configuration file %s\n", fn); >+ return; >+ } >+ >+ while(fgets(buf, sizeof(buf), f) != NULL) { >+ buf[strcspn(buf, "\n")] = '\0'; >+ >+ anchor = 0; >+ >+ st_logf("line: %s\n", buf); >+ >+ p = buf; >+ while (isspace(*p)) >+ p++; >+ if (*p == '#') >+ continue; >+ while (isspace(*p)) >+ p++; >+ >+ s = NULL; >+ id = strtok_r(p, "\t", &s); >+ if (id == NULL) >+ continue; >+ label = strtok_r(NULL, "\t", &s); >+ if (label == NULL) >+ continue; >+ cert = strtok_r(NULL, "\t", &s); >+ if (cert == NULL) >+ continue; >+ key = strtok_r(NULL, "\t", &s); >+ >+ /* XXX */ >+ if (strcmp(id, "anchor") == 0) { >+ id = "\x00\x00"; >+ anchor = 1; >+ } >+ >+ st_logf("adding: %s\n", label); >+ >+ add_certificate(label, cert, key, id, anchor); >+ } >+} >+ >+static CK_RV >+func_not_supported(void) >+{ >+ st_logf("function not supported\n"); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_Initialize(CK_VOID_PTR a) >+{ >+ CK_C_INITIALIZE_ARGS_PTR args = a; >+ st_logf("Initialize\n"); >+ size_t i; >+ >+ OpenSSL_add_all_algorithms(); >+ ERR_load_crypto_strings(); >+ >+ srandom(getpid() ^ time(NULL)); >+ >+ for (i = 0; i < MAX_NUM_SESSION; i++) { >+ soft_token.state[i].session_handle = CK_INVALID_HANDLE; >+ soft_token.state[i].find.attributes = NULL; >+ soft_token.state[i].find.num_attributes = 0; >+ soft_token.state[i].find.next_object = -1; >+ reset_crypto_state(&soft_token.state[i]); >+ } >+ >+ soft_token.flags.hardware_slot = 1; >+ soft_token.flags.app_error_fatal = 0; >+ soft_token.flags.login_done = 0; >+ >+ soft_token.object.objs = NULL; >+ soft_token.object.num_objs = 0; >+ >+ soft_token.logfile = NULL; >+#if 1 >+// soft_token.logfile = stdout; >+#endif >+#if 0 >+ soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a"); >+#endif >+ >+ if (a != NULL_PTR) { >+ st_logf("\tCreateMutex:\t%p\n", args->CreateMutex); >+ st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex); >+ st_logf("\tLockMutext\t%p\n", args->LockMutex); >+ st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex); >+ st_logf("\tFlags\t%04x\n", (unsigned int)args->flags); >+ } >+ >+ { >+ char *fn = NULL, *home = NULL; >+ >+ if (getuid() == geteuid()) { >+ fn = getenv("SOFTPKCS11RC"); >+ if (fn) >+ fn = strdup(fn); >+ home = getenv("HOME"); >+ } >+ if (fn == NULL && home == NULL) { >+ struct passwd *pw = getpwuid(getuid()); >+ if(pw != NULL) >+ home = pw->pw_dir; >+ } >+ if (fn == NULL) { >+ if (home) >+ asprintf(&fn, "%s/.soft-token.rc", home); >+ else >+ fn = strdup("/etc/soft-token.rc"); >+ } >+ >+ read_conf_file(fn); >+ free(fn); >+ } >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_Finalize(CK_VOID_PTR args) >+{ >+ size_t i; >+ >+ st_logf("Finalize\n"); >+ >+ for (i = 0; i < MAX_NUM_SESSION; i++) { >+ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) { >+ application_error("application finalized without " >+ "closing session\n"); >+ close_session(&soft_token.state[i]); >+ } >+ } >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_GetInfo(CK_INFO_PTR args) >+{ >+ st_logf("GetInfo\n"); >+ >+ memset(args, 17, sizeof(*args)); >+ args->cryptokiVersion.major = 2; >+ args->cryptokiVersion.minor = 10; >+ snprintf_fill((char *)args->manufacturerID, >+ sizeof(args->manufacturerID), >+ ' ', >+ "SoftToken"); >+ snprintf_fill((char *)args->libraryDescription, >+ sizeof(args->libraryDescription), ' ', >+ "SoftToken"); >+ args->libraryVersion.major = 1; >+ args->libraryVersion.minor = 8; >+ >+ return CKR_OK; >+} >+ >+extern CK_FUNCTION_LIST funcs; >+ >+CK_RV >+C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) >+{ >+ *ppFunctionList = &funcs; >+ return CKR_OK; >+} >+ >+CK_RV >+C_GetSlotList(CK_BBOOL tokenPresent, >+ CK_SLOT_ID_PTR pSlotList, >+ CK_ULONG_PTR pulCount) >+{ >+ st_logf("GetSlotList: %s\n", >+ tokenPresent ? "tokenPresent" : "token not Present"); >+ if (pSlotList) >+ pSlotList[0] = 1; >+ *pulCount = 1; >+ return CKR_OK; >+} >+ >+CK_RV >+C_GetSlotInfo(CK_SLOT_ID slotID, >+ CK_SLOT_INFO_PTR pInfo) >+{ >+ st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session()); >+ >+ memset(pInfo, 18, sizeof(*pInfo)); >+ >+ if (slotID != 1) >+ return CKR_ARGUMENTS_BAD; >+ >+ snprintf_fill((char *)pInfo->slotDescription, >+ sizeof(pInfo->slotDescription), >+ ' ', >+ "SoftToken (slot)"); >+ snprintf_fill((char *)pInfo->manufacturerID, >+ sizeof(pInfo->manufacturerID), >+ ' ', >+ "SoftToken (slot)"); >+ pInfo->flags = CKF_TOKEN_PRESENT; >+ if (soft_token.flags.hardware_slot) >+ pInfo->flags |= CKF_HW_SLOT; >+ pInfo->hardwareVersion.major = 1; >+ pInfo->hardwareVersion.minor = 0; >+ pInfo->firmwareVersion.major = 1; >+ pInfo->firmwareVersion.minor = 0; >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_GetTokenInfo(CK_SLOT_ID slotID, >+ CK_TOKEN_INFO_PTR pInfo) >+{ >+ st_logf("GetTokenInfo: %s\n", has_session()); >+ >+ memset(pInfo, 19, sizeof(*pInfo)); >+ >+ snprintf_fill((char *)pInfo->label, >+ sizeof(pInfo->label), >+ ' ', >+ "SoftToken (token)"); >+ snprintf_fill((char *)pInfo->manufacturerID, >+ sizeof(pInfo->manufacturerID), >+ ' ', >+ "SoftToken (token)"); >+ snprintf_fill((char *)pInfo->model, >+ sizeof(pInfo->model), >+ ' ', >+ "SoftToken (token)"); >+ snprintf_fill((char *)pInfo->serialNumber, >+ sizeof(pInfo->serialNumber), >+ ' ', >+ "4711"); >+ pInfo->flags = >+ CKF_TOKEN_INITIALIZED | >+ CKF_USER_PIN_INITIALIZED; >+ >+ if (soft_token.flags.login_done == 0) >+ pInfo->flags |= CKF_LOGIN_REQUIRED; >+ >+ /* CFK_RNG | >+ CKF_RESTORE_KEY_NOT_NEEDED | >+ */ >+ pInfo->ulMaxSessionCount = MAX_NUM_SESSION; >+ pInfo->ulSessionCount = soft_token.open_sessions; >+ pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION; >+ pInfo->ulRwSessionCount = soft_token.open_sessions; >+ pInfo->ulMaxPinLen = 1024; >+ pInfo->ulMinPinLen = 0; >+ pInfo->ulTotalPublicMemory = 4711; >+ pInfo->ulFreePublicMemory = 4712; >+ pInfo->ulTotalPrivateMemory = 4713; >+ pInfo->ulFreePrivateMemory = 4714; >+ pInfo->hardwareVersion.major = 2; >+ pInfo->hardwareVersion.minor = 0; >+ pInfo->firmwareVersion.major = 2; >+ pInfo->firmwareVersion.minor = 0; >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_GetMechanismList(CK_SLOT_ID slotID, >+ CK_MECHANISM_TYPE_PTR pMechanismList, >+ CK_ULONG_PTR pulCount) >+{ >+ st_logf("GetMechanismList\n"); >+ >+ *pulCount = 2; >+ if (pMechanismList == NULL_PTR) >+ return CKR_OK; >+ pMechanismList[0] = CKM_RSA_X_509; >+ pMechanismList[1] = CKM_RSA_PKCS; >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_GetMechanismInfo(CK_SLOT_ID slotID, >+ CK_MECHANISM_TYPE type, >+ CK_MECHANISM_INFO_PTR pInfo) >+{ >+ st_logf("GetMechanismInfo: slot %d type: %d\n", >+ (int)slotID, (int)type); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_InitToken(CK_SLOT_ID slotID, >+ CK_UTF8CHAR_PTR pPin, >+ CK_ULONG ulPinLen, >+ CK_UTF8CHAR_PTR pLabel) >+{ >+ st_logf("InitToken: slot %d\n", (int)slotID); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_OpenSession(CK_SLOT_ID slotID, >+ CK_FLAGS flags, >+ CK_VOID_PTR pApplication, >+ CK_NOTIFY Notify, >+ CK_SESSION_HANDLE_PTR phSession) >+{ >+ size_t i; >+ >+ st_logf("OpenSession: slot: %d\n", (int)slotID); >+ >+ if (soft_token.open_sessions == MAX_NUM_SESSION) >+ return CKR_SESSION_COUNT; >+ >+ soft_token.application = pApplication; >+ soft_token.notify = Notify; >+ >+ for (i = 0; i < MAX_NUM_SESSION; i++) >+ if (soft_token.state[i].session_handle == CK_INVALID_HANDLE) >+ break; >+ if (i == MAX_NUM_SESSION) >+ abort(); >+ >+ soft_token.open_sessions++; >+ >+ soft_token.state[i].session_handle = >+ (CK_SESSION_HANDLE)(random() & 0xfffff); >+ *phSession = soft_token.state[i].session_handle; >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_CloseSession(CK_SESSION_HANDLE hSession) >+{ >+ struct session_state *state; >+ st_logf("CloseSession\n"); >+ >+ if (verify_session_handle(hSession, &state) != CKR_OK) >+ application_error("closed session not open"); >+ else >+ close_session(state); >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_CloseAllSessions(CK_SLOT_ID slotID) >+{ >+ size_t i; >+ >+ st_logf("CloseAllSessions\n"); >+ >+ for (i = 0; i < MAX_NUM_SESSION; i++) >+ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) >+ close_session(&soft_token.state[i]); >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_GetSessionInfo(CK_SESSION_HANDLE hSession, >+ CK_SESSION_INFO_PTR pInfo) >+{ >+ st_logf("GetSessionInfo\n"); >+ >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ >+ memset(pInfo, 20, sizeof(*pInfo)); >+ >+ pInfo->slotID = 1; >+ if (soft_token.flags.login_done) >+ pInfo->state = CKS_RO_USER_FUNCTIONS; >+ else >+ pInfo->state = CKS_RO_PUBLIC_SESSION; >+ pInfo->flags = CKF_SERIAL_SESSION; >+ pInfo->ulDeviceError = 0; >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_Login(CK_SESSION_HANDLE hSession, >+ CK_USER_TYPE userType, >+ CK_UTF8CHAR_PTR pPin, >+ CK_ULONG ulPinLen) >+{ >+ char *pin = NULL; >+ int i; >+ >+ st_logf("Login\n"); >+ >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ >+ if (pPin != NULL_PTR) { >+ asprintf(&pin, "%.*s", (int)ulPinLen, pPin); >+ st_logf("type: %d password: %s\n", (int)userType, pin); >+ } >+ >+ for (i = 0; i < soft_token.object.num_objs; i++) { >+ struct st_object *o = soft_token.object.objs[i]; >+ FILE *f; >+ >+ if (o->type != STO_T_PRIVATE_KEY) >+ continue; >+ >+ if (o->u.private_key.key) >+ continue; >+ >+ f = fopen(o->u.private_key.file, "r"); >+ if (f == NULL) { >+ st_logf("can't open private file: %s\n", o->u.private_key.file); >+ continue; >+ } >+ >+ o->u.private_key.key = PEM_read_PrivateKey(f, NULL, NULL, pin); >+ fclose(f); >+ if (o->u.private_key.key == NULL) { >+ st_logf("failed to read key: %s error: %s\n", >+ o->u.private_key.file, >+ ERR_error_string(ERR_get_error(), NULL)); >+ /* just ignore failure */; >+ continue; >+ } >+ >+ /* XXX check keytype */ >+ RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); >+ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); >+ >+ if (X509_check_private_key(o->u.private_key.cert, o->u.private_key.key) != 1) { >+ EVP_PKEY_free(o->u.private_key.key); >+ o->u.private_key.key = NULL; >+ st_logf("private key %s doesn't verify\n", o->u.private_key.file); >+ continue; >+ } >+ >+ soft_token.flags.login_done = 1; >+ } >+ free(pin); >+ >+ return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT; >+} >+ >+CK_RV >+C_Logout(CK_SESSION_HANDLE hSession) >+{ >+ st_logf("Logout\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_GetObjectSize(CK_SESSION_HANDLE hSession, >+ CK_OBJECT_HANDLE hObject, >+ CK_ULONG_PTR pulSize) >+{ >+ st_logf("GetObjectSize\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_GetAttributeValue(CK_SESSION_HANDLE hSession, >+ CK_OBJECT_HANDLE hObject, >+ CK_ATTRIBUTE_PTR pTemplate, >+ CK_ULONG ulCount) >+{ >+ struct session_state *state; >+ struct st_object *obj; >+ CK_ULONG i; >+ CK_RV ret; >+ int j; >+ >+ st_logf("GetAttributeValue: %lx\n", >+ (unsigned long)HANDLE_OBJECT_ID(hObject)); >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) { >+ st_logf("object not found: %lx\n", >+ (unsigned long)HANDLE_OBJECT_ID(hObject)); >+ return ret; >+ } >+ >+ ret = CKR_OK; >+ for (i = 0; i < ulCount; i++) { >+ st_logf(" getting 0x%08lx\n", (unsigned long)pTemplate[i].type); >+ for (j = 0; j < obj->num_attributes; j++) { >+ if (obj->attrs[j].secret) { >+ pTemplate[i].ulValueLen = (CK_ULONG)-1; >+ break; >+ } >+ if (pTemplate[i].type == obj->attrs[j].attribute.type) { >+ if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) { >+ if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen) >+ memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue, >+ obj->attrs[j].attribute.ulValueLen); >+ } >+ pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen; >+ break; >+ } >+ } >+ if (j == obj->num_attributes) { >+ st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type); >+ pTemplate[i].ulValueLen = (CK_ULONG)-1; >+ ret = CKR_ATTRIBUTE_TYPE_INVALID; >+ } >+ >+ } >+ return ret; >+} >+ >+CK_RV >+C_FindObjectsInit(CK_SESSION_HANDLE hSession, >+ CK_ATTRIBUTE_PTR pTemplate, >+ CK_ULONG ulCount) >+{ >+ struct session_state *state; >+ >+ st_logf("FindObjectsInit\n"); >+ >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ if (state->find.next_object != -1) { >+ application_error("application didn't do C_FindObjectsFinal\n"); >+ find_object_final(state); >+ } >+ if (ulCount) { >+ CK_ULONG i; >+ >+ print_attributes(pTemplate, ulCount); >+ >+ state->find.attributes = >+ calloc(1, ulCount * sizeof(state->find.attributes[0])); >+ if (state->find.attributes == NULL) >+ return CKR_DEVICE_MEMORY; >+ for (i = 0; i < ulCount; i++) { >+ state->find.attributes[i].pValue = >+ malloc(pTemplate[i].ulValueLen); >+ if (state->find.attributes[i].pValue == NULL) { >+ find_object_final(state); >+ return CKR_DEVICE_MEMORY; >+ } >+ memcpy(state->find.attributes[i].pValue, >+ pTemplate[i].pValue, pTemplate[i].ulValueLen); >+ state->find.attributes[i].type = pTemplate[i].type; >+ state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen; >+ } >+ state->find.num_attributes = ulCount; >+ state->find.next_object = 0; >+ } else { >+ st_logf("find all objects\n"); >+ state->find.attributes = NULL; >+ state->find.num_attributes = 0; >+ state->find.next_object = 0; >+ } >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_FindObjects(CK_SESSION_HANDLE hSession, >+ CK_OBJECT_HANDLE_PTR phObject, >+ CK_ULONG ulMaxObjectCount, >+ CK_ULONG_PTR pulObjectCount) >+{ >+ struct session_state *state; >+ int i; >+ >+ st_logf("FindObjects\n"); >+ >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ if (state->find.next_object == -1) { >+ application_error("application didn't do C_FindObjectsInit\n"); >+ return CKR_ARGUMENTS_BAD; >+ } >+ if (ulMaxObjectCount == 0) { >+ application_error("application asked for 0 objects\n"); >+ return CKR_ARGUMENTS_BAD; >+ } >+ *pulObjectCount = 0; >+ for (i = state->find.next_object; i < soft_token.object.num_objs; i++) { >+ st_logf("FindObjects: %d\n", i); >+ state->find.next_object = i + 1; >+ if (attributes_match(soft_token.object.objs[i], >+ state->find.attributes, >+ state->find.num_attributes)) { >+ *phObject++ = soft_token.object.objs[i]->object_handle; >+ ulMaxObjectCount--; >+ (*pulObjectCount)++; >+ if (ulMaxObjectCount == 0) >+ break; >+ } >+ } >+ return CKR_OK; >+} >+ >+CK_RV >+C_FindObjectsFinal(CK_SESSION_HANDLE hSession) >+{ >+ struct session_state *state; >+ >+ st_logf("FindObjectsFinal\n"); >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ find_object_final(state); >+ return CKR_OK; >+} >+ >+static CK_RV >+commonInit(CK_ATTRIBUTE *attr_match, int attr_match_len, >+ const CK_MECHANISM_TYPE *mechs, int mechs_len, >+ const CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey, >+ struct st_object **o) >+{ >+ CK_RV ret; >+ int i; >+ >+ *o = NULL; >+ if ((ret = object_handle_to_object(hKey, o)) != CKR_OK) >+ return ret; >+ >+ ret = attributes_match(*o, attr_match, attr_match_len); >+ if (!ret) { >+ application_error("called commonInit on key that doesn't " >+ "support required attr"); >+ return CKR_ARGUMENTS_BAD; >+ } >+ >+ for (i = 0; i < mechs_len; i++) >+ if (mechs[i] == pMechanism->mechanism) >+ break; >+ if (i == mechs_len) { >+ application_error("called mech (%08lx) not supported\n", >+ pMechanism->mechanism); >+ return CKR_ARGUMENTS_BAD; >+ } >+ return CKR_OK; >+} >+ >+ >+static CK_RV >+dup_mechanism(CK_MECHANISM_PTR *dup, const CK_MECHANISM_PTR pMechanism) >+{ >+ CK_MECHANISM_PTR p; >+ >+ p = malloc(sizeof(*p)); >+ if (p == NULL) >+ return CKR_DEVICE_MEMORY; >+ >+ if (*dup) >+ free(*dup); >+ *dup = p; >+ memcpy(p, pMechanism, sizeof(*p)); >+ >+ return CKR_OK; >+} >+ >+ >+CK_RV >+C_EncryptInit(CK_SESSION_HANDLE hSession, >+ CK_MECHANISM_PTR pMechanism, >+ CK_OBJECT_HANDLE hKey) >+{ >+ struct session_state *state; >+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; >+ CK_BBOOL bool_true = CK_TRUE; >+ CK_ATTRIBUTE attr[] = { >+ { CKA_ENCRYPT, &bool_true, sizeof(bool_true) } >+ }; >+ struct st_object *o; >+ CK_RV ret; >+ >+ st_logf("EncryptInit\n"); >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), >+ mechs, sizeof(mechs)/sizeof(mechs[0]), >+ pMechanism, hKey, &o); >+ if (ret) >+ return ret; >+ >+ ret = dup_mechanism(&state->encrypt_mechanism, pMechanism); >+ if (ret == CKR_OK) >+ state->encrypt_object = OBJECT_ID(o); >+ >+ return ret; >+} >+ >+CK_RV >+C_Encrypt(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pData, >+ CK_ULONG ulDataLen, >+ CK_BYTE_PTR pEncryptedData, >+ CK_ULONG_PTR pulEncryptedDataLen) >+{ >+ struct session_state *state; >+ struct st_object *o; >+ void *buffer = NULL; >+ CK_RV ret; >+ RSA *rsa; >+ int padding, len, buffer_len, padding_len; >+ >+ st_logf("Encrypt\n"); >+ >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ if (state->encrypt_object == -1) >+ return CKR_ARGUMENTS_BAD; >+ >+ o = soft_token.object.objs[state->encrypt_object]; >+ >+ if (o->u.public_key == NULL) { >+ st_logf("public key NULL\n"); >+ return CKR_ARGUMENTS_BAD; >+ } >+ >+ rsa = EVP_PKEY_get0_RSA(o->u.public_key); >+ if (rsa == NULL) >+ return CKR_ARGUMENTS_BAD; >+ >+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ >+ >+ buffer_len = RSA_size(rsa); >+ >+ buffer = malloc(buffer_len); >+ if (buffer == NULL) { >+ ret = CKR_DEVICE_MEMORY; >+ goto out; >+ } >+ >+ ret = CKR_OK; >+ switch(state->encrypt_mechanism->mechanism) { >+ case CKM_RSA_PKCS: >+ padding = RSA_PKCS1_PADDING; >+ padding_len = RSA_PKCS1_PADDING_SIZE; >+ break; >+ case CKM_RSA_X_509: >+ padding = RSA_NO_PADDING; >+ padding_len = 0; >+ break; >+ default: >+ ret = CKR_FUNCTION_NOT_SUPPORTED; >+ goto out; >+ } >+ >+ if (buffer_len + padding_len < (long) ulDataLen) { >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ if (pulEncryptedDataLen == NULL) { >+ st_logf("pulEncryptedDataLen NULL\n"); >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ if (pData == NULL_PTR) { >+ st_logf("data NULL\n"); >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ len = RSA_public_encrypt(ulDataLen, pData, buffer, rsa, padding); >+ if (len <= 0) { >+ ret = CKR_DEVICE_ERROR; >+ goto out; >+ } >+ if (len > buffer_len) >+ abort(); >+ >+ if (pEncryptedData != NULL_PTR) >+ memcpy(pEncryptedData, buffer, len); >+ *pulEncryptedDataLen = len; >+ >+ out: >+ if (buffer) { >+ memset(buffer, 0, buffer_len); >+ free(buffer); >+ } >+ return ret; >+} >+ >+CK_RV >+C_EncryptUpdate(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pPart, >+ CK_ULONG ulPartLen, >+ CK_BYTE_PTR pEncryptedPart, >+ CK_ULONG_PTR pulEncryptedPartLen) >+{ >+ st_logf("EncryptUpdate\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+ >+CK_RV >+C_EncryptFinal(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pLastEncryptedPart, >+ CK_ULONG_PTR pulLastEncryptedPartLen) >+{ >+ st_logf("EncryptFinal\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+ >+/* C_DecryptInit initializes a decryption operation. */ >+CK_RV >+C_DecryptInit(CK_SESSION_HANDLE hSession, >+ CK_MECHANISM_PTR pMechanism, >+ CK_OBJECT_HANDLE hKey) >+{ >+ struct session_state *state; >+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; >+ CK_BBOOL bool_true = CK_TRUE; >+ CK_ATTRIBUTE attr[] = { >+ { CKA_DECRYPT, &bool_true, sizeof(bool_true) } >+ }; >+ struct st_object *o; >+ CK_RV ret; >+ >+ st_logf("DecryptInit\n"); >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), >+ mechs, sizeof(mechs)/sizeof(mechs[0]), >+ pMechanism, hKey, &o); >+ if (ret) >+ return ret; >+ >+ ret = dup_mechanism(&state->decrypt_mechanism, pMechanism); >+ if (ret == CKR_OK) >+ state->decrypt_object = OBJECT_ID(o); >+ >+ return CKR_OK; >+} >+ >+ >+CK_RV >+C_Decrypt(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pEncryptedData, >+ CK_ULONG ulEncryptedDataLen, >+ CK_BYTE_PTR pData, >+ CK_ULONG_PTR pulDataLen) >+{ >+ struct session_state *state; >+ struct st_object *o; >+ void *buffer = NULL; >+ CK_RV ret; >+ RSA *rsa; >+ int padding, len, buffer_len, padding_len; >+ >+ st_logf("Decrypt\n"); >+ >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ if (state->decrypt_object == -1) >+ return CKR_ARGUMENTS_BAD; >+ >+ o = soft_token.object.objs[state->decrypt_object]; >+ >+ if (o->u.private_key.key == NULL) { >+ st_logf("private key NULL\n"); >+ return CKR_ARGUMENTS_BAD; >+ } >+ >+ rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); >+ if (rsa == NULL) >+ return CKR_ARGUMENTS_BAD; >+ >+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ >+ >+ buffer_len = RSA_size(rsa); >+ >+ buffer = malloc(buffer_len); >+ if (buffer == NULL) { >+ ret = CKR_DEVICE_MEMORY; >+ goto out; >+ } >+ >+ ret = CKR_OK; >+ switch(state->decrypt_mechanism->mechanism) { >+ case CKM_RSA_PKCS: >+ padding = RSA_PKCS1_PADDING; >+ padding_len = RSA_PKCS1_PADDING_SIZE; >+ break; >+ case CKM_RSA_X_509: >+ padding = RSA_NO_PADDING; >+ padding_len = 0; >+ break; >+ default: >+ ret = CKR_FUNCTION_NOT_SUPPORTED; >+ goto out; >+ } >+ >+ if (buffer_len + padding_len < (long) ulEncryptedDataLen) { >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ if (pulDataLen == NULL) { >+ st_logf("pulDataLen NULL\n"); >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ if (pEncryptedData == NULL_PTR) { >+ st_logf("data NULL\n"); >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ len = RSA_private_decrypt(ulEncryptedDataLen, pEncryptedData, buffer, >+ rsa, padding); >+ if (len <= 0) { >+ ret = CKR_DEVICE_ERROR; >+ goto out; >+ } >+ if (len > buffer_len) >+ abort(); >+ >+ if (pData != NULL_PTR) >+ memcpy(pData, buffer, len); >+ *pulDataLen = len; >+ >+ out: >+ if (buffer) { >+ memset(buffer, 0, buffer_len); >+ free(buffer); >+ } >+ return ret; >+} >+ >+ >+CK_RV >+C_DecryptUpdate(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pEncryptedPart, >+ CK_ULONG ulEncryptedPartLen, >+ CK_BYTE_PTR pPart, >+ CK_ULONG_PTR pulPartLen) >+ >+{ >+ st_logf("DecryptUpdate\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+ >+CK_RV >+C_DecryptFinal(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pLastPart, >+ CK_ULONG_PTR pulLastPartLen) >+{ >+ st_logf("DecryptFinal\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_DigestInit(CK_SESSION_HANDLE hSession, >+ CK_MECHANISM_PTR pMechanism) >+{ >+ st_logf("DigestInit\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_SignInit(CK_SESSION_HANDLE hSession, >+ CK_MECHANISM_PTR pMechanism, >+ CK_OBJECT_HANDLE hKey) >+{ >+ struct session_state *state; >+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; >+ CK_BBOOL bool_true = CK_TRUE; >+ CK_ATTRIBUTE attr[] = { >+ { CKA_SIGN, &bool_true, sizeof(bool_true) } >+ }; >+ struct st_object *o; >+ CK_RV ret; >+ >+ st_logf("SignInit\n"); >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), >+ mechs, sizeof(mechs)/sizeof(mechs[0]), >+ pMechanism, hKey, &o); >+ if (ret) >+ return ret; >+ >+ ret = dup_mechanism(&state->sign_mechanism, pMechanism); >+ if (ret == CKR_OK) >+ state->sign_object = OBJECT_ID(o); >+ >+ return CKR_OK; >+} >+ >+CK_RV >+C_Sign(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pData, >+ CK_ULONG ulDataLen, >+ CK_BYTE_PTR pSignature, >+ CK_ULONG_PTR pulSignatureLen) >+{ >+ struct session_state *state; >+ struct st_object *o; >+ void *buffer = NULL; >+ CK_RV ret; >+ RSA *rsa; >+ int padding, len, buffer_len; >+ size_t padding_len; >+ >+ st_logf("Sign\n"); >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ if (state->sign_object == -1) >+ return CKR_ARGUMENTS_BAD; >+ >+ o = soft_token.object.objs[state->sign_object]; >+ >+ if (o->u.private_key.key == NULL) { >+ st_logf("private key NULL\n"); >+ return CKR_ARGUMENTS_BAD; >+ } >+ >+ rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); >+ if (rsa == NULL) >+ return CKR_ARGUMENTS_BAD; >+ >+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ >+ >+ buffer_len = RSA_size(rsa); >+ >+ buffer = malloc(buffer_len); >+ if (buffer == NULL) { >+ ret = CKR_DEVICE_MEMORY; >+ goto out; >+ } >+ >+ switch(state->sign_mechanism->mechanism) { >+ case CKM_RSA_PKCS: >+ padding = RSA_PKCS1_PADDING; >+ padding_len = RSA_PKCS1_PADDING_SIZE; >+ break; >+ case CKM_RSA_X_509: >+ padding = RSA_NO_PADDING; >+ padding_len = 0; >+ break; >+ default: >+ ret = CKR_FUNCTION_NOT_SUPPORTED; >+ goto out; >+ } >+ >+ if ((size_t) buffer_len < ulDataLen + padding_len) { >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ if (pulSignatureLen == NULL) { >+ st_logf("signature len NULL\n"); >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ if (pData == NULL_PTR) { >+ st_logf("data NULL\n"); >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ len = RSA_private_encrypt(ulDataLen, pData, buffer, rsa, padding); >+ st_logf("private encrypt done\n"); >+ if (len <= 0) { >+ ret = CKR_DEVICE_ERROR; >+ goto out; >+ } >+ if (len > buffer_len) >+ abort(); >+ >+ if (pSignature != NULL_PTR) >+ memcpy(pSignature, buffer, len); >+ *pulSignatureLen = len; >+ >+ ret = CKR_OK; >+ >+ out: >+ if (buffer) { >+ memset(buffer, 0, buffer_len); >+ free(buffer); >+ } >+ return ret; >+} >+ >+CK_RV >+C_SignUpdate(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pPart, >+ CK_ULONG ulPartLen) >+{ >+ st_logf("SignUpdate\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+ >+CK_RV >+C_SignFinal(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pSignature, >+ CK_ULONG_PTR pulSignatureLen) >+{ >+ st_logf("SignUpdate\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_VerifyInit(CK_SESSION_HANDLE hSession, >+ CK_MECHANISM_PTR pMechanism, >+ CK_OBJECT_HANDLE hKey) >+{ >+ struct session_state *state; >+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; >+ CK_BBOOL bool_true = CK_TRUE; >+ CK_ATTRIBUTE attr[] = { >+ { CKA_VERIFY, &bool_true, sizeof(bool_true) } >+ }; >+ struct st_object *o; >+ CK_RV ret; >+ >+ st_logf("VerifyInit\n"); >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), >+ mechs, sizeof(mechs)/sizeof(mechs[0]), >+ pMechanism, hKey, &o); >+ if (ret) >+ return ret; >+ >+ ret = dup_mechanism(&state->verify_mechanism, pMechanism); >+ if (ret == CKR_OK) >+ state->verify_object = OBJECT_ID(o); >+ >+ return ret; >+} >+ >+CK_RV >+C_Verify(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pData, >+ CK_ULONG ulDataLen, >+ CK_BYTE_PTR pSignature, >+ CK_ULONG ulSignatureLen) >+{ >+ struct session_state *state; >+ struct st_object *o; >+ void *buffer = NULL; >+ CK_RV ret; >+ RSA *rsa; >+ int padding, len, buffer_len; >+ >+ st_logf("Verify\n"); >+ VERIFY_SESSION_HANDLE(hSession, &state); >+ >+ if (state->verify_object == -1) >+ return CKR_ARGUMENTS_BAD; >+ >+ o = soft_token.object.objs[state->verify_object]; >+ >+ if (o->u.public_key == NULL) { >+ st_logf("public key NULL\n"); >+ return CKR_ARGUMENTS_BAD; >+ } >+ >+ rsa = EVP_PKEY_get0_RSA(o->u.public_key); >+ if (rsa == NULL) >+ return CKR_ARGUMENTS_BAD; >+ >+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ >+ >+ buffer_len = RSA_size(rsa); >+ >+ buffer = malloc(buffer_len); >+ if (buffer == NULL) { >+ ret = CKR_DEVICE_MEMORY; >+ goto out; >+ } >+ >+ ret = CKR_OK; >+ switch(state->verify_mechanism->mechanism) { >+ case CKM_RSA_PKCS: >+ padding = RSA_PKCS1_PADDING; >+ break; >+ case CKM_RSA_X_509: >+ padding = RSA_NO_PADDING; >+ break; >+ default: >+ ret = CKR_FUNCTION_NOT_SUPPORTED; >+ goto out; >+ } >+ >+ if (buffer_len < (long) ulDataLen) { >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ if (pSignature == NULL) { >+ st_logf("signature NULL\n"); >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ if (pData == NULL_PTR) { >+ st_logf("data NULL\n"); >+ ret = CKR_ARGUMENTS_BAD; >+ goto out; >+ } >+ >+ len = RSA_public_decrypt(ulDataLen, pData, buffer, rsa, padding); >+ st_logf("private encrypt done\n"); >+ if (len <= 0) { >+ ret = CKR_DEVICE_ERROR; >+ goto out; >+ } >+ if (len > buffer_len) >+ abort(); >+ >+ if ((size_t) len != ulSignatureLen) { >+ ret = CKR_GENERAL_ERROR; >+ goto out; >+ } >+ >+ if (memcmp(pSignature, buffer, len) != 0) { >+ ret = CKR_GENERAL_ERROR; >+ goto out; >+ } >+ >+ out: >+ if (buffer) { >+ memset(buffer, 0, buffer_len); >+ free(buffer); >+ } >+ return ret; >+} >+ >+ >+CK_RV >+C_VerifyUpdate(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pPart, >+ CK_ULONG ulPartLen) >+{ >+ st_logf("VerifyUpdate\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_VerifyFinal(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR pSignature, >+ CK_ULONG ulSignatureLen) >+{ >+ st_logf("VerifyFinal\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+CK_RV >+C_GenerateRandom(CK_SESSION_HANDLE hSession, >+ CK_BYTE_PTR RandomData, >+ CK_ULONG ulRandomLen) >+{ >+ st_logf("GenerateRandom\n"); >+ VERIFY_SESSION_HANDLE(hSession, NULL); >+ return CKR_FUNCTION_NOT_SUPPORTED; >+} >+ >+ >+CK_FUNCTION_LIST funcs = { >+ { 2, 11 }, >+ C_Initialize, >+ C_Finalize, >+ C_GetInfo, >+ C_GetFunctionList, >+ C_GetSlotList, >+ C_GetSlotInfo, >+ C_GetTokenInfo, >+ C_GetMechanismList, >+ C_GetMechanismInfo, >+ C_InitToken, >+ (void *)func_not_supported, /* C_InitPIN */ >+ (void *)func_not_supported, /* C_SetPIN */ >+ C_OpenSession, >+ C_CloseSession, >+ C_CloseAllSessions, >+ C_GetSessionInfo, >+ (void *)func_not_supported, /* C_GetOperationState */ >+ (void *)func_not_supported, /* C_SetOperationState */ >+ C_Login, >+ C_Logout, >+ (void *)func_not_supported, /* C_CreateObject */ >+ (void *)func_not_supported, /* C_CopyObject */ >+ (void *)func_not_supported, /* C_DestroyObject */ >+ (void *)func_not_supported, /* C_GetObjectSize */ >+ C_GetAttributeValue, >+ (void *)func_not_supported, /* C_SetAttributeValue */ >+ C_FindObjectsInit, >+ C_FindObjects, >+ C_FindObjectsFinal, >+ C_EncryptInit, >+ C_Encrypt, >+ C_EncryptUpdate, >+ C_EncryptFinal, >+ C_DecryptInit, >+ C_Decrypt, >+ C_DecryptUpdate, >+ C_DecryptFinal, >+ C_DigestInit, >+ (void *)func_not_supported, /* C_Digest */ >+ (void *)func_not_supported, /* C_DigestUpdate */ >+ (void *)func_not_supported, /* C_DigestKey */ >+ (void *)func_not_supported, /* C_DigestFinal */ >+ C_SignInit, >+ C_Sign, >+ C_SignUpdate, >+ C_SignFinal, >+ (void *)func_not_supported, /* C_SignRecoverInit */ >+ (void *)func_not_supported, /* C_SignRecover */ >+ C_VerifyInit, >+ C_Verify, >+ C_VerifyUpdate, >+ C_VerifyFinal, >+ (void *)func_not_supported, /* C_VerifyRecoverInit */ >+ (void *)func_not_supported, /* C_VerifyRecover */ >+ (void *)func_not_supported, /* C_DigestEncryptUpdate */ >+ (void *)func_not_supported, /* C_DecryptDigestUpdate */ >+ (void *)func_not_supported, /* C_SignEncryptUpdate */ >+ (void *)func_not_supported, /* C_DecryptVerifyUpdate */ >+ (void *)func_not_supported, /* C_GenerateKey */ >+ (void *)func_not_supported, /* C_GenerateKeyPair */ >+ (void *)func_not_supported, /* C_WrapKey */ >+ (void *)func_not_supported, /* C_UnwrapKey */ >+ (void *)func_not_supported, /* C_DeriveKey */ >+ (void *)func_not_supported, /* C_SeedRandom */ >+ C_GenerateRandom, >+ (void *)func_not_supported, /* C_GetFunctionStatus */ >+ (void *)func_not_supported, /* C_CancelFunction */ >+ (void *)func_not_supported /* C_WaitForSlotEvent */ >+}; >diff --git a/regress/unittests/Makefile b/regress/unittests/Makefile >index e975f6ca..5baecd02 100644 >--- a/regress/unittests/Makefile >+++ b/regress/unittests/Makefile >@@ -1,6 +1,6 @@ > # $OpenBSD: Makefile,v 1.9 2017/03/14 01:20:29 dtucker Exp $ > > 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 > > .include <bsd.subdir.mk> >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 >+SRCS=tests.c >+REGRESS_TARGETS=run-regress-${PROG} >+ >+run-regress-${PROG}: ${PROG} >+ env ${TEST_ENV} ./${PROG} >+ >+.include <bsd.regress.mk> >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 >+ * >+ * Authors: Jakub Jelen <jjelen@redhat.com> >+ * >+ * 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" >+ >+#include <locale.h> >+#include <string.h> >+ >+#include "../test_helper/test_helper.h" >+ >+#include "ssh-pkcs11-uri.h" >+ >+#define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL) >+ >+/* prototypes are not public -- specify them here internally for tests */ >+struct sshbuf *percent_encode(const char *, size_t, char *); >+int percent_decode(char *, char **); >+ >+void >+compare_uri(struct pkcs11_uri *a, struct pkcs11_uri *b) >+{ >+ ASSERT_PTR_NE(a, NULL); >+ ASSERT_PTR_NE(b, NULL); >+ ASSERT_SIZE_T_EQ(a->id_len, b->id_len); >+ ASSERT_MEM_EQ(a->id, b->id, a->id_len); >+ if (b->object != NULL) >+ ASSERT_STRING_EQ(a->object, b->object); >+ else /* both should be null */ >+ ASSERT_PTR_EQ(a->object, b->object); >+ if (b->module_path != NULL) >+ ASSERT_STRING_EQ(a->module_path, b->module_path); >+ else /* both should be null */ >+ ASSERT_PTR_EQ(a->module_path, b->module_path); >+ if (b->token != NULL) >+ ASSERT_STRING_EQ(a->token, b->token); >+ else /* both should be null */ >+ ASSERT_PTR_EQ(a->token, b->token); >+ if (b->manuf != NULL) >+ ASSERT_STRING_EQ(a->manuf, b->manuf); >+ else /* both should be null */ >+ ASSERT_PTR_EQ(a->manuf, b->manuf); >+ if (b->lib_manuf != NULL) >+ ASSERT_STRING_EQ(a->lib_manuf, b->lib_manuf); >+ else /* both should be null */ >+ ASSERT_PTR_EQ(a->lib_manuf, b->lib_manuf); >+} >+ >+void >+check_parse_rv(char *uri, struct pkcs11_uri *expect, int expect_rv) >+{ >+ char *buf = NULL, *str; >+ struct pkcs11_uri *pkcs11uri = NULL; >+ int rv; >+ >+ if (expect_rv == 0) >+ str = "Valid"; >+ else >+ str = "Invalid"; >+ asprintf(&buf, "%s PKCS#11 URI parsing: %s", str, uri); >+ TEST_START(buf); >+ free(buf); >+ pkcs11uri = pkcs11_uri_init(); >+ rv = pkcs11_uri_parse(uri, pkcs11uri); >+ ASSERT_INT_EQ(rv, expect_rv); >+ if (rv == 0) /* in case of failure result is undefined */ >+ compare_uri(pkcs11uri, expect); >+ pkcs11_uri_cleanup(pkcs11uri); >+ free(expect); >+ TEST_DONE(); >+} >+ >+void >+check_parse(char *uri, struct pkcs11_uri *expect) >+{ >+ check_parse_rv(uri, expect, 0); >+} >+ >+struct pkcs11_uri * >+compose_uri(unsigned char *id, size_t id_len, char *token, char *lib_manuf, >+ char *manuf, char *module_path, char *object) >+{ >+ struct pkcs11_uri *uri = pkcs11_uri_init(); >+ if (id_len > 0) { >+ uri->id_len = id_len; >+ uri->id = id; >+ } >+ uri->module_path = module_path; >+ uri->token = token; >+ uri->lib_manuf = lib_manuf; >+ uri->manuf = manuf; >+ uri->object = object; >+ return uri; >+} >+ >+static void >+test_parse_valid(void) >+{ >+ /* path arguments */ >+ check_parse("pkcs11:id=%01", >+ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL)); >+ check_parse("pkcs11:id=%00%01", >+ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL)); >+ check_parse("pkcs11:token=SSH%20Keys", >+ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); >+ check_parse("pkcs11:library-manufacturer=OpenSC", >+ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL)); >+ check_parse("pkcs11:manufacturer=piv_II", >+ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL)); >+ check_parse("pkcs11:object=SIGN%20Key", >+ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key")); >+ /* query arguments */ >+ check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", >+ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); >+ >+ /* combinations */ >+ /* ID SHOULD be percent encoded */ >+ check_parse("pkcs11:token=SSH%20Key;id=0", >+ compose_uri("0", 1, "SSH Key", NULL, NULL, NULL, NULL)); >+ check_parse( >+ "pkcs11:manufacturer=CAC?module-path=/usr/lib64/p11-kit-proxy.so", >+ compose_uri(NULL, 0, NULL, NULL, "CAC", >+ "/usr/lib64/p11-kit-proxy.so", NULL)); >+ check_parse( >+ "pkcs11:object=RSA%20Key?module-path=/usr/lib64/pkcs11/opencryptoki.so", >+ compose_uri(NULL, 0, NULL, NULL, NULL, >+ "/usr/lib64/pkcs11/opencryptoki.so", "RSA Key")); >+ >+ /* empty path component matches everything */ >+ check_parse("pkcs11:", EMPTY_URI); >+ >+ /* empty string is a valid to match against (and different from NULL) */ >+ check_parse("pkcs11:token=", >+ compose_uri(NULL, 0, "", NULL, NULL, NULL, NULL)); >+ /* Percent character needs to be percent-encoded */ >+ check_parse("pkcs11:token=%25", >+ compose_uri(NULL, 0, "%", NULL, NULL, NULL, NULL)); >+} >+ >+static void >+test_parse_invalid(void) >+{ >+ /* Invalid percent encoding */ >+ check_parse_rv("pkcs11:id=%0", EMPTY_URI, -1); >+ /* Invalid percent encoding */ >+ check_parse_rv("pkcs11:id=%ZZ", EMPTY_URI, -1); >+ /* Space MUST be percent encoded -- XXX not enforced yet */ >+ check_parse("pkcs11:token=SSH Keys", >+ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); >+ /* MUST NOT contain duplicate attributes of the same name */ >+ check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1); >+ /* Unrecognized attribute in path SHOULD be error */ >+ check_parse_rv("pkcs11:key_name=SSH", EMPTY_URI, -1); >+ /* Unrecognized attribute in query SHOULD be ignored */ >+ check_parse("pkcs11:?key_name=SSH", EMPTY_URI); >+} >+ >+void >+check_gen(char *expect, struct pkcs11_uri *uri) >+{ >+ char *buf = NULL, *uri_str; >+ >+ asprintf(&buf, "Valid PKCS#11 URI generation: %s", expect); >+ TEST_START(buf); >+ free(buf); >+ uri_str = pkcs11_uri_get(uri); >+ ASSERT_PTR_NE(uri_str, NULL); >+ ASSERT_STRING_EQ(uri_str, expect); >+ free(uri_str); >+ TEST_DONE(); >+} >+ >+static void >+test_generate_valid(void) >+{ >+ /* path arguments */ >+ check_gen("pkcs11:id=%01", >+ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL)); >+ check_gen("pkcs11:id=%00%01", >+ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL)); >+ check_gen("pkcs11:token=SSH%20Keys", /* space must be percent encoded */ >+ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); >+ /* library-manufacturer is not implmented now */ >+ /*check_gen("pkcs11:library-manufacturer=OpenSC", >+ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL));*/ >+ check_gen("pkcs11:manufacturer=piv_II", >+ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL)); >+ check_gen("pkcs11:object=RSA%20Key", >+ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key")); >+ /* query arguments */ >+ check_gen("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", >+ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); >+ >+ /* combinations */ >+ check_gen("pkcs11:id=%02;token=SSH%20Keys", >+ compose_uri("\x02", 1, "SSH Keys", NULL, NULL, NULL, NULL)); >+ check_gen("pkcs11:id=%EE%02?module-path=/usr/lib64/p11-kit-proxy.so", >+ compose_uri("\xEE\x02", 2, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); >+ check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II", >+ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key")); >+ >+ /* empty path component matches everything */ >+ check_gen("pkcs11:", EMPTY_URI); >+ >+} >+ >+void >+check_encode(char *source, size_t len, char *whitelist, char *expect) >+{ >+ char *buf = NULL; >+ struct sshbuf *b; >+ >+ asprintf(&buf, "percent_encode: expected %s", expect); >+ TEST_START(buf); >+ free(buf); >+ >+ b = percent_encode(source, len, whitelist); >+ ASSERT_STRING_EQ(sshbuf_ptr(b), expect); >+ sshbuf_free(b); >+ TEST_DONE(); >+} >+ >+static void >+test_percent_encode_multibyte(void) >+{ >+ /* SHOULD be encoded as octets according to the UTF-8 character encoding */ >+ >+ /* multi-byte characters are "for free" */ >+ check_encode("$", 1, "", "%24"); >+ check_encode("¢", 2, "", "%C2%A2"); >+ check_encode("â¬", 3, "", "%E2%82%AC"); >+ check_encode("ð", 4, "", "%F0%90%8D%88"); >+ >+ /* CK_UTF8CHAR is unsigned char (1 byte) */ >+ /* labels SHOULD be normalized to NFC [UAX15] */ >+ >+} >+ >+static void >+test_percent_encode(void) >+{ >+ /* Without whitelist encodes everything (for CKA_ID) */ >+ check_encode("A*", 2, "", "%41%2A"); >+ check_encode("\x00", 1, "", "%00"); >+ check_encode("\x7F", 1, "", "%7F"); >+ check_encode("\x80", 1, "", "%80"); >+ check_encode("\xff", 1, "", "%FF"); >+ >+ /* Default whitelist encodes anything but safe letters */ >+ check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST, >+ "test%000alpha"); >+ check_encode(" ", 1, PKCS11_URI_WHITELIST, >+ "%20"); /* Space MUST be percent encoded */ >+ check_encode("/", 1, PKCS11_URI_WHITELIST, >+ "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */ >+ check_encode("?", 1, PKCS11_URI_WHITELIST, >+ "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */ >+ check_encode("#", 1, PKCS11_URI_WHITELIST, >+ "%23"); /* '#' MUST be always percent encoded */ >+ check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST, >+ "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch"); >+ >+ /* Components in query can have '/' unencoded (useful for paths) */ >+ check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/", >+ "/path/to.file"); >+} >+ >+void >+check_decode(char *source, char *expect, int expect_len) >+{ >+ char *buf = NULL, *out = NULL; >+ int rv; >+ >+ asprintf(&buf, "percent_decode: %s", source); >+ TEST_START(buf); >+ free(buf); >+ >+ rv = percent_decode(source, &out); >+ ASSERT_INT_EQ(rv, expect_len); >+ if (rv >= 0) >+ ASSERT_MEM_EQ(out, expect, expect_len); >+ free(out); >+ TEST_DONE(); >+} >+ >+static void >+test_percent_decode(void) >+{ >+ /* simple valid cases */ >+ check_decode("%00", "\x00", 1); >+ check_decode("%FF", "\xFF", 1); >+ >+ /* normal strings shold be kept intact */ >+ check_decode("strings are left", "strings are left", 16); >+ check_decode("10%25 of trees", "10% of trees", 12); >+ >+ /* make sure no more than 2 bytes are parsed */ >+ check_decode("%222", "\x22" "2", 2); >+ >+ /* invalid expects failure */ >+ check_decode("%0", "", -1); >+ check_decode("%Z", "", -1); >+ check_decode("%FG", "", -1); >+} >+ >+void >+tests(void) >+{ >+ test_percent_encode(); >+ test_percent_encode_multibyte(); >+ test_percent_decode(); >+ test_parse_valid(); >+ test_parse_invalid(); >+ test_generate_valid(); >+} >diff --git a/ssh-add.c b/ssh-add.c >index 2afd4833..c2c8ef22 100644 >--- a/ssh-add.c >+++ b/ssh-add.c >@@ -64,6 +64,7 @@ > #include "misc.h" > #include "ssherr.h" > #include "digest.h" >+#include "ssh-pkcs11-uri.h" > > /* argv0 */ > extern char *__progname; >@@ -183,6 +184,24 @@ delete_all(int agent_fd) > return ret; > } > >+#ifdef ENABLE_PKCS11 >+static int update_card(int, int, const char *); >+ >+int >+update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri) >+{ >+ struct pkcs11_uri *uri; >+ >+ /* dry-run parse to make sure the URI is valid and to report errors */ >+ uri = pkcs11_uri_init(); >+ if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0) >+ fatal("Failed to parse PKCS#11 URI"); >+ pkcs11_uri_cleanup(uri); >+ >+ return update_card(agent_fd, adding, pkcs11_uri); >+} >+#endif >+ > 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) > static int > do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) > { >+#ifdef ENABLE_PKCS11 >+ if (strlen(file) >= strlen(PKCS11_URI_SCHEME) && >+ strncmp(file, PKCS11_URI_SCHEME, >+ strlen(PKCS11_URI_SCHEME)) == 0) { >+ return update_pkcs11_uri(agent_fd, !deleting, file); >+ } >+#endif > if (deleting) { > if (delete_file(agent_fd, file, key_only, qflag) == -1) > return -1; >diff --git a/ssh-agent.c b/ssh-agent.c >index 39888a72..fd3e13bb 100644 >--- a/ssh-agent.c >+++ b/ssh-agent.c >@@ -532,10 +532,70 @@ no_identities(SocketEntry *e) > } > > #ifdef ENABLE_PKCS11 >+static char * >+sanitize_pkcs11_provider(const char *provider) >+{ >+ struct pkcs11_uri *uri = NULL; >+ char *sane_uri, *module_path = NULL; /* default path */ >+ char canonical_provider[PATH_MAX]; >+ >+ if (provider == NULL) >+ return NULL; >+ >+ if (strlen(provider) >= strlen(PKCS11_URI_SCHEME) && >+ strncmp(provider, PKCS11_URI_SCHEME, >+ strlen(PKCS11_URI_SCHEME)) == 0) { >+ /* PKCS#11 URI */ >+ uri = pkcs11_uri_init(); >+ if (uri == NULL) { >+ error("Failed to init PCKS#11 URI"); >+ return NULL; >+ } >+ >+ if (pkcs11_uri_parse(provider, uri) != 0) { >+ error("Failed to parse PKCS#11 URI"); >+ return NULL; >+ } >+ /* validate also provider from URI */ >+ if (uri->module_path) >+ module_path = strdup(uri->module_path); >+ } else >+ module_path = strdup(provider); /* simple path */ >+ >+ if (module_path != NULL) { /* do not validate default NULL path in URI */ >+ if (realpath(module_path, canonical_provider) == NULL) { >+ verbose("failed PKCS#11 provider \"%.100s\": realpath: %s", >+ module_path, strerror(errno)); >+ free(module_path); >+ return NULL; >+ } >+ free(module_path); >+ if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { >+ verbose("refusing PKCS#11 provider \"%.100s\": " >+ "not whitelisted", canonical_provider); >+ return NULL; >+ } >+ >+ /* copy verified and sanitized provider path back to the uri */ >+ if (uri) { >+ free(uri->module_path); >+ uri->module_path = xstrdup(canonical_provider); >+ } >+ } >+ >+ if (uri) { >+ sane_uri = pkcs11_uri_get(uri); >+ pkcs11_uri_cleanup(uri); >+ return sane_uri; >+ } else { >+ return xstrdup(canonical_provider); /* simple path */ >+ } >+} >+ > static void > process_add_smartcard_key(SocketEntry *e) > { >- 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; >@@ -571,28 +631,23 @@ process_add_smartcard_key(SocketEntry *e) > goto send; > } > } >- if (realpath(provider, canonical_provider) == NULL) { >- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", >- provider, strerror(errno)); >- goto send; >- } >- if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { >- verbose("refusing PKCS#11 add of \"%.100s\": " >- "provider not whitelisted", canonical_provider); >+ >+ sane_uri = sanitize_pkcs11_provider(provider); >+ if (sane_uri == NULL) > goto send; >- } >- debug("%s: add %.100s", __func__, canonical_provider); >+ > if (lifetime && !death) > death = monotime() + lifetime; > >- count = pkcs11_add_provider(canonical_provider, pin, &keys); >+ debug("%s: add %.100s", __func__, sane_uri); >+ count = pkcs11_add_provider(sane_uri, pin, &keys); > for (i = 0; i < count; i++) { > k = keys[i]; > if (lookup_identity(k) == NULL) { > id = xcalloc(1, sizeof(Identity)); > id->key = k; >- id->provider = xstrdup(canonical_provider); >- id->comment = xstrdup(canonical_provider); /* XXX */ >+ id->provider = xstrdup(sane_uri); >+ id->comment = xstrdup(sane_uri); > id->death = death; > id->confirm = confirm; > TAILQ_INSERT_TAIL(&idtab->idlist, id, next); >@@ -606,6 +661,7 @@ process_add_smartcard_key(SocketEntry *e) > send: > free(pin); > free(provider); >+ free(sane_uri); > free(keys); > send_status(e, success); > } >@@ -613,7 +669,7 @@ send: > static void > process_remove_smartcard_key(SocketEntry *e) > { >- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; >+ char *provider = NULL, *pin = NULL, *sane_uri = NULL; > int r, success = 0; > Identity *id, *nxt; > >@@ -624,30 +680,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) > goto send; >- } > >- debug("%s: remove %.100s", __func__, canonical_provider); >+ debug("%s: remove %.100s", __func__, sane_uri); > for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { > nxt = TAILQ_NEXT(id, next); > /* Skip file--based keys */ > if (id->provider == NULL) > continue; >- if (!strcmp(canonical_provider, id->provider)) { >+ if (!strcmp(sane_uri, id->provider)) { > TAILQ_REMOVE(&idtab->idlist, id, next); > free_identity(id); > idtab->nentries--; > } > } >- if (pkcs11_del_provider(canonical_provider) == 0) >+ if (pkcs11_del_provider(sane_uri) == 0) > success = 1; > else > error("%s: pkcs11_del_provider failed", __func__); > send: > free(provider); >+ free(sane_uri); > send_status(e, success); > } > #endif /* ENABLE_PKCS11 */ >diff --git a/ssh-keygen.c b/ssh-keygen.c >index a09e0798..eff48ad1 100644 >--- a/ssh-keygen.c >+++ b/ssh-keygen.c >@@ -816,6 +816,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..dab296bb 100644 >--- a/ssh-pkcs11-client.c >+++ b/ssh-pkcs11-client.c >@@ -195,6 +195,8 @@ pkcs11_add_provider(char *name, char *pin, Key ***keysp) > u_int blen; > Buffer msg; > >+ debug("%s: called, name = %s", __func__, name); >+ > if (fd < 0 && pkcs11_start_helper() < 0) > return (-1); > >@@ -208,6 +210,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 *)); >+ debug("%s: nkeys = %d", __func__, nkeys); > for (i = 0; i < nkeys; i++) { > blob = buffer_get_string(&msg, &blen); > free(buffer_get_string(&msg, NULL)); >diff --git a/ssh-pkcs11-uri.c b/ssh-pkcs11-uri.c >new file mode 100644 >index 00000000..35ed6a09 >--- /dev/null >+++ b/ssh-pkcs11-uri.c >@@ -0,0 +1,399 @@ >+/* >+ * Copyright (c) 2017 Red Hat >+ * >+ * Authors: Jakub Jelen <jjelen@redhat.com> >+ * >+ * 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 <stdio.h> >+#include <string.h> >+ >+#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 *b = NULL; >+ char tmp[4], *cp; >+ size_t i; >+ >+ if ((b = sshbuf_new()) == NULL) >+ return NULL; >+ for (i = 0; i < length; i++) { >+ cp = strchr(whitelist, data[i]); >+ /* if c is specified as '\0' pointer to terminator is returned !! */ >+ if (cp != NULL && *cp != '\0') { >+ if (sshbuf_put(b, &data[i], 1) != 0) >+ goto err; >+ } else >+ if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3 >+ || sshbuf_put(b, tmp, 3) != 0) >+ goto err; >+ } >+ if (sshbuf_put(b, "\0", 1) == 0) >+ return b; >+err: >+ sshbuf_free(b); >+ return NULL; >+} >+ >+char * >+pkcs11_uri_append(char *part, const char *separator, const char *key, >+ struct sshbuf *value) >+{ >+ char *new_part; >+ size_t size; >+ >+ if (value == NULL) >+ return NULL; >+ >+ size = asprintf(&new_part, >+ "%s%s%s" PKCS11_URI_VALUE_SEPARATOR "%s", >+ (part != NULL ? part : ""), >+ (part != NULL ? separator : ""), >+ key, sshbuf_ptr(value)); >+ sshbuf_free(value); >+ free(part); >+ >+ if (size < 0) >+ return NULL; >+ return new_part; >+} >+ >+char * >+pkcs11_uri_get(struct pkcs11_uri *uri) >+{ >+ size_t size = -1; >+ char *p = NULL, *path = NULL, *query = NULL; >+ >+ /* compose a percent-encoded ID */ >+ if (uri->id_len > 0) { >+ struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, ""); >+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, >+ PKCS11_URI_ID, key_id); >+ if (path == NULL) >+ goto err; >+ } >+ >+ /* Write object label */ >+ if (uri->object) { >+ struct sshbuf *label = percent_encode(uri->object, strlen(uri->object), >+ PKCS11_URI_WHITELIST); >+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, >+ PKCS11_URI_OBJECT, label); >+ if (path == NULL) >+ goto err; >+ } >+ >+ /* Write token label */ >+ if (uri->token) { >+ struct sshbuf *label = percent_encode(uri->token, strlen(uri->token), >+ PKCS11_URI_WHITELIST); >+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, >+ PKCS11_URI_TOKEN, label); >+ if (path == NULL) >+ goto err; >+ } >+ >+ /* Write manufacturer */ >+ if (uri->manuf) { >+ struct sshbuf *manuf = percent_encode(uri->manuf, >+ strlen(uri->manuf), PKCS11_URI_WHITELIST); >+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, >+ PKCS11_URI_MANUF, manuf); >+ if (path == NULL) >+ goto err; >+ } >+ >+ /* Write module_path */ >+ if (uri->module_path) { >+ struct sshbuf *module = percent_encode(uri->module_path, >+ strlen(uri->module_path), PKCS11_URI_WHITELIST "/"); >+ query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR, >+ PKCS11_URI_MODULE_PATH, module); >+ if (query == NULL) >+ goto err; >+ } >+ >+ size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s", >+ path != NULL ? path : "", >+ query != NULL ? "?" : "", >+ query != NULL ? query : ""); >+err: >+ free(query); >+ free(path); >+ if (size < 0) >+ return NULL; >+ return p; >+} >+ >+struct pkcs11_uri * >+pkcs11_uri_init() >+{ >+ struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri)); >+ return d; >+} >+ >+void >+pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11) >+{ >+ free(pkcs11->id); >+ free(pkcs11->module_path); >+ free(pkcs11->token); >+ free(pkcs11->object); >+ free(pkcs11->lib_manuf); >+ free(pkcs11->manuf); >+ free(pkcs11); >+} >+ >+int >+pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11) >+{ >+ char *saveptr1, *saveptr2, *str1, *str2, *tok; >+ int rv = 0, len; >+ char *p = NULL; >+ >+ size_t scheme_len = strlen(PKCS11_URI_SCHEME); >+ if (strlen(uri) < scheme_len || /* empty URI matches everything */ >+ strncmp(uri, PKCS11_URI_SCHEME, scheme_len) != 0) { >+ error("%s: The '%s' does not look like PKCS#11 URI", >+ __func__, uri); >+ return -1; >+ } >+ >+ if (pkcs11 == NULL) { >+ error("%s: Bad arguments. The pkcs11 can't be null", __func__); >+ return -1; >+ } >+ >+ /* skip URI schema name */ >+ p = strdup(uri); >+ str1 = p; >+ >+ /* everything before ? */ >+ tok = strtok_r(str1, "?", &saveptr1); >+ if (tok == NULL) { >+ free(p); >+ error("%s: pk11-path expected, got EOF", __func__); >+ return -1; >+ } >+ >+ /* skip URI schema name: >+ * the scheme ensures that there is at least something before "?" >+ * allowing empty pk11-path. Resulting token at worst pointing to >+ * \0 byte */ >+ tok = tok + scheme_len; >+ >+ /* parse pk11-path */ >+ for (str2 = tok; ; str2 = NULL) { >+ char **charptr; >+ pkcs11uriOpCodes opcode; >+ tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2); >+ if (tok == NULL) >+ break; >+ opcode = parse_token(tok); >+ if (opcode == pBadOption) { >+ verbose("Unknown key in PKCS#11 URI: %s", tok); >+ return -1; >+ } >+ >+ char *arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */ >+ switch (opcode) { >+ case pId: >+ /* CKA_ID */ >+ if (pkcs11->id != NULL) { >+ verbose("%s: The id already set in the PKCS#11 URI", >+ __func__); >+ rv = -1; >+ } >+ len = percent_decode(arg, &pkcs11->id); >+ if (len <= 0) { >+ verbose("%s: Failed to percent-decode CKA_ID: %s", >+ __func__, arg); >+ rv = -1; >+ } else >+ pkcs11->id_len = len; >+ debug3("%s: Setting CKA_ID = %s from PKCS#11 URI", >+ __func__, arg); >+ break; >+ case pToken: >+ /* CK_TOKEN_INFO -> label */ >+ charptr = &pkcs11->token; >+ parse_string: >+ if (*charptr != NULL) { >+ verbose("%s: The %s already set in the PKCS#11 URI", >+ keywords[opcode].name, __func__); >+ rv = -1; >+ } >+ percent_decode(arg, charptr); >+ debug3("%s: Setting %s = %s from PKCS#11 URI", >+ __func__, keywords[opcode].name, *charptr); >+ break; >+ >+ case pObject: >+ /* CK_TOKEN_INFO -> manufacturerID */ >+ charptr = &pkcs11->object; >+ goto parse_string; >+ >+ case pManufacturer: >+ /* CK_TOKEN_INFO -> manufacturerID */ >+ charptr = &pkcs11->manuf; >+ goto parse_string; >+ >+ case pLibraryManufacturer: >+ /* CK_INFO -> manufacturerID */ >+ charptr = &pkcs11->lib_manuf; >+ goto parse_string; >+ >+ case pBadOption: >+ default: >+ /* Unrecognized attribute in the URI path SHOULD be error */ >+ verbose("%s: Unknown part of path in PKCS#11 URI: %s", >+ __func__, tok); >+ rv = -1; >+ } >+ } >+ >+ tok = strtok_r(NULL, "?", &saveptr1); >+ if (tok == NULL) { >+ free(p); >+ return rv; >+ } >+ /* parse pk11-query (optional) */ >+ for (str2 = tok; ; str2 = NULL) { >+ size_t key_len = strlen(PKCS11_URI_MODULE_PATH) + 1; >+ tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2); >+ if (tok == NULL) >+ break; >+ if (strncasecmp(tok, PKCS11_URI_MODULE_PATH >+ PKCS11_URI_VALUE_SEPARATOR, key_len) == 0) { >+ /* module-path is PKCS11Provider */ >+ if (pkcs11->module_path != NULL) { >+ verbose("%s: Multiple module-path attributes are" >+ "not supported the PKCS#11 URI", __func__); >+ rv = -1; >+ } >+ percent_decode(tok + key_len, &pkcs11->module_path); >+ debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI\n", >+ __func__, pkcs11->module_path); >+ /* } else if ( pin-value ) { */ >+ } else { >+ /* Unrecognized attribute in the URI query SHOULD be ignored */ >+ verbose("%s: Unknown part of query in PKCS#11 URI: %s", >+ __func__, tok); >+ } >+ } >+ free(p); >+ return rv; >+} >+ >+#endif /* ENABLE_PKCS11 */ >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 >+ * >+ * Authors: Jakub Jelen <jjelen@redhat.com> >+ * >+ * 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. >+ */ >+ >+#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..f54d905e 100644 >--- a/ssh-pkcs11.c >+++ b/ssh-pkcs11.c >@@ -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; >@@ -69,11 +70,47 @@ struct pkcs11_key { > int (*orig_finish)(RSA *rsa); > RSA_METHOD *rsa_method; > char *keyid; >+ char *label; > int keyid_len; > }; > > 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->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; >+ >+ 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) > { >@@ -124,6 +161,8 @@ pkcs11_provider_unref(struct pkcs11_provider *p) > 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 *); >+ > /* 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); >+ >+ 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, 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 +399,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 = RSA_meth_get_finish(def); > if ((k11->rsa_method = RSA_meth_dup(def)) == NULL || > RSA_meth_set1_name(k11->rsa_method, "pkcs11") == 0 || >@@ -381,7 +459,7 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) > 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) { >@@ -405,38 +483,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 +556,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; >@@ -473,13 +576,13 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, > 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; > } >@@ -487,22 +590,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,31 +613,31 @@ 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 { > BIGNUM *rsa_n, *rsa_e; > >- 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); > if (RSA_set0_key(rsa, rsa_n, rsa_e, NULL) == 0) > error("RSA_set0_key failed"); > } > } 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,11 +649,12 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, > error("RSAPublicKey_dup"); > } > X509_free(x509); >+ EVP_PKEY_free(evp); > } > if (rsa) > RSA_get0_key(rsa, &n, &e, NULL); > if (rsa && n && 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 +673,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) >@@ -580,6 +684,31 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, > /* register a new provider, fails if provider already exists */ > 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); >+ } >+ >+ rv = pkcs11_add_provider_by_uri(uri, pin, keyp); >+ pkcs11_uri_cleanup(uri); >+ return rv; >+} >+ >+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; >@@ -589,11 +718,26 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) > 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 */ >@@ -606,31 +750,38 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) > 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, >@@ -643,14 +794,14 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) > } > 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)); >@@ -661,39 +812,54 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) > 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); >@@ -701,6 +867,8 @@ fail: > } > if (handle) > dlclose(handle); >+ free(provider_id); >+ 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 2d8f6506..120e1ec4 100644 >--- a/ssh.c >+++ b/ssh.c >@@ -769,6 +769,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 " >@@ -1987,6 +1996,46 @@ 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 (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(struct passwd *pw) >@@ -1999,10 +2048,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 */ > > n_ids = n_certs = 0; > memset(identity_files, 0, sizeof(identity_files)); >@@ -2011,35 +2056,43 @@ load_public_identity_files(struct passwd *pw) > 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) > 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); >+ 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 27c649fa..d2766e48 100644 >--- a/ssh_config.5 >+++ b/ssh_config.5 >@@ -912,6 +912,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.
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 2817
:
3111
| 3119