Imported Upstream version 2.74.0
[platform/upstream/glib-networking.git] / tls / tests / connection.c
index 751490d..0f8aa2d 100644 (file)
@@ -1,11 +1,13 @@
-/* GIO TLS tests
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO TLS tests
  *
  * Copyright 2011 Collabora, Ltd.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * Public License along with this library; if not, see
  * <http://www.gnu.org/licenses/>.
  *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
  * Author: Stef Walter <stefw@collabora.co.uk>
  */
 
+#include "config.h"
+
+#include "mock-interaction.h"
+
 #include <gio/gio.h>
 
 #include <sys/types.h>
 #include <string.h>
 
-#define TEST_FILE(name) (SRCDIR "/files/" name)
+#ifdef BACKEND_IS_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/pkcs11.h>
+#else
+#include "openssl-include.h"
+#endif
+
+static const gchar *
+tls_test_file_path (const char *name)
+{
+  const gchar *const_path;
+  gchar *path;
+
+  path = g_test_build_filename (G_TEST_DIST, "files", name, NULL);
+  if (!g_path_is_absolute (path))
+    {
+      gchar *cwd, *abs;
+
+      cwd = g_get_current_dir ();
+      abs = g_build_filename (cwd, path, NULL);
+      g_free (cwd);
+      g_free (path);
+      path = abs;
+    }
+
+  const_path = g_intern_string (path);
+  g_free (path);
+  return const_path;
+}
 
 #define TEST_DATA "You win again, gravity!\n"
 #define TEST_DATA_LENGTH 24
 
