[glib-networking/mcatanzaro/tls-thread: 13/26] progress



commit b18b5f107968ffe93b5a4ccd0c10f90d8a1a9baf
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Sun Dec 22 10:29:13 2019 -0600

    progress

 tls/base/gtlsconnection-base.c           |  25 +--
 tls/base/gtlsconnection-base.h           |   2 -
 tls/base/gtlsoperationsthread-base.c     |  39 +++++
 tls/base/gtlsoperationsthread-base.h     |   4 +
 tls/gnutls/gtlsclientconnection-gnutls.c | 113 +------------
 tls/gnutls/gtlsconnection-gnutls.c       |  54 +-----
 tls/gnutls/gtlsconnection-gnutls.h       |   7 -
 tls/gnutls/gtlsoperationsthread-gnutls.c | 271 ++++++++++++++++++++++---------
 tls/gnutls/gtlsserverconnection-gnutls.c |  53 ------
 9 files changed, 244 insertions(+), 324 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 3be3a85..f2fe850 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -248,6 +248,8 @@ g_tls_connection_base_initable_init (GInitable    *initable,
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
 
   priv->thread = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->create_op_thread (tls);
+  if (priv->certificate)
+    g_tls_operations_thread_base_set_own_certificate (priv->certificate);
 
   return TRUE;
 }
@@ -453,6 +455,9 @@ g_tls_connection_base_set_property (GObject      *object,
       if (priv->certificate)
         g_object_unref (priv->certificate);
       priv->certificate = g_value_dup_object (value);
+
+      if (priv->thread)
+        g_tls_operations_thread_base_set_own_certificate (priv->certificate);
       break;
 
     case PROP_INTERACTION:
@@ -2445,26 +2450,6 @@ g_tls_connection_base_get_base_iostream (GTlsConnectionBase *tls)
   return priv->base_io_stream;
 }
 
-GPollableInputStream *
-g_tls_connection_base_get_base_istream (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  g_assert (!g_tls_connection_base_is_dtls (tls));
-
-  return priv->base_istream;
-}
-
-GPollableOutputStream *
-g_tls_connection_base_get_base_ostream (GTlsConnectionBase *tls)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  g_assert (!g_tls_connection_base_is_dtls (tls));
-
-  return priv->base_ostream;
-}
-
 void
 g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (GTlsConnectionBase *tls)
 {
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 65058d0..ea764c6 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -119,8 +119,6 @@ gboolean                  g_tls_connection_base_is_dtls                 (GTlsCon
 GDatagramBased           *g_tls_connection_base_get_base_socket         (GTlsConnectionBase *tls);
 
 GIOStream                *g_tls_connection_base_get_base_iostream       (GTlsConnectionBase *tls);
-GPollableInputStream     *g_tls_connection_base_get_base_istream        (GTlsConnectionBase *tls);
-GPollableOutputStream    *g_tls_connection_base_get_base_ostream        (GTlsConnectionBase *tls);
 
 void                      g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate
                                                                         (GTlsConnectionBase *tls);
diff --git a/tls/base/gtlsoperationsthread-base.c b/tls/base/gtlsoperationsthread-base.c
index 32959e7..44975af 100644
--- a/tls/base/gtlsoperationsthread-base.c
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -75,6 +75,10 @@ typedef struct {
   GMainContext *op_thread_context;
 
   GAsyncQueue *queue;
+
+  /* This mutex guards everything below. */
+  GMutex mutex;
+  gchar *own_certificate_pem;
 } GTlsOperationsThreadBasePrivate;
 
 typedef enum {
@@ -408,6 +412,33 @@ execute_op (GTlsOperationsThreadBase *self,
   return result;
 }
 
+void
+g_tls_operations_thread_base_set_own_certificate (GTlsOperationsThreadBase *self,
+                                                  GTlsCertificate          *cert)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+  g_mutex_lock (&priv->mutex);
+  g_clear_pointer (&priv->own_certificate_pem, g_free);
+  g_object_get (cert,
+                "certificate-pem", &priv->own_certificate_pem,
+                NULL);
+  g_mutex_unlock (&priv->mutex);
+}
+
+gchar *
+g_tls_operations_thread_base_get_own_certificate_pem (GTlsOperationsThreadBase *self)
+{
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  gchar *copy;
+
+  g_mutex_lock (&priv->mutex);
+  copy = g_strdup (priv->own_certificate_pem);
+  g_mutex_unlock (&priv->mutex);
+
+  return copy;
+}
+
 void
 g_tls_operations_thread_base_copy_client_session_state (GTlsOperationsThreadBase *self,
                                                         GTlsOperationsThreadBase *source)
@@ -437,6 +468,7 @@ g_tls_operations_thread_base_set_server_identity (GTlsOperationsThreadBase *self
 GTlsConnectionBaseStatus
 g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase  *self,
                                         const gchar              **advertised_protocols,
+                                        GTlsAuthenticationMode     auth_mode,
                                         gint64                     timeout,
                                         GCancellable              *cancellable,
                                         GError                   **error)
@@ -447,6 +479,7 @@ g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase  *self,
   op = g_tls_thread_handshake_operation_new (self,
                                              priv->connection,
                                              advertised_protocols,
+                                             auth_mode,
                                              timeout,
                                              cancellable);
   return execute_op (self, g_steal_pointer (&op), NULL, error);
@@ -1052,6 +1085,8 @@ g_tls_operations_thread_base_init (GTlsOperationsThreadBase *self)
   priv->op_thread = g_thread_new ("[glib-networking] GTlsOperationsThreadBase TLS operations thread",
                                   tls_op_thread,
                                   self);
+
+  g_mutex_init (&priv->mutex);
 }
 
 static void
@@ -1072,6 +1107,10 @@ g_tls_operations_thread_base_finalize (GObject *object)
 
   g_clear_weak_pointer (&priv->connection);
 
+  g_mutex_clear (&priv->mutex);
+
+  g_clear_pointer (&priv->own_certificate);
+
   G_OBJECT_CLASS (g_tls_operations_thread_base_parent_class)->finalize (object);
 }
 
