[glib-networking/wip/danw/alpn] gnutls: implement ALPN properties



commit 355eb0728abd683706c4524a9923da56868d7789
Author: Dan Winship <danw gnome org>
Date:   Tue Dec 9 11:09:23 2014 +0100

    gnutls: implement ALPN properties
    
    Implement GTlsConnection:advertised-protocols and
    :negotiated-protocol.

 configure.ac                       |    4 +-
 tls/gnutls/gtlsconnection-gnutls.c |   83 ++++++++++++++++++++++++++++++-
 tls/tests/connection.c             |   96 ++++++++++++++++++++++++++++++++++++
 3 files changed, 179 insertions(+), 4 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 618b182..590ddf9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -33,8 +33,8 @@ AM_GLIB_GNU_GETTEXT
 dnl *****************************
 dnl *** Check GLib GIO        ***
 dnl *****************************
-AM_PATH_GLIB_2_0(2.39.1,,AC_MSG_ERROR(GLIB not found),gio)
-GLIB_CFLAGS="$GLIB_CFLAGS -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_42"
+AM_PATH_GLIB_2_0(2.43.1,,AC_MSG_ERROR(GLIB not found),gio)
+GLIB_CFLAGS="$GLIB_CFLAGS -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_44"
 
 GIO_MODULE_DIR=$($PKG_CONFIG --variable giomoduledir gio-2.0)
 AS_IF([test "$GIO_MODULE_DIR" = ""],
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 7436451..72c6594 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -87,7 +87,9 @@ enum
   PROP_CERTIFICATE,
   PROP_INTERACTION,
   PROP_PEER_CERTIFICATE,
-  PROP_PEER_CERTIFICATE_ERRORS
+  PROP_PEER_CERTIFICATE_ERRORS,
+  PROP_ADVERTISED_PROTOCOLS,
+  PROP_NEGOTIATED_PROTOCOL,
 };
 
 struct _GTlsConnectionGnutlsPrivate
@@ -145,6 +147,9 @@ struct _GTlsConnectionGnutlsPrivate
   GTlsInteraction *interaction;
   gchar *interaction_id;
 
+  char **advertised_protocols;
+  char *negotiated_protocol;
+
   GMutex        op_mutex;
   GCancellable *waiting_for_op;
 
@@ -353,6 +358,9 @@ g_tls_connection_gnutls_finalize (GObject *object)
   g_free (gnutls->priv->interaction_id);
   g_clear_object (&gnutls->priv->interaction);
 
+  g_clear_pointer (&gnutls->priv->advertised_protocols, g_strfreev);
+  g_clear_pointer (&gnutls->priv->negotiated_protocol, g_free);
+
   g_clear_error (&gnutls->priv->handshake_error);
   g_clear_error (&gnutls->priv->read_error);
   g_clear_error (&gnutls->priv->write_error);
