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



commit 730c9cf0b2eb5cdaf81bedd56c522058334d405a
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Mon Dec 23 12:05:06 2019 -0600

    progress

 tls/base/gtlsconnection-base.c           | 214 +++++---------------------
 tls/base/gtlsconnection-base.h           |  17 +--
 tls/base/gtlsoperationsthread-base.c     | 254 ++++++++++++++++++++++++-------
 tls/base/gtlsoperationsthread-base.h     | 165 ++++++++++----------
 tls/gnutls/gtlsconnection-gnutls.c       |  36 +----
 tls/gnutls/gtlsoperationsthread-gnutls.c |  30 +++-
 6 files changed, 352 insertions(+), 364 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 28860da..88b47b2 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -93,12 +93,6 @@ typedef struct
   GTlsCertificate       *peer_certificate;
   GTlsCertificateFlags   peer_certificate_errors;
 
-  /* FIXME: remove */
-  GMutex                 verify_certificate_mutex;
-  GCond                  verify_certificate_condition;
-  gboolean               peer_certificate_accepted;
-  gboolean               peer_certificate_examined;
-
   gboolean               require_close_notify;
   GTlsRehandshakeMode    rehandshake_mode;
 
@@ -126,11 +120,10 @@ typedef struct
   /* FIXME: remove a few of these */
   gboolean       need_handshake;
   gboolean       need_finish_handshake;
-  gboolean       sync_handshake_in_progress;
   gboolean       started_handshake;
   gboolean       handshaking;
   gboolean       ever_handshaked;
-  GMainContext  *handshake_context; /* FIXME remove */
+  gboolean       peer_certificate_accepted;
   GTask         *async_implicit_handshake;
   GError        *handshake_error;
 
@@ -228,9 +221,6 @@ g_tls_connection_base_init (GTlsConnectionBase *tls)
   priv->database_is_unset = TRUE;
   priv->is_system_certdb = TRUE;
 
-  g_mutex_init (&priv->verify_certificate_mutex);
-  g_cond_init (&priv->verify_certificate_condition);
-
   g_mutex_init (&priv->op_mutex);
 
   priv->waiting_for_op = g_cancellable_new ();
@@ -271,13 +261,8 @@ g_tls_connection_base_finalize (GObject *object)
   g_clear_object (&priv->certificate);
   g_clear_object (&priv->peer_certificate);
 
-  g_mutex_clear (&priv->verify_certificate_mutex);
-  g_cond_clear (&priv->verify_certificate_condition);
-
   g_clear_object (&priv->interaction);
 
-  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
-
   /* This must always be NULL at this point, as it holds a reference to @tls as
    * its source object. However, we clear it anyway just in case this changes
    * in future. */
@@ -605,7 +590,6 @@ claim_op (GTlsConnectionBase    *tls,
 
           /* If we already have an error, ignore further errors. */
           success = finish_handshake (tls, success, my_error ? NULL : &my_error);
-          g_clear_pointer (&priv->handshake_context, g_main_context_unref);
 
           g_mutex_lock (&priv->op_mutex);
 
@@ -624,6 +608,8 @@ claim_op (GTlsConnectionBase    *tls,
         }
     }
 
