[glib-networking] Implement ALPN support
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking] Implement ALPN support
- Date: Thu, 20 Dec 2018 17:01:32 +0000 (UTC)
commit 5e085d7f4fdc3d6a69f6f2ef4dca986b1a9571a1
Author: Scott Hutton <schutton cisco com>
Date: Wed Dec 19 19:34:30 2018 -0800
Implement ALPN support
Fixes #47
tls/gnutls/gtlsconnection-gnutls.c | 109 +++++++++++++++++++++++++++++++++++++
tls/tests/connection.c | 100 ++++++++++++++++++++++++++++++++++
tls/tests/dtls-connection.c | 101 ++++++++++++++++++++++++++++++++++
3 files changed, 310 insertions(+)
---
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 3200079..31ce8cf 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -130,6 +130,10 @@ enum
PROP_INTERACTION,
PROP_PEER_CERTIFICATE,
PROP_PEER_CERTIFICATE_ERRORS,
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ PROP_ADVERTISED_PROTOCOLS,
+ PROP_NEGOTIATED_PROTOCOL,
+#endif
};
typedef struct
@@ -214,6 +218,11 @@ typedef struct
GTlsInteraction *interaction;
gchar *interaction_id;
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ gchar **advertised_protocols;
+ gchar *negotiated_protocol;
+#endif
+
GMutex op_mutex;
GCancellable *waiting_for_op;
@@ -456,6 +465,11 @@ g_tls_connection_gnutls_finalize (GObject *object)
g_free (priv->interaction_id);
g_clear_object (&priv->interaction);
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ g_clear_pointer (&priv->advertised_protocols, g_strfreev);
+ g_clear_pointer (&priv->negotiated_protocol, g_free);
+#endif
+
g_clear_error (&priv->handshake_error);
g_clear_error (&priv->read_error);
g_clear_error (&priv->write_error);
@@ -534,6 +548,16 @@ g_tls_connection_gnutls_get_property (GObject *object,
g_value_set_flags (value, priv->peer_certificate_errors);
break;
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ case PROP_ADVERTISED_PROTOCOLS:
+ g_value_set_boxed (value, priv->advertised_protocols);
+ break;
+
+ case PROP_NEGOTIATED_PROTOCOL:
+ g_value_set_string (value, priv->negotiated_protocol);
+ break;
+#endif
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -628,6 +652,13 @@ g_tls_connection_gnutls_set_property (GObject *object,
priv->interaction = g_value_dup_object (value);
break;
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ case PROP_ADVERTISED_PROTOCOLS:
+ g_clear_pointer (&priv->advertised_protocols, g_strfreev);
+ priv->advertised_protocols = g_value_dup_boxed (value);
+ break;
+#endif
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -2047,9 +2078,56 @@ handshake_thread (GTask *task,
static void
begin_handshake (GTlsConnectionGnutls *gnutls)
{
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+
+ if (priv->advertised_protocols)
+ {
+ gnutls_datum_t *protocols;
+ int n_protos, i;
+
+ n_protos = g_strv_length (priv->advertised_protocols);
+ protocols = g_new (gnutls_datum_t, n_protos);
+ for (i = 0; priv->advertised_protocols[i]; i++)
+ {
+ protocols[i].size = strlen (priv->advertised_protocols[i]);
+ protocols[i].data = g_memdup (priv->advertised_protocols[i], protocols[i].size);
+ }
+ gnutls_alpn_set_protocols (priv->session, protocols, n_protos, 0);
+ g_free (protocols);
+ }
+#endif
+
G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
}
+#if GLIB_CHECK_VERSION(2, 59, 1)
+static void
+update_negotiated_protocol (GTlsConnectionGnutls *gnutls)
+{
+ GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+ gchar *orig_negotiated_protocol;
+ gnutls_datum_t protocol;
+
+ /*
+ * Preserve the prior negotiated protocol before clearing it
+ */
+ orig_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
+
+
+ if (gnutls_alpn_get_selected_protocol (priv->session, &protocol) == 0 && protocol.size > 0)
+ priv->negotiated_protocol = g_strndup ((gchar *)protocol.data, protocol.size);
+
+ /*
+ * Notify only if the negotiated protocol changed
+ */
+ if (g_strcmp0 (orig_negotiated_protocol, priv->negotiated_protocol) != 0)
+ g_object_notify (G_OBJECT (gnutls), "negotiated-protocol");
+
+ g_free (orig_negotiated_protocol);
+}
+#endif
+
static gboolean
finish_handshake (GTlsConnectionGnutls *gnutls,
GTask *task,
@@ -2082,6 +2160,11 @@ finish_handshake (GTlsConnectionGnutls *gnutls,
_("Unacceptable TLS certificate"));
}
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ if (!*error && priv->advertised_protocols)
+ update_negotiated_protocol (gnutls);
+#endif
+
if (*error && priv->started_handshake)
priv->handshake_error = g_error_copy (*error);
@@ -3007,6 +3090,24 @@ g_tls_connection_gnutls_dtls_shutdown_finish (GDtlsConnection *conn,
return g_task_propagate_boolean (G_TASK (result), error);
}
+#if GLIB_CHECK_VERSION(2, 59, 1)
+static void
+g_tls_connection_gnutls_dtls_set_advertised_protocols (GDtlsConnection *conn,
+ const gchar * const *protocols)
+{
+ g_object_set (conn, "advertised-protocols", protocols, NULL);
+}
+
+const gchar *
+g_tls_connection_gnutls_dtls_get_negotiated_protocol (GDtlsConnection *conn)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
+ GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+
+ return priv->negotiated_protocol;
+}
+#endif
+
static void
g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
{
@@ -3039,6 +3140,10 @@ 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");
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ g_object_class_override_property (gobject_class, PROP_ADVERTISED_PROTOCOLS, "advertised-protocols");
+ g_object_class_override_property (gobject_class, PROP_NEGOTIATED_PROTOCOL, "negotiated-protocol");
+#endif
}
static void
@@ -3056,6 +3161,10 @@ g_tls_connection_gnutls_dtls_connection_iface_init (GDtlsConnectionInterface *if
iface->shutdown = g_tls_connection_gnutls_dtls_shutdown;
iface->shutdown_async = g_tls_connection_gnutls_dtls_shutdown_async;
iface->shutdown_finish = g_tls_connection_gnutls_dtls_shutdown_finish;
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ iface->set_advertised_protocols = g_tls_connection_gnutls_dtls_set_advertised_protocols;
+ iface->get_negotiated_protocol = g_tls_connection_gnutls_dtls_get_negotiated_protocol;
+#endif
}
static void
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 41d73c8..41b66b7 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -79,6 +79,9 @@ typedef struct {
gboolean server_should_close;
gboolean server_running;
GTlsCertificate *server_certificate;
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ const gchar * const *server_protocols;
+#endif
char buf[128];
gssize nread, nwrote;
@@ -302,6 +305,14 @@ on_incoming_connection (GSocketService *service,
if (test->database)
g_tls_connection_set_database (G_TLS_CONNECTION (test->server_connection), test->database);
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ if (test->server_protocols)
+ {
+ g_tls_connection_set_advertised_protocols (G_TLS_CONNECTION (test->server_connection),
+ test->server_protocols);
+ }
+#endif
+
stream = g_io_stream_get_output_stream (test->server_connection);
g_output_stream_write_async (stream, TEST_DATA,
@@ -2068,6 +2079,86 @@ test_readwrite_after_connection_destroyed (TestConnection *test,
g_object_unref (ostream);
}
+static void
+test_alpn (TestConnection *test,
+ const char * const *client_protocols,
+ const char * const *server_protocols,
+ const char *negotiated_protocol)
+{
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ 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);
+ 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 GLib 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[])
@@ -2140,6 +2231,15 @@ main (int argc,
g_test_add ("/tls/connection/readwrite-after-connection-destroyed", TestConnection, NULL,
setup_connection, test_readwrite_after_connection_destroyed, 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 */
diff --git a/tls/tests/dtls-connection.c b/tls/tests/dtls-connection.c
index 105b879..a589650 100644
--- a/tls/tests/dtls-connection.c
+++ b/tls/tests/dtls-connection.c
@@ -89,6 +89,9 @@ typedef struct {
gboolean expect_server_error;
GError *server_error;
gboolean server_running;
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ const gchar * const *server_protocols;
+#endif
char buf[128];
gssize nread, nwrote;
@@ -397,6 +400,14 @@ on_incoming_connection (GSocket *socket,
if (test->database)
g_dtls_connection_set_database (G_DTLS_CONNECTION (test->server_connection), test->database);
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ if (test->server_protocols)
+ {
+ g_dtls_connection_set_advertised_protocols (G_DTLS_CONNECTION (test->server_connection),
+ test->server_protocols);
+ }
+#endif
+
if (test->test_data->server_should_disappear)
{
close_server_connection (test, FALSE);
@@ -726,6 +737,83 @@ test_connection_timeouts_read (TestConnection *test,
g_assert_error (test->read_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
}
+static void
+test_alpn (TestConnection *test,
+ const char * const *client_protocols,
+ const char * const *server_protocols,
+ const char *negotiated_protocol)
+{
+#if GLIB_CHECK_VERSION(2, 59, 1)
+ GDatagramBased *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_server_and_connect_to_it (test, FALSE);
+ test->client_connection = g_dtls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_object_unref (connection);
+
+ if (client_protocols)
+ {
+ g_dtls_connection_set_advertised_protocols (G_DTLS_CONNECTION (test->client_connection),
+ client_protocols);
+ }
+
+ g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+ read_test_data_async (test);
+ while (!test->loop_finished)
+ g_main_context_iteration (test->client_context, TRUE);
+
+ g_assert_no_error (test->server_error);
+ g_assert_no_error (test->read_error);
+
+ g_assert_cmpstr (g_dtls_connection_get_negotiated_protocol (G_DTLS_CONNECTION (test->server_connection)),
==, negotiated_protocol);
+ g_assert_cmpstr (g_dtls_connection_get_negotiated_protocol (G_DTLS_CONNECTION (test->client_connection)),
==, negotiated_protocol);
+#else
+ g_test_skip ("no support for ALPN in this GLib 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[])
@@ -807,6 +895,19 @@ main (int argc,
setup_connection, test_connection_timeouts_read,
teardown_connection);
+ g_test_add ("/dtls/connection/alpn/match", TestConnection, &blocking,
+ setup_connection, test_alpn_match,
+ teardown_connection);
+ g_test_add ("/dtls/connection/alpn/no-match", TestConnection, &blocking,
+ setup_connection, test_alpn_no_match,
+ teardown_connection);
+ g_test_add ("/dtls/connection/alpn/client-only", TestConnection, &blocking,
+ setup_connection, test_alpn_client_only,
+ teardown_connection);
+ g_test_add ("/dtls/connection/alpn/server-only", TestConnection, &blocking,
+ 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]