[gnome-online-accounts/gnome-3-8] mail-client: Learn to do STARTTLS connections



commit 7a8ba7da32b51b57cc607ab3fa231a9fae38cabe
Author: Debarshi Ray <debarshir gnome org>
Date:   Sat Mar 9 02:31:58 2013 +0100

    mail-client: Learn to do STARTTLS connections
    
    What we were doing previously was completely broken. Once the
    GoaMailAuth implementations have sent the STARTTLS command over an
    unencrypted channel, we have to initiate the handshaking ourselves to
    upgrade the connection.
    
    There is no need to use g_tls_client_connection_set_use_ssl3. For SSL
    connections on a dedicated port, g_socket_client_set_tls is enough.
    
    Fixes: https://bugzilla.gnome.org/695355

 src/goabackend/goamailclient.c |  172 ++++++++++++++++++++++++++++++++++++----
 1 files changed, 155 insertions(+), 17 deletions(-)
---
diff --git a/src/goabackend/goamailclient.c b/src/goabackend/goamailclient.c
index 8449d54..4f34a5a 100644
--- a/src/goabackend/goamailclient.c
+++ b/src/goabackend/goamailclient.c
@@ -74,13 +74,17 @@ typedef struct
   GCancellable *cancellable;
   GDataInputStream *input;
   GDataOutputStream *output;
+  GIOStream *tls_conn;
   GSimpleAsyncResult *res;
+  GSocket *socket;
   GSocketClient *sc;
   GSocketConnection *conn;
   GTlsCertificateFlags cert_flags;
-  GTlsClientConnection *tls_conn;
   GoaMailAuth *auth;
   GoaTlsType tls_type;
+  gboolean accept_ssl_errors;
+  gchar *host_and_port;
+  guint16 default_port;
 } CheckData;
 
 static void
@@ -92,8 +96,10 @@ mail_client_check_data_free (CheckData *data)
   g_clear_object (&data->cancellable);
   g_clear_object (&data->input);
   g_clear_object (&data->output);
+  g_clear_object (&data->socket);
   g_clear_object (&data->conn);
   g_clear_object (&data->tls_conn);
+  g_free (data->host_and_port);
   g_slice_free (CheckData, data);
 }
 
@@ -122,9 +128,9 @@ mail_client_check_event_cb (GSocketClient *sc,
   if (event != G_SOCKET_CLIENT_TLS_HANDSHAKING)
     return;
 
-  data->tls_conn = G_TLS_CLIENT_CONNECTION (g_object_ref (connection));
-  if (data->tls_type == GOA_TLS_TYPE_SSL)
-    g_tls_client_connection_set_use_ssl3 (data->tls_conn, TRUE);
+  data->tls_conn = g_object_ref (connection);
+  if (data->accept_ssl_errors)
+    g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (data->tls_conn), 0);
 
   g_signal_connect (data->tls_conn,
                     "accept-certificate",
@@ -133,7 +139,7 @@ mail_client_check_event_cb (GSocketClient *sc,
 }
 
 static void
-mail_client_check_auth_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+mail_client_check_auth_run_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
   CheckData *data = user_data;
   GError *error;
@@ -162,6 +168,133 @@ mail_client_check_auth_cb (GObject *source_object, GAsyncResult *res, gpointer u
 }
 
 static void
+mail_client_check_tls_conn_handshake_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  CheckData *data = user_data;
+  GDataInputStream *input;
+  GDataOutputStream *output;
+  GInputStream *base_input;
+  GError *error;
+  GOutputStream *base_output;
+
+  input = NULL;
+  output = NULL;
+
+  error = NULL;
+  if (!g_tls_connection_handshake_finish (G_TLS_CONNECTION (data->tls_conn), res, &error))
+    {
+      goa_warning ("g_tls_connection_handshake() failed: %s (%s, %d)",
+                   error->message,
+                   g_quark_to_string (error->domain),
+                   error->code);
+      if (error->code == G_TLS_ERROR_BAD_CERTIFICATE)
+        {
+          GError *tls_error;
+
+          tls_error = NULL;
+          goa_utils_set_error_ssl (&tls_error, data->cert_flags);
+          g_simple_async_result_take_error (data->res, tls_error);
+          g_error_free (error);
+        }
+      else
+        {
+          error->domain = GOA_ERROR;
+          error->code = GOA_ERROR_FAILED; /* TODO: more specific */
+          g_simple_async_result_take_error (data->res, error);
+        }
+
+      goto error;
+    }
+
+  g_clear_object (&data->conn);
+  data->conn = g_tcp_wrapper_connection_new (data->tls_conn, data->socket);
+
+  base_input = g_io_stream_get_input_stream (G_IO_STREAM (data->conn));
+  input = g_data_input_stream_new (base_input);
+  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (input), FALSE);
+  g_data_input_stream_set_newline_type (input, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
+  goa_mail_auth_set_input (data->auth, input);
+
+  base_output = g_io_stream_get_output_stream (G_IO_STREAM (data->conn));
+  output = g_data_output_stream_new (base_output);
+  g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (output), FALSE);
+  goa_mail_auth_set_output (data->auth, output);
+
+  goa_mail_auth_run (data->auth, data->cancellable, mail_client_check_auth_run_cb, data);
+  goto out;
+
+ error:
+  g_simple_async_result_set_op_res_gboolean (data->res, FALSE);
+  g_simple_async_result_complete_in_idle (data->res);
+  mail_client_check_data_free (data);
+
+ out:
+  g_clear_object (&input);
+  g_clear_object (&output);
+}
+
+static void
+mail_client_check_auth_starttls_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  CheckData *data = user_data;
+  GSocketConnectable *server_identity;
+  GError *error;
+
+  server_identity = NULL;
+
+  error = NULL;
+  if (!goa_mail_auth_starttls_finish (data->auth, res, &error))
+    {
+      goa_warning ("goa_mail_auth_starttls() failed: %s (%s, %d)",
+                   error->message,
+                   g_quark_to_string (error->domain),
+                   error->code);
+      g_simple_async_result_take_error (data->res, error);
+      goto error;
+    }
+
+  error = NULL;
+  server_identity = g_network_address_parse (data->host_and_port, data->default_port, &error);
+  if (server_identity == NULL)
+    {
+      g_simple_async_result_take_error (data->res, error);
+      goto error;
+    }
+
+  error = NULL;
+  data->tls_conn = g_tls_client_connection_new (G_IO_STREAM (data->conn), server_identity, &error);
+  if (data->tls_conn == NULL)
+    {
+      g_simple_async_result_take_error (data->res, error);
+      goto error;
+    }
+
+  if (data->accept_ssl_errors)
+    g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (data->tls_conn), 0);
+
+  g_signal_connect (data->tls_conn,
+                    "accept-certificate",
+                    G_CALLBACK (mail_client_check_accept_certificate_cb),
+                    data);
+
+  g_tls_connection_handshake_async (G_TLS_CONNECTION (data->tls_conn),
+                                    G_PRIORITY_DEFAULT,
+                                    data->cancellable,
+                                    mail_client_check_tls_conn_handshake_cb,
+                                    data);
+
+  goto out;
+
+ error:
+  g_simple_async_result_set_op_res_gboolean (data->res, FALSE);
+  g_simple_async_result_complete_in_idle (data->res);
+  mail_client_check_data_free (data);
+
+ out:
+  g_clear_object (&server_identity);
+}
+
+static void
 mail_client_check_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
   CheckData *data = user_data;
