summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--macros/neon.m411
-rw-r--r--neon.mak3
-rw-r--r--src/Makefile.in35
-rw-r--r--src/ne_gnutls.c48
-rw-r--r--src/ne_pkcs11.c466
-rw-r--r--src/ne_pkcs11.h80
-rw-r--r--src/ne_private.h4
-rw-r--r--src/ne_privssl.h3
-rw-r--r--src/ne_session.c7
9 files changed, 634 insertions, 23 deletions
diff --git a/macros/neon.m4 b/macros/neon.m4
index 6b476e6..9b3abbd 100644
--- a/macros/neon.m4
+++ b/macros/neon.m4
@@ -967,6 +967,17 @@ gnutls)
if test ${ac_cv_func_gnutls_x509_dn_get_rdn_ava}X${ac_cv_header_iconv_h} = yesXyes; then
AC_CHECK_FUNCS(iconv)
fi
+
+ if test x${ac_cv_func_gnutls_sign_callback_set} = xyes; then
+ # PKCS#11... ho!
+ NE_PKG_CONFIG(NE_PK11, pakchois,
+ [AC_MSG_NOTICE(using pakchois for PKCS11 support)
+ AC_DEFINE(HAVE_PAKCHOIS, 1, [Define if pakchois library supported])
+ CPPFLAGS="$CPPFLAGS ${NE_PK11_CFLAGS}"
+ NEON_LIBS="${NEON_LIBS} ${NE_PK11_LIBS}"],
+ [AC_MSG_NOTICE(pakchois library not found; no PKCS11 support)])
+ fi
+
;;
*) # Default to off; only create crypto-enabled binaries if requested.
NE_DISABLE_SUPPORT(SSL, [SSL support is not enabled])
diff --git a/neon.mak b/neon.mak
index d77b2a9..9c580b5 100644
--- a/neon.mak
+++ b/neon.mak
@@ -116,6 +116,7 @@ LIB32_OBJS= \
"$(INTDIR)\ne_dates.obj" \
"$(INTDIR)\ne_i18n.obj" \
"$(INTDIR)\ne_md5.obj" \
+ "$(INTDIR)\ne_pkcs11.obj" \
"$(INTDIR)\ne_redirect.obj" \
"$(INTDIR)\ne_request.obj" \
"$(INTDIR)\ne_session.obj" \
@@ -174,6 +175,7 @@ CLEAN: $(ZLIB_CLEAN)
-@erase "$(INTDIR)\ne_session.obj"
-@erase "$(INTDIR)\ne_openssl.obj"
-@erase "$(INTDIR)\ne_stubssl.obj"
+ -@erase "$(INTDIR)\ne_pkcs11.obj"
-@erase "$(INTDIR)\ne_socket.obj"
-@erase "$(INTDIR)\ne_sspi.obj"
-@erase "$(INTDIR)\ne_string.obj"
@@ -220,6 +222,7 @@ CLEAN: $(ZLIB_CLEAN)
"$(INTDIR)\ne_session.obj": .\src\ne_session.c
"$(INTDIR)\ne_openssl.obj": .\src\ne_openssl.c
"$(INTDIR)\ne_stubssl.obj": .\src\ne_stubssl.c
+"$(INTDIR)\ne_pkcs11.obj": .\src\ne_pkcs11.c
"$(INTDIR)\ne_socket.obj": .\src\ne_socket.c
"$(INTDIR)\ne_sspi.obj": .\src\ne_sspi.c
"$(INTDIR)\ne_string.obj": .\src\ne_string.c
diff --git a/src/Makefile.in b/src/Makefile.in
index 4f8af46..1645e48 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -41,7 +41,7 @@ NEON_BASEOBJS = ne_request.@NEON_OBJEXT@ ne_session.@NEON_OBJEXT@ \
ne_md5.@NEON_OBJEXT@ ne_utils.@NEON_OBJEXT@ \
ne_socket.@NEON_OBJEXT@ ne_auth.@NEON_OBJEXT@ \
ne_redirect.@NEON_OBJEXT@ ne_compress.@NEON_OBJEXT@ \
- ne_i18n.@NEON_OBJEXT@
+ ne_i18n.@NEON_OBJEXT@ ne_pkcs11.@NEON_OBJEXT@
NEON_DAVOBJS = $(NEON_BASEOBJS) \
ne_207.@NEON_OBJEXT@ ne_xml.@NEON_OBJEXT@ \
@@ -96,7 +96,7 @@ check-incl:
update-deps:
for f in `echo $(OBJECTS) | sed 's/\\.@NEON_OBJEXT@/.c/g'`; do \
$(CC) $(CPPFLAGS) -MM -c $$f; \
- done | sed 's, \.\./, $$(top_builddir)/,g;s/\.o: /.@NEON''_OBJEXT@: /' > new-deps
+ done | sed 's, \.\./, $$(top_builddir)/,g;s, /[^ ]*.h,,g;/^ .$$/d;s/\.o: /.@NEON''_OBJEXT@: /' > new-deps
sed '/[-]--CUT---/q' Makefile.in > Makefile.new
cat Makefile.new new-deps > Makefile.in
rm new-deps Makefile.new
@@ -104,10 +104,10 @@ update-deps:
#### ---CUT--- DO NOT REMOVE THIS LINE. Generated dependencies follow. ####
ne_request.@NEON_OBJEXT@: ne_request.c $(top_builddir)/config.h ne_internal.h ne_defs.h ne_alloc.h \
ne_request.h ne_utils.h ne_string.h ne_session.h ne_ssl.h ne_uri.h \
- ne_socket.h ne_private.h
+ ne_socket.h ne_private.h ne_pkcs11.h
ne_session.@NEON_OBJEXT@: ne_session.c $(top_builddir)/config.h ne_session.h ne_ssl.h ne_defs.h \
ne_uri.h ne_socket.h ne_alloc.h ne_utils.h ne_internal.h ne_string.h \
- ne_dates.h ne_private.h ne_request.h
+ ne_dates.h ne_private.h ne_request.h ne_pkcs11.h
ne_basic.@NEON_OBJEXT@: ne_basic.c $(top_builddir)/config.h ne_request.h ne_utils.h ne_defs.h \
ne_string.h ne_alloc.h ne_session.h ne_ssl.h ne_uri.h ne_socket.h \
ne_basic.h ne_207.h ne_xml.h ne_locks.h ne_dates.h ne_internal.h
@@ -117,10 +117,13 @@ ne_dates.@NEON_OBJEXT@: ne_dates.c $(top_builddir)/config.h ne_alloc.h ne_defs.h
ne_string.h
ne_alloc.@NEON_OBJEXT@: ne_alloc.c $(top_builddir)/config.h ne_alloc.h ne_defs.h
ne_md5.@NEON_OBJEXT@: ne_md5.c $(top_builddir)/config.h ne_md5.h ne_defs.h ne_string.h ne_alloc.h
-ne_utils.@NEON_OBJEXT@: ne_utils.c $(top_builddir)/config.h ne_utils.h ne_defs.h ne_string.h \
- ne_alloc.h ne_dates.h
-ne_socket.@NEON_OBJEXT@: ne_socket.c $(top_builddir)/config.h ne_privssl.h ne_ssl.h ne_defs.h \
- ne_socket.h ne_internal.h ne_utils.h ne_string.h ne_alloc.h ne_sspi.h
+ne_utils.@NEON_OBJEXT@: ne_utils.c $(top_builddir)/config.h \
+ ne_utils.h ne_defs.h \
+ ne_string.h ne_alloc.h ne_dates.h
+ne_socket.@NEON_OBJEXT@: ne_socket.c $(top_builddir)/config.h \
+ ne_privssl.h ne_ssl.h \
+ ne_defs.h ne_socket.h ne_internal.h ne_utils.h ne_string.h ne_alloc.h \
+ ne_sspi.h
ne_auth.@NEON_OBJEXT@: ne_auth.c $(top_builddir)/config.h ne_md5.h ne_defs.h ne_dates.h \
ne_request.h ne_utils.h ne_string.h ne_alloc.h ne_session.h ne_ssl.h \
ne_uri.h ne_socket.h ne_auth.h ne_internal.h
@@ -131,6 +134,12 @@ ne_compress.@NEON_OBJEXT@: ne_compress.c $(top_builddir)/config.h ne_request.h n
ne_defs.h ne_string.h ne_alloc.h ne_session.h ne_ssl.h ne_uri.h \
ne_socket.h ne_compress.h ne_internal.h
ne_i18n.@NEON_OBJEXT@: ne_i18n.c $(top_builddir)/config.h ne_i18n.h ne_defs.h
+ne_pkcs11.@NEON_OBJEXT@: ne_pkcs11.c $(top_builddir)/config.h ne_pkcs11.h ne_defs.h ne_session.h \
+ ne_ssl.h ne_uri.h ne_socket.h \
+ ne_internal.h \
+ ne_alloc.h ne_private.h ne_request.h ne_utils.h ne_string.h \
+ ne_privssl.h \
+
ne_207.@NEON_OBJEXT@: ne_207.c $(top_builddir)/config.h ne_alloc.h ne_defs.h ne_utils.h ne_xml.h \
ne_207.h ne_request.h ne_string.h ne_session.h ne_ssl.h ne_uri.h \
ne_socket.h ne_basic.h ne_internal.h
@@ -149,9 +158,7 @@ ne_xmlreq.@NEON_OBJEXT@: ne_xmlreq.c $(top_builddir)/config.h ne_internal.h ne_d
ne_acl.@NEON_OBJEXT@: ne_acl.c $(top_builddir)/config.h ne_request.h ne_utils.h ne_defs.h \
ne_string.h ne_alloc.h ne_session.h ne_ssl.h ne_uri.h ne_socket.h \
ne_locks.h ne_acl.h ne_xml.h
-ne_openssl.@NEON_OBJEXT@: ne_openssl.c $(top_builddir)/config.h ne_ssl.h ne_defs.h ne_string.h \
- ne_alloc.h ne_session.h ne_uri.h ne_socket.h ne_internal.h ne_private.h \
- ne_request.h ne_utils.h ne_privssl.h
-ne_gnutls.@NEON_OBJEXT@: ne_gnutls.c $(top_builddir)/config.h ne_ssl.h ne_defs.h ne_string.h \
- ne_alloc.h ne_session.h ne_uri.h ne_socket.h ne_internal.h ne_private.h \
- ne_request.h ne_utils.h ne_privssl.h
+ne_gnutls.@NEON_OBJEXT@: ne_gnutls.c $(top_builddir)/config.h \
+ ne_ssl.h ne_defs.h \
+ ne_string.h ne_alloc.h ne_session.h ne_uri.h ne_socket.h ne_internal.h \
+ ne_private.h ne_request.h ne_utils.h ne_pkcs11.h ne_privssl.h
diff --git a/src/ne_gnutls.c b/src/ne_gnutls.c
index 0a1fe33..1dfbe52 100644
--- a/src/ne_gnutls.c
+++ b/src/ne_gnutls.c
@@ -1,6 +1,6 @@
/*
neon SSL/TLS support using GNU TLS
- Copyright (C) 2002-2007, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 2002-2008, Joe Orton <joe@manyfish.co.uk>
Copyright (C) 2004, Aleix Conchillo Flaque <aleix@member.fsf.org>
This library is free software; you can redistribute it and/or
@@ -69,6 +69,7 @@ struct ne_ssl_certificate_s {
struct ne_ssl_client_cert_s {
gnutls_pkcs12 p12;
int decrypted; /* non-zero if successfully decrypted. */
+ int keyless;
ne_ssl_certificate cert;
gnutls_x509_privkey pkey;
char *friendly_name;
@@ -502,13 +503,18 @@ static ne_ssl_client_cert *dup_client_cert(const ne_ssl_client_cert *cc)
ne_ssl_client_cert *newcc = ne_calloc(sizeof *newcc);
newcc->decrypted = 1;
-
- ret = gnutls_x509_privkey_init(&newcc->pkey);
- if (ret != 0) goto dup_error;
-
- ret = gnutls_x509_privkey_cpy(newcc->pkey, cc->pkey);
- if (ret != 0) goto dup_error;
+ if (cc->keyless) {
+ newcc->keyless = 1;
+ }
+ else {
+ ret = gnutls_x509_privkey_init(&newcc->pkey);
+ if (ret != 0) goto dup_error;
+
+ ret = gnutls_x509_privkey_cpy(newcc->pkey, cc->pkey);
+ if (ret != 0) goto dup_error;
+ }
+
newcc->cert.subject = x509_crt_copy(cc->cert.subject);
if (!newcc->cert.subject) goto dup_error;
@@ -533,7 +539,7 @@ static int provide_client_cert(gnutls_session session,
ne_session *sess = gnutls_session_get_ptr(session);
if (!sess) {
- return -1;
+ return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
}
if (!sess->client_cert && sess->ssl_provide_fn) {
@@ -558,10 +564,11 @@ static int provide_client_cert(gnutls_session session,
/* tell GNU TLS not to deallocate the certs. */
st->deinit_all = 0;
} else {
- return -1;
+ return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
}
} else {
NE_DEBUG(NE_DBG_SSL, "No client certificate supplied.\n");
+ return GNUTLS_E_NO_CERTIFICATE_FOUND;
}
return 0;
@@ -938,6 +945,29 @@ ne_ssl_client_cert *ne_ssl_clicert_read(const char *filename)
}
}
+ne_ssl_client_cert *ne__ssl_clicert_exkey_import(const unsigned char *der,
+ size_t der_len)
+{
+ ne_ssl_client_cert *cc;
+ gnutls_x509_crt x5;
+ gnutls_datum datum;
+
+ datum.data = (unsigned char *)der;
+ datum.size = der_len;
+
+ if (gnutls_x509_crt_init(&x5)
+ || gnutls_x509_crt_import(x5, &datum, GNUTLS_X509_FMT_DER)) {
+ NE_DEBUG(NE_DBG_SSL, "ssl: crt_import failed.\n");
+ return NULL;
+ }
+
+ cc = ne_calloc(sizeof *cc);
+ cc->keyless = 1;
+ populate_cert(&cc->cert, x5);
+
+ return cc;
+}
+
int ne_ssl_clicert_encrypted(const ne_ssl_client_cert *cc)
{
return !cc->decrypted;
diff --git a/src/ne_pkcs11.c b/src/ne_pkcs11.c
new file mode 100644
index 0000000..6cf508e
--- /dev/null
+++ b/src/ne_pkcs11.c
@@ -0,0 +1,466 @@
+/*
+ neon PKCS#11 support
+ Copyright (C) 2008, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+*/
+
+#include "config.h"
+
+#include "ne_pkcs11.h"
+
+#ifdef HAVE_PAKCHOIS
+#include <string.h>
+
+#include <pakchois.h>
+
+#include "ne_internal.h"
+#include "ne_alloc.h"
+#include "ne_private.h"
+#include "ne_privssl.h"
+
+struct pk11_context {
+ pakchois_module_t *module;
+ pakchois_session_t *session;
+ ck_object_handle_t privkey;
+};
+
+static void pk11_destroy(void *userdata)
+{
+ struct pk11_context *pk11 = userdata;
+
+ if (pk11->session) {
+ pakchois_close_session(pk11->session);
+ }
+ pakchois_module_destroy(pk11->module);
+ ne_free(pk11);
+}
+
+static int pk11_find_x509(pakchois_session_t *pks, ne_session *sess,
+ unsigned char *certid, unsigned long *cid_len)
+{
+ struct ck_attribute a[3];
+ ck_object_class_t class;
+ ck_certificate_type_t type;
+ ck_rv_t rv;
+ ck_object_handle_t obj;
+ unsigned long count;
+ int found = 0;
+
+ /* Find objects with cert class and X.509 cert type. */
+ class = CKO_CERTIFICATE;
+ type = CKC_X_509;
+
+ a[0].type = CKA_CLASS;
+ a[0].value = &class;
+ a[0].value_len = sizeof class;
+ a[1].type = CKA_CERTIFICATE_TYPE;
+ a[1].value = &type;
+ a[1].value_len = sizeof type;
+
+ rv = pakchois_find_objects_init(pks, a, 2);
+ if (rv != CKR_OK) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: FindObjectsInit failed.\n");
+ return 0;
+ }
+
+ while (pakchois_find_objects(pks, &obj, 1, &count) == CKR_OK
+ && count == 1) {
+ unsigned char value[8192], subject[8192];
+
+ a[0].type = CKA_VALUE;
+ a[0].value = value;
+ a[0].value_len = sizeof value;
+ a[1].type = CKA_ID;
+ a[1].value = certid;
+ a[1].value_len = *cid_len;
+ a[2].type = CKA_SUBJECT;
+ a[2].value = subject;
+ a[2].value_len = sizeof subject;
+
+ if (pakchois_get_attribute_value(pks, obj, a, 3) == CKR_OK) {
+ ne_ssl_client_cert *cc;
+
+ cc = ne__ssl_clicert_exkey_import(value, a[0].value_len);
+ if (cc) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: Imported X.509 cert.\n");
+ ne_ssl_set_clicert(sess, cc);
+ found = 1;
+ *cid_len = a[1].value_len;
+ break;
+ }
+ }
+ else {
+ NE_DEBUG(NE_DBG_SSL, "pk11: Skipped cert, missing attrs.\n");
+ }
+ }
+
+ pakchois_find_objects_final(pks);
+ return found;
+}
+
+static int pk11_find_pkey(struct pk11_context *ctx, pakchois_session_t *pks,
+ unsigned char *certid, unsigned long cid_len)
+{
+ struct ck_attribute a[3];
+ ck_object_class_t class;
+ ck_key_type_t type;
+ ck_rv_t rv;
+ ck_object_handle_t obj;
+ unsigned long count;
+ int found = 0;
+
+ class = CKO_PRIVATE_KEY;
+ type = CKK_RSA; /* FIXME: check from the cert whether DSA or RSA */
+
+ /* Find an object with private key class and a certificate ID
+ * which matches the certificate. */
+ /* FIXME: also match the cert subject. */
+ a[0].type = CKA_CLASS;
+ a[0].value = &class;
+ a[0].value_len = sizeof class;
+ a[1].type = CKA_KEY_TYPE;
+ a[1].value = &type;
+ a[1].value_len = sizeof type;
+ a[2].type = CKA_ID;
+ a[2].value = certid;
+ a[2].value_len = cid_len;
+
+ rv = pakchois_find_objects_init(pks, a, 3);
+ if (rv != CKR_OK) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: FindObjectsInit failed.\n");
+ /* TODO: error propagation */
+ return 0;
+ }
+
+ rv = pakchois_find_objects(pks, &obj, 1, &count);
+ if (rv == CKR_OK && count == 1) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: Found private key.\n");
+ found = 1;
+ ctx->privkey = obj;
+ }
+
+ pakchois_find_objects_final(pks);
+
+ return found;
+}
+
+static int find_client_cert(ne_session *sess, struct pk11_context *ctx,
+ pakchois_session_t *pks)
+{
+ unsigned char certid[8192];
+ unsigned long cid_len = sizeof certid;
+
+ /* TODO: match cert subject too. */
+ return pk11_find_x509(pks, sess, certid, &cid_len)
+ && pk11_find_pkey(ctx, pks, certid, cid_len);
+}
+
+/* Callback invoked by GnuTLS to provide the signature. The signature
+ * operation is handled here by the PKCS#11 provider. */
+static int pk11_sign_callback(gnutls_session_t session,
+ void *userdata,
+ gnutls_certificate_type_t cert_type,
+ const gnutls_datum_t *cert,
+ const gnutls_datum_t *hash,
+ gnutls_datum_t *signature)
+{
+ struct pk11_context *ctx = userdata;
+ ck_rv_t rv;
+ struct ck_mechanism mech;
+ unsigned long siglen;
+
+ if (!ctx->session || ctx->privkey == CK_INVALID_HANDLE) {
+ NE_DEBUG(NE_DBG_SSL, "p11: Boo, can't sign :(\n");
+ return GNUTLS_E_NO_CERTIFICATE_FOUND;
+ }
+
+ /* FIXME: from the object determine whether this should be
+ * CKM_DSA, or CKM_RSA_PKCS, or something unknown (&fail). */
+ mech.mechanism = CKM_RSA_PKCS;
+ mech.parameter = NULL;
+ mech.parameter_len = 0;
+
+ /* Initialize signing operation; using the private key discovered
+ * earlier. */
+ rv = pakchois_sign_init(ctx->session, &mech, ctx->privkey);
+ if (rv != CKR_OK) {
+ NE_DEBUG(NE_DBG_SSL, "p11: SignInit failed: %lx.\n", rv);
+ return GNUTLS_E_PK_SIGN_FAILED;
+ }
+
+ /* Work out how long the signature must be: */
+ rv = pakchois_sign(ctx->session, hash->data, hash->size, NULL, &siglen);
+ if (rv != CKR_OK) {
+ NE_DEBUG(NE_DBG_SSL, "p11: Sign failed.\n");
+ return GNUTLS_E_PK_SIGN_FAILED;
+ }
+
+ signature->data = gnutls_malloc(siglen);
+ signature->size = siglen;
+
+ rv = pakchois_sign(ctx->session, hash->data, hash->size,
+ signature->data, &siglen);
+ if (rv != CKR_OK) {
+ NE_DEBUG(NE_DBG_SSL, "p11: Sign failed.\n");
+ return GNUTLS_E_PK_SIGN_FAILED;
+ }
+
+ NE_DEBUG(NE_DBG_SSL, "p11: signed.\n");
+
+ return 0;
+}
+
+static void terminate_string(unsigned char *str, size_t len)
+{
+ unsigned char *ptr = str + len - 1;
+
+ while ((*ptr == ' ' || *ptr == '\t' || *ptr == '\0') && ptr >= str)
+ ptr--;
+
+ if (ptr == str - 1)
+ str[0] = '\0';
+ else if (ptr == str + len - 1)
+ str[len-1] = '\0';
+ else
+ ptr[1] = '\0';
+}
+
+static int pk11_login(pakchois_module_t *module, ck_slot_id_t slot_id,
+ ne_session *sess, pakchois_session_t *pks,
+ struct ck_slot_info *sinfo)
+{
+ struct ck_token_info tinfo;
+ int attempt = 0;
+ ck_rv_t rv;
+
+ if (pakchois_get_token_info(module, slot_id, &tinfo) != CKR_OK) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: GetTokenInfo failed\n");
+ /* TODO: propagate error. */
+ return -1;
+ }
+
+ if ((tinfo.flags & CKF_LOGIN_REQUIRED) == 0) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: No login required.\n");
+ return 0;
+ }
+
+ /* For a token with a "protected" (out-of-band) authentication
+ * path, calling login with a NULL username is all that is
+ * required. */
+ if (tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
+ if (pakchois_login(pks, CKU_USER, NULL, 0) == CKR_OK) {
+ return 0;
+ }
+ else {
+ NE_DEBUG(NE_DBG_SSL, "pk11: Protected login failed.\n");
+ /* TODO: error propagation. */
+ return -1;
+ }
+ }
+
+ /* Otherwise, PIN entry is necessary for login, so fail if there's
+ * no callback. */
+ if (!sess->ssl_pk11pin_fn) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: No pin callback but login required.\n");
+ /* TODO: propagate error. */
+ return -1;
+ }
+
+ terminate_string(sinfo->slot_description, sizeof sinfo->slot_description);
+
+ do {
+ char pin[NE_SSL_P11PINLEN];
+ unsigned int flags = 0;
+
+ /* If login has been attempted once already, check the token
+ * status again, the flags might change. */
+ if (attempt++) {
+ if (pakchois_get_token_info(module, slot_id, &tinfo) != CKR_OK) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: GetTokenInfo failed\n");
+ /* TODO: propagate error. */
+ return -1;
+ }
+ }
+
+ if (tinfo.flags & CKF_USER_PIN_COUNT_LOW)
+ flags |= NE_SSL_P11PIN_COUNT_LOW;
+ if (tinfo.flags & CKF_USER_PIN_FINAL_TRY)
+ flags |= NE_SSL_P11PIN_FINAL_TRY;
+
+ terminate_string(tinfo.label, sizeof tinfo.label);
+
+ if (sess->ssl_pk11pin_fn(sess->ssl_pk11pin_ud, attempt,
+ (const char *)sinfo->slot_description,
+ (const char *)tinfo.label,
+ flags, pin)) {
+ return -1;
+ }
+
+ rv = pakchois_login(pks, CKU_USER, (unsigned char *)pin, strlen(pin));
+
+ /* Try to scrub the pin off the stack. Clever compilers will
+ * probably optimize this away, oh well. */
+ memset(pin, 0, sizeof pin);
+ } while (rv == CKR_PIN_INCORRECT);
+
+ NE_DEBUG(NE_DBG_SSL, "pk11: Login result = %lu\n", rv);
+
+ return rv == CKR_OK ? 0 : -1;
+}
+
+static void pk11_provide(void *userdata, ne_session *sess,
+ const ne_ssl_dname *const *dnames,
+ int dncount)
+{
+ struct pk11_context *ctx = userdata;
+ ck_slot_id_t *slots;
+ unsigned long scount, n;
+
+ if (pakchois_get_slot_list(ctx->module, 1, NULL, &scount) != CKR_OK
+ || scount == 0) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: No slots.\n");
+ /* TODO: propagate error. */
+ return;
+ }
+
+ slots = ne_malloc(scount * sizeof *slots);
+ if (pakchois_get_slot_list(ctx->module, 1, slots, &scount) != CKR_OK) {
+ ne_free(slots);
+ NE_DEBUG(NE_DBG_SSL, "pk11: Really, no slots?\n");
+ /* TODO: propagate error. */
+ return;
+ }
+
+ NE_DEBUG(NE_DBG_SSL, "pk11: Found %ld slots.\n", scount);
+
+ for (n = 0; n < scount; n++) {
+ pakchois_session_t *pks;
+ ck_rv_t rv;
+ struct ck_slot_info sinfo;
+
+ if (pakchois_get_slot_info(ctx->module, slots[n], &sinfo) != CKR_OK) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: GetSlotInfo failed\n");
+ continue;
+ }
+
+ if ((sinfo.flags & CKF_TOKEN_PRESENT) == 0) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: slot empty, ignoring\n");
+ continue;
+ }
+
+ rv = pakchois_open_session(ctx->module, slots[n],
+ CKF_SERIAL_SESSION,
+ NULL, NULL, &pks);
+ if (rv != CKR_OK) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: could not open slot, %ld (%ld: %ld)\n",
+ rv, n, slots[n]);
+ continue;
+ }
+
+ if (pk11_login(ctx->module, slots[n], sess, pks, &sinfo) == 0) {
+ if (find_client_cert(sess, ctx, pks)) {
+ NE_DEBUG(NE_DBG_SSL, "pk11: Setup complete.\n");
+ ctx->session = pks;
+
+ return;
+ }
+ }
+
+ pakchois_close_session(pks);
+ }
+
+ ne_free(slots);
+}
+
+static int pk11_setup(ne_session *sess, pakchois_module_t *module)
+{
+ struct pk11_context *ctx;
+
+ ctx = ne_malloc(sizeof *ctx);
+ ctx->module = module;
+ ctx->session = NULL;
+ ctx->privkey = CK_INVALID_HANDLE;
+
+ ne_hook_destroy_session(sess, pk11_destroy, ctx);
+
+ ne_ssl_provide_clicert(sess, pk11_provide, ctx);
+
+ /* FIXME: this is bad, because it means only one PKCS#11 provider
+ * can be used at a time ("last through the door wins"), but needs
+ * to be done early currently so that ne_sock_connect_ssl installs
+ * the callback. */
+
+ sess->ssl_context->sign_func = pk11_sign_callback;
+ sess->ssl_context->sign_data = ctx;
+
+ return NE_OK;
+}
+
+int ne_ssl_provide_pkcs11_clicert(ne_session *sess, const char *provider)
+{
+ pakchois_module_t *pm;
+ ck_rv_t rv;
+
+ rv = pakchois_module_load(&pm, provider);
+ if (rv != CKR_OK) {
+ ne_set_error(sess, _("Could not load PKCS#11 module: %ld"), rv);
+ return NE_LOOKUP;
+ }
+
+ return pk11_setup(sess, pm);
+}
+
+int ne_ssl_provide_nsspk11_clicert(ne_session *sess,
+ const char *provider,
+ const char *directory,
+ const char *cert_prefix,
+ const char *key_prefix,
+ const char *secmod_db)
+{
+ pakchois_module_t *pm;
+ ck_rv_t rv;
+
+ rv = pakchois_module_nssload(&pm, provider,
+ directory, cert_prefix,
+ key_prefix, secmod_db);
+ if (rv != CKR_OK) {
+ ne_set_error(sess, _("Could not load PKCS#11 module: %ld"), rv);
+ return NE_LOOKUP;
+ }
+
+ return pk11_setup(sess, pm);
+}
+
+#else /* !HAVE_PAKCHOIS */
+
+int ne_ssl_provide_pkcs11_clicert(ne_session *sess, const char *provider)
+{
+ return NE_FAILED;
+}
+
+int ne_ssl_provide_nsspk11_clicert(ne_session *sess,
+ const char *provider,
+ const char *directory,
+ const char *cert_prefix,
+ const char *key_prefix,
+ const char *secmod_db)
+{
+ return NE_FAILED;
+}
+
+#endif /* HAVE_PAKCHOIS */
+
diff --git a/src/ne_pkcs11.h b/src/ne_pkcs11.h
new file mode 100644
index 0000000..e947f15
--- /dev/null
+++ b/src/ne_pkcs11.h
@@ -0,0 +1,80 @@
+/*
+ PKCS#11 support for neon
+ Copyright (C) 2008, Joe Orton <joe@manyfish.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_PKCS11_H
+#define NE_PKCS11_H 1
+
+#include "ne_defs.h"
+#include "ne_session.h"
+
+NE_BEGIN_DECLS
+
+#define NE_SSL_P11PIN_COUNT_LOW (0x01) /* an incorrect PIN has been
+ * entered. */
+#define NE_SSL_P11PIN_FINAL_TRY (0x02) /* token will become locked if
+ * entered PIN is incorrect */
+
+/* Callback for PKCS#11 PIN entry. The callback provides the PIN code
+ * to unlock the token with label 'token_label' in the slot described
+ * by 'slot_descr'.
+ *
+ * The PIN code, as a NUL-terminated ASCII string, should be copied
+ * into the 'pin' buffer (of fixed length NE_SSL_P11PINLEN), and
+ * return 0 to indicate success. Alternatively, the callback may
+ * return -1 to indicate failure (in which case, the pin buffer is
+ * ignored). When the PIN is needed for the first time, the
+ *
+ * The NE_SSL_P11PIN_COUNT_LOW and/or NE_SSL_P11PIN_FAST_TRY hints may
+ * be set in the 'flags' argument, if these hints are made available
+ * by the token. */
+typedef int (*ne_ssl_pkcs11_pin_fn)(void *userdata, int attempt,
+ const char *slot_descr,
+ const char *token_label,
+ unsigned int flags,
+ char *pin);
+#define NE_SSL_P11PINLEN (256)
+
+void ne_ssl_set_pkcs11_pin(ne_session *sess, ne_ssl_pkcs11_pin_fn fn,
+ void *userdata);
+
+/* Use a PKCS#11 provider of given name to supply a client certificate
+ * if requested. Returns NE_OK on success, NE_LOOKUP if the provider
+ * could not be loaded/initialized (in which case, the session error
+ * string is set), and NE_FAILED if PKCS#11 is not supported. */
+int ne_ssl_provide_pkcs11_clicert(ne_session *sess,
+ const char *provider);
+
+/* Use a NSS softoken pseudo-PKCS#11 provider of given name
+ * (e.g. "softokn3") to supply a client certificate if requested,
+ * using database in given directory name; the other parameters may be
+ * NULL. Returns NE_OK on success, NE_LOOKUP if the provider could
+ * not be loaded/initialized (in which case, the session error string
+ * is set), and NE_FAILED if PKCS#11 is not supported. */
+int ne_ssl_provide_nsspk11_clicert(ne_session *sess,
+ const char *provider,
+ const char *directory,
+ const char *cert_prefix,
+ const char *key_prefix,
+ const char *secmod_db);
+
+NE_END_DECLS
+
+#endif /* NE_PKCS11_H */
diff --git a/src/ne_private.h b/src/ne_private.h
index 69b4b35..810f6c4 100644
--- a/src/ne_private.h
+++ b/src/ne_private.h
@@ -28,6 +28,7 @@
#include "ne_request.h"
#include "ne_socket.h"
#include "ne_ssl.h"
+#include "ne_pkcs11.h"
struct host_info {
char *hostname;
@@ -107,6 +108,9 @@ struct ne_session_s {
ne_ssl_provide_fn ssl_provide_fn;
void *ssl_provide_ud;
+ ne_ssl_pkcs11_pin_fn ssl_pk11pin_fn;
+ void *ssl_pk11pin_ud;
+
ne_session_status_info status;
/* Error string */
diff --git a/src/ne_privssl.h b/src/ne_privssl.h
index 46c28cb..60192c0 100644
--- a/src/ne_privssl.h
+++ b/src/ne_privssl.h
@@ -79,6 +79,9 @@ struct ne_ssl_context_s {
typedef gnutls_session ne_ssl_socket;
+ne_ssl_client_cert *ne__ssl_clicert_exkey_import(const unsigned char *der,
+ size_t der_len);
+
#endif /* HAVE_GNUTLS */
ne_ssl_socket ne__sock_sslsock(ne_socket *sock);
diff --git a/src/ne_session.c b/src/ne_session.c
index b0bf31a..165916a 100644
--- a/src/ne_session.c
+++ b/src/ne_session.c
@@ -318,6 +318,13 @@ void ne_ssl_provide_clicert(ne_session *sess,
sess->ssl_provide_ud = userdata;
}
+void ne_ssl_set_pkcs11_pin(ne_session *sess,
+ ne_ssl_pkcs11_pin_fn fn, void *userdata)
+{
+ sess->ssl_pk11pin_fn = fn;
+ sess->ssl_pk11pin_ud = userdata;
+}
+
void ne_ssl_trust_cert(ne_session *sess, const ne_ssl_certificate *cert)
{
#ifdef NE_HAVE_SSL