+typedef enum {
+  WRITE_THEN_CLOSE,
+  WRITE_THEN_WAIT,
+  HANDSHAKE_ONLY
+} ServerConnectionReceivedStrategy;
+
 typedef struct {
+  GMainContext *context;
   GMainLoop *loop;
   GSocketService *service;
   GTlsDatabase *database;
@@ -41,6 +85,14 @@ typedef struct {
   gboolean rehandshake;
   GTlsCertificateFlags accept_flags;
   GError *read_error;
+  GError *server_error;
+  gboolean ignore_client_close_error;
+  ServerConnectionReceivedStrategy connection_received_strategy;
+  gboolean server_running;
+  gboolean server_ever_handshaked;
+  GTlsCertificate *server_certificate;
+  const gchar * const *server_protocols;
+  gulong incoming_connection_delay;
 
   char buf[128];
   gssize nread, nwrote;
@@ -49,21 +101,45 @@ typedef struct {
 static void
 setup_connection (TestConnection *test, gconstpointer data)
 {
-  GInetAddress *inet;
-  guint16 port;
-
-  test->loop = g_main_loop_new (NULL, FALSE);
-
+  test->context = g_main_context_default ();
+  test->loop = g_main_loop_new (test->context, FALSE);
   test->auth_mode = G_TLS_AUTHENTICATION_NONE;
+}
 
-  /* This is where the server listens and the client connects */
-  port = g_random_int_range (50000, 65000);
-  inet = g_inet_address_new_from_string ("127.0.0.1");
-  test->address = G_SOCKET_ADDRESS (g_inet_socket_address_new (inet, port));
-  g_object_unref (inet);
+/* Waits about 10 seconds for @var to be NULL/FALSE */
+#define WAIT_UNTIL_UNSET(var)                                \
+  if (var)                                                   \
+    {                                                        \
+      int i;                                                 \
+                                                             \
+      for (i = 0; i < 13 && (var); i++)                      \
+        {                                                    \
+          g_usleep (1000 * (1 << i));                        \
+          g_main_context_iteration (test->context, FALSE);   \
+        }                                                    \
+                                                             \
+      g_assert_true (!(var));                                \
+    }
+
+/* Waits about 10 seconds for @var's ref_count to drop to 1 */
+#define WAIT_UNTIL_UNREFFED(var)                                \
+  if (var)                                                      \
+    {                                                           \
+      int i;                                                    \
+                                                                \
+      for (i = 0; i < 13 && G_OBJECT (var)->ref_count > 1; i++) \
+        {                                                       \
+          g_usleep (1000 * (1 << i));                           \
+          g_main_context_iteration (NULL, FALSE);               \
+        }                                                       \
+                                                                \
+      g_assert_cmpuint (G_OBJECT (var)->ref_count, ==, 1);      \
+    }
 
-  /* The identity matches the server certificate */
-  test->identity = g_network_address_new ("server.example.com", port);
+static void
+wait_until_server_finished (TestConnection *test)
+{
+    WAIT_UNTIL_UNSET (test->server_running);
 }
 
 static void
@@ -75,61 +151,93 @@ teardown_connection (TestConnection *test, gconstpointer data)
       /* The outstanding accept_async will hold a ref on test->service,
        * which we want to wait for it to release if we're valgrinding.
        */
-      g_object_add_weak_pointer (G_OBJECT (test->service), (gpointer *)&test->service);
+      g_socket_listener_close (G_SOCKET_LISTENER (test->service));
+      WAIT_UNTIL_UNREFFED (test->service);
       g_object_unref (test->service);
-      while (test->service)
-       g_main_context_iteration (NULL, FALSE);
+      test->service = NULL;
     }
 
   if (test->server_connection)
     {
-      g_assert (G_IS_TLS_SERVER_CONNECTION (test->server_connection));
-      g_object_add_weak_pointer (G_OBJECT (test->server_connection),
-                                (gpointer *)&test->server_connection);
+      WAIT_UNTIL_UNSET (test->server_running);
+
+      WAIT_UNTIL_UNREFFED (test->server_connection);
       g_object_unref (test->server_connection);
-      g_assert (test->server_connection == NULL);
+      test->server_connection = NULL;
     }
 
   if (test->client_connection)
     {
-      g_assert (G_IS_TLS_CLIENT_CONNECTION (test->client_connection));
-      g_object_add_weak_pointer (G_OBJECT (test->client_connection),
-                                (gpointer *)&test->client_connection);
+      WAIT_UNTIL_UNREFFED (test->client_connection);
       g_object_unref (test->client_connection);
-      g_assert (test->client_connection == NULL);
+      test->client_connection = NULL;
     }
 
   if (test->database)
     {
-      g_assert (G_IS_TLS_DATABASE (test->database));
-      g_object_add_weak_pointer (G_OBJECT (test->database),
-                                (gpointer *)&test->database);
+      WAIT_UNTIL_UNREFFED (test->database);
       g_object_unref (test->database);
-      g_assert (test->database == NULL);
+      test->database = NULL;
     }
 
-  g_object_unref (test->address);
-  g_object_unref (test->identity);
+  g_clear_object (&test->address);
+  g_clear_object (&test->identity);
+  g_clear_object (&test->server_certificate);
+
   g_main_loop_unref (test->loop);
+
   g_clear_error (&test->read_error);
+  g_clear_error (&test->server_error);
+}
+
+static void
+start_server (TestConnection *test)
+{
+  GInetAddress *inet;
+  GSocketAddress *addr;
+  GInetSocketAddress *iaddr;
+  GError *error = NULL;
+
+  inet = g_inet_address_new_from_string ("127.0.0.1");
+  addr = g_inet_socket_address_new (inet, 0);
+  g_object_unref (inet);
+
+  g_socket_listener_add_address (G_SOCKET_LISTENER (test->service), addr,
+                                 G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP,
+                                 NULL, &test->address, &error);
+  g_assert_no_error (error);
+
+  g_object_unref (addr);
+
+  /* The hostname in test->identity matches the server certificate. */
+  iaddr = G_INET_SOCKET_ADDRESS (test->address);
+  test->identity = g_network_address_new ("server.example.com",
+                                          g_inet_socket_address_get_port (iaddr));
+
+  test->server_running = TRUE;
 }
 
 static gboolean
-on_accept_certificate (GTlsClientConnection *conn, GTlsCertificate *cert,
-                       GTlsCertificateFlags errors, gpointer user_data)
+on_accept_certificate (GTlsConnection       *conn,
+                       GTlsCertificate      *cert,
+                       GTlsCertificateFlags  errors,
+                       gpointer              user_data)
 {
   TestConnection *test = user_data;
+
+  g_assert_true (G_IS_TLS_CERTIFICATE (cert));
+
   return errors == test->accept_flags;
 }
 
 static void on_output_write_finish (GObject        *object,
-                                   GAsyncResult   *res,
-                                   gpointer        user_data);
+                                    GAsyncResult   *res,
+                                    gpointer        user_data);
 
 static void
 on_rehandshake_finish (GObject        *object,
-                      GAsyncResult   *res,
-                      gpointer        user_data)
+                       GAsyncResult   *res,
+                       gpointer        user_data)
 {
   TestConnection *test = user_data;
   GError *error = NULL;
@@ -140,19 +248,31 @@ on_rehandshake_finish (GObject        *object,
 
   stream = g_io_stream_get_output_stream (test->server_connection);
   g_output_stream_write_async (stream, TEST_DATA + TEST_DATA_LENGTH / 2,
-                              TEST_DATA_LENGTH / 2,
+                               TEST_DATA_LENGTH / 2,
                                G_PRIORITY_DEFAULT, NULL,
                                on_output_write_finish, test);
 }
 
 static void
-on_output_close_finish (GObject        *object,
+on_server_close_finish (GObject        *object,
                         GAsyncResult   *res,
                         gpointer        user_data)
 {
+  TestConnection *test = user_data;
   GError *error = NULL;
-  g_output_stream_close_finish (G_OUTPUT_STREAM (object), res, &error);
-  g_assert_no_error (error);
+
+  g_io_stream_close_finish (G_IO_STREAM (object), res, &error);
+  // FIXME: https://gitlab.gnome.org/GNOME/glib-networking/issues/105
+  // g_assert_no_error (error);
+
+  test->server_running = FALSE;
+}
+
+static void
+close_server_connection (TestConnection *test)
+{
+  g_io_stream_close_async (test->server_connection, G_PRIORITY_DEFAULT, NULL,
+                           on_server_close_finish, test);
 }
 
 static void
@@ -161,22 +281,32 @@ on_output_write_finish (GObject        *object,
                         gpointer        user_data)
 {
   TestConnection *test = user_data;
-  GError *error = NULL;
 
-  g_output_stream_write_finish (G_OUTPUT_STREAM (object), res, &error);
-  g_assert_no_error (error);
+  g_assert_no_error (test->server_error);
+  g_output_stream_write_finish (G_OUTPUT_STREAM (object), res, &test->server_error);
 
-  if (test->rehandshake)
+  if (!test->server_error && test->rehandshake)
     {
       test->rehandshake = FALSE;
       g_tls_connection_handshake_async (G_TLS_CONNECTION (test->server_connection),
-                                       G_PRIORITY_DEFAULT, NULL,
-                                       on_rehandshake_finish, test);
+                                        G_PRIORITY_DEFAULT, NULL,
+                                        on_rehandshake_finish, test);
       return;
     }
 
-  g_output_stream_close_async (G_OUTPUT_STREAM (object), G_PRIORITY_DEFAULT, NULL,
-                               on_output_close_finish, test);
+  if (test->connection_received_strategy == WRITE_THEN_CLOSE)
+    close_server_connection (test);
+}
+
+static void
+on_server_handshake_finish (GObject      *object,
+                            GAsyncResult *res,
+                            gpointer      user_data)
+{
+  TestConnection *test = user_data;
+  g_tls_connection_handshake_finish (G_TLS_CONNECTION (object), res, &test->server_error);
+  g_assert_no_error (test->server_error);
+  test->server_ever_handshaked = TRUE;
 }
 
 static gboolean
@@ -190,13 +320,21 @@ on_incoming_connection (GSocketService     *service,
   GTlsCertificate *cert;
   GError *error = NULL;
 
-  cert = g_tls_certificate_new_from_file (TEST_FILE ("server-and-key.pem"), &error);
-  g_assert_no_error (error);
+  if (test->incoming_connection_delay != 0)
+    g_usleep (test->incoming_connection_delay);
 
+  g_assert_null (test->server_connection);
   test->server_connection = g_tls_server_connection_new (G_IO_STREAM (connection),
-                                                         cert, &error);
+                                                         test->server_certificate, &error);
   g_assert_no_error (error);
-  g_object_unref (cert);
+
+  if (!test->server_certificate)
+    {
+      cert = g_tls_certificate_new_from_file (tls_test_file_path ("server-and-key.pem"), &error);
+      g_assert_no_error (error);
+      g_tls_connection_set_certificate (G_TLS_CONNECTION (test->server_connection), cert);
+      g_object_unref (cert);
+    }
 
   g_object_set (test->server_connection, "authentication-mode", test->auth_mode, NULL);
   g_signal_connect (test->server_connection, "accept-certificate",
@@ -205,39 +343,55 @@ on_incoming_connection (GSocketService     *service,
   if (test->database)
     g_tls_connection_set_database (G_TLS_CONNECTION (test->server_connection), test->database);
 
+  if (test->server_protocols)
+    {
+      g_tls_connection_set_advertised_protocols (G_TLS_CONNECTION (test->server_connection),
+                                                 test->server_protocols);
+    }
+
   stream = g_io_stream_get_output_stream (test->server_connection);
 
-  g_output_stream_write_async (stream, TEST_DATA,
-                              test->rehandshake ? TEST_DATA_LENGTH / 2 : TEST_DATA_LENGTH,
-                               G_PRIORITY_DEFAULT, NULL,
-                               on_output_write_finish, test);
+  if (test->connection_received_strategy == WRITE_THEN_CLOSE ||
+      test->connection_received_strategy == WRITE_THEN_WAIT)
+    {
+      g_output_stream_write_async (stream, TEST_DATA,
+                                   test->rehandshake ? TEST_DATA_LENGTH / 2 : TEST_DATA_LENGTH,
+                                   G_PRIORITY_DEFAULT, NULL,
+                                   on_output_write_finish, test);
+    }
+  else
+    {
+      g_tls_connection_handshake_async (G_TLS_CONNECTION (test->server_connection),
+                                        G_PRIORITY_DEFAULT, NULL,
+                                        on_server_handshake_finish, test);
+    }
+
   return FALSE;
 }
 
 static void
-start_async_server_service (TestConnection *test, GTlsAuthenticationMode auth_mode)
+start_async_server_service (TestConnection                   *test,
+                            GTlsAuthenticationMode            auth_mode,
+                            ServerConnectionReceivedStrategy  connection_received_strategy)
 {
-  GError *error = NULL;
-
   test->service = g_socket_service_new ();
-  g_socket_listener_add_address (G_SOCKET_LISTENER (test->service),
-                                 G_SOCKET_ADDRESS (test->address),
-                                 G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP,
-                                 NULL, NULL, &error);
-  g_assert_no_error (error);
+  start_server (test);
 
   test->auth_mode = auth_mode;
   g_signal_connect (test->service, "incoming", G_CALLBACK (on_incoming_connection), test);
+
+  test->connection_received_strategy = connection_received_strategy;
 }
 
 static GIOStream *
-start_async_server_and_connect_to_it (TestConnection *test, GTlsAuthenticationMode auth_mode)
+start_async_server_and_connect_to_it (TestConnection         *test,
+                                      GTlsAuthenticationMode  auth_mode)
 {
   GSocketClient *client;
   GError *error = NULL;
   GSocketConnection *connection;
 
-  start_async_server_service (test, auth_mode);
+  start_async_server_service (test, auth_mode, WRITE_THEN_CLOSE);
 
   client = g_socket_client_new ();
   connection = g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
@@ -250,9 +404,9 @@ start_async_server_and_connect_to_it (TestConnection *test, GTlsAuthenticationMo
 
 static void
 run_echo_server (GThreadedSocketService *service,
-                GSocketConnection      *connection,
-                GObject                *source_object,
-                gpointer                user_data)
+                 GSocketConnection      *connection,
+                 GObject                *source_object,
+                 gpointer                user_data)
 {
   TestConnection *test = user_data;
   GTlsConnection *tlsconn;
@@ -263,8 +417,15 @@ run_echo_server (GThreadedSocketService *service,
   gssize nread, nwrote, total;
   gchar buf[128];
 
-  cert = g_tls_certificate_new_from_file (TEST_FILE ("server-and-key.pem"), &error);
-  g_assert_no_error (error);
+  if (test->server_certificate)
+    {
+      cert = g_object_ref (test->server_certificate);
+    }
+  else
+    {
+      cert = g_tls_certificate_new_from_file (tls_test_file_path ("server-and-key.pem"), &error);
+      g_assert_no_error (error);
+    }
 
   test->server_connection = g_tls_server_connection_new (G_IO_STREAM (connection),
                                                          cert, &error);
@@ -285,34 +446,32 @@ run_echo_server (GThreadedSocketService *service,
       g_assert_cmpint (nread, >=, 0);
 
       if (nread == 0)
-       break;
+        break;
 
       for (total = 0; total < nread; total += nwrote)
-       {
-         nwrote = g_output_stream_write (ostream, buf + total, nread - total, NULL, &error);
-         g_assert_no_error (error);
-       }
+        {
+          nwrote = g_output_stream_write (ostream, buf + total, nread - total, NULL, &error);
+          g_assert_no_error (error);
+        }
 
       if (test->rehandshake)
-       {
-         test->rehandshake = FALSE;
-         g_tls_connection_handshake (tlsconn, NULL, &error);
-         g_assert_no_error (error);
-       }
+        {
+          test->rehandshake = FALSE;
+          g_tls_connection_handshake (tlsconn, NULL, &error);
+          g_assert_no_error (error);
+        }
     }
+
+  g_io_stream_close (test->server_connection, NULL, &error);
+  g_assert_no_error (error);
+  test->server_running = FALSE;
 }
 
 static void
 start_echo_server_service (TestConnection *test)
 {
-  GError *error = NULL;
-
   test->service = g_threaded_socket_service_new (5);
-  g_socket_listener_add_address (G_SOCKET_LISTENER (test->service),
-                                 G_SOCKET_ADDRESS (test->address),
-                                 G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP,
-                                 NULL, NULL, &error);
-  g_assert_no_error (error);
+  start_server (test);
 
   g_signal_connect (test->service, "run", G_CALLBACK (run_echo_server), test);
 }
@@ -336,6 +495,26 @@ start_echo_server_and_connect_to_it (TestConnection *test)
 }
 
 static void
+on_client_connection_close_finish (GObject        *object,
+                                   GAsyncResult   *res,
+                                   gpointer        user_data)
+{
+  TestConnection *test = user_data;
+  GError *error = NULL;
+
+  g_io_stream_close_finish (G_IO_STREAM (object), res, &error);
+
+  /* FIXME: When running test_client_auth_failure(), GnuTLS throws a
+   * G_TLS_CERTIFICATE_REQUIRED error here for TLS 1.3, but no error for TLS
+   * 1.2. What's up with this difference? Can we have consistent errors?
+   */
+  if (!test->ignore_client_close_error)
+    g_assert_no_error (error);
+
+  g_main_loop_quit (test->loop);
+}
+
+static void
 on_input_read_finish (GObject        *object,
                       GAsyncResult   *res,
                       gpointer        user_data)
@@ -347,7 +526,7 @@ on_input_read_finish (GObject        *object,
                                                NULL, &test->read_error);
   if (!test->read_error)
     {
-      g_assert (line);
+      g_assert_nonnull (line);
 
       check = g_strdup (TEST_DATA);
       g_strstrip (check);
@@ -356,7 +535,8 @@ on_input_read_finish (GObject        *object,
       g_free (line);
     }
 
-  g_main_loop_quit (test->loop);
+  g_io_stream_close_async (test->client_connection, G_PRIORITY_DEFAULT,
+                           NULL, on_client_connection_close_finish, test);
 }
 
 static void
@@ -365,7 +545,7 @@ read_test_data_async (TestConnection *test)
   GDataInputStream *stream;
 
   stream = g_data_input_stream_new (g_io_stream_get_input_stream (test->client_connection));
-  g_assert (stream);
+  g_assert_nonnull (stream);
 
   g_data_input_stream_read_line_async (stream, G_PRIORITY_DEFAULT, NULL,
                                        on_input_read_finish, test);
@@ -390,7 +570,10 @@ test_basic_connection (TestConnection *test,
 
   read_test_data_async (test);
   g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
   g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
 }
 
 static void
@@ -400,14 +583,14 @@ test_verified_connection (TestConnection *test,
   GIOStream *connection;
   GError *error = NULL;
 
-  test->database = g_tls_file_database_new (TEST_FILE ("ca-roots.pem"), &error);
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
   g_assert_no_error (error);
-  g_assert (test->database);
+  g_assert_nonnull (test->database);
 
   connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
   test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
   g_assert_no_error (error);
-  g_assert (test->client_connection);
+  g_assert_nonnull (test->client_connection);
   g_object_unref (connection);
 
   g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
@@ -418,406 +601,2479 @@ test_verified_connection (TestConnection *test,
 
   read_test_data_async (test);
   g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
   g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
 }
 
 static void
-test_client_auth_connection (TestConnection *test,
-                             gconstpointer   data)
+test_verified_chain (TestConnection *test,
+                     gconstpointer   data)
 {
-  GIOStream *connection;
+  GTlsBackend *backend;
+  GTlsCertificate *server_cert;
+  GTlsCertificate *intermediate_cert;
+  char *cert_data = NULL;
+  char *key_data = NULL;
   GError *error = NULL;
-  GTlsCertificate *cert;
 
-  test->database = g_tls_file_database_new (TEST_FILE ("ca-roots.pem"), &error);
-  g_assert_no_error (error);
-  g_assert (test->database);
+  backend = g_tls_backend_get_default ();
 
-  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
-  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  /* Prepare the intermediate cert. */
+  intermediate_cert = g_tls_certificate_new_from_file (tls_test_file_path ("intermediate-ca.pem"), &error);
   g_assert_no_error (error);
-  g_assert (test->client_connection);
-  g_object_unref (connection);
-
-  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+  g_assert_nonnull (intermediate_cert);
 
-  cert = g_tls_certificate_new_from_file (TEST_FILE ("client-and-key.pem"), &error);
+  /* Prepare the server cert. */
+  g_clear_pointer (&cert_data, g_free);
+  g_file_get_contents (tls_test_file_path ("server-intermediate.pem"),
+                       &cert_data, NULL, &error);
   g_assert_no_error (error);
+  g_assert_nonnull (cert_data);
 
-  g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert);
-  g_object_unref (cert);
-
-  /* All validation in this test */
-  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
-                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+  g_file_get_contents (tls_test_file_path ("server-intermediate-key.pem"),
+                       &key_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (key_data);
+
+  server_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                                NULL, &error,
+                                "issuer", intermediate_cert,
+                                "certificate-pem", cert_data,
+                                "private-key-pem", key_data,
+                                NULL);
+  g_assert_no_error (error);
+  g_assert_nonnull (server_cert);
 
-  read_test_data_async (test);
-  g_main_loop_run (test->loop);
-  g_assert_no_error (test->read_error);
-}
+  g_object_unref (intermediate_cert);
+  g_free (cert_data);
+  g_free (key_data);
 
-static void
-test_client_auth_rehandshake (TestConnection *test,
-                             gconstpointer   data)
-{
-  test->rehandshake = TRUE;
-  test_client_auth_connection (test, data);
+  test->server_certificate = server_cert;
+  test_verified_connection (test, data);
 }
 
 static void
-test_connection_no_database (TestConnection *test,
-                             gconstpointer   data)
+test_verified_chain_with_redundant_root_cert (TestConnection *test,
+                                              gconstpointer   data)
 {
-  GIOStream *connection;
+  GTlsBackend *backend;
+  GTlsCertificate *server_cert;
+  GTlsCertificate *intermediate_cert;
+  GTlsCertificate *root_cert;
+  char *cert_data = NULL;
+  char *key_data = NULL;
   GError *error = NULL;
 
-  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
-  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
-  g_assert_no_error (error);
-  g_assert (test->client_connection);
-  g_object_unref (connection);
+  backend = g_tls_backend_get_default ();
 
-  /* Overrides loading of the default database */
-  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), NULL);
+  /* The root is redundant. It should not hurt anything. */
+  root_cert = g_tls_certificate_new_from_file (tls_test_file_path ("ca.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (root_cert);
 
-  /* All validation in this test */
-  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
-                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+  /* Prepare the intermediate cert. */
+  g_file_get_contents (tls_test_file_path ("intermediate-ca.pem"),
+                       &cert_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert_data);
 
-  test->accept_flags = G_TLS_CERTIFICATE_UNKNOWN_CA;
-  g_signal_connect (test->client_connection, "accept-certificate",
-                    G_CALLBACK (on_accept_certificate), test);
+  intermediate_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                                      NULL, &error,
+                                      "issuer", root_cert,
+                                      "certificate-pem", cert_data,
+                                      NULL);
+  g_assert_no_error (error);
+  g_assert_nonnull (intermediate_cert);
 
-  read_test_data_async (test);
-  g_main_loop_run (test->loop);
-  g_assert_no_error (test->read_error);
-}
+  /* Prepare the server cert. */
+  g_clear_pointer (&cert_data, g_free);
+  g_file_get_contents (tls_test_file_path ("server-intermediate.pem"),
+                       &cert_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert_data);
 
-static void
-handshake_failed_cb (GObject      *source,
-                    GAsyncResult *result,
-                    gpointer      user_data)
-{
-  TestConnection *test = user_data;
-  GError *error = NULL;
+  g_file_get_contents (tls_test_file_path ("server-intermediate-key.pem"),
+                       &key_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (key_data);
+
+  server_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                                NULL, &error,
+                                "issuer", intermediate_cert,
+                                "certificate-pem", cert_data,
+                                "private-key-pem", key_data,
+                                NULL);
+  g_assert_no_error (error);
+  g_assert_nonnull (server_cert);
 
-  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_object_unref (intermediate_cert);
+  g_object_unref (root_cert);
+  g_free (cert_data);
+  g_free (key_data);
 
-  g_main_loop_quit (test->loop);
+  test->server_certificate = server_cert;
+  test_verified_connection (test, data);
 }
 
 static void
-test_failed_connection (TestConnection *test,
-                       gconstpointer   data)
+test_verified_chain_with_duplicate_server_cert (TestConnection *test,
+                                                gconstpointer   data)
 {
-  GIOStream *connection;
+  /* This is another common server misconfiguration. Apache reads certificates
+   * from two configuration files: one for the server cert, and one for the rest
+   * of the chain. If the server cert is pasted into both files, it will be sent
+   * twice. We should be tolerant of this. */
+
+  GTlsBackend *backend;
+  GTlsCertificate *server_cert;
+  GTlsCertificate *extra_server_cert;
+  GTlsCertificate *intermediate_cert;
+  char *cert_data = NULL;
+  char *key_data = NULL;
   GError *error = NULL;
-  GSocketConnectable *bad_addr;
 
-  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  backend = g_tls_backend_get_default ();
 
-  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);
+  /* Prepare the intermediate cert. */
+  intermediate_cert = g_tls_certificate_new_from_file (tls_test_file_path ("intermediate-ca.pem"), &error);
   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);
+  g_assert_nonnull (intermediate_cert);
 
-  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);
-}
+  /* Prepare the server cert. */
+  g_clear_pointer (&cert_data, g_free);
+  g_file_get_contents (tls_test_file_path ("server-intermediate.pem"),
+                       &cert_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert_data);
 