diff --git a/tls/base/gtlsoperationsthread-base.h b/tls/base/gtlsoperationsthread-base.h
index 039f3b5..9e61055 100644
--- a/tls/base/gtlsoperationsthread-base.h
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -95,6 +95,10 @@ struct _GTlsOperationsThreadBaseClass
 /* FIXME: remove!!! */
 GTlsConnectionBase       *g_tls_operations_thread_base_get_connection            (GTlsOperationsThreadBase  
*self);
 
+void                      g_tls_operations_thread_base_set_own_certificate       (GTlsOperationsThreadBase  
*self,
+                                                                                  GTlsCertificate           
*cert);
+gchar                    *g_tls_operations_thread_base_get_own_certificate_pem   (GTlsOperationsThreadBase  
*self);
+
 void                      g_tls_operations_thread_base_copy_client_session_state (GTlsOperationsThreadBase  
*self,
                                                                                   GTlsOperationsThreadBase  
*source);
 
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 4a8c549..0a8a7f0 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -55,11 +55,6 @@ struct _GTlsClientConnectionGnutls
   gboolean use_ssl3;
 
   GPtrArray *accepted_cas;
-  gboolean accepted_cas_changed;
-
-  gnutls_pcert_st *pcert;
-  unsigned int pcert_length;
-  gnutls_privkey_t pkey;
 };
 
 static void g_tls_client_connection_gnutls_initable_interface_init (GInitableIface  *iface);
@@ -67,15 +62,6 @@ static void g_tls_client_connection_gnutls_initable_interface_init (GInitableIfa
 static void g_tls_client_connection_gnutls_client_connection_interface_init (GTlsClientConnectionInterface 
*iface);
 static void g_tls_client_connection_gnutls_dtls_client_connection_interface_init 
(GDtlsClientConnectionInterface *iface);
 
-static int g_tls_client_connection_gnutls_handshake_thread_retrieve_function (gnutls_session_t              
session,
-                                                                              const gnutls_datum_t         
*req_ca_rdn,
-                                                                              int                           
nreqs,
-                                                                              const gnutls_pk_algorithm_t  
*pk_algos,
-                                                                              int                           
pk_algos_length,
-                                                                              gnutls_pcert_st             
**pcert,
-                                                                              unsigned int                 
*pcert_length,
-                                                                              gnutls_privkey_t             
*pkey);
-
 static GInitableIface *g_tls_client_connection_gnutls_parent_initable_iface;
 
 G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionGnutls, g_tls_client_connection_gnutls, 
G_TYPE_TLS_CONNECTION_GNUTLS,
@@ -86,16 +72,6 @@ G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionGnutls, g_tls_client_connection_gnu
                          G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_CLIENT_CONNECTION,
                                                 
g_tls_client_connection_gnutls_dtls_client_connection_interface_init));
 
