af10de
diff --git a/Makefile.in b/Makefile.in
af10de
index ac959c1f..f8ed1781 100644
af10de
--- a/Makefile.in
af10de
+++ b/Makefile.in
af10de
@@ -93,7 +93,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
7e9748
 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
7e9748
 	kexgssc.o \
7e9748
 	msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
7e9748
-	ssh-pkcs11.o smult_curve25519_ref.o \
7e9748
+	ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \
7e9748
 	poly1305.o chacha.o cipher-chachapoly.o \
7e9748
 	ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \
af10de
 	sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \
af10de
@@ -248,6 +248,8 @@ clean:	regressclean
7e9748
 	rm -f regress/unittests/match/test_match$(EXEEXT)
7e9748
 	rm -f regress/unittests/utf8/*.o
7e9748
 	rm -f regress/unittests/utf8/test_utf8$(EXEEXT)
7e9748
+	rm -f regress/unittests/pkcs11/*.o
7e9748
+	rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT)
bbf61d
	rm -f regress/misc/kexfuzz/*.o
bbf61d
	rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT)
bbf61d
	(cd openbsd-compat && $(MAKE) clean)
af10de
@@ -276,6 +278,8 @@ distclean:	regressclean
7e9748
 	rm -f regress/unittests/match/test_match
7e9748
 	rm -f regress/unittests/utf8/*.o
7e9748
 	rm -f regress/unittests/utf8/test_utf8
7e9748
+	rm -f regress/unittests/pkcs11/*.o
7e9748
+	rm -f regress/unittests/pkcs11/test_pkcs11
bbf61d
	rm -f regress/misc/kexfuzz/*.o
bbf61d
	rm -f regress/misc/kexfuzz
bbf61d
	(cd openbsd-compat && $(MAKE) distclean)
af10de
@@ -437,6 +441,7 @@ regress-prep:
af10de
 	$(MKDIR_P) `pwd`/regress/unittests/kex
af10de
 	$(MKDIR_P) `pwd`/regress/unittests/match
af10de
 	$(MKDIR_P) `pwd`/regress/unittests/utf8
af10de
+	$(MKDIR_P) `pwd`/regress/unittests/pkcs11
af10de
 	$(MKDIR_P) `pwd`/regress/misc/kexfuzz
7e9748
 	[ -f `pwd`/regress/Makefile ] || \
af10de
 	    ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile
af10de
@@ -455,6 +460,11 @@ regress/netcat$(EXEEXT): $(srcdir)/regress/netcat.c $(REGRESSLIBS)
7e9748
 	$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/netcat.c \
7e9748
 	$(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
7e9748
 
7e9748
+regress/soft-pkcs11.so: $(srcdir)/regress/soft-pkcs11.c $(REGRESSLIBS)
7e9748
+	$(CC) $(CFLAGS) $(CPPFLAGS) -fpic -c $(srcdir)/regress/soft-pkcs11.c \
7e9748
+	 -o $(srcdir)/regress/soft-pkcs11.o
7e9748
+	$(CC) -shared -o $@ $(srcdir)/regress/soft-pkcs11.o
7e9748
+
7e9748
 regress/check-perm$(EXEEXT): $(srcdir)/regress/check-perm.c $(REGRESSLIBS)
7e9748
 	$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/check-perm.c \
7e9748
 	$(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
af10de
@@ -556,6 +566,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \
7e9748
 	    regress/unittests/test_helper/libtest_helper.a \
7e9748
 	    -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
7e9748
 
7e9748
+UNITTESTS_TEST_PKCS11_OBJS=\
7e9748
+	regress/unittests/pkcs11/tests.o
7e9748
+
7e9748
+regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \
7e9748
+    ${UNITTESTS_TEST_PKCS11_OBJS} \
7e9748
+    regress/unittests/test_helper/libtest_helper.a libssh.a
7e9748
+	$(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \
7e9748
+	    regress/unittests/test_helper/libtest_helper.a \
7e9748
+	    -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
7e9748
+
7e9748
 MISC_KEX_FUZZ_OBJS=\
7e9748
 	regress/misc/kexfuzz/kexfuzz.o
7e9748
 
af10de
@@ -566,6 +586,7 @@ regress/misc/kexfuzz/kexfuzz$(EXEEXT): ${MISC_KEX_FUZZ_OBJS} libssh.a
7e9748
 regress-binaries: regress/modpipe$(EXEEXT) \
7e9748
 	regress/setuid-allowed$(EXEEXT) \
7e9748
 	regress/netcat$(EXEEXT) \
7e9748
+	regress/soft-pkcs11.so \
7e9748
 	regress/check-perm$(EXEEXT) \
bbf61d
 	regress/mkdtemp$(EXEEXT) \
7e9748
 	regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \
af10de
@@ -575,6 +596,7 @@ regress-binaries: regress/modpipe$(EXEEXT) \
7e9748
 	regress/unittests/kex/test_kex$(EXEEXT) \
7e9748
 	regress/unittests/match/test_match$(EXEEXT) \
7e9748
 	regress/unittests/utf8/test_utf8$(EXEEXT) \
7e9748
+	regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \
7e9748
 	regress/misc/kexfuzz/kexfuzz$(EXEEXT)
7e9748
 
bbf61d
 tests interop-tests t-exec unit: regress-prep regress-binaries $(TARGETS)
af10de
diff --git a/authfd.c b/authfd.c
af10de
index 1eff7ba9..35153f47 100644
af10de
--- a/authfd.c
af10de
+++ b/authfd.c
af10de
@@ -312,6 +312,8 @@ ssh_free_identitylist(struct ssh_identitylist *idl)
af10de
 		if (idl->comments != NULL)
af10de
 			free(idl->comments[i]);
af10de
 	}
af10de
+	free(idl->keys);
af10de
+	free(idl->comments);
af10de
 	free(idl);
af10de
 }
af10de
 
af10de
diff --git a/configure.ac b/configure.ac
af10de
index d7bcaf01..171a8597 100644
af10de
--- a/configure.ac
af10de
+++ b/configure.ac
af10de
@@ -1895,12 +1895,14 @@ AC_LINK_IFELSE(
af10de
 	[AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).])
af10de
 ])
af10de
 
af10de
+SCARD_MSG="yes"
af10de
 disable_pkcs11=
af10de
 AC_ARG_ENABLE([pkcs11],
af10de
 	[  --disable-pkcs11        disable PKCS#11 support code [no]],
af10de
 	[
af10de
 		if test "x$enableval" = "xno" ; then
af10de
 			disable_pkcs11=1
af10de
+			SCARD_MSG="no"
af10de
 		fi
af10de
 	]
af10de
 )
af10de
@@ -1916,6 +1918,40 @@ if test "x$openssl" = "xyes" && test "x$disable_pkcs11" = "x"; then
af10de
 	)
af10de
 fi
af10de
 
af10de
+# Check whether we have a p11-kit, we got default provider on command line
af10de
+DEFAULT_PKCS11_PROVIDER_MSG="no"
af10de
+AC_ARG_WITH([default-pkcs11-provider],
af10de
+	[  --with-default-pkcs11-provider[[=PATH]]   Use default pkcs11 provider (p11-kit detected by default)],
af10de
+	[ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then
af10de
+		if test "x$withval" = "xyes" ; then
af10de
+			AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no])
af10de
+			if test "x$PKGCONFIG" != "xno"; then
af10de
+				AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit])
af10de
+				if "$PKGCONFIG" "p11-kit-1"; then
af10de
+					AC_MSG_RESULT([yes])
af10de
+					use_pkgconfig_for_p11kit=yes
af10de
+				else
af10de
+					AC_MSG_RESULT([no])
af10de
+				fi
af10de
+			fi
af10de
+		else
af10de
+			PKCS11_PATH="${withval}"
af10de
+		fi
af10de
+		if test "x$use_pkgconfig_for_p11kit" = "xyes"; then
af10de
+			PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1`
af10de
+		fi
af10de
+		AC_CHECK_FILE("$PKCS11_PATH",
af10de
+			[ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)])
af10de
+			  DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH"
af10de
+			],
af10de
+			[ AC_MSG_ERROR([Requested PKCS11 provided not found]) ]
af10de
+		)
af10de
+	else
af10de
+		AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider])
af10de
+	fi ]
af10de
+)
af10de
+
af10de
+
af10de
 # IRIX has a const char return value for gai_strerror()
af10de
 AC_CHECK_FUNCS([gai_strerror], [
af10de
 	AC_DEFINE([HAVE_GAI_STRERROR])
af10de
@@ -5226,6 +5262,7 @@ echo "           Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG"
af10de
 echo "              Random number source: $RAND_MSG"
af10de
 echo "             Privsep sandbox style: $SANDBOX_STYLE"
af10de
 echo "                Vendor patch level: $SSH_VENDOR_PATCHLEVEL"
af10de
+echo "          Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG"
af10de
 
af10de
 echo ""
af10de
 
af10de
diff --git a/regress/Makefile b/regress/Makefile
af10de
index d15898ad..9c15afa4 100644
af10de
--- a/regress/Makefile
af10de
+++ b/regress/Makefile
af10de
@@ -108,9 +110,11 @@ CLEANFILES=	*.core actual agent-key.* authorized_keys_${USERNAME} \
af10de
 		known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \
af10de
 		modpipe netcat no_identity_config \
af10de
 		pidfile putty.rsa2 ready regress.log \
af10de
-		remote_pid revoked-* rsa rsa-agent rsa-agent.pub rsa.pub \
af10de
+		remote_pid revoked-* rsa rsa-agent rsa-agent.pub \
af10de
+		rsa-agent-cert.pub rsa.pub \
af10de
 		rsa1 rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \
af10de
-		rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \
af10de
+		soft-pkcs11.so soft-pkcs11.o pkcs11*.crt pkcs11*.key \
af10de
+		pkcs11.info rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \
af10de
 		scp-ssh-wrapper.scp setuid-allowed sftp-server.log \
af10de
 		sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \
af10de
 		ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \
af10de
@@ -225,6 +229,7 @@ unit:
af10de
 		V="" ; \
af10de
 		test "x${USE_VALGRIND}" = "x" || \
af10de
 		    V=${.CURDIR}/valgrind-unit.sh ; \
af10de
+		$$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \
af10de
 		$$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \
af10de
 		$$V ${.OBJDIR}/unittests/sshkey/test_sshkey \
af10de
 			-d ${.CURDIR}/unittests/sshkey/testdata ; \
af10de
diff --git a/regress/agent-pkcs11.sh b/regress/agent-pkcs11.sh
af10de
index db3018b8..4991d0bc 100644
af10de
--- a/regress/agent-pkcs11.sh
af10de
+++ b/regress/agent-pkcs11.sh
7e9748
@@ -4,10 +4,17 @@
7e9748
 tid="pkcs11 agent test"
7e9748
 
7e9748
 TEST_SSH_PIN=""
7e9748
-TEST_SSH_PKCS11=/usr/local/lib/soft-pkcs11.so.0.0
7e9748
+TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so
7e9748
 
7e9748
 test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist"
7e9748
 
7e9748
+# requires ssh-agent built with correct path to ssh-pkcs11-helper
7e9748
+# otherwise it fails to start the helper
7e9748
+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER"
7e9748
+if [ $? -ne 0 ]; then
7e9748
+	fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so"
7e9748
+fi
7e9748
+
7e9748
 # setup environment for soft-pkcs11 token
7e9748
 SOFTPKCS11RC=$OBJ/pkcs11.info
7e9748
 export SOFTPKCS11RC
7e9748
@@ -23,7 +30,7 @@ notty() {
7e9748
 }
7e9748
 
7e9748
 trace "start agent"
7e9748
-eval `${SSHAGENT} -s` > /dev/null
7e9748
+eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null
7e9748
 r=$?
7e9748
 if [ $r -ne 0 ]; then
7e9748
 	fail "could not start ssh-agent: exit code $r"
7e9748
@@ -60,7 +67,7 @@ else
7e9748
 	fi
7e9748
 
7e9748
 	trace "remove pkcs11 keys"
7e9748
-	echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1
7e9748
+	${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1
7e9748
 	r=$?
7e9748
 	if [ $r -ne 0 ]; then
7e9748
 		fail "ssh-add -e failed: exit code $r"
af10de
diff --git a/regress/locl.h b/regress/locl.h
af10de
new file mode 100644
af10de
index 00000000..afefe27d
af10de
--- /dev/null
af10de
+++ b/regress/locl.h
7e9748
@@ -0,0 +1,79 @@
7e9748
+/*
7e9748
+ * Copyright (c) 2004, Stockholms universitet
7e9748
+ * (Stockholm University, Stockholm Sweden)
7e9748
+ * All rights reserved.
7e9748
+ *
7e9748
+ * Redistribution and use in source and binary forms, with or without
7e9748
+ * modification, are permitted provided that the following conditions
7e9748
+ * are met:
7e9748
+ *
7e9748
+ * 1. Redistributions of source code must retain the above copyright
7e9748
+ *    notice, this list of conditions and the following disclaimer.
7e9748
+ *
7e9748
+ * 2. Redistributions in binary form must reproduce the above copyright
7e9748
+ *    notice, this list of conditions and the following disclaimer in the
7e9748
+ *    documentation and/or other materials provided with the distribution.
7e9748
+ *
7e9748
+ * 3. Neither the name of the university nor the names of its contributors
7e9748
+ *    may be used to endorse or promote products derived from this software
7e9748
+ *    without specific prior written permission.
7e9748
+ *
7e9748
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
7e9748
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
7e9748
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
7e9748
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
7e9748
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
7e9748
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
7e9748
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
7e9748
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
7e9748
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
7e9748
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
7e9748
+ * POSSIBILITY OF SUCH DAMAGE.
7e9748
+ */
7e9748
+
7e9748
+/* $Id: locl.h,v 1.5 2005/08/28 15:30:31 lha Exp $ */
7e9748
+
7e9748
+#ifdef HAVE_CONFIG_H
7e9748
+#include <config.h>
7e9748
+#endif
7e9748
+
7e9748
+#include <openssl/err.h>
7e9748
+#include <openssl/evp.h>
7e9748
+#include <openssl/pem.h>
7e9748
+#include <openssl/rand.h>
7e9748
+#include <openssl/x509.h>
7e9748
+#include "../libcrypto-compat.h"
7e9748
+
7e9748
+#include <ctype.h>
7e9748
+#include <errno.h>
7e9748
+#include <pwd.h>
7e9748
+#include <stdarg.h>
7e9748
+#define _GNU_SOURCE
7e9748
+#include <stdio.h>
7e9748
+#include <stdlib.h>
7e9748
+#include <string.h>
7e9748
+#include <unistd.h>
7e9748
+
7e9748
+#include "../pkcs11.h"
7e9748
+
7e9748
+#define OPENSSL_ASN1_MALLOC_ENCODE(T, B, BL, S, R)			\
7e9748
+{									\
7e9748
+  unsigned char *p;							\
7e9748
+  (BL) = i2d_##T((S), NULL);						\
7e9748
+  if ((BL) <= 0) {							\
7e9748
+     (R) = EINVAL;							\
7e9748
+  } else {								\
7e9748
+    (B) = malloc((BL));							\
7e9748
+    if ((B) == NULL) {							\
7e9748
+       (R) = ENOMEM;							\
7e9748
+    } else {								\
7e9748
+        p = (B);							\
7e9748
+        (R) = 0;							\
7e9748
+        (BL) = i2d_##T((S), &p);					\
7e9748
+        if ((BL) <= 0) {						\
7e9748
+           free((B));                                          		\
7e9748
+           (R) = EINVAL;						\
7e9748
+        }								\
7e9748
+    }									\
7e9748
+  }									\
7e9748
+}
af10de
diff --git a/regress/pkcs11.sh b/regress/pkcs11.sh
af10de
new file mode 100644
af10de
index 00000000..cf98e379
af10de
--- /dev/null
af10de
+++ b/regress/pkcs11.sh
af10de
@@ -0,0 +1,300 @@
7e9748
+#
7e9748
+#  Copyright (c) 2017 Red Hat
7e9748
+#
7e9748
+#  Authors: Jakub Jelen <jjelen@redhat.com>
7e9748
+#
7e9748
+#  Permission to use, copy, modify, and distribute this software for any
7e9748
+#  purpose with or without fee is hereby granted, provided that the above
7e9748
+#  copyright notice and this permission notice appear in all copies.
7e9748
+#
7e9748
+#  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
7e9748
+#  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
7e9748
+#  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
7e9748
+#  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
7e9748
+#  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
7e9748
+#  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
7e9748
+#  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
7e9748
+
7e9748
+tid="pkcs11 tests with soft token"
7e9748
+
7e9748
+TEST_SSH_PIN=""
7e9748
+TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so
7e9748
+
7e9748
+test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist"
7e9748
+
7e9748
+# requires ssh-agent built with correct path to ssh-pkcs11-helper
7e9748
+# otherwise it fails to start the helper
7e9748
+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER"
7e9748
+if [ $? -ne 0 ]; then
7e9748
+	fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so"
7e9748
+fi
7e9748
+
7e9748
+# setup environment for soft-pkcs11 token
7e9748
+SOFTPKCS11RC=$OBJ/pkcs11.info
7e9748
+rm -f $SOFTPKCS11RC
7e9748
+export SOFTPKCS11RC
7e9748
+# prevent ssh-agent from calling ssh-askpass
7e9748
+SSH_ASKPASS=/usr/bin/true
7e9748
+export SSH_ASKPASS
7e9748
+unset DISPLAY
7e9748
+
7e9748
+# start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh)
7e9748
+notty() {
7e9748
+	perl -e 'use POSIX; POSIX::setsid();
7e9748
+	    if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@"
7e9748
+}
7e9748
+
7e9748
+create_key() {
7e9748
+	ID=$1
7e9748
+	LABEL=$2
7e9748
+	rm -f $OBJ/pkcs11-${ID}.key $OBJ/pkcs11-${ID}.crt
7e9748
+	openssl genrsa -out $OBJ/pkcs11-${ID}.key 2048 > /dev/null 2>&1
7e9748
+	chmod 600 $OBJ/pkcs11-${ID}.key
7e9748
+	openssl req -key $OBJ/pkcs11-${ID}.key -new -x509 \
7e9748
+	    -out $OBJ/pkcs11-${ID}.crt -text -subj '/CN=pkcs11 test' >/dev/null
7e9748
+	printf "${ID}\t${LABEL}\t$OBJ/pkcs11-${ID}.crt\t$OBJ/pkcs11-${ID}.key\n" \
7e9748
+	    >> $SOFTPKCS11RC
7e9748
+}
7e9748
+
7e9748
+trace "Create a key pairs on soft token"
7e9748
+ID1="02"
7e9748
+ID2="04"
7e9748
+create_key "$ID1" "SSH RSA Key"
7e9748
+create_key "$ID2" "SSH RSA Key 2"
7e9748
+
7e9748
+trace "List the keys in the ssh-keygen with PKCS#11 URIs"
7e9748
+${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys
7e9748
+if [ $? -ne 0 ]; then
7e9748
+	fail "keygen fails to enumerate keys on PKCS#11 token"
7e9748
+fi
7e9748
+grep "pkcs11:" $OBJ/token_keys > /dev/null
7e9748
+if [ $? -ne 0 ]; then
7e9748
+	fail "The keys from ssh-keygen do not contain PKCS#11 URI as a comment"
7e9748
+fi
7e9748
+tail -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER
7e9748
+
7e9748
+
7e9748
+trace "Simple connect with ssh (without PKCS#11 URI)"
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \
7e9748
+    -F $OBJ/ssh_proxy somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -ne 5 ]; then
7e9748
+	fail "ssh connect with pkcs11 failed (exit code $r)"
7e9748
+fi
7e9748
+
7e9748
+
7e9748
+trace "Connect with PKCS#11 URI"
7e9748
+trace "  (second key should succeed)"
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
7e9748
+    -i "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -ne 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI failed (exit code $r)"
7e9748
+fi
7e9748
+
7e9748
+trace "  (first key should fail)"
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
7e9748
+     -i "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -eq 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI succeeded (should fail)"
7e9748
+fi
7e9748
+
7e9748
+trace "Connect with various filtering options in PKCS#11 URI"
7e9748
+trace "  (by object label, second key should succeed)"
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
7e9748
+    -i "pkcs11:object=SSH%20RSA%20Key%202?module-path=${TEST_SSH_PKCS11}" somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -ne 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI failed (exit code $r)"
7e9748
+fi
7e9748
+
7e9748
+trace "  (by object label, first key should fail)"
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
7e9748
+     -i "pkcs11:object=SSH%20RSA%20Key?module-path=${TEST_SSH_PKCS11}" somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -eq 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI succeeded (should fail)"
7e9748
+fi
7e9748
+
7e9748
+trace "  (by token label, second key should succeed)"
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
7e9748
+    -i "pkcs11:id=${ID2};token=SoftToken%20(token)?module-path=${TEST_SSH_PKCS11}" somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -ne 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI failed (exit code $r)"
7e9748
+fi
7e9748
+
7e9748
+trace "  (by wrong token label, should fail)"
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
7e9748
+     -i "pkcs11:token=SoftToken?module-path=${TEST_SSH_PKCS11}" somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -eq 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI succeeded (should fail)"
7e9748
+fi
7e9748
+
7e9748
+
7e9748
+
7e9748
+
7e9748
+trace "Test PKCS#11 URI specification in configuration files"
ab24bd
+echo "IdentityFile \"pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}\"" \
7e9748
+    >> $OBJ/ssh_proxy
7e9748
+trace "  (second key should succeed)"
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -ne 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI in config failed (exit code $r)"
7e9748
+fi
7e9748
+
7e9748
+trace "  (first key should fail)"
7e9748
+head -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -eq 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI in config succeeded (should fail)"
7e9748
+fi
7e9748
+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy
7e9748
+
7e9748
+trace "Test PKCS#11 URI specification in configuration files with bogus spaces"
ab24bd
+echo "IdentityFile \"    pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}    \"" \
7e9748
+    >> $OBJ/ssh_proxy
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -ne 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI with bogus spaces in config failed" \
7e9748
+	    "(exit code $r)"
7e9748
+fi
7e9748
+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy
7e9748
+
7e9748
+
7e9748
+trace "Combination of PKCS11Provider and PKCS11URI on commandline"
7e9748
+trace "  (first key should succeed)"
7e9748
+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
7e9748
+    -i "pkcs11:id=${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -ne 5 ]; then
7e9748
+	fail "ssh connect with PKCS#11 URI and provider combination" \
7e9748
+	    "failed (exit code $r)"
7e9748
+fi
7e9748
+
7e9748
+trace "Regress: Missing provider in PKCS11URI option"
7e9748
+${SSH} -F $OBJ/ssh_proxy \
ab24bd
+    -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5
7e9748
+r=$?
7e9748
+if [ $r -eq 139 ]; then
7e9748
+	fail "ssh connect with missing provider_id from configuration option" \
7e9748
+	    "crashed (exit code $r)"
7e9748
+fi
7e9748
+
7e9748
+
7e9748
+trace "SSH Agent can work with PKCS#11 URI"
7e9748
+trace "start the agent"
7e9748
+eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null
7e9748
+
7e9748
+r=$?
7e9748
+if [ $r -ne 0 ]; then
7e9748
+	fail "could not start ssh-agent: exit code $r"
7e9748
+else
7e9748
+	trace "add whole provider to agent"
7e9748
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
7e9748
+	    "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
7e9748
+	r=$?
7e9748
+	if [ $r -ne 0 ]; then
7e9748
+		fail "ssh-add failed with whole provider: exit code $r"
7e9748
+	fi
7e9748
+
7e9748
+	trace " pkcs11 list via agent (all keys)"
7e9748
+	${SSHADD} -l > /dev/null 2>&1
7e9748
+	r=$?
7e9748
+	if [ $r -ne 0 ]; then
7e9748
+		fail "ssh-add -l failed with whole provider: exit code $r"
7e9748
+	fi
7e9748
+
7e9748
+	trace " pkcs11 connect via agent (all keys)"
7e9748
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
7e9748
+	r=$?
7e9748
+	if [ $r -ne 5 ]; then
7e9748
+		fail "ssh connect failed with whole provider (exit code $r)"
7e9748
+	fi
7e9748
+
7e9748
+	trace " remove pkcs11 keys (all keys)"
7e9748
+	${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
7e9748
+	r=$?
7e9748
+	if [ $r -ne 0 ]; then
7e9748
+		fail "ssh-add -d failed with whole provider: exit code $r"
7e9748
+	fi
7e9748
+
7e9748
+	trace "add only first key to the agent"
7e9748
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
7e9748
+	    "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
7e9748
+	r=$?
7e9748
+	if [ $r -ne 0 ]; then
7e9748
+		fail "ssh-add failed with first key: exit code $r"
7e9748
+	fi
7e9748
+
7e9748
+	trace " pkcs11 connect via agent (first key)"
7e9748
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
7e9748
+	r=$?
7e9748
+	if [ $r -ne 5 ]; then
7e9748
+		fail "ssh connect failed with first key (exit code $r)"
7e9748
+	fi
7e9748
+
7e9748
+	trace " remove first pkcs11 key"
7e9748
+	${SSHADD} -d "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" \
7e9748
+	    > /dev/null 2>&1
7e9748
+	r=$?
7e9748
+	if [ $r -ne 0 ]; then
7e9748
+		fail "ssh-add -d failed with first key: exit code $r"
7e9748
+	fi
7e9748
+
7e9748
+	trace "add only second key to the agent"
7e9748
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
7e9748
+	    "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
7e9748
+	r=$?
7e9748
+	if [ $r -ne 0 ]; then
7e9748
+		fail "ssh-add failed with second key: exit code $r"
7e9748
+	fi
7e9748
+
7e9748
+	trace " pkcs11 connect via agent (second key should fail)"
7e9748
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
7e9748
+	r=$?
7e9748
+	if [ $r -eq 5 ]; then
7e9748
+		fail "ssh connect passed without key (should fail)"
7e9748
+	fi
7e9748
+
af10de
+	trace "add also the first key to the agent"
af10de
+	echo ${TEST_SSH_PIN} | notty ${SSHADD} \
af10de
+	    "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1
af10de
+	r=$?
af10de
+	if [ $r -ne 0 ]; then
af10de
+		fail "ssh-add failed with first key: exit code $r"
af10de
+	fi
af10de
+
7e9748
+	trace " remove second pkcs11 key"
7e9748
+	${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \
7e9748
+	    > /dev/null 2>&1
7e9748
+	r=$?
7e9748
+	if [ $r -ne 0 ]; then
7e9748
+		fail "ssh-add -d failed with second key: exit code $r"
7e9748
+	fi
7e9748
+
7e9748
+	trace " remove already-removed pkcs11 key should fail"
af10de
+	${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \
7e9748
+	    > /dev/null 2>&1
7e9748
+	r=$?
7e9748
+	if [ $r -eq 0 ]; then
7e9748
+		fail "ssh-add -d passed with non-existing key (should fail)"
7e9748
+	fi
7e9748
+
af10de
+	trace " pkcs11 connect via agent (the first key should be still usable)"
af10de
+	${SSH} -F $OBJ/ssh_proxy somehost exit 5
af10de
+	r=$?
af10de
+	if [ $r -ne 5 ]; then
af10de
+		fail "ssh connect failed with first key (after removing second): exit code $r"
af10de
+	fi
af10de
+
7e9748
+	trace "kill agent"
7e9748
+	${SSHAGENT} -k > /dev/null
7e9748
+fi
7e9748
+
7e9748
+rm -rf $OBJ/.tokens $OBJ/token_keys
af10de
diff --git a/regress/soft-pkcs11.c b/regress/soft-pkcs11.c
af10de
new file mode 100644
af10de
index 00000000..8b4981bd
af10de
--- /dev/null
af10de
+++ b/regress/soft-pkcs11.c
7e9748
@@ -0,0 +1,2058 @@
7e9748
+/*
7e9748
+ * Copyright (c) 2004-2006, Stockholms universitet
7e9748
+ * (Stockholm University, Stockholm Sweden)
7e9748
+ * All rights reserved.
7e9748
+ *
7e9748
+ * Redistribution and use in source and binary forms, with or without
7e9748
+ * modification, are permitted provided that the following conditions
7e9748
+ * are met:
7e9748
+ *
7e9748
+ * 1. Redistributions of source code must retain the above copyright
7e9748
+ *    notice, this list of conditions and the following disclaimer.
7e9748
+ *
7e9748
+ * 2. Redistributions in binary form must reproduce the above copyright
7e9748
+ *    notice, this list of conditions and the following disclaimer in the
7e9748
+ *    documentation and/or other materials provided with the distribution.
7e9748
+ *
7e9748
+ * 3. Neither the name of the university nor the names of its contributors
7e9748
+ *    may be used to endorse or promote products derived from this software
7e9748
+ *    without specific prior written permission.
7e9748
+ *
7e9748
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
7e9748
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
7e9748
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
7e9748
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
7e9748
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
7e9748
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
7e9748
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
7e9748
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
7e9748
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
7e9748
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
7e9748
+ * POSSIBILITY OF SUCH DAMAGE.
7e9748
+ */
7e9748
+
7e9748
+#include "locl.h"
7e9748
+
7e9748
+/* RCSID("$Id: main.c,v 1.24 2006/01/11 12:42:53 lha Exp $"); */
7e9748
+
7e9748
+#define OBJECT_ID_MASK		0xfff
7e9748
+#define HANDLE_OBJECT_ID(h)	((h) & OBJECT_ID_MASK)
7e9748
+#define OBJECT_ID(obj)		HANDLE_OBJECT_ID((obj)->object_handle)
7e9748
+
7e9748
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
7e9748
+ #define RSA_PKCS1_SSLeay RSA_PKCS1_OpenSSL
7e9748
+#endif
7e9748
+
7e9748
+struct st_attr {
7e9748
+    CK_ATTRIBUTE attribute;
7e9748
+    int secret;
7e9748
+};
7e9748
+
7e9748
+struct st_object {
7e9748
+    CK_OBJECT_HANDLE object_handle;
7e9748
+    struct st_attr *attrs;
7e9748
+    int num_attributes;
7e9748
+    enum {
7e9748
+	STO_T_CERTIFICATE,
7e9748
+	STO_T_PRIVATE_KEY,
7e9748
+	STO_T_PUBLIC_KEY
7e9748
+    } type;
7e9748
+    union {
7e9748
+	X509 *cert;
7e9748
+	EVP_PKEY *public_key;
7e9748
+	struct {
7e9748
+	    const char *file;
7e9748
+	    EVP_PKEY *key;
7e9748
+	    X509 *cert;
7e9748
+	} private_key;
7e9748
+    } u;
7e9748
+};
7e9748
+
7e9748
+static struct soft_token {
7e9748
+    CK_VOID_PTR application;
7e9748
+    CK_NOTIFY notify;
7e9748
+    struct {
7e9748
+	struct st_object **objs;
7e9748
+	int num_objs;
7e9748
+    } object;
7e9748
+    struct {
7e9748
+	int hardware_slot;
7e9748
+	int app_error_fatal;
7e9748
+	int login_done;
7e9748
+    } flags;
7e9748
+    int open_sessions;
7e9748
+    struct session_state {
7e9748
+	CK_SESSION_HANDLE session_handle;
7e9748
+
7e9748
+	struct {
7e9748
+	    CK_ATTRIBUTE *attributes;
7e9748
+	    CK_ULONG num_attributes;
7e9748
+	    int next_object;
7e9748
+	} find;
7e9748
+
7e9748
+	int encrypt_object;
7e9748
+	CK_MECHANISM_PTR encrypt_mechanism;
7e9748
+	int decrypt_object;
7e9748
+	CK_MECHANISM_PTR decrypt_mechanism;
7e9748
+	int sign_object;
7e9748
+	CK_MECHANISM_PTR sign_mechanism;
7e9748
+	int verify_object;
7e9748
+	CK_MECHANISM_PTR verify_mechanism;
7e9748
+	int digest_object;
7e9748
+    } state[10];
7e9748
+#define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0]))
7e9748
+    FILE *logfile;
7e9748
+} soft_token;
7e9748
+
7e9748
+static void
7e9748
+application_error(const char *fmt, ...)
7e9748
+{
7e9748
+    va_list ap;
7e9748
+    va_start(ap, fmt);
7e9748
+    vprintf(fmt, ap);
7e9748
+    va_end(ap);
7e9748
+    if (soft_token.flags.app_error_fatal)
7e9748
+	abort();
7e9748
+}
7e9748
+
7e9748
+static void
7e9748
+st_logf(const char *fmt, ...)
7e9748
+{
7e9748
+    va_list ap;
7e9748
+    if (soft_token.logfile == NULL)
7e9748
+	return;
7e9748
+    va_start(ap, fmt);
7e9748
+    vfprintf(soft_token.logfile, fmt, ap);
7e9748
+    va_end(ap);
7e9748
+    fflush(soft_token.logfile);
7e9748
+}
7e9748
+
7e9748
+static void
7e9748
+snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...)
7e9748
+{
7e9748
+    int len;
7e9748
+    va_list ap;
7e9748
+    len = vsnprintf(str, size, fmt, ap);
7e9748
+    va_end(ap);
7e9748
+    if (len < 0 || len > (int) size)
7e9748
+	return;
7e9748
+    while(len < (int) size)
7e9748
+	str[len++] = fillchar;
7e9748
+}
7e9748
+
7e9748
+#ifndef TEST_APP
7e9748
+#define printf error_use_st_logf
7e9748
+#endif
7e9748
+
7e9748
+#define VERIFY_SESSION_HANDLE(s, state)			\
7e9748
+{							\
7e9748
+    CK_RV ret;						\
7e9748
+    ret = verify_session_handle(s, state);		\
7e9748
+    if (ret != CKR_OK) {				\
7e9748
+	/* return CKR_OK */;				\
7e9748
+    }							\
7e9748
+}
7e9748
+
7e9748
+static CK_RV
7e9748
+verify_session_handle(CK_SESSION_HANDLE hSession,
7e9748
+		      struct session_state **state)
7e9748
+{
7e9748
+    size_t i;
7e9748
+
7e9748
+    for (i = 0; i < MAX_NUM_SESSION; i++){
7e9748
+	if (soft_token.state[i].session_handle == hSession)
7e9748
+	    break;
7e9748
+    }
7e9748
+    if (i == MAX_NUM_SESSION) {
7e9748
+	application_error("use of invalid handle: 0x%08lx\n",
7e9748
+			  (unsigned long)hSession);
7e9748
+	return CKR_SESSION_HANDLE_INVALID;
7e9748
+    }
7e9748
+    if (state)
7e9748
+	*state = &soft_token.state[i];
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+static CK_RV
7e9748
+object_handle_to_object(CK_OBJECT_HANDLE handle,
7e9748
+			struct st_object **object)
7e9748
+{
7e9748
+    int i = HANDLE_OBJECT_ID(handle);
7e9748
+
7e9748
+    *object = NULL;
7e9748
+    if (i >= soft_token.object.num_objs)
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+    if (soft_token.object.objs[i] == NULL)
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+    if (soft_token.object.objs[i]->object_handle != handle)
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+    *object = soft_token.object.objs[i];
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+static int
7e9748
+attributes_match(const struct st_object *obj,
7e9748
+		 const CK_ATTRIBUTE *attributes,
7e9748
+		 CK_ULONG num_attributes)
7e9748
+{
7e9748
+    CK_ULONG i;
7e9748
+    int j;
7e9748
+    st_logf("attributes_match: %ld\n", (unsigned long)OBJECT_ID(obj));
7e9748
+
7e9748
+    for (i = 0; i < num_attributes; i++) {
7e9748
+	int match = 0;
7e9748
+	for (j = 0; j < obj->num_attributes; j++) {
7e9748
+	    if (attributes[i].type == obj->attrs[j].attribute.type &&
7e9748
+		attributes[i].ulValueLen == obj->attrs[j].attribute.ulValueLen &&
7e9748
+		memcmp(attributes[i].pValue, obj->attrs[j].attribute.pValue,
7e9748
+		       attributes[i].ulValueLen) == 0) {
7e9748
+		match = 1;
7e9748
+		break;
7e9748
+	    }
7e9748
+	}
7e9748
+	if (match == 0) {
7e9748
+	    st_logf("type %d attribute have no match\n", attributes[i].type);
7e9748
+	    return 0;
7e9748
+	}
7e9748
+    }
7e9748
+    st_logf("attribute matches\n");
7e9748
+    return 1;
7e9748
+}
7e9748
+
7e9748
+static void
7e9748
+print_attributes(const CK_ATTRIBUTE *attributes,
7e9748
+		 CK_ULONG num_attributes)
7e9748
+{
7e9748
+    CK_ULONG i;
7e9748
+
7e9748
+    st_logf("find objects: attrs: %lu\n", (unsigned long)num_attributes);
7e9748
+
7e9748
+    for (i = 0; i < num_attributes; i++) {
7e9748
+	st_logf("  type: ");
7e9748
+	switch (attributes[i].type) {
7e9748
+	case CKA_TOKEN: {
7e9748
+	    CK_BBOOL *ck_true;
7e9748
+	    if (attributes[i].ulValueLen != sizeof(CK_BBOOL)) {
7e9748
+		application_error("token attribute wrong length\n");
7e9748
+		break;
7e9748
+	    }
7e9748
+	    ck_true = attributes[i].pValue;
7e9748
+	    st_logf("token: %s", *ck_true ? "TRUE" : "FALSE");
7e9748
+	    break;
7e9748
+	}
7e9748
+	case CKA_CLASS: {
7e9748
+	    CK_OBJECT_CLASS *class;
7e9748
+	    if (attributes[i].ulValueLen != sizeof(CK_ULONG)) {
7e9748
+		application_error("class attribute wrong length\n");
7e9748
+		break;
7e9748
+	    }
7e9748
+	    class = attributes[i].pValue;
7e9748
+	    st_logf("class ");
7e9748
+	    switch (*class) {
7e9748
+	    case CKO_CERTIFICATE:
7e9748
+		st_logf("certificate");
7e9748
+		break;
7e9748
+	    case CKO_PUBLIC_KEY:
7e9748
+		st_logf("public key");
7e9748
+		break;
7e9748
+	    case CKO_PRIVATE_KEY:
7e9748
+		st_logf("private key");
7e9748
+		break;
7e9748
+	    case CKO_SECRET_KEY:
7e9748
+		st_logf("secret key");
7e9748
+		break;
7e9748
+	    case CKO_DOMAIN_PARAMETERS:
7e9748
+		st_logf("domain parameters");
7e9748
+		break;
7e9748
+	    default:
7e9748
+		st_logf("[class %lx]", (long unsigned)*class);
7e9748
+		break;
7e9748
+	    }
7e9748
+	    break;
7e9748
+	}
7e9748
+	case CKA_PRIVATE:
7e9748
+	    st_logf("private");
7e9748
+	    break;
7e9748
+	case CKA_LABEL:
7e9748
+	    st_logf("label");
7e9748
+	    break;
7e9748
+	case CKA_APPLICATION:
7e9748
+	    st_logf("application");
7e9748
+	    break;
7e9748
+	case CKA_VALUE:
7e9748
+	    st_logf("value");
7e9748
+	    break;
7e9748
+	case CKA_ID:
7e9748
+	    st_logf("id");
7e9748
+	    break;
7e9748
+	default:
7e9748
+	    st_logf("[unknown 0x%08lx]", (unsigned long)attributes[i].type);
7e9748
+	    break;
7e9748
+	}
7e9748
+	st_logf("\n");
7e9748
+    }
7e9748
+}
7e9748
+
7e9748
+static struct st_object *
7e9748
+add_st_object(void)
7e9748
+{
7e9748
+    struct st_object *o, **objs;
7e9748
+    int i;
7e9748
+
7e9748
+    o = malloc(sizeof(*o));
7e9748
+    if (o == NULL)
7e9748
+	return NULL;
7e9748
+    memset(o, 0, sizeof(*o));
7e9748
+    o->attrs = NULL;
7e9748
+    o->num_attributes = 0;
7e9748
+
7e9748
+    for (i = 0; i < soft_token.object.num_objs; i++) {
7e9748
+	if (soft_token.object.objs == NULL) {
7e9748
+	    soft_token.object.objs[i] = o;
7e9748
+	    break;
7e9748
+	}
7e9748
+    }
7e9748
+    if (i == soft_token.object.num_objs) {
7e9748
+	objs = realloc(soft_token.object.objs,
7e9748
+		       (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0]));
7e9748
+	if (objs == NULL) {
7e9748
+	    free(o);
7e9748
+	    return NULL;
7e9748
+	}
7e9748
+	soft_token.object.objs = objs;
7e9748
+	soft_token.object.objs[soft_token.object.num_objs++] = o;
7e9748
+    }	
7e9748
+    soft_token.object.objs[i]->object_handle =
7e9748
+	(random() & (~OBJECT_ID_MASK)) | i;
7e9748
+
7e9748
+    return o;
7e9748
+}
7e9748
+
7e9748
+static CK_RV
7e9748
+add_object_attribute(struct st_object *o,
7e9748
+		     int secret,
7e9748
+		     CK_ATTRIBUTE_TYPE type,
7e9748
+		     CK_VOID_PTR pValue,
7e9748
+		     CK_ULONG ulValueLen)
7e9748
+{
7e9748
+    struct st_attr *a;
7e9748
+    int i;
7e9748
+
7e9748
+    i = o->num_attributes;
7e9748
+    a = realloc(o->attrs, (i + 1) * sizeof(o->attrs[0]));
7e9748
+    if (a == NULL)
7e9748
+	return CKR_DEVICE_MEMORY;
7e9748
+    o->attrs = a;
7e9748
+    o->attrs[i].secret = secret;
7e9748
+    o->attrs[i].attribute.type = type;
7e9748
+    o->attrs[i].attribute.pValue = malloc(ulValueLen);
7e9748
+    if (o->attrs[i].attribute.pValue == NULL && ulValueLen != 0)
7e9748
+	return CKR_DEVICE_MEMORY;
7e9748
+    memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen);
7e9748
+    o->attrs[i].attribute.ulValueLen = ulValueLen;
7e9748
+    o->num_attributes++;
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+static CK_RV
7e9748
+add_pubkey_info(struct st_object *o, CK_KEY_TYPE key_type, EVP_PKEY *key)
7e9748
+{
7e9748
+    switch (key_type) {
7e9748
+    case CKK_RSA: {
7e9748
+	CK_BYTE *modulus = NULL;
7e9748
+	size_t modulus_len = 0;
7e9748
+	CK_ULONG modulus_bits = 0;
7e9748
+	CK_BYTE *exponent = NULL;
7e9748
+	size_t exponent_len = 0;
7e9748
+	RSA* rsa = NULL;
7e9748
+	const BIGNUM *n = NULL, *e = NULL;
7e9748
+
7e9748
+	rsa = EVP_PKEY_get0_RSA(key);
7e9748
+	RSA_get0_key(rsa, &n, &e, NULL);
7e9748
+
7e9748
+	modulus_bits = BN_num_bits(n);
7e9748
+
7e9748
+	modulus_len = BN_num_bytes(n);
7e9748
+	modulus = malloc(modulus_len);
7e9748
+	BN_bn2bin(n, modulus);
7e9748
+	
7e9748
+	exponent_len = BN_num_bytes(e);
7e9748
+	exponent = malloc(exponent_len);
7e9748
+	BN_bn2bin(e, exponent);
7e9748
+	
7e9748
+	add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len);
7e9748
+	add_object_attribute(o, 0, CKA_MODULUS_BITS,
7e9748
+			     &modulus_bits, sizeof(modulus_bits));
7e9748
+	add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT,
7e9748
+			     exponent, exponent_len);
7e9748
+
7e9748
+	RSA_set_method(rsa, RSA_PKCS1_OpenSSL());
7e9748
+
7e9748
+	free(modulus);
7e9748
+	free(exponent);
7e9748
+    }
7e9748
+    default:
7e9748
+	/* XXX */
7e9748
+	break;
7e9748
+    }
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+
7e9748
+static int
7e9748
+pem_callback(char *buf, int num, int w, void *key)
7e9748
+{
7e9748
+    return -1;
7e9748
+}
7e9748
+
7e9748
+
7e9748
+static CK_RV
7e9748
+add_certificate(char *label,
7e9748
+		const char *cert_file,
7e9748
+		const char *private_key_file,
7e9748
+		char *id,
7e9748
+		int anchor)
7e9748
+{
7e9748
+    struct st_object *o = NULL;
7e9748
+    CK_BBOOL bool_true = CK_TRUE;
7e9748
+    CK_BBOOL bool_false = CK_FALSE;
7e9748
+    CK_OBJECT_CLASS c;
7e9748
+    CK_CERTIFICATE_TYPE cert_type = CKC_X_509;
7e9748
+    CK_KEY_TYPE key_type;
7e9748
+    CK_MECHANISM_TYPE mech_type;
7e9748
+    void *cert_data = NULL;
7e9748
+    size_t cert_length;
7e9748
+    void *subject_data = NULL;
7e9748
+    size_t subject_length;
7e9748
+    void *issuer_data = NULL;
7e9748
+    size_t issuer_length;
7e9748
+    void *serial_data = NULL;
7e9748
+    size_t serial_length;
7e9748
+    CK_RV ret = CKR_GENERAL_ERROR;
7e9748
+    X509 *cert;
7e9748
+    EVP_PKEY *public_key;
7e9748
+
7e9748
+    size_t id_len = strlen(id);
7e9748
+
7e9748
+    {
7e9748
+	FILE *f;
7e9748
+	
7e9748
+	f = fopen(cert_file, "r");
7e9748
+	if (f == NULL) {
7e9748
+	    st_logf("failed to open file %s\n", cert_file);
7e9748
+	    return CKR_GENERAL_ERROR;
7e9748
+	}
7e9748
+
7e9748
+	cert = PEM_read_X509(f, NULL, NULL, NULL);
7e9748
+	fclose(f);
7e9748
+	if (cert == NULL) {
7e9748
+	    st_logf("failed reading PEM cert\n");
7e9748
+	    return CKR_GENERAL_ERROR;
7e9748
+	}
7e9748
+
7e9748
+	OPENSSL_ASN1_MALLOC_ENCODE(X509, cert_data, cert_length, cert, ret);
7e9748
+	if (ret)
7e9748
+	    goto out;
7e9748
+
7e9748
+	OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, issuer_data, issuer_length,
7e9748
+				   X509_get_issuer_name(cert), ret);
7e9748
+	if (ret)
7e9748
+	    goto out;
7e9748
+
7e9748
+	OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, subject_data, subject_length,
7e9748
+				   X509_get_subject_name(cert), ret);
7e9748
+	if (ret)
7e9748
+	    goto out;
7e9748
+
7e9748
+	OPENSSL_ASN1_MALLOC_ENCODE(ASN1_INTEGER, serial_data, serial_length,
7e9748
+				   X509_get_serialNumber(cert), ret);
7e9748
+	if (ret)
7e9748
+	    goto out;
7e9748
+
7e9748
+    }
7e9748
+
7e9748
+    st_logf("done parsing, adding to internal structure\n");
7e9748
+
7e9748
+    o = add_st_object();
7e9748
+    if (o == NULL) {
7e9748
+	ret = CKR_DEVICE_MEMORY;
7e9748
+	goto out;
7e9748
+    }
7e9748
+    o->type = STO_T_CERTIFICATE;
7e9748
+    o->u.cert = cert;
7e9748
+    public_key = X509_get_pubkey(o->u.cert);
7e9748
+
7e9748
+    switch (EVP_PKEY_base_id(public_key)) {
7e9748
+    case EVP_PKEY_RSA:
7e9748
+	key_type = CKK_RSA;
7e9748
+	break;
7e9748
+    case EVP_PKEY_DSA:
7e9748
+	key_type = CKK_DSA;
7e9748
+	break;
7e9748
+    default:
7e9748
+	/* XXX */
7e9748
+	break;
7e9748
+    }
7e9748
+
7e9748
+    c = CKO_CERTIFICATE;
7e9748
+    add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c));
7e9748
+    add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true));
7e9748
+    add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false));
7e9748
+    add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false));
7e9748
+    add_object_attribute(o, 0, CKA_LABEL, label, strlen(label));
7e9748
+
7e9748
+    add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type));
7e9748
+    add_object_attribute(o, 0, CKA_ID, id, id_len);
7e9748
+
7e9748
+    add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length);
7e9748
+    add_object_attribute(o, 0, CKA_ISSUER, issuer_data, issuer_length);
7e9748
+    add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data, serial_length);
7e9748
+    add_object_attribute(o, 0, CKA_VALUE, cert_data, cert_length);
7e9748
+    if (anchor)
7e9748
+	add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true));
7e9748
+    else
7e9748
+	add_object_attribute(o, 0, CKA_TRUSTED, &bool_false, sizeof(bool_false));
7e9748
+
7e9748
+    st_logf("add cert ok: %lx\n", (unsigned long)OBJECT_ID(o));
7e9748
+
7e9748
+    o = add_st_object();
7e9748
+    if (o == NULL) {
7e9748
+	ret = CKR_DEVICE_MEMORY;
7e9748
+	goto out;
7e9748
+    }
7e9748
+    o->type = STO_T_PUBLIC_KEY;
7e9748
+    o->u.public_key = public_key;
7e9748
+
7e9748
+    c = CKO_PUBLIC_KEY;
7e9748
+    add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c));
7e9748
+    add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true));
7e9748
+    add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false));
7e9748
+    add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false));
7e9748
+    add_object_attribute(o, 0, CKA_LABEL, label, strlen(label));
7e9748
+
7e9748
+    add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type));
7e9748
+    add_object_attribute(o, 0, CKA_ID, id, id_len);
7e9748
+    add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */
7e9748
+    add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */
7e9748
+    add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false));
7e9748
+    add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false));
7e9748
+    mech_type = CKM_RSA_X_509;
7e9748
+    add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type));
7e9748
+
7e9748
+    add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length);
7e9748
+    add_object_attribute(o, 0, CKA_ENCRYPT, &bool_true, sizeof(bool_true));
7e9748
+    add_object_attribute(o, 0, CKA_VERIFY, &bool_true, sizeof(bool_true));
7e9748
+    add_object_attribute(o, 0, CKA_VERIFY_RECOVER, &bool_false, sizeof(bool_false));
7e9748
+    add_object_attribute(o, 0, CKA_WRAP, &bool_true, sizeof(bool_true));
7e9748
+    add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true));
7e9748
+
7e9748
+    add_pubkey_info(o, key_type, public_key);
7e9748
+
7e9748
+    st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o));
7e9748
+
7e9748
+    if (private_key_file) {
7e9748
+	CK_FLAGS flags;
7e9748
+	FILE *f;
7e9748
+
7e9748
+	o = add_st_object();
7e9748
+	if (o == NULL) {
7e9748
+	    ret = CKR_DEVICE_MEMORY;
7e9748
+	    goto out;
7e9748
+	}
7e9748
+	o->type = STO_T_PRIVATE_KEY;
7e9748
+	o->u.private_key.file = strdup(private_key_file);
7e9748
+	o->u.private_key.key = NULL;
7e9748
+
7e9748
+	o->u.private_key.cert = cert;
7e9748
+
7e9748
+	c = CKO_PRIVATE_KEY;
7e9748
+	add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c));
7e9748
+	add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true));
7e9748
+	add_object_attribute(o, 0, CKA_PRIVATE, &bool_true, sizeof(bool_false));
7e9748
+	add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false));
7e9748
+	add_object_attribute(o, 0, CKA_LABEL, label, strlen(label));
7e9748
+
7e9748
+	add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type));
7e9748
+	add_object_attribute(o, 0, CKA_ID, id, id_len);
7e9748
+	add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */
7e9748
+	add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */
7e9748
+	add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false));
7e9748
+	add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false));
7e9748
+	mech_type = CKM_RSA_X_509;
7e9748
+	add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type));
7e9748
+
7e9748
+	add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length);
7e9748
+	add_object_attribute(o, 0, CKA_SENSITIVE, &bool_true, sizeof(bool_true));
7e9748
+	add_object_attribute(o, 0, CKA_SECONDARY_AUTH, &bool_false, sizeof(bool_true));
7e9748
+	flags = 0;
7e9748
+	add_object_attribute(o, 0, CKA_AUTH_PIN_FLAGS, &flags, sizeof(flags));
7e9748
+
7e9748
+	add_object_attribute(o, 0, CKA_DECRYPT, &bool_true, sizeof(bool_true));
7e9748
+	add_object_attribute(o, 0, CKA_SIGN, &bool_true, sizeof(bool_true));
7e9748
+	add_object_attribute(o, 0, CKA_SIGN_RECOVER, &bool_false, sizeof(bool_false));
7e9748
+	add_object_attribute(o, 0, CKA_UNWRAP, &bool_true, sizeof(bool_true));
7e9748
+	add_object_attribute(o, 0, CKA_EXTRACTABLE, &bool_true, sizeof(bool_true));
7e9748
+	add_object_attribute(o, 0, CKA_NEVER_EXTRACTABLE, &bool_false, sizeof(bool_false));
7e9748
+
7e9748
+	add_pubkey_info(o, key_type, public_key);
7e9748
+
7e9748
+	f = fopen(private_key_file, "r");
7e9748
+	if (f == NULL) {
7e9748
+	    st_logf("failed to open private key\n");
7e9748
+	    return CKR_GENERAL_ERROR;
7e9748
+	}
7e9748
+
7e9748
+	o->u.private_key.key = PEM_read_PrivateKey(f, NULL, pem_callback, NULL);
7e9748
+	fclose(f);
7e9748
+	if (o->u.private_key.key == NULL) {
7e9748
+	    st_logf("failed to read private key a startup\n");
7e9748
+	    /* don't bother with this failure for now,
7e9748
+	       fix it at C_Login time */;
7e9748
+	} else {
7e9748
+	    /* XXX verify keytype */
7e9748
+
7e9748
+	    if (key_type == CKK_RSA) {
7e9748
+		RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key);
7e9748
+		RSA_set_method(rsa, RSA_PKCS1_OpenSSL());
7e9748
+	    }
7e9748
+
7e9748
+	    if (X509_check_private_key(cert, o->u.private_key.key) != 1) {
7e9748
+		EVP_PKEY_free(o->u.private_key.key);
7e9748
+		o->u.private_key.key = NULL;
7e9748
+		st_logf("private key doesn't verify\n");
7e9748
+	    } else {
7e9748
+		st_logf("private key usable\n");
7e9748
+		soft_token.flags.login_done = 1;
7e9748
+	    }
7e9748
+	}
7e9748
+    }
7e9748
+
7e9748
+    ret = CKR_OK;
7e9748
+ out:
7e9748
+    if (ret != CKR_OK) {
7e9748
+	st_logf("something went wrong when adding cert!\n");
7e9748
+
7e9748
+	/* XXX wack o */;
7e9748
+    }
7e9748
+    free(cert_data);
7e9748
+    free(serial_data);
7e9748
+    free(issuer_data);
7e9748
+    free(subject_data);
7e9748
+
7e9748
+    return ret;
7e9748
+}
7e9748
+
7e9748
+static void
7e9748
+find_object_final(struct session_state *state)
7e9748
+{
7e9748
+    if (state->find.attributes) {
7e9748
+	CK_ULONG i;
7e9748
+
7e9748
+	for (i = 0; i < state->find.num_attributes; i++) {
7e9748
+	    if (state->find.attributes[i].pValue)
7e9748
+		free(state->find.attributes[i].pValue);
7e9748
+	}
7e9748
+	free(state->find.attributes);
7e9748
+	state->find.attributes = NULL;
7e9748
+	state->find.num_attributes = 0;
7e9748
+	state->find.next_object = -1;
7e9748
+    }
7e9748
+}
7e9748
+
7e9748
+static void
7e9748
+reset_crypto_state(struct session_state *state)
7e9748
+{
7e9748
+    state->encrypt_object = -1;
7e9748
+    if (state->encrypt_mechanism)
7e9748
+	free(state->encrypt_mechanism);
7e9748
+    state->encrypt_mechanism = NULL_PTR;
7e9748
+    state->decrypt_object = -1;
7e9748
+    if (state->decrypt_mechanism)
7e9748
+	free(state->decrypt_mechanism);
7e9748
+    state->decrypt_mechanism = NULL_PTR;
7e9748
+    state->sign_object = -1;
7e9748
+    if (state->sign_mechanism)
7e9748
+	free(state->sign_mechanism);
7e9748
+    state->sign_mechanism = NULL_PTR;
7e9748
+    state->verify_object = -1;
7e9748
+    if (state->verify_mechanism)
7e9748
+	free(state->verify_mechanism);
7e9748
+    state->verify_mechanism = NULL_PTR;
7e9748
+    state->digest_object = -1;
7e9748
+}
7e9748
+
7e9748
+static void
7e9748
+close_session(struct session_state *state)
7e9748
+{
7e9748
+    if (state->find.attributes) {
7e9748
+	application_error("application didn't do C_FindObjectsFinal\n");
7e9748
+	find_object_final(state);
7e9748
+    }
7e9748
+
7e9748
+    state->session_handle = CK_INVALID_HANDLE;
7e9748
+    soft_token.application = NULL_PTR;
7e9748
+    soft_token.notify = NULL_PTR;
7e9748
+    reset_crypto_state(state);
7e9748
+}
7e9748
+
7e9748
+static const char *
7e9748
+has_session(void)
7e9748
+{
7e9748
+    return soft_token.open_sessions > 0 ? "yes" : "no";
7e9748
+}
7e9748
+
7e9748
+static void
7e9748
+read_conf_file(const char *fn)
7e9748
+{
7e9748
+    char buf[1024], *cert, *key, *id, *label, *s, *p;
7e9748
+    int anchor;
7e9748
+    FILE *f;
7e9748
+
7e9748
+    f = fopen(fn, "r");
7e9748
+    if (f == NULL) {
7e9748
+	st_logf("can't open configuration file %s\n", fn);
7e9748
+	return;
7e9748
+    }
7e9748
+
7e9748
+    while(fgets(buf, sizeof(buf), f) != NULL) {
7e9748
+	buf[strcspn(buf, "\n")] = '\0';
7e9748
+
7e9748
+	anchor = 0;
7e9748
+
7e9748
+	st_logf("line: %s\n", buf);
7e9748
+
7e9748
+	p = buf;
7e9748
+	while (isspace(*p))
7e9748
+	    p++;
7e9748
+	if (*p == '#')
7e9748
+	    continue;
7e9748
+	while (isspace(*p))
7e9748
+	    p++;
7e9748
+
7e9748
+	s = NULL;
7e9748
+	id = strtok_r(p, "\t", &s);
7e9748
+	if (id == NULL)
7e9748
+	    continue;
7e9748
+	label = strtok_r(NULL, "\t", &s);
7e9748
+	if (label == NULL)
7e9748
+	    continue;
7e9748
+	cert = strtok_r(NULL, "\t", &s);
7e9748
+	if (cert == NULL)
7e9748
+	    continue;
7e9748
+	key = strtok_r(NULL, "\t", &s);
7e9748
+
7e9748
+	/* XXX */
7e9748
+	if (strcmp(id, "anchor") == 0) {
7e9748
+	    id = "\x00\x00";
7e9748
+	    anchor = 1;
7e9748
+	}
7e9748
+
7e9748
+	st_logf("adding: %s\n", label);
7e9748
+
7e9748
+	add_certificate(label, cert, key, id, anchor);
7e9748
+    }
7e9748
+}
7e9748
+
7e9748
+static CK_RV
7e9748
+func_not_supported(void)
7e9748
+{
7e9748
+    st_logf("function not supported\n");
7e9748
+    return CKR_FUNCTION_NOT_SUPPORTED;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_Initialize(CK_VOID_PTR a)
7e9748
+{
7e9748
+    CK_C_INITIALIZE_ARGS_PTR args = a;
7e9748
+    st_logf("Initialize\n");
7e9748
+    size_t i;
7e9748
+
7e9748
+    OpenSSL_add_all_algorithms();
7e9748
+    ERR_load_crypto_strings();
7e9748
+
7e9748
+    srandom(getpid() ^ time(NULL));
7e9748
+
7e9748
+    for (i = 0; i < MAX_NUM_SESSION; i++) {
7e9748
+	soft_token.state[i].session_handle = CK_INVALID_HANDLE;
7e9748
+	soft_token.state[i].find.attributes = NULL;
7e9748
+	soft_token.state[i].find.num_attributes = 0;
7e9748
+	soft_token.state[i].find.next_object = -1;
7e9748
+	reset_crypto_state(&soft_token.state[i]);
7e9748
+    }
7e9748
+
7e9748
+    soft_token.flags.hardware_slot = 1;
7e9748
+    soft_token.flags.app_error_fatal = 0;
7e9748
+    soft_token.flags.login_done = 0;
7e9748
+
7e9748
+    soft_token.object.objs = NULL;
7e9748
+    soft_token.object.num_objs = 0;
7e9748
+
7e9748
+    soft_token.logfile = NULL;
7e9748
+#if 1
7e9748
+//     soft_token.logfile = stdout;
7e9748
+#endif
7e9748
+#if 0
7e9748
+    soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a");
7e9748
+#endif
7e9748
+
7e9748
+    if (a != NULL_PTR) {
7e9748
+	st_logf("\tCreateMutex:\t%p\n", args->CreateMutex);
7e9748
+	st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex);
7e9748
+	st_logf("\tLockMutext\t%p\n", args->LockMutex);
7e9748
+	st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex);
7e9748
+	st_logf("\tFlags\t%04x\n", (unsigned int)args->flags);
7e9748
+    }
7e9748
+
7e9748
+    {
7e9748
+	char *fn = NULL, *home = NULL;
7e9748
+
7e9748
+	if (getuid() == geteuid()) {
7e9748
+	    fn = getenv("SOFTPKCS11RC");
7e9748
+	    if (fn)
7e9748
+		fn = strdup(fn);
7e9748
+	    home = getenv("HOME");
7e9748
+	}
7e9748
+	if (fn == NULL && home == NULL) {
7e9748
+	    struct passwd *pw = getpwuid(getuid());	
7e9748
+	    if(pw != NULL)
7e9748
+		home = pw->pw_dir;
7e9748
+	}
7e9748
+	if (fn == NULL) {
7e9748
+	    if (home)
7e9748
+		asprintf(&fn, "%s/.soft-token.rc", home);
7e9748
+	    else
7e9748
+		fn = strdup("/etc/soft-token.rc");
7e9748
+	}
7e9748
+
7e9748
+	read_conf_file(fn);
7e9748
+	free(fn);
7e9748
+    }
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_Finalize(CK_VOID_PTR args)
7e9748
+{
7e9748
+    size_t i;
7e9748
+
7e9748
+    st_logf("Finalize\n");
7e9748
+
7e9748
+    for (i = 0; i < MAX_NUM_SESSION; i++) {
7e9748
+	if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) {
7e9748
+	    application_error("application finalized without "
7e9748
+			      "closing session\n");
7e9748
+	    close_session(&soft_token.state[i]);
7e9748
+	}
7e9748
+    }
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetInfo(CK_INFO_PTR args)
7e9748
+{
7e9748
+    st_logf("GetInfo\n");
7e9748
+
7e9748
+    memset(args, 17, sizeof(*args));
7e9748
+    args->cryptokiVersion.major = 2;
7e9748
+    args->cryptokiVersion.minor = 10;
7e9748
+    snprintf_fill((char *)args->manufacturerID,
7e9748
+		  sizeof(args->manufacturerID),
7e9748
+		  ' ',
7e9748
+		  "SoftToken");
7e9748
+    snprintf_fill((char *)args->libraryDescription,
7e9748
+		  sizeof(args->libraryDescription), ' ',
7e9748
+		  "SoftToken");
7e9748
+    args->libraryVersion.major = 1;
7e9748
+    args->libraryVersion.minor = 8;
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+extern CK_FUNCTION_LIST funcs;
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
7e9748
+{
7e9748
+    *ppFunctionList = &funcs;
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetSlotList(CK_BBOOL tokenPresent,
7e9748
+	      CK_SLOT_ID_PTR pSlotList,
7e9748
+	      CK_ULONG_PTR   pulCount)
7e9748
+{
7e9748
+    st_logf("GetSlotList: %s\n",
7e9748
+	    tokenPresent ? "tokenPresent" : "token not Present");
7e9748
+    if (pSlotList)
7e9748
+	pSlotList[0] = 1;
7e9748
+    *pulCount = 1;
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetSlotInfo(CK_SLOT_ID slotID,
7e9748
+	      CK_SLOT_INFO_PTR pInfo)
7e9748
+{
7e9748
+    st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session());
7e9748
+
7e9748
+    memset(pInfo, 18, sizeof(*pInfo));
7e9748
+
7e9748
+    if (slotID != 1)
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+
7e9748
+    snprintf_fill((char *)pInfo->slotDescription,
7e9748
+		  sizeof(pInfo->slotDescription),
7e9748
+		  ' ',
7e9748
+		  "SoftToken (slot)");
7e9748
+    snprintf_fill((char *)pInfo->manufacturerID,
7e9748
+		  sizeof(pInfo->manufacturerID),
7e9748
+		  ' ',
7e9748
+		  "SoftToken (slot)");
7e9748
+    pInfo->flags = CKF_TOKEN_PRESENT;
7e9748
+    if (soft_token.flags.hardware_slot)
7e9748
+	pInfo->flags |= CKF_HW_SLOT;
7e9748
+    pInfo->hardwareVersion.major = 1;
7e9748
+    pInfo->hardwareVersion.minor = 0;
7e9748
+    pInfo->firmwareVersion.major = 1;
7e9748
+    pInfo->firmwareVersion.minor = 0;
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetTokenInfo(CK_SLOT_ID slotID,
7e9748
+	       CK_TOKEN_INFO_PTR pInfo)
7e9748
+{
7e9748
+    st_logf("GetTokenInfo: %s\n", has_session());
7e9748
+
7e9748
+    memset(pInfo, 19, sizeof(*pInfo));
7e9748
+
7e9748
+    snprintf_fill((char *)pInfo->label,
7e9748
+		  sizeof(pInfo->label),
7e9748
+		  ' ',
7e9748
+		  "SoftToken (token)");
7e9748
+    snprintf_fill((char *)pInfo->manufacturerID,
7e9748
+		  sizeof(pInfo->manufacturerID),
7e9748
+		  ' ',
7e9748
+		  "SoftToken (token)");
7e9748
+    snprintf_fill((char *)pInfo->model,
7e9748
+		  sizeof(pInfo->model),
7e9748
+		  ' ',
7e9748
+		  "SoftToken (token)");
7e9748
+    snprintf_fill((char *)pInfo->serialNumber,
7e9748
+		  sizeof(pInfo->serialNumber),
7e9748
+		  ' ',
7e9748
+		  "4711");
7e9748
+    pInfo->flags =
7e9748
+	CKF_TOKEN_INITIALIZED |
7e9748
+	CKF_USER_PIN_INITIALIZED;
7e9748
+
7e9748
+    if (soft_token.flags.login_done == 0)
7e9748
+	pInfo->flags |= CKF_LOGIN_REQUIRED;
7e9748
+
7e9748
+    /* CFK_RNG |
7e9748
+       CKF_RESTORE_KEY_NOT_NEEDED |
7e9748
+    */
7e9748
+    pInfo->ulMaxSessionCount = MAX_NUM_SESSION;
7e9748
+    pInfo->ulSessionCount = soft_token.open_sessions;
7e9748
+    pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION;
7e9748
+    pInfo->ulRwSessionCount = soft_token.open_sessions;
7e9748
+    pInfo->ulMaxPinLen = 1024;
7e9748
+    pInfo->ulMinPinLen = 0;
7e9748
+    pInfo->ulTotalPublicMemory = 4711;
7e9748
+    pInfo->ulFreePublicMemory = 4712;
7e9748
+    pInfo->ulTotalPrivateMemory = 4713;
7e9748
+    pInfo->ulFreePrivateMemory = 4714;
7e9748
+    pInfo->hardwareVersion.major = 2;
7e9748
+    pInfo->hardwareVersion.minor = 0;
7e9748
+    pInfo->firmwareVersion.major = 2;
7e9748
+    pInfo->firmwareVersion.minor = 0;
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetMechanismList(CK_SLOT_ID slotID,
7e9748
+		   CK_MECHANISM_TYPE_PTR pMechanismList,
7e9748
+		   CK_ULONG_PTR pulCount)
7e9748
+{
7e9748
+    st_logf("GetMechanismList\n");
7e9748
+
7e9748
+    *pulCount = 2;
7e9748
+    if (pMechanismList == NULL_PTR)
7e9748
+	return CKR_OK;
7e9748
+    pMechanismList[0] = CKM_RSA_X_509;
7e9748
+    pMechanismList[1] = CKM_RSA_PKCS;
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetMechanismInfo(CK_SLOT_ID slotID,
7e9748
+		   CK_MECHANISM_TYPE type,
7e9748
+		   CK_MECHANISM_INFO_PTR pInfo)
7e9748
+{
7e9748
+    st_logf("GetMechanismInfo: slot %d type: %d\n",
7e9748
+	    (int)slotID, (int)type);
7e9748
+    return CKR_FUNCTION_NOT_SUPPORTED;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_InitToken(CK_SLOT_ID slotID,
7e9748
+	    CK_UTF8CHAR_PTR pPin,
7e9748
+	    CK_ULONG ulPinLen,
7e9748
+	    CK_UTF8CHAR_PTR pLabel)
7e9748
+{
7e9748
+    st_logf("InitToken: slot %d\n", (int)slotID);
7e9748
+    return CKR_FUNCTION_NOT_SUPPORTED;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_OpenSession(CK_SLOT_ID slotID,
7e9748
+	      CK_FLAGS flags,
7e9748
+	      CK_VOID_PTR pApplication,
7e9748
+	      CK_NOTIFY Notify,
7e9748
+	      CK_SESSION_HANDLE_PTR phSession)
7e9748
+{
7e9748
+    size_t i;
7e9748
+
7e9748
+    st_logf("OpenSession: slot: %d\n", (int)slotID);
7e9748
+
7e9748
+    if (soft_token.open_sessions == MAX_NUM_SESSION)
7e9748
+	return CKR_SESSION_COUNT;
7e9748
+
7e9748
+    soft_token.application = pApplication;
7e9748
+    soft_token.notify = Notify;
7e9748
+
7e9748
+    for (i = 0; i < MAX_NUM_SESSION; i++)
7e9748
+	if (soft_token.state[i].session_handle == CK_INVALID_HANDLE)
7e9748
+	    break;
7e9748
+    if (i == MAX_NUM_SESSION)
7e9748
+	abort();
7e9748
+
7e9748
+    soft_token.open_sessions++;
7e9748
+
7e9748
+    soft_token.state[i].session_handle =
7e9748
+	(CK_SESSION_HANDLE)(random() & 0xfffff);
7e9748
+    *phSession = soft_token.state[i].session_handle;
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_CloseSession(CK_SESSION_HANDLE hSession)
7e9748
+{
7e9748
+    struct session_state *state;
7e9748
+    st_logf("CloseSession\n");
7e9748
+
7e9748
+    if (verify_session_handle(hSession, &state) != CKR_OK)
7e9748
+	application_error("closed session not open");
7e9748
+    else
7e9748
+	close_session(state);
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_CloseAllSessions(CK_SLOT_ID slotID)
7e9748
+{
7e9748
+    size_t i;
7e9748
+
7e9748
+    st_logf("CloseAllSessions\n");
7e9748
+
7e9748
+    for (i = 0; i < MAX_NUM_SESSION; i++)
7e9748
+	if (soft_token.state[i].session_handle != CK_INVALID_HANDLE)
7e9748
+	    close_session(&soft_token.state[i]);
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetSessionInfo(CK_SESSION_HANDLE hSession,
7e9748
+		 CK_SESSION_INFO_PTR pInfo)
7e9748
+{
7e9748
+    st_logf("GetSessionInfo\n");
7e9748
+
7e9748
+    VERIFY_SESSION_HANDLE(hSession, NULL);
7e9748
+
7e9748
+    memset(pInfo, 20, sizeof(*pInfo));
7e9748
+
7e9748
+    pInfo->slotID = 1;
7e9748
+    if (soft_token.flags.login_done)
7e9748
+	pInfo->state = CKS_RO_USER_FUNCTIONS;
7e9748
+    else
7e9748
+	pInfo->state = CKS_RO_PUBLIC_SESSION;
7e9748
+    pInfo->flags = CKF_SERIAL_SESSION;
7e9748
+    pInfo->ulDeviceError = 0;
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_Login(CK_SESSION_HANDLE hSession,
7e9748
+	CK_USER_TYPE userType,
7e9748
+	CK_UTF8CHAR_PTR pPin,
7e9748
+	CK_ULONG ulPinLen)
7e9748
+{
7e9748
+    char *pin = NULL;
7e9748
+    int i;
7e9748
+
7e9748
+    st_logf("Login\n");
7e9748
+
7e9748
+    VERIFY_SESSION_HANDLE(hSession, NULL);
7e9748
+
7e9748
+    if (pPin != NULL_PTR) {
7e9748
+	asprintf(&pin, "%.*s", (int)ulPinLen, pPin);
7e9748
+	st_logf("type: %d password: %s\n", (int)userType, pin);
7e9748
+    }
7e9748
+
7e9748
+    for (i = 0; i < soft_token.object.num_objs; i++) {
7e9748
+	struct st_object *o = soft_token.object.objs[i];
7e9748
+	FILE *f;
7e9748
+
7e9748
+	if (o->type != STO_T_PRIVATE_KEY)
7e9748
+	    continue;
7e9748
+
7e9748
+	if (o->u.private_key.key)
7e9748
+	    continue;
7e9748
+
7e9748
+	f = fopen(o->u.private_key.file, "r");
7e9748
+	if (f == NULL) {
7e9748
+	    st_logf("can't open private file: %s\n", o->u.private_key.file);
7e9748
+	    continue;
7e9748
+	}
7e9748
+	
7e9748
+	o->u.private_key.key = PEM_read_PrivateKey(f, NULL, NULL, pin);
7e9748
+	fclose(f);
7e9748
+	if (o->u.private_key.key == NULL) {
7e9748
+	    st_logf("failed to read key: %s error: %s\n",
7e9748
+		    o->u.private_key.file,
7e9748
+		    ERR_error_string(ERR_get_error(), NULL));
7e9748
+	    /* just ignore failure */;
7e9748
+	    continue;
7e9748
+	}
7e9748
+
7e9748
+	/* XXX check keytype */
7e9748
+	RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key);
7e9748
+	RSA_set_method(rsa, RSA_PKCS1_OpenSSL());
7e9748
+
7e9748
+	if (X509_check_private_key(o->u.private_key.cert, o->u.private_key.key) != 1) {
7e9748
+	    EVP_PKEY_free(o->u.private_key.key);
7e9748
+	    o->u.private_key.key = NULL;
7e9748
+	    st_logf("private key %s doesn't verify\n", o->u.private_key.file);
7e9748
+	    continue;
7e9748
+	}
7e9748
+
7e9748
+	soft_token.flags.login_done = 1;
7e9748
+    }
7e9748
+    free(pin);
7e9748
+
7e9748
+    return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_Logout(CK_SESSION_HANDLE hSession)
7e9748
+{
7e9748
+    st_logf("Logout\n");
7e9748
+    VERIFY_SESSION_HANDLE(hSession, NULL);
7e9748
+    return CKR_FUNCTION_NOT_SUPPORTED;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetObjectSize(CK_SESSION_HANDLE hSession,
7e9748
+		CK_OBJECT_HANDLE hObject,
7e9748
+		CK_ULONG_PTR pulSize)
7e9748
+{
7e9748
+    st_logf("GetObjectSize\n");
7e9748
+    VERIFY_SESSION_HANDLE(hSession, NULL);
7e9748
+    return CKR_FUNCTION_NOT_SUPPORTED;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_GetAttributeValue(CK_SESSION_HANDLE hSession,
7e9748
+		    CK_OBJECT_HANDLE hObject,
7e9748
+		    CK_ATTRIBUTE_PTR pTemplate,
7e9748
+		    CK_ULONG ulCount)
7e9748
+{
7e9748
+    struct session_state *state;
7e9748
+    struct st_object *obj;
7e9748
+    CK_ULONG i;
7e9748
+    CK_RV ret;
7e9748
+    int j;
7e9748
+
7e9748
+    st_logf("GetAttributeValue: %lx\n",
7e9748
+	    (unsigned long)HANDLE_OBJECT_ID(hObject));
7e9748
+    VERIFY_SESSION_HANDLE(hSession, &state);
7e9748
+
7e9748
+    if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) {
7e9748
+	st_logf("object not found: %lx\n",
7e9748
+		(unsigned long)HANDLE_OBJECT_ID(hObject));
7e9748
+	return ret;
7e9748
+    }
7e9748
+
7e9748
+    ret = CKR_OK;
7e9748
+    for (i = 0; i < ulCount; i++) {
7e9748
+	st_logf("	getting 0x%08lx\n", (unsigned long)pTemplate[i].type);
7e9748
+	for (j = 0; j < obj->num_attributes; j++) {
7e9748
+	    if (obj->attrs[j].secret) {
7e9748
+		pTemplate[i].ulValueLen = (CK_ULONG)-1;
7e9748
+		break;
7e9748
+	    }
7e9748
+	    if (pTemplate[i].type == obj->attrs[j].attribute.type) {
7e9748
+		if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) {
7e9748
+		    if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen)
7e9748
+			memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue,
7e9748
+			       obj->attrs[j].attribute.ulValueLen);
7e9748
+		}
7e9748
+		pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen;
7e9748
+		break;
7e9748
+	    }
7e9748
+	}
7e9748
+	if (j == obj->num_attributes) {
7e9748
+	    st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type);
7e9748
+	    pTemplate[i].ulValueLen = (CK_ULONG)-1;
7e9748
+            ret = CKR_ATTRIBUTE_TYPE_INVALID;
7e9748
+	}
7e9748
+
7e9748
+    }
7e9748
+    return ret;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_FindObjectsInit(CK_SESSION_HANDLE hSession,
7e9748
+		  CK_ATTRIBUTE_PTR pTemplate,
7e9748
+		  CK_ULONG ulCount)
7e9748
+{
7e9748
+    struct session_state *state;
7e9748
+
7e9748
+    st_logf("FindObjectsInit\n");
7e9748
+
7e9748
+    VERIFY_SESSION_HANDLE(hSession, &state);
7e9748
+
7e9748
+    if (state->find.next_object != -1) {
7e9748
+	application_error("application didn't do C_FindObjectsFinal\n");
7e9748
+	find_object_final(state);
7e9748
+    }
7e9748
+    if (ulCount) {
7e9748
+	CK_ULONG i;
7e9748
+
7e9748
+	print_attributes(pTemplate, ulCount);
7e9748
+
7e9748
+	state->find.attributes =
7e9748
+	    calloc(1, ulCount * sizeof(state->find.attributes[0]));
7e9748
+	if (state->find.attributes == NULL)
7e9748
+	    return CKR_DEVICE_MEMORY;
7e9748
+	for (i = 0; i < ulCount; i++) {
7e9748
+	    state->find.attributes[i].pValue =
7e9748
+		malloc(pTemplate[i].ulValueLen);
7e9748
+	    if (state->find.attributes[i].pValue == NULL) {
7e9748
+		find_object_final(state);
7e9748
+		return CKR_DEVICE_MEMORY;
7e9748
+	    }
7e9748
+	    memcpy(state->find.attributes[i].pValue,
7e9748
+		   pTemplate[i].pValue, pTemplate[i].ulValueLen);
7e9748
+	    state->find.attributes[i].type = pTemplate[i].type;
7e9748
+	    state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen;
7e9748
+	}
7e9748
+	state->find.num_attributes = ulCount;
7e9748
+	state->find.next_object = 0;
7e9748
+    } else {
7e9748
+	st_logf("find all objects\n");
7e9748
+	state->find.attributes = NULL;
7e9748
+	state->find.num_attributes = 0;
7e9748
+	state->find.next_object = 0;
7e9748
+    }
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_FindObjects(CK_SESSION_HANDLE hSession,
7e9748
+	      CK_OBJECT_HANDLE_PTR phObject,
7e9748
+	      CK_ULONG ulMaxObjectCount,
7e9748
+	      CK_ULONG_PTR pulObjectCount)
7e9748
+{
7e9748
+    struct session_state *state;
7e9748
+    int i;
7e9748
+
7e9748
+    st_logf("FindObjects\n");
7e9748
+
7e9748
+    VERIFY_SESSION_HANDLE(hSession, &state);
7e9748
+
7e9748
+    if (state->find.next_object == -1) {
7e9748
+	application_error("application didn't do C_FindObjectsInit\n");
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+    }
7e9748
+    if (ulMaxObjectCount == 0) {
7e9748
+	application_error("application asked for 0 objects\n");
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+    }
7e9748
+    *pulObjectCount = 0;
7e9748
+    for (i = state->find.next_object; i < soft_token.object.num_objs; i++) {
7e9748
+	st_logf("FindObjects: %d\n", i);
7e9748
+	state->find.next_object = i + 1;
7e9748
+	if (attributes_match(soft_token.object.objs[i],
7e9748
+			     state->find.attributes,
7e9748
+			     state->find.num_attributes)) {
7e9748
+	    *phObject++ = soft_token.object.objs[i]->object_handle;
7e9748
+	    ulMaxObjectCount--;
7e9748
+	    (*pulObjectCount)++;
7e9748
+	    if (ulMaxObjectCount == 0)
7e9748
+		break;
7e9748
+	}
7e9748
+    }
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
7e9748
+{
7e9748
+    struct session_state *state;
7e9748
+
7e9748
+    st_logf("FindObjectsFinal\n");
7e9748
+    VERIFY_SESSION_HANDLE(hSession, &state);
7e9748
+    find_object_final(state);
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+static CK_RV
7e9748
+commonInit(CK_ATTRIBUTE *attr_match, int attr_match_len,
7e9748
+	   const CK_MECHANISM_TYPE *mechs, int mechs_len,
7e9748
+	   const CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey,
7e9748
+	   struct st_object **o)
7e9748
+{
7e9748
+    CK_RV ret;
7e9748
+    int i;
7e9748
+
7e9748
+    *o = NULL;
7e9748
+    if ((ret = object_handle_to_object(hKey, o)) != CKR_OK)
7e9748
+	return ret;
7e9748
+
7e9748
+    ret = attributes_match(*o, attr_match, attr_match_len);
7e9748
+    if (!ret) {
7e9748
+	application_error("called commonInit on key that doesn't "
7e9748
+			  "support required attr");
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+    }
7e9748
+
7e9748
+    for (i = 0; i < mechs_len; i++)
7e9748
+	if (mechs[i] == pMechanism->mechanism)
7e9748
+	    break;
7e9748
+    if (i == mechs_len) {
7e9748
+	application_error("called mech (%08lx) not supported\n",
7e9748
+			  pMechanism->mechanism);
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+    }
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+
7e9748
+static CK_RV
7e9748
+dup_mechanism(CK_MECHANISM_PTR *dup, const CK_MECHANISM_PTR pMechanism)
7e9748
+{
7e9748
+    CK_MECHANISM_PTR p;
7e9748
+
7e9748
+    p = malloc(sizeof(*p));
7e9748
+    if (p == NULL)
7e9748
+	return CKR_DEVICE_MEMORY;
7e9748
+
7e9748
+    if (*dup)
7e9748
+	free(*dup);
7e9748
+    *dup = p;
7e9748
+    memcpy(p, pMechanism, sizeof(*p));
7e9748
+
7e9748
+    return CKR_OK;
7e9748
+}
7e9748
+
7e9748
+
7e9748
+CK_RV
7e9748
+C_EncryptInit(CK_SESSION_HANDLE hSession,
7e9748
+	      CK_MECHANISM_PTR pMechanism,
7e9748
+	      CK_OBJECT_HANDLE hKey)
7e9748
+{
7e9748
+    struct session_state *state;
7e9748
+    CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 };
7e9748
+    CK_BBOOL bool_true = CK_TRUE;
7e9748
+    CK_ATTRIBUTE attr[] = {
7e9748
+	{ CKA_ENCRYPT, &bool_true, sizeof(bool_true) }
7e9748
+    };
7e9748
+    struct st_object *o;
7e9748
+    CK_RV ret;
7e9748
+
7e9748
+    st_logf("EncryptInit\n");
7e9748
+    VERIFY_SESSION_HANDLE(hSession, &state);
7e9748
+
7e9748
+    ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]),
7e9748
+		     mechs, sizeof(mechs)/sizeof(mechs[0]),
7e9748
+		     pMechanism, hKey, &o);
7e9748
+    if (ret)
7e9748
+	return ret;
7e9748
+
7e9748
+    ret = dup_mechanism(&state->encrypt_mechanism, pMechanism);
7e9748
+    if (ret == CKR_OK)
7e9748
+	state->encrypt_object = OBJECT_ID(o);
7e9748
+			
7e9748
+    return ret;
7e9748
+}
7e9748
+
7e9748
+CK_RV
7e9748
+C_Encrypt(CK_SESSION_HANDLE hSession,
7e9748
+	  CK_BYTE_PTR pData,
7e9748
+	  CK_ULONG ulDataLen,
7e9748
+	  CK_BYTE_PTR pEncryptedData,
7e9748
+	  CK_ULONG_PTR pulEncryptedDataLen)
7e9748
+{
7e9748
+    struct session_state *state;
7e9748
+    struct st_object *o;
7e9748
+    void *buffer = NULL;
7e9748
+    CK_RV ret;
7e9748
+    RSA *rsa;
7e9748
+    int padding, len, buffer_len, padding_len;
7e9748
+
7e9748
+    st_logf("Encrypt\n");
7e9748
+
7e9748
+    VERIFY_SESSION_HANDLE(hSession, &state);
7e9748
+
7e9748
+    if (state->encrypt_object == -1)
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+
7e9748
+    o = soft_token.object.objs[state->encrypt_object];
7e9748
+
7e9748
+    if (o->u.public_key == NULL) {
7e9748
+	st_logf("public key NULL\n");
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+    }
7e9748
+
7e9748
+    rsa = EVP_PKEY_get0_RSA(o->u.public_key);
7e9748
+    if (rsa == NULL)
7e9748
+	return CKR_ARGUMENTS_BAD;
7e9748
+
7e9748
+    RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */
7e9748
+
7e9748
+    buffer_len = RSA_size(rsa);
7e9748
+
7e9748
+    buffer = malloc(buffer_len);
7e9748
+    if (buffer == NULL) {
7e9748
+	ret = CKR_DEVICE_MEMORY;
7e9748
+	goto out;
7e9748
+    }
7e9748
+
7e9748
+    ret = CKR_OK;
7e9748
+    switch(state->encrypt_mechanism->mechanism) {
7e9748
+    case CKM_RSA_PKCS:
7e9748
+	padding = RSA_PKCS1_PADDING;
7e9748
+	padding_len = RSA_PKCS1_PADDING_SIZE;
7e9748
+	break;
7e9748
+    case CKM_RSA_X_509:
7e9748
+	padding = RSA_NO_PADDING;
7e9748
+	padding_len = 0;
7e9748
+	break;
7e9748
+    default:
7e9748
+	ret = CKR_FUNCTION_NOT_SUPPORTED;
7e9748
+	goto out;
7e9748
+    }
7e9748
+
7e9748
+    if (buffer_len + padding_len < (long) ulDataLen) {
7e9748
+	ret = CKR_ARGUMENTS_BAD;
7e9748
+	goto out;
7e9748
+    }
7e9748
+
7e9748
+    if (pulEncryptedDataLen == NULL) {
7e9748
+	st_logf("pulEncryptedDataLen NULL\n");
7e9748
+	ret = CKR_ARGUMENTS_BAD;
7e9748
+	goto out;
7e9748
+    }