-static void
-socket_client_connected (GObject      *source,
-                        GAsyncResult *result,
-                        gpointer      user_data)
-{
-  TestConnection *test = user_data;
-  GSocketConnection *connection;
-  GError *error = NULL;
+  g_file_get_contents (tls_test_file_path ("server-intermediate-key.pem"),
+                       &key_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (key_data);
 
-  connection = g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
-                                              result, &error);
+  server_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                                NULL, &error,
+                                "issuer", intermediate_cert,
+                                "certificate-pem", cert_data,
+                                NULL);
   g_assert_no_error (error);
-  test->client_connection = G_IO_STREAM (connection);
+  g_assert_nonnull (server_cert);
+
+  /* Prepare the server cert... again. Private key must go on this one. */
+  extra_server_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                                      NULL, &error,
+                                      "issuer", server_cert,
+                                      "certificate-pem", cert_data,
+                                      "private-key-pem", key_data,
+                                      NULL);
+  g_assert_no_error (error);
+  g_assert_nonnull (extra_server_cert);
 
-  g_main_loop_quit (test->loop);
+  g_object_unref (intermediate_cert);
+  g_object_unref (server_cert);
+  g_free (cert_data);
+  g_free (key_data);
+
+  test->server_certificate = extra_server_cert;
+  test_verified_connection (test, data);
 }
 
 static void
-test_connection_socket_client (TestConnection *test,
-                              gconstpointer   data)
+test_verified_unordered_chain (TestConnection *test,
+                               gconstpointer   data)
 {
-  GSocketClient *client;
-  GTlsCertificateFlags flags;
-  GSocketConnection *connection;
-  GIOStream *base;
+  GTlsBackend *backend;
+  GTlsCertificate *server_cert;
+  GTlsCertificate *intermediate_cert;
+  GTlsCertificate *root_cert;
+  char *cert_data = NULL;
+  char *key_data = NULL;
   GError *error = NULL;
 
-  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE);
-  client = g_socket_client_new ();
-  g_socket_client_set_tls (client, TRUE);
-  flags = G_TLS_CERTIFICATE_VALIDATE_ALL & ~G_TLS_CERTIFICATE_UNKNOWN_CA;
-  /* test->address doesn't match the server's cert */
-  flags = flags & ~G_TLS_CERTIFICATE_BAD_IDENTITY;
-  g_socket_client_set_tls_validation_flags (client, flags);
+  backend = g_tls_backend_get_default ();
 
-  g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (test->address),
-                                NULL, socket_client_connected, test);
-  g_main_loop_run (test->loop);
+  /* Prepare the intermediate cert (to be sent last, out of order)! */
+  intermediate_cert = g_tls_certificate_new_from_file (tls_test_file_path ("intermediate-ca.pem"),
+                                                       &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (intermediate_cert);
 
-  connection = (GSocketConnection *)test->client_connection;
-  test->client_connection = NULL;
+  g_file_get_contents (tls_test_file_path ("ca.pem"), &cert_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert_data);
+
+  /* Prepare the root cert (to be sent in the middle of the chain). */
+  root_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                              NULL, &error,
+                              "issuer", intermediate_cert,
+                              "certificate-pem", cert_data,
+                              NULL);
+  g_assert_no_error (error);
+  g_assert_nonnull (root_cert);
 
-  g_assert (G_IS_TCP_WRAPPER_CONNECTION (connection));
-  base = g_tcp_wrapper_connection_get_base_io_stream (G_TCP_WRAPPER_CONNECTION (connection));
-  g_assert (G_IS_TLS_CONNECTION (base));
+  g_clear_pointer (&cert_data, g_free);
+  g_file_get_contents (tls_test_file_path ("server-intermediate.pem"),
+                       &cert_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert_data);
 
-  g_io_stream_close (G_IO_STREAM (connection), NULL, &error);
+  g_file_get_contents (tls_test_file_path ("server-intermediate-key.pem"),
+                       &key_data, NULL, &error);
   g_assert_no_error (error);
-  g_object_unref (connection);
+  g_assert_nonnull (key_data);
+
+  /* Prepare the server cert. */
+  server_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                                NULL, &error,
+                                "issuer", root_cert,
+                                "certificate-pem", cert_data,
+                                "private-key-pem", key_data,
+                                NULL);
+  g_assert_no_error (error);
+  g_assert_nonnull (server_cert);
 
-  g_object_unref (client);
+  g_object_unref (intermediate_cert);
+  g_object_unref (root_cert);
+  g_free (cert_data);
+  g_free (key_data);
+
+  test->server_certificate = server_cert;
+  test_verified_connection (test, data);
 }
 
 static void
-socket_client_failed (GObject      *source,
-                     GAsyncResult *result,
-                     gpointer      user_data)
+test_verified_chain_with_alternative_ca_cert (TestConnection *test,
+                                              gconstpointer   data)
 {
-  TestConnection *test = user_data;
+  GTlsBackend *backend;
+  GTlsCertificate *server_cert;
+  GTlsCertificate *intermediate_cert;
+  GTlsCertificate *root_cert;
+  char *cert_data = NULL;
+  char *key_data = NULL;
   GError *error = NULL;
 
-  g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
-                                 result, &error);
-  g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
-  g_clear_error (&error);
+  backend = g_tls_backend_get_default ();
 
-  g_main_loop_quit (test->loop);
-}
+  /* This "root" cert is issued by a CA that is not in the trust store. So it's
+   * not really a root, but it has the same public key as a cert in the trust
+   * store. If the client insists on a traditional chain of trust, this will
+   * fail, since the issuer is untrusted. */
+  root_cert = g_tls_certificate_new_from_file (tls_test_file_path ("ca-alternative.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (root_cert);
+
+  /* Prepare the intermediate cert. Modern TLS libraries are expected to notice
+   * that it is signed by the same public key as a certificate in the root
+   * store, and accept the certificate, ignoring the untrusted "root" sent next
+   * in the chain, which servers send for compatibility with clients that don't
+   * have the new CA cert in the trust store yet. (In this scenario, the old
+   * client still trusts the old CA cert.) */
+  g_file_get_contents (tls_test_file_path ("intermediate-ca.pem"),
+                       &cert_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert_data);
 
-static void
-test_connection_socket_client_failed (TestConnection *test,
-                                     gconstpointer   data)
-{
-  GSocketClient *client;
+  intermediate_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                                      NULL, &error,
+                                      "issuer", root_cert,
+                                      "certificate-pem", cert_data,
+                                      NULL);
+  g_assert_no_error (error);
+  g_assert_nonnull (intermediate_cert);
 
-  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE);
-  client = g_socket_client_new ();
-  g_socket_client_set_tls (client, TRUE);
-  /* this time we don't adjust the validation flags */
+  /* Prepare the server cert. */
+  g_clear_pointer (&cert_data, g_free);
+  g_file_get_contents (tls_test_file_path ("server-intermediate.pem"),
+                       &cert_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert_data);
 
-  g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (test->address),
-                                NULL, socket_client_failed, test);
-  g_main_loop_run (test->loop);
+  g_file_get_contents (tls_test_file_path ("server-intermediate-key.pem"),
+                       &key_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (key_data);
+
+  server_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                                NULL, &error,
+                                "issuer", intermediate_cert,
+                                "certificate-pem", cert_data,
+                                "private-key-pem", key_data,
+                                NULL);
+  g_assert_no_error (error);
+  g_assert_nonnull (server_cert);
 
-  g_object_unref (client);
+  g_object_unref (intermediate_cert);
+  g_object_unref (root_cert);
+  g_free (cert_data);
+  g_free (key_data);
+
+  test->server_certificate = server_cert;
+  test_verified_connection (test, data);
 }
 
 static void