-static void
-clear_gnutls_certificate_copy (GTlsClientConnectionGnutls *gnutls)
-{
-  g_tls_certificate_gnutls_copy_free (gnutls->pcert, gnutls->pcert_length, gnutls->pkey);
-
-  gnutls->pcert = NULL;
-  gnutls->pcert_length = 0;
-  gnutls->pkey = NULL;
-}
-
 static void
 g_tls_client_connection_gnutls_init (GTlsClientConnectionGnutls *gnutls)
 {
@@ -120,8 +96,6 @@ g_tls_client_connection_gnutls_finalize (GObject *object)
   g_clear_object (&gnutls->server_identity);
   g_clear_pointer (&gnutls->accepted_cas, g_ptr_array_unref);
 
-  clear_gnutls_certificate_copy (gnutls);
-
   G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->finalize (object);
 }
 
@@ -133,14 +107,10 @@ g_tls_client_connection_gnutls_initable_init (GInitable       *initable,
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
   GTlsOperationsThreadBase *thread = g_tls_connection_base_get_op_thread (G_TLS_CONNECTION_BASE (gnutls));
   const gchar *hostname;
-  gnutls_certificate_credentials_t creds;
 
   if (!g_tls_client_connection_gnutls_parent_initable_iface->init (initable, cancellable, error))
     return FALSE;
 
-  creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
-  gnutls_certificate_set_retrieve_function2 (creds, 
g_tls_client_connection_gnutls_handshake_thread_retrieve_function);
-
   hostname = get_server_identity (G_TLS_CLIENT_CONNECTION_GNUTLS (gnutls));
   if (hostname)
     g_tls_operations_thread_base_set_server_identity (thread, hostname);
@@ -178,8 +148,7 @@ g_tls_client_connection_gnutls_get_property (GObject    *object,
         {
           for (i = 0; i < gnutls->accepted_cas->len; ++i)
             {
-              accepted_cas = g_list_prepend (accepted_cas, g_byte_array_ref (
-                                             gnutls->accepted_cas->pdata[i]));
+              accepted_cas = g_list_prepend (accepted_cas, g_byte_array_ref 
(gnutls->accepted_cas->pdata[i]));
             }
           accepted_cas = g_list_reverse (accepted_cas);
         }
@@ -230,86 +199,6 @@ g_tls_client_connection_gnutls_set_property (GObject      *object,
     }
 }
 
-static int
-g_tls_client_connection_gnutls_handshake_thread_retrieve_function (gnutls_session_t              session,
-                                                                   const gnutls_datum_t         *req_ca_rdn,
-                                                                   int                           nreqs,
-                                                                   const gnutls_pk_algorithm_t  *pk_algos,
-                                                                   int                           
pk_algos_length,
-                                                                   gnutls_pcert_st             **pcert,
-                                                                   unsigned int                 
*pcert_length,
-                                                                   gnutls_privkey_t             *pkey)
-{
-  GTlsConnectionBase *tls = gnutls_transport_get_ptr (session);
-  GTlsClientConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
-  GTlsConnectionGnutls *conn = G_TLS_CONNECTION_GNUTLS (gnutls);
-  GPtrArray *accepted_cas;
-  gboolean had_accepted_cas;
-  GByteArray *dn;
-  int i;
-
-  /* FIXME: Here we are supposed to ensure that the certificate supports one of
-   * the algorithms given in pk_algos.
-   */
-
-  had_accepted_cas = gnutls->accepted_cas != NULL;
-
-  accepted_cas = g_ptr_array_new_with_free_func ((GDestroyNotify)g_byte_array_unref);
-  for (i = 0; i < nreqs; i++)
-    {
-      dn = g_byte_array_new ();
-      g_byte_array_append (dn, req_ca_rdn[i].data, req_ca_rdn[i].size);
-      g_ptr_array_add (accepted_cas, dn);
-    }
-
-  if (gnutls->accepted_cas)
-    g_ptr_array_unref (gnutls->accepted_cas);
-  gnutls->accepted_cas = accepted_cas;
-
-  gnutls->accepted_cas_changed = gnutls->accepted_cas || had_accepted_cas;
-
-  clear_gnutls_certificate_copy (gnutls);
-  g_tls_connection_gnutls_handshake_thread_get_certificate (conn, pcert, pcert_length, pkey);
-
-  if (*pcert_length == 0)
-    {
-      g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
-
-      if (g_tls_connection_base_handshake_thread_request_certificate (tls))
-        g_tls_connection_gnutls_handshake_thread_get_certificate (conn, pcert, pcert_length, pkey);
-
-      if (*pcert_length == 0)
-        {
-          g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
-
-          /* If there is still no client certificate, this connection will
-           * probably fail, but we must not give up yet. The certificate might
-           * be optional, e.g. if the server is using
-           * G_TLS_AUTHENTICATION_REQUESTED, not G_TLS_AUTHENTICATION_REQUIRED.
-           */
-          g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (tls);
-          return 0;
-        }
-    }
-
-  if (!*pkey)
-    {
-      g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
-
-      /* No private key. GnuTLS expects it to be non-null if pcert_length is
-       * nonzero, so we have to abort now.
-       */
-      g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (tls);
-      return -1;
-    }
-
-  gnutls->pcert = *pcert;
-  gnutls->pcert_length = *pcert_length;
-  gnutls->pkey = *pkey;
-
-  return 0;
-}
-
 static void
 g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
                                                    gchar              **negotiated_protocol,
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 1c49fd1..3be6fed 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -58,8 +58,6 @@ static GInitableIface *g_tls_connection_gnutls_parent_initable_iface;
 
 static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
 
-static int verify_certificate_cb (gnutls_session_t session);
-
 static gnutls_priority_t priority;
 
 typedef struct
@@ -89,19 +87,6 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
   priv->cancellable = g_cancellable_new ();
 }
 
