Commit 355eb072 authored by Dan Winship's avatar Dan Winship
Browse files

gnutls: implement ALPN properties

Implement GTlsConnection:advertised-protocols and
:negotiated-protocol.
parent 26e58fd2
......@@ -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" = ""],
......
......@@ -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
......
......@@ -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 */
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment