]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
- markus@cvs.openbsd.org 2010/02/08 10:50:20
authorDamien Miller <djm@mindrot.org>
Thu, 11 Feb 2010 22:21:02 +0000 (09:21 +1100)
committerDamien Miller <djm@mindrot.org>
Thu, 11 Feb 2010 22:21:02 +0000 (09:21 +1100)
     [pathnames.h readconf.c readconf.h scp.1 sftp.1 ssh-add.1 ssh-add.c]
     [ssh-agent.c ssh-keygen.1 ssh-keygen.c ssh.1 ssh.c ssh_config.5]
     replace our obsolete smartcard code with PKCS#11.
        ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-11/v2-20/pkcs-11v2-20.pdf
     ssh(1) and ssh-keygen(1) use dlopen(3) directly to talk to a PKCS#11
     provider (shared library) while ssh-agent(1) delegates PKCS#11 to
     a forked a ssh-pkcs11-helper process.
     PKCS#11 is currently a compile time option.
     feedback and ok djm@; inspired by patches from Alon Bar-Lev
`

20 files changed:
ChangeLog
Makefile.in
configure.ac
pathnames.h
readconf.c
readconf.h
scp.1
sftp.1
ssh-add.1
ssh-add.c
ssh-agent.c
ssh-keygen.1
ssh-keygen.c
ssh-pkcs11-client.c [new file with mode: 0644]
ssh-pkcs11-helper.c [new file with mode: 0644]
ssh-pkcs11.c [new file with mode: 0644]
ssh-pkcs11.h [new file with mode: 0644]
ssh.1
ssh.c
ssh_config.5

index 2c815a3ab83fbf14fa2b3bb31713cfbee6d1b3f9..7761161712bdfb8ef3f1989cb5299ed070e0e60d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,16 @@
      make buffer_get_string_ret() really non-fatal in all cases (it was
      using buffer_get_int(), which could fatal() on buffer empty);
      ok markus dtucker
+   - markus@cvs.openbsd.org 2010/02/08 10:50:20
+     [pathnames.h readconf.c readconf.h scp.1 sftp.1 ssh-add.1 ssh-add.c]
+     [ssh-agent.c ssh-keygen.1 ssh-keygen.c ssh.1 ssh.c ssh_config.5]
+     replace our obsolete smartcard code with PKCS#11.
+        ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-11/v2-20/pkcs-11v2-20.pdf
+     ssh(1) and ssh-keygen(1) use dlopen(3) directly to talk to a PKCS#11
+     provider (shared library) while ssh-agent(1) delegates PKCS#11 to
+     a forked a ssh-pkcs11-helper process.
+     PKCS#11 is currently a compile time option.
+     feedback and ok djm@; inspired by patches from Alon Bar-Lev
 
 20100210
  - (djm) add -lselinux to LIBS before calling AC_CHECK_FUNCS for
index d7f338c0f76f410c1a60cab58383b0d66cf4cf53..0c45bfca684a935a2939a5f9fe6aff658f909f3e 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.303 2010/01/08 08:27:57 dtucker Exp $
+# $Id: Makefile.in,v 1.304 2010/02/11 22:21:02 djm Exp $
 
 # uncomment if you run a non bourne compatable shell. Ie. csh
 #SHELL = @SH@
@@ -25,6 +25,7 @@ SSH_PROGRAM=@bindir@/ssh
 ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass
 SFTP_SERVER=$(libexecdir)/sftp-server
 SSH_KEYSIGN=$(libexecdir)/ssh-keysign
+SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper
 RAND_HELPER=$(libexecdir)/ssh-rand-helper
 PRIVSEP_PATH=@PRIVSEP_PATH@
 SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@
@@ -35,6 +36,7 @@ PATHS= -DSSHDIR=\"$(sysconfdir)\" \
        -D_PATH_SSH_ASKPASS_DEFAULT=\"$(ASKPASS_PROGRAM)\" \
        -D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \
        -D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \
+       -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \
        -D_PATH_SSH_PIDDIR=\"$(piddir)\" \
        -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" \
        -DSSH_RAND_HELPER=\"$(RAND_HELPER)\"
@@ -60,7 +62,7 @@ EXEEXT=@EXEEXT@
 INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@
 INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@
 
-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT)
+TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT)
 
 LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
        canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \
@@ -71,7 +73,8 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
        atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
        monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \
        kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \
-       entropy.o scard-opensc.o gss-genr.o umac.o jpake.o schnorr.o
+       entropy.o scard-opensc.o gss-genr.o umac.o jpake.o schnorr.o \
+       ssh-pkcs11.o
 
 SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
        sshconnect.o sshconnect1.o sshconnect2.o mux.o \
@@ -147,8 +150,8 @@ scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o
 ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o
        $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
 
-ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o
-       $(LD) -o $@ ssh-agent.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
+ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o
+       $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
 
 ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o
        $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
@@ -156,6 +159,9 @@ ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o
 ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o roaming_dummy.o readconf.o
        $(LD) -o $@ ssh-keysign.o readconf.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
 
+ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o
+       $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
+
 ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o roaming_dummy.o
        $(LD) -o $@ ssh-keyscan.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS)
 
@@ -265,6 +271,7 @@ install-files: scard-install
                $(INSTALL) -m 0755 $(STRIP_OPT) ssh-rand-helper $(DESTDIR)$(libexecdir)/ssh-rand-helper ; \
        fi
        $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign $(DESTDIR)$(SSH_KEYSIGN)
+       $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper $(DESTDIR)$(SSH_PKCS11_HELPER)
        $(INSTALL) -m 0755 $(STRIP_OPT) sftp $(DESTDIR)$(bindir)/sftp
        $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server $(DESTDIR)$(SFTP_SERVER)
        $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
@@ -368,6 +375,7 @@ uninstall:
        -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT)
        -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
        -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT)
+       -rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT)
        -rm -f $(DESTDIR)$(RAND_HELPER)$(EXEEXT)
        -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
        -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1
@@ -393,6 +401,7 @@ tests interop-tests:        $(TARGETS)
        TEST_SSH_SSHAGENT="$${BUILDDIR}/ssh-agent"; \
        TEST_SSH_SSHADD="$${BUILDDIR}/ssh-add"; \
        TEST_SSH_SSHKEYGEN="$${BUILDDIR}/ssh-keygen"; \
+       TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \
        TEST_SSH_SSHKEYSCAN="$${BUILDDIR}/ssh-keyscan"; \
        TEST_SSH_SFTP="$${BUILDDIR}/sftp"; \
        TEST_SSH_SFTPSERVER="$${BUILDDIR}/sftp-server"; \
@@ -413,6 +422,7 @@ tests interop-tests:        $(TARGETS)
                TEST_SSH_SSHAGENT="$${TEST_SSH_SSHAGENT}" \
                TEST_SSH_SSHADD="$${TEST_SSH_SSHADD}" \
                TEST_SSH_SSHKEYGEN="$${TEST_SSH_SSHKEYGEN}" \
+               TEST_SSH_SSHPKCS11HELPER="$${TEST_SSH_SSHPKCS11HELPER}" \
                TEST_SSH_SSHKEYSCAN="$${TEST_SSH_SSHKEYSCAN}" \
                TEST_SSH_SFTP="$${TEST_SSH_SFTP}" \
                TEST_SSH_SFTPSERVER="$${TEST_SSH_SFTPSERVER}" \
index 5fc1d4a46ec967ea4a913c731591c01075e255ac..717d315fd2bf811a53f6111c27443ba1e9548c53 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: configure.ac,v 1.440 2010/02/09 23:19:29 djm Exp $
+# $Id: configure.ac,v 1.441 2010/02/11 22:21:02 djm Exp $
 #
 # Copyright (c) 1999-2004 Damien Miller
 #
@@ -15,7 +15,7 @@
 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 AC_INIT(OpenSSH, Portable, openssh-unix-dev@mindrot.org)
-AC_REVISION($Revision: 1.440 $)
+AC_REVISION($Revision: 1.441 $)
 AC_CONFIG_SRCDIR([ssh.c])
 
 AC_CONFIG_HEADER(config.h)
@@ -4197,6 +4197,10 @@ else
        AC_SUBST(TEST_SSH_IPV6, yes)
 fi
 
+if test "x$enable_pkcs11" != "xno" ; then
+       AC_DEFINE([ENABLE_PKCS11], [], [Enable for PKCS#11 support])
+fi
+
 AC_EXEEXT
 AC_CONFIG_FILES([Makefile buildpkg.sh opensshd.init openssh.xml \
        openbsd-compat/Makefile openbsd-compat/regress/Makefile \
index 80c5d9cbb7097a05fb6c3d069f078632c592dacf..32b9e065b8235e9ca01ce15bfb941d1e16ba96b2 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: pathnames.h,v 1.17 2008/12/29 02:23:26 stevesk Exp $ */
+/* $OpenBSD: pathnames.h,v 1.18 2010/02/08 10:50:20 markus Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
 #define _PATH_SSH_KEY_SIGN             "/usr/libexec/ssh-keysign"
 #endif
 
+/* Location of ssh-keysign for hostbased authentication */
+#ifndef _PATH_SSH_PKCS11_HELPER
+#define _PATH_SSH_PKCS11_HELPER                "/usr/libexec/ssh-pkcs11-helper"
+#endif
+
 /* xauth for X11 forwarding */
 #ifndef _PATH_XAUTH
 #define _PATH_XAUTH                    "/usr/X11R6/bin/xauth"
index d424c16974477eeca622230dbe6edcf05feea5eb..8bdc8caf1abd30fac22ad442060b17f15fbe4dfe 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.182 2010/01/09 23:04:13 dtucker Exp $ */
+/* $OpenBSD: readconf.c,v 1.183 2010/02/08 10:50:20 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -123,7 +123,7 @@ typedef enum {
        oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
        oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
        oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
-       oHostKeyAlgorithms, oBindAddress, oSmartcardDevice,
+       oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
        oClearAllForwardings, oNoHostAuthenticationForLocalhost,
        oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
        oAddressFamily, oGssAuthentication, oGssDelegateCreds,
@@ -205,10 +205,12 @@ static struct {
        { "preferredauthentications", oPreferredAuthentications },
        { "hostkeyalgorithms", oHostKeyAlgorithms },
        { "bindaddress", oBindAddress },
-#ifdef SMARTCARD
-       { "smartcarddevice", oSmartcardDevice },
+#ifdef ENABLE_PKCS11
+       { "smartcarddevice", oPKCS11Provider },
+       { "pkcs11provider", oPKCS11Provider },
 #else
        { "smartcarddevice", oUnsupported },
+       { "pkcs11provider", oUnsupported },
 #endif
        { "clearallforwardings", oClearAllForwardings },
        { "enablesshkeysign", oEnableSSHKeysign },
@@ -609,8 +611,8 @@ parse_string:
                charptr = &options->bind_address;
                goto parse_string;
 
-       case oSmartcardDevice:
-               charptr = &options->smartcard_device;
+       case oPKCS11Provider:
+               charptr = &options->pkcs11_provider;
                goto parse_string;
 
        case oProxyCommand:
@@ -1051,7 +1053,7 @@ initialize_options(Options * options)
        options->log_level = SYSLOG_LEVEL_NOT_SET;
        options->preferred_authentications = NULL;
        options->bind_address = NULL;
-       options->smartcard_device = NULL;
+       options->pkcs11_provider = NULL;
        options->enable_ssh_keysign = - 1;
        options->no_host_authentication_for_localhost = - 1;
        options->identities_only = - 1;
index f7c0b9c6d13f93f8e17e4adb6a0b9e422ecbd6f8..4264751c516c6189aba8f40874d89eba43664b17 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.81 2010/01/09 23:04:13 dtucker Exp $ */
+/* $OpenBSD: readconf.h,v 1.82 2010/02/08 10:50:20 markus Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -84,7 +84,7 @@ typedef struct {
        char   *user_hostfile2;
        char   *preferred_authentications;
        char   *bind_address;   /* local socket address for connection to sshd */
-       char   *smartcard_device; /* Smartcard reader device */
+       char   *pkcs11_provider; /* PKCS#11 provider */
        int     verify_host_key_dns;    /* Verify host key using DNS */
 
        int     num_identity_files;     /* Number of files for RSA/DSA identities. */
diff --git a/scp.1 b/scp.1
index 74ee5db13153842b147aa0fa6a30d750a12b6e1d..bc5e259f54fef9d72d44becf8c50ad62f2e96b4a 100644 (file)
--- a/scp.1
+++ b/scp.1
@@ -9,9 +9,9 @@
 .\"
 .\" Created: Sun May  7 00:14:37 1995 ylo
 .\"
-.\" $OpenBSD: scp.1,v 1.49 2010/01/09 23:04:13 dtucker Exp $
+.\" $OpenBSD: scp.1,v 1.50 2010/02/08 10:50:20 markus Exp $
 .\"
-.Dd $Mdocdate: January 9 2010 $
+.Dd $Mdocdate: February 8 2010 $
 .Dt SCP 1
 .Os
 .Sh NAME
@@ -153,6 +153,7 @@ For full details of the options listed below, and their possible values, see
 .It NoHostAuthenticationForLocalhost
 .It NumberOfPasswordPrompts
 .It PasswordAuthentication
+.It PKCS11Provider
 .It Port
 .It PreferredAuthentications
 .It Protocol
@@ -164,7 +165,6 @@ For full details of the options listed below, and their possible values, see
 .It SendEnv
 .It ServerAliveInterval
 .It ServerAliveCountMax
-.It SmartcardDevice
 .It StrictHostKeyChecking
 .It TCPKeepAlive
 .It UsePrivilegedPort
diff --git a/sftp.1 b/sftp.1
index 175dc6520f26d0d44a4ea1b75f7073315f6b5f1b..777b02a5842463b4eabb92e065861202ca3f5a3b 100644 (file)
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: sftp.1,v 1.82 2010/01/13 12:48:34 jmc Exp $
+.\" $OpenBSD: sftp.1,v 1.83 2010/02/08 10:50:20 markus Exp $
 .\"
 .\" Copyright (c) 2001 Damien Miller.  All rights reserved.
 .\"
@@ -22,7 +22,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: January 13 2010 $
+.Dd $Mdocdate: February 8 2010 $
 .Dt SFTP 1
 .Os
 .Sh NAME
@@ -202,6 +202,7 @@ For full details of the options listed below, and their possible values, see
 .It NoHostAuthenticationForLocalhost
 .It NumberOfPasswordPrompts
 .It PasswordAuthentication
+.It PKCS11Provider
 .It Port
 .It PreferredAuthentications
 .It Protocol
@@ -213,7 +214,6 @@ For full details of the options listed below, and their possible values, see
 .It SendEnv
 .It ServerAliveInterval
 .It ServerAliveCountMax
-.It SmartcardDevice
 .It StrictHostKeyChecking
 .It TCPKeepAlive
 .It UsePrivilegedPort
index ee9a00ff0801d6e6862c9a60756e2f83e0af999a..a5dc3311a18222d62df1aa5de720321a06676b9b 100644 (file)
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ssh-add.1,v 1.48 2009/10/22 15:02:12 sobrado Exp $
+.\"    $OpenBSD: ssh-add.1,v 1.49 2010/02/08 10:50:20 markus Exp $
 .\"
 .\"  -*- nroff -*-
 .\"
@@ -37,7 +37,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: October 22 2009 $
+.Dd $Mdocdate: February 8 2010 $
 .Dt SSH-ADD 1
 .Os
 .Sh NAME
@@ -101,17 +101,17 @@ If no public key is found at a given path,
 will append
 .Pa .pub
 and retry.
-.It Fl e Ar reader
-Remove key in smartcard
-.Ar reader .
+.It Fl e Ar pkcs11
+Remove key provided by
+.Ar pkcs11 .
 .It Fl L
 Lists public key parameters of all identities currently represented
 by the agent.
 .It Fl l
 Lists fingerprints of all identities currently represented by the agent.
-.It Fl s Ar reader
-Add key in smartcard
-.Ar reader .
+.It Fl s Ar pkcs11
+Add key provider by
+.Ar pkcs11 .
 .It Fl t Ar life
 Set a maximum lifetime when adding identities to an agent.
 The lifetime may be specified in seconds or in a time format
index 084478d78c6d98875225379c1c513dbc447dd17c..90e5be20b1c18986bccba738c57e33d57d7e6f74 100644 (file)
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-add.c,v 1.91 2009/08/27 17:44:52 djm Exp $ */
+/* $OpenBSD: ssh-add.c,v 1.92 2010/02/08 10:50:20 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -211,7 +211,7 @@ update_card(AuthenticationConnection *ac, int add, const char *id)
        char *pin;
        int ret = -1;
 
-       pin = read_passphrase("Enter passphrase for smartcard: ", RP_ALLOW_STDIN);
+       pin = read_passphrase("Enter passphrase for PKCS#11: ", RP_ALLOW_STDIN);
        if (pin == NULL)
                return -1;
 
@@ -317,10 +317,8 @@ usage(void)
        fprintf(stderr, "  -X          Unlock agent.\n");
        fprintf(stderr, "  -t life     Set lifetime (in seconds) when adding identities.\n");
        fprintf(stderr, "  -c          Require confirmation to sign using identities\n");
-#ifdef SMARTCARD
-       fprintf(stderr, "  -s reader   Add key in smartcard reader.\n");
-       fprintf(stderr, "  -e reader   Remove key in smartcard reader.\n");
-#endif
+       fprintf(stderr, "  -s pkcs11   Add keys from PKCS#11 provider.\n");
+       fprintf(stderr, "  -e pkcs11   Remove keys provided by PKCS#11 provider.\n");
 }
 
 int
@@ -329,7 +327,7 @@ main(int argc, char **argv)
        extern char *optarg;
        extern int optind;
        AuthenticationConnection *ac = NULL;
-       char *sc_reader_id = NULL;
+       char *pkcs11provider = NULL;
        int i, ch, deleting = 0, ret = 0;
 
        /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
@@ -371,11 +369,11 @@ main(int argc, char **argv)
                                ret = 1;
                        goto done;
                case 's':
-                       sc_reader_id = optarg;
+                       pkcs11provider = optarg;
                        break;
                case 'e':
                        deleting = 1;
-                       sc_reader_id = optarg;
+                       pkcs11provider = optarg;
                        break;
                case 't':
                        if ((lifetime = convtime(optarg)) == -1) {
@@ -392,8 +390,8 @@ main(int argc, char **argv)
        }
        argc -= optind;
        argv += optind;
-       if (sc_reader_id != NULL) {
-               if (update_card(ac, !deleting, sc_reader_id) == -1)
+       if (pkcs11provider != NULL) {
+               if (update_card(ac, !deleting, pkcs11provider) == -1)
                        ret = 1;
                goto done;
        }
index df3a87d9a53021252a4856f0834ab05438805e04..f745c2513575ea463019627f2c3372d7adbb38c2 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.162 2009/09/01 14:43:17 djm Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.163 2010/02/08 10:50:20 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -76,8 +76,8 @@
 #include "log.h"
 #include "misc.h"
 
-#ifdef SMARTCARD
-#include "scard.h"
+#ifdef ENABLE_PKCS11
+#include "ssh-pkcs11.h"
 #endif
 
 #if defined(HAVE_SYS_PRCTL_H)
@@ -105,6 +105,7 @@ typedef struct identity {
        TAILQ_ENTRY(identity) next;
        Key *key;
        char *comment;
+       char *provider;
        u_int death;
        u_int confirm;
 } Identity;
@@ -171,6 +172,7 @@ static void
 free_identity(Identity *id)
 {
        key_free(id->key);
+       xfree(id->provider);
        xfree(id->comment);
        xfree(id);
 }
@@ -549,7 +551,7 @@ process_add_identity(SocketEntry *e, int version)
        if (lifetime && !death)
                death = time(NULL) + lifetime;
        if ((id = lookup_identity(k, version)) == NULL) {
-               id = xmalloc(sizeof(Identity));
+               id = xcalloc(1, sizeof(Identity));
                id->key = k;
                TAILQ_INSERT_TAIL(&tab->idlist, id, next);
                /* Increment the number of identities. */
@@ -609,17 +611,17 @@ no_identities(SocketEntry *e, u_int type)
        buffer_free(&msg);
 }
 
-#ifdef SMARTCARD
+#ifdef ENABLE_PKCS11
 static void
 process_add_smartcard_key(SocketEntry *e)
 {
-       char *sc_reader_id = NULL, *pin;
-       int i, type, version, success = 0, death = 0, confirm = 0;
-       Key **keys, *k;
+       char *provider = NULL, *pin;
+       int i, type, version, count = 0, success = 0, death = 0, confirm = 0;
+       Key **keys = NULL, *k;
        Identity *id;
        Idtab *tab;
 
-       sc_reader_id = buffer_get_string(&e->request, NULL);
+       provider = buffer_get_string(&e->request, NULL);
        pin = buffer_get_string(&e->request, NULL);
 
        while (buffer_len(&e->request)) {
@@ -633,30 +635,22 @@ process_add_smartcard_key(SocketEntry *e)
                default:
                        error("process_add_smartcard_key: "
                            "Unknown constraint type %d", type);
-                       xfree(sc_reader_id);
-                       xfree(pin);
                        goto send;
                }
        }
        if (lifetime && !death)
                death = time(NULL) + lifetime;
 
-       keys = sc_get_keys(sc_reader_id, pin);
-       xfree(sc_reader_id);
-       xfree(pin);
-
-       if (keys == NULL || keys[0] == NULL) {
-               error("sc_get_keys failed");
-               goto send;
-       }
-       for (i = 0; keys[i] != NULL; i++) {
+       count = pkcs11_add_provider(provider, pin, &keys);
+       for (i = 0; i < count; i++) {
                k = keys[i];
                version = k->type == KEY_RSA1 ? 1 : 2;
                tab = idtab_lookup(version);
                if (lookup_identity(k, version) == NULL) {
-                       id = xmalloc(sizeof(Identity));
+                       id = xcalloc(1, sizeof(Identity));
                        id->key = k;
-                       id->comment = sc_get_key_label(k);
+                       id->provider = xstrdup(provider);
+                       id->comment = xstrdup(provider); /* XXX */
                        id->death = death;
                        id->confirm = confirm;
                        TAILQ_INSERT_TAIL(&tab->idlist, id, next);
@@ -667,8 +661,13 @@ process_add_smartcard_key(SocketEntry *e)
                }
                keys[i] = NULL;
        }
-       xfree(keys);
 send:
+       if (pin)
+               xfree(pin);
+       if (provider)
+               xfree(provider);
+       if (keys)
+               xfree(keys);
        buffer_put_int(&e->output, 1);
        buffer_put_char(&e->output,
            success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
@@ -677,42 +676,37 @@ send:
 static void
 process_remove_smartcard_key(SocketEntry *e)
 {
-       char *sc_reader_id = NULL, *pin;
-       int i, version, success = 0;
-       Key **keys, *k = NULL;
-       Identity *id;
+       char *provider = NULL, *pin = NULL;
+       int version, success = 0;
+       Identity *id, *nxt;
        Idtab *tab;
 
-       sc_reader_id = buffer_get_string(&e->request, NULL);
+       provider = buffer_get_string(&e->request, NULL);
        pin = buffer_get_string(&e->request, NULL);
-       keys = sc_get_keys(sc_reader_id, pin);
-       xfree(sc_reader_id);
        xfree(pin);
 
-       if (keys == NULL || keys[0] == NULL) {
-               error("sc_get_keys failed");
-               goto send;
-       }
-       for (i = 0; keys[i] != NULL; i++) {
-               k = keys[i];
-               version = k->type == KEY_RSA1 ? 1 : 2;
-               if ((id = lookup_identity(k, version)) != NULL) {
-                       tab = idtab_lookup(version);
-                       TAILQ_REMOVE(&tab->idlist, id, next);
-                       tab->nentries--;
-                       free_identity(id);
-                       success = 1;
+       for (version = 1; version < 3; version++) {
+               tab = idtab_lookup(version);
+               for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) {
+                       nxt = TAILQ_NEXT(id, next);
+                       if (!strcmp(provider, id->provider)) {
+                               TAILQ_REMOVE(&tab->idlist, id, next);
+                               free_identity(id);
+                               tab->nentries--;
+                       }
                }
-               key_free(k);
-               keys[i] = NULL;
        }
-       xfree(keys);
-send:
+       if (pkcs11_del_provider(provider) == 0)
+               success = 1;
+       else
+               error("process_remove_smartcard_key:"
+                   " pkcs11_del_provider failed");
+       xfree(provider);
        buffer_put_int(&e->output, 1);
        buffer_put_char(&e->output,
            success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
 }
-#endif /* SMARTCARD */
+#endif /* ENABLE_PKCS11 */
 
 /* dispatch incoming messages */
 
@@ -797,7 +791,7 @@ process_message(SocketEntry *e)
        case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
                process_remove_all_identities(e, 2);
                break;
-#ifdef SMARTCARD
+#ifdef ENABLE_PKCS11
        case SSH_AGENTC_ADD_SMARTCARD_KEY:
        case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED:
                process_add_smartcard_key(e);
@@ -805,7 +799,7 @@ process_message(SocketEntry *e)
        case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
                process_remove_smartcard_key(e);
                break;
-#endif /* SMARTCARD */
+#endif /* ENABLE_PKCS11 */
        default:
                /* Unknown message.  Respond with failure. */
                error("Unknown message %d", type);
@@ -1009,6 +1003,9 @@ static void
 cleanup_handler(int sig)
 {
        cleanup_socket();
+#ifdef ENABLE_PKCS11
+       pkcs11_terminate();
+#endif
        _exit(2);
 }
 
@@ -1255,6 +1252,10 @@ main(int ac, char **av)
 #endif
 
 skip:
+
+#ifdef ENABLE_PKCS11
+       pkcs11_init(0);
+#endif
        new_socket(AUTH_SOCKET, sock);
        if (ac > 0)
                parent_alive_interval = 10;
index 9e59c16f7bda91258f7daefbff5b487ecf38a975..7dc76976a237ef7c5a62d7abab723ef282b22aac 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ssh-keygen.1,v 1.80 2009/10/24 00:48:34 dtucker Exp $
+.\"    $OpenBSD: ssh-keygen.1,v 1.81 2010/02/08 10:50:20 markus Exp $
 .\"
 .\"  -*- nroff -*-
 .\"
@@ -37,7 +37,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: October 24 2009 $
+.Dd $Mdocdate: February 8 2010 $
 .Dt SSH-KEYGEN 1
 .Os
 .Sh NAME
@@ -201,9 +201,10 @@ Requests changing the comment in the private and public key files.
 This operation is only supported for RSA1 keys.
 The program will prompt for the file containing the private keys, for
 the passphrase if the key has one, and for the new comment.
-.It Fl D Ar reader
-Download the RSA public key stored in the smartcard in
-.Ar reader .
+.It Fl D Ar pkcs11
+Download the RSA public keys stored in the
+.Ar pkcs11
+provider.
 .It Fl e
 This option will read a private or public OpenSSH key file and
 print the key in
@@ -313,9 +314,6 @@ for protocol version 1 and
 or
 .Dq dsa
 for protocol version 2.
-.It Fl U Ar reader
-Upload an existing RSA private key into the smartcard in
-.Ar reader .
 .It Fl v
 Verbose mode.
 Causes
index 7f5185f8e4b70a04bf37f2168ea0ec630f832765..005f9c7ab6282bdc40d0d5689e401113974bd377 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.176 2010/01/11 10:51:07 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.177 2010/02/08 10:50:20 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -49,8 +49,8 @@
 #include "hostfile.h"
 #include "dns.h"
 
-#ifdef SMARTCARD
-#include "scard.h"
+#ifdef ENABLE_PKCS11
+#include "ssh-pkcs11.h"
 #endif
 
 /* Number of bits in the RSA/DSA key.  This value can be set on the command line. */
@@ -459,51 +459,29 @@ do_print_public(struct passwd *pw)
        exit(0);
 }
 
-#ifdef SMARTCARD
 static void
-do_upload(struct passwd *pw, const char *sc_reader_id)
-{
-       Key *prv = NULL;
-       struct stat st;
-       int ret;
-
-       if (!have_identity)
-               ask_filename(pw, "Enter file in which the key is");
-       if (stat(identity_file, &st) < 0) {
-               perror(identity_file);
-               exit(1);
-       }
-       prv = load_identity(identity_file);
-       if (prv == NULL) {
-               error("load failed");
-               exit(1);
-       }
-       ret = sc_put_key(prv, sc_reader_id);
-       key_free(prv);
-       if (ret < 0)
-               exit(1);
-       logit("loading key done");
-       exit(0);
-}
-
-static void
-do_download(struct passwd *pw, const char *sc_reader_id)
+do_download(struct passwd *pw, const char *pkcs11provider)
 {
+#ifdef ENABLE_PKCS11
        Key **keys = NULL;
-       int i;
+       int i, nkeys;
 
-       keys = sc_get_keys(sc_reader_id, NULL);
-       if (keys == NULL)
-               fatal("cannot read public key from smartcard");
-       for (i = 0; keys[i]; i++) {
+       pkcs11_init(0);
+       nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys);
+       if (nkeys <= 0)
+               fatal("cannot read public key from pkcs11");
+       for (i = 0; i < nkeys; i++) {
                key_write(keys[i], stdout);
                key_free(keys[i]);
                fprintf(stdout, "\n");
        }
        xfree(keys);
+       pkcs11_terminate();
        exit(0);
+#else
+       fatal("no pkcs11 support");
+#endif /* ENABLE_PKCS11 */
 }
-#endif /* SMARTCARD */
 
 static void
 do_fingerprint(struct passwd *pw)
@@ -1044,9 +1022,9 @@ usage(void)
        fprintf(stderr, "  -b bits     Number of bits in the key to create.\n");
        fprintf(stderr, "  -C comment  Provide new comment.\n");
        fprintf(stderr, "  -c          Change comment in private and public key files.\n");
-#ifdef SMARTCARD
-       fprintf(stderr, "  -D reader   Download public key from smartcard.\n");
-#endif /* SMARTCARD */
+#ifdef ENABLE_PKCS11
+       fprintf(stderr, "  -D pkcs11   Download public key from pkcs11 token.\n");
+#endif
        fprintf(stderr, "  -e          Convert OpenSSH to RFC 4716 key file.\n");
        fprintf(stderr, "  -F hostname Find hostname in known hosts file.\n");
        fprintf(stderr, "  -f filename Filename of the key file.\n");
@@ -1065,9 +1043,6 @@ usage(void)
        fprintf(stderr, "  -S start    Start point (hex) for generating DH-GEX moduli.\n");
        fprintf(stderr, "  -T file     Screen candidates for DH-GEX moduli.\n");
        fprintf(stderr, "  -t type     Specify type of key to create.\n");
-#ifdef SMARTCARD
-       fprintf(stderr, "  -U reader   Upload private key to smartcard.\n");
-#endif /* SMARTCARD */
        fprintf(stderr, "  -v          Verbose.\n");
        fprintf(stderr, "  -W gen      Generator to use for generating DH-GEX moduli.\n");
        fprintf(stderr, "  -y          Read private key file and print public key.\n");
@@ -1082,12 +1057,12 @@ int
 main(int argc, char **argv)
 {
        char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
-       char out_file[MAXPATHLEN], *reader_id = NULL;
+       char out_file[MAXPATHLEN], *pkcs11provider = NULL;
        char *rr_hostname = NULL;
        Key *private, *public;
        struct passwd *pw;
        struct stat st;
-       int opt, type, fd, download = 0;
+       int opt, type, fd;
        u_int32_t memory = 0, generator_wanted = 0, trials = 100;
        int do_gen_candidates = 0, do_screen_candidates = 0;
        BIGNUM *start = NULL;
@@ -1120,7 +1095,7 @@ main(int argc, char **argv)
        }
 
        while ((opt = getopt(argc, argv,
-           "degiqpclBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) {
+           "degiqpclBHvxXyF:b:f:t:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) {
                switch (opt) {
                case 'b':
                        bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr);
@@ -1192,10 +1167,7 @@ main(int argc, char **argv)
                        key_type_name = optarg;
                        break;
                case 'D':
-                       download = 1;
-                       /*FALLTHROUGH*/
-               case 'U':
-                       reader_id = optarg;
+                       pkcs11provider = optarg;
                        break;
                case 'v':
                        if (log_level == SYSLOG_LEVEL_INFO)
@@ -1303,16 +1275,8 @@ main(int argc, char **argv)
                        exit(0);
                }
        }
-       if (reader_id != NULL) {
-#ifdef SMARTCARD
-               if (download)
-                       do_download(pw, reader_id);
-               else
-                       do_upload(pw, reader_id);
-#else /* SMARTCARD */
-               fatal("no support for smartcards.");
-#endif /* SMARTCARD */
-       }
+       if (pkcs11provider != NULL)
+               do_download(pw, pkcs11provider);
 
        if (do_gen_candidates) {
                FILE *out = fopen(out_file, "w");
diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c
new file mode 100644 (file)
index 0000000..6ffdd93
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2010 Markus Friedl.  All rights reserved.
+ *
+ * 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 <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "pathnames.h"
+#include "xmalloc.h"
+#include "buffer.h"
+#include "log.h"
+#include "misc.h"
+#include "key.h"
+#include "authfd.h"
+#include "atomicio.h"
+#include "ssh-pkcs11.h"
+
+/* borrows code from sftp-server and ssh-agent */
+
+int fd = -1;
+pid_t pid = -1;
+
+static void
+send_msg(Buffer *m)
+{
+       u_char buf[4];
+       int mlen = buffer_len(m);
+
+       put_u32(buf, mlen);
+       if (atomicio(vwrite, fd, buf, 4) != 4 ||
+           atomicio(vwrite, fd, buffer_ptr(m),
+           buffer_len(m)) != buffer_len(m))
+               error("write to helper failed");
+       buffer_consume(m, mlen);
+}
+
+static int
+recv_msg(Buffer *m)
+{
+       u_int l, len;
+       u_char buf[1024];
+
+       if ((len = atomicio(read, fd, buf, 4)) != 4) {
+               error("read from helper failed: %u", len);
+               return (0); /* XXX */
+       }
+       len = get_u32(buf);
+       if (len > 256 * 1024)
+               fatal("response too long: %u", len);
+       /* read len bytes into m */
+       buffer_clear(m);
+       while (len > 0) {
+               l = len;
+               if (l > sizeof(buf))
+                       l = sizeof(buf);
+               if (atomicio(read, fd, buf, l) != l) {
+                       error("response from helper failed.");
+                       return (0); /* XXX */
+               }
+               buffer_append(m, buf, l);
+               len -= l;
+       }
+       return (buffer_get_char(m));
+}
+
+int
+pkcs11_init(int interactive)
+{
+       return (0);
+}
+
+void
+pkcs11_terminate(void)
+{
+       close(fd);
+}
+
+static int
+pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
+    int padding)
+{
+       Key key;
+       u_char *blob, *signature = NULL;
+       u_int blen, slen = 0;
+       int ret = -1;
+       Buffer msg;
+
+       if (padding != RSA_PKCS1_PADDING)
+               return (-1);
+       key.type = KEY_RSA;
+       key.rsa = rsa;
+       if (key_to_blob(&key, &blob, &blen) == 0)
+               return -1;
+       buffer_init(&msg);
+       buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
+       buffer_put_string(&msg, blob, blen);
+       buffer_put_string(&msg, from, flen);
+       buffer_put_int(&msg, 0);
+       xfree(blob);
+       send_msg(&msg);
+
+       if (recv_msg(&msg) == SSH2_AGENT_SIGN_RESPONSE) {
+               signature = buffer_get_string(&msg, &slen);
+               if (slen <= (u_int)RSA_size(rsa)) {
+                       memcpy(to, signature, slen);
+                       ret = slen;
+               }
+               xfree(signature);
+       }
+       return (ret);
+}
+
+/* redirect the private key encrypt operation to the ssh-pkcs11-helper */
+static int
+wrap_key(RSA *rsa)
+{
+       static RSA_METHOD helper_rsa;
+
+       memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa));
+       helper_rsa.name = "ssh-pkcs11-helper";
+       helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt;
+       RSA_set_method(rsa, &helper_rsa);
+       return (0);
+}
+
+static int
+pkcs11_start_helper(void)
+{
+       int pair[2];
+
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
+               error("socketpair: %s", strerror(errno));
+               return (-1);
+       }
+       if ((pid = fork()) == -1) {
+               error("fork: %s", strerror(errno));
+               return (-1);
+       } else if (pid == 0) {
+               if ((dup2(pair[1], STDIN_FILENO) == -1) ||
+                   (dup2(pair[1], STDOUT_FILENO) == -1)) {
+                       fprintf(stderr, "dup2: %s\n", strerror(errno));
+                       _exit(1);
+               }
+               close(pair[0]);
+               close(pair[1]);
+               execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER,
+                   (char *) 0);
+               fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER,
+                   strerror(errno));
+               _exit(1);
+       }
+       close(pair[1]);
+       fd = pair[0];
+       return (0);
+}
+
+int
+pkcs11_add_provider(char *name, char *pin, Key ***keysp)
+{
+       Key *k;
+       int i, nkeys;
+       u_char *blob;
+       u_int blen;
+       Buffer msg;
+
+       if (fd < 0 && pkcs11_start_helper() < 0)
+               return (-1);
+
+       buffer_init(&msg);
+       buffer_put_char(&msg, SSH_AGENTC_ADD_SMARTCARD_KEY);
+       buffer_put_cstring(&msg, name);
+       buffer_put_cstring(&msg, pin);
+       send_msg(&msg);
+       buffer_clear(&msg);
+
+       if (recv_msg(&msg) == SSH2_AGENT_IDENTITIES_ANSWER) {
+               nkeys = buffer_get_int(&msg);
+               *keysp = xcalloc(nkeys, sizeof(Key *));
+               for (i = 0; i < nkeys; i++) {
+                       blob = buffer_get_string(&msg, &blen);
+                       xfree(buffer_get_string(&msg, NULL));
+                       k = key_from_blob(blob, blen);
+                       wrap_key(k->rsa);
+                       (*keysp)[i] = k;
+                       xfree(blob);
+               }
+       } else {
+               nkeys = -1;
+       }
+       buffer_free(&msg);
+       return (nkeys);
+}
+
+int
+pkcs11_del_provider(char *name)
+{
+       int ret = -1;
+       Buffer msg;
+
+       buffer_init(&msg);
+       buffer_put_char(&msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY);
+       buffer_put_cstring(&msg, name);
+       buffer_put_cstring(&msg, "");
+       send_msg(&msg);
+       buffer_clear(&msg);
+
+       if (recv_msg(&msg) == SSH_AGENT_SUCCESS)
+               ret = 0;
+       buffer_free(&msg);
+       return (ret);
+}
diff --git a/ssh-pkcs11-helper.c b/ssh-pkcs11-helper.c
new file mode 100644 (file)
index 0000000..f996270
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2010 Markus Friedl.  All rights reserved.
+ *
+ * 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 <sys/queue.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "log.h"
+#include "misc.h"
+#include "key.h"
+#include "authfd.h"
+#include "ssh-pkcs11.h"
+
+/* borrows code from sftp-server and ssh-agent */
+
+struct pkcs11_keyinfo {
+       Key             *key;
+       char            *providername;
+       TAILQ_ENTRY(pkcs11_keyinfo) next;
+};
+
+TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist;
+
+#define MAX_MSG_LENGTH         10240 /*XXX*/
+
+/* helper */
+#define get_int()                      buffer_get_int(&iqueue);
+#define get_string(lenp)               buffer_get_string(&iqueue, lenp);
+
+/* input and output queue */
+Buffer iqueue;
+Buffer oqueue;
+
+static void
+add_key(Key *k, char *name)
+{
+       struct pkcs11_keyinfo *ki;
+
+       ki = xcalloc(1, sizeof(*ki));
+       ki->providername = xstrdup(name);
+       ki->key = k;
+       TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next);
+}
+
+static void
+del_keys_by_name(char *name)
+{
+       struct pkcs11_keyinfo *ki, *nxt;
+
+       for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) {
+               nxt = TAILQ_NEXT(ki, next);
+               if (!strcmp(ki->providername, name)) {
+                       TAILQ_REMOVE(&pkcs11_keylist, ki, next);
+                       xfree(ki->providername);
+                       key_free(ki->key);
+                       free(ki);
+               }
+       }
+}
+
+/* lookup matching 'private' key */
+static Key *
+lookup_key(Key *k)
+{
+       struct pkcs11_keyinfo *ki;
+
+       TAILQ_FOREACH(ki, &pkcs11_keylist, next) {
+               debug("check %p %s", ki, ki->providername);
+               if (key_equal(k, ki->key))
+                       return (ki->key);
+       }
+       return (NULL);
+}
+
+static void
+send_msg(Buffer *m)
+{
+       int mlen = buffer_len(m);
+
+       buffer_put_int(&oqueue, mlen);
+       buffer_append(&oqueue, buffer_ptr(m), mlen);
+       buffer_consume(m, mlen);
+}
+
+static void
+process_add(void)
+{
+       char *name, *pin;
+       Key **keys;
+       int i, nkeys;
+       u_char *blob;
+       u_int blen;
+       Buffer msg;
+
+       buffer_init(&msg);
+       name = get_string(NULL);
+       pin = get_string(NULL);
+       if ((nkeys = pkcs11_add_provider(name, pin, &keys)) > 0) {
+               buffer_put_char(&msg, SSH2_AGENT_IDENTITIES_ANSWER);
+               buffer_put_int(&msg, nkeys);
+               for (i = 0; i < nkeys; i++) {
+                       key_to_blob(keys[i], &blob, &blen);
+                       buffer_put_string(&msg, blob, blen);
+                       buffer_put_cstring(&msg, name);
+                       xfree(blob);
+                       add_key(keys[i], name);
+               }
+               xfree(keys);
+       } else {
+               buffer_put_char(&msg, SSH_AGENT_FAILURE);
+       }
+       xfree(pin);
+       xfree(name);
+       send_msg(&msg);
+       buffer_free(&msg);
+}
+
+static void
+process_del(void)
+{
+       char *name, *pin;
+       Buffer msg;
+
+       buffer_init(&msg);
+       name = get_string(NULL);
+       pin = get_string(NULL);
+       del_keys_by_name(name);
+       if (pkcs11_del_provider(name) == 0)
+                buffer_put_char(&msg, SSH_AGENT_SUCCESS);
+       else
+                buffer_put_char(&msg, SSH_AGENT_FAILURE);
+       xfree(pin);
+       xfree(name);
+       send_msg(&msg);
+       buffer_free(&msg);
+}
+
+static void
+process_sign(void)
+{
+       u_char *blob, *data, *signature = NULL;
+       u_int blen, dlen, slen = 0;
+       int ok = -1, flags, ret;
+       Key *key, *found;
+       Buffer msg;
+
+       blob = get_string(&blen);
+       data = get_string(&dlen);
+       flags = get_int(); /* XXX ignore */
+
+       if ((key = key_from_blob(blob, blen)) != NULL) {
+               if ((found = lookup_key(key)) != NULL) {
+                       slen = RSA_size(key->rsa);
+                       signature = xmalloc(slen);
+                       if ((ret = RSA_private_encrypt(dlen, data, signature,
+                           found->rsa, RSA_PKCS1_PADDING)) != -1) {
+                               slen = ret;
+                               ok = 0;
+                       }
+               }
+               key_free(key);
+       }
+       buffer_init(&msg);
+       if (ok == 0) {
+               buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE);
+               buffer_put_string(&msg, signature, slen);
+       } else {
+               buffer_put_char(&msg, SSH_AGENT_FAILURE);
+       }
+       xfree(data);
+       xfree(blob);
+       if (signature != NULL)
+               xfree(signature);
+       send_msg(&msg);
+       buffer_free(&msg);
+}
+
+static void
+process(void)
+{
+       u_int msg_len;
+       u_int buf_len;
+       u_int consumed;
+       u_int type;
+       u_char *cp;
+
+       buf_len = buffer_len(&iqueue);
+       if (buf_len < 5)
+               return;         /* Incomplete message. */
+       cp = buffer_ptr(&iqueue);
+       msg_len = get_u32(cp);
+       if (msg_len > MAX_MSG_LENGTH) {
+               error("bad message len %d", msg_len);
+               cleanup_exit(11);
+       }
+       if (buf_len < msg_len + 4)
+               return;
+       buffer_consume(&iqueue, 4);
+       buf_len -= 4;
+       type = buffer_get_char(&iqueue);
+       switch (type) {
+       case SSH_AGENTC_ADD_SMARTCARD_KEY:
+               debug("process_add");
+               process_add();
+               break;
+       case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
+               debug("process_del");
+               process_del();
+               break;
+       case SSH2_AGENTC_SIGN_REQUEST:
+               debug("process_sign");
+               process_sign();
+               break;
+       default:
+               error("Unknown message %d", type);
+               break;
+       }
+       /* discard the remaining bytes from the current packet */
+       if (buf_len < buffer_len(&iqueue)) {
+               error("iqueue grew unexpectedly");
+               cleanup_exit(255);
+       }
+       consumed = buf_len - buffer_len(&iqueue);
+       if (msg_len < consumed) {
+               error("msg_len %d < consumed %d", msg_len, consumed);
+               cleanup_exit(255);
+       }
+       if (msg_len > consumed)
+               buffer_consume(&iqueue, msg_len - consumed);
+}
+
+void
+cleanup_exit(int i)
+{
+       /* XXX */
+       _exit(i);
+}
+
+int
+main(int argc, char **argv)
+{
+       fd_set *rset, *wset;
+       int in, out, max, log_stderr = 0;
+       ssize_t len, olen, set_size;
+       SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
+       LogLevel log_level = SYSLOG_LEVEL_ERROR;
+       char buf[4*4096];
+
+       TAILQ_INIT(&pkcs11_keylist);
+       pkcs11_init(0);
+
+       extern char *optarg;
+       extern char *__progname;
+
+       log_init(__progname, log_level, log_facility, log_stderr);
+
+       in = STDIN_FILENO;
+       out = STDOUT_FILENO;
+
+       max = 0;
+       if (in > max)
+               max = in;
+       if (out > max)
+               max = out;
+
+       buffer_init(&iqueue);
+       buffer_init(&oqueue);
+
+       set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
+       rset = (fd_set *)xmalloc(set_size);
+       wset = (fd_set *)xmalloc(set_size);
+
+       for (;;) {
+               memset(rset, 0, set_size);
+               memset(wset, 0, set_size);
+
+               /*
+                * Ensure that we can read a full buffer and handle
+                * the worst-case length packet it can generate,
+                * otherwise apply backpressure by stopping reads.
+                */
+               if (buffer_check_alloc(&iqueue, sizeof(buf)) &&
+                   buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
+                       FD_SET(in, rset);
+
+               olen = buffer_len(&oqueue);
+               if (olen > 0)
+                       FD_SET(out, wset);
+
+               if (select(max+1, rset, wset, NULL, NULL) < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       error("select: %s", strerror(errno));
+                       cleanup_exit(2);
+               }
+
+               /* copy stdin to iqueue */
+               if (FD_ISSET(in, rset)) {
+                       len = read(in, buf, sizeof buf);
+                       if (len == 0) {
+                               debug("read eof");
+                               cleanup_exit(0);
+                       } else if (len < 0) {
+                               error("read: %s", strerror(errno));
+                               cleanup_exit(1);
+                       } else {
+                               buffer_append(&iqueue, buf, len);
+                       }
+               }
+               /* send oqueue to stdout */
+               if (FD_ISSET(out, wset)) {
+                       len = write(out, buffer_ptr(&oqueue), olen);
+                       if (len < 0) {
+                               error("write: %s", strerror(errno));
+                               cleanup_exit(1);
+                       } else {
+                               buffer_consume(&oqueue, len);
+                       }
+               }
+
+               /*
+                * Process requests from client if we can fit the results
+                * into the output buffer, otherwise stop processing input
+                * and let the output queue drain.
+                */
+               if (buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
+                       process();
+       }
+}
diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c
new file mode 100644 (file)
index 0000000..f824543
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2010 Markus Friedl.  All rights reserved.
+ *
+ * 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 <sys/types.h>
+#include <sys/queue.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <string.h>
+#include <dlfcn.h>
+
+#define CRYPTOKI_COMPAT
+#include "pkcs11.h"
+
+#include "log.h"
+#include "misc.h"
+#include "key.h"
+#include "ssh-pkcs11.h"
+#include "xmalloc.h"
+
+struct pkcs11_slotinfo {
+       CK_TOKEN_INFO           token;
+       CK_SESSION_HANDLE       session;
+       int                     logged_in;
+};
+
+struct pkcs11_provider {
+       char                    *name;
+       void                    *handle;
+       CK_FUNCTION_LIST        *function_list;
+       CK_INFO                 info;
+       CK_ULONG                nslots;
+       CK_SLOT_ID              *slotlist;
+       struct pkcs11_slotinfo  *slotinfo;
+       int                     valid;
+       int                     refcount;
+       TAILQ_ENTRY(pkcs11_provider) next;
+};
+
+TAILQ_HEAD(, pkcs11_provider) pkcs11_providers;
+
+struct pkcs11_key {
+       struct pkcs11_provider  *provider;
+       CK_ULONG                slotidx;
+       int                     (*orig_finish)(RSA *rsa);
+       RSA_METHOD              rsa_method;
+       char                    *keyid;
+       int                     keyid_len;
+};
+
+int pkcs11_interactive = 0;
+
+int
+pkcs11_init(int interactive)
+{
+       pkcs11_interactive = interactive;
+       TAILQ_INIT(&pkcs11_providers);
+       return (0);
+}
+
+/*
+ * finalize a provider shared libarary, it's no longer usable.
+ * however, there might still be keys referencing this provider,
+ * so the actuall freeing of memory is handled by pkcs11_provider_unref().
+ * this is called when a provider gets unregistered.
+ */
+static void
+pkcs11_provider_finalize(struct pkcs11_provider *p)
+{
+       CK_RV rv;
+       CK_ULONG i;
+
+       debug("pkcs11_provider_finalize: %p refcount %d valid %d",
+           p, p->refcount, p->valid);
+       if (!p->valid)
+               return;
+       for (i = 0; i < p->nslots; i++) {
+               if (p->slotinfo[i].session &&
+                   (rv = p->function_list->C_CloseSession(
+                   p->slotinfo[i].session)) != CKR_OK)
+                       error("C_CloseSession failed: %lu", rv);
+       }
+       if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
+               error("C_Finalize failed: %lu", rv);
+       p->valid = 0;
+       p->function_list = NULL;
+       dlclose(p->handle);
+}
+
+/*
+ * remove a reference to the provider.
+ * called when a key gets destroyed or when the provider is unregistered.
+ */
+static void
+pkcs11_provider_unref(struct pkcs11_provider *p)
+{
+       debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
+       if (--p->refcount <= 0) {
+               if (p->valid)
+                       error("pkcs11_provider_unref: %p still valid", p);
+               xfree(p->slotlist);
+               xfree(p->slotinfo);
+               xfree(p);
+       }
+}
+
+/* unregister all providers, keys might still point to the providers */
+void
+pkcs11_terminate(void)
+{
+       struct pkcs11_provider *p;
+
+       while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) {
+               TAILQ_REMOVE(&pkcs11_providers, p, next);
+               pkcs11_provider_finalize(p);
+               pkcs11_provider_unref(p);
+       }
+}
+
+/* lookup provider by name */
+static struct pkcs11_provider *
+pkcs11_provider_lookup(char *provider_id)
+{
+       struct pkcs11_provider *p;
+
+       TAILQ_FOREACH(p, &pkcs11_providers, next) {
+               debug("check %p %s", p, p->name);
+               if (!strcmp(provider_id, p->name))
+                       return (p);
+       }
+       return (NULL);
+}
+
+/* unregister provider by name */
+int
+pkcs11_del_provider(char *provider_id)
+{
+       struct pkcs11_provider *p;
+
+       if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
+               TAILQ_REMOVE(&pkcs11_providers, p, next);
+               pkcs11_provider_finalize(p);
+               pkcs11_provider_unref(p);
+               return (0);
+       }
+       return (-1);
+}
+
+/* openssl callback for freeing an RSA key */
+static int
+pkcs11_rsa_finish(RSA *rsa)
+{
+       struct pkcs11_key       *k11;
+       int rv = -1;
+
+       if ((k11 = RSA_get_app_data(rsa)) != NULL) {
+               if (k11->orig_finish)
+                       rv = k11->orig_finish(rsa);
+               if (k11->provider)
+                       pkcs11_provider_unref(k11->provider);
+               if (k11->keyid)
+                       xfree(k11->keyid);
+               xfree(k11);
+       }
+       return (rv);
+}
+
+/* openssl callback doing the actual signing operation */
+static int
+pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
+    int padding)
+{
+       struct pkcs11_key       *k11;
+       struct pkcs11_slotinfo  *si;
+       CK_FUNCTION_LIST        *f;
+       CK_OBJECT_HANDLE        obj;
+       CK_ULONG                tlen = 0, nfound = 0;
+       CK_RV                   rv;
+       CK_OBJECT_CLASS         private_key_class = CKO_PRIVATE_KEY;
+       CK_BBOOL                true = CK_TRUE;
+       CK_MECHANISM            mech = {
+               CKM_RSA_PKCS, NULL_PTR, 0
+       };
+       CK_ATTRIBUTE            key_filter[] = {
+               {CKA_CLASS, &private_key_class, sizeof(private_key_class) },
+               {CKA_ID, NULL, 0},
+               {CKA_SIGN, &true, sizeof(true) }
+       };
+       char                    *pin, prompt[1024];
+       int                     rval = -1;
+
+       if ((k11 = RSA_get_app_data(rsa)) == NULL) {
+               error("RSA_get_app_data failed for rsa %p", rsa);
+               return (-1);
+       }
+       if (!k11->provider || !k11->provider->valid) {
+               error("no pkcs11 (valid) provider for rsa %p", rsa);
+               return (-1);
+       }
+       f = k11->provider->function_list;
+       si = &k11->provider->slotinfo[k11->slotidx];
+       if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
+               if (!pkcs11_interactive) {
+                       error("need pin");
+                       return (-1);
+               }
+               snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ",
+                   si->token.label);
+               pin = read_passphrase(prompt, RP_ALLOW_EOF);
+               if (pin == NULL)
+                       return (-1);    /* bail out */
+               if ((rv = f->C_Login(si->session, CKU_USER, pin, strlen(pin)))
+                   != CKR_OK) {
+                       xfree(pin);
+                       error("C_Login failed: %lu", rv);
+                       return (-1);
+               }
+               xfree(pin);
+               si->logged_in = 1;
+       }
+       key_filter[1].pValue = k11->keyid;
+       key_filter[1].ulValueLen = k11->keyid_len;
+       if ((rv = f->C_FindObjectsInit(si->session, key_filter, 3)) != CKR_OK) {
+               error("C_FindObjectsInit failed: %lu", rv);
+               return (-1);
+       }
+       if ((rv = f->C_FindObjects(si->session, &obj, 1, &nfound)) != CKR_OK ||
+           nfound != 1) {
+               error("C_FindObjects failed (%lu nfound): %lu", nfound, rv);
+       } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
+               error("C_SignInit failed: %lu", rv);
+       } else {
+               /* XXX handle CKR_BUFFER_TOO_SMALL */
+               tlen = RSA_size(rsa);
+               rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
+               if (rv == CKR_OK) 
+                       rval = tlen;
+               else 
+                       error("C_Sign failed: %lu", rv);
+       }
+       if ((rv = f->C_FindObjectsFinal(si->session)) != CKR_OK)
+               error("C_FindObjectsFinal failed: %lu", rv);
+       return (rval);
+}
+
+static int
+pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
+    int padding)
+{
+       return (-1);
+}
+
+/* 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)
+{
+       struct pkcs11_key       *k11;
+       const RSA_METHOD        *def = RSA_get_default_method();
+
+       k11 = xcalloc(1, sizeof(*k11));
+       k11->provider = provider;
+       provider->refcount++;   /* provider referenced by RSA key */
+       k11->slotidx = slotidx;
+       /* identify key object on smartcard */
+       k11->keyid_len = keyid_attrib->ulValueLen;
+       k11->keyid = xmalloc(k11->keyid_len);
+       memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
+       k11->orig_finish = def->finish;
+       memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method));
+       k11->rsa_method.name = "pkcs11";
+       k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt;
+       k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt;
+       k11->rsa_method.finish = pkcs11_rsa_finish;
+       RSA_set_method(rsa, &k11->rsa_method);
+       RSA_set_app_data(rsa, k11);
+       return (0);
+}
+
+/* remove trailing spaces */
+static void
+rmspace(char *buf, size_t len)
+{
+       size_t i;
+
+       if (!len)
+               return;
+       for (i = len - 1;  i > 0; i--)
+               if (i == len - 1 || buf[i] == ' ')
+                       buf[i] = '\0';
+               else
+                       break;
+}
+
+/*
+ * open a pkcs11 session and login if required.
+ * if pin == NULL we delay login until key use
+ */
+static int
+pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin)
+{
+       CK_RV                   rv;
+       CK_FUNCTION_LIST        *f;
+       CK_SESSION_HANDLE       session;
+       int                     login_required;
+
+       f = p->function_list;
+       login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED;
+       if (pin && login_required && !strlen(pin)) {
+               error("pin required");
+               return (-1);
+       }
+       if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
+           CKF_SERIAL_SESSION, NULL, NULL, &session))
+           != CKR_OK) {
+               error("C_OpenSession failed: %lu", rv);
+               return (-1);
+       }
+       if (login_required && pin) {
+               if ((rv = f->C_Login(session, CKU_USER, pin, strlen(pin)))
+                   != CKR_OK) {
+                       error("C_Login failed: %lu", rv);
+                       if ((rv = f->C_CloseSession(session)) != CKR_OK)
+                               error("C_CloseSession failed: %lu", rv);
+                       return (-1);
+               }
+               p->slotinfo[slotidx].logged_in = 1;
+       }
+       p->slotinfo[slotidx].session = session;
+       return (0);
+}
+
+/*
+ * lookup public keys for token in slot identified by slotidx,
+ * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
+ * keysp points to an (possibly empty) array with *nkeys keys.
+ */
+static int
+pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, Key ***keysp,
+    int *nkeys)
+{
+       Key                     *key;
+       RSA                     *rsa;
+       int                     i;
+       CK_RV                   rv;
+       CK_OBJECT_HANDLE        obj;
+       CK_ULONG                nfound;
+       CK_SESSION_HANDLE       session;
+       CK_FUNCTION_LIST        *f;
+       CK_OBJECT_CLASS         pubkey_class = CKO_PUBLIC_KEY;
+       CK_ATTRIBUTE            pubkey_filter[] = {
+               { CKA_CLASS, &pubkey_class, sizeof(pubkey_class) }
+       };
+       CK_ATTRIBUTE            attribs[] = {
+               { CKA_ID, NULL, 0 },
+               { CKA_MODULUS, NULL, 0 },
+               { CKA_PUBLIC_EXPONENT, NULL, 0 }
+       };
+
+       f = p->function_list;
+       session = p->slotinfo[slotidx].session;
+       /* setup a filter the looks for public keys */
+       if ((rv = f->C_FindObjectsInit(session, pubkey_filter, 1)) != CKR_OK) {
+               error("C_FindObjectsInit failed: %lu", rv);
+               return (-1);
+       }
+       while (1) {
+               /* XXX 3 attributes in attribs[] */
+               for (i = 0; i < 3; i++) {
+                       attribs[i].pValue = NULL;
+                       attribs[i].ulValueLen = 0;
+               }
+               if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK
+                   || nfound == 0)
+                       break;
+               /* found a key, so figure out size of the attributes */
+               if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3))
+                   != CKR_OK) {
+                       error("C_GetAttributeValue failed: %lu", rv);
+                       continue;
+               }
+               /* allocate buffers for attributes, XXX check ulValueLen? */
+               for (i = 0; i < 3; i++)
+                       attribs[i].pValue = xmalloc(attribs[i].ulValueLen);
+               /* retrieve ID, modulus and public exponent of RSA key */
+               if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3))
+                   != CKR_OK) {
+                       error("C_GetAttributeValue failed: %lu", rv);
+               } else if ((rsa = RSA_new()) == NULL) {
+                       error("RSA_new failed");
+               } else {
+                       rsa->n = BN_bin2bn(attribs[1].pValue,
+                           attribs[1].ulValueLen, NULL);
+                       rsa->e = BN_bin2bn(attribs[2].pValue,
+                           attribs[2].ulValueLen, NULL);
+                       if (rsa->n && rsa->e &&
+                           pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) {
+                               key = key_new(KEY_UNSPEC);
+                               key->rsa = rsa;
+                               key->type = KEY_RSA;
+                               key->flags |= KEY_FLAG_EXT;
+                               /* expand key array and add key */
+                               *keysp = xrealloc(*keysp, *nkeys + 1,
+                                   sizeof(Key *));
+                               (*keysp)[*nkeys] = key;
+                               *nkeys = *nkeys + 1;
+                               debug("have %d keys", *nkeys);
+                       } else {
+                               RSA_free(rsa);
+                       }
+               }
+               for (i = 0; i < 3; i++)
+                       xfree(attribs[i].pValue);
+       }
+       if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)
+               error("C_FindObjectsFinal failed: %lu", rv);
+       return (0);
+}
+
+/* register a new provider, fails if provider already exists */
+int
+pkcs11_add_provider(char *provider_id, char *pin, Key ***keyp)
+{
+       int nkeys, need_finalize = 0;
+       struct pkcs11_provider *p = NULL;
+       void *handle = NULL;
+       CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);
+       CK_RV rv;
+       CK_FUNCTION_LIST *f = NULL;
+       CK_TOKEN_INFO *token;
+       CK_ULONG i;
+
+       *keyp = NULL;
+       if (pkcs11_provider_lookup(provider_id) != NULL) {
+               error("provider already registered: %s", provider_id);
+               goto fail;
+       }
+       /* open shared pkcs11-libarary */
+       if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
+               error("dlopen %s failed: %s", provider_id, dlerror());
+               goto fail;
+       }
+       if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
+               error("dlsym(C_GetFunctionList) failed: %s", dlerror());
+               goto fail;
+       }
+       p = xcalloc(1, sizeof(*p));
+       p->name = xstrdup(provider_id);
+       p->handle = handle;
+       /* setup the pkcs11 callbacks */
+       if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
+               error("C_GetFunctionList failed: %lu", rv);
+               goto fail;
+       }
+       p->function_list = f;
+       if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
+               error("C_Initialize failed: %lu", rv);
+               goto fail;
+       }
+       need_finalize = 1;
+       if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
+               error("C_GetInfo failed: %lu", rv);
+               goto fail;
+       }
+       rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
+       rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
+       debug("manufacturerID <%s> cryptokiVersion %d.%d"
+           " libraryDescription <%s> libraryVersion %d.%d",
+           p->info.manufacturerID,
+           p->info.cryptokiVersion.major,
+           p->info.cryptokiVersion.minor,
+           p->info.libraryDescription,
+           p->info.libraryVersion.major,
+           p->info.libraryVersion.minor);
+       if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
+               error("C_GetSlotList failed: %lu", rv);
+               goto fail;
+       }
+       if (p->nslots == 0) {
+               error("no slots");
+               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 failed: %lu", rv);
+               goto fail;
+       }
+       p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
+       p->valid = 1;
+       nkeys = 0;
+       for (i = 0; i < p->nslots; i++) {
+               token = &p->slotinfo[i].token;
+               if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
+                   != CKR_OK) {
+                       error("C_GetTokenInfo failed: %lu", rv);
+                       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));
+               debug("label <%s> manufacturerID <%s> model <%s> serial <%s>"
+                   " flags 0x%lx",
+                   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);
+       }
+       if (nkeys > 0) {
+               TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
+               p->refcount++;  /* add to provider list */
+               return (nkeys);
+       }
+       error("no keys");
+       /* 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 failed: %lu", rv);
+       if (p) {
+               if (p->slotlist)
+                       xfree(p->slotlist);
+               if (p->slotinfo)
+                       xfree(p->slotinfo);
+               xfree(p);
+       }
+       if (handle)
+               dlclose(handle);
+       return (-1);
+}
diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h
new file mode 100644 (file)
index 0000000..fae41a7
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2010 Markus Friedl.  All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+int    pkcs11_init(int);
+void   pkcs11_terminate(void);
+int    pkcs11_add_provider(char *, char *, Key ***);
+int    pkcs11_del_provider(char *);
diff --git a/ssh.1 b/ssh.1
index 1ff2cce4d721a692abfb46daacfb198b51636220..97a2455ab3281fabda0d4065a73b1c0bb8d50404 100644 (file)
--- a/ssh.1
+++ b/ssh.1
@@ -34,8 +34,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh.1,v 1.290 2010/01/11 01:39:46 dtucker Exp $
-.Dd $Mdocdate: January 11 2010 $
+.\" $OpenBSD: ssh.1,v 1.291 2010/02/08 10:50:20 markus Exp $
+.Dd $Mdocdate: February 8 2010 $
 .Dt SSH 1
 .Os
 .Sh NAME
@@ -284,12 +284,12 @@ will wait for all remote port forwards to be successfully established
 before placing itself in the background.
 .It Fl g
 Allows remote hosts to connect to local forwarded ports.
-.It Fl I Ar smartcard_device
-Specify the device
+.It Fl I Ar pkcs11
+Specify the PKCS#11 shared libarary
 .Nm
-should use to communicate with a smartcard used for storing the user's
+should use to communicate with a PKCS#11 token used for storing the user's
 private RSA key.
-This option is only available if support for smartcard devices
+This option is only available if support for PKCS#11
 is compiled in (default is no support).
 .It Fl i Ar identity_file
 Selects a file from which the identity (private key) for
@@ -469,6 +469,7 @@ For full details of the options listed below, and their possible values, see
 .It NumberOfPasswordPrompts
 .It PasswordAuthentication
 .It PermitLocalCommand
+.It PKCS11Provider
 .It Port
 .It PreferredAuthentications
 .It Protocol
@@ -481,7 +482,6 @@ For full details of the options listed below, and their possible values, see
 .It SendEnv
 .It ServerAliveInterval
 .It ServerAliveCountMax
-.It SmartcardDevice
 .It StrictHostKeyChecking
 .It TCPKeepAlive
 .It Tunnel
diff --git a/ssh.c b/ssh.c
index 97afdcfeeb2144f2b8108493ff06b386345db7f2..63523b42ab4252a7389047645c5f3c7d27c66803 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.332 2010/01/26 01:28:35 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.333 2010/02/08 10:50:20 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 #include "roaming.h"
 #include "version.h"
 
-#ifdef SMARTCARD
-#include "scard.h"
+#ifdef ENABLE_PKCS11
+#include "ssh-pkcs11.h"
 #endif
 
 extern char *__progname;
@@ -362,10 +362,10 @@ main(int ac, char **av)
                            xstrdup(optarg);
                        break;
                case 'I':
-#ifdef SMARTCARD
-                       options.smartcard_device = xstrdup(optarg);
+#ifdef ENABLE_PKCS11
+                       options.pkcs11_provider = xstrdup(optarg);
 #else
-                       fprintf(stderr, "no support for smartcards.\n");
+                       fprintf(stderr, "no support for PKCS#11.\n");
 #endif
                        break;
                case 't':
@@ -1305,14 +1305,17 @@ load_public_identity_files(void)
        int i = 0;
        Key *public;
        struct passwd *pw;
-#ifdef SMARTCARD
+#ifdef ENABLE_PKCS11
        Key **keys;
+       int nkeys;
 
-       if (options.smartcard_device != NULL &&
+       if (options.pkcs11_provider != NULL &&
            options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
-           (keys = sc_get_keys(options.smartcard_device, NULL)) != NULL) {
+           (pkcs11_init(!options.batch_mode) == 0) &&
+           (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL,
+           &keys)) > 0) {
                int count = 0;
-               for (i = 0; keys[i] != NULL; i++) {
+               for (i = 0; i < nkeys; i++) {
                        count++;
                        memmove(&options.identity_files[1],
                            &options.identity_files[0],
@@ -1322,14 +1325,16 @@ load_public_identity_files(void)
                            sizeof(Key *) * (SSH_MAX_IDENTITY_FILES - 1));
                        options.num_identity_files++;
                        options.identity_keys[0] = keys[i];
-                       options.identity_files[0] = sc_get_key_label(keys[i]);
+                       options.identity_files[0] =
+                           xstrdup(options.pkcs11_provider); /* XXX */
                }
                if (options.num_identity_files > SSH_MAX_IDENTITY_FILES)
                        options.num_identity_files = SSH_MAX_IDENTITY_FILES;
                i = count;
                xfree(keys);
+               /* XXX leaks some keys */
        }
-#endif /* SMARTCARD */
+#endif /* ENABLE_PKCS11 */
        if ((pw = getpwuid(original_real_uid)) == NULL)
                fatal("load_public_identity_files: getpwuid failed");
        pwname = xstrdup(pw->pw_name);
index 01f5f430474d1f087bb22a64f07df1ff445094b4..350a8eacd94e05bfe0fb53c64fe7953e1d1f1de0 100644 (file)
@@ -34,8 +34,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh_config.5,v 1.126 2010/01/09 23:04:13 dtucker Exp $
-.Dd $Mdocdate: January 9 2010 $
+.\" $OpenBSD: ssh_config.5,v 1.127 2010/02/08 10:50:20 markus Exp $
+.Dd $Mdocdate: February 8 2010 $
 .Dt SSH_CONFIG 5
 .Os
 .Sh NAME
@@ -711,6 +711,13 @@ or
 .Dq no .
 The default is
 .Dq no .
+.It Cm PKCS11Provider
+Specifies which PKCS#11 provider to use.
+The argument to this keyword is the PKCS#11 shared libary
+.Xr ssh 1
+should use to communicate with a PKCS#11 token used for storing the user's
+private RSA key.
+By default, no device is specified and PKCS#11 support is not activated.
 .It Cm Port
 Specifies the port number to connect on the remote host.
 The default is 22.
@@ -927,13 +934,6 @@ channel to request a response from the server.
 The default
 is 0, indicating that these messages will not be sent to the server.
 This option applies to protocol version 2 only.
-.It Cm SmartcardDevice
-Specifies which smartcard device to use.
-The argument to this keyword is the device
-.Xr ssh 1
-should use to communicate with a smartcard used for storing the user's
-private RSA key.
-By default, no device is specified and smartcard support is not activated.
 .It Cm StrictHostKeyChecking
 If this flag is set to
 .Dq yes ,