[glib-networking/wip/tlssplit: 11/11] tls: split out non-gnutls-specific code from gnutls backend
- From: Ignacio Casal Quinteiro <icq src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/tlssplit: 11/11] tls: split out non-gnutls-specific code from gnutls backend
- Date: Thu, 12 May 2016 08:06:56 +0000 (UTC)
commit f69fd3d01d253b7ebb870be3f1bbdf581bb86154
Author: Dan Winship <danw gnome org>
Date: Thu Nov 10 18:50:06 2011 -0500
tls: split out non-gnutls-specific code from gnutls backend
Create GTlsConnectionBase, GTlsInputStreamBase, GTlsOutputStreamBase,
which implement the generic "translate from a BSD-sockets-y API to the
GTlsConnection API" semantics, and rebase GTlsConnectionGnutls on top
of that.
https://bugzilla.gnome.org/show_bug.cgi?id=761092
Makefile.am | 2 +
configure.ac | 1 +
po/POTFILES.in | 1 +
tls/base/Makefile.am | 15 +
tls/base/gtlsconnection-base.c | 1275 +++++++++++++++++++++++++++
tls/base/gtlsconnection-base.h | 215 +++++
tls/base/gtlsinputstream-base.c | 249 ++++++
tls/base/gtlsinputstream-base.h | 51 ++
tls/base/gtlsoutputstream-base.c | 251 ++++++
tls/base/gtlsoutputstream-base.h | 51 ++
tls/gnutls/Makefile.am | 6 +-
tls/gnutls/gtlsclientconnection-gnutls.c | 74 +-
tls/gnutls/gtlsconnection-gnutls.c | 1398 ++++--------------------------
tls/gnutls/gtlsconnection-gnutls.h | 42 +-
tls/gnutls/gtlsinputstream-gnutls.c | 249 ------
tls/gnutls/gtlsinputstream-gnutls.h | 51 --
tls/gnutls/gtlsoutputstream-gnutls.c | 251 ------
tls/gnutls/gtlsoutputstream-gnutls.h | 51 --
tls/gnutls/gtlsserverconnection-gnutls.c | 26 +-
19 files changed, 2331 insertions(+), 1928 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 9e04e2e..d5ec8c8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,6 +15,8 @@ endif
SUBDIRS += proxy/tests
+SUBDIRS += tls/base
+
if HAVE_PKCS11
SUBDIRS += tls/pkcs11
endif
diff --git a/configure.ac b/configure.ac
index 5b7677a..0d031df 100644
--- a/configure.ac
+++ b/configure.ac
@@ -246,6 +246,7 @@ AC_CONFIG_FILES([Makefile
proxy/libproxy/Makefile
proxy/gnome/Makefile
proxy/tests/Makefile
+ tls/base/Makefile
tls/gnutls/Makefile
tls/pkcs11/Makefile
tls/tests/Makefile
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4323e7f..5608e05 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,4 +1,5 @@
proxy/libproxy/glibproxyresolver.c
+tls/base/gtlsconnection-base.c
tls/gnutls/gtlscertificate-gnutls.c
tls/gnutls/gtlsclientconnection-gnutls.c
tls/gnutls/gtlsconnection-gnutls.c
diff --git a/tls/base/Makefile.am b/tls/base/Makefile.am
new file mode 100644
index 0000000..52c5055
--- /dev/null
+++ b/tls/base/Makefile.am
@@ -0,0 +1,15 @@
+include $(top_srcdir)/glib-networking.mk
+
+noinst_LTLIBRARIES += libtlsbase.la
+
+libtlsbase_la_SOURCES = \
+ gtlsconnection-base.c \
+ gtlsconnection-base.h \
+ gtlsinputstream-base.c \
+ gtlsinputstream-base.h \
+ gtlsoutputstream-base.c \
+ gtlsoutputstream-base.h \
+ $(NULL)
+
+libtlsbase_la_LDFLAGS = $(module_flags)
+libtlsbase_la_LIBADD = $(GLIB_LIBS)
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
new file mode 100644
index 0000000..4f39fbf
--- /dev/null
+++ b/tls/base/gtlsconnection-base.c
@@ -0,0 +1,1275 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2009-2011 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+
+#include "gtlsconnection-base.h"
+#include "gtlsinputstream-base.h"
+#include "gtlsoutputstream-base.h"
+
+#include <glib/gi18n-lib.h>
+
+static gboolean do_implicit_handshake (GTlsConnectionBase *tls,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+static gboolean finish_handshake (GTlsConnectionBase *tls,
+ GTask *task,
+ GError **error);
+
+G_DEFINE_ABSTRACT_TYPE (GTlsConnectionBase, g_tls_connection_base, G_TYPE_TLS_CONNECTION);
+
+enum
+{
+ PROP_0,
+ PROP_BASE_IO_STREAM,
+ PROP_REQUIRE_CLOSE_NOTIFY,
+ PROP_REHANDSHAKE_MODE,
+ PROP_USE_SYSTEM_CERTDB,
+ PROP_DATABASE,
+ PROP_CERTIFICATE,
+ PROP_INTERACTION,
+ PROP_PEER_CERTIFICATE,
+ PROP_PEER_CERTIFICATE_ERRORS
+};
+
+static void
+g_tls_connection_base_init (GTlsConnectionBase *tls)
+{
+ tls->need_handshake = TRUE;
+ tls->database_is_unset = TRUE;
+ tls->is_system_certdb = TRUE;
+
+ g_mutex_init (&tls->op_mutex);
+ tls->waiting_for_op = g_cancellable_new ();
+ g_cancellable_cancel (tls->waiting_for_op);
+}
+
+static void
+g_tls_connection_base_finalize (GObject *object)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+
+ g_clear_object (&tls->base_io_stream);
+
+ g_clear_object (&tls->tls_istream);
+ g_clear_object (&tls->tls_ostream);
+
+ g_clear_object (&tls->database);
+ g_clear_object (&tls->certificate);
+ g_clear_error (&tls->certificate_error);
+ g_clear_object (&tls->peer_certificate);
+
+ g_clear_object (&tls->interaction);
+
+ /* This must always be NULL at this, as it holds a referehce to @gnutls as
+ * its source object. However, we clear it anyway just in case this changes
+ * in future. */
+ g_clear_object (&tls->implicit_handshake);
+
+ g_clear_error (&tls->handshake_error);
+ g_clear_error (&tls->read_error);
+ g_clear_error (&tls->write_error);
+ g_clear_object (&tls->read_cancellable);
+ g_clear_object (&tls->write_cancellable);
+
+ g_clear_object (&tls->waiting_for_op);
+ g_mutex_clear (&tls->op_mutex);
+
+ g_clear_pointer (&tls->app_data_buf, g_byte_array_unref);
+
+ G_OBJECT_CLASS (g_tls_connection_base_parent_class)->finalize (object);
+}
+
+static void
+g_tls_connection_base_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+ GTlsBackend *backend;
+
+ switch (prop_id)
+ {
+ case PROP_BASE_IO_STREAM:
+ g_value_set_object (value, tls->base_io_stream);
+ break;
+
+ case PROP_REQUIRE_CLOSE_NOTIFY:
+ g_value_set_boolean (value, tls->require_close_notify);
+ break;
+
+ case PROP_REHANDSHAKE_MODE:
+ g_value_set_enum (value, tls->rehandshake_mode);
+ break;
+
+ case PROP_USE_SYSTEM_CERTDB:
+ g_value_set_boolean (value, tls->is_system_certdb);
+ break;
+
+ case PROP_DATABASE:
+ if (tls->database_is_unset)
+ {
+ backend = g_tls_backend_get_default ();
+ tls->database = g_tls_backend_get_default_database (backend);
+ tls->database_is_unset = FALSE;
+ }
+ g_value_set_object (value, tls->database);
+ break;
+
+ case PROP_CERTIFICATE:
+ g_value_set_object (value, tls->certificate);
+ break;
+
+ case PROP_INTERACTION:
+ g_value_set_object (value, tls->interaction);
+ break;
+
+ case PROP_PEER_CERTIFICATE:
+ g_value_set_object (value, tls->peer_certificate);
+ break;
+
+ case PROP_PEER_CERTIFICATE_ERRORS:
+ g_value_set_flags (value, tls->peer_certificate_errors);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_connection_base_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+ GInputStream *istream;
+ GOutputStream *ostream;
+ gboolean system_certdb;
+ GTlsBackend *backend;
+
+ switch (prop_id)
+ {
+ case PROP_BASE_IO_STREAM:
+ if (tls->base_io_stream)
+ {
+ g_object_unref (tls->base_io_stream);
+ tls->base_istream = NULL;
+ tls->base_ostream = NULL;
+ }
+ tls->base_io_stream = g_value_dup_object (value);
+ if (!tls->base_io_stream)
+ return;
+
+ istream = g_io_stream_get_input_stream (tls->base_io_stream);
+ ostream = g_io_stream_get_output_stream (tls->base_io_stream);
+
+ if (G_IS_POLLABLE_INPUT_STREAM (istream) &&
+ g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (istream)))
+ {
+ tls->base_istream = G_POLLABLE_INPUT_STREAM (istream);
+ tls->tls_istream = g_tls_input_stream_base_new (tls);
+ }
+ if (G_IS_POLLABLE_OUTPUT_STREAM (ostream) &&
+ g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (ostream)))
+ {
+ tls->base_ostream = G_POLLABLE_OUTPUT_STREAM (ostream);
+ tls->tls_ostream = g_tls_output_stream_base_new (tls);
+ }
+ break;
+
+ case PROP_REQUIRE_CLOSE_NOTIFY:
+ tls->require_close_notify = g_value_get_boolean (value);
+ break;
+
+ case PROP_REHANDSHAKE_MODE:
+ tls->rehandshake_mode = g_value_get_enum (value);
+ break;
+
+ case PROP_USE_SYSTEM_CERTDB:
+ system_certdb = g_value_get_boolean (value);
+ if (system_certdb != tls->is_system_certdb)
+ {
+ g_clear_object (&tls->database);
+ if (system_certdb)
+ {
+ backend = g_tls_backend_get_default ();
+ tls->database = g_tls_backend_get_default_database (backend);
+ }
+ tls->is_system_certdb = system_certdb;
+ tls->database_is_unset = FALSE;
+ }
+ break;
+
+ case PROP_DATABASE:
+ g_clear_object (&tls->database);
+ tls->database = g_value_dup_object (value);
+ tls->is_system_certdb = FALSE;
+ tls->database_is_unset = FALSE;
+ break;
+
+ case PROP_CERTIFICATE:
+ if (tls->certificate)
+ g_object_unref (tls->certificate);
+ tls->certificate = g_value_dup_object (value);
+ break;
+
+ case PROP_INTERACTION:
+ g_clear_object (&tls->interaction);
+ tls->interaction = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+typedef enum {
+ G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+ G_TLS_CONNECTION_BASE_OP_READ,
+ G_TLS_CONNECTION_BASE_OP_WRITE,
+ G_TLS_CONNECTION_BASE_OP_CLOSE_READ,
+ G_TLS_CONNECTION_BASE_OP_CLOSE_WRITE,
+ G_TLS_CONNECTION_BASE_OP_CLOSE_BOTH,
+} GTlsConnectionBaseOp;
+
+static gboolean
+claim_op (GTlsConnectionBase *tls,
+ GTlsConnectionBaseOp op,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ try_again:
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+
+ g_mutex_lock (&tls->op_mutex);
+
+ if (((op == G_TLS_CONNECTION_BASE_OP_HANDSHAKE ||
+ op == G_TLS_CONNECTION_BASE_OP_READ) &&
+ (tls->read_closing || tls->read_closed)) ||
+ ((op == G_TLS_CONNECTION_BASE_OP_HANDSHAKE ||
+ op == G_TLS_CONNECTION_BASE_OP_WRITE) &&
+ (tls->write_closing || tls->write_closed)))
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+ _("Connection is closed"));
+ g_mutex_unlock (&tls->op_mutex);
+ return FALSE;
+ }
+
+ if (tls->handshake_error &&
+ op != G_TLS_CONNECTION_BASE_OP_CLOSE_BOTH &&
+ op != G_TLS_CONNECTION_BASE_OP_CLOSE_READ &&
+ op != G_TLS_CONNECTION_BASE_OP_CLOSE_WRITE)
+ {
+ if (error)
+ *error = g_error_copy (tls->handshake_error);
+ g_mutex_unlock (&tls->op_mutex);
+ return FALSE;
+ }
+
+ if (op != G_TLS_CONNECTION_BASE_OP_HANDSHAKE)
+ {
+ if (op != G_TLS_CONNECTION_BASE_OP_CLOSE_BOTH &&
+ op != G_TLS_CONNECTION_BASE_OP_CLOSE_READ &&
+ op != G_TLS_CONNECTION_BASE_OP_CLOSE_WRITE &&
+ tls->need_handshake && !tls->handshaking)
+ {
+ tls->handshaking = TRUE;
+ if (!do_implicit_handshake (tls, blocking, cancellable, error))
+ {
+ g_cancellable_reset (tls->waiting_for_op);
+ g_mutex_unlock (&tls->op_mutex);
+ return FALSE;
+ }
+ }
+
+ if (tls->need_finish_handshake &&
+ tls->implicit_handshake)
+ {
+ GError *my_error = NULL;
+ gboolean success;
+
+ tls->need_finish_handshake = FALSE;
+
+ g_mutex_unlock (&tls->op_mutex);
+ success = finish_handshake (tls, tls->implicit_handshake, &my_error);
+ g_clear_object (&tls->implicit_handshake);
+ g_mutex_lock (&tls->op_mutex);
+
+ if (op != G_TLS_CONNECTION_BASE_OP_CLOSE_BOTH &&
+ op != G_TLS_CONNECTION_BASE_OP_CLOSE_READ &&
+ op != G_TLS_CONNECTION_BASE_OP_CLOSE_WRITE &&
+ (!success || g_cancellable_set_error_if_cancelled (cancellable, &my_error)))
+ {
+ g_propagate_error (error, my_error);
+ g_mutex_unlock (&tls->op_mutex);
+ return FALSE;
+ }
+
+ g_clear_error (&my_error);
+ }
+ }
+
+ if ((op != G_TLS_CONNECTION_BASE_OP_WRITE && tls->reading) ||
+ (op != G_TLS_CONNECTION_BASE_OP_READ && tls->writing) ||
+ (op != G_TLS_CONNECTION_BASE_OP_HANDSHAKE && tls->handshaking))
+ {
+ GPollFD fds[2];
+ int nfds;
+
+ g_cancellable_reset (tls->waiting_for_op);
+
+ g_mutex_unlock (&tls->op_mutex);
+
+ if (!blocking)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+ _("Operation would block"));
+ return FALSE;
+ }
+
+ g_cancellable_make_pollfd (tls->waiting_for_op, &fds[0]);
+ if (g_cancellable_make_pollfd (cancellable, &fds[1]))
+ nfds = 2;
+ else
+ nfds = 1;
+
+ g_poll (fds, nfds, -1);
+
+ if (nfds > 1)
+ g_cancellable_release_fd (cancellable);
+
+ goto try_again;
+ }
+
+ if (op == G_TLS_CONNECTION_BASE_OP_HANDSHAKE)
+ tls->handshaking = TRUE;
+ if (op == G_TLS_CONNECTION_BASE_OP_CLOSE_BOTH ||
+ op == G_TLS_CONNECTION_BASE_OP_CLOSE_READ)
+ tls->read_closing = TRUE;
+ if (op == G_TLS_CONNECTION_BASE_OP_CLOSE_BOTH ||
+ op == G_TLS_CONNECTION_BASE_OP_CLOSE_WRITE)
+ tls->write_closing = TRUE;
+
+ if (op != G_TLS_CONNECTION_BASE_OP_WRITE)
+ tls->reading = TRUE;
+ if (op != G_TLS_CONNECTION_BASE_OP_READ)
+ tls->writing = TRUE;
+
+ g_mutex_unlock (&tls->op_mutex);
+ return TRUE;
+}
+
+static void
+yield_op (GTlsConnectionBase *tls,
+ GTlsConnectionBaseOp op,
+ GTlsConnectionBaseStatus status)
+{
+ g_mutex_lock (&tls->op_mutex);
+
+ if (op == G_TLS_CONNECTION_BASE_OP_HANDSHAKE)
+ tls->handshaking = FALSE;
+ else if (status == G_TLS_CONNECTION_BASE_REHANDSHAKE && !tls->handshaking)
+ tls->need_handshake = TRUE;
+
+ if (op == G_TLS_CONNECTION_BASE_OP_CLOSE_BOTH ||
+ op == G_TLS_CONNECTION_BASE_OP_CLOSE_READ)
+ tls->read_closing = FALSE;
+ if (op == G_TLS_CONNECTION_BASE_OP_CLOSE_BOTH ||
+ op == G_TLS_CONNECTION_BASE_OP_CLOSE_WRITE)
+ tls->write_closing = FALSE;
+
+ if (op != G_TLS_CONNECTION_BASE_OP_WRITE)
+ tls->reading = FALSE;
+ if (op != G_TLS_CONNECTION_BASE_OP_READ)
+ tls->writing = FALSE;
+
+ g_cancellable_cancel (tls->waiting_for_op);
+ g_mutex_unlock (&tls->op_mutex);
+}
+
+static void
+g_tls_connection_base_real_push_io (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean blocking,
+ GCancellable *cancellable)
+{
+ if (direction & G_IO_IN)
+ {
+ tls->read_blocking = blocking;
+ tls->read_cancellable = cancellable;
+ g_clear_error (&tls->read_error);
+ }
+
+ if (direction & G_IO_OUT)
+ {
+ tls->write_blocking = blocking;
+ tls->write_cancellable = cancellable;
+ g_clear_error (&tls->write_error);
+ }
+}
+
+void
+g_tls_connection_base_push_io (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean blocking,
+ GCancellable *cancellable)
+{
+ g_assert (direction & (G_IO_IN | G_IO_OUT));
+ g_return_if_fail (G_IS_TLS_CONNECTION_BASE (tls));
+
+ G_TLS_CONNECTION_BASE_GET_CLASS (tls)->push_io (tls, direction,
+ blocking, cancellable);
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_base_real_pop_io (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean success,
+ GError **error)
+{
+ GError *my_error = NULL;
+
+ if (direction & G_IO_IN)
+ {
+ tls->read_cancellable = NULL;
+ if (!success)
+ {
+ my_error = tls->read_error;
+ tls->read_error = NULL;
+ }
+ else
+ g_clear_error (&tls->read_error);
+ }
+ if (direction & G_IO_OUT)
+ {
+ tls->write_cancellable = NULL;
+ if (!success && !my_error)
+ {
+ my_error = tls->write_error;
+ tls->write_error = NULL;
+ }
+ else
+ g_clear_error (&tls->write_error);
+ }
+
+ if (success)
+ return G_TLS_CONNECTION_BASE_OK;
+
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ g_propagate_error (error, my_error);
+ return G_TLS_CONNECTION_BASE_WOULD_BLOCK;
+ }
+ else if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
+ {
+ g_propagate_error (error, my_error);
+ return G_TLS_CONNECTION_BASE_TIMED_OUT;
+ }
+ else if (my_error)
+ g_propagate_error (error, my_error);
+
+ return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+GTlsConnectionBaseStatus
+g_tls_connection_base_pop_io (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean success,
+ GError **error)
+{
+ g_assert (direction & (G_IO_IN | G_IO_OUT));
+ g_assert (!error || !*error);
+ g_return_val_if_fail (G_IS_TLS_CONNECTION_BASE (tls), G_TLS_CONNECTION_BASE_ERROR);
+
+ return G_TLS_CONNECTION_BASE_GET_CLASS (tls)->pop_io (tls, direction,
+ success, error);
+}
+
+gboolean
+g_tls_connection_base_check (GTlsConnectionBase *tls,
+ GIOCondition condition)
+{
+ /* Racy, but worst case is that we just get WOULD_BLOCK back */
+ if (tls->need_finish_handshake)
+ return TRUE;
+
+ /* If a handshake or close is in progress, then tls_istream and
+ * tls_ostream are blocked, regardless of the base stream status.
+ */
+ if (tls->handshaking)
+ return FALSE;
+
+ if (((condition & G_IO_IN) && tls->read_closing) ||
+ ((condition & G_IO_OUT) && tls->write_closing))
+ return FALSE;
+
+ if (condition & G_IO_IN)
+ return g_pollable_input_stream_is_readable (tls->base_istream);
+ else
+ return g_pollable_output_stream_is_writable (tls->base_ostream);
+}
+
+typedef struct {
+ GSource source;
+
+ GTlsConnectionBase *tls;
+ GObject *stream;
+
+ GSource *child_source;
+ GIOCondition condition;
+
+ gboolean io_waiting;
+ gboolean op_waiting;
+} GTlsConnectionBaseSource;
+
+static gboolean
+tls_source_prepare (GSource *source,
+ gint *timeout)
+{
+ *timeout = -1;
+ return FALSE;
+}
+
+static gboolean
+tls_source_check (GSource *source)
+{
+ return FALSE;
+}
+
+static void
+tls_source_sync (GTlsConnectionBaseSource *tls_source)
+{
+ GTlsConnectionBase *tls = tls_source->tls;
+ gboolean io_waiting, op_waiting;
+
+ /* Was the source destroyed earlier in this main context iteration? */
+ if (g_source_is_destroyed ((GSource *) tls_source))
+ return;
+
+ g_mutex_lock (&tls->op_mutex);
+ if (((tls_source->condition & G_IO_IN) && tls->reading) ||
+ ((tls_source->condition & G_IO_OUT) && tls->writing) ||
+ (tls->handshaking && !tls->need_finish_handshake))
+ op_waiting = TRUE;
+ else
+ op_waiting = FALSE;
+
+ if (!op_waiting && !tls->need_handshake &&
+ !tls->need_finish_handshake)
+ io_waiting = TRUE;
+ else
+ io_waiting = FALSE;
+ g_mutex_unlock (&tls->op_mutex);
+
+ if (op_waiting == tls_source->op_waiting &&
+ io_waiting == tls_source->io_waiting)
+ return;
+ tls_source->op_waiting = op_waiting;
+ tls_source->io_waiting = io_waiting;
+
+ if (tls_source->child_source)
+ {
+ g_source_remove_child_source ((GSource *)tls_source,
+ tls_source->child_source);
+ g_source_unref (tls_source->child_source);
+ }
+
+ if (op_waiting)
+ tls_source->child_source = g_cancellable_source_new (tls->waiting_for_op);
+ else if (io_waiting && G_IS_POLLABLE_INPUT_STREAM (tls_source->stream))
+ tls_source->child_source = g_pollable_input_stream_create_source (tls->base_istream, NULL);
+ else if (io_waiting && G_IS_POLLABLE_OUTPUT_STREAM (tls_source->stream))
+ tls_source->child_source = g_pollable_output_stream_create_source (tls->base_ostream, NULL);
+ else
+ tls_source->child_source = g_timeout_source_new (0);
+
+ g_source_set_dummy_callback (tls_source->child_source);
+ g_source_add_child_source ((GSource *)tls_source, tls_source->child_source);
+}
+
+static gboolean
+tls_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GPollableSourceFunc func = (GPollableSourceFunc)callback;
+ GTlsConnectionBaseSource *tls_source = (GTlsConnectionBaseSource *)source;
+ gboolean ret;
+
+ ret = (*func) (tls_source->stream, user_data);
+ if (ret)
+ tls_source_sync (tls_source);
+
+ return ret;
+}
+
+static void
+tls_source_finalize (GSource *source)
+{
+ GTlsConnectionBaseSource *tls_source = (GTlsConnectionBaseSource *)source;
+
+ g_object_unref (tls_source->tls);
+ g_source_unref (tls_source->child_source);
+}
+
+static gboolean
+g_tls_connection_tls_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 GSourceFuncs tls_source_funcs =
+{
+ tls_source_prepare,
+ tls_source_check,
+ tls_source_dispatch,
+ tls_source_finalize,
+ (GSourceFunc)g_tls_connection_tls_source_closure_callback,
+ (GSourceDummyMarshal)g_cclosure_marshal_generic
+};
+
+GSource *
+g_tls_connection_base_create_source (GTlsConnectionBase *tls,
+ GIOCondition condition,
+ GCancellable *cancellable)
+{
+ GSource *source, *cancellable_source;
+ GTlsConnectionBaseSource *tls_source;
+
+ source = g_source_new (&tls_source_funcs, sizeof (GTlsConnectionBaseSource));
+ g_source_set_name (source, "GTlsConnectionBaseSource");
+ tls_source = (GTlsConnectionBaseSource *)source;
+ tls_source->tls = g_object_ref (tls);
+ tls_source->condition = condition;
+ if (condition & G_IO_IN)
+ tls_source->stream = G_OBJECT (tls->tls_istream);
+ else if (condition & G_IO_OUT)
+ tls_source->stream = G_OBJECT (tls->tls_ostream);
+
+ tls_source->op_waiting = (gboolean) -1;
+ tls_source->io_waiting = (gboolean) -1;
+ tls_source_sync (tls_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;
+}
+
+gboolean
+g_tls_connection_base_accept_peer_certificate (GTlsConnectionBase *tls,
+ GTlsCertificate *peer_certificate,
+ GTlsCertificateFlags peer_certificate_errors)
+{
+ gboolean accepted = FALSE;
+
+ if (G_IS_TLS_CLIENT_CONNECTION (tls))
+ {
+ GTlsCertificateFlags validation_flags =
+ g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (tls));
+
+ if ((peer_certificate_errors & validation_flags) == 0)
+ accepted = TRUE;
+ }
+
+ if (!accepted)
+ {
+ accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (tls),
+ peer_certificate,
+ peer_certificate_errors);
+ }
+
+ return accepted;
+}
+
+void
+g_tls_connection_base_set_peer_certificate (GTlsConnectionBase *tls,
+ GTlsCertificate *peer_certificate,
+ GTlsCertificateFlags peer_certificate_errors)
+{
+ if (tls->peer_certificate)
+ g_object_unref (tls->peer_certificate);
+ tls->peer_certificate = peer_certificate;
+ if (tls->peer_certificate)
+ g_object_ref (tls->peer_certificate);
+
+ tls->peer_certificate_errors = peer_certificate_errors;
+
+ g_object_notify (G_OBJECT (tls), "peer-certificate");
+ g_object_notify (G_OBJECT (tls), "peer-certificate-errors");
+}
+
+static void
+handshake_thread (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GTlsConnectionBase *tls = object;
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+ GError *error = NULL;
+
+ tls->started_handshake = FALSE;
+ tls->certificate_requested = FALSE;
+
+ if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+ TRUE, cancellable, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_clear_error (&tls->handshake_error);
+
+ if (tls->ever_handshaked && !tls->need_handshake)
+ {
+ GTlsConnectionBaseStatus status;
+
+ status = tls_class->request_rehandshake (tls, cancellable, &error);
+ if (status != G_TLS_CONNECTION_BASE_OK)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+ }
+
+ g_clear_object (&tls->peer_certificate);
+ tls->peer_certificate_errors = 0;
+
+ tls->started_handshake = TRUE;
+ tls_class->handshake (tls, cancellable, &error);
+ tls->need_handshake = FALSE;
+
+ if (error)
+ {
+ if ((g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
+#if GLIB_CHECK_VERSION (2, 35, 3)
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
+#endif
+ g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) &&
+ tls->certificate_requested)
+ {
+ g_clear_error (&error);
+ if (tls->certificate_error)
+ {
+ error = tls->certificate_error;
+ tls->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);
+ }
+ else
+ {
+ tls->ever_handshaked = TRUE;
+ g_task_return_boolean (task, TRUE);
+ }
+}
+
+static gboolean
+finish_handshake (GTlsConnectionBase *tls,
+ GTask *task,
+ GError **error)
+{
+ GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+ GError *my_error = NULL;
+
+ if (g_task_propagate_boolean (task, &my_error))
+ tls_class->complete_handshake (tls, &my_error);
+
+ if (my_error && tls->started_handshake)
+ tls->handshake_error = g_error_copy (my_error);
+
+ if (!my_error)
+ return TRUE;
+
+ g_propagate_error (error, my_error);
+ return FALSE;
+}
+
+static gboolean
+g_tls_connection_base_handshake (GTlsConnection *conn,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
+ GTask *task;
+ gboolean success;
+ GError *my_error = NULL;
+
+ task = g_task_new (conn, cancellable, NULL, NULL);
+ g_task_set_source_tag (task, g_tls_connection_base_handshake);
+ g_task_run_in_thread_sync (task, handshake_thread);
+ success = finish_handshake (tls, task, &my_error);
+ g_object_unref (task);
+
+ yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+ G_TLS_CONNECTION_BASE_OK);
+
+ if (my_error)
+ g_propagate_error (error, my_error);
+ return success;
+}
+
+/* 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;
+ GTlsConnectionBase *tls = g_task_get_source_object (caller_task);
+ GError *error = NULL;
+ gboolean need_finish_handshake, success;
+
+ g_mutex_lock (&tls->op_mutex);
+ if (tls->need_finish_handshake)
+ {
+ need_finish_handshake = TRUE;
+ tls->need_finish_handshake = FALSE;
+ }
+ else
+ need_finish_handshake = FALSE;
+ g_mutex_unlock (&tls->op_mutex);
+
+ if (need_finish_handshake)
+ {
+ success = finish_handshake (tls, G_TASK (result), &error);
+ if (success)
+ g_task_return_boolean (caller_task, TRUE);
+ else
+ g_task_return_error (caller_task, error);
+ }
+ else if (tls->handshake_error)
+ g_task_return_error (caller_task, g_error_copy (tls->handshake_error));
+ else
+ g_task_return_boolean (caller_task, TRUE);
+
+ g_object_unref (caller_task);
+}
+
+static void
+async_handshake_thread (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GTlsConnectionBase *tls = object;
+
+ handshake_thread (task, object, task_data, cancellable);
+
+ g_mutex_lock (&tls->op_mutex);
+ tls->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.
+ */
+ tls->handshaking = FALSE;
+ g_mutex_unlock (&tls->op_mutex);
+
+ yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+ G_TLS_CONNECTION_BASE_OK);
+}
+
+static void
+g_tls_connection_base_handshake_async (GTlsConnection *conn,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *thread_task, *caller_task;
+
+ 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);
+
+ g_task_run_in_thread (thread_task, async_handshake_thread);
+ g_object_unref (thread_task);
+}
+
+static gboolean
+g_tls_connection_base_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);
+}
+
+static void
+implicit_handshake_completed (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+
+ g_mutex_lock (&tls->op_mutex);
+ tls->need_finish_handshake = TRUE;
+ g_mutex_unlock (&tls->op_mutex);
+
+ yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+ G_TLS_CONNECTION_BASE_OK);
+}
+
+static gboolean
+do_implicit_handshake (GTlsConnectionBase *tls,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* We have op_mutex */
+
+ tls->implicit_handshake = g_task_new (tls, cancellable,
+ implicit_handshake_completed,
+ NULL);
+ g_task_set_source_tag (tls->implicit_handshake, do_implicit_handshake);
+
+ if (blocking)
+ {
+ GError *my_error = NULL;
+ gboolean success;
+
+ g_mutex_unlock (&tls->op_mutex);
+ g_task_run_in_thread_sync (tls->implicit_handshake,
+ handshake_thread);
+ success = finish_handshake (tls,
+ tls->implicit_handshake,
+ &my_error);
+ g_clear_object (&tls->implicit_handshake);
+ yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+ G_TLS_CONNECTION_BASE_OK);
+ g_mutex_lock (&tls->op_mutex);
+
+ if (my_error)
+ g_propagate_error (error, my_error);
+ return success;
+ }
+ else
+ {
+ g_task_run_in_thread (tls->implicit_handshake,
+ handshake_thread);
+
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+ _("Operation would block"));
+ return FALSE;
+ }
+}
+
+gssize
+g_tls_connection_base_read (GTlsConnectionBase *tls,
+ void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionBaseStatus status;
+ gssize nread;
+
+ do
+ {
+ if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_READ,
+ blocking, cancellable, error))
+ return -1;
+
+ if (tls->app_data_buf && !tls->handshaking)
+ {
+ nread = MIN (count, tls->app_data_buf->len);
+ memcpy (buffer, tls->app_data_buf->data, nread);
+ if (nread == tls->app_data_buf->len)
+ g_clear_pointer (&tls->app_data_buf, g_byte_array_unref);
+ else
+ g_byte_array_remove_range (tls->app_data_buf, 0, nread);
+ status = G_TLS_CONNECTION_BASE_OK;
+ }
+ else
+ {
+ status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
+ read_fn (tls, buffer, count, blocking, &nread, cancellable, error);
+ }
+
+ yield_op (tls, G_TLS_CONNECTION_BASE_OP_READ, status);
+ }
+ while (status == G_TLS_CONNECTION_BASE_REHANDSHAKE);
+
+ if (status == G_TLS_CONNECTION_BASE_OK)
+ return nread;
+ else
+ return -1;
+}
+
+gssize
+g_tls_connection_base_write (GTlsConnectionBase *tls,
+ const void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionBaseStatus status;
+ gssize nwrote;
+
+ do
+ {
+ if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_WRITE,
+ blocking, cancellable, error))
+ return -1;
+
+ status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
+ write_fn (tls, buffer, count, blocking, &nwrote, cancellable, error);
+
+ yield_op (tls, G_TLS_CONNECTION_BASE_OP_WRITE, status);
+ }
+ while (status == G_TLS_CONNECTION_BASE_REHANDSHAKE);
+
+ if (status == G_TLS_CONNECTION_BASE_OK)
+ return nwrote;
+ else
+ return -1;
+}
+
+static GInputStream *
+g_tls_connection_base_get_input_stream (GIOStream *stream)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (stream);
+
+ return tls->tls_istream;
+}
+
+static GOutputStream *
+g_tls_connection_base_get_output_stream (GIOStream *stream)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (stream);
+
+ return tls->tls_ostream;
+}
+
+gboolean
+g_tls_connection_base_close_internal (GIOStream *stream,
+ GTlsDirection direction,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (stream);
+ GTlsConnectionBaseOp op;
+ GTlsConnectionBaseStatus status;
+ gboolean success = TRUE;
+ GError *close_error = NULL, *stream_error = NULL;
+
+ /* This can be called from g_io_stream_close(), g_input_stream_close() or
+ * g_output_stream_close(). In all cases, we only do the close_fn() 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_BASE_OP_CLOSE_BOTH;
+ else if (direction == G_TLS_DIRECTION_READ)
+ op = G_TLS_CONNECTION_BASE_OP_CLOSE_READ;
+ else
+ op = G_TLS_CONNECTION_BASE_OP_CLOSE_WRITE;
+
+ if (!claim_op (tls, op, TRUE, cancellable, error))
+ return FALSE;
+
+ if (tls->ever_handshaked && !tls->write_closed &&
+ direction & G_TLS_DIRECTION_WRITE)
+ {
+ status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
+ close_fn (tls, cancellable, &close_error);
+
+ tls->write_closed = TRUE;
+ }
+ else
+ status = G_TLS_CONNECTION_BASE_OK;
+
+ if (!tls->read_closed && direction & G_TLS_DIRECTION_READ)
+ tls->read_closed = TRUE;
+
+ /* Close the underlying streams. Do this even if the close_fn() call failed,
+ * as the parent GIOStream will have set its internal closed flag and hence
+ * this implementation will never be called again. */
+ if (direction == G_TLS_DIRECTION_BOTH)
+ success = g_io_stream_close (tls->base_io_stream,
+ cancellable, &stream_error);
+ else if (direction & G_TLS_DIRECTION_READ)
+ success = g_input_stream_close (g_io_stream_get_input_stream (tls->base_io_stream),
+ cancellable, &stream_error);
+ else if (direction & G_TLS_DIRECTION_WRITE)
+ success = g_output_stream_close (g_io_stream_get_output_stream (tls->base_io_stream),
+ cancellable, &stream_error);
+
+ yield_op (tls, op, status);
+
+ /* Propagate errors. */
+ if (status != G_TLS_CONNECTION_BASE_OK)
+ {
+ g_propagate_error (error, close_error);
+ g_clear_error (&stream_error);
+ }
+ else if (!success)
+ {
+ g_propagate_error (error, stream_error);
+ g_clear_error (&close_error);
+ }
+
+ return success && status == G_TLS_CONNECTION_BASE_OK;
+}
+
+static gboolean
+g_tls_connection_base_close (GIOStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_tls_connection_base_close_internal (stream,
+ G_TLS_DIRECTION_BOTH,
+ 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;
+ GError *error = NULL;
+
+ if (!g_tls_connection_base_close (stream, cancellable, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+g_tls_connection_base_close_async (GIOStream *stream,
+ 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_base_close_async);
+ g_task_set_priority (task, io_priority);
+ g_task_run_in_thread (task, close_thread);
+ g_object_unref (task);
+}
+
+static gboolean
+g_tls_connection_base_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_base_class_init (GTlsConnectionBaseClass *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_base_get_property;
+ gobject_class->set_property = g_tls_connection_base_set_property;
+ gobject_class->finalize = g_tls_connection_base_finalize;
+
+ connection_class->handshake = g_tls_connection_base_handshake;
+ connection_class->handshake_async = g_tls_connection_base_handshake_async;
+ connection_class->handshake_finish = g_tls_connection_base_handshake_finish;
+
+ iostream_class->get_input_stream = g_tls_connection_base_get_input_stream;
+ iostream_class->get_output_stream = g_tls_connection_base_get_output_stream;
+ iostream_class->close_fn = g_tls_connection_base_close;
+ iostream_class->close_async = g_tls_connection_base_close_async;
+ iostream_class->close_finish = g_tls_connection_base_close_finish;
+
+ klass->push_io = g_tls_connection_base_real_push_io;
+ klass->pop_io = g_tls_connection_base_real_pop_io;
+
+ g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream");
+ 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");
+}
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
new file mode 100644
index 0000000..0809644
--- /dev/null
+++ b/tls/base/gtlsconnection-base.h
@@ -0,0 +1,215 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2009-2011 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#ifndef __G_TLS_CONNECTION_BASE_H__
+#define __G_TLS_CONNECTION_BASE_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CONNECTION_BASE (g_tls_connection_base_get_type ())
+#define G_TLS_CONNECTION_BASE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst),
G_TYPE_TLS_CONNECTION_BASE, GTlsConnectionBase))
+#define G_TLS_CONNECTION_BASE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CONNECTION_BASE,
GTlsConnectionBaseClass))
+#define G_IS_TLS_CONNECTION_BASE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst),
G_TYPE_TLS_CONNECTION_BASE))
+#define G_IS_TLS_CONNECTION_BASE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CONNECTION_BASE))
+#define G_TLS_CONNECTION_BASE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst),
G_TYPE_TLS_CONNECTION_BASE, GTlsConnectionBaseClass))
+
+typedef struct _GTlsConnectionBasePrivate GTlsConnectionBasePrivate;
+typedef struct _GTlsConnectionBaseClass GTlsConnectionBaseClass;
+typedef struct _GTlsConnectionBase GTlsConnectionBase;
+
+typedef enum {
+ G_TLS_CONNECTION_BASE_OK,
+ G_TLS_CONNECTION_BASE_WOULD_BLOCK,
+ G_TLS_CONNECTION_BASE_TIMED_OUT,
+ G_TLS_CONNECTION_BASE_REHANDSHAKE,
+ G_TLS_CONNECTION_BASE_TRY_AGAIN,
+ G_TLS_CONNECTION_BASE_ERROR,
+} GTlsConnectionBaseStatus;
+
+struct _GTlsConnectionBaseClass
+{
+ GTlsConnectionClass parent_class;
+
+ GTlsConnectionBaseStatus (*request_rehandshake) (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error);
+ GTlsConnectionBaseStatus (*handshake) (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error);
+ GTlsConnectionBaseStatus (*complete_handshake) (GTlsConnectionBase *tls,
+ GError **error);
+
+ void (*push_io) (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean blocking,
+ GCancellable *cancellable);
+ GTlsConnectionBaseStatus (*pop_io) (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean success,
+ GError **error);
+
+ GTlsConnectionBaseStatus (*read_fn) (GTlsConnectionBase *tls,
+ void *buffer,
+ gsize count,
+ gboolean blocking,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error);
+ GTlsConnectionBaseStatus (*write_fn) (GTlsConnectionBase *tls,
+ const void *buffer,
+ gsize count,
+ gboolean blocking,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error);
+
+ GTlsConnectionBaseStatus (*close_fn) (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error);
+};
+
+struct _GTlsConnectionBase
+{
+ GTlsConnection parent_instance;
+
+ GIOStream *base_io_stream;
+ GPollableInputStream *base_istream;
+ GPollableOutputStream *base_ostream;
+
+ GTlsDatabase *database;
+ GTlsInteraction *interaction;
+
+ GTlsCertificate *certificate;
+ gboolean certificate_requested;
+ GError *certificate_error;
+ GTlsCertificate *peer_certificate;
+ GTlsCertificateFlags peer_certificate_errors;
+
+ gboolean require_close_notify;
+ GTlsRehandshakeMode rehandshake_mode;
+
+ /* 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 sending the first handshake packet (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;
+ gboolean need_finish_handshake;
+ gboolean started_handshake;
+ gboolean handshaking;
+ gboolean ever_handshaked;
+ 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;
+
+ gboolean reading;
+ gboolean read_blocking;
+ GError *read_error;
+ GCancellable *read_cancellable;
+
+ gboolean writing;
+ gboolean write_blocking;
+ GError *write_error;
+ GCancellable *write_cancellable;
+
+ /*< private >*/
+ gboolean is_system_certdb;
+ gboolean database_is_unset;
+
+ GInputStream *tls_istream;
+ GOutputStream *tls_ostream;
+
+ GMutex op_mutex;
+ GCancellable *waiting_for_op;
+};
+
+GType g_tls_connection_base_get_type (void) G_GNUC_CONST;
+
+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,
+ gboolean blocking,
+ 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,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+gssize g_tls_connection_base_write (GTlsConnectionBase *tls,
+ const void *buffer,
+ gsize size,
+ gboolean blocking,
+ 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);
+
+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_base_close_internal (GIOStream *stream,
+ GTlsDirection direction,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CONNECTION_BASE_H___ */
diff --git a/tls/base/gtlsinputstream-base.c b/tls/base/gtlsinputstream-base.c
new file mode 100644
index 0000000..3438a11
--- /dev/null
+++ b/tls/base/gtlsinputstream-base.c
@@ -0,0 +1,249 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+#include "gtlsinputstream-base.h"
+
+static void g_tls_input_stream_base_pollable_iface_init (GPollableInputStreamInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsInputStreamBase, g_tls_input_stream_base, G_TYPE_INPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
g_tls_input_stream_base_pollable_iface_init)
+ )
+
+struct _GTlsInputStreamBasePrivate
+{
+ GWeakRef weak_conn;
+};
+
+static void
+g_tls_input_stream_base_dispose (GObject *object)
+{
+ GTlsInputStreamBase *stream = G_TLS_INPUT_STREAM_BASE (object);
+
+ g_weak_ref_set (&stream->priv->weak_conn, NULL);
+
+ G_OBJECT_CLASS (g_tls_input_stream_base_parent_class)->dispose (object);
+}
+
+static void
+g_tls_input_stream_base_finalize (GObject *object)
+{
+ GTlsInputStreamBase *stream = G_TLS_INPUT_STREAM_BASE (object);
+
+ g_weak_ref_clear (&stream->priv->weak_conn);
+
+ G_OBJECT_CLASS (g_tls_input_stream_base_parent_class)->finalize (object);
+}
+
+static gssize
+g_tls_input_stream_base_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsInputStreamBase *tls_stream = G_TLS_INPUT_STREAM_BASE (stream);
+ GTlsConnectionBase *conn;
+ gssize ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+ g_return_val_if_fail (conn != NULL, -1);
+
+ ret = g_tls_connection_base_read (conn,
+ buffer, count, TRUE,
+ cancellable, error);
+ g_object_unref (conn);
+ return ret;
+}
+
+static gboolean
+g_tls_input_stream_base_pollable_is_readable (GPollableInputStream *pollable)
+{
+ GTlsInputStreamBase *tls_stream = G_TLS_INPUT_STREAM_BASE (pollable);
+ GTlsConnectionBase *conn;
+ gboolean ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+ g_return_val_if_fail (conn != NULL, FALSE);
+
+ ret = g_tls_connection_base_check (conn, G_IO_IN);
+
+ g_object_unref (conn);
+ return ret;
+}
+
+static GSource *
+g_tls_input_stream_base_pollable_create_source (GPollableInputStream *pollable,
+ GCancellable *cancellable)
+{
+ GTlsInputStreamBase *tls_stream = G_TLS_INPUT_STREAM_BASE (pollable);
+ GTlsConnectionBase *conn;
+ GSource *ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+ g_return_val_if_fail (conn != NULL, NULL);
+
+ ret = g_tls_connection_base_create_source (conn, G_IO_IN, cancellable);
+ g_object_unref (conn);
+ return ret;
+}
+
+static gssize
+g_tls_input_stream_base_pollable_read_nonblocking (GPollableInputStream *pollable,
+ void *buffer,
+ gsize size,
+ GError **error)
+{
+ GTlsInputStreamBase *tls_stream = G_TLS_INPUT_STREAM_BASE (pollable);
+ GTlsConnectionBase *conn;
+ gssize ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+ g_return_val_if_fail (conn != NULL, -1);
+
+ ret = g_tls_connection_base_read (conn, buffer, size, FALSE, NULL, error);
+
+ g_object_unref (conn);
+ return ret;
+}
+
+static gboolean
+g_tls_input_stream_base_close (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsInputStreamBase *tls_stream = G_TLS_INPUT_STREAM_BASE (stream);
+ GIOStream *conn;
+ gboolean ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+
+ /* Special case here because this is called by the finalize
+ * of the main GTlsConnection object.
+ */
+ if (conn == NULL)
+ return TRUE;
+
+ ret = g_tls_connection_base_close_internal (conn, G_TLS_DIRECTION_READ,
+ cancellable, error);
+
+ g_object_unref (conn);
+ return ret;
+}
+
+/* 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)
+{
+ GTlsInputStreamBase *tls_stream = object;
+ GError *error = NULL;
+ GIOStream *conn;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+
+ if (conn && !g_tls_connection_base_close_internal (conn,
+ G_TLS_DIRECTION_READ,
+ cancellable, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ if (conn)
+ g_object_unref (conn);
+}
+
+
+static void
+g_tls_input_stream_base_close_async (GInputStream *stream,
+ 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_input_stream_base_close_async);
+ g_task_set_priority (task, io_priority);
+ g_task_run_in_thread (task, close_thread);
+ g_object_unref (task);
+}
+
+static gboolean
+g_tls_input_stream_base_close_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
+ g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
+ g_tls_input_stream_base_close_async, FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+g_tls_input_stream_base_class_init (GTlsInputStreamBaseClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GTlsInputStreamBasePrivate));
+
+ gobject_class->dispose = g_tls_input_stream_base_dispose;
+ gobject_class->finalize = g_tls_input_stream_base_finalize;
+
+ input_stream_class->read_fn = g_tls_input_stream_base_read;
+ input_stream_class->close_fn = g_tls_input_stream_base_close;
+ input_stream_class->close_async = g_tls_input_stream_base_close_async;
+ input_stream_class->close_finish = g_tls_input_stream_base_close_finish;
+}
+
+static void
+g_tls_input_stream_base_pollable_iface_init (GPollableInputStreamInterface *iface)
+{
+ iface->is_readable = g_tls_input_stream_base_pollable_is_readable;
+ iface->create_source = g_tls_input_stream_base_pollable_create_source;
+ iface->read_nonblocking = g_tls_input_stream_base_pollable_read_nonblocking;
+}
+
+static void
+g_tls_input_stream_base_init (GTlsInputStreamBase *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_TLS_INPUT_STREAM_BASE,
GTlsInputStreamBasePrivate);
+}
+
+GInputStream *
+g_tls_input_stream_base_new (GTlsConnectionBase *conn)
+{
+ GTlsInputStreamBase *tls_stream;
+
+ tls_stream = g_object_new (G_TYPE_TLS_INPUT_STREAM_BASE, NULL);
+ g_weak_ref_init (&tls_stream->priv->weak_conn, conn);
+
+ return G_INPUT_STREAM (tls_stream);
+}
diff --git a/tls/base/gtlsinputstream-base.h b/tls/base/gtlsinputstream-base.h
new file mode 100644
index 0000000..5f0694e
--- /dev/null
+++ b/tls/base/gtlsinputstream-base.h
@@ -0,0 +1,51 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#ifndef __G_TLS_INPUT_STREAM_BASE_H__
+#define __G_TLS_INPUT_STREAM_BASE_H__
+
+#include <gio/gio.h>
+#include "gtlsconnection-base.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_INPUT_STREAM_BASE (g_tls_input_stream_base_get_type ())
+#define G_TLS_INPUT_STREAM_BASE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst),
G_TYPE_TLS_INPUT_STREAM_BASE, GTlsInputStreamBase))
+#define G_TLS_INPUT_STREAM_BASE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),
G_TYPE_TLS_INPUT_STREAM_BASE, GTlsInputStreamBaseClass))
+#define G_IS_TLS_INPUT_STREAM_BASE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst),
G_TYPE_TLS_INPUT_STREAM_BASE))
+#define G_IS_TLS_INPUT_STREAM_BASE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),
G_TYPE_TLS_INPUT_STREAM_BASE))
+#define G_TLS_INPUT_STREAM_BASE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst),
G_TYPE_TLS_INPUT_STREAM_BASE, GTlsInputStreamBaseClass))
+
+typedef struct _GTlsInputStreamBasePrivate GTlsInputStreamBasePrivate;
+typedef struct _GTlsInputStreamBaseClass GTlsInputStreamBaseClass;
+typedef struct _GTlsInputStreamBase GTlsInputStreamBase;
+
+struct _GTlsInputStreamBaseClass
+{
+ GInputStreamClass parent_class;
+};
+
+struct _GTlsInputStreamBase
+{
+ GInputStream parent_instance;
+ GTlsInputStreamBasePrivate *priv;
+};
+
+GType g_tls_input_stream_base_get_type (void) G_GNUC_CONST;
+GInputStream *g_tls_input_stream_base_new (GTlsConnectionBase *conn);
+
+G_END_DECLS
+
+#endif /* __G_TLS_INPUT_STREAM_BASE_H___ */
diff --git a/tls/base/gtlsoutputstream-base.c b/tls/base/gtlsoutputstream-base.c
new file mode 100644
index 0000000..d9bcf8e
--- /dev/null
+++ b/tls/base/gtlsoutputstream-base.c
@@ -0,0 +1,251 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+#include "gtlsoutputstream-base.h"
+
+static void g_tls_output_stream_base_pollable_iface_init (GPollableOutputStreamInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsOutputStreamBase, g_tls_output_stream_base, G_TYPE_OUTPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
g_tls_output_stream_base_pollable_iface_init)
+ )
+
+struct _GTlsOutputStreamBasePrivate
+{
+ GWeakRef weak_conn;
+};
+
+static void
+g_tls_output_stream_base_dispose (GObject *object)
+{
+ GTlsOutputStreamBase *stream = G_TLS_OUTPUT_STREAM_BASE (object);
+
+ g_weak_ref_set (&stream->priv->weak_conn, NULL);
+
+ G_OBJECT_CLASS (g_tls_output_stream_base_parent_class)->dispose (object);
+}
+
+static void
+g_tls_output_stream_base_finalize (GObject *object)
+{
+ GTlsOutputStreamBase *stream = G_TLS_OUTPUT_STREAM_BASE (object);
+
+ g_weak_ref_clear (&stream->priv->weak_conn);
+
+ G_OBJECT_CLASS (g_tls_output_stream_base_parent_class)->finalize (object);
+}
+
+static gssize
+g_tls_output_stream_base_write (GOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOutputStreamBase *tls_stream = G_TLS_OUTPUT_STREAM_BASE (stream);
+ GTlsConnectionBase *conn;
+ gssize ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+ g_return_val_if_fail (conn != NULL, -1);
+
+ ret = g_tls_connection_base_write (conn, buffer, count, TRUE,
+ cancellable, error);
+ g_object_unref (conn);
+ return ret;
+}
+
+static gboolean
+g_tls_output_stream_base_pollable_is_writable (GPollableOutputStream *pollable)
+{
+ GTlsOutputStreamBase *tls_stream = G_TLS_OUTPUT_STREAM_BASE (pollable);
+ GTlsConnectionBase *conn;
+ gboolean ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+ g_return_val_if_fail (conn != NULL, FALSE);
+
+ ret = g_tls_connection_base_check (conn, G_IO_OUT);
+
+ g_object_unref (conn);
+
+ return ret;
+}
+
+static GSource *
+g_tls_output_stream_base_pollable_create_source (GPollableOutputStream *pollable,
+ GCancellable *cancellable)
+{
+ GTlsOutputStreamBase *tls_stream = G_TLS_OUTPUT_STREAM_BASE (pollable);
+ GTlsConnectionBase *conn;
+ GSource *ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+ g_return_val_if_fail (conn != NULL, NULL);
+
+ ret = g_tls_connection_base_create_source (conn,
+ G_IO_OUT,
+ cancellable);
+ g_object_unref (conn);
+ return ret;
+}
+
+static gssize
+g_tls_output_stream_base_pollable_write_nonblocking (GPollableOutputStream *pollable,
+ const void *buffer,
+ gsize size,
+ GError **error)
+{
+ GTlsOutputStreamBase *tls_stream = G_TLS_OUTPUT_STREAM_BASE (pollable);
+ GTlsConnectionBase *conn;
+ gssize ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+ g_return_val_if_fail (conn != NULL, -1);
+
+ ret = g_tls_connection_base_write (conn, buffer, size, FALSE, NULL, error);
+
+ g_object_unref (conn);
+ return ret;
+}
+
+static gboolean
+g_tls_output_stream_base_close (GOutputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOutputStreamBase *tls_stream = G_TLS_OUTPUT_STREAM_BASE (stream);
+ GIOStream *conn;
+ gboolean ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+
+ /* Special case here because this is called by the finalize
+ * of the main GTlsConnection object.
+ */
+ if (conn == NULL)
+ return TRUE;
+
+ ret = g_tls_connection_base_close_internal (conn, G_TLS_DIRECTION_WRITE,
+ cancellable, error);
+
+ g_object_unref (conn);
+ return ret;
+}
+
+/* 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)
+{
+ GTlsOutputStreamBase *tls_stream = object;
+ GError *error = NULL;
+ GIOStream *conn;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+
+ if (conn && !g_tls_connection_base_close_internal (conn,
+ G_TLS_DIRECTION_WRITE,
+ cancellable, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ if (conn)
+ g_object_unref (conn);
+}
+
+
+static void
+g_tls_output_stream_base_close_async (GOutputStream *stream,
+ 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_output_stream_base_close_async);
+ g_task_set_priority (task, io_priority);
+ g_task_run_in_thread (task, close_thread);
+ g_object_unref (task);
+}
+
+static gboolean
+g_tls_output_stream_base_close_finish (GOutputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
+ g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
+ g_tls_output_stream_base_close_async, FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+g_tls_output_stream_base_class_init (GTlsOutputStreamBaseClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GTlsOutputStreamBasePrivate));
+
+ gobject_class->dispose = g_tls_output_stream_base_dispose;
+ gobject_class->finalize = g_tls_output_stream_base_finalize;
+
+ output_stream_class->write_fn = g_tls_output_stream_base_write;
+ output_stream_class->close_fn = g_tls_output_stream_base_close;
+ output_stream_class->close_async = g_tls_output_stream_base_close_async;
+ output_stream_class->close_finish = g_tls_output_stream_base_close_finish;
+}
+
+static void
+g_tls_output_stream_base_pollable_iface_init (GPollableOutputStreamInterface *iface)
+{
+ iface->is_writable = g_tls_output_stream_base_pollable_is_writable;
+ iface->create_source = g_tls_output_stream_base_pollable_create_source;
+ iface->write_nonblocking = g_tls_output_stream_base_pollable_write_nonblocking;
+}
+
+static void
+g_tls_output_stream_base_init (GTlsOutputStreamBase *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_TLS_OUTPUT_STREAM_BASE,
GTlsOutputStreamBasePrivate);
+}
+
+GOutputStream *
+g_tls_output_stream_base_new (GTlsConnectionBase *conn)
+{
+ GTlsOutputStreamBase *tls_stream;
+
+ tls_stream = g_object_new (G_TYPE_TLS_OUTPUT_STREAM_BASE, NULL);
+ g_weak_ref_init (&tls_stream->priv->weak_conn, conn);
+
+ return G_OUTPUT_STREAM (tls_stream);
+}
diff --git a/tls/base/gtlsoutputstream-base.h b/tls/base/gtlsoutputstream-base.h
new file mode 100644
index 0000000..b0a25e1
--- /dev/null
+++ b/tls/base/gtlsoutputstream-base.h
@@ -0,0 +1,51 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#ifndef __G_TLS_OUTPUT_STREAM_BASE_H__
+#define __G_TLS_OUTPUT_STREAM_BASE_H__
+
+#include <gio/gio.h>
+#include "gtlsconnection-base.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_OUTPUT_STREAM_BASE (g_tls_output_stream_base_get_type ())
+#define G_TLS_OUTPUT_STREAM_BASE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst),
G_TYPE_TLS_OUTPUT_STREAM_BASE, GTlsOutputStreamBase))
+#define G_TLS_OUTPUT_STREAM_BASE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),
G_TYPE_TLS_OUTPUT_STREAM_BASE, GTlsOutputStreamBaseClass))
+#define G_IS_TLS_OUTPUT_STREAM_BASE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst),
G_TYPE_TLS_OUTPUT_STREAM_BASE))
+#define G_IS_TLS_OUTPUT_STREAM_BASE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),
G_TYPE_TLS_OUTPUT_STREAM_BASE))
+#define G_TLS_OUTPUT_STREAM_BASE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst),
G_TYPE_TLS_OUTPUT_STREAM_BASE, GTlsOutputStreamBaseClass))
+
+typedef struct _GTlsOutputStreamBasePrivate GTlsOutputStreamBasePrivate;
+typedef struct _GTlsOutputStreamBaseClass GTlsOutputStreamBaseClass;
+typedef struct _GTlsOutputStreamBase GTlsOutputStreamBase;
+
+struct _GTlsOutputStreamBaseClass
+{
+ GOutputStreamClass parent_class;
+};
+
+struct _GTlsOutputStreamBase
+{
+ GOutputStream parent_instance;
+ GTlsOutputStreamBasePrivate *priv;
+};
+
+GType g_tls_output_stream_base_get_type (void) G_GNUC_CONST;
+GOutputStream *g_tls_output_stream_base_new (GTlsConnectionBase *conn);
+
+G_END_DECLS
+
+#endif /* __G_TLS_OUTPUT_STREAM_BASE_H___ */
diff --git a/tls/gnutls/Makefile.am b/tls/gnutls/Makefile.am
index 2fb482f..38f7547 100644
--- a/tls/gnutls/Makefile.am
+++ b/tls/gnutls/Makefile.am
@@ -38,22 +38,20 @@ libgiognutls_la_SOURCES = \
gtlsdatabase-gnutls.h \
gtlsfiledatabase-gnutls.c \
gtlsfiledatabase-gnutls.h \
- gtlsinputstream-gnutls.c \
- gtlsinputstream-gnutls.h \
- gtlsoutputstream-gnutls.c \
- gtlsoutputstream-gnutls.h \
gtlsserverconnection-gnutls.c \
gtlsserverconnection-gnutls.h \
$(P11_SRCS) \
$(NULL)
AM_CPPFLAGS += \
+ -I$(top_srcdir)/tls/base \
$(P11_CFLAGS) \
$(GNUTLS_CFLAGS) \
$(NULL)
libgiognutls_la_LDFLAGS = $(module_flags)
libgiognutls_la_LIBADD = \
+ ../base/libtlsbase.la \
$(P11_LIBADD) \
$(GLIB_LIBS) \
$(GNUTLS_LIBS) \
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index d5d63fa..64fd725 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -28,6 +28,7 @@
#include <gnutls/x509.h>
#include <string.h>
+#include "gtlsconnection-base.h"
#include "gtlsclientconnection-gnutls.h"
#include "gtlsbackend-gnutls.h"
#include "gtlscertificate-gnutls.h"
@@ -71,8 +72,6 @@ struct _GTlsClientConnectionGnutlsPrivate
GBytes *session_id;
GBytes *session_data;
- gboolean cert_requested;
- GError *cert_error;
GPtrArray *accepted_cas;
};
@@ -153,7 +152,6 @@ g_tls_client_connection_gnutls_finalize (GObject *object)
g_clear_pointer (&gnutls->priv->accepted_cas, g_ptr_array_unref);
g_clear_pointer (&gnutls->priv->session_id, g_bytes_unref);
g_clear_pointer (&gnutls->priv->session_data, g_bytes_unref);
- g_clear_error (&gnutls->priv->cert_error);
G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->finalize (object);
}
@@ -278,12 +276,13 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t s
gnutls_retr2_st *st)
{
GTlsClientConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
+ GTlsConnectionBase *tls = gnutls_transport_get_ptr (session);
GTlsConnectionGnutls *conn = G_TLS_CONNECTION_GNUTLS (gnutls);
GPtrArray *accepted_cas;
GByteArray *dn;
int i;
- gnutls->priv->cert_requested = TRUE;
+ tls->certificate_requested = TRUE;
accepted_cas = g_ptr_array_new_with_free_func ((GDestroyNotify)g_byte_array_unref);
for (i = 0; i < nreqs; i++)
@@ -302,8 +301,8 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t s
if (st->ncerts == 0)
{
- g_clear_error (&gnutls->priv->cert_error);
- if (g_tls_connection_gnutls_request_certificate (conn, &gnutls->priv->cert_error))
+ g_clear_error (&tls->certificate_error);
+ if (g_tls_connection_gnutls_request_certificate (conn, &tls->certificate_error))
g_tls_connection_gnutls_get_certificate (conn, st);
}
@@ -321,15 +320,17 @@ g_tls_client_connection_gnutls_failed (GTlsConnectionGnutls *conn)
g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->priv->session_id);
}
-static void
-g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+static GTlsConnectionBaseStatus
+g_tls_client_connection_gnutls_handshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
{
- GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
/* Try to get a cached session */
if (gnutls->priv->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->priv->session_data, NULL),
g_bytes_get_size (gnutls->priv->session_data));
}
@@ -340,7 +341,7 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
session_data = g_tls_backend_gnutls_lookup_session (GNUTLS_CLIENT, gnutls->priv->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->priv->session_data, g_bytes_unref);
@@ -348,36 +349,23 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
}
}
- gnutls->priv->cert_requested = FALSE;
+ return G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->
+ handshake (tls, cancellable, error);
}
-static void
-g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
- GError **inout_error)
+static GTlsConnectionBaseStatus
+g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase *tls,
+ GError **error)
{
- GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
+ GTlsConnectionBaseStatus status;
int resumed;
- g_assert (inout_error != NULL);
+ status = G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->
+ complete_handshake (tls, error);
- if (g_error_matches (*inout_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS) &&
- gnutls->priv->cert_requested)
- {
- g_clear_error (inout_error);
- if (gnutls->priv->cert_error)
- {
- *inout_error = gnutls->priv->cert_error;
- gnutls->priv->cert_error = NULL;
- }
- else
- {
- g_set_error_literal (inout_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
- _("Server required TLS certificate"));
- }
- }
-
- resumed = gnutls_session_is_resumed (g_tls_connection_gnutls_get_session (conn));
- if (*inout_error || !resumed)
+ resumed = gnutls_session_is_resumed (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)));
+ if (status == G_TLS_CONNECTION_BASE_OK || !resumed)
{
/* Clear session data since the server did not accept what we provided. */
gnutls->priv->session_data_override = FALSE;
@@ -386,11 +374,11 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->priv->session_id);
}
- if (!*inout_error && !resumed)
+ if (status == G_TLS_CONNECTION_BASE_OK && !resumed)
{
gnutls_datum_t session_datum;
- if (gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (conn),
+ if (gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
&session_datum) == 0)
{
gnutls->priv->session_data = g_bytes_new_with_free_func (session_datum.data,
@@ -403,6 +391,8 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
gnutls->priv->session_data);
}
}
+
+ return status;
}
static void
@@ -428,7 +418,8 @@ static void
g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GTlsConnectionGnutlsClass *connection_gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+ GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
g_type_class_add_private (klass, sizeof (GTlsClientConnectionGnutlsPrivate));
@@ -437,9 +428,10 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
gobject_class->constructed = g_tls_client_connection_gnutls_constructed;
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->handshake = g_tls_client_connection_gnutls_handshake;
+ base_class->complete_handshake = g_tls_client_connection_gnutls_complete_handshake;
+
+ gnutls_class->failed = g_tls_client_connection_gnutls_failed;
g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index ca4730b..70899fa 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -31,9 +31,6 @@
#include "gtlsconnection-gnutls.h"
#include "gtlsbackend-gnutls.h"
#include "gtlscertificate-gnutls.h"
-#include "gtlsinputstream-gnutls.h"
-#include "gtlsoutputstream-gnutls.h"
-#include "gtlsserverconnection-gnutls.h"
#ifdef HAVE_PKCS11
#include <p11-kit/pin.h>
@@ -59,11 +56,6 @@ static ssize_t g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t transp
void *buf,
size_t buflen);
-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);
-
#ifdef HAVE_PKCS11
static P11KitPin* on_pin_prompt_callback (const char *pinfile,
P11KitUri *pin_uri,
@@ -72,111 +64,29 @@ static P11KitPin* on_pin_prompt_callback (const char *pinfile,
void *callback_data);
#endif
-static void g_tls_connection_gnutls_init_priorities (void);
+static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
-static gboolean do_implicit_handshake (GTlsConnectionGnutls *gnutls,
- gboolean blocking,
- GCancellable *cancellable,
- GError **error);
-static gboolean finish_handshake (GTlsConnectionGnutls *gnutls,
- GTask *thread_task,
- GError **error);
+static void g_tls_connection_gnutls_init_priorities (void);
-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_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
g_tls_connection_gnutls_initable_iface_init);
g_tls_connection_gnutls_init_priorities ();
- );
-
-
-enum
-{
- PROP_0,
- PROP_BASE_IO_STREAM,
- PROP_REQUIRE_CLOSE_NOTIFY,
- PROP_REHANDSHAKE_MODE,
- PROP_USE_SYSTEM_CERTDB,
- PROP_DATABASE,
- PROP_CERTIFICATE,
- PROP_INTERACTION,
- PROP_PEER_CERTIFICATE,
- PROP_PEER_CERTIFICATE_ERRORS
-};
+ )
struct _GTlsConnectionGnutlsPrivate
{
- GIOStream *base_io_stream;
- GPollableInputStream *base_istream;
- GPollableOutputStream *base_ostream;
-
gnutls_certificate_credentials_t creds;
gnutls_session_t session;
- GTlsCertificate *certificate, *peer_certificate;
- GTlsCertificateFlags peer_certificate_errors;
- GTlsCertificate *peer_certificate_tmp;
- GTlsCertificateFlags peer_certificate_errors_tmp;
-
- 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;
- gboolean started_handshake, handshaking, ever_handshaked;
- 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;
-
- GInputStream *tls_istream;
- GOutputStream *tls_ostream;
-
- GTlsInteraction *interaction;
gchar *interaction_id;
- GMutex op_mutex;
- GCancellable *waiting_for_op;
-
- gboolean reading;
- gboolean read_blocking;
- GError *read_error;
- GCancellable *read_cancellable;
-
- gboolean writing;
- gboolean write_blocking;
- GError *write_error;
- GCancellable *write_cancellable;
-
#ifndef GNUTLS_E_PREMATURE_TERMINATION
gboolean eof;
#endif
+
+ GTlsCertificate *peer_certificate_tmp;
+ GTlsCertificateFlags peer_certificate_errors_tmp;
};
static gint unique_interaction_id = 0;
@@ -192,11 +102,6 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
gnutls_certificate_set_verify_flags (gnutls->priv->creds,
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
- gnutls->priv->need_handshake = TRUE;
-
- gnutls->priv->database_is_unset = TRUE;
- gnutls->priv->is_system_certdb = TRUE;
-
unique_id = g_atomic_int_add (&unique_interaction_id, 1);
gnutls->priv->interaction_id = g_strdup_printf ("gtls:%d", unique_id);
@@ -204,10 +109,6 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
p11_kit_pin_register_callback (gnutls->priv->interaction_id,
on_pin_prompt_callback, gnutls, NULL);
#endif
-
- gnutls->priv->waiting_for_op = g_cancellable_new ();
- g_cancellable_cancel (gnutls->priv->waiting_for_op);
- g_mutex_init (&gnutls->priv->op_mutex);
}
/* First field is "fallback", second is "allow unsafe rehandshaking" */
@@ -294,7 +195,7 @@ g_tls_connection_gnutls_set_handshake_priority (GTlsConnectionGnutls *gnutls)
fallback = g_tls_client_connection_get_use_ssl3 (G_TLS_CLIENT_CONNECTION (gnutls));
else
fallback = FALSE;
- unsafe_rehandshake = (gnutls->priv->rehandshake_mode == G_TLS_REHANDSHAKE_UNSAFELY);
+ unsafe_rehandshake = (G_TLS_CONNECTION_BASE (gnutls)->rehandshake_mode == G_TLS_REHANDSHAKE_UNSAFELY);
gnutls_priority_set (gnutls->priv->session,
priorities[fallback][unsafe_rehandshake]);
}
@@ -305,12 +206,13 @@ g_tls_connection_gnutls_initable_init (GInitable *initable,
GError **error)
{
GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (initable);
gboolean client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
guint flags = client ? GNUTLS_CLIENT : GNUTLS_SERVER;
int status;
- g_return_val_if_fail (gnutls->priv->base_istream != NULL &&
- gnutls->priv->base_ostream != NULL, FALSE);
+ g_return_val_if_fail (tls->base_istream != NULL &&
+ tls->base_ostream != NULL, FALSE);
gnutls_init (&gnutls->priv->session, flags);
@@ -331,9 +233,6 @@ g_tls_connection_gnutls_initable_init (GInitable *initable,
g_tls_connection_gnutls_pull_func);
gnutls_transport_set_ptr (gnutls->priv->session, gnutls);
- gnutls->priv->tls_istream = g_tls_input_stream_gnutls_new (gnutls);
- gnutls->priv->tls_ostream = g_tls_output_stream_gnutls_new (gnutls);
-
return TRUE;
}
@@ -342,188 +241,22 @@ g_tls_connection_gnutls_finalize (GObject *object)
{
GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
- g_clear_object (&gnutls->priv->base_io_stream);
-
- g_clear_object (&gnutls->priv->tls_istream);
- g_clear_object (&gnutls->priv->tls_ostream);
-
if (gnutls->priv->session)
gnutls_deinit (gnutls->priv->session);
if (gnutls->priv->creds)
gnutls_certificate_free_credentials (gnutls->priv->creds);
- g_clear_object (&gnutls->priv->database);
- g_clear_object (&gnutls->priv->certificate);
- g_clear_object (&gnutls->priv->peer_certificate);
- g_clear_object (&gnutls->priv->peer_certificate_tmp);
-
- g_clear_pointer (&gnutls->priv->app_data_buf, g_byte_array_unref);
-
#ifdef HAVE_PKCS11
p11_kit_pin_unregister_callback (gnutls->priv->interaction_id,
on_pin_prompt_callback, gnutls);
#endif
g_free (gnutls->priv->interaction_id);
- g_clear_object (&gnutls->priv->interaction);
-
- g_clear_error (&gnutls->priv->handshake_error);
- g_clear_error (&gnutls->priv->read_error);
- g_clear_error (&gnutls->priv->write_error);
-
- /* This must always be NULL at this, as it holds a referehce to @gnutls as
- * its source object. However, we clear it anyway just in case this changes
- * in future. */
- g_clear_object (&gnutls->priv->implicit_handshake);
- g_clear_object (&gnutls->priv->read_cancellable);
- g_clear_object (&gnutls->priv->write_cancellable);
-
- g_clear_object (&gnutls->priv->waiting_for_op);
- g_mutex_clear (&gnutls->priv->op_mutex);
+ g_clear_object (&gnutls->priv->peer_certificate_tmp);
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);
- GTlsBackend *backend;
-
- switch (prop_id)
- {
- case PROP_BASE_IO_STREAM:
- g_value_set_object (value, gnutls->priv->base_io_stream);
- break;
-
- case PROP_REQUIRE_CLOSE_NOTIFY:
- g_value_set_boolean (value, gnutls->priv->require_close_notify);
- break;
-
- case PROP_REHANDSHAKE_MODE:
- g_value_set_enum (value, gnutls->priv->rehandshake_mode);
- break;
-
- case PROP_USE_SYSTEM_CERTDB:
- g_value_set_boolean (value, gnutls->priv->is_system_certdb);
- break;
-
- case PROP_DATABASE:
- if (gnutls->priv->database_is_unset)
- {
- backend = g_tls_backend_get_default ();
- gnutls->priv->database = g_tls_backend_get_default_database (backend);
- gnutls->priv->database_is_unset = FALSE;
- }
- g_value_set_object (value, gnutls->priv->database);
- break;
-
- case PROP_CERTIFICATE:
- g_value_set_object (value, gnutls->priv->certificate);
- break;
-
- case PROP_INTERACTION:
- g_value_set_object (value, gnutls->priv->interaction);
- break;
-
- case PROP_PEER_CERTIFICATE:
- g_value_set_object (value, gnutls->priv->peer_certificate);
- break;
-
- case PROP_PEER_CERTIFICATE_ERRORS:
- g_value_set_flags (value, gnutls->priv->peer_certificate_errors);
- break;
-
- 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);
- GInputStream *istream;
- GOutputStream *ostream;
- gboolean system_certdb;
- GTlsBackend *backend;
-
- switch (prop_id)
- {
- case PROP_BASE_IO_STREAM:
- if (gnutls->priv->base_io_stream)
- {
- g_object_unref (gnutls->priv->base_io_stream);
- gnutls->priv->base_istream = NULL;
- gnutls->priv->base_ostream = NULL;
- }
- gnutls->priv->base_io_stream = g_value_dup_object (value);
- if (!gnutls->priv->base_io_stream)
- return;
-
- istream = g_io_stream_get_input_stream (gnutls->priv->base_io_stream);
- ostream = g_io_stream_get_output_stream (gnutls->priv->base_io_stream);
-
- if (G_IS_POLLABLE_INPUT_STREAM (istream) &&
- g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (istream)))
- gnutls->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)))
- gnutls->priv->base_ostream = G_POLLABLE_OUTPUT_STREAM (ostream);
- break;
-
- case PROP_REQUIRE_CLOSE_NOTIFY:
- gnutls->priv->require_close_notify = g_value_get_boolean (value);
- break;
-
- case PROP_REHANDSHAKE_MODE:
- gnutls->priv->rehandshake_mode = g_value_get_enum (value);
- break;
-
- case PROP_USE_SYSTEM_CERTDB:
- system_certdb = g_value_get_boolean (value);
- if (system_certdb != gnutls->priv->is_system_certdb)
- {
- g_clear_object (&gnutls->priv->database);
- if (system_certdb)
- {
- backend = g_tls_backend_get_default ();
- gnutls->priv->database = g_tls_backend_get_default_database (backend);
- }
- gnutls->priv->is_system_certdb = system_certdb;
- gnutls->priv->database_is_unset = FALSE;
- }
- break;
-
- case PROP_DATABASE:
- g_clear_object (&gnutls->priv->database);
- gnutls->priv->database = g_value_dup_object (value);
- gnutls->priv->is_system_certdb = FALSE;
- gnutls->priv->database_is_unset = FALSE;
- break;
-
- case PROP_CERTIFICATE:
- if (gnutls->priv->certificate)
- g_object_unref (gnutls->priv->certificate);
- gnutls->priv->certificate = g_value_dup_object (value);
- break;
-
- case PROP_INTERACTION:
- g_clear_object (&gnutls->priv->interaction);
- gnutls->priv->interaction = g_value_dup_object (value);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
gnutls_certificate_credentials_t
g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *gnutls)
{
@@ -552,319 +285,103 @@ g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
gnutls->priv->interaction_id, st);
}
-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,
- gboolean blocking,
- GCancellable *cancellable,
- GError **error)
-{
- try_again:
- if (g_cancellable_set_error_if_cancelled (cancellable, error))
- return FALSE;
-
- g_mutex_lock (&gnutls->priv->op_mutex);
-
- if (((op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE ||
- op == G_TLS_CONNECTION_GNUTLS_OP_READ) &&
- (gnutls->priv->read_closing || gnutls->priv->read_closed)) ||
- ((op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE ||
- op == G_TLS_CONNECTION_GNUTLS_OP_WRITE) &&
- (gnutls->priv->write_closing || gnutls->priv->write_closed)))
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
- _("Connection is closed"));
- g_mutex_unlock (&gnutls->priv->op_mutex);
- return FALSE;
- }
-
- if (gnutls->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 (gnutls->priv->handshake_error);
- g_mutex_unlock (&gnutls->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 &&
- gnutls->priv->need_handshake)
- {
- gnutls->priv->need_handshake = FALSE;
- gnutls->priv->handshaking = TRUE;
- if (!do_implicit_handshake (gnutls, blocking, cancellable, error))
- {
- g_mutex_unlock (&gnutls->priv->op_mutex);
- return FALSE;
- }
- }
-
- if (gnutls->priv->need_finish_handshake &&
- gnutls->priv->implicit_handshake)
- {
- GError *my_error = NULL;
- gboolean success;
-
- gnutls->priv->need_finish_handshake = FALSE;
-
- g_mutex_unlock (&gnutls->priv->op_mutex);
- success = finish_handshake (gnutls, gnutls->priv->implicit_handshake, &my_error);
- g_clear_object (&gnutls->priv->implicit_handshake);
- g_mutex_lock (&gnutls->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 (&gnutls->priv->op_mutex);
- return FALSE;
- }
-
- g_clear_error (&my_error);
- }
- }
-
- if ((op != G_TLS_CONNECTION_GNUTLS_OP_WRITE && gnutls->priv->reading) ||
- (op != G_TLS_CONNECTION_GNUTLS_OP_READ && gnutls->priv->writing) ||
- (op != G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE && gnutls->priv->handshaking))
- {
- GPollFD fds[2];
- int nfds;
-
- g_cancellable_reset (gnutls->priv->waiting_for_op);
-
- g_mutex_unlock (&gnutls->priv->op_mutex);
-
- if (!blocking)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
- _("Operation would block"));
- return FALSE;
- }
-
- g_cancellable_make_pollfd (gnutls->priv->waiting_for_op, &fds[0]);
- if (g_cancellable_make_pollfd (cancellable, &fds[1]))
- nfds = 2;
- else
- nfds = 1;
-
- g_poll (fds, nfds, -1);
-
- if (nfds > 1)
- g_cancellable_release_fd (cancellable);
-
- goto try_again;
- }
-
- if (op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
- {
- gnutls->priv->handshaking = TRUE;
- gnutls->priv->need_handshake = FALSE;
- }
- if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ)
- gnutls->priv->read_closing = TRUE;
- if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
- gnutls->priv->write_closing = TRUE;
-
- if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
- gnutls->priv->reading = TRUE;
- if (op != G_TLS_CONNECTION_GNUTLS_OP_READ)
- gnutls->priv->writing = TRUE;
-
- g_mutex_unlock (&gnutls->priv->op_mutex);
- return TRUE;
-}
-
-static void
-yield_op (GTlsConnectionGnutls *gnutls,
- GTlsConnectionGnutlsOp op)
-{
- g_mutex_lock (&gnutls->priv->op_mutex);
-
- if (op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
- gnutls->priv->handshaking = FALSE;
- if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ)
- gnutls->priv->read_closing = FALSE;
- if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
- gnutls->priv->write_closing = FALSE;
-
- if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
- gnutls->priv->reading = FALSE;
- if (op != G_TLS_CONNECTION_GNUTLS_OP_READ)
- gnutls->priv->writing = FALSE;
-
- g_cancellable_cancel (gnutls->priv->waiting_for_op);
- g_mutex_unlock (&gnutls->priv->op_mutex);
-}
-
-static void
-begin_gnutls_io (GTlsConnectionGnutls *gnutls,
- GIOCondition direction,
- gboolean blocking,
- GCancellable *cancellable)
-{
- g_assert (direction & (G_IO_IN | G_IO_OUT));
-
- if (direction & G_IO_IN)
- {
- gnutls->priv->read_blocking = blocking;
- gnutls->priv->read_cancellable = cancellable;
- g_clear_error (&gnutls->priv->read_error);
- }
-
- if (direction & G_IO_OUT)
- {
- gnutls->priv->write_blocking = blocking;
- gnutls->priv->write_cancellable = cancellable;
- g_clear_error (&gnutls->priv->write_error);
- }
-}
-
-static int
+static GTlsConnectionBaseStatus
end_gnutls_io (GTlsConnectionGnutls *gnutls,
GIOCondition direction,
- int status,
+ int ret,
GError **error,
const char *err_fmt,
...) G_GNUC_PRINTF(5, 6);
-static int
+static GTlsConnectionBaseStatus
end_gnutls_io (GTlsConnectionGnutls *gnutls,
GIOCondition direction,
- int status,
+ int ret,
GError **error,
const char *err_fmt,
...)
{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (gnutls);
GError *my_error = NULL;
+ GTlsConnectionBaseStatus status;
- g_assert (direction & (G_IO_IN | G_IO_OUT));
- g_assert (!error || !*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)
+ 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)
{
- gnutls->priv->read_cancellable = NULL;
- if (status < 0)
- {
- my_error = gnutls->priv->read_error;
- gnutls->priv->read_error = NULL;
- }
- else
- g_clear_error (&gnutls->priv->read_error);
- }
- if (direction & G_IO_OUT)
- {
- gnutls->priv->write_cancellable = NULL;
- if (status < 0 && !my_error)
- {
- my_error = gnutls->priv->write_error;
- gnutls->priv->write_error = NULL;
- }
- else
- g_clear_error (&gnutls->priv->write_error);
+ if (my_error)
+ g_propagate_error (error, my_error);
+ return status;
}
- if (status >= 0)
- return status;
+ /* status == G_TLS_CONNECTION_BASE_ERROR */
- if (gnutls->priv->handshaking && !gnutls->priv->ever_handshaked)
+ if (tls->handshaking && !tls->ever_handshaked)
{
- if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
-#if GLIB_CHECK_VERSION (2, 35, 3)
- g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
-#endif
- status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
- status == GNUTLS_E_FATAL_ALERT_RECEIVED ||
- status == GNUTLS_E_DECRYPTION_FAILED ||
- status == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
+ if (ret == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
+ ret == GNUTLS_E_FATAL_ALERT_RECEIVED ||
+ ret == GNUTLS_E_DECRYPTION_FAILED ||
+ ret == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
{
- g_clear_error (&my_error);
+ g_error_free (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 (ret == GNUTLS_E_REHANDSHAKE)
{
- 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 (gnutls->priv->rehandshake_mode == G_TLS_REHANDSHAKE_NEVER)
+ if (tls->rehandshake_mode == G_TLS_REHANDSHAKE_NEVER)
{
+ g_error_free (my_error);
g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
_("Peer requested illegal TLS rehandshake"));
- return GNUTLS_E_PULL_ERROR;
+ G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
+ return G_TLS_CONNECTION_BASE_ERROR;
}
- g_mutex_lock (&gnutls->priv->op_mutex);
- if (!gnutls->priv->handshaking)
- gnutls->priv->need_handshake = TRUE;
- g_mutex_unlock (&gnutls->priv->op_mutex);
- return status;
+ return G_TLS_CONNECTION_BASE_REHANDSHAKE;
}
else if (
#ifdef GNUTLS_E_PREMATURE_TERMINATION
- status == GNUTLS_E_PREMATURE_TERMINATION
+ ret == GNUTLS_E_PREMATURE_TERMINATION
#else
- status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH && gnutls->priv->eof
+ ret == GNUTLS_E_UNEXPECTED_PACKET_LENGTH && gnutls->priv->eof
#endif
)
{
- if (gnutls->priv->handshaking && !gnutls->priv->ever_handshaked)
+ if (tls->handshaking && !tls->ever_handshaked)
{
g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
_("Peer failed to perform TLS handshake"));
return GNUTLS_E_PULL_ERROR;
}
- else if (gnutls->priv->require_close_notify)
+ else if (tls->require_close_notify)
{
+ g_error_free (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)
+ else if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND)
{
g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
_("TLS connection peer did not send a certificate"));
return status;
}
- if (error)
+ g_propagate_error (error, my_error);
+ if (error && !*error)
{
va_list ap;
@@ -872,209 +389,18 @@ end_gnutls_io (GTlsConnectionGnutls *gnutls,
*error = g_error_new_valist (G_TLS_ERROR, G_TLS_ERROR_MISC, err_fmt, ap);
va_end (ap);
}
- return status;
+
+ return G_TLS_CONNECTION_BASE_ERROR;
}
#define BEGIN_GNUTLS_IO(gnutls, direction, blocking, cancellable) \
- begin_gnutls_io (gnutls, direction, blocking, cancellable); \
+ g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (gnutls), \
+ direction, blocking, cancellable); \
do {
-#define END_GNUTLS_IO(gnutls, direction, ret, errmsg, err) \
- } while ((ret = end_gnutls_io (gnutls, direction, ret, err, errmsg, gnutls_strerror (ret))) ==
GNUTLS_E_AGAIN);
-
-gboolean
-g_tls_connection_gnutls_check (GTlsConnectionGnutls *gnutls,
- GIOCondition condition)
-{
- /* Racy, but worst case is that we just get WOULD_BLOCK back */
- if (gnutls->priv->need_finish_handshake)
- return TRUE;
-
- /* If a handshake or close is in progress, then tls_istream and
- * tls_ostream are blocked, regardless of the base stream status.
- */
- if (gnutls->priv->handshaking)
- return FALSE;
-
- if (((condition & G_IO_IN) && gnutls->priv->read_closing) ||
- ((condition & G_IO_OUT) && gnutls->priv->write_closing))
- return FALSE;
-
- if (condition & G_IO_IN)
- return g_pollable_input_stream_is_readable (gnutls->priv->base_istream);
- else
- return g_pollable_output_stream_is_writable (gnutls->priv->base_ostream);
-}
-
-typedef struct {
- GSource source;
-
- GTlsConnectionGnutls *gnutls;
- GObject *stream;
-
- 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;
-}
-
-static void
-gnutls_source_sync (GTlsConnectionGnutlsSource *gnutls_source)
-{
- GTlsConnectionGnutls *gnutls = gnutls_source->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 (&gnutls->priv->op_mutex);
- if (((gnutls_source->condition & G_IO_IN) && gnutls->priv->reading) ||
- ((gnutls_source->condition & G_IO_OUT) && gnutls->priv->writing) ||
- (gnutls->priv->handshaking && !gnutls->priv->need_finish_handshake))
- op_waiting = TRUE;
- else
- op_waiting = FALSE;
-
- if (!op_waiting && !gnutls->priv->need_handshake &&
- !gnutls->priv->need_finish_handshake)
- io_waiting = TRUE;
- else
- io_waiting = FALSE;
- g_mutex_unlock (&gnutls->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 (gnutls->priv->waiting_for_op);
- else if (io_waiting && G_IS_POLLABLE_INPUT_STREAM (gnutls_source->stream))
- gnutls_source->child_source = g_pollable_input_stream_create_source (gnutls->priv->base_istream, NULL);
- else if (io_waiting && G_IS_POLLABLE_OUTPUT_STREAM (gnutls_source->stream))
- gnutls_source->child_source = g_pollable_output_stream_create_source (gnutls->priv->base_ostream, NULL);
- else
- gnutls_source->child_source = g_timeout_source_new (0);
-
- g_source_set_dummy_callback (gnutls_source->child_source);
- g_source_add_child_source ((GSource *)gnutls_source, gnutls_source->child_source);
-}
-
-static gboolean
-gnutls_source_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
-{
- GPollableSourceFunc func = (GPollableSourceFunc)callback;
- GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
- gboolean ret;
-
- ret = (*func) (gnutls_source->stream, 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 GSourceFuncs gnutls_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
-};
-
-GSource *
-g_tls_connection_gnutls_create_source (GTlsConnectionGnutls *gnutls,
- GIOCondition condition,
- GCancellable *cancellable)
-{
- GSource *source, *cancellable_source;
- GTlsConnectionGnutlsSource *gnutls_source;
-
- source = g_source_new (&gnutls_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 (condition & G_IO_IN)
- gnutls_source->stream = G_OBJECT (gnutls->priv->tls_istream);
- else if (condition & G_IO_OUT)
- gnutls_source->stream = G_OBJECT (gnutls->priv->tls_ostream);
-
- 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;
-}
+#define END_GNUTLS_IO(gnutls, direction, ret, status, errmsg, err) \
+ status = end_gnutls_io (gnutls, direction, ret, err, errmsg, gnutls_strerror (ret)); \
+ } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
static void
set_gnutls_error (GTlsConnectionGnutls *gnutls,
@@ -1102,25 +428,24 @@ g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t transport_data,
void *buf,
size_t buflen)
{
+ GTlsConnectionBase *tls = transport_data;
GTlsConnectionGnutls *gnutls = transport_data;
ssize_t ret;
- /* If gnutls->priv->read_error is non-%NULL when we're called, it means
+ /* If tls->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 (&gnutls->priv->read_error);
+ g_clear_error (&tls->read_error);
- ret = g_pollable_stream_read (G_INPUT_STREAM (gnutls->priv->base_istream),
- buf, buflen,
- gnutls->priv->read_blocking,
- gnutls->priv->read_cancellable,
- &gnutls->priv->read_error);
+ ret = g_pollable_stream_read (G_INPUT_STREAM (tls->base_istream),
+ buf, buflen, tls->read_blocking,
+ tls->read_cancellable, &tls->read_error);
if (ret < 0)
- set_gnutls_error (gnutls, gnutls->priv->read_error);
+ set_gnutls_error (gnutls, tls->read_error);
#ifndef GNUTLS_E_PREMATURE_TERMINATION
else if (ret == 0)
gnutls->priv->eof = TRUE;
@@ -1134,23 +459,48 @@ 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;
ssize_t ret;
/* See comment in pull_func. */
- g_clear_error (&gnutls->priv->write_error);
+ g_clear_error (&tls->write_error);
- ret = g_pollable_stream_write (G_OUTPUT_STREAM (gnutls->priv->base_ostream),
- buf, buflen,
- gnutls->priv->write_blocking,
- gnutls->priv->write_cancellable,
- &gnutls->priv->write_error);
+ ret = g_pollable_stream_write (G_OUTPUT_STREAM (tls->base_ostream),
+ buf, buflen, tls->write_blocking,
+ tls->write_cancellable, &tls->write_error);
if (ret < 0)
- set_gnutls_error (gnutls, gnutls->priv->write_error);
+ set_gnutls_error (gnutls, tls->write_error);
return ret;
}
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_request_rehandshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionGnutls *gnutls;
+ GTlsConnectionBaseStatus status;
+ int ret;
+
+ /* On a client-side connection, gnutls_handshake() itself will start
+ * a rehandshake, so we only need to do something special here for
+ * server-side connections.
+ */
+ if (!G_IS_TLS_SERVER_CONNECTION (tls))
+ return G_TLS_CONNECTION_BASE_OK;
+
+ gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+
+ BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+ ret = gnutls_rehandshake (gnutls->priv->session);
+ END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status,
+ _("Error performing TLS handshake: %s"), error);
+
+ return status;
+}
+
static GTlsCertificate *
get_peer_certificate_from_session (GTlsConnectionGnutls *gnutls)
{
@@ -1217,50 +567,15 @@ verify_peer_certificate (GTlsConnectionGnutls *gnutls,
return errors;
}
-static void
-handshake_thread (GTask *task,
- gpointer object,
- gpointer task_data,
- GCancellable *cancellable)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_handshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
{
- GTlsConnectionGnutls *gnutls = object;
- gboolean is_client;
- GError *error = NULL;
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+ GTlsConnectionBaseStatus status;
int ret;
- gnutls->priv->started_handshake = FALSE;
-
- if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE,
- TRUE, cancellable, &error))
- {
- g_task_return_error (task, error);
- return;
- }
-
- g_clear_error (&gnutls->priv->handshake_error);
-
- is_client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
-
- if (!is_client && gnutls->priv->ever_handshaked &&
- !gnutls->priv->implicit_handshake)
- {
- BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
- ret = gnutls_rehandshake (gnutls->priv->session);
- END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
- _("Error performing TLS handshake: %s"), &error);
-
- if (error)
- {
- g_task_return_error (task, error);
- return;
- }
- }
-
- gnutls->priv->started_handshake = TRUE;
-
- g_clear_object (&gnutls->priv->peer_certificate);
- gnutls->priv->peer_certificate_errors = 0;
-
g_tls_connection_gnutls_set_handshake_priority (gnutls);
BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
@@ -1273,14 +588,14 @@ handshake_thread (GTask *task,
ret = gnutls_record_recv (gnutls->priv->session, buf, sizeof (buf));
if (ret > -1)
{
- if (!gnutls->priv->app_data_buf)
- gnutls->priv->app_data_buf = g_byte_array_new ();
- g_byte_array_append (gnutls->priv->app_data_buf, buf, ret);
+ if (!tls->app_data_buf)
+ tls->app_data_buf = g_byte_array_new ();
+ g_byte_array_append (tls->app_data_buf, buf, ret);
ret = GNUTLS_E_AGAIN;
}
}
- END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
- _("Error performing TLS handshake: %s"), &error);
+ END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status,
+ _("Error performing TLS handshake: %s"), error);
if (ret == 0 && gnutls_certificate_type_get (gnutls->priv->session) == GNUTLS_CRT_X509)
{
@@ -1289,470 +604,107 @@ handshake_thread (GTask *task,
gnutls->priv->peer_certificate_errors_tmp = verify_peer_certificate (gnutls,
gnutls->priv->peer_certificate_tmp);
else if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
{
- g_set_error_literal (&error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
_("Server did not return a valid TLS certificate"));
}
}
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->finish_handshake (gnutls, &error);
-
- if (error)
- {
- g_task_return_error (task, error);
- }
- else
- {
- gnutls->priv->ever_handshaked = TRUE;
- g_task_return_boolean (task, TRUE);
- }
-}
-
-static gboolean
-accept_peer_certificate (GTlsConnectionGnutls *gnutls,
- GTlsCertificate *peer_certificate,
- GTlsCertificateFlags peer_certificate_errors)
-{
- gboolean accepted = FALSE;
-
- if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
- {
- GTlsCertificateFlags validation_flags =
- g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (gnutls));
-
- if ((peer_certificate_errors & validation_flags) == 0)
- accepted = TRUE;
- }
-
- if (!accepted)
- {
- accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls),
- peer_certificate,
- peer_certificate_errors);
- }
-
- return accepted;
-}
-
-static void
-begin_handshake (GTlsConnectionGnutls *gnutls)
-{
- G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
+ return status;
}
-static gboolean
-finish_handshake (GTlsConnectionGnutls *gnutls,
- GTask *task,
- GError **error)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase *tls,
+ GError **error)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
GTlsCertificate *peer_certificate;
- GTlsCertificateFlags peer_certificate_errors;
-
- g_assert (error != NULL);
+ GTlsCertificateFlags peer_certificate_errors = 0;
+ gboolean status = G_TLS_CONNECTION_BASE_OK;
peer_certificate = gnutls->priv->peer_certificate_tmp;
gnutls->priv->peer_certificate_tmp = NULL;
peer_certificate_errors = gnutls->priv->peer_certificate_errors_tmp;
gnutls->priv->peer_certificate_errors_tmp = 0;
- if (g_task_propagate_boolean (task, error) && peer_certificate)
+ if (peer_certificate)
{
- if (!accept_peer_certificate (gnutls, peer_certificate,
- peer_certificate_errors))
+ 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;
}
- gnutls->priv->peer_certificate = peer_certificate;
- gnutls->priv->peer_certificate_errors = peer_certificate_errors;
- g_object_notify (G_OBJECT (gnutls), "peer-certificate");
- g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
- }
-
- if (*error && gnutls->priv->started_handshake)
- gnutls->priv->handshake_error = g_error_copy (*error);
-
- return (*error == NULL);
-}
-
-static gboolean
-g_tls_connection_gnutls_handshake (GTlsConnection *conn,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
- GTask *task;
- gboolean success;
- GError *my_error = NULL;
-
- task = g_task_new (conn, cancellable, NULL, NULL);
- g_task_set_source_tag (task, g_tls_connection_gnutls_handshake);
- begin_handshake (gnutls);
- g_task_run_in_thread_sync (task, handshake_thread);
- success = finish_handshake (gnutls, task, &my_error);
- g_object_unref (task);
-
- yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE);
-
- if (my_error)
- g_propagate_error (error, my_error);
- return success;
-}
-
-/* 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);
- GError *error = NULL;
- gboolean need_finish_handshake, success;
-
- g_mutex_lock (&gnutls->priv->op_mutex);
- if (gnutls->priv->need_finish_handshake)
- {
- need_finish_handshake = TRUE;
- gnutls->priv->need_finish_handshake = FALSE;
- }
- else
- need_finish_handshake = FALSE;
- g_mutex_unlock (&gnutls->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 (gnutls->priv->handshake_error)
- g_task_return_error (caller_task, g_error_copy (gnutls->priv->handshake_error));
- else
- g_task_return_boolean (caller_task, TRUE);
-
- g_object_unref (caller_task);
-}
-
-static void
-async_handshake_thread (GTask *task,
- gpointer object,
- gpointer task_data,
- GCancellable *cancellable)
-{
- GTlsConnectionGnutls *gnutls = object;
-
- handshake_thread (task, object, task_data, cancellable);
-
- g_mutex_lock (&gnutls->priv->op_mutex);
- gnutls->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.
- */
- gnutls->priv->handshaking = FALSE;
- g_mutex_unlock (&gnutls->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)
-{
- GTask *thread_task, *caller_task;
-
- 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);
- 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);
-}
-
-static gboolean
-do_implicit_handshake (GTlsConnectionGnutls *gnutls,
- gboolean blocking,
- GCancellable *cancellable,
- GError **error)
-{
- /* We have op_mutex */
-
- gnutls->priv->implicit_handshake = g_task_new (gnutls, cancellable, NULL, NULL);
- g_task_set_source_tag (gnutls->priv->implicit_handshake,
- do_implicit_handshake);
-
- begin_handshake (gnutls);
-
- if (blocking)
- {
- GError *my_error = NULL;
- gboolean success;
-
- g_mutex_unlock (&gnutls->priv->op_mutex);
- g_task_run_in_thread_sync (gnutls->priv->implicit_handshake,
- handshake_thread);
- success = finish_handshake (gnutls,
- gnutls->priv->implicit_handshake,
- &my_error);
- g_clear_object (&gnutls->priv->implicit_handshake);
- yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE);
- g_mutex_lock (&gnutls->priv->op_mutex);
-
- if (my_error)
- g_propagate_error (error, my_error);
- return success;
+ g_tls_connection_base_set_peer_certificate (G_TLS_CONNECTION_BASE (gnutls),
+ peer_certificate,
+ peer_certificate_errors);
}
- else
- {
- g_task_run_in_thread (gnutls->priv->implicit_handshake,
- async_handshake_thread);
-
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
- _("Operation would block"));
- return FALSE;
- }
+ return status;
}
-gssize
-g_tls_connection_gnutls_read (GTlsConnectionGnutls *gnutls,
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_read (GTlsConnectionBase *tls,
void *buffer,
gsize count,
gboolean blocking,
+ gssize *nread,
GCancellable *cancellable,
GError **error)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+ GTlsConnectionBaseStatus status;
gssize ret;
- if (gnutls->priv->app_data_buf && !gnutls->priv->handshaking)
- {
- ret = MIN (count, gnutls->priv->app_data_buf->len);
- memcpy (buffer, gnutls->priv->app_data_buf->data, ret);
- if (ret == gnutls->priv->app_data_buf->len)
- g_clear_pointer (&gnutls->priv->app_data_buf, g_byte_array_unref);
- else
- g_byte_array_remove_range (gnutls->priv->app_data_buf, 0, ret);
- return ret;
- }
-
- again:
- if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ,
- blocking, cancellable, error))
- return -1;
-
BEGIN_GNUTLS_IO (gnutls, G_IO_IN, blocking, cancellable);
ret = gnutls_record_recv (gnutls->priv->session, buffer, count);
- END_GNUTLS_IO (gnutls, G_IO_IN, ret, _("Error reading data from TLS socket: %s"), 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: %s"), error);
if (ret >= 0)
- return ret;
- else if (ret == GNUTLS_E_REHANDSHAKE)
- goto again;
- else
- return -1;
+ *nread = ret;
+ return status;
}
-gssize
-g_tls_connection_gnutls_write (GTlsConnectionGnutls *gnutls,
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_write (GTlsConnectionBase *tls,
const void *buffer,
gsize count,
gboolean blocking,
+ gssize *nwrote,
GCancellable *cancellable,
GError **error)
{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+ GTlsConnectionBaseStatus status;
gssize ret;
- again:
- if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE,
- blocking, cancellable, error))
- return -1;
-
BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, blocking, cancellable);
ret = gnutls_record_send (gnutls->priv->session, buffer, count);
- END_GNUTLS_IO (gnutls, G_IO_OUT, ret, _("Error writing data to TLS socket: %s"), 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: %s"), error);
if (ret >= 0)
- return ret;
- else if (ret == GNUTLS_E_REHANDSHAKE)
- goto again;
- else
- return -1;
-}
-
-static GInputStream *
-g_tls_connection_gnutls_get_input_stream (GIOStream *stream)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
-
- return gnutls->priv->tls_istream;
-}
-
-static GOutputStream *
-g_tls_connection_gnutls_get_output_stream (GIOStream *stream)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
-
- return gnutls->priv->tls_ostream;
-}
-
-gboolean
-g_tls_connection_gnutls_close_internal (GIOStream *stream,
- GTlsDirection direction,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
- 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() or
- * g_output_stream_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, TRUE, cancellable, error))
- return FALSE;
-
- if (gnutls->priv->ever_handshaked && !gnutls->priv->write_closed &&
- direction & G_TLS_DIRECTION_WRITE)
- {
- BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
- ret = gnutls_bye (gnutls->priv->session, GNUTLS_SHUT_WR);
- END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
- _("Error performing TLS close: %s"), &gnutls_error);
-
- gnutls->priv->write_closed = TRUE;
- }
-
- if (!gnutls->priv->read_closed && direction & G_TLS_DIRECTION_READ)
- gnutls->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 (direction == G_TLS_DIRECTION_BOTH)
- success = g_io_stream_close (gnutls->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 (gnutls->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 (gnutls->priv->base_io_stream),
- cancellable, &stream_error);
-
- 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,
- 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;
- GError *error = NULL;
-
- if (!g_tls_connection_gnutls_close_internal (stream, G_TLS_DIRECTION_BOTH,
- cancellable, &error))
- g_task_return_error (task, error);
- else
- g_task_return_boolean (task, TRUE);
+ *nwrote = ret;
+ return status;
}
-static void
-g_tls_connection_gnutls_close_async (GIOStream *stream,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_close (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
{
- GTask *task;
-
- task = g_task_new (stream, cancellable, callback, user_data);
- g_task_set_source_tag (task, g_tls_connection_gnutls_close_async);
- g_task_set_priority (task, io_priority);
- g_task_run_in_thread (task, close_thread);
- g_object_unref (task);
-}
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+ GTlsConnectionBaseStatus status;
+ int ret;
-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);
+ BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+ ret = gnutls_bye (gnutls->priv->session, GNUTLS_SHUT_WR);
+ END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status,
+ _("Error performing TLS close: %s"), error);
- return g_task_propagate_boolean (G_TASK (result), error);
+ return status;
}
#ifdef HAVE_PKCS11
@@ -1764,14 +716,14 @@ on_pin_prompt_callback (const char *pinfile,
P11KitPinFlags pin_flags,
void *callback_data)
{
- GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (callback_data);
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (callback_data);
GTlsInteractionResult result;
GTlsPasswordFlags flags = 0;
GTlsPassword *password;
P11KitPin *pin = NULL;
GError *error = NULL;
- if (!gnutls->priv->interaction)
+ if (!tls->interaction)
return NULL;
if (pin_flags & P11_KIT_PIN_FLAGS_RETRY)
@@ -1783,7 +735,7 @@ on_pin_prompt_callback (const char *pinfile,
password = g_pkcs11_pin_new (flags, pin_description);
- result = g_tls_interaction_ask_password (gnutls->priv->interaction, password,
+ result = g_tls_interaction_ask_password (tls->interaction, password,
g_cancellable_get_current (), &error);
switch (result)
@@ -1812,34 +764,18 @@ 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);
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
g_type_class_add_private (klass, sizeof (GTlsConnectionGnutlsPrivate));
- 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;
-
- g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream");
- 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");
+ base_class->request_rehandshake = g_tls_connection_gnutls_request_rehandshake;
+ base_class->handshake = g_tls_connection_gnutls_handshake;
+ base_class->complete_handshake = g_tls_connection_gnutls_complete_handshake;
+ base_class->read_fn = g_tls_connection_gnutls_read;
+ base_class->write_fn = g_tls_connection_gnutls_write;
+ base_class->close_fn = g_tls_connection_gnutls_close;
}
static void
@@ -1855,16 +791,18 @@ g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls *self,
GTlsInteractionResult res = G_TLS_INTERACTION_UNHANDLED;
GTlsInteraction *interaction;
GTlsConnection *conn;
+ GTlsConnectionBase *tls;
g_return_val_if_fail (G_IS_TLS_CONNECTION_GNUTLS (self), FALSE);
conn = G_TLS_CONNECTION (self);
+ tls = G_TLS_CONNECTION_BASE (self);
interaction = g_tls_connection_get_interaction (conn);
if (!interaction)
return FALSE;
res = g_tls_interaction_invoke_request_certificate (interaction, conn, 0,
- self->priv->read_cancellable, error);
+ tls->read_cancellable, error);
return res != G_TLS_INTERACTION_FAILED;
}
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index a7323a8..4d4d9a4 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -19,6 +19,8 @@
#include <gio/gio.h>
#include <gnutls/gnutls.h>
+#include "gtlsconnection-base.h"
+
G_BEGIN_DECLS
#define G_TYPE_TLS_CONNECTION_GNUTLS (g_tls_connection_gnutls_get_type ())
@@ -34,18 +36,14 @@ typedef struct _GTlsConnectionGnutls GTlsConnectionGnut
struct _GTlsConnectionGnutlsClass
{
- GTlsConnectionClass parent_class;
+ GTlsConnectionBaseClass parent_class;
void (*failed) (GTlsConnectionGnutls *gnutls);
-
- void (*begin_handshake) (GTlsConnectionGnutls *gnutls);
- void (*finish_handshake) (GTlsConnectionGnutls *gnutls,
- GError **inout_error);
};
struct _GTlsConnectionGnutls
{
- GTlsConnection parent_instance;
+ GTlsConnectionBase parent_instance;
GTlsConnectionGnutlsPrivate *priv;
};
@@ -60,38 +58,6 @@ void g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnu
gboolean g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls *gnutls,
GError **error);
-gssize g_tls_connection_gnutls_read (GTlsConnectionGnutls *gnutls,
- void *buffer,
- gsize size,
- gboolean blocking,
- GCancellable *cancellable,
- GError **error);
-gssize g_tls_connection_gnutls_write (GTlsConnectionGnutls *gnutls,
- const void *buffer,
- gsize size,
- gboolean blocking,
- 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,
- GCancellable *cancellable,
- GError **error);
-
G_END_DECLS
#endif /* __G_TLS_CONNECTION_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index aea76fb..1eba4cc 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -167,10 +167,12 @@ g_tls_server_connection_gnutls_failed (GTlsConnectionGnutls *conn)
gnutls_db_remove_session (g_tls_connection_gnutls_get_session (conn));
}
-static void
-g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+static GTlsConnectionBaseStatus
+g_tls_server_connection_gnutls_handshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
{
- GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (conn);
+ GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (tls);
gnutls_session_t session;
gnutls_certificate_request_t req_mode;
@@ -188,14 +190,11 @@ g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
break;
}
- session = g_tls_connection_gnutls_get_session (conn);
+ session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls));
gnutls_certificate_server_set_request (session, req_mode);
-}
-static void
-g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls *gnutls,
- GError **inout_error)
-{
+ return G_TLS_CONNECTION_BASE_CLASS (g_tls_server_connection_gnutls_parent_class)->
+ handshake (tls, cancellable, error);
}
/* Session cache management */
@@ -260,16 +259,17 @@ static void
g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GTlsConnectionGnutlsClass *connection_gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+ GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
g_type_class_add_private (klass, sizeof (GTlsServerConnectionGnutlsPrivate));
gobject_class->get_property = g_tls_server_connection_gnutls_get_property;
gobject_class->set_property = g_tls_server_connection_gnutls_set_property;
- connection_gnutls_class->failed = g_tls_server_connection_gnutls_failed;
- connection_gnutls_class->begin_handshake = g_tls_server_connection_gnutls_begin_handshake;
- connection_gnutls_class->finish_handshake = g_tls_server_connection_gnutls_finish_handshake;
+ base_class->handshake = g_tls_server_connection_gnutls_handshake;
+
+ gnutls_class->failed = g_tls_server_connection_gnutls_failed;
g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]