@@ -170,7 +303,6 @@ mail_client_check_connect_cb (GObject *source_object, GAsyncResult *res, gpointe
   GInputStream *base_input;
   GError *error;
   GOutputStream *base_output;
-  GSocket *socket;
 
   error = NULL;
   data->conn = g_socket_client_connect_to_host_finish (data->sc, res, &error);
@@ -200,8 +332,8 @@ mail_client_check_connect_cb (GObject *source_object, GAsyncResult *res, gpointe
     }
 
   /* fail quickly */
-  socket = g_socket_connection_get_socket (data->conn);
-  g_socket_set_timeout (socket, COMMAND_TIMEOUT_SEC);
+  data->socket = g_object_ref (g_socket_connection_get_socket (data->conn));
+  g_socket_set_timeout (data->socket, COMMAND_TIMEOUT_SEC);
 
   base_input = g_io_stream_get_input_stream (G_IO_STREAM (data->conn));
   input = g_data_input_stream_new (base_input);
@@ -216,7 +348,11 @@ mail_client_check_connect_cb (GObject *source_object, GAsyncResult *res, gpointe
   goa_mail_auth_set_output (data->auth, output);
   g_object_unref (output);
 
-  goa_mail_auth_run (data->auth, data->cancellable, mail_client_check_auth_cb, data);
+  if (data->tls_type == GOA_TLS_TYPE_STARTTLS)
+    goa_mail_auth_starttls (data->auth, data->cancellable, mail_client_check_auth_starttls_cb, data);
+  else
+    goa_mail_auth_run (data->auth, data->cancellable, mail_client_check_auth_run_cb, data);
+
   return;
 
  error:
@@ -247,16 +383,18 @@ goa_mail_client_check (GoaMailClient       *client,
   data->res = g_simple_async_result_new (G_OBJECT (client), callback, user_data, goa_mail_client_check);
 
   data->sc = g_socket_client_new ();
-  if (tls_type != GOA_TLS_TYPE_NONE)
-    g_socket_client_set_tls (data->sc, TRUE);
-  g_signal_connect (data->sc, "event", G_CALLBACK (mail_client_check_event_cb), data);
+  if (tls_type == GOA_TLS_TYPE_SSL)
+    {
+      g_socket_client_set_tls (data->sc, TRUE);
+      g_signal_connect (data->sc, "event", G_CALLBACK (mail_client_check_event_cb), data);
+    }
 
+  data->host_and_port = g_strdup (host_and_port);
   data->tls_type = tls_type;
+  data->accept_ssl_errors = accept_ssl_errors;
+  data->default_port = default_port;
   data->auth = g_object_ref (auth);
 
-  if (accept_ssl_errors)
-    g_socket_client_set_tls_validation_flags (data->sc, 0);
-
   if (cancellable != NULL)
     {
       data->cancellable = g_object_ref (cancellable);
@@ -264,8 +402,8 @@ goa_mail_client_check (GoaMailClient       *client,
     }
 
   g_socket_client_connect_to_host_async (data->sc,
-                                         host_and_port,
-                                         default_port,
+                                         data->host_and_port,
+                                         data->default_port,
                                          data->cancellable,
                                          mail_client_check_connect_cb,
                                          data);


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