[glib-networking] gnutls: add server-side session cache support



commit 16c9fe1bd6030016730f83c50d747b6ff6fffda9
Author: Dan Winship <danw gnome org>
Date:   Sat Dec 3 19:32:01 2011 +0100

    gnutls: add server-side session cache support
    
    https://bugzilla.gnome.org/show_bug.cgi?id=636574

 configure.ac                             |    2 +-
 tls/gnutls/gtlsbackend-gnutls.c          |   99 +++++++++++++++++------------
 tls/gnutls/gtlsbackend-gnutls.h          |   12 ++--
 tls/gnutls/gtlsclientconnection-gnutls.c |   49 +++++++++------
 tls/gnutls/gtlsconnection-gnutls.c       |    3 +
 tls/gnutls/gtlsconnection-gnutls.h       |    2 +
 tls/gnutls/gtlsserverconnection-gnutls.c |   80 ++++++++++++++++++++++++
 7 files changed, 182 insertions(+), 65 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a408a54..0964b60 100644
--- a/configure.ac
+++ b/configure.ac
@@ -226,7 +226,7 @@ if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
 		-Wall -Wstrict-prototypes -Werror=missing-prototypes \
 		-Werror=implicit-function-declaration \
 		-Werror=pointer-arith -Werror=init-self -Werror=format=2 \
-		-Werror=missing-include-dirs -Werror=aggregate-return \
+		-Werror=missing-include-dirs \
 		-Werror=declaration-after-statement"
 fi
 
diff --git a/tls/gnutls/gtlsbackend-gnutls.c b/tls/gnutls/gtlsbackend-gnutls.c
index 6db3fec..3597753 100644
--- a/tls/gnutls/gtlsbackend-gnutls.c
+++ b/tls/gnutls/gtlsbackend-gnutls.c
@@ -166,26 +166,26 @@ g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
  */
 
 G_LOCK_DEFINE_STATIC (session_cache_lock);
-GHashTable *session_cache;
+GHashTable *client_session_cache, *server_session_cache;
 
 #define SESSION_CACHE_MAX_SIZE 50
 #define SESSION_CACHE_MAX_AGE (60 * 60) /* one hour */
 
 typedef struct {
-  gchar      *session_id;
-  GByteArray *session_data;
-  time_t      last_used;
+  GBytes *session_id;
+  GBytes *session_data;
+  time_t  last_used;
 } GTlsBackendGnutlsCacheData;
 
 static void
-session_cache_cleanup (void)
+session_cache_cleanup (GHashTable *cache)
 {
   GHashTableIter iter;
   gpointer key, value;
   GTlsBackendGnutlsCacheData *cache_data;
   time_t expired = time (NULL) - SESSION_CACHE_MAX_AGE;
 
-  g_hash_table_iter_init (&iter, session_cache);
+  g_hash_table_iter_init (&iter, cache);
   while (g_hash_table_iter_next (&iter, &key, &value))
     {
       cache_data = value;
@@ -199,81 +199,98 @@ cache_data_free (gpointer data)
 {
   GTlsBackendGnutlsCacheData *cache_data = data;
 
-  g_free (cache_data->session_id);
-  g_byte_array_unref (cache_data->session_data);
+  g_bytes_unref (cache_data->session_id);
+  g_bytes_unref (cache_data->session_data);
   g_slice_free (GTlsBackendGnutlsCacheData, cache_data);
 }
 
+static GHashTable *
+get_session_cache (gnutls_connection_end_t type,
+		   gboolean                create)
+{
+  GHashTable **cache_p;
+
+  cache_p = (type == GNUTLS_CLIENT) ? &client_session_cache : &server_session_cache;
+  if (!*cache_p && create)
+    {
+      *cache_p = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
+					NULL, cache_data_free);
+    }
+  return *cache_p;
+}
+
 void
-g_tls_backend_gnutls_cache_session_data (const gchar *session_id,
-					 guchar      *session_data,
-					 gsize        session_data_length)
+g_tls_backend_gnutls_store_session (gnutls_connection_end_t  type,
+				    GBytes                  *session_id,
+				    GBytes                  *session_data)
 {
   GTlsBackendGnutlsCacheData *cache_data;
+  GHashTable *cache;
 
   G_LOCK (session_cache_lock);
 
-  if (!session_cache)
-    session_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
-					   NULL, cache_data_free);
-
-  cache_data = g_hash_table_lookup (session_cache, session_id);
+  cache = get_session_cache (type, TRUE);
+  cache_data = g_hash_table_lookup (cache, session_id);
   if (cache_data)
     {
-      if (cache_data->session_data->len == session_data_length &&
-	  memcmp (cache_data->session_data->data,
-		  session_data, session_data_length) == 0)
+      if (!g_bytes_equal (cache_data->session_data, session_data))
 	{
-	  cache_data->last_used = time (NULL);
-	  G_UNLOCK (session_cache_lock);
-	  return;
+	  g_bytes_unref (cache_data->session_data);
+	  cache_data->session_data = g_bytes_ref (session_data);
 	}
-
-      g_byte_array_set_size (cache_data->session_data, 0);
     }
   else
     {
-      if (g_hash_table_size (session_cache) >= SESSION_CACHE_MAX_SIZE)
-	session_cache_cleanup ();
+      if (g_hash_table_size (cache) >= SESSION_CACHE_MAX_SIZE)
+	session_cache_cleanup (cache);
 
       cache_data = g_slice_new (GTlsBackendGnutlsCacheData);
-      cache_data->session_id = g_strdup (session_id);
-      cache_data->session_data = g_byte_array_sized_new (session_data_length);
+      cache_data->session_id = g_bytes_ref (session_id);
+      cache_data->session_data = g_bytes_ref (session_data);
 
-      g_hash_table_insert (session_cache, cache_data->session_id, cache_data);
+      g_hash_table_insert (cache, cache_data->session_id, cache_data);
     }
-
-  g_byte_array_append (cache_data->session_data,
-		       session_data, session_data_length);
   cache_data->last_used = time (NULL);
+
   G_UNLOCK (session_cache_lock);
 }
 
 void
