[glib-networking/mcatanzaro/tls-thread: 12/24] progress



commit 94fdde36fa80b22edf152ed8d4f7505365bac71c
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Sun Dec 22 14:10:07 2019 -0600

    progress

 tls/base/gtlsconnection-base.c           | 106 ++++++------------
 tls/base/gtlsconnection-base.h           |   3 +
 tls/base/gtlsoperationsthread-base.c     |  25 ++++-
 tls/base/gtlsoperationsthread-base.h     |   4 +
 tls/gnutls/gtlsclientconnection-gnutls.c |  38 +++----
 tls/gnutls/gtlsconnection-gnutls.c       | 147 +-----------------------
 tls/gnutls/gtlsoperationsthread-gnutls.c | 186 ++++++++++++++++++++++++++++---
 tls/gnutls/gtlsserverconnection-gnutls.c |   1 -
 8 files changed, 247 insertions(+), 263 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index c15bca1..a79ae93 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -135,7 +135,6 @@ typedef struct
   GMainContext  *handshake_context; /* FIXME remove */
   GTask         *async_implicit_handshake;
   GError        *handshake_error;
-  GByteArray    *app_data_buf;
 
   /* read_closed means the read direction has closed; write_closed similarly.
    * If (and only if) both are set, the entire GTlsConnection is closed. */
@@ -290,8 +289,6 @@ g_tls_connection_base_finalize (GObject *object)
   g_clear_object (&priv->waiting_for_op);
   g_mutex_clear (&priv->op_mutex);
 
-  g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
-
   g_clear_pointer (&priv->advertised_protocols, g_strfreev);
   g_clear_pointer (&priv->negotiated_protocol, g_free);
 
