[glib-networking/wip/verification: 1/2] progress



commit 2089592f39f34ec53ee6364adc2173302b7497f4
Author: Michael Catanzaro <mcatanzaro igalia com>
Date:   Thu Mar 29 16:42:41 2018 -0500

    progress

 meson.build                        |    2 +-
 tls/gnutls/gtlsconnection-gnutls.c |  203 +++++++++++++++++++++++++++++-------
 2 files changed, 165 insertions(+), 40 deletions(-)
---
diff --git a/meson.build b/meson.build
index 4c62023..ca77ec6 100644
--- a/meson.build
+++ b/meson.build
@@ -75,7 +75,7 @@ if enable_gnome_proxy_support
 endif
 
 # *** Checks for GnuTLS     ***
-gnutls_dep = dependency('gnutls', version: '>= 3.4', required: true)
+gnutls_dep = dependency('gnutls', version: '>= 3.4.6', required: true)
 
 # *** Checks for p11-kit  ***
 enable_pkcs11_support = get_option('pkcs11_support')
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 01cd215..024a1ae 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -121,10 +121,18 @@ static gboolean do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
                                        gint64                 timeout,
                                        GCancellable          *cancellable,
                                        GError               **error);
+
 static gboolean finish_handshake (GTlsConnectionGnutls  *gnutls,
-                                  GTask                 *thread_task,
+                                  GTask                 *task,
                                   GError               **error);
 