-simul_async_read_complete (GObject      *object,
-                          GAsyncResult *result,
-                          gpointer      user_data)
+test_invalid_chain_with_alternative_ca_cert (TestConnection *test,
+                                             gconstpointer   data)
 {
-  TestConnection *test = user_data;
-  gssize nread;
+  GTlsBackend *backend;
+  GTlsCertificate *server_cert;
+  GTlsCertificate *root_cert;
+  GIOStream *connection;
+  char *cert_data = NULL;
+  char *key_data = NULL;
   GError *error = NULL;
 
-  nread = g_input_stream_read_finish (G_INPUT_STREAM (object),
-                                     result, &error);
+  backend = g_tls_backend_get_default ();
+
+  /* This certificate has the same public key as a certificate in the root store. */
+  root_cert = g_tls_certificate_new_from_file (tls_test_file_path ("ca-alternative.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (root_cert);
+
+  /* The intermediate cert is not sent. The chain should be rejected, since without intermediate.pem
+   * there is no proof that ca-alternative.pem signed server-intermediate.pem. */
+  g_file_get_contents (tls_test_file_path ("server-intermediate.pem"),
+                       &cert_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert_data);
+
+  g_file_get_contents (tls_test_file_path ("server-intermediate-key.pem"),
+                       &key_data, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (key_data);
+
+  server_cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+                                NULL, &error,
+                                "issuer", root_cert,
+                                "certificate-pem", cert_data,
+                                "private-key-pem", key_data,
+                                NULL);
+  g_assert_no_error (error);
+  g_assert_nonnull (server_cert);
+
+  g_object_unref (root_cert);
+  g_free (cert_data);
+  g_free (key_data);
+
+  test->server_certificate = server_cert;
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* Make sure this test doesn't expire. */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL & ~G_TLS_CERTIFICATE_EXPIRED);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+
+#ifdef BACKEND_IS_GNUTLS
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS);
+#elif defined(BACKEND_IS_OPENSSL)
+  /* FIXME: This is not OK. There should be a NOT_TLS errors. But some times
+   * we either get no error or BROKEN_PIPE
+   */
+#endif
+}
+
+static void
+on_notify_accepted_cas (GObject *obj,
+                        GParamSpec *spec,
+                        gpointer user_data)
+{
+  gboolean *changed = user_data;
+  *changed = TRUE;
+}
+
+static void
+test_client_auth_connection (TestConnection *test,
+                             gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+  GTlsCertificate *cert;
+  GTlsCertificate *peer;
+  gboolean cas_changed;
+  GSocketClient *client;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("client-and-key.pem"), &error);
+  g_assert_no_error (error);
+
+  g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  cas_changed = FALSE;
+  g_signal_connect (test->client_connection, "notify::accepted-cas",
+                    G_CALLBACK (on_notify_accepted_cas), &cas_changed);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
+  g_assert_nonnull (peer);
+  g_assert_true (g_tls_certificate_is_same (peer, cert));
+  g_assert_true (cas_changed);
+
+  g_object_unref (cert);
+  g_object_unref (test->client_connection);
+  g_clear_object (&test->server_connection);
+
+  /* Now start a new connection to the same server with a different client cert */
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                0);
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("client2-and-key.pem"), &error);
+  g_assert_no_error (error);
+  g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert);
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  /* peer should see the second client cert */
+  peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
+  g_assert_nonnull (peer);
+  g_assert_true (g_tls_certificate_is_same (peer, cert));
+  g_object_unref (cert);
+}
+
+#ifdef BACKEND_IS_GNUTLS
+static void
+run_until_object_is_destroyed (GMainContext *context,
+                               GWeakRef     *weak_ref)
+{
+  GObject *object;
+
+  while ((object = g_weak_ref_get (weak_ref)))
+    {
+      g_object_unref (object);
+      g_main_context_iteration (context, FALSE);
+    }
+}
+#endif
+
+static void
+test_client_auth_pkcs11_connection (TestConnection *test,
+                                    gconstpointer   data)
+{
+#ifndef BACKEND_IS_GNUTLS
+  g_test_skip ("This backend does not support PKCS #11");
+#else
+  GIOStream *connection;
+  GError *error = NULL;
+  GTlsCertificate *cert;
+  GTlsCertificate *peer;
+  gboolean cas_changed;
+  GSocketClient *client;
+  GTlsInteraction *interaction;
+  GWeakRef weak_ref;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  interaction = mock_interaction_new_static_password ("ABC123");
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_weak_ref_init (&weak_ref, test->client_connection);
+
+  g_tls_connection_set_interaction (G_TLS_CONNECTION (test->client_connection), interaction);
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  cert = g_tls_certificate_new_from_pkcs11_uris ("pkcs11:model=mock;manufacturer=GLib-Networking;serial=1;token=Mock%20Certificate;id=%4D%6F%63%6B%20%43%65%72%74%69%66%69%63%61%74%65;object=Mock%20Certificate;type=cert",
+                                                 "pkcs11:model=mock;manufacturer=GLib-Networking;serial=1;token=Mock%20Certificate;id=%4D%6F%63%6B%20%50%72%69%76%61%74%65%20%4B%65%79;object=Mock%20Private%20Key;type=private",
+                                                 &error);
+  g_assert_no_error (error);
+
+  g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  cas_changed = FALSE;
+  g_signal_connect (test->client_connection, "notify::accepted-cas",
+                    G_CALLBACK (on_notify_accepted_cas), &cas_changed);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
+  g_assert_nonnull (peer);
+  g_assert_true (g_tls_certificate_is_same (peer, cert));
+  g_assert_true (cas_changed);
+
+  g_object_unref (cert);
+  g_object_unref (test->client_connection);
+  g_clear_object (&test->server_connection);
+
+  /* The mock PKCS#11 module allows only a single PKCS#11 connection at a time.
+   * This means we have to ensure the original GTlsClientConnection is finalized
+   * before creating the next one.
+   */
+  run_until_object_is_destroyed (test->context, &weak_ref);
+  g_weak_ref_clear (&weak_ref);
+
+  /* Now start a new connection to the same server with a different client cert.
+   * Also test using a single URI matching both the cert and private key.
+   */
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_interaction (G_TLS_CONNECTION (test->client_connection), interaction);
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                0);
+  cert = g_tls_certificate_new_from_pkcs11_uris ("pkcs11:model=mock;manufacturer=GLib-Networking;serial=1;token=Mock%20Certificate;id=%4D%6F%63%6B%20%50%72%69%76%61%74%65%20%4B%65%79%20%32",
+                                                 NULL,
+                                                 &error);
+  g_assert_no_error (error);
+  g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert);
+  g_object_unref (cert);
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  /* peer should see the second client cert */
+  peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
+  g_assert_nonnull (peer);
+  g_assert_true (g_tls_certificate_is_same (peer, cert));
+
+  g_object_unref (interaction);
+#endif
+}
+
+static void
+test_client_auth_rehandshake (TestConnection *test,
+                              gconstpointer   data)
+{
+  test->rehandshake = TRUE;
+  test_client_auth_connection (test, data);
+}
+
+static void
+test_client_auth_failure (TestConnection *test,
+                          gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+  gboolean accepted_changed;
+  GSocketClient *client;
+  GTlsCertificate *cert;
+  GTlsCertificate *peer;
+  GTlsInteraction *interaction;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* No Certificate set */
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  accepted_changed = FALSE;
+  g_signal_connect (test->client_connection, "notify::accepted-cas",
+                    G_CALLBACK (on_notify_accepted_cas), &accepted_changed);
+
+  test->ignore_client_close_error = TRUE;
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  /* FIXME: We should always receive G_TLS_ERROR_CERTIFICATE_REQUIRED here. But
+   * on our TLS 1.2 CI, sometimes we receive GNUTLS_E_PREMATURE_TERMINATION,
+   * which we translate to G_TLS_ERROR_NOT_TLS because we have never handshaked
+   * successfully. If the timing is different and it occurs after the handshake,
+   * then we get G_TLS_ERROR_EOF. Sadly, I can't reproduce the issue locally, so
+   * my odds of fixing it are slim to none. The connection is at least failing
+   * as we expect, just not with the desired error.
+   */
+  if (!g_error_matches (test->read_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS) &&
+      !g_error_matches (test->read_error, G_TLS_ERROR, G_TLS_ERROR_EOF))
+    {
+      g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
+    }
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
+
+  g_assert_true (accepted_changed);
+
+  g_object_unref (test->client_connection);
+  g_clear_object (&test->server_connection);
+  g_clear_error (&test->read_error);
+  g_clear_error (&test->server_error);
+
+  test->ignore_client_close_error = FALSE;
+
+  /* Now start a new connection to the same server with a valid client cert;
+   * this should succeed, and not use the cached failed session from above */
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* Have the interaction return a certificate */
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("client-and-key.pem"), &error);
+  g_assert_no_error (error);
+  interaction = mock_interaction_new_static_certificate (cert);
+  g_tls_connection_set_interaction (G_TLS_CONNECTION (test->client_connection), interaction);
+  g_object_unref (interaction);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  accepted_changed = FALSE;
+  g_signal_connect (test->client_connection, "notify::accepted-cas",
+                    G_CALLBACK (on_notify_accepted_cas), &accepted_changed);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
+  g_assert_nonnull (peer);
+  g_assert_true (g_tls_certificate_is_same (peer, cert));
+  g_assert_true (accepted_changed);
+
+  g_object_unref (cert);
+}
+
+static void
+test_client_auth_fail_missing_client_private_key (TestConnection *test,
+                                                  gconstpointer   data)
+{
+  GTlsCertificate *cert;
+  GIOStream *connection;
+  GError *error = NULL;
+
+  g_test_bug ("793712");
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* Oops: we "accidentally" set client.pem rather than client-and-key.pem. The
+   * connection will fail, but we should not crash.
+   */
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("client.pem"), &error);
+  g_assert_no_error (error);
+
+  g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+#if BACKEND_IS_OPENSSL && defined(G_OS_WIN32)
+  test->ignore_client_close_error = TRUE;
+#endif
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+#if BACKEND_IS_OPENSSL && defined(G_OS_WIN32)
+  test->ignore_client_close_error = FALSE;
+#endif
+
+  g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
+#if BACKEND_IS_OPENSSL
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
+#else
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS);
+#endif
+}
+
+static void
+test_client_auth_request_cert (TestConnection *test,
+                               gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+  GTlsCertificate *cert;
+  GTlsCertificate *peer;
+  GTlsInteraction *interaction;
+  gboolean cas_changed;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* Have the interaction return a certificate */
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("client-and-key.pem"), &error);
+  g_assert_no_error (error);
+  interaction = mock_interaction_new_static_certificate (cert);
+  g_tls_connection_set_interaction (G_TLS_CONNECTION (test->client_connection), interaction);
+  g_object_unref (interaction);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  cas_changed = FALSE;
+  g_signal_connect (test->client_connection, "notify::accepted-cas",
+                    G_CALLBACK (on_notify_accepted_cas), &cas_changed);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
+  g_assert_nonnull (peer);
+  g_assert_true (g_tls_certificate_is_same (peer, cert));
+  g_assert_true (cas_changed);
+
+  g_object_unref (cert);
+}
+
+static void
+test_client_auth_request_fail (TestConnection *test,
+                               gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+  GTlsInteraction *interaction;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* Have the interaction return an error */
+  interaction = mock_interaction_new_static_error (G_FILE_ERROR, G_FILE_ERROR_ACCES, "Request message");
+  g_tls_connection_set_interaction (G_TLS_CONNECTION (test->client_connection), interaction);
+  g_object_unref (interaction);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  test->ignore_client_close_error = TRUE;
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  /* FIXME: We should always receive G_TLS_ERROR_CERTIFICATE_REQUIRED here. But
+   * on our TLS 1.2 CI, sometimes we receive GNUTLS_E_PREMATURE_TERMINATION,
+   * which we translate to G_TLS_ERROR_NOT_TLS because we have never handshaked
+   * successfully. If the timing is different and it occurs after the handshake,
+   * then we get G_TLS_ERROR_EOF. Sadly, I can't reproduce the issue locally, so
+   * my odds of fixing it are slim to none. The connection is at least failing
+   * as we expect, just not with the desired error.
+   */
+  if (!g_error_matches (test->read_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS) &&
+      !g_error_matches (test->read_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED) &&
+      !g_error_matches (test->read_error, G_TLS_ERROR, G_TLS_ERROR_EOF))
+    {
+      /* G_FILE_ERROR_ACCES is the error returned by our mock interaction object
+       * when the GTlsInteraction's certificate request fails.
+       */
+      g_assert_error (test->read_error, G_FILE_ERROR, G_FILE_ERROR_ACCES);
+    }
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
+
+  g_io_stream_close (test->server_connection, NULL, NULL);
+  g_io_stream_close (test->client_connection, NULL, NULL);
+}
+
+static void
+test_client_auth_request_none (TestConnection *test,
+                               gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  /* Request, but don't provide, a client certificate */
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUESTED);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* All validation in this test */
+  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);
+  wait_until_server_finished (test);
+
+  /* The connection should succeed and everything should work. We only REQUESTED
+   * authentication, in contrast to G_TLS_AUTHENTICATION_REQUIRED where this
+   * should fail.
+   */
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+}
+
+
+static void
+test_connection_no_database (TestConnection *test,
+                             gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  /* Overrides loading of the default database */
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), NULL);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  test->accept_flags = G_TLS_CERTIFICATE_UNKNOWN_CA;
+  g_signal_connect (test->client_connection, "accept-certificate",
+                    G_CALLBACK (on_accept_certificate), test);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_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_async_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);
+  wait_until_server_finished (test);
+
+  g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+
+#ifdef BACKEND_IS_GNUTLS
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS);
+#elif defined(BACKEND_IS_OPENSSL)
+  /* FIXME: This is not OK. There should be a NOT_TLS errors. But some times
+   * we either get no error or BROKEN_PIPE
+   */
+#endif
+}
+
+static void
+socket_client_connected (GObject      *source,
+                         GAsyncResult *result,
+                         gpointer      user_data)
+{
+  TestConnection *test = user_data;
+  GSocketConnection *connection;
+  GError *error = NULL;
+
+  connection = g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
+                                               result, &error);
+  g_assert_no_error (error);
+  test->client_connection = G_IO_STREAM (connection);
+
+  g_main_loop_quit (test->loop);
+}
+
+static void
+test_connection_socket_client (TestConnection *test,
+                               gconstpointer   data)
+{
+  GSocketClient *client;
+  GTlsCertificateFlags flags;
+  GSocketConnection *connection;
+  GIOStream *base;
+  GError *error = NULL;
+
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, WRITE_THEN_CLOSE);
+  client = g_socket_client_new ();
+  g_socket_client_set_tls (client, TRUE);
+  flags = G_TLS_CERTIFICATE_VALIDATE_ALL & ~G_TLS_CERTIFICATE_UNKNOWN_CA;
+  /* test->address doesn't match the server's cert */
+  flags = flags & ~G_TLS_CERTIFICATE_BAD_IDENTITY;
+  g_socket_client_set_tls_validation_flags (client, flags);
+
+  g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (test->address),
+                                 NULL, socket_client_connected, test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  connection = (GSocketConnection *)test->client_connection;
+  test->client_connection = NULL;
+
+  g_assert_true (G_IS_TCP_WRAPPER_CONNECTION (connection));
+  base = g_tcp_wrapper_connection_get_base_io_stream (G_TCP_WRAPPER_CONNECTION (connection));
+  g_assert_true (G_IS_TLS_CONNECTION (base));
+
+  g_io_stream_close (G_IO_STREAM (connection), NULL, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  g_object_unref (client);
+}
+
+static void
+socket_client_failed (GObject      *source,
+                      GAsyncResult *result,
+                      gpointer      user_data)
+{
+  TestConnection *test = user_data;
+  GError *error = NULL;
+
+  g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
+                                  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_connection_socket_client_failed (TestConnection *test,
+                                      gconstpointer   data)
+{
+  GSocketClient *client;
+
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, WRITE_THEN_CLOSE);
+  client = g_socket_client_new ();
+  g_socket_client_set_tls (client, TRUE);
+  /* this time we don't adjust the validation flags */
+
+  g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (test->address),
+                                 NULL, socket_client_failed, test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+#ifdef BACKEND_IS_GNUTLS
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS);
+#else
+  /* FIXME: This is not OK. There should be a NOT_TLS errors. But some times
+   * we either get no error or BROKEN_PIPE
+   */
+#endif
+
+  g_object_unref (client);
+}
+
+static gboolean
+socket_client_timed_out_write (gpointer user_data)
+{
+  TestConnection *test = user_data;
+  GInputStream *input_stream;
+  GOutputStream *output_stream;
+  GError *error = NULL;
+  gchar buffer[TEST_DATA_LENGTH];
+  gssize size;
+
+  input_stream = g_io_stream_get_input_stream (test->client_connection);
+  output_stream = g_io_stream_get_output_stream (test->client_connection);
+
+  /* read TEST_DATA_LENGTH once */
+  size = g_input_stream_read (input_stream, &buffer, TEST_DATA_LENGTH,
+                              NULL, &error);
+  if (error)
+    {
+      /* This should very rarely ever happen, but in practice it can take more
+       * than one second to read under heavy load, or when running many tests
+       * simultaneously, so don't fail if this happens.
+       */
+      g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
+      g_assert_cmpint (size, ==, -1);
+      g_clear_error (&error);
+    }
+  else
+    {
+      g_assert_no_error (error);
+      g_assert_cmpint (size, ==, TEST_DATA_LENGTH);
+
+      /* read TEST_DATA_LENGTH again to cause the time out */
+      size = g_input_stream_read (input_stream, &buffer, TEST_DATA_LENGTH,
+                                  NULL, &error);
+      g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
+      g_assert_cmpint (size, ==, -1);
+      g_clear_error (&error);
+    }
+
+  /* write after a timeout, session should still be valid */
+  size = g_output_stream_write (output_stream, TEST_DATA, TEST_DATA_LENGTH,
+                                NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (size, ==, TEST_DATA_LENGTH);
+
+  g_main_loop_quit (test->loop);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+socket_client_timed_out_write_connected (GObject      *source,
+                                         GAsyncResult *result,
+                                         gpointer      user_data)
+{
+  TestConnection *test = user_data;
+  GSocketConnection *connection;
+  GError *error = NULL;
+
+  connection = g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
+                                               result, &error);
+  g_assert_no_error (error);
+  test->client_connection = G_IO_STREAM (connection);
+
+  /* We need to use an idle callback here to guarantee that the upcoming call
+   * to g_input_stream_read() executes on the next iteration of the main
+   * context. Otherwise, we could deadlock ourselves: the read would not be able
+   * to complete if GTask executes socket_client_timed_out_write_connected()
+   * using g_task_return_now() instead of posting the invocation to the next
+   * iteration of the main context, because the server will not progress until
+   * the main context is iterated, but iteration would be blocked waiting for
+   * client's read to complete.
+   */
+  g_idle_add (socket_client_timed_out_write, test);
+}
+
+static void
+test_connection_read_time_out_write (TestConnection *test,
+                                     gconstpointer   data)
+{
+  GSocketClient *client;
+  GTlsCertificateFlags flags;
+  GSocketConnection *connection;
+  GIOStream *base;
+  GError *error = NULL;
+
+  /* Don't close the server connection after writing TEST_DATA. */
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, WRITE_THEN_WAIT);
+  client = g_socket_client_new ();
+  /* Set a 1 second time out on the socket */
+  g_socket_client_set_timeout (client, 1);
+  g_socket_client_set_tls (client, TRUE);
+  flags = G_TLS_CERTIFICATE_VALIDATE_ALL & ~G_TLS_CERTIFICATE_UNKNOWN_CA;
+  /* test->address doesn't match the server's cert */
+  flags = flags & ~G_TLS_CERTIFICATE_BAD_IDENTITY;
+  g_socket_client_set_tls_validation_flags (client, flags);
+
+  g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (test->address),
+                                 NULL, socket_client_timed_out_write_connected, test);
+
+  g_main_loop_run (test->loop);
+
+  /* Close the server now */
+  close_server_connection (test);
+
+  connection = (GSocketConnection *)test->client_connection;
+  test->client_connection = NULL;
+
+  g_assert_true (G_IS_TCP_WRAPPER_CONNECTION (connection));
+  base = g_tcp_wrapper_connection_get_base_io_stream (G_TCP_WRAPPER_CONNECTION (connection));
+  g_assert_true (G_IS_TLS_CONNECTION (base));
+
+  g_io_stream_close (G_IO_STREAM (connection), NULL, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  g_object_unref (client);
+}
+
+static void
+simul_async_read_complete (GObject      *object,
+                           GAsyncResult *result,
+                           gpointer      user_data)
+{
+  TestConnection *test = user_data;
+  gssize nread;
+  GError *error = NULL;
+
+  nread = g_input_stream_read_finish (G_INPUT_STREAM (object),
+                                      result, &error);
   g_assert_no_error (error);
 
   test->nread += nread;
   g_assert_cmpint (test->nread, <=, TEST_DATA_LENGTH);
 
-  if (test->nread == TEST_DATA_LENGTH)
-    {
-      g_io_stream_close (test->client_connection, NULL, &error);
-      g_assert_no_error (error);
-      g_main_loop_quit (test->loop);
-    }
-  else
-    {
-      g_input_stream_read_async (G_INPUT_STREAM (object),
-                                test->buf + test->nread,
-                                TEST_DATA_LENGTH / 2,
-                                G_PRIORITY_DEFAULT, NULL,
-                                simul_async_read_complete, test);
-    }
+  if (test->nread == TEST_DATA_LENGTH)
+    {
+      g_io_stream_close (test->client_connection, NULL, &error);
+      g_assert_no_error (error);
+      g_main_loop_quit (test->loop);
+    }
+  else
+    {
+      g_input_stream_read_async (G_INPUT_STREAM (object),
+                                 test->buf + test->nread,
+                                 TEST_DATA_LENGTH / 2,
+                                 G_PRIORITY_DEFAULT, NULL,
+                                 simul_async_read_complete, test);
+    }
+}
+
+static void
+simul_async_write_complete (GObject      *object,
+                            GAsyncResult *result,
+                            gpointer      user_data)
+{
+  TestConnection *test = user_data;
+  gssize nwrote;
+  GError *error = NULL;
+
+  nwrote = g_output_stream_write_finish (G_OUTPUT_STREAM (object),
+                                         result, &error);
+  g_assert_no_error (error);
+
+  test->nwrote += nwrote;
+  if (test->nwrote < TEST_DATA_LENGTH)
+    {
+      g_output_stream_write_async (G_OUTPUT_STREAM (object),
+                                   &TEST_DATA[test->nwrote],
+                                   TEST_DATA_LENGTH - test->nwrote,
+                                   G_PRIORITY_DEFAULT, NULL,
+                                   simul_async_write_complete, test);
+    }
+}
+
+static void
+test_simultaneous_async (TestConnection *test,
+                         gconstpointer   data)
+{
+  GIOStream *connection;
+  GTlsCertificateFlags flags;
+  GError *error = NULL;
+
+  connection = start_echo_server_and_connect_to_it (test);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  flags = G_TLS_CERTIFICATE_VALIDATE_ALL &
+    ~(G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY);
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                flags);
+
+  memset (test->buf, 0, sizeof (test->buf));
+  test->nread = test->nwrote = 0;
+
+  g_input_stream_read_async (g_io_stream_get_input_stream (test->client_connection),
+                             test->buf, TEST_DATA_LENGTH / 2,
+                             G_PRIORITY_DEFAULT, NULL,
+                             simul_async_read_complete, test);
+  g_output_stream_write_async (g_io_stream_get_output_stream (test->client_connection),
+                               TEST_DATA, TEST_DATA_LENGTH / 2,
+                               G_PRIORITY_DEFAULT, NULL,
+                               simul_async_write_complete, test);
+
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_cmpint (test->nread, ==, TEST_DATA_LENGTH);
+  g_assert_cmpint (test->nwrote, ==, TEST_DATA_LENGTH);
+  g_assert_cmpstr (test->buf, ==, TEST_DATA);
+}
+
+static void
+test_simultaneous_async_rehandshake (TestConnection *test,
+                                     gconstpointer   data)
+{
+  test->rehandshake = TRUE;
+  test_simultaneous_async (test, data);
+}
+
+static gpointer
+simul_read_thread (gpointer user_data)
+{
+  TestConnection *test = user_data;
+  GInputStream *istream = g_io_stream_get_input_stream (test->client_connection);
+  GError *error = NULL;
+  gssize nread;
+
+  while (test->nread < TEST_DATA_LENGTH)
+    {
+      nread = g_input_stream_read (istream,
+                                   test->buf + test->nread,
+                                   MIN (TEST_DATA_LENGTH / 2, TEST_DATA_LENGTH - test->nread),
+                                   NULL, &error);
+      g_assert_no_error (error);
+
+      test->nread += nread;
+    }
+
+  return NULL;
+}
+
+static gpointer
+simul_write_thread (gpointer user_data)
+{
+  TestConnection *test = user_data;
+  GOutputStream *ostream = g_io_stream_get_output_stream (test->client_connection);
+  GError *error = NULL;
+  gssize nwrote;
+
+  while (test->nwrote < TEST_DATA_LENGTH)
+    {
+      nwrote = g_output_stream_write (ostream,
+                                      &TEST_DATA[test->nwrote],
+                                      MIN (TEST_DATA_LENGTH / 2, TEST_DATA_LENGTH - test->nwrote),
+                                      NULL, &error);
+      g_assert_no_error (error);
+
+      test->nwrote += nwrote;
+    }
+
+  return NULL;
+}
+
+static void
+test_simultaneous_sync (TestConnection *test,
+                        gconstpointer   data)
+{
+  GIOStream *connection;
+  GTlsCertificateFlags flags;
+  GError *error = NULL;
+  GThread *read_thread, *write_thread;
+
+  connection = start_echo_server_and_connect_to_it (test);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  flags = G_TLS_CERTIFICATE_VALIDATE_ALL &
+    ~(G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY);
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                flags);
+
+  memset (test->buf, 0, sizeof (test->buf));
+  test->nread = test->nwrote = 0;
+
+  read_thread = g_thread_new ("reader", simul_read_thread, test);
+  write_thread = g_thread_new ("writer", simul_write_thread, test);
+
+  /* We need to run the main loop to get the GThreadedSocketService to
+   * receive the connection and spawn the server thread.
+   */
+  while (!test->server_connection)
+    g_main_context_iteration (test->context, FALSE);
+
+  g_thread_join (write_thread);
+  g_thread_join (read_thread);
+
+  g_assert_cmpint (test->nread, ==, TEST_DATA_LENGTH);
+  g_assert_cmpint (test->nwrote, ==, TEST_DATA_LENGTH);
+  g_assert_cmpstr (test->buf, ==, TEST_DATA);
+
+  g_io_stream_close (test->client_connection, NULL, &error);
+  g_assert_no_error (error);
+}
+
+static void
+test_simultaneous_sync_rehandshake (TestConnection *test,
+                                    gconstpointer   data)
+{
+  test->rehandshake = TRUE;
+  test_simultaneous_sync (test, data);
+}
+
+static void
+test_close_immediately (TestConnection *test,
+                        gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  /*
+   * At this point the server won't get a chance to run. But regardless
+   * closing should not wait on the server, trying to handshake or something.
+   */
+  g_io_stream_close (test->client_connection, NULL, &error);
+  g_assert_no_error (error);
+}
+
+static void
+close_server_connection_uncleanly (TestConnection *test)
+{
+  GIOStream *base_iostream;
+  GError *error = NULL;
+
+  /* Instead of closing the GTlsConnection itself, we'll directly close its
+   * underlying output stream in order to ensure the TLS close notify is never
+   * sent.
+   */
+  g_object_get (test->server_connection,
+                "base-io-stream", &base_iostream,
+                NULL);
+
+  g_io_stream_close (base_iostream, NULL, &error);
+  g_assert_no_error (error);
+
+  test->server_running = FALSE;
+
+  g_object_unref (base_iostream);
+}
+
+static void
+test_unclean_close_by_server (TestConnection *test,
+                              gconstpointer   data)
+{
+  GSocketClient *client;
+  GTlsCertificateFlags flags;
+  GTlsConnection *client_connection;
+  gssize nread;
+
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, HANDSHAKE_ONLY);
+  client = g_socket_client_new ();
+  g_socket_client_set_tls (client, TRUE);
+  flags = G_TLS_CERTIFICATE_VALIDATE_ALL & ~G_TLS_CERTIFICATE_UNKNOWN_CA;
+  /* test->address doesn't match the server's cert */
+  flags = flags & ~G_TLS_CERTIFICATE_BAD_IDENTITY;
+  g_socket_client_set_tls_validation_flags (client, flags);
+
+  g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (test->address),
+                                 NULL, socket_client_connected, test);
+  g_main_loop_run (test->loop);
+
+  /* The server might not have completed its handshake yet. We want to
+   * wait until the handshake has completed successfully before closing
+   * the connection.
+   */
+  while (!test->server_ever_handshaked)
+    g_main_context_iteration (test->context, TRUE);
+
+  close_server_connection_uncleanly (test);
+
+  /* Because the server closed its connection uncleanly, we should receive
+   * G_TLS_ERROR_EOF to warn that the close notify alert was not received,
+   * indicating a truncation attack. The only other acceptable error here
+   * is connection closed, which is an uncommon race.
+   */
+  nread = g_input_stream_read (g_io_stream_get_input_stream (test->client_connection),
+                               test->buf, TEST_DATA_LENGTH,
+                               NULL, &test->read_error);
+  if (!g_error_matches (test->read_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE))
+    g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_EOF);
+  g_assert_cmpint (nread, ==, -1);
+
+  /* Now do it again, except this time, we ignore truncation attacks by
+   * disabling require_close_notify.
+   */
+  g_clear_error (&test->read_error);
+  g_clear_object (&test->address);
+  g_clear_object (&test->identity);
+  g_socket_service_stop (test->service);
+  g_clear_object (&test->service);
+  g_clear_object (&test->server_connection);
+  g_clear_object (&test->client_connection);
+  test->server_ever_handshaked = FALSE;
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, HANDSHAKE_ONLY);
+
+  g_socket_client_set_tls (client, TRUE);
+  g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (test->address),
+                                 NULL, socket_client_connected, test);
+  g_main_loop_run (test->loop);
+
+  while (!test->server_ever_handshaked)
+    g_main_context_iteration (test->context, TRUE);
+
+  close_server_connection_uncleanly (test);
+
+  client_connection = G_TLS_CONNECTION (g_tcp_wrapper_connection_get_base_io_stream (G_TCP_WRAPPER_CONNECTION (test->client_connection)));
+  g_tls_connection_set_require_close_notify (client_connection, FALSE);
+
+  nread = g_input_stream_read (g_io_stream_get_input_stream (test->client_connection),
+                               test->buf, TEST_DATA_LENGTH,
+                               NULL, &test->read_error);
+  if (!g_error_matches (test->read_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE))
+    g_assert_no_error (test->read_error);
+  g_assert_cmpint (nread, ==, 0);
+
+  g_object_unref (client);
+}
+
+static gboolean
+async_implicit_handshake_dispatch (GPollableInputStream *stream,
+                                   gpointer user_data)
+{
+  TestConnection *test = user_data;
+  GError *error = NULL;
+  gchar buffer[TEST_DATA_LENGTH];
+  gssize size;
+  gboolean keep_running;
+
+  size = g_pollable_input_stream_read_nonblocking (stream, buffer,
+                                                   TEST_DATA_LENGTH,
+                                                   NULL, &error);
+
+  keep_running = (-1 == size);
+
+  if (keep_running)
+    {
+      g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK);
+      g_error_free (error);
+    }
+  else
+    {
+      g_assert_no_error (error);
+      g_assert_cmpint (size, ==, TEST_DATA_LENGTH);
+      g_main_loop_quit (test->loop);
+    }
+
+  return keep_running;
+}
+
+static void
+test_async_implicit_handshake (TestConnection *test, gconstpointer   data)
+{
+  GTlsCertificateFlags flags;
+  GIOStream *stream;
+  GInputStream *input_stream;
+  GSource *input_source;
+  GError *error = NULL;
+
+  g_test_bug ("710691");
+
+  stream = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (stream, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (stream);
+
+  flags = G_TLS_CERTIFICATE_VALIDATE_ALL &
+    ~(G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY);
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                flags);
+
+  /**
+   * Create a source from the client's input stream. The dispatch
+   * callback will be called a first time, which will perform a
+   * non-blocking read triggering the asynchronous implicit
+   * handshaking.
+   */
+  input_stream = g_io_stream_get_input_stream (test->client_connection);
+  input_source =
+    g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (input_stream),
+                                           NULL);
+
+  g_source_set_callback (input_source,
+                         (GSourceFunc) async_implicit_handshake_dispatch,
+                         test, NULL);
+
+  g_source_attach (input_source, NULL);
+  g_source_unref (input_source);
+
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_io_stream_close (G_IO_STREAM (test->client_connection), NULL, &error);
+  g_assert_no_error (error);
+  g_object_unref (test->client_connection);
+  test->client_connection = NULL;
+}
+
+static void
+handshake_completed (GObject      *object,
+                     GAsyncResult *result,
+                     gpointer      user_data)
+{
+  gboolean *complete = user_data;
+
+  *complete = TRUE;
+  return;
+}
+
+static void
+test_output_stream_close (TestConnection *test,
+                          gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+  gboolean ret;
+  gboolean handshake_complete = FALSE;
+  gssize size;
+
+#ifdef BACKEND_IS_OPENSSL
+# if OPENSSL_VERSION_NUMBER >= 0x10101000L
+  /* FIXME: This test fails most of the times with openssl 1.1.1, my guess is that
+   * there is still some threading issue and we endup calling input_stream_read
+   * from different threads and the same time.
+   */
+  g_test_skip ("this is not supported with openssl 1.1.1");
+  return;
+# endif
+#endif
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  /* No validation at all in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                0);
+
+  g_tls_connection_handshake_async (G_TLS_CONNECTION (test->client_connection),
+                                    G_PRIORITY_DEFAULT, NULL,
+                                    handshake_completed, &handshake_complete);
+
+  while (!handshake_complete)
+    g_main_context_iteration (test->context, TRUE);
+
+  ret = g_output_stream_close (g_io_stream_get_output_stream (test->client_connection),
+                               NULL, &error);
+  g_assert_no_error (error);
+  g_assert_true (ret);
+
+  /* Verify that double close returns TRUE */
+  ret = g_output_stream_close (g_io_stream_get_output_stream (test->client_connection),
+                               NULL, &error);
+  g_assert_no_error (error);
+  g_assert_true (ret);
+
+  size = g_output_stream_write (g_io_stream_get_output_stream (test->client_connection),
+                                "data", 4, NULL, &error);
+  g_assert_cmpint (size, ==, -1);
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED);
+  g_clear_error (&error);
+
+  /* We closed the output stream, but not the input stream, so receiving
+   * data should still work.
+   */
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  ret = g_io_stream_close (test->client_connection, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_true (ret);
+}
+
+static void
+test_garbage_database (TestConnection *test,
+                       gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("garbage.pem"), &error);
+  g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC);
+  g_assert_null (test->database);
+  g_clear_error (&error);
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* All validation in this test */
+  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);
+  wait_until_server_finished (test);
+
+  /* Should reject the server's certificate, because our TLS database contains
+   * no valid certificates.
+   */
+  g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+#ifdef BACKEND_IS_GNUTLS
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS);
+#endif
+}
+
+static void
+test_readwrite_after_connection_destroyed (TestConnection *test,
+                                           gconstpointer   data)
+{
+  GIOStream *connection;
+  GOutputStream *ostream;
+  GInputStream *istream;
+  unsigned char buffer[1];
+  GError *error = NULL;
+
+  g_test_bug ("792219");
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  istream = g_object_ref (g_io_stream_get_input_stream (test->client_connection));
+  ostream = g_object_ref (g_io_stream_get_output_stream (test->client_connection));
+  g_clear_object (&test->client_connection);
+
+  /* The GTlsConnection has been destroyed, but its underlying streams
+   * live on, because we have reffed them. Verify that attempts to read
+   * and write produce only nice GErrors.
+   */
+  g_input_stream_read (istream, buffer, sizeof (buffer), NULL, &error);
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED);
+  g_clear_error (&error);
+
+  g_output_stream_write (ostream, TEST_DATA, TEST_DATA_LENGTH,
+                         G_PRIORITY_DEFAULT, &error);
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED);
+  g_clear_error (&error);
+
+  g_input_stream_close (istream, NULL, &error);
+  g_assert_no_error (error);
+
+  g_output_stream_close (ostream, NULL, &error);
+  g_assert_no_error (error);
+
+  g_object_unref (istream);
+  g_object_unref (ostream);
+}
+
+static void
+test_alpn (TestConnection *test,
+           const char * const *client_protocols,
+           const char * const *server_protocols,
+           const char *negotiated_protocol)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+
+  test->server_protocols = server_protocols;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  if (client_protocols)
+    {
+      g_tls_connection_set_advertised_protocols (G_TLS_CONNECTION (test->client_connection),
+                                                 client_protocols);
+    }
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  g_assert_cmpstr (g_tls_connection_get_negotiated_protocol (G_TLS_CONNECTION (test->server_connection)), ==, negotiated_protocol);
+  g_assert_cmpstr (g_tls_connection_get_negotiated_protocol (G_TLS_CONNECTION (test->client_connection)), ==, negotiated_protocol);
+}
+
+static void
+test_alpn_match (TestConnection *test,
+                 gconstpointer   data)
+{
+  const char * const client_protocols[] = { "one", "two", "three", NULL };
+  const char * const server_protocols[] = { "four", "seven", "nine", "two", NULL };
+
+  test_alpn (test, client_protocols, server_protocols, "two");
+}
+
+static void
+test_alpn_no_match (TestConnection *test,
+                    gconstpointer   data)
+{
+  const char * const client_protocols[] = { "one", "two", "three", NULL };
+  const char * const server_protocols[] = { "four", "seven", "nine", NULL };
+
+  test_alpn (test, client_protocols, server_protocols, NULL);
+}
+
+static void
+test_alpn_client_only (TestConnection *test,
+                       gconstpointer   data)
+{
+  const char * const client_protocols[] = { "one", "two", "three", NULL };
+
+  test_alpn (test, client_protocols, NULL, NULL);
+}
+
+static void
+test_alpn_server_only (TestConnection *test,
+                       gconstpointer   data)
+{
+  const char * const server_protocols[] = { "four", "seven", "nine", "two", NULL };
+
+  test_alpn (test, NULL, server_protocols, NULL);
+}
+
+static gboolean
+on_accept_certificate_with_sync_close (GTlsClientConnection *conn,
+                                       GTlsCertificate      *cert,
+                                       GTlsCertificateFlags  errors,
+                                       gpointer              user_data)
+{
+  GError *error = NULL;
+
+  /* Attempting to perform a sync operation that would block the
+   * handshake should fail, not deadlock.
+   */
+  g_io_stream_close (G_IO_STREAM (conn), NULL, &error);
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+  g_error_free (error);
+
+  /* FIXME: When writing this test, I initially wanted to return FALSE
+   * here to reject the connection. However, this surfaces a bug that I
+   * have not fixed yet. The problem is the server is not seeing the end
+   * of its g_output_stream_write() when the client fails the handshake.
+   * No good. The server's implicit handshake failure should trigger a
+   * write failure as well, instead of stalling. This needs to be fixed.
+   *
+   * Fixing this would allow us to guarantee that this callback is
+   * actually executed by checking test->read_error at the bottom of
+   * test_sync_op_during_handshake(). Currently, this test would still
+   * pass even if this callback were to be improperly skipped.
+   */
+  return TRUE;
+}
+
+static void
+test_sync_op_during_handshake (TestConnection *test,
+                               gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  /* For this test, we need validation to fail to ensure that the
+   * accept-certificate signal gets emitted.
+   */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  g_signal_connect (test->client_connection, "accept-certificate",
+                    G_CALLBACK (on_accept_certificate_with_sync_close), test);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+}
+
+static void
+test_socket_timeout (TestConnection *test,
+                     gconstpointer   data)
+{
+  GIOStream *connection;
+  GSocketClient *client;
+  GError *error = NULL;
+
+  test->incoming_connection_delay = (gulong)(1.5 * G_USEC_PER_SEC);
+
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, WRITE_THEN_CLOSE);
+
+  client = g_socket_client_new ();
+  g_socket_client_set_timeout (client, 1);
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  /* No validation at all in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                0);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_error (test->read_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
+#ifndef BACKEND_IS_OPENSSL
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS);
+#endif
+}
+
+static void
+test_connection_binding_match_tls_unique (TestConnection *test,
+                                          gconstpointer   data)
+{
+  GSocketClient *client;
+  GIOStream *connection;
+  GByteArray *client_cb, *server_cb;
+  gchar *client_b64, *server_b64;
+  gboolean client_supports_tls_unique;
+  gboolean server_supports_tls_unique;
+  GError *error = NULL;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, WRITE_THEN_WAIT);
+
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* All validation in this test */
+  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);
+
+  /* tls-unique is supported by the OpenSSL backend always. It's supported by
+   * the GnuTLS backend only with TLS 1.2 or older. Since the test needs to be
+   * independent of backend and TLS version, this is allowed to fail....
+   */
+  client_supports_tls_unique = g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->client_connection),
+                                                                          G_TLS_CHANNEL_BINDING_TLS_UNIQUE, NULL, NULL);
+  server_supports_tls_unique = g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->server_connection),
+                                                                          G_TLS_CHANNEL_BINDING_TLS_UNIQUE, NULL, NULL);
+  g_assert_cmpint (client_supports_tls_unique, ==, server_supports_tls_unique);
+
+  /* Real test: retrieve bindings and compare */
+  if (client_supports_tls_unique)
+    {
+      g_assert_false (g_tls_connection_get_protocol_version (
+            G_TLS_CONNECTION (test->client_connection)) == G_TLS_PROTOCOL_VERSION_TLS_1_3);
+      client_cb = g_byte_array_new ();
+      server_cb = g_byte_array_new ();
+      g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->client_connection),
+                                                                G_TLS_CHANNEL_BINDING_TLS_UNIQUE, client_cb, NULL));
+      g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->server_connection),
+                                                                G_TLS_CHANNEL_BINDING_TLS_UNIQUE, server_cb, NULL));
+      g_assert_cmpint (client_cb->len, >, 0);
+      g_assert_cmpint (server_cb->len, >, 0);
+
+      client_b64 = g_base64_encode (client_cb->data, client_cb->len);
+      server_b64 = g_base64_encode (server_cb->data, server_cb->len);
+      g_assert_cmpstr (client_b64, ==, server_b64);
+
+      g_free (client_b64);
+      g_free (server_b64);
+      g_byte_array_unref (client_cb);
+      g_byte_array_unref (server_cb);
+    }
+  else
+    {
+      g_assert_true (g_tls_connection_get_protocol_version (
+            G_TLS_CONNECTION (test->client_connection)) == G_TLS_PROTOCOL_VERSION_TLS_1_3);
+      g_test_skip ("tls-unique is not supported");
+    }
+
+  /* drop the mic */
+  close_server_connection (test);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+}
+
+/* create_files.sh should update this digest but if anything goes wrong
+ * please make sure the string below matches the output of
+ * openssl x509 -outform der -in files/server.pem | openssl sha256 -binary | base64
+ **/
+#define SERVER_CERT_DIGEST_B64 "sdRMUK4PwcHXUPAMwglrSy4Fi8Ybfim61hfucliJ19s="
+static void
+test_connection_binding_match_tls_server_end_point (TestConnection *test,
+                                                    gconstpointer   data)
+{
+  GSocketClient *client;
+  GIOStream *connection;
+  GByteArray *client_cb, *server_cb;
+  gchar *client_b64, *server_b64;
+  GError *error = NULL;
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, WRITE_THEN_WAIT);
+
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* All validation in this test */
+  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);
+
+  /* Smoke test: ensure both sides support tls-server-end-point */
+  g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->client_connection),
+                                        G_TLS_CHANNEL_BINDING_TLS_SERVER_END_POINT, NULL, NULL));
+  g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->server_connection),
+                                        G_TLS_CHANNEL_BINDING_TLS_SERVER_END_POINT, NULL, NULL));
+
+  /* Real test: retrieve bindings and compare */
+  client_cb = g_byte_array_new ();
+  server_cb = g_byte_array_new ();
+  g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->client_connection),
+                                        G_TLS_CHANNEL_BINDING_TLS_SERVER_END_POINT, client_cb, NULL));
+  g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->server_connection),
+                                        G_TLS_CHANNEL_BINDING_TLS_SERVER_END_POINT, server_cb, NULL));
+
+  client_b64 = g_base64_encode (client_cb->data, client_cb->len);
+  server_b64 = g_base64_encode (server_cb->data, server_cb->len);
+  g_assert_cmpstr (client_b64, ==, server_b64);
+  g_assert_cmpstr (client_b64, ==, SERVER_CERT_DIGEST_B64);
+  g_assert_cmpstr (server_b64, ==, SERVER_CERT_DIGEST_B64);
+
+  g_free (client_b64);
+  g_free (server_b64);
+  g_byte_array_unref (client_cb);
+  g_byte_array_unref (server_cb);
+
+  /* drop the mic */
+  close_server_connection (test);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
 }
 
 static void