@@ -1424,6 +1421,15 @@ handshake (GTlsConnectionBase  *tls,
 {
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
   GTlsAuthenticationMode auth_mode = G_TLS_AUTHENTICATION_NONE;
+  gchar *original_negotiated_protocol;
+  GList *accepted_cas;
+
+  /* FIXME: in async codepaths, this function is still called on a secondary
+   * handshake thread. That means use of priv members here needs to be guarded
+   * by a mutex. Additionally, it is SUPER UNSAFE to notify negotiated-protocol
+   * on this thread. We need to either eliminate this extra thread, or tighten
+   * up the threadsafety of this function.
+   */
 
   g_tls_log_debug (tls, "TLS handshake starts");
 
@@ -1460,10 +1466,31 @@ handshake (GTlsConnectionBase  *tls,
                     NULL);
     }
 
+  original_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
+
   priv->started_handshake = TRUE;
-  g_tls_operations_thread_base_handshake (priv->thread, (const gchar **)priv->advertised_protocols, 
auth_mode, timeout, cancellable, error);
+
+  g_tls_operations_thread_base_handshake (priv->thread,
+                                          (const gchar **)priv->advertised_protocols,
+                                          auth_mode,
+                                          timeout,
+                                          &priv->negotiated_protocol,
+                                          &accepted_cas,
+                                          cancellable,
+                                          error);
+
   priv->need_handshake = FALSE;
 
+  /* FIXME: notify should be next to accepted-cas? */
+  if (g_strcmp0 (original_negotiated_protocol, priv->negotiated_protocol) != 0)
+    g_object_notify (G_OBJECT (tls), "negotiated-protocol");
+  g_free (original_negotiated_protocol);
+
+  if (G_IS_TLS_CLIENT_CONNECTION (tls))
+    G_TLS_CONNECTION_BASE_GET_CLASS (tls)->set_accepted_cas (tls, accepted_cas);
+  else
+    g_assert (!accepted_cas);
+
   if (error && *error)
     {
       g_tls_log_debug (tls, "TLS handshake failed: %s", (*error)->message);
@@ -1482,13 +1509,10 @@ finish_handshake (GTlsConnectionBase  *tls,
 {
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
   GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
-  gchar *original_negotiated_protocol;
   GError *my_error = NULL;
 
   g_tls_log_debug (tls, "finishing TLS handshake");
 
-  original_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
-
   if (success)
     {
       if (tls_class->is_session_resumed && tls_class->is_session_resumed (tls))
@@ -1517,15 +1541,9 @@ finish_handshake (GTlsConnectionBase  *tls,
         }
     }
 
-  if (tls_class->complete_handshake)
-    {
-      /* If we already have an error, ignore further errors. */
-      tls_class->complete_handshake (tls, &priv->negotiated_protocol, my_error ? NULL : &my_error);
-
-      if (g_strcmp0 (original_negotiated_protocol, priv->negotiated_protocol) != 0)
-        g_object_notify (G_OBJECT (tls), "negotiated-protocol");
-    }
-  g_free (original_negotiated_protocol);
+  /* FIXME: notify only when accepted-cas has actually changed. */
+  if (G_IS_TLS_CLIENT_CONNECTION (tls))
+    g_object_notify (G_OBJECT (tls), "accepted-cas");
 
   if (my_error && priv->started_handshake)
     priv->handshake_error = g_error_copy (my_error);
@@ -1547,7 +1565,6 @@ g_tls_connection_base_handshake (GTlsConnection   *conn,
 {
   GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
   gboolean success;
   GError *my_error = NULL;
 
@@ -1865,20 +1882,7 @@ g_tls_connection_base_read (GTlsConnectionBase  *tls,
                      timeout, cancellable, error))
         return -1;
 
-      if (priv->app_data_buf && !priv->handshaking)
-        {
-          nread = MIN (size, priv->app_data_buf->len);
-          memcpy (buffer, priv->app_data_buf->data, nread);
-          if (nread == priv->app_data_buf->len)
-            g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
-          else
-            g_byte_array_remove_range (priv->app_data_buf, 0, nread);
-          status = G_TLS_CONNECTION_BASE_OK;
-        }
-      else
-        {
-          status = g_tls_operations_thread_base_read (priv->thread, buffer, size, timeout, &nread, 
cancellable, error);
-        }
+      status = g_tls_operations_thread_base_read (priv->thread, buffer, size, timeout, &nread, cancellable, 
error);
 
       yield_op (tls, G_TLS_CONNECTION_BASE_OP_READ, status);
     }
@@ -1914,32 +1918,7 @@ g_tls_connection_base_read_message (GTlsConnectionBase  *tls,
                    timeout, cancellable, error))
       return -1;
 
-    /* Copy data out of the app data buffer first. */
-    if (priv->app_data_buf && !priv->handshaking)
-      {
-        nread = 0;
-
-        for (guint i = 0; i < num_vectors; i++)
-          {
-            gsize count;
-            GInputVector *vec = &vectors[i];
-
-            count = MIN (vec->size, priv->app_data_buf->len);
-            nread += count;
-
-            memcpy (vec->buffer, priv->app_data_buf->data, count);
-            if (count == priv->app_data_buf->len)
-              g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
-            else
-              g_byte_array_remove_range (priv->app_data_buf, 0, count);
-            status = G_TLS_CONNECTION_BASE_OK;
-          }
-      }
-    else
-      {
-        status = g_tls_operations_thread_base_read_message (priv->thread, vectors, num_vectors, timeout, 
&nread, cancellable, error);
-      }
-
+    status = g_tls_operations_thread_base_read_message (priv->thread, vectors, num_vectors, timeout, &nread, 
cancellable, error);
     yield_op (tls, G_TLS_CONNECTION_BASE_OP_READ, status);
   } while (status == G_TLS_CONNECTION_BASE_REHANDSHAKE);
 
@@ -2479,19 +2458,6 @@ g_tls_connection_base_handshake_thread_request_certificate (GTlsConnectionBase *
   return res != G_TLS_INTERACTION_FAILED;
 }
 
-void
-g_tls_connection_base_handshake_thread_buffer_application_data (GTlsConnectionBase *tls,
-                                                                guint8             *data,
-                                                                gsize               length)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
-  if (!priv->app_data_buf)
-    priv->app_data_buf = g_byte_array_new ();
-
-  g_byte_array_append (priv->app_data_buf, data, length);
-}
-
 GTlsOperationsThreadBase *
 g_tls_connection_base_get_op_thread (GTlsConnectionBase *tls)
 {
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index ea764c6..3f1f5bf 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -68,6 +68,9 @@ struct _GTlsConnectionBaseClass
                                                              GIOCondition          direction,
                                                              gboolean              success,
                                                              GError              **error);
+
+  void                        (*set_accepted_cas)           (GTlsConnectionBase    *tls,
+                                                             GList                 *accepted_cas);
 };
 
 gboolean                  g_tls_connection_base_handshake_thread_verify_certificate
