From 978a9ebf2e6bede35c11cec2c7d17e2a8ff88bf3 Mon Sep 17 00:00:00 2001 From: joe Date: Wed, 2 Sep 2015 12:45:56 +0000 Subject: 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 --- macros/neon.m4 | 7 ++--- src/ne_gnutls.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++-------- src/ne_pkcs11.c | 18 ++++++------- src/ne_privssl.h | 12 ++++----- src/ne_socket.c | 5 ---- 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 +#ifdef HAVE_GNUTLS_PRIVKEY_IMPORT_EXT +#include +#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)); -- cgit v1.2.1