-simul_async_write_complete (GObject      *object,
-                           GAsyncResult *result,
-                           gpointer      user_data)
+test_connection_binding_match_tls_exporter (TestConnection *test,
+                                            gconstpointer   data)
 {
-  TestConnection *test = user_data;
-  gssize nwrote;
+  GSocketClient *client;
+  GIOStream *connection;
+  GByteArray *client_cb, *server_cb;
+  gchar *client_b64, *server_b64;
   GError *error = NULL;
 
-  nwrote = g_output_stream_write_finish (G_OUTPUT_STREAM (object),
-                                        result, &error);
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
   g_assert_no_error (error);
+  g_assert_nonnull (test->database);
 
-  test->nwrote += nwrote;
-  if (test->nwrote < TEST_DATA_LENGTH)
-    {
-      g_output_stream_write_async (G_OUTPUT_STREAM (object),
-                                  TEST_DATA + test->nwrote,
-                                  TEST_DATA_LENGTH - test->nwrote,
-                                  G_PRIORITY_DEFAULT, NULL,
-                                  simul_async_write_complete, test);
-    }
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, WRITE_THEN_WAIT);
+
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* All validation in this test */
+  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);
+
+  /* Smoke test: ensure both sides support tls-exporter */
+  g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->client_connection),
+                                                    G_TLS_CHANNEL_BINDING_TLS_EXPORTER, NULL, NULL));
+  g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->server_connection),
+                                                    G_TLS_CHANNEL_BINDING_TLS_EXPORTER, NULL, NULL));
+
+  /* Real test: retrieve bindings and compare */
+  client_cb = g_byte_array_new ();
+  server_cb = g_byte_array_new ();
+  g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->client_connection),
+                                                    G_TLS_CHANNEL_BINDING_TLS_EXPORTER, client_cb, NULL));
+  g_assert_true (g_tls_connection_get_channel_binding_data (G_TLS_CONNECTION (test->server_connection),
+                                                    G_TLS_CHANNEL_BINDING_TLS_EXPORTER, server_cb, NULL));
+
+  client_b64 = g_base64_encode (client_cb->data, client_cb->len);
+  server_b64 = g_base64_encode (server_cb->data, server_cb->len);
+  g_assert_cmpstr (client_b64, ==, server_b64);
+
+  g_free (client_b64);
+  g_free (server_b64);
+  g_byte_array_unref (client_cb);
+  g_byte_array_unref (server_cb);
+
+  /* drop the mic */
+  close_server_connection (test);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
 }
 
 static void