diff --git a/tls/base/gtlsoperationsthread-base.c b/tls/base/gtlsoperationsthread-base.c
index 44975af..63910a9 100644
--- a/tls/base/gtlsoperationsthread-base.c
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -101,9 +101,13 @@ typedef struct {
   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 */
 
   union {
     void *data;                    /* for read/write */
@@ -173,7 +177,6 @@ g_tls_thread_copy_client_session_state_operation_new (GTlsOperationsThreadBase *
   return op;
 }
 
-/* FIXME: dumb, move this into handshake operation as is done for authentication mode */
 static GTlsThreadOperation *
 g_tls_thread_set_server_identity_operation_new (GTlsOperationsThreadBase *thread,
                                                 GTlsConnectionBase       *connection,
@@ -362,7 +365,11 @@ 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_strfreev (op->advertised_protocols);
+      g_assert (!op->accepted_cas);
+      g_assert (!op->negotiated_protocol);
+    }
 
   if (op->type != G_TLS_THREAD_OP_SHUTDOWN_THREAD)
     {
@@ -470,10 +477,13 @@ 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)
 {
   GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+  GTlsConnectionBaseStatus status;
   GTlsThreadOperation *op;
 
   op = g_tls_thread_handshake_operation_new (self,
@@ -482,7 +492,10 @@ g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase  *self,
                                              auth_mode,
                                              timeout,
                                              cancellable);
-  return execute_op (self, g_steal_pointer (&op), NULL, error);
+  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);
+  return status;
 }
 
 GTlsConnectionBaseStatus
@@ -907,6 +920,8 @@ process_op (GAsyncQueue         *queue,
                                              (const gchar **)op->advertised_protocols,
                                              op->auth_mode,
                                              op->timeout,
+                                             &op->negotiated_protocol,
+                                             &op->accepted_cas,
                                              op->cancellable,
                                              &op->error);
       break;
@@ -1015,9 +1030,7 @@ tls_op_thread (gpointer data)
 
   g_main_loop_run (main_loop);
 
-  /* FIXME FIXME: what happens if there are still ops in progress?
-   * They should be cancelled somehow. Figure out how.
-   */
+  g_assert (!queue_has_pending_op (priv->queue));
 
   g_main_context_pop_thread_default (priv->op_thread_context);
 
diff --git a/tls/base/gtlsoperationsthread-base.h b/tls/base/gtlsoperationsthread-base.h
index 9e61055..5980b41 100644
--- a/tls/base/gtlsoperationsthread-base.h
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -47,6 +47,8 @@ struct _GTlsOperationsThreadBaseClass
                                                              const gchar              **advertised_protocols,
                                                              GTlsAuthenticationMode     auth_mode,
                                                              gint64                     timeout,
+                                                             gchar                    **negotiated_protocol,
+                                                             GList                    **accepted_cas,
                                                              GCancellable              *cancellable,
                                                              GError                   **error);
 
@@ -109,6 +111,8 @@ GTlsConnectionBaseStatus  g_tls_operations_thread_base_handshake
                                                                                   const gchar              
**advertised_protocols,
                                                                                   GTlsAuthenticationMode     
auth_mode,
                                                                                   gint64                     
timeout,
+                                                                                  gchar                    
**negotiated_protocol,
+                                                                                  GList                    
**accepted_cas,
                                                                                   GCancellable              
*cancellable,
                                                                                   GError                   
