Description: fix building with GnuTLS 3.4
 Rewrite GnuTLS PKCS#11 support to work (exclusively) with the new
 GnuTLS 3.x API.
Origin: upstream, r1963
Author: Joe Orton <joe@light.plus.com>
Bug-Debian: https://bugs.debian.org/782832
Last-Update: 2015-10-03

---

--- neon27-0.30.1.orig/macros/neon.m4
+++ neon27-0.30.1/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!
--- neon27-0.30.1.orig/src/ne_gnutls.c
+++ neon27-0.30.1/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_ce
     
     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_se
     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(in
     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_impor
     }
 }
 
-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_exke
     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)
 {
--- neon27-0.30.1.orig/src/ne_pkcs11.c
+++ neon27-0.30.1/src/ne_pkcs11.c
@@ -156,6 +156,13 @@ static RSA_METHOD *pk11_rsa_method(ne_ss
 }
 #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_
             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_pkcs1
 #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_p
 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);
 }
 
--- neon27-0.30.1.orig/src/ne_privssl.h
+++ neon27-0.30.1/src/ne_privssl.h
@@ -58,6 +58,10 @@ ne__ssl_clicert_exkey_import(const unsig
 
 #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 */
 
--- neon27-0.30.1.orig/src/ne_socket.c
+++ neon27-0.30.1/src/ne_socket.c
@@ -1793,11 +1793,6 @@ int ne_sock_connect_ssl(ne_socket *sock,
     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));
