[glib-networking] Add a GNUTLS-based GTlsBackend
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking] Add a GNUTLS-based GTlsBackend
- Date: Fri, 26 Nov 2010 21:04:21 +0000 (UTC)
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, >ls_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]