[glib-networking] Add a GNUTLS-based GTlsBackend



commit b3c34ce3db83774e33209276dcbaa2b7b6d23b2b
Author: Dan Winship <danw gnome org>
Date:   Sun Nov 7 08:11:56 2010 -0500

    Add a GNUTLS-based GTlsBackend
    
    https://bugzilla.gnome.org/show_bug.cgi?id=588189

 Makefile.am                              |    4 +
 configure.ac                             |   72 ++-
 tls/gnutls/Makefile.am                   |   41 ++
 tls/gnutls/gnutls-module.c               |   47 ++
 tls/gnutls/gtlsbackend-gnutls.c          |  210 ++++++
 tls/gnutls/gtlsbackend-gnutls.h          |   50 ++
 tls/gnutls/gtlscertificate-gnutls.c      |  362 ++++++++++
 tls/gnutls/gtlscertificate-gnutls.h      |   55 ++
 tls/gnutls/gtlsclientconnection-gnutls.c |  310 ++++++++
 tls/gnutls/gtlsclientconnection-gnutls.h |   46 ++
 tls/gnutls/gtlsconnection-gnutls.c       | 1154 ++++++++++++++++++++++++++++++
 tls/gnutls/gtlsconnection-gnutls.h       |   77 ++
 tls/gnutls/gtlsinputstream-gnutls.c      |  232 ++++++
 tls/gnutls/gtlsinputstream-gnutls.h      |   48 ++
 tls/gnutls/gtlsoutputstream-gnutls.c     |  232 ++++++
 tls/gnutls/gtlsoutputstream-gnutls.h     |   48 ++
 tls/gnutls/gtlsserverconnection-gnutls.c |  193 +++++
 tls/gnutls/gtlsserverconnection-gnutls.h |   47 ++
 18 files changed, 3222 insertions(+), 6 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index f880c5a..30f8aea 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,3 +8,7 @@ SUBDIRS = po
 if HAVE_LIBPROXY
 SUBDIRS += proxy/libproxy
 endif
+
+if HAVE_GNUTLS
+SUBDIRS += tls/gnutls
+endif
diff --git a/configure.ac b/configure.ac
index 3b11307..8f56fd7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -33,7 +33,7 @@ dnl *****************************
 dnl *** Check GLib GIO        ***
 dnl *****************************
 
-PKG_CHECK_MODULES(GIO, [gio-2.0 >= 2.25.15])
+PKG_CHECK_MODULES(GIO, [gio-2.0 >= 2.27.3])
 AC_SUBST(GIO_CFLAGS)
 AC_SUBST(GIO_LIBS)
 
@@ -42,6 +42,9 @@ AS_IF([test "x$GIO_MODULE_DIR" = "x"],
       [AC_MSG_FAILURE(GIO_MODULE_DIR is missing from gio-2.0.pc)])
 AC_SUBST(GIO_MODULE_DIR)
 
+proxy_support=no
+tls_support=no
+
 dnl *****************************
 dnl *** Checks for LibProxy   ***
 dnl *****************************
@@ -53,18 +56,75 @@ AC_ARG_WITH(libproxy,
     [with_libproxy=check])
 AS_IF([test "x$with_libproxy" != "xno"],
     [PKG_CHECK_MODULES(LIBPROXY, [libproxy-1.0 >= 0.3.1],
-        [with_libproxy=yes],
+        [with_libproxy=yes; proxy_support=libproxy],
         [AS_IF([test "x$with_libproxy" = "xyes"],
                [AC_MSG_FAILURE("$LIBPROXY_PKG_ERRORS")])])])
 AM_CONDITIONAL(HAVE_LIBPROXY, [test "x$with_libproxy" = "xyes"])
 AC_SUBST(LIBPROXY_CFLAGS)
 AC_SUBST(LIBPROXY_LIBS)
 
-dnl Checks for typedefs, structures, and compiler characteristics.
-
-dnl Checks for library functions.
+dnl *****************************
+dnl *** Checks for GNUTLS     ***
+dnl *****************************
+AC_ARG_WITH(gnutls,
+    [AC_HELP_STRING([--with-gnutls],
+                    [support for GNUTLS @<:@default=yes@:>@])],
+    [],
+    [with_gnutls=yes])
+AS_IF([test "x$with_gnutls" != "xno"],
+    [PKG_CHECK_MODULES(GNUTLS, [gnutls >= 2.1.7],
+        [with_gnutls=yes
+	 tls_support=gnutls
+	 AM_PATH_LIBGCRYPT([])],
+        [AS_IF([test "x$with_gnutls" = "xyes"],
+               [AC_MSG_FAILURE("$GNUTLS_PKG_ERRORS")])])])
+AM_CONDITIONAL(HAVE_GNUTLS, [test "x$with_gnutls" = "xyes"])
+AC_SUBST(GNUTLS_CFLAGS)
+AC_SUBST(GNUTLS_LIBS)
+
+if test "x$with_gnutls" = "xyes"; then
+    AC_MSG_CHECKING([location of system Certificate Authority list])
+    AC_ARG_WITH(ca-certificates,
+		[AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@],
+				[path to system Certificate Authority list])])
+    if test "$with_ca_certificates" = "no"; then
+        AC_MSG_RESULT([disabled])
+    else
+        if test -z "$with_ca_certificates"; then
+	    for f in /etc/pki/tls/certs/ca-bundle.crt \
+	       	     /etc/ssl/certs/ca-certificates.crt; do
+		if test -f "$f"; then
+		    with_ca_certificates="$f"
+		fi
+	    done
+	    if test -z "$with_ca_certificates"; then
+		AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable])
+	    fi
+        fi
+
+        AC_MSG_RESULT($with_ca_certificates)
+        AC_DEFINE_UNQUOTED(GTLS_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list])
+    fi
+fi
 
+dnl *****************************
+dnl *** done                  ***
+dnl *****************************
 AC_CONFIG_FILES([Makefile
                  po/Makefile.in po/Makefile
-                 proxy/libproxy/Makefile])
+                 proxy/libproxy/Makefile
+		 tls/gnutls/Makefile
+		])
 AC_OUTPUT
