/* 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;
}
{
}
-static GTlsDatabase*
+static GTlsDatabase *
g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
{
GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
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
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;
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);
}
}
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);
}