[glib-networking/mcatanzaro/tls-thread: 11/24] progress
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/mcatanzaro/tls-thread: 11/24] progress
- Date: Sat, 28 Dec 2019 21:04:24 +0000 (UTC)
commit e85acc7f4b30d057b8f928b24e6e075ceecd0c45
Author: Michael Catanzaro <mcatanzaro gnome org>
Date: Sun Dec 22 10:29:13 2019 -0600
progress
tls/base/gtlsconnection-base.c | 25 +--
tls/base/gtlsconnection-base.h | 2 -
tls/base/gtlsoperationsthread-base.c | 39 +++++
tls/base/gtlsoperationsthread-base.h | 4 +
tls/gnutls/gtlsclientconnection-gnutls.c | 113 +------------
tls/gnutls/gtlsconnection-gnutls.c | 54 +-----
tls/gnutls/gtlsconnection-gnutls.h | 7 -
tls/gnutls/gtlsoperationsthread-gnutls.c | 271 ++++++++++++++++++++++---------
tls/gnutls/gtlsserverconnection-gnutls.c | 53 ------
9 files changed, 244 insertions(+), 324 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 9b493dc..c15bca1 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -248,6 +248,8 @@ g_tls_connection_base_initable_init (GInitable *initable,
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
priv->thread = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->create_op_thread (tls);
+ if (priv->certificate)
+ g_tls_operations_thread_base_set_own_certificate (priv->certificate);
return TRUE;
}
@@ -453,6 +455,9 @@ g_tls_connection_base_set_property (GObject *object,
if (priv->certificate)
g_object_unref (priv->certificate);
priv->certificate = g_value_dup_object (value);
+
+ if (priv->thread)
+ g_tls_operations_thread_base_set_own_certificate (priv->certificate);
break;
case PROP_INTERACTION:
@@ -2442,26 +2447,6 @@ g_tls_connection_base_get_base_iostream (GTlsConnectionBase *tls)
return priv->base_io_stream;
}
-GPollableInputStream *
-g_tls_connection_base_get_base_istream (GTlsConnectionBase *tls)
-{
- GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
- g_assert (!g_tls_connection_base_is_dtls (tls));
-
- return priv->base_istream;
-}
-
-GPollableOutputStream *
-g_tls_connection_base_get_base_ostream (GTlsConnectionBase *tls)
-{
- GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-
- g_assert (!g_tls_connection_base_is_dtls (tls));
-
- return priv->base_ostream;
-}
-
void
g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (GTlsConnectionBase *tls)
{
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 65058d0..ea764c6 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -119,8 +119,6 @@ gboolean g_tls_connection_base_is_dtls (GTlsCon
GDatagramBased *g_tls_connection_base_get_base_socket (GTlsConnectionBase *tls);
GIOStream *g_tls_connection_base_get_base_iostream (GTlsConnectionBase *tls);
-GPollableInputStream *g_tls_connection_base_get_base_istream (GTlsConnectionBase *tls);
-GPollableOutputStream *g_tls_connection_base_get_base_ostream (GTlsConnectionBase *tls);
void g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate
(GTlsConnectionBase *tls);
diff --git a/tls/base/gtlsoperationsthread-base.c b/tls/base/gtlsoperationsthread-base.c
index 32959e7..44975af 100644
--- a/tls/base/gtlsoperationsthread-base.c
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -75,6 +75,10 @@ typedef struct {
GMainContext *op_thread_context;
GAsyncQueue *queue;
+
+ /* This mutex guards everything below. */
+ GMutex mutex;
+ gchar *own_certificate_pem;
} GTlsOperationsThreadBasePrivate;
typedef enum {
@@ -408,6 +412,33 @@ execute_op (GTlsOperationsThreadBase *self,
return result;
}
+void
+g_tls_operations_thread_base_set_own_certificate (GTlsOperationsThreadBase *self,
+ GTlsCertificate *cert)
+{
+ GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+ g_mutex_lock (&priv->mutex);
+ g_clear_pointer (&priv->own_certificate_pem, g_free);
+ g_object_get (cert,
+ "certificate-pem", &priv->own_certificate_pem,
+ NULL);
+ g_mutex_unlock (&priv->mutex);
+}
+
+gchar *
+g_tls_operations_thread_base_get_own_certificate_pem (GTlsOperationsThreadBase *self)
+{
+ GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+ gchar *copy;
+
+ g_mutex_lock (&priv->mutex);
+ copy = g_strdup (priv->own_certificate_pem);
+ g_mutex_unlock (&priv->mutex);
+
+ return copy;
+}
+
void
g_tls_operations_thread_base_copy_client_session_state (GTlsOperationsThreadBase *self,
GTlsOperationsThreadBase *source)
@@ -437,6 +468,7 @@ g_tls_operations_thread_base_set_server_identity (GTlsOperationsThreadBase *self
GTlsConnectionBaseStatus
g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase *self,
const gchar **advertised_protocols,
+ GTlsAuthenticationMode auth_mode,
gint64 timeout,
GCancellable *cancellable,
GError **error)
@@ -447,6 +479,7 @@ g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase *self,
op = g_tls_thread_handshake_operation_new (self,
priv->connection,
advertised_protocols,
+ auth_mode,
timeout,
cancellable);
return execute_op (self, g_steal_pointer (&op), NULL, error);
@@ -1052,6 +1085,8 @@ g_tls_operations_thread_base_init (GTlsOperationsThreadBase *self)
priv->op_thread = g_thread_new ("[glib-networking] GTlsOperationsThreadBase TLS operations thread",
tls_op_thread,
self);
+
+ g_mutex_init (&priv->mutex);
}
static void
@@ -1072,6 +1107,10 @@ g_tls_operations_thread_base_finalize (GObject *object)
g_clear_weak_pointer (&priv->connection);
+ g_mutex_clear (&priv->mutex);
+
+ g_clear_pointer (&priv->own_certificate);
+
G_OBJECT_CLASS (g_tls_operations_thread_base_parent_class)->finalize (object);
}
diff --git a/tls/base/gtlsoperationsthread-base.h b/tls/base/gtlsoperationsthread-base.h
index 039f3b5..9e61055 100644
--- a/tls/base/gtlsoperationsthread-base.h
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -95,6 +95,10 @@ struct _GTlsOperationsThreadBaseClass
/* FIXME: remove!!! */
GTlsConnectionBase *g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase
*self);
+void g_tls_operations_thread_base_set_own_certificate (GTlsOperationsThreadBase
*self,
+ GTlsCertificate
*cert);
+gchar *g_tls_operations_thread_base_get_own_certificate_pem (GTlsOperationsThreadBase
*self);
+
void g_tls_operations_thread_base_copy_client_session_state (GTlsOperationsThreadBase
*self,
GTlsOperationsThreadBase
*source);
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 4a8c549..0a8a7f0 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -55,11 +55,6 @@ struct _GTlsClientConnectionGnutls
gboolean use_ssl3;
GPtrArray *accepted_cas;
- gboolean accepted_cas_changed;
-
- gnutls_pcert_st *pcert;
- unsigned int pcert_length;
- gnutls_privkey_t pkey;
};
static void g_tls_client_connection_gnutls_initable_interface_init (GInitableIface *iface);
@@ -67,15 +62,6 @@ static void g_tls_client_connection_gnutls_initable_interface_init (GInitableIfa
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);
-static int g_tls_client_connection_gnutls_handshake_thread_retrieve_function (gnutls_session_t
session,
- const gnutls_datum_t
*req_ca_rdn,
- int
nreqs,
- const gnutls_pk_algorithm_t
*pk_algos,
- int
pk_algos_length,
- gnutls_pcert_st
**pcert,
- unsigned int
*pcert_length,
- gnutls_privkey_t
*pkey);
-
static GInitableIface *g_tls_client_connection_gnutls_parent_initable_iface;
G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionGnutls, g_tls_client_connection_gnutls,
G_TYPE_TLS_CONNECTION_GNUTLS,
@@ -86,16 +72,6 @@ G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionGnutls, g_tls_client_connection_gnu
G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_CLIENT_CONNECTION,
g_tls_client_connection_gnutls_dtls_client_connection_interface_init));
-static void
-clear_gnutls_certificate_copy (GTlsClientConnectionGnutls *gnutls)
-{
- g_tls_certificate_gnutls_copy_free (gnutls->pcert, gnutls->pcert_length, gnutls->pkey);
-
- gnutls->pcert = NULL;
- gnutls->pcert_length = 0;
- gnutls->pkey = NULL;
-}
-
static void
g_tls_client_connection_gnutls_init (GTlsClientConnectionGnutls *gnutls)
{
@@ -120,8 +96,6 @@ g_tls_client_connection_gnutls_finalize (GObject *object)
g_clear_object (&gnutls->server_identity);
g_clear_pointer (&gnutls->accepted_cas, g_ptr_array_unref);
- clear_gnutls_certificate_copy (gnutls);
-
G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->finalize (object);
}
@@ -133,14 +107,10 @@ g_tls_client_connection_gnutls_initable_init (GInitable *initable,
GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
GTlsOperationsThreadBase *thread = g_tls_connection_base_get_op_thread (G_TLS_CONNECTION_BASE (gnutls));
const gchar *hostname;
- gnutls_certificate_credentials_t creds;
if (!g_tls_client_connection_gnutls_parent_initable_iface->init (initable, cancellable, error))
return FALSE;
- creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
- gnutls_certificate_set_retrieve_function2 (creds,
g_tls_client_connection_gnutls_handshake_thread_retrieve_function);
-
hostname = get_server_identity (G_TLS_CLIENT_CONNECTION_GNUTLS (gnutls));
if (hostname)
g_tls_operations_thread_base_set_server_identity (thread, hostname);
@@ -178,8 +148,7 @@ g_tls_client_connection_gnutls_get_property (GObject *object,
{
for (i = 0; i < gnutls->accepted_cas->len; ++i)
{
- accepted_cas = g_list_prepend (accepted_cas, g_byte_array_ref (
- gnutls->accepted_cas->pdata[i]));
+ accepted_cas = g_list_prepend (accepted_cas, g_byte_array_ref
(gnutls->accepted_cas->pdata[i]));
}
accepted_cas = g_list_reverse (accepted_cas);
}
@@ -230,86 +199,6 @@ g_tls_client_connection_gnutls_set_property (GObject *object,
}
}
-static int
-g_tls_client_connection_gnutls_handshake_thread_retrieve_function (gnutls_session_t session,
- const gnutls_datum_t *req_ca_rdn,
- int nreqs,
- const gnutls_pk_algorithm_t *pk_algos,
- int
pk_algos_length,
- gnutls_pcert_st **pcert,
- unsigned int
*pcert_length,
- gnutls_privkey_t *pkey)
-{
- GTlsConnectionBase *tls = gnutls_transport_get_ptr (session);
- GTlsClientConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
- GTlsConnectionGnutls *conn = G_TLS_CONNECTION_GNUTLS (gnutls);
- GPtrArray *accepted_cas;
- gboolean had_accepted_cas;
- GByteArray *dn;
- int i;
-
- /* FIXME: Here we are supposed to ensure that the certificate supports one of
- * the algorithms given in pk_algos.
- */
-
- had_accepted_cas = gnutls->accepted_cas != NULL;
-
- accepted_cas = g_ptr_array_new_with_free_func ((GDestroyNotify)g_byte_array_unref);
- for (i = 0; i < nreqs; i++)
- {
- dn = g_byte_array_new ();
- g_byte_array_append (dn, req_ca_rdn[i].data, req_ca_rdn[i].size);
- g_ptr_array_add (accepted_cas, dn);
- }
-
- if (gnutls->accepted_cas)
- g_ptr_array_unref (gnutls->accepted_cas);
- gnutls->accepted_cas = accepted_cas;
-
- gnutls->accepted_cas_changed = gnutls->accepted_cas || had_accepted_cas;
-
- clear_gnutls_certificate_copy (gnutls);
- g_tls_connection_gnutls_handshake_thread_get_certificate (conn, pcert, pcert_length, pkey);
-
- if (*pcert_length == 0)
- {
- g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
-
- if (g_tls_connection_base_handshake_thread_request_certificate (tls))
- g_tls_connection_gnutls_handshake_thread_get_certificate (conn, pcert, pcert_length, pkey);
-
- if (*pcert_length == 0)
- {
- g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
-
- /* If there is still no client certificate, this connection will
- * probably fail, but we must not give up yet. The certificate might
- * be optional, e.g. if the server is using
- * G_TLS_AUTHENTICATION_REQUESTED, not G_TLS_AUTHENTICATION_REQUIRED.
- */
- g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (tls);
- return 0;
- }
- }
-
- if (!*pkey)
- {
- g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
-
- /* No private key. GnuTLS expects it to be non-null if pcert_length is
- * nonzero, so we have to abort now.
- */
- g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (tls);
- return -1;
- }
-
- gnutls->pcert = *pcert;
- gnutls->pcert_length = *pcert_length;
- gnutls->pkey = *pkey;
-
- return 0;
-}
-
static void
g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase *tls,
gchar **negotiated_protocol,
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 1c49fd1..3be6fed 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -58,8 +58,6 @@ static GInitableIface *g_tls_connection_gnutls_parent_initable_iface;
static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
-static int verify_certificate_cb (gnutls_session_t session);
-
static gnutls_priority_t priority;
typedef struct
@@ -89,19 +87,6 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
priv->cancellable = g_cancellable_new ();
}
-static void
-g_tls_connection_gnutls_set_handshake_priority (GTlsConnectionGnutls *gnutls)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- int ret;
-
- g_assert (priority);
-
- ret = gnutls_priority_set (priv->session, priority);
- if (ret != GNUTLS_E_SUCCESS)
- g_warning ("Failed to set GnuTLS session priority: %s", gnutls_strerror (ret));
-}
-
static gboolean
g_tls_connection_gnutls_initable_init (GInitable *initable,
GCancellable *cancellable,
@@ -215,6 +200,7 @@ g_tls_connection_gnutls_handshake_thread_get_certificate (GTlsConnectionGnutls
if (cert)
{
/* Send along a pre-initialized privkey so we can handle the callback here. */
+ /* FIXME: this was never safe, we're not on the right thread here */
gnutls_privkey_t privkey;
gnutls_privkey_init (&privkey);
gnutls_privkey_set_pin_function (privkey, on_pin_request, gnutls);
@@ -239,7 +225,7 @@ g_tls_connection_gnutls_create_op_thread (GTlsConnectionBase *tls)
GDatagramBased *base_socket = NULL;
gboolean client = G_IS_TLS_CLIENT_CONNECTION (tls);
guint flags = client ? GNUTLS_CLIENT : GNUTLS_SERVER;
- GTlsOperationsThreadGnutls *thread;
+ GTlsOperationsThreadBase *thread;
g_object_get (tls,
"base-io-stream", &base_io_stream,
@@ -286,16 +272,6 @@ g_tls_connection_gnutls_retrieve_peer_certificate (GTlsConnectionBase *tls)
return G_TLS_CERTIFICATE (chain);
}
-static int
-verify_certificate_cb (gnutls_session_t session)
-{
- GTlsConnectionBase *tls = gnutls_session_get_ptr (session);
-
- /* Return 0 for the handshake to continue, non-zero to terminate.
- * Complete opposite of what OpenSSL does. */
- return !g_tls_connection_base_handshake_thread_verify_certificate (tls);
-}
-
static void
g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase *tls,
gchar **negotiated_protocol,
@@ -321,29 +297,6 @@ g_tls_connection_gnutls_is_session_resumed (GTlsConnectionBase *tls)
return gnutls_session_is_resumed (priv->session);
}
-static void
-initialize_gnutls_priority (void)
-{
- const gchar *priority_override;
- const gchar *error_pos = NULL;
- int ret;
-
- g_assert (!priority);
-
- priority_override = g_getenv ("G_TLS_GNUTLS_PRIORITY");
- if (priority_override)
- {
- ret = gnutls_priority_init2 (&priority, priority_override, &error_pos, 0);
- if (ret != GNUTLS_E_SUCCESS)
- g_warning ("Failed to set GnuTLS session priority with beginning at %s: %s", error_pos,
gnutls_strerror (ret));
- return;
- }
-
- ret = gnutls_priority_init2 (&priority, "%COMPAT:-VERS-TLS1.1:-VERS-TLS1.0", &error_pos,
GNUTLS_PRIORITY_INIT_DEF_APPEND);
- if (ret != GNUTLS_E_SUCCESS)
- g_warning ("Failed to set GnuTLS session priority with error beginning at %s: %s", error_pos,
gnutls_strerror (ret));
-}
-
static void
g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
{
@@ -353,12 +306,9 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
gobject_class->finalize = g_tls_connection_gnutls_finalize;
base_class->create_op_thread = g_tls_connection_gnutls_create_op_thread;
- base_class->handshake_thread_safe_renegotiation_status =
g_tls_connection_gnutls_handshake_thread_safe_renegotiation_status;
base_class->retrieve_peer_certificate = g_tls_connection_gnutls_retrieve_peer_certificate;
base_class->complete_handshake = g_tls_connection_gnutls_complete_handshake;
base_class->is_session_resumed = g_tls_connection_gnutls_is_session_resumed;
-
- initialize_gnutls_priority ();
}
static void
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index edb617f..d7747d9 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -41,11 +41,4 @@ struct _GTlsConnectionGnutlsClass
GTlsConnectionBaseClass parent_class;
};
-gnutls_certificate_credentials_t g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
-
-void g_tls_connection_gnutls_handshake_thread_get_certificate (GTlsConnectionGnutls *gnutls,
- gnutls_pcert_st **pcert,
- unsigned int *pcert_length,
- gnutls_privkey_t *pkey);
-
G_END_DECLS
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
index 9d25cd9..6688d58 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.c
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -29,6 +29,7 @@
#include "gtlsoperationsthread-gnutls.h"
#include "gtlsbackend-gnutls.h"
+#include "gtlscertificate-gnutls.h"
#include "gtlsconnection-gnutls.h"
#include <errno.h>
@@ -65,6 +66,13 @@ struct _GTlsOperationsThreadGnutls {
GCancellable *op_cancellable;
GError *op_error;
+
+ gnutls_pcert_st *pcert;
+ unsigned int pcert_length;
+ gnutls_privkey_t pkey;
+
+ GPtrArray *accepted_cas;
+ gboolean accepted_cas_changed;
};
enum
@@ -287,84 +295,6 @@ initialize_gnutls_priority (void)
g_warning ("Failed to set GnuTLS session priority with error beginning at %s: %s", error_pos,
gnutls_strerror (ret));
}
-static void
-compute_session_id (GTlsOperationsThreadGnutls *self)
-{
- GSocketAddress *remote_addr;
- GInetAddress *iaddr;
- guint port;
-
- /* The testsuite expects handshakes to actually happen. E.g. a test might
- * check to see that a handshake succeeds and then later check that a new
- * handshake fails. If we get really unlucky and the same port number is
- * reused for the server socket between connections, then we'll accidentally
- * resume the old session and skip certificate verification. Such failures
- * are difficult to debug because they require running the tests hundreds of
- * times simultaneously to reproduce (the port number does not get reused
- * quickly enough if the tests are run sequentially).
- *
- * So session resumption will just need to be tested manually.
- */
- if (g_test_initialized ())
- return;
-
- /* 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.
- */
- if (G_IS_SOCKET_CONNECTION (self->base_iostream))
- {
- remote_addr = g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (self->base_iostream), NULL);
- if (G_IS_INET_SOCKET_ADDRESS (remote_addr))
- {
- GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
- const gchar *server_hostname;
- gchar *addrstr, *session_id;
- GTlsCertificate *cert = NULL;
- gchar *cert_hash = 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 = self->server_identity;
-
- /* 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 (gnutls), "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);
- }
- session_id = g_strdup_printf ("%s/%s/%d/%s", addrstr,
- server_hostname ? server_hostname : "",
- port,
- cert_hash ? cert_hash : "");
- self->session_id = g_bytes_new_take (session_id, strlen (session_id));
- g_free (addrstr);
- g_free (cert_hash);
- }
- g_object_unref (remote_addr);
- }
-}
-
static void
g_tls_operations_thread_gnutls_copy_client_session_state (GTlsOperationsThreadBase *base,
GTlsOperationsThreadBase *base_source)
@@ -372,6 +302,8 @@ g_tls_operations_thread_gnutls_copy_client_session_state (GTlsOperationsThreadBa
GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
GTlsOperationsThreadGnutls *source = G_TLS_OPERATIONS_THREAD_GNUTLS (base_source);
+ g_assert (is_client (self));
+
/* Precondition: source has handshaked, conn has not. */
g_return_if_fail (!self->session_id);
g_return_if_fail (source->session_id);
@@ -465,9 +397,87 @@ set_advertised_protocols (GTlsOperationsThreadGnutls *self,
g_free (protocols);
}
+static void
+compute_session_id (GTlsOperationsThreadGnutls *self)
+{
+ GSocketAddress *remote_addr;
+ GInetAddress *iaddr;
+ guint port;
+
+ g_assert (is_client (self));
+
+ /* The testsuite expects handshakes to actually happen. E.g. a test might
+ * check to see that a handshake succeeds and then later check that a new
+ * handshake fails. If we get really unlucky and the same port number is
+ * reused for the server socket between connections, then we'll accidentally
+ * resume the old session and skip certificate verification. Such failures
+ * are difficult to debug because they require running the tests hundreds of
+ * times simultaneously to reproduce (the port number does not get reused
+ * quickly enough if the tests are run sequentially).
+ *
+ * So session resumption will just need to be tested manually.
+ */
+ if (g_test_initialized ())
+ return;
+
+ /* 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.
+ */
+ if (G_IS_SOCKET_CONNECTION (self->base_iostream))
+ {
+ remote_addr = g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (self->base_iostream), NULL);
+ if (G_IS_INET_SOCKET_ADDRESS (remote_addr))
+ {
+ GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
+ const gchar *server_hostname;
+ gchar *addrstr;
+ gchar *session_id;
+ gchar *pem;
+ gchar *cert_hash = 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 = self->server_identity;
+
+ /* 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.
+ */
+ pem = g_tls_operations_thread_base_get_own_certificate_pem (G_TLS_OPERATIONS_THREAD_BASE (self));
+ if (pem)
+ {
+ cert_hash = g_compute_checksum_for_string (G_CHECKSUM_SHA256, pem, -1);
+ g_free (pem);
+ }
+
+ session_id = g_strdup_printf ("%s/%s/%d/%s", addrstr,
+ server_hostname ? server_hostname : "",
+ port,
+ cert_hash ? cert_hash : "");
+ self->session_id = g_bytes_new_take (session_id, strlen (session_id));
+ g_free (addrstr);
+ g_free (cert_hash);
+ }
+ g_object_unref (remote_addr);
+ }
+}
+
static void
set_session_data (GTlsOperationsThreadGnutls *self)
{
+ g_assert (is_client (self));
+
compute_session_id (self);
if (self->session_data_override)
@@ -1024,6 +1034,107 @@ g_tls_operations_thread_gnutls_pull_timeout_func (gnutls_transport_ptr_t transpo
return 0;
}
+static int
+verify_certificate_cb (gnutls_session_t session)
+{
+ GTlsOperationsThreadGnutls *self = gnutls_session_get_ptr (session);
+
+ /* Return 0 for the handshake to continue, non-zero to terminate.
+ * Complete opposite of what OpenSSL does.
+ */
+ return !g_tls_connection_base_handshake_thread_verify_certificate (tls);
+}
+
+static void
+clear_gnutls_certificate_copy (GTlsOperationsThreadGnutls *self)
+{
+ g_tls_certificate_gnutls_copy_free (self->pcert, self->pcert_length, self->pkey);
+
+ self->pcert = NULL;
+ self->pcert_length = 0;
+ self->pkey = NULL;
+}
+
+static int
+retrieve_certificate_cb (gnutls_session_t session,
+ const gnutls_datum_t *req_ca_rdn,
+ int nreqs,
+ const gnutls_pk_algorithm_t *pk_algos,
+ int pk_algos_length,
+ gnutls_pcert_st **pcert,
+ unsigned int *pcert_length,
+ gnutls_privkey_t *pkey)
+{
+ GTlsOperationsThreadGnutls *self = gnutls_transport_get_ptr (session);
+ gboolean had_accepted_cas;
+ GByteArray *dn;
+ int i;
+
+ /* FIXME: Here we are supposed to ensure that the certificate supports one of
+ * the algorithms given in pk_algos.
+ */
+
+ if (is_client (self))
+ {
+ had_accepted_cas = self->accepted_cas && self->accepted_cas->len != 0;
+
+ g_clear_pointer (&self->accepted_cas, g_ptr_array_unref);
+ self->accepted_cas = g_ptr_array_new_with_free_func ((GDestroyNotify)g_byte_array_unref);
+ for (i = 0; i < nreqs; i++)
+ {
+ dn = g_byte_array_new ();
+ g_byte_array_append (dn, req_ca_rdn[i].data, req_ca_rdn[i].size);
+ g_ptr_array_add (self->accepted_cas, dn);
+ }
+
+ self->accepted_cas_changed = self->accepted_cas || had_accepted_cas;
+ }
+
+ clear_gnutls_certificate_copy (self);
+ g_tls_connection_gnutls_handshake_thread_get_certificate (conn, pcert, pcert_length, pkey);
+
+ if (is_client (self))
+ {
+ if (*pcert_length == 0)
+ {
+ g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
+
+ if (g_tls_connection_base_handshake_thread_request_certificate (tls))
+ g_tls_connection_gnutls_handshake_thread_get_certificate (conn, pcert, pcert_length, pkey);
+
+ if (*pcert_length == 0)
+ {
+ g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
+
+ /* If there is still no client certificate, this connection will
+ * probably fail, but we must not give up yet. The certificate might
+ * be optional, e.g. if the server is using
+ * G_TLS_AUTHENTICATION_REQUESTED, not G_TLS_AUTHENTICATION_REQUIRED.
+ */
+ g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (tls);
+ return 0;
+ }
+ }
+
+ if (!*pkey)
+ {
+ g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
+
+ /* No private key. GnuTLS expects it to be non-null if pcert_length is
+ * nonzero, so we have to abort now.
+ */
+ g_tls_connection_base_handshake_thread_set_missing_requested_client_certificate (tls);
+ return -1;
+ }
+ }
+
+ self->pcert = *pcert;
+ self->pcert_length = *pcert_length;
+ self->pkey = *pkey;
+
+ return 0;
+}
+
static int
session_ticket_received_cb (gnutls_session_t session,
guint htype,
@@ -1117,10 +1228,13 @@ g_tls_operations_thread_gnutls_finalize (GObject *object)
g_clear_pointer (&self->session_data, g_bytes_unref);
g_clear_pointer (&self->server_identity, g_free);
+ g_clear_pointer (&self->accepted_cas, g_ptr_array_unref);
g_clear_object (&self->base_iostream);
g_clear_object (&self->base_socket);
+ clear_gnutls_certificate_copy (self);
+
g_assert (!self->op_cancellable);
g_assert (!self->op_error);
@@ -1144,6 +1258,7 @@ g_tls_operations_thread_gnutls_constructed (GObject *object)
if (ret != GNUTLS_E_SUCCESS)
return FALSE;
#endif
+ gnutls_certificate_set_retrieve_function2 (self->creds, retrieve_certificate_cb);
gnutls_init (&self->session, self->init_flags);
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index 8a44285..7f54c84 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -45,10 +45,6 @@ struct _GTlsServerConnectionGnutls
GTlsConnectionGnutls parent_instance;
GTlsAuthenticationMode authentication_mode;
-
- gnutls_pcert_st *pcert;
- unsigned int pcert_length;
- gnutls_privkey_t pkey;
};
static void g_tls_server_connection_gnutls_initable_interface_init (GInitableIface *iface);
@@ -66,70 +62,22 @@ G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionGnutls, g_tls_server_connection_gnu
NULL)
)
-static void
-clear_gnutls_certificate_copy (GTlsServerConnectionGnutls *gnutls)
-{
- g_tls_certificate_gnutls_copy_free (gnutls->pcert, gnutls->pcert_length, gnutls->pkey);
-
- gnutls->pcert = NULL;
- gnutls->pcert_length = 0;
- gnutls->pkey = NULL;
-}
-
static void
g_tls_server_connection_gnutls_init (GTlsServerConnectionGnutls *gnutls)
{
}
-static void
-g_tls_server_connection_gnutls_finalize (GObject *object)
-{
- GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
-
- clear_gnutls_certificate_copy (gnutls);
-
- G_OBJECT_CLASS (g_tls_server_connection_gnutls_parent_class)->finalize (object);
-}
-
-static int
-g_tls_server_connection_gnutls_handshake_thread_retrieve_function (gnutls_session_t session,
- const gnutls_datum_t *req_ca_rdn,
- int nreqs,
- const gnutls_pk_algorithm_t *pk_algos,
- int
pk_algos_length,
- gnutls_pcert_st **pcert,
- unsigned int
*pcert_length,
- gnutls_privkey_t *pkey)
-{
- GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (gnutls_transport_get_ptr (session));
-
- clear_gnutls_certificate_copy (gnutls);
-
- g_tls_connection_gnutls_handshake_thread_get_certificate (G_TLS_CONNECTION_GNUTLS (gnutls),
- pcert, pcert_length, pkey);
-
- gnutls->pcert = *pcert;
- gnutls->pcert_length = *pcert_length;
- gnutls->pkey = *pkey;
-
- return 0;
-}
-
static gboolean
g_tls_server_connection_gnutls_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
GTlsCertificate *cert;
gnutls_certificate_credentials_t creds;
if (!g_tls_server_connection_gnutls_parent_initable_iface->init (initable, cancellable, error))
return FALSE;
- creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
- gnutls_certificate_set_retrieve_function2 (creds,
g_tls_server_connection_gnutls_handshake_thread_retrieve_function);
-
/* Currently we don't know ahead of time if a PKCS #11 backed certificate has a private key. */
cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (initable));
if (cert && !g_tls_certificate_gnutls_has_key (G_TLS_CERTIFICATE_GNUTLS (cert)) &&
@@ -186,7 +134,6 @@ g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klas
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- gobject_class->finalize = g_tls_server_connection_gnutls_finalize;
gobject_class->get_property = g_tls_server_connection_gnutls_get_property;
gobject_class->set_property = g_tls_server_connection_gnutls_set_property;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]