[glib-networking/mcatanzaro/session-resumption: 14/17] gnutls: support TLS 1.3 session tickets client-side
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/mcatanzaro/session-resumption: 14/17] gnutls: support TLS 1.3 session tickets client-side
- Date: Tue, 1 Oct 2019 23:05:53 +0000 (UTC)
commit 1dfb04f1caa587a3000feda8c5eab369ab7f8a14
Author: Michael Catanzaro <mcatanzaro gnome org>
Date: Wed Aug 14 14:16:44 2019 -0500
gnutls: support TLS 1.3 session tickets client-side
This removes support for session resumption with TLS 1.2, which is
sufficiently different that it's annoying to try to support them both.
The main difference is that in TLS 1.3 we can receive any number of
session tickets from the server, and we should use each one at most
once.
tls/gnutls/gtlsbackend-gnutls.c | 85 ++++++++---------------
tls/gnutls/gtlsbackend-gnutls.h | 10 +--
tls/gnutls/gtlsclientconnection-gnutls.c | 114 ++++++++++++++++---------------
tls/gnutls/gtlsconnection-gnutls.c | 6 +-
tls/gnutls/gtlsconnection-gnutls.h | 2 -
5 files changed, 91 insertions(+), 126 deletions(-)
---
diff --git a/tls/gnutls/gtlsbackend-gnutls.c b/tls/gnutls/gtlsbackend-gnutls.c
index d0e5eaa..bd6863f 100644
--- a/tls/gnutls/gtlsbackend-gnutls.c
+++ b/tls/gnutls/gtlsbackend-gnutls.c
@@ -172,15 +172,14 @@ g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
*/
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 */
typedef struct {
- GBytes *session_id;
- GBytes *session_data;
- time_t last_used;
+ GQueue *session_tickets; /* (owned) GBytes */
+ gint64 last_used;
} GTlsBackendGnutlsCacheData;
static void
@@ -189,7 +188,7 @@ session_cache_cleanup (GHashTable *cache)
GHashTableIter iter;
gpointer key, value;
GTlsBackendGnutlsCacheData *cache_data;
- time_t expired = time (NULL) - SESSION_CACHE_MAX_AGE;
+ gint64 expired = g_get_monotonic_time () - SESSION_CACHE_MAX_AGE;
g_hash_table_iter_init (&iter, cache);
while (g_hash_table_iter_next (&iter, &key, &value))
@@ -201,84 +200,51 @@ session_cache_cleanup (GHashTable *cache)
}
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->last_used = g_get_monotonic_time ();
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 +252,17 @@ 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);
+ cache_data->last_used = g_get_monotonic_time ();
}
}
diff --git a/tls/gnutls/gtlsbackend-gnutls.h b/tls/gnutls/gtlsbackend-gnutls.h
index 7b92bf3..fb33361 100644
--- a/tls/gnutls/gtlsbackend-gnutls.h
+++ b/tls/gnutls/gtlsbackend-gnutls.h
@@ -35,12 +35,8 @@ G_DECLARE_FINAL_TYPE (GTlsBackendGnutls, g_tls_backend_gnutls, G, TLS_BACKEND_GN
void g_tls_backend_gnutls_register (GIOModule *module);
-void g_tls_backend_gnutls_store_session (unsigned int type,
- GBytes *session_id,
- GBytes *session_data);
-void g_tls_backend_gnutls_remove_session (unsigned int type,
- GBytes *session_id);
-GBytes *g_tls_backend_gnutls_lookup_session (unsigned int type,
- GBytes *session_id);
+void g_tls_backend_gnutls_store_session_data (GBytes *session_id,
+ GBytes *session_data);
+GBytes *g_tls_backend_gnutls_lookup_session_data (GBytes *session_id);
G_END_DECLS
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 31104f1..8061889 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -52,10 +52,15 @@ struct _GTlsClientConnectionGnutls
GTlsCertificateFlags validation_flags;
GSocketConnectable *server_identity;
gboolean use_ssl3;
- gboolean session_data_override;
+ /* session_data is either the session ticket that was used to resume this
+ * connection, or the most recent session ticket received from the server.
+ * Because session ticket reuse is generally undesirable, it should only be
+ * accessed if session_data_override is set.
+ */
GBytes *session_id;
GBytes *session_data;
+ gboolean session_data_override;
GPtrArray *accepted_cas;
gboolean accepted_cas_changed;
@@ -65,7 +70,7 @@ struct _GTlsClientConnectionGnutls
gnutls_privkey_t pkey;
};
-static void g_tls_client_connection_gnutls_initable_interface_init (GInitableIface *iface);
+static void g_tls_client_connection_gnutls_initable_interface_init (GInitableIface *iface);
static void g_tls_client_connection_gnutls_client_connection_interface_init (GTlsClientConnectionInterface
*iface);
static void g_tls_client_connection_gnutls_dtls_client_connection_interface_init
(GDtlsClientConnectionInterface *iface);
@@ -137,12 +142,17 @@ g_tls_client_connection_gnutls_compute_session_id (GTlsClientConnectionGnutls *g
if (g_test_initialized ())
return;
- /* Create a TLS session ID. We base it on the IP address since
+ /* Create a TLS "session ID." We base it on the IP address since
* different hosts serving the same hostname/service will probably
* not share the same session cache. We base it on the
* server-identity because at least some servers will fail (rather
* than just failing to resume the session) if we don't.
* (https://bugs.launchpad.net/bugs/823325)
+ *
+ * Note that our session IDs have no relation to TLS protocol
+ * session IDs, e.g. as provided by gnutls_session_get_id2(). Unlike
+ * our session IDs, actual TLS session IDs can no longer be used for
+ * session resumption.
*/
g_object_get (G_OBJECT (gnutls), "base-io-stream", &base_conn, NULL);
if (G_IS_SOCKET_CONNECTION (base_conn))
@@ -191,6 +201,32 @@ g_tls_client_connection_gnutls_compute_session_id (GTlsClientConnectionGnutls *g
g_clear_object (&base_conn);
}
+static int
+handshake_thread_session_ticket_received_cb (gnutls_session_t session,
+ guint htype,
+ guint when,
+ guint incoming,
+ const gnutls_datum_t *msg)
+{
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (gnutls_session_get_ptr (session));
+ gnutls_datum_t session_datum;
+
+ if (gnutls_session_get_data2 (session, &session_datum) == GNUTLS_E_SUCCESS)
+ {
+ g_clear_pointer (&gnutls->session_data, g_bytes_unref);
+ gnutls->session_data = g_bytes_new_with_free_func (session_datum.data,
+ session_datum.size,
+ (GDestroyNotify)gnutls_free,
+ session_datum.data);
+
+ if (gnutls->session_id)
+ g_tls_backend_gnutls_store_session_data (gnutls->session_id,
+ gnutls->session_data);
+ }
+
+ return 0;
+}
+
static void
g_tls_client_connection_gnutls_finalize (GObject *object)
{
@@ -237,6 +273,9 @@ g_tls_client_connection_gnutls_initable_init (GInitable *initable,
g_free (normalized_hostname);
}
+ gnutls_handshake_set_hook_function (session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
+ GNUTLS_HOOK_POST, handshake_thread_session_ticket_received_cb);
+
return TRUE;
}
@@ -407,21 +446,6 @@ g_tls_client_connection_gnutls_handshake_thread_retrieve_function (gnutls_sessio
return 0;
}
-static void
-g_tls_client_connection_gnutls_clear_session_data (GTlsClientConnectionGnutls *gnutls)
-{
- gnutls->session_data_override = FALSE;
- g_clear_pointer (&gnutls->session_data, g_bytes_unref);
- if (gnutls->session_id)
- g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->session_id);
-}
-
-static void
-g_tls_client_connection_gnutls_failed (GTlsConnectionGnutls *gnutls)
-{
- g_tls_client_connection_gnutls_clear_session_data (G_TLS_CLIENT_CONNECTION_GNUTLS (gnutls));
-}
-
static void
g_tls_client_connection_gnutls_prepare_handshake (GTlsConnectionBase *tls,
gchar **advertised_protocols)
@@ -430,9 +454,9 @@ g_tls_client_connection_gnutls_prepare_handshake (GTlsConnectionBase *tls,
g_tls_client_connection_gnutls_compute_session_id (gnutls);
- /* Try to get a cached session */
if (gnutls->session_data_override)
{
+ g_assert (gnutls->session_data);
gnutls_session_set_data (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
g_bytes_get_data (gnutls->session_data, NULL),
g_bytes_get_size (gnutls->session_data));
@@ -441,14 +465,14 @@ g_tls_client_connection_gnutls_prepare_handshake (GTlsConnectionBase *tls,
{
GBytes *session_data;
- session_data = g_tls_backend_gnutls_lookup_session (GNUTLS_CLIENT, gnutls->session_id);
+ session_data = g_tls_backend_gnutls_lookup_session_data (gnutls->session_id);
if (session_data)
{
gnutls_session_set_data (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
g_bytes_get_data (session_data, NULL),
g_bytes_get_size (session_data));
g_clear_pointer (&gnutls->session_data, g_bytes_unref);
- gnutls->session_data = session_data;
+ gnutls->session_data = g_steal_pointer (&session_data);
}
}
@@ -462,7 +486,6 @@ g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase *tls,
GError **error)
{
GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
- int resumed;
G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->complete_handshake (tls,
negotiated_protocol, error);
@@ -471,28 +494,6 @@ g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase *tls,
*/
if (gnutls->accepted_cas_changed)
g_object_notify (G_OBJECT (gnutls), "accepted-cas");
-
- resumed = gnutls_session_is_resumed (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)));
- if (!resumed)
- {
- gnutls_datum_t session_datum;
-
- g_tls_client_connection_gnutls_clear_session_data (G_TLS_CLIENT_CONNECTION_GNUTLS (tls));
-
- if (gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
- &session_datum) == 0)
- {
- gnutls->session_data = g_bytes_new_with_free_func (session_datum.data,
- session_datum.size,
- (GDestroyNotify)gnutls_free,
- session_datum.data);
-
- if (gnutls->session_id)
- g_tls_backend_gnutls_store_session (GNUTLS_CLIENT,
- gnutls->session_id,
- gnutls->session_data);
- }
- }
}
static void
@@ -502,16 +503,24 @@ g_tls_client_connection_gnutls_copy_session_state (GTlsClientConnection *conn,
GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
GTlsClientConnectionGnutls *gnutls_source = G_TLS_CLIENT_CONNECTION_GNUTLS (source);
- if (gnutls_source->session_data)
+ /* Precondition: source has handshaked, conn has not. */
+ g_return_if_fail (!gnutls->session_id);
+ g_return_if_fail (gnutls_source->session_id);
+
+ /* Prefer to use a new session ticket, if possible. */
+ gnutls->session_data = g_tls_backend_gnutls_lookup_session_data (gnutls_source->session_id);
+
+ if (!gnutls->session_data && gnutls_source->session_data)
{
- gnutls->session_data_override = TRUE;
+ /* If it's not possible, we'll try to reuse the old ticket, even though
+ * this is a privacy risk since TLS 1.3. Applications should not use this
+ * function unless they need us to try as hard as possible to resume a
+ * session, even at the cost of privacy.
+ */
gnutls->session_data = g_bytes_ref (gnutls_source->session_data);
-
- if (gnutls->session_id)
- g_tls_backend_gnutls_store_session (GNUTLS_CLIENT,
- gnutls->session_id,
- gnutls->session_data);
}
+
+ gnutls->session_data_override = !!gnutls->session_data;
}
static void
@@ -519,7 +528,6 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
- GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
gobject_class->get_property = g_tls_client_connection_gnutls_get_property;
gobject_class->set_property = g_tls_client_connection_gnutls_set_property;
@@ -528,8 +536,6 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
base_class->prepare_handshake = g_tls_client_connection_gnutls_prepare_handshake;
base_class->complete_handshake = g_tls_client_connection_gnutls_complete_handshake;
- gnutls_class->failed = g_tls_client_connection_gnutls_failed;
-
g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
g_object_class_override_property (gobject_class, PROP_USE_SSL3, "use-ssl3");
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 8f16523..51e8293 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -478,11 +478,7 @@ end_gnutls_io (GTlsConnectionGnutls *gnutls,
#define END_GNUTLS_IO(gnutls, direction, ret, status, errmsg, err) \
status = end_gnutls_io (gnutls, direction, ret, err, errmsg); \
- } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN); \
- \
- if (status == G_TLS_CONNECTION_BASE_ERROR && \
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)-> failed) \
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
+ } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
static void
set_gnutls_error (GTlsConnectionGnutls *gnutls,
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index db3b726..55ae5ee 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -39,8 +39,6 @@ G_DECLARE_DERIVABLE_TYPE (GTlsConnectionGnutls, g_tls_connection_gnutls, G, TLS_
struct _GTlsConnectionGnutlsClass
{
GTlsConnectionBaseClass parent_class;
-
- void (*failed) (GTlsConnectionGnutls *tls);
};
gnutls_certificate_credentials_t g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]