[glib-networking/mcatanzaro/base-rebase: 32/33] progress
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/mcatanzaro/base-rebase: 32/33] progress
- Date: Fri, 19 Apr 2019 22:33:48 +0000 (UTC)
commit b897e524ef6eedc1a6b8a643c031ee4f02ce6c3c
Author: Michael Catanzaro <mcatanzaro igalia com>
Date: Fri Apr 19 14:34:37 2019 -0500
progress
meson.build | 2 +-
tls/base/gtlsconnection-base.c | 300 ++++-
tls/base/gtlsconnection-base.h | 50 +-
tls/gnutls/gtlsclientconnection-gnutls.c | 74 +-
tls/gnutls/gtlsconnection-gnutls.c | 1831 +++++-------------------------
tls/gnutls/gtlsconnection-gnutls.h | 47 +-
tls/gnutls/gtlsinputstream-gnutls.c | 268 -----
tls/gnutls/gtlsinputstream-gnutls.h | 41 -
tls/gnutls/gtlsoutputstream-gnutls.c | 269 -----
tls/gnutls/gtlsoutputstream-gnutls.h | 41 -
tls/gnutls/gtlsserverconnection-gnutls.c | 25 +-
tls/openssl/gtlsconnection-openssl.c | 74 +-
tls/tests/connection.c | 12 -
tls/tests/dtls-connection.c | 8 -
14 files changed, 677 insertions(+), 2365 deletions(-)
---
diff --git a/meson.build b/meson.build
index 8d01f52..c5ca8f4 100644
--- a/meson.build
+++ b/meson.build
@@ -43,7 +43,7 @@ if host_system.contains('linux')
endif
# *** Check GLib GIO ***
-glib_dep = dependency('glib-2.0', version: '>= 2.55.1',
+glib_dep = dependency('glib-2.0', version: '>= 2.60.0',
fallback: ['glib', 'libglib_dep'])
gio_dep = dependency('gio-2.0',
fallback: ['glib', 'libgio_dep'])
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 984231a..73f2a8b 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -87,7 +87,7 @@ typedef struct
GTlsInteraction *interaction;
GTlsCertificate *certificate;
- gboolean certificate_requested;
+ gboolean missing_requested_client_certificate;
GError *certificate_error;
GTlsCertificate *peer_certificate;
GTlsCertificateFlags peer_certificate_errors;
@@ -145,6 +145,9 @@ typedef struct
GMutex op_mutex;
GCancellable *waiting_for_op;
+
+ gchar **advertised_protocols;
+ gchar *negotiated_protocol;
} GTlsConnectionBasePrivate;
static void g_tls_connection_base_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
@@ -182,7 +185,11 @@ enum
PROP_CERTIFICATE,
PROP_INTERACTION,
PROP_PEER_CERTIFICATE,
- PROP_PEER_CERTIFICATE_ERRORS
+ PROP_PEER_CERTIFICATE_ERRORS,
+#if GLIB_CHECK_VERSION(2, 60, 0)
+ PROP_ADVERTISED_PROTOCOLS,
+ PROP_NEGOTIATED_PROTOCOL,
+#endif
};
static gboolean
@@ -242,6 +249,11 @@ g_tls_connection_base_finalize (GObject *object)
g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
+#if GLIB_CHECK_VERSION(2, 60, 0)
+ g_clear_pointer (&priv->advertised_protocols, g_strfreev);
+ g_clear_pointer (&priv->negotiated_protocol, g_free);
+#endif
+
G_OBJECT_CLASS (g_tls_connection_base_parent_class)->finalize (object);
}
@@ -303,6 +315,14 @@ g_tls_connection_base_get_property (GObject *object,
g_value_set_flags (value, priv->peer_certificate_errors);
break;
+ case PROP_ADVERTISED_PROTOCOLS:
+ g_value_set_boxed (value, priv->advertised_protocols);
+ break;
+
+ case PROP_NEGOTIATED_PROTOCOL:
+ g_value_set_string (value, priv->negotiated_protocol);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -403,6 +423,11 @@ g_tls_connection_base_set_property (GObject *object,
priv->interaction = g_value_dup_object (value);
break;
+ case PROP_ADVERTISED_PROTOCOLS:
+ g_clear_pointer (&priv->advertised_protocols, g_strfreev);
+ priv->advertised_protocols = g_value_dup_boxed (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -715,7 +740,7 @@ g_tls_connection_base_pop_io (GTlsConnectionBase *tls,
/* Checks whether the underlying base stream or GDatagramBased meets
* @condition. */
-static gboolean
+gboolean
g_tls_connection_base_base_check (GTlsConnectionBase *tls,
GIOCondition condition)
{
@@ -1084,39 +1109,6 @@ g_tls_connection_base_condition_wait (GDatagramBased *datagram_based,
return !g_cancellable_set_error_if_cancelled (cancellable, error);
}
-gboolean
-g_tls_connection_base_accept_peer_certificate (GTlsConnectionBase *tls,
- GTlsCertificate *peer_certificate,
- GTlsCertificateFlags peer_certificate_errors)
-{
- GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
- gboolean accepted = FALSE;
-
- if (G_IS_TLS_CLIENT_CONNECTION (tls) && priv->peer_certificate)
- {
- GTlsCertificateFlags validation_flags;
-
- if (!g_tls_connection_base_is_dtls (tls))
- validation_flags =
- g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (tls));
- else
- validation_flags =
- g_dtls_client_connection_get_validation_flags (G_DTLS_CLIENT_CONNECTION (tls));
-
- if ((peer_certificate_errors & validation_flags) == 0)
- accepted = TRUE;
- }
-
- if (!accepted)
- {
- accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (tls),
- peer_certificate,
- peer_certificate_errors);
- }
-
- return accepted;
-}
-
void
g_tls_connection_base_set_peer_certificate (GTlsConnectionBase *tls,
GTlsCertificate *peer_certificate,
@@ -1149,7 +1141,7 @@ handshake_thread (GTask *task,
timeout = *((gint64 *)task_data);
priv->started_handshake = FALSE;
- priv->certificate_requested = FALSE;
+ priv->missing_requested_client_certificate = FALSE;
if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
timeout, cancellable, &error))
@@ -1176,15 +1168,12 @@ handshake_thread (GTask *task,
priv->peer_certificate_errors = 0;
priv->started_handshake = TRUE;
- tls_class->handshake (tls, timeout, cancellable, &error);
+ tls_class->handshake_thread_handshake (tls, timeout, cancellable, &error);
priv->need_handshake = FALSE;
if (error)
{
- if ((g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
- g_error_matches (error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
- g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) &&
- priv->certificate_requested)
+ if (*error != NULL && priv->missing_requested_client_certificate)
{
g_clear_error (&error);
if (priv->certificate_error)
@@ -1207,6 +1196,44 @@ handshake_thread (GTask *task,
}
}
+static void
+sync_handshake_thread_completed (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ g_assert (g_main_context_is_owner (priv->handshake_context));
+
+ g_mutex_lock (&priv->op_mutex);
+ priv->sync_handshake_completed = TRUE;
+ g_mutex_unlock (&priv->op_mutex);
+
+ g_main_context_wakeup (priv->handshake_context);
+}
+
+static void
+crank_sync_handshake_context (GTlsConnectionBase *tls,
+ GCancellable *cancellable)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ /* need_finish_handshake will be set inside sync_handshake_thread_completed(),
+ * which should only ever be invoked while iterating the handshake context
+ * here. So need_finish_handshake should only change on this thread.
+ */
+ g_mutex_lock (&priv->op_mutex);
+ priv->sync_handshake_completed = FALSE;
+ while (!priv->sync_handshake_completed && !g_cancellable_is_cancelled (cancellable))
+ {
+ g_mutex_unlock (&priv->op_mutex);
+ g_main_context_iteration (priv->handshake_context, TRUE);
+ g_mutex_lock (&priv->op_mutex);
+ }
+ g_mutex_unlock (&priv->op_mutex);
+}
+
static gboolean
finish_handshake (GTlsConnectionBase *tls,
GTask *task,
@@ -1214,10 +1241,17 @@ finish_handshake (GTlsConnectionBase *tls,
{
GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+ gchar *original_negotiated_protocol;
GError *my_error = NULL;
+ orig_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
+
if (g_task_propagate_boolean (task, &my_error))
- tls_class->complete_handshake (tls, &my_error);
+ tls_class->complete_handshake (tls, &priv->negotiated_protocol, &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);
@@ -1235,19 +1269,33 @@ g_tls_connection_base_handshake (GTlsConnection *conn,
GError **error)
{
GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
GTask *task;
gboolean success;
gint64 *timeout = NULL;
GError *my_error = NULL;
- task = g_task_new (conn, cancellable, NULL, NULL);
+ g_assert (priv->handshake_context == NULL);
+ priv->handshake_context = g_main_context_new ();
+
+ g_main_context_push_thread_default (priv->handshake_context);
+
+ 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_return_on_cancel (task, TRUE);
timeout = g_new0 (gint64, 1);
*timeout = -1; /* blocking */
g_task_set_task_data (task, timeout, g_free);
- g_task_run_in_thread_sync (task, handshake_thread);
+ g_task_run_in_thread (task, handshake_thread);
+ crank_sync_handshake_context (tls, cancellable);
+
+ g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
success = finish_handshake (tls, task, &my_error);
g_object_unref (task);
@@ -1285,6 +1333,8 @@ handshake_thread_completed (GObject *object,
GError *error = NULL;
gboolean need_finish_handshake, success;
+ g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
g_mutex_lock (&priv->op_mutex);
if (priv->need_finish_handshake)
{
@@ -1336,15 +1386,20 @@ async_handshake_thread (GTask *task,
}
static void
-g_tls_connection_base_handshake_async (GTlsConnection *conn,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+g_tls_connection_base_handshake_async (GTlsConnection *conn,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (G_TLS_CONNECTION_GNUTLS
(conn));
GTask *thread_task, *caller_task;
gint64 *timeout = NULL;
+ g_assert (!priv->handshake_context);
+ priv->handshake_context = g_main_context_ref_thread_default ();
+
caller_task = g_task_new (conn, cancellable, callback, user_data);
g_task_set_source_tag (caller_task, g_tls_connection_base_handshake_async);
g_task_set_priority (caller_task, io_priority);
@@ -1361,9 +1416,9 @@ g_tls_connection_base_handshake_async (GTlsConnection *conn,
}
static gboolean
-g_tls_connection_base_handshake_finish (GTlsConnection *conn,
- GAsyncResult *result,
- GError **error)
+g_tls_connection_base_handshake_finish (GTlsConnection *conn,
+ GAsyncResult *result,
+ GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, conn), FALSE);
@@ -1417,9 +1472,20 @@ do_implicit_handshake (GTlsConnectionBase *tls,
/* We have op_mutex */
+ g_assert (priv->handshake_context == NULL);
+ if (timeout != 0)
+ {
+ priv->handshake_context = g_main_context_new ();
+ g_main_context_push_thread_default (priv->handshake_context);
+ }
+ else
+ {
+ priv->handshake_context = g_main_context_ref_thread_default ();
+ }
+
g_assert (priv->implicit_handshake == NULL);
priv->implicit_handshake = g_task_new (tls, cancellable,
- implicit_handshake_completed,
+ timeout ? sync_handshake_thread_completed : NULL,
NULL);
g_task_set_source_tag (priv->implicit_handshake, do_implicit_handshake);
@@ -1440,8 +1506,15 @@ do_implicit_handshake (GTlsConnectionBase *tls,
*thread_timeout = timeout;
g_mutex_unlock (&priv->op_mutex);
- g_task_run_in_thread_sync (priv->implicit_handshake,
- handshake_thread);
+
+ g_task_set_return_on_cancel (priv->implicit_handshake, TRUE);
+ g_task_run_in_thread (priv->implicit_handshake, handshake_thread);
+ crank_sync_handshake_context (tls, cancellable);
+
+ g_main_context_pop_thread_default (priv->handshake_context);
+
+ g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
success = finish_handshake (tls,
priv->implicit_handshake,
&my_error);
@@ -1556,6 +1629,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);
}
@@ -1691,6 +1765,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);
@@ -2001,15 +2076,85 @@ g_tls_connection_base_dtls_shutdown_finish (GDtlsConnection *conn,
return g_task_propagate_boolean (G_TASK (result), error);
}
+#if GLIB_CHECK_VERSION(2, 60, 0)
+static void
+g_tls_connection_base_dtls_set_advertised_protocols (GDtlsConnection *conn,
+ const gchar * const *protocols)
+{
+ g_object_set (conn, "advertised-protocols", protocols, NULL);
+}
+
+const gchar *
+g_tls_connection_base_dtls_get_negotiated_protocol (GDtlsConnection *conn)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (gnutls);
+
+ return priv->negotiated_protocol;
+}
+#endif
+
+gboolean
+g_tls_connection_base_is_dtls (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ return !!priv->base_socket;
+}
+
+GDatagramBased *
+g_tls_connection_base_get_base_socket (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME:
+// g_assert (g_tls_connection_base_is_dtls (tls));
+
+ return priv->base_socket;
+}
+
+GIOStream *
+g_tls_connection_base_get_base_iostream (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME:
+// g_assert (!g_tls_connection_base_is_dtls (tls));
+
+ return priv->base_iostream;
+}
+
+GInputStream *
+g_tls_connection_base_get_base_istream (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME:
+// g_assert (!g_tls_connection_base_is_dtls (tls));
+
+ return priv->base_istream;
+}
+
+GOutputStream *
+g_tls_connection_base_get_base_ostream (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME:
+// g_assert (!g_tls_connection_base_is_dtls (tls));
+
+ return priv->base_ostream;
+}
+
void
-g_tls_connection_base_set_certificate_requested (GTlsConnectionBase *tls)
+g_tls_connection_base_set_missing_requested_client_certificate (GTlsConnectionBase *tls)
{
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
/* FIXME: Assert this is only used on the handshake thread. */
/* FIXME: Assert it's not already requested? Probably. */
- priv->certificate_requested = TRUE;
+ priv->missing_requested_client_certificate = TRUE;
}
GError **
@@ -2036,6 +2181,22 @@ g_tls_connection_base_get_write_error (GTlsConnectionBase *tls)
return &priv->write_error;
}
+gint64
+g_tls_connection_base_get_read_timeout (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ return priv->read_timeout;
+}
+
+gint64
+g_tls_connection_base_get_write_timeout (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ return priv->write_timeout;
+}
+
gboolean
g_tls_connection_base_is_handshaking (GTlsConnectionBase *tls)
{
@@ -2074,6 +2235,21 @@ g_tls_connection_base_request_certificate (GTlsConnectionBase *tls,
return res != G_TLS_INTERACTION_FAILED;
}
+void
+g_tls_connection_base_buffer_application_data (GTlsConnectionBase *tls,
+ guint8 *data,
+ gsize length)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME: Called from handshake thread, needs a mutex!!
+
+ if (!priv->app_data_buf)
+ priv->app_data_buf = g_byte_array_new ();
+
+ g_byte_array_append (priv->app_data_buf, data, length);
+}
+
static void
g_tls_connection_base_class_init (GTlsConnectionBaseClass *klass)
{
@@ -2109,6 +2285,10 @@ g_tls_connection_base_class_init (GTlsConnectionBaseClass *klass)
g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE, "peer-certificate");
g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE_ERRORS, "peer-certificate-errors");
+#if GLIB_CHECK_VERSION(2, 60, 0)
+ g_object_class_override_property (gobject_class, PROP_ADVERTISED_PROTOCOLS, "advertised-protocols");
+ g_object_class_override_property (gobject_class, PROP_NEGOTIATED_PROTOCOL, "negotiated-protocol");
+#endif
}
static void
@@ -2120,6 +2300,10 @@ g_tls_connection_base_dtls_connection_iface_init (GDtlsConnectionInterface *ifac
iface->shutdown = g_tls_connection_base_dtls_shutdown;
iface->shutdown_async = g_tls_connection_base_dtls_shutdown_async;
iface->shutdown_finish = g_tls_connection_base_dtls_shutdown_finish;
+#if GLIB_CHECK_VERSION(2, 60, 0)
+ iface->set_advertised_protocols = g_tls_connection_base_dtls_set_advertised_protocols;
+ iface->get_negotiated_protocol = g_tls_connection_gnutls_dtls_get_negotiated_protocol;
+#endif
}
static void
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 2bea4ad..66e2048 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -52,17 +52,21 @@ typedef enum {
struct _GTlsConnectionBaseClass
{
- GTlsConnectionClass parent_class;
+ GTlsConnectionClass parent_class;
GTlsConnectionBaseStatus (*request_rehandshake) (GTlsConnectionBase *tls,
gint64 timeout,
GCancellable *cancellable,
GError **error);
- GTlsConnectionBaseStatus (*handshake) (GTlsConnectionBase *tls,
+ void (*prepare_handshake) (GTlsConnectionBase *tls,
+ gchar **advertised_protocols);
+ GTlsConnectionBaseStatus (*handshake_thread_handshake)
+ (GTlsConnectionBase *tls,
gint64 timeout,
GCancellable *cancellable,
GError **error);
- GTlsConnectionBaseStatus (*complete_handshake) (GTlsConnectionBase *tls,
+ void (*complete_handshake) (GTlsConnectionBase *tls,
+ gchar **negotiated_protocol,
GError **error);
void (*push_io) (GTlsConnectionBase *tls,
@@ -110,10 +114,6 @@ struct _GTlsConnectionBaseClass
GError **error);
};
-gboolean g_tls_connection_base_accept_peer_certificate (GTlsConnectionBase *tls,
- GTlsCertificate *peer_certificate,
- GTlsCertificateFlags peer_certificate_errors);
-
void g_tls_connection_base_set_peer_certificate (GTlsConnectionBase *tls,
GTlsCertificate *peer_certificate,
GTlsCertificateFlags peer_certificate_errors);
@@ -143,6 +143,8 @@ gssize g_tls_connection_base_write (GTlsConnectionBase *tls,
gboolean g_tls_connection_base_check (GTlsConnectionBase *tls,
GIOCondition condition);
+gboolean g_tls_connection_base_base_check (GTlsConnectionBase *tls,
+ GIOCondition condition);
GSource *g_tls_connection_base_create_source (GTlsConnectionBase *tls,
GIOCondition condition,
GCancellable *cancellable);
@@ -153,18 +155,36 @@ gboolean g_tls_connection_base_close_internal (GIOStream *stream,
GCancellable *cancellable,
GError **error);
-void g_tls_connection_base_set_certificate_requested (GTlsConnectionBase *tls);
+gboolean g_tls_connection_base_is_dtls (GTlsConnectionBase *tls);
-GError **g_tls_connection_base_get_certificate_error (GTlsConnectionBase *tls);
-GError **g_tls_connection_base_get_read_error (GTlsConnectionBase *tls);
-GError **g_tls_connection_base_get_write_error (GTlsConnectionBase *tls);
+GDatagramBased *g_tls_connection_base_get_base_socket (GTlsConnectionBase *tls);
-gboolean g_tls_connection_base_is_handshaking (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);
-gboolean g_tls_connection_base_ever_handshaked (GTlsConnectionBase *tls);
+void g_tls_connection_base_set_missing_requested_client_certificate
+ (GTlsConnectionBase *tls);
-gboolean g_tls_connection_base_request_certificate (GTlsConnectionBase *tls,
- GError **error);
+GError **g_tls_connection_base_get_certificate_error (GTlsConnectionBase *tls);
+GError **g_tls_connection_base_get_read_error (GTlsConnectionBase *tls);
+GError **g_tls_connection_base_get_write_error (GTlsConnectionBase *tls);
+
+gint64 g_tls_connection_base_get_read_timeout (GTlsConnectionBase *tls);
+gint64 g_tls_connection_base_get_write_timeout (GTlsConnectionBase *tls);
+
+gboolean g_tls_connection_base_is_handshaking (GTlsConnectionBase *tls);
+
+gboolean g_tls_connection_base_ever_handshaked (GTlsConnectionBase *tls);
+
+gboolean g_tls_connection_base_request_certificate (GTlsConnectionBase *tls,
+ GError **error);
+
+void g_tls_connection_base_buffer_application_data (GTlsConnectionBase *tls,
+ guint8 *data,
+ gsize length);
+
+void g_tls_connection_base_set_advertised_protocols
void GTLS_DEBUG (gpointer gnutls,
const char *message,
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index cac2deb..fc5227b 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -30,6 +30,7 @@
#include <gnutls/x509.h>
#include <string.h>
+#include "gtlsconnection-base.h"
#include "gtlsclientconnection-gnutls.h"
#include "gtlsbackend-gnutls.h"
#include "gtlscertificate-gnutls.h"
@@ -56,8 +57,6 @@ struct _GTlsClientConnectionGnutls
GBytes *session_id;
GBytes *session_data;
- gboolean requested_cert_missing;
- GError *cert_error;
GPtrArray *accepted_cas;
gnutls_pcert_st *pcert;
@@ -189,7 +188,6 @@ g_tls_client_connection_gnutls_finalize (GObject *object)
g_clear_pointer (&gnutls->accepted_cas, g_ptr_array_unref);
g_clear_pointer (&gnutls->session_id, g_bytes_unref);
g_clear_pointer (&gnutls->session_data, g_bytes_unref);
- g_clear_error (&gnutls->cert_error);
clear_gnutls_certificate_copy (gnutls);
@@ -324,6 +322,7 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
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;
@@ -353,9 +352,9 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
if (*pcert_length == 0)
{
g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
- g_clear_error (&gnutls->cert_error);
+ g_clear_error (g_tls_connection_base_get_certificate_error (tls));
- if (g_tls_connection_gnutls_request_certificate (conn, &gnutls->cert_error))
+ if (g_tls_connection_gnutls_request_certificate (conn, g_tls_connection_base_get_certificate_error
(tls)))
g_tls_connection_gnutls_get_certificate (conn, pcert, pcert_length, pkey);
if (*pcert_length == 0)
@@ -365,7 +364,7 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
/* If there is still no client certificate, this connection will
* probably fail, but no reason to give up: let's try anyway.
*/
- gnutls->requested_cert_missing = TRUE;
+ g_tls_connection_base_set_missing_requested_client_certificate (tls);
return 0;
}
}
@@ -377,7 +376,7 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
/* No private key. GnuTLS expects it to be non-null if pcert_length is
* nonzero, so we have to abort now.
*/
- gnutls->requested_cert_missing = TRUE;
+ g_tls_connection_base_set_missing_requested_client_certificate (tls);
return -1;
}
@@ -399,17 +398,19 @@ g_tls_client_connection_gnutls_failed (GTlsConnectionGnutls *conn)
g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->session_id);
}
-static void
-g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+static GTlsConnectionBaseStatus
+g_tls_client_connection_gnutls_handshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
{
- GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
g_tls_client_connection_gnutls_compute_session_id (gnutls);
/* Try to get a cached session */
if (gnutls->session_data_override)
{
- gnutls_session_set_data (g_tls_connection_gnutls_get_session (conn),
+ gnutls_session_set_data (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
g_bytes_get_data (gnutls->session_data, NULL),
g_bytes_get_size (gnutls->session_data));
}
@@ -420,7 +421,7 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
session_data = g_tls_backend_gnutls_lookup_session (GNUTLS_CLIENT, gnutls->session_id);
if (session_data)
{
- gnutls_session_set_data (g_tls_connection_gnutls_get_session (conn),
+ gnutls_session_set_data (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
g_bytes_get_data (session_data, NULL),
g_bytes_get_size (session_data));
g_clear_pointer (&gnutls->session_data, g_bytes_unref);
@@ -428,35 +429,22 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
}
}
- gnutls->requested_cert_missing = FALSE;
+ return G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->
+ handshake (tls, cancellable, error);
}
-static void
-g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
- GError **inout_error)
+static GTlsConnectionBaseStatus
+g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase *tls,
+ GError **error)
{
- GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
+ GTlsConnectionBaseStatus status;
int resumed;
- g_assert (inout_error != NULL);
+ status = G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->complete_handshake
(tls, error);
- if (*inout_error != NULL && gnutls->requested_cert_missing)
- {
- g_clear_error (inout_error);
- if (gnutls->cert_error)
- {
- *inout_error = gnutls->cert_error;
- gnutls->cert_error = NULL;
- }
- else
- {
- g_set_error_literal (inout_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
- _("Server required TLS certificate"));
- }
- }
-
- resumed = gnutls_session_is_resumed (g_tls_connection_gnutls_get_session (conn));
- if (*inout_error || !resumed)
+ resumed = gnutls_session_is_resumed (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)));
+ if (status != G_TLS_CONNECTION_BASE_OK || !resumed)
{
/* Clear session data since the server did not accept what we provided. */
gnutls->session_data_override = FALSE;
@@ -465,11 +453,11 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->session_id);
}
- if (!*inout_error && !resumed)
+ if (status == G_TLS_CONNECTION_BASE_OK && !resumed)
{
gnutls_datum_t session_datum;
- if (gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (conn),
+ if (gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
&session_datum) == 0)
{
gnutls->session_data = g_bytes_new_with_free_func (session_datum.data,
@@ -483,6 +471,8 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
gnutls->session_data);
}
}
+
+ return status;
}
static void
@@ -508,15 +498,17 @@ static void
g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GTlsConnectionGnutlsClass *connection_gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+ GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
gobject_class->get_property = g_tls_client_connection_gnutls_get_property;
gobject_class->set_property = g_tls_client_connection_gnutls_set_property;
gobject_class->finalize = g_tls_client_connection_gnutls_finalize;
- connection_gnutls_class->failed = g_tls_client_connection_gnutls_failed;
- connection_gnutls_class->begin_handshake = g_tls_client_connection_gnutls_begin_handshake;
- connection_gnutls_class->finish_handshake = g_tls_client_connection_gnutls_finish_handshake;
+ base_class->begin_handshake = g_tls_client_connection_gnutls_handshake;
+ base_class->complete_handshake = g_tls_client_connection_gnutls_complete_handshake;
+
+ gnutls_class->failed = g_tls_client_connection_gnutls_failed;
g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 774d668..0d1bd37 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -36,9 +36,6 @@
#include "gtlsbackend-gnutls.h"
#include "gtlscertificate-gnutls.h"
#include "gtlsclientconnection-gnutls.h"
-#include "gtlsinputstream-gnutls.h"
-#include "gtlsoutputstream-gnutls.h"
-#include "gtlsserverconnection-gnutls.h"
#ifdef G_OS_WIN32
#include <winsock2.h>
@@ -53,35 +50,6 @@
#include <glib/gi18n-lib.h>
#include <glib/gprintf.h>
-/*
- * GTlsConnectionGnutls is the base abstract implementation of TLS and DTLS
- * support, for both the client and server side of a connection. The choice
- * between TLS and DTLS is made by setting the base-io-stream or
- * base-socket properties — exactly one of them must be set at
- * construction time.
- *
- * Client and server specific code is in the GTlsClientConnectionGnutls and
- * GTlsServerConnectionGnutls concrete subclasses, although the line about where
- * code is put is a little blurry, and there are various places in
- * GTlsConnectionGnutls which check G_IS_TLS_CLIENT_CONNECTION(self) to switch
- * to a client-only code path.
- *
- * This abstract class implements a lot of interfaces:
- * • Derived from GTlsConnection (itself from GIOStream), for TLS and streaming
- * communications.
- * • Implements GDtlsConnection and GDatagramBased, for DTLS and datagram
- * communications.
- * • Implements GInitable for failable GnuTLS initialisation.
- *
- * The GTlsClientConnectionGnutls and GTlsServerConnectionGnutls subclasses are
- * both derived from GTlsConnectionGnutls (and hence GIOStream), and both
- * implement the relevant TLS and DTLS interfaces:
- * • GTlsClientConnection
- * • GDtlsClientConnection
- * • GTlsServerConnection
- * • GDtlsServerConnection
- */
-
static ssize_t g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t transport_data,
const void *buf,
size_t buflen);
@@ -95,156 +63,23 @@ static ssize_t g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t transp
static int g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data,
unsigned int ms);
-
-static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
-static gboolean g_tls_connection_gnutls_initable_init (GInitable *initable,
- GCancellable *cancellable,
- GError **error);
-static void g_tls_connection_gnutls_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
-static void g_tls_connection_gnutls_datagram_based_iface_init (GDatagramBasedInterface *iface);
+static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
static void g_tls_connection_gnutls_init_priorities (void);
static int verify_certificate_cb (gnutls_session_t session);
-static gboolean do_implicit_handshake (GTlsConnectionGnutls *gnutls,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
-static gboolean finish_handshake (GTlsConnectionGnutls *gnutls,
- GTask *task,
- GError **error);
-
-enum
-{
- PROP_0,
- /* For this class: */
- PROP_BASE_IO_STREAM,
- PROP_BASE_SOCKET,
- /* For GTlsConnection and GDtlsConnection: */
- PROP_REQUIRE_CLOSE_NOTIFY,
- PROP_REHANDSHAKE_MODE,
- PROP_USE_SYSTEM_CERTDB,
- PROP_DATABASE,
- PROP_CERTIFICATE,
- PROP_INTERACTION,
- PROP_PEER_CERTIFICATE,
- PROP_PEER_CERTIFICATE_ERRORS,
-#if GLIB_CHECK_VERSION(2, 60, 0)
- PROP_ADVERTISED_PROTOCOLS,
- PROP_NEGOTIATED_PROTOCOL,
-#endif
-};
-
typedef struct
{
- /* When operating in stream mode, as a GTlsConnection. These are
- * mutually-exclusive with base_socket. There are two different
- * GIOStreams here: (a) base_io_stream and (b) the GTlsConnectionGnutls
- * itself. base_io_stream is the GIOStream used to create the GTlsConnection,
- * and corresponds to the GTlsConnection::base-io-stream property.
- * base_istream and base_ostream are the GInputStream and GOutputStream,
- * respectively, of base_io_stream. These are for the underlying sockets that
- * don't know about TLS.
- *
- * Then the GTlsConnectionGnutls also has tls_istream and tls_ostream which
- * wrap the aforementioned base streams with a TLS session.
- *
- * When operating in datagram mode, none of these are used.
- */
- GIOStream *base_io_stream;
- GPollableInputStream *base_istream;
- GPollableOutputStream *base_ostream;
- GInputStream *tls_istream;
- GOutputStream *tls_ostream;
-
- /* When operating in datagram mode, as a GDtlsConnection, the
- * GTlsConnectionGnutls is itself the DTLS GDatagramBased. It uses base_socket
- * for the underlying I/O. It is mutually-exclusive with base_io_stream and
- * the other streams.
- */
- GDatagramBased *base_socket;
-
gnutls_certificate_credentials_t creds;
gnutls_session_t session;
-
- GTlsCertificate *certificate, *peer_certificate;
- GTlsCertificateFlags peer_certificate_errors;
-
- GMutex verify_certificate_mutex;
- GCond verify_certificate_condition;
- gboolean peer_certificate_accepted;
- gboolean peer_certificate_examined;
-
- gboolean require_close_notify;
- GTlsRehandshakeMode rehandshake_mode;
- gboolean is_system_certdb;
- GTlsDatabase *database;
- gboolean database_is_unset;
-
- /* need_handshake means the next claim_op() will get diverted into
- * an implicit handshake (unless it's an OP_HANDSHAKE or OP_CLOSE*).
- * need_finish_handshake means the next claim_op() will get diverted
- * into finish_handshake() (unless it's an OP_CLOSE*).
- *
- * handshaking is TRUE as soon as a handshake thread is queued. For
- * a sync handshake it becomes FALSE after finish_handshake()
- * completes in the calling thread, but for an async implicit
- * handshake, it becomes FALSE (and need_finish_handshake becomes
- * TRUE) at the end of the handshaking thread (and then the next
- * non-close op will call finish_handshake()). We can't just wait
- * for handshake_thread_completed() to run, because it's possible
- * that its main loop is being blocked by a synchronous op which is
- * waiting for handshaking to become FALSE...
- *
- * started_handshake indicates that the current handshake attempt
- * got at least as far as calling gnutls_handshake() (and so any
- * error should be copied to handshake_error and returned on all
- * future operations). ever_handshaked indicates that TLS has
- * been successfully negotiated at some point.
- */
- gboolean need_handshake, need_finish_handshake, sync_handshake_completed;
- gboolean started_handshake, handshaking, ever_handshaked;
- GMainContext *handshake_context;
- GTask *implicit_handshake;
- GError *handshake_error;
- GByteArray *app_data_buf;
-
- /* read_closed means the read direction has closed; write_closed similarly.
- * If (and only if) both are set, the entire GTlsConnection is closed. */
- gboolean read_closing, read_closed;
- gboolean write_closing, write_closed;
-
- GTlsInteraction *interaction;
gchar *interaction_id;
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
- gchar **advertised_protocols;
- gchar *negotiated_protocol;
-#endif
-
- GMutex op_mutex;
- GCancellable *waiting_for_op;
-
- gboolean reading;
- gint64 read_timeout;
- GError *read_error;
- GCancellable *read_cancellable;
-
- gboolean writing;
- gint64 write_timeout;
- GError *write_error;
- GCancellable *write_cancellable;
} GTlsConnectionGnutlsPrivate;
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls, G_TYPE_TLS_CONNECTION,
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls, G_TYPE_TLS_CONNECTION_BASE,
G_ADD_PRIVATE (GTlsConnectionGnutls);
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
g_tls_connection_gnutls_initable_iface_init);
- G_IMPLEMENT_INTERFACE (G_TYPE_DATAGRAM_BASED,
- g_tls_connection_gnutls_datagram_based_iface_init);
- G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_CONNECTION,
- g_tls_connection_gnutls_dtls_connection_iface_init);
g_tls_connection_gnutls_init_priorities ();
);
@@ -261,17 +96,8 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
g_mutex_init (&priv->verify_certificate_mutex);
g_cond_init (&priv->verify_certificate_condition);
- priv->need_handshake = TRUE;
-
- priv->database_is_unset = TRUE;
- priv->is_system_certdb = TRUE;
-
unique_id = g_atomic_int_add (&unique_interaction_id, 1);
priv->interaction_id = g_strdup_printf ("gtls:%d", unique_id);
-
- priv->waiting_for_op = g_cancellable_new ();
- g_cancellable_cancel (priv->waiting_for_op);
- g_mutex_init (&priv->op_mutex);
}
/* First field is "fallback", second is "allow unsafe rehandshaking" */
@@ -358,19 +184,11 @@ g_tls_connection_gnutls_set_handshake_priority (GTlsConnectionGnutls *gnutls)
}
else
fallback = FALSE;
- unsafe_rehandshake = (priv->rehandshake_mode == G_TLS_REHANDSHAKE_UNSAFELY);
+ unsafe_rehandshake = g_tls_connection_get_rehandshake_mode (G_TLS_CONNECTION (gnutls)) ==
G_TLS_REHANDSHAKE_UNSAFELY;
gnutls_priority_set (priv->session,
priorities[fallback][unsafe_rehandshake]);
}
-static gboolean
-g_tls_connection_gnutls_is_dtls (GTlsConnectionGnutls *gnutls)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- return (priv->base_socket != NULL);
-}
-
static gboolean
g_tls_connection_gnutls_initable_init (GInitable *initable,
GCancellable *cancellable,
@@ -378,17 +196,22 @@ g_tls_connection_gnutls_initable_init (GInitable *initable,
{
GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+ GIOStream *base_io_stream = NULL;
+ GDatagramBased *base_socket = NULL;
gboolean client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
guint flags = client ? GNUTLS_CLIENT : GNUTLS_SERVER;
int status;
- g_return_val_if_fail ((priv->base_istream == NULL) ==
- (priv->base_ostream == NULL), FALSE);
- g_return_val_if_fail ((priv->base_socket == NULL) !=
- (priv->base_istream == NULL), FALSE);
+// FIXME: Avoid g_object_get?
+ g_object_get (gnutls,
+ "base-io-stream", &base_io_stream,
+ "base-socket", &base_socket,
+ NULL);
- /* Check whether to use DTLS or TLS. */
- if (g_tls_connection_gnutls_is_dtls (gnutls))
+ /* Ensure we are in TLS mode or DTLS mode. */
+ g_return_val_if_fail (!!base_io_stream != !!base_socket, FALSE);
+
+ if (base_socket)
flags |= GNUTLS_DATAGRAM;
gnutls_init (&priv->session, flags);
@@ -416,7 +239,7 @@ g_tls_connection_gnutls_initable_init (GInitable *initable,
gnutls_transport_set_ptr (priv->session, gnutls);
/* GDatagramBased supports vectored I/O; GPollableOutputStream does not. */
- if (priv->base_socket != NULL)
+ if (base_socket != NULL)
{
gnutls_transport_set_vec_push_function (priv->session,
g_tls_connection_gnutls_vec_push_func);
@@ -426,13 +249,6 @@ g_tls_connection_gnutls_initable_init (GInitable *initable,
if (flags & GNUTLS_DATAGRAM)
gnutls_dtls_set_mtu (priv->session, 1400);
- /* Create output streams if operating in streaming mode. */
- if (!(flags & GNUTLS_DATAGRAM))
- {
- priv->tls_istream = g_tls_input_stream_gnutls_new (gnutls);
- priv->tls_ostream = g_tls_output_stream_gnutls_new (gnutls);
- }
-
return TRUE;
}
@@ -442,228 +258,16 @@ g_tls_connection_gnutls_finalize (GObject *object)
GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- g_clear_object (&priv->base_io_stream);
- g_clear_object (&priv->base_socket);
-
- g_clear_object (&priv->tls_istream);
- g_clear_object (&priv->tls_ostream);
-
if (priv->session)
gnutls_deinit (priv->session);
if (priv->creds)
gnutls_certificate_free_credentials (priv->creds);
- g_clear_object (&priv->database);
- g_clear_object (&priv->certificate);
- g_clear_object (&priv->peer_certificate);
-
- g_mutex_clear (&priv->verify_certificate_mutex);
- g_cond_clear (&priv->verify_certificate_condition);
-
- g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
-
g_free (priv->interaction_id);
- g_clear_object (&priv->interaction);
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
- g_clear_pointer (&priv->advertised_protocols, g_strfreev);
- g_clear_pointer (&priv->negotiated_protocol, g_free);
-#endif
-
- g_clear_error (&priv->handshake_error);
- g_clear_error (&priv->read_error);
- g_clear_error (&priv->write_error);
-
- g_clear_pointer (&priv->handshake_context, g_main_context_unref);
-
- /* This must always be NULL here, as it holds a reference to @gnutls as
- * its source object. However, we clear it anyway just in case this changes
- * in future. */
- g_clear_object (&priv->implicit_handshake);
-
- g_clear_object (&priv->read_cancellable);
- g_clear_object (&priv->write_cancellable);
-
- g_clear_object (&priv->waiting_for_op);
- g_mutex_clear (&priv->op_mutex);
G_OBJECT_CLASS (g_tls_connection_gnutls_parent_class)->finalize (object);
}
-static void
-g_tls_connection_gnutls_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GTlsBackend *backend;
-
- switch (prop_id)
- {
- case PROP_BASE_IO_STREAM:
- g_value_set_object (value, priv->base_io_stream);
- break;
-
- case PROP_BASE_SOCKET:
- g_value_set_object (value, priv->base_socket);
- break;
-
- case PROP_REQUIRE_CLOSE_NOTIFY:
- g_value_set_boolean (value, priv->require_close_notify);
- break;
-
- case PROP_REHANDSHAKE_MODE:
- g_value_set_enum (value, priv->rehandshake_mode);
- break;
-
- case PROP_USE_SYSTEM_CERTDB:
- g_value_set_boolean (value, priv->is_system_certdb);
- break;
-
- case PROP_DATABASE:
- if (priv->database_is_unset)
- {
- backend = g_tls_backend_get_default ();
- priv->database = g_tls_backend_get_default_database (backend);
- priv->database_is_unset = FALSE;
- }
- g_value_set_object (value, priv->database);
- break;
-
- case PROP_CERTIFICATE:
- g_value_set_object (value, priv->certificate);
- break;
-
- case PROP_INTERACTION:
- g_value_set_object (value, priv->interaction);
- break;
-
- case PROP_PEER_CERTIFICATE:
- g_value_set_object (value, priv->peer_certificate);
- break;
-
- case PROP_PEER_CERTIFICATE_ERRORS:
- g_value_set_flags (value, priv->peer_certificate_errors);
- break;
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
- case PROP_ADVERTISED_PROTOCOLS:
- g_value_set_boxed (value, priv->advertised_protocols);
- break;
-
- case PROP_NEGOTIATED_PROTOCOL:
- g_value_set_string (value, priv->negotiated_protocol);
- break;
-#endif
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
-g_tls_connection_gnutls_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GInputStream *istream;
- GOutputStream *ostream;
- gboolean system_certdb;
- GTlsBackend *backend;
-
- switch (prop_id)
- {
- case PROP_BASE_IO_STREAM:
- g_assert (g_value_get_object (value) == NULL ||
- priv->base_socket == NULL);
-
- if (priv->base_io_stream)
- {
- g_object_unref (priv->base_io_stream);
- priv->base_istream = NULL;
- priv->base_ostream = NULL;
- }
- priv->base_io_stream = g_value_dup_object (value);
- if (!priv->base_io_stream)
- return;
-
- istream = g_io_stream_get_input_stream (priv->base_io_stream);
- ostream = g_io_stream_get_output_stream (priv->base_io_stream);
-
- if (G_IS_POLLABLE_INPUT_STREAM (istream) &&
- g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (istream)))
- priv->base_istream = G_POLLABLE_INPUT_STREAM (istream);
- if (G_IS_POLLABLE_OUTPUT_STREAM (ostream) &&
- g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (ostream)))
- priv->base_ostream = G_POLLABLE_OUTPUT_STREAM (ostream);
- break;
-
- case PROP_BASE_SOCKET:
- g_assert (g_value_get_object (value) == NULL ||
- priv->base_io_stream == NULL);
-
- g_clear_object (&priv->base_socket);
- priv->base_socket = g_value_dup_object (value);
- break;
-
- case PROP_REQUIRE_CLOSE_NOTIFY:
- priv->require_close_notify = g_value_get_boolean (value);
- break;
-
- case PROP_REHANDSHAKE_MODE:
- priv->rehandshake_mode = g_value_get_enum (value);
- break;
-
- case PROP_USE_SYSTEM_CERTDB:
- system_certdb = g_value_get_boolean (value);
- if (system_certdb != priv->is_system_certdb)
- {
- g_clear_object (&priv->database);
- if (system_certdb)
- {
- backend = g_tls_backend_get_default ();
- priv->database = g_tls_backend_get_default_database (backend);
- }
- priv->is_system_certdb = system_certdb;
- priv->database_is_unset = FALSE;
- }
- break;
-
- case PROP_DATABASE:
- g_clear_object (&priv->database);
- priv->database = g_value_dup_object (value);
- priv->is_system_certdb = FALSE;
- priv->database_is_unset = FALSE;
- break;
-
- case PROP_CERTIFICATE:
- if (priv->certificate)
- g_object_unref (priv->certificate);
- priv->certificate = g_value_dup_object (value);
- break;
-
- case PROP_INTERACTION:
- g_clear_object (&priv->interaction);
- priv->interaction = g_value_dup_object (value);
- break;
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
- case PROP_ADVERTISED_PROTOCOLS:
- g_clear_pointer (&priv->advertised_protocols, g_strfreev);
- priv->advertised_protocols = g_value_dup_boxed (value);
- break;
-#endif
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
gnutls_certificate_credentials_t
g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *gnutls)
{
@@ -693,809 +297,190 @@ g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
if (cert)
{
- g_tls_certificate_gnutls_copy (G_TLS_CERTIFICATE_GNUTLS (cert),
- priv->interaction_id,
- pcert, pcert_length, pkey);
- }
- else
- {
- *pcert = NULL;
- *pcert_length = 0;
- *pkey = NULL;
- }
-}
-
-typedef enum {
- G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE,
- G_TLS_CONNECTION_GNUTLS_OP_READ,
- G_TLS_CONNECTION_GNUTLS_OP_WRITE,
- G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ,
- G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE,
- G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH,
-} GTlsConnectionGnutlsOp;
-
-static gboolean
-claim_op (GTlsConnectionGnutls *gnutls,
- GTlsConnectionGnutlsOp op,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- try_again:
- if (g_cancellable_set_error_if_cancelled (cancellable, error))
- return FALSE;
-
- g_mutex_lock (&priv->op_mutex);
-
- if (((op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE ||
- op == G_TLS_CONNECTION_GNUTLS_OP_READ) &&
- (priv->read_closing || priv->read_closed)) ||
- ((op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE ||
- op == G_TLS_CONNECTION_GNUTLS_OP_WRITE) &&
- (priv->write_closing || priv->write_closed)))
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
- _("Connection is closed"));
- g_mutex_unlock (&priv->op_mutex);
- return FALSE;
- }
-
- if (priv->handshake_error &&
- op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH &&
- op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ &&
- op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
- {
- if (error)
- *error = g_error_copy (priv->handshake_error);
- g_mutex_unlock (&priv->op_mutex);
- return FALSE;
- }
-
- if (op != G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
- {
- if (op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH &&
- op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ &&
- op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE &&
- priv->need_handshake)
- {
- priv->need_handshake = FALSE;
- priv->handshaking = TRUE;
- if (!do_implicit_handshake (gnutls, timeout, cancellable, error))
- {
- g_mutex_unlock (&priv->op_mutex);
- return FALSE;
- }
- }
-
- if (priv->need_finish_handshake &&
- priv->implicit_handshake)
- {
- GError *my_error = NULL;
- gboolean success;
-
- priv->need_finish_handshake = FALSE;
-
- g_mutex_unlock (&priv->op_mutex);
- success = finish_handshake (gnutls, priv->implicit_handshake, &my_error);
- g_clear_object (&priv->implicit_handshake);
- g_clear_pointer (&priv->handshake_context, g_main_context_unref);
- g_mutex_lock (&priv->op_mutex);
-
- if (op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH &&
- op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ &&
- op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE &&
- (!success || g_cancellable_set_error_if_cancelled (cancellable, &my_error)))
- {
- g_propagate_error (error, my_error);
- g_mutex_unlock (&priv->op_mutex);
- return FALSE;
- }
-
- g_clear_error (&my_error);
- }
- }
-
- if (priv->handshaking &&
- timeout != 0 &&
- g_main_context_is_owner (priv->handshake_context))
- {
- /* Cannot perform a blocking operation during a handshake on the
- * same thread that triggered the handshake. The only way this can
- * occur is if the application is doing something weird in its
- * accept-certificate callback. Allowing a blocking op would stall
- * the handshake (forever, if there's no timeout). Even a close
- * op would deadlock here.
- */
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot perform blocking operation during
TLS handshake"));
- g_mutex_unlock (&priv->op_mutex);
- return FALSE;
- }
-
- if ((op != G_TLS_CONNECTION_GNUTLS_OP_WRITE && priv->reading) ||
- (op != G_TLS_CONNECTION_GNUTLS_OP_READ && priv->writing) ||
- (op != G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE && priv->handshaking))
- {
- GPollFD fds[2];
- int nfds;
- gint64 start_time;
- gint result = 1; /* if the loop is never entered, it’s as if we cancelled early */
-
- g_cancellable_reset (priv->waiting_for_op);
-
- g_mutex_unlock (&priv->op_mutex);
-
- if (timeout == 0)
- {
- /* Intentionally not translated because this is not a fatal error to be
- * presented to the user, and to avoid this showing up in profiling. */
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, "Operation would block");
- return FALSE;
- }
-
- g_cancellable_make_pollfd (priv->waiting_for_op, &fds[0]);
- if (g_cancellable_make_pollfd (cancellable, &fds[1]))
- nfds = 2;
- else
- nfds = 1;
-
- /* Convert from microseconds to milliseconds. */
- if (timeout != -1)
- timeout = timeout / 1000;
-
- /* Poll until cancellation or the timeout is reached. */
- start_time = g_get_monotonic_time ();
-
- while (!g_cancellable_is_cancelled (priv->waiting_for_op) &&
- !g_cancellable_is_cancelled (cancellable))
- {
- result = g_poll (fds, nfds, timeout);
-
- if (result == 0)
- break;
- if (result != -1 || errno != EINTR)
- continue;
-
- if (timeout != -1)
- {
- timeout -= (g_get_monotonic_time () - start_time) / 1000;
- if (timeout < 0)
- timeout = 0;
- }
- }
-
- if (nfds > 1)
- g_cancellable_release_fd (cancellable);
-
- if (result == 0)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
- _("Socket I/O timed out"));
- return FALSE;
- }
-
- goto try_again;
- }
-
- if (op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
- {
- priv->handshaking = TRUE;
- priv->need_handshake = FALSE;
- }
- if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ)
- priv->read_closing = TRUE;
- if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
- priv->write_closing = TRUE;
-
- if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
- priv->reading = TRUE;
- if (op != G_TLS_CONNECTION_GNUTLS_OP_READ)
- priv->writing = TRUE;
-
- g_mutex_unlock (&priv->op_mutex);
- return TRUE;
-}
-
-static void
-yield_op (GTlsConnectionGnutls *gnutls,
- GTlsConnectionGnutlsOp op)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- g_mutex_lock (&priv->op_mutex);
-
- if (op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
- priv->handshaking = FALSE;
- if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ)
- priv->read_closing = FALSE;
- if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
- priv->write_closing = FALSE;
-
- if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
- priv->reading = FALSE;
- if (op != G_TLS_CONNECTION_GNUTLS_OP_READ)
- priv->writing = FALSE;
-
- g_cancellable_cancel (priv->waiting_for_op);
- g_mutex_unlock (&priv->op_mutex);
-}
-
-static void
-begin_gnutls_io (GTlsConnectionGnutls *gnutls,
- GIOCondition direction,
- gint64 timeout,
- GCancellable *cancellable)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- g_assert (direction & (G_IO_IN | G_IO_OUT));
-
- if (direction & G_IO_IN)
- {
- priv->read_timeout = timeout;
- priv->read_cancellable = cancellable;
- g_clear_error (&priv->read_error);
- }
-
- if (direction & G_IO_OUT)
- {
- priv->write_timeout = timeout;
- priv->write_cancellable = cancellable;
- g_clear_error (&priv->write_error);
- }
-}
-
-static int
-end_gnutls_io (GTlsConnectionGnutls *gnutls,
- GIOCondition direction,
- int status,
- GError **error,
- const char *err_prefix);
-
-static int
-end_gnutls_io (GTlsConnectionGnutls *gnutls,
- GIOCondition direction,
- int status,
- GError **error,
- const char *err_prefix)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GError *my_error = NULL;
-
- g_assert (direction & (G_IO_IN | G_IO_OUT));
- g_assert (!error || !*error);
-
- /* 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 (status == GNUTLS_E_AGAIN ||
- status == GNUTLS_E_WARNING_ALERT_RECEIVED)
- return GNUTLS_E_AGAIN;
-
- if (direction & G_IO_IN)
- {
- priv->read_cancellable = NULL;
- if (status < 0)
- {
- my_error = priv->read_error;
- priv->read_error = NULL;
- }
- else
- g_clear_error (&priv->read_error);
- }
- if (direction & G_IO_OUT)
- {
- priv->write_cancellable = NULL;
- if (status < 0 && !my_error)
- {
- my_error = priv->write_error;
- priv->write_error = NULL;
- }
- else
- g_clear_error (&priv->write_error);
- }
-
- if (status >= 0)
- return status;
-
- if (priv->handshaking && !priv->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) ||
- 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_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
- _("Peer failed to perform TLS handshake"));
- return GNUTLS_E_PULL_ERROR;
- }
- }
-
- if (my_error)
- {
- if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) &&
- !g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
- g_propagate_error (error, my_error);
- return status;
- }
- else if (status == GNUTLS_E_REHANDSHAKE)
- {
- if (priv->rehandshake_mode == G_TLS_REHANDSHAKE_NEVER)
- {
- g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
- _("Peer requested illegal TLS rehandshake"));
- return GNUTLS_E_PULL_ERROR;
- }
-
- g_mutex_lock (&priv->op_mutex);
- if (!priv->handshaking)
- priv->need_handshake = TRUE;
- g_mutex_unlock (&priv->op_mutex);
- return status;
- }
- else if (status == GNUTLS_E_PREMATURE_TERMINATION)
- {
- if (priv->handshaking && !priv->ever_handshaked)
- {
- g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
- _("Peer failed to perform TLS handshake"));
- return GNUTLS_E_PULL_ERROR;
- }
- else if (priv->require_close_notify)
- {
- g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_EOF,
- _("TLS connection closed unexpectedly"));
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
- return status;
- }
- else
- return 0;
- }
- else if (status == GNUTLS_E_NO_CERTIFICATE_FOUND
-#ifdef GNUTLS_E_CERTIFICATE_REQUIRED
- || status == GNUTLS_E_CERTIFICATE_REQUIRED /* Added in GnuTLS 3.6.7 */
-#endif
- )
- {
- g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
- _("TLS connection peer did not send a certificate"));
- return status;
- }
- else if (status == GNUTLS_E_CERTIFICATE_ERROR)
- {
- g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
- _("Unacceptable TLS certificate"));
- return status;
- }
- else if (status == GNUTLS_E_FATAL_ALERT_RECEIVED)
- {
- g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
- _("Peer sent fatal TLS alert: %s"),
- gnutls_alert_get_name (gnutls_alert_get (priv->session)));
- return status;
- }
- else if (status == GNUTLS_E_INAPPROPRIATE_FALLBACK)
- {
- g_set_error_literal (error, G_TLS_ERROR,
-#if GLIB_CHECK_VERSION(2, 60, 0)
- G_TLS_ERROR_INAPPROPRIATE_FALLBACK,
-#else
- G_TLS_ERROR_MISC,
-#endif
- _("Protocol version downgrade attack detected"));
- return status;
- }
- else if (status == GNUTLS_E_LARGE_PACKET)
- {
- guint mtu = gnutls_dtls_get_data_mtu (priv->session);
- 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 status;
- }
- else if (status == GNUTLS_E_TIMEDOUT)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
- _("The operation timed out"));
- return status;
- }
-
- if (error)
- {
- *error = g_error_new (G_TLS_ERROR, G_TLS_ERROR_MISC, "%s: %s",
- err_prefix, gnutls_strerror (status));
- }
- return status;
-}
-
-#define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable) \
- begin_gnutls_io (gnutls, direction, timeout, cancellable); \
- do {
-
-#define END_GNUTLS_IO(gnutls, direction, ret, errmsg, err) \
- } while ((ret = end_gnutls_io (gnutls, direction, ret, err, errmsg)) == GNUTLS_E_AGAIN);
-
-/* Checks whether the underlying base stream or GDatagramBased meets
- * @condition. */
-static gboolean
-g_tls_connection_gnutls_base_check (GTlsConnectionGnutls *gnutls,
- GIOCondition condition)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- if (g_tls_connection_gnutls_is_dtls (gnutls))
- return g_datagram_based_condition_check (priv->base_socket,
- condition);
- else if (condition & G_IO_IN)
- return g_pollable_input_stream_is_readable (priv->base_istream);
- else if (condition & G_IO_OUT)
- return g_pollable_output_stream_is_writable (priv->base_ostream);
- else
- g_assert_not_reached ();
-}
-
-/* Checks whether the (D)TLS stream meets @condition; not the underlying base
- * stream or GDatagramBased. */
-gboolean
-g_tls_connection_gnutls_check (GTlsConnectionGnutls *gnutls,
- GIOCondition condition)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- /* Racy, but worst case is that we just get WOULD_BLOCK back */
- if (priv->need_finish_handshake)
- return TRUE;
-
- /* If a handshake or close is in progress, then tls_istream and
- * tls_ostream are blocked, regardless of the base stream status.
- */
- if (priv->handshaking)
- return FALSE;
-
- if (((condition & G_IO_IN) && priv->read_closing) ||
- ((condition & G_IO_OUT) && priv->write_closing))
- return FALSE;
-
- /* Defer to the base stream or GDatagramBased. */
- return g_tls_connection_gnutls_base_check (gnutls, condition);
-}
-
-typedef struct {
- GSource source;
-
- GTlsConnectionGnutls *gnutls;
- /* Either a GDatagramBased (datagram mode), or a GPollableInputStream or
- * GPollableOutputStream (streaming mode):
- */
- GObject *base;
-
- GSource *child_source;
- GIOCondition condition;
-
- gboolean io_waiting;
- gboolean op_waiting;
-} GTlsConnectionGnutlsSource;
-
-static gboolean
-gnutls_source_prepare (GSource *source,
- gint *timeout)
-{
- *timeout = -1;
- return FALSE;
-}
-
-static gboolean
-gnutls_source_check (GSource *source)
-{
- return FALSE;
-}
-
-/* Use a custom dummy callback instead of g_source_set_dummy_callback(), as that
- * uses a GClosure and is slow. (The GClosure is necessary to deal with any
- * function prototype.) */
-static gboolean
-dummy_callback (gpointer data)
-{
- return G_SOURCE_CONTINUE;
-}
-
-static void
-gnutls_source_sync (GTlsConnectionGnutlsSource *gnutls_source)
-{
- GTlsConnectionGnutls *gnutls = gnutls_source->gnutls;
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- gboolean io_waiting, op_waiting;
-
- /* Was the source destroyed earlier in this main context iteration? */
- if (g_source_is_destroyed ((GSource *)gnutls_source))
- return;
-
- g_mutex_lock (&priv->op_mutex);
- if (((gnutls_source->condition & G_IO_IN) && priv->reading) ||
- ((gnutls_source->condition & G_IO_OUT) && priv->writing) ||
- (priv->handshaking && !priv->need_finish_handshake))
- op_waiting = TRUE;
- else
- op_waiting = FALSE;
-
- if (!op_waiting && !priv->need_handshake &&
- !priv->need_finish_handshake)
- io_waiting = TRUE;
- else
- io_waiting = FALSE;
- g_mutex_unlock (&priv->op_mutex);
-
- if (op_waiting == gnutls_source->op_waiting &&
- io_waiting == gnutls_source->io_waiting)
- return;
- gnutls_source->op_waiting = op_waiting;
- gnutls_source->io_waiting = io_waiting;
-
- if (gnutls_source->child_source)
- {
- g_source_remove_child_source ((GSource *)gnutls_source,
- gnutls_source->child_source);
- g_source_unref (gnutls_source->child_source);
- }
-
- if (op_waiting)
- gnutls_source->child_source = g_cancellable_source_new (priv->waiting_for_op);
- else if (io_waiting && G_IS_DATAGRAM_BASED (gnutls_source->base))
- gnutls_source->child_source = g_datagram_based_create_source (priv->base_socket,
gnutls_source->condition, NULL);
- else if (io_waiting && G_IS_POLLABLE_INPUT_STREAM (gnutls_source->base))
- gnutls_source->child_source = g_pollable_input_stream_create_source (priv->base_istream, NULL);
- else if (io_waiting && G_IS_POLLABLE_OUTPUT_STREAM (gnutls_source->base))
- gnutls_source->child_source = g_pollable_output_stream_create_source (priv->base_ostream, NULL);
- else
- gnutls_source->child_source = g_timeout_source_new (0);
-
- g_source_set_callback (gnutls_source->child_source, dummy_callback, NULL, NULL);
- g_source_add_child_source ((GSource *)gnutls_source, gnutls_source->child_source);
-}
-
-static gboolean
-gnutls_source_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
-{
- GDatagramBasedSourceFunc datagram_based_func = (GDatagramBasedSourceFunc)callback;
- GPollableSourceFunc pollable_func = (GPollableSourceFunc)callback;
- GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
- gboolean ret;
-
- if (G_IS_DATAGRAM_BASED (gnutls_source->base))
- ret = (*datagram_based_func) (G_DATAGRAM_BASED (gnutls_source->base),
- gnutls_source->condition, user_data);
- else
- ret = (*pollable_func) (gnutls_source->base, user_data);
-
- if (ret)
- gnutls_source_sync (gnutls_source);
-
- return ret;
-}
-
-static void
-gnutls_source_finalize (GSource *source)
-{
- GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
-
- g_object_unref (gnutls_source->gnutls);
- g_source_unref (gnutls_source->child_source);
-}
-
-static gboolean
-g_tls_connection_gnutls_source_closure_callback (GObject *stream,
- gpointer data)
-{
- GClosure *closure = data;
-
- GValue param = { 0, };
- GValue result_value = { 0, };
- gboolean result;
-
- g_value_init (&result_value, G_TYPE_BOOLEAN);
-
- g_value_init (¶m, G_TYPE_OBJECT);
- g_value_set_object (¶m, stream);
-
- g_closure_invoke (closure, &result_value, 1, ¶m, NULL);
-
- result = g_value_get_boolean (&result_value);
- g_value_unset (&result_value);
- g_value_unset (¶m);
-
- return result;
-}
-
-static gboolean
-g_tls_connection_gnutls_source_dtls_closure_callback (GObject *stream,
- GIOCondition condition,
- gpointer data)
-{
- GClosure *closure = data;
-
- GValue param[2] = { G_VALUE_INIT, G_VALUE_INIT };
- GValue result_value = G_VALUE_INIT;
- gboolean result;
-
- g_value_init (&result_value, G_TYPE_BOOLEAN);
-
- g_value_init (¶m[0], G_TYPE_DATAGRAM_BASED);
- g_value_set_object (¶m[0], stream);
- g_value_init (¶m[1], G_TYPE_IO_CONDITION);
- g_value_set_flags (¶m[1], condition);
-
- g_closure_invoke (closure, &result_value, 2, param, NULL);
-
- result = g_value_get_boolean (&result_value);
- g_value_unset (&result_value);
- g_value_unset (¶m[0]);
- g_value_unset (¶m[1]);
-
- return result;
-}
-
-static GSourceFuncs gnutls_tls_source_funcs =
-{
- gnutls_source_prepare,
- gnutls_source_check,
- gnutls_source_dispatch,
- gnutls_source_finalize,
- (GSourceFunc)g_tls_connection_gnutls_source_closure_callback,
- (GSourceDummyMarshal)g_cclosure_marshal_generic
-};
-
-static GSourceFuncs gnutls_dtls_source_funcs =
-{
- gnutls_source_prepare,
- gnutls_source_check,
- gnutls_source_dispatch,
- gnutls_source_finalize,
- (GSourceFunc)g_tls_connection_gnutls_source_dtls_closure_callback,
- (GSourceDummyMarshal)g_cclosure_marshal_generic
-};
-
-GSource *
-g_tls_connection_gnutls_create_source (GTlsConnectionGnutls *gnutls,
- GIOCondition condition,
- GCancellable *cancellable)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GSource *source, *cancellable_source;
- GTlsConnectionGnutlsSource *gnutls_source;
-
- if (g_tls_connection_gnutls_is_dtls (gnutls))
- {
- source = g_source_new (&gnutls_dtls_source_funcs,
- sizeof (GTlsConnectionGnutlsSource));
+ g_tls_certificate_gnutls_copy (G_TLS_CERTIFICATE_GNUTLS (cert),
+ priv->interaction_id,
+ pcert, pcert_length, pkey);
}
else
{
- source = g_source_new (&gnutls_tls_source_funcs,
- sizeof (GTlsConnectionGnutlsSource));
+ *pcert = NULL;
+ *pcert_length = 0;
+ *pkey = NULL;
}
- g_source_set_name (source, "GTlsConnectionGnutlsSource");
- gnutls_source = (GTlsConnectionGnutlsSource *)source;
- gnutls_source->gnutls = g_object_ref (gnutls);
- gnutls_source->condition = condition;
- if (g_tls_connection_gnutls_is_dtls (gnutls))
- gnutls_source->base = G_OBJECT (gnutls);
- else if (priv->tls_istream != NULL && condition & G_IO_IN)
- gnutls_source->base = G_OBJECT (priv->tls_istream);
- else if (priv->tls_ostream != NULL && condition & G_IO_OUT)
- gnutls_source->base = G_OBJECT (priv->tls_ostream);
- else
- g_assert_not_reached ();
+}
+
+static GTlsConnectionBaseStatus
+end_gnutls_io (GTlsConnectionGnutls *gnutls,
+ GIOCondition direction,
+ int ret,
+ GError **error,
+ const char *err_prefix)
+{
+ GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (gnutls);
+ GTlsConnectionBaseStatus status;
+ gboolean handshaking;
+ gboolean ever_handshaked;
+ GError *my_error = NULL;
- gnutls_source->op_waiting = (gboolean) -1;
- gnutls_source->io_waiting = (gboolean) -1;
- gnutls_source_sync (gnutls_source);
+ /* 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;
- if (cancellable)
+ 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)
{
- cancellable_source = g_cancellable_source_new (cancellable);
- g_source_set_dummy_callback (cancellable_source);
- g_source_add_child_source (source, cancellable_source);
- g_source_unref (cancellable_source);
+ if (my_error)
+ g_propagate_error (error, my_error);
+ return status;
}
- return source;
-}
+ g_assert (status == G_TLS_CONNECTION_BASE_ERROR);
-static GSource *
-g_tls_connection_gnutls_dtls_create_source (GDatagramBased *datagram_based,
- GIOCondition condition,
- GCancellable *cancellable)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+// FIXME: Right place?
+ G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
- return g_tls_connection_gnutls_create_source (gnutls, condition, cancellable);
-}
+ handshaking = g_tls_connection_base_is_handshaking (tls);
+ ever_handshaked = g_tls_connection_base_ever_handshaked (tls);
-static GIOCondition
-g_tls_connection_gnutls_condition_check (GDatagramBased *datagram_based,
- GIOCondition condition)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+ if (handshaking && !ever_handshaked)
+ {
+ 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_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+ _("Peer failed to perform TLS handshake"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+ }
- return (g_tls_connection_gnutls_check (gnutls, condition)) ? condition : 0;
-}
+ if (ret == GNUTLS_E_REHANDSHAKE)
+ {
+ if (g_tls_connection_get_rehandshake_mode (G_TLS_CONNECTION (gnutls)) == G_TLS_REHANDSHAKE_NEVER)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Peer requested illegal TLS rehandshake"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
-static gboolean
-g_tls_connection_gnutls_condition_wait (GDatagramBased *datagram_based,
- GIOCondition condition,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GPollFD fds[2];
- guint n_fds;
- gint result = 1; /* if the loop is never entered, it’s as if we cancelled early */
- gint64 start_time;
+ return G_TLS_CONNECTION_BASE_REHANDSHAKE;
+ }
- if (g_cancellable_set_error_if_cancelled (cancellable, error))
- return FALSE;
+ if (ret == GNUTLS_E_PREMATURE_TERMINATION)
+ {
+ if (handshaking && !ever_handshaked)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+ _("Peer failed to perform TLS handshake"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
- /* Convert from microseconds to milliseconds. */
- if (timeout != -1)
- timeout = timeout / 1000;
+ if (g_tls_connection_get_require_close_notify (G_TLS_CONNECTION (gnutls)))
+ {
+ 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;
+ }
- start_time = g_get_monotonic_time ();
+ return G_TLS_CONNECTION_BASE_OK;
+ }
- g_cancellable_make_pollfd (priv->waiting_for_op, &fds[0]);
- n_fds = 1;
+ 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 (g_cancellable_make_pollfd (cancellable, &fds[1]))
- n_fds++;
+ if (ret == GNUTLS_E_CERTIFICATE_ERROR)
+ {
+ 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;
+ }
- while (!g_tls_connection_gnutls_condition_check (datagram_based, condition) &&
- !g_cancellable_is_cancelled (cancellable))
+ if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
{
- result = g_poll (fds, n_fds, timeout);
- if (result == 0)
- break;
- if (result != -1 || errno != EINTR)
- continue;
+ 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 (priv->session)));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
- if (timeout != -1)
- {
- timeout -= (g_get_monotonic_time () - start_time) / 1000;
- if (timeout < 0)
- timeout = 0;
- }
+ if (ret == GNUTLS_E_INAPPROPRIATE_FALLBACK)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR,
+#if GLIB_CHECK_VERSION(2, 60, 0)
+ G_TLS_ERROR_INAPPROPRIATE_FALLBACK,
+#else
+ G_TLS_ERROR_MISC,
+#endif
+ _("Protocol version downgrade attack detected"));
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- if (n_fds > 1)
- g_cancellable_release_fd (cancellable);
+ if (ret == GNUTLS_E_LARGE_PACKET)
+ {
+ guint mtu = gnutls_dtls_get_data_mtu (priv->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 (result == 0)
+ if (ret == GNUTLS_E_TIMEDOUT)
{
+ g_clear_error (&my_error);
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
- _("Socket I/O timed out"));
- return FALSE;
+ _("The operation timed out"));
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- return !g_cancellable_set_error_if_cancelled (cancellable, 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 (status));
+ }
+ return G_TLS_CONNECTION_BASE_ERROR;
}
+#define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable) \
+ g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (gnutls), \
+ direction, timeout, cancellable); \
+ do {
+
+#define END_GNUTLS_IO(gnutls, direction, ret, status, errmsg, err) \
+ status = end_gnutls_io (gnutls, direction, ret, err, errmsg, gnutls_strerror (ret)); \
+ } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+
static void
set_gnutls_error (GTlsConnectionGnutls *gnutls,
GError *error)
{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (gnutls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
/* We set EINTR rather than EAGAIN for G_IO_ERROR_WOULD_BLOCK so
@@ -1511,7 +496,7 @@ set_gnutls_error (GTlsConnectionGnutls *gnutls,
{
/* Return EAGAIN while handshaking so that GnuTLS handles retries for us
* internally in its handshaking code. */
- if (priv->base_socket && priv->handshaking)
+ if (g_tls_connection_base_is_dtls (tls) && g_tls_connection_base_is_handshaking (tls))
gnutls_transport_set_errno (priv->session, EAGAIN);
else
gnutls_transport_set_errno (priv->session, EINTR);
@@ -1529,43 +514,44 @@ g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t transport_data,
void *buf,
size_t buflen)
{
+ GTlsConnectionBase *tls = transport_data;
GTlsConnectionGnutls *gnutls = transport_data;
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
ssize_t ret;
- /* If priv->read_error is non-%NULL when we're called, it means
- * that an error previously occurred, but gnutls decided not to
+ /* If read_error is nonnull when we're called, it means
+ * that an error previously occurred, but GnuTLS decided not to
* propagate it. So it's correct for us to just clear it. (Usually
* this means it ignored an EAGAIN after a short read, and now
* we'll return EAGAIN again, which it will obey this time.)
*/
- g_clear_error (&priv->read_error);
+ g_clear_error (g_tls_connection_base_get_read_error (tls));
- if (g_tls_connection_gnutls_is_dtls (gnutls))
+ if (g_tls_connection_base_is_dtls (tls))
{
GInputVector vector = { buf, buflen };
GInputMessage message = { NULL, &vector, 1, 0, 0, NULL, NULL };
- ret = g_datagram_based_receive_messages (priv->base_socket,
+ ret = g_datagram_based_receive_messages (g_tls_connection_base_get_base_socket (tls),
&message, 1, 0,
- priv->handshaking ? 0 : priv->read_timeout,
- priv->read_cancellable,
- &priv->read_error);
+ g_tls_connection_base_is_handshaking (tls) ? 0 :
g_tls_connection_base_get_read_timeout (tls),
+ g_tls_connection_base_get_read_cancellable (tls),
+ g_tls_connection_base_get_read_error (tls));
if (ret > 0)
ret = message.bytes_received;
}
else
{
- ret = g_pollable_stream_read (G_INPUT_STREAM (priv->base_istream),
+ ret = g_pollable_stream_read (G_INPUT_STREAM (g_tls_connection_base_get_base_istream (tls)),
buf, buflen,
- (priv->read_timeout != 0),
- priv->read_cancellable,
- &priv->read_error);
+ g_tls_connection_base_get_read_timeout (tls) != 0,
+ g_tls_connection_base_get_read_cancellable (tls),
+ g_tls_connection_base_get_read_error (tls));
}
if (ret < 0)
- set_gnutls_error (gnutls, priv->read_error);
+ set_gnutls_error (gnutls, *g_tls_connection_base_get_read_error (tls));
return ret;
}
@@ -1575,38 +561,39 @@ g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t transport_data,
const void *buf,
size_t buflen)
{
+ GTlsConnectionbase *tls = transport_data;
GTlsConnectionGnutls *gnutls = transport_data;
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
ssize_t ret;
/* See comment in pull_func. */
- g_clear_error (&priv->write_error);
+ g_clear_error (g_tls_connection_base_get_write_error (tls));
- if (g_tls_connection_gnutls_is_dtls (gnutls))
+ if (g_tls_connection_base_is_dtls (tls))
{
GOutputVector vector = { buf, buflen };
GOutputMessage message = { NULL, &vector, 1, 0, NULL, 0 };
- ret = g_datagram_based_send_messages (priv->base_socket,
+ ret = g_datagram_based_send_messages (g_tls_connection_base_get_base_socket (tls),
&message, 1, 0,
- priv->write_timeout,
- priv->write_cancellable,
- &priv->write_error);
+ g_tls_connection_base_get_write_timeout (tls),
+ g_tls_connection_base_get_write_cancellable (tls),
+ g_tls_connection_base_get_write_error (tls));
if (ret > 0)
ret = message.bytes_sent;
}
else
{
- ret = g_pollable_stream_write (G_OUTPUT_STREAM (priv->base_ostream),
+ ret = g_pollable_stream_write (G_OUTPUT_STREAM (g_tls_connection_base_get_base_ostream (tls)),
buf, buflen,
- (priv->write_timeout != 0),
- priv->write_cancellable,
- &priv->write_error);
+ g_tls_connection_base_get_write_timeout (tls) != 0,
+ g_tls_connection_base_get_write_cancellable (tls),
+ g_tls_connection_base_get_write_error (tls));
}
if (ret < 0)
- set_gnutls_error (gnutls, priv->write_error);
+ set_gnutls_error (gnutls, *g_tls_connection_base_get_write_error (tls));
return ret;
}
@@ -1616,17 +603,17 @@ g_tls_connection_gnutls_vec_push_func (gnutls_transport_ptr_t transport_data,
const giovec_t *iov,
int iovcnt)
{
+ GTlsConnectionBase *tls = transport_data;
GTlsConnectionGnutls *gnutls = transport_data;
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
ssize_t ret;
GOutputMessage message = { NULL, };
GOutputVector *vectors;
- /* This function should only be set if we’re using base_socket. */
- g_assert (priv->base_socket != NULL);
+ g_assert (g_tls_connection_base_is_dtls (tls));
/* See comment in pull_func. */
- g_clear_error (&priv->write_error);
+ g_clear_error (g_tls_connection_base_get_write_error (tls));
/* this entire expression will be evaluated at compile time */
if (sizeof *iov == sizeof *vectors &&
@@ -1655,16 +642,16 @@ g_tls_connection_gnutls_vec_push_func (gnutls_transport_ptr_t transport_data,
message.num_vectors = iovcnt;
}
- ret = g_datagram_based_send_messages (priv->base_socket,
+ ret = g_datagram_based_send_messages (g_tls_connection_base_get_base_socket (tls),
&message, 1, 0,
- priv->write_timeout,
- priv->write_cancellable,
- &priv->write_error);
+ g_tls_connection_base_get_write_timeout (tls),
+ g_tls_connection_base_get_write_cancellable (tls),
+ g_tls_connection_base_get_write_error (tls));
if (ret > 0)
ret = message.bytes_sent;
else if (ret < 0)
- set_gnutls_error (gnutls, priv->write_error);
+ set_gnutls_error (gnutls, *g_tls_connection_base_get_write_error (tls));
return ret;
}
@@ -1706,12 +693,13 @@ static int
g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data,
unsigned int ms)
{
+ GTlsConnectionBase *tls = transport_data;
GTlsConnectionGnutls *gnutls = transport_data;
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
/* Fast path. */
- if (g_tls_connection_gnutls_base_check (gnutls, G_IO_IN) ||
- g_cancellable_is_cancelled (priv->read_cancellable))
+ if (g_tls_connection_base_base_check (tls, G_IO_IN) ||
+ g_cancellable_is_cancelled (g_tls_connection_base_get_read_cancellable (tls)))
return 1;
/* If @ms is 0, GnuTLS wants an instant response, so there’s no need to
@@ -1732,15 +720,17 @@ g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data
/* Create a read source. We cannot use g_source_set_ready_time() on this
* to combine it with the @timeout_source, as that could mess with the
* internals of the #GDatagramBased’s #GSource implementation. */
- if (g_tls_connection_gnutls_is_dtls (gnutls))
+ if (g_tls_connection_base_is_dtls (tls))
{
- read_source = g_datagram_based_create_source (priv->base_socket, G_IO_IN, NULL);
+ read_source = g_datagram_based_create_source (g_tls_connection_base_get_base_socket (tls),
+ G_IO_IN, NULL);
g_source_set_callback (read_source, (GSourceFunc)read_datagram_based_cb,
&read_done, NULL);
}
else
{
- read_source = g_pollable_input_stream_create_source (priv->base_istream, NULL);
+ read_source = g_pollable_input_stream_create_source (g_tls_connection_base_get_base_istream (tls),
+ NULL);
g_source_set_callback (read_source, (GSourceFunc)read_pollable_cb,
&read_done, NULL);
}
@@ -1760,14 +750,40 @@ g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data
/* If @read_source was dispatched due to cancellation, the resulting error
* will be handled in g_tls_connection_gnutls_pull_func(). */
- if (g_tls_connection_gnutls_base_check (gnutls, G_IO_IN) ||
- g_cancellable_is_cancelled (priv->read_cancellable))
+ if (g_tls_connection_base_base_check (tls, G_IO_IN) ||
+ g_cancellable_is_cancelled (g_tls_connection_base_get_read_cancellable (tls)))
return 1;
}
return 0;
}
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_request_rehandshake (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionGnutls *gnutls;
+ GTlsConnectionBaseStatus status;
+ int ret;
+
+ /* On a client-side connection, gnutls_handshake() itself will start
+ * a rehandshake, so we only need to do something special here for
+ * server-side connections.
+ */
+ if (!G_IS_TLS_SERVER_CONNECTION (tls))
+ return G_TLS_CONNECTION_BASE_OK;
+
+ gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+
+ BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
+ ret = gnutls_rehandshake (gnutls->priv->session);
+ END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status, _("Error performing TLS handshake: %s"), error);
+
+ return status;
+}
+
static GTlsCertificate *
get_peer_certificate_from_session (GTlsConnectionGnutls *gnutls)
{
@@ -1787,6 +803,8 @@ get_peer_certificate_from_session (GTlsConnectionGnutls *gnutls)
return G_TLS_CERTIFICATE (chain);
}
+//MOVE
+#if 0
static GTlsCertificateFlags
verify_peer_certificate (GTlsConnectionGnutls *gnutls,
GTlsCertificate *peer_certificate)
@@ -1842,28 +860,28 @@ static void
update_peer_certificate_and_compute_errors (GTlsConnectionGnutls *gnutls)
{
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+ GTlsCertificate *peer_certificate = NULL;
+ GTlsCertificateFlags peer_certificate_errors = 0;
/* This function must be called from the handshake context thread
- * (probably the main thread, NOT the handshake thread) because it
- * emits notifies that are application-visible.
+ * (probably the main thread, NOT the handshake thread) because
+ * g_tls_connection_base_set_peer_certificate() emits notifies
+ * that are application-visible.
*
* verify_certificate_mutex should be locked.
*/
g_assert (priv->handshake_context);
g_assert (g_main_context_is_owner (priv->handshake_context));
- g_clear_object (&priv->peer_certificate);
- priv->peer_certificate_errors = 0;
-
if (gnutls_certificate_type_get (priv->session) == GNUTLS_CRT_X509)
{
- priv->peer_certificate = get_peer_certificate_from_session (gnutls);
- if (priv->peer_certificate)
- priv->peer_certificate_errors = verify_peer_certificate (gnutls, priv->peer_certificate);
+ peer_certificate = get_peer_certificate_from_session (gnutls);
+ if (peer_certificate)
+ peer_certificate_errors = verify_peer_certificate (gnutls, peer_certificate);
}
- g_object_notify (G_OBJECT (gnutls), "peer-certificate");
- g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
+ g_tls_connection_base_set_peer_certificate (G_TLS_CONNECTION_BASE (gnutls),
+ peer_certificate, peer_certificate_errors);
}
static gboolean
@@ -1951,80 +969,41 @@ verify_certificate_cb (gnutls_session_t session)
/* Return 0 for the handshake to continue, non-zero to terminate. */
return !accepted;
}
+#endif
static void
-handshake_thread (GTask *task,
- gpointer object,
- gpointer task_data,
- GCancellable *cancellable)
+g_tls_connection_gnutls_prepare_handshake (GTlsConnectionBase *tls,
+ gchar **advertised_protocols)
{
- GTlsConnectionGnutls *gnutls = object;
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GError *error = NULL;
- int ret;
- gint64 start_time;
- gint64 timeout;
-
- /* A timeout, in microseconds, must be provided as a gint64* task_data. */
- g_assert (task_data != NULL);
-
- timeout = *((gint64 *)task_data);
- start_time = g_get_monotonic_time ();
- priv->started_handshake = FALSE;
-
- if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE,
- timeout, cancellable, &error))
- {
- g_task_return_error (task, error);
- return;
- }
-
- g_clear_error (&priv->handshake_error);
- if (priv->ever_handshaked && !priv->implicit_handshake)
+ if (advertised_protocols)
{
- if (priv->rehandshake_mode != G_TLS_REHANDSHAKE_UNSAFELY &&
- !gnutls_safe_renegotiation_status (priv->session))
- {
- g_task_return_new_error (task, G_TLS_ERROR, G_TLS_ERROR_MISC,
- _("Peer does not support safe renegotiation"));
- return;
- }
+ gnutls_datum_t *protocols;
+ int n_protos, i;
- if (!G_IS_TLS_CLIENT_CONNECTION (gnutls))
+ n_protos = g_strv_length (advertised_protocols);
+ protocols = g_new (gnutls_datum_t, n_protos);
+ for (i = 0; advertised_protocols[i]; i++)
{
- /* Adjust the timeout for the next operation in the sequence. */
- if (timeout > 0)
- {
- unsigned int timeout_ms;
-
- timeout -= (g_get_monotonic_time () - start_time);
- if (timeout <= 0)
- timeout = 1;
-
- /* Convert from microseconds to milliseconds, but ensure the timeout
- * remains positive. */
- timeout_ms = (timeout + 999) / 1000;
-
- gnutls_handshake_set_timeout (priv->session, timeout_ms);
- gnutls_dtls_set_timeouts (priv->session, 1000 /* default */,
- timeout_ms);
- }
-
- BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
- ret = gnutls_rehandshake (priv->session);
- END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
- _("Error performing TLS handshake"), &error);
-
- if (error)
- {
- g_task_return_error (task, error);
- return;
- }
+ protocols[i].size = strlen (advertised_protocols[i]);
+ protocols[i].data = g_memdup (advertised_protocols[i], protocols[i].size);
}
+ gnutls_alpn_set_protocols (priv->session, protocols, n_protos, 0);
+ g_free (protocols);
}
+}
- priv->started_handshake = TRUE;
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_handshake_thread_handshake (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+ GTlsConnectionBaseStatus status;
+ int ret;
if (!priv->ever_handshaked)
g_tls_connection_gnutls_set_handshake_priority (gnutls);
@@ -2057,93 +1036,25 @@ handshake_thread (GTask *task,
ret = gnutls_record_recv (priv->session, buf, sizeof (buf));
if (ret > -1)
{
- if (!priv->app_data_buf)
- priv->app_data_buf = g_byte_array_new ();
- g_byte_array_append (priv->app_data_buf, buf, ret);
+ g_tls_connection_base_buffer_application_data (tls, buf, ret);
ret = GNUTLS_E_AGAIN;
}
}
- END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
- _("Error performing TLS handshake"), &error);
-
- /* This calls the finish_handshake code of GTlsClientConnectionGnutls
- * or GTlsServerConnectionGnutls. It has nothing to do with
- * GTlsConnectionGnutls's own finish_handshake function, which still
- * needs to be called at this point.
- */
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->finish_handshake (gnutls, &error);
-
- if (error)
- {
- g_task_return_error (task, error);
- }
- else
- {
- priv->ever_handshaked = TRUE;
- g_task_return_boolean (task, TRUE);
- }
-}
-
-static void
-begin_handshake (GTlsConnectionGnutls *gnutls)
-{
-#if GLIB_CHECK_VERSION(2, 60, 0)
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- if (priv->advertised_protocols)
- {
- gnutls_datum_t *protocols;
- int n_protos, i;
-
- n_protos = g_strv_length (priv->advertised_protocols);
- protocols = g_new (gnutls_datum_t, n_protos);
- for (i = 0; priv->advertised_protocols[i]; i++)
- {
- protocols[i].size = strlen (priv->advertised_protocols[i]);
- protocols[i].data = g_memdup (priv->advertised_protocols[i], protocols[i].size);
- }
- gnutls_alpn_set_protocols (priv->session, protocols, n_protos, 0);
- g_free (protocols);
- }
-#endif
+ END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status,
+ _("Error performing TLS handshake"), error);
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
+ return status;
}
-#if GLIB_CHECK_VERSION(2, 60, 0)
static void
-update_negotiated_protocol (GTlsConnectionGnutls *gnutls)
+g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase *tls,
+ gchar **negotiated_protocol,
+ GError **error)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- gchar *orig_negotiated_protocol;
- gnutls_datum_t protocol;
-
- /*
- * Preserve the prior negotiated protocol before clearing it
- */
- orig_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
-
- if (gnutls_alpn_get_selected_protocol (priv->session, &protocol) == 0 && protocol.size > 0)
- priv->negotiated_protocol = g_strndup ((gchar *)protocol.data, protocol.size);
-
- /*
- * Notify only if the negotiated protocol changed
- */
- if (g_strcmp0 (orig_negotiated_protocol, priv->negotiated_protocol) != 0)
- g_object_notify (G_OBJECT (gnutls), "negotiated-protocol");
-
- g_free (orig_negotiated_protocol);
-}
-#endif
-
-static gboolean
-finish_handshake (GTlsConnectionGnutls *gnutls,
- GTask *task,
- GError **error)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- g_assert (error != NULL);
+ g_clear_pointer (&priv->handshake_context, g_main_context_unref);
if (gnutls_session_is_resumed (priv->session))
{
@@ -2169,10 +1080,9 @@ finish_handshake (GTlsConnectionGnutls *gnutls,
_("Unacceptable TLS certificate"));
}
-#if GLIB_CHECK_VERSION(2, 60, 0)
+// FIXME: this needs to remain in GnuTLS class
if (!*error && priv->advertised_protocols)
update_negotiated_protocol (gnutls);
-#endif
if (*error && priv->started_handshake)
priv->handshake_error = g_error_copy (*error);
@@ -2180,44 +1090,6 @@ finish_handshake (GTlsConnectionGnutls *gnutls,
return (*error == NULL);
}
-static void
-sync_handshake_thread_completed (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- g_assert (g_main_context_is_owner (priv->handshake_context));
-
- g_mutex_lock (&priv->op_mutex);
- priv->sync_handshake_completed = TRUE;
- g_mutex_unlock (&priv->op_mutex);
-
- g_main_context_wakeup (priv->handshake_context);
-}
-
-static void
-crank_sync_handshake_context (GTlsConnectionGnutls *gnutls,
- GCancellable *cancellable)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- /* need_finish_handshake will be set inside sync_handshake_thread_completed(),
- * which should only ever be invoked while iterating the handshake context
- * here. So need_finish_handshake should only change on this thread.
- */
- g_mutex_lock (&priv->op_mutex);
- priv->sync_handshake_completed = FALSE;
- while (!priv->sync_handshake_completed && !g_cancellable_is_cancelled (cancellable))
- {
- g_mutex_unlock (&priv->op_mutex);
- g_main_context_iteration (priv->handshake_context, TRUE);
- g_mutex_lock (&priv->op_mutex);
- }
- g_mutex_unlock (&priv->op_mutex);
-}
-
static gboolean
g_tls_connection_gnutls_handshake (GTlsConnection *conn,
GCancellable *cancellable,
@@ -3099,142 +1971,27 @@ g_tls_connection_gnutls_dtls_shutdown_finish (GDtlsConnection *conn,
return g_task_propagate_boolean (G_TASK (result), error);
}
-#if GLIB_CHECK_VERSION(2, 60, 0)
-static void
-g_tls_connection_gnutls_dtls_set_advertised_protocols (GDtlsConnection *conn,
- const gchar * const *protocols)
-{
- g_object_set (conn, "advertised-protocols", protocols, NULL);
-}
-
-const gchar *
-g_tls_connection_gnutls_dtls_get_negotiated_protocol (GDtlsConnection *conn)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- return priv->negotiated_protocol;
-}
-#endif
-
static void
g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GTlsConnectionClass *connection_class = G_TLS_CONNECTION_CLASS (klass);
- GIOStreamClass *iostream_class = G_IO_STREAM_CLASS (klass);
-
- gobject_class->get_property = g_tls_connection_gnutls_get_property;
- gobject_class->set_property = g_tls_connection_gnutls_set_property;
- gobject_class->finalize = g_tls_connection_gnutls_finalize;
-
- connection_class->handshake = g_tls_connection_gnutls_handshake;
- connection_class->handshake_async = g_tls_connection_gnutls_handshake_async;
- connection_class->handshake_finish = g_tls_connection_gnutls_handshake_finish;
-
- iostream_class->get_input_stream = g_tls_connection_gnutls_get_input_stream;
- iostream_class->get_output_stream = g_tls_connection_gnutls_get_output_stream;
- iostream_class->close_fn = g_tls_connection_gnutls_close;
- iostream_class->close_async = g_tls_connection_gnutls_close_async;
- iostream_class->close_finish = g_tls_connection_gnutls_close_finish;
-
- /* For GTlsConnection and GDtlsConnection: */
- g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream");
- g_object_class_override_property (gobject_class, PROP_BASE_SOCKET, "base-socket");
- g_object_class_override_property (gobject_class, PROP_REQUIRE_CLOSE_NOTIFY, "require-close-notify");
- g_object_class_override_property (gobject_class, PROP_REHANDSHAKE_MODE, "rehandshake-mode");
- g_object_class_override_property (gobject_class, PROP_USE_SYSTEM_CERTDB, "use-system-certdb");
- g_object_class_override_property (gobject_class, PROP_DATABASE, "database");
- g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
- g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
- g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE, "peer-certificate");
- g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE_ERRORS, "peer-certificate-errors");
-#if GLIB_CHECK_VERSION(2, 60, 0)
- g_object_class_override_property (gobject_class, PROP_ADVERTISED_PROTOCOLS, "advertised-protocols");
- g_object_class_override_property (gobject_class, PROP_NEGOTIATED_PROTOCOL, "negotiated-protocol");
-#endif
-}
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
-static void
-g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
-{
- iface->init = g_tls_connection_gnutls_initable_init;
-}
+ gobject_class->finalize = g_tls_connection_gnutls_finalize;
-static void
-g_tls_connection_gnutls_dtls_connection_iface_init (GDtlsConnectionInterface *iface)
-{
- iface->handshake = g_tls_connection_gnutls_dtls_handshake;
- iface->handshake_async = g_tls_connection_gnutls_dtls_handshake_async;
- iface->handshake_finish = g_tls_connection_gnutls_dtls_handshake_finish;
- iface->shutdown = g_tls_connection_gnutls_dtls_shutdown;
- iface->shutdown_async = g_tls_connection_gnutls_dtls_shutdown_async;
- iface->shutdown_finish = g_tls_connection_gnutls_dtls_shutdown_finish;
-#if GLIB_CHECK_VERSION(2, 60, 0)
- iface->set_advertised_protocols = g_tls_connection_gnutls_dtls_set_advertised_protocols;
- iface->get_negotiated_protocol = g_tls_connection_gnutls_dtls_get_negotiated_protocol;
-#endif
+ base_class->request_rehandshake = g_tls_connection_gnutls_request_rehandshake;
+ base_class->prepare_handshake = g_tls_connection_gnutls_prepare_handshake;
+ base_class->handshake_thread_handshake = g_tls_connection_gnutls_handshake_thread_handshake;
+ base_class->complete_handshake = g_tls_connection_gnutls_complete_handshake;
+ 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;
}
static void
-g_tls_connection_gnutls_datagram_based_iface_init (GDatagramBasedInterface *iface)
-{
- iface->receive_messages = g_tls_connection_gnutls_receive_messages;
- iface->send_messages = g_tls_connection_gnutls_send_messages;
- iface->create_source = g_tls_connection_gnutls_dtls_create_source;
- iface->condition_check = g_tls_connection_gnutls_condition_check;
- iface->condition_wait = g_tls_connection_gnutls_condition_wait;
-}
-
-gboolean
-g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls *gnutls,
- GError **error)
-{
- GTlsInteractionResult res = G_TLS_INTERACTION_UNHANDLED;
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GTlsInteraction *interaction;
- GTlsConnection *conn;
-
- g_return_val_if_fail (G_IS_TLS_CONNECTION_GNUTLS (gnutls), FALSE);
-
- conn = G_TLS_CONNECTION (gnutls);
-
- interaction = g_tls_connection_get_interaction (conn);
- if (!interaction)
- return FALSE;
-
- res = g_tls_interaction_invoke_request_certificate (interaction, conn, 0,
- priv->read_cancellable, error);
- return res != G_TLS_INTERACTION_FAILED;
-}
-
-void
-GTLS_DEBUG (gpointer gnutls,
- const char *message,
- ...)
+g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
{
- char *result = NULL;
- int ret;
-
- g_assert (G_IS_TLS_CONNECTION (gnutls));
-
- va_list args;
- va_start (args, message);
-
- ret = g_vasprintf (&result, message, args);
- g_assert (ret > 0);
-
- if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
- g_printf ("CLIENT %p: ", gnutls);
- else if (G_IS_TLS_SERVER_CONNECTION (gnutls))
- g_printf ("SERVER %p: ", gnutls);
- else
- g_assert_not_reached ();
-
- g_printf ("%s\n", result);
-
- fflush (stdout);
-
- g_free (result);
- va_end (args);
+ iface->init = g_tls_connection_gnutls_initable_init;
}
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index 028960b..f513a74 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -29,15 +29,17 @@
#include <gnutls/abstract.h>
#include <gnutls/gnutls.h>
+#include "gtlsconnection-base.h"
+
G_BEGIN_DECLS
#define G_TYPE_TLS_CONNECTION_GNUTLS (g_tls_connection_gnutls_get_type ())
-G_DECLARE_DERIVABLE_TYPE (GTlsConnectionGnutls, g_tls_connection_gnutls, G, TLS_CONNECTION_GNUTLS,
GTlsConnection)
+G_DECLARE_DERIVABLE_TYPE (GTlsConnectionGnutls, g_tls_connection_gnutls, G, TLS_CONNECTION_GNUTLS,
GTlsConnectionBase)
struct _GTlsConnectionGnutlsClass
{
- GTlsConnectionClass parent_class;
+ GTlsConnectionBaseClass parent_class;
void (*failed) (GTlsConnectionGnutls *gnutls);
@@ -47,6 +49,7 @@ struct _GTlsConnectionGnutlsClass
};
gnutls_certificate_credentials_t g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
+
gnutls_session_t g_tls_connection_gnutls_get_session (GTlsConnectionGnutls *connection);
void g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
@@ -54,46 +57,6 @@ void g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnu
unsigned int *pcert_length,
gnutls_privkey_t *pkey);
-gboolean g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls *gnutls,
- GError **error);
-
-gssize g_tls_connection_gnutls_read (GTlsConnectionGnutls *gnutls,
- void *buffer,
- gsize size,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
-gssize g_tls_connection_gnutls_write (GTlsConnectionGnutls *gnutls,
- const void *buffer,
- gsize size,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
-
-gboolean g_tls_connection_gnutls_check (GTlsConnectionGnutls *gnutls,
- GIOCondition condition);
-GSource *g_tls_connection_gnutls_create_source (GTlsConnectionGnutls *gnutls,
- GIOCondition condition,
- GCancellable *cancellable);
-
-typedef enum {
- G_TLS_DIRECTION_NONE = 0,
- G_TLS_DIRECTION_READ = 1 << 0,
- G_TLS_DIRECTION_WRITE = 1 << 1,
-} GTlsDirection;
-
-#define G_TLS_DIRECTION_BOTH (G_TLS_DIRECTION_READ | G_TLS_DIRECTION_WRITE)
-
-gboolean g_tls_connection_gnutls_close_internal (GIOStream *stream,
- GTlsDirection direction,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
-
-void GTLS_DEBUG (gpointer gnutls,
- const char *message,
- ...);
-
G_END_DECLS
#endif /* __G_TLS_CONNECTION_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index b3aebd5..2ed3476 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -209,10 +209,12 @@ g_tls_server_connection_gnutls_failed (GTlsConnectionGnutls *conn)
gnutls_db_remove_session (g_tls_connection_gnutls_get_session (conn));
}
-static void
-g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+static GTlsConnectionBaseStatus
+g_tls_server_connection_gnutls_handshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
{
- GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (conn);
+ GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (tls);
gnutls_session_t session;
gnutls_certificate_request_t req_mode;
@@ -230,14 +232,10 @@ g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
break;
}
- session = g_tls_connection_gnutls_get_session (conn);
+ session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls));
gnutls_certificate_server_set_request (session, req_mode);
-}
-static void
-g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls *gnutls,
- GError **inout_error)
-{
+ return G_TLS_CONNECTION_BASE_CLASS (g_tls_server_connection_gnutls_parent_class)->handshake (tls,
cancellable, error);
}
/* Session cache management */
@@ -302,15 +300,16 @@ static void
g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GTlsConnectionGnutlsClass *connection_gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+ GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_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;
- connection_gnutls_class->failed = g_tls_server_connection_gnutls_failed;
- connection_gnutls_class->begin_handshake = g_tls_server_connection_gnutls_begin_handshake;
- connection_gnutls_class->finish_handshake = g_tls_server_connection_gnutls_finish_handshake;
+ base_class->handshake = g_tls_server_connection_gnutls_handshake;
+
+ gnutls_class->failed = g_tls_server_connection_gnutls_failed;
g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
}
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index 629f651..76f8740 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -320,6 +320,7 @@ verify_ocsp_response (GTlsConnectionOpenssl *openssl,
#endif
}
+/* FIXME: Share with GnuTLS */
static GTlsCertificateFlags
verify_peer_certificate (GTlsConnectionOpenssl *openssl,
GTlsCertificate *peer_certificate)
@@ -371,11 +372,45 @@ verify_peer_certificate (GTlsConnectionOpenssl *openssl,
return errors;
}
+/* FIXME: Share with GnuTLS */
+static gboolean
+accept_peer_certificate (GTlsConnectionBase *tls,
+ GTlsCertificate *peer_certificate,
+ GTlsCertificateFlags peer_certificate_errors)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+ gboolean accepted = FALSE;
+
+ if (G_IS_TLS_CLIENT_CONNECTION (tls) && priv->peer_certificate)
+ {
+ GTlsCertificateFlags validation_flags;
+
+ if (!g_tls_connection_base_is_dtls (tls))
+ validation_flags =
+ g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (tls));
+ else
+ validation_flags =
+ g_dtls_client_connection_get_validation_flags (G_DTLS_CLIENT_CONNECTION (tls));
+
+ if ((peer_certificate_errors & validation_flags) == 0)
+ accepted = TRUE;
+ }
+
+ if (!accepted)
+ {
+ accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (tls),
+ peer_certificate,
+ peer_certificate_errors);
+ }
+
+ return accepted;
+}
+
static GTlsConnectionBaseStatus
-g_tls_connection_openssl_handshake (GTlsConnectionBase *tls,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
+g_tls_connection_openssl_handshake_thread_handshake (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
{
GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
GTlsConnectionOpensslPrivate *priv;
@@ -407,18 +442,20 @@ g_tls_connection_openssl_handshake (GTlsConnectionBase *tls,
return status;
}
-static GTlsConnectionBaseStatus
+static void
g_tls_connection_openssl_complete_handshake (GTlsConnectionBase *tls,
+ gchar **negotiated_protocol,
GError **error)
{
GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
GTlsConnectionOpensslPrivate *priv;
GTlsCertificate *peer_certificate;
GTlsCertificateFlags peer_certificate_errors = 0;
- GTlsConnectionBaseStatus status = G_TLS_CONNECTION_BASE_OK;
priv = g_tls_connection_openssl_get_instance_private (openssl);
+// FIXME
+#if 0
peer_certificate = priv->peer_certificate_tmp;
priv->peer_certificate_tmp = NULL;
peer_certificate_errors = priv->peer_certificate_errors_tmp;
@@ -426,8 +463,8 @@ g_tls_connection_openssl_complete_handshake (GTlsConnectionBase *tls,
if (peer_certificate)
{
- if (!g_tls_connection_base_accept_peer_certificate (tls, peer_certificate,
- peer_certificate_errors))
+ /* FIXME: This is too late. Verification should occur during the handshake. */
+ if (!accept_peer_certificate (tls, peer_certificate, peer_certificate_errors))
{
g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
_("Unacceptable TLS certificate"));
@@ -439,8 +476,7 @@ g_tls_connection_openssl_complete_handshake (GTlsConnectionBase *tls,
peer_certificate_errors);
g_clear_object (&peer_certificate);
}
-
- return status;
+#endif
}
static void
@@ -584,16 +620,16 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
- gobject_class->finalize = g_tls_connection_openssl_finalize;
+ gobject_class->finalize = g_tls_connection_openssl_finalize;
- base_class->request_rehandshake = g_tls_connection_openssl_request_rehandshake;
- base_class->handshake = g_tls_connection_openssl_handshake;
- base_class->complete_handshake = g_tls_connection_openssl_complete_handshake;
- 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;
+ base_class->request_rehandshake = g_tls_connection_openssl_request_rehandshake;
+ base_class->handshake_thread_handshake = g_tls_connection_openssl_handshake_thread_handshake;
+ base_class->complete_handshake = g_tls_connection_openssl_complete_handshake;
+ 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;
}
static gboolean
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 5f25ea4..3ae8993 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -82,9 +82,7 @@ typedef struct {
gboolean server_should_close;
gboolean server_running;
GTlsCertificate *server_certificate;
-#if GLIB_CHECK_VERSION(2, 60, 0)
const gchar * const *server_protocols;
-#endif
char buf[128];
gssize nread, nwrote;
@@ -306,13 +304,11 @@ on_incoming_connection (GSocketService *service,
if (test->database)
g_tls_connection_set_database (G_TLS_CONNECTION (test->server_connection), test->database);
-#if GLIB_CHECK_VERSION(2, 60, 0)
if (test->server_protocols)
{
g_tls_connection_set_advertised_protocols (G_TLS_CONNECTION (test->server_connection),
test->server_protocols);
}
-#endif
stream = g_io_stream_get_output_stream (test->server_connection);
@@ -1941,11 +1937,7 @@ test_fallback (TestConnection *test,
#pragma GCC diagnostic pop
#endif
-#if GLIB_CHECK_VERSION(2, 60, 0)
g_set_error_literal (&test->expected_server_error, G_TLS_ERROR, G_TLS_ERROR_INAPPROPRIATE_FALLBACK, "");
-#else
- g_set_error_literal (&test->expected_server_error, G_TLS_ERROR, G_TLS_ERROR_MISC, "");
-#endif
g_tls_connection_handshake_async (tlsconn, G_PRIORITY_DEFAULT, NULL,
quit_on_handshake_complete, test);
@@ -2110,7 +2102,6 @@ test_alpn (TestConnection *test,
const char * const *server_protocols,
const char *negotiated_protocol)
{
-#if GLIB_CHECK_VERSION(2, 60, 0)
GIOStream *connection;
GError *error = NULL;
@@ -2141,9 +2132,6 @@ test_alpn (TestConnection *test,
g_assert_cmpstr (g_tls_connection_get_negotiated_protocol (G_TLS_CONNECTION (test->server_connection)),
==, negotiated_protocol);
g_assert_cmpstr (g_tls_connection_get_negotiated_protocol (G_TLS_CONNECTION (test->client_connection)),
==, negotiated_protocol);
-#else
- g_test_skip ("no support for ALPN in this GLib version");
-#endif
}
static void
diff --git a/tls/tests/dtls-connection.c b/tls/tests/dtls-connection.c
index 1304d96..7006340 100644
--- a/tls/tests/dtls-connection.c
+++ b/tls/tests/dtls-connection.c
@@ -89,9 +89,7 @@ typedef struct {
gboolean expect_server_error;
GError *server_error;
gboolean server_running;
-#if GLIB_CHECK_VERSION(2, 60, 0)
const gchar * const *server_protocols;
-#endif
char buf[128];
gssize nread, nwrote;
@@ -400,13 +398,11 @@ on_incoming_connection (GSocket *socket,
if (test->database)
g_dtls_connection_set_database (G_DTLS_CONNECTION (test->server_connection), test->database);
-#if GLIB_CHECK_VERSION(2, 60, 0)
if (test->server_protocols)
{
g_dtls_connection_set_advertised_protocols (G_DTLS_CONNECTION (test->server_connection),
test->server_protocols);
}
-#endif
if (test->test_data->server_should_disappear)
{
@@ -743,7 +739,6 @@ test_alpn (TestConnection *test,
const char * const *server_protocols,
const char *negotiated_protocol)
{
-#if GLIB_CHECK_VERSION(2, 60, 0)
GDatagramBased *connection;
GError *error = NULL;
@@ -775,9 +770,6 @@ test_alpn (TestConnection *test,
g_assert_cmpstr (g_dtls_connection_get_negotiated_protocol (G_DTLS_CONNECTION (test->server_connection)),
==, negotiated_protocol);
g_assert_cmpstr (g_dtls_connection_get_negotiated_protocol (G_DTLS_CONNECTION (test->client_connection)),
==, negotiated_protocol);
-#else
- g_test_skip ("no support for ALPN in this GLib version");
-#endif
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]