[glib-networking/mcatanzaro/tls-thread: 2/4] wip
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/mcatanzaro/tls-thread: 2/4] wip
- Date: Wed, 4 Dec 2019 22:16:57 +0000 (UTC)
commit e936c9b087ee8790961638c132afd60ba116b923
Author: Michael Catanzaro <mcatanzaro gnome org>
Date: Wed Dec 4 10:29:55 2019 -0600
wip
tls/base/gtlsconnection-base.c | 8 +-
tls/base/gtlsconnection-base.h | 28 ---
tls/base/gtlsoperationsthread-base.c | 91 ++++++--
tls/base/gtlsoperationsthread-base.h | 78 +++++--
tls/gnutls/gtlsconnection-gnutls.c | 175 +-------------
tls/gnutls/gtlsconnection-gnutls.h | 1 +
tls/gnutls/gtlsoperationsthread-gnutls.c | 358 ++++++++++++++++++++++++++++-
tls/gnutls/gtlsoperationsthread-gnutls.h | 3 +
tls/openssl/gtlsbio.c | 52 -----
tls/openssl/gtlsconnection-openssl.c | 126 +---------
tls/openssl/gtlsconnection-openssl.h | 3 +
tls/openssl/gtlsoperationsthread-openssl.c | 231 +++++++++++++++++++
tls/openssl/gtlsoperationsthread-openssl.h | 3 +
13 files changed, 748 insertions(+), 409 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 19da6f1..c5daf3b 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -2036,9 +2036,7 @@ g_tls_connection_base_read_message (GTlsConnectionBase *tls,
}
else
{
- g_assert (G_TLS_CONNECTION_BASE_GET_CLASS (tls)->read_message_fn);
- status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
- read_message_fn (tls, vectors, num_vectors, timeout, &nread, cancellable, error);
+ status = g_tls_operations_thread_base_read_message (priv->thread, vectors, num_vectors, timeout,
&nread, cancellable, error);
}
yield_op (tls, G_TLS_CONNECTION_BASE_OP_READ, status);
@@ -2187,9 +2185,7 @@ g_tls_connection_base_write_message (GTlsConnectionBase *tls,
timeout, cancellable, error))
return -1;
- g_assert (G_TLS_CONNECTION_BASE_GET_CLASS (tls)->read_message_fn);
- status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
- write_message_fn (tls, vectors, num_vectors, timeout, &nwrote, cancellable, error);
+ status = g_tls_operations_thread_base_write_message (priv->thread, vectors, num_vectors, timeout,
&nwrote, cancellable, error);
yield_op (tls, G_TLS_CONNECTION_BASE_OP_WRITE, status);
} while (status == G_TLS_CONNECTION_BASE_REHANDSHAKE);
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 791c06b..a91ea40 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -96,34 +96,6 @@ struct _GTlsConnectionBaseClass
GError **error);
/* FIXME: must remove timeout parameters from all vfuncs, including handshake vfuncs */
- GTlsConnectionBaseStatus (*read_fn) (GTlsConnectionBase *tls,
- void *buffer,
- gsize size,
- gssize *nread,
- GCancellable *cancellable,
- GError **error);
- GTlsConnectionBaseStatus (*read_message_fn) (GTlsConnectionBase *tls,
- GInputVector *vectors,
- guint num_vectors,
- gint64 timeout,
- gssize *nread,
- GCancellable *cancellable,
- GError **error);
-
- GTlsConnectionBaseStatus (*write_fn) (GTlsConnectionBase *tls,
- const void *buffer,
- gsize size,
- gssize *nwrote,
- GCancellable *cancellable,
- GError **error);
- GTlsConnectionBaseStatus (*write_message_fn) (GTlsConnectionBase *tls,
- GOutputVector *vectors,
- guint num_vectors,
- gint64 timeout,
- gssize *nwrote,
- GCancellable *cancellable,
- GError **error);
-
GTlsConnectionBaseStatus (*close_fn) (GTlsConnectionBase *tls,
gint64 timeout,
GCancellable *cancellable,
diff --git a/tls/base/gtlsoperationsthread-base.c b/tls/base/gtlsoperationsthread-base.c
index 1f77d3e..ce3e120 100644
--- a/tls/base/gtlsoperationsthread-base.c
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -28,7 +28,6 @@
#include "gtlsoperationsthread-base.h"
#include <glib/gi18n-lib.h>
-#include <glib/gstdio.h>
/* The purpose of this class is to ensure the underlying TLS library is only
* ever used on a single thread. There are multiple benefits of this:
@@ -70,20 +69,23 @@
* write operations on one thread without either one blocking the other.
*/
typedef struct {
+ /* FIXME: remove to prevent misuse? */
GTlsConnectionBase *connection; /* unowned */
GThread *op_thread;
GMainContext *op_thread_context;
- /* When pushing ops onto the queue, call g_main_context_wakeup() on
- * op_thread_context to ensure the op is noticed.
+ /* Important: when pushing ops onto the queue, call g_main_context_wakeup()
+ * on op_thread_context to ensure the op is noticed.
*/
GAsyncQueue *queue;
} GTlsOperationsThreadBasePrivate;
typedef enum {
G_TLS_THREAD_OP_READ,
+ G_TLS_THREAD_OP_READ_MESSAGE,
G_TLS_THREAD_OP_WRITE,
+ G_TLS_THREAD_OP_WRITE_MESSAGE,
G_TLS_THREAD_OP_SHUTDOWN
} GTlsThreadOperationType;
@@ -91,6 +93,7 @@ typedef struct {
GTlsThreadOperationType type;
GIOCondition io_condition;
+ GTlsOperationsThreadBase *thread;
GTlsConnectionBase *connection; /* FIXME: threadsafety nightmare, not OK */
/* Input */
@@ -127,17 +130,19 @@ static GParamSpec *obj_properties[LAST_PROP];
G_DEFINE_TYPE_WITH_PRIVATE (GTlsOperationsThreadBase, g_tls_operations_thread_base, G_TYPE_OBJECT)
static GTlsThreadOperation *
-g_tls_thread_operation_new (GTlsThreadOperationType type,
- GTlsConnectionBase *connection,
- void *data,
- gsize size,
- gint64 timeout,
- GCancellable *cancellable)
+g_tls_thread_operation_new (GTlsThreadOperationType type,
+ GTlsOperationsThreadBase *thread,
+ GTlsConnectionBase *connection,
+ void *data,
+ gsize size,
+ gint64 timeout,
+ GCancellable *cancellable)
{
GTlsThreadOperation *op;
op = g_new0 (GTlsThreadOperation, 1);
op->type = type;
+ op->thread = thread; /* FIXME: use a weak ref? */
op->connection = g_object_ref (connection);
op->data = data;
op->size = size;
@@ -150,9 +155,13 @@ g_tls_thread_operation_new (GTlsThreadOperationType type,
switch (type)
{
case G_TLS_THREAD_OP_READ:
+ /* fallthrough */
+ case G_TLS_THREAD_OP_READ_MESSAGE:
op->io_condition = G_IO_IN;
break;
case G_TLS_THREAD_OP_WRITE:
+ /* fallthough */
+ case G_TLS_THREAD_OP_WRITE_MESSAGE:
op->io_condition = G_IO_OUT;
break;
/* FIXME: more pls */
@@ -198,6 +207,14 @@ wait_for_op_completion (GTlsThreadOperation *op)
g_mutex_unlock (&op->finished_mutex);
}
+GTlsConnectionBase *
+g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase *self)
+{
+ GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+ return priv->connection;
+}
+
GTlsConnectionBaseStatus
g_tls_operations_thread_base_read (GTlsOperationsThreadBase *self,
void *buffer,
@@ -212,6 +229,7 @@ g_tls_operations_thread_base_read (GTlsOperationsThreadBase *self,
GTlsConnectionBaseStatus result;
op = g_tls_thread_operation_new (G_TLS_THREAD_OP_READ,
+ self,
priv->connection,
buffer, size, timeout,
cancellable);
@@ -235,6 +253,19 @@ g_tls_operations_thread_base_read (GTlsOperationsThreadBase *self,
return result;
}
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_read_message (GTlsOperationsThreadBase *self,
+ GInputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* FIXME: not enough room in GTlsThreadOperation to store the parameters */
+ g_assert_not_reached ();
+}
+
GTlsConnectionBaseStatus
g_tls_operations_thread_base_write (GTlsOperationsThreadBase *self,
const void *buffer,
@@ -249,6 +280,7 @@ g_tls_operations_thread_base_write (GTlsOperationsThreadBase *self,
GTlsConnectionBaseStatus result;
op = g_tls_thread_operation_new (G_TLS_THREAD_OP_WRITE,
+ self,
priv->connection,
(void *)buffer, size, timeout,
cancellable);
@@ -272,6 +304,19 @@ g_tls_operations_thread_base_write (GTlsOperationsThreadBase *self,
return result;
}
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_write_message (GTlsOperationsThreadBase *self,
+ GOutputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* FIXME: not enough room in GTlsThreadOperation to store the parameters */
+ g_assert_not_reached ();
+}
+
typedef struct {
GSource source;
@@ -580,18 +625,26 @@ process_op (GAsyncQueue *queue,
{
/* FIXME: handle all op types, including handshake and directional closes */
case G_TLS_THREAD_OP_READ:
- op->result = G_TLS_CONNECTION_BASE_GET_CLASS (op->connection)->read_fn (op->connection,
- op->data, op->size,
- &op->count,
- op->cancellable,
- &op->error);
+ op->result = G_TLS_OPERATIONS_THREAD_BASE_GET_CLASS (op->thread)->read_fn (op->thread,
+ op->data, op->size,
+ &op->count,
+ op->cancellable,
+ &op->error);
+ break;
+ case G_TLS_THREAD_OP_READ_MESSAGE:
+ g_assert (G_TLS_OPERATIONS_THREAD_BASE_GET_CLASS (op->thread)->read_message_fn);
+ g_assert_not_reached (); /* FIXME */
break;
case G_TLS_THREAD_OP_WRITE:
- op->result = G_TLS_CONNECTION_BASE_GET_CLASS (op->connection)->write_fn (op->connection,
- op->data, op->size,
- &op->count,
- op->cancellable,
- &op->error);
+ op->result = G_TLS_OPERATIONS_THREAD_BASE_GET_CLASS (op->thread)->write_fn (op->thread,
+ op->data, op->size,
+ &op->count,
+ op->cancellable,
+ &op->error);
+ break;
+ case G_TLS_THREAD_OP_WRITE_MESSAGE:
+ g_assert (G_TLS_OPERATIONS_THREAD_BASE_GET_CLASS (op->thread)->write_message_fn);
+ g_assert_not_reached (); /* FIXME */
break;
case G_TLS_THREAD_OP_SHUTDOWN:
g_assert_not_reached ();
diff --git a/tls/base/gtlsoperationsthread-base.h b/tls/base/gtlsoperationsthread-base.h
index f567b14..6962c85 100644
--- a/tls/base/gtlsoperationsthread-base.h
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -37,22 +37,70 @@ G_DECLARE_DERIVABLE_TYPE (GTlsOperationsThreadBase, g_tls_operations_thread_base
struct _GTlsOperationsThreadBaseClass
{
GObjectClass parent_class;
+
+ /* FIXME: must remove timeout parameters from all vfuncs, including handshake vfuncs */
+ GTlsConnectionBaseStatus (*read_fn) (GTlsOperationsThreadBase *self,
+ void *buffer,
+ gsize size,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error);
+ GTlsConnectionBaseStatus (*read_message_fn) (GTlsOperationsThreadBase *self,
+ GInputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error);
+
+ GTlsConnectionBaseStatus (*write_fn) (GTlsOperationsThreadBase *self,
+ const void *buffer,
+ gsize size,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error);
+ GTlsConnectionBaseStatus (*write_message_fn) (GTlsOperationsThreadBase *self,
+ GOutputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error);
};
-GTlsConnectionBaseStatus g_tls_operations_thread_base_read (GTlsOperationsThreadBase *self,
- void *buffer,
- gsize size,
- gint64 timeout,
- gssize *nread,
- GCancellable *cancellable,
- GError **error);
-
-GTlsConnectionBaseStatus g_tls_operations_thread_base_write (GTlsOperationsThreadBase *self,
- const void *buffer,
- gsize size,
- gint64 timeout,
- gssize *nwrote,
- GCancellable *cancellable,
- GError **error);
+/* FIXME: remove? */
+GTlsConnectionBase *g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase *self);
+
+GTlsConnectionBaseStatus g_tls_operations_thread_base_read (GTlsOperationsThreadBase *self,
+ void *buffer,
+ gsize size,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable
*cancellable,
+ GError **error);
+
+GTlsConnectionBaseStatus g_tls_operations_thread_base_read_message (GTlsOperationsThreadBase *self,
+ GInputVector *vectors,
+ guint
num_vectors,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable
*cancellable,
+ GError **error);
+
+GTlsConnectionBaseStatus g_tls_operations_thread_base_write (GTlsOperationsThreadBase *self,
+ const void *buffer,
+ gsize size,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable
*cancellable,
+ GError **error);
+
+GTlsConnectionBaseStatus g_tls_operations_thread_base_write_message (GTlsOperationsThreadBase *self,
+ GOutputVector *vectors,
+ guint
num_vectors,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable
*cancellable,
+ GError **error);
G_END_DECLS
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 0efb4b7..4852aec 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -74,7 +74,7 @@ static gnutls_priority_t priority;
typedef struct
{
gnutls_certificate_credentials_t creds;
- gnutls_session_t session;
+ gnutls_session_t session; /* FIXME: should be used only by GTlsOperationsThreadGnutls */
gchar *interaction_id;
GCancellable *cancellable;
} GTlsConnectionGnutlsPrivate;
@@ -313,6 +313,7 @@ g_tls_connection_gnutls_handshake_thread_get_certificate (GTlsConnectionGnutls
}
}
+// FIXME: remove
static GTlsConnectionBaseStatus
end_gnutls_io (GTlsConnectionGnutls *gnutls,
GIOCondition direction,
@@ -466,6 +467,8 @@ end_gnutls_io (GTlsConnectionGnutls *gnutls,
return G_TLS_CONNECTION_BASE_ERROR;
}
+// FIXME: remove
+
#define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable) \
g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (gnutls), \
direction, timeout, cancellable); \
@@ -923,172 +926,6 @@ g_tls_connection_gnutls_is_session_resumed (GTlsConnectionBase *tls)
return gnutls_session_is_resumed (priv->session);
}
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_read (GTlsConnectionBase *tls,
- void *buffer,
- gsize size,
- gssize *nread,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GTlsConnectionBaseStatus status;
- gssize ret;
-
- BEGIN_GNUTLS_IO (gnutls, G_IO_IN, 0, cancellable);
- ret = gnutls_record_recv (priv->session, buffer, size);
- END_GNUTLS_IO (gnutls, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
-
- *nread = MAX (ret, 0);
- return status;
-}
-
-static gsize
-input_vectors_from_gnutls_datum_t (GInputVector *vectors,
- guint num_vectors,
- const gnutls_datum_t *datum)
-{
- guint i;
- gsize total = 0;
-
- /* Copy into the receive vectors. */
- for (i = 0; i < num_vectors && total < datum->size; i++)
- {
- gsize count;
- GInputVector *vec = &vectors[i];
-
- count = MIN (vec->size, datum->size - total);
-
- memcpy (vec->buffer, datum->data + total, count);
- total += count;
- }
-
- g_assert (total <= datum->size);
-
- return total;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_read_message (GTlsConnectionBase *tls,
- GInputVector *vectors,
- guint num_vectors,
- gint64 timeout,
- gssize *nread,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GTlsConnectionBaseStatus status;
- gssize ret;
- gnutls_packet_t packet = { 0, };
-
- BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout, cancellable);
-
- /* Receive the entire datagram (zero-copy). */
- ret = gnutls_record_recv_packet (priv->session, &packet);
-
- if (ret > 0)
- {
- gnutls_datum_t data = { 0, };
-
- gnutls_packet_get (packet, &data, NULL);
- ret = input_vectors_from_gnutls_datum_t (vectors, num_vectors, &data);
- gnutls_packet_deinit (packet);
- }
-
- END_GNUTLS_IO (gnutls, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
-
- *nread = MAX (ret, 0);
- return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_write (GTlsConnectionBase *tls,
- const void *buffer,
- gsize size,
- gssize *nwrote,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GTlsConnectionBaseStatus status;
- gssize ret;
-
- BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, 0, cancellable);
- ret = gnutls_record_send (priv->session, buffer, size);
- END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
-
- *nwrote = MAX (ret, 0);
- return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_gnutls_write_message (GTlsConnectionBase *tls,
- GOutputVector *vectors,
- guint num_vectors,
- gint64 timeout,
- gssize *nwrote,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GTlsConnectionBaseStatus status;
- gssize ret;
- guint i;
- gsize total_message_size;
-
- /* Calculate the total message size and check it’s not too big. */
- for (i = 0, total_message_size = 0; i < num_vectors; i++)
- total_message_size += vectors[i].size;
-
- if (g_tls_connection_base_is_dtls (tls) &&
- gnutls_dtls_get_data_mtu (priv->session) < total_message_size)
- {
- char *message;
- guint mtu = gnutls_dtls_get_data_mtu (priv->session);
-
- ret = GNUTLS_E_LARGE_PACKET;
- message = g_strdup_printf("%s %s",
- ngettext ("Message of size %lu byte is too large for DTLS connection",
- "Message of size %lu bytes is too large for DTLS connection",
total_message_size),
- ngettext ("(maximum is %u byte)", "(maximum is %u bytes)", mtu));
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
- message,
- total_message_size,
- mtu);
- g_free (message);
-
- return G_TLS_CONNECTION_BASE_ERROR;
- }
-
- /* Queue up the data from all the vectors. */
- gnutls_record_cork (priv->session);
-
- for (i = 0; i < num_vectors; i++)
- {
- ret = gnutls_record_send (priv->session,
- vectors[i].buffer, vectors[i].size);
-
- if (ret < 0 || ret < vectors[i].size)
- {
- /* Uncork to restore state, then bail. The peer will receive a
- * truncated datagram. */
- break;
- }
- }
-
- BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout, cancellable);
- ret = gnutls_record_uncork (priv->session, 0 /* flags */);
- END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
-
- *nwrote = MAX (ret, 0);
- return status;
-}
-
static GTlsConnectionBaseStatus
g_tls_connection_gnutls_close (GTlsConnectionBase *tls,
gint64 timeout,
@@ -1146,10 +983,6 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
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;
- base_class->read_fn = g_tls_connection_gnutls_read;
- base_class->read_message_fn = g_tls_connection_gnutls_read_message;
- base_class->write_fn = g_tls_connection_gnutls_write;
- base_class->write_message_fn = g_tls_connection_gnutls_write_message;
base_class->close_fn = g_tls_connection_gnutls_close;
initialize_gnutls_priority ();
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index 55ae5ee..3e9fa9b 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -43,6 +43,7 @@ struct _GTlsConnectionGnutlsClass
gnutls_certificate_credentials_t g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
+/* FIXME: remove to ensure threadsafety */
gnutls_session_t g_tls_connection_gnutls_get_session (GTlsConnectionGnutls *connection);
void g_tls_connection_gnutls_handshake_thread_get_certificate (GTlsConnectionGnutls *gnutls,
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
index d48a356..81b118b 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.c
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -2,6 +2,8 @@
/*
* GIO - GLib Input, Output and Streaming Library
*
+ * Copyright 2009 Red Hat, Inc
+ * Copyright 2015, 2016 Collabora, Ltd.
* Copyright 2019 Igalia S.L.
* Copyright 2019 Metrological Group B.V.
*
@@ -25,15 +27,360 @@
#include "config.h"
-#include "gtlsconnection-gnutls.h"
+#include <glib/gi18n-lib.h>
+#include <gnutls/dtls.h>
+
#include "gtlsoperationsthread-gnutls.h"
+#include "gtlsconnection-gnutls.h"
struct _GTlsOperationsThreadGnutls {
GTlsOperationsThreadBase parent_instance;
+
+ gnutls_session_t session;
};
G_DEFINE_TYPE (GTlsOperationsThreadGnutls, g_tls_operations_thread_gnutls, G_TYPE_TLS_OPERATIONS_THREAD_BASE)
+static GTlsConnectionBaseStatus
+end_gnutls_io (GTlsOperationsThreadGnutls *self,
+ GIOCondition direction,
+ int ret,
+ GError **error,
+ const char *err_prefix)
+{
+ GTlsConnectionBase *tls;
+ GTlsConnectionBaseStatus status;
+ gboolean handshaking;
+ gboolean ever_handshaked;
+ GError *my_error = NULL;
+
+ /* We intentionally do not check for GNUTLS_E_INTERRUPTED here
+ * Instead, the caller may poll for the source to become ready again.
+ * (Note that GTlsOutputStreamGnutls and GTlsInputStreamGnutls inherit
+ * from GPollableOutputStream and GPollableInputStream, respectively.)
+ * See also the comment in set_gnutls_error().
+ */
+ if (ret == GNUTLS_E_AGAIN ||
+ ret == GNUTLS_E_WARNING_ALERT_RECEIVED)
+ return G_TLS_CONNECTION_BASE_TRY_AGAIN;
+
+ tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+
+ status = g_tls_connection_base_pop_io (tls, direction, ret >= 0, &my_error);
+ if (status == G_TLS_CONNECTION_BASE_OK ||
+ status == G_TLS_CONNECTION_BASE_WOULD_BLOCK ||
+ status == G_TLS_CONNECTION_BASE_TIMED_OUT)
+ {
+ if (my_error)
+ g_propagate_error (error, my_error);
+ return status;
+ }
+
+ g_assert (status == G_TLS_CONNECTION_BASE_ERROR);
+
+ handshaking = g_tls_connection_base_is_handshaking (tls);
+ ever_handshaked = g_tls_connection_base_ever_handshaked (tls);
+
+ if (handshaking && !ever_handshaked)
+ {
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
+ g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE))
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+ _("Peer failed to perform TLS handshake: %s"), my_error->message);
+ g_clear_error (&my_error);
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
+ status == GNUTLS_E_DECRYPTION_FAILED ||
+ status == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
+ {
+ g_clear_error (&my_error);
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+ _("Peer failed to perform TLS handshake: %s"), gnutls_strerror (ret));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+ }
+
+ if (ret == GNUTLS_E_REHANDSHAKE)
+ return G_TLS_CONNECTION_BASE_REHANDSHAKE;
+
+ if (ret == GNUTLS_E_PREMATURE_TERMINATION)
+ {
+ if (handshaking && !ever_handshaked)
+ {
+ g_clear_error (&my_error);
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+ _("Peer failed to perform TLS handshake: %s"), gnutls_strerror (ret));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (g_tls_connection_get_require_close_notify (G_TLS_CONNECTION (tls)))
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_EOF,
+ _("TLS connection closed unexpectedly"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ return G_TLS_CONNECTION_BASE_OK;
+ }
+
+ if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND
+#ifdef GNUTLS_E_CERTIFICATE_REQUIRED
+ || ret == GNUTLS_E_CERTIFICATE_REQUIRED /* Added in GnuTLS 3.6.7 */
+#endif
+ )
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+ _("TLS connection peer did not send a certificate"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (ret == GNUTLS_E_CERTIFICATE_ERROR)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Unacceptable TLS certificate"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
+ {
+ g_clear_error (&my_error);
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Peer sent fatal TLS alert: %s"),
+ gnutls_alert_get_name (gnutls_alert_get (self->session)));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (ret == GNUTLS_E_INAPPROPRIATE_FALLBACK)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR,
+ G_TLS_ERROR_INAPPROPRIATE_FALLBACK,
+ _("Protocol version downgrade attack detected"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (ret == GNUTLS_E_LARGE_PACKET)
+ {
+ guint mtu = gnutls_dtls_get_data_mtu (self->session);
+ g_clear_error (&my_error);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
+ ngettext ("Message is too large for DTLS connection; maximum is %u byte",
+ "Message is too large for DTLS connection; maximum is %u bytes", mtu), mtu);
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (ret == GNUTLS_E_TIMEDOUT)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ _("The operation timed out"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (error && my_error)
+ g_propagate_error (error, my_error);
+
+ if (error && !*error)
+ {
+ *error = g_error_new (G_TLS_ERROR, G_TLS_ERROR_MISC, "%s: %s",
+ err_prefix, gnutls_strerror (ret));
+ }
+
+ return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+#define BEGIN_GNUTLS_IO(self, direction, timeout, cancellable) \
+ g_tls_connection_base_push_io (g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE
(self)), \
+ direction, timeout, cancellable); \
+ do {
+
+#define END_GNUTLS_IO(self, direction, ret, status, errmsg, err) \
+ status = end_gnutls_io (self, direction, ret, err, errmsg); \
+ } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_read (GTlsOperationsThreadBase *base,
+ void *buffer,
+ gsize size,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+ GTlsConnectionBaseStatus status;
+ gssize ret;
+
+ BEGIN_GNUTLS_IO (self, G_IO_IN, 0, cancellable);
+ ret = gnutls_record_recv (self->session, buffer, size);
+ END_GNUTLS_IO (self, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
+
+ *nread = MAX (ret, 0);
+ return status;
+}
+
+static gsize
+input_vectors_from_gnutls_datum_t (GInputVector *vectors,
+ guint num_vectors,
+ const gnutls_datum_t *datum)
+{
+ guint i;
+ gsize total = 0;
+
+ /* Copy into the receive vectors. */
+ for (i = 0; i < num_vectors && total < datum->size; i++)
+ {
+ gsize count;
+ GInputVector *vec = &vectors[i];
+
+ count = MIN (vec->size, datum->size - total);
+
+ memcpy (vec->buffer, datum->data + total, count);
+ total += count;
+ }
+
+ g_assert (total <= datum->size);
+
+ return total;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_read_message (GTlsOperationsThreadBase *base,
+ GInputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+ GTlsConnectionBaseStatus status;
+ gssize ret;
+ gnutls_packet_t packet = { 0, };
+
+ BEGIN_GNUTLS_IO (self, G_IO_IN, timeout, cancellable);
+
+ /* Receive the entire datagram (zero-copy). */
+ ret = gnutls_record_recv_packet (self->session, &packet);
+
+ if (ret > 0)
+ {
+ gnutls_datum_t data = { 0, };
+
+ gnutls_packet_get (packet, &data, NULL);
+ ret = input_vectors_from_gnutls_datum_t (vectors, num_vectors, &data);
+ gnutls_packet_deinit (packet);
+ }
+
+ END_GNUTLS_IO (self, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
+
+ *nread = MAX (ret, 0);
+ return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_write (GTlsOperationsThreadBase *base,
+ const void *buffer,
+ gsize size,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+ GTlsConnectionBaseStatus status;
+ gssize ret;
+
+ BEGIN_GNUTLS_IO (self, G_IO_OUT, 0, cancellable);
+ ret = gnutls_record_send (self->session, buffer, size);
+ END_GNUTLS_IO (self, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
+
+ *nwrote = MAX (ret, 0);
+ return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_write_message (GTlsOperationsThreadBase *base,
+ GOutputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+ GTlsConnectionBase *connection;
+ GTlsConnectionBaseStatus status;
+ gssize ret;
+ guint i;
+ gsize total_message_size;
+
+ connection = g_tls_operations_thread_base_get_connection (base);
+
+ /* Calculate the total message size and check it’s not too big. */
+ for (i = 0, total_message_size = 0; i < num_vectors; i++)
+ total_message_size += vectors[i].size;
+
+ if (g_tls_connection_base_is_dtls (connection) &&
+ gnutls_dtls_get_data_mtu (self->session) < total_message_size)
+ {
+ char *message;
+ guint mtu = gnutls_dtls_get_data_mtu (self->session);
+
+ ret = GNUTLS_E_LARGE_PACKET;
+ message = g_strdup_printf("%s %s",
+ ngettext ("Message of size %lu byte is too large for DTLS connection",
+ "Message of size %lu bytes is too large for DTLS connection",
total_message_size),
+ ngettext ("(maximum is %u byte)", "(maximum is %u bytes)", mtu));
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
+ message,
+ total_message_size,
+ mtu);
+ g_free (message);
+
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ /* Queue up the data from all the vectors. */
+ gnutls_record_cork (self->session);
+
+ for (i = 0; i < num_vectors; i++)
+ {
+ ret = gnutls_record_send (self->session,
+ vectors[i].buffer, vectors[i].size);
+
+ if (ret < 0 || ret < vectors[i].size)
+ {
+ /* Uncork to restore state, then bail. The peer will receive a
+ * truncated datagram. */
+ break;
+ }
+ }
+
+ BEGIN_GNUTLS_IO (self, G_IO_OUT, timeout, cancellable);
+ ret = gnutls_record_uncork (self->session, 0 /* flags */);
+ END_GNUTLS_IO (self, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
+
+ *nwrote = MAX (ret, 0);
+ return status;
+}
+
+static void
+g_tls_operations_thread_gnutls_constructed (GObject *object)
+{
+ GTlsOperationsThreadGnutls *self = G_TYPE_TLS_OPERATIONS_THREAD_GNUTLS (object);
+ GTlsConnectionBase *tls;
+
+ G_OBJECT_CLASS (g_tls_operations_thread_gnutls_parent_class)->constructed (object);
+
+ tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+ self->session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls));
+}
+
static void
g_tls_operations_thread_gnutls_init (GTlsOperationsThreadGnutls *self)
{
@@ -42,6 +389,15 @@ g_tls_operations_thread_gnutls_init (GTlsOperationsThreadGnutls *self)
static void
g_tls_operations_thread_gnutls_class_init (GTlsOperationsThreadGnutlsClass *klass)
{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsOperationsThreadBaseClass *base_class = G_TLS_OPERATIONS_THREAD_BASE_CLASS (klass);
+
+ gobject_class->constructed = g_tls_operations_thread_gnutls_constructed;
+
+ base_class->read_fn = g_tls_operations_thread_gnutls_read;
+ base_class->read_message_fn = g_tls_operations_thread_gnutls_read_message;
+ base_class->write_fn = g_tls_operations_thread_gnutls_write;
+ base_class->write_message_fn = g_tls_operations_thread_gnutls_write_message;
}
GTlsOperationsThreadBase *
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.h b/tls/gnutls/gtlsoperationsthread-gnutls.h
index 4bffd58..8a0acad 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.h
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.h
@@ -24,6 +24,9 @@
#pragma once
+#include <gio/gio.h>
+
+#include "gtlsconnection-gnutls.h"
#include "gtlsoperationsthread-base.h"
G_BEGIN_DECLS
diff --git a/tls/openssl/gtlsbio.c b/tls/openssl/gtlsbio.c
index b138432..e19edd2 100644
--- a/tls/openssl/gtlsbio.c
+++ b/tls/openssl/gtlsbio.c
@@ -35,8 +35,6 @@ typedef struct {
gboolean write_blocking;
GError **read_error;
GError **write_error;
- GMainContext *context;
- GMainLoop *loop;
} GTlsBio;
static void
@@ -45,8 +43,6 @@ free_gbio (gpointer user_data)
GTlsBio *bio = (GTlsBio *)user_data;
g_object_unref (bio->io_stream);
- g_main_context_unref (bio->context);
- g_main_loop_unref (bio->loop);
g_free (bio);
}
@@ -294,8 +290,6 @@ g_tls_bio_new (GIOStream *io_stream)
gbio = g_new0 (GTlsBio, 1);
gbio->io_stream = g_object_ref (io_stream);
- gbio->context = g_main_context_new ();
- gbio->loop = g_main_loop_new (gbio->context, FALSE);
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
ret->ptr = gbio;
@@ -403,49 +397,3 @@ g_tls_bio_set_write_error (BIO *bio,
#endif
gbio->write_error = error;
}
-
-static gboolean
-on_source_ready (GObject *pollable_stream,
- gpointer user_data)
-{
- GMainLoop *loop = user_data;
-
- g_main_loop_quit (loop);
-
- return G_SOURCE_REMOVE;
-}
-
-void
-g_tls_bio_wait_available (BIO *bio,
- GIOCondition condition,
- GCancellable *cancellable)
-{
- GTlsBio *gbio;
- GSource *source;
-
- g_return_if_fail (bio);
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
- gbio = (GTlsBio *)bio->ptr;
-#else
- gbio = BIO_get_data (bio);
-#endif
-
- g_main_context_push_thread_default (gbio->context);
-
- if (condition & G_IO_IN)
- source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream
(gbio->io_stream)),
- cancellable);
- else
- source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream
(gbio->io_stream)),
- cancellable);
-
- g_source_set_callback (source, (GSourceFunc)on_source_ready, gbio->loop, NULL);
- g_source_attach (source, gbio->context);
-
- g_main_loop_run (gbio->loop);
- g_main_context_pop_thread_default (gbio->context);
-
- g_source_destroy (source);
- g_source_unref (source);
-}
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index fc4603c..d356433 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -42,7 +42,6 @@
typedef struct _GTlsConnectionOpensslPrivate
{
BIO *bio;
- GMutex ssl_mutex;
gboolean shutting_down;
} GTlsConnectionOpensslPrivate;
@@ -54,19 +53,6 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionOpenssl, g_tls_connection_openss
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
g_tls_connection_openssl_initable_iface_init))
-static void
-g_tls_connection_openssl_finalize (GObject *object)
-{
- GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (object);
- GTlsConnectionOpensslPrivate *priv;
-
- priv = g_tls_connection_openssl_get_instance_private (openssl);
-
- g_mutex_clear (&priv->ssl_mutex);
-
- G_OBJECT_CLASS (g_tls_connection_openssl_parent_class)->finalize (object);
-}
-
static GTlsOperationsThreadBase *
g_tls_connection_openssl_create_op_thread (GTlsConnectionBase *tls)
{
@@ -352,8 +338,6 @@ g_tls_connection_openssl_push_io (GTlsConnectionBase *tls,
g_clear_error (error);
g_tls_bio_set_write_error (priv->bio, error);
}
-
- g_mutex_lock (&priv->ssl_mutex);
}
static GTlsConnectionBaseStatus
@@ -367,8 +351,6 @@ g_tls_connection_openssl_pop_io (GTlsConnectionBase *tls,
priv = g_tls_connection_openssl_get_instance_private (openssl);
- g_mutex_unlock (&priv->ssl_mutex);
-
if (direction & G_IO_IN)
g_tls_bio_set_read_cancellable (priv->bio, NULL);
@@ -379,95 +361,6 @@ g_tls_connection_openssl_pop_io (GTlsConnectionBase *tls,
success, error);
}
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_read (GTlsConnectionBase *tls,
- void *buffer,
- gsize size,
- gssize *nread,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
- GTlsConnectionOpensslPrivate *priv;
- GTlsConnectionBaseStatus status;
- SSL *ssl;
- gssize ret;
-
- priv = g_tls_connection_openssl_get_instance_private (openssl);
-
- ssl = g_tls_connection_openssl_get_ssl (openssl);
-
- /* FIXME: revert back to use BEGIN/END_OPENSSL_IO once we move all the ssl
- * operations into a worker thread
- */
- while (TRUE)
- {
- char error_str[256];
-
- /* We want to always be non blocking here to avoid deadlocks */
- g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
- G_IO_IN, 0, cancellable);
-
- ret = SSL_read (ssl, buffer, size);
-
- ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
- status = end_openssl_io (openssl, G_IO_IN, ret, FALSE, error,
- _("Error reading data from TLS socket"), error_str);
-
- if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
- break;
-
- /* Wait for the socket to be available again to avoid an infinite loop */
- g_tls_bio_wait_available (priv->bio, G_IO_IN, cancellable);
- }
-
- *nread = MAX (ret, 0);
- return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_write (GTlsConnectionBase *tls,
- const void *buffer,
- gsize size,
- gssize *nwrote,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
- GTlsConnectionOpensslPrivate *priv;
- GTlsConnectionBaseStatus status;
- SSL *ssl;
- gssize ret;
-
- priv = g_tls_connection_openssl_get_instance_private (openssl);
-
- ssl = g_tls_connection_openssl_get_ssl (openssl);
-
- while (TRUE)
- {
- char error_str[256];
-
- /* We want to always be non blocking here to avoid deadlocks */
- g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
- G_IO_OUT, 0, cancellable);
-
- ret = SSL_write (ssl, buffer, size);
-
- ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
- status = end_openssl_io (openssl, G_IO_OUT, ret, FALSE, error,
- _("Error writing data to TLS socket"), error_str);
-
- if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
- break;
-
- /* Wait for the socket to be available again to avoid an infinite loop */
- g_tls_bio_wait_available (priv->bio, G_IO_OUT, cancellable);
- }
-
- *nwrote = MAX (ret, 0);
- return status;
-}
-
static GTlsConnectionBaseStatus
g_tls_connection_openssl_close (GTlsConnectionBase *tls,
gint64 timeout,
@@ -497,14 +390,20 @@ g_tls_connection_openssl_close (GTlsConnectionBase *tls,
return status;
}
+/* FIXME: remove */
+gboolean
+g_tls_connection_openssl_get_shutting_down (GTlsConnectionOpenssl *openssl)
+{
+ GTlsConnectionOpensslPrivate *priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ return priv->shutting_down;
+}
+
static void
g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
- object_class->finalize = g_tls_connection_openssl_finalize;
-
base_class->create_op_thread = g_tls_connection_openssl_create_op_thread;
base_class->handshake_thread_safe_renegotiation_status =
g_tls_connection_openssl_handshake_thread_safe_renegotiation_status;
base_class->handshake_thread_request_rehandshake =
g_tls_connection_openssl_handshake_thread_request_rehandshake;
@@ -512,8 +411,6 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
base_class->retrieve_peer_certificate =
g_tls_connection_openssl_retrieve_peer_certificate;
base_class->push_io = g_tls_connection_openssl_push_io;
base_class->pop_io = g_tls_connection_openssl_pop_io;
- base_class->read_fn = g_tls_connection_openssl_read;
- base_class->write_fn = g_tls_connection_openssl_write;
base_class->close_fn = g_tls_connection_openssl_close;
}
@@ -561,11 +458,6 @@ g_tls_connection_openssl_initable_iface_init (GInitableIface *iface)
static void
g_tls_connection_openssl_init (GTlsConnectionOpenssl *openssl)
{
- GTlsConnectionOpensslPrivate *priv;
-
- priv = g_tls_connection_openssl_get_instance_private (openssl);
-
- g_mutex_init (&priv->ssl_mutex);
}
SSL *
diff --git a/tls/openssl/gtlsconnection-openssl.h b/tls/openssl/gtlsconnection-openssl.h
index 7b85fdc..c5aca9c 100644
--- a/tls/openssl/gtlsconnection-openssl.h
+++ b/tls/openssl/gtlsconnection-openssl.h
@@ -47,4 +47,7 @@ SSL *g_tls_connection_openssl_get_ssl (GTlsConnectionOpenssl *connection);
GTlsConnectionOpenssl *g_tls_connection_openssl_get_connection_from_ssl (SSL *ssl);
+/* FIXME: remove */
+gboolean g_tls_connection_openssl_get_shutting_down (GTlsConnectionOpenssl *connection);
+
G_END_DECLS
diff --git a/tls/openssl/gtlsoperationsthread-openssl.c b/tls/openssl/gtlsoperationsthread-openssl.c
index 22ae78c..43f4ca4 100644
--- a/tls/openssl/gtlsoperationsthread-openssl.c
+++ b/tls/openssl/gtlsoperationsthread-openssl.c
@@ -2,6 +2,7 @@
/*
* GIO - GLib Input, Output and Streaming Library
*
+ * Copyright 2015 NICE s.r.l.
* Copyright 2019 Igalia S.L.
* Copyright 2019 Metrological Group B.V.
*
@@ -25,15 +26,238 @@
#include "config.h"
+#include <glib/gi18n-lib.h>
+
#include "gtlsconnection-openssl.h"
#include "gtlsoperationsthread-openssl.h"
struct _GTlsOperationsThreadOpenssl {
GTlsOperationsThreadBase parent_instance;
+
+ SSL *ssl;
};
G_DEFINE_TYPE (GTlsOperationsThreadOpenssl, g_tls_operations_thread_openssl,
G_TYPE_TLS_OPERATIONS_THREAD_BASE)
+static GTlsConnectionBaseStatus
+end_openssl_io (GTlsOperationsThreadOpenssl *self,
+ GIOCondition direction,
+ int ret,
+ gboolean blocking,
+ GError **error,
+ const char *err_prefix,
+ const char *err_str)
+{
+ GTlsConnectionBase *tls;
+ int err_code, err, err_lib, reason;
+ GError *my_error = NULL;
+ GTlsConnectionBaseStatus status;
+
+ tls = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+
+ err_code = SSL_get_error (self->ssl, ret);
+
+ status = g_tls_connection_base_pop_io (tls, direction, ret > 0, &my_error);
+
+ if ((err_code == SSL_ERROR_WANT_READ ||
+ err_code == SSL_ERROR_WANT_WRITE) &&
+ blocking)
+ {
+ if (my_error)
+ g_error_free (my_error);
+ return G_TLS_CONNECTION_BASE_TRY_AGAIN;
+ }
+
+ if (err_code == SSL_ERROR_ZERO_RETURN)
+ return G_TLS_CONNECTION_BASE_OK;
+
+ if (status == G_TLS_CONNECTION_BASE_OK ||
+ status == G_TLS_CONNECTION_BASE_WOULD_BLOCK ||
+ status == G_TLS_CONNECTION_BASE_TIMED_OUT)
+ {
+ if (my_error)
+ g_propagate_error (error, my_error);
+ return status;
+ }
+
+ /* This case is documented that it may happen and that is perfectly fine */
+ if (err_code == SSL_ERROR_SYSCALL &&
+ ((g_tls_connection_openssl_get_shutting_down (G_TLS_CONNECTION_OPENSSL (tls)) && !my_error) ||
+ g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE)))
+ {
+ g_clear_error (&my_error);
+ return G_TLS_CONNECTION_BASE_OK;
+ }
+
+ err = ERR_get_error ();
+ err_lib = ERR_GET_LIB (err);
+ reason = ERR_GET_REASON (err);
+
+ if (g_tls_connection_base_is_handshaking (tls) && !g_tls_connection_base_ever_handshaked (tls))
+ {
+ if (reason == SSL_R_BAD_PACKET_LENGTH ||
+ reason == SSL_R_UNKNOWN_ALERT_TYPE ||
+ reason == SSL_R_DECRYPTION_FAILED ||
+ reason == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC ||
+ reason == SSL_R_BAD_PROTOCOL_VERSION_NUMBER ||
+ reason == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE ||
+ reason == SSL_R_UNKNOWN_PROTOCOL)
+ {
+ g_clear_error (&my_error);
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+ _("Peer failed to perform TLS handshake: %s"), ERR_reason_error_string (err));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+ }
+
+#ifdef SSL_R_SHUTDOWN_WHILE_IN_INIT
+ /* XXX: this error happens on ubuntu when shutting down the connection, it
+ * seems to be a bug in a specific version of openssl, so let's handle it
+ * gracefully
+ */
+ if (reason == SSL_R_SHUTDOWN_WHILE_IN_INIT)
+ {
+ g_clear_error (&my_error);
+ return G_TLS_CONNECTION_BASE_OK;
+ }
+#endif
+
+ if (reason == SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE
+#ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
+ || reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
+#endif
+ )
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+ _("TLS connection peer did not send a certificate"));
+ return status;
+ }
+
+ if (reason == SSL_R_CERTIFICATE_VERIFY_FAILED)
+ {
+ g_clear_error (&my_error);
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Unacceptable TLS certificate"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (reason == SSL_R_TLSV1_ALERT_UNKNOWN_CA)
+ {
+ g_clear_error (&my_error);
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Unacceptable TLS certificate authority"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (err_lib == ERR_LIB_RSA && reason == RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Digest too big for RSA key"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ if (my_error)
+ g_propagate_error (error, my_error);
+ else
+ /* FIXME: this is just for debug */
+ g_message ("end_openssl_io %s: %d, %d, %d", G_IS_TLS_CLIENT_CONNECTION (tls) ? "client" : "server",
err_code, err_lib, reason);
+
+ if (error && !*error)
+ *error = g_error_new (G_TLS_ERROR, G_TLS_ERROR_MISC, "%s: %s", err_prefix, err_str);
+
+ return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+#define BEGIN_OPENSSL_IO(self, direction, timeout, cancellable) \
+ do { \
+ char error_str[256]; \
+ g_tls_connection_base_push_io (g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE
(self)), \
+ direction, timeout, cancellable);
+
+#define END_OPENSSL_IO(self, direction, ret, timeout, status, errmsg, err) \
+ ERR_error_string_n (SSL_get_error (self->ssl, ret), error_str, sizeof (error_str)); \
+ status = end_openssl_io (self, direction, ret, timeout == -1, err, errmsg, error_str); \
+ } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_openssl_read (GTlsOperationsThreadBase *base,
+ void *buffer,
+ gsize size,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOperationsThreadOpenssl *self = G_TYPE_TLS_OPERATIONS_THREAD_OPENSSL (base);
+ GTlsConnectionBaseStatus status;
+ gssize ret;
+
+ BEGIN_OPENSSL_IO (self, G_IO_OUT, timeout, cancellable);
+ ret = SSL_write (self->ssl, buffer, count);
+ END_OPENSSL_IO (self, G_IO_OUT, ret, status,
+ _("Error writing data to TLS socket"), error);
+
+
+ *nread = MAX (ret, 0);
+ return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_write (GTlsConnectionBase *tls,
+ const void *buffer,
+ gsize size,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
+ GTlsConnectionOpensslPrivate *priv;
+ GTlsConnectionBaseStatus status;
+ SSL *ssl;
+ gssize ret;
+
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+
+ while (TRUE)
+ {
+ char error_str[256];
+
+ /* We want to always be non blocking here to avoid deadlocks */
+ g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl),
+ G_IO_OUT, 0, cancellable);
+
+ ret = SSL_write (ssl, buffer, size);
+
+ ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof (error_str));
+ status = end_openssl_io (openssl, G_IO_OUT, ret, FALSE, error,
+ _("Error writing data to TLS socket"), error_str);
+
+ if (status != G_TLS_CONNECTION_BASE_TRY_AGAIN)
+ break;
+
+ /* Wait for the socket to be available again to avoid an infinite loop */
+ g_tls_bio_wait_available (priv->bio, G_IO_OUT, cancellable);
+ }
+
+ *nwrote = MAX (ret, 0);
+ return status;
+}
+
+static void
+g_tls_operations_thread_openssl_constructed (GObject *object)
+{
+ GTlsOperationsThreadOpenssl *self = G_TYPE_TLS_OPERATIONS_THREAD_OPENSSL (object);
+ GTlsConnectionOpenssl *openssl;
+
+ G_OBJECT_CLASS (g_tls_operations_thread_openssl_parent_class)->constructed (object);
+
+ openssl = g_tls_operations_thread_base_get_connection (G_TLS_OPERATIONS_THREAD_BASE (self));
+ self->ssl = g_tls_connection_openssl_get_ssl (openssl);
+}
+
static void
g_tls_operations_thread_openssl_init (GTlsOperationsThreadOpenssl *self)
{
@@ -42,6 +266,13 @@ g_tls_operations_thread_openssl_init (GTlsOperationsThreadOpenssl *self)
static void
g_tls_operations_thread_openssl_class_init (GTlsOperationsThreadOpensslClass *klass)
{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsOperationsThreadBaseClass *base_class = G_TLS_OPERATIONS_THREAD_BASE_CLASS (klass);
+
+ gobject_class->constructed = g_tls_operations_thread_openssl_constructed;
+
+ base_class->read_fn = g_tls_operations_thread_openssl_read;
+ base_class->write_fn = g_tls_operations_thread_openssl_write;
}
GTlsOperationsThreadBase *
diff --git a/tls/openssl/gtlsoperationsthread-openssl.h b/tls/openssl/gtlsoperationsthread-openssl.h
index 318c8a2..730bf75 100644
--- a/tls/openssl/gtlsoperationsthread-openssl.h
+++ b/tls/openssl/gtlsoperationsthread-openssl.h
@@ -24,6 +24,9 @@
#pragma once
+#include <gio/gio.h>
+
+#include "gtlsconnection-openssl.h"
#include "gtlsoperationsthread-base.h"
G_BEGIN_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]