**error);
 
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 0a8a7f0..96d2893 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -54,7 +54,7 @@ struct _GTlsClientConnectionGnutls
   GSocketConnectable *server_identity;
   gboolean use_ssl3;
 
-  GPtrArray *accepted_cas;
+  GList *accepted_cas;
 };
 
 static void g_tls_client_connection_gnutls_initable_interface_init (GInitableIface  *iface);
@@ -94,7 +94,12 @@ g_tls_client_connection_gnutls_finalize (GObject *object)
   GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object);
 
   g_clear_object (&gnutls->server_identity);
-  g_clear_pointer (&gnutls->accepted_cas, g_ptr_array_unref);
+
+  if (gnutls->accepted_cas)
+    {
+      g_list_free_full (gnutls->accepted_cas, (GDestroyNotify)g_byte_array_unref);
+      gnutls->accepted_cas = NULL;
+    }
 
   G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->finalize (object);
 }
@@ -125,8 +130,6 @@ g_tls_client_connection_gnutls_get_property (GObject    *object,
                                              GParamSpec *pspec)
 {
   GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object);
-  GList *accepted_cas;
-  gint i;
 
   switch (prop_id)
     {
@@ -143,16 +146,7 @@ g_tls_client_connection_gnutls_get_property (GObject    *object,
       break;
 
     case PROP_ACCEPTED_CAS:
-      accepted_cas = NULL;
-      if (gnutls->accepted_cas)
-        {
-          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_reverse (accepted_cas);
-        }
-      g_value_set_pointer (value, accepted_cas);
+      g_value_set_pointer (value, g_list_copy (gnutls->accepted_cas));
       break;
 
     default:
@@ -200,19 +194,15 @@ g_tls_client_connection_gnutls_set_property (GObject      *object,
 }
 
 static void
-g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
-                                                   gchar              **negotiated_protocol,
-                                                   GError             **error)
+g_tls_client_connection_gnutls_set_accepted_cas (GTlsConnectionBase *tls,
+                                                 GList              *accepted_cas)
 {
   GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
 
-  G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->complete_handshake (tls, 
negotiated_protocol, error);
+  if (gnutls->accepted_cas)
+    g_list_free_full (gnutls->accepted_cas, (GDestroyNotify)g_byte_array_unref);
 
-  /* It may have changed during the handshake, but we have to wait until here
-   * because we can't emit notifies on the handshake thread.
-   */
-  if (gnutls->accepted_cas_changed)
-    g_object_notify (G_OBJECT (gnutls), "accepted-cas");
+  gnutls->accepted_cas = g_steal_pointer (&accepted_cas);
 }
 
 static void
@@ -235,7 +225,7 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
   gobject_class->set_property = g_tls_client_connection_gnutls_set_property;
   gobject_class->finalize     = g_tls_client_connection_gnutls_finalize;
 
-  base_class->complete_handshake = g_tls_client_connection_gnutls_complete_handshake;
+  base_class->set_accepted_cas = g_tls_client_connection_gnutls_set_accepted_cas;
 
   g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
   g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 3be6fed..6b8f88f 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -58,13 +58,10 @@ static GInitableIface *g_tls_connection_gnutls_parent_initable_iface;
 
 static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
 
-static gnutls_priority_t priority;
-
 typedef struct
 {
   gnutls_session_t session; /* FIXME: should be used only by GTlsOperationsThreadGnutls */
-  gchar *interaction_id;
-  GCancellable *cancellable;
+
 } GTlsConnectionGnutlsPrivate;
 
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls, G_TYPE_TLS_CONNECTION_BASE,
@@ -73,18 +70,9 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls,
                                                          g_tls_connection_gnutls_initable_iface_init);
                                   );
 
-static gint unique_interaction_id = 0;
-
 static void
 g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
 {
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  int unique_id;
-
-  unique_id = g_atomic_int_add (&unique_interaction_id, 1);
-  priv->interaction_id = g_strdup_printf ("gtls:%d", unique_id);
-
-  priv->cancellable = g_cancellable_new ();
 }
 
 static gboolean
