diff options
author | Goncalo Gomes <goncalo.gomes@youview.com> | 2022-08-09 10:44:44 +0100 |
---|---|---|
committer | Marge Bot <marge-bot@gnome.org> | 2022-09-12 15:46:51 +0000 |
commit | 4be360a85761e4b83aa851bc7413698b3155ffb3 (patch) | |
tree | 2824b8e49d5c85c43afba39392b0dcb37465d59b | |
parent | 25cfeb4c1819c15f9c4476fdaffc8ba360ba4dfe (diff) | |
download | glib-networking-4be360a85761e4b83aa851bc7413698b3155ffb3.tar.gz |
Use available cached tickets when creating connections
Client certificates could be very slow to process in certain platforms.
Hence the client should reuse sessions, if it can, in order to skip any
operations that would otherwise slowdown the connection.
We use the same assumptions as the GnuTLS implementation for consistency:
- Cache maximum size is 50
- Session validity should be maximum 10 minutes as per
https://arxiv.org/abs/1810.07304 (section 6)
- TLSv1.3 tickets should only be used once as per RFC 8446 §C.4 to avoid
client tracking (https://www.rfc-editor.org/rfc/rfc8446.html#appendix-C.4)
Glib-networking will use the last session present in the cache for each
connection using the same session id, derived from IP/hostname/port/certificate.
The server is responsible to provide us with session tickets that we can use.
This commit also disables SSL_OP_NO_TICKET.
Fixes #147
Signed-off-by: Goncalo Gomes <goncalo.gomes@youview.com>
Part-of: <https://gitlab.gnome.org/GNOME/glib-networking/-/merge_requests/221>
-rw-r--r-- | tls/base/gtlsconnection-base.c | 19 | ||||
-rw-r--r-- | tls/gnutls/gtlsclientconnection-gnutls.c | 11 | ||||
-rw-r--r-- | tls/openssl/gtlsbackend-openssl.c | 168 | ||||
-rw-r--r-- | tls/openssl/gtlsbackend-openssl.h | 5 | ||||
-rw-r--r-- | tls/openssl/gtlsclientconnection-openssl.c | 107 | ||||
-rw-r--r-- | tls/openssl/gtlsserverconnection-openssl.c | 3 | ||||
-rw-r--r-- | tls/tests/connection.c | 253 | ||||
-rw-r--r-- | tls/tests/meson.build | 8 |
8 files changed, 564 insertions, 10 deletions
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c index bcbdf49..3b5c6ad 100644 --- a/tls/base/gtlsconnection-base.c +++ b/tls/base/gtlsconnection-base.c @@ -216,7 +216,8 @@ enum PROP_ADVERTISED_PROTOCOLS, PROP_NEGOTIATED_PROTOCOL, PROP_PROTOCOL_VERSION, - PROP_CIPHERSUITE_NAME + PROP_CIPHERSUITE_NAME, + PROP_SESSION_REUSED }; gboolean @@ -366,6 +367,10 @@ g_tls_connection_base_get_property (GObject *object, g_value_set_string (value, priv->ciphersuite_name); break; + case PROP_SESSION_REUSED: + g_value_set_boolean (value, FALSE); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -469,6 +474,10 @@ g_tls_connection_base_set_property (GObject *object, priv->advertised_protocols = g_value_dup_boxed (value); break; + case PROP_SESSION_REUSED: + g_assert_not_reached (); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -2795,6 +2804,14 @@ g_tls_connection_base_class_init (GTlsConnectionBaseClass *klass) klass->push_io = g_tls_connection_base_real_push_io; klass->pop_io = g_tls_connection_base_real_pop_io; + g_object_class_install_property (gobject_class, PROP_SESSION_REUSED, + g_param_spec_boolean ("session-reused", + _("Session Reused"), + _("Indicates whether a session has been reused"), + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* For GTlsConnection and GDtlsConnection: */ g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream"); g_object_class_override_property (gobject_class, PROP_BASE_SOCKET, "base-socket"); diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c index 9045270..39b06d7 100644 --- a/tls/gnutls/gtlsclientconnection-gnutls.c +++ b/tls/gnutls/gtlsclientconnection-gnutls.c @@ -42,7 +42,8 @@ enum PROP_VALIDATION_FLAGS, PROP_SERVER_IDENTITY, PROP_USE_SSL3, - PROP_ACCEPTED_CAS + PROP_ACCEPTED_CAS, + PROP_SESSION_REUSED }; struct _GTlsClientConnectionGnutls @@ -52,6 +53,7 @@ struct _GTlsClientConnectionGnutls GTlsCertificateFlags validation_flags; GSocketConnectable *server_identity; gboolean use_ssl3; + gboolean session_reused; /* session_data is either the session ticket that was used to resume this * connection, or the most recent session ticket received from the server. @@ -321,6 +323,10 @@ g_tls_client_connection_gnutls_get_property (GObject *object, g_value_set_pointer (value, accepted_cas); break; + case PROP_SESSION_REUSED: + g_value_set_boolean (value, gnutls->session_reused); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -485,6 +491,7 @@ g_tls_client_connection_gnutls_prepare_handshake (GTlsConnectionBase *tls, g_bytes_get_size (session_data)); g_clear_pointer (&gnutls->session_data, g_bytes_unref); gnutls->session_data = g_steal_pointer (&session_data); + gnutls->session_reused = TRUE; } } @@ -571,6 +578,7 @@ g_tls_client_connection_gnutls_copy_session_state (GTlsClientConnection *conn, } gnutls->session_data_override = !!gnutls->session_data; + gnutls->session_reused = gnutls->session_data_override; } static void @@ -600,6 +608,7 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity"); g_object_class_override_property (gobject_class, PROP_USE_SSL3, "use-ssl3"); g_object_class_override_property (gobject_class, PROP_ACCEPTED_CAS, "accepted-cas"); + g_object_class_override_property (gobject_class, PROP_SESSION_REUSED, "session-reused"); } static void diff --git a/tls/openssl/gtlsbackend-openssl.c b/tls/openssl/gtlsbackend-openssl.c index 23cd8de..aa08bd9 100644 --- a/tls/openssl/gtlsbackend-openssl.c +++ b/tls/openssl/gtlsbackend-openssl.c @@ -243,6 +243,174 @@ g_tls_backend_openssl_interface_init (GTlsBackendInterface *iface) iface->get_dtls_server_connection_type = g_tls_server_connection_openssl_get_type; } +/* 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); +static GHashTable *client_session_cache; /* (owned) GString -> (owned) GTlsBackendOpensslCacheData */ + +#define SESSION_CACHE_MAX_SIZE 50 +#define SESSION_CACHE_MAX_AGE (10ll * 60ll * G_USEC_PER_SEC) /* ten minutes */ + +typedef struct { + SSL_SESSION *session_ticket; + gint64 expiration_time; +} GTlsBackendOpensslCacheData; + +static void +session_cache_cleanup (GHashTable *cache) +{ + gint64 time; + GHashTableIter iter; + gpointer key, value; + GTlsBackendOpensslCacheData *cache_data; + GString *session_id = NULL; + gint64 expiration_time = 0; + gboolean removed = FALSE; + + time = g_get_monotonic_time (); + + g_hash_table_iter_init (&iter, cache); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + cache_data = value; + if (cache_data->expiration_time > expiration_time) + { + expiration_time = cache_data->expiration_time; + session_id = key; + } + + if (time > cache_data->expiration_time) + { + removed = TRUE; + g_hash_table_iter_remove (&iter); + } + } + + if (!removed && session_id) + g_hash_table_remove (cache, session_id); +} + +static void +cache_data_free (GTlsBackendOpensslCacheData *data) +{ + SSL_SESSION_free (data->session_ticket); + g_free (data); +} + +static void +string_free (GString *data) +{ + g_string_free (data, TRUE); +} + +static GHashTable * +get_session_cache (gboolean create) +{ + if (!client_session_cache && create) + { + client_session_cache = g_hash_table_new_full ((GHashFunc)g_string_hash, + (GEqualFunc)g_string_equal, + (GDestroyNotify)string_free, + (GDestroyNotify)cache_data_free); + } + return client_session_cache; +} + +void +g_tls_backend_openssl_store_session_data (GString *session_id, + SSL_SESSION *session_data) +{ + GTlsBackendOpensslCacheData *cache_data; + GHashTable *cache; + + if (!session_id || !session_data) + return; + + G_LOCK (session_cache_lock); + + cache = get_session_cache (TRUE); + cache_data = g_hash_table_lookup (cache, session_id); + if (!cache_data) + { + if (g_hash_table_size (cache) >= SESSION_CACHE_MAX_SIZE) + session_cache_cleanup (cache); + + if (g_hash_table_size (cache) < SESSION_CACHE_MAX_SIZE) + { + cache_data = g_new (GTlsBackendOpensslCacheData, 1); + cache_data->session_ticket = NULL; + g_hash_table_insert (cache, g_string_new (session_id->str), cache_data); + } + } + + if (cache_data) + { + if (SSL_SESSION_up_ref (session_data)) + { + SSL_SESSION_free (cache_data->session_ticket); + cache_data->session_ticket = session_data; + cache_data->expiration_time = g_get_monotonic_time () + SESSION_CACHE_MAX_AGE; + } + else + g_warning ("Failed to acquire TLS session, will not be resumeable"); + } + + G_UNLOCK (session_cache_lock); +} + +SSL_SESSION * +g_tls_backend_openssl_lookup_session_data (GString *session_id) +{ + GTlsBackendOpensslCacheData *cache_data; + SSL_SESSION *session_data = NULL; + GHashTable *cache; + + if (!session_id) + return NULL; + + G_LOCK (session_cache_lock); + + cache = get_session_cache (FALSE); + if (cache) + { + cache_data = g_hash_table_lookup (cache, session_id); + if (cache_data) + { + if (g_get_monotonic_time () > cache_data->expiration_time) + { + g_hash_table_remove (cache, session_id); + G_UNLOCK (session_cache_lock); + return NULL; + } + + session_data = cache_data->session_ticket; + if (!SSL_SESSION_up_ref (session_data)) + { + g_debug ("Failed to acquire cached TLS session, will not try to resume session"); + session_data = NULL; + } + + /* 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. + */ + if (SSL_SESSION_get_protocol_version (cache_data->session_ticket) == TLS1_3_VERSION) + g_hash_table_remove (cache, session_id); + } + } + + G_UNLOCK (session_cache_lock); + + return session_data; +} + void g_tls_backend_openssl_register (GIOModule *module) { diff --git a/tls/openssl/gtlsbackend-openssl.h b/tls/openssl/gtlsbackend-openssl.h index 9e53806..fa18df3 100644 --- a/tls/openssl/gtlsbackend-openssl.h +++ b/tls/openssl/gtlsbackend-openssl.h @@ -26,6 +26,7 @@ #pragma once #include <gio/gio.h> +#include "openssl-include.h" G_BEGIN_DECLS @@ -35,4 +36,8 @@ G_DECLARE_FINAL_TYPE (GTlsBackendOpenssl, g_tls_backend_openssl, G, TLS_BACKEND_ void g_tls_backend_openssl_register (GIOModule *module); +void g_tls_backend_openssl_store_session_data (GString *session_id, + SSL_SESSION *session_data); +SSL_SESSION *g_tls_backend_openssl_lookup_session_data (GString *session_id); + G_END_DECLS diff --git a/tls/openssl/gtlsclientconnection-openssl.c b/tls/openssl/gtlsclientconnection-openssl.c index 263596b..6a5a288 100644 --- a/tls/openssl/gtlsclientconnection-openssl.c +++ b/tls/openssl/gtlsclientconnection-openssl.c @@ -29,8 +29,8 @@ #include <errno.h> #include <string.h> -#include "openssl-include.h" #include "gtlsconnection-base.h" +#include "gtlsbackend-openssl.h" #include "gtlsclientconnection-openssl.h" #include "gtlsbackend-openssl.h" #include "gtlscertificate-openssl.h" @@ -44,6 +44,8 @@ struct _GTlsClientConnectionOpenssl GTlsCertificateFlags validation_flags; GSocketConnectable *server_identity; gboolean use_ssl3; + gboolean session_reused; + GString *session_id; STACK_OF (X509_NAME) *ca_list; @@ -58,7 +60,8 @@ enum PROP_VALIDATION_FLAGS, PROP_SERVER_IDENTITY, PROP_USE_SSL3, - PROP_ACCEPTED_CAS + PROP_ACCEPTED_CAS, + PROP_SESSION_REUSED }; static void g_tls_client_connection_openssl_initable_interface_init (GInitableIface *iface); @@ -86,6 +89,8 @@ g_tls_client_connection_openssl_finalize (GObject *object) SSL_CTX_free (openssl->ssl_ctx); SSL_SESSION_free (openssl->session); + g_string_free (openssl->session_id, TRUE); + G_OBJECT_CLASS (g_tls_client_connection_openssl_parent_class)->finalize (object); } @@ -101,6 +106,75 @@ get_server_identity (GTlsClientConnectionOpenssl *openssl) } static void +g_tls_client_connection_openssl_constructed (GObject *object) +{ + GTlsClientConnectionOpenssl *openssl = G_TLS_CLIENT_CONNECTION_OPENSSL (object); + GSocketConnection *base_conn; + + /* 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. + */ + g_object_get (G_OBJECT (openssl), "base-io-stream", &base_conn, NULL); + if (G_IS_SOCKET_CONNECTION (base_conn)) + { + GSocketAddress *remote_addr; + remote_addr = g_socket_connection_get_remote_address (base_conn, NULL); + if (G_IS_INET_SOCKET_ADDRESS (remote_addr)) + { + guint port; + GInetAddress *iaddr; + GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr); + const gchar *server_hostname; + gchar *addrstr = NULL, *cert_hash = NULL; + GTlsCertificate *cert = NULL; + + 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 (openssl); + + /* If we have a certificate, make its hash part of the session ID, so + * that different connections to the same server can use different + * certificates. + */ + g_object_get (G_OBJECT (openssl), "certificate", &cert, NULL); + if (cert) + { + GByteArray *der = NULL; + g_object_get (G_OBJECT (cert), "certificate", &der, NULL); + if (der) + { + cert_hash = g_compute_checksum_for_data (G_CHECKSUM_SHA256, der->data, der->len); + g_byte_array_unref (der); + } + g_object_unref (cert); + } + + openssl->session_id = g_string_new (NULL); + g_string_printf (openssl->session_id, "%s/%s/%d/%s", addrstr, + server_hostname ? server_hostname : "", + port, + cert_hash ? cert_hash : ""); + g_free (addrstr); + g_free (cert_hash); + } + g_object_unref (remote_addr); + } + g_object_unref (base_conn); + + if (G_OBJECT_CLASS (g_tls_client_connection_openssl_parent_class)->constructed) + G_OBJECT_CLASS (g_tls_client_connection_openssl_parent_class)->constructed (object); +} + +static void g_tls_client_connection_openssl_get_property (GObject *object, guint prop_id, GValue *value, @@ -151,6 +225,10 @@ g_tls_client_connection_openssl_get_property (GObject *object, g_value_set_pointer (value, accepted_cas); break; + case PROP_SESSION_REUSED: + g_value_set_boolean (value, openssl->session_reused); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -272,6 +350,7 @@ g_tls_client_connection_openssl_class_init (GTlsClientConnectionOpensslClass *kl gobject_class->finalize = g_tls_client_connection_openssl_finalize; gobject_class->get_property = g_tls_client_connection_openssl_get_property; gobject_class->set_property = g_tls_client_connection_openssl_set_property; + gobject_class->constructed = g_tls_client_connection_openssl_constructed; base_class->complete_handshake = g_tls_client_connection_openssl_complete_handshake; base_class->verify_peer_certificate = g_tls_client_connection_openssl_verify_peer_certificate; @@ -282,6 +361,7 @@ g_tls_client_connection_openssl_class_init (GTlsClientConnectionOpensslClass *kl g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity"); g_object_class_override_property (gobject_class, PROP_USE_SSL3, "use-ssl3"); g_object_class_override_property (gobject_class, PROP_ACCEPTED_CAS, "accepted-cas"); + g_object_class_override_property (gobject_class, PROP_SESSION_REUSED, "session-reused"); } static void @@ -432,6 +512,13 @@ set_curve_list (GTlsClientConnectionOpenssl *client) } #endif +static int g_tls_client_connection_openssl_new_session (SSL *s, SSL_SESSION *sess) +{ + GTlsClientConnectionOpenssl *client = G_TLS_CLIENT_CONNECTION_OPENSSL (g_tls_connection_openssl_get_connection_from_ssl (s)); + g_tls_backend_openssl_store_session_data (client->session_id, sess); + return 0; +} + static gboolean g_tls_client_connection_openssl_initable_init (GInitable *initable, GCancellable *cancellable, @@ -442,7 +529,11 @@ g_tls_client_connection_openssl_initable_init (GInitable *initable, const char *hostname; char error_buffer[256]; - client->session = SSL_SESSION_new (); + client->session = g_tls_backend_openssl_lookup_session_data (client->session_id); + if (!client->session) + client->session = SSL_SESSION_new (); + else + client->session_reused = TRUE; client->ssl_ctx = SSL_CTX_new (g_tls_connection_base_is_dtls (G_TLS_CONNECTION_BASE (client)) #if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined (LIBRESSL_VERSION_NUMBER) @@ -468,8 +559,7 @@ g_tls_client_connection_openssl_initable_init (GInitable *initable, return FALSE; /* Only TLS 1.2 or higher */ - options = SSL_OP_NO_TICKET | - SSL_OP_NO_COMPRESSION | + options = SSL_OP_NO_COMPRESSION | #ifdef SSL_OP_NO_TLSv1_1 SSL_OP_NO_TLSv1_1 | #endif @@ -496,6 +586,11 @@ g_tls_client_connection_openssl_initable_init (GInitable *initable, SSL_CTX_set_client_cert_cb (client->ssl_ctx, handshake_thread_retrieve_certificate); + SSL_CTX_set_session_cache_mode (client->ssl_ctx, + SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE); + + SSL_CTX_sess_set_new_cb (client->ssl_ctx, g_tls_client_connection_openssl_new_session); + #ifdef SSL_CTX_set1_sigalgs_list set_signature_algorithm_list (client); #endif @@ -514,6 +609,8 @@ g_tls_client_connection_openssl_initable_init (GInitable *initable, return FALSE; } + SSL_set_session (client->ssl, client->session); + if (data_index == -1) { data_index = SSL_get_ex_new_index (0, (void *)"gtlsclientconnection", NULL, NULL, NULL); } diff --git a/tls/openssl/gtlsserverconnection-openssl.c b/tls/openssl/gtlsserverconnection-openssl.c index d24de05..3bfe198 100644 --- a/tls/openssl/gtlsserverconnection-openssl.c +++ b/tls/openssl/gtlsserverconnection-openssl.c @@ -399,8 +399,7 @@ g_tls_server_connection_openssl_initable_init (GInitable *initable, return FALSE; /* Only TLS 1.2 or higher */ - options = SSL_OP_NO_TICKET | - SSL_OP_NO_COMPRESSION | + options = SSL_OP_NO_COMPRESSION | SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE | diff --git a/tls/tests/connection.c b/tls/tests/connection.c index 0f8aa2d..7d514cb 100644 --- a/tls/tests/connection.c +++ b/tls/tests/connection.c @@ -40,6 +40,11 @@ #include "openssl-include.h" #endif +#if defined(G_OS_UNIX) +#include <dlfcn.h> +static struct timespec offset; +#endif + static const gchar * tls_test_file_path (const char *name) { @@ -104,6 +109,10 @@ setup_connection (TestConnection *test, gconstpointer data) test->context = g_main_context_default (); test->loop = g_main_loop_new (test->context, FALSE); test->auth_mode = G_TLS_AUTHENTICATION_NONE; +#if defined(G_OS_UNIX) + offset.tv_sec = 0; + offset.tv_nsec = 0; +#endif } /* Waits about 10 seconds for @var to be NULL/FALSE */ @@ -145,6 +154,11 @@ wait_until_server_finished (TestConnection *test) static void teardown_connection (TestConnection *test, gconstpointer data) { +#if defined(G_OS_UNIX) + offset.tv_sec = 0; + offset.tv_nsec = 0; +#endif + if (test->service) { g_socket_service_stop (test->service); @@ -552,6 +566,241 @@ read_test_data_async (TestConnection *test) g_object_unref (stream); } +#if defined(G_OS_UNIX) +typedef int (*clock_gettime_fnptr)(clockid_t clk_id, struct timespec *tp); +static __thread clock_gettime_fnptr original_clock_gettime = NULL; + +int +clock_gettime (clockid_t clk_id, + struct timespec *tp) +{ + int ret = -1; + if (!original_clock_gettime) + { + original_clock_gettime = dlsym (RTLD_NEXT, "clock_gettime"); + if (!original_clock_gettime) + { + errno = EINVAL; + return -1; + } + } + + ret = original_clock_gettime (clk_id, tp); + if (ret == 0 && tp) + { + tp->tv_sec += offset.tv_sec; + tp->tv_nsec += offset.tv_nsec; + } + + return ret; +} +#endif + +static void +test_connection_session_resume_ten_minute_expiry (TestConnection *test, + gconstpointer data) +{ + GIOStream *connection; + GError *error = NULL; + GTlsCertificate *cert; + GSocketClient *client; + gboolean reused = FALSE; + +#if !defined(G_OS_UNIX) + g_test_skip ("test_connection_session_resume_ten_minute_expiry requires interposing clock_gettime which is only available in UNIX platforms"); + return; +#endif + + test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error); + g_assert_no_error (error); + g_assert_nonnull (test->database); + + connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED); + test->client_connection = g_tls_client_connection_new (connection, test->identity, &error); + g_assert_no_error (error); + g_assert_nonnull (test->client_connection); + g_object_unref (connection); + + cert = g_tls_certificate_new_from_file (tls_test_file_path ("client-and-key.pem"), &error); + g_assert_no_error (error); + g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert); + g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database); + + g_object_get (G_OBJECT (test->client_connection), + "session-reused", &reused, + NULL); + + read_test_data_async (test); + g_main_loop_run (test->loop); + wait_until_server_finished (test); + + g_assert_no_error (test->read_error); + g_assert_no_error (test->server_error); + + g_object_unref (cert); + g_object_unref (test->client_connection); + g_clear_object (&test->server_connection); + + /* First connection was not reused */ + g_assert_false (reused); + +#if defined(G_OS_UNIX) + /* Expiry should be 10 min */ + offset.tv_sec = 11 * 60; +#endif + + /* Now start a new connection to the same server */ + client = g_socket_client_new (); + connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address), + NULL, &error)); + g_assert_no_error (error); + g_object_unref (client); + test->client_connection = g_tls_client_connection_new (connection, test->identity, &error); + g_assert_no_error (error); + g_assert_nonnull (test->client_connection); + g_object_unref (connection); + + cert = g_tls_certificate_new_from_file (tls_test_file_path ("client-and-key.pem"), &error); + g_assert_no_error (error); + g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert); + g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database); + + g_object_get (G_OBJECT (test->client_connection), + "session-reused", &reused, + NULL); + + read_test_data_async (test); + g_main_loop_run (test->loop); + wait_until_server_finished (test); + + g_assert_no_error (test->read_error); + g_assert_no_error (test->server_error); + + g_object_unref (cert); + + /* Second connection *DID NOT* reuse the first connection */ +#if !defined(BACKEND_IS_GNUTLS) + // FIXME: https://gitlab.gnome.org/GNOME/glib-networking/issues/194 + g_assert_false (reused); +#endif +} + +static void +test_connection_session_resume_multiple_times (TestConnection *test, + gconstpointer data) +{ + GIOStream *connection; + GError *error = NULL; + GTlsCertificate *cert; + GSocketClient *client; + gboolean reused = FALSE; + + test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error); + g_assert_no_error (error); + g_assert_nonnull (test->database); + + connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED); + test->client_connection = g_tls_client_connection_new (connection, test->identity, &error); + g_assert_no_error (error); + g_assert_nonnull (test->client_connection); + g_object_unref (connection); + + cert = g_tls_certificate_new_from_file (tls_test_file_path ("client-and-key.pem"), &error); + g_assert_no_error (error); + g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert); + g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database); + + g_object_get (G_OBJECT (test->client_connection), + "session-reused", &reused, + NULL); + + read_test_data_async (test); + g_main_loop_run (test->loop); + wait_until_server_finished (test); + + g_assert_no_error (test->read_error); + g_assert_no_error (test->server_error); + + g_object_unref (cert); + g_object_unref (test->client_connection); + g_clear_object (&test->server_connection); + + /* First connection was not reused */ + g_assert_false (reused); + + /* Now start a new connection to the same server */ + client = g_socket_client_new (); + connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address), + NULL, &error)); + g_assert_no_error (error); + g_object_unref (client); + test->client_connection = g_tls_client_connection_new (connection, test->identity, &error); + g_assert_no_error (error); + g_assert_nonnull (test->client_connection); + g_object_unref (connection); + + cert = g_tls_certificate_new_from_file (tls_test_file_path ("client-and-key.pem"), &error); + g_assert_no_error (error); + g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert); + g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database); + + g_object_get (G_OBJECT (test->client_connection), + "session-reused", &reused, + NULL); + + read_test_data_async (test); + g_main_loop_run (test->loop); + wait_until_server_finished (test); + + g_assert_no_error (test->read_error); + g_assert_no_error (test->server_error); + + g_object_unref (cert); + g_object_unref (test->client_connection); + g_clear_object (&test->server_connection); + + /* Second connection reused the first connection */ +#if !defined(BACKEND_IS_GNUTLS) + // FIXME: https://gitlab.gnome.org/GNOME/glib-networking/issues/194 + g_assert_true (reused); +#endif + + /* Now start a third connection to the same server */ + client = g_socket_client_new (); + connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address), + NULL, &error)); + g_assert_no_error (error); + g_object_unref (client); + test->client_connection = g_tls_client_connection_new (connection, test->identity, &error); + g_assert_no_error (error); + g_assert_nonnull (test->client_connection); + g_object_unref (connection); + + cert = g_tls_certificate_new_from_file (tls_test_file_path ("client-and-key.pem"), &error); + g_assert_no_error (error); + g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert); + g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database); + + g_object_get (G_OBJECT (test->client_connection), + "session-reused", &reused, + NULL); + + read_test_data_async (test); + g_main_loop_run (test->loop); + wait_until_server_finished (test); + + g_assert_no_error (test->read_error); + g_assert_no_error (test->server_error); + + g_object_unref (cert); + + /* Third connection reused the first connection */ +#if !defined(BACKEND_IS_GNUTLS) + // FIXME: https://gitlab.gnome.org/GNOME/glib-networking/issues/194 + g_assert_true (reused); +#endif +} + static void test_basic_connection (TestConnection *test, gconstpointer data) @@ -3122,6 +3371,10 @@ main (int argc, g_free (module_path); #endif + g_test_add ("/tls/" BACKEND "/connection/session/resume_multiple_times", TestConnection, NULL, + setup_connection, test_connection_session_resume_multiple_times, teardown_connection); + g_test_add ("/tls/" BACKEND "/connection/session/reuse_ten_minute_expiry", TestConnection, NULL, + setup_connection, test_connection_session_resume_ten_minute_expiry, teardown_connection); g_test_add ("/tls/" BACKEND "/connection/basic", TestConnection, NULL, setup_connection, test_basic_connection, teardown_connection); g_test_add ("/tls/" BACKEND "/connection/verified", TestConnection, NULL, diff --git a/tls/tests/meson.build b/tls/tests/meson.build index 4afcc81..6c51ac7 100644 --- a/tls/tests/meson.build +++ b/tls/tests/meson.build @@ -48,6 +48,7 @@ test_programs = [ foreach backend: backends foreach program: test_programs program_name = program[0] + '-' + backend + program_deps = program[2] test_conf = configuration_data() test_conf.set('installed_tests_dir', installed_tests_execdir) @@ -67,8 +68,13 @@ foreach backend: backends '-DBACKEND="@0@"'.format(backend), '-DBACKEND_IS_' + backend.to_upper(), '-DSIZEOF_TIME_T=@0@'.format(cc.sizeof('time_t', prefix: '#include <time.h>')), + '-D_GNU_SOURCE', ] + if not ['windows'].contains(host_system) + program_deps += cc.find_library('dl') + endif + if backend == 'openssl' incs += openssl_inc endif @@ -77,7 +83,7 @@ foreach backend: backends program_name, [program[0] + '.c'] + program[1], include_directories: incs, - dependencies: program[2], + dependencies: program_deps, c_args: test_cflags, install: enable_installed_tests, install_dir: installed_tests_execdir |