-static void
-g_tls_connection_gnutls_set_handshake_priority (GTlsConnectionGnutls *gnutls)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  int ret;
-
-  g_assert (priority);
-
-  ret = gnutls_priority_set (priv->session, priority);
-  if (ret != GNUTLS_E_SUCCESS)
-    g_warning ("Failed to set GnuTLS session priority: %s", gnutls_strerror (ret));
-}
-
 static gboolean
 g_tls_connection_gnutls_initable_init (GInitable     *initable,
                                        GCancellable  *cancellable,
@@ -215,6 +200,7 @@ g_tls_connection_gnutls_handshake_thread_get_certificate (GTlsConnectionGnutls
   if (cert)
     {
       /* Send along a pre-initialized privkey so we can handle the callback here. */
+      /* FIXME: this was never safe, we're not on the right thread here */
       gnutls_privkey_t privkey;
       gnutls_privkey_init (&privkey);
       gnutls_privkey_set_pin_function (privkey, on_pin_request, gnutls);
@@ -239,7 +225,7 @@ g_tls_connection_gnutls_create_op_thread (GTlsConnectionBase *tls)
   GDatagramBased *base_socket = NULL;
   gboolean client = G_IS_TLS_CLIENT_CONNECTION (tls);
   guint flags = client ? GNUTLS_CLIENT : GNUTLS_SERVER;
-  GTlsOperationsThreadGnutls *thread;
+  GTlsOperationsThreadBase *thread;
 
   g_object_get (tls,
                 "base-io-stream", &base_io_stream,
@@ -286,16 +272,6 @@ g_tls_connection_gnutls_retrieve_peer_certificate (GTlsConnectionBase *tls)
   return G_TLS_CERTIFICATE (chain);
 }
 
-static int
-verify_certificate_cb (gnutls_session_t session)
-{
-  GTlsConnectionBase *tls = gnutls_session_get_ptr (session);
-
-  /* Return 0 for the handshake to continue, non-zero to terminate.
-   * Complete opposite of what OpenSSL does. */
-  return !g_tls_connection_base_handshake_thread_verify_certificate (tls);
-}
-
 static void
 g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
                                             gchar              **negotiated_protocol,
@@ -321,29 +297,6 @@ g_tls_connection_gnutls_is_session_resumed (GTlsConnectionBase *tls)
   return gnutls_session_is_resumed (priv->session);
 }
 
-static void
-initialize_gnutls_priority (void)
-{
-  const gchar *priority_override;
-  const gchar *error_pos = NULL;
-  int ret;
-
-  g_assert (!priority);
-
-  priority_override = g_getenv ("G_TLS_GNUTLS_PRIORITY");
-  if (priority_override)
-    {
-      ret = gnutls_priority_init2 (&priority, priority_override, &error_pos, 0);
-      if (ret != GNUTLS_E_SUCCESS)
-        g_warning ("Failed to set GnuTLS session priority with beginning at %s: %s", error_pos, 
gnutls_strerror (ret));
-      return;
-    }
-
-  ret = gnutls_priority_init2 (&priority, "%COMPAT:-VERS-TLS1.1:-VERS-TLS1.0", &error_pos, 
GNUTLS_PRIORITY_INIT_DEF_APPEND);
-  if (ret != GNUTLS_E_SUCCESS)
-    g_warning ("Failed to set GnuTLS session priority with error beginning at %s: %s", error_pos, 
gnutls_strerror (ret));
-}
-
 static void
 g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
 {
@@ -353,12 +306,9 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
   gobject_class->finalize                                = g_tls_connection_gnutls_finalize;
 
   base_class->create_op_thread                           = g_tls_connection_gnutls_create_op_thread;
-  base_class->handshake_thread_safe_renegotiation_status = 
g_tls_connection_gnutls_handshake_thread_safe_renegotiation_status;
   base_class->retrieve_peer_certificate                  = g_tls_connection_gnutls_retrieve_peer_certificate;
   base_class->complete_handshake                         = g_tls_connection_gnutls_complete_handshake;
   base_class->is_session_resumed                         = g_tls_connection_gnutls_is_session_resumed;
-
-  initialize_gnutls_priority ();
 }
 
 static void
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index edb617f..d7747d9 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -41,11 +41,4 @@ struct _GTlsConnectionGnutlsClass
   GTlsConnectionBaseClass parent_class;
 };
 
-gnutls_certificate_credentials_t g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
-
-void     g_tls_connection_gnutls_handshake_thread_get_certificate     (GTlsConnectionGnutls  *gnutls,
-                                                                       gnutls_pcert_st      **pcert,
-                                                                       unsigned int          *pcert_length,
-                                                                       gnutls_privkey_t      *pkey);
-
 G_END_DECLS
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
index 9d25cd9..6688d58 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.c
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -29,6 +29,7 @@
 #include "gtlsoperationsthread-gnutls.h"
 
 #include "gtlsbackend-gnutls.h"
+#include "gtlscertificate-gnutls.h"
 #include "gtlsconnection-gnutls.h"
 
 #include <errno.h>
@@ -65,6 +66,13 @@ struct _GTlsOperationsThreadGnutls {
 
   GCancellable            *op_cancellable;
   GError                  *op_error;
+
+  gnutls_pcert_st         *pcert;
+  unsigned int             pcert_length;
+  gnutls_privkey_t         pkey;
+
+  GPtrArray               *accepted_cas;
+  gboolean                 accepted_cas_changed;
 };
 
 enum
@@ -287,84 +295,6 @@ initialize_gnutls_priority (void)
     g_warning ("Failed to set GnuTLS session priority with error beginning at %s: %s", error_pos, 
gnutls_strerror (ret));
 }
 
-static void
-compute_session_id (GTlsOperationsThreadGnutls *self)
-{
-  GSocketAddress *remote_addr;
-  GInetAddress *iaddr;
-  guint port;
-
-  /* The testsuite expects handshakes to actually happen. E.g. a test might
-   * check to see that a handshake succeeds and then later check that a new
-   * handshake fails. If we get really unlucky and the same port number is
-   * reused for the server socket between connections, then we'll accidentally
-   * resume the old session and skip certificate verification. Such failures
-   * are difficult to debug because they require running the tests hundreds of
-   * times simultaneously to reproduce (the port number does not get reused
-   * quickly enough if the tests are run sequentially).
-   *
-   * So session resumption will just need to be tested manually.
-   */
-  if (g_test_initialized ())
-    return;
-
-  /* Create a TLS "session ID." We base it on the IP address since
-   * different hosts serving the same hostname/service will probably
-   * not share the same session cache. We base it on the
-   * server-identity because at least some servers will fail (rather
-   * than just failing to resume the session) if we don't.
-   * (https://bugs.launchpad.net/bugs/823325)
-   *
-   * Note that our session IDs have no relation to TLS protocol
-   * session IDs, e.g. as provided by gnutls_session_get_id2(). Unlike
-   * our session IDs, actual TLS session IDs can no longer be used for
-   * session resumption.
-   */
-  if (G_IS_SOCKET_CONNECTION (self->base_iostream))
-    {
-      remote_addr = g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (self->base_iostream), NULL);
-      if (G_IS_INET_SOCKET_ADDRESS (remote_addr))
-        {
-          GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
-          const gchar *server_hostname;
-          gchar *addrstr, *session_id;
-          GTlsCertificate *cert = NULL;
-          gchar *cert_hash = NULL;
-
-          iaddr = g_inet_socket_address_get_address (isaddr);
-          port = g_inet_socket_address_get_port (isaddr);
-
-          addrstr = g_inet_address_to_string (iaddr);
-          server_hostname = self->server_identity;
-
-          /* If we have a certificate, make its hash part of the session ID, so
-           * that different connections to the same server can use different
-           * certificates.
-           */
-          g_object_get (G_OBJECT (gnutls), "certificate", &cert, NULL);
-          if (cert)
-            {
-              GByteArray *der = NULL;
-              g_object_get (G_OBJECT (cert), "certificate", &der, NULL);
-              if (der)
-                {
-                  cert_hash = g_compute_checksum_for_data (G_CHECKSUM_SHA256, der->data, der->len);
-                  g_byte_array_unref (der);
-                }
-              g_object_unref (cert);
-            }
-          session_id = g_strdup_printf ("%s/%s/%d/%s", addrstr,
-                                        server_hostname ? server_hostname : "",
-                                        port,
-                                        cert_hash ? cert_hash : "");
-          self->session_id = g_bytes_new_take (session_id, strlen (session_id));
-          g_free (addrstr);
-          g_free (cert_hash);
-        }
-      g_object_unref (remote_addr);
-    }
-}
-
 static void
 g_tls_operations_thread_gnutls_copy_client_session_state (GTlsOperationsThreadBase *base,
                                                           GTlsOperationsThreadBase *base_source)
