Imported Upstream version 2.72.alpha
[platform/upstream/glib-networking.git] / tls / gnutls / gtlsbackend-gnutls.c
index c955327..65a8db8 100644 (file)
@@ -72,7 +72,7 @@ gtls_gnutls_init (gpointer data)
 
   /* Leak the module to keep it from being unloaded. */
   plugin = g_type_get_plugin (G_TYPE_TLS_BACKEND_GNUTLS);
-  if (plugin != NULL)
+  if (plugin)
     g_type_plugin_use (plugin);
   return NULL;
 }
@@ -120,7 +120,7 @@ g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
 {
 }
 
-static GTlsDatabase*
+static GTlsDatabase *
 g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
 {
   GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
@@ -165,22 +165,25 @@ g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
   iface->get_dtls_server_connection_type = g_tls_server_connection_gnutls_get_type;
 }
 
-/* Session cache support; all the details are sort of arbitrary. Note
- * that having session_cache_cleanup() be a little bit slow isn't the
- * end of the world, since it will still be faster than the network
- * is. (NSS uses a linked list for its cache...)
+/* Session cache support. We try to be careful of TLS session tracking
+ * and so have adopted the recommendations of arXiv:1810.07304 section 6
+ * in using a 10-minute cache lifetime and in never updating the
+ * expiration time of cache entries when they are accessed to ensure a
+ * new session gets used after 10 minutes even if the cached one was
+ * resumed more recently.
+ *
+ * https://arxiv.org/abs/1810.07304
  */
 
 G_LOCK_DEFINE_STATIC (session_cache_lock);
-GHashTable *client_session_cache, *server_session_cache;
+GHashTable *client_session_cache; /* (owned) GBytes -> (owned) GTlsBackendGnutlsCacheData */
 
 #define SESSION_CACHE_MAX_SIZE 50
-#define SESSION_CACHE_MAX_AGE (60 * 60) /* one hour */
+#define SESSION_CACHE_MAX_AGE (10ll * 60ll * G_USEC_PER_SEC) /* ten minutes */
 
 typedef struct {
-  GBytes *session_id;
-  GBytes *session_data;
-  time_t  last_used;
+  GQueue *session_tickets; /* (owned) GBytes */
+  gint64  expiration_time;
 } GTlsBackendGnutlsCacheData;
 
 static void
@@ -189,96 +192,63 @@ 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, cache);
   while (g_hash_table_iter_next (&iter, &key, &value))
     {
       cache_data = value;
-      if (cache_data->last_used < expired)
+      if (g_get_monotonic_time () > cache_data->expiration_time)
         g_hash_table_iter_remove (&iter);
     }
 }
 
 static void
-cache_data_free (gpointer data)
+cache_data_free (GTlsBackendGnutlsCacheData *data)
 {
-  GTlsBackendGnutlsCacheData *cache_data = data;
-
-  g_bytes_unref (cache_data->session_id);
-  g_bytes_unref (cache_data->session_data);
-  g_free (cache_data);
+  g_queue_free_full (data->session_tickets, (GDestroyNotify)g_bytes_unref);
+  g_free (data);
 }
 
 static GHashTable *
-get_session_cache (unsigned int            type,
-                   gboolean                create)
+get_session_cache (gboolean create)
 {
-  GHashTable **cache_p;
-
-  cache_p = (type == GNUTLS_CLIENT) ? &client_session_cache : &server_session_cache;
-  if (!*cache_p && create)
+  if (!client_session_cache && create)
     {
-      *cache_p = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
-                                        NULL, cache_data_free);
+      client_session_cache = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
+                                                    (GDestroyNotify)g_bytes_unref, (GDestroyNotify)cache_data_free);
     }
-  return *cache_p;
+  return client_session_cache;
 }
 
 void
-g_tls_backend_gnutls_store_session (unsigned int             type,
-                                    GBytes                  *session_id,
-                                    GBytes                  *session_data)
+g_tls_backend_gnutls_store_session_data (GBytes *session_id,
+                                         GBytes *session_data)
 {
   GTlsBackendGnutlsCacheData *cache_data;
   GHashTable *cache;
 
   G_LOCK (session_cache_lock);
 
-  cache = get_session_cache (type, TRUE);
+  cache = get_session_cache (TRUE);
   cache_data = g_hash_table_lookup (cache, session_id);
-  if (cache_data)
-    {
-      if (!g_bytes_equal (cache_data->session_data, session_data))
-        {
-          g_bytes_unref (cache_data->session_data);
-          cache_data->session_data = g_bytes_ref (session_data);
-        }
-    }
-  else
+  if (!cache_data)
     {
       if (g_hash_table_size (cache) >= SESSION_CACHE_MAX_SIZE)
         session_cache_cleanup (cache);
 
       cache_data = g_new (GTlsBackendGnutlsCacheData, 1);
-      cache_data->session_id = g_bytes_ref (session_id);
-      cache_data->session_data = g_bytes_ref (session_data);
-
-      g_hash_table_insert (cache, cache_data->session_id, cache_data);
+      cache_data->session_tickets = g_queue_new ();
+      g_hash_table_insert (cache, g_bytes_ref (session_id), cache_data);
     }
-  cache_data->last_used = time (NULL);
 
-  G_UNLOCK (session_cache_lock);
-}
-
-void
-g_tls_backend_gnutls_remove_session (unsigned int             type,
-                                     GBytes                  *session_id)
-{
-  GHashTable *cache;
-
-  G_LOCK (session_cache_lock);
-
-  cache = get_session_cache (type, FALSE);
-  if (cache)
-    g_hash_table_remove (cache, session_id);
+  g_queue_push_tail (cache_data->session_tickets, g_bytes_ref (session_data));
+  cache_data->expiration_time = g_get_monotonic_time () + SESSION_CACHE_MAX_AGE;
 
   G_UNLOCK (session_cache_lock);
 }
 
 GBytes *
-g_tls_backend_gnutls_lookup_session (unsigned int             type,
-                                     GBytes                  *session_id)
+g_tls_backend_gnutls_lookup_session_data (GBytes *session_id)
 {
   GTlsBackendGnutlsCacheData *cache_data;
   GBytes *session_data = NULL;
@@ -286,14 +256,16 @@ g_tls_backend_gnutls_lookup_session (unsigned int             type,
 
   G_LOCK (session_cache_lock);
 
-  cache = get_session_cache (type, FALSE);
+  cache = get_session_cache (FALSE);
   if (cache)
     {
       cache_data = g_hash_table_lookup (cache, session_id);
       if (cache_data)
         {
-          cache_data->last_used = time (NULL);
-          session_data = g_bytes_ref (cache_data->session_data);
+          /* Note that session tickets should be used only once since TLS 1.3,
+           * so we remove from the queue after retrieval. See RFC 8446 §C.4.
+           */
+          session_data = g_queue_pop_head (cache_data->session_tickets);
         }
     }
 
@@ -306,10 +278,10 @@ void
 g_tls_backend_gnutls_register (GIOModule *module)
 {
   g_tls_backend_gnutls_register_type (G_TYPE_MODULE (module));
-  if (module == NULL)
+  if (!module)
     g_io_extension_point_register (G_TLS_BACKEND_EXTENSION_POINT_NAME);
   g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
-                                  g_tls_backend_gnutls_get_type(),
+                                  g_tls_backend_gnutls_get_type (),
                                   "gnutls",
                                   0);
 }