[glib-networking/wip/openssl: 12/19] Add openssl module
- From: Ignacio Casal Quinteiro <icq src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/openssl: 12/19] Add openssl module
- Date: Thu, 12 May 2016 08:24:03 +0000 (UTC)
commit 169de9b6daf31fe23e36f0a574cb387d1db7a7dc
Author: Ignacio Casal Quinteiro <icq gnome org>
Date: Thu Aug 13 08:57:55 2015 +0200
Add openssl module
This patch also contains fixes from Fan Chun-wei.
Makefile.am | 4 +
configure.ac | 24 +-
po/POTFILES.in | 4 +
tls/openssl/Makefile.am | 37 ++
tls/openssl/gtlsbackend-openssl.c | 264 ++++++++
tls/openssl/gtlsbackend-openssl.h | 48 ++
tls/openssl/gtlsbio.c | 296 +++++++++
tls/openssl/gtlsbio.h | 55 ++
tls/openssl/gtlscertificate-openssl.c | 695 +++++++++++++++++++++
tls/openssl/gtlscertificate-openssl.h | 69 +++
tls/openssl/gtlsclientconnection-openssl.c | 490 +++++++++++++++
tls/openssl/gtlsclientconnection-openssl.h | 56 ++
tls/openssl/gtlsconnection-openssl.c | 576 ++++++++++++++++++
tls/openssl/gtlsconnection-openssl.h | 68 ++
tls/openssl/gtlsdatabase-openssl.c | 39 ++
tls/openssl/gtlsdatabase-openssl.h | 63 ++
tls/openssl/gtlsfiledatabase-openssl.c | 911 ++++++++++++++++++++++++++++
tls/openssl/gtlsfiledatabase-openssl.h | 64 ++
tls/openssl/gtlsserverconnection-openssl.c | 316 ++++++++++
tls/openssl/gtlsserverconnection-openssl.h | 57 ++
tls/openssl/openssl-module.c | 51 ++
tls/openssl/openssl-util.c | 487 +++++++++++++++
tls/openssl/openssl-util.h | 99 +++
tls/tests/Makefile.am | 21 +
tls/tests/connection.c | 12 +
25 files changed, 4804 insertions(+), 2 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d5ec8c8..0ac5673 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -25,6 +25,10 @@ if HAVE_GNUTLS
SUBDIRS += tls/gnutls
endif
+if HAVE_OPENSSL
+SUBDIRS += tls/openssl
+endif
+
if HAVE_TLS
SUBDIRS += tls/tests
endif
diff --git a/configure.ac b/configure.ac
index 0d031df..48e5376 100644
--- a/configure.ac
+++ b/configure.ac
@@ -157,10 +157,29 @@ AM_CONDITIONAL(HAVE_PKCS11, [test "$with_pkcs11" = "yes"])
AC_SUBST(PKCS11_CFLAGS)
AC_SUBST(PKCS11_LIBS)
+dnl **************************
+dnl *** Checks for OpenSSL ***
+dnl **************************
+
+AC_ARG_WITH(openssl,
+ [AC_HELP_STRING([--with-openssl],
+ [support for OpenSSL @<:@default=check@:>@])],
+ [],
+ [with_openssl=check])
+AS_IF([test "$with_openssl" != "no"],
+ [PKG_CHECK_MODULES(OPENSSL, [openssl],
+ [with_openssl=yes
+ tls_support="openssl $tls_support"],
+ [AS_IF([test "x$with_openssl" = "xyes"],
+ [AC_MSG_FAILURE("$OPENSSL_PKG_ERRORS")])])])
+AM_CONDITIONAL(HAVE_OPENSSL, [test "$with_openssl" = "yes"])
+AC_SUBST(OPENSSL_CFLAGS)
+AC_SUBST(OPENSSL_LIBS)
+
dnl ***********************************
dnl *** Do we have any TLS backend? ***
dnl ***********************************
-AM_CONDITIONAL(HAVE_TLS, [test "$with_gnutls" = "yes"])
+AM_CONDITIONAL(HAVE_TLS, [test "$with_gnutls" = "yes" -o "$with_openssl" = "yes"])
dnl ************************************
dnl *** Enable lcov coverage reports ***
@@ -248,6 +267,7 @@ AC_CONFIG_FILES([Makefile
proxy/tests/Makefile
tls/base/Makefile
tls/gnutls/Makefile
+ tls/openssl/Makefile
tls/pkcs11/Makefile
tls/tests/Makefile
])
@@ -256,7 +276,7 @@ AC_OUTPUT
echo ""
echo " Proxy support: ${proxy_support:-no}"
echo " TLS support: ${tls_support:-no}"
-if test "$tls_support" != "no"; then
+if test "$with_gnutls" != "no"; then
echo " PKCS#11 Support: $pkcs11_support"
echo " TLS CA file: ${with_ca_certificates:-(none)}"
if test "x$with_ca_certificates" != xno -a -n "$with_ca_certificates"; then
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5608e05..aded13c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,5 +4,9 @@ tls/gnutls/gtlscertificate-gnutls.c
tls/gnutls/gtlsclientconnection-gnutls.c
tls/gnutls/gtlsconnection-gnutls.c
tls/gnutls/gtlsserverconnection-gnutls.c
+tls/openssl/gtlscertificate-openssl.c
+tls/openssl/gtlsclientconnection-openssl.c
+tls/openssl/gtlsconnection-openssl.c
+tls/openssl/gtlsserverconnection-openssl.c
tls/pkcs11/gpkcs11pin.c
tls/pkcs11/gpkcs11slot.c
diff --git a/tls/openssl/Makefile.am b/tls/openssl/Makefile.am
new file mode 100644
index 0000000..2636029
--- /dev/null
+++ b/tls/openssl/Makefile.am
@@ -0,0 +1,37 @@
+include $(top_srcdir)/glib-networking.mk
+
+giomodule_LTLIBRARIES = libgioopenssl.la
+
+libgioopenssl_la_SOURCES = \
+ openssl-module.c \
+ gtlsbackend-openssl.h \
+ gtlsbackend-openssl.c \
+ gtlscertificate-openssl.h \
+ gtlscertificate-openssl.c \
+ gtlsconnection-openssl.h \
+ gtlsconnection-openssl.c \
+ gtlsserverconnection-openssl.h \
+ gtlsserverconnection-openssl.c \
+ gtlsclientconnection-openssl.h \
+ gtlsclientconnection-openssl.c \
+ gtlsdatabase-openssl.h \
+ gtlsdatabase-openssl.c \
+ gtlsfiledatabase-openssl.h \
+ gtlsfiledatabase-openssl.c \
+ gtlsbio.h \
+ gtlsbio.c \
+ openssl-util.h \
+ openssl-util.c \
+ $(NULL)
+
+AM_CPPFLAGS += \
+ -I$(top_srcdir)/tls/base \
+ $(OPENSSL_CFLAGS) \
+ $(NULL)
+
+libgioopenssl_la_LDFLAGS = $(module_flags)
+libgioopenssl_la_LIBADD = \
+ ../base/libtlsbase.la \
+ $(GLIB_LIBS) \
+ $(OPENSSL_LIBS) \
+ $(NULL)
diff --git a/tls/openssl/gtlsbackend-openssl.c b/tls/openssl/gtlsbackend-openssl.c
new file mode 100644
index 0000000..79a6d47
--- /dev/null
+++ b/tls/openssl/gtlsbackend-openssl.c
@@ -0,0 +1,264 @@
+/*
+ * gtlsbackend-openssl.c
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <openssl/ssl.h>
+#include <openssl/crypto.h>
+
+#include "gtlsbackend-openssl.h"
+#include "gtlscertificate-openssl.h"
+#include "gtlsserverconnection-openssl.h"
+#include "gtlsclientconnection-openssl.h"
+#include "gtlsfiledatabase-openssl.h"
+
+typedef struct _GTlsBackendOpensslPrivate
+{
+ GMutex mutex;
+ GTlsDatabase *default_database;
+} GTlsBackendOpensslPrivate;
+
+static void g_tls_backend_openssl_interface_init (GTlsBackendInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendOpenssl, g_tls_backend_openssl, G_TYPE_OBJECT, 0,
+ G_ADD_PRIVATE_DYNAMIC (GTlsBackendOpenssl)
+ G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_TLS_BACKEND,
+ g_tls_backend_openssl_interface_init))
+
+static GMutex *mutex_array = NULL;
+
+struct CRYPTO_dynlock_value {
+ GMutex mutex;
+};
+
+static unsigned long
+id_cb (void)
+{
+ return (unsigned long) g_thread_self ();
+}
+
+static void
+locking_cb (int mode,
+ int n,
+ const char *file,
+ int line)
+{
+ if (mode & CRYPTO_LOCK)
+ g_mutex_lock (&mutex_array[n]);
+ else
+ g_mutex_unlock (&mutex_array[n]);
+}
+
+static struct CRYPTO_dynlock_value *
+dyn_create_cb (const char *file,
+ int line)
+{
+ struct CRYPTO_dynlock_value *value = g_try_new (struct CRYPTO_dynlock_value, 1);
+
+ if (value)
+ g_mutex_init (&value->mutex);
+
+ return value;
+}
+
+static void
+dyn_lock_cb (int mode,
+ struct CRYPTO_dynlock_value *l,
+ const char *file,
+ int line)
+{
+ if (mode & CRYPTO_LOCK)
+ g_mutex_lock (&l->mutex);
+ else
+ g_mutex_unlock (&l->mutex);
+}
+
+static void
+dyn_destroy_cb (struct CRYPTO_dynlock_value *l,
+ const char *file,
+ int line)
+{
+ g_mutex_clear (&l->mutex);
+ g_free (l);
+}
+
+static gpointer
+gtls_openssl_init (gpointer data)
+{
+ int i;
+
+ /* Initialize openssl threading */
+ mutex_array = g_malloc_n (CRYPTO_num_locks(), sizeof (GMutex));
+ for (i = 0; i < CRYPTO_num_locks (); ++i)
+ g_mutex_init(&mutex_array[i]);
+
+ CRYPTO_set_id_callback (id_cb);
+ CRYPTO_set_locking_callback (locking_cb);
+ CRYPTO_set_dynlock_create_callback (dyn_create_cb);
+ CRYPTO_set_dynlock_lock_callback (dyn_lock_cb);
+ CRYPTO_set_dynlock_destroy_callback (dyn_destroy_cb);
+
+ SSL_library_init ();
+ SSL_load_error_strings ();
+ OpenSSL_add_all_algorithms ();
+
+ /* Leak the module to keep it from being unloaded. */
+ g_type_plugin_use (g_type_get_plugin (G_TYPE_TLS_BACKEND_OPENSSL));
+
+ return NULL;
+}
+
+static GOnce openssl_inited = G_ONCE_INIT;
+
+static void
+g_tls_backend_openssl_init (GTlsBackendOpenssl *backend)
+{
+ GTlsBackendOpensslPrivate *priv;
+
+ priv = g_tls_backend_openssl_get_instance_private (backend);
+
+ /* Once we call gtls_openssl_init(), we can't allow the module to be
+ * unloaded (since if openssl gets unloaded but gcrypt doesn't, then
+ * gcrypt will have dangling pointers to openssl's mutex functions).
+ * So we initialize it from here rather than at class init time so
+ * that it doesn't happen unless the app is actually using TLS (as
+ * opposed to just calling g_io_modules_scan_all_in_directory()).
+ */
+ g_once (&openssl_inited, gtls_openssl_init, NULL);
+
+ g_mutex_init (&priv->mutex);
+}
+
+static void
+g_tls_backend_openssl_finalize (GObject *object)
+{
+ int i;
+
+ GTlsBackendOpenssl *backend = G_TLS_BACKEND_OPENSSL (object);
+ GTlsBackendOpensslPrivate *priv;
+
+ priv = g_tls_backend_openssl_get_instance_private (backend);
+
+ g_clear_object (&priv->default_database);
+ g_mutex_clear (&priv->mutex);
+
+ CRYPTO_set_id_callback (NULL);
+ CRYPTO_set_locking_callback (NULL);
+ CRYPTO_set_dynlock_create_callback (NULL);
+ CRYPTO_set_dynlock_lock_callback (NULL);
+ CRYPTO_set_dynlock_destroy_callback (NULL);
+ for (i = 0; i < CRYPTO_num_locks(); ++i)
+ g_mutex_clear (&mutex_array[i]);
+ g_free (mutex_array);
+
+ G_OBJECT_CLASS (g_tls_backend_openssl_parent_class)->finalize (object);
+}
+
+static GTlsDatabase *
+g_tls_backend_openssl_real_create_database (GTlsBackendOpenssl *self,
+ GError **error)
+{
+ const gchar *anchor_file = NULL;
+#ifdef GTLS_SYSTEM_CA_FILE
+ anchor_file = GTLS_SYSTEM_CA_FILE;
+#endif
+ return g_tls_file_database_new (anchor_file, error);
+}
+
+static void
+g_tls_backend_openssl_class_init (GTlsBackendOpensslClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = g_tls_backend_openssl_finalize;
+
+ klass->create_database = g_tls_backend_openssl_real_create_database;
+}
+
+static void
+g_tls_backend_openssl_class_finalize (GTlsBackendOpensslClass *backend_class)
+{
+}
+
+static GTlsDatabase*
+g_tls_backend_openssl_get_default_database (GTlsBackend *backend)
+{
+ GTlsBackendOpenssl *openssl_backend = G_TLS_BACKEND_OPENSSL (backend);
+ GTlsBackendOpensslPrivate *priv;
+ GTlsDatabase *result;
+ GError *error = NULL;
+
+ priv = g_tls_backend_openssl_get_instance_private (openssl_backend);
+
+ g_mutex_lock (&priv->mutex);
+
+ if (priv->default_database)
+ {
+ result = g_object_ref (priv->default_database);
+ }
+ else
+ {
+ g_assert (G_TLS_BACKEND_OPENSSL_GET_CLASS (openssl_backend)->create_database);
+ result = G_TLS_BACKEND_OPENSSL_GET_CLASS (openssl_backend)->create_database (openssl_backend, &error);
+ if (error)
+ {
+ g_warning ("Couldn't load TLS file database: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ g_assert (result);
+ priv->default_database = g_object_ref (result);
+ }
+ }
+
+ g_mutex_unlock (&priv->mutex);
+
+ return result;
+}
+
+static void
+g_tls_backend_openssl_interface_init (GTlsBackendInterface *iface)
+{
+ iface->get_certificate_type = g_tls_certificate_openssl_get_type;
+ iface->get_client_connection_type = g_tls_client_connection_openssl_get_type;
+ iface->get_server_connection_type = g_tls_server_connection_openssl_get_type;
+ iface->get_file_database_type = g_tls_file_database_openssl_get_type;
+ iface->get_default_database = g_tls_backend_openssl_get_default_database;
+}
+
+void
+g_tls_backend_openssl_register (GIOModule *module)
+{
+ g_tls_backend_openssl_register_type (G_TYPE_MODULE (module));
+ g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
+ g_tls_backend_openssl_get_type(),
+ "openssl",
+ 0);
+}
diff --git a/tls/openssl/gtlsbackend-openssl.h b/tls/openssl/gtlsbackend-openssl.h
new file mode 100644
index 0000000..410b0fb
--- /dev/null
+++ b/tls/openssl/gtlsbackend-openssl.h
@@ -0,0 +1,48 @@
+/*
+ * gtlsbackend-openssl.h
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#ifndef __G_TLS_BACKEND_OPENSSL_H__
+#define __G_TLS_BACKEND_OPENSSL_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_BACKEND_OPENSSL (g_tls_backend_openssl_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GTlsBackendOpenssl, g_tls_backend_openssl,
+ G, TLS_BACKEND_OPENSSL, GObject)
+
+struct _GTlsBackendOpensslClass
+{
+ GObjectClass parent_class;
+
+ GTlsDatabase* (*create_database) (GTlsBackendOpenssl *backend,
+ GError **error);
+};
+
+void g_tls_backend_openssl_register (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* __G_TLS_BACKEND_OPENSSL_H___ */
diff --git a/tls/openssl/gtlsbio.c b/tls/openssl/gtlsbio.c
new file mode 100644
index 0000000..17d10a0
--- /dev/null
+++ b/tls/openssl/gtlsbio.c
@@ -0,0 +1,296 @@
+/*
+ * gtlsbio.c
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#include "gtlsbio.h"
+
+#include <string.h>
+
+typedef struct {
+ GIOStream *io_stream;
+ GCancellable *read_cancellable;
+ GCancellable *write_cancellable;
+ gboolean read_blocking;
+ gboolean write_blocking;
+ GError **read_error;
+ GError **write_error;
+} GTlsBio;
+
+static void
+free_gbio (gpointer user_data)
+{
+ GTlsBio *bio = (GTlsBio *)user_data;
+
+ g_object_unref (bio->io_stream);
+ g_free (bio);
+}
+
+static int
+gtls_bio_create (BIO *bio)
+{
+ bio->init = 0;
+ bio->num = 0;
+ bio->ptr = NULL;
+ bio->flags = 0;
+ return 1;
+}
+
+static int
+gtls_bio_destroy (BIO *bio)
+{
+ if (bio == NULL)
+ return 0;
+
+ if (bio->shutdown)
+ {
+ if (bio->ptr != NULL)
+ {
+ free_gbio (bio->ptr);
+ bio->ptr = NULL;
+ }
+ bio->init = 0;
+ }
+
+ return 1;
+}
+
+static long
+gtls_bio_ctrl (BIO *b,
+ int cmd,
+ long num,
+ void *ptr)
+{
+ long ret = 1;
+
+ switch (cmd)
+ {
+ case BIO_CTRL_GET_CLOSE:
+ ret = b->shutdown;
+ break;
+ case BIO_CTRL_SET_CLOSE:
+ b->shutdown = (int)num;
+ break;
+ case BIO_CTRL_DUP:
+ case BIO_CTRL_FLUSH:
+ ret = 1;
+ break;
+ case BIO_CTRL_PUSH:
+ case BIO_CTRL_POP:
+ ret = 0;
+ break;
+ default:
+ g_debug ("Got unsupported command: %d", cmd);
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int
+gtls_bio_write (BIO *bio,
+ const char *in,
+ int inl)
+{
+ GTlsBio *gbio;
+ gssize written;
+ GError *error = NULL;
+
+ if (!bio->init || in == NULL || inl == 0)
+ return 0;
+
+ gbio = (GTlsBio *)bio->ptr;
+
+ BIO_clear_retry_flags (bio);
+ written = g_pollable_stream_write (g_io_stream_get_output_stream (gbio->io_stream),
+ in, inl,
+ gbio->write_blocking,
+ gbio->write_cancellable,
+ &error);
+
+ if (written == -1)
+ {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ BIO_set_retry_write (bio);
+
+ g_propagate_error (gbio->write_error, error);
+ }
+
+ return written;
+}
+
+static int
+gtls_bio_read (BIO *bio,
+ char *out,
+ int outl)
+{
+ GTlsBio *gbio;
+ gssize read;
+ GError *error = NULL;
+
+ if (!bio->init || out == NULL || outl == 0)
+ return 0;
+
+ gbio = (GTlsBio *)bio->ptr;
+
+ BIO_clear_retry_flags (bio);
+ read = g_pollable_stream_read (g_io_stream_get_input_stream (gbio->io_stream),
+ out, outl,
+ gbio->read_blocking,
+ gbio->read_cancellable,
+ &error);
+
+ if (read == -1)
+ {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ BIO_set_retry_read (bio);
+
+ g_propagate_error (gbio->read_error, error);
+ }
+
+ return read;
+}
+
+static int
+gtls_bio_puts(BIO *bio,
+ const char *str)
+{
+ return gtls_bio_write (bio, str, (int)strlen (str));
+}
+
+static int
+gtls_bio_gets(BIO *bio,
+ char *buf,
+ int len)
+{
+ return -1;
+}
+
+static BIO_METHOD methods_gtls = {
+ BIO_TYPE_SOURCE_SINK,
+ "gtls",
+ gtls_bio_write,
+ gtls_bio_read,
+ gtls_bio_puts,
+ gtls_bio_gets,
+ gtls_bio_ctrl,
+ gtls_bio_create,
+ gtls_bio_destroy
+};
+
+static BIO_METHOD *
+BIO_s_gtls (void)
+{
+ return &methods_gtls;
+}
+
+BIO *
+g_tls_bio_new (GIOStream *io_stream)
+{
+ BIO *ret;
+ GTlsBio *gbio;
+
+ ret = BIO_new(BIO_s_gtls ());
+ if (ret == NULL)
+ return NULL;
+
+ gbio = g_new0 (GTlsBio, 1);
+ gbio->io_stream = g_object_ref (io_stream);
+
+ ret->ptr = gbio;
+ ret->init = 1;
+
+ return ret;
+}
+
+void
+g_tls_bio_set_read_cancellable (BIO *bio,
+ GCancellable *cancellable)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+ gbio = (GTlsBio *)bio->ptr;
+ gbio->read_cancellable = cancellable;
+}
+
+void
+g_tls_bio_set_read_blocking (BIO *bio,
+ gboolean blocking)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+ gbio = (GTlsBio *)bio->ptr;
+ gbio->read_blocking = blocking;
+}
+
+void
+g_tls_bio_set_read_error (BIO *bio,
+ GError **error)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+ gbio = (GTlsBio *)bio->ptr;
+ gbio->read_error = error;
+}
+
+void
+g_tls_bio_set_write_cancellable (BIO *bio,
+ GCancellable *cancellable)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+ gbio = (GTlsBio *)bio->ptr;
+ gbio->write_cancellable = cancellable;
+}
+
+void
+g_tls_bio_set_write_blocking (BIO *bio,
+ gboolean blocking)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+ gbio = (GTlsBio *)bio->ptr;
+ gbio->write_blocking = blocking;
+}
+
+void
+g_tls_bio_set_write_error (BIO *bio,
+ GError **error)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+ gbio = (GTlsBio *)bio->ptr;
+ gbio->write_error = error;
+}
diff --git a/tls/openssl/gtlsbio.h b/tls/openssl/gtlsbio.h
new file mode 100644
index 0000000..701f5d1
--- /dev/null
+++ b/tls/openssl/gtlsbio.h
@@ -0,0 +1,55 @@
+/*
+ * gtlsbio.h
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#ifndef __G_TLS_BIO_H__
+#define __G_TLS_BIO_H__
+
+#include <gio/gio.h>
+#include <openssl/bio.h>
+
+G_BEGIN_DECLS
+
+BIO *g_tls_bio_new (GIOStream *io_stream);
+
+void g_tls_bio_set_read_cancellable (BIO *bio,
+ GCancellable *cancellable);
+
+void g_tls_bio_set_read_blocking (BIO *bio,
+ gboolean blocking);
+
+void g_tls_bio_set_read_error (BIO *bio,
+ GError **error);
+
+void g_tls_bio_set_write_cancellable (BIO *bio,
+ GCancellable *cancellable);
+
+void g_tls_bio_set_write_blocking (BIO *bio,
+ gboolean blocking);
+
+void g_tls_bio_set_write_error (BIO *bio,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_BIO_H__ */
diff --git a/tls/openssl/gtlscertificate-openssl.c b/tls/openssl/gtlscertificate-openssl.c
new file mode 100644
index 0000000..d6af639
--- /dev/null
+++ b/tls/openssl/gtlscertificate-openssl.c
@@ -0,0 +1,695 @@
+/*
+ * gtlscertificate-openssl.c
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include "gtlscertificate-openssl.h"
+#include "openssl-util.h"
+#include <glib/gi18n-lib.h>
+
+typedef struct _GTlsCertificateOpensslPrivate
+{
+ X509 *cert;
+ EVP_PKEY *key;
+
+ GTlsCertificateOpenssl *issuer;
+
+ GError *construct_error;
+
+ guint have_cert : 1;
+ guint have_key : 1;
+} GTlsCertificateOpensslPrivate;
+
+enum
+{
+ PROP_0,
+
+ PROP_CERTIFICATE,
+ PROP_CERTIFICATE_PEM,
+ PROP_PRIVATE_KEY,
+ PROP_PRIVATE_KEY_PEM,
+ PROP_ISSUER
+};
+
+static void g_tls_certificate_openssl_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsCertificateOpenssl, g_tls_certificate_openssl, G_TYPE_TLS_CERTIFICATE,
+ G_ADD_PRIVATE (GTlsCertificateOpenssl)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_tls_certificate_openssl_initable_iface_init))
+
+static void
+g_tls_certificate_openssl_finalize (GObject *object)
+{
+ GTlsCertificateOpenssl *openssl = G_TLS_CERTIFICATE_OPENSSL (object);
+ GTlsCertificateOpensslPrivate *priv;
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ if (priv->cert)
+ X509_free (priv->cert);
+ if (priv->key)
+ EVP_PKEY_free (priv->key);
+
+ g_clear_object (&priv->issuer);
+
+ g_clear_error (&priv->construct_error);
+
+ G_OBJECT_CLASS (g_tls_certificate_openssl_parent_class)->finalize (object);
+}
+
+static void
+g_tls_certificate_openssl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsCertificateOpenssl *openssl = G_TLS_CERTIFICATE_OPENSSL (object);
+ GTlsCertificateOpensslPrivate *priv;
+ GByteArray *certificate;
+ guint8 *data;
+ BIO *bio;
+ char *certificate_pem;
+ int size;
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ switch (prop_id)
+ {
+ case PROP_CERTIFICATE:
+ /* NOTE: we do the two calls to avoid openssl allocating the buffer for us */
+ size = i2d_X509 (priv->cert, NULL);
+ if (size < 0)
+ certificate = NULL;
+ else
+ {
+ certificate = g_byte_array_sized_new (size);
+ certificate->len = size;
+ data = certificate->data;
+ size = i2d_X509 (priv->cert, &data);
+ if (size < 0)
+ {
+ g_byte_array_free (certificate, TRUE);
+ certificate = NULL;
+ }
+ }
+ g_value_take_boxed (value, certificate);
+ break;
+
+ case PROP_CERTIFICATE_PEM:
+ bio = BIO_new (BIO_s_mem ());
+
+ if (!PEM_write_bio_X509 (bio, priv->cert) || !BIO_write (bio, "\0", 1))
+ certificate_pem = NULL;
+ else
+ {
+ BIO_get_mem_data (bio, &certificate_pem);
+ g_value_set_string (value, certificate_pem);
+
+ BIO_free_all (bio);
+ }
+ break;
+
+ case PROP_ISSUER:
+ g_value_set_object (value, priv->issuer);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_certificate_openssl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsCertificateOpenssl *openssl = G_TLS_CERTIFICATE_OPENSSL (object);
+ GTlsCertificateOpensslPrivate *priv;
+ GByteArray *bytes;
+ guint8 *data;
+ BIO *bio;
+ const char *string;
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ switch (prop_id)
+ {
+ case PROP_CERTIFICATE:
+ bytes = g_value_get_boxed (value);
+ if (!bytes)
+ break;
+ g_return_if_fail (priv->have_cert == FALSE);
+ /* see that we cannot use bytes->data directly since it will move the pointer */
+ data = bytes->data;
+ priv->cert = d2i_X509 (NULL, (const unsigned char **)&data, bytes->len);
+ if (priv->cert != NULL)
+ priv->have_cert = TRUE;
+ else if (!priv->construct_error)
+ {
+ priv->construct_error =
+ g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Could not parse DER certificate: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ }
+
+ break;
+
+ case PROP_CERTIFICATE_PEM:
+ string = g_value_get_string (value);
+ if (!string)
+ break;
+ g_return_if_fail (priv->have_cert == FALSE);
+ bio = BIO_new_mem_buf ((gpointer)string, -1);
+ priv->cert = PEM_read_bio_X509 (bio, NULL, NULL, NULL);
+ BIO_free (bio);
+ if (priv->cert != NULL)
+ priv->have_cert = TRUE;
+ else if (!priv->construct_error)
+ {
+ priv->construct_error =
+ g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Could not parse PEM certificate: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ }
+ break;
+
+ case PROP_PRIVATE_KEY:
+ bytes = g_value_get_boxed (value);
+ if (!bytes)
+ break;
+ g_return_if_fail (priv->have_key == FALSE);
+ bio = BIO_new_mem_buf (bytes->data, bytes->len);
+ priv->key = d2i_PrivateKey_bio (bio, NULL);
+ BIO_free (bio);
+ if (priv->key != NULL)
+ priv->have_key = TRUE;
+ else if (!priv->construct_error)
+ {
+ priv->construct_error =
+ g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Could not parse DER private key: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ }
+ break;
+
+ case PROP_PRIVATE_KEY_PEM:
+ string = g_value_get_string (value);
+ if (!string)
+ break;
+ g_return_if_fail (priv->have_key == FALSE);
+ bio = BIO_new_mem_buf ((gpointer)string, -1);
+ priv->key = PEM_read_bio_PrivateKey (bio, NULL, NULL, NULL);
+ BIO_free (bio);
+ if (priv->key != NULL)
+ priv->have_key = TRUE;
+ else if (!priv->construct_error)
+ {
+ priv->construct_error =
+ g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Could not parse PEM private key: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ }
+ break;
+
+ case PROP_ISSUER:
+ priv->issuer = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_certificate_openssl_init (GTlsCertificateOpenssl *openssl)
+{
+}
+
+static gboolean
+g_tls_certificate_openssl_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsCertificateOpenssl *openssl = G_TLS_CERTIFICATE_OPENSSL (initable);
+ GTlsCertificateOpensslPrivate *priv;
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ if (priv->construct_error)
+ {
+ g_propagate_error (error, priv->construct_error);
+ priv->construct_error = NULL;
+ return FALSE;
+ }
+ else if (!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;
+}
+
+static GTlsCertificateFlags
+g_tls_certificate_openssl_verify (GTlsCertificate *cert,
+ GSocketConnectable *identity,
+ GTlsCertificate *trusted_ca)
+{
+ GTlsCertificateOpenssl *cert_openssl;
+ GTlsCertificateOpensslPrivate *priv;
+ GTlsCertificateFlags gtls_flags;
+ X509 *x;
+ STACK_OF(X509) *untrusted;
+ gint i;
+
+ cert_openssl = G_TLS_CERTIFICATE_OPENSSL (cert);
+ priv = g_tls_certificate_openssl_get_instance_private (cert_openssl);
+ x = priv->cert;
+
+ untrusted = sk_X509_new_null ();
+ for (; cert_openssl; cert_openssl = priv->issuer)
+ {
+ priv = g_tls_certificate_openssl_get_instance_private (cert_openssl);
+ sk_X509_push (untrusted, priv->cert);
+ }
+
+ gtls_flags = 0;
+
+ if (trusted_ca)
+ {
+ X509_STORE *store;
+ X509_STORE_CTX csc;
+ STACK_OF(X509) *trusted;
+
+ store = X509_STORE_new ();
+
+ if (!X509_STORE_CTX_init (&csc, store, x, untrusted))
+ {
+ sk_X509_free (untrusted);
+ X509_STORE_CTX_cleanup (&csc);
+ X509_STORE_free (store);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ trusted = sk_X509_new_null ();
+ cert_openssl = G_TLS_CERTIFICATE_OPENSSL (trusted_ca);
+ for (; cert_openssl; cert_openssl = priv->issuer)
+ {
+ priv = g_tls_certificate_openssl_get_instance_private (cert_openssl);
+ sk_X509_push (trusted, priv->cert);
+ }
+
+ X509_STORE_CTX_trusted_stack (&csc, trusted);
+ if (X509_verify_cert (&csc) <= 0)
+ gtls_flags |= g_tls_certificate_openssl_convert_error (X509_STORE_CTX_get_error (&csc));
+
+ sk_X509_free (trusted);
+ X509_STORE_CTX_cleanup (&csc);
+ X509_STORE_free (store);
+ }
+
+ /* We have to check these ourselves since openssl
+ * does not give us flags and UNKNOWN_CA will take priority.
+ */
+ for (i = 0; i < sk_X509_num (untrusted); i++)
+ {
+ X509 *c = sk_X509_value (untrusted, i);
+ ASN1_TIME *not_before = X509_get_notBefore (c);
+ ASN1_TIME *not_after = X509_get_notAfter (c);
+
+ if (X509_cmp_current_time (not_before) > 0)
+ gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
+
+ if (X509_cmp_current_time (not_after) < 0)
+ gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
+ }
+
+ sk_X509_free (untrusted);
+
+ if (identity)
+ gtls_flags |= g_tls_certificate_openssl_verify_identity (G_TLS_CERTIFICATE_OPENSSL (cert), identity);
+
+ return gtls_flags;
+}
+
+static void
+g_tls_certificate_openssl_class_init (GTlsCertificateOpensslClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsCertificateClass *certificate_class = G_TLS_CERTIFICATE_CLASS (klass);
+
+ gobject_class->get_property = g_tls_certificate_openssl_get_property;
+ gobject_class->set_property = g_tls_certificate_openssl_set_property;
+ gobject_class->finalize = g_tls_certificate_openssl_finalize;
+
+ certificate_class->verify = g_tls_certificate_openssl_verify;
+
+ 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");
+ g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
+}
+
+static void
+g_tls_certificate_openssl_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = g_tls_certificate_openssl_initable_init;
+}
+
+GTlsCertificate *
+g_tls_certificate_openssl_new (GBytes *bytes,
+ GTlsCertificate *issuer)
+{
+ GTlsCertificateOpenssl *openssl;
+
+ openssl = g_object_new (G_TYPE_TLS_CERTIFICATE_OPENSSL,
+ "issuer", issuer,
+ NULL);
+ g_tls_certificate_openssl_set_data (openssl, bytes);
+
+ return G_TLS_CERTIFICATE (openssl);
+}
+
+GTlsCertificate *
+g_tls_certificate_openssl_new_from_x509 (X509 *x,
+ GTlsCertificate *issuer)
+{
+ GTlsCertificateOpenssl *openssl;
+ GTlsCertificateOpensslPrivate *priv;
+
+ openssl = g_object_new (G_TYPE_TLS_CERTIFICATE_OPENSSL,
+ "issuer", issuer,
+ NULL);
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ priv->cert = X509_dup (x);
+ priv->have_cert = TRUE;
+
+ return G_TLS_CERTIFICATE (openssl);
+}
+
+void
+g_tls_certificate_openssl_set_data (GTlsCertificateOpenssl *openssl,
+ GBytes *bytes)
+{
+ GTlsCertificateOpensslPrivate *priv;
+ const unsigned char *data;
+
+ g_return_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (openssl));
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ g_return_if_fail (!priv->have_cert);
+
+ data = (const unsigned char *)g_bytes_get_data (bytes, NULL);
+ priv->cert = d2i_X509 (NULL, &data, g_bytes_get_size (bytes));
+
+ if (priv->cert != NULL)
+ priv->have_cert = TRUE;
+}
+
+GBytes *
+g_tls_certificate_openssl_get_bytes (GTlsCertificateOpenssl *openssl)
+{
+ GByteArray *array;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (openssl), NULL);
+
+ g_object_get (openssl, "certificate", &array, NULL);
+ return g_byte_array_free_to_bytes (array);
+}
+
+X509 *
+g_tls_certificate_openssl_get_cert (GTlsCertificateOpenssl *openssl)
+{
+ GTlsCertificateOpensslPrivate *priv;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (openssl), FALSE);
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ return priv->cert;
+}
+
+EVP_PKEY *
+g_tls_certificate_openssl_get_key (GTlsCertificateOpenssl *openssl)
+{
+ GTlsCertificateOpensslPrivate *priv;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (openssl), FALSE);
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ return priv->key;
+}
+
+void
+g_tls_certificate_openssl_set_issuer (GTlsCertificateOpenssl *openssl,
+ GTlsCertificateOpenssl *issuer)
+{
+ GTlsCertificateOpensslPrivate *priv;
+
+ g_return_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (openssl));
+ g_return_if_fail (!issuer || G_IS_TLS_CERTIFICATE_OPENSSL (issuer));
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ if (g_set_object (&priv->issuer, issuer))
+ g_object_notify (G_OBJECT (openssl), "issuer");
+}
+
+static gboolean
+verify_identity_hostname (GTlsCertificateOpenssl *openssl,
+ GSocketConnectable *identity)
+{
+ GTlsCertificateOpensslPrivate *priv;
+ const char *hostname;
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ if (G_IS_NETWORK_ADDRESS (identity))
+ hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
+ else if (G_IS_NETWORK_SERVICE (identity))
+ hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
+ else
+ return FALSE;
+
+ return g_tls_X509_check_host (priv->cert, hostname, strlen (hostname), 0, NULL) == 1;
+}
+
+static gboolean
+verify_identity_ip (GTlsCertificateOpenssl *openssl,
+ GSocketConnectable *identity)
+{
+ GTlsCertificateOpensslPrivate *priv;
+ GInetAddress *addr;
+ gsize addr_size;
+ const guint8 *addr_bytes;
+ gboolean ret;
+
+ priv = g_tls_certificate_openssl_get_instance_private (openssl);
+
+ if (G_IS_INET_SOCKET_ADDRESS (identity))
+ addr = g_object_ref (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (identity)));
+ else {
+ const char *hostname;
+
+ if (G_IS_NETWORK_ADDRESS (identity))
+ hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
+ else if (G_IS_NETWORK_SERVICE (identity))
+ hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
+ else
+ return FALSE;
+
+ addr = g_inet_address_new_from_string (hostname);
+ if (!addr)
+ return FALSE;
+ }
+
+ addr_bytes = g_inet_address_to_bytes (addr);
+ addr_size = g_inet_address_get_native_size (addr);
+
+ ret = g_tls_X509_check_ip (priv->cert, addr_bytes, addr_size, 0) == 1;
+
+ g_object_unref (addr);
+ return ret;
+}
+
+GTlsCertificateFlags
+g_tls_certificate_openssl_verify_identity (GTlsCertificateOpenssl *openssl,
+ GSocketConnectable *identity)
+{
+ if (verify_identity_hostname (openssl, identity))
+ return 0;
+ else if (verify_identity_ip (openssl, identity))
+ return 0;
+
+ /* FIXME: check sRVName and uniformResourceIdentifier
+ * subjectAltNames, if appropriate for @identity.
+ */
+
+ return G_TLS_CERTIFICATE_BAD_IDENTITY;
+}
+
+GTlsCertificateFlags
+g_tls_certificate_openssl_convert_error (guint openssl_error)
+{
+ GTlsCertificateFlags gtls_flags;
+
+ gtls_flags = 0;
+
+ /* FIXME: should we add more ? */
+ switch (openssl_error)
+ {
+ case X509_V_OK:
+ break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ gtls_flags = G_TLS_CERTIFICATE_NOT_ACTIVATED;
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ gtls_flags = G_TLS_CERTIFICATE_EXPIRED;
+ break;
+ case X509_V_ERR_CERT_REVOKED:
+ gtls_flags = G_TLS_CERTIFICATE_REVOKED;
+ break;
+ case X509_V_ERR_AKID_SKID_MISMATCH:
+ gtls_flags = G_TLS_CERTIFICATE_BAD_IDENTITY;
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ gtls_flags = G_TLS_CERTIFICATE_UNKNOWN_CA;
+ break;
+ default:
+ g_message ("certificate error: %s", X509_verify_cert_error_string (openssl_error));
+ gtls_flags = G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ return gtls_flags;
+}
+
+static gboolean
+is_issuer (GTlsCertificateOpenssl *cert,
+ GTlsCertificateOpenssl *issuer)
+{
+ X509 *x;
+ X509 *issuer_x;
+ X509_STORE *store;
+ X509_STORE_CTX csc;
+ STACK_OF(X509) *trusted;
+ gboolean ret = FALSE;
+ gint err;
+
+ x = g_tls_certificate_openssl_get_cert (cert);
+ issuer_x = g_tls_certificate_openssl_get_cert (issuer);
+
+ store = X509_STORE_new ();
+
+ if (!X509_STORE_CTX_init (&csc, store, x, NULL))
+ goto end;
+
+ trusted = sk_X509_new_null ();
+ sk_X509_push (trusted, issuer_x);
+
+ X509_STORE_CTX_trusted_stack (&csc, trusted);
+ X509_STORE_CTX_set_flags (&csc, X509_V_FLAG_CB_ISSUER_CHECK);
+
+ /* FIXME: is this the right way to do it? */
+ if (X509_verify_cert (&csc) <= 0)
+ {
+ err = X509_STORE_CTX_get_error (&csc);
+ if (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)
+ ret = TRUE;
+ }
+ else
+ ret = TRUE;
+
+end:
+ X509_STORE_CTX_cleanup (&csc);
+ X509_STORE_free (store);
+
+ return ret;
+}
+
+GTlsCertificateOpenssl *
+g_tls_certificate_openssl_build_chain (X509 *x,
+ STACK_OF (X509) *chain)
+{
+ GPtrArray *glib_certs;
+ GTlsCertificateOpenssl *issuer;
+ GTlsCertificateOpenssl *result;
+ guint i, j;
+
+ g_return_val_if_fail (x != NULL, NULL);
+ g_return_val_if_fail (chain, NULL);
+
+ glib_certs = g_ptr_array_new_full (sk_X509_num (chain), g_object_unref);
+ g_ptr_array_add (glib_certs, g_tls_certificate_openssl_new_from_x509 (x, NULL));
+ for (i = 1; i < sk_X509_num (chain); i++)
+ g_ptr_array_add (glib_certs, g_tls_certificate_openssl_new_from_x509 (sk_X509_value (chain, i), NULL));
+
+ /* Some servers send certs out of order, or will send duplicate
+ * certs, so we need to be careful when assigning the issuer of
+ * our new GTlsCertificateOpenssl.
+ */
+ for (i = 0; i < glib_certs->len; i++)
+ {
+ issuer = NULL;
+
+ if (i < glib_certs->len - 1 &&
+ is_issuer (glib_certs->pdata[i], glib_certs->pdata[i + 1]))
+ {
+ issuer = glib_certs->pdata[i + 1];
+ }
+ else
+ {
+ for (j = 0; j < glib_certs->len; j++)
+ {
+ if (j != i &&
+ is_issuer (glib_certs->pdata[i], glib_certs->pdata[j]))
+ {
+ issuer = glib_certs->pdata[j];
+ break;
+ }
+ }
+ }
+
+ if (issuer)
+ g_tls_certificate_openssl_set_issuer (glib_certs->pdata[i], issuer);
+ }
+
+ result = g_object_ref (glib_certs->pdata[0]);
+ g_ptr_array_unref (glib_certs);
+
+ return result;
+}
diff --git a/tls/openssl/gtlscertificate-openssl.h b/tls/openssl/gtlscertificate-openssl.h
new file mode 100644
index 0000000..6b40502
--- /dev/null
+++ b/tls/openssl/gtlscertificate-openssl.h
@@ -0,0 +1,69 @@
+/*
+ * gtlscertificate-openssl.h
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#ifndef __G_TLS_CERTIFICATE_OPENSSL_H__
+#define __G_TLS_CERTIFICATE_OPENSSL_H__
+
+#include <gio/gio.h>
+#include <openssl/ssl.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CERTIFICATE_OPENSSL (g_tls_certificate_openssl_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GTlsCertificateOpenssl, g_tls_certificate_openssl,
+ G, TLS_CERTIFICATE_OPENSSL, GTlsCertificate)
+
+struct _GTlsCertificateOpensslClass
+{
+ GTlsCertificateClass parent_class;
+};
+
+GTlsCertificate *g_tls_certificate_openssl_new (GBytes *bytes,
+ GTlsCertificate *issuer);
+
+GTlsCertificate *g_tls_certificate_openssl_new_from_x509 (X509 *x,
+ GTlsCertificate *issuer);
+
+void g_tls_certificate_openssl_set_data (GTlsCertificateOpenssl *openssl,
+ GBytes *bytes);
+
+GBytes * g_tls_certificate_openssl_get_bytes (GTlsCertificateOpenssl *openssl);
+
+X509 *g_tls_certificate_openssl_get_cert (GTlsCertificateOpenssl *openssl);
+EVP_PKEY *g_tls_certificate_openssl_get_key (GTlsCertificateOpenssl *openssl);
+
+void g_tls_certificate_openssl_set_issuer (GTlsCertificateOpenssl *openssl,
+ GTlsCertificateOpenssl *issuer);
+
+GTlsCertificateFlags g_tls_certificate_openssl_verify_identity (GTlsCertificateOpenssl *openssl,
+ GSocketConnectable *identity);
+
+GTlsCertificateFlags g_tls_certificate_openssl_convert_error (guint
openssl_error);
+
+GTlsCertificateOpenssl *g_tls_certificate_openssl_build_chain (X509 *x,
+ STACK_OF (X509) *chain);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CERTIFICATE_OPENSSL_H___ */
diff --git a/tls/openssl/gtlsclientconnection-openssl.c b/tls/openssl/gtlsclientconnection-openssl.c
new file mode 100644
index 0000000..6742086
--- /dev/null
+++ b/tls/openssl/gtlsclientconnection-openssl.c
@@ -0,0 +1,490 @@
+/*
+ * gtlsclientconnection-openssl.c
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <errno.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509_vfy.h>
+#include <string.h>
+
+#include "gtlsconnection-base.h"
+#include "gtlsclientconnection-openssl.h"
+#include "gtlsbackend-openssl.h"
+#include "gtlscertificate-openssl.h"
+#include <glib/gi18n-lib.h>
+
+typedef struct _GTlsClientConnectionOpensslPrivate
+{
+ GTlsCertificateFlags validation_flags;
+ GSocketConnectable *server_identity;
+ gboolean use_ssl3;
+ gboolean session_data_override;
+
+ GBytes *session_id;
+ GBytes *session_data;
+
+ STACK_OF (X509_NAME) *ca_list;
+
+ SSL_SESSION *session;
+ SSL *ssl;
+ SSL_CTX *ssl_ctx;
+} GTlsClientConnectionOpensslPrivate;
+
+enum
+{
+ PROP_0,
+ PROP_VALIDATION_FLAGS,
+ PROP_SERVER_IDENTITY,
+ PROP_USE_SSL3,
+ PROP_ACCEPTED_CAS
+};
+
+static void g_tls_client_connection_openssl_initable_interface_init (GInitableIface *iface);
+
+static void g_tls_client_connection_openssl_client_connection_interface_init (GTlsClientConnectionInterface
*iface);
+
+static GInitableIface *g_tls_client_connection_openssl_parent_initable_iface;
+
+G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionOpenssl, g_tls_client_connection_openssl,
G_TYPE_TLS_CONNECTION_OPENSSL,
+ G_ADD_PRIVATE (GTlsClientConnectionOpenssl)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_tls_client_connection_openssl_initable_interface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION,
+
g_tls_client_connection_openssl_client_connection_interface_init))
+
+static void
+g_tls_client_connection_openssl_finalize (GObject *object)
+{
+ GTlsClientConnectionOpenssl *openssl = G_TLS_CLIENT_CONNECTION_OPENSSL (object);
+ GTlsClientConnectionOpensslPrivate *priv;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (openssl);
+
+ g_clear_object (&priv->server_identity);
+ g_clear_pointer (&priv->session_id, g_bytes_unref);
+ g_clear_pointer (&priv->session_data, g_bytes_unref);
+
+ SSL_free (priv->ssl);
+ SSL_CTX_free (priv->ssl_ctx);
+
+ G_OBJECT_CLASS (g_tls_client_connection_openssl_parent_class)->finalize (object);
+}
+
+static const gchar *
+get_server_identity (GTlsClientConnectionOpenssl *openssl)
+{
+ GTlsClientConnectionOpensslPrivate *priv;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (openssl);
+
+ if (G_IS_NETWORK_ADDRESS (priv->server_identity))
+ return g_network_address_get_hostname (G_NETWORK_ADDRESS (priv->server_identity));
+ else if (G_IS_NETWORK_SERVICE (priv->server_identity))
+ return g_network_service_get_domain (G_NETWORK_SERVICE (priv->server_identity));
+ else
+ return NULL;
+}
+
+static void
+g_tls_client_connection_openssl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsClientConnectionOpenssl *openssl = G_TLS_CLIENT_CONNECTION_OPENSSL (object);
+ GTlsClientConnectionOpensslPrivate *priv;
+ GList *accepted_cas;
+ gint i;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (openssl);
+
+ switch (prop_id)
+ {
+ case PROP_VALIDATION_FLAGS:
+ g_value_set_flags (value, priv->validation_flags);
+ break;
+
+ case PROP_SERVER_IDENTITY:
+ g_value_set_object (value, priv->server_identity);
+ break;
+
+ case PROP_USE_SSL3:
+ g_value_set_boolean (value, priv->use_ssl3);
+ break;
+
+ case PROP_ACCEPTED_CAS:
+ accepted_cas = NULL;
+ if (priv->ca_list)
+ {
+ for (i = 0; i < sk_X509_NAME_num (priv->ca_list); ++i)
+ {
+ int size;
+
+ size = i2d_X509_NAME (sk_X509_NAME_value (priv->ca_list, i), NULL);
+ if (size > 0)
+ {
+ unsigned char *ca;
+
+ ca = g_malloc (size);
+ size = i2d_X509_NAME (sk_X509_NAME_value (priv->ca_list, i), &ca);
+ if (size > 0)
+ accepted_cas = g_list_prepend (accepted_cas, g_byte_array_new_take (
+ ca, size));
+ else
+ g_free (ca);
+ }
+ }
+ accepted_cas = g_list_reverse (accepted_cas);
+ }
+ g_value_set_pointer (value, accepted_cas);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_client_connection_openssl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsClientConnectionOpenssl *openssl = G_TLS_CLIENT_CONNECTION_OPENSSL (object);
+ GTlsClientConnectionOpensslPrivate *priv;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (openssl);
+
+ switch (prop_id)
+ {
+ case PROP_VALIDATION_FLAGS:
+ priv->validation_flags = g_value_get_flags (value);
+ break;
+
+ case PROP_SERVER_IDENTITY:
+ if (priv->server_identity)
+ g_object_unref (priv->server_identity);
+ priv->server_identity = g_value_dup_object (value);
+ break;
+
+ case PROP_USE_SSL3:
+ priv->use_ssl3 = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_client_connection_openssl_constructed (GObject *object)
+{
+ GTlsClientConnectionOpenssl *openssl = G_TLS_CLIENT_CONNECTION_OPENSSL (object);
+ GTlsClientConnectionOpensslPrivate *priv;
+ GSocketConnection *base_conn;
+ GSocketAddress *remote_addr;
+ GInetAddress *iaddr;
+ guint port;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (openssl);
+
+ /* Create a TLS session ID. We base it on the IP address since
+ * different hosts serving the same hostname/service will probably
+ * not share the same session cache. We base it on the
+ * server-identity because at least some servers will fail (rather
+ * than just failing to resume the session) if we don't.
+ * (https://bugs.launchpad.net/bugs/823325)
+ */
+ g_object_get (G_OBJECT (openssl), "base-io-stream", &base_conn, NULL);
+ if (G_IS_SOCKET_CONNECTION (base_conn))
+ {
+ remote_addr = g_socket_connection_get_remote_address (base_conn, NULL);
+ if (G_IS_INET_SOCKET_ADDRESS (remote_addr))
+ {
+ GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
+ const gchar *server_hostname;
+ gchar *addrstr, *session_id;
+
+ iaddr = g_inet_socket_address_get_address (isaddr);
+ port = g_inet_socket_address_get_port (isaddr);
+
+ addrstr = g_inet_address_to_string (iaddr);
+ server_hostname = get_server_identity (openssl);
+ session_id = g_strdup_printf ("%s/%s/%d", addrstr,
+ server_hostname ? server_hostname : "",
+ port);
+ priv->session_id = g_bytes_new_take (session_id, strlen (session_id));
+ g_free (addrstr);
+ }
+ g_object_unref (remote_addr);
+ }
+ g_object_unref (base_conn);
+
+ G_OBJECT_CLASS (g_tls_client_connection_openssl_parent_class)->constructed (object);
+}
+
+static GTlsConnectionBaseStatus
+g_tls_client_connection_openssl_handshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_openssl_parent_class)->
+ handshake (tls, cancellable, error);
+}
+
+static GTlsConnectionBaseStatus
+g_tls_client_connection_openssl_complete_handshake (GTlsConnectionBase *tls,
+ GError **error)
+{
+ GTlsConnectionBaseStatus status;
+
+ status = G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_openssl_parent_class)->
+ complete_handshake (tls, error);
+
+ return status;
+}
+
+static SSL *
+g_tls_client_connection_openssl_get_ssl (GTlsConnectionOpenssl *connection)
+{
+ GTlsClientConnectionOpenssl *client = G_TLS_CLIENT_CONNECTION_OPENSSL (connection);
+ GTlsClientConnectionOpensslPrivate *priv;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (client);
+
+ return priv->ssl;
+}
+
+static SSL_CTX *
+g_tls_client_connection_openssl_get_ssl_ctx (GTlsConnectionOpenssl *connection)
+{
+ GTlsClientConnectionOpenssl *client = G_TLS_CLIENT_CONNECTION_OPENSSL (connection);
+ GTlsClientConnectionOpensslPrivate *priv;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (client);
+
+ return priv->ssl_ctx;
+}
+
+static void
+g_tls_client_connection_openssl_class_init (GTlsClientConnectionOpensslClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+ GTlsConnectionOpensslClass *connection_class = G_TLS_CONNECTION_OPENSSL_CLASS (klass);
+
+ gobject_class->finalize = g_tls_client_connection_openssl_finalize;
+ gobject_class->get_property = g_tls_client_connection_openssl_get_property;
+ gobject_class->set_property = g_tls_client_connection_openssl_set_property;
+ gobject_class->constructed = g_tls_client_connection_openssl_constructed;
+
+ base_class->handshake = g_tls_client_connection_openssl_handshake;
+ base_class->complete_handshake = g_tls_client_connection_openssl_complete_handshake;
+
+ connection_class->get_ssl = g_tls_client_connection_openssl_get_ssl;
+ connection_class->get_ssl_ctx = g_tls_client_connection_openssl_get_ssl_ctx;
+
+ 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_openssl_init (GTlsClientConnectionOpenssl *openssl)
+{
+}
+
+
+static void
+g_tls_client_connection_openssl_copy_session_state (GTlsClientConnection *conn,
+ GTlsClientConnection *source)
+{
+}
+
+static void
+g_tls_client_connection_openssl_client_connection_interface_init (GTlsClientConnectionInterface *iface)
+{
+ iface->copy_session_state = g_tls_client_connection_openssl_copy_session_state;
+}
+
+static int data_index;
+
+static int
+retrieve_certificate (SSL *ssl,
+ X509 **x509,
+ EVP_PKEY **pkey)
+{
+ GTlsClientConnectionOpenssl *client;
+ GTlsClientConnectionOpensslPrivate *priv;
+ GTlsConnectionBase *tls;
+ GTlsConnectionOpenssl *openssl;
+ GTlsCertificate *cert;
+ gboolean set_certificate = FALSE;
+
+ client = SSL_get_ex_data (ssl, data_index);
+ tls = G_TLS_CONNECTION_BASE (client);
+ openssl = G_TLS_CONNECTION_OPENSSL (client);
+
+ priv = g_tls_client_connection_openssl_get_instance_private (client);
+
+ tls->certificate_requested = TRUE;
+
+ priv->ca_list = SSL_get_client_CA_list (priv->ssl);
+ g_object_notify (G_OBJECT (client), "accepted-cas");
+
+ cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (client));
+ if (cert != NULL)
+ set_certificate = TRUE;
+ else
+ {
+ g_clear_error (&tls->certificate_error);
+ if (g_tls_connection_openssl_request_certificate (openssl, &tls->certificate_error))
+ {
+ cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (client));
+ set_certificate = (cert != NULL);
+ }
+ }
+
+ if (set_certificate)
+ {
+ EVP_PKEY *key;
+
+ key = g_tls_certificate_openssl_get_key (G_TLS_CERTIFICATE_OPENSSL (cert));
+ /* increase ref count */
+ CRYPTO_add (&key->references, 1, CRYPTO_LOCK_EVP_PKEY);
+ *pkey = key;
+
+ *x509 = X509_dup (g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (cert)));
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+generate_session_id (const SSL *ssl,
+ unsigned char *id,
+ unsigned int *id_len)
+{
+ GTlsClientConnectionOpenssl *client;
+ GTlsClientConnectionOpensslPrivate *priv;
+ int len;
+
+ client = SSL_get_ex_data (ssl, data_index);
+ priv = g_tls_client_connection_openssl_get_instance_private (client);
+
+ len = MIN (*id_len, g_bytes_get_size (priv->session_id));
+ memcpy (id, g_bytes_get_data (priv->session_id, NULL), len);
+
+ return 1;
+}
+
+static gboolean
+g_tls_client_connection_openssl_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsClientConnectionOpenssl *client = G_TLS_CLIENT_CONNECTION_OPENSSL (initable);
+ GTlsClientConnectionOpensslPrivate *priv;
+ long options;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (client);
+
+ priv->session = SSL_SESSION_new ();
+
+ priv->ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
+ if (priv->ssl_ctx == NULL)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Could not create TLS context: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ return FALSE;
+ }
+
+ options = SSL_OP_NO_TICKET;
+
+ /* Only TLS 1.2 or higher */
+ SSL_CTX_set_options (priv->ssl_ctx, options);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10200000L
+ {
+ const char *hostname;
+
+ hostname = get_server_identity (client);
+ if (hostname)
+ {
+ X509_VERIFY_PARAM *param;
+
+ param = X509_VERIFY_PARAM_new ();
+ X509_VERIFY_PARAM_set1_host (param, hostname);
+ SSL_CTX_set1_param (priv->ssl_ctx, param);
+ X509_VERIFY_PARAM_free (param);
+ }
+ }
+#endif
+
+ SSL_CTX_set_generate_session_id (priv->ssl_ctx, generate_session_id);
+ SSL_CTX_add_session (priv->ssl_ctx, priv->session);
+
+ SSL_CTX_set_client_cert_cb (priv->ssl_ctx, retrieve_certificate);
+
+ SSL_CTX_set_cipher_list (priv->ssl_ctx, "HIGH:!DSS:!aNULL STRENGTH");
+
+ priv->ssl = SSL_new (priv->ssl_ctx);
+ if (priv->ssl == NULL)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Could not create TLS connection: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ return FALSE;
+ }
+
+ data_index = SSL_get_ex_new_index (0, "gtlsclientconnection", NULL, NULL, NULL);
+ SSL_set_ex_data (priv->ssl, data_index, client);
+
+ SSL_set_connect_state (priv->ssl);
+
+ if (!g_tls_client_connection_openssl_parent_initable_iface->
+ init (initable, cancellable, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+g_tls_client_connection_openssl_initable_interface_init (GInitableIface *iface)
+{
+ g_tls_client_connection_openssl_parent_initable_iface = g_type_interface_peek_parent (iface);
+
+ iface->init = g_tls_client_connection_openssl_initable_init;
+}
diff --git a/tls/openssl/gtlsclientconnection-openssl.h b/tls/openssl/gtlsclientconnection-openssl.h
new file mode 100644
index 0000000..e686fc1
--- /dev/null
+++ b/tls/openssl/gtlsclientconnection-openssl.h
@@ -0,0 +1,56 @@
+/*
+ * gtlsclientconnection-openssl.h
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#ifndef __G_TLS_CLIENT_CONNECTION_OPENSSL_H__
+#define __G_TLS_CLIENT_CONNECTION_OPENSSL_H__
+
+#include "gtlsconnection-openssl.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CLIENT_CONNECTION_OPENSSL (g_tls_client_connection_openssl_get_type ())
+#define G_TLS_CLIENT_CONNECTION_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst),
G_TYPE_TLS_CLIENT_CONNECTION_OPENSSL, GTlsClientConnectionOpenssl))
+#define G_TLS_CLIENT_CONNECTION_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),
G_TYPE_TLS_CLIENT_CONNECTION_OPENSSL, GTlsClientConnectionOpensslClass))
+#define G_IS_TLS_CLIENT_CONNECTION_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst),
G_TYPE_TLS_CLIENT_CONNECTION_OPENSSL))
+#define G_IS_TLS_CLIENT_CONNECTION_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),
G_TYPE_TLS_CLIENT_CONNECTION_OPENSSL))
+#define G_TLS_CLIENT_CONNECTION_OPENSSL_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst),
G_TYPE_TLS_CLIENT_CONNECTION_OPENSSL, GTlsClientConnectionOpensslClass))
+
+typedef struct _GTlsClientConnectionOpensslClass GTlsClientConnectionOpensslClass;
+typedef struct _GTlsClientConnectionOpenssl GTlsClientConnectionOpenssl;
+
+struct _GTlsClientConnectionOpensslClass
+{
+ GTlsConnectionOpensslClass parent_class;
+};
+
+struct _GTlsClientConnectionOpenssl
+{
+ GTlsConnectionOpenssl parent_instance;
+};
+
+GType g_tls_client_connection_openssl_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_CLIENT_CONNECTION_OPENSSL_H___ */
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
new file mode 100644
index 0000000..a2e9d4e
--- /dev/null
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -0,0 +1,576 @@
+/*
+ * gtlsconnection-openssl.c
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include "gtlsconnection-openssl.h"
+#include "gtlsbackend-openssl.h"
+#include "gtlscertificate-openssl.h"
+#include "gtlsbio.h"
+
+#include <glib/gi18n-lib.h>
+
+typedef struct _GTlsConnectionOpensslPrivate
+{
+ BIO *bio;
+
+ GTlsCertificate *peer_certificate_tmp;
+ GTlsCertificateFlags peer_certificate_errors_tmp;
+
+ gboolean shutting_down;
+} GTlsConnectionOpensslPrivate;
+
+static void g_tls_connection_openssl_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionOpenssl, g_tls_connection_openssl,
G_TYPE_TLS_CONNECTION_BASE,
+ G_ADD_PRIVATE (GTlsConnectionOpenssl)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_tls_connection_openssl_initable_iface_init))
+
+static void
+g_tls_connection_openssl_finalize (GObject *object)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (object);
+ GTlsConnectionOpensslPrivate *priv;
+
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ g_clear_object (&priv->peer_certificate_tmp);
+
+ G_OBJECT_CLASS (g_tls_connection_openssl_parent_class)->finalize (object);
+}
+
+static GTlsConnectionBaseStatus
+end_openssl_io (GTlsConnectionOpenssl *openssl,
+ GIOCondition direction,
+ int ret,
+ GError **error,
+ const char *err_fmt,
+ ...) G_GNUC_PRINTF(5, 6);
+
+static GTlsConnectionBaseStatus
+end_openssl_io (GTlsConnectionOpenssl *openssl,
+ GIOCondition direction,
+ int ret,
+ GError **error,
+ const char *err_fmt,
+ ...)
+{
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (openssl);
+ GTlsConnectionOpensslPrivate *priv;
+ int err_code, err, reason;
+ GError *my_error = NULL;
+ GTlsConnectionBaseStatus status;
+ SSL *ssl;
+
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+
+ err_code = SSL_get_error (ssl, ret);
+
+ status = g_tls_connection_base_pop_io (tls, direction, ret > 0, &my_error);
+
+ /* NOTE: this is tricky! The tls bio will set to retry if the operation
+ * would block, and we would get an error code with WANT_READ or WANT_WRITE,
+ * though if in that case we try again we would end up in an infinite loop
+ * since we will not let the glib main loop to do its stuff and we would
+ * be getting a would block forever. Instead we need to also check the error
+ * we get from the socket operation to understand whether to try again. See
+ * that we propagate the WOULD_BLOCK error a bit more down.
+ */
+ if ((err_code == SSL_ERROR_WANT_READ ||
+ err_code == SSL_ERROR_WANT_WRITE) &&
+ status != G_TLS_CONNECTION_BASE_WOULD_BLOCK)
+ return G_TLS_CONNECTION_BASE_TRY_AGAIN;
+
+ if (err_code == SSL_ERROR_ZERO_RETURN)
+ return G_TLS_CONNECTION_BASE_OK;
+
+ if (status == G_TLS_CONNECTION_BASE_OK ||
+ status == G_TLS_CONNECTION_BASE_WOULD_BLOCK ||
+ status == G_TLS_CONNECTION_BASE_TIMED_OUT)
+ {
+ if (my_error)
+ g_propagate_error (error, my_error);
+ return status;
+ }
+
+ /* This case is documented that it may happen and that is perfectly fine */
+ if (err_code == SSL_ERROR_SYSCALL && priv->shutting_down && !my_error)
+ return G_TLS_CONNECTION_BASE_OK;
+
+ err = ERR_get_error ();
+ reason = ERR_GET_REASON (err);
+
+ if (tls->handshaking && !tls->ever_handshaked)
+ {
+ if (reason == SSL_R_BAD_PACKET_LENGTH ||
+ reason == SSL_R_UNKNOWN_ALERT_TYPE ||
+ reason == SSL_R_DECRYPTION_FAILED ||
+ reason == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC ||
+ reason == SSL_R_BAD_PROTOCOL_VERSION_NUMBER ||
+ reason == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE ||
+ reason == SSL_R_UNKNOWN_PROTOCOL)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+ _("Peer failed to perform TLS handshake"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+ }
+
+ if (reason == SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE ||
+ reason == SSL_R_NO_CERTIFICATE_RETURNED)
+ {
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+ _("TLS connection peer did not send a certificate"));
+ return status;
+ }
+
+ if (my_error != NULL)
+ g_propagate_error (error, my_error);
+ else
+ /* FIXME: this is just for debug */
+ g_message ("end_openssl_io %s: %d, %d", G_IS_TLS_CLIENT_CONNECTION (openssl) ? "client" : "server",
err_code, reason);
+
+ if (error && !*error)
+ {
+ va_list ap;
+
+ va_start (ap, err_fmt);
+ *error = g_error_new_valist (G_TLS_ERROR, G_TLS_ERROR_MISC, err_fmt, ap);
+ va_end (ap);
+ }
+
+ return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+#define BEGIN_OPENSSL_IO(openssl, direction, blocking, cancellable) \
+ g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (openssl), \
+ direction, blocking, cancellable); \
+ do {
+
+#define END_OPENSSL_IO(openssl, direction, ret, status, errmsg, err) \
+ status = end_openssl_io (openssl, direction, ret, err, errmsg, ERR_error_string (SSL_get_error (ssl,
ret), NULL)); \
+ } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_request_rehandshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionOpenssl *openssl;
+ GTlsConnectionBaseStatus status;
+ SSL *ssl;
+ int ret;
+
+ /* On a client-side connection, SSL_renegotiate() itself will start
+ * a rehandshake, so we only need to do something special here for
+ * server-side connections.
+ */
+ if (!G_IS_TLS_SERVER_CONNECTION (tls))
+ return G_TLS_CONNECTION_BASE_OK;
+
+ openssl = G_TLS_CONNECTION_OPENSSL (tls);
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+
+ BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+ ret = SSL_renegotiate (ssl);
+ END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, status,
+ _("Error performing TLS handshake: %s"), error);
+
+ return status;
+}
+
+static GTlsCertificate *
+get_peer_certificate (GTlsConnectionOpenssl *openssl)
+{
+ X509 *peer;
+ STACK_OF (X509) *certs;
+ GTlsCertificateOpenssl *chain;
+ SSL *ssl;
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+
+ peer = SSL_get_peer_certificate (ssl);
+ if (peer == NULL)
+ return NULL;
+
+ certs = SSL_get_peer_cert_chain (ssl);
+ if (certs == NULL)
+ return NULL;
+
+ chain = g_tls_certificate_openssl_build_chain (peer, certs);
+ if (!chain)
+ return NULL;
+
+ return G_TLS_CERTIFICATE (chain);
+}
+
+static GTlsCertificateFlags
+verify_peer_certificate (GTlsConnectionOpenssl *openssl,
+ GTlsCertificate *peer_certificate)
+{
+ GTlsConnection *conn = G_TLS_CONNECTION (openssl);
+ GSocketConnectable *peer_identity;
+ GTlsDatabase *database;
+ GTlsCertificateFlags errors;
+ gboolean is_client;
+
+ is_client = G_IS_TLS_CLIENT_CONNECTION (openssl);
+ if (is_client)
+ peer_identity = g_tls_client_connection_get_server_identity (G_TLS_CLIENT_CONNECTION (openssl));
+ else
+ peer_identity = NULL;
+
+ errors = 0;
+
+ database = g_tls_connection_get_database (conn);
+ if (database == NULL)
+ {
+ errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+ errors |= g_tls_certificate_verify (peer_certificate, peer_identity, NULL);
+ }
+ else
+ {
+ GError *error = NULL;
+
+ errors |= g_tls_database_verify_chain (database, peer_certificate,
+ is_client ?
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER :
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT,
+ peer_identity,
+ g_tls_connection_get_interaction (conn),
+ G_TLS_DATABASE_VERIFY_NONE,
+ NULL, &error);
+ if (error)
+ {
+ g_warning ("failure verifying certificate chain: %s",
+ error->message);
+ g_assert (errors != 0);
+ g_clear_error (&error);
+ }
+ }
+
+ return errors;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_handshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
+ GTlsConnectionOpensslPrivate *priv;
+ GTlsConnectionBaseStatus status;
+ SSL *ssl;
+ int ret;
+
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+
+ BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+ ret = SSL_do_handshake (ssl);
+ END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, status,
+ _("Error performing TLS handshake: %s"), error);
+
+ if (ret > 0)
+ {
+ priv->peer_certificate_tmp = get_peer_certificate (openssl);
+ if (priv->peer_certificate_tmp)
+ priv->peer_certificate_errors_tmp = verify_peer_certificate (openssl, priv->peer_certificate_tmp);
+ else if (G_IS_TLS_CLIENT_CONNECTION (openssl))
+ {
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Server did not return a valid TLS certificate"));
+ }
+ }
+
+ return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_complete_handshake (GTlsConnectionBase *tls,
+ GError **error)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
+ GTlsConnectionOpensslPrivate *priv;
+ GTlsCertificate *peer_certificate;
+ GTlsCertificateFlags peer_certificate_errors = 0;
+ GTlsConnectionBaseStatus status = G_TLS_CONNECTION_BASE_OK;
+
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ peer_certificate = priv->peer_certificate_tmp;
+ priv->peer_certificate_tmp = NULL;
+ peer_certificate_errors = priv->peer_certificate_errors_tmp;
+ priv->peer_certificate_errors_tmp = 0;
+
+ if (peer_certificate)
+ {
+ if (!g_tls_connection_base_accept_peer_certificate (tls, peer_certificate,
+ peer_certificate_errors))
+ {
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Unacceptable TLS certificate"));
+ status = G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ g_tls_connection_base_set_peer_certificate (G_TLS_CONNECTION_BASE (openssl),
+ peer_certificate,
+ peer_certificate_errors);
+ }
+
+ return status;
+}
+
+static void
+g_tls_connection_openssl_push_io (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean blocking,
+ GCancellable *cancellable)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
+ GTlsConnectionOpensslPrivate *priv;
+
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ G_TLS_CONNECTION_BASE_CLASS (g_tls_connection_openssl_parent_class)->push_io (tls, direction,
+ blocking, cancellable);
+
+ if (direction & G_IO_IN)
+ {
+ g_tls_bio_set_read_cancellable (priv->bio, cancellable);
+ g_tls_bio_set_read_blocking (priv->bio, blocking);
+ g_clear_error (&tls->read_error);
+ g_tls_bio_set_read_error (priv->bio, &tls->read_error);
+ }
+
+ if (direction & G_IO_OUT)
+ {
+ g_tls_bio_set_write_cancellable (priv->bio, cancellable);
+ g_tls_bio_set_write_blocking (priv->bio, blocking);
+ g_clear_error (&tls->write_error);
+ g_tls_bio_set_write_error (priv->bio, &tls->write_error);
+ }
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_pop_io (GTlsConnectionBase *tls,
+ GIOCondition direction,
+ gboolean success,
+ GError **error)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
+ GTlsConnectionOpensslPrivate *priv;
+
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ if (direction & G_IO_IN)
+ g_tls_bio_set_read_cancellable (priv->bio, NULL);
+
+ if (direction & G_IO_OUT)
+ g_tls_bio_set_write_cancellable (priv->bio, NULL);
+
+ return G_TLS_CONNECTION_BASE_CLASS (g_tls_connection_openssl_parent_class)->pop_io (tls, direction,
+ success, error);
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_read (GTlsConnectionBase *tls,
+ void *buffer,
+ gsize count,
+ gboolean blocking,
+ gssize *nread,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
+ GTlsConnectionBaseStatus status;
+ SSL *ssl;
+ gssize ret;
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+
+ BEGIN_OPENSSL_IO (openssl, G_IO_IN, blocking, cancellable);
+ ret = SSL_read (ssl, buffer, count);
+ END_OPENSSL_IO (openssl, G_IO_IN, ret, status,
+ _("Error reading data from TLS socket: %s"), error);
+
+ if (ret >= 0)
+ *nread = ret;
+ return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_write (GTlsConnectionBase *tls,
+ const void *buffer,
+ gsize count,
+ gboolean blocking,
+ gssize *nwrote,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
+ GTlsConnectionBaseStatus status;
+ SSL *ssl;
+ gssize ret;
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+
+ BEGIN_OPENSSL_IO (openssl, G_IO_OUT, blocking, cancellable);
+ ret = SSL_write (ssl, buffer, count);
+ END_OPENSSL_IO (openssl, G_IO_OUT, ret, status,
+ _("Error writing data to TLS socket: %s"), error);
+
+ if (ret >= 0)
+ *nwrote = ret;
+ return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_close (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
+ GTlsConnectionOpensslPrivate *priv;
+ GTlsConnectionBaseStatus status;
+ SSL *ssl;
+ int ret;
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ priv->shutting_down = TRUE;
+
+ BEGIN_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+ ret = SSL_shutdown (ssl);
+ END_OPENSSL_IO (openssl, G_IO_IN | G_IO_OUT, ret, status,
+ _("Error performing TLS close: %s"), error);
+
+ return status;
+}
+
+static void
+g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+
+ gobject_class->finalize = g_tls_connection_openssl_finalize;
+
+ base_class->request_rehandshake = g_tls_connection_openssl_request_rehandshake;
+ base_class->handshake = g_tls_connection_openssl_handshake;
+ base_class->complete_handshake = g_tls_connection_openssl_complete_handshake;
+ base_class->push_io = g_tls_connection_openssl_push_io;
+ base_class->pop_io = g_tls_connection_openssl_pop_io;
+ base_class->read_fn = g_tls_connection_openssl_read;
+ base_class->write_fn = g_tls_connection_openssl_write;
+ base_class->close_fn = g_tls_connection_openssl_close;
+}
+
+static gboolean
+g_tls_connection_openssl_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (initable);
+ GTlsConnectionOpensslPrivate *priv;
+ GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (initable);
+ SSL *ssl;
+
+ g_return_val_if_fail (tls->base_istream != NULL &&
+ tls->base_ostream != NULL, FALSE);
+
+ priv = g_tls_connection_openssl_get_instance_private (openssl);
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+ g_assert (ssl != NULL);
+
+ priv->bio = g_tls_bio_new (tls->base_io_stream);
+
+ SSL_set_bio (ssl, priv->bio, priv->bio);
+
+ return TRUE;
+}
+
+static void
+g_tls_connection_openssl_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = g_tls_connection_openssl_initable_init;
+}
+
+static void
+g_tls_connection_openssl_init (GTlsConnectionOpenssl *openssl)
+{
+}
+
+SSL *
+g_tls_connection_openssl_get_ssl (GTlsConnectionOpenssl *openssl)
+{
+ g_return_val_if_fail (G_IS_TLS_CONNECTION_OPENSSL (openssl), NULL);
+
+ return G_TLS_CONNECTION_OPENSSL_GET_CLASS (openssl)->get_ssl (openssl);
+}
+
+SSL_CTX *
+g_tls_connection_openssl_get_ssl_ctx (GTlsConnectionOpenssl *openssl)
+{
+ g_return_val_if_fail (G_IS_TLS_CONNECTION_OPENSSL (openssl), NULL);
+
+ return G_TLS_CONNECTION_OPENSSL_GET_CLASS (openssl)->get_ssl_ctx (openssl);
+}
+
+gboolean
+g_tls_connection_openssl_request_certificate (GTlsConnectionOpenssl *openssl,
+ GError **error)
+{
+ GTlsInteractionResult res = G_TLS_INTERACTION_UNHANDLED;
+ GTlsInteraction *interaction;
+ GTlsConnection *conn;
+ GTlsConnectionBase *tls;
+
+ g_return_val_if_fail (G_IS_TLS_CONNECTION_OPENSSL (openssl), FALSE);
+
+ conn = G_TLS_CONNECTION (openssl);
+ tls = G_TLS_CONNECTION_BASE (openssl);
+
+ interaction = g_tls_connection_get_interaction (conn);
+ if (!interaction)
+ return FALSE;
+
+ res = g_tls_interaction_invoke_request_certificate (interaction, conn, 0,
+ tls->read_cancellable, error);
+ return res != G_TLS_INTERACTION_FAILED;
+}
diff --git a/tls/openssl/gtlsconnection-openssl.h b/tls/openssl/gtlsconnection-openssl.h
new file mode 100644
index 0000000..0689189
--- /dev/null
+++ b/tls/openssl/gtlsconnection-openssl.h
@@ -0,0 +1,68 @@
+/*
+ * gtlsconnection-openssl.h
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#ifndef __G_TLS_CONNECTION_OPENSSL_H__
+#define __G_TLS_CONNECTION_OPENSSL_H__
+
+#include <gio/gio.h>
+
+#include "gtlsconnection-base.h"
+#include <openssl/ssl.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CONNECTION_OPENSSL (g_tls_connection_openssl_get_type ())
+#define G_TLS_CONNECTION_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst),
G_TYPE_TLS_CONNECTION_OPENSSL, GTlsConnectionOpenssl))
+#define G_TLS_CONNECTION_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),
G_TYPE_TLS_CONNECTION_OPENSSL, GTlsConnectionOpensslClass))
+#define G_IS_TLS_CONNECTION_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst),
G_TYPE_TLS_CONNECTION_OPENSSL))
+#define G_IS_TLS_CONNECTION_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),
G_TYPE_TLS_CONNECTION_OPENSSL))
+#define G_TLS_CONNECTION_OPENSSL_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst),
G_TYPE_TLS_CONNECTION_OPENSSL, GTlsConnectionOpensslClass))
+
+typedef struct _GTlsConnectionOpensslClass GTlsConnectionOpensslClass;
+typedef struct _GTlsConnectionOpenssl GTlsConnectionOpenssl;
+
+struct _GTlsConnectionOpensslClass
+{
+ GTlsConnectionBaseClass parent_class;
+
+ SSL *(*get_ssl) (GTlsConnectionOpenssl *connection);
+ SSL_CTX *(*get_ssl_ctx) (GTlsConnectionOpenssl *connection);
+};
+
+struct _GTlsConnectionOpenssl
+{
+ GTlsConnectionBase parent_instance;
+};
+
+GType g_tls_connection_openssl_get_type (void) G_GNUC_CONST;
+
+SSL *g_tls_connection_openssl_get_ssl (GTlsConnectionOpenssl *connection);
+SSL_CTX *g_tls_connection_openssl_get_ssl_ctx (GTlsConnectionOpenssl *connection);
+
+gboolean g_tls_connection_openssl_request_certificate (GTlsConnectionOpenssl *openssl,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CONNECTION_OPENSSL_H___ */
diff --git a/tls/openssl/gtlsdatabase-openssl.c b/tls/openssl/gtlsdatabase-openssl.c
new file mode 100644
index 0000000..93461a2
--- /dev/null
+++ b/tls/openssl/gtlsdatabase-openssl.c
@@ -0,0 +1,39 @@
+/*
+ * gtlsdatabase-openssl.c
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#include "config.h"
+
+#include "gtlsdatabase-openssl.h"
+
+G_DEFINE_ABSTRACT_TYPE (GTlsDatabaseOpenssl, g_tls_database_openssl, G_TYPE_TLS_DATABASE)
+
+static void
+g_tls_database_openssl_class_init (GTlsDatabaseOpensslClass *klass)
+{
+}
+
+static void
+g_tls_database_openssl_init (GTlsDatabaseOpenssl *openssl)
+{
+}
diff --git a/tls/openssl/gtlsdatabase-openssl.h b/tls/openssl/gtlsdatabase-openssl.h
new file mode 100644
index 0000000..fd31352
--- /dev/null
+++ b/tls/openssl/gtlsdatabase-openssl.h
@@ -0,0 +1,63 @@
+/*
+ * gtlsdatabase-openssl.h
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#ifndef __G_TLS_DATABASE_OPENSSL_H__
+#define __G_TLS_DATABASE_OPENSSL_H__
+
+#include <gio/gio.h>
+
+#include "gtlscertificate-openssl.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ G_TLS_DATABASE_OPENSSL_PINNED_CERTIFICATE = 1,
+ G_TLS_DATABASE_OPENSSL_ANCHORED_CERTIFICATE = 2,
+} GTlsDatabaseOpensslAssertion;
+
+#define G_TYPE_TLS_DATABASE_OPENSSL (g_tls_database_openssl_get_type ())
+#define G_TLS_DATABASE_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst),
G_TYPE_TLS_DATABASE_OPENSSL, GTlsDatabaseOpenssl))
+#define G_TLS_DATABASE_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),
G_TYPE_TLS_DATABASE_OPENSSL, GTlsDatabaseOpensslClass))
+#define G_IS_TLS_DATABASE_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst),
G_TYPE_TLS_DATABASE_OPENSSL))
+#define G_IS_TLS_DATABASE_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),
G_TYPE_TLS_DATABASE_OPENSSL))
+#define G_TLS_DATABASE_OPENSSL_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst),
G_TYPE_TLS_DATABASE_OPENSSL, GTlsDatabaseOpensslClass))
+
+typedef struct _GTlsDatabaseOpensslClass GTlsDatabaseOpensslClass;
+typedef struct _GTlsDatabaseOpenssl GTlsDatabaseOpenssl;
+
+struct _GTlsDatabaseOpensslClass
+{
+ GTlsDatabaseClass parent_class;
+};
+
+struct _GTlsDatabaseOpenssl
+{
+ GTlsDatabase parent_instance;
+};
+
+GType g_tls_database_openssl_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_DATABASE_OPENSSL_H___ */
diff --git a/tls/openssl/gtlsfiledatabase-openssl.c b/tls/openssl/gtlsfiledatabase-openssl.c
new file mode 100644
index 0000000..3f1c01d
--- /dev/null
+++ b/tls/openssl/gtlsfiledatabase-openssl.c
@@ -0,0 +1,911 @@
+/*
+ * gtlsfiledatabase-openssl.c
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#include "config.h"
+
+#include "gtlsfiledatabase-openssl.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <openssl/ssl.h>
+
+typedef struct _GTlsFileDatabaseOpensslPrivate
+{
+ /* read-only after construct */
+ gchar *anchor_filename;
+
+ /* protected by mutex */
+ GMutex mutex;
+
+ /*
+ * These are hash tables of gulong -> GPtrArray<GBytes>. The values of
+ * the ptr array are full DER encoded certificate values. The keys are byte
+ * arrays containing either subject DNs, issuer DNs, or full DER encoded certs
+ */
+ GHashTable *subjects;
+ GHashTable *issuers;
+
+ /*
+ * This is a table of GBytes -> GBytes. The values and keys are
+ * DER encoded certificate values.
+ */
+ GHashTable *complete;
+
+ /*
+ * This is a table of gchar * -> GTlsCertificate.
+ */
+ GHashTable *certs_by_handle;
+} GTlsFileDatabaseOpensslPrivate;
+
+enum {
+ STATUS_FAILURE,
+ STATUS_INCOMPLETE,
+ STATUS_SELFSIGNED,
+ STATUS_PINNED,
+ STATUS_ANCHORED,
+};
+
+enum
+{
+ PROP_0,
+ PROP_ANCHORS,
+};
+
+static void g_tls_file_database_openssl_file_database_interface_init (GTlsFileDatabaseInterface *iface);
+
+static void g_tls_file_database_openssl_initable_interface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsFileDatabaseOpenssl, g_tls_file_database_openssl, G_TYPE_TLS_DATABASE_OPENSSL,
+ G_ADD_PRIVATE (GTlsFileDatabaseOpenssl)
+ G_IMPLEMENT_INTERFACE (G_TYPE_TLS_FILE_DATABASE,
+ g_tls_file_database_openssl_file_database_interface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_tls_file_database_openssl_initable_interface_init))
+
+static GHashTable *
+bytes_multi_table_new (void)
+{
+ return g_hash_table_new_full (g_int_hash, g_int_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_ptr_array_unref);
+}
+
+static void
+bytes_multi_table_insert (GHashTable *table,
+ gulong key,
+ GBytes *value)
+{
+ GPtrArray *multi;
+
+ multi = g_hash_table_lookup (table, &key);
+ if (multi == NULL)
+ {
+ int *key_ptr;
+
+ key_ptr = g_new (int, 1);
+ *key_ptr = (int)key;
+ multi = g_ptr_array_new_with_free_func ((GDestroyNotify)g_bytes_unref);
+ g_hash_table_insert (table, key_ptr, multi);
+ }
+ g_ptr_array_add (multi, g_bytes_ref (value));
+}
+
+static GBytes *
+bytes_multi_table_lookup_ref_one (GHashTable *table,
+ gulong key)
+{
+ GPtrArray *multi;
+
+ multi = g_hash_table_lookup (table, &key);
+ if (multi == NULL)
+ return NULL;
+
+ g_assert (multi->len > 0);
+ return g_bytes_ref (multi->pdata[0]);
+}
+
+static GList *
+bytes_multi_table_lookup_ref_all (GHashTable *table,
+ gulong key)
+{
+ GPtrArray *multi;
+ GList *list = NULL;
+ guint i;
+
+ multi = g_hash_table_lookup (table, &key);
+ if (multi == NULL)
+ return NULL;
+
+ for (i = 0; i < multi->len; i++)
+ list = g_list_prepend (list, g_bytes_ref (multi->pdata[i]));
+
+ return g_list_reverse (list);
+}
+
+static gchar *
+create_handle_for_certificate (const gchar *filename,
+ GBytes *der)
+{
+ gchar *bookmark;
+ gchar *uri_part;
+ gchar *uri;
+
+ /*
+ * Here we create a URI that looks like:
+ *
file:///etc/ssl/certs/ca-certificates.crt#11b2641821252596420e468c275771f5e51022c121a17bd7a89a2f37b6336c8f
+ */
+
+ uri_part = g_filename_to_uri (filename, NULL, NULL);
+ if (!uri_part)
+ return NULL;
+
+ bookmark = g_compute_checksum_for_bytes (G_CHECKSUM_SHA256, der);
+ uri = g_strconcat (uri_part, "#", bookmark, NULL);
+
+ g_free (bookmark);
+ g_free (uri_part);
+
+ return uri;
+}
+
+static gboolean
+load_anchor_file (GTlsFileDatabaseOpenssl *file_database,
+ const gchar *filename,
+ GHashTable *subjects,
+ GHashTable *issuers,
+ GHashTable *complete,
+ GHashTable *certs_by_handle,
+ GError **error)
+{
+ GTlsFileDatabaseOpensslPrivate *priv;
+ GList *list;
+ GList *l;
+ GBytes *der;
+ gchar *handle;
+ GError *my_error = NULL;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ list = g_tls_certificate_list_new_from_file (filename, &my_error);
+ if (my_error)
+ {
+ g_propagate_error (error, my_error);
+ return FALSE;
+ }
+
+ for (l = list; l; l = l->next)
+ {
+ X509 *x;
+ unsigned long subject;
+ unsigned long issuer;
+
+ x = g_tls_certificate_openssl_get_cert (l->data);
+ subject = X509_subject_name_hash (x);
+ issuer = X509_issuer_name_hash (x);
+
+ der = g_tls_certificate_openssl_get_bytes (l->data);
+ g_return_val_if_fail (der != NULL, FALSE);
+
+ g_hash_table_insert (complete, g_bytes_ref (der),
+ g_bytes_ref (der));
+
+ bytes_multi_table_insert (subjects, subject, der);
+ bytes_multi_table_insert (issuers, issuer, der);
+
+ handle = create_handle_for_certificate (priv->anchor_filename, der);
+ g_hash_table_insert (certs_by_handle, handle, g_object_ref (l->data));
+
+ g_bytes_unref (der);
+
+ g_object_unref (l->data);
+ }
+ g_list_free (list);
+
+ return TRUE;
+}
+
+static void
+g_tls_file_database_openssl_finalize (GObject *object)
+{
+ GTlsFileDatabaseOpenssl *file_database = G_TLS_FILE_DATABASE_OPENSSL (object);
+ GTlsFileDatabaseOpensslPrivate *priv;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ g_clear_pointer (&priv->subjects, g_hash_table_destroy);
+ g_clear_pointer (&priv->issuers, g_hash_table_destroy);
+ g_clear_pointer (&priv->complete, g_hash_table_destroy);
+ g_clear_pointer (&priv->certs_by_handle, g_hash_table_destroy);
+
+ g_free (priv->anchor_filename);
+ priv->anchor_filename = NULL;
+
+ g_mutex_clear (&priv->mutex);
+
+ G_OBJECT_CLASS (g_tls_file_database_openssl_parent_class)->finalize (object);
+}
+
+static void
+g_tls_file_database_openssl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsFileDatabaseOpenssl *file_database = G_TLS_FILE_DATABASE_OPENSSL (object);
+ GTlsFileDatabaseOpensslPrivate *priv;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ switch (prop_id)
+ {
+ case PROP_ANCHORS:
+ g_value_set_string (value, priv->anchor_filename);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_file_database_openssl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsFileDatabaseOpenssl *file_database = G_TLS_FILE_DATABASE_OPENSSL (object);
+ GTlsFileDatabaseOpensslPrivate *priv;
+ gchar *anchor_path;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ switch (prop_id)
+ {
+ case PROP_ANCHORS:
+ anchor_path = g_value_dup_string (value);
+ if (anchor_path && !g_path_is_absolute (anchor_path))
+ {
+ g_warning ("The anchor file name for used with a GTlsFileDatabase "
+ "must be an absolute path, and not relative: %s", anchor_path);
+ }
+ else
+ {
+ priv->anchor_filename = anchor_path;
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_file_database_openssl_init (GTlsFileDatabaseOpenssl *file_database)
+{
+ GTlsFileDatabaseOpensslPrivate *priv;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ g_mutex_init (&priv->mutex);
+}
+
+static gchar *
+g_tls_file_database_openssl_create_certificate_handle (GTlsDatabase *database,
+ GTlsCertificate *certificate)
+{
+ GTlsFileDatabaseOpenssl *file_database = G_TLS_FILE_DATABASE_OPENSSL (database);
+ GTlsFileDatabaseOpensslPrivate *priv;
+ GBytes *der;
+ gboolean contains;
+ gchar *handle = NULL;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ der = g_tls_certificate_openssl_get_bytes (G_TLS_CERTIFICATE_OPENSSL (certificate));
+ g_return_val_if_fail (der != NULL, FALSE);
+
+ g_mutex_lock (&priv->mutex);
+
+ /* At the same time look up whether this certificate is in list */
+ contains = g_hash_table_lookup (priv->complete, der) ? TRUE : FALSE;
+
+ g_mutex_unlock (&priv->mutex);
+
+ /* Certificate is in the database */
+ if (contains)
+ handle = create_handle_for_certificate (priv->anchor_filename, der);
+
+ g_bytes_unref (der);
+ return handle;
+}
+
+static GTlsCertificate *
+g_tls_file_database_openssl_lookup_certificate_for_handle (GTlsDatabase *database,
+ const gchar *handle,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseOpenssl *file_database = G_TLS_FILE_DATABASE_OPENSSL (database);
+ GTlsFileDatabaseOpensslPrivate *priv;
+ GTlsCertificate *cert;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return NULL;
+
+ if (!handle)
+ return NULL;
+
+ g_mutex_lock (&priv->mutex);
+
+ cert = g_hash_table_lookup (priv->certs_by_handle, handle);
+
+ g_mutex_unlock (&priv->mutex);
+
+ return cert ? g_object_ref (cert) : NULL;
+}
+
+static GTlsCertificate *
+g_tls_file_database_openssl_lookup_certificate_issuer (GTlsDatabase *database,
+ GTlsCertificate *certificate,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseOpenssl *file_database = G_TLS_FILE_DATABASE_OPENSSL (database);
+ GTlsFileDatabaseOpensslPrivate *priv;
+ X509 *x;
+ unsigned long issuer_hash;
+ GBytes *der;
+ GTlsCertificate *issuer = NULL;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (certificate), NULL);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return NULL;
+
+ if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
+ return NULL;
+
+ /* Dig out the issuer of this certificate */
+ x = g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (certificate));
+ issuer_hash = X509_issuer_name_hash (x);
+
+ g_mutex_lock (&priv->mutex);
+ der = bytes_multi_table_lookup_ref_one (priv->subjects, issuer_hash);
+ g_mutex_unlock (&priv->mutex);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ issuer = NULL;
+ else if (der != NULL)
+ issuer = g_tls_certificate_openssl_new (der, NULL);
+
+ if (der != NULL)
+ g_bytes_unref (der);
+ return issuer;
+
+ return NULL;
+}
+
+static GList *
+g_tls_file_database_openssl_lookup_certificates_issued_by (GTlsDatabase *database,
+ GByteArray *issuer_raw_dn,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseOpenssl *file_database = G_TLS_FILE_DATABASE_OPENSSL (database);
+ GTlsFileDatabaseOpensslPrivate *priv;
+ X509_NAME *x_name;
+ const unsigned char *in;
+ GList *issued = NULL;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return NULL;
+
+ /* We don't have any private keys here */
+ if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
+ return NULL;
+
+ in = issuer_raw_dn->data;
+ x_name = d2i_X509_NAME (NULL, &in, issuer_raw_dn->len);
+ if (x_name != NULL)
+ {
+ unsigned long issuer_hash;
+ GList *ders, *l;
+
+ issuer_hash = X509_NAME_hash (x_name);
+
+ /* Find the full DER value of the certificate */
+ g_mutex_lock (&priv->mutex);
+ ders = bytes_multi_table_lookup_ref_all (priv->issuers, issuer_hash);
+ g_mutex_unlock (&priv->mutex);
+
+ for (l = ders; l != NULL; l = g_list_next (l))
+ {
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ {
+ g_list_free_full (issued, g_object_unref);
+ issued = NULL;
+ break;
+ }
+
+ issued = g_list_prepend (issued, g_tls_certificate_openssl_new (l->data, NULL));
+ }
+
+ g_list_free_full (ders, (GDestroyNotify)g_bytes_unref);
+ X509_NAME_free (x_name);
+ }
+
+ return issued;
+}
+
+static gboolean
+lookup_assertion (GTlsDatabaseOpenssl *database,
+ GTlsCertificateOpenssl *certificate,
+ GTlsDatabaseOpensslAssertion assertion,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseOpenssl *file_database = G_TLS_FILE_DATABASE_OPENSSL (database);
+ GTlsFileDatabaseOpensslPrivate *priv;
+ GBytes *der = NULL;
+ gboolean contains;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+
+ /* We only have anchored certificate assertions here */
+ if (assertion != G_TLS_DATABASE_OPENSSL_ANCHORED_CERTIFICATE)
+ return FALSE;
+
+ /*
+ * TODO: We should be parsing any Extended Key Usage attributes and
+ * comparing them to the purpose.
+ */
+
+ der = g_tls_certificate_openssl_get_bytes (certificate);
+
+ g_mutex_lock (&priv->mutex);
+ contains = g_hash_table_lookup (priv->complete, der) ? TRUE : FALSE;
+ g_mutex_unlock (&priv->mutex);
+
+ g_bytes_unref (der);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+
+ /* All certificates in our file are anchored certificates */
+ return contains;
+}
+
+static gboolean
+is_self_signed (GTlsCertificateOpenssl *certificate)
+{
+ X509 *cert;
+ X509_STORE *store;
+ X509_STORE_CTX csc;
+ STACK_OF(X509) *trusted;
+ gboolean ret = FALSE;
+
+ store = X509_STORE_new ();
+ cert = g_tls_certificate_openssl_get_cert (certificate);
+
+ if (!X509_STORE_CTX_init(&csc, store, cert, NULL))
+ goto end;
+
+ trusted = sk_X509_new_null ();
+ sk_X509_push (trusted, cert);
+
+ X509_STORE_CTX_trusted_stack (&csc, trusted);
+ X509_STORE_CTX_set_flags (&csc, X509_V_FLAG_CHECK_SS_SIGNATURE);
+
+ ret = X509_verify_cert (&csc) > 0;
+
+end:
+ X509_STORE_CTX_cleanup (&csc);
+ X509_STORE_free (store);
+
+ return ret;
+}
+
+static gint
+build_certificate_chain (GTlsDatabaseOpenssl *openssl,
+ GTlsCertificateOpenssl *chain,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GTlsDatabaseVerifyFlags flags,
+ GCancellable *cancellable,
+ GTlsCertificateOpenssl **anchor,
+ GError **error)
+{
+
+ GTlsCertificateOpenssl *certificate;
+ GTlsCertificateOpenssl *previous;
+ GTlsCertificate *issuer;
+ gboolean certificate_is_from_db;
+
+ g_assert (anchor);
+ g_assert (chain);
+ g_assert (purpose);
+ g_assert (error);
+ g_assert (!*error);
+
+ /*
+ * Remember that the first certificate never changes in the chain.
+ * When we find a self-signed, pinned or anchored certificate, all
+ * issuers are truncated from the chain.
+ */
+
+ *anchor = NULL;
+ previous = NULL;
+ certificate = chain;
+ certificate_is_from_db = FALSE;
+
+ /* First check for pinned certificate */
+ if (lookup_assertion (openssl, certificate,
+ G_TLS_DATABASE_OPENSSL_PINNED_CERTIFICATE,
+ purpose, identity, cancellable, error))
+ {
+ g_tls_certificate_openssl_set_issuer (certificate, NULL);
+ return STATUS_PINNED;
+ }
+ else if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+
+ for (;;)
+ {
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return STATUS_FAILURE;
+
+ /* Look up whether this certificate is an anchor */
+ if (lookup_assertion (openssl, certificate,
+ G_TLS_DATABASE_OPENSSL_ANCHORED_CERTIFICATE,
+ purpose, identity, cancellable, error))
+ {
+ g_tls_certificate_openssl_set_issuer (certificate, NULL);
+ *anchor = certificate;
+ return STATUS_ANCHORED;
+ }
+ else if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+
+ /* Is it self-signed? */
+ if (is_self_signed (certificate))
+ {
+ /*
+ * Since at this point we would fail with 'self-signed', can we replace
+ * this certificate with one from the database and do better?
+ */
+ if (previous && !certificate_is_from_db)
+ {
+ issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (openssl),
+ G_TLS_CERTIFICATE (previous),
+ interaction,
+ G_TLS_DATABASE_LOOKUP_NONE,
+ cancellable, error);
+ if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+ else if (issuer)
+ {
+ /* Replaced with certificate in the db, restart step again with this certificate */
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (issuer), STATUS_FAILURE);
+ g_tls_certificate_openssl_set_issuer (previous, G_TLS_CERTIFICATE_OPENSSL (issuer));
+ certificate = G_TLS_CERTIFICATE_OPENSSL (issuer);
+ certificate_is_from_db = TRUE;
+ g_object_unref (issuer);
+ continue;
+ }
+ }
+
+ g_tls_certificate_openssl_set_issuer (certificate, NULL);
+ return STATUS_SELFSIGNED;
+ }
+
+ previous = certificate;
+
+ /* Bring over the next certificate in the chain */
+ issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (certificate));
+ if (issuer)
+ {
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (issuer), STATUS_FAILURE);
+ certificate = G_TLS_CERTIFICATE_OPENSSL (issuer);
+ certificate_is_from_db = FALSE;
+ }
+
+ /* Search for the next certificate in chain */
+ else
+ {
+ issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (openssl),
+ G_TLS_CERTIFICATE (certificate),
+ interaction,
+ G_TLS_DATABASE_LOOKUP_NONE,
+ cancellable, error);
+ if (*error)
+ return STATUS_FAILURE;
+ else if (!issuer)
+ return STATUS_INCOMPLETE;
+
+ /* Found a certificate in chain, use for next step */
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (issuer), STATUS_FAILURE);
+ g_tls_certificate_openssl_set_issuer (certificate, G_TLS_CERTIFICATE_OPENSSL (issuer));
+ certificate = G_TLS_CERTIFICATE_OPENSSL (issuer);
+ certificate_is_from_db = TRUE;
+ g_object_unref (issuer);
+ }
+ }
+
+ g_assert_not_reached ();
+}
+
+static GTlsCertificateFlags
+double_check_before_after_dates (GTlsCertificateOpenssl *chain)
+{
+ GTlsCertificateFlags gtls_flags = 0;
+ X509 *cert;
+
+ while (chain)
+ {
+ ASN1_TIME *not_before;
+ ASN1_TIME *not_after;
+
+ cert = g_tls_certificate_openssl_get_cert (chain);
+ not_before = X509_get_notBefore (cert);
+ not_after = X509_get_notAfter (cert);
+
+ if (X509_cmp_current_time (not_before) > 0)
+ gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
+
+ if (X509_cmp_current_time (not_after) < 0)
+ gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
+
+ chain = G_TLS_CERTIFICATE_OPENSSL (g_tls_certificate_get_issuer
+ (G_TLS_CERTIFICATE (chain)));
+ }
+
+ return gtls_flags;
+}
+
+static STACK_OF(X509) *
+convert_certificate_chain_to_openssl (GTlsCertificateOpenssl *chain)
+{
+ GTlsCertificate *cert;
+ STACK_OF(X509) *openssl_chain;
+
+ openssl_chain = sk_X509_new_null ();
+
+ for (cert = G_TLS_CERTIFICATE (chain); cert; cert = g_tls_certificate_get_issuer (cert))
+ sk_X509_push (openssl_chain, g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (cert)));
+
+ return openssl_chain;
+}
+
+static GTlsCertificateFlags
+g_tls_file_database_openssl_verify_chain (GTlsDatabase *database,
+ GTlsCertificate *chain,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GTlsDatabaseVerifyFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsDatabaseOpenssl *openssl;
+ GTlsCertificateOpenssl *anchor;
+ STACK_OF(X509) *certs, *anchors;
+ X509_STORE *store;
+ X509_STORE_CTX csc;
+ X509 *x;
+ gint status;
+ GTlsCertificateFlags result = 0;
+ GError *err = NULL;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (chain),
+ G_TLS_CERTIFICATE_GENERIC_ERROR);
+
+ openssl = G_TLS_DATABASE_OPENSSL (database);
+ anchor = NULL;
+
+ status = build_certificate_chain (openssl, G_TLS_CERTIFICATE_OPENSSL (chain), purpose,
+ identity, interaction, flags, cancellable, &anchor, &err);
+ if (status == STATUS_FAILURE)
+ {
+ g_propagate_error (error, err);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ /*
+ * A pinned certificate is verified on its own, without any further
+ * verification.
+ */
+ if (status == STATUS_PINNED)
+ return 0;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ certs = convert_certificate_chain_to_openssl (G_TLS_CERTIFICATE_OPENSSL (chain));
+
+ store = X509_STORE_new ();
+
+ x = g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (chain));
+ if (!X509_STORE_CTX_init(&csc, store, x, certs))
+ {
+ X509_STORE_CTX_cleanup (&csc);
+ X509_STORE_free (store);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ if (anchor)
+ {
+ g_assert (g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (anchor)) == NULL);
+ anchors = convert_certificate_chain_to_openssl (G_TLS_CERTIFICATE_OPENSSL (anchor));
+ X509_STORE_CTX_trusted_stack (&csc, anchors);
+ }
+ else
+ anchors = NULL;
+
+ if (X509_verify_cert (&csc) <= 0)
+ result = g_tls_certificate_openssl_convert_error (X509_STORE_CTX_get_error (&csc));
+
+ X509_STORE_CTX_cleanup (&csc);
+ X509_STORE_free (store);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ /* We have to check these ourselves since openssl
+ * does not give us flags and UNKNOWN_CA will take priority.
+ */
+ result |= double_check_before_after_dates (G_TLS_CERTIFICATE_OPENSSL (chain));
+
+ if (identity)
+ result |= g_tls_certificate_openssl_verify_identity (G_TLS_CERTIFICATE_OPENSSL (chain),
+ identity);
+
+ return result;
+}
+
+static void
+g_tls_file_database_openssl_class_init (GTlsFileDatabaseOpensslClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+
+ gobject_class->get_property = g_tls_file_database_openssl_get_property;
+ gobject_class->set_property = g_tls_file_database_openssl_set_property;
+ gobject_class->finalize = g_tls_file_database_openssl_finalize;
+
+ database_class->create_certificate_handle = g_tls_file_database_openssl_create_certificate_handle;
+ database_class->lookup_certificate_for_handle = g_tls_file_database_openssl_lookup_certificate_for_handle;
+ database_class->lookup_certificate_issuer = g_tls_file_database_openssl_lookup_certificate_issuer;
+ database_class->lookup_certificates_issued_by = g_tls_file_database_openssl_lookup_certificates_issued_by;
+ database_class->verify_chain = g_tls_file_database_openssl_verify_chain;
+
+ g_object_class_override_property (gobject_class, PROP_ANCHORS, "anchors");
+}
+
+static void
+g_tls_file_database_openssl_file_database_interface_init (GTlsFileDatabaseInterface *iface)
+{
+}
+
+static gboolean
+g_tls_file_database_openssl_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseOpenssl *file_database = G_TLS_FILE_DATABASE_OPENSSL (initable);
+ GTlsFileDatabaseOpensslPrivate *priv;
+ GHashTable *subjects, *issuers, *complete, *certs_by_handle;
+ gboolean result;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+
+ subjects = bytes_multi_table_new ();
+ issuers = bytes_multi_table_new ();
+
+ complete = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
+ (GDestroyNotify)g_bytes_unref,
+ (GDestroyNotify)g_bytes_unref);
+
+ certs_by_handle = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_object_unref);
+
+ if (priv->anchor_filename)
+ result = load_anchor_file (file_database,
+ priv->anchor_filename,
+ subjects, issuers, complete,
+ certs_by_handle,
+ error);
+ else
+ result = TRUE;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ result = FALSE;
+
+ if (result)
+ {
+ g_mutex_lock (&priv->mutex);
+ if (!priv->subjects)
+ {
+ priv->subjects = subjects;
+ subjects = NULL;
+ }
+ if (!priv->issuers)
+ {
+ priv->issuers = issuers;
+ issuers = NULL;
+ }
+ if (!priv->complete)
+ {
+ priv->complete = complete;
+ complete = NULL;
+ }
+ if (!priv->certs_by_handle)
+ {
+ priv->certs_by_handle = certs_by_handle;
+ certs_by_handle = NULL;
+ }
+ g_mutex_unlock (&priv->mutex);
+ }
+
+ if (subjects != NULL)
+ g_hash_table_unref (subjects);
+ if (issuers != NULL)
+ g_hash_table_unref (issuers);
+ if (complete != NULL)
+ g_hash_table_unref (complete);
+ if (certs_by_handle != NULL)
+ g_hash_table_unref (certs_by_handle);
+ return result;
+}
+
+static void
+g_tls_file_database_openssl_initable_interface_init (GInitableIface *iface)
+{
+ iface->init = g_tls_file_database_openssl_initable_init;
+}
diff --git a/tls/openssl/gtlsfiledatabase-openssl.h b/tls/openssl/gtlsfiledatabase-openssl.h
new file mode 100644
index 0000000..4ec6627
--- /dev/null
+++ b/tls/openssl/gtlsfiledatabase-openssl.h
@@ -0,0 +1,64 @@
+/*
+ * gtlsfiledatabase-openssl.h
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#ifndef __G_TLS_FILE_DATABASE_OPENSSL_H__
+#define __G_TLS_FILE_DATABASE_OPENSSL_H__
+
+#include <gio/gio.h>
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include "gtlsdatabase-openssl.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_FILE_DATABASE_OPENSSL (g_tls_file_database_openssl_get_type ())
+#define G_TLS_FILE_DATABASE_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst),
G_TYPE_TLS_FILE_DATABASE_OPENSSL, GTlsFileDatabaseOpenssl))
+#define G_TLS_FILE_DATABASE_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),
G_TYPE_TLS_FILE_DATABASE_OPENSSL, GTlsFileDatabaseOpensslClass))
+#define G_IS_TLS_FILE_DATABASE_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst),
G_TYPE_TLS_FILE_DATABASE_OPENSSL))
+#define G_IS_TLS_FILE_DATABASE_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),
G_TYPE_TLS_FILE_DATABASE_OPENSSL))
+#define G_TLS_FILE_DATABASE_OPENSSL_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst),
G_TYPE_TLS_FILE_DATABASE_OPENSSL, GTlsFileDatabaseOpensslClass))
+
+typedef struct _GTlsFileDatabaseOpensslClass GTlsFileDatabaseOpensslClass;
+typedef struct _GTlsFileDatabaseOpenssl GTlsFileDatabaseOpenssl;
+
+struct _GTlsFileDatabaseOpensslClass
+{
+ GTlsDatabaseOpensslClass parent_class;
+};
+
+struct _GTlsFileDatabaseOpenssl
+{
+ GTlsDatabaseOpenssl parent_instance;
+};
+
+GType g_tls_file_database_openssl_get_type (void) G_GNUC_CONST;
+
+GTlsDatabase *g_tls_file_database_openssl_new (const gchar *anchor_file);
+
+G_END_DECLS
+
+#endif /* __G_TLS_FILE_DATABASE_OPENSSL_H___ */
diff --git a/tls/openssl/gtlsserverconnection-openssl.c b/tls/openssl/gtlsserverconnection-openssl.c
new file mode 100644
index 0000000..e9add65
--- /dev/null
+++ b/tls/openssl/gtlsserverconnection-openssl.c
@@ -0,0 +1,316 @@
+/*
+ * gtlsserverconnection-openssl.c
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#include "config.h"
+#include "glib.h"
+#include "gtlsserverconnection-openssl.h"
+#include "gtlscertificate-openssl.h"
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <glib/gi18n-lib.h>
+
+typedef struct _GTlsServerConnectionOpensslPrivate
+{
+ GTlsAuthenticationMode authentication_mode;
+ SSL_SESSION *session;
+ SSL *ssl;
+ SSL_CTX *ssl_ctx;
+} GTlsServerConnectionOpensslPrivate;
+
+enum
+{
+ PROP_0,
+ PROP_AUTHENTICATION_MODE
+};
+
+static void g_tls_server_connection_openssl_initable_interface_init (GInitableIface *iface);
+
+static void g_tls_server_connection_openssl_server_connection_interface_init (GTlsServerConnectionInterface
*iface);
+
+static GInitableIface *g_tls_server_connection_openssl_parent_initable_iface;
+
+G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionOpenssl, g_tls_server_connection_openssl,
G_TYPE_TLS_CONNECTION_OPENSSL,
+ G_ADD_PRIVATE (GTlsServerConnectionOpenssl)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_tls_server_connection_openssl_initable_interface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION,
+
g_tls_server_connection_openssl_server_connection_interface_init))
+
+static void
+g_tls_server_connection_openssl_finalize (GObject *object)
+{
+ GTlsServerConnectionOpenssl *openssl = G_TLS_SERVER_CONNECTION_OPENSSL (object);
+ GTlsServerConnectionOpensslPrivate *priv;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (openssl);
+
+ SSL_free (priv->ssl);
+ SSL_CTX_free (priv->ssl_ctx);
+ SSL_SESSION_free (priv->session);
+
+ G_OBJECT_CLASS (g_tls_server_connection_openssl_parent_class)->finalize (object);
+}
+
+static void
+g_tls_server_connection_openssl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsServerConnectionOpenssl *openssl = G_TLS_SERVER_CONNECTION_OPENSSL (object);
+ GTlsServerConnectionOpensslPrivate *priv;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (openssl);
+
+ switch (prop_id)
+ {
+ case PROP_AUTHENTICATION_MODE:
+ g_value_set_enum (value, priv->authentication_mode);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_server_connection_openssl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsServerConnectionOpenssl *openssl = G_TLS_SERVER_CONNECTION_OPENSSL (object);
+ GTlsServerConnectionOpensslPrivate *priv;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (openssl);
+
+ switch (prop_id)
+ {
+ case PROP_AUTHENTICATION_MODE:
+ priv->authentication_mode = g_value_get_enum (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static int
+verify_callback (int preverify_ok,
+ X509_STORE_CTX *ctx)
+{
+ return 1;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_server_connection_openssl_handshake (GTlsConnectionBase *tls,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsServerConnectionOpenssl *openssl = G_TLS_SERVER_CONNECTION_OPENSSL (tls);
+ GTlsServerConnectionOpensslPrivate *priv;
+ int req_mode = 0;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (openssl);
+
+ switch (priv->authentication_mode)
+ {
+ case G_TLS_AUTHENTICATION_REQUIRED:
+ req_mode = SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ case G_TLS_AUTHENTICATION_REQUESTED:
+ req_mode |= SSL_VERIFY_PEER;
+ break;
+ case G_TLS_AUTHENTICATION_NONE:
+ default:
+ req_mode = SSL_VERIFY_NONE;
+ break;
+ }
+
+ SSL_set_verify (priv->ssl, req_mode, verify_callback);
+ /* FIXME: is this ok? */
+ SSL_set_verify_depth (priv->ssl, 0);
+
+ return G_TLS_CONNECTION_BASE_CLASS (g_tls_server_connection_openssl_parent_class)->
+ handshake (tls, cancellable, error);
+}
+
+static SSL *
+g_tls_server_connection_openssl_get_ssl (GTlsConnectionOpenssl *connection)
+{
+ GTlsServerConnectionOpenssl *server = G_TLS_SERVER_CONNECTION_OPENSSL (connection);
+ GTlsServerConnectionOpensslPrivate *priv;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (server);
+
+ return priv->ssl;
+}
+
+static SSL_CTX *
+g_tls_server_connection_openssl_get_ssl_ctx (GTlsConnectionOpenssl *connection)
+{
+ GTlsServerConnectionOpenssl *server = G_TLS_SERVER_CONNECTION_OPENSSL (connection);
+ GTlsServerConnectionOpensslPrivate *priv;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (server);
+
+ return priv->ssl_ctx;
+}
+
+static void
+g_tls_server_connection_openssl_class_init (GTlsServerConnectionOpensslClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+ GTlsConnectionOpensslClass *connection_class = G_TLS_CONNECTION_OPENSSL_CLASS (klass);
+
+ gobject_class->finalize = g_tls_server_connection_openssl_finalize;
+ gobject_class->get_property = g_tls_server_connection_openssl_get_property;
+ gobject_class->set_property = g_tls_server_connection_openssl_set_property;
+
+ base_class->handshake = g_tls_server_connection_openssl_handshake;
+
+ connection_class->get_ssl = g_tls_server_connection_openssl_get_ssl;
+ connection_class->get_ssl_ctx = g_tls_server_connection_openssl_get_ssl_ctx;
+
+ g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
+}
+
+static void
+g_tls_server_connection_openssl_init (GTlsServerConnectionOpenssl *openssl)
+{
+}
+
+static void
+g_tls_server_connection_openssl_server_connection_interface_init (GTlsServerConnectionInterface *iface)
+{
+}
+
+static gboolean
+g_tls_server_connection_openssl_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsServerConnectionOpenssl *server = G_TLS_SERVER_CONNECTION_OPENSSL (initable);
+ GTlsServerConnectionOpensslPrivate *priv;
+ GTlsCertificate *cert;
+ long options;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (server);
+
+ priv->session = SSL_SESSION_new ();
+
+ priv->ssl_ctx = SSL_CTX_new (SSLv23_server_method ());
+ if (priv->ssl_ctx == NULL)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Could not create TLS context: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ return FALSE;
+ }
+
+ options = SSL_OP_NO_TICKET;
+
+ /* Only TLS 1.2 or higher */
+ SSL_CTX_set_options (priv->ssl_ctx, options);
+
+ cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (initable));
+ if (cert != NULL)
+ {
+ EVP_PKEY *key;
+ X509 *x;
+ GTlsCertificate *issuer;
+
+ key = g_tls_certificate_openssl_get_key (G_TLS_CERTIFICATE_OPENSSL (cert));
+
+ if (key == NULL)
+ {
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Certificate has no private key"));
+ return FALSE;
+ }
+
+ if (SSL_CTX_use_PrivateKey (priv->ssl_ctx, key) <= 0)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("There is a problem with the certificate private key: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ return FALSE;
+ }
+
+ x = g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (cert));
+ if (SSL_CTX_use_certificate (priv->ssl_ctx, x) <= 0)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("There is a problem with the certificate: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ return FALSE;
+ }
+
+ /* Add all the issuers to create the full certificate chain */
+ for (issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (cert));
+ issuer != NULL;
+ issuer = g_tls_certificate_get_issuer (issuer))
+ {
+ X509 *issuer_x;
+
+ /* Be careful here and duplicate the certificate since the context
+ * will take the ownership
+ */
+ issuer_x = X509_dup (g_tls_certificate_openssl_get_cert (G_TLS_CERTIFICATE_OPENSSL (issuer)));
+ if (!SSL_CTX_add_extra_chain_cert (priv->ssl_ctx, issuer_x))
+ g_warning ("There was a problem adding the extra chain certificate: %s",
+ ERR_error_string (ERR_get_error (), NULL));
+ }
+ }
+
+ SSL_CTX_add_session (priv->ssl_ctx, priv->session);
+
+ SSL_CTX_set_cipher_list (priv->ssl_ctx, "HIGH:!DSS:!aNULL STRENGTH");
+
+ priv->ssl = SSL_new (priv->ssl_ctx);
+ if (priv->ssl == NULL)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Could not create TLS connection: %s"),
+ ERR_error_string (ERR_get_error (), NULL));
+ return FALSE;
+ }
+
+ SSL_set_accept_state (priv->ssl);
+
+ if (!g_tls_server_connection_openssl_parent_initable_iface->
+ init (initable, cancellable, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+g_tls_server_connection_openssl_initable_interface_init (GInitableIface *iface)
+{
+ g_tls_server_connection_openssl_parent_initable_iface = g_type_interface_peek_parent (iface);
+
+ iface->init = g_tls_server_connection_openssl_initable_init;
+}
diff --git a/tls/openssl/gtlsserverconnection-openssl.h b/tls/openssl/gtlsserverconnection-openssl.h
new file mode 100644
index 0000000..96e0fb7
--- /dev/null
+++ b/tls/openssl/gtlsserverconnection-openssl.h
@@ -0,0 +1,57 @@
+/*
+ * gtlsserverconnection-openssl.h
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#ifndef __G_TLS_SERVER_CONNECTION_OPENSSL_H__
+#define __G_TLS_SERVER_CONNECTION_OPENSSL_H__
+
+#include <gio/gio.h>
+#include "gtlsconnection-openssl.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_SERVER_CONNECTION_OPENSSL (g_tls_server_connection_openssl_get_type ())
+#define G_TLS_SERVER_CONNECTION_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst),
G_TYPE_TLS_SERVER_CONNECTION_OPENSSL, GTlsServerConnectionOpenssl))
+#define G_TLS_SERVER_CONNECTION_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),
G_TYPE_TLS_SERVER_CONNECTION_OPENSSL, GTlsServerConnectionOpensslClass))
+#define G_IS_TLS_SERVER_CONNECTION_OPENSSL(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst),
G_TYPE_TLS_SERVER_CONNECTION_OPENSSL))
+#define G_IS_TLS_SERVER_CONNECTION_OPENSSL_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),
G_TYPE_TLS_SERVER_CONNECTION_OPENSSL))
+#define G_TLS_SERVER_CONNECTION_OPENSSL_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst),
G_TYPE_TLS_SERVER_CONNECTION_OPENSSL, GTlsServerConnectionOpensslClass))
+
+typedef struct _GTlsServerConnectionOpensslClass GTlsServerConnectionOpensslClass;
+typedef struct _GTlsServerConnectionOpenssl GTlsServerConnectionOpenssl;
+
+struct _GTlsServerConnectionOpensslClass
+{
+ GTlsConnectionOpensslClass parent_class;
+};
+
+struct _GTlsServerConnectionOpenssl
+{
+ GTlsConnectionOpenssl parent_instance;
+};
+
+GType g_tls_server_connection_openssl_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_SERVER_CONNECTION_OPENSSL_H___ */
diff --git a/tls/openssl/openssl-module.c b/tls/openssl/openssl-module.c
new file mode 100644
index 0000000..f3e8b18
--- /dev/null
+++ b/tls/openssl/openssl-module.c
@@ -0,0 +1,51 @@
+/*
+ * gtlsbio.c
+ *
+ * Copyright (C) 2015 NICE s.r.l.
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Authors: Ignacio Casal Quinteiro
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gtlsbackend-openssl.h"
+
+
+void
+g_io_module_load (GIOModule *module)
+{
+ g_tls_backend_openssl_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/openssl/openssl-util.c b/tls/openssl/openssl-util.c
new file mode 100644
index 0000000..5ba63f1
--- /dev/null
+++ b/tls/openssl/openssl-util.c
@@ -0,0 +1,487 @@
+/* v3_utl.c */
+/*
+ * Written by Dr Stephen N Henson (steve openssl org) for the OpenSSL
+ * project.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2003 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * licensing OpenSSL org
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay cryptsoft com). This product includes software written by Tim
+ * Hudson (tjh cryptsoft com).
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ */
+/* X509 v3 extension utilities */
+
+/* NOTE: this has been copied from openssl */
+
+#include "openssl-util.h"
+#include <string.h>
+#include <openssl/x509v3.h>
+
+#ifdef _MSC_VER
+#define strncasecmp _strnicmp
+#endif
+
+typedef int (*equal_fn) (const unsigned char *pattern, size_t pattern_len,
+ const unsigned char *subject, size_t subject_len,
+ unsigned int flags);
+
+
+/* Skip pattern prefix to match "wildcard" subject */
+static void skip_prefix(const unsigned char **p, size_t *plen,
+ const unsigned char *subject, size_t subject_len,
+ unsigned int flags)
+{
+ const unsigned char *pattern = *p;
+ size_t pattern_len = *plen;
+
+ /*
+ * If subject starts with a leading '.' followed by more octets, and
+ * pattern is longer, compare just an equal-length suffix with the
+ * full subject (starting at the '.'), provided the prefix contains
+ * no NULs.
+ */
+ if ((flags & _G_TLS_X509_CHECK_FLAG_DOT_SUBDOMAINS) == 0)
+ return;
+
+ while (pattern_len > subject_len && *pattern) {
+ if ((flags & G_TLS_X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS) &&
+ *pattern == '.')
+ break;
+ ++pattern;
+ --pattern_len;
+ }
+
+ /* Skip if entire prefix acceptable */
+ if (pattern_len == subject_len) {
+ *p = pattern;
+ *plen = pattern_len;
+ }
+}
+
+/* Compare while ASCII ignoring case. */
+static int equal_nocase(const unsigned char *pattern, size_t pattern_len,
+ const unsigned char *subject, size_t subject_len,
+ unsigned int flags)
+{
+ skip_prefix(&pattern, &pattern_len, subject, subject_len, flags);
+ if (pattern_len != subject_len)
+ return 0;
+ while (pattern_len) {
+ unsigned char l = *pattern;
+ unsigned char r = *subject;
+ /* The pattern must not contain NUL characters. */
+ if (l == 0)
+ return 0;
+ if (l != r) {
+ if ('A' <= l && l <= 'Z')
+ l = (l - 'A') + 'a';
+ if ('A' <= r && r <= 'Z')
+ r = (r - 'A') + 'a';
+ if (l != r)
+ return 0;
+ }
+ ++pattern;
+ ++subject;
+ --pattern_len;
+ }
+ return 1;
+}
+
+/* Compare using memcmp. */
+static int equal_case(const unsigned char *pattern, size_t pattern_len,
+ const unsigned char *subject, size_t subject_len,
+ unsigned int flags)
+{
+ skip_prefix(&pattern, &pattern_len, subject, subject_len, flags);
+ if (pattern_len != subject_len)
+ return 0;
+ return !memcmp(pattern, subject, pattern_len);
+}
+
+/*
+ * RFC 5280, section 7.5, requires that only the domain is compared in a
+ * case-insensitive manner.
+ */
+static int equal_email(const unsigned char *a, size_t a_len,
+ const unsigned char *b, size_t b_len,
+ unsigned int unused_flags)
+{
+ size_t i = a_len;
+ if (a_len != b_len)
+ return 0;
+ /*
+ * We search backwards for the '@' character, so that we do not have to
+ * deal with quoted local-parts. The domain part is compared in a
+ * case-insensitive manner.
+ */
+ while (i > 0) {
+ --i;
+ if (a[i] == '@' || b[i] == '@') {
+ if (!equal_nocase(a + i, a_len - i, b + i, a_len - i, 0))
+ return 0;
+ break;
+ }
+ }
+ if (i == 0)
+ i = a_len;
+ return equal_case(a, i, b, i, 0);
+}
+
+/*
+ * Compare an ASN1_STRING to a supplied string. If they match return 1. If
+ * cmp_type > 0 only compare if string matches the type, otherwise convert it
+ * to UTF8.
+ */
+
+static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
+ unsigned int flags, const char *b, size_t blen,
+ char **peername)
+{
+ int rv = 0;
+
+ if (!a->data || !a->length)
+ return 0;
+ if (cmp_type > 0) {
+ if (cmp_type != a->type)
+ return 0;
+ if (cmp_type == V_ASN1_IA5STRING)
+ rv = equal(a->data, a->length, (unsigned char *)b, blen, flags);
+ else if (a->length == (int)blen && !memcmp(a->data, b, blen))
+ rv = 1;
+ if (rv > 0 && peername)
+ *peername = BUF_strndup((char *)a->data, a->length);
+ } else {
+ int astrlen;
+ unsigned char *astr;
+ astrlen = ASN1_STRING_to_UTF8(&astr, a);
+ if (astrlen < 0) {
+ /*
+ * -1 could be an internal malloc failure or a decoding error from
+ * malformed input; we can't distinguish.
+ */
+ return -1;
+ }
+ rv = equal(astr, astrlen, (unsigned char *)b, blen, flags);
+ if (rv > 0 && peername)
+ *peername = BUF_strndup((char *)astr, astrlen);
+ OPENSSL_free(astr);
+ }
+ return rv;
+}
+
+/*
+ * Compare the prefix and suffix with the subject, and check that the
+ * characters in-between are valid.
+ */
+static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
+ const unsigned char *suffix, size_t suffix_len,
+ const unsigned char *subject, size_t subject_len,
+ unsigned int flags)
+{
+ const unsigned char *wildcard_start;
+ const unsigned char *wildcard_end;
+ const unsigned char *p;
+ int allow_multi = 0;
+ int allow_idna = 0;
+
+ if (subject_len < prefix_len + suffix_len)
+ return 0;
+ if (!equal_nocase(prefix, prefix_len, subject, prefix_len, flags))
+ return 0;
+ wildcard_start = subject + prefix_len;
+ wildcard_end = subject + (subject_len - suffix_len);
+ if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len, flags))
+ return 0;
+ /*
+ * If the wildcard makes up the entire first label, it must match at
+ * least one character.
+ */
+ if (prefix_len == 0 && *suffix == '.') {
+ if (wildcard_start == wildcard_end)
+ return 0;
+ allow_idna = 1;
+ if (flags & G_TLS_X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS)
+ allow_multi = 1;
+ }
+ /* IDNA labels cannot match partial wildcards */
+ if (!allow_idna &&
+ subject_len >= 4 && strncasecmp((char *)subject, "xn--", 4) == 0)
+ return 0;
+ /* The wildcard may match a literal '*' */
+ if (wildcard_end == wildcard_start + 1 && *wildcard_start == '*')
+ return 1;
+ /*
+ * Check that the part matched by the wildcard contains only
+ * permitted characters and only matches a single label unless
+ * allow_multi is set.
+ */
+ for (p = wildcard_start; p != wildcard_end; ++p)
+ if (!(('0' <= *p && *p <= '9') ||
+ ('A' <= *p && *p <= 'Z') ||
+ ('a' <= *p && *p <= 'z') ||
+ *p == '-' || (allow_multi && *p == '.')))
+ return 0;
+ return 1;
+}
+
+#define LABEL_START (1 << 0)
+#define LABEL_END (1 << 1)
+#define LABEL_HYPHEN (1 << 2)
+#define LABEL_IDNA (1 << 3)
+
+static const unsigned char *valid_star(const unsigned char *p, size_t len,
+ unsigned int flags)
+{
+ const unsigned char *star = 0;
+ size_t i;
+ int state = LABEL_START;
+ int dots = 0;
+ for (i = 0; i < len; ++i) {
+ /*
+ * Locate first and only legal wildcard, either at the start
+ * or end of a non-IDNA first and not final label.
+ */
+ if (p[i] == '*') {
+ int atstart = (state & LABEL_START);
+ int atend = (i == len - 1 || p[i + 1] == '.');
+ /*-
+ * At most one wildcard per pattern.
+ * No wildcards in IDNA labels.
+ * No wildcards after the first label.
+ */
+ if (star != NULL || (state & LABEL_IDNA) != 0 || dots)
+ return NULL;
+ /* Only full-label '*.example.com' wildcards? */
+ if ((flags & G_TLS_X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS)
+ && (!atstart || !atend))
+ return NULL;
+ /* No 'foo*bar' wildcards */
+ if (!atstart && !atend)
+ return NULL;
+ star = &p[i];
+ state &= ~LABEL_START;
+ } else if (('a' <= p[i] && p[i] <= 'z')
+ || ('A' <= p[i] && p[i] <= 'Z')
+ || ('0' <= p[i] && p[i] <= '9')) {
+ if ((state & LABEL_START) != 0
+ && len - i >= 4 && strncasecmp((char *)&p[i], "xn--", 4) == 0)
+ state |= LABEL_IDNA;
+ state &= ~(LABEL_HYPHEN | LABEL_START);
+ } else if (p[i] == '.') {
+ if ((state & (LABEL_HYPHEN | LABEL_START)) != 0)
+ return NULL;
+ state = LABEL_START;
+ ++dots;
+ } else if (p[i] == '-') {
+ if ((state & LABEL_HYPHEN) != 0)
+ return NULL;
+ state |= LABEL_HYPHEN;
+ } else
+ return NULL;
+ }
+
+ /*
+ * The final label must not end in a hyphen or ".", and
+ * there must be at least two dots after the star.
+ */
+ if ((state & (LABEL_START | LABEL_HYPHEN)) != 0 || dots < 2)
+ return NULL;
+ return star;
+}
+
+/* Compare using wildcards. */
+static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
+ const unsigned char *subject, size_t subject_len,
+ unsigned int flags)
+{
+ const unsigned char *star = NULL;
+
+ /*
+ * Subject names starting with '.' can only match a wildcard pattern
+ * via a subject sub-domain pattern suffix match.
+ */
+ if (!(subject_len > 1 && subject[0] == '.'))
+ star = valid_star(pattern, pattern_len, flags);
+ if (star == NULL)
+ return equal_nocase(pattern, pattern_len,
+ subject, subject_len, flags);
+ return wildcard_match(pattern, star - pattern,
+ star + 1, (pattern + pattern_len) - star - 1,
+ subject, subject_len, flags);
+}
+
+static int do_x509_check(X509 *x, const char *chk, size_t chklen,
+ unsigned int flags, int check_type, char **peername)
+{
+ GENERAL_NAMES *gens = NULL;
+ X509_NAME *name = NULL;
+ int i;
+ int cnid;
+ int alt_type;
+ int san_present = 0;
+ int rv = 0;
+ equal_fn equal;
+
+ /* See below, this flag is internal-only */
+ flags &= ~_G_TLS_X509_CHECK_FLAG_DOT_SUBDOMAINS;
+ if (check_type == GEN_EMAIL) {
+ cnid = NID_pkcs9_emailAddress;
+ alt_type = V_ASN1_IA5STRING;
+ equal = equal_email;
+ } else if (check_type == GEN_DNS) {
+ cnid = NID_commonName;
+ /* Implicit client-side DNS sub-domain pattern */
+ if (chklen > 1 && chk[0] == '.')
+ flags |= _G_TLS_X509_CHECK_FLAG_DOT_SUBDOMAINS;
+ alt_type = V_ASN1_IA5STRING;
+ if (flags & G_TLS_X509_CHECK_FLAG_NO_WILDCARDS)
+ equal = equal_nocase;
+ else
+ equal = equal_wildcard;
+ } else {
+ cnid = 0;
+ alt_type = V_ASN1_OCTET_STRING;
+ equal = equal_case;
+ }
+
+ if (chklen == 0)
+ chklen = strlen(chk);
+
+ gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+ if (gens) {
+ for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
+ GENERAL_NAME *gen;
+ ASN1_STRING *cstr;
+ gen = sk_GENERAL_NAME_value(gens, i);
+ if (gen->type != check_type)
+ continue;
+ san_present = 1;
+ if (check_type == GEN_EMAIL)
+ cstr = gen->d.rfc822Name;
+ else if (check_type == GEN_DNS)
+ cstr = gen->d.dNSName;
+ else
+ cstr = gen->d.iPAddress;
+ /* Positive on success, negative on error! */
+ if ((rv = do_check_string(cstr, alt_type, equal, flags,
+ chk, chklen, peername)) != 0)
+ break;
+ }
+ GENERAL_NAMES_free(gens);
+ if (rv != 0)
+ return rv;
+ if (!cnid
+ || (san_present
+ && !(flags & G_TLS_X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT)))
+ return 0;
+ }
+ i = -1;
+ name = X509_get_subject_name(x);
+ while ((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0) {
+ X509_NAME_ENTRY *ne;
+ ASN1_STRING *str;
+ ne = X509_NAME_get_entry(name, i);
+ str = X509_NAME_ENTRY_get_data(ne);
+ /* Positive on success, negative on error! */
+ if ((rv = do_check_string(str, -1, equal, flags,
+ chk, chklen, peername)) != 0)
+ return rv;
+ }
+ return 0;
+}
+
+int g_tls_X509_check_host(X509 *x, const char *chk, size_t chklen,
+ unsigned int flags, char **peername)
+{
+ if (chk == NULL)
+ return -2;
+ /*
+ * Embedded NULs are disallowed, except as the last character of a
+ * string of length 2 or more (tolerate caller including terminating
+ * NUL in string length).
+ */
+ if (chklen == 0)
+ chklen = strlen(chk);
+ else if (memchr(chk, '\0', chklen > 1 ? chklen - 1 : chklen))
+ return -2;
+ if (chklen > 1 && chk[chklen - 1] == '\0')
+ --chklen;
+ return do_x509_check(x, chk, chklen, flags, GEN_DNS, peername);
+}
+
+int g_tls_X509_check_email(X509 *x, const char *chk, size_t chklen,
+ unsigned int flags)
+{
+ if (chk == NULL)
+ return -2;
+ /*
+ * Embedded NULs are disallowed, except as the last character of a
+ * string of length 2 or more (tolerate caller including terminating
+ * NUL in string length).
+ */
+ if (chklen == 0)
+ chklen = strlen((char *)chk);
+ else if (memchr(chk, '\0', chklen > 1 ? chklen - 1 : chklen))
+ return -2;
+ if (chklen > 1 && chk[chklen - 1] == '\0')
+ --chklen;
+ return do_x509_check(x, chk, chklen, flags, GEN_EMAIL, NULL);
+}
+
+int g_tls_X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
+ unsigned int flags)
+{
+ if (chk == NULL)
+ return -2;
+ return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, NULL);
+}
diff --git a/tls/openssl/openssl-util.h b/tls/openssl/openssl-util.h
new file mode 100644
index 0000000..72cd2c9
--- /dev/null
+++ b/tls/openssl/openssl-util.h
@@ -0,0 +1,99 @@
+/* v3_utl.c */
+/*
+ * Written by Dr Stephen N Henson (steve openssl org) for the OpenSSL
+ * project.
+ */
+/* ====================================================================
+ * Copyright (c) 1999-2003 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * licensing OpenSSL org
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay cryptsoft com). This product includes software written by Tim
+ * Hudson (tjh cryptsoft com).
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ */
+/* X509 v3 extension utilities */
+
+#ifndef __G_TLS_OPENSSL_UTIL_H__
+#define __G_TLS_OPENSSL_UTIL_H__
+
+#include <openssl/x509.h>
+
+/*
+ * Always check subject name for host match even if subject alt names present
+ */
+# define G_TLS_X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1
+/* Disable wildcard matching for dnsName fields and common name. */
+# define G_TLS_X509_CHECK_FLAG_NO_WILDCARDS 0x2
+/* Wildcards must not match a partial label. */
+# define G_TLS_X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0x4
+/* Allow (non-partial) wildcards to match multiple labels. */
+# define G_TLS_X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS 0x8
+/* Constraint verifier subdomain patterns to match a single labels. */
+# define G_TLS_X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0x10
+/*
+ * Match reference identifiers starting with "." to any sub-domain.
+ * This is a non-public flag, turned on implicitly when the subject
+ * reference identity is a DNS name.
+ */
+# define _G_TLS_X509_CHECK_FLAG_DOT_SUBDOMAINS 0x8000
+
+int g_tls_X509_check_host(X509 *x, const char *chk, size_t chklen,
+ unsigned int flags, char **peername);
+
+int g_tls_X509_check_email(X509 *x, const char *chk, size_t chklen,
+ unsigned int flags);
+
+int g_tls_X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
+ unsigned int flags);
+
+#endif /* __G_TLS_OPENSSL_UTIL_H__ */
+
+
diff --git a/tls/tests/Makefile.am b/tls/tests/Makefile.am
index 0ecd483..8245c49 100644
--- a/tls/tests/Makefile.am
+++ b/tls/tests/Makefile.am
@@ -32,6 +32,27 @@ file_database_gnutls_LDADD = $(GLIB_LIBS) $(GNUTLS_LIBS)
endif
+if HAVE_OPENSSL
+test_programs += \
+ certificate-openssl \
+ file-database-openssl \
+ connection-openssl \
+ $(NULL)
+
+certificate_openssl_SOURCES = certificate.c
+certificate_openssl_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_CFLAGS) -DBACKEND=\""openssl"\"
-DWITH_BACKEND_OPENSSL
+certificate_openssl_LDADD = $(GLIB_LIBS) $(OPENSSL_LIBS)
+
+connection_openssl_SOURCES = connection.c mock-interaction.c mock-interaction.h
+connection_openssl_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_CFLAGS) -DBACKEND=\""openssl"\" -DWITH_BACKEND_OPENSSL
+connection_openssl_LDADD = $(GLIB_LIBS) $(OPENSSL_LIBS)
+
+file_database_openssl_SOURCES = file-database.c
+file_database_openssl_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_CFLAGS) -DBACKEND=\""openssl"\"
-DWITH_BACKEND_OPENSSL
+file_database_openssl_LDADD = $(GLIB_LIBS) $(OPENSSL_LIBS)
+
+endif
+
if HAVE_PKCS11
test_programs += \
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 8f4095e..beb8243 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -1439,6 +1439,7 @@ test_simultaneous_async (TestConnection *test,
g_assert_cmpstr (test->buf, ==, TEST_DATA);
}
+#ifdef WITH_BACKEND_GNUTLS
static gboolean
check_gnutls_has_rehandshaking_bug (void)
{
@@ -1451,16 +1452,22 @@ check_gnutls_has_rehandshaking_bug (void)
!strcmp (version, "3.3.9") ||
!strcmp (version, "3.3.10"));
}
+#endif
static void
test_simultaneous_async_rehandshake (TestConnection *test,
gconstpointer data)
{
+#ifdef WITH_BACKEND_GNUTLS
if (check_gnutls_has_rehandshaking_bug ())
{
g_test_skip ("test would fail due to gnutls bug 108690");
return;
}
+#else
+ g_test_skip ("this needs more research on openssl");
+ return;
+#endif
test->rehandshake = TRUE;
test_simultaneous_async (test, data);
@@ -1556,11 +1563,16 @@ static void
test_simultaneous_sync_rehandshake (TestConnection *test,
gconstpointer data)
{
+#ifdef WITH_BACKEND_GNUTLS
if (check_gnutls_has_rehandshaking_bug ())
{
g_test_skip ("test would fail due to gnutls bug 108690");
return;
}
+#else
+ g_test_skip ("this needs more research on openssl");
+ return;
+#endif
test->rehandshake = TRUE;
test_simultaneous_sync (test, data);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]