-g_tls_backend_gnutls_uncache_session_data (const gchar *session_id)
+g_tls_backend_gnutls_remove_session (gnutls_connection_end_t  type,
+				     GBytes                  *session_id)
 {
+  GHashTable *cache;
+
   G_LOCK (session_cache_lock);
-  if (session_cache)
-    g_hash_table_remove (session_cache, session_id);
+
+  cache = get_session_cache (type, FALSE);
+  if (cache)
+    g_hash_table_remove (cache, session_id);
+
   G_UNLOCK (session_cache_lock);
 }
 
-GByteArray *
-g_tls_backend_gnutls_lookup_session_data (const gchar *session_id)
+GBytes *
+g_tls_backend_gnutls_lookup_session (gnutls_connection_end_t  type,
+				     GBytes                  *session_id)
 {
   GTlsBackendGnutlsCacheData *cache_data;
-  GByteArray *session_data = NULL;
+  GBytes *session_data = NULL;
+  GHashTable *cache;
 
   G_LOCK (session_cache_lock);
-  if (session_cache)
+
+  cache = get_session_cache (type, FALSE);
+  if (cache)
     {
-      cache_data = g_hash_table_lookup (session_cache, session_id);
+      cache_data = g_hash_table_lookup (cache, session_id);
       if (cache_data)
 	{
 	  cache_data->last_used = time (NULL);
-	  session_data = g_byte_array_ref (cache_data->session_data);
+	  session_data = g_bytes_ref (cache_data->session_data);
 	}
     }
+
   G_UNLOCK (session_cache_lock);
 
   return session_data;
diff --git a/tls/gnutls/gtlsbackend-gnutls.h b/tls/gnutls/gtlsbackend-gnutls.h
index 3a75dfe..ceb686a 100644
--- a/tls/gnutls/gtlsbackend-gnutls.h
+++ b/tls/gnutls/gtlsbackend-gnutls.h
@@ -46,11 +46,13 @@ struct _GTlsBackendGnutls
 GType g_tls_backend_gnutls_get_type (void) G_GNUC_CONST;
 void  g_tls_backend_gnutls_register (GIOModule *module);
 
-void         g_tls_backend_gnutls_cache_session_data        (const gchar *session_id,
-							     guchar      *session_data,
-							     gsize        session_data_length);
-void         g_tls_backend_gnutls_uncache_session_data      (const gchar *session_id);
-GByteArray  *g_tls_backend_gnutls_lookup_session_data       (const gchar *session_id);
+void    g_tls_backend_gnutls_store_session  (gnutls_connection_end_t  type,
+					     GBytes                  *session_id,
+					     GBytes                  *session_data);
+void    g_tls_backend_gnutls_remove_session (gnutls_connection_end_t  type,
+					     GBytes                  *session_id);
+GBytes *g_tls_backend_gnutls_lookup_session (gnutls_connection_end_t  type,
+					     GBytes                  *session_id);
 
 G_END_DECLS
 
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index da435bf..2292d05 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -58,7 +58,7 @@ struct _GTlsClientConnectionGnutlsPrivate
   GSocketConnectable *server_identity;
   gboolean use_ssl3;
 
-  char *session_id;
+  GBytes *session_id;
 
   gboolean cert_requested;
   GPtrArray *accepted_cas;
@@ -111,17 +111,17 @@ g_tls_client_connection_gnutls_constructed (GObject *object)
 	{
 	  GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
 	  const gchar *server_hostname;
-	  gchar *addrstr;
+	  gchar *addrstr, *session_id;
 
 	  iaddr = g_inet_socket_address_get_address (isaddr);
 	  port = g_inet_socket_address_get_port (isaddr);
 
 	  addrstr = g_inet_address_to_string (iaddr);
 	  server_hostname = get_server_identity (gnutls);
-	  gnutls->priv->session_id =
-	    g_strdup_printf ("%s/%s/%d", addrstr,
-			     server_hostname ? server_hostname : "",
-			     port);
+	  session_id = g_strdup_printf ("%s/%s/%d", addrstr,
+					server_hostname ? server_hostname : "",
+					port);
+	  gnutls->priv->session_id = g_bytes_new_take (session_id, strlen (session_id));
 	  g_free (addrstr);
 	}
       g_object_unref (remote_addr);
@@ -142,7 +142,7 @@ g_tls_client_connection_gnutls_finalize (GObject *object)
   if (gnutls->priv->accepted_cas)
     g_ptr_array_unref (gnutls->priv->accepted_cas);
   if (gnutls->priv->session_id)
-    g_free (gnutls->priv->session_id);
+    g_bytes_unref (gnutls->priv->session_id);
 
   G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->finalize (object);
 }