+
+echo ""
+echo "  Proxy support: $proxy_support"
+echo "  TLS support:   $tls_support"
+if test "$tls_support" != "no"; then
+    echo "  TLS CA file:   ${with_ca_certificates:-(none)}"
+    if test -n "$with_ca_certificates"; then
+	if ! test -f "$with_ca_certificates"; then
+	    AC_MSG_WARN([Specified certificate authority file '$with_ca_certificates' does not exist])
+	fi
+    fi
+fi
diff --git a/tls/gnutls/Makefile.am b/tls/gnutls/Makefile.am
new file mode 100644
index 0000000..4d2824b
--- /dev/null
+++ b/tls/gnutls/Makefile.am
@@ -0,0 +1,41 @@
+include $(top_srcdir)/Makefile.decl
+
+NULL =
+
+module_flags = -export_dynamic -avoid-version -module -no-undefined -export-symbols-regex '^g_io_module_(load|unload|query)'
+
+giomodule_LTLIBRARIES = libgiognutls.la
+giomoduledir = $(GIO_MODULE_DIR)
+
+libgiognutls_la_SOURCES = 		\
+	gnutls-module.c			\
+	gtlsbackend-gnutls.c		\
+	gtlsbackend-gnutls.h		\
+	gtlscertificate-gnutls.c	\
+	gtlscertificate-gnutls.h	\
+	gtlsclientconnection-gnutls.c	\
+	gtlsclientconnection-gnutls.h	\
+	gtlsconnection-gnutls.c		\
+	gtlsconnection-gnutls.h		\
+	gtlsinputstream-gnutls.c	\
+	gtlsinputstream-gnutls.h	\
+	gtlsoutputstream-gnutls.c	\
+	gtlsoutputstream-gnutls.h	\
+	gtlsserverconnection-gnutls.c	\
+	gtlsserverconnection-gnutls.h	\
+	$(NULL)
+
+libgiognutls_la_CFLAGS = \
+	-DG_LOG_DOMAIN=\"GLib-Net\"	\
+	$(GIO_CFLAGS)			\
+	$(LIBGNUTLS_CFLAGS)		\
+	$(LIBGCRYPT_CFLAGS)		\
+	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"	\
+	-DG_DISABLE_DEPRECATED
+
+libgiognutls_la_LDFLAGS = $(module_flags)
+libgiognutls_la_LIBADD =		\
+	$(GIO_LIBS)			\
+	$(GNUTLS_LIBS)			\
+	$(LIBGCRYPT_LIBS)		\
+	$(NULL)
diff --git a/tls/gnutls/gnutls-module.c b/tls/gnutls/gnutls-module.c
new file mode 100644
index 0000000..393757f
--- /dev/null
+++ b/tls/gnutls/gnutls-module.c
@@ -0,0 +1,47 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gtlsbackend-gnutls.h"
+
+
+void
+g_io_module_load (GIOModule *module)
+{
+  g_tls_backend_gnutls_register (module);
+}
+
+void
+g_io_module_unload (GIOModule *module)
+{
+}
+
+gchar **
+g_io_module_query (void)
+{
+  gchar *eps[] = {
+    G_TLS_BACKEND_EXTENSION_POINT_NAME,
+    NULL
+  };
+  return g_strdupv (eps);
+}
diff --git a/tls/gnutls/gtlsbackend-gnutls.c b/tls/gnutls/gtlsbackend-gnutls.c
new file mode 100644
index 0000000..fdf081c
--- /dev/null
+++ b/tls/gnutls/gtlsbackend-gnutls.c
@@ -0,0 +1,210 @@
+/* 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+
+#include <gnutls/gnutls.h>
+#include <gcrypt.h>
+#ifndef G_OS_WIN32
+#include <pthread.h>
+#endif
+
+#include "gtlsbackend-gnutls.h"
+#include "gtlscertificate-gnutls.h"
+#include "gtlsclientconnection-gnutls.h"
+#include "gtlsserverconnection-gnutls.h"
+
+static void gtls_gnutls_init (void);
+static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendGnutls, g_tls_backend_gnutls, G_TYPE_OBJECT, 0,
+				G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_TLS_BACKEND,
+							       g_tls_backend_gnutls_interface_init);
+				gtls_gnutls_init ();)
+
+#if defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) && !defined(G_OS_WIN32)
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
+#ifdef G_OS_WIN32
+
+static int
+gtls_gcry_win32_mutex_init (void **priv)
+{
+	int err = 0;
+	CRITICAL_SECTION *lock = (CRITICAL_SECTION*)malloc (sizeof (CRITICAL_SECTION));
+
+	if (!lock)
+		err = ENOMEM;
+	if (!err) {
+		InitializeCriticalSection (lock);
+		*priv = lock;
+	}
+	return err;
+}
+
+static int
+gtls_gcry_win32_mutex_destroy (void **lock)
+{
+	DeleteCriticalSection ((CRITICAL_SECTION*)*lock);
+	free (*lock);
+	return 0;
+}
+
+static int
+gtls_gcry_win32_mutex_lock (void **lock)
+{
+	EnterCriticalSection ((CRITICAL_SECTION*)*lock);
+	return 0;
+}
+
+static int
+gtls_gcry_win32_mutex_unlock (void **lock)
+{
+	LeaveCriticalSection ((CRITICAL_SECTION*)*lock);
+	return 0;
+}
+
+
+static struct gcry_thread_cbs gtls_gcry_threads_win32 = {		 \
+	(GCRY_THREAD_OPTION_USER | (GCRY_THREAD_OPTION_VERSION << 8)),	 \
+	NULL, gtls_gcry_win32_mutex_init, gtls_gcry_win32_mutex_destroy, \
+	gtls_gcry_win32_mutex_lock, gtls_gcry_win32_mutex_unlock,	 \
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+#endif
+
+static void
+gtls_gnutls_init (void)
+{
+#if defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) && !defined(G_OS_WIN32)
+  gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+#elif defined(G_OS_WIN32)
+  gcry_control (GCRYCTL_SET_THREAD_CBS, &gtls_gcry_threads_win32);
+#endif
+  gnutls_global_init ();
+}
+
+static void
+g_tls_backend_gnutls_init (GTlsBackendGnutls *backend)
+{
+}
+
+static void
+g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
+{
+}
+
+static void
+g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
+{
+}
+
+static void
+g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
+{
+  iface->get_certificate_type       = g_tls_certificate_gnutls_get_type;
+  iface->get_client_connection_type = g_tls_client_connection_gnutls_get_type;
+  iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
+}
+
+#ifdef GTLS_SYSTEM_CA_FILE
+/* Parsing the system CA list takes a noticeable amount of time.
+ * So we only do it once, and only when we actually need to see it.
+ */
+static const GList *
+get_ca_lists (gnutls_x509_crt_t **cas,
+	      int                *num_cas)
+{
+  static gnutls_x509_crt_t *ca_list_gnutls;
+  static int ca_list_length;
+  static GList *ca_list;
+
+  if (g_once_init_enter ((volatile gsize *)&ca_list_gnutls))
+    {
+      GError *error = NULL;
+      gnutls_x509_crt_t *x509_crts;
+      GList *c;
+      int i;
+
+      ca_list = g_tls_certificate_list_new_from_file (GTLS_SYSTEM_CA_FILE, &error);
+      if (error)
+	{
+	  g_warning ("Failed to read system CA file %s: %s.",
+		     GTLS_SYSTEM_CA_FILE, error->message);
+	  g_error_free (error);
+	  /* Note that this is not a security problem, since if
+	   * G_TLS_VALIDATE_CA is set, then this just means validation
+	   * will always fail, and if it isn't set, then it doesn't
+	   * matter that we couldn't read the CAs.
+	   */
+	}
+
+      ca_list_length = g_list_length (ca_list);
+      x509_crts = g_new (gnutls_x509_crt_t, ca_list_length);
+      for (c = ca_list, i = 0; c; c = c->next, i++)
+	x509_crts[i] = g_tls_certificate_gnutls_get_cert (c->data);
+
+      g_once_init_leave ((volatile gsize *)&ca_list_gnutls, GPOINTER_TO_SIZE (x509_crts));
+    }
+
+  if (cas)
+    *cas = ca_list_gnutls;
+  if (num_cas)
+    *num_cas = ca_list_length;
+  
+  return ca_list;
+}
+#endif
+
+const GList *
+g_tls_backend_gnutls_get_system_ca_list_gtls (void)
+{
+#ifdef GTLS_SYSTEM_CA_FILE
+  return get_ca_lists (NULL, NULL);
+#else
+  return NULL;
+#endif
+}
+
+void
+g_tls_backend_gnutls_get_system_ca_list_gnutls (gnutls_x509_crt_t **cas,
+						int                *num_cas)
+{
+#ifdef GTLS_SYSTEM_CA_FILE
+  get_ca_lists (cas, num_cas);
+#else
+  *cas = NULL;
+  *num_cas = 0;
+#endif
+}
+
+void
+g_tls_backend_gnutls_register (GIOModule *module)
+{
+  g_tls_backend_gnutls_register_type (G_TYPE_MODULE (module));
+  g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
+				  g_tls_backend_gnutls_get_type(),
+				  "gnutls",
+				  0);
+}
diff --git a/tls/gnutls/gtlsbackend-gnutls.h b/tls/gnutls/gtlsbackend-gnutls.h
new file mode 100644
index 0000000..04e664b
--- /dev/null
+++ b/tls/gnutls/gtlsbackend-gnutls.h
@@ -0,0 +1,50 @@
+/* GIO - GLib Backend, Output and Gnutlsing 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.
+ */
+
+#ifndef __G_TLS_BACKEND_GNUTLS_H__
+#define __G_TLS_BACKEND_GNUTLS_H__
+
+#include <gio/gio.h>
+#include <gnutls/gnutls.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_BACKEND_GNUTLS            (g_tls_backend_gnutls_get_type ())
+#define G_TLS_BACKEND_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutls))
+#define G_TLS_BACKEND_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsClass))
+#define G_IS_TLS_BACKEND_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_BACKEND_GNUTLS))
+#define G_IS_TLS_BACKEND_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_BACKEND_GNUTLS))
+#define G_TLS_BACKEND_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsClass))
+
+typedef struct _GTlsBackendGnutlsClass GTlsBackendGnutlsClass;
+typedef struct _GTlsBackendGnutls      GTlsBackendGnutls;
+
+struct _GTlsBackendGnutlsClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GTlsBackendGnutls
+{
+  GObject parent_instance;
+};
+
+GType g_tls_backend_gnutls_get_type (void) G_GNUC_CONST;
+void  g_tls_backend_gnutls_register (GIOModule *module);
+
+const GList *g_tls_backend_gnutls_get_system_ca_list_gtls   (void) G_GNUC_CONST;
+void         g_tls_backend_gnutls_get_system_ca_list_gnutls (gnutls_x509_crt_t **cas,
+							     int                *num_cas);
+
+G_END_DECLS
+
+#endif /* __G_TLS_BACKEND_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
new file mode 100644
index 0000000..93580c2
--- /dev/null
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -0,0 +1,362 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2009 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <string.h>
+
+#include "gtlscertificate-gnutls.h"
+#include <glib/gi18n-lib.h>
+
+static void g_tls_certificate_gnutls_get_property (GObject      *object,
+						   guint         prop_id,
+						   GValue       *value,
+						   GParamSpec   *pspec);
+static void g_tls_certificate_gnutls_set_property (GObject      *object,
+						   guint         prop_id,
+						   const GValue *value,
+						   GParamSpec   *pspec);
+static void g_tls_certificate_gnutls_finalize     (GObject      *object);
+
+static void     g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface);
+static gboolean g_tls_certificate_gnutls_initable_init       (GInitable       *initable,
+							      GCancellable    *cancellable,
+							      GError         **error);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsCertificateGnutls, g_tls_certificate_gnutls, G_TYPE_TLS_CERTIFICATE,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+						g_tls_certificate_gnutls_initable_iface_init);)
+
+enum
+{
+  PROP_0,
+
+  PROP_CERTIFICATE,
+  PROP_CERTIFICATE_PEM,
+  PROP_PRIVATE_KEY,
+  PROP_PRIVATE_KEY_PEM
+};
+
+struct _GTlsCertificateGnutlsPrivate
+{
+  gnutls_x509_crt_t cert;
+  gnutls_x509_privkey_t key;
+
+  GError *construct_error;
+
+  guint have_cert : 1;
+  guint have_key  : 1;
+};
+
+static void
+g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsCertificateGnutlsPrivate));
+
+  gobject_class->get_property = g_tls_certificate_gnutls_get_property;
+  gobject_class->set_property = g_tls_certificate_gnutls_set_property;
+  gobject_class->finalize     = g_tls_certificate_gnutls_finalize;
+
+  g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
+  g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem");
+  g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
+  g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
+}
+
+static void
+g_tls_certificate_gnutls_finalize (GObject *object)
+{
+  GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
+
+  gnutls_x509_crt_deinit (gnutls->priv->cert);
+  gnutls_x509_privkey_deinit (gnutls->priv->key);
+
+  g_clear_error (&gnutls->priv->construct_error);
+
+  G_OBJECT_CLASS (g_tls_certificate_gnutls_parent_class)->finalize (object);
+}
+
+static void
+g_tls_certificate_gnutls_get_property (GObject    *object,
+				       guint       prop_id,
+				       GValue     *value,
+				       GParamSpec *pspec)
+{
+  GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
+  GByteArray *certificate;
+  char *certificate_pem;
+  int status;
+  size_t size;
+
+  switch (prop_id)
+    {
+    case PROP_CERTIFICATE:
+      size = 0;
+      status = gnutls_x509_crt_export (gnutls->priv->cert,
+				       GNUTLS_X509_FMT_DER,
+				       NULL, &size);
+      if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
+	certificate = NULL;
+      else
+	{
+	  certificate = g_byte_array_sized_new (size);
+	  certificate->len = size;
+	  status = gnutls_x509_crt_export (gnutls->priv->cert,
+					   GNUTLS_X509_FMT_DER,
+					   certificate->data, &size);
+	  if (status != 0)
+	    {
+	      g_byte_array_free (certificate, TRUE);
+	      certificate = NULL;
+	    }
+	}
+      g_value_take_boxed (value, certificate);
+      break;
+
+    case PROP_CERTIFICATE_PEM:
+      size = 0;
+      status = gnutls_x509_crt_export (gnutls->priv->cert,
+				       GNUTLS_X509_FMT_PEM,
+				       NULL, &size);
+      if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
+	certificate_pem = NULL;
+      else
+	{
+	  certificate_pem = g_malloc (size);
+	  status = gnutls_x509_crt_export (gnutls->priv->cert,
+					   GNUTLS_X509_FMT_PEM,
+					   certificate_pem, &size);
+	  if (status != 0)
+	    {
+	      g_free (certificate_pem);
+	      certificate_pem = NULL;
+	    }
+	}
+      g_value_take_string (value, certificate_pem);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_certificate_gnutls_set_property (GObject      *object,
+				       guint         prop_id,
+				       const GValue *value,
+				       GParamSpec   *pspec)
+{
+  GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
+  GByteArray *bytes;
+  const char *string;
+  gnutls_datum_t data;
+  int status;
+
+  switch (prop_id)
+    {
+    case PROP_CERTIFICATE:
+      bytes = g_value_get_boxed (value);
+      if (!bytes)
+	break;
+      g_return_if_fail (gnutls->priv->have_cert == FALSE);
+      data.data = bytes->data;
+      data.size = bytes->len;
+      status = gnutls_x509_crt_import (gnutls->priv->cert, &data,
+				       GNUTLS_X509_FMT_DER);
+      if (status == 0)
+	gnutls->priv->have_cert = TRUE;
+      else if (!gnutls->priv->construct_error)
+	{
+	  gnutls->priv->construct_error =
+	    g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			 _("Could not parse DER certificate: %s"),
+			 gnutls_strerror (status));
+	}
+
+      break;
+
+    case PROP_CERTIFICATE_PEM:
+      string = g_value_get_string (value);
+      if (!string)
+	break;
+      g_return_if_fail (gnutls->priv->have_cert == FALSE);
+      data.data = (void *)string;
+      data.size = strlen (string);
+      status = gnutls_x509_crt_import (gnutls->priv->cert, &data,
+				       GNUTLS_X509_FMT_PEM);
+      if (status == 0)
+	gnutls->priv->have_cert = TRUE;
+      else if (!gnutls->priv->construct_error)
+	{
+	  gnutls->priv->construct_error =
+	    g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			 _("Could not parse PEM certificate: %s"),
+			 gnutls_strerror (status));
+	}
+      break;
+
+    case PROP_PRIVATE_KEY:
+      bytes = g_value_get_boxed (value);
+      if (!bytes)
+	break;
+      g_return_if_fail (gnutls->priv->have_key == FALSE);
+      data.data = bytes->data;
+      data.size = bytes->len;
+      status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
+					   GNUTLS_X509_FMT_DER);
+      if (status == 0)
+	gnutls->priv->have_key = TRUE;
+      else if (!gnutls->priv->construct_error)
+	{
+	  gnutls->priv->construct_error =
+	    g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			 _("Could not parse DER private key: %s"),
+			 gnutls_strerror (status));
+	}
+      break;
+
+    case PROP_PRIVATE_KEY_PEM:
+      string = g_value_get_string (value);
+      if (!string)
+	break;
+      g_return_if_fail (gnutls->priv->have_key == FALSE);
+      data.data = (void *)string;
+      data.size = strlen (string);
+      status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
+					   GNUTLS_X509_FMT_PEM);
+      if (status == 0)
+	gnutls->priv->have_key = TRUE;
+      else if (!gnutls->priv->construct_error)
+	{
+	  gnutls->priv->construct_error =
+	    g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			 _("Could not parse PEM private key: %s"),
+			 gnutls_strerror (status));
+	}
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls)
+{
+  gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls,
+					      G_TYPE_TLS_CERTIFICATE_GNUTLS,
+					      GTlsCertificateGnutlsPrivate);
+
+  gnutls_x509_crt_init (&gnutls->priv->cert);
+  gnutls_x509_privkey_init (&gnutls->priv->key);
+}
+
+static void
+g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface)
+{
+  iface->init = g_tls_certificate_gnutls_initable_init;
+}
+
+static gboolean
+g_tls_certificate_gnutls_initable_init (GInitable       *initable,
+					GCancellable    *cancellable,
+					GError         **error)
+{
+  GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (initable);
+
+  if (gnutls->priv->construct_error)
+    {
+      g_propagate_error (error, gnutls->priv->construct_error);
+      gnutls->priv->construct_error = NULL;
+      return FALSE;
+    }
+  else if (!gnutls->priv->have_cert)
+    {
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			   _("No certificate data provided"));
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+GTlsCertificate *
+g_tls_certificate_gnutls_new (const gnutls_datum *datum,
+			      GTlsCertificate    *issuer)
+{
+  GTlsCertificateGnutls *gnutls;
+
+  gnutls = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS,
+			 "issuer", issuer,
+			 NULL);
+  if (gnutls_x509_crt_import (gnutls->priv->cert, datum,
+			      GNUTLS_X509_FMT_DER) == 0)
+    gnutls->priv->have_cert = TRUE;
+
+  return G_TLS_CERTIFICATE (gnutls);
+}
+
+const gnutls_x509_crt_t
+g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls)
+{
+  return gnutls->priv->cert;
+}
+
+const gnutls_x509_privkey_t
+g_tls_certificate_gnutls_get_key (GTlsCertificateGnutls *gnutls)
+{
+  return gnutls->priv->key;
+}
+
+gnutls_x509_crt_t
+g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls)
+{
+  gnutls_x509_crt_t cert;
+  gnutls_datum data;
+  size_t size;
+
+  size = 0;
+  gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER,
+			  NULL, &size);
+  data.data = g_malloc (size);
+  data.size = size;
+  gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER,
+			  data.data, &size);
+
+  gnutls_x509_crt_init (&cert);
+  gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER);
+  g_free (data.data);
+
+  return cert;
+}
+
+gnutls_x509_privkey_t
+g_tls_certificate_gnutls_copy_key  (GTlsCertificateGnutls *gnutls)
+{
+  gnutls_x509_privkey_t key;
+
+  gnutls_x509_privkey_init (&key);
+  gnutls_x509_privkey_cpy (key, gnutls->priv->key);
+  return key;
+}
diff --git a/tls/gnutls/gtlscertificate-gnutls.h b/tls/gnutls/gtlscertificate-gnutls.h
new file mode 100644
index 0000000..32f9058
--- /dev/null
+++ b/tls/gnutls/gtlscertificate-gnutls.h
@@ -0,0 +1,55 @@
+/* GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright © 2009 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.
+ */
+
+#ifndef __G_TLS_CERTIFICATE_GNUTLS_H__
+#define __G_TLS_CERTIFICATE_GNUTLS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CERTIFICATE_GNUTLS            (g_tls_certificate_gnutls_get_type ())
+#define G_TLS_CERTIFICATE_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CERTIFICATE_GNUTLS, GTlsCertificateGnutls))
+#define G_TLS_CERTIFICATE_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CERTIFICATE_GNUTLS, GTlsCertificateGnutlsClass))
+#define G_IS_TLS_CERTIFICATE_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CERTIFICATE_GNUTLS))
+#define G_IS_TLS_CERTIFICATE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CERTIFICATE_GNUTLS))
+#define G_TLS_CERTIFICATE_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CERTIFICATE_GNUTLS, GTlsCertificateGnutlsClass))
+
+typedef struct _GTlsCertificateGnutlsPrivate                   GTlsCertificateGnutlsPrivate;
+typedef struct _GTlsCertificateGnutlsClass                     GTlsCertificateGnutlsClass;
+typedef struct _GTlsCertificateGnutls                          GTlsCertificateGnutls;
+
+struct _GTlsCertificateGnutlsClass
+{
+  GTlsCertificateClass parent_class;
+};
+
+struct _GTlsCertificateGnutls
+{
+  GTlsCertificate parent_instance;
+  GTlsCertificateGnutlsPrivate *priv;
+};
+
+GType g_tls_certificate_gnutls_get_type (void) G_GNUC_CONST;
+
+GTlsCertificate *            g_tls_certificate_gnutls_new       (const gnutls_datum    *datum,
+								 GTlsCertificate       *issuer);
+
+const gnutls_x509_crt_t      g_tls_certificate_gnutls_get_cert  (GTlsCertificateGnutls *gnutls);
+const gnutls_x509_privkey_t  g_tls_certificate_gnutls_get_key   (GTlsCertificateGnutls *gnutls);
+
+gnutls_x509_crt_t            g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls);
+gnutls_x509_privkey_t        g_tls_certificate_gnutls_copy_key  (GTlsCertificateGnutls *gnutls);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CERTIFICATE_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
new file mode 100644
index 0000000..ebe871f
--- /dev/null
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -0,0 +1,310 @@
+/* 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <string.h>
+
+#include "gtlsclientconnection-gnutls.h"
+#include <glib/gi18n-lib.h>
+
+enum
+{
+  PROP_0,
+  PROP_VALIDATION_FLAGS,
+  PROP_SERVER_IDENTITY,
+  PROP_USE_SSL3,
+  PROP_ACCEPTED_CAS
+};
+
+static void g_tls_client_connection_gnutls_get_property (GObject    *object,
+							 guint       prop_id,
+							 GValue     *value,
+							 GParamSpec *pspec);
+static void g_tls_client_connection_gnutls_set_property (GObject      *object,
+							 guint         prop_id,
+							 const GValue *value,
+							 GParamSpec   *pspec);
+static void g_tls_client_connection_gnutls_finalize     (GObject      *object);
+
+static void     g_tls_client_connection_gnutls_begin_handshake  (GTlsConnectionGnutls  *conn);
+static gboolean g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
+								 gboolean               success,
+								 GError               **inout_error);
+
+static void g_tls_client_connection_gnutls_client_connection_interface_init (GTlsClientConnectionInterface *iface);
+
+static int g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t             session,
+							     const gnutls_datum_t        *req_ca_rdn,
+							     int                          nreqs,
+							     const gnutls_pk_algorithm_t *pk_algos,
+							     int                          pk_algos_length,
+							     gnutls_retr_st              *st);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionGnutls, g_tls_client_connection_gnutls, G_TYPE_TLS_CONNECTION_GNUTLS,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION,
+						g_tls_client_connection_gnutls_client_connection_interface_init));
+
+struct _GTlsClientConnectionGnutlsPrivate
+{
+  GTlsCertificateFlags validation_flags;
+  GSocketConnectable *server_identity;
+  gboolean use_ssl3;
+
+  gboolean cert_requested;
+  char **accepted_cas;
+};
+
+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);
+
+  g_type_class_add_private (klass, sizeof (GTlsClientConnectionGnutlsPrivate));
+
+  gobject_class->get_property = g_tls_client_connection_gnutls_get_property;
+  gobject_class->set_property = g_tls_client_connection_gnutls_set_property;
+  gobject_class->finalize     = g_tls_client_connection_gnutls_finalize;
+
+  connection_gnutls_class->begin_handshake = g_tls_client_connection_gnutls_begin_handshake;
+  connection_gnutls_class->finish_handshake = g_tls_client_connection_gnutls_finish_handshake;
+
+  g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
+  g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
+  g_object_class_override_property (gobject_class, PROP_USE_SSL3, "use-ssl3");
+  g_object_class_override_property (gobject_class, PROP_ACCEPTED_CAS, "accepted-cas");
+}
+
+static void
+g_tls_client_connection_gnutls_client_connection_interface_init (GTlsClientConnectionInterface *iface)
+{
+}
+
+static void
+g_tls_client_connection_gnutls_init (GTlsClientConnectionGnutls *gnutls)
+{
+  gnutls_certificate_credentials_t creds;
+
+  gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS, GTlsClientConnectionGnutlsPrivate);
+
+  creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
+  gnutls_certificate_client_set_retrieve_function (creds, g_tls_client_connection_gnutls_retrieve_function);
+}
+
+static void
+g_tls_client_connection_gnutls_finalize (GObject *object)
+{
+  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object);
+
+  if (gnutls->priv->server_identity)
+    g_object_unref (gnutls->priv->server_identity);
+  if (gnutls->priv->accepted_cas)
+    g_strfreev (gnutls->priv->accepted_cas);
+
+  G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->finalize (object);
+}
+
+static void
+g_tls_client_connection_gnutls_get_property (GObject    *object,
+					     guint       prop_id,
+					     GValue     *value,
+					     GParamSpec *pspec)
+{
+  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_VALIDATION_FLAGS:
+      g_value_set_flags (value, gnutls->priv->validation_flags);
+      break;
+
+    case PROP_SERVER_IDENTITY:
+      g_value_set_object (value, gnutls->priv->server_identity);
+      break;
+
+    case PROP_USE_SSL3:
+      g_value_set_boolean (value, gnutls->priv->use_ssl3);
+      break;
+
+    case PROP_ACCEPTED_CAS:
+      g_value_set_boxed (value, gnutls->priv->accepted_cas);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_client_connection_gnutls_set_property (GObject      *object,
+					     guint         prop_id,
+					     const GValue *value,
+					     GParamSpec   *pspec)
+{
+  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_VALIDATION_FLAGS:
+      gnutls->priv->validation_flags = g_value_get_flags (value);
+      break;
+
+    case PROP_SERVER_IDENTITY:
+      if (gnutls->priv->server_identity)
+	g_object_unref (gnutls->priv->server_identity);
+      gnutls->priv->server_identity = g_value_dup_object (value);
+
+      if (G_IS_NETWORK_ADDRESS (gnutls->priv->server_identity))
+	{
+	  const char *hostname;
+	  gnutls_session_t session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (gnutls));
+
+	  hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (gnutls->priv->server_identity));
+	  gnutls_server_name_set (session, GNUTLS_NAME_DNS,
+				  hostname, strlen (hostname));
+	}
+      break;
+
+    case PROP_USE_SSL3:
+      gnutls->priv->use_ssl3 = g_value_get_boolean (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static int
+g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t             session,
+						  const gnutls_datum_t        *req_ca_rdn,
+						  int                          nreqs,
+						  const gnutls_pk_algorithm_t *pk_algos,
+						  int                          pk_algos_length,
+						  gnutls_retr_st              *st)
+{
+  GTlsClientConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
+  GPtrArray *accepted_cas;
+  int i;
+  char *buf, dummy[1];
+  size_t size;
+
+  gnutls->priv->cert_requested = TRUE;
+
+  accepted_cas = g_ptr_array_new ();
+  for (i = 0; i < nreqs; i++)
+    {
+      size = sizeof (dummy);
+      gnutls_x509_rdn_get (&req_ca_rdn[i], dummy, &size);
+      buf = g_malloc (size);
+      gnutls_x509_rdn_get (&req_ca_rdn[i], buf, &size);
+      g_ptr_array_add (accepted_cas, buf);
+    }
+  g_ptr_array_add (accepted_cas, NULL);
+
+  gnutls->priv->accepted_cas = (char **)accepted_cas->pdata;
+  g_ptr_array_free (accepted_cas, FALSE);
+
+  g_tls_connection_gnutls_get_certificate (G_TLS_CONNECTION_GNUTLS (gnutls), st);
+  return 0;
+}
+
+static gboolean
+validate_handshake (GTlsClientConnectionGnutls *gnutls)
+{
+  GTlsCertificateFlags errors;
+  gboolean accepted;
+
+  errors = g_tls_connection_gnutls_validate_peer (G_TLS_CONNECTION_GNUTLS (gnutls));
+
+  /* FIXME: implement the full hostname/servicename/URI check
+   * according to draft-saintandre-tls-server-id-check
+   */
+  if ((gnutls->priv->validation_flags & G_TLS_CERTIFICATE_BAD_IDENTITY) &&
+      gnutls->priv->server_identity &&
+      G_IS_NETWORK_ADDRESS (gnutls->priv->server_identity))
+    {
+      gnutls_session session;
+      gnutls_x509_crt x509_cert;
+      const gnutls_datum_t *certs;
+      const char *hostname;
+      unsigned int num_certs;
+
+      session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (gnutls));
+      hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (gnutls->priv->server_identity));
+
+      gnutls_x509_crt_init (&x509_cert);
+      certs = gnutls_certificate_get_peers (session, &num_certs);
+      gnutls_x509_crt_import (x509_cert, &certs[0], GNUTLS_X509_FMT_DER);
+      if (!gnutls_x509_crt_check_hostname (x509_cert, hostname))
+	errors |= G_TLS_CERTIFICATE_BAD_IDENTITY;
+      gnutls_x509_crt_deinit (x509_cert);
+    }
+
+  errors &= gnutls->priv->validation_flags;
+  if (errors)
+    {
+      GTlsCertificate *peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (gnutls));
+      accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls), peer, errors);
+    }
+  else
+    accepted = TRUE;
+
+  return accepted;
+}
+
+static void
+g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+{
+  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+
+  gnutls->priv->cert_requested = FALSE;
+}
+
+static gboolean
+g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
+						 gboolean               success,
+						 GError               **inout_error)
+{
+  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+
+  if (success)
+    {
+      if (validate_handshake (gnutls))
+	return TRUE;
+
+      g_set_error_literal (inout_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			   _("Unacceptable TLS certificate"));
+      return FALSE;
+    }
+
+  if (g_error_matches (*inout_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS) &&
+      gnutls->priv->cert_requested)
+    {
+      g_clear_error (inout_error);
+      g_set_error_literal (inout_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+			   _("Server required TLS certificate"));
+    }
+  return FALSE;
+}
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.h b/tls/gnutls/gtlsclientconnection-gnutls.h
new file mode 100644
index 0000000..d56f9c8
--- /dev/null
+++ b/tls/gnutls/gtlsclientconnection-gnutls.h
@@ -0,0 +1,46 @@
+/* GIO - GLib ClientConnection, Output and Gnutlsing 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.
+ */
+
+#ifndef __G_TLS_CLIENT_CONNECTION_GNUTLS_H__
+#define __G_TLS_CLIENT_CONNECTION_GNUTLS_H__
+
+#include "gtlsconnection-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS            (g_tls_client_connection_gnutls_get_type ())
+#define G_TLS_CLIENT_CONNECTION_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS, GTlsClientConnectionGnutls))
+#define G_TLS_CLIENT_CONNECTION_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS, GTlsClientConnectionGnutlsClass))
+#define G_IS_TLS_CLIENT_CONNECTION_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS))
+#define G_IS_TLS_CLIENT_CONNECTION_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS))
+#define G_TLS_CLIENT_CONNECTION_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS, GTlsClientConnectionGnutlsClass))
+
+typedef struct _GTlsClientConnectionGnutlsPrivate GTlsClientConnectionGnutlsPrivate;
+typedef struct _GTlsClientConnectionGnutlsClass   GTlsClientConnectionGnutlsClass;
+typedef struct _GTlsClientConnectionGnutls        GTlsClientConnectionGnutls;
+
+struct _GTlsClientConnectionGnutlsClass
+{
+  GTlsConnectionGnutlsClass parent_class;
+};
+
+struct _GTlsClientConnectionGnutls
+{
+  GTlsConnectionGnutls parent_instance;
+  GTlsClientConnectionGnutlsPrivate *priv;
+};
+
+GType g_tls_client_connection_gnutls_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_CLIENT_CONNECTION_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
new file mode 100644
index 0000000..2c28050
--- /dev/null
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -0,0 +1,1154 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2009 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "gtlsconnection-gnutls.h"
+#include "gtlsbackend-gnutls.h"
+#include "gtlscertificate-gnutls.h"
+#include "gtlsinputstream-gnutls.h"
+#include "gtlsoutputstream-gnutls.h"
+#include "gtlsserverconnection-gnutls.h"
+#include <glib/gi18n-lib.h>
+
+static void g_tls_connection_gnutls_get_property (GObject    *object,
+						  guint       prop_id,
+						  GValue     *value,
+						  GParamSpec *pspec);
+static void g_tls_connection_gnutls_set_property (GObject      *object,
+						  guint         prop_id,
+						  const GValue *value,
+						  GParamSpec   *pspec);
+static void g_tls_connection_gnutls_finalize     (GObject      *object);
+
+static gboolean     g_tls_connection_gnutls_handshake        (GTlsConnection       *connection,
+							      GCancellable         *cancellable,
+							      GError              **error);
+static void         g_tls_connection_gnutls_handshake_async  (GTlsConnection       *conn,
+							      int                   io_priority,
+							      GCancellable         *cancellable,
+							      GAsyncReadyCallback   callback,
+							      gpointer              user_data);
+static gboolean     g_tls_connection_gnutls_handshake_finish (GTlsConnection       *conn,
+							      GAsyncResult         *result,
+							      GError              **error);
+
+static GInputStream  *g_tls_connection_gnutls_get_input_stream  (GIOStream *stream);
+static GOutputStream *g_tls_connection_gnutls_get_output_stream (GIOStream *stream);
+
+static gboolean     g_tls_connection_gnutls_close        (GIOStream           *stream,
+							  GCancellable        *cancellable,
+							  GError             **error);
+static void         g_tls_connection_gnutls_close_async  (GIOStream           *stream,
+							  int                  io_priority,
+							  GCancellable        *cancellable,
+							  GAsyncReadyCallback  callback,
+							  gpointer             user_data);
+static gboolean     g_tls_connection_gnutls_close_finish (GIOStream           *stream,
+							  GAsyncResult        *result,
+							  GError             **error);
+
+static ssize_t g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
+						  const void             *buf,
+						  size_t                  buflen);
+static ssize_t g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
+						  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);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls, G_TYPE_TLS_CONNECTION,
+				  G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+							 g_tls_connection_gnutls_initable_iface_init));
+
+
+enum
+{
+  PROP_0,
+  PROP_BASE_IO_STREAM,
+  PROP_REQUIRE_CLOSE_NOTIFY,
+  PROP_REHANDSHAKE_MODE
+};
+
+struct _GTlsConnectionGnutlsPrivate
+{
+  GIOStream *base_io_stream;
+  GPollableInputStream *base_istream;
+  GPollableOutputStream *base_ostream;
+
+  GList *ca_list;
+  gnutls_certificate_credentials creds;
+  gnutls_session session;
+  gboolean require_close_notify;
+  GTlsRehandshakeMode rehandshake_mode;
+  gboolean need_handshake, handshaking, ever_handshaked;
+  gboolean closing;
+
+  GInputStream *tls_istream;
+  GOutputStream *tls_ostream;
+
+  GError *error;
+  GCancellable *cancellable;
+  gboolean blocking, eof;
+  GIOCondition internal_direction;
+};
+
+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);
+
+  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");
+}
+
+static void
+g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = g_tls_connection_gnutls_initable_init;
+}
+
+static void
+g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
+{
+  gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_CONNECTION_GNUTLS, GTlsConnectionGnutlsPrivate);
+
+  gnutls_certificate_allocate_credentials (&gnutls->priv->creds);
+  gnutls_certificate_set_verify_flags (gnutls->priv->creds,
+				       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+
+  gnutls->priv->need_handshake = TRUE;
+}
+
+static gboolean
+g_tls_connection_gnutls_initable_init (GInitable     *initable,
+				       GCancellable  *cancellable,
+				       GError       **error)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
+  gboolean client, use_ssl3 = FALSE;
+  gnutls_x509_crt_t *cas;
+  int num_cas;
+  int status;
+
+  g_return_val_if_fail (gnutls->priv->base_istream != NULL &&
+			gnutls->priv->base_ostream != NULL, FALSE);
+
+  /* Make sure gnutls->priv->session has been initialized (it may have
+   * already been initialized by a construct-time property setter).
+   */
+  g_tls_connection_gnutls_get_session (gnutls);
+
+  client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
+  if (client)
+      g_object_get (G_OBJECT (gnutls), "use-ssl3", &use_ssl3, NULL);
+  if (use_ssl3)
+    {
+      status = gnutls_priority_set_direct (gnutls->priv->session,
+					   "NORMAL:!VERS-TLS1.2:!VERS-TLS1.1:!VERS-TLS1.0",
+					   NULL);
+    }
+  else
+    {
+      status = gnutls_priority_set_direct (gnutls->priv->session,
+					   "NORMAL", NULL);
+    }
+  if (status != 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+		   _("Could not create TLS connection: %s"),
+		   gnutls_strerror (status));
+      return FALSE;
+    }
+
+  g_tls_backend_gnutls_get_system_ca_list_gnutls (&cas, &num_cas);
+  gnutls_certificate_set_x509_trust (gnutls->priv->creds, cas, num_cas);
+
+  status = gnutls_credentials_set (gnutls->priv->session,
+				   GNUTLS_CRD_CERTIFICATE,
+				   gnutls->priv->creds);
+  if (status != 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+		   _("Could not create TLS connection: %s"),
+		   gnutls_strerror (status));
+      return FALSE;
+    }
+
+  gnutls_transport_set_push_function (gnutls->priv->session,
+				      g_tls_connection_gnutls_push_func);
+  gnutls_transport_set_pull_function (gnutls->priv->session,
+				      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;
+}
+
+static void
+g_tls_connection_gnutls_finalize (GObject *object)
+{
+  GTlsConnectionGnutls *connection = G_TLS_CONNECTION_GNUTLS (object);
+
+  if (connection->priv->base_io_stream)
+    g_object_unref (connection->priv->base_io_stream);
+
+  if (connection->priv->session)
+    gnutls_deinit (connection->priv->session);
+
+  if (connection->priv->tls_istream)
+    g_object_unref (connection->priv->tls_istream);
+  if (connection->priv->tls_ostream) 
+    g_object_unref (connection->priv->tls_ostream);
+
+  if (connection->priv->creds)
+    gnutls_certificate_free_credentials (connection->priv->creds);
+
+  if (connection->priv->error)
+    g_error_free (connection->priv->error);
+
+  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);
+
+  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;
+
+    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);
+  GList *list, *copy, *c;
+  GInputStream *istream;
+  GOutputStream *ostream;
+
+  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;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+gnutls_certificate_credentials
+g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *gnutls)
+{
+  return gnutls->priv->creds;
+}
+
+gnutls_session
+g_tls_connection_gnutls_get_session (GTlsConnectionGnutls *gnutls)
+{
+  /* Ideally we would initialize gnutls->priv->session from
+   * g_tls_connection_gnutls_init(), but we can't tell if it's a
+   * client or server connection at that point... And
+   * g_tls_connection_gnutls_initiable_init() is too late, because
+   * construct-time property setters may need to modify it.
+   */
+  if (!gnutls->priv->session)
+    {
+      gboolean client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
+      gnutls_init (&gnutls->priv->session, client ? GNUTLS_CLIENT : GNUTLS_SERVER);
+    }
+
+  return gnutls->priv->session;
+}
+
+void
+g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
+					 gnutls_retr_st       *st)
+{
+  GTlsCertificate *cert;
+
+  cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (gnutls));
+  if (cert)
+    g_object_ref (cert);
+  else
+    cert = g_tls_connection_emit_need_certificate (G_TLS_CONNECTION (gnutls));
+
+  st->type = GNUTLS_CRT_X509;
+  if (cert)
+    {
+      GTlsCertificateGnutls *gnutlscert = G_TLS_CERTIFICATE_GNUTLS (cert);
+
+      st->ncerts = 1;
+      st->cert.x509 = gnutls_malloc (sizeof (gnutls_x509_crt_t));
+      st->cert.x509[0] = g_tls_certificate_gnutls_copy_cert (gnutlscert);
+      st->key.x509 = g_tls_certificate_gnutls_copy_key (gnutlscert);
+      st->deinit_all = TRUE;
+
+      g_object_unref (cert);
+    }
+  else
+    st->ncerts = 0;
+}
+
+static const struct {
+  int gnutls_flag;
+  GTlsCertificateFlags gtls_flag;
+} flags_map[] = {
+  { GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_SIGNER_NOT_CA, G_TLS_CERTIFICATE_UNKNOWN_CA },
+  { GNUTLS_CERT_NOT_ACTIVATED, G_TLS_CERTIFICATE_NOT_ACTIVATED },
+  { GNUTLS_CERT_EXPIRED, G_TLS_CERTIFICATE_EXPIRED },
+  { GNUTLS_CERT_REVOKED, G_TLS_CERTIFICATE_REVOKED },
+  { GNUTLS_CERT_INSECURE_ALGORITHM, G_TLS_CERTIFICATE_INSECURE }
+};
+static const int flags_map_size = G_N_ELEMENTS (flags_map);
+
+GTlsCertificateFlags
+g_tls_connection_gnutls_validate_peer (GTlsConnectionGnutls *gnutls)
+{
+  int status, i;
+  GTlsCertificateFlags gtls_errors;
+
+  status = gnutls_certificate_verify_peers (gnutls->priv->session);
+
+  /* Convert GNUTLS status to GTlsCertificateFlags. GNUTLS sets
+   * GNUTLS_CERT_INVALID if it sets any other flag, so we want to
+   * strip that out unless it's the only flag set. Then we convert
+   * specific flags we recognize, and if there are any flags left over
+   * at the end, we add G_TLS_CERTIFICATE_GENERIC_ERROR.
+   */
+  gtls_errors = 0;
+
+  if (status != GNUTLS_CERT_INVALID)
+    status = status & ~GNUTLS_CERT_INVALID;
+  for (i = 0; i < flags_map_size && status != 0; i++)
+    {
+      if (status & flags_map[i].gnutls_flag)
+	{
+	  status &= ~flags_map[i].gnutls_flag;
+	  gtls_errors |= flags_map[i].gtls_flag;
+	}
+    }
+  if (status)
+    gtls_errors |= G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+  return gtls_errors;
+}
+
+void
+begin_gnutls_io (GTlsConnectionGnutls  *gnutls,
+		 gboolean               blocking,
+		 GCancellable          *cancellable)
+{
+  gnutls->priv->blocking = blocking;
+  gnutls->priv->cancellable = cancellable;
+  gnutls->priv->internal_direction = 0;
+  g_clear_error (&gnutls->priv->error);
+}
+
+int
+end_gnutls_io (GTlsConnectionGnutls  *gnutls,
+	       int                    status,
+	       const char            *generic_error,
+	       GError               **error)
+{
+  gnutls->priv->cancellable = NULL;
+
+  if (status >= 0)
+    {
+      g_clear_error (&gnutls->priv->error);
+      return status;
+    }
+
+  if (gnutls->priv->handshaking && !gnutls->priv->ever_handshaked)
+    {
+      if (g_error_matches (gnutls->priv->error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
+	  status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
+	  status == GNUTLS_E_FATAL_ALERT_RECEIVED ||
+	  status == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
+	{
+	  g_clear_error (&gnutls->priv->error);
+	  g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+			       _("Peer failed to perform TLS handshake"));
+	  return GNUTLS_E_PULL_ERROR;
+	}
+    }
+
+  if (gnutls->priv->error)
+    {
+      if (g_error_matches (gnutls->priv->error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+	status = GNUTLS_E_AGAIN;
+      g_propagate_error (error, gnutls->priv->error);
+      gnutls->priv->error = NULL;
+      return status;
+    }
+  else if (status == GNUTLS_E_REHANDSHAKE)
+    {
+      gnutls->priv->need_handshake = TRUE;
+      return status;
+    }
+  else if (status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
+    {
+      if (gnutls->priv->eof)
+	{
+	  if (gnutls->priv->require_close_notify)
+	    {
+	      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_EOF,
+				   _("TLS connection closed unexpectedly"));
+	      return status;
+	    }
+	  else
+	    return 0;
+	}
+    }
+
+  g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+	       generic_error, gnutls_strerror (status));
+  return status;
+}
+
+#define BEGIN_GNUTLS_IO(gnutls, blocking, cancellable)	\
+  begin_gnutls_io (gnutls, blocking, cancellable);	\
+  do {
+
+#define END_GNUTLS_IO(gnutls, ret, errmsg, error)	\
+  } while ((ret == GNUTLS_E_AGAIN ||			\
+            ret == GNUTLS_E_WARNING_ALERT_RECEIVED) &&	\
+           !gnutls->priv->error);			\
+  ret = end_gnutls_io (gnutls, ret, errmsg, error)
+
+gboolean
+g_tls_connection_gnutls_check (GTlsConnectionGnutls  *gnutls,
+			       GIOCondition           condition)
+{
+  if (gnutls->priv->handshaking || gnutls->priv->closing)
+    condition = gnutls->priv->internal_direction;
+
+  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          base_direction;
+  GIOCondition          current_direction;
+} 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_child_source (GTlsConnectionGnutlsSource *gnutls_source,
+				 GIOCondition                direction)
+{
+  GTlsConnectionGnutls *gnutls = gnutls_source->gnutls;
+  GSource *source = (GSource *)gnutls_source;
+
+  if (direction == gnutls_source->current_direction)
+    return;
+
+  if (gnutls_source->child_source)
+    {
+      g_source_remove_child_source (source, gnutls_source->child_source);
+      g_source_unref (gnutls_source->child_source);
+    }
+
+  if (direction & G_IO_IN)
+    gnutls_source->child_source = g_pollable_input_stream_create_source (gnutls->priv->base_istream, NULL);
+  else
+    gnutls_source->child_source = g_pollable_output_stream_create_source (gnutls->priv->base_ostream, NULL);
+
+  g_source_set_dummy_callback (gnutls_source->child_source);
+  g_source_add_child_source (source, gnutls_source->child_source);
+  gnutls_source->current_direction = direction;
+}
+
+static gboolean
+gnutls_source_dispatch (GSource     *source,
+			GSourceFunc  callback,
+			gpointer     user_data)
+{
+  GPollableSourceFunc func = (GPollableSourceFunc)callback;
+  GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
+  GTlsConnectionGnutls *gnutls = gnutls_source->gnutls;
+  gboolean ret;
+
+  ret = (*func) (gnutls_source->stream, user_data);
+  if (ret)
+    {
+      GIOCondition direction = gnutls->priv->internal_direction ? gnutls->priv->internal_direction : gnutls_source->base_direction;
+
+      gnutls_source_sync_child_source (gnutls_source, direction);
+    }
+
+  return ret;
+}
+
+static void
+gnutls_source_finalize (GSource *source)
+{
+  GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
+
+  g_object_unref (gnutls_source->gnutls);
+
+  if (gnutls_source->child_source)
+    g_source_unref (gnutls_source->child_source);
+}
+
+static GSourceFuncs gnutls_source_funcs =
+{
+  gnutls_source_prepare,
+  gnutls_source_check,
+  gnutls_source_dispatch,
+  gnutls_source_finalize
+};
+
+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->base_direction = condition & (G_IO_IN | G_IO_OUT);
+  if (gnutls_source->base_direction == G_IO_IN)
+    gnutls_source->stream = G_OBJECT (gnutls->priv->tls_istream);
+  else if (gnutls_source->base_direction == G_IO_OUT)
+    gnutls_source->stream = G_OBJECT (gnutls->priv->tls_ostream);
+  else
+    {
+      gnutls_source->base_direction = gnutls->priv->internal_direction;
+      gnutls_source->stream = NULL;
+    }
+  gnutls_source_sync_child_source (gnutls_source, gnutls_source->base_direction);
+
+  if (cancellable)
+    {
+      cancellable_source = g_cancellable_source_new (cancellable);
+      g_source_set_dummy_callback (cancellable_source);
+      g_source_add_child_source (source, cancellable_source);
+      g_source_unref (cancellable_source);
+    }
+
+  return source;
+}
+
+static void
+set_gnutls_error (GTlsConnectionGnutls *gnutls, GIOCondition direction)
+{
+  if (g_error_matches (gnutls->priv->error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+    gnutls_transport_set_errno (gnutls->priv->session, EINTR);
+  else if (g_error_matches (gnutls->priv->error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      gnutls_transport_set_errno (gnutls->priv->session, EAGAIN);
+      gnutls->priv->internal_direction = direction;
+    }
+  else
+    gnutls_transport_set_errno (gnutls->priv->session, EIO);
+}
+
+static ssize_t
+g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
+				   void                   *buf,
+				   size_t                  buflen)
+{
+  GTlsConnectionGnutls *gnutls = transport_data;
+  ssize_t ret;
+
+  /* If gnutls->priv->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->error);
+
+  if (gnutls->priv->blocking)
+    {
+      ret = g_input_stream_read (G_INPUT_STREAM (gnutls->priv->base_istream),
+				 buf, buflen,
+				 gnutls->priv->cancellable,
+				 &gnutls->priv->error);
+    }
+  else
+    {
+      ret = g_pollable_input_stream_read_nonblocking (gnutls->priv->base_istream,
+						      buf, buflen,
+						      gnutls->priv->cancellable,
+						      &gnutls->priv->error);
+    }
+
+  if (ret < 0)
+    set_gnutls_error (gnutls, G_IO_IN);
+  else if (ret == 0)
+    gnutls->priv->eof = TRUE;
+
+  return ret;
+}
+
+static ssize_t
+g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
+				   const void             *buf,
+				   size_t                  buflen)
+{
+  GTlsConnectionGnutls *gnutls = transport_data;
+  ssize_t ret;
+
+  /* See comment in pull_func. */
+  g_clear_error (&gnutls->priv->error);
+
+  if (gnutls->priv->blocking)
+    {
+      ret = g_output_stream_write (G_OUTPUT_STREAM (gnutls->priv->base_ostream),
+				   buf, buflen,
+				   gnutls->priv->cancellable,
+				   &gnutls->priv->error);
+    }
+  else
+    {
+      ret = g_pollable_output_stream_write_nonblocking (gnutls->priv->base_ostream,
+							buf, buflen,
+							gnutls->priv->cancellable,
+							&gnutls->priv->error);
+    }
+  if (ret < 0)
+    set_gnutls_error (gnutls, G_IO_OUT);
+
+  return ret;
+}
+
+static gboolean
+handshake_internal (GTlsConnectionGnutls  *gnutls,
+		    gboolean               blocking,
+		    GCancellable          *cancellable,
+		    GError               **error)
+{
+  int ret;
+
+  if (G_IS_TLS_SERVER_CONNECTION_GNUTLS (gnutls) &&
+      gnutls->priv->ever_handshaked && !gnutls->priv->need_handshake)
+    {
+      BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+      ret = gnutls_rehandshake (gnutls->priv->session);
+      END_GNUTLS_IO (gnutls, ret, _("Error performing TLS handshake: %s"), error);
+
+      if (ret != 0)
+	return FALSE;
+    }
+
+  gnutls->priv->handshaking = TRUE;
+  G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
+
+  BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+  ret = gnutls_handshake (gnutls->priv->session);
+  END_GNUTLS_IO (gnutls, ret, _("Error performing TLS handshake: %s"), error);
+
+  if (ret == GNUTLS_E_AGAIN)
+    return FALSE;
+
+  gnutls->priv->handshaking = FALSE;
+  gnutls->priv->need_handshake = FALSE;
+  gnutls->priv->ever_handshaked = TRUE;
+
+  if (ret == 0 &&
+      gnutls_certificate_type_get (gnutls->priv->session) == GNUTLS_CRT_X509)
+    {
+      GTlsCertificate *chain, *cert;
+      const gnutls_datum_t *certs;
+      unsigned int num_certs;
+      int i;
+
+      certs = gnutls_certificate_get_peers (gnutls->priv->session, &num_certs);
+      chain = NULL;
+      if (certs)
+	{
+	  for (i = num_certs - 1; i >= 0; i--)
+	    {
+	      cert = g_tls_certificate_gnutls_new (&certs[i], chain);
+	      chain = cert;
+	    }
+	}
+
+      g_tls_connection_set_peer_certificate (G_TLS_CONNECTION (gnutls), chain);
+    }
+  else
+    g_tls_connection_set_peer_certificate (G_TLS_CONNECTION (gnutls), NULL);
+
+  return G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->finish_handshake (gnutls, ret == 0, error);
+}
+
+static gboolean
+handshake_in_progress_or_failed (GTlsConnectionGnutls  *gnutls,
+				 gboolean               blocking,
+				 GCancellable          *cancellable,
+				 GError               **error)
+{
+  if (!(gnutls->priv->need_handshake || gnutls->priv->handshaking))
+    return FALSE;
+
+  return !handshake_internal (gnutls, blocking, cancellable, error);
+}
+
+static gboolean
+g_tls_connection_gnutls_handshake (GTlsConnection   *conn,
+				   GCancellable     *cancellable,
+				   GError          **error)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
+
+  return handshake_internal (gnutls, TRUE, cancellable, error);
+}
+
+static gboolean
+g_tls_connection_gnutls_handshake_ready (GObject  *pollable_stream,
+					 gpointer  user_data)
+{
+  GTlsConnectionGnutls *gnutls;
+  GSimpleAsyncResult *simple = user_data;
+  gboolean success;
+  GError *error = NULL;
+
+  gnutls = G_TLS_CONNECTION_GNUTLS (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+  g_object_unref (gnutls);
+
+  success = handshake_internal (gnutls, FALSE, NULL, &error);
+  if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      g_error_free (error);
+      return TRUE;
+    }
+
+  if (error)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+  else
+    g_simple_async_result_set_op_res_gboolean (simple, success);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+
+  return FALSE;
+}
+
+static void
+g_tls_connection_gnutls_handshake_async (GTlsConnection       *conn,
+					 int                   io_priority,
+					 GCancellable         *cancellable,
+					 GAsyncReadyCallback   callback,
+					 gpointer              user_data)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
+  GSimpleAsyncResult *simple;
+  gboolean success;
+  GError *error = NULL;
+  GSource *source;
+
+  simple = g_simple_async_result_new (G_OBJECT (conn), callback, user_data,
+				      g_tls_connection_gnutls_handshake_async);
+  success = handshake_internal (gnutls, FALSE, cancellable, &error);
+  if (success)
+    {
+      g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+    }
+  else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+    }
+
+  source = g_tls_connection_gnutls_create_source (gnutls, 0, cancellable);
+  g_source_set_callback (source,
+			 (GSourceFunc) g_tls_connection_gnutls_handshake_ready,
+			 simple, NULL);
+  g_source_set_priority (source, io_priority);
+  g_source_attach (source, g_main_context_get_thread_default ());
+  g_source_unref (source);
+}
+
+static gboolean
+g_tls_connection_gnutls_handshake_finish (GTlsConnection       *conn,
+					  GAsyncResult         *result,
+					  GError              **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (conn), g_tls_connection_gnutls_handshake_async), FALSE);
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+gssize
+g_tls_connection_gnutls_read (GTlsConnectionGnutls  *gnutls,
+			      void                  *buffer,
+			      gsize                  count,
+			      gboolean               blocking,
+			      GCancellable          *cancellable,
+			      GError               **error)
+{
+  gssize ret;
+
+ again:
+  if (handshake_in_progress_or_failed (gnutls, blocking, cancellable, error))
+    return -1;
+
+  BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+  ret = gnutls_record_recv (gnutls->priv->session, buffer, count);
+  END_GNUTLS_IO (gnutls, ret, _("Error reading data from TLS socket: %s"), error);
+
+  if (ret >= 0)
+    return ret;
+  else if (ret == GNUTLS_E_REHANDSHAKE)
+    goto again;
+  else
+    return -1;
+}
+
+gssize
+g_tls_connection_gnutls_write (GTlsConnectionGnutls  *gnutls,
+			       const void            *buffer,
+			       gsize                  count,
+			       gboolean               blocking,
+			       GCancellable          *cancellable,
+			       GError               **error)
+{
+  gssize ret;
+
+ again:
+  if (handshake_in_progress_or_failed (gnutls, blocking, cancellable, error))
+    return -1;
+
+  BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+  ret = gnutls_record_send (gnutls->priv->session, buffer, count);
+  END_GNUTLS_IO (gnutls, ret, _("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;
+}
+
+static gboolean
+close_internal (GTlsConnectionGnutls  *gnutls,
+		gboolean               blocking,
+		GCancellable          *cancellable,
+		GError               **error)
+{
+  int ret;
+
+  if (!gnutls->priv->require_close_notify)
+    return TRUE;
+
+  /* If we haven't finished the initial handshake yet, there's no
+   * reason to finish it just so we can close.
+   */
+  if (gnutls->priv->handshaking && !gnutls->priv->ever_handshaked)
+    return TRUE;
+
+  if (handshake_in_progress_or_failed (gnutls, blocking, cancellable, error))
+    return FALSE;
+
+  gnutls->priv->closing = TRUE;
+  BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+  ret = gnutls_bye (gnutls->priv->session, GNUTLS_SHUT_WR);
+  END_GNUTLS_IO (gnutls, ret, _("Error performing TLS close: %s"), error);
+  if (ret == 0 || !error || !g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    gnutls->priv->closing = FALSE;
+
+  return ret == 0;
+}
+
+static gboolean
+g_tls_connection_gnutls_close (GIOStream     *stream,
+			       GCancellable  *cancellable,
+			       GError       **error)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
+
+  if (!close_internal (gnutls, TRUE, cancellable, error))
+    return FALSE;
+  return g_io_stream_close (gnutls->priv->base_io_stream,
+			    cancellable, error);
+}
+
+typedef struct {
+  GSimpleAsyncResult *simple;
+  GCancellable *cancellable;
+  int io_priority;
+} AsyncCloseData;
+
+static void
+close_base_stream_cb (GObject      *base_stream,
+		      GAsyncResult *result,
+		      gpointer      user_data)
+{
+  gboolean success;
+  GError *error = NULL;
+  AsyncCloseData *acd = user_data;
+
+  success = g_io_stream_close_finish (G_IO_STREAM (base_stream),
+				      result, &error);
+  if (success)
+    g_simple_async_result_set_op_res_gboolean (acd->simple, TRUE);
+  else
+    {
+      g_simple_async_result_set_from_error (acd->simple, error);
+      g_error_free (error);
+    }
+
+  g_simple_async_result_complete (acd->simple);
+  g_object_unref (acd->simple);
+  if (acd->cancellable)
+    g_object_unref (acd->cancellable);
+  g_slice_free (AsyncCloseData, acd);
+}
+
+static gboolean
+g_tls_connection_gnutls_close_ready (GObject  *pollable_stream,
+				     gpointer  user_data)
+{
+  GTlsConnectionGnutls *gnutls;
+  AsyncCloseData *acd = user_data;
+  gboolean success;
+  GError *error = NULL;
+
+  gnutls = G_TLS_CONNECTION_GNUTLS (g_async_result_get_source_object (G_ASYNC_RESULT (acd->simple)));
+  g_object_unref (gnutls);
+
+  success = close_internal (gnutls, FALSE, NULL, &error);
+  if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      g_error_free (error);
+      return TRUE;
+    }
+
+  if (error)
+    {
+      g_simple_async_result_set_from_error (acd->simple, error);
+      g_simple_async_result_complete (acd->simple);
+      g_error_free (error);
+      g_object_unref (acd->simple);
+      if (acd->cancellable)
+	g_object_unref (acd->cancellable);
+      g_slice_free (AsyncCloseData, acd);
+    }
+  else
+    {
+      g_io_stream_close_async (gnutls->priv->base_io_stream,
+			       acd->io_priority, acd->cancellable,
+			       close_base_stream_cb, acd);
+    }
+
+  return FALSE;
+}
+
+static void
+g_tls_connection_gnutls_close_async (GIOStream           *stream,
+				     int                  io_priority,
+				     GCancellable        *cancellable,
+				     GAsyncReadyCallback  callback,
+				     gpointer             user_data)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
+  GSimpleAsyncResult *simple;
+  gboolean success;
+  GError *error = NULL;
+  AsyncCloseData *acd;
+  GSource *source;
+
+  simple = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
+				      g_tls_connection_gnutls_close_async);
+
+  success = close_internal (gnutls, FALSE, cancellable, &error);
+  if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+    }
+
+  if (error)
+    g_error_free (error);
+
+  acd = g_slice_new (AsyncCloseData);
+  acd->simple = simple;
+  acd->cancellable = g_object_ref (cancellable);
+  acd->io_priority = io_priority;
+
+  if (success)
+    {
+      g_io_stream_close_async (gnutls->priv->base_io_stream,
+			       io_priority, cancellable,
+			       close_base_stream_cb, acd);
+      return;
+    }
+
+  source = g_tls_connection_gnutls_create_source (gnutls, 0, acd->cancellable);
+  g_source_set_callback (source,
+			 (GSourceFunc) g_tls_connection_gnutls_close_ready,
+			 acd, NULL);
+  g_source_set_priority (source, acd->io_priority);
+  g_source_attach (source, g_main_context_get_thread_default ());
+  g_source_unref (source);
+}
+
+static gboolean
+g_tls_connection_gnutls_close_finish (GIOStream           *stream,
+				      GAsyncResult        *result,
+				      GError             **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), g_tls_connection_gnutls_close_async), FALSE);
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return g_simple_async_result_get_op_res_gboolean (simple);
+}
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
new file mode 100644
index 0000000..834fc16
--- /dev/null
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -0,0 +1,77 @@
+/* GIO - GLib Connection, Output and Gnutlsing Library
+ *
+ * Copyright © 2009 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.
+ */
+
+#ifndef __G_TLS_CONNECTION_GNUTLS_H__
+#define __G_TLS_CONNECTION_GNUTLS_H__
+
+#include <gio/gio.h>
+#include <gnutls/gnutls.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CONNECTION_GNUTLS            (g_tls_connection_gnutls_get_type ())
+#define G_TLS_CONNECTION_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CONNECTION_GNUTLS, GTlsConnectionGnutls))
+#define G_TLS_CONNECTION_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CONNECTION_GNUTLS, GTlsConnectionGnutlsClass))
+#define G_IS_TLS_CONNECTION_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CONNECTION_GNUTLS))
+#define G_IS_TLS_CONNECTION_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CONNECTION_GNUTLS))
+#define G_TLS_CONNECTION_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CONNECTION_GNUTLS, GTlsConnectionGnutlsClass))
+
+typedef struct _GTlsConnectionGnutlsPrivate                   GTlsConnectionGnutlsPrivate;
+typedef struct _GTlsConnectionGnutlsClass                     GTlsConnectionGnutlsClass;
+typedef struct _GTlsConnectionGnutls                          GTlsConnectionGnutls;
+
+struct _GTlsConnectionGnutlsClass
+{
+  GTlsConnectionClass parent_class;
+
+  void     (*begin_handshake)  (GTlsConnectionGnutls  *gnutls);
+  gboolean (*finish_handshake) (GTlsConnectionGnutls  *gnutls,
+				gboolean               success,
+				GError               **inout_error);
+};
+
+struct _GTlsConnectionGnutls
+{
+  GTlsConnection parent_instance;
+  GTlsConnectionGnutlsPrivate *priv;
+};
+
+GType g_tls_connection_gnutls_get_type (void) G_GNUC_CONST;
+
+gnutls_certificate_credentials g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
+gnutls_session                 g_tls_connection_gnutls_get_session     (GTlsConnectionGnutls *connection);
+void                           g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
+									 gnutls_retr_st       *st);
+GTlsCertificateFlags           g_tls_connection_gnutls_validate_peer   (GTlsConnectionGnutls *gnutls);
+
+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);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CONNECTION_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsinputstream-gnutls.c b/tls/gnutls/gtlsinputstream-gnutls.c
new file mode 100644
index 0000000..0874576
--- /dev/null
+++ b/tls/gnutls/gtlsinputstream-gnutls.c
@@ -0,0 +1,232 @@
+/* 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gtlsinputstream-gnutls.h"
+
+static void g_tls_input_stream_gnutls_pollable_iface_init (GPollableInputStreamInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsInputStreamGnutls, g_tls_input_stream_gnutls, G_TYPE_INPUT_STREAM,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM, g_tls_input_stream_gnutls_pollable_iface_init)
+			 )
+
+struct _GTlsInputStreamGnutlsPrivate
+{
+  GTlsConnectionGnutls *conn;
+
+  /* pending operation metadata */
+  GCancellable *cancellable;
+  gpointer buffer;
+  gsize count;
+};
+
+static void
+g_tls_input_stream_gnutls_finalize (GObject *object)
+{
+  GTlsInputStreamGnutls *stream = G_TLS_INPUT_STREAM_GNUTLS (object);
+
+  if (stream->priv->conn)
+    g_object_unref (stream->priv->conn);
+
+  G_OBJECT_CLASS (g_tls_input_stream_gnutls_parent_class)->finalize (object);
+}
+
+static gssize
+g_tls_input_stream_gnutls_read (GInputStream  *stream,
+				void          *buffer,
+				gsize          count,
+				GCancellable  *cancellable,
+				GError       **error)
+{
+  GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (stream);
+
+  return g_tls_connection_gnutls_read (tls_stream->priv->conn,
+				       buffer, count, TRUE,
+				       cancellable, error);
+}
+
+static gboolean
+g_tls_input_stream_gnutls_read_ready (GPollableInputStream *stream,
+				      gpointer              user_data)
+{
+  GTlsInputStreamGnutls *tls_stream;
+  GSimpleAsyncResult *simple = user_data;
+  gssize nread;
+  GError *error = NULL;
+
+  tls_stream = G_TLS_INPUT_STREAM_GNUTLS (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+  g_object_unref (tls_stream);
+
+  nread = g_tls_connection_gnutls_read (tls_stream->priv->conn,
+					tls_stream->priv->buffer,
+					tls_stream->priv->count, FALSE,
+					tls_stream->priv->cancellable,
+					&error);
+  if (nread == -1 &&
+      g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      g_error_free (error);
+      return TRUE;
+    }
+
+  if (error)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+  else
+    g_simple_async_result_set_op_res_gssize (simple, nread);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+
+  return FALSE;
+}
+
+static void
+g_tls_input_stream_gnutls_read_async (GInputStream        *stream,
+				      void                *buffer,
+				      gsize                count,
+				      gint                 io_priority,
+				      GCancellable        *cancellable,
+				      GAsyncReadyCallback  callback,
+				      gpointer             user_data)
+{
+  GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (stream);
+  GSimpleAsyncResult *simple;
+  gssize nread;
+  GError *error = NULL;
+  GSource *source;
+
+  simple = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
+				      g_tls_input_stream_gnutls_read_async);
+  nread = g_tls_connection_gnutls_read (tls_stream->priv->conn,
+					buffer, count, FALSE,
+					cancellable, &error);
+
+  if (nread >= 0 ||
+      !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      if (error)
+	{
+	  g_simple_async_result_set_from_error (simple, error);
+	  g_error_free (error);
+	}
+      else
+	g_simple_async_result_set_op_res_gssize (simple, nread);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+
+  tls_stream->priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+  tls_stream->priv->buffer = buffer;
+  tls_stream->priv->count = count;
+
+  source = g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
+						  G_IO_IN,
+						  tls_stream->priv->cancellable);
+  g_source_set_callback (source,
+			 (GSourceFunc) g_tls_input_stream_gnutls_read_ready,
+			 simple, NULL);
+  g_source_attach (source, g_main_context_get_thread_default ());
+  g_source_unref (source);
+}
+
+static gssize
+g_tls_input_stream_gnutls_read_finish (GInputStream  *stream,
+				       GAsyncResult  *result,
+				       GError       **error)
+{
+  g_return_val_if_fail (G_IS_TLS_INPUT_STREAM_GNUTLS (stream), -1);
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), g_tls_input_stream_gnutls_read_async), -1);
+
+  return g_simple_async_result_get_op_res_gssize (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+static gboolean
+g_tls_input_stream_gnutls_pollable_is_readable (GPollableInputStream *pollable)
+{
+  GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (pollable);
+
+  return g_tls_connection_gnutls_check (tls_stream->priv->conn, G_IO_IN); 
+}
+
+static GSource *
+g_tls_input_stream_gnutls_pollable_create_source (GPollableInputStream *pollable,
+						  GCancellable         *cancellable)
+{
+  GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (pollable);
+
+  return g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
+						G_IO_IN,
+						cancellable);
+}
+
+static gssize
+g_tls_input_stream_gnutls_pollable_read_nonblocking (GPollableInputStream  *pollable,
+						     void                  *buffer,
+						     gsize                  size,
+						     GError               **error)
+{
+  GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (pollable);
+
+  return g_tls_connection_gnutls_read (tls_stream->priv->conn,
+				       buffer, size, FALSE,
+				       NULL, error);
+}
+
+static void
+g_tls_input_stream_gnutls_class_init (GTlsInputStreamGnutlsClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsInputStreamGnutlsPrivate));
+
+  gobject_class->finalize = g_tls_input_stream_gnutls_finalize;
+
+  input_stream_class->read_fn = g_tls_input_stream_gnutls_read;
+  input_stream_class->read_async = g_tls_input_stream_gnutls_read_async;
+  input_stream_class->read_finish = g_tls_input_stream_gnutls_read_finish;
+}
+
+static void
+g_tls_input_stream_gnutls_pollable_iface_init (GPollableInputStreamInterface *iface)
+{
+  iface->is_readable = g_tls_input_stream_gnutls_pollable_is_readable;
+  iface->create_source = g_tls_input_stream_gnutls_pollable_create_source;
+  iface->read_nonblocking = g_tls_input_stream_gnutls_pollable_read_nonblocking;
+}
+
+static void
+g_tls_input_stream_gnutls_init (GTlsInputStreamGnutls *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_TLS_INPUT_STREAM_GNUTLS, GTlsInputStreamGnutlsPrivate);
+}
+
+GInputStream *
+g_tls_input_stream_gnutls_new (GTlsConnectionGnutls *conn)
+{
+  GTlsInputStreamGnutls *tls_stream;
+
+  tls_stream = g_object_new (G_TYPE_TLS_INPUT_STREAM_GNUTLS, NULL);
+  tls_stream->priv->conn = g_object_ref (conn);
+  return G_INPUT_STREAM (tls_stream);
+}
diff --git a/tls/gnutls/gtlsinputstream-gnutls.h b/tls/gnutls/gtlsinputstream-gnutls.h
new file mode 100644
index 0000000..1b0bdba
--- /dev/null
+++ b/tls/gnutls/gtlsinputstream-gnutls.h
@@ -0,0 +1,48 @@
+/* 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.
+ */
+
+#ifndef __G_TLS_INPUT_STREAM_GNUTLS_H__
+#define __G_TLS_INPUT_STREAM_GNUTLS_H__
+
+#include <gio/gio.h>
+#include "gtlsconnection-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_INPUT_STREAM_GNUTLS            (g_tls_input_stream_gnutls_get_type ())
+#define G_TLS_INPUT_STREAM_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_INPUT_STREAM_GNUTLS, GTlsInputStreamGnutls))
+#define G_TLS_INPUT_STREAM_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_INPUT_STREAM_GNUTLS, GTlsInputStreamGnutlsClass))
+#define G_IS_TLS_INPUT_STREAM_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_INPUT_STREAM_GNUTLS))
+#define G_IS_TLS_INPUT_STREAM_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_INPUT_STREAM_GNUTLS))
+#define G_TLS_INPUT_STREAM_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_INPUT_STREAM_GNUTLS, GTlsInputStreamGnutlsClass))
+
+typedef struct _GTlsInputStreamGnutlsPrivate GTlsInputStreamGnutlsPrivate;
+typedef struct _GTlsInputStreamGnutlsClass   GTlsInputStreamGnutlsClass;
+typedef struct _GTlsInputStreamGnutls        GTlsInputStreamGnutls;
+
+struct _GTlsInputStreamGnutlsClass
+{
+  GInputStreamClass parent_class;
+};
+
+struct _GTlsInputStreamGnutls
+{
+  GInputStream parent_instance;
+  GTlsInputStreamGnutlsPrivate *priv;
+};
+
+GType         g_tls_input_stream_gnutls_get_type (void) G_GNUC_CONST;
+GInputStream *g_tls_input_stream_gnutls_new      (GTlsConnectionGnutls *conn);
+
+G_END_DECLS
+
+#endif /* __G_TLS_INPUT_STREAM_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsoutputstream-gnutls.c b/tls/gnutls/gtlsoutputstream-gnutls.c
new file mode 100644
index 0000000..e69e445
--- /dev/null
+++ b/tls/gnutls/gtlsoutputstream-gnutls.c
@@ -0,0 +1,232 @@
+/* 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gtlsoutputstream-gnutls.h"
+
+static void g_tls_output_stream_gnutls_pollable_iface_init (GPollableOutputStreamInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsOutputStreamGnutls, g_tls_output_stream_gnutls, G_TYPE_OUTPUT_STREAM,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, g_tls_output_stream_gnutls_pollable_iface_init)
+			 )
+
+struct _GTlsOutputStreamGnutlsPrivate
+{
+  GTlsConnectionGnutls *conn;
+
+  /* pending operation metadata */
+  GCancellable *cancellable;
+  gconstpointer buffer;
+  gsize count;
+};
+
+static void
+g_tls_output_stream_gnutls_finalize (GObject *object)
+{
+  GTlsOutputStreamGnutls *stream = G_TLS_OUTPUT_STREAM_GNUTLS (object);
+
+  if (stream->priv->conn)
+    g_object_unref (stream->priv->conn);
+
+  G_OBJECT_CLASS (g_tls_output_stream_gnutls_parent_class)->finalize (object);
+}
+
+static gssize
+g_tls_output_stream_gnutls_write (GOutputStream  *stream,
+				  const void     *buffer,
+				  gsize           count,
+				  GCancellable   *cancellable,
+				  GError        **error)
+{
+  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (stream);
+
+  return g_tls_connection_gnutls_write (tls_stream->priv->conn,
+					buffer, count, TRUE,
+					cancellable, error);
+}
+
+static gboolean
+g_tls_output_stream_gnutls_write_ready (GIOStreamAdapter *adapter,
+					gpointer          user_data)
+{
+  GTlsOutputStreamGnutls *tls_stream;
+  GSimpleAsyncResult *simple = user_data;
+  gssize nwrote;
+  GError *error = NULL;
+
+  tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+  g_object_unref (tls_stream);
+
+  nwrote = g_tls_connection_gnutls_write (tls_stream->priv->conn,
+					  tls_stream->priv->buffer,
+					  tls_stream->priv->count, FALSE,
+					  tls_stream->priv->cancellable,
+					  &error);
+  if (nwrote == -1 &&
+      g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      g_error_free (error);
+      return TRUE;
+    }
+
+  if (error)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+  else
+    g_simple_async_result_set_op_res_gssize (simple, nwrote);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+
+  return FALSE;
+}
+
+static void
+g_tls_output_stream_gnutls_write_async (GOutputStream        *stream,
+					const void           *buffer,
+					gsize                 count,
+					gint                  io_priority,
+					GCancellable         *cancellable,
+					GAsyncReadyCallback   callback,
+					gpointer              user_data)
+{
+  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (stream);
+  GSimpleAsyncResult *simple;
+  gssize nwrote;
+  GError *error = NULL;
+  GSource *source;
+
+  simple = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
+				      g_tls_output_stream_gnutls_write_async);
+  nwrote = g_tls_connection_gnutls_write (tls_stream->priv->conn,
+					  buffer, count, FALSE,
+					  cancellable, &error);
+
+  if (nwrote >= 0 ||
+      !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      if (error)
+	{
+	  g_simple_async_result_set_from_error (simple, error);
+	  g_error_free (error);
+	}
+      else
+	g_simple_async_result_set_op_res_gssize (simple, nwrote);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+
+  tls_stream->priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+  tls_stream->priv->buffer = buffer;
+  tls_stream->priv->count = count;
+
+  source = g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
+						  G_IO_OUT,
+						  tls_stream->priv->cancellable);
+  g_source_set_callback (source,
+			 (GSourceFunc) g_tls_output_stream_gnutls_write_ready,
+			 simple, NULL);
+  g_source_attach (source, g_main_context_get_thread_default ());
+  g_source_unref (source);
+}
+
+static gssize
+g_tls_output_stream_gnutls_write_finish (GOutputStream  *stream,
+					 GAsyncResult   *result,
+					 GError        **error)
+{
+  g_return_val_if_fail (G_IS_TLS_OUTPUT_STREAM_GNUTLS (stream), -1);
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), g_tls_output_stream_gnutls_write_async), -1);
+
+  return g_simple_async_result_get_op_res_gssize (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+static gboolean
+g_tls_output_stream_gnutls_pollable_is_writable (GPollableOutputStream *pollable)
+{
+  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+
+  return g_tls_connection_gnutls_check (tls_stream->priv->conn, G_IO_OUT); 
+}
+
+static GSource *
+g_tls_output_stream_gnutls_pollable_create_source (GPollableOutputStream *pollable,
+						   GCancellable         *cancellable)
+{
+  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+
+  return g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
+						G_IO_OUT,
+						cancellable);
+}
+
+static gssize
+g_tls_output_stream_gnutls_pollable_write_nonblocking (GPollableOutputStream  *pollable,
+						       const void             *buffer,
+						       gsize                   size,
+						       GError                **error)
+{
+  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+
+  return g_tls_connection_gnutls_write (tls_stream->priv->conn,
+					buffer, size, FALSE,
+					NULL, error);
+}
+
+static void
+g_tls_output_stream_gnutls_class_init (GTlsOutputStreamGnutlsClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsOutputStreamGnutlsPrivate));
+
+  gobject_class->finalize = g_tls_output_stream_gnutls_finalize;
+
+  output_stream_class->write_fn = g_tls_output_stream_gnutls_write;
+  output_stream_class->write_async = g_tls_output_stream_gnutls_write_async;
+  output_stream_class->write_finish = g_tls_output_stream_gnutls_write_finish;
+}
+
+static void
+g_tls_output_stream_gnutls_pollable_iface_init (GPollableOutputStreamInterface *iface)
+{
+  iface->is_writable = g_tls_output_stream_gnutls_pollable_is_writable;
+  iface->create_source = g_tls_output_stream_gnutls_pollable_create_source;
+  iface->write_nonblocking = g_tls_output_stream_gnutls_pollable_write_nonblocking;
+}
+
+static void
+g_tls_output_stream_gnutls_init (GTlsOutputStreamGnutls *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, GTlsOutputStreamGnutlsPrivate);
+}
+
+GOutputStream *
+g_tls_output_stream_gnutls_new (GTlsConnectionGnutls *conn)
+{
+  GTlsOutputStreamGnutls *tls_stream;
+
+  tls_stream = g_object_new (G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, NULL);
+  tls_stream->priv->conn = g_object_ref (conn);
+  return G_OUTPUT_STREAM (tls_stream);
+}
diff --git a/tls/gnutls/gtlsoutputstream-gnutls.h b/tls/gnutls/gtlsoutputstream-gnutls.h
new file mode 100644
index 0000000..10660a6
--- /dev/null
+++ b/tls/gnutls/gtlsoutputstream-gnutls.h
@@ -0,0 +1,48 @@
+/* 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.
+ */
+
+#ifndef __G_TLS_OUTPUT_STREAM_GNUTLS_H__
+#define __G_TLS_OUTPUT_STREAM_GNUTLS_H__
+
+#include <gio/gio.h>
+#include "gtlsconnection-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_OUTPUT_STREAM_GNUTLS            (g_tls_output_stream_gnutls_get_type ())
+#define G_TLS_OUTPUT_STREAM_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, GTlsOutputStreamGnutls))
+#define G_TLS_OUTPUT_STREAM_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, GTlsOutputStreamGnutlsClass))
+#define G_IS_TLS_OUTPUT_STREAM_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS))
+#define G_IS_TLS_OUTPUT_STREAM_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS))
+#define G_TLS_OUTPUT_STREAM_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, GTlsOutputStreamGnutlsClass))
+
+typedef struct _GTlsOutputStreamGnutlsPrivate GTlsOutputStreamGnutlsPrivate;
+typedef struct _GTlsOutputStreamGnutlsClass   GTlsOutputStreamGnutlsClass;
+typedef struct _GTlsOutputStreamGnutls        GTlsOutputStreamGnutls;
+
+struct _GTlsOutputStreamGnutlsClass
+{
+  GOutputStreamClass parent_class;
+};
+
+struct _GTlsOutputStreamGnutls
+{
+  GOutputStream parent_instance;
+  GTlsOutputStreamGnutlsPrivate *priv;
+};
+
+GType          g_tls_output_stream_gnutls_get_type (void) G_GNUC_CONST;
+GOutputStream *g_tls_output_stream_gnutls_new      (GTlsConnectionGnutls *conn);
+
+G_END_DECLS
+
+#endif /* __G_TLS_OUTPUT_STREAM_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
new file mode 100644
index 0000000..119f847
--- /dev/null
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -0,0 +1,193 @@
+/* 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "gtlsserverconnection-gnutls.h"
+#include "gtlscertificate-gnutls.h"
+#include <glib/gi18n-lib.h>
+
+enum
+{
+  PROP_0,
+  PROP_AUTHENTICATION_MODE
+};
+
+static void g_tls_server_connection_gnutls_get_property (GObject    *object,
+							 guint       prop_id,
+							 GValue     *value,
+							 GParamSpec *pspec);
+static void g_tls_server_connection_gnutls_set_property (GObject      *object,
+							 guint         prop_id,
+							 const GValue *value,
+							 GParamSpec   *pspec);
+
+static void     g_tls_server_connection_gnutls_begin_handshake  (GTlsConnectionGnutls  *conn);
+static gboolean g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
+								 gboolean               success,
+								 GError               **inout_error);
+
+static void g_tls_server_connection_gnutls_server_connection_interface_init (GTlsServerConnectionInterface *iface);
+
+static int g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t  session,
+							     gnutls_retr_st   *st);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionGnutls, g_tls_server_connection_gnutls, G_TYPE_TLS_CONNECTION_GNUTLS,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION,
+						g_tls_server_connection_gnutls_server_connection_interface_init))
+
+struct _GTlsServerConnectionGnutlsPrivate
+{
+  GTlsAuthenticationMode authentication_mode;
+};
+
+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);
+
+  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->begin_handshake = g_tls_server_connection_gnutls_begin_handshake;
+  connection_gnutls_class->finish_handshake = g_tls_server_connection_gnutls_finish_handshake;
+
+  g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
+}
+
+static void
+g_tls_server_connection_gnutls_server_connection_interface_init (GTlsServerConnectionInterface *iface)
+{
+}
+
+static void
+g_tls_server_connection_gnutls_init (GTlsServerConnectionGnutls *gnutls)
+{
+  gnutls_certificate_credentials_t creds;
+
+  gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutlsPrivate);
+
+  creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
+  gnutls_certificate_server_set_retrieve_function (creds, g_tls_server_connection_gnutls_retrieve_function);
+}
+
+static void
+g_tls_server_connection_gnutls_get_property (GObject    *object,
+					     guint       prop_id,
+					     GValue     *value,
+					     GParamSpec *pspec)
+{
+  GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_AUTHENTICATION_MODE:
+      g_value_set_enum (value, gnutls->priv->authentication_mode);
+      break;
+      
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_server_connection_gnutls_set_property (GObject      *object,
+					     guint         prop_id,
+					     const GValue *value,
+					     GParamSpec   *pspec)
+{
+  GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_AUTHENTICATION_MODE:
+      gnutls->priv->authentication_mode = g_value_get_enum (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static int
+g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t  session,
+						  gnutls_retr_st   *st)
+{
+  g_tls_connection_gnutls_get_certificate (gnutls_transport_get_ptr (session), st);
+  return 0;
+}
+
+static void
+g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+{
+  GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (conn);
+  gnutls_session_t session;
+  gnutls_certificate_request_t req_mode;
+
+  switch (gnutls->priv->authentication_mode)
+    {
+    case G_TLS_AUTHENTICATION_REQUESTED:
+      req_mode = GNUTLS_CERT_REQUEST;
+      break;
+    case G_TLS_AUTHENTICATION_REQUIRED:
+      req_mode = GNUTLS_CERT_REQUIRE;
+      break;
+    default:
+      req_mode = GNUTLS_CERT_IGNORE;
+      break;
+    }
+
+  session = g_tls_connection_gnutls_get_session (conn);
+  gnutls_certificate_server_set_request (session, req_mode);
+}
+
+static gboolean
+g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *gnutls,
+						 gboolean               success,
+						 GError               **inout_error)
+{
+  GTlsCertificateFlags gtls_errors;
+  GTlsCertificate *peer;
+
+  if (!success)
+    return FALSE;
+
+  peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (gnutls));
+  if (peer)
+    {
+      gtls_errors = g_tls_connection_gnutls_validate_peer (gnutls);
+      if (!g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls), peer, gtls_errors))
+	{
+	  g_set_error_literal (inout_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			       _("Unacceptable TLS certificate"));
+	  return FALSE;
+	}
+    }
+
+  return TRUE;
+}
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.h b/tls/gnutls/gtlsserverconnection-gnutls.h
new file mode 100644
index 0000000..f5e7b74
--- /dev/null
+++ b/tls/gnutls/gtlsserverconnection-gnutls.h
@@ -0,0 +1,47 @@
+/* GIO - GLib ServerConnection, Output and Gnutlsing 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.
+ */
+
+#ifndef __G_TLS_SERVER_CONNECTION_GNUTLS_H__
+#define __G_TLS_SERVER_CONNECTION_GNUTLS_H__
+
+#include <gio/gio.h> 
+#include "gtlsconnection-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_SERVER_CONNECTION_GNUTLS            (g_tls_server_connection_gnutls_get_type ())
+#define G_TLS_SERVER_CONNECTION_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutls))
+#define G_TLS_SERVER_CONNECTION_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutlsClass))
+#define G_IS_TLS_SERVER_CONNECTION_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS))
+#define G_IS_TLS_SERVER_CONNECTION_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS))
+#define G_TLS_SERVER_CONNECTION_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutlsClass))
+
+typedef struct _GTlsServerConnectionGnutlsPrivate                   GTlsServerConnectionGnutlsPrivate;
+typedef struct _GTlsServerConnectionGnutlsClass                     GTlsServerConnectionGnutlsClass;
+typedef struct _GTlsServerConnectionGnutls                          GTlsServerConnectionGnutls;
+
+struct _GTlsServerConnectionGnutlsClass
+{
+  GTlsConnectionGnutlsClass parent_class;
+};
+
+struct _GTlsServerConnectionGnutls
+{
+  GTlsConnectionGnutls parent_instance;
+  GTlsServerConnectionGnutlsPrivate *priv;
+};
+
+GType g_tls_server_connection_gnutls_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_SERVER_CONNECTION_GNUTLS_H___ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]