@@ -104,120 +92,6 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
   return TRUE;
 }
 
-static void
-g_tls_connection_gnutls_finalize (GObject *object)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  if (priv->cancellable)
-    {
-      g_cancellable_cancel (priv->cancellable);
-      g_clear_object (&priv->cancellable);
-    }
-
-  g_free (priv->interaction_id);
-
-  G_OBJECT_CLASS (g_tls_connection_gnutls_parent_class)->finalize (object);
-}
-
-static int
-on_pin_request (void         *userdata,
-                int           attempt,
-                const char   *token_url,
-                const char   *token_label,
-                unsigned int  callback_flags,
-                char         *pin,
-                size_t        pin_max)
-{
-  GTlsConnection *connection = G_TLS_CONNECTION (userdata);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (G_TLS_CONNECTION_GNUTLS 
(connection));
-  GTlsInteraction *interaction = g_tls_connection_get_interaction (connection);
-  GTlsInteractionResult result;
-  GTlsPassword *password;
-  GTlsPasswordFlags password_flags = 0;
-  GError *error = NULL;
-  gchar *description;
-  int ret = -1;
-
-  if (!interaction)
-    return -1;
-
-  if (callback_flags & GNUTLS_PIN_WRONG)
-    password_flags |= G_TLS_PASSWORD_RETRY;
-  if (callback_flags & GNUTLS_PIN_COUNT_LOW)
-    password_flags |= G_TLS_PASSWORD_MANY_TRIES;
-  if (callback_flags & GNUTLS_PIN_FINAL_TRY || attempt > 5) /* Give up at some point */
-    password_flags |= G_TLS_PASSWORD_FINAL_TRY;
-
-  description = g_strdup_printf (" %s (%s)", token_label, token_url);
-  password = g_tls_password_new (password_flags, description);
-  result = g_tls_interaction_invoke_ask_password (interaction, password,
-                                                  priv->cancellable,
-                                                  &error);
-  g_free (description);
-
-  switch (result)
-    {
-    case G_TLS_INTERACTION_FAILED:
-      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-        g_warning ("Error getting PIN: %s", error->message);
-      g_error_free (error);
-      break;
-    case G_TLS_INTERACTION_UNHANDLED:
-      break;
-    case G_TLS_INTERACTION_HANDLED:
-      {
-        gsize password_size;
-        const guchar *password_data = g_tls_password_get_value (password, &password_size);
-        if (password_size > pin_max)
-          g_warning ("PIN is larger than max PIN size");
-
-        memcpy (pin, password_data, MIN (password_size, pin_max));
-        ret = GNUTLS_E_SUCCESS;
-        break;
-      }
-    default:
-      g_assert_not_reached ();
-    }
-
-  g_object_unref (password);
-
-  return ret;
-}
-
-void
-g_tls_connection_gnutls_handshake_thread_get_certificate (GTlsConnectionGnutls  *gnutls,
-                                                          gnutls_pcert_st      **pcert,
-                                                          unsigned int          *pcert_length,
-                                                          gnutls_privkey_t      *pkey)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsCertificate *cert;
-
-  cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (gnutls));
-
-  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);
-
-      g_tls_certificate_gnutls_copy (G_TLS_CERTIFICATE_GNUTLS (cert),
-                                     priv->interaction_id,
-                                     pcert, pcert_length, &privkey);
-      *pkey = privkey;
-    }
-  else
-    {
-      *pcert = NULL;
-      *pcert_length = 0;
-      *pkey = NULL;
-    }
-}
-
 static GTlsOperationsThreadBase *
 g_tls_connection_gnutls_create_op_thread (GTlsConnectionBase *tls)
 {
@@ -272,22 +146,6 @@ g_tls_connection_gnutls_retrieve_peer_certificate (GTlsConnectionBase *tls)
   return G_TLS_CERTIFICATE (chain);
 }
 