@@ -262,6 +262,15 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t             s
 }
 
 static void
+g_tls_client_connection_gnutls_failed (GTlsConnectionGnutls *conn)
+{
+  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+
+  if (gnutls->priv->session_id)
+    g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->priv->session_id);
+}
+
+static void
 g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
 {
   GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
@@ -269,14 +278,15 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
   /* Try to get a cached session */
   if (gnutls->priv->session_id)
     {
-      GByteArray *session_data;
+      GBytes *session_data;
 
-      session_data = g_tls_backend_gnutls_lookup_session_data (gnutls->priv->session_id);
+      session_data = g_tls_backend_gnutls_lookup_session (GNUTLS_CLIENT, gnutls->priv->session_id);
       if (session_data)
 	{
 	  gnutls_session_set_data (g_tls_connection_gnutls_get_session (conn),
-				   session_data->data, session_data->len);
-	  g_byte_array_unref (session_data);
+				   g_bytes_get_data (session_data),
+				   g_bytes_get_size (session_data));
+	  g_bytes_unref (session_data);
 	}
     }
 
@@ -341,19 +351,21 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
 
   if (gnutls->priv->session_id)
     {
-      gnutls_datum session_data;
+      gnutls_datum session_datum;
 
       if (success &&
 	  gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (conn),
-				    &session_data) == 0)
+				    &session_datum) == 0)
 	{
-	  g_tls_backend_gnutls_cache_session_data (gnutls->priv->session_id,
-						   session_data.data,
-						   session_data.size);
-	  gnutls_free (session_data.data);
+	  GBytes *session_data = g_bytes_new_with_free_func (session_datum.data, session_datum.size,
+							     (GDestroyNotify)gnutls_free, session_datum.data);
+
+	  g_tls_backend_gnutls_store_session (GNUTLS_CLIENT, gnutls->priv->session_id,
+					      session_data);
+	  g_bytes_unref (session_data);
 	}
       else
-	g_tls_backend_gnutls_uncache_session_data (gnutls->priv->session_id);
+	g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->priv->session_id);
     }
 }
 
@@ -370,6 +382,7 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
   gobject_class->constructed  = g_tls_client_connection_gnutls_constructed;
   gobject_class->finalize     = g_tls_client_connection_gnutls_finalize;
 