@@ -424,6 +432,14 @@ g_tls_connection_gnutls_get_property (GObject    *object,
       g_value_set_flags (value, gnutls->priv->peer_certificate_errors);
       break;
 
+    case PROP_ADVERTISED_PROTOCOLS:
+      g_value_set_boxed (value, gnutls->priv->advertised_protocols);
+      break;
+
+    case PROP_NEGOTIATED_PROTOCOL:
+      g_value_set_string (value, gnutls->priv->negotiated_protocol);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -506,6 +522,11 @@ g_tls_connection_gnutls_set_property (GObject      *object,
       gnutls->priv->interaction = g_value_dup_object (value);
       break;
 
+    case PROP_ADVERTISED_PROTOCOLS:
+      g_clear_pointer (&gnutls->priv->advertised_protocols, g_strfreev);
+      gnutls->priv->advertised_protocols = g_value_dup_boxed (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -1230,6 +1251,25 @@ handshake_thread (GTask        *task,
 
   g_tls_connection_gnutls_set_handshake_priority (gnutls);
 
+#if (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 2) || (GNUTLS_VERSION_MAJOR > 3)
+  if (gnutls->priv->advertised_protocols)
+    {
+      gnutls_datum_t *protocols;
+      int n_protos, i;
+
+      n_protos = g_strv_length (gnutls->priv->advertised_protocols);
+      protocols = g_new (gnutls_datum_t, n_protos);
+      for (i = 0; gnutls->priv->advertised_protocols[i]; i++)
+       {
+         protocols[i].size = strlen (gnutls->priv->advertised_protocols[i]);
+         protocols[i].data = g_memdup (gnutls->priv->advertised_protocols[i], protocols[i].size);
+       }
+      gnutls_alpn_set_protocols (gnutls->priv->session,
+                                protocols, n_protos, 0);
+      g_free (protocols);
+    }
+#endif
+
   BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
   ret = gnutls_handshake (gnutls->priv->session);
   if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
@@ -1308,7 +1348,23 @@ accept_peer_certificate (GTlsConnectionGnutls *gnutls,
 static void
 begin_handshake (GTlsConnectionGnutls *gnutls)
 {
+  g_object_freeze_notify (G_OBJECT (gnutls));
+  if (gnutls->priv->peer_certificate)
+    {
+      g_clear_object (&gnutls->priv->peer_certificate);
+      gnutls->priv->peer_certificate_errors = 0;
+      g_object_notify (G_OBJECT (gnutls), "peer-certificate");
+      g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
+    }
+  if (gnutls->priv->negotiated_protocol)
+    {
+      g_clear_pointer (&gnutls->priv->negotiated_protocol, g_free);
+      g_object_notify (G_OBJECT (gnutls), "negotiated-protocol");
+    }
+
   G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
+
+  g_object_thaw_notify (G_OBJECT (gnutls));
 }
 
 static gboolean
@@ -1326,7 +1382,12 @@ finish_handshake (GTlsConnectionGnutls  *gnutls,
   peer_certificate_errors = gnutls->priv->peer_certificate_errors_tmp;
   gnutls->priv->peer_certificate_errors_tmp = 0;
 
-  if (g_task_propagate_boolean (task, error) && peer_certificate)
+  if (!g_task_propagate_boolean (task, error))
+    g_clear_object (&peer_certificate);
+
+  g_object_freeze_notify (G_OBJECT (gnutls));
+
+  if (!*error && peer_certificate)
     {
       if (!accept_peer_certificate (gnutls, peer_certificate,
                                    peer_certificate_errors))
@@ -1341,6 +1402,22 @@ finish_handshake (GTlsConnectionGnutls  *gnutls,
       g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
     }
 
+#if (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 2) || (GNUTLS_VERSION_MAJOR > 3)
+  if (!*error && gnutls->priv->advertised_protocols)
+    {
+      gnutls_datum_t protocol;
+
+      if (gnutls_alpn_get_selected_protocol (gnutls->priv->session, &protocol) == 0 &&
+         protocol.size > 0)
+       {
+         gnutls->priv->negotiated_protocol = g_strndup (protocol.data, protocol.size);
+         g_object_notify (G_OBJECT (gnutls), "negotiated-protocol");
+       }
+    }
+#endif
+
+  g_object_thaw_notify (G_OBJECT (gnutls));
+
   if (*error && gnutls->priv->started_handshake)
     gnutls->priv->handshake_error = g_error_copy (*error);
 
@@ -1768,6 +1845,8 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
   g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
   g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE, "peer-certificate");
   g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE_ERRORS, "peer-certificate-errors");
+  g_object_class_override_property (gobject_class, PROP_ADVERTISED_PROTOCOLS, "advertised-protocols");
+  g_object_class_override_property (gobject_class, PROP_NEGOTIATED_PROTOCOL, "negotiated-protocol");
 }
 
 static void
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 6bcb9c2..544450a 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -72,6 +72,7 @@ typedef struct {
   GError *server_error;
   gboolean server_should_close;
   gboolean server_running;
+  const char * const *server_protocols;
 
   char buf[128];
   gssize nread, nwrote;
@@ -277,6 +278,12 @@ 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,
@@ -1462,6 +1469,86 @@ test_fallback_subprocess (TestConnection *test,
   g_assert_no_error (error);
 }
 
+static void
+test_alpn (TestConnection *test,
+          const char * const *client_protocols,
+          const char * const *server_protocols,
+          const char *negotiated_protocol)
+{
+#if (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 2) || (GNUTLS_VERSION_MAJOR > 3)
+  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 (test->database);
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE, TRUE);
+  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);
+
+  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);
+#else
+  g_test_skip ("no support for ALPN in this gnutls version");
+#endif
+}
+
+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);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -1544,6 +1631,15 @@ main (int   argc,
              TestConnection, NULL,
               setup_connection, test_fallback_subprocess, teardown_connection);
 
+  g_test_add ("/tls/connection/alpn/match", TestConnection, NULL,
+              setup_connection, test_alpn_match, teardown_connection);
+  g_test_add ("/tls/connection/alpn/no-match", TestConnection, NULL,
+              setup_connection, test_alpn_no_match, teardown_connection);
+  g_test_add ("/tls/connection/alpn/client-only", TestConnection, NULL,
+              setup_connection, test_alpn_client_only, teardown_connection);
+  g_test_add ("/tls/connection/alpn/server-only", TestConnection, NULL,
+              setup_connection, test_alpn_server_only, teardown_connection);
+
   ret = g_test_run();
 
   /* for valgrinding */


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