[glib-networking] gnutls: preserve handshake errors



commit e1a341ff46d6a741312a45ada989454f0daea0d0
Author: Dan Winship <danw gnome org>
Date:   Sun Feb 12 11:30:22 2012 -0500

    gnutls: preserve handshake errors
    
    If an error occurs during handshake, remember it for later, since the
    connection is now essentially broken.

 tls/gnutls/gtlsconnection-gnutls.c |   39 +++++++++++++++----
 tls/tests/connection.c             |   72 +++++++++++++++++++++++++++++++----
 2 files changed, 94 insertions(+), 17 deletions(-)
---
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 3f872a9..65658e3 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -98,6 +98,7 @@ struct _GTlsConnectionGnutlsPrivate
   GTlsDatabase *database;
   gboolean database_is_unset;
   gboolean need_handshake, handshaking, ever_handshaked;
+  GError *handshake_error;
   gboolean closing;
 
   GInputStream *tls_istream;
@@ -248,6 +249,7 @@ g_tls_connection_gnutls_finalize (GObject *object)
   g_clear_object (&gnutls->priv->interaction);
 
   g_clear_error (&gnutls->priv->error);
+  g_clear_error (&gnutls->priv->handshake_error);
 
   G_OBJECT_CLASS (g_tls_connection_gnutls_parent_class)->finalize (object);
 }
@@ -523,14 +525,14 @@ end_gnutls_io (GTlsConnectionGnutls  *gnutls,
   begin_gnutls_io (gnutls, blocking, cancellable);	\
   do {
 
-#define END_GNUTLS_IO(gnutls, ret, errmsg, error)	\
+#define END_GNUTLS_IO(gnutls, ret, errmsg, err)	\
   } while ((ret == GNUTLS_E_AGAIN ||			\
             ret == GNUTLS_E_WARNING_ALERT_RECEIVED) &&	\
            !gnutls->priv->error);			\
-  ret = end_gnutls_io (gnutls, ret, error);		\
-  if (ret < 0 && ret != GNUTLS_E_REHANDSHAKE && error && !*error) \
+  ret = end_gnutls_io (gnutls, ret, err);		\
+  if (ret < 0 && ret != GNUTLS_E_REHANDSHAKE && err && !*err) \
     {							\
-      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,\
+      g_set_error (err, G_TLS_ERROR, G_TLS_ERROR_MISC,\
                    errmsg, gnutls_strerror (ret));	\
     }							\
   ;
@@ -777,6 +779,8 @@ handshake_internal (GTlsConnectionGnutls  *gnutls,
   GTlsCertificateFlags peer_certificate_errors = 0;
   int ret;
 
+  g_clear_error (&gnutls->priv->handshake_error);
+
   if (G_IS_TLS_SERVER_CONNECTION_GNUTLS (gnutls) &&
       gnutls->priv->ever_handshaked && !gnutls->priv->handshaking &&
       !gnutls->priv->need_handshake)
@@ -808,10 +812,15 @@ handshake_internal (GTlsConnectionGnutls  *gnutls,
 
   BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
   ret = gnutls_handshake (gnutls->priv->session);
-  END_GNUTLS_IO (gnutls, ret, _("Error performing TLS handshake: %s"), error);
+  END_GNUTLS_IO (gnutls, ret, _("Error performing TLS handshake: %s"),
+		 &gnutls->priv->handshake_error);
 
   if (ret == GNUTLS_E_AGAIN)
-    return FALSE;
+    {
+      g_propagate_error (error, gnutls->priv->handshake_error);
+      gnutls->priv->handshake_error = NULL;
+      return FALSE;
+    }
 
   gnutls->priv->handshaking = FALSE;
   gnutls->priv->need_handshake = FALSE;
@@ -855,13 +864,20 @@ handshake_internal (GTlsConnectionGnutls  *gnutls,
 
       if (!accepted)
 	{
-	  g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+	  g_set_error_literal (&gnutls->priv->handshake_error,
+			       G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
 			       _("Unacceptable TLS certificate"));
+	  if (error)
+	    *error = g_error_copy (gnutls->priv->handshake_error);
 	  return FALSE;
 	}
     }
 
-  G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->finish_handshake (gnutls, ret == 0, error);
+  G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->
+    finish_handshake (gnutls, ret == 0, &gnutls->priv->handshake_error);
+
+  if (gnutls->priv->handshake_error && error)
+    *error = g_error_copy (gnutls->priv->handshake_error);
   return (ret == 0);
 }
 
@@ -871,6 +887,13 @@ handshake_in_progress_or_failed (GTlsConnectionGnutls  *gnutls,
 				 GCancellable          *cancellable,
 				 GError               **error)
 {
+  if (gnutls->priv->handshake_error)
+    {
+      if (error)
+	*error = g_error_copy (gnutls->priv->handshake_error);
+      return TRUE;
+    }
+
   if (!(gnutls->priv->need_handshake || gnutls->priv->handshaking))
     return FALSE;
 
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 7ad66ad..30fdb4e 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -40,6 +40,7 @@ typedef struct {
   GTlsAuthenticationMode auth_mode;
   gboolean rehandshake;
   GTlsCertificateFlags accept_flags;
+  GError *read_error;
 } TestConnection;
 
 static void
@@ -107,6 +108,7 @@ teardown_connection (TestConnection *test, gconstpointer data)
   g_object_unref (test->address);
   g_object_unref (test->identity);
   g_main_loop_unref (test->loop);
+  g_clear_error (&test->read_error);
 }
 
 static gboolean
@@ -249,19 +251,20 @@ on_input_read_finish (GObject        *object,
                       gpointer        user_data)
 {
   TestConnection *test = user_data;
-  GError *error = NULL;
   gchar *line, *check;
 
   line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (object), res,
-                                               NULL, &error);
-  g_assert_no_error (error);
-  g_assert (line);
+                                               NULL, &test->read_error);
+  if (!test->read_error)
+    {
+      g_assert (line);
 
-  check = g_strdup (TEST_DATA);
-  g_strstrip (check);
-  g_assert_cmpstr (line, ==, check);
-  g_free (check);
-  g_free (line);
+      check = g_strdup (TEST_DATA);
+      g_strstrip (check);
+      g_assert_cmpstr (line, ==, check);
+      g_free (check);
+      g_free (line);
+    }
 
   g_main_loop_quit (test->loop);
 }
@@ -297,6 +300,7 @@ test_basic_connection (TestConnection *test,
 
   read_test_data_async (test);
   g_main_loop_run (test->loop);
+  g_assert_no_error (test->read_error);
 }
 
 static void
@@ -324,6 +328,7 @@ test_verified_connection (TestConnection *test,
 
   read_test_data_async (test);
   g_main_loop_run (test->loop);
+  g_assert_no_error (test->read_error);
 }
 
 static void
@@ -358,6 +363,7 @@ test_client_auth_connection (TestConnection *test,
 
   read_test_data_async (test);
   g_main_loop_run (test->loop);
+  g_assert_no_error (test->read_error);
 }
 
 static void
@@ -394,6 +400,52 @@ test_connection_no_database (TestConnection *test,
 
   read_test_data_async (test);
   g_main_loop_run (test->loop);
+  g_assert_no_error (test->read_error);
+}
+
+static void
+handshake_failed_cb (GObject      *source,
+		     GAsyncResult *result,
+		     gpointer      user_data)
+{
+  TestConnection *test = user_data;
+  GError *error = NULL;
+
+  g_tls_connection_handshake_finish (G_TLS_CONNECTION (test->client_connection),
+				     result, &error);
+  g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+  g_clear_error (&error);
+
+  g_main_loop_quit (test->loop);
+}
+
+static void
+test_failed_connection (TestConnection *test,
+			gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+  GSocketConnectable *bad_addr;
+
+  connection = start_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+
+  bad_addr = g_network_address_new ("wrong.example.com", 80);
+  test->client_connection = g_tls_client_connection_new (connection, bad_addr, &error);
+  g_object_unref (bad_addr);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  g_tls_connection_handshake_async (G_TLS_CONNECTION (test->client_connection),
+				    G_PRIORITY_DEFAULT, NULL,
+				    handshake_failed_cb, test);
+  g_main_loop_run (test->loop);
+
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
 }
 
 static void
@@ -506,6 +558,8 @@ main (int   argc,
               setup_connection, test_client_auth_rehandshake, teardown_connection);
   g_test_add ("/tls/connection/no-database", TestConnection, NULL,
               setup_connection, test_connection_no_database, teardown_connection);
+  g_test_add ("/tls/connection/failed", TestConnection, NULL,
+              setup_connection, test_failed_connection, teardown_connection);
   g_test_add ("/tls/connection/socket-client", TestConnection, NULL,
               setup_connection, test_connection_socket_client, teardown_connection);
   g_test_add ("/tls/connection/socket-client-failed", TestConnection, NULL,



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