+  connection_gnutls_class->failed           = g_tls_client_connection_gnutls_failed;
   connection_gnutls_class->begin_handshake  = g_tls_client_connection_gnutls_begin_handshake;
   connection_gnutls_class->verify_peer      = g_tls_client_connection_gnutls_verify_peer;
   connection_gnutls_class->finish_handshake = g_tls_client_connection_gnutls_finish_handshake;
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 36e0df2..f1655d6 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -487,6 +487,8 @@ end_gnutls_io (GTlsConnectionGnutls  *gnutls,
     {
       if (g_error_matches (gnutls->priv->error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
 	status = GNUTLS_E_AGAIN;
+      else
+	G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
       g_propagate_error (error, gnutls->priv->error);
       gnutls->priv->error = NULL;
       return status;
@@ -515,6 +517,7 @@ end_gnutls_io (GTlsConnectionGnutls  *gnutls,
 	{
 	  g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_EOF,
 			       _("TLS connection closed unexpectedly"));
+	  G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
 	  return status;
 	}
       else
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index fa4f133..b69eca1 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -33,6 +33,8 @@ struct _GTlsConnectionGnutlsClass
 {
   GTlsConnectionClass parent_class;
 
+  void     (*failed)           (GTlsConnectionGnutls  *gnutls);
+
   void     (*begin_handshake)  (GTlsConnectionGnutls  *gnutls);
   gboolean (*verify_peer)      (GTlsConnectionGnutls  *gnutls,
 				GTlsCertificate       *peer_certificate,
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index 24d9ac2..807895b 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -25,6 +25,7 @@
 #include <gnutls/x509.h>
 
 #include "gtlsserverconnection-gnutls.h"
+#include "gtlsbackend-gnutls.h"
 #include "gtlscertificate-gnutls.h"
 #include <glib/gi18n-lib.h>
 
@@ -45,6 +46,14 @@ 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_db_store    (void            *user_data,
+								  gnutls_datum_t   key,
+								  gnutls_datum_t   data);
+static int            g_tls_server_connection_gnutls_db_remove   (void            *user_data,
+								  gnutls_datum_t   key);
+static gnutls_datum_t g_tls_server_connection_gnutls_db_retrieve (void            *user_data,
+								  gnutls_datum_t   key);
+
 static GInitableIface *g_tls_server_connection_gnutls_parent_initable_iface;
 
 G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionGnutls, g_tls_server_connection_gnutls, G_TYPE_TLS_CONNECTION_GNUTLS,
@@ -63,11 +72,17 @@ static void
 g_tls_server_connection_gnutls_init (GTlsServerConnectionGnutls *gnutls)
 {
   gnutls_certificate_credentials_t creds;
+  gnutls_session_t session;
 
   gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutlsPrivate);
 
   creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
   gnutls_certificate_set_retrieve_function (creds, g_tls_server_connection_gnutls_retrieve_function);
+
+  session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (gnutls));
+  gnutls_db_set_retrieve_function (session, g_tls_server_connection_gnutls_db_retrieve);
+  gnutls_db_set_store_function (session, g_tls_server_connection_gnutls_db_store);
+  gnutls_db_set_remove_function (session, g_tls_server_connection_gnutls_db_remove);
 }
 
 static gboolean
@@ -143,6 +158,12 @@ g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t             s
 }
 
 static void
+g_tls_server_connection_gnutls_failed (GTlsConnectionGnutls *conn)
+{
+  gnutls_db_remove_session (g_tls_connection_gnutls_get_session (conn));
+}
+
+static void
 g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
 {
   GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (conn);
@@ -206,6 +227,64 @@ g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *gnutls,
 {
 }
 
+/* Session cache management */
+
+static int
+g_tls_server_connection_gnutls_db_store (void            *user_data,
+					 gnutls_datum_t   key,
+					 gnutls_datum_t   data)
+{
+  GBytes *session_id, *session_data;
+
+  session_id = g_bytes_new (key.data, key.size);
+  session_data = g_bytes_new (data.data, data.size);
+  g_tls_backend_gnutls_store_session (GNUTLS_SERVER, session_id, session_data);
+  g_bytes_unref (session_id);
+  g_bytes_unref (session_data);
+
+  return 0;
+}
+
+static int
+g_tls_server_connection_gnutls_db_remove (void            *user_data,
+					  gnutls_datum_t   key)
+{
+  GBytes *session_id;
+
+  session_id = g_bytes_new (key.data, key.size);
+  g_tls_backend_gnutls_remove_session (GNUTLS_SERVER, session_id);
+  g_bytes_unref (session_id);
+
+  return 0;
+}
+
+static gnutls_datum_t
+g_tls_server_connection_gnutls_db_retrieve (void            *user_data,
+					    gnutls_datum_t   key)
+{
+  GBytes *session_id, *session_data;
+  gnutls_datum_t data;
+
+  session_id = g_bytes_new (key.data, key.size);
+  session_data = g_tls_backend_gnutls_lookup_session (GNUTLS_SERVER, session_id);
+  g_bytes_unref (session_id);
+
+  if (session_data)
+    {
+      data.size = g_bytes_get_size (session_data);
+      data.data = gnutls_malloc (data.size);
+      memcpy (data.data, g_bytes_get_data (session_data), data.size);
+      g_bytes_unref (session_data);
+    }
+  else
+    {
+      data.size = 0;
+      data.data = NULL;
+    }
+
+  return data;
+}
+
 static void
 g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klass)
 {
@@ -217,6 +296,7 @@ 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;
 
+  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->verify_peer      = g_tls_server_connection_gnutls_verify_peer;
   connection_gnutls_class->finish_handshake = g_tls_server_connection_gnutls_finish_handshake;



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