summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2015-09-02 12:45:56 +0000
committerjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2015-09-02 12:45:56 +0000
commit978a9ebf2e6bede35c11cec2c7d17e2a8ff88bf3 (patch)
treee6718707b37e2ae46220042ceae4523eb7650a3e
parentb6fe6e6968f8b5e4f64448804f3765cb5eb3107f (diff)
downloadneon-978a9ebf2e6bede35c11cec2c7d17e2a8ff88bf3.tar.gz
Rewrite GnuTLS PKCS#11 support to work (exclusively) with the new
GnuTLS 3.x API. * src/ne_privssl.h: Drop session-wide signing callback in favour of per-clicert signing callback API. * src/ne_socket.c (ne_sock_connect_ssl): Likewise. * src/ne_gnutls.c: Store a signing function in the ccert object. (dup_client_cert): Dupe the above. (provide_client_cert): Support new retrieve API and new signing callback. (ne__ssl_clicert_exkey_import): Take callback & userdata. * src/ne_pkcs11.c (pk11_sign_callback): Adjust for privkey-based signing API. (ne_ssl_set_pkcs11_provider): Drop session-global callback. * macros/neon.m4 (NEON_SSL): Check for new retrieve API, gnutls_privkey_import_ext. Enable PKCS#11 if latter present. git-svn-id: http://svn.webdav.org/repos/projects/neon/trunk@1964 61a7d7f5-40b7-0310-9c16-bb0ea8cb1845
-rw-r--r--macros/neon.m47
-rw-r--r--src/ne_gnutls.c82
-rw-r--r--src/ne_pkcs11.c18
-rw-r--r--src/ne_privssl.h12
-rw-r--r--src/ne_socket.c5
5 files changed, 90 insertions, 34 deletions
diff --git a/macros/neon.m4 b/macros/neon.m4
index 5f878ba..51b62c8 100644
--- a/macros/neon.m4
+++ b/macros/neon.m4
@@ -982,10 +982,11 @@ gnutls)
# Check for functions in later releases
NE_CHECK_FUNCS([gnutls_session_get_data2 gnutls_x509_dn_get_rdn_ava \
- gnutls_sign_callback_set \
gnutls_certificate_get_issuer \
gnutls_certificate_get_x509_cas \
- gnutls_x509_crt_sign2])
+ gnutls_x509_crt_sign2 \
+ gnutls_certificate_set_retrieve_function2 \
+ gnutls_privkey_import_ext])
# fail if gnutls_x509_crt_sign2 is not found (it was introduced in 1.2.0, which is required)
if test x${ac_cv_func_gnutls_x509_crt_sign2} != xyes; then
@@ -1039,7 +1040,7 @@ posix|yes)
;;
esac
-case ${with_pakchois}X${ac_cv_func_gnutls_sign_callback_set}Y${ne_cv_lib_ssl097} in
+case ${with_pakchois}X${ac_cv_func_gnutls_privkey_import_ext}Y${ne_cv_lib_ssl097} in
noX*Y*) ;;
*X*Yyes|*XyesY*)
# PKCS#11... ho!
diff --git a/src/ne_gnutls.c b/src/ne_gnutls.c
index 2982b2f..a074fbf 100644
--- a/src/ne_gnutls.c
+++ b/src/ne_gnutls.c
@@ -89,6 +89,13 @@ struct ne_ssl_client_cert_s {
ne_ssl_certificate cert;
gnutls_x509_privkey_t pkey;
char *friendly_name;
+#ifdef HAVE_GNUTLS_PRIVKEY_IMPORT_EXT
+ /* Signing callback & userdata provided by ne_pkcs11.c. It would
+ * be better to rewrite the whole module to use gnutls_privkey_t
+ * directly, but it seems impossible to dup such an object. */
+ gnutls_privkey_sign_func sign_func;
+ void *sign_ud;
+#endif
};
/* Returns the highest used index in subject (or issuer) DN of
@@ -525,6 +532,10 @@ static ne_ssl_client_cert *dup_client_cert(const ne_ssl_client_cert *cc)
if (cc->keyless) {
newcc->keyless = 1;
+#ifdef HAVE_GNUTLS_PRIVKEY_IMPORT_EXT
+ newcc->sign_func = cc->sign_func;
+ newcc->sign_ud = cc->sign_ud;
+#endif
}
else {
ret = gnutls_x509_privkey_init(&newcc->pkey);
@@ -553,7 +564,15 @@ dup_error:
static int provide_client_cert(gnutls_session_t session,
const gnutls_datum_t *req_ca_rdn, int nreqs,
const gnutls_pk_algorithm_t *sign_algos,
- int sign_algos_length, gnutls_retr_st *st)
+ int sign_algos_length,
+#ifdef HAVE_GNUTLS_CERTIFICATE_SET_RETRIEVE_FUNCTION2
+ gnutls_pcert_st **pcert,
+ unsigned int *pcert_length,
+ gnutls_privkey_t *pkey
+#else
+ gnutls_retr2_st *st
+#endif
+ )
{
ne_session *sess = gnutls_session_get_ptr(session);
@@ -611,27 +630,59 @@ static int provide_client_cert(gnutls_session_t session,
if (sess->client_cert) {
gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
if (type == GNUTLS_CRT_X509
-#if LIBGNUTLS_VERSION_NUMBER > 0x030000
- /* Ugly hack; prevent segfaults w/GnuTLS 3.0. */
- && sess->client_cert->pkey != NULL
+ && (sess->client_cert->pkey || sess->client_cert->keyless)) {
+ int ret;
+
+#ifdef HAVE_GNUTLS_CERTIFICATE_SET_RETRIEVE_FUNCTION2
+ *pkey = gnutls_malloc(sizeof *pkey);
+ gnutls_privkey_init(pkey);
+
+#ifdef HAVE_GNUTLS_PRIVKEY_IMPORT_EXT
+ if (sess->client_cert->sign_func) {
+ int algo = gnutls_x509_crt_get_pk_algorithm(sess->client_cert->cert.subject, NULL);
+ NE_DEBUG(NE_DBG_SSL, "ssl: Signing for %s.\n", gnutls_pk_algorithm_get_name(algo));
+
+ ret = gnutls_privkey_import_ext(*pkey, algo, sess->client_cert->sign_ud,
+ sess->client_cert->sign_func, NULL, 0);
+ }
+ else
#endif
- ) {
- NE_DEBUG(NE_DBG_SSL, "Supplying client certificate.\n");
+ if (sess->client_cert->keyless) {
+ ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+ }
+ else {
+ ret = gnutls_privkey_import_x509(*pkey, sess->client_cert->pkey, 0);
+ }
- st->type = type;
+ if (ret) {
+ NE_DEBUG(NE_DBG_SSL, "ssl: Failed to import private key: %s.\n", gnutls_strerror(ret));
+ ne_set_error(sess, _("Failed to import private key: %s"), gnutls_strerror(ret));
+ return ret;
+ }
+
+ *pcert = gnutls_malloc(sizeof *pcert);
+ gnutls_pcert_import_x509(*pcert, sess->client_cert->cert.subject, 0);
+ *pcert_length = 1;
+#else /* !HAVE_GNUTLS_CERTIFICATE_SET_RETRIEVE_FUNCTION2 */
+ st->cert_type = type;
st->ncerts = 1;
st->cert.x509 = &sess->client_cert->cert.subject;
st->key.x509 = sess->client_cert->pkey;
/* tell GNU TLS not to deallocate the certs. */
st->deinit_all = 0;
+#endif
} else {
return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
}
}
else {
- NE_DEBUG(NE_DBG_SSL, "No client certificate supplied.\n");
+ NE_DEBUG(NE_DBG_SSL, "ssl: No client certificate supplied.\n");
+#ifdef HAVE_GNUTLS_CERTIFICATE_SET_RETRIEVE_FUNCTION2
+ *pcert_length = 0;
+#else
st->ncerts = 0;
+#endif
sess->ssl_cc_requested = 1;
return 0;
}
@@ -649,8 +700,12 @@ ne_ssl_context *ne_ssl_context_create(int flags)
ne_ssl_context *ctx = ne_calloc(sizeof *ctx);
gnutls_certificate_allocate_credentials(&ctx->cred);
if (flags == NE_SSL_CTX_CLIENT) {
+#ifdef HAVE_GNUTLS_CERTIFICATE_SET_RETRIEVE_FUNCTION2
+ gnutls_certificate_set_retrieve_function2(ctx->cred, provide_client_cert);
+#else
gnutls_certificate_client_set_retrieve_function(ctx->cred,
provide_client_cert);
+#endif
}
gnutls_certificate_set_verify_flags(ctx->cred,
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
@@ -1206,8 +1261,10 @@ ne_ssl_client_cert *ne_ssl_clicert_import(const unsigned char *buffer, size_t bu
}
}
-ne_ssl_client_cert *ne__ssl_clicert_exkey_import(const unsigned char *der,
- size_t der_len)
+#ifdef HAVE_GNUTLS_PRIVKEY_IMPORT_EXT
+ne_ssl_client_cert *ne__ssl_clicert_exkey_import(const unsigned char *der, size_t der_len,
+ gnutls_privkey_sign_func sign_func,
+ void *userdata)
{
ne_ssl_client_cert *cc;
gnutls_x509_crt_t x5;
@@ -1226,9 +1283,12 @@ ne_ssl_client_cert *ne__ssl_clicert_exkey_import(const unsigned char *der,
cc->keyless = 1;
cc->decrypted = 1;
populate_cert(&cc->cert, x5);
+ cc->sign_func = sign_func;
+ cc->sign_ud = userdata;
- return cc;
+ return cc;
}
+#endif
int ne_ssl_clicert_encrypted(const ne_ssl_client_cert *cc)
{
diff --git a/src/ne_pkcs11.c b/src/ne_pkcs11.c
index e2e1791..daec1ce 100644
--- a/src/ne_pkcs11.c
+++ b/src/ne_pkcs11.c
@@ -156,6 +156,13 @@ static RSA_METHOD *pk11_rsa_method(ne_ssl_pkcs11_provider *prov)
}
#endif
+#ifdef HAVE_GNUTLS
+static int pk11_sign_callback(gnutls_privkey_t pkey,
+ void *userdata,
+ const gnutls_datum_t *raw_data,
+ gnutls_datum_t *signature);
+#endif
+
static int pk11_find_x509(ne_ssl_pkcs11_provider *prov,
pakchois_session_t *pks,
unsigned char *certid, unsigned long *cid_len)
@@ -203,7 +210,7 @@ static int pk11_find_x509(ne_ssl_pkcs11_provider *prov,
ne_ssl_client_cert *cc;
#ifdef HAVE_GNUTLS
- cc = ne__ssl_clicert_exkey_import(value, a[0].value_len);
+ cc = ne__ssl_clicert_exkey_import(value, a[0].value_len, pk11_sign_callback, prov);
#else
cc = ne__ssl_clicert_exkey_import(value, a[0].value_len, pk11_rsa_method(prov));
#endif
@@ -298,10 +305,8 @@ static int find_client_cert(ne_ssl_pkcs11_provider *prov,
#ifdef HAVE_GNUTLS
/* 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,
+static int pk11_sign_callback(gnutls_privkey_t pkey,
void *userdata,
- gnutls_certificate_type_t cert_type,
- const gnutls_datum_t *cert,
const gnutls_datum_t *hash,
gnutls_datum_t *signature)
{
@@ -571,11 +576,6 @@ void ne_ssl_pkcs11_provider_pin(ne_ssl_pkcs11_provider *provider,
void ne_ssl_set_pkcs11_provider(ne_session *sess,
ne_ssl_pkcs11_provider *provider)
{
-#ifdef HAVE_GNUTLS
- sess->ssl_context->sign_func = pk11_sign_callback;
- sess->ssl_context->sign_data = provider;
-#endif
-
ne_ssl_provide_clicert(sess, pk11_provide, provider);
}
diff --git a/src/ne_privssl.h b/src/ne_privssl.h
index 57d53c8..8597805 100644
--- a/src/ne_privssl.h
+++ b/src/ne_privssl.h
@@ -58,6 +58,10 @@ ne__ssl_clicert_exkey_import(const unsigned char *der,
#include <gnutls/gnutls.h>
+#ifdef HAVE_GNUTLS_PRIVKEY_IMPORT_EXT
+#include <gnutls/abstract.h>
+#endif
+
struct ne_ssl_context_s {
gnutls_certificate_credentials_t cred;
int verify; /* non-zero if client cert verification required */
@@ -78,17 +82,13 @@ struct ne_ssl_context_s {
} client;
#endif
} cache;
-
-#ifdef HAVE_GNUTLS_SIGN_CALLBACK_SET
- gnutls_sign_func sign_func;
- void *sign_data;
-#endif
};
typedef gnutls_session_t ne_ssl_socket;
NE_PRIVATE ne_ssl_client_cert *
-ne__ssl_clicert_exkey_import(const unsigned char *der, size_t der_len);
+ne__ssl_clicert_exkey_import(const unsigned char *der, size_t der_len,
+ gnutls_privkey_sign_func sign_func, void *userdata);
#endif /* HAVE_GNUTLS */
diff --git a/src/ne_socket.c b/src/ne_socket.c
index 370448d..5e78d70 100644
--- a/src/ne_socket.c
+++ b/src/ne_socket.c
@@ -1793,11 +1793,6 @@ int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx, void *userdata)
gnutls_session_set_ptr(sock->ssl, userdata);
gnutls_credentials_set(sock->ssl, GNUTLS_CRD_CERTIFICATE, ctx->cred);
-#ifdef HAVE_GNUTLS_SIGN_CALLBACK_SET
- if (ctx->sign_func)
- gnutls_sign_callback_set(sock->ssl, ctx->sign_func, ctx->sign_data);
-#endif
-
if (ctx->hostname) {
gnutls_server_name_set(sock->ssl, GNUTLS_NAME_DNS, ctx->hostname,
strlen(ctx->hostname));