-static void
-g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
-                                            gchar              **negotiated_protocol,
-                                            GError             **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  gnutls_datum_t protocol;
-
-  if (gnutls_alpn_get_selected_protocol (priv->session, &protocol) == 0 && protocol.size > 0)
-    {
-      g_assert (!*negotiated_protocol);
-      *negotiated_protocol = g_strndup ((gchar *)protocol.data, protocol.size);
-    }
-}
-
 static gboolean
 g_tls_connection_gnutls_is_session_resumed (GTlsConnectionBase *tls)
 {
@@ -303,11 +161,8 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
 
-  gobject_class->finalize                                = g_tls_connection_gnutls_finalize;
-
   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->complete_handshake                         = g_tls_connection_gnutls_complete_handshake;
   base_class->is_session_resumed                         = g_tls_connection_gnutls_is_session_resumed;
 }
 
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
index 6688d58..aa98265 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.c
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -52,8 +52,6 @@ struct _GTlsOperationsThreadGnutls {
   GBytes                  *session_data;
   gboolean                 session_data_override; /* FIXME: sort this all out */
 
-  gchar                   *server_identity;
-
   gnutls_session_t         session;
 
   GIOStream               *base_iostream;
@@ -71,8 +69,13 @@ struct _GTlsOperationsThreadGnutls {
   unsigned int             pcert_length;
   gnutls_privkey_t         pkey;
 
-  GPtrArray               *accepted_cas;
-  gboolean                 accepted_cas_changed;
+  GList                   *accepted_cas;
+
+  gchar                   *server_identity;
+
+  gchar                   *interaction_id;
+
+  GByteArray              *application_data_buffer;
 };
 
 enum
@@ -531,12 +534,15 @@ g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase  *base,
                                           const gchar              **advertised_protocols,
                                           GTlsAuthenticationMode     auth_mode,
                                           gint64                     timeout,
+                                          gchar                    **negotiated_protocol,
+                                          GList                    **accepted_cas,
                                           GCancellable              *cancellable,
                                           GError                   **error)
 {
   GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
   GTlsConnectionBase *tls;
   GTlsConnectionBaseStatus status;
+  gnutls_datum_t protocol;
   int ret;
 
   tls = g_tls_operations_thread_base_get_connection (base);
@@ -568,8 +574,9 @@ g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase  *base,
       ret = gnutls_record_recv (self->session, buf, sizeof (buf));
       if (ret > -1)
         {
-          /* FIXME: no longer belongs in GTlsConnectionBase, don't use GTlsConnectionBase */
-          g_tls_connection_base_handshake_thread_buffer_application_data (tls, buf, ret);
+          if (!self->application_data_buffer)
+            self->application_data_buffer = g_byte_array_new ();
+          g_byte_array_append (self->application_data_buffer, buf, ret);
           ret = GNUTLS_E_AGAIN;
         }
     }
@@ -579,6 +586,11 @@ g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase  *base,
   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);
+
+  *accepted_cas = g_list_copy (self->accepted_cas);
+
   return status;
 }
 