+  /* FIXME: store a GThread member to bring back this check */
+#if 0
   if (priv->handshaking &&
       op != G_TLS_CONNECTION_BASE_OP_HANDSHAKE &&
       timeout != 0 &&
@@ -641,6 +627,7 @@ claim_op (GTlsConnectionBase    *tls,
       g_tls_log_debug (tls, "claim_op failed: %s", (*error)->message);
       return FALSE;
     }
+#endif
 
   if ((op != G_TLS_CONNECTION_BASE_OP_WRITE && priv->reading) ||
       (op != G_TLS_CONNECTION_BASE_OP_READ && priv->writing) ||
@@ -1237,7 +1224,6 @@ static GTlsCertificateFlags
 verify_peer_certificate (GTlsConnectionBase *tls,
                          GTlsCertificate    *peer_certificate)
 {
-  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
   GSocketConnectable *peer_identity;
   GTlsDatabase *database;
   GTlsCertificateFlags errors;
@@ -1280,32 +1266,24 @@ verify_peer_certificate (GTlsConnectionBase *tls,
         }
     }
 
-  if (tls_class->verify_peer_certificate)
-    errors |= tls_class->verify_peer_certificate (tls, peer_certificate, errors);
-
   return errors;
 }
 
-static void
-update_peer_certificate_and_compute_errors (GTlsConnectionBase *tls)
+static gboolean
+verify_certificate_cb (GTlsOperationsThreadBase *thread,
+                       GTlsCertificate          *peer_certificate,
+                       GTlsConnectionBase       *tls)
 {
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-  GTlsCertificate *peer_certificate = NULL;
   GTlsCertificateFlags peer_certificate_errors = 0;
+  gboolean accepted = FALSE;
 
-  /* This function must be called from the handshake context thread
-   * (probably the main thread, NOT the handshake thread) because
-   * it emits notifies that are application-visible.
-   *
-   * verify_certificate_mutex should be locked.
+  /* FIXME: when doing async handshake as sync-on-a-thread, this function will
+   * be called from the handshake thread, which is unsafe. The code assumes
+   * it is used from the main thread.
+   * FIXME: eliminate handshake context.
    */
-#if 0
-  /* FIXME: sabotage */
-  g_assert (priv->handshake_context);
-  g_assert (g_main_context_is_owner (priv->handshake_context));
-#endif
 
-  peer_certificate = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->retrieve_peer_certificate (tls);
   if (peer_certificate)
     peer_certificate_errors = verify_peer_certificate (tls, peer_certificate);
 
@@ -1316,23 +1294,6 @@ update_peer_certificate_and_compute_errors (GTlsConnectionBase *tls)
 
   g_object_notify (G_OBJECT (tls), "peer-certificate");
   g_object_notify (G_OBJECT (tls), "peer-certificate-errors");
-}
-
-static gboolean
-accept_or_reject_peer_certificate (gpointer user_data)
-{
-  GTlsConnectionBase *tls = user_data;
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-  gboolean accepted = FALSE;
-
-#if 0
-  /* FIXME: sabotage */
-  g_assert (g_main_context_is_owner (priv->handshake_context));
-#endif
-
-  g_mutex_lock (&priv->verify_certificate_mutex);
-
-  update_peer_certificate_and_compute_errors (tls);
 
   if (G_IS_TLS_CLIENT_CONNECTION (tls) && priv->peer_certificate)
     {
@@ -1351,76 +1312,36 @@ accept_or_reject_peer_certificate (gpointer user_data)
 
   if (!accepted)
     {
-      gboolean sync_handshake_in_progress;
-
-      g_mutex_lock (&priv->op_mutex);
-      sync_handshake_in_progress = priv->sync_handshake_in_progress;
-      g_mutex_unlock (&priv->op_mutex);
-
-      if (sync_handshake_in_progress)
-        g_main_context_pop_thread_default (priv->handshake_context);
-
       accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (tls),
                                                            priv->peer_certificate,
                                                            priv->peer_certificate_errors);
-
-      if (sync_handshake_in_progress)
-        g_main_context_push_thread_default (priv->handshake_context);
     }
 
   priv->peer_certificate_accepted = accepted;
 
-  /* This has to be the very last statement before signaling the
-   * condition variable because otherwise the code could spuriously
-   * wakeup and continue before we are done here.
-   */
-  priv->peer_certificate_examined = TRUE;
-
-  g_cond_signal (&priv->verify_certificate_condition);
-  g_mutex_unlock (&priv->verify_certificate_mutex);
-
-  g_object_notify (G_OBJECT (tls), "peer-certificate");
-  g_object_notify (G_OBJECT (tls), "peer-certificate-errors");
-
-  return G_SOURCE_REMOVE;
+  return accepted;
 }
 
-gboolean
-g_tls_connection_base_handshake_thread_verify_certificate (GTlsConnectionBase *tls)
+static void
+session_resumed_cb (GTlsOperationsThreadBase *thread,
+                    GTlsCertificate          *peer_certificate,
+                    GTlsConnectionBase       *tls)
 {
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-  gboolean accepted;
-
-  g_tls_log_debug (tls, "verifying peer certificate");
 
-  /* FIXME: sabotage */
-  accept_or_reject_peer_certificate (tls);
-  accepted = priv->peer_certificate_accepted;
-  return accepted;
-#if 0
-  g_mutex_lock (&priv->verify_certificate_mutex);
-  priv->peer_certificate_examined = FALSE;
-  priv->peer_certificate_accepted = FALSE;
-  g_mutex_unlock (&priv->verify_certificate_mutex);
-
-  /* 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.
+  /* FIXME: when doing async handshake as sync-on-a-thread, this function will
+   * be called from the handshake thread, which is unsafe. The code assumes
+   * it is used from the main thread.
+   * FIXME: eliminate handshake context.
    */
-  g_assert (priv->handshake_context);
-  g_main_context_invoke (priv->handshake_context, accept_or_reject_peer_certificate, tls);
 
-  /* We'll block the handshake thread until the original thread has
-   * decided whether to accept the certificate.
-   */
-  g_mutex_lock (&priv->verify_certificate_mutex);
-  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);
+  g_set_object (&priv->peer_certificate, peer_certificate);
+  g_clear_object (&peer_certificate);
 
-  return accepted;
-#endif
+  priv->peer_certificate_errors = 0;
+
+  g_object_notify (G_OBJECT (tls), "peer-certificate");
+  g_object_notify (G_OBJECT (tls), "peer-certificate-errors");
 }
 
 static gboolean
@@ -1483,9 +1404,12 @@ handshake (GTlsConnectionBase  *tls,
                                           (const gchar **)priv->advertised_protocols,
                                           auth_mode,
                                           timeout,
+                                          (GTlsVerifyCertificateFunc)verify_certificate_cb,
+                                          (GTlsSessionResumedFunc)session_resumed_cb,
                                           &priv->negotiated_protocol,
                                           &accepted_cas,
                                           cancellable,
+                                          tls,
                                           error);
 
   priv->need_handshake = FALSE;
