[glib-networking/wip/danw/sni: 3/3] gnutls: Add SNI support
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/danw/sni: 3/3] gnutls: Add SNI support
- Date: Wed, 31 May 2017 19:04:07 +0000 (UTC)
commit a27b4fd94f6f0bbbaca123c8dce2cf51aaf55b7e
Author: Dan Winship <danw gnome org>
Date: Thu Dec 11 21:23:18 2014 +0100
gnutls: Add SNI support
Implement the new GTlsServerConnection:server-identity property, and
test that it can be used to adjust the server certificate
appropriately during the handshake.
Based on a patch from Marcin Lewandowski
https://bugzilla.gnome.org/show_bug.cgi?id=681312
tls/gnutls/gtlsserverconnection-gnutls.c | 65 ++++++++++++++++++++++++-
tls/tests/connection.c | 79 ++++++++++++++++++++++++++++++
2 files changed, 143 insertions(+), 1 deletions(-)
---
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index 11afb71..d92c739 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -29,10 +29,13 @@
#include "gtlscertificate-gnutls.h"
#include <glib/gi18n-lib.h>
+#define MAX_SERVER_NAME_LEN 255
+
enum
{
PROP_0,
- PROP_AUTHENTICATION_MODE
+ PROP_AUTHENTICATION_MODE,
+ PROP_SERVER_IDENTITY
};
static void g_tls_server_connection_gnutls_initable_interface_init (GInitableIface *iface);
@@ -46,6 +49,8 @@ static int g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t
int pk_algos_length,
gnutls_retr2_st *st);
+static int g_tls_server_connection_gnutls_server_name_cb (gnutls_session_t session);
+
static int g_tls_server_connection_gnutls_db_store (void *user_data,
gnutls_datum_t key,
gnutls_datum_t data);
@@ -66,6 +71,7 @@ G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionGnutls, g_tls_server_connection_gnu
struct _GTlsServerConnectionGnutlsPrivate
{
GTlsAuthenticationMode authentication_mode;
+ char *server_identity;
};
static void
@@ -121,6 +127,10 @@ g_tls_server_connection_gnutls_get_property (GObject *object,
g_value_set_enum (value, gnutls->priv->authentication_mode);
break;
+ case PROP_SERVER_IDENTITY:
+ g_value_set_string (value, gnutls->priv->server_identity);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -140,11 +150,26 @@ g_tls_server_connection_gnutls_set_property (GObject *object,
gnutls->priv->authentication_mode = g_value_get_enum (value);
break;
+ case PROP_SERVER_IDENTITY:
+ g_clear_pointer (&gnutls->priv->server_identity, g_free);
+ gnutls->priv->server_identity = g_value_dup_string (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
+static void
+g_tls_server_connection_gnutls_finalize (GObject *object)
+{
+ GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
+
+ g_free (gnutls->priv->server_identity);
+
+ G_OBJECT_CLASS (g_tls_server_connection_gnutls_parent_class)->finalize (object);
+}
+
static int
g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t session,
const gnutls_datum_t *req_ca_rdn,
@@ -185,7 +210,11 @@ g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
}
session = g_tls_connection_gnutls_get_session (conn);
+
gnutls_certificate_server_set_request (session, req_mode);
+ gnutls_handshake_set_post_client_hello_function (session, g_tls_server_connection_gnutls_server_name_cb);
+
+ g_clear_pointer (&gnutls->priv->server_identity, g_free);
}
static void
@@ -194,6 +223,37 @@ g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls *gnutls,
{
}
+static int
+g_tls_server_connection_gnutls_server_name_cb (gnutls_session_t session)
+{
+ GTlsServerConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
+ gchar name[MAX_SERVER_NAME_LEN];
+ gsize length = MAX_SERVER_NAME_LEN;
+ guint type;
+ int ret;
+
+ ret = gnutls_server_name_get (session, name, &length, &type, 0);
+ if (ret == 0 && type == GNUTLS_NAME_DNS)
+ {
+ if (gnutls->priv->server_identity)
+ g_free (gnutls->priv->server_identity);
+ gnutls->priv->server_identity = g_strdup (name);
+ g_object_notify (G_OBJECT (gnutls), "server-identity");
+ }
+ else if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
+ g_warning ("ignoring too-long SNI name");
+
+ return 0;
+}
+
+static const char *
+g_tls_server_connection_gnutls_get_server_identity (GTlsServerConnection *conn)
+{
+ GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (conn);
+
+ return gnutls->priv->server_identity;
+}
+
/* Session cache management */
static int
@@ -262,17 +322,20 @@ g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klas
gobject_class->get_property = g_tls_server_connection_gnutls_get_property;
gobject_class->set_property = g_tls_server_connection_gnutls_set_property;
+ gobject_class->finalize = g_tls_server_connection_gnutls_finalize;
connection_gnutls_class->failed = g_tls_server_connection_gnutls_failed;
connection_gnutls_class->begin_handshake = g_tls_server_connection_gnutls_begin_handshake;
connection_gnutls_class->finish_handshake = g_tls_server_connection_gnutls_finish_handshake;
g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
+ g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
}
static void
g_tls_server_connection_gnutls_server_connection_interface_init (GTlsServerConnectionInterface *iface)
{
+ iface->get_server_identity = g_tls_server_connection_gnutls_get_server_identity;
}
static void
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 6bcb9c2..830c54d 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;
+ gboolean do_sni;
char buf[128];
gssize nread, nwrote;
@@ -251,6 +252,36 @@ on_output_write_finish (GObject *object,
close_server_connection (test);
}
+static void
+on_received_server_identity (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ TestConnection *test = user_data;
+ const gchar *identity;
+ GTlsCertificate *cert;
+ GError *error = NULL;
+
+ identity = g_tls_server_connection_get_server_identity (G_TLS_SERVER_CONNECTION (object));
+
+ if (!test->do_sni)
+ {
+ g_assert_cmpstr (identity, ==, "server.example.com");
+ return;
+ }
+
+ if (!strcmp (identity, "other.example.com"))
+ {
+ cert = g_tls_certificate_new_from_files (tls_test_file_path ("other.pem"),
+ tls_test_file_path ("other-key.pem"),
+ &error);
+ g_assert_no_error (error);
+
+ g_tls_connection_set_certificate (G_TLS_CONNECTION (test->server_connection), cert);
+ g_object_unref (cert);
+ }
+}
+
static gboolean
on_incoming_connection (GSocketService *service,
GSocketConnection *connection,
@@ -270,9 +301,12 @@ on_incoming_connection (GSocketService *service,
g_assert_no_error (error);
g_object_unref (cert);
+
g_object_set (test->server_connection, "authentication-mode", test->auth_mode, NULL);
g_signal_connect (test->server_connection, "accept-certificate",
G_CALLBACK (on_accept_certificate), test);
+ g_signal_connect (test->server_connection, "notify::server-identity",
+ G_CALLBACK (on_received_server_identity), test);
if (test->database)
g_tls_connection_set_database (G_TLS_CONNECTION (test->server_connection), test->database);
@@ -768,6 +802,9 @@ test_failed_connection (TestConnection *test,
g_assert_no_error (error);
g_object_unref (connection);
+ /* Set this so that on_received_server_identity() won't assert */
+ test->do_sni = TRUE;
+
g_tls_connection_handshake_async (G_TLS_CONNECTION (test->client_connection),
G_PRIORITY_DEFAULT, NULL,
handshake_failed_cb, test);
@@ -1462,6 +1499,41 @@ test_fallback_subprocess (TestConnection *test,
g_assert_no_error (error);
}
+static void
+test_sni (TestConnection *test,
+ gconstpointer data)
+{
+ const char *identity = (const char *) data;
+ GIOStream *connection;
+ GError *error = NULL;
+
+ test->do_sni = TRUE;
+ connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE, TRUE);
+
+ test->identity = g_network_address_new (identity, 80);
+ test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_object_unref (connection);
+
+ test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
+ g_assert_no_error (error);
+ g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+ read_test_data_async (test);
+ g_main_loop_run (test->loop);
+
+ if (!strcmp (identity, "fail.example.com"))
+ {
+ g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+ g_assert_no_error (test->server_error);
+ }
+ else
+ {
+ g_assert_no_error (test->read_error);
+ g_assert_no_error (test->server_error);
+ }
+}
+
int
main (int argc,
char *argv[])
@@ -1544,6 +1616,13 @@ main (int argc,
TestConnection, NULL,
setup_connection, test_fallback_subprocess, teardown_connection);
+ g_test_add ("/tls/connection/sni/server", TestConnection, "server.example.com",
+ setup_connection, test_sni, teardown_connection);
+ g_test_add ("/tls/connection/sni/other", TestConnection, "other.example.com",
+ setup_connection, test_sni, teardown_connection);
+ g_test_add ("/tls/connection/sni/fail", TestConnection, "fail.example.com",
+ setup_connection, test_sni, teardown_connection);
+
ret = g_test_run();
/* for valgrinding */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]