-test_simultaneous_async (TestConnection *test,
-                        gconstpointer   data)
+test_connection_missing_server_identity (TestConnection *test,
+                                         gconstpointer   data)
 {
   GIOStream *connection;
-  GTlsCertificateFlags flags;
   GError *error = NULL;
 
-  connection = start_echo_server_and_connect_to_it (test);
-  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  /* We pass NULL instead of test->identity when creating the client
+   * connection. This means verification must fail with
+   * G_TLS_CERTIFICATE_BAD_IDENTITY.
+   */
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, NULL, &error);
   g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
   g_object_unref (connection);
 
-  flags = G_TLS_CERTIFICATE_VALIDATE_ALL &
-    ~(G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY);
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* All validation in this test */
   g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
-                                                flags);
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
 
-  memset (test->buf, 0, sizeof (test->buf));
-  test->nread = test->nwrote = 0;
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
 
-  g_input_stream_read_async (g_io_stream_get_input_stream (test->client_connection),
-                            test->buf, TEST_DATA_LENGTH / 2,
-                            G_PRIORITY_DEFAULT, NULL,
-                            simul_async_read_complete, test);
-  g_output_stream_write_async (g_io_stream_get_output_stream (test->client_connection),
-                              TEST_DATA, TEST_DATA_LENGTH / 2,
-                              G_PRIORITY_DEFAULT, NULL,
-                              simul_async_write_complete, test);
+  g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+
+#ifdef BACKEND_IS_GNUTLS
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS);
+#elif defined(BACKEND_IS_OPENSSL)
+  /* FIXME: This is not OK. There should be a NOT_TLS errors. But some times
+   * we either get no error or BROKEN_PIPE
+   */
+#endif
+
+  g_clear_error (&test->read_error);
+  g_clear_error (&test->server_error);
+
+  g_clear_object (&test->address);
+  g_clear_object (&test->identity);
+
+  g_clear_object (&test->client_connection);
+  g_clear_object (&test->server_connection);
+
+  g_socket_service_stop (test->service);
+  g_clear_object (&test->service);
+
+  /* Now do the same thing again, this time ignoring bad identity. */
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL & ~G_TLS_CERTIFICATE_BAD_IDENTITY);
 
