[glib-networking/wip/danw/sni: 3/3] gnutls: Add SNI support



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]