+static int verify_certificate_cb (gnutls_session_t session);
+
+// FIXME: can this be moved?
+static gboolean accept_peer_certificate (GTlsConnectionGnutls *gnutls,
+                                         GTlsCertificate      *peer_certificate,
+                                         GTlsCertificateFlags  peer_certificate_errors);
+
 enum
 {
   PROP_0,
@@ -174,8 +182,11 @@ typedef struct
 
   GTlsCertificate *certificate, *peer_certificate;
   GTlsCertificateFlags peer_certificate_errors;
-  GTlsCertificate *peer_certificate_tmp;
-  GTlsCertificateFlags peer_certificate_errors_tmp;
+
+  GMutex verify_certificate_mutex;
+  GCond verify_certificate_condition;
+  gboolean peer_certificate_accepted;
+  gboolean peer_certificate_examined;
 
   gboolean require_close_notify;
   GTlsRehandshakeMode rehandshake_mode;
@@ -206,6 +217,7 @@ typedef struct
    */
   gboolean need_handshake, need_finish_handshake;
   gboolean started_handshake, handshaking, ever_handshaked;
+  GMainContext *handshake_context;
   GTask *implicit_handshake;
   GError *handshake_error;
   GByteArray *app_data_buf;
@@ -253,6 +265,9 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
 
   gnutls_certificate_allocate_credentials (&priv->creds);
 
+  g_mutex_init (&priv->verify_certificate_mutex);
+  g_cond_init (&priv->verify_certificate_condition);
+
   priv->need_handshake = TRUE;
 
   priv->database_is_unset = TRUE;
@@ -387,6 +402,9 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
 
   gnutls_init (&priv->session, flags);
 
+  gnutls_session_set_ptr (priv->session, gnutls);
+  gnutls_session_set_verify_function (priv->session, verify_certificate_cb);
+
   status = gnutls_credentials_set (priv->session,
                                    GNUTLS_CRD_CERTIFICATE,
                                    priv->creds);
@@ -447,7 +465,9 @@ g_tls_connection_gnutls_finalize (GObject *object)
   g_clear_object (&priv->database);
   g_clear_object (&priv->certificate);
   g_clear_object (&priv->peer_certificate);
-  g_clear_object (&priv->peer_certificate_tmp);
+
+  g_mutex_clear (&priv->verify_certificate_mutex);
+  g_cond_clear (&priv->verify_certificate_condition);
 
   g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
 
@@ -462,6 +482,8 @@ g_tls_connection_gnutls_finalize (GObject *object)
   g_clear_error (&priv->read_error);
   g_clear_error (&priv->write_error);
 
+  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
   /* This must always be NULL here, as it holds a reference to @gnutls as
    * its source object. However, we clear it anyway just in case this changes
    * in future. */
@@ -749,6 +771,7 @@ claim_op (GTlsConnectionGnutls    *gnutls,
           g_mutex_unlock (&priv->op_mutex);
           success = finish_handshake (gnutls, priv->implicit_handshake, &my_error);
           g_clear_object (&priv->implicit_handshake);
+          g_clear_pointer (&priv->handshake_context, g_main_context_unref);
           g_mutex_lock (&priv->op_mutex);
 
           if (op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH &&
@@ -1762,6 +1785,73 @@ verify_peer_certificate (GTlsConnectionGnutls *gnutls,
   return errors;
 }
 
+static gboolean
+accept_certificate_cb (gpointer user_data)
+{
+  GTlsConnectionGnutls *gnutls = user_data;
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+
+  g_assert (g_main_context_is_owner (priv->handshake_context));
+
+  g_mutex_lock (&priv->verify_certificate_mutex);
+
+  if (gnutls_certificate_type_get (priv->session) == GNUTLS_CRT_X509)
+    {
+      priv->peer_certificate = get_peer_certificate_from_session (gnutls);
+      if (priv->peer_certificate)
+        priv->peer_certificate_errors = verify_peer_certificate (gnutls, priv->peer_certificate);
+    }
+
+  priv->peer_certificate_accepted = accept_peer_certificate (gnutls,
+                                                             priv->peer_certificate,
+                                                             priv->peer_certificate_errors);
+  priv->peer_certificate_examined = TRUE;
+
+  g_cond_signal (&priv->verify_certificate_condition);
+  g_mutex_unlock (&priv->verify_certificate_mutex);
+
+  g_object_notify (G_OBJECT (gnutls), "peer-certificate");
+  g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
+
+  return G_SOURCE_REMOVE;
+}
+
+static int
+verify_certificate_cb (gnutls_session_t session)
+{
+  GTlsConnectionGnutls *gnutls = gnutls_session_get_ptr (session);
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+  gboolean accepted;
+  GSource *source;
+
+  g_mutex_lock (&priv->verify_certificate_mutex);
+
+  g_assert (priv->handshake_context);
+
+  priv->peer_certificate_examined = FALSE;
+  priv->peer_certificate_accepted = FALSE;
+
+  /* Invoke the callback on the handshake context's thread. This is
+   * necessary because we need to ensure the accept-certificate signal
+   * is emitted on the original thread.
+   */
+  source = g_idle_source_new ();
+  g_source_set_name (source, "[glib-networking] verify_certificate_cb");
+  g_source_set_callback (source, accept_certificate_cb, gnutls, NULL);
+  g_source_set_priority (source, G_PRIORITY_HIGH);
+  g_source_attach (source, priv->handshake_context);
+  g_source_unref (source);
+
+  while (!priv->peer_certificate_examined)
+    g_cond_wait (&priv->verify_certificate_condition, &priv->verify_certificate_mutex);
+  accepted = priv->peer_certificate_accepted;
+
+  g_mutex_unlock (&priv->verify_certificate_mutex);
+
+  /* Return 0 for the handshake to continue, non-zero to terminate. */
+  return !accepted;
+}
+
 static void
 handshake_thread (GTask        *task,
                   gpointer      object,
@@ -1870,16 +1960,10 @@ handshake_thread (GTask        *task,
   END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
                  _("Error performing TLS handshake"), &error);
 
-  if (ret == 0 && gnutls_certificate_type_get (priv->session) == GNUTLS_CRT_X509)
+  if (!priv->peer_certificate && G_IS_TLS_CLIENT_CONNECTION (gnutls))
     {
-      priv->peer_certificate_tmp = get_peer_certificate_from_session (gnutls);
-      if (priv->peer_certificate_tmp)
-        priv->peer_certificate_errors_tmp = verify_peer_certificate (gnutls, priv->peer_certificate_tmp);
-      else if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
-        {
-          g_set_error_literal (&error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                               _("Server did not return a valid TLS certificate"));
-        }
+      g_set_error_literal (&error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                           _("Server did not return a valid TLS certificate"));
     }
 
   /* This calls the finish_handshake code of GTlsClientConnectionGnutls
@@ -1944,29 +2028,13 @@ finish_handshake (GTlsConnectionGnutls  *gnutls,
                   GError               **error)
 {
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsCertificate *peer_certificate;
-  GTlsCertificateFlags peer_certificate_errors;
 
   g_assert (error != NULL);
 
-  peer_certificate = priv->peer_certificate_tmp;
-  priv->peer_certificate_tmp = NULL;
-  peer_certificate_errors = priv->peer_certificate_errors_tmp;
-  priv->peer_certificate_errors_tmp = 0;
-
-  if (g_task_propagate_boolean (task, error) && peer_certificate)
+  if (g_task_propagate_boolean (task, error) && !priv->peer_certificate_accepted)
     {
-      if (!accept_peer_certificate (gnutls, peer_certificate,
-                                    peer_certificate_errors))
-        {
-          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                               _("Unacceptable TLS certificate"));
-        }
-
-      priv->peer_certificate = peer_certificate;
-      priv->peer_certificate_errors = peer_certificate_errors;
-      g_object_notify (G_OBJECT (gnutls), "peer-certificate");
-      g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                           _("Unacceptable TLS certificate"));
     }
 
   if (*error && priv->started_handshake)
@@ -1975,26 +2043,65 @@ finish_handshake (GTlsConnectionGnutls  *gnutls,
   return (*error == NULL);
 }
 
+static void
+sync_handshake_thread_completed (GObject      *object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+
+  g_mutex_lock (&priv->op_mutex);
+  priv->need_finish_handshake = TRUE;
+  g_mutex_unlock (&priv->op_mutex);
+}
+
+static void
+crank_sync_handshake_context (GTlsConnectionGnutls *gnutls)
+{
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+
+  g_mutex_lock (&priv->op_mutex);
+  priv->need_finish_handshake = FALSE;
+  while (!priv->need_finish_handshake)
+    {
+      g_mutex_unlock (&priv->op_mutex);
+      g_main_context_iteration (priv->handshake_context, TRUE);
+      g_mutex_lock (&priv->op_mutex);
+    }
+  g_mutex_unlock (&priv->op_mutex);
+}
+
 static gboolean
 g_tls_connection_gnutls_handshake (GTlsConnection   *conn,
                                    GCancellable     *cancellable,
                                    GError          **error)
 {
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
   GTask *task;
   gboolean success;
   gint64 *timeout = NULL;
   GError *my_error = NULL;
 
-  task = g_task_new (conn, cancellable, NULL, NULL);
+  begin_handshake (gnutls);
+
+  task = g_task_new (conn, cancellable, sync_handshake_thread_completed, NULL);
   g_task_set_source_tag (task, g_tls_connection_gnutls_handshake);
 
   timeout = g_new0 (gint64, 1);
   *timeout = -1;  /* blocking */
   g_task_set_task_data (task, timeout, g_free);
 
-  begin_handshake (gnutls);
-  g_task_run_in_thread_sync (task, handshake_thread);
+  g_assert (priv->handshake_context == NULL);
+  priv->handshake_context = g_main_context_new ();
+  g_main_context_push_thread_default (priv->handshake_context);
+
+  g_task_run_in_thread (task, handshake_thread);
+  crank_sync_handshake_context (gnutls);
+
+  g_main_context_pop_thread_default (priv->handshake_context);
+  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
   success = finish_handshake (gnutls, task, &my_error);
   g_object_unref (task);
 
@@ -2030,6 +2137,8 @@ handshake_thread_completed (GObject      *object,
   GError *error = NULL;
   gboolean need_finish_handshake, success;
 
+  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
   g_mutex_lock (&priv->op_mutex);
   if (priv->need_finish_handshake)
     {
@@ -2052,7 +2161,6 @@ handshake_thread_completed (GObject      *object,
     g_task_return_error (caller_task, g_error_copy (priv->handshake_error));
   else
     g_task_return_boolean (caller_task, TRUE);
-
   g_object_unref (caller_task);
 }
 
@@ -2086,6 +2194,7 @@ g_tls_connection_gnutls_handshake_async (GTlsConnection       *conn,
                                          GAsyncReadyCallback   callback,
                                          gpointer              user_data)
 {
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (G_TLS_CONNECTION_GNUTLS 
(conn));
   GTask *thread_task, *caller_task;
   gint64 *timeout = NULL;
 
@@ -2104,6 +2213,9 @@ g_tls_connection_gnutls_handshake_async (GTlsConnection       *conn,
   *timeout = -1;  /* blocking */
   g_task_set_task_data (thread_task, timeout, g_free);
 
+  g_assert (!priv->handshake_context);
+  priv->handshake_context = g_main_context_ref_thread_default ();
+
   g_task_run_in_thread (thread_task, async_handshake_thread);
   g_object_unref (thread_task);
 }
@@ -2150,7 +2262,9 @@ do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
   /* We have op_mutex */
 
   g_assert (priv->implicit_handshake == NULL);
-  priv->implicit_handshake = g_task_new (gnutls, cancellable, NULL, NULL);
+  priv->implicit_handshake = g_task_new (gnutls, cancellable,
+                                         timeout ? sync_handshake_thread_completed : NULL,
+                                         NULL);
   g_task_set_source_tag (priv->implicit_handshake,
                          do_implicit_handshake);
 
@@ -2172,9 +2286,17 @@ do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
        * operation is complete or errors. */
       *thread_timeout = timeout;
 
+      g_assert (priv->handshake_context == NULL);
+      priv->handshake_context = g_main_context_new ();
+      g_main_context_push_thread_default (priv->handshake_context);
+
       g_mutex_unlock (&priv->op_mutex);
-      g_task_run_in_thread_sync (priv->implicit_handshake,
-                                 handshake_thread);
+      g_task_run_in_thread (priv->implicit_handshake, handshake_thread);
+      crank_sync_handshake_context (gnutls);
+
+      g_main_context_pop_thread_default (priv->handshake_context);
+      g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
       success = finish_handshake (gnutls,
                                   priv->implicit_handshake,
                                   &my_error);
@@ -2194,6 +2316,9 @@ do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
        * about. Run the actual operation as blocking in its thread. */
       *thread_timeout = -1;  /* blocking */
 
+      g_assert (priv->handshake_context == NULL);
+      priv->handshake_context = g_main_context_ref_thread_default ();
+
       g_task_run_in_thread (priv->implicit_handshake,
                             async_handshake_thread);
 


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