@@ -594,6 +606,17 @@ g_tls_operations_thread_gnutls_read (GTlsOperationsThreadBase  *base,
   GTlsConnectionBaseStatus status;
   gssize ret;
 
+  if (self->application_data_buffer)
+    {
+      *nread = MIN (size, self->application_data_buffer->len);
+      memcpy (buffer, self->application_data_buffer->data, nread);
+      if (*nread == self->application_data_buffer->len)
+        g_clear_pointer (&self->application_data_buffer, g_byte_array_unref);
+      else
+        g_byte_array_remove_range (self->application_data_buffer, 0, *nread);
+      return G_TLS_CONNECTION_BASE_OK;
+    }
+
   BEGIN_GNUTLS_IO (self, G_IO_IN, cancellable);
   ret = gnutls_record_recv (self->session, buffer, size);
   END_GNUTLS_IO (self, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
@@ -640,6 +663,28 @@ g_tls_operations_thread_gnutls_read_message (GTlsOperationsThreadBase  *base,
   gssize ret;
   gnutls_packet_t packet = { 0, };
 
+    /* Copy data out of the app data buffer first. */
+    if (self->application_data_buffer)
+      {
+        *nread = 0;
+
+        for (guint i = 0; i < num_vectors; i++)
+          {
+            gsize count;
+            GInputVector *vec = &vectors[i];
+
+            count = MIN (vec->size, self->application_data_buffer->len);
+            *nread += count;
+
+            memcpy (vec->buffer, self->application_data_buffer->data, count);
+            if (count == self->application_data_buffer->len)
+              g_clear_pointer (&self->application_data_buffer, g_byte_array_unref);
+            else
+              g_byte_array_remove_range (self->application_data_buffer, 0, count);
+            return G_TLS_CONNECTION_BASE_OK;
+          }
+      }
+
   BEGIN_GNUTLS_IO (self, G_IO_IN, cancellable);
 
   /* Receive the entire datagram (zero-copy). */
@@ -1045,6 +1090,70 @@ verify_certificate_cb (gnutls_session_t session)
   return !g_tls_connection_base_handshake_thread_verify_certificate (tls);
 }
 
+static int
+pin_request_cb (void         *userdata,
+                int           attempt,
+                const char   *token_url,
+                const char   *token_label,
+                unsigned int  callback_flags,
+                char         *pin,
+                size_t        pin_max)
+{
+  GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (userdata);
+  GTlsInteraction *interaction = g_tls_connection_get_interaction (connection);
+  GTlsInteractionResult result;
+  GTlsPassword *password;
+  GTlsPasswordFlags password_flags = 0;
+  GError *error = NULL;
+  gchar *description;
+  int ret = -1;
+
+  if (!interaction)
+    return -1;
+
+  if (callback_flags & GNUTLS_PIN_WRONG)
+    password_flags |= G_TLS_PASSWORD_RETRY;
+  if (callback_flags & GNUTLS_PIN_COUNT_LOW)
+    password_flags |= G_TLS_PASSWORD_MANY_TRIES;
+  if (callback_flags & GNUTLS_PIN_FINAL_TRY || attempt > 5) /* Give up at some point */
+    password_flags |= G_TLS_PASSWORD_FINAL_TRY;
+
+  description = g_strdup_printf (" %s (%s)", token_label, token_url);
+  password = g_tls_password_new (password_flags, description);
+  result = g_tls_interaction_invoke_ask_password (interaction, password,
+                                                  self->op_cancellable,
+                                                  &error);
+  g_free (description);
+
+  switch (result)
+    {
+    case G_TLS_INTERACTION_FAILED:
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("Error getting PIN: %s", error->message);
+      g_error_free (error);
+      break;
+    case G_TLS_INTERACTION_UNHANDLED:
+      break;
+    case G_TLS_INTERACTION_HANDLED:
+      {
+        gsize password_size;
+        const guchar *password_data = g_tls_password_get_value (password, &password_size);
+        if (password_size > pin_max)
+          g_warning ("PIN is larger than max PIN size");
+
+        memcpy (pin, password_data, MIN (password_size, pin_max));
+        ret = GNUTLS_E_SUCCESS;
+        break;
+      }
+    default:
+      g_assert_not_reached ();
+    }
+
+  g_object_unref (password);
+
+  return ret;
+}
+
 static void
 clear_gnutls_certificate_copy (GTlsOperationsThreadGnutls *self)
 {
@@ -1055,6 +1164,41 @@ clear_gnutls_certificate_copy (GTlsOperationsThreadGnutls *self)
   self->pkey = NULL;
 }
 
+static void
+get_own_certificate (GTlsOperationsThreadGnutls  *self,
+                     gnutls_pcert_st            **pcert,
+                     unsigned int                *pcert_length,
+                     gnutls_privkey_t            *pkey)
+{
+  char *pem;
+  GTlsCertificate *cert = NULL;
+
+  pem = g_tls_operations_thread_base_get_own_certificate_pem (G_TLS_OPERATIONS_THREAD_BASE (self));
+  if (pem)
+    {
+      cert = g_tls_certificate_new_from_pem (pem, -1, NULL);
+      g_free (pem);
+    }
+
+  if (cert)
+    {
+      gnutls_privkey_t privkey;
+      gnutls_privkey_init (&privkey);
+      gnutls_privkey_set_pin_function (privkey, pin_request_cb, self);
+
+      g_tls_certificate_gnutls_copy (G_TLS_CERTIFICATE_GNUTLS (cert),
+                                     self->interaction_id,
+                                     pcert, pcert_length, &privkey);
+      *pkey = privkey;
+    }
+  else
+    {
+      *pcert = NULL;
+      *pcert_length = 0;
+      *pkey = NULL;
+    }
+}
+
 static int
 retrieve_certificate_cb (gnutls_session_t              session,
                          const gnutls_datum_t         *req_ca_rdn,
@@ -1066,7 +1210,6 @@ retrieve_certificate_cb (gnutls_session_t              session,
                          gnutls_privkey_t             *pkey)
 {
   GTlsOperationsThreadGnutls *self = gnutls_transport_get_ptr (session);
-  gboolean had_accepted_cas;
   GByteArray *dn;
   int i;
 
@@ -1076,22 +1219,24 @@ retrieve_certificate_cb (gnutls_session_t              session,
 
   if (is_client (self))
     {
-      had_accepted_cas = self->accepted_cas && self->accepted_cas->len != 0;
+      if (self->accepted_cas)
+        {
+          g_list_free_full (self->accepted_cas, (GDestroyNotify)g_byte_array_unref);
+          self->accepted_cas = NULL;
+        }
 
-      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 = g_list_prepend (self->accepted_cas, dn);
         }
 
-      self->accepted_cas_changed = self->accepted_cas || had_accepted_cas;
+      self->accepted_cas = g_list_reverse (self->accepted_cas);
     }
 
   clear_gnutls_certificate_copy (self);
-  g_tls_connection_gnutls_handshake_thread_get_certificate (conn, pcert, pcert_length, pkey);
+  get_own_certificate (self, pcert, pcert_length, pkey);
 
   if (is_client (self))
     {
@@ -1100,7 +1245,7 @@ retrieve_certificate_cb (gnutls_session_t              session,
           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);
+            get_own_certificate (self, pcert, pcert_length, pkey);
 
           if (*pcert_length == 0)
             {
@@ -1226,15 +1371,21 @@ g_tls_operations_thread_gnutls_finalize (GObject *object)
   g_clear_pointer (&self->creds, gnutls_certificate_free_credentials);
   g_clear_pointer (&self->session_id, g_bytes_unref);
   g_clear_pointer (&self->session_data, g_bytes_unref);
-
+  g_clear_pointer (&self->application_data_buffer, g_byte_array_unref);
   g_clear_pointer (&self->server_identity, g_free);
-  g_clear_pointer (&self->accepted_cas, g_ptr_array_unref);
+  g_clear_pointer (&self->interaction_id, g_free);
 
   g_clear_object (&self->base_iostream);
   g_clear_object (&self->base_socket);
 
   clear_gnutls_certificate_copy (self);
 
+  if (self->accepted_cas)
+    {
+      g_list_free_full (self->accepted_cas, (GDestroyNotify)g_byte_array_unref);
+      self->accepted_cas = NULL;
+    }
+
   g_assert (!self->op_cancellable);
   g_assert (!self->op_error);
 
@@ -1310,6 +1461,9 @@ g_tls_operations_thread_gnutls_constructed (GObject *object)
 static void
 g_tls_operations_thread_gnutls_init (GTlsOperationsThreadGnutls *self)
 {
+  static int unique_interaction_id = 0;
+
+  self->interaction_id = g_strdup_printf ("gtls:%d", unique_interaction_id++);
 }
 
 static void
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index 7f54c84..1d834ca 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -73,7 +73,6 @@ g_tls_server_connection_gnutls_initable_init (GInitable       *initable,
                                               GError         **error)
 {
   GTlsCertificate *cert;
-  gnutls_certificate_credentials_t creds;
 
   if (!g_tls_server_connection_gnutls_parent_initable_iface->init (initable, cancellable, error))
     return FALSE;



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