+  read_test_data_async (test);
   g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
 
-  g_assert_cmpint (test->nread, ==, TEST_DATA_LENGTH);
-  g_assert_cmpint (test->nwrote, ==, TEST_DATA_LENGTH);
-  g_assert_cmpstr (test->buf, ==, TEST_DATA);
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+}
+
+typedef struct {
+  TestConnection *test;
+  gboolean peer_certificate_notified;
+  gboolean peer_certificate_errors_notified;
+} NotifyTestData;
+
+static gboolean
+on_accept_certificate_peer_certificate_notify (GTlsConnection       *conn,
+                                               GTlsCertificate      *cert,
+                                               GTlsCertificateFlags  errors,
+                                               NotifyTestData       *data)
+{
+  TestConnection *test = data->test;
+
+  g_assert_true (G_IS_TLS_CERTIFICATE (cert));
+
+  /* We guarantee these props are not set until after the handshake. */
+  g_assert_null (g_tls_connection_get_peer_certificate (conn));
+  g_assert_cmpint (g_tls_connection_get_peer_certificate_errors (conn), ==, 0);
+
+  g_assert_false (data->peer_certificate_notified);
+  g_assert_false (data->peer_certificate_errors_notified);
+
+  return errors == test->accept_flags;
 }
 
 static void
-test_simultaneous_async_rehandshake (TestConnection *test,
-                                    gconstpointer   data)
+on_peer_certificate_notify (GTlsConnection *conn,
+                            GParamSpec     *pspec,
+                            gboolean       *notified)
 {
-  test->rehandshake = TRUE;
-  test_simultaneous_async (test, data);
+  *notified = TRUE;
 }
 
-static gpointer
-simul_read_thread (gpointer user_data)
+static void
+on_peer_certificate_errors_notify (GTlsConnection *conn,
+                                   GParamSpec     *pspec,
+                                   gboolean       *notified)
 {
-  TestConnection *test = user_data;
-  GInputStream *istream = g_io_stream_get_input_stream (test->client_connection);
+  *notified = TRUE;
+}
+
+static void
+test_peer_certificate_notify (TestConnection *test,
+                              gconstpointer   data)
+{
+  NotifyTestData notify_data = { test, FALSE, FALSE };
+  GIOStream *connection;
   GError *error = NULL;
-  gssize nread;
 
-  while (test->nread < TEST_DATA_LENGTH)
-    {
-      nread = g_input_stream_read (istream,
-                                  test->buf + test->nread,
-                                  MIN (TEST_DATA_LENGTH / 2, TEST_DATA_LENGTH - test->nread),
-                                  NULL, &error);
-      g_assert_no_error (error);
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
 
-      test->nread += nread;
-    }
+  /* For this test, we need validation to fail to ensure that the
+   * accept-certificate signal gets emitted.
+   */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
 
-  return NULL;
-}
+  g_signal_connect (test->client_connection, "accept-certificate",
+                    G_CALLBACK (on_accept_certificate_peer_certificate_notify), &notify_data);
+  g_signal_connect (test->client_connection, "notify::peer-certificate",
+                    G_CALLBACK (on_peer_certificate_notify), &notify_data.peer_certificate_notified);
+  g_signal_connect (test->client_connection, "notify::peer-certificate-errors",
+                    G_CALLBACK (on_peer_certificate_errors_notify), &notify_data.peer_certificate_errors_notified);
 
-static gpointer
-simul_write_thread (gpointer user_data)
-{
-  TestConnection *test = user_data;
-  GOutputStream *ostream = g_io_stream_get_output_stream (test->client_connection);
-  GError *error = NULL;
-  gssize nwrote;
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
 
-  while (test->nwrote < TEST_DATA_LENGTH)
-    {
-      nwrote = g_output_stream_write (ostream,
-                                     TEST_DATA + test->nwrote,
-                                     MIN (TEST_DATA_LENGTH / 2, TEST_DATA_LENGTH - test->nwrote),
-                                     NULL, &error);
-      g_assert_no_error (error);
+  g_assert_true (notify_data.peer_certificate_notified);
+  g_assert_true (notify_data.peer_certificate_errors_notified);
 
-      test->nwrote += nwrote;
-    }
+  g_assert_true (G_IS_TLS_CERTIFICATE (g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->client_connection))));
+  g_assert_cmpint (g_tls_connection_get_peer_certificate_errors (G_TLS_CONNECTION (test->client_connection)), !=, 0);
 
-  return NULL;
+  g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+#ifdef BACKEND_IS_GNUTLS
+  g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS);
+#elif defined(BACKEND_IS_OPENSSL)
+  /* FIXME: This is not OK. There should be a NOT_TLS errors. But some times
+   * we either get no error or BROKEN_PIPE
+   */
+#endif
 }
 
 static void
-test_simultaneous_sync (TestConnection *test,
-                       gconstpointer   data)
+test_tls_info (TestConnection *test,
+               gconstpointer   data)
 {
   GIOStream *connection;
-  GTlsCertificateFlags flags;
+  char *ciphersuite_name;
   GError *error = NULL;
-  GThread *read_thread, *write_thread;
 
-  connection = start_echo_server_and_connect_to_it (test);
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
   test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
   g_assert_no_error (error);
   g_object_unref (connection);
 
-  flags = G_TLS_CERTIFICATE_VALIDATE_ALL &
-    ~(G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY);
+  g_assert_cmpint (g_tls_connection_get_protocol_version (G_TLS_CONNECTION (test->client_connection)), ==, G_TLS_PROTOCOL_VERSION_UNKNOWN);
+  g_assert_null (g_tls_connection_get_ciphersuite_name (G_TLS_CONNECTION (test->client_connection)));
+
+  /* No validation at all in this test */
   g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
-                                                flags);
+                                                0);
 
