[glib-networking/mcatanzaro/base-rebase: 42/44] Make the GnuTLS backend use the base classes
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/mcatanzaro/base-rebase: 42/44] Make the GnuTLS backend use the base classes
- Date: Fri, 3 May 2019 03:25:22 +0000 (UTC)
commit ed1306ab405055a072dc9b7907bb433890da7403
Author: Michael Catanzaro <mcatanzaro igalia com>
Date: Fri Apr 19 14:34:37 2019 -0500
Make the GnuTLS backend use the base classes
I was hoping to avoid a huge unreadable commit like this, but it's just
not practical to do otherwise. This commit unifies three very distinct
branches of the code:
* glib-networking master, GnuTLS backend: currently in the best shape,
and most up-to-date, but not using the base classes
* glib-networking master, OpenSSL backend: uses the base classes, but
missing the past 2.5 years' of development on the GnuTLS backend
* glib-networking wip/tlssplit branch, where GnuTLS does use the base
classes, but everything is 2.5 years' outdated
The main conflicts here were the addition of DTLS support and the
certificate verification rework.
meson.build | 2 +-
tls/base/gtlsconnection-base.c | 545 +++++-
tls/base/gtlsconnection-base.h | 259 +--
tls/gnutls/gtlsclientconnection-gnutls.c | 85 +-
tls/gnutls/gtlsconnection-gnutls.c | 2859 ++++------------------------
tls/gnutls/gtlsconnection-gnutls.h | 53 +-
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 | 26 +-
tls/gnutls/meson.build | 10 +-
tls/openssl/gtlsbackend-openssl.c | 35 -
tls/openssl/gtlsclientconnection-openssl.c | 76 +-
tls/openssl/gtlsconnection-openssl.c | 169 +-
tls/openssl/gtlsdatabase-openssl.h | 4 +
tls/openssl/gtlsserverconnection-openssl.c | 24 +-
tls/openssl/meson.build | 2 +-
tls/tests/connection.c | 12 -
tls/tests/dtls-connection.c | 8 -
20 files changed, 1127 insertions(+), 3661 deletions(-)
---
diff --git a/meson.build b/meson.build
index 3fb6590..10141dc 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..462bda0 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -87,11 +87,16 @@ typedef struct
GTlsInteraction *interaction;
GTlsCertificate *certificate;
- gboolean certificate_requested;
+ gboolean missing_requested_client_certificate;
GError *certificate_error;
GTlsCertificate *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;
@@ -118,9 +123,11 @@ typedef struct
*/
gboolean need_handshake;
gboolean need_finish_handshake;
+ gboolean sync_handshake_completed;
gboolean started_handshake;
gboolean handshaking;
gboolean ever_handshaked;
+ GMainContext *handshake_context;
GTask *implicit_handshake;
GError *handshake_error;
GByteArray *app_data_buf;
@@ -145,6 +152,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,10 +192,14 @@ 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
+gboolean
g_tls_connection_base_is_dtls (GTlsConnectionBase *tls)
{
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
@@ -202,7 +216,11 @@ g_tls_connection_base_init (GTlsConnectionBase *tls)
priv->database_is_unset = TRUE;
priv->is_system_certdb = TRUE;
+ g_mutex_init (&priv->verify_certificate_mutex);
+ g_cond_init (&priv->verify_certificate_condition);
+
g_mutex_init (&priv->op_mutex);
+
priv->waiting_for_op = g_cancellable_new ();
g_cancellable_cancel (priv->waiting_for_op);
}
@@ -224,8 +242,13 @@ g_tls_connection_base_finalize (GObject *object)
g_clear_error (&priv->certificate_error);
g_clear_object (&priv->peer_certificate);
+ g_mutex_clear (&priv->verify_certificate_mutex);
+ g_cond_clear (&priv->verify_certificate_condition);
+
g_clear_object (&priv->interaction);
+ g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
/* This must always be NULL at this point, as it holds a reference to @tls as
* its source object. However, we clear it anyway just in case this changes
* in future. */
@@ -242,6 +265,9 @@ g_tls_connection_base_finalize (GObject *object)
g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
+ g_clear_pointer (&priv->advertised_protocols, g_strfreev);
+ g_clear_pointer (&priv->negotiated_protocol, g_free);
+
G_OBJECT_CLASS (g_tls_connection_base_parent_class)->finalize (object);
}
@@ -303,6 +329,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 +437,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);
}
@@ -483,6 +522,7 @@ claim_op (GTlsConnectionBase *tls,
g_mutex_unlock (&priv->op_mutex);
success = finish_handshake (tls, 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_BASE_OP_CLOSE_BOTH &&
@@ -655,6 +695,7 @@ g_tls_connection_base_real_pop_io (GTlsConnectionBase *tls,
GError **error)
{
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
GError *my_error = NULL;
if (direction & G_IO_IN)
@@ -696,6 +737,9 @@ g_tls_connection_base_real_pop_io (GTlsConnectionBase *tls,
else if (my_error)
g_propagate_error (error, my_error);
+ if (tls_class->failed)
+ tls_class->failed (tls);
+
return G_TLS_CONNECTION_BASE_ERROR;
}
@@ -715,7 +759,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,15 +1128,102 @@ 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)
+static GTlsCertificateFlags
+verify_peer_certificate (GTlsConnectionBase *tls,
+ GTlsCertificate *peer_certificate)
+{
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+ GSocketConnectable *peer_identity;
+ GTlsDatabase *database;
+ GTlsCertificateFlags errors;
+ gboolean is_client;
+
+ is_client = G_IS_TLS_CLIENT_CONNECTION (tls);
+
+ if (!is_client)
+ peer_identity = NULL;
+ else if (!g_tls_connection_base_is_dtls (tls))
+ peer_identity = g_tls_client_connection_get_server_identity (G_TLS_CLIENT_CONNECTION (tls));
+ else
+ peer_identity = g_dtls_client_connection_get_server_identity (G_DTLS_CLIENT_CONNECTION (tls));
+
+ errors = 0;
+
+ database = g_tls_connection_get_database (G_TLS_CONNECTION (tls));
+ if (database == NULL)
+ {
+ errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+ errors |= g_tls_certificate_verify (peer_certificate, peer_identity, NULL);
+ }
+ else
+ {
+ GError *error = NULL;
+
+ errors |= g_tls_database_verify_chain (database, peer_certificate,
+ is_client ?
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER :
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT,
+ peer_identity,
+ g_tls_connection_get_interaction (G_TLS_CONNECTION (tls)),
+ G_TLS_DATABASE_VERIFY_NONE,
+ NULL, &error);
+ if (error)
+ {
+ g_warning ("failure verifying certificate chain: %s",
+ error->message);
+ g_assert (errors != 0);
+ g_clear_error (&error);
+ }
+ }
+
+ if (tls_class->verify_peer_certificate)
+ errors |= tls_class->verify_peer_certificate (tls, peer_certificate, errors);
+
+ return errors;
+}
+
+static void
+update_peer_certificate_and_compute_errors (GTlsConnectionBase *tls)
{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+ 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.
+ *
+ * verify_certificate_mutex should be locked.
+ */
+ g_assert (priv->handshake_context);
+ g_assert (g_main_context_is_owner (priv->handshake_context));
+
+ peer_certificate = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->retrieve_peer_certificate (tls);
+ if (peer_certificate)
+ peer_certificate_errors = verify_peer_certificate (tls, peer_certificate);
+
+ g_set_object (&priv->peer_certificate, peer_certificate);
+
+ priv->peer_certificate_errors = peer_certificate_errors;
+
+ g_object_notify (G_OBJECT (tls), "peer-certificate");
+ g_object_notify (G_OBJECT (tls), "peer-certificate-errors");
+}
+
+static gboolean
+accept_or_reject_peer_certificate (gpointer user_data)
+{
+ GTlsConnectionBase *tls = user_data;
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
gboolean accepted = FALSE;
- if (G_IS_TLS_CLIENT_CONNECTION (tls) && priv->peer_certificate)
+ g_assert (g_main_context_is_owner (priv->handshake_context));
+
+ g_mutex_lock (&priv->verify_certificate_mutex);
+
+ update_peer_certificate_and_compute_errors (tls);
+
+ if (G_IS_TLS_CLIENT_CONNECTION (tls) && priv->peer_certificate != NULL)
{
GTlsCertificateFlags validation_flags;
@@ -1103,33 +1234,64 @@ g_tls_connection_base_accept_peer_certificate (GTlsConnectionBase *tls,
validation_flags =
g_dtls_client_connection_get_validation_flags (G_DTLS_CLIENT_CONNECTION (tls));
- if ((peer_certificate_errors & validation_flags) == 0)
+ if ((priv->peer_certificate_errors & validation_flags) == 0)
accepted = TRUE;
}
if (!accepted)
{
+ g_main_context_pop_thread_default (priv->handshake_context);
accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (tls),
- peer_certificate,
- peer_certificate_errors);
+ priv->peer_certificate,
+ priv->peer_certificate_errors);
+ g_main_context_push_thread_default (priv->handshake_context);
}
- return accepted;
+ priv->peer_certificate_accepted = accepted;
+
+ /* This has to be the very last statement before signaling the
+ * condition variable because otherwise the code could spuriously
+ * wakeup and continue before we are done here.
+ */
+ priv->peer_certificate_examined = TRUE;
+
+ g_cond_signal (&priv->verify_certificate_condition);
+ g_mutex_unlock (&priv->verify_certificate_mutex);
+
+ g_object_notify (G_OBJECT (tls), "peer-certificate");
+ g_object_notify (G_OBJECT (tls), "peer-certificate-errors");
+
+ return G_SOURCE_REMOVE;
}
-void
-g_tls_connection_base_set_peer_certificate (GTlsConnectionBase *tls,
- GTlsCertificate *peer_certificate,
- GTlsCertificateFlags peer_certificate_errors)
+gboolean
+g_tls_connection_base_handshake_thread_verify_certificate (GTlsConnectionBase *tls)
{
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+ gboolean accepted;
- g_set_object (&priv->peer_certificate, peer_certificate);
+ g_mutex_lock (&priv->verify_certificate_mutex);
+ priv->peer_certificate_examined = FALSE;
+ priv->peer_certificate_accepted = FALSE;
+ g_mutex_unlock (&priv->verify_certificate_mutex);
- priv->peer_certificate_errors = peer_certificate_errors;
+ /* Invoke the callback on the handshake context's thread. This is
+ * necessary because we need to ensure the accept-certificate signal
+ * is emitted on the original thread.
+ */
+ g_assert (priv->handshake_context);
+ g_main_context_invoke (priv->handshake_context, accept_or_reject_peer_certificate, tls);
- g_object_notify (G_OBJECT (tls), "peer-certificate");
- g_object_notify (G_OBJECT (tls), "peer-certificate-errors");
+ /* We'll block the handshake thread until the original thread has
+ * decided whether to accept the certificate.
+ */
+ g_mutex_lock (&priv->verify_certificate_mutex);
+ while (!priv->peer_certificate_examined)
+ g_cond_wait (&priv->verify_certificate_condition, &priv->verify_certificate_mutex);
+ accepted = priv->peer_certificate_accepted;
+ g_mutex_unlock (&priv->verify_certificate_mutex);
+
+ return accepted;
}
static void
@@ -1142,14 +1304,16 @@ handshake_thread (GTask *task,
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
GError *error = NULL;
+ gint64 start_time;
gint64 timeout;
/* A timeout, in microseconds, must be provided as a gint64* task_data. */
g_assert (task_data != NULL);
+ start_time = g_get_monotonic_time ();
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))
@@ -1164,6 +1328,14 @@ handshake_thread (GTask *task,
{
GTlsConnectionBaseStatus status;
+ /* Adjust the timeout for the next operation in the sequence. */
+ if (timeout > 0)
+ {
+ timeout -= (g_get_monotonic_time () - start_time);
+ if (timeout <= 0)
+ timeout = 1;
+ }
+
status = tls_class->request_rehandshake (tls, timeout, cancellable, &error);
if (status != G_TLS_CONNECTION_BASE_OK)
{
@@ -1175,28 +1347,30 @@ handshake_thread (GTask *task,
g_clear_object (&priv->peer_certificate);
priv->peer_certificate_errors = 0;
+ /* Adjust the timeout for the next operation in the sequence. */
+ if (timeout > 0)
+ {
+ timeout -= (g_get_monotonic_time () - start_time);
+ if (timeout <= 0)
+ timeout = 1;
+ }
+
priv->started_handshake = TRUE;
- tls_class->handshake (tls, timeout, cancellable, &error);
+ tls_class->handshake_thread_handshake (tls, timeout, cancellable, &error);
priv->need_handshake = FALSE;
- if (error)
+ if (error != NULL && priv->missing_requested_client_certificate)
{
- 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)
+ g_clear_error (&error);
+ if (priv->certificate_error)
{
- g_clear_error (&error);
- if (priv->certificate_error)
- {
- error = priv->certificate_error;
- priv->certificate_error = NULL;
- }
- else
- {
- g_set_error_literal (&error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
- _("Server required TLS certificate"));
- }
+ error = priv->certificate_error;
+ priv->certificate_error = NULL;
+ }
+ else
+ {
+ g_set_error_literal (&error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+ _("Server required TLS certificate"));
}
g_task_return_error (task, error);
}
@@ -1207,17 +1381,89 @@ 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,
GError **error)
{
- GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+ gchar *original_negotiated_protocol;
GError *my_error = NULL;
+ original_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
+
if (g_task_propagate_boolean (task, &my_error))
- tls_class->complete_handshake (tls, &my_error);
+ {
+ if (tls_class->is_session_resumed && tls_class->is_session_resumed (tls))
+ {
+ /* Because this session was resumed, we skipped certificate
+ * verification on this handshake, so we missed our earlier
+ * chance to set peer_certificate and peer_certificate_errors.
+ * Do so here instead.
+ *
+ * The certificate has already been accepted, so we don't do
+ * anything with the result here.
+ */
+ g_mutex_lock (&priv->verify_certificate_mutex);
+ update_peer_certificate_and_compute_errors (tls);
+ priv->peer_certificate_examined = TRUE;
+ priv->peer_certificate_accepted = TRUE;
+ g_mutex_unlock (&priv->verify_certificate_mutex);
+ }
+
+ if (tls_class->complete_handshake)
+ 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);
+
+ /* FIXME: Return an error from the handshake thread instead. */
+ if (priv->peer_certificate && !priv->peer_certificate_accepted)
+ {
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Unacceptable TLS certificate"));
+ }
+ }
if (my_error && priv->started_handshake)
priv->handshake_error = g_error_copy (my_error);
@@ -1235,19 +1481,34 @@ 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);
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (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);
@@ -1308,6 +1569,7 @@ handshake_thread_completed (GObject *object,
else
g_task_return_boolean (caller_task, TRUE);
+ g_clear_pointer (&priv->handshake_context, g_main_context_unref);
g_object_unref (caller_task);
}
@@ -1336,18 +1598,28 @@ 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 (tls);
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
GTask *thread_task, *caller_task;
gint64 *timeout = NULL;
+ g_assert (!priv->handshake_context);
+ priv->handshake_context = g_main_context_ref_thread_default ();
+
+ if (tls_class->prepare_handshake)
+ tls_class->prepare_handshake (tls, priv->advertised_protocols);
+
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);
+
thread_task = g_task_new (conn, cancellable, handshake_thread_completed, caller_task);
g_task_set_source_tag (thread_task, g_tls_connection_base_handshake_async);
g_task_set_priority (thread_task, io_priority);
@@ -1361,9 +1633,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);
@@ -1390,22 +1662,6 @@ g_tls_connection_base_dtls_handshake_finish (GDtlsConnection *conn,
result, error);
}
-static void
-implicit_handshake_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_mutex_lock (&priv->op_mutex);
- priv->need_finish_handshake = TRUE;
- g_mutex_unlock (&priv->op_mutex);
-
- yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
- G_TLS_CONNECTION_BASE_OK);
-}
-
static gboolean
do_implicit_handshake (GTlsConnectionBase *tls,
gint64 timeout,
@@ -1413,13 +1669,25 @@ do_implicit_handshake (GTlsConnectionBase *tls,
GError **error)
{
GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
gint64 *thread_timeout = NULL;
/* 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);
@@ -1427,6 +1695,9 @@ do_implicit_handshake (GTlsConnectionBase *tls,
g_task_set_task_data (priv->implicit_handshake,
thread_timeout, g_free);
+ if (tls_class->prepare_handshake)
+ tls_class->prepare_handshake (tls, priv->advertised_protocols);
+
if (timeout != 0)
{
GError *my_error = NULL;
@@ -1440,14 +1711,23 @@ 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);
+
success = finish_handshake (tls,
priv->implicit_handshake,
&my_error);
+
+ g_main_context_pop_thread_default (priv->handshake_context);
+ g_clear_pointer (&priv->handshake_context, g_main_context_unref);
g_clear_object (&priv->implicit_handshake);
+
yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
G_TLS_CONNECTION_BASE_OK);
+
g_mutex_lock (&priv->op_mutex);
if (my_error)
@@ -1556,6 +1836,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 +1972,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 +2283,71 @@ g_tls_connection_base_dtls_shutdown_finish (GDtlsConnection *conn,
return g_task_propagate_boolean (G_TASK (result), error);
}
+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 (tls);
+
+ return priv->negotiated_protocol;
+}
+
+GDatagramBased *
+g_tls_connection_base_get_base_socket (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ 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);
+
+ g_assert (!g_tls_connection_base_is_dtls (tls));
+
+ return priv->base_io_stream;
+}
+
+GPollableInputStream *
+g_tls_connection_base_get_base_istream (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ g_assert (!g_tls_connection_base_is_dtls (tls));
+
+ return priv->base_istream;
+}
+
+GPollableOutputStream *
+g_tls_connection_base_get_base_ostream (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ g_assert (!g_tls_connection_base_is_dtls (tls));
+
+ return priv->base_ostream;
+}
+
void
-g_tls_connection_base_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 +2374,38 @@ 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;
+}
+
+GCancellable *
+g_tls_connection_base_get_read_cancellable (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ return priv->read_cancellable;
+}
+
+GCancellable *
+g_tls_connection_base_get_write_cancellable (GTlsConnectionBase *tls)
+{
+ GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+ return priv->write_cancellable;
+}
+
gboolean
g_tls_connection_base_is_handshaking (GTlsConnectionBase *tls)
{
@@ -2074,6 +2444,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 +2494,8 @@ 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");
+ g_object_class_override_property (gobject_class, PROP_ADVERTISED_PROTOCOLS, "advertised-protocols");
+ g_object_class_override_property (gobject_class, PROP_NEGOTIATED_PROTOCOL, "negotiated-protocol");
}
static void
@@ -2120,6 +2507,8 @@ 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;
+ iface->set_advertised_protocols = g_tls_connection_base_dtls_set_advertised_protocols;
+ iface->get_negotiated_protocol = g_tls_connection_base_dtls_get_negotiated_protocol;
}
static void
@@ -2133,14 +2522,14 @@ g_tls_connection_base_datagram_based_iface_init (GDatagramBasedInterface *iface)
}
void
-GTLS_DEBUG (gpointer gnutls,
+GTLS_DEBUG (gpointer connection,
const char *message,
...)
{
char *result = NULL;
int ret;
- g_assert (G_IS_TLS_CONNECTION (gnutls));
+ g_assert (G_IS_TLS_CONNECTION (connection));
va_list args;
va_start (args, message);
@@ -2148,10 +2537,10 @@ GTLS_DEBUG (gpointer gnutls,
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);
+ if (G_IS_TLS_CLIENT_CONNECTION (connection))
+ g_printf ("CLIENT %p: ", connection);
+ else if (G_IS_TLS_SERVER_CONNECTION (connection))
+ g_printf ("SERVER %p: ", connection);
else
g_assert_not_reached ();
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 2bea4ad..c04294a 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -43,9 +43,9 @@ typedef enum {
} GTlsConnectionBaseStatus;
typedef enum {
- G_TLS_DIRECTION_NONE = 0,
- G_TLS_DIRECTION_READ = 1 << 0,
- G_TLS_DIRECTION_WRITE = 1 << 1,
+ 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)
@@ -54,121 +54,148 @@ struct _GTlsConnectionBaseClass
{
GTlsConnectionClass parent_class;
- GTlsConnectionBaseStatus (*request_rehandshake) (GTlsConnectionBase *tls,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
- GTlsConnectionBaseStatus (*handshake) (GTlsConnectionBase *tls,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
- GTlsConnectionBaseStatus (*complete_handshake) (GTlsConnectionBase *tls,
- GError **error);
-
- void (*push_io) (GTlsConnectionBase *tls,
- GIOCondition direction,
- gint64 timeout,
- GCancellable *cancellable);
- GTlsConnectionBaseStatus (*pop_io) (GTlsConnectionBase *tls,
- GIOCondition direction,
- gboolean success,
- GError **error);
-
- GTlsConnectionBaseStatus (*read_fn) (GTlsConnectionBase *tls,
- void *buffer,
- gsize count,
- gint64 timeout,
- gssize *nread,
- GCancellable *cancellable,
- GError **error);
- GTlsConnectionBaseStatus (*read_message_fn) (GTlsConnectionBase *tls,
- GInputVector *vectors,
- guint num_vectors,
- gint64 timeout,
- gssize *nread,
- GCancellable *cancellable,
- GError **error);
-
- GTlsConnectionBaseStatus (*write_fn) (GTlsConnectionBase *tls,
- const void *buffer,
- gsize count,
- gint64 timeout,
- gssize *nwrote,
- GCancellable *cancellable,
- GError **error);
- GTlsConnectionBaseStatus (*write_message_fn) (GTlsConnectionBase *tls,
- GOutputVector *vectors,
- guint num_vectors,
- gint64 timeout,
- gssize *nwrote,
- GCancellable *cancellable,
- GError **error);
-
- GTlsConnectionBaseStatus (*close_fn) (GTlsConnectionBase *tls,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
+ GTlsConnectionBaseStatus (*request_rehandshake) (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error);
+
+ void (*prepare_handshake) (GTlsConnectionBase *tls,
+ gchar **advertised_protocols);
+ GTlsConnectionBaseStatus (*handshake_thread_handshake) (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error);
+ GTlsCertificate *(*retrieve_peer_certificate) (GTlsConnectionBase *tls);
+ GTlsCertificateFlags (*verify_peer_certificate) (GTlsConnectionBase *tls,
+ GTlsCertificate *certificate,
+ GTlsCertificateFlags flags);
+ void (*complete_handshake) (GTlsConnectionBase *tls,
+ gchar **negotiated_protocol,
+ GError **error);
+
+ gboolean (*is_session_resumed) (GTlsConnectionBase *tls);
+
+ void (*push_io) (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gint64 timeout,
+ GCancellable *cancellable);
+ GTlsConnectionBaseStatus (*pop_io) (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean success,
+ GError **error);
+
+ void (*failed) (GTlsConnectionBase *tls);
+
+ GTlsConnectionBaseStatus (*read_fn) (GTlsConnectionBase *tls,
+ void *buffer,
+ gsize count,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error);
+ GTlsConnectionBaseStatus (*read_message_fn) (GTlsConnectionBase *tls,
+ GInputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error);
+
+ GTlsConnectionBaseStatus (*write_fn) (GTlsConnectionBase *tls,
+ const void *buffer,
+ gsize count,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error);
+ GTlsConnectionBaseStatus (*write_message_fn) (GTlsConnectionBase *tls,
+ GOutputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error);
+
+ GTlsConnectionBaseStatus (*close_fn) (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ 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);
-
-void g_tls_connection_base_push_io (GTlsConnectionBase *tls,
- GIOCondition direction,
- gint64 timeout,
- GCancellable *cancellable);
-GTlsConnectionBaseStatus
- g_tls_connection_base_pop_io (GTlsConnectionBase *tls,
- GIOCondition direction,
- gboolean success,
- GError **error);
-
-gssize g_tls_connection_base_read (GTlsConnectionBase *tls,
- void *buffer,
- gsize size,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
-gssize g_tls_connection_base_write (GTlsConnectionBase *tls,
- const void *buffer,
- gsize size,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
-
-gboolean g_tls_connection_base_check (GTlsConnectionBase *tls,
- GIOCondition condition);
-GSource *g_tls_connection_base_create_source (GTlsConnectionBase *tls,
- GIOCondition condition,
- GCancellable *cancellable);
-
-gboolean g_tls_connection_base_close_internal (GIOStream *stream,
- GTlsDirection direction,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error);
-
-void g_tls_connection_base_set_certificate_requested (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);
-
-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 GTLS_DEBUG (gpointer gnutls,
- const char *message,
- ...);
+gboolean g_tls_connection_base_handshake_thread_verify_certificate
+ (GTlsConnectionBase *tls);
+
+void g_tls_connection_base_push_io (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gint64 timeout,
+ GCancellable *cancellable);
+GTlsConnectionBaseStatus g_tls_connection_base_pop_io (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean success,
+ GError **error);
+
+gssize g_tls_connection_base_read (GTlsConnectionBase *tls,
+ void *buffer,
+ gsize size,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error);
+gssize g_tls_connection_base_write (GTlsConnectionBase *tls,
+ const void *buffer,
+ gsize size,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error);
+
+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);
+
+gboolean g_tls_connection_base_close_internal (GIOStream *stream,
+ GTlsDirection direction,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean g_tls_connection_base_is_dtls (GTlsConnectionBase *tls);
+
+GDatagramBased *g_tls_connection_base_get_base_socket (GTlsConnectionBase *tls);
+
+GIOStream *g_tls_connection_base_get_base_iostream (GTlsConnectionBase *tls);
+GPollableInputStream *g_tls_connection_base_get_base_istream (GTlsConnectionBase *tls);
+GPollableOutputStream *g_tls_connection_base_get_base_ostream (GTlsConnectionBase *tls);
+
+void g_tls_connection_base_set_missing_requested_client_certificate
+ (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);
+
+gint64 g_tls_connection_base_get_read_timeout (GTlsConnectionBase *tls);
+gint64 g_tls_connection_base_get_write_timeout (GTlsConnectionBase *tls);
+
+GCancellable *g_tls_connection_base_get_read_cancellable (GTlsConnectionBase *tls);
+GCancellable *g_tls_connection_base_get_write_cancellable (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 GTLS_DEBUG (gpointer connection,
+ const char *message,
+ ...);
G_END_DECLS
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index cac2deb..12ea5f0 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_base_request_certificate (tls, 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;
}
@@ -389,10 +388,8 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
}
static void
-g_tls_client_connection_gnutls_failed (GTlsConnectionGnutls *conn)
+g_tls_client_connection_gnutls_clear_session_data (GTlsClientConnectionGnutls *gnutls)
{
- GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
-
gnutls->session_data_override = FALSE;
g_clear_pointer (&gnutls->session_data, g_bytes_unref);
if (gnutls->session_id)
@@ -400,16 +397,23 @@ g_tls_client_connection_gnutls_failed (GTlsConnectionGnutls *conn)
}
static void
-g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+g_tls_client_connection_gnutls_failed (GTlsConnectionBase *tls)
{
- GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+ g_tls_client_connection_gnutls_clear_session_data (G_TLS_CLIENT_CONNECTION_GNUTLS (tls));
+}
+
+static void
+g_tls_client_connection_gnutls_prepare_handshake (GTlsConnectionBase *tls,
+ gchar **advertised_protocols)
+{
+ 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 +424,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,48 +432,29 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
}
}
- gnutls->requested_cert_missing = FALSE;
+ G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->
+ prepare_handshake (tls, advertised_protocols);
}
static void
-g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
- GError **inout_error)
+g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase *tls,
+ gchar **negotiated_protocol,
+ GError **error)
{
- GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
int resumed;
- g_assert (inout_error != NULL);
-
- 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)
- {
- /* Clear session data since the server did not accept what we provided. */
- gnutls->session_data_override = FALSE;
- g_clear_pointer (&gnutls->session_data, g_bytes_unref);
- if (gnutls->session_id)
- g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->session_id);
- }
+ G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->
+ complete_handshake (tls, negotiated_protocol, error);
- if (!*inout_error && !resumed)
+ resumed = gnutls_session_is_resumed (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)));
+ if (!resumed)
{
gnutls_datum_t session_datum;
- if (gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (conn),
+ g_tls_client_connection_gnutls_clear_session_data (G_TLS_CLIENT_CONNECTION_GNUTLS (tls));
+
+ 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,
@@ -508,15 +493,15 @@ 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);
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->prepare_handshake = g_tls_client_connection_gnutls_prepare_handshake;
+ base_class->complete_handshake = g_tls_client_connection_gnutls_complete_handshake;
+ base_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..f05801b 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 ();
);
@@ -258,20 +93,8 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
gnutls_certificate_allocate_credentials (&priv->creds);
- 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 +181,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 +193,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);
+
+ /* Ensure we are in TLS mode or DTLS mode. */
+ g_return_val_if_fail (!!base_io_stream != !!base_socket, FALSE);
- /* Check whether to use DTLS or TLS. */
- if (g_tls_connection_gnutls_is_dtls (gnutls))
+ if (base_socket)
flags |= GNUTLS_DATAGRAM;
gnutls_init (&priv->session, flags);
@@ -416,7 +236,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 +246,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 +255,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)
{
@@ -705,867 +306,242 @@ g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
}
}
-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
+static GTlsConnectionBaseStatus
end_gnutls_io (GTlsConnectionGnutls *gnutls,
GIOCondition direction,
- int status,
+ 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;
- 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 (ret == GNUTLS_E_AGAIN ||
+ ret == GNUTLS_E_WARNING_ALERT_RECEIVED)
+ return G_TLS_CONNECTION_BASE_TRY_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)
+ 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)
{
- 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 (my_error)
+ g_propagate_error (error, my_error);
+ return status;
}
- if (status >= 0)
- return status;
+ g_assert (status == G_TLS_CONNECTION_BASE_ERROR);
- if (priv->handshaking && !priv->ever_handshaked)
+ handshaking = g_tls_connection_base_is_handshaking (tls);
+ ever_handshaked = g_tls_connection_base_ever_handshaked (tls);
+
+ if (handshaking && !ever_handshaked)
{
- if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
- g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
- status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
+ 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 GNUTLS_E_PULL_ERROR;
+ return G_TLS_CONNECTION_BASE_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 (ret == GNUTLS_E_REHANDSHAKE)
{
- if (priv->rehandshake_mode == G_TLS_REHANDSHAKE_NEVER)
+ 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 GNUTLS_E_PULL_ERROR;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- g_mutex_lock (&priv->op_mutex);
- if (!priv->handshaking)
- priv->need_handshake = TRUE;
- g_mutex_unlock (&priv->op_mutex);
- return status;
+ return G_TLS_CONNECTION_BASE_REHANDSHAKE;
}
- else if (status == GNUTLS_E_PREMATURE_TERMINATION)
+
+ if (ret == GNUTLS_E_PREMATURE_TERMINATION)
{
- if (priv->handshaking && !priv->ever_handshaked)
+ 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 GNUTLS_E_PULL_ERROR;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- else if (priv->require_close_notify)
+
+ 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"));
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
- return status;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- else
- return 0;
+
+ return G_TLS_CONNECTION_BASE_OK;
}
- else if (status == GNUTLS_E_NO_CERTIFICATE_FOUND
+
+ if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND
#ifdef GNUTLS_E_CERTIFICATE_REQUIRED
- || status == GNUTLS_E_CERTIFICATE_REQUIRED /* Added in GnuTLS 3.6.7 */
+ || 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 status;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- else if (status == GNUTLS_E_CERTIFICATE_ERROR)
+
+ 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 status;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- else if (status == GNUTLS_E_FATAL_ALERT_RECEIVED)
+
+ if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
{
+ g_clear_error (&my_error);
g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
_("Peer sent fatal TLS alert: %s"),
gnutls_alert_get_name (gnutls_alert_get (priv->session)));
- return status;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- else if (status == GNUTLS_E_INAPPROPRIATE_FALLBACK)
+
+ 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 status;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- else if (status == GNUTLS_E_LARGE_PACKET)
+
+ 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 status;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- else if (status == GNUTLS_E_TIMEDOUT)
+
+ if (ret == GNUTLS_E_TIMEDOUT)
{
+ g_clear_error (&my_error);
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
_("The operation timed out"));
- return status;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- if (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));
+ err_prefix, gnutls_strerror (status));
}
- return status;
+
+ return G_TLS_CONNECTION_BASE_ERROR;
}
#define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable) \
- 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, errmsg, err) \
- } while ((ret = end_gnutls_io (gnutls, direction, ret, err, errmsg)) == GNUTLS_E_AGAIN);
+#define END_GNUTLS_IO(gnutls, direction, ret, status, errmsg, err) \
+ status = end_gnutls_io (gnutls, direction, ret, err, errmsg); \
+ } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
-/* Checks whether the underlying base stream or GDatagramBased meets
- * @condition. */
-static gboolean
-g_tls_connection_gnutls_base_check (GTlsConnectionGnutls *gnutls,
- GIOCondition condition)
+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);
- 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);
+ /* We set EINTR rather than EAGAIN for G_IO_ERROR_WOULD_BLOCK so
+ * that GNUTLS_E_AGAIN only gets returned for gnutls-internal
+ * reasons, not for actual socket EAGAINs (and we have access
+ * to @error at the higher levels, so we can distinguish them
+ * that way later).
+ */
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ gnutls_transport_set_errno (priv->session, EINTR);
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ /* Return EAGAIN while handshaking so that GnuTLS handles retries for us
+ * internally in its handshaking code. */
+ 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);
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
+ gnutls_transport_set_errno (priv->session, EINTR);
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE))
+ gnutls_transport_set_errno (priv->session, EMSGSIZE);
else
- g_assert_not_reached ();
+ gnutls_transport_set_errno (priv->session, EIO);
}
-/* 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)
+static ssize_t
+g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t transport_data,
+ void *buf,
+ size_t buflen)
{
- 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;
+ GTlsConnectionBase *tls = transport_data;
+ GTlsConnectionGnutls *gnutls = transport_data;
+ ssize_t ret;
- /* If a handshake or close is in progress, then tls_istream and
- * tls_ostream are blocked, regardless of the base stream status.
+ /* 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.)
*/
- if (priv->handshaking)
- return FALSE;
+ g_clear_error (g_tls_connection_base_get_read_error (tls));
- if (((condition & G_IO_IN) && priv->read_closing) ||
- ((condition & G_IO_OUT) && priv->write_closing))
- return FALSE;
+ if (g_tls_connection_base_is_dtls (tls))
+ {
+ GInputVector vector = { buf, buflen };
+ GInputMessage message = { NULL, &vector, 1, 0, 0, NULL, NULL };
- /* Defer to the base stream or GDatagramBased. */
- return g_tls_connection_gnutls_base_check (gnutls, condition);
-}
+ ret = g_datagram_based_receive_messages (g_tls_connection_base_get_base_socket (tls),
+ &message, 1, 0,
+ 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));
-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));
- }
- else
- {
- source = g_source_new (&gnutls_tls_source_funcs,
- sizeof (GTlsConnectionGnutlsSource));
- }
- 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 ();
-
- gnutls_source->op_waiting = (gboolean) -1;
- gnutls_source->io_waiting = (gboolean) -1;
- gnutls_source_sync (gnutls_source);
-
- if (cancellable)
- {
- 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);
- }
-
- return source;
-}
-
-static GSource *
-g_tls_connection_gnutls_dtls_create_source (GDatagramBased *datagram_based,
- GIOCondition condition,
- GCancellable *cancellable)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
-
- return g_tls_connection_gnutls_create_source (gnutls, condition, cancellable);
-}
-
-static GIOCondition
-g_tls_connection_gnutls_condition_check (GDatagramBased *datagram_based,
- GIOCondition condition)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
-
- return (g_tls_connection_gnutls_check (gnutls, condition)) ? condition : 0;
-}
-
-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;
-
- if (g_cancellable_set_error_if_cancelled (cancellable, error))
- return FALSE;
-
- /* Convert from microseconds to milliseconds. */
- if (timeout != -1)
- timeout = timeout / 1000;
-
- start_time = g_get_monotonic_time ();
-
- g_cancellable_make_pollfd (priv->waiting_for_op, &fds[0]);
- n_fds = 1;
-
- if (g_cancellable_make_pollfd (cancellable, &fds[1]))
- n_fds++;
-
- while (!g_tls_connection_gnutls_condition_check (datagram_based, condition) &&
- !g_cancellable_is_cancelled (cancellable))
- {
- result = g_poll (fds, n_fds, 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 (n_fds > 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;
- }
-
- return !g_cancellable_set_error_if_cancelled (cancellable, error);
-}
-
-static void
-set_gnutls_error (GTlsConnectionGnutls *gnutls,
- GError *error)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- /* We set EINTR rather than EAGAIN for G_IO_ERROR_WOULD_BLOCK so
- * that GNUTLS_E_AGAIN only gets returned for gnutls-internal
- * reasons, not for actual socket EAGAINs (and we have access
- * to @error at the higher levels, so we can distinguish them
- * that way later).
- */
-
- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
- gnutls_transport_set_errno (priv->session, EINTR);
- else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
- {
- /* Return EAGAIN while handshaking so that GnuTLS handles retries for us
- * internally in its handshaking code. */
- if (priv->base_socket && priv->handshaking)
- gnutls_transport_set_errno (priv->session, EAGAIN);
- else
- gnutls_transport_set_errno (priv->session, EINTR);
- }
- else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
- gnutls_transport_set_errno (priv->session, EINTR);
- else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE))
- gnutls_transport_set_errno (priv->session, EMSGSIZE);
- else
- gnutls_transport_set_errno (priv->session, EIO);
-}
-
-static ssize_t
-g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t transport_data,
- void *buf,
- size_t buflen)
-{
- 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
- * 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);
-
- if (g_tls_connection_gnutls_is_dtls (gnutls))
- {
- GInputVector vector = { buf, buflen };
- GInputMessage message = { NULL, &vector, 1, 0, 0, NULL, NULL };
-
- ret = g_datagram_based_receive_messages (priv->base_socket,
- &message, 1, 0,
- priv->handshaking ? 0 : priv->read_timeout,
- priv->read_cancellable,
- &priv->read_error);
-
- if (ret > 0)
- ret = message.bytes_received;
- }
- else
- {
- ret = g_pollable_stream_read (G_INPUT_STREAM (priv->base_istream),
- buf, buflen,
- (priv->read_timeout != 0),
- priv->read_cancellable,
- &priv->read_error);
- }
+ if (ret > 0)
+ ret = message.bytes_received;
+ }
+ else
+ {
+ ret = g_pollable_stream_read (G_INPUT_STREAM (g_tls_connection_base_get_base_istream (tls)),
+ buf, buflen,
+ 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 +551,38 @@ 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 +592,16 @@ 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 +630,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 +681,11 @@ static int
g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data,
unsigned int ms)
{
- GTlsConnectionGnutls *gnutls = transport_data;
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+ GTlsConnectionBase *tls = transport_data;
/* 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 +706,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,22 +736,51 @@ 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 = G_TLS_CONNECTION_GNUTLS (tls);
+ GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (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;
+
+ 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, status, _("Error performing TLS handshake: %s"), error);
+
+ return status;
+}
+
static GTlsCertificate *
-get_peer_certificate_from_session (GTlsConnectionGnutls *gnutls)
+g_tls_connection_gnutls_retrieve_peer_certificate (GTlsConnectionBase *tls)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
const gnutls_datum_t *certs;
GTlsCertificateGnutls *chain;
unsigned int num_certs;
+ if (gnutls_certificate_type_get (priv->session) != GNUTLS_CRT_X509)
+ return NULL;
+
certs = gnutls_certificate_get_peers (priv->session, &num_certs);
if (!certs || !num_certs)
return NULL;
@@ -1787,751 +792,138 @@ get_peer_certificate_from_session (GTlsConnectionGnutls *gnutls)
return G_TLS_CERTIFICATE (chain);
}
-static GTlsCertificateFlags
-verify_peer_certificate (GTlsConnectionGnutls *gnutls,
- GTlsCertificate *peer_certificate)
+static int
+verify_certificate_cb (gnutls_session_t session)
{
- GTlsConnection *conn = G_TLS_CONNECTION (gnutls);
- GSocketConnectable *peer_identity;
- GTlsDatabase *database;
- GTlsCertificateFlags errors;
- gboolean is_client;
-
- is_client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
-
- if (!is_client)
- peer_identity = NULL;
- else if (!g_tls_connection_gnutls_is_dtls (gnutls))
- peer_identity = g_tls_client_connection_get_server_identity (G_TLS_CLIENT_CONNECTION (gnutls));
- else
- peer_identity = g_dtls_client_connection_get_server_identity (G_DTLS_CLIENT_CONNECTION (gnutls));
-
- errors = 0;
-
- database = g_tls_connection_get_database (conn);
- if (database == NULL)
- {
- errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
- errors |= g_tls_certificate_verify (peer_certificate, peer_identity, NULL);
- }
- else
- {
- GError *error = NULL;
-
- errors |= g_tls_database_verify_chain (database, peer_certificate,
- is_client ?
- G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER :
- G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT,
- peer_identity,
- g_tls_connection_get_interaction (conn),
- G_TLS_DATABASE_VERIFY_NONE,
- NULL, &error);
- if (error)
- {
- g_warning ("failure verifying certificate chain: %s",
- error->message);
- g_assert (errors != 0);
- g_clear_error (&error);
- }
- }
+ GTlsConnectionBase *tls = gnutls_session_get_ptr (session);
- return errors;
+ /* Return 0 for the handshake to continue, non-zero to terminate.
+ * Complete opposite of what OpenSSL does. */
+ return !g_tls_connection_base_handshake_thread_verify_certificate (tls);
}
static void
-update_peer_certificate_and_compute_errors (GTlsConnectionGnutls *gnutls)
+g_tls_connection_gnutls_prepare_handshake (GTlsConnectionBase *tls,
+ gchar **advertised_protocols)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- /* 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.
- *
- * 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)
+ if (advertised_protocols)
{
- priv->peer_certificate = get_peer_certificate_from_session (gnutls);
- if (priv->peer_certificate)
- priv->peer_certificate_errors = verify_peer_certificate (gnutls, priv->peer_certificate);
- }
+ gnutls_datum_t *protocols;
+ int n_protos, i;
- g_object_notify (G_OBJECT (gnutls), "peer-certificate");
- g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
+ n_protos = g_strv_length (advertised_protocols);
+ protocols = g_new (gnutls_datum_t, n_protos);
+ for (i = 0; advertised_protocols[i]; i++)
+ {
+ 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);
+ }
}
-static gboolean
-accept_or_reject_peer_certificate (gpointer user_data)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_handshake_thread_handshake (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
{
- GTlsConnectionGnutls *gnutls = user_data;
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- gboolean accepted = FALSE;
-
- g_assert (g_main_context_is_owner (priv->handshake_context));
-
- g_mutex_lock (&priv->verify_certificate_mutex);
+ GTlsConnectionBaseStatus status;
+ int ret;
- update_peer_certificate_and_compute_errors (gnutls);
+ if (!g_tls_connection_base_ever_handshaked (tls))
+ g_tls_connection_gnutls_set_handshake_priority (gnutls);
- if (G_IS_TLS_CLIENT_CONNECTION (gnutls) && priv->peer_certificate != NULL)
+ if (timeout > 0)
{
- GTlsCertificateFlags validation_flags;
+ unsigned int timeout_ms;
- if (!g_tls_connection_gnutls_is_dtls (gnutls))
- validation_flags =
- g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (gnutls));
- else
- validation_flags =
- g_dtls_client_connection_get_validation_flags (G_DTLS_CLIENT_CONNECTION (gnutls));
+ /* Convert from microseconds to milliseconds, but ensure the timeout
+ * remains positive. */
+ timeout_ms = (timeout + 999) / 1000;
- if ((priv->peer_certificate_errors & validation_flags) == 0)
- accepted = TRUE;
+ gnutls_handshake_set_timeout (priv->session, timeout_ms);
+ gnutls_dtls_set_timeouts (priv->session, 1000 /* default */, timeout_ms);
}
- if (!accepted)
+ BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
+ ret = gnutls_handshake (priv->session);
+ if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
{
- g_main_context_pop_thread_default (priv->handshake_context);
- accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls),
- priv->peer_certificate,
- priv->peer_certificate_errors);
- g_main_context_push_thread_default (priv->handshake_context);
- }
-
- priv->peer_certificate_accepted = accepted;
+ guint8 buf[1024];
- /* This has to be the very last statement before signaling the
- * condition variable because otherwise the code could spuriously
- * wakeup and continue before we are done here.
- */
- priv->peer_certificate_examined = TRUE;
+ /* Got app data while waiting for rehandshake; buffer it and try again */
+ ret = gnutls_record_recv (priv->session, buf, sizeof (buf));
+ if (ret > -1)
+ {
+ 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, status,
+ _("Error performing TLS handshake"), error);
- g_cond_signal (&priv->verify_certificate_condition);
- g_mutex_unlock (&priv->verify_certificate_mutex);
+ return status;
+}
- g_object_notify (G_OBJECT (gnutls), "peer-certificate");
- g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
+static void
+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);
+ gnutls_datum_t protocol;
- return G_SOURCE_REMOVE;
+ if (gnutls_alpn_get_selected_protocol (priv->session, &protocol) == 0 && protocol.size > 0)
+ {
+ g_assert (!*negotiated_protocol);
+ *negotiated_protocol = g_strndup ((gchar *)protocol.data, protocol.size);
+ }
}
-static int
-verify_certificate_cb (gnutls_session_t session)
+static gboolean
+g_tls_connection_gnutls_is_session_resumed (GTlsConnectionBase *tls)
{
- GTlsConnectionGnutls *gnutls = gnutls_session_get_ptr (session);
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- gboolean accepted;
- g_mutex_lock (&priv->verify_certificate_mutex);
- priv->peer_certificate_examined = FALSE;
- priv->peer_certificate_accepted = FALSE;
- g_mutex_unlock (&priv->verify_certificate_mutex);
-
- /* Invoke the callback on the handshake context's thread. This is
- * necessary because we need to ensure the accept-certificate signal
- * is emitted on the original thread.
- */
- g_assert (priv->handshake_context);
- g_main_context_invoke (priv->handshake_context, accept_or_reject_peer_certificate, gnutls);
-
- /* We'll block the handshake thread until the original thread has
- * decided whether to accept the certificate.
- */
- g_mutex_lock (&priv->verify_certificate_mutex);
- while (!priv->peer_certificate_examined)
- g_cond_wait (&priv->verify_certificate_condition, &priv->verify_certificate_mutex);
- accepted = priv->peer_certificate_accepted;
- g_mutex_unlock (&priv->verify_certificate_mutex);
-
- /* Return 0 for the handshake to continue, non-zero to terminate. */
- return !accepted;
-}
-
-static void
-handshake_thread (GTask *task,
- gpointer object,
- gpointer task_data,
- GCancellable *cancellable)
-{
- GTlsConnectionGnutls *gnutls = object;
- 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 (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;
- }
-
- if (!G_IS_TLS_CLIENT_CONNECTION (gnutls))
- {
- /* 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;
- }
- }
- }
-
- priv->started_handshake = TRUE;
-
- if (!priv->ever_handshaked)
- g_tls_connection_gnutls_set_handshake_priority (gnutls);
-
- /* 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_handshake (priv->session);
- if (ret == GNUTLS_E_GOT_APPLICATION_DATA)
- {
- guint8 buf[1024];
-
- /* Got app data while waiting for rehandshake; buffer it and try again */
- ret = gnutls_record_recv (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);
- 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
-
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
-}
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
-static void
-update_negotiated_protocol (GTlsConnectionGnutls *gnutls)
-{
- 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);
-
- if (gnutls_session_is_resumed (priv->session))
- {
- /* Because this session was resumed, we skipped certificate
- * verification on this handshake, so we missed our earlier
- * chance to set peer_certificate and peer_certificate_errors.
- * Do so here instead.
- *
- * The certificate has already been accepted, so we don't do
- * anything with the result here.
- */
- g_mutex_lock (&priv->verify_certificate_mutex);
- update_peer_certificate_and_compute_errors (gnutls);
- priv->peer_certificate_examined = TRUE;
- priv->peer_certificate_accepted = TRUE;
- g_mutex_unlock (&priv->verify_certificate_mutex);
- }
-
- if (g_task_propagate_boolean (task, error) &&
- priv->peer_certificate && !priv->peer_certificate_accepted)
- {
- g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
- _("Unacceptable TLS certificate"));
- }
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
- if (!*error && priv->advertised_protocols)
- update_negotiated_protocol (gnutls);
-#endif
-
- if (*error && priv->started_handshake)
- priv->handshake_error = g_error_copy (*error);
-
- 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,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GTask *task;
- gboolean success;
- gint64 *timeout = NULL;
- GError *my_error = NULL;
-
- g_assert (priv->handshake_context == NULL);
- priv->handshake_context = g_main_context_new ();
-
- g_main_context_push_thread_default (priv->handshake_context);
-
- begin_handshake (gnutls);
-
- task = g_task_new (conn, cancellable, sync_handshake_thread_completed, NULL);
- g_task_set_source_tag (task, g_tls_connection_gnutls_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 (task, handshake_thread);
- crank_sync_handshake_context (gnutls, cancellable);
-
- success = finish_handshake (gnutls, task, &my_error);
-
- g_main_context_pop_thread_default (priv->handshake_context);
- g_clear_pointer (&priv->handshake_context, g_main_context_unref);
- g_object_unref (task);
-
- yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE);
-
- if (my_error)
- g_propagate_error (error, my_error);
- return success;
-}
-
-static gboolean
-g_tls_connection_gnutls_dtls_handshake (GDtlsConnection *conn,
- GCancellable *cancellable,
- GError **error)
-{
- return g_tls_connection_gnutls_handshake (G_TLS_CONNECTION (conn),
- cancellable, error);
-}
-
-/* In the async version we use two GTasks; one to run handshake_thread() and
- * then call handshake_thread_completed(), and a second to call the caller's
- * original callback after we call finish_handshake().
- */
-
-static void
-handshake_thread_completed (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- GTask *caller_task = user_data;
- GTlsConnectionGnutls *gnutls = g_task_get_source_object (caller_task);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GError *error = NULL;
- gboolean need_finish_handshake, success;
-
- g_mutex_lock (&priv->op_mutex);
- if (priv->need_finish_handshake)
- {
- need_finish_handshake = TRUE;
- priv->need_finish_handshake = FALSE;
- }
- else
- need_finish_handshake = FALSE;
- g_mutex_unlock (&priv->op_mutex);
-
- if (need_finish_handshake)
- {
- success = finish_handshake (gnutls, G_TASK (result), &error);
- if (success)
- g_task_return_boolean (caller_task, TRUE);
- else
- g_task_return_error (caller_task, error);
- }
- else if (priv->handshake_error)
- g_task_return_error (caller_task, g_error_copy (priv->handshake_error));
- else
- g_task_return_boolean (caller_task, TRUE);
-
- g_clear_pointer (&priv->handshake_context, g_main_context_unref);
- g_object_unref (caller_task);
-}
-
-static void
-async_handshake_thread (GTask *task,
- gpointer object,
- gpointer task_data,
- GCancellable *cancellable)
-{
- GTlsConnectionGnutls *gnutls = object;
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- handshake_thread (task, object, task_data, cancellable);
-
- g_mutex_lock (&priv->op_mutex);
- priv->need_finish_handshake = TRUE;
- /* yield_op will clear handshaking too, but we don't want the
- * connection to be briefly "handshaking && need_finish_handshake"
- * after we unlock the mutex.
- */
- priv->handshaking = FALSE;
- g_mutex_unlock (&priv->op_mutex);
-
- yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE);
-}
-
-static void
-g_tls_connection_gnutls_handshake_async (GTlsConnection *conn,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_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_gnutls_handshake_async);
- g_task_set_priority (caller_task, io_priority);
-
- begin_handshake (G_TLS_CONNECTION_GNUTLS (conn));
-
- thread_task = g_task_new (conn, cancellable,
- handshake_thread_completed, caller_task);
- g_task_set_source_tag (thread_task, g_tls_connection_gnutls_handshake_async);
- g_task_set_priority (thread_task, io_priority);
-
- timeout = g_new0 (gint64, 1);
- *timeout = -1; /* blocking */
- g_task_set_task_data (thread_task, timeout, g_free);
-
- g_task_run_in_thread (thread_task, async_handshake_thread);
- g_object_unref (thread_task);
-}
-
-static gboolean
-g_tls_connection_gnutls_handshake_finish (GTlsConnection *conn,
- GAsyncResult *result,
- GError **error)
-{
- g_return_val_if_fail (g_task_is_valid (result, conn), FALSE);
-
- return g_task_propagate_boolean (G_TASK (result), error);
+ return gnutls_session_is_resumed (priv->session);
}
-static void
-g_tls_connection_gnutls_dtls_handshake_async (GDtlsConnection *conn,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- g_tls_connection_gnutls_handshake_async (G_TLS_CONNECTION (conn), io_priority,
- cancellable, callback, user_data);
-}
-
-static gboolean
-g_tls_connection_gnutls_dtls_handshake_finish (GDtlsConnection *conn,
- GAsyncResult *result,
- GError **error)
-{
- return g_tls_connection_gnutls_handshake_finish (G_TLS_CONNECTION (conn),
- result, error);
-}
-
-static gboolean
-do_implicit_handshake (GTlsConnectionGnutls *gnutls,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- gint64 *thread_timeout = NULL;
-
- /* 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 (gnutls, cancellable,
- timeout ? sync_handshake_thread_completed : NULL,
- NULL);
- g_task_set_source_tag (priv->implicit_handshake,
- do_implicit_handshake);
-
- thread_timeout = g_new0 (gint64, 1);
- g_task_set_task_data (priv->implicit_handshake,
- thread_timeout, g_free);
-
- begin_handshake (gnutls);
-
- if (timeout != 0)
- {
- GError *my_error = NULL;
- gboolean success;
-
- /* In the blocking case, run the handshake operation synchronously in
- * another thread, and delegate handling the timeout to that thread; it
- * should return G_IO_ERROR_TIMED_OUT iff (timeout > 0) and the operation
- * times out. If (timeout < 0) it should block indefinitely until the
- * operation is complete or errors. */
- *thread_timeout = timeout;
-
- g_mutex_unlock (&priv->op_mutex);
-
- g_task_set_return_on_cancel (priv->implicit_handshake, TRUE);
- g_task_run_in_thread (priv->implicit_handshake, handshake_thread);
-
- crank_sync_handshake_context (gnutls, cancellable);
-
- success = finish_handshake (gnutls,
- priv->implicit_handshake,
- &my_error);
-
- g_main_context_pop_thread_default (priv->handshake_context);
- g_clear_pointer (&priv->handshake_context, g_main_context_unref);
- g_clear_object (&priv->implicit_handshake);
-
- yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE);
-
- g_mutex_lock (&priv->op_mutex);
-
- if (my_error)
- g_propagate_error (error, my_error);
- return success;
- }
- else
- {
- /* In the non-blocking case, start the asynchronous handshake operation
- * and return EWOULDBLOCK to the caller, who will handle polling for
- * completion of the handshake and whatever operation they actually cared
- * about. Run the actual operation as blocking in its thread. */
- *thread_timeout = -1; /* blocking */
-
- g_task_run_in_thread (priv->implicit_handshake,
- async_handshake_thread);
-
- /* 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;
- }
-}
-
-gssize
-g_tls_connection_gnutls_read (GTlsConnectionGnutls *gnutls,
- void *buffer,
- gsize count,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_read (GTlsConnectionBase *tls,
+ void *buffer,
+ gsize count,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+ GTlsConnectionBaseStatus status;
gssize ret;
- if (priv->app_data_buf && !priv->handshaking)
- {
- ret = MIN (count, priv->app_data_buf->len);
- memcpy (buffer, priv->app_data_buf->data, ret);
- if (ret == priv->app_data_buf->len)
- g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
- else
- g_byte_array_remove_range (priv->app_data_buf, 0, ret);
- return ret;
- }
-
- again:
- if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ,
- timeout, cancellable, error))
- return -1;
-
BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout, cancellable);
ret = gnutls_record_recv (priv->session, buffer, count);
- END_GNUTLS_IO (gnutls, G_IO_IN, ret, _("Error reading data from TLS socket"), error);
-
- yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ);
+ END_GNUTLS_IO (gnutls, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
if (ret >= 0)
- return ret;
- else if (ret == GNUTLS_E_REHANDSHAKE)
- goto again;
- else
- return -1;
+ *nread = ret;
+ return status;
}
static gsize
-input_vectors_from_gnutls_datum_t (GInputVector *vectors,
- guint num_vectors,
- const gnutls_datum_t *datum)
+input_vectors_from_gnutls_datum_t (GInputVector *vectors,
+ guint num_vectors,
+ const gnutls_datum_t *datum)
{
guint i;
gsize total = 0;
@@ -2553,47 +945,21 @@ input_vectors_from_gnutls_datum_t (GInputVector *vectors,
return total;
}
-static gssize
-g_tls_connection_gnutls_read_message (GTlsConnectionGnutls *gnutls,
- GInputVector *vectors,
- guint num_vectors,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_read_message (GTlsConnectionBase *tls,
+ GInputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- guint i;
+ GTlsConnectionBaseStatus status;
gssize ret;
gnutls_packet_t packet = { 0, };
- /* Copy data out of the app data buffer first. */
- if (priv->app_data_buf && !priv->handshaking)
- {
- ret = 0;
-
- for (i = 0; i < num_vectors; i++)
- {
- gsize count;
- GInputVector *vec = &vectors[i];
-
- count = MIN (vec->size, priv->app_data_buf->len);
- ret += count;
-
- memcpy (vec->buffer, priv->app_data_buf->data, count);
- if (count == priv->app_data_buf->len)
- g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
- else
- g_byte_array_remove_range (priv->app_data_buf, 0, count);
- }
-
- return ret;
- }
-
- again:
- if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ,
- timeout, cancellable, error))
- return -1;
-
BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout, cancellable);
/* Receive the entire datagram (zero-copy). */
@@ -2608,148 +974,57 @@ g_tls_connection_gnutls_read_message (GTlsConnectionGnutls *gnutls,
gnutls_packet_deinit (packet);
}
- END_GNUTLS_IO (gnutls, G_IO_IN, ret, _("Error reading data from TLS socket"), error);
-
- yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ);
+ END_GNUTLS_IO (gnutls, G_IO_IN, ret, status, _("Error reading data from TLS socket"), error);
if (ret >= 0)
- return ret;
- else if (ret == GNUTLS_E_REHANDSHAKE)
- goto again;
- else
- return -1;
-}
-
-static gint
-g_tls_connection_gnutls_receive_messages (GDatagramBased *datagram_based,
- GInputMessage *messages,
- guint num_messages,
- gint flags,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls;
- guint i;
- GError *child_error = NULL;
-
- gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
-
- if (flags != G_SOCKET_MSG_NONE)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- _("Receive flags are not supported"));
- return -1;
- }
-
- for (i = 0; i < num_messages && child_error == NULL; i++)
- {
- GInputMessage *message = &messages[i];
- gssize n_bytes_read;
-
- n_bytes_read = g_tls_connection_gnutls_read_message (gnutls,
- message->vectors,
- message->num_vectors,
- timeout,
- cancellable,
- &child_error);
-
- if (message->address != NULL)
- *message->address = NULL;
- message->flags = G_SOCKET_MSG_NONE;
- if (message->control_messages != NULL)
- *message->control_messages = NULL;
- message->num_control_messages = 0;
-
- if (n_bytes_read > 0)
- {
- message->bytes_received = n_bytes_read;
- }
- else if (n_bytes_read == 0)
- {
- /* EOS. */
- break;
- }
- else if (i > 0 &&
- (g_error_matches (child_error,
- G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
- g_error_matches (child_error,
- G_IO_ERROR, G_IO_ERROR_TIMED_OUT)))
- {
- /* Blocked or timed out after receiving some messages successfully. */
- g_clear_error (&child_error);
- break;
- }
- else
- {
- /* Error, including G_IO_ERROR_WOULD_BLOCK or G_IO_ERROR_TIMED_OUT on
- * the first message; or G_IO_ERROR_CANCELLED at any time. */
- break;
- }
- }
-
- if (child_error != NULL)
- {
- g_propagate_error (error, child_error);
- return -1;
- }
-
- return i;
+ *nread = ret;
+ return status;
}
-gssize
-g_tls_connection_gnutls_write (GTlsConnectionGnutls *gnutls,
- const void *buffer,
- gsize count,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_write (GTlsConnectionBase *tls,
+ const void *buffer,
+ gsize count,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+ GTlsConnectionBaseStatus status;
gssize ret;
- again:
- if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE,
- timeout, cancellable, error))
- return -1;
-
BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout, cancellable);
ret = gnutls_record_send (priv->session, buffer, count);
- END_GNUTLS_IO (gnutls, G_IO_OUT, ret, _("Error writing data to TLS socket"), error);
-
- yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE);
+ END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
if (ret >= 0)
- return ret;
- else if (ret == GNUTLS_E_REHANDSHAKE)
- goto again;
- else
- return -1;
+ *nwrote = ret;
+ return status;
}
-static gssize
-g_tls_connection_gnutls_write_message (GTlsConnectionGnutls *gnutls,
- GOutputVector *vectors,
- guint num_vectors,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_write_message (GTlsConnectionBase *tls,
+ GOutputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+ GTlsConnectionBaseStatus status;
gssize ret;
guint i;
gsize total_message_size;
- again:
- if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE,
- timeout, cancellable, error))
- return -1;
-
/* Calculate the total message size and check it’s not too big. */
for (i = 0, total_message_size = 0; i < num_vectors; i++)
total_message_size += vectors[i].size;
- if (priv->base_socket != NULL &&
+ if (g_tls_connection_base_is_dtls (tls) &&
gnutls_dtls_get_data_mtu (priv->session) < total_message_size)
{
char *message;
@@ -2766,7 +1041,7 @@ g_tls_connection_gnutls_write_message (GTlsConnectionGnutls *gnutls,
mtu);
g_free (message);
- goto done;
+ return G_TLS_CONNECTION_BASE_ERROR;
}
/* Queue up the data from all the vectors. */
@@ -2787,454 +1062,54 @@ g_tls_connection_gnutls_write_message (GTlsConnectionGnutls *gnutls,
BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout, cancellable);
ret = gnutls_record_uncork (priv->session, 0 /* flags */);
- END_GNUTLS_IO (gnutls, G_IO_OUT, ret, _("Error writing data to TLS socket"), error);
-
- done:
- yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE);
-
- if (ret >= 0)
- return ret;
- else if (ret == GNUTLS_E_REHANDSHAKE)
- goto again;
- else
- return -1;
-}
+ END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status, _("Error writing data to TLS socket"), error);
-static gint
-g_tls_connection_gnutls_send_messages (GDatagramBased *datagram_based,
- GOutputMessage *messages,
- guint num_messages,
- gint flags,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls;
- guint i;
- GError *child_error = NULL;
-
- gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
-
- if (flags != G_SOCKET_MSG_NONE)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- _("Send flags are not supported"));
- return -1;
- }
-
- for (i = 0; i < num_messages && child_error == NULL; i++)
- {
- GOutputMessage *message = &messages[i];
- gssize n_bytes_sent;
-
- n_bytes_sent = g_tls_connection_gnutls_write_message (gnutls,
- message->vectors,
- message->num_vectors,
- timeout,
- cancellable,
- &child_error);
-
- if (n_bytes_sent >= 0)
- {
- message->bytes_sent = n_bytes_sent;
- }
- else if (i > 0 &&
- (g_error_matches (child_error,
- G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
- g_error_matches (child_error,
- G_IO_ERROR, G_IO_ERROR_TIMED_OUT)))
- {
- /* Blocked or timed out after sending some messages successfully. */
- g_clear_error (&child_error);
- break;
- }
- else
- {
- /* Error, including G_IO_ERROR_WOULD_BLOCK or G_IO_ERROR_TIMED_OUT
- * on the first message; or G_IO_ERROR_CANCELLED at any time. */
- break;
- }
- }
-
- if (child_error != NULL)
- {
- g_propagate_error (error, child_error);
- return -1;
- }
-
- return i;
-}
-
-static GInputStream *
-g_tls_connection_gnutls_get_input_stream (GIOStream *stream)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- return priv->tls_istream;
-}
-
-static GOutputStream *
-g_tls_connection_gnutls_get_output_stream (GIOStream *stream)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
- GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- return priv->tls_ostream;
+ if (ret > 0)
+ *nwrote = ret;
+ return status;
}
-gboolean
-g_tls_connection_gnutls_close_internal (GIOStream *stream,
- GTlsDirection direction,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_close (GTlsConnectionBase *tls,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
- GTlsConnectionGnutlsOp op;
- gboolean success = TRUE;
- int ret = 0;
- GError *gnutls_error = NULL, *stream_error = NULL;
-
- /* This can be called from g_io_stream_close(), g_input_stream_close(),
- * g_output_stream_close() or g_tls_connection_close(). In all cases, we only
- * do the gnutls_bye() for writing. The difference is how we set the flags on
- * this class and how the underlying stream is closed.
- */
-
- g_return_val_if_fail (direction != G_TLS_DIRECTION_NONE, FALSE);
-
- if (direction == G_TLS_DIRECTION_BOTH)
- op = G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH;
- else if (direction == G_TLS_DIRECTION_READ)
- op = G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ;
- else
- op = G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE;
-
- if (!claim_op (gnutls, op, timeout, cancellable, error))
- return FALSE;
-
- if (priv->ever_handshaked && !priv->write_closed &&
- direction & G_TLS_DIRECTION_WRITE)
- {
- BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
- ret = gnutls_bye (priv->session, GNUTLS_SHUT_WR);
- END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
- _("Error performing TLS close"), &gnutls_error);
-
- priv->write_closed = TRUE;
- }
-
- if (!priv->read_closed && direction & G_TLS_DIRECTION_READ)
- priv->read_closed = TRUE;
-
- /* Close the underlying streams. Do this even if the gnutls_bye() call failed,
- * as the parent GIOStream will have set its internal closed flag and hence
- * this implementation will never be called again. */
- if (priv->base_io_stream != NULL)
- {
- if (direction == G_TLS_DIRECTION_BOTH)
- success = g_io_stream_close (priv->base_io_stream,
- cancellable, &stream_error);
- else if (direction & G_TLS_DIRECTION_READ)
- success = g_input_stream_close (g_io_stream_get_input_stream (priv->base_io_stream),
- cancellable, &stream_error);
- else if (direction & G_TLS_DIRECTION_WRITE)
- success = g_output_stream_close (g_io_stream_get_output_stream (priv->base_io_stream),
- cancellable, &stream_error);
- }
- else if (g_tls_connection_gnutls_is_dtls (gnutls))
- {
- /* We do not close underlying #GDatagramBaseds. There is no
- * g_datagram_based_close() method since different datagram-based
- * protocols vary wildly in how they close. */
- success = TRUE;
- }
- else
- {
- g_assert_not_reached ();
- }
-
- yield_op (gnutls, op);
-
- /* Propagate errors. */
- if (ret != 0)
- {
- g_propagate_error (error, gnutls_error);
- g_clear_error (&stream_error);
- }
- else if (!success)
- {
- g_propagate_error (error, stream_error);
- g_clear_error (&gnutls_error);
- }
-
- return success && (ret == 0);
-}
-
-static gboolean
-g_tls_connection_gnutls_close (GIOStream *stream,
- GCancellable *cancellable,
- GError **error)
-{
- return g_tls_connection_gnutls_close_internal (stream,
- G_TLS_DIRECTION_BOTH,
- -1, /* blocking */
- cancellable, error);
-}
-
-static gboolean
-g_tls_connection_gnutls_dtls_shutdown (GDtlsConnection *conn,
- gboolean shutdown_read,
- gboolean shutdown_write,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsDirection direction = G_TLS_DIRECTION_NONE;
-
- if (shutdown_read)
- direction |= G_TLS_DIRECTION_READ;
- if (shutdown_write)
- direction |= G_TLS_DIRECTION_WRITE;
-
- return g_tls_connection_gnutls_close_internal (G_IO_STREAM (conn),
- direction,
- -1, /* blocking */
- cancellable, error);
-}
-
-/* We do async close as synchronous-in-a-thread so we don't need to
- * implement G_IO_IN/G_IO_OUT flip-flopping just for this one case
- * (since handshakes are also done synchronously now).
- */
-static void
-close_thread (GTask *task,
- gpointer object,
- gpointer task_data,
- GCancellable *cancellable)
-{
- GIOStream *stream = object;
- GTlsDirection direction;
- GError *error = NULL;
-
- direction = GPOINTER_TO_INT (g_task_get_task_data (task));
-
- if (!g_tls_connection_gnutls_close_internal (stream, direction,
- -1, /* blocking */
- cancellable, &error))
- g_task_return_error (task, error);
- else
- g_task_return_boolean (task, TRUE);
-}
-
-static void
-g_tls_connection_gnutls_close_internal_async (GIOStream *stream,
- GTlsDirection direction,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GTask *task;
-
- task = g_task_new (stream, cancellable, callback, user_data);
- g_task_set_source_tag (task, g_tls_connection_gnutls_close_internal_async);
- g_task_set_priority (task, io_priority);
- g_task_set_task_data (task, GINT_TO_POINTER (direction), NULL);
- g_task_run_in_thread (task, close_thread);
- g_object_unref (task);
-}
-
-static void
-g_tls_connection_gnutls_close_async (GIOStream *stream,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- g_tls_connection_gnutls_close_internal_async (stream, G_TLS_DIRECTION_BOTH,
- io_priority, cancellable,
- callback, user_data);
-}
-
-static gboolean
-g_tls_connection_gnutls_close_finish (GIOStream *stream,
- GAsyncResult *result,
- GError **error)
-{
- g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
-
- return g_task_propagate_boolean (G_TASK (result), error);
-}
-
-static void
-g_tls_connection_gnutls_dtls_shutdown_async (GDtlsConnection *conn,
- gboolean shutdown_read,
- gboolean shutdown_write,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GTlsDirection direction = G_TLS_DIRECTION_NONE;
-
- if (shutdown_read)
- direction |= G_TLS_DIRECTION_READ;
- if (shutdown_write)
- direction |= G_TLS_DIRECTION_WRITE;
-
- g_tls_connection_gnutls_close_internal_async (G_IO_STREAM (conn), direction,
- io_priority, cancellable,
- callback, user_data);
-}
-
-static gboolean
-g_tls_connection_gnutls_dtls_shutdown_finish (GDtlsConnection *conn,
- GAsyncResult *result,
- GError **error)
-{
- g_return_val_if_fail (g_task_is_valid (result, conn), FALSE);
-
- 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);
-}
+ GTlsConnectionBaseStatus status;
+ int ret;
-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);
+ BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
+ ret = gnutls_bye (priv->session, GNUTLS_SHUT_WR);
+ END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status, _("Error performing TLS close: %s"), error);
- return priv->negotiated_protocol;
+ return status;
}
-#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->retrieve_peer_certificate = g_tls_connection_gnutls_retrieve_peer_certificate;
+ base_class->complete_handshake = g_tls_connection_gnutls_complete_handshake;
+ base_class->is_session_resumed = g_tls_connection_gnutls_is_session_resumed;
+ base_class->read_fn = g_tls_connection_gnutls_read;
+ base_class->read_message_fn = g_tls_connection_gnutls_read_message;
+ base_class->write_fn = g_tls_connection_gnutls_write;
+ base_class->write_message_fn = g_tls_connection_gnutls_write_message;
+ base_class->close_fn = g_tls_connection_gnutls_close;
}
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..03c5ef4 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -29,24 +29,21 @@
#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;
-
- void (*failed) (GTlsConnectionGnutls *gnutls);
-
- void (*begin_handshake) (GTlsConnectionGnutls *gnutls);
- void (*finish_handshake) (GTlsConnectionGnutls *gnutls,
- GError **inout_error);
+ GTlsConnectionBaseClass parent_class;
};
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 +51,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..e046cf5 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -204,15 +204,16 @@ g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t
}
static void
-g_tls_server_connection_gnutls_failed (GTlsConnectionGnutls *conn)
+g_tls_server_connection_gnutls_failed (GTlsConnectionBase *tls)
{
- gnutls_db_remove_session (g_tls_connection_gnutls_get_session (conn));
+ gnutls_db_remove_session (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)));
}
static void
-g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+g_tls_server_connection_gnutls_prepare_handshake (GTlsConnectionBase *tls,
+ gchar **advertised_protocols)
{
- 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 +231,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)
-{
+ G_TLS_CONNECTION_BASE_CLASS (g_tls_server_connection_gnutls_parent_class)->prepare_handshake (tls,
advertised_protocols);
}
/* Session cache management */
@@ -302,15 +299,14 @@ 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);
- gobject_class->finalize = g_tls_server_connection_gnutls_finalize;
+ 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->prepare_handshake = g_tls_server_connection_gnutls_prepare_handshake;
+ base_class->failed = g_tls_server_connection_gnutls_failed;
g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
}
diff --git a/tls/gnutls/meson.build b/tls/gnutls/meson.build
index 4ff127e..ac46981 100644
--- a/tls/gnutls/meson.build
+++ b/tls/gnutls/meson.build
@@ -6,8 +6,6 @@ sources = files(
'gtlsconnection-gnutls.c',
'gtlsdatabase-gnutls.c',
'gtlsfiledatabase-gnutls.c',
- 'gtlsinputstream-gnutls.c',
- 'gtlsoutputstream-gnutls.c',
'gtlsserverconnection-gnutls.c'
)
@@ -18,7 +16,8 @@ deps = [
glib_dep,
gmodule_dep,
gobject_dep,
- gnutls_dep
+ gnutls_dep,
+ tlsbase_dep
]
module = shared_module(
@@ -34,10 +33,13 @@ module = shared_module(
)
if get_option('static_modules')
+ # link_whole is a workaround for a meson bug
+ # https://github.com/mesonbuild/meson/pull/3939
static_library('giognutls',
objects: module.extract_all_objects(),
install: true,
- install_dir: gio_module_dir
+ install_dir: gio_module_dir,
+ link_whole: [tlsbase]
)
pkg.generate(module)
endif
diff --git a/tls/openssl/gtlsbackend-openssl.c b/tls/openssl/gtlsbackend-openssl.c
index 486dd1c..8c2aab1 100644
--- a/tls/openssl/gtlsbackend-openssl.c
+++ b/tls/openssl/gtlsbackend-openssl.c
@@ -184,41 +184,6 @@ g_tls_backend_openssl_finalize (GObject *object)
G_OBJECT_CLASS (g_tls_backend_openssl_parent_class)->finalize (object);
}
-static GTlsDatabase *
-g_tls_backend_openssl_create_database (GTlsBackendOpenssl *self,
- GError **error)
-{
- gchar *anchor_file = NULL;
- GTlsDatabase *database;
-
-#ifdef G_OS_WIN32
- if (g_getenv ("G_TLS_OPENSSL_HANDLE_CERT_RELOCATABLE") != NULL)
- {
- gchar *module_dir;
-
- module_dir = g_win32_get_package_installation_directory_of_module (NULL);
- anchor_file = g_build_filename (module_dir, "bin", "cert.pem", NULL);
- g_free (module_dir);
- }
-#endif
-
- if (anchor_file == NULL)
- {
- const gchar *openssl_cert_file;
-
- openssl_cert_file = g_getenv (X509_get_default_cert_file_env ());
- if (openssl_cert_file == NULL)
- openssl_cert_file = X509_get_default_cert_file ();
-
- anchor_file = g_strdup (openssl_cert_file);
- }
-
- database = g_tls_file_database_new (anchor_file, error);
- g_free (anchor_file);
-
- return database;
-}
-
static void
g_tls_backend_openssl_class_init (GTlsBackendOpensslClass *klass)
{
diff --git a/tls/openssl/gtlsclientconnection-openssl.c b/tls/openssl/gtlsclientconnection-openssl.c
index 39ebcf1..e125543 100644
--- a/tls/openssl/gtlsclientconnection-openssl.c
+++ b/tls/openssl/gtlsclientconnection-openssl.c
@@ -34,6 +34,7 @@
#include "gtlsclientconnection-openssl.h"
#include "gtlsbackend-openssl.h"
#include "gtlscertificate-openssl.h"
+#include "gtlsdatabase-openssl.h"
#include <glib/gi18n-lib.h>
#define DEFAULT_CIPHER_LIST "HIGH:!DSS:!aNULL@STRENGTH"
@@ -234,26 +235,54 @@ g_tls_client_connection_openssl_constructed (GObject *object)
G_OBJECT_CLASS (g_tls_client_connection_openssl_parent_class)->constructed (object);
}
-static GTlsConnectionBaseStatus
-g_tls_client_connection_openssl_handshake (GTlsConnectionBase *tls,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
+static GTlsCertificateFlags
+verify_ocsp_response (GTlsClientConnectionOpenssl *openssl,
+ GTlsCertificate *peer_certificate)
{
- return G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_openssl_parent_class)->
- handshake (tls, timeout, cancellable, error);
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
+ SSL *ssl = NULL;
+ OCSP_RESPONSE *resp = NULL;
+ GTlsDatabase *database;
+ long len = 0;
+ unsigned char *p = NULL;
+
+ ssl = g_tls_connection_openssl_get_ssl (G_TLS_CONNECTION_OPENSSL (openssl));
+ len = SSL_get_tlsext_status_ocsp_resp (ssl, &p);
+ /* Soft fail in case of no response is the best we can do
+ * FIXME: this makes it security theater, why bother with OCSP at all? */
+ if (p == NULL)
+ return 0;
+
+ resp = d2i_OCSP_RESPONSE (NULL, (const unsigned char **)&p, len);
+ if (resp == NULL)
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ database = g_tls_connection_get_database (G_TLS_CONNECTION (openssl));
+
+ /* If there's no database, then G_TLS_CERTIFICATE_UNKNOWN_CA must be flagged,
+ * and this function is only called if there are no flags.
+ */
+ g_assert (database);
+
+ return g_tls_database_openssl_verify_ocsp_response (G_TLS_DATABASE_OPENSSL (database),
+ peer_certificate,
+ resp);
+#else
+ return 0;
+#endif
}
-static GTlsConnectionBaseStatus
-g_tls_client_connection_openssl_complete_handshake (GTlsConnectionBase *tls,
- GError **error)
+static GTlsCertificateFlags
+g_tls_client_connection_openssl_verify_peer_certificate (GTlsConnectionBase *tls,
+ GTlsCertificate *certificate,
+ GTlsCertificateFlags flags)
{
- GTlsConnectionBaseStatus status;
+ GTlsClientConnectionOpenssl *openssl = G_TLS_CLIENT_CONNECTION_OPENSSL (tls);
- status = G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_openssl_parent_class)->
- complete_handshake (tls, error);
+ if (flags == 0)
+ flags = verify_ocsp_response (openssl, certificate);
- return status;
+ return flags;
}
static SSL *
@@ -267,17 +296,16 @@ g_tls_client_connection_openssl_class_init (GTlsClientConnectionOpensslClass *kl
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
- GTlsConnectionOpensslClass *connection_class = G_TLS_CONNECTION_OPENSSL_CLASS (klass);
+ GTlsConnectionOpensslClass *openssl_class = G_TLS_CONNECTION_OPENSSL_CLASS (klass);
- gobject_class->finalize = g_tls_client_connection_openssl_finalize;
- gobject_class->get_property = g_tls_client_connection_openssl_get_property;
- gobject_class->set_property = g_tls_client_connection_openssl_set_property;
- gobject_class->constructed = g_tls_client_connection_openssl_constructed;
+ gobject_class->finalize = g_tls_client_connection_openssl_finalize;
+ gobject_class->get_property = g_tls_client_connection_openssl_get_property;
+ gobject_class->set_property = g_tls_client_connection_openssl_set_property;
+ gobject_class->constructed = g_tls_client_connection_openssl_constructed;
- base_class->handshake = g_tls_client_connection_openssl_handshake;
- base_class->complete_handshake = g_tls_client_connection_openssl_complete_handshake;
+ base_class->verify_peer_certificate = g_tls_client_connection_openssl_verify_peer_certificate;
- connection_class->get_ssl = g_tls_client_connection_openssl_get_ssl;
+ openssl_class->get_ssl = g_tls_client_connection_openssl_get_ssl;
g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
@@ -318,8 +346,9 @@ retrieve_certificate (SSL *ssl,
client = SSL_get_ex_data (ssl, data_index);
tls = G_TLS_CONNECTION_BASE (client);
+ certificate_error = g_tls_connection_base_get_certificate_error (tls);
- g_tls_connection_base_set_certificate_requested (tls);
+ g_tls_connection_base_request_certificate (tls, certificate_error);
client->ca_list = SSL_get_client_CA_list (client->ssl);
g_object_notify (G_OBJECT (client), "accepted-cas");
@@ -329,7 +358,6 @@ retrieve_certificate (SSL *ssl,
set_certificate = TRUE;
else
{
- certificate_error = g_tls_connection_base_get_certificate_error (tls);
g_clear_error (certificate_error);
if (g_tls_connection_base_request_certificate (tls, certificate_error))
{
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index a49a49e..1c56290 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -262,8 +262,9 @@ g_tls_connection_openssl_request_rehandshake (GTlsConnectionBase *tls,
}
static GTlsCertificate *
-get_peer_certificate (GTlsConnectionOpenssl *openssl)
+g_tls_connection_openssl_retrieve_peer_certificate (GTlsConnectionBase *tls)
{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
X509 *peer;
STACK_OF (X509) *certs;
GTlsCertificateOpenssl *chain;
@@ -290,101 +291,31 @@ get_peer_certificate (GTlsConnectionOpenssl *openssl)
return G_TLS_CERTIFICATE (chain);
}
-static GTlsCertificateFlags
-verify_ocsp_response (GTlsConnectionOpenssl *openssl,
- GTlsDatabase *database,
- GTlsCertificate *peer_certificate)
+static int
+handshake_thread_verify_certificate_cb (int preverify_ok,
+ X509_STORE_CTX *x509_ctx)
{
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
- !defined(OPENSSL_NO_OCSP)
- SSL *ssl = NULL;
- OCSP_RESPONSE *resp = NULL;
- long len = 0;
- unsigned char *p = NULL;
-
- ssl = g_tls_connection_openssl_get_ssl (openssl);
- len = SSL_get_tlsext_status_ocsp_resp (ssl, &p);
- /* Soft fail in case of no response is the best we can do */
- if (p == NULL)
- return 0;
-
- resp = d2i_OCSP_RESPONSE (NULL, (const unsigned char **)&p, len);
- if (resp == NULL)
- return G_TLS_CERTIFICATE_GENERIC_ERROR;
-
- return g_tls_database_openssl_verify_ocsp_response (G_TLS_DATABASE_OPENSSL (database),
- peer_certificate,
- resp);
-#else
+ // FIXME: Get the GTlsConnectionOpenssl out of the X509_STORE_CTX using
+ // x509_STORE_CTX_get_ex_data... somehow. We probably have to pass
+ // the GTlsConnectionOpenssl to the GTlsFileDatabaseOpenssl...
+ // somehow.
+ // return !g_tls_connection_base_handshake_thread_verify_certificate (
+ /* Return 1 for the handshake to continue, 0 to terminate.
+ * Complete opposite of what GnuTLS does. */
return 0;
-#endif
-}
-
-static GTlsCertificateFlags
-verify_peer_certificate (GTlsConnectionOpenssl *openssl,
- GTlsCertificate *peer_certificate)
-{
- GTlsConnection *conn = G_TLS_CONNECTION (openssl);
- GSocketConnectable *peer_identity;
- GTlsDatabase *database;
- GTlsCertificateFlags errors;
- gboolean is_client;
-
- is_client = G_IS_TLS_CLIENT_CONNECTION (openssl);
- if (is_client)
- peer_identity = g_tls_client_connection_get_server_identity (G_TLS_CLIENT_CONNECTION (openssl));
- else
- peer_identity = NULL;
-
- errors = 0;
-
- database = g_tls_connection_get_database (conn);
- if (database == NULL)
- {
- errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
- errors |= g_tls_certificate_verify (peer_certificate, peer_identity, NULL);
- }
- else
- {
- GError *error = NULL;
-
- errors |= g_tls_database_verify_chain (database, peer_certificate,
- is_client ?
- G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER :
- G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT,
- peer_identity,
- g_tls_connection_get_interaction (conn),
- G_TLS_DATABASE_VERIFY_NONE,
- NULL, &error);
- if (error)
- {
- g_warning ("failure verifying certificate chain: %s",
- error->message);
- g_assert (errors != 0);
- g_clear_error (&error);
- }
- }
-
- if (is_client && (errors == 0))
- errors = verify_ocsp_response (openssl, database, peer_certificate);
-
- return errors;
}
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;
GTlsConnectionBaseStatus status;
SSL *ssl;
int ret;
- priv = g_tls_connection_openssl_get_instance_private (openssl);
-
ssl = g_tls_connection_openssl_get_ssl (openssl);
BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, timeout, cancellable);
@@ -392,54 +323,6 @@ g_tls_connection_openssl_handshake (GTlsConnectionBase *tls,
END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, status,
_("Error performing TLS handshake: %s"), error);
- if (ret > 0)
- {
- priv->peer_certificate_tmp = get_peer_certificate (openssl);
- if (priv->peer_certificate_tmp)
- priv->peer_certificate_errors_tmp = verify_peer_certificate (openssl, priv->peer_certificate_tmp);
- else if (G_IS_TLS_CLIENT_CONNECTION (openssl))
- {
- g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
- _("Server did not return a valid TLS certificate"));
- }
- }
-
- return status;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_connection_openssl_complete_handshake (GTlsConnectionBase *tls,
- 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);
-
- peer_certificate = priv->peer_certificate_tmp;
- priv->peer_certificate_tmp = NULL;
- peer_certificate_errors = priv->peer_certificate_errors_tmp;
- priv->peer_certificate_errors_tmp = 0;
-
- if (peer_certificate)
- {
- if (!g_tls_connection_base_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"));
- status = G_TLS_CONNECTION_BASE_ERROR;
- }
-
- g_tls_connection_base_set_peer_certificate (G_TLS_CONNECTION_BASE (openssl),
- peer_certificate,
- peer_certificate_errors);
- g_clear_object (&peer_certificate);
- }
-
return status;
}
@@ -584,16 +467,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->retrieve_peer_certificate = g_tls_connection_openssl_retrieve_peer_certificate;
+ base_class->push_io = g_tls_connection_openssl_push_io;
+ base_class->pop_io = g_tls_connection_openssl_pop_io;
+ base_class->read_fn = g_tls_connection_openssl_read;
+ base_class->write_fn = g_tls_connection_openssl_write;
+ base_class->close_fn = g_tls_connection_openssl_close;
}
static gboolean
@@ -617,6 +500,8 @@ g_tls_connection_openssl_initable_init (GInitable *initable,
ssl = g_tls_connection_openssl_get_ssl (openssl);
g_assert (ssl != NULL);
+ SSL_set_verify (ssl, SSL_VERIFY_PEER, handshake_thread_verify_certificate_cb);
+
priv->bio = g_tls_bio_new (base_io_stream);
SSL_set_bio (ssl, priv->bio, priv->bio);
diff --git a/tls/openssl/gtlsdatabase-openssl.h b/tls/openssl/gtlsdatabase-openssl.h
index 8a43ccb..9d7f45e 100644
--- a/tls/openssl/gtlsdatabase-openssl.h
+++ b/tls/openssl/gtlsdatabase-openssl.h
@@ -39,6 +39,10 @@ G_DECLARE_DERIVABLE_TYPE (GTlsDatabaseOpenssl, g_tls_database_openssl, G, TLS_DA
struct _GTlsDatabaseOpensslClass
{
GTlsDatabaseClass parent_class;
+
+ gboolean (*populate_trust_list) (GTlsDatabaseOpenssl *self,
+ X509_STORE *store,
+ GError **error);
};
GTlsDatabaseOpenssl *g_tls_database_openssl_new (GError **error);
diff --git a/tls/openssl/gtlsserverconnection-openssl.c b/tls/openssl/gtlsserverconnection-openssl.c
index c29486e..86a51ab 100644
--- a/tls/openssl/gtlsserverconnection-openssl.c
+++ b/tls/openssl/gtlsserverconnection-openssl.c
@@ -172,20 +172,12 @@ g_tls_server_connection_openssl_set_property (GObject *object,
}
}
-static int
-verify_callback (int preverify_ok,
- X509_STORE_CTX *ctx)
-{
- return 1;
-}
-
-static GTlsConnectionBaseStatus
-g_tls_server_connection_openssl_handshake (GTlsConnectionBase *tls,
- gint64 timeout,
- GCancellable *cancellable,
- GError **error)
+static void
+g_tls_server_connection_openssl_prepare_handshake (GTlsConnectionBase *tls,
+ gchar **advertised_protocols)
{
GTlsServerConnectionOpenssl *openssl = G_TLS_SERVER_CONNECTION_OPENSSL (tls);
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS
(g_tls_server_connection_openssl_parent_class);
int req_mode = 0;
switch (openssl->authentication_mode)
@@ -201,12 +193,12 @@ g_tls_server_connection_openssl_handshake (GTlsConnectionBase *tls,
break;
}
- SSL_set_verify (openssl->ssl, req_mode, verify_callback);
+ SSL_set_verify (openssl->ssl, req_mode, NULL);
/* FIXME: is this ok? */
SSL_set_verify_depth (openssl->ssl, 0);
- return G_TLS_CONNECTION_BASE_CLASS (g_tls_server_connection_openssl_parent_class)->
- handshake (tls, timeout, cancellable, error);
+ if (base_class->prepare_handshake)
+ base_class->prepare_handshake (tls, advertised_protocols);
}
static SSL *
@@ -241,7 +233,7 @@ g_tls_server_connection_openssl_class_init (GTlsServerConnectionOpensslClass *kl
gobject_class->get_property = g_tls_server_connection_openssl_get_property;
gobject_class->set_property = g_tls_server_connection_openssl_set_property;
- base_class->handshake = g_tls_server_connection_openssl_handshake;
+ base_class->prepare_handshake = g_tls_server_connection_openssl_prepare_handshake;
connection_class->get_ssl = g_tls_server_connection_openssl_get_ssl;
diff --git a/tls/openssl/meson.build b/tls/openssl/meson.build
index 89b8d37..0ac25c8 100644
--- a/tls/openssl/meson.build
+++ b/tls/openssl/meson.build
@@ -20,8 +20,8 @@ deps = [
glib_dep,
gmodule_dep,
gobject_dep,
- tlsbase_dep,
openssl_dep,
+ tlsbase_dep,
]
module = shared_module(
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 146f1ff..7f8d35e 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -84,9 +84,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;
@@ -308,13 +306,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);
@@ -1961,11 +1957,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);
@@ -2140,7 +2132,6 @@ test_alpn (TestConnection *test,
return;
#endif
-#if GLIB_CHECK_VERSION(2, 60, 0)
GIOStream *connection;
GError *error = NULL;
@@ -2171,9 +2162,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]