@@ -372,6 +302,8 @@ g_tls_operations_thread_gnutls_copy_client_session_state (GTlsOperationsThreadBa
   GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
   GTlsOperationsThreadGnutls *source = G_TLS_OPERATIONS_THREAD_GNUTLS (base_source);
 
+  g_assert (is_client (self));
+
   /* Precondition: source has handshaked, conn has not. */
   g_return_if_fail (!self->session_id);
   g_return_if_fail (source->session_id);
@@ -465,9 +397,87 @@ set_advertised_protocols (GTlsOperationsThreadGnutls  *self,
   g_free (protocols);
 }
 
+static void
+compute_session_id (GTlsOperationsThreadGnutls *self)
+{
+  GSocketAddress *remote_addr;
+  GInetAddress *iaddr;
+  guint port;
+
+  g_assert (is_client (self));
+
+  /* The testsuite expects handshakes to actually happen. E.g. a test might
+   * check to see that a handshake succeeds and then later check that a new
+   * handshake fails. If we get really unlucky and the same port number is
+   * reused for the server socket between connections, then we'll accidentally
+   * resume the old session and skip certificate verification. Such failures
+   * are difficult to debug because they require running the tests hundreds of
+   * times simultaneously to reproduce (the port number does not get reused
+   * quickly enough if the tests are run sequentially).
+   *
+   * So session resumption will just need to be tested manually.
+   */
+  if (g_test_initialized ())
+    return;
+
+  /* Create a TLS "session ID." We base it on the IP address since
+   * different hosts serving the same hostname/service will probably
+   * not share the same session cache. We base it on the
+   * server-identity because at least some servers will fail (rather
+   * than just failing to resume the session) if we don't.
+   * (https://bugs.launchpad.net/bugs/823325)
+   *
+   * Note that our session IDs have no relation to TLS protocol
+   * session IDs, e.g. as provided by gnutls_session_get_id2(). Unlike
+   * our session IDs, actual TLS session IDs can no longer be used for
+   * session resumption.
+   */
+  if (G_IS_SOCKET_CONNECTION (self->base_iostream))
+    {
+      remote_addr = g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (self->base_iostream), NULL);
+      if (G_IS_INET_SOCKET_ADDRESS (remote_addr))
+        {
+          GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
+          const gchar *server_hostname;
+          gchar *addrstr;
+          gchar *session_id;
+          gchar *pem;
+          gchar *cert_hash = NULL;
+
+          iaddr = g_inet_socket_address_get_address (isaddr);
+          port = g_inet_socket_address_get_port (isaddr);
+
+          addrstr = g_inet_address_to_string (iaddr);
+          server_hostname = self->server_identity;
+
+          /* If we have a certificate, make its hash part of the session ID, so
+           * that different connections to the same server can use different
+           * certificates.
+           */
+          pem = g_tls_operations_thread_base_get_own_certificate_pem (G_TLS_OPERATIONS_THREAD_BASE (self));
+          if (pem)
+            {
+              cert_hash = g_compute_checksum_for_string (G_CHECKSUM_SHA256, pem, -1);
+              g_free (pem);
+            }
+
+          session_id = g_strdup_printf ("%s/%s/%d/%s", addrstr,
+                                        server_hostname ? server_hostname : "",
+                                        port,
+                                        cert_hash ? cert_hash : "");
+          self->session_id = g_bytes_new_take (session_id, strlen (session_id));
+          g_free (addrstr);
+          g_free (cert_hash);
+        }
+      g_object_unref (remote_addr);
+    }
+}
+
 static void
 set_session_data (GTlsOperationsThreadGnutls *self)
 {
+  g_assert (is_client (self));
+
   compute_session_id (self);
 
   if (self->session_data_override)
@@ -1024,6 +1034,107 @@ g_tls_operations_thread_gnutls_pull_timeout_func (gnutls_transport_ptr_t transpo
   return 0;
 }
 
+static int
+verify_certificate_cb (gnutls_session_t session)
+{
+  GTlsOperationsThreadGnutls *self = gnutls_session_get_ptr (session);
+
+  /* Return 0 for the handshake to continue, non-zero to terminate.
+   * Complete opposite of what OpenSSL does.
+   */
+  return !g_tls_connection_base_handshake_thread_verify_certificate (tls);
+}
+
+static void
+clear_gnutls_certificate_copy (GTlsOperationsThreadGnutls *self)
+{
+  g_tls_certificate_gnutls_copy_free (self->pcert, self->pcert_length, self->pkey);
+
+  self->pcert = NULL;
+  self->pcert_length = 0;
+  self->pkey = NULL;
+}
+
+static int
+retrieve_certificate_cb (gnutls_session_t              session,
+                         const gnutls_datum_t         *req_ca_rdn,
+                         int                           nreqs,
+                         const gnutls_pk_algorithm_t  *pk_algos,
+                         int                           pk_algos_length,
+                         gnutls_pcert_st             **pcert,
+                         unsigned int                 *pcert_length,
+                         gnutls_privkey_t             *pkey)
+{
+  GTlsOperationsThreadGnutls *self = gnutls_transport_get_ptr (session);
+  gboolean had_accepted_cas;
+  GByteArray *dn;
+  int i;
+
+  /* FIXME: Here we are supposed to ensure that the certificate supports one of
+   * the algorithms given in pk_algos.
+   */
+
+  if (is_client (self))
+    {
+      had_accepted_cas = self->accepted_cas && self->accepted_cas->len != 0;
+
+      g_clear_pointer (&self->accepted_cas, g_ptr_array_unref);
+      self->accepted_cas = g_ptr_array_new_with_free_func ((GDestroyNotify)g_byte_array_unref);
+      for (i = 0; i < nreqs; i++)
+        {
+          dn = g_byte_array_new ();
+          g_byte_array_append (dn, req_ca_rdn[i].data, req_ca_rdn[i].size);
+          g_ptr_array_add (self->accepted_cas, dn);
+        }
+
+      self->accepted_cas_changed = self->accepted_cas || had_accepted_cas;
+    }
+
+  clear_gnutls_certificate_copy (self);
+  g_tls_connection_gnutls_handshake_thread_get_certificate (conn, pcert, pcert_length, pkey);
+
+  if (is_client (self))
+    {
+      if (*pcert_length == 0)
+        {
+          g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
+
+          if (g_tls_connection_base_handshake_thread_request_certificate (tls))
+            g_tls_connection_gnutls_handshake_thread_get_certificate (conn, pcert, pcert_length, pkey);
+
+          if (*pcert_length == 0)
+            {
+              g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
+
+              /* If there is still no client certificate, this connection will
+               * probably fail, but we must not give up yet. The certificate might
+               * be optional, e.g. if the server is using
+               * G_TLS_AUTHENTICATION_REQUESTED, not G_TLS_AUTHENTICATION_REQUIRED.
+               */
+              g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (tls);
+              return 0;
+            }
+        }
+
+      if (!*pkey)
+        {
+          g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
+
+          /* No private key. GnuTLS expects it to be non-null if pcert_length is
+           * nonzero, so we have to abort now.
+           */
+          g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (tls);
+          return -1;
+        }
+    }
+
+  self->pcert = *pcert;
+  self->pcert_length = *pcert_length;
+  self->pkey = *pkey;
+
+  return 0;
+}
+
 static int
 session_ticket_received_cb (gnutls_session_t      session,
                             guint                 htype,
@@ -1117,10 +1228,13 @@ g_tls_operations_thread_gnutls_finalize (GObject *object)
   g_clear_pointer (&self->session_data, g_bytes_unref);
 
   g_clear_pointer (&self->server_identity, g_free);
+  g_clear_pointer (&self->accepted_cas, g_ptr_array_unref);
 
   g_clear_object (&self->base_iostream);
   g_clear_object (&self->base_socket);
 
+  clear_gnutls_certificate_copy (self);
+
   g_assert (!self->op_cancellable);
   g_assert (!self->op_error);
 
@@ -1144,6 +1258,7 @@ g_tls_operations_thread_gnutls_constructed (GObject *object)
   if (ret != GNUTLS_E_SUCCESS)
     return FALSE;
 #endif
+  gnutls_certificate_set_retrieve_function2 (self->creds, retrieve_certificate_cb);
 
   gnutls_init (&self->session, self->init_flags);
 
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index 8a44285..7f54c84 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -45,10 +45,6 @@ struct _GTlsServerConnectionGnutls
   GTlsConnectionGnutls parent_instance;
 
   GTlsAuthenticationMode authentication_mode;
-
-  gnutls_pcert_st *pcert;
-  unsigned int pcert_length;
-  gnutls_privkey_t pkey;
 };
 
 static void     g_tls_server_connection_gnutls_initable_interface_init (GInitableIface  *iface);
@@ -66,70 +62,22 @@ G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionGnutls, g_tls_server_connection_gnu
                                                 NULL)
 )
 