-  memset (test->buf, 0, sizeof (test->buf));
-  test->nread = test->nwrote = 0;
 
-  read_thread = g_thread_new ("reader", simul_read_thread, test);
-  write_thread = g_thread_new ("writer", simul_write_thread, test);
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
 
-  /* We need to run the main loop to get the GThreadedSocketService to
-   * receive the connection and spawn the server thread.
-   */
-  while (!test->server_connection)
-    g_main_context_iteration (NULL, FALSE);
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
 
-  g_thread_join (write_thread);
-  g_thread_join (read_thread);
+  g_assert_cmpint (g_tls_connection_get_protocol_version (G_TLS_CONNECTION (test->client_connection)), !=, G_TLS_PROTOCOL_VERSION_UNKNOWN);
+  ciphersuite_name = g_tls_connection_get_ciphersuite_name (G_TLS_CONNECTION (test->client_connection));
+  g_assert_nonnull (ciphersuite_name);
+  g_free (ciphersuite_name);
+}
 
-  g_assert_cmpint (test->nread, ==, TEST_DATA_LENGTH);
-  g_assert_cmpint (test->nwrote, ==, TEST_DATA_LENGTH);
-  g_assert_cmpstr (test->buf, ==, TEST_DATA);
+static void
+test_connection_oscp_must_staple (TestConnection *test,
+                                  gconstpointer   data)
+{
+  GSocketClient *client;
+  GIOStream *connection;
+  GError *error = NULL;
 
-  g_io_stream_close (test->client_connection, NULL, &error);
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  test->server_certificate = g_tls_certificate_new_from_file (tls_test_file_path ("server-ocsp-required-by-server-and-key.pem"), &error);
+  g_assert_no_error (error);
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, WRITE_THEN_WAIT);
+
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
   g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  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);
+
+  close_server_connection (test);
+  wait_until_server_finished (test);
+
+  /* The server certificate states it supports status_request but our server does not
+   * actually set or support that.
+   * To be secure this must error as a bad certificate. */
+  g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+
+  g_clear_error (&test->read_error);
+  g_clear_error (&test->server_error);
 }
 
 static void
-test_simultaneous_sync_rehandshake (TestConnection *test,
-                                   gconstpointer   data)
+test_connection_oscp_must_staple_intermediate_certificate (TestConnection *test,
+                                                           gconstpointer   data)
 {
-  test->rehandshake = TRUE;
-  test_simultaneous_sync (test, data);
+  GSocketClient *client;
+  GIOStream *connection;
+  GError *error = NULL;
+
+#ifdef BACKEND_IS_OPENSSL
+  g_test_skip ("OCSP Must-Staple on intermediate certificates is not supported with the OpenSSL backend");
+  return;
+#endif
+
+  test->database = g_tls_file_database_new (tls_test_file_path ("ca-ocsp.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->database);
+
+  test->server_certificate = g_tls_certificate_new_from_file (tls_test_file_path ("server-ocsp-required-by-ca-and-key.pem"), &error);
+  g_assert_no_error (error);
+  start_async_server_service (test, G_TLS_AUTHENTICATION_NONE, WRITE_THEN_WAIT);
+
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (test->client_connection);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  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);
+
+  close_server_connection (test);
+  wait_until_server_finished (test);
+
+  /* The CA certificate states it supports status_request but our server does not
+   * actually set or support that.
+   * To be secure this must error as a bad certificate. */
+  g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+
+  g_clear_error (&test->read_error);
+  g_clear_error (&test->server_error);
 }
 
 int
@@ -825,40 +3081,139 @@ main (int   argc,
       char *argv[])
 {
   int ret;
+#ifdef BACKEND_IS_GNUTLS
+  char *module_path;
+  const char *spy_path;
+#endif
 
-  g_type_init ();
   g_test_init (&argc, &argv, NULL);
+  g_test_bug_base ("http://bugzilla.gnome.org/");
 
   g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
-  g_setenv ("GIO_EXTRA_MODULES", TOP_BUILDDIR "/tls/gnutls/.libs", TRUE);
-  g_setenv ("GIO_USE_TLS", "gnutls", TRUE);
+  g_setenv ("GIO_USE_TLS", BACKEND, TRUE);
+
+  g_assert_true (g_ascii_strcasecmp (G_OBJECT_TYPE_NAME (g_tls_backend_get_default ()), "GTlsBackend" BACKEND) == 0);
+
+#ifdef BACKEND_IS_GNUTLS
+  module_path = g_test_build_filename (G_TEST_BUILT, "mock-pkcs11.so", NULL);
+  g_assert_true (g_file_test (module_path, G_FILE_TEST_EXISTS));
 
-  g_test_add ("/tls/connection/basic", TestConnection, NULL,
+  /* This just adds extra logging which is useful for debugging */
+  spy_path = g_getenv ("PKCS11SPY_PATH");
+  if (!spy_path)
+    {
+      spy_path = "/usr/lib64/pkcs11-spy.so"; /* Fedora's path */
+      if (!g_file_test (spy_path, G_FILE_TEST_EXISTS))
+        spy_path = "/usr/lib/x86_64-linux-gnu/pkcs11-spy.so"; /* Debian/Ubuntu's path */
+    }
+
+  if (g_file_test (spy_path, G_FILE_TEST_EXISTS))
+    {
+      g_debug ("Using PKCS #11 Spy");
+      g_setenv ("PKCS11SPY", module_path, TRUE);
+      g_free (module_path);
+      module_path = g_strdup (spy_path);
+    }
+
+  ret = gnutls_pkcs11_init (GNUTLS_PKCS11_FLAG_MANUAL, NULL);
+  g_assert_cmpint (ret, ==, GNUTLS_E_SUCCESS);
+  ret = gnutls_pkcs11_add_provider (module_path, NULL);
+  g_assert_cmpint (ret, ==, GNUTLS_E_SUCCESS);
+  g_free (module_path);
+#endif
+
+  g_test_add ("/tls/" BACKEND "/connection/basic", TestConnection, NULL,
               setup_connection, test_basic_connection, teardown_connection);
-  g_test_add ("/tls/connection/verified", TestConnection, NULL,
+  g_test_add ("/tls/" BACKEND "/connection/verified", TestConnection, NULL,
               setup_connection, test_verified_connection, teardown_connection);
-  g_test_add ("/tls/connection/client-auth", TestConnection, NULL,
+  g_test_add ("/tls/" BACKEND "/connection/verified-chain", TestConnection, NULL,
+              setup_connection, test_verified_chain, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/verified-chain-with-redundant-root-cert", TestConnection, NULL,
+              setup_connection, test_verified_chain_with_redundant_root_cert, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/verified-chain-with-duplicate-server-cert", TestConnection, NULL,
+              setup_connection, test_verified_chain_with_duplicate_server_cert, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/verified-unordered-chain", TestConnection, NULL,
+              setup_connection, test_verified_unordered_chain, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/verified-chain-with-alternative-ca-cert", TestConnection, NULL,
+              setup_connection, test_verified_chain_with_alternative_ca_cert, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/invalid-chain-with-alternative-ca-cert", TestConnection, NULL,
+              setup_connection, test_invalid_chain_with_alternative_ca_cert, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/client-auth", TestConnection, NULL,
               setup_connection, test_client_auth_connection, teardown_connection);
-  g_test_add ("/tls/connection/client-auth-rehandshake", TestConnection, NULL,
+  g_test_add ("/tls/" BACKEND "/connection/client-auth-rehandshake", TestConnection, NULL,
               setup_connection, test_client_auth_rehandshake, teardown_connection);
-  g_test_add ("/tls/connection/no-database", TestConnection, NULL,
+  g_test_add ("/tls/" BACKEND "/connection/client-auth-failure", TestConnection, NULL,
+              setup_connection, test_client_auth_failure, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/client-auth-fail-missing-client-private-key", TestConnection, NULL,
+              setup_connection, test_client_auth_fail_missing_client_private_key, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/client-auth-request-cert", TestConnection, NULL,
+              setup_connection, test_client_auth_request_cert, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/client-auth-request-fail", TestConnection, NULL,
+              setup_connection, test_client_auth_request_fail, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/client-auth-request-none", TestConnection, NULL,
+              setup_connection, test_client_auth_request_none, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/client-auth-pkcs11", TestConnection, NULL,
+              setup_connection, test_client_auth_pkcs11_connection, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/no-database", TestConnection, NULL,
               setup_connection, test_connection_no_database, teardown_connection);
-  g_test_add ("/tls/connection/failed", TestConnection, NULL,
+  g_test_add ("/tls/" BACKEND "/connection/failed", TestConnection, NULL,
               setup_connection, test_failed_connection, teardown_connection);
-  g_test_add ("/tls/connection/socket-client", TestConnection, NULL,
+  g_test_add ("/tls/" BACKEND "/connection/socket-client", TestConnection, NULL,
               setup_connection, test_connection_socket_client, teardown_connection);
-  g_test_add ("/tls/connection/socket-client-failed", TestConnection, NULL,
+  g_test_add ("/tls/" BACKEND "/connection/socket-client-failed", TestConnection, NULL,
               setup_connection, test_connection_socket_client_failed, teardown_connection);
-  g_test_add ("/tls/connection/simultaneous-async", TestConnection, NULL,
+  g_test_add ("/tls/" BACKEND "/connection/read-time-out-then-write", TestConnection, NULL,
+              setup_connection, test_connection_read_time_out_write, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/simultaneous-async", TestConnection, NULL,
               setup_connection, test_simultaneous_async, teardown_connection);
-  g_test_add ("/tls/connection/simultaneous-sync", TestConnection, NULL,
-             setup_connection, test_simultaneous_sync, teardown_connection);
-  g_test_add ("/tls/connection/simultaneous-async-rehandshake", TestConnection, NULL,
+  g_test_add ("/tls/" BACKEND "/connection/simultaneous-sync", TestConnection, NULL,
+              setup_connection, test_simultaneous_sync, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/simultaneous-async-rehandshake", TestConnection, NULL,
               setup_connection, test_simultaneous_async_rehandshake, teardown_connection);
-  g_test_add ("/tls/connection/simultaneous-sync-rehandshake", TestConnection, NULL,
-             setup_connection, test_simultaneous_sync_rehandshake, teardown_connection);
-
-  ret = g_test_run();
+  g_test_add ("/tls/" BACKEND "/connection/simultaneous-sync-rehandshake", TestConnection, NULL,
+              setup_connection, test_simultaneous_sync_rehandshake, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/close-immediately", TestConnection, NULL,
+              setup_connection, test_close_immediately, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/unclean-close-by-server", TestConnection, NULL,
+              setup_connection, test_unclean_close_by_server, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/async-implicit-handshake", TestConnection, NULL,
+              setup_connection, test_async_implicit_handshake, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/output-stream-close", TestConnection, NULL,
+              setup_connection, test_output_stream_close, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/garbage-database", TestConnection, NULL,
+              setup_connection, test_garbage_database, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/readwrite-after-connection-destroyed", TestConnection, NULL,
+              setup_connection, test_readwrite_after_connection_destroyed, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/alpn/match", TestConnection, NULL,
+              setup_connection, test_alpn_match, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/alpn/no-match", TestConnection, NULL,
+              setup_connection, test_alpn_no_match, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/alpn/client-only", TestConnection, NULL,
+              setup_connection, test_alpn_client_only, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/alpn/server-only", TestConnection, NULL,
+              setup_connection, test_alpn_server_only, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/sync-op-during-handshake", TestConnection, NULL,
+              setup_connection, test_sync_op_during_handshake, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/socket-timeout", TestConnection, NULL,
+              setup_connection, test_socket_timeout, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/missing-server-identity", TestConnection, NULL,
+              setup_connection, test_connection_missing_server_identity, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/peer-certificate-notify", TestConnection, NULL,
+              setup_connection, test_peer_certificate_notify, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/binding/match-tls-unique", TestConnection, NULL,
+              setup_connection, test_connection_binding_match_tls_unique, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/binding/match-tls-server-end-point", TestConnection, NULL,
+              setup_connection, test_connection_binding_match_tls_server_end_point, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/binding/match-tls-exporter", TestConnection, NULL,
+              setup_connection, test_connection_binding_match_tls_exporter, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/tls-info", TestConnection, NULL,
+              setup_connection, test_tls_info, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/oscp/must-staple", TestConnection, NULL,
+              setup_connection, test_connection_oscp_must_staple, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/oscp/must-staple-intermediate-certificate", TestConnection, NULL,
+              setup_connection, test_connection_oscp_must_staple_intermediate_certificate, teardown_connection);
+
+  ret = g_test_run ();
 
   /* for valgrinding */
   g_main_context_unref (g_main_context_default ());