[glib-networking/mcatanzaro/tls-thread] progress
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/mcatanzaro/tls-thread] progress
- Date: Sat, 21 Dec 2019 20:38:18 +0000 (UTC)
commit feb0751798b995f3ad07f236d3cb63512ccdcfa8
Author: Michael Catanzaro <mcatanzaro gnome org>
Date: Mon Dec 16 10:22:47 2019 -0600
progress
tls/base/gtlsconnection-base.c | 170 ++++++++++++++++++++++++++++---
tls/base/gtlsconnection-base.h | 1 +
tls/base/gtlsoperationsthread-base.c | 61 ++++++++---
tls/base/gtlsoperationsthread-base.h | 14 ++-
tls/gnutls/gtlsoperationsthread-gnutls.c | 89 ++++++++++++++++
5 files changed, 304 insertions(+), 31 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 406a31d..c214342 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -54,7 +54,7 @@
* communications.
* • Implements GDtlsConnection and GDatagramBased, for DTLS and datagram
* communications.
- * • Implements GInitable for failable initialisation.
+ * • Implements GInitable for failable initialization.
*/
typedef struct
@@ -95,6 +95,7 @@ typedef struct
GTlsCertificate *peer_certificate;
GTlsCertificateFlags peer_certificate_errors;
+ /* FIXME: remove */
GMutex verify_certificate_mutex;
GCond verify_certificate_condition;
gboolean peer_certificate_accepted;
@@ -124,6 +125,7 @@ typedef struct
* future operations). ever_handshaked indicates that TLS has been
* successfully negotiated at some point.
*/
+ /* FIXME: remove a few of these */
gboolean need_handshake;
gboolean need_finish_handshake;
gboolean sync_handshake_in_progress;
@@ -1445,6 +1447,85 @@ g_tls_connection_base_handshake_thread_verify_certificate (GTlsConnectionBase *t
return accepted;
}
+static gboolean /* FIXME rename */
+op_thread_handshake (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+ gint64 start_time;
+
+ g_tls_log_debug (tls, "TLS handshake starts");
+
+ start_time = g_get_monotonic_time ();
+
+ priv->started_handshake = FALSE;
+ priv->missing_requested_client_certificate = FALSE;
+
+ if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+ timeout, cancellable, error))
+ {
+ g_tls_log_debug (tls, "TLS handshake failed: claiming op failed");
+ return FALSE;
+ }
+
+ g_clear_error (&priv->handshake_error);
+
+ if (priv->ever_handshaked && !priv->need_handshake)
+ {
+ GTlsConnectionBaseStatus status;
+
+ /* FIXME: no longer handshake thread */
+ if (tls_class->handshake_thread_safe_renegotiation_status (tls) !=
G_TLS_SAFE_RENEGOTIATION_SUPPORTED_BY_PEER)
+ {
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Peer does not support safe renegotiation"));
+ g_tls_log_debug (tls, "TLS handshake failed: peer does not support safe renegotiation");
+ return FALSE;
+ }
+
+ /* Adjust the timeout for the next operation in the sequence. */
+ if (timeout > 0)
+ {
+ timeout -= (g_get_monotonic_time () - start_time);
+ if (timeout <= 0)
+ timeout = 1;
+ }
+
+ /* FIXME: no longer handshake thread */
+ status = tls_class->handshake_thread_request_rehandshake (tls, timeout, cancellable, error);
+ if (status != G_TLS_CONNECTION_BASE_OK)
+ {
+ g_tls_log_debug (tls, "TLS handshake failed: %s", *error ? (*error)->message : "no error");
+ return FALSE;
+ }
+ }
+
+ /* Adjust the timeout for the next operation in the sequence. */
+ if (timeout > 0)
+ {
+ timeout -= (g_get_monotonic_time () - start_time);
+ if (timeout <= 0)
+ timeout = 1;
+ }
+
+ priv->started_handshake = TRUE;
+ g_tls_operations_thread_base_handshake (priv->thread, timeout, cancellable, error);
+ priv->need_handshake = FALSE;
+
+ if (error && *error)
+ {
+ g_tls_log_debug (tls, "TLS handshake failed: %s", (*error)->message);
+ return FALSE;
+ }
+
+ priv->ever_handshaked = TRUE;
+ g_tls_log_debug (tls, "TLS handshake succeeded");
+ return TRUE;
+}
+
static void
handshake_thread (GTask *task,
gpointer object,
@@ -1583,7 +1664,7 @@ crank_sync_handshake_context (GTlsConnectionBase *tls,
g_mutex_unlock (&priv->op_mutex);
}
-static gboolean
+static gboolean /* FIXME remove */
finish_handshake (GTlsConnectionBase *tls,
GTask *task,
GError **error)
@@ -1647,6 +1728,70 @@ finish_handshake (GTlsConnectionBase *tls,
return FALSE;
}
+static gboolean /* FIXME rename */
+finish_op_thread_handshake (GTlsConnectionBase *tls,
+ gboolean success,
+ GError **error)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+ gchar *original_negotiated_protocol;
+ GError *my_error = NULL;
+
+ g_tls_log_debug (tls, "finishing TLS handshake");
+
+ original_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
+
+ if (success)
+ {
+ if (tls_class->is_session_resumed && tls_class->is_session_resumed (tls))
+ {
+ /* Because this session was resumed, we skipped certificate
+ * verification on this handshake, so we missed our earlier
+ * chance to set peer_certificate and peer_certificate_errors.
+ * Do so here instead.
+ *
+ * The certificate has already been accepted, so we don't do
+ * anything with the result here.
+ */
+ g_mutex_lock (&priv->verify_certificate_mutex);
+ update_peer_certificate_and_compute_errors (tls);
+ priv->peer_certificate_examined = TRUE;
+ priv->peer_certificate_accepted = TRUE;
+ g_mutex_unlock (&priv->verify_certificate_mutex);
+ }
+
+ /* FIXME: Return an error from the handshake thread instead? */
+ if (priv->peer_certificate && !priv->peer_certificate_accepted)
+ {
+ g_set_error_literal (&my_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Unacceptable TLS certificate"));
+ }
+ }
+
+ if (tls_class->complete_handshake)
+ {
+ /* If we already have an error, ignore further errors. */
+ tls_class->complete_handshake (tls, &priv->negotiated_protocol, my_error ? NULL : &my_error);
+
+ if (g_strcmp0 (original_negotiated_protocol, priv->negotiated_protocol) != 0)
+ g_object_notify (G_OBJECT (tls), "negotiated-protocol");
+ }
+ g_free (original_negotiated_protocol);
+
+ if (my_error && priv->started_handshake)
+ priv->handshake_error = g_error_copy (my_error);
+
+ if (!my_error) {
+ g_tls_log_debug (tls, "TLS handshake has finished successfully");
+ return TRUE;
+ }
+
+ g_tls_log_debug (tls, "TLS handshake has finished with error: %s", my_error->message);
+ g_propagate_error (error, my_error);
+ return FALSE;
+}
+
static gboolean
g_tls_connection_base_handshake (GTlsConnection *conn,
GCancellable *cancellable,
@@ -1655,9 +1800,7 @@ g_tls_connection_base_handshake (GTlsConnection *conn,
GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
- GTask *task;
gboolean success;
- gint64 *timeout = NULL;
GError *my_error = NULL;
g_tls_log_debug (tls, "Starting synchronous TLS handshake");
@@ -1670,20 +1813,15 @@ g_tls_connection_base_handshake (GTlsConnection *conn,
if (tls_class->prepare_handshake)
tls_class->prepare_handshake (tls, priv->advertised_protocols);
- task = g_task_new (conn, cancellable, sync_handshake_thread_completed, NULL);
- g_task_set_source_tag (task, g_tls_connection_base_handshake);
- g_task_set_name (task, "[glib-networking] g_tls_connection_base_handshake");
- g_task_set_return_on_cancel (task, TRUE);
+ success = op_thread_handshake (tls, -1 /* blocking */, cancellable, error);
- timeout = g_new0 (gint64, 1);
- *timeout = -1; /* blocking */
- g_task_set_task_data (task, timeout, g_free);
+ g_mutex_lock (&priv->op_mutex);
+ priv->sync_handshake_in_progress = FALSE;
+ g_mutex_unlock (&priv->op_mutex);
- g_task_run_in_thread (task, handshake_thread);
- crank_sync_handshake_context (tls, cancellable);
+ g_main_context_wakeup (priv->handshake_context);
- success = finish_handshake (tls, task, &my_error);
- g_object_unref (task);
+ success = finish_op_thread_handshake (tls, success, &my_error);
g_main_context_pop_thread_default (priv->handshake_context);
g_clear_pointer (&priv->handshake_context, g_main_context_unref);
@@ -1873,7 +2011,7 @@ do_implicit_handshake (GTlsConnectionBase *tls,
GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
gint64 *thread_timeout = NULL;
- g_tls_log_debug (tls, "Implcit TLS handshaking starts");
+ g_tls_log_debug (tls, "Implicit TLS handshaking starts");
/* We have op_mutex */
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 5e89b09..fa155f3 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -63,6 +63,7 @@ struct _GTlsConnectionBaseClass
GTlsOperationsThreadBase *(*create_op_thread) (GTlsConnectionBase *tls);
+ /* FIXME: deal with all the handshaking stuff */
void (*prepare_handshake) (GTlsConnectionBase *tls,
gchar **advertised_protocols);
GTlsSafeRenegotiationStatus (*handshake_thread_safe_renegotiation_status)
diff --git a/tls/base/gtlsoperationsthread-base.c b/tls/base/gtlsoperationsthread-base.c
index 85db976..825e6c2 100644
--- a/tls/base/gtlsoperationsthread-base.c
+++ b/tls/base/gtlsoperationsthread-base.c
@@ -78,6 +78,7 @@ typedef struct {
} GTlsOperationsThreadBasePrivate;
typedef enum {
+ G_TLS_THREAD_OP_HANDSHAKE,
G_TLS_THREAD_OP_READ,
G_TLS_THREAD_OP_READ_MESSAGE,
G_TLS_THREAD_OP_WRITE,
@@ -138,6 +139,14 @@ static GParamSpec *obj_properties[LAST_PROP];
G_DEFINE_TYPE_WITH_PRIVATE (GTlsOperationsThreadBase, g_tls_operations_thread_base, G_TYPE_OBJECT)
+GTlsConnectionBase *
+g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase *self)
+{
+ GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+
+ return priv->connection;
+}
+
static GTlsThreadOperation *
g_tls_thread_operation_new (GTlsThreadOperationType type,
GTlsOperationsThreadBase *thread,
@@ -164,13 +173,13 @@ g_tls_thread_operation_new (GTlsThreadOperationType type,
switch (type)
{
case G_TLS_THREAD_OP_READ:
- /* fallthrough */
op->io_condition = G_IO_IN;
break;
case G_TLS_THREAD_OP_WRITE:
- /* fallthrough */
op->io_condition = G_IO_OUT;
break;
+ case G_TLS_THREAD_OP_HANDSHAKE:
+ /* fallthrough */
case G_TLS_THREAD_OP_CLOSE:
op->io_condition = G_IO_IN | G_IO_OUT;
break;
@@ -181,6 +190,7 @@ g_tls_thread_operation_new (GTlsThreadOperationType type,
return op;
}
+#if 0
static GTlsThreadOperation *
g_tls_thread_operation_new_async (GTlsThreadOperationType type,
GTlsOperationsThreadBase *thread,
@@ -205,6 +215,7 @@ g_tls_thread_operation_new_async (GTlsThreadOperationType type,
return op;
}
+#endif
static GTlsThreadOperation *
g_tls_thread_operation_new_with_input_vectors (GTlsOperationsThreadBase *thread,
@@ -293,14 +304,6 @@ 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;
-}
-
static GTlsConnectionBaseStatus
execute_sync_op (GTlsOperationsThreadBase *self,
GTlsThreadOperation *op /* owned */,
@@ -333,6 +336,7 @@ execute_sync_op (GTlsOperationsThreadBase *self,
return result;
}
+#if 0
static void
execute_async_op (GTlsOperationsThreadBase *self,
GTlsThreadOperation *op)
@@ -341,9 +345,34 @@ execute_async_op (GTlsOperationsThreadBase *self,
g_assert (op->task);
+ /* FIXME: Design flaw? Here the queue owns the ops only for async tasks.
+ * But it doesn't free them when destroyed (though there should not be any
+ * when destroyed anyway?). It's confusing to have both owned and unowned ops
+ * stored in the same queue. Do we need ops to be refcounted?
+ */
g_async_queue_push (priv->queue, g_steal_pointer (&op));
g_main_context_wakeup (priv->op_thread_context);
}
+#endif
+
+GTlsConnectionBaseStatus
+g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase *self,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOperationsThreadBasePrivate *priv = g_tls_operations_thread_base_get_instance_private (self);
+ GTlsThreadOperation *op;
+
+ op = g_tls_thread_operation_new (G_TLS_THREAD_OP_HANDSHAKE,
+ self,
+ priv->connection,
+ NULL, 0,
+ timeout,
+ cancellable);
+
+ return execute_sync_op (self, g_steal_pointer (&op), NULL, error);
+}
GTlsConnectionBaseStatus
g_tls_operations_thread_base_read (GTlsOperationsThreadBase *self,
@@ -795,8 +824,13 @@ process_op (GAsyncQueue *queue,
switch (op->type)
{
+ case G_TLS_THREAD_OP_HANDSHAKE:
+ op->result = base_class->handshake_fn (op->thread,
+ op->timeout,
+ op->cancellable,
+ &op->error);
+ break;
case G_TLS_THREAD_OP_READ:
- g_assert (base_class->read_fn);
op->result = base_class->read_fn (op->thread,
op->data, op->size,
&op->count,
@@ -812,7 +846,6 @@ process_op (GAsyncQueue *queue,
&op->error);
break;
case G_TLS_THREAD_OP_WRITE:
- g_assert (base_class->write_fn);
op->result = base_class->write_fn (op->thread,
op->data, op->size,
&op->count,
@@ -828,7 +861,6 @@ process_op (GAsyncQueue *queue,
&op->error);
break;
case G_TLS_THREAD_OP_CLOSE:
- g_assert (base_class->close_fn);
op->result = base_class->close_fn (op->thread,
op->cancellable,
&op->error);
@@ -882,6 +914,9 @@ finished:
g_task_return_error (op->task, op->error);
else
g_task_return_int (op->task, op->result);
+
+ /* The op is owned only for async ops, not for sync ops. */
+ g_tls_thread_operation_free (op);
}
else /* sync op */
{
diff --git a/tls/base/gtlsoperationsthread-base.h b/tls/base/gtlsoperationsthread-base.h
index ea99778..63f7a34 100644
--- a/tls/base/gtlsoperationsthread-base.h
+++ b/tls/base/gtlsoperationsthread-base.h
@@ -38,6 +38,11 @@ struct _GTlsOperationsThreadBaseClass
{
GObjectClass parent_class;
+ GTlsConnectionBaseStatus (*handshake_fn) (GTlsOperationsThreadBase *self,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error);
+
GTlsConnectionBaseStatus (*read_fn) (GTlsOperationsThreadBase *self,
void *buffer,
gsize size,
@@ -64,13 +69,18 @@ struct _GTlsOperationsThreadBaseClass
GCancellable *cancellable,
GError **error);
- GTlsConnectionBaseStatus (*close_fn) (GTlsOperationsThreadBase *tls,
+ GTlsConnectionBaseStatus (*close_fn) (GTlsOperationsThreadBase *self,
GCancellable *cancellable,
GError **error);
};
/* FIXME: remove? */
-GTlsConnectionBase *g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase *self);
+GTlsConnectionBase *g_tls_operations_thread_base_get_connection (GTlsOperationsThreadBase *self);
+
+GTlsConnectionBaseStatus g_tls_operations_thread_base_handshake (GTlsOperationsThreadBase *self,
+ gint64 timeout,
+ GCancellable
*cancellable,
+ GError **error);
GTlsConnectionBaseStatus g_tls_operations_thread_base_read (GTlsOperationsThreadBase *self,
void *buffer,
diff --git a/tls/gnutls/gtlsoperationsthread-gnutls.c b/tls/gnutls/gtlsoperationsthread-gnutls.c
index 7920e4d..11b1bc9 100644
--- a/tls/gnutls/gtlsoperationsthread-gnutls.c
+++ b/tls/gnutls/gtlsoperationsthread-gnutls.c
@@ -39,6 +39,8 @@ struct _GTlsOperationsThreadGnutls {
gnutls_session_t session;
};
+static gnutls_priority_t priority;
+
G_DEFINE_TYPE (GTlsOperationsThreadGnutls, g_tls_operations_thread_gnutls, G_TYPE_TLS_OPERATIONS_THREAD_BASE)
static GTlsConnectionBaseStatus
@@ -200,6 +202,90 @@ end_gnutls_io (GTlsOperationsThreadGnutls *self,
status = end_gnutls_io (self, direction, ret, err, errmsg); \
} while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+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
+set_handshake_priority (GTlsOperationsThreadGnutls *self)
+{
+ int ret;
+
+ g_assert (priority);
+
+ ret = gnutls_priority_set (self->session, priority);
+ if (ret != GNUTLS_E_SUCCESS)
+ g_warning ("Failed to set GnuTLS session priority: %s", gnutls_strerror (ret));
+}
+
+static GTlsConnectionBaseStatus
+g_tls_operations_thread_gnutls_handshake (GTlsOperationsThreadBase *base,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOperationsThreadGnutls *self = G_TLS_OPERATIONS_THREAD_GNUTLS (base);
+ GTlsConnectionBase *tls;
+ GTlsConnectionBaseStatus status;
+ int ret;
+
+ tls = g_tls_operations_thread_base_get_connection (base);
+
+ if (!g_tls_connection_base_ever_handshaked (tls))
+ set_handshake_priority (self);
+
+ if (timeout > 0)
+ {
+ unsigned int timeout_ms;
+
+ /* Convert from microseconds to milliseconds, but ensure the timeout
+ * remains positive. */
+ timeout_ms = (timeout + 999) / 1000;
+
+ gnutls_handshake_set_timeout (self->session, timeout_ms);
+ gnutls_dtls_set_timeouts (self->session, 1000 /* default */, timeout_ms);
+ }
+
+ BEGIN_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, cancellable);
+ ret = gnutls_handshake (self->session);
+ if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
+ {
+ guint8 buf[1024];
+
+ /* Got app data while waiting for rehandshake; buffer it and try again */
+ ret = gnutls_record_recv (self->session, buf, sizeof (buf));
+ if (ret > -1)
+ {
+ /* FIXME: no longer belongs in GTlsConnectionBase? */
+ g_tls_connection_base_handshake_thread_buffer_application_data (tls, buf, ret);
+ ret = GNUTLS_E_AGAIN;
+ }
+ }
+ END_GNUTLS_IO (self, G_IO_IN | G_IO_OUT, ret, status,
+ _("Error performing TLS handshake"), error);
+
+ return status;
+}
+
static GTlsConnectionBaseStatus
g_tls_operations_thread_gnutls_read (GTlsOperationsThreadBase *base,
void *buffer,
@@ -404,11 +490,14 @@ g_tls_operations_thread_gnutls_class_init (GTlsOperationsThreadGnutlsClass *klas
gobject_class->constructed = g_tls_operations_thread_gnutls_constructed;
+ base_class->handshake_fn = g_tls_operations_thread_gnutls_handshake;
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;
base_class->close_fn = g_tls_operations_thread_gnutls_close;
+
+ initialize_gnutls_priority ();
}
GTlsOperationsThreadBase *
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]