@@ -1517,37 +1441,15 @@ finish_handshake (GTlsConnectionBase  *tls,
                   GError             **error)
 {
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
   GError *my_error = NULL;
 
   g_tls_log_debug (tls, "finishing TLS handshake");
 
-  if (success)
+  /* FIXME: Return an error from the handshake instead? */
+  if (success && priv->peer_certificate && !priv->peer_certificate_accepted)
     {
-      if (tls_class->is_session_resumed && tls_class->is_session_resumed (tls))
-        {
-          /* Because this session was resumed, we skipped certificate
-           * verification on this handshake, so we missed our earlier
-           * chance to set peer_certificate and peer_certificate_errors.
-           * Do so here instead.
-           *
-           * The certificate has already been accepted, so we don't do
-           * anything with the result here.
-           */
-          g_mutex_lock (&priv->verify_certificate_mutex);
-          update_peer_certificate_and_compute_errors (tls);
-          priv->peer_certificate_examined = TRUE;
-          priv->peer_certificate_accepted = TRUE;
-          g_mutex_unlock (&priv->verify_certificate_mutex);
-        }
-
-      /* FIXME: Return an error from the handshake thread instead?
-       * FIXME: no more handshake thread */
-      if (priv->peer_certificate && !priv->peer_certificate_accepted)
-        {
-          g_set_error_literal (&my_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                               _("Unacceptable TLS certificate"));
-        }
+      g_set_error_literal (&my_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                           _("Unacceptable TLS certificate"));
     }
 
   /* FIXME: notify only when accepted-cas has actually changed. */
@@ -1573,31 +1475,16 @@ g_tls_connection_base_handshake (GTlsConnection   *conn,
                                  GError          **error)
 {
   GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
   gboolean success;
   GError *my_error = NULL;
 
   g_tls_log_debug (tls, "Starting synchronous TLS handshake");
 
-  g_assert (!priv->handshake_context);
-  priv->handshake_context = g_main_context_new ();
-
-  g_main_context_push_thread_default (priv->handshake_context);
-
   success = handshake (tls, -1 /* blocking */, cancellable, error);
 
-  g_mutex_lock (&priv->op_mutex);
-  priv->sync_handshake_in_progress = FALSE;
-  g_mutex_unlock (&priv->op_mutex);
-
-  g_main_context_wakeup (priv->handshake_context);
-
   /* If we already have an error, ignore further errors. */
   success = finish_handshake (tls, success, my_error ? NULL : &my_error);
 
-  g_main_context_pop_thread_default (priv->handshake_context);
-  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
-
   yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
             G_TLS_CONNECTION_BASE_OK);
 
@@ -1650,12 +1537,7 @@ async_handshake_thread_completed (GObject      *object,
     need_finish_handshake = FALSE;
   g_mutex_unlock (&priv->op_mutex);
 
-  /* We have to clear handshake_context before g_task_return_* because it can
-   * return immediately to application code inside g_task_return_*,
-   * and the application code could then start a new TLS operation.
-   *
-   * But we can't clear until after finish_handshake().
-   */
+  /* FIXME: this looks weird, why do we ignore the result of the GTask in the !need_finish_handshake case? */
   if (need_finish_handshake)
     {
       success = g_task_propagate_boolean (G_TASK (result), &error);
@@ -1663,8 +1545,6 @@ async_handshake_thread_completed (GObject      *object,
       /* If we already have an error, ignore further errors. */
       success = finish_handshake (tls, success, error ? NULL : &error);
 
-      g_clear_pointer (&priv->handshake_context, g_main_context_unref);
-
       if (success)
         g_task_return_boolean (caller_task, TRUE);
       else
@@ -1672,8 +1552,6 @@ async_handshake_thread_completed (GObject      *object,
     }
   else
     {
-      g_clear_pointer (&priv->handshake_context, g_main_context_unref);
-
       if (priv->handshake_error)
         g_task_return_error (caller_task, g_error_copy (priv->handshake_error));
       else
@@ -1723,14 +1601,10 @@ g_tls_connection_base_handshake_async (GTlsConnection      *conn,
                                        gpointer             user_data)
 {
   GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
   GTask *thread_task, *caller_task;
 
   g_tls_log_debug (tls, "Starting asynchronous TLS handshake");
 
-  g_assert (!priv->handshake_context);
-  priv->handshake_context = g_main_context_ref_thread_default ();
-
   caller_task = g_task_new (conn, cancellable, callback, user_data);
   g_task_set_source_tag (caller_task, g_tls_connection_base_handshake_async);
   g_task_set_name (caller_task, "[glib-networking] g_tls_connection_base_handshake_async (caller task)");
@@ -1787,9 +1661,6 @@ start_async_implicit_handshake (GTlsConnectionBase  *tls,
 
   /* We have op_mutex */
 
-  g_assert (!priv->handshake_context);
-  priv->handshake_context = g_main_context_ref_thread_default ();
-
   g_assert (!priv->async_implicit_handshake);
   priv->async_implicit_handshake = g_task_new (tls, cancellable,
                                                NULL, NULL);
@@ -1825,27 +1696,14 @@ do_sync_implicit_handshake (GTlsConnectionBase  *tls,
 
   /* We have op_mutex */
 
-  g_assert (!priv->handshake_context);
-  priv->handshake_context = g_main_context_new ();
-  g_main_context_push_thread_default (priv->handshake_context);
-
   g_mutex_unlock (&priv->op_mutex);
 
   success = handshake (tls, timeout, cancellable, &my_error);
 
-  g_mutex_lock (&priv->op_mutex);
-  priv->sync_handshake_in_progress = FALSE;
-  g_mutex_unlock (&priv->op_mutex);
-
-  g_main_context_wakeup (priv->handshake_context);
-
   /* If we already have an error, ignore further errors. */
   success = finish_handshake (tls, success,
                               my_error ? NULL : &my_error);
 
-  g_main_context_pop_thread_default (priv->handshake_context);
-  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
-
   yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
             G_TLS_CONNECTION_BASE_OK);
 
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 938ce11..3b8f6e6 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -40,7 +40,7 @@ typedef enum {
   G_TLS_CONNECTION_BASE_REHANDSHAKE,
   G_TLS_CONNECTION_BASE_TRY_AGAIN,
   G_TLS_CONNECTION_BASE_ERROR,
-} GTlsConnectionBaseStatus; /* FIXME: move? rename? GTlsOperationsThreadBaseStatus */
+} GTlsConnectionBaseStatus; /* FIXME: move? rename? GTlsOperationsThreadBaseStatus? */
 
 typedef enum {
   G_TLS_DIRECTION_NONE = 0,
@@ -58,8 +58,6 @@ struct _GTlsConnectionBaseClass
 
   GTlsOperationsThreadBase   *(*create_op_thread)           (GTlsConnectionBase   *tls);
 
-  gboolean                    (*is_session_resumed)         (GTlsConnectionBase   *tls);
-
   void                        (*push_io)                    (GTlsConnectionBase   *tls,
                                                              GIOCondition          direction,
                                                              gint64                timeout, /* FIXME: remove 
timeout */
@@ -73,10 +71,6 @@ struct _GTlsConnectionBaseClass
                                                              GList                 *accepted_cas);
 };
 
-/* FIXME: no handshake_thread stuff */
-gboolean                  g_tls_connection_base_handshake_thread_verify_certificate
-                                                                        (GTlsConnectionBase *tls);
-
 void                      g_tls_connection_base_push_io                 (GTlsConnectionBase *tls,
                                                                          GIOCondition        direction,
                                                                          gint64              timeout, /* 
FIXME: remove timeout */
@@ -116,7 +110,7 @@ gboolean                  g_tls_connection_base_close_internal          (GIOStre
                                                                          GCancellable   *cancellable,
                                                                          GError        **error);
 
-/* FIXME: audit, which are still needed? */
+/* FIXME: audit, which are still needed? in public header? */
 
 gboolean                  g_tls_connection_base_is_dtls                 (GTlsConnectionBase *tls);
 
@@ -124,13 +118,6 @@ GDatagramBased           *g_tls_connection_base_get_base_socket         (GTlsCon
 
 GIOStream                *g_tls_connection_base_get_base_iostream       (GTlsConnectionBase *tls);
 
-/* FIXME: no handshake_thread stuff */
-
-void                      g_tls_connection_base_handshake_thread_buffer_application_data
-                                                                        (GTlsConnectionBase *tls,
-                                                                         guint8             *data,
-                                                                         gsize               length);
-
 /* FIXME: needed? */
 GTlsOperationsThreadBase *g_tls_connection_base_get_op_thread           (GTlsConnectionBase *tls);
 
diff --git a/tls/base/gtlsoperationsthread-base.c b/tls/base/gtlsoperationsthread-base.c
index 0ee81f1..2f17ca3 100644
--- a/tls/base/gtlsoperationsthread-base.c
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -68,7 +68,7 @@
  * write operations on one thread without either one blocking the other.
  */
 typedef struct {
-  /* FIXME: remove to prevent misuse */
+  /* FIXME: remove to prevent misuse? */
   GTlsConnectionBase *connection;
 
   GThread *op_thread;
@@ -102,6 +102,23 @@ typedef enum {
   G_TLS_THREAD_OP_SHUTDOWN_THREAD
 } GTlsThreadOperationType;
 
+struct _HandshakeContext
+{
+  GMainContext *caller_context;
+  GTlsVerifyCertificateFunc verify_callback;
+  gboolean certificate_verified;
+  gpointer user_data;
+};
+
+typedef struct {
+  HandshakeContext *context;
+  gchar **advertised_protocols;
+  GTlsAuthenticationMode auth_mode;
+  gchar *negotiated_protocol;
+  GList *accepted_cas;
+  GTlsCertificate *peer_certificate;
+} HandshakeData;
+
 typedef struct {
   GTlsThreadOperationType type;
   GIOCondition io_condition;
@@ -109,39 +126,32 @@ typedef struct {
   GTlsOperationsThreadBase *thread;
   GTlsConnectionBase *connection; /* FIXME: threadsafety nightmare, not OK */
 
-  GTlsOperationsThreadBase *source; /* for copy client session state */
-
-  gchar *server_identity;           /* for set server identity */
-
-  gchar *negotiated_protocol;       /* for handshake */
-  gchar **advertised_protocols;     /* for handshake */
-  GTlsAuthenticationMode auth_mode; /* for handshake */
-  GList *accepted_cas;              /* for handshake */
-
+  /* Op input */
   union {
-    void *data;                    /* for read/write */
-    GInputVector *input_vectors;   /* for read message */
-    GOutputVector *output_vectors; /* for write message */
+    GTlsOperationsThreadBase *source; /* for copy client session state */
+    gchar *server_identity;           /* for set server identity */
+    HandshakeData *handshake_data;    /* for handshake */
+    void *data;                       /* for read/write */
+    GInputVector *input_vectors;      /* for read message */
+    GOutputVector *output_vectors;    /* for write message */
   };
-
   union {
     gsize size;        /* for read/write */
     guint num_vectors; /* for read/write message */
   };
-
   gint64 timeout;
   gint64 start_time;
 
   GCancellable *cancellable;
 
-  GMutex finished_mutex;
-  GCond finished_condition;
-  gboolean finished;
-
-  /* Result */
+  /* Op output */
   GTlsConnectionBaseStatus result;
   gssize count; /* Bytes read or written */
   GError *error;
+
+  GMutex finished_mutex;
+  GCond finished_condition;
+  gboolean finished;
 } GTlsThreadOperation;
 
 static gboolean process_op (GAsyncQueue         *queue,
@@ -280,6 +290,57 @@ g_tls_operations_thread_base_get_is_missing_requested_client_certificate (GTlsOp
   return ret;
 }
 
+static HandshakeContext *
+handshake_context_new (GMainContext              *caller_context,
+                       GTlsVerifyCertificateFunc  verify_callback,
+                       gpointer                   user_data)
+{
+  HandshakeContext *context;
+
+  context = g_new0 (HandshakeContext, 1);
+  context->caller_context = g_main_context_ref (caller_context);
+  context->verify_callback = verify_callback;
+  context->user_data = user_data;
+
+  return context;
+}
+
+static void
+handshake_context_free (HandshakeContext *context)
+{
+  g_main_context_unref (context->caller_context);
+
+  g_free (context);
+}
+
+static HandshakeData *
+handshake_data_new (HandshakeContext        *context,
+                    const gchar            **advertised_protocols,
+                    GTlsAuthenticationMode   mode)
+{
+  HandshakeData *data;
+
+  data = g_new0 (HandshakeData, 1);
+  data->context = context;
+  data->advertised_protocols = g_strdupv ((gchar **)advertised_protocols);
+  data->auth_mode = mode;
+
+  return data;
+}
+
+static void
+handshake_data_free (HandshakeData *data)
+{
+  g_strfreev (data->advertised_protocols);
+
+  g_clear_object (&data->peer_certificate);
+
+  g_assert (!data->accepted_cas);
+  g_assert (!data->negotiated_protocol);
+
+  g_free (data);
+}
+
 static GTlsThreadOperation *
 g_tls_thread_copy_client_session_state_operation_new (GTlsOperationsThreadBase *thread,
                                                       GTlsConnectionBase       *connection,
@@ -320,6 +381,7 @@ g_tls_thread_set_server_identity_operation_new (GTlsOperationsThreadBase *thread
 
 static GTlsThreadOperation *
 g_tls_thread_handshake_operation_new (GTlsOperationsThreadBase  *thread,
+                                      HandshakeContext          *context,
                                       GTlsConnectionBase        *connection,
                                       const gchar              **advertised_protocols,
                                       GTlsAuthenticationMode     auth_mode,
@@ -333,11 +395,13 @@ g_tls_thread_handshake_operation_new (GTlsOperationsThreadBase  *thread,
   op->io_condition = G_IO_IN | G_IO_OUT;
   op->thread = thread;
   op->connection = connection;
-  op->advertised_protocols = g_strdupv ((gchar **)advertised_protocols);
-  op->auth_mode = auth_mode;
   op->timeout = timeout;
   op->cancellable = cancellable;
 
+  op->handshake_data = handshake_data_new (context,
+                                           advertised_protocols,
+                                           auth_mode);
+
   g_mutex_init (&op->finished_mutex);
   g_cond_init (&op->finished_condition);
 
@@ -487,11 +551,7 @@ g_tls_thread_operation_free (GTlsThreadOperation *op)
     g_free (op->server_identity);
 
   if (op->type == G_TLS_THREAD_OP_HANDSHAKE)
-    {
-      g_strfreev (op->advertised_protocols);
-      g_assert (!op->accepted_cas);
-      g_assert (!op->negotiated_protocol);
-    }
+    handshake_data_free (op->handshake_data);
 
   if (op->type != G_TLS_THREAD_OP_SHUTDOWN_THREAD)
     {
@@ -513,7 +573,7 @@ wait_for_op_completion (GTlsThreadOperation *op)
 
 static GTlsConnectionBaseStatus
 execute_op (GTlsOperationsThreadBase *self,
-            GTlsThreadOperation      *op /* owned */,
+            GTlsThreadOperation      *op,
             gssize                   *count,
             GError                  **error)
 {
@@ -536,8 +596,6 @@ execute_op (GTlsOperationsThreadBase *self,
       op->error = NULL;
     }
 
-  g_tls_thread_operation_free (op);
-
   return result;
 }
 
@@ -548,10 +606,13 @@ g_tls_operations_thread_base_copy_client_session_state (GTlsOperationsThreadBase
   GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
   GTlsThreadOperation *op;
 
+  g_assert (!g_main_context_is_owner (priv->op_thread_context));
+
   op = g_tls_thread_copy_client_session_state_operation_new (self,
                                                              priv->connection,
                                                              source);
-  execute_op (self, g_steal_pointer (&op), NULL, NULL);
+  execute_op (self, op, NULL, NULL);
+  g_tls_thread_operation_free (op);
 }
 
 void
@@ -561,39 +622,96 @@ g_tls_operations_thread_base_set_server_identity (GTlsOperationsThreadBase *self
   GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
   GTlsThreadOperation *op;
 
+  g_assert (!g_main_context_is_owner (priv->op_thread_context));
+
   op = g_tls_thread_set_server_identity_operation_new (self,
                                                        priv->connection,
                                                        server_identity);
-  execute_op (self, g_steal_pointer (&op), NULL, NULL);
+  execute_op (self, op, NULL, NULL);
+  g_tls_thread_operation_free (op);
+}
+
+#if 0
+static gboolean
+invoke_verify_certificate_callback_cb (gpointer user_data)
+{
+
+}
+#endif
+
+gboolean
+g_tls_operations_thread_base_verify_certificate (GTlsOperationsThreadBase *self,
+                                                 HandshakeContext         *context)
+{
+#if 0
+  GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  gboolean accepted;
+
+  g_assert (g_main_context_is_owner (priv->op_thread_context));
+
+  /* Invoke the caller's callback on the calling thread, not the op thread. */
+  g_main_context_invoke (context->caller_context, accept_or_reject_peer_certificate, tls);
+
+  /* Block the op thread until the calling thread's callback finishes. */
+  g_mutex_lock (&priv->verify_certificate_mutex);
+  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);
+
+  context->certificate_verified = TRUE;
+
+  return accepted;
+#endif
 }
 
 GTlsConnectionBaseStatus
-g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase  *self,
-                                        const gchar              **advertised_protocols,
-                                        GTlsAuthenticationMode     auth_mode,
-                                        gint64                     timeout,
-                                        gchar                    **negotiated_protocol,
-                                        GList                    **accepted_cas,
-                                        GCancellable              *cancellable,
-                                        GError                   **error)
+g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase   *self,
+                                        const gchar               **advertised_protocols,
+                                        GTlsAuthenticationMode      auth_mode,
+                                        gint64                      timeout,
+                                        GTlsVerifyCertificateFunc   verify_callback,
+                                        GTlsSessionResumedFunc      resumed_callback,
+                                        gchar                     **negotiated_protocol,
+                                        GList                     **accepted_cas,
+                                        GCancellable               *cancellable,
+                                        gpointer                    user_data,
+                                        GError                    **error)
 {
   GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
   GTlsConnectionBaseStatus status;
   GTlsThreadOperation *op;
+  HandshakeContext *context;
+
+  g_assert (!g_main_context_is_owner (priv->op_thread_context));
 
   g_mutex_lock (&priv->mutex);
   priv->missing_requested_client_certificate = FALSE;
   g_mutex_unlock (&priv->mutex);
 
+  context = handshake_context_new (g_main_context_get_thread_default (),
+                                   verify_callback,
+                                   user_data);
+
   op = g_tls_thread_handshake_operation_new (self,
+                                             context,
                                              priv->connection,
                                              advertised_protocols,
                                              auth_mode,
                                              timeout,
                                              cancellable);
-  status = execute_op (self, g_steal_pointer (&op), NULL, error);
-  *negotiated_protocol = g_steal_pointer (&op->negotiated_protocol);
-  *accepted_cas = g_steal_pointer (&op->accepted_cas);
+  status = execute_op (self, op, NULL, error);
+
+  /* FIXME: is this right? Probably we should really check for session resumption? is_session_resumed? */
+  if (!context->certificate_verified)
+    resumed_callback (self, op->handshake_data->peer_certificate, user_data);
+
+  *negotiated_protocol = g_steal_pointer (&op->handshake_data->negotiated_protocol);
+  *accepted_cas = g_steal_pointer (&op->handshake_data->accepted_cas);
+
+  handshake_context_free (context);
+  g_tls_thread_operation_free (op);
+
   return status;
 }
 
@@ -607,14 +725,20 @@ g_tls_operations_thread_base_read (GTlsOperationsThreadBase  *self,
                                    GError                   **error)
 {
   GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsConnectionBaseStatus status;
   GTlsThreadOperation *op;
 
+  g_assert (!g_main_context_is_owner (priv->op_thread_context));
+
   op = g_tls_thread_read_operation_new (self,
                                         priv->connection,
                                         buffer, size,
                                         timeout,
                                         cancellable);
-  return execute_op (self, g_steal_pointer (&op), nread, error);
+  status = execute_op (self, op, nread, error);
+  g_tls_thread_operation_free (op);
+
+  return status;
 }
 
 GTlsConnectionBaseStatus
@@ -627,14 +751,20 @@ g_tls_operations_thread_base_read_message (GTlsOperationsThreadBase  *self,
                                            GError                   **error)
 {
   GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsConnectionBaseStatus status;
   GTlsThreadOperation *op;
 
+  g_assert (!g_main_context_is_owner (priv->op_thread_context));
+
   op = g_tls_thread_read_message_operation_new (self,
                                                 priv->connection,
                                                 vectors, num_vectors,
                                                 timeout,
                                                 cancellable);
-  return execute_op (self, g_steal_pointer (&op), nread, error);
+  status = execute_op (self, op, nread, error);
+  g_tls_thread_operation_free (op);
+
+  return status;
 }
 
 GTlsConnectionBaseStatus
@@ -647,14 +777,20 @@ g_tls_operations_thread_base_write (GTlsOperationsThreadBase  *self,
                                     GError                   **error)
 {
   GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsConnectionBaseStatus status;
   GTlsThreadOperation *op;
 
+  g_assert (!g_main_context_is_owner (priv->op_thread_context));
+
   op = g_tls_thread_write_operation_new (self,
                                          priv->connection,
                                          buffer, size,
                                          timeout,
                                          cancellable);
-  return execute_op (self, g_steal_pointer (&op), nwrote, error);
+  status = execute_op (self, op, nwrote, error);
+  g_tls_thread_operation_free (op);
+
+  return status;
 }
 
 GTlsConnectionBaseStatus
@@ -667,14 +803,20 @@ g_tls_operations_thread_base_write_message (GTlsOperationsThreadBase  *self,
                                             GError                   **error)
 {
   GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsConnectionBaseStatus status;
   GTlsThreadOperation *op;
 
+  g_assert (!g_main_context_is_owner (priv->op_thread_context));
+
   op = g_tls_thread_write_message_operation_new (self,
                                                  priv->connection,
                                                  vectors, num_vectors,
                                                  timeout,
                                                  cancellable);
-  return execute_op (self, g_steal_pointer (&op), nwrote, error);
+  status = execute_op (self, op, nwrote, error);
+  g_tls_thread_operation_free (op);
+
+  return status;
 }
 
 GTlsConnectionBaseStatus
@@ -683,12 +825,18 @@ g_tls_operations_thread_base_close (GTlsOperationsThreadBase  *self,
                                     GError                   **error)
 {
   GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsConnectionBaseStatus status;
   GTlsThreadOperation *op;
 
+  g_assert (!g_main_context_is_owner (priv->op_thread_context));
+
   op = g_tls_thread_close_operation_new (self,
                                          priv->connection,
                                          cancellable);
-  return execute_op (self, g_steal_pointer (&op), NULL, error);
+  status = execute_op (self, op, NULL, error);
+  g_tls_thread_operation_free (op);
+
+  return status;
 }
 
 typedef struct {
@@ -1016,11 +1164,13 @@ process_op (GAsyncQueue         *queue,
       break;
     case G_TLS_THREAD_OP_HANDSHAKE:
       op->result = base_class->handshake_fn (op->thread,
-                                             (const gchar **)op->advertised_protocols,
-                                             op->auth_mode,
+                                             op->handshake_data->context,
+                                             (const gchar **)op->handshake_data->advertised_protocols,
+                                             op->handshake_data->auth_mode,
                                              op->timeout,
-                                             &op->negotiated_protocol,
-                                             &op->accepted_cas,
+                                             &op->handshake_data->negotiated_protocol,
+                                             &op->handshake_data->accepted_cas,
+                                             &op->handshake_data->peer_certificate,
                                              op->cancellable,
                                              &op->error);
       break;
diff --git a/tls/base/gtlsoperationsthread-base.h b/tls/base/gtlsoperationsthread-base.h
index 6658108..299a35f 100644
--- a/tls/base/gtlsoperationsthread-base.h
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -34,35 +34,29 @@ G_BEGIN_DECLS
 
 G_DECLARE_DERIVABLE_TYPE (GTlsOperationsThreadBase, g_tls_operations_thread_base, G, 
TLS_OPERATIONS_THREAD_BASE, GObject)
 
+typedef struct _HandshakeContext HandshakeContext;
+
 struct _GTlsOperationsThreadBaseClass
 {
   GObjectClass parent_class;
 
   void                        (*copy_client_session_state)  (GTlsOperationsThreadBase  *self,
                                                              GTlsOperationsThreadBase  *source);
+
   void                        (*set_server_identity)        (GTlsOperationsThreadBase  *self,
                                                              const gchar               *server_identity);
 
   GTlsConnectionBaseStatus    (*handshake_fn)               (GTlsOperationsThreadBase  *self,
+                                                             HandshakeContext          *context,
                                                              const gchar              **advertised_protocols,
                                                              GTlsAuthenticationMode     auth_mode,
                                                              gint64                     timeout,
                                                              gchar                    **negotiated_protocol,
                                                              GList                    **accepted_cas,
+                                                             GTlsCertificate          **peer_certificate,
                                                              GCancellable              *cancellable,
                                                              GError                   **error);
 
-/* FIXME: working on these... */
-#if 0
-  GTlsCertificate            *(*retrieve_peer_certificate)  (GTlsOperationsThreadBase  *self);
-  GTlsCertificateFlags        (*verify_peer_certificate)    (GTlsOperationsThreadBase  *self,
-                                                             GTlsCertificate           *certificate,
-                                                             GTlsCertificateFlags       flags);
-  void                        (*complete_handshake)         (GTlsOperationsThreadBase  *self,
-                                                             gchar                    **negotiated_protocol,
-                                                             GError                   **error);
-#endif
-
   GTlsConnectionBaseStatus    (*read_fn)                    (GTlsOperationsThreadBase  *self,
                                                              void                      *buffer,
                                                              gsize                      size,
@@ -94,73 +88,88 @@ struct _GTlsOperationsThreadBaseClass
                                                              GError                   **error);
 };
 
+typedef gboolean (*GTlsVerifyCertificateFunc) (GTlsOperationsThreadBase *thread,
+                                               GTlsCertificate          *peer_certificate,
+                                               gpointer                  user_data);
+typedef void     (*GTlsSessionResumedFunc)    (GTlsOperationsThreadBase *thread,
+                                               GTlsCertificate          *peer_certificate,
+                                               gpointer                  user_data);
+
 /* 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_set_interaction           (GTlsOperationsThreadBase  
*self,
-                                                                                  GTlsInteraction           
*interaction);
-GTlsInteraction          *g_tls_operations_thread_base_ref_interaction           (GTlsOperationsThreadBase  
*self);
-GError                   *g_tls_operations_thread_base_take_interaction_error    (GTlsOperationsThreadBase  
*self);
-
-gboolean                  g_tls_operations_thread_base_request_certificate       (GTlsOperationsThreadBase  
*self,
-                                                                                  GCancellable              
*cancellable);
-
-void                      g_tls_operations_thread_base_set_is_missing_requested_client_certificate 
(GTlsOperationsThreadBase *self);
-gboolean                  g_tls_operations_thread_base_get_is_missing_requested_client_certificate 
(GTlsOperationsThreadBase *self);
-
-void                      g_tls_operations_thread_base_copy_client_session_state (GTlsOperationsThreadBase  
*self,
-                                                                                  GTlsOperationsThreadBase  
*source);
-
-void                      g_tls_operations_thread_base_set_server_identity       (GTlsOperationsThreadBase  
*self,
-                                                                                  const gchar               
*server_identity);
-
-GTlsConnectionBaseStatus  g_tls_operations_thread_base_handshake                 (GTlsOperationsThreadBase  
*self,
-                                                                                  const gchar              
**advertised_protocols,
-                                                                                  GTlsAuthenticationMode     
auth_mode,
-                                                                                  gint64                     
timeout,
-                                                                                  gchar                    
**negotiated_protocol,
-                                                                                  GList                    
**accepted_cas,
-                                                                                  GCancellable              
*cancellable,
-                                                                                  GError                   
**error);
-
-GTlsConnectionBaseStatus  g_tls_operations_thread_base_read                      (GTlsOperationsThreadBase  
*self,
-                                                                                  void                      
*buffer,
-                                                                                  gsize                      
size,
-                                                                                  gint64                     
timeout,
-                                                                                  gssize                    
*nread,
-                                                                                  GCancellable              
*cancellable,
-                                                                                  GError                   
**error);
-
-GTlsConnectionBaseStatus  g_tls_operations_thread_base_read_message              (GTlsOperationsThreadBase  
*self,
-                                                                                  GInputVector              
*vectors,
-                                                                                  guint                      
num_vectors,
-                                                                                  gint64                     
timeout,
-                                                                                  gssize                    
*nread,
-                                                                                  GCancellable              
*cancellable,
-                                                                                  GError                   
**error);
-
-GTlsConnectionBaseStatus  g_tls_operations_thread_base_write                     (GTlsOperationsThreadBase  
*self,
-                                                                                  const void                
*buffer,
-                                                                                  gsize                      
size,
-                                                                                  gint64                     
timeout,
-                                                                                  gssize                    
*nwrote,
-                                                                                  GCancellable              
*cancellable,
-                                                                                  GError                   
**error);
-
-GTlsConnectionBaseStatus  g_tls_operations_thread_base_write_message             (GTlsOperationsThreadBase  
*self,
-                                                                                  GOutputVector             
*vectors,
-                                                                                  guint                      
num_vectors,
-                                                                                  gint64                     
timeout,
-                                                                                  gssize                    
*nwrote,
-                                                                                  GCancellable              
*cancellable,
-                                                                                  GError                   
**error);
-
-GTlsConnectionBaseStatus  g_tls_operations_thread_base_close                     (GTlsOperationsThreadBase  
*self,
-                                                                                  GCancellable              
*cancellable,
-                                                                                  GError                   
**error);
+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_set_interaction           (GTlsOperationsThreadBase   
*self,
+                                                                                  GTlsInteraction            
*interaction);
+GTlsInteraction          *g_tls_operations_thread_base_ref_interaction           (GTlsOperationsThreadBase   
*self);
+GError                   *g_tls_operations_thread_base_take_interaction_error    (GTlsOperationsThreadBase   
*self);
+
+gboolean                  g_tls_operations_thread_base_request_certificate       (GTlsOperationsThreadBase   
*self,
+                                                                                  GCancellable               
*cancellable);
+
+void                      g_tls_operations_thread_base_set_is_missing_requested_client_certificate
+                                                                                 (GTlsOperationsThreadBase  
*self);
+gboolean                  g_tls_operations_thread_base_get_is_missing_requested_client_certificate
+                                                                                 (GTlsOperationsThreadBase  
*self);
+
+gboolean                  g_tls_operations_thread_base_verify_certificate        (GTlsOperationsThreadBase  
*self,
+                                                                                  HandshakeContext          
*context);
+
+void                      g_tls_operations_thread_base_copy_client_session_state (GTlsOperationsThreadBase   
*self,
+                                                                                  GTlsOperationsThreadBase   
*source);
+
+void                      g_tls_operations_thread_base_set_server_identity       (GTlsOperationsThreadBase   
*self,
+                                                                                  const gchar                
*server_identity);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_handshake                 (GTlsOperationsThreadBase   
*self,
+                                                                                  const gchar               
**advertised_protocols,
+                                                                                  GTlsAuthenticationMode     
 auth_mode,
+                                                                                  gint64                     
 timeout,
+                                                                                  GTlsVerifyCertificateFunc  
 verify_callback,
+                                                                                  GTlsSessionResumedFunc     
 resumed_callback,
+                                                                                  gchar                     
**negotiated_protocol,
+                                                                                  GList                     
**accepted_cas,
+                                                                                  GCancellable               
*cancellable,
+                                                                                  gpointer                   
 user_data,
+                                                                                  GError                    
**error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_read                      (GTlsOperationsThreadBase   
*self,
+                                                                                  void                       
*buffer,
+                                                                                  gsize                      
 size,
+                                                                                  gint64                     
 timeout,
+                                                                                  gssize                     
*nread,
+                                                                                  GCancellable               
*cancellable,
+                                                                                  GError                    
**error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_read_message              (GTlsOperationsThreadBase   
*self,
+                                                                                  GInputVector               
*vectors,
+                                                                                  guint                      
 num_vectors,
+                                                                                  gint64                     
 timeout,
+                                                                                  gssize                     
*nread,
+                                                                                  GCancellable               
*cancellable,
+                                                                                  GError                    
**error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_write                     (GTlsOperationsThreadBase   
*self,
+                                                                                  const void                 
*buffer,
+                                                                                  gsize                      
 size,
+                                                                                  gint64                     
 timeout,
+                                                                                  gssize                     
*nwrote,
+                                                                                  GCancellable               
*cancellable,
+                                                                                  GError                    
**error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_write_message             (GTlsOperationsThreadBase   
*self,
+                                                                                  GOutputVector              
*vectors,
+                                                                                  guint                      
 num_vectors,
+                                                                                  gint64                     
 timeout,
+                                                                                  gssize                     
*nwrote,
+                                                                                  GCancellable               
*cancellable,
+                                                                                  GError                    
**error);
+
+GTlsConnectionBaseStatus  g_tls_operations_thread_base_close                     (GTlsOperationsThreadBase   
*self,
+                                                                                  GCancellable               
*cancellable,
+                                                                                  GError                    
**error);
 
 G_END_DECLS
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 40115c9..90e96aa 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -123,46 +123,12 @@ g_tls_connection_gnutls_create_op_thread (GTlsConnectionBase *tls)
   return thread;
 }
 
-static GTlsCertificate *
-g_tls_connection_gnutls_retrieve_peer_certificate (GTlsConnectionBase *tls)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  const gnutls_datum_t *certs;
-  GTlsCertificateGnutls *chain;
-  unsigned int num_certs;
-
-  if (gnutls_certificate_type_get (priv->session) != GNUTLS_CRT_X509)
-    return NULL;
-
-  certs = gnutls_certificate_get_peers (priv->session, &num_certs);
-  if (!certs || !num_certs)
-    return NULL;
-
-  chain = g_tls_certificate_gnutls_build_chain (certs, num_certs, GNUTLS_X509_FMT_DER);
-  if (!chain)
-    return NULL;
-
-  return G_TLS_CERTIFICATE (chain);
-}
-
-static gboolean
-g_tls_connection_gnutls_is_session_resumed (GTlsConnectionBase *tls)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  return gnutls_session_is_resumed (priv->session);
-}
-
 static void
 g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
 {
   GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
 
-  base_class->create_op_thread                           = g_tls_connection_gnutls_create_op_thread;
-  base_class->retrieve_peer_certificate                  = g_tls_connection_gnutls_retrieve_peer_certificate;
-  base_class->is_session_resumed                         = g_tls_connection_gnutls_is_session_resumed;
+  base_class->create_op_thread = g_tls_connection_gnutls_create_op_thread;
 }
 
 static void
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
index 560b6e1..297f4ce 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.c
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -59,6 +59,7 @@ struct _GTlsOperationsThreadGnutls {
   GOutputStream           *base_ostream;
   GDatagramBased          *base_socket;
 
+  HandshakeContext        *handshake_context;
   gboolean                 handshaking;
   gboolean                 ever_handshaked;
 
@@ -538,21 +539,24 @@ set_authentication_mode (GTlsOperationsThreadGnutls *self,
 
 static GTlsConnectionBaseStatus
 g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase  *base,
+                                          HandshakeContext          *context,
                                           const gchar              **advertised_protocols,
                                           GTlsAuthenticationMode     auth_mode,
                                           gint64                     timeout,
                                           gchar                    **negotiated_protocol,
                                           GList                    **accepted_cas,
+                                          GTlsCertificate          **peer_certificate,
                                           GCancellable              *cancellable,
                                           GError                   **error)
 {
   GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
   GTlsConnectionBaseStatus status;
+  GTlsCertificateGnutls *chain;
+  const gnutls_datum_t *certs;
+  unsigned int num_certs;
   gnutls_datum_t protocol;
   int ret;
 
-  tls = g_tls_operations_thread_base_get_connection (base);
-
   if (!self->ever_handshaked)
     set_handshake_priority (self);
 
@@ -569,6 +573,7 @@ g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase  *base,
     set_authentication_mode (self, auth_mode);
 
   self->handshaking = TRUE;
+  self->handshake_context = context;
 
   BEGIN_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, cancellable);
   ret = gnutls_handshake (self->session);
@@ -589,14 +594,29 @@ g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase  *base,
   END_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, ret, status,
                  _("Error performing TLS handshake"), error);
 
+  self->handshake_context = NULL;
   self->handshaking = FALSE;
   self->ever_handshaked = TRUE;
 
   if (gnutls_alpn_get_selected_protocol (self->session, &protocol) == 0 && protocol.size > 0)
     *negotiated_protocol = g_strndup ((gchar *)protocol.data, protocol.size);
+  else
+    *negotiated_protocol = NULL;
 
   *accepted_cas = g_list_copy (self->accepted_cas);
 
+  *peer_certificate = NULL;
+  if (gnutls_certificate_type_get (self->session) == GNUTLS_CRT_X509)
+    {
+      certs = gnutls_certificate_get_peers (self->session, &num_certs);
+      if (certs && num_certs > 0)
+        {
+          chain = g_tls_certificate_gnutls_build_chain (certs, num_certs, GNUTLS_X509_FMT_DER);
+          if (chain)
+            *peer_certificate = G_TLS_CERTIFICATE (chain);
+        }
+    }
+
   return status;
 }
 
@@ -1093,7 +1113,8 @@ verify_certificate_cb (gnutls_session_t 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);
+  return !g_tls_operations_thread_base_verify_certificate (G_TLS_OPERATIONS_THREAD_BASE (self),
+                                                           self->handshake_context);
 }
 
 static int
@@ -1406,14 +1427,11 @@ g_tls_operations_thread_gnutls_initable_init (GInitable     *initable,
                                               GError       **error)
 {
   GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (initable);
-  GTlsConnectionBase *tls;
   int ret;
 
   if (!g_tls_operations_thread_gnutls_parent_initable_iface->init (initable, cancellable, error))
     return FALSE;
 
-  tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
-
   ret = gnutls_certificate_allocate_credentials (&self->creds);
   if (ret != 0)
     {


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