summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGoncalo Gomes <goncalo.gomes@youview.com>2022-08-09 10:44:44 +0100
committerMarge Bot <marge-bot@gnome.org>2022-09-12 15:46:51 +0000
commit4be360a85761e4b83aa851bc7413698b3155ffb3 (patch)
tree2824b8e49d5c85c43afba39392b0dcb37465d59b
parent25cfeb4c1819c15f9c4476fdaffc8ba360ba4dfe (diff)
downloadglib-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.c19
-rw-r--r--tls/gnutls/gtlsclientconnection-gnutls.c11
-rw-r--r--tls/openssl/gtlsbackend-openssl.c168
-rw-r--r--tls/openssl/gtlsbackend-openssl.h5
-rw-r--r--tls/openssl/gtlsclientconnection-openssl.c107
-rw-r--r--tls/openssl/gtlsserverconnection-openssl.c3
-rw-r--r--tls/tests/connection.c253
-rw-r--r--tls/tests/meson.build8
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