-static void
-clear_gnutls_certificate_copy (GTlsServerConnectionGnutls *gnutls)
-{
-  g_tls_certificate_gnutls_copy_free (gnutls->pcert, gnutls->pcert_length, gnutls->pkey);
-
-  gnutls->pcert = NULL;
-  gnutls->pcert_length = 0;
-  gnutls->pkey = NULL;
-}
-
 static void
 g_tls_server_connection_gnutls_init (GTlsServerConnectionGnutls *gnutls)
 {
 }
 
-static void
-g_tls_server_connection_gnutls_finalize (GObject *object)
-{
-  GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
-
-  clear_gnutls_certificate_copy (gnutls);
-
-  G_OBJECT_CLASS (g_tls_server_connection_gnutls_parent_class)->finalize (object);
-}
-
-static int
-g_tls_server_connection_gnutls_handshake_thread_retrieve_function (gnutls_session_t              session,
-                                                                   const gnutls_datum_t         *req_ca_rdn,
-                                                                   int                           nreqs,
-                                                                   const gnutls_pk_algorithm_t  *pk_algos,
-                                                                   int                           
pk_algos_length,
-                                                                   gnutls_pcert_st             **pcert,
-                                                                   unsigned int                 
*pcert_length,
-                                                                   gnutls_privkey_t             *pkey)
-{
-  GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (gnutls_transport_get_ptr (session));
-
-  clear_gnutls_certificate_copy (gnutls);
-
-  g_tls_connection_gnutls_handshake_thread_get_certificate (G_TLS_CONNECTION_GNUTLS (gnutls),
-                                                            pcert, pcert_length, pkey);
-
-  gnutls->pcert = *pcert;
-  gnutls->pcert_length = *pcert_length;
-  gnutls->pkey = *pkey;
-
-  return 0;
-}
-
 static gboolean
 g_tls_server_connection_gnutls_initable_init (GInitable       *initable,
                                               GCancellable    *cancellable,
                                               GError         **error)
 {
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
   GTlsCertificate *cert;
   gnutls_certificate_credentials_t creds;
 
   if (!g_tls_server_connection_gnutls_parent_initable_iface->init (initable, cancellable, error))
     return FALSE;
 
-  creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
-  gnutls_certificate_set_retrieve_function2 (creds, 
g_tls_server_connection_gnutls_handshake_thread_retrieve_function);
-
   /* Currently we don't know ahead of time if a PKCS #11 backed certificate has a private key. */
   cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (initable));
   if (cert && !g_tls_certificate_gnutls_has_key (G_TLS_CERTIFICATE_GNUTLS (cert)) &&
@@ -186,7 +134,6 @@ g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klas
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-  gobject_class->finalize     = g_tls_server_connection_gnutls_finalize;
   gobject_class->get_property = g_tls_server_connection_gnutls_get_property;
   gobject_class->set_property = g_tls_server_connection_gnutls_set_property;
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]