[glib-networking/wip/nacho/openssl: 11/21] Add openssl backend
- From: Ignacio Casal Quinteiro <icq src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/nacho/openssl: 11/21] Add openssl backend
- Date: Tue, 18 Sep 2018 13:14:36 +0000 (UTC)
commit b4bfe5150f731726732c894306980d91716b86fe
Author: Ignacio Casal Quinteiro <qignacio amazon com>
Date: Mon Sep 10 15:11:23 2018 +0200
Add openssl backend
meson.build | 49 ++
meson_options.txt | 1 +
tls/openssl/gtlsbackend-openssl.c | 292 ++++++++++
tls/openssl/gtlsbackend-openssl.h | 48 ++
tls/openssl/gtlsbio.c | 398 ++++++++++++++
tls/openssl/gtlsbio.h | 55 ++
tls/openssl/gtlscertificate-openssl.c | 701 ++++++++++++++++++++++++
tls/openssl/gtlscertificate-openssl.h | 69 +++
tls/openssl/gtlsclientconnection-openssl.c | 573 +++++++++++++++++++
tls/openssl/gtlsclientconnection-openssl.h | 56 ++
tls/openssl/gtlsconnection-openssl.c | 644 ++++++++++++++++++++++
tls/openssl/gtlsconnection-openssl.h | 68 +++
tls/openssl/gtlsdatabase-openssl.c | 39 ++
tls/openssl/gtlsdatabase-openssl.h | 63 +++
tls/openssl/gtlsfiledatabase-openssl.c | 852 +++++++++++++++++++++++++++++
tls/openssl/gtlsfiledatabase-openssl.h | 62 +++
tls/openssl/gtlsserverconnection-openssl.c | 421 ++++++++++++++
tls/openssl/gtlsserverconnection-openssl.h | 57 ++
tls/openssl/meson.build | 44 ++
tls/openssl/openssl-include.h | 58 ++
tls/openssl/openssl-module.c | 65 +++
tls/openssl/openssl-util.c | 488 +++++++++++++++++
tls/openssl/openssl-util.h | 99 ++++
23 files changed, 5202 insertions(+)
---
diff --git a/meson.build b/meson.build
index 46cd898..d679c11 100644
--- a/meson.build
+++ b/meson.build
@@ -85,6 +85,51 @@ if gnutls_dep.found()
endif
endif
+# *** Checks for OpenSSL ***
+openssl_dep = dependency('openssl', required: get_option('openssl'))
+if openssl_dep.found()
+ backends += ['openssl']
+elif cc.get_id() == 'msvc'
+ # MSVC builds of OpenSSL does not generate pkg-config files,
+ # so we check for it manually here in this case, if we can't find those files
+ # Based on the CMake check for OpenSSL in CURL's CMakeLists.txt,
+ # on which headers we should check for
+ cc.has_header('openssl/crypto.h')
+ cc.has_header('openssl/engine.h')
+ cc.has_header('openssl/err.h')
+ cc.has_header('openssl/pem.h')
+ cc.has_header('openssl/rsa.h')
+ cc.has_header('openssl/ssl.h')
+ cc.has_header('openssl/x509.h')
+ cc.has_header('openssl/rand.h')
+ cc.has_header('openssl/tls1.h')
+
+ # OpenSSL 1.1.x and 1.0.x (or earlier) have different .lib names,
+ # so we need to look for the correct pair
+
+ # Find either libcrypto.lib (1.1.x) or libeay32.lib (1.0.x or earlier) first
+ libcrypto_dep = cc.find_library('libcrypto', required: false)
+ if not libcrypto_dep.found()
+ libeay_dep = cc.find_library('libeay32')
+ endif
+
+ # Find the corresponding SSL library depending on which crypto .lib we found
+ if libcrypto_dep.found()
+ libssl_dep = cc.find_library('libssl')
+ elif libeay_dep.found()
+ ssleay_dep = cc.find_library('ssleay32')
+ endif
+
+ # Now set the openssl dependency to the corresponding pair of .lib files that we found
+ if libcrypto_dep.found()
+ openssl_dep = [libcrypto_dep, libssl_dep]
+ else
+ openssl_dep = [libeay_dep, ssleay_dep]
+ endif
+
+ backends += ['openssl']
+endif
+
if backends.length() == 0
error('No TLS backends enabled. Please enable at least one TLS backend')
endif
@@ -139,6 +184,10 @@ if gnutls_dep.found()
subdir('tls/gnutls')
endif
+if backends.contains('openssl')
+ subdir('tls/openssl')
+endif
+
subdir('tls/tests')
# Will automatically pick it up from the cross file if defined
diff --git a/meson_options.txt b/meson_options.txt
index 9ae11ed..04706be 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,4 +1,5 @@
option('gnutls', type: 'feature', value: 'auto', description: 'support for GnuTLS networking configration')
+option('openssl', type: 'feature', value: 'disabled', description: 'support for OpenSSL networking
configration')
option('libproxy', type: 'feature', value: 'auto', description: 'support for libproxy proxy configration')
option('gnome_proxy', type: 'feature', value: 'auto', description: 'support for GNOME desktop proxy
configuration')
option('pkcs11', type: 'feature', value: 'disabled', description: 'support for PKCS#11 using p11-kit')
diff --git a/tls/openssl/gtlsbackend-openssl.c b/tls/openssl/gtlsbackend-openssl.c
new file mode 100644
index 0000000..6515c77
--- /dev/null
+++ b/tls/openssl/gtlsbackend-openssl.c
@@ -0,0 +1,292 @@
+/*
+ * 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-include.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)
+{
+ gchar *anchor_file = NULL;
+ GTlsDatabase *database;
+
+#ifdef G_OS_WIN32
+ if (g_getenv ("G_TLS_OPENSSL_HANDLE_CERT_RELOCATABLE") != NULL)
+ {
+ gchar *module_dir;
+
+ module_dir = g_win32_get_package_installation_directory_of_module (NULL);
+ anchor_file = g_build_filename (module_dir, "bin", "cert.pem", NULL);
+ g_free (module_dir);
+ }
+#endif
+
+#ifdef GTLS_SYSTEM_CA_FILE
+ if (anchor_file == NULL)
+ anchor_file = g_strdup (GTLS_SYSTEM_CA_FILE);
+#endif
+
+ if (anchor_file == NULL)
+ {
+ const gchar *openssl_cert_file;
+
+ openssl_cert_file = g_getenv (X509_get_default_cert_file_env ());
+ if (openssl_cert_file == NULL)
+ openssl_cert_file = X509_get_default_cert_file ();
+
+ anchor_file = g_strdup (openssl_cert_file);
+ }
+
+ database = g_tls_file_database_new (anchor_file, error);
+ g_free (anchor_file);
+
+ return database;
+}
+
+static void
+g_tls_backend_openssl_class_init (GTlsBackendOpensslClass *klass)
+{
+ 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",
+ -1);
+}
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..7169dea
--- /dev/null
+++ b/tls/openssl/gtlsbio.c
@@ -0,0 +1,398 @@
+/*
+ * 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)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ bio->init = 0;
+ bio->num = 0;
+ bio->ptr = NULL;
+ bio->flags = 0;
+#else
+ BIO_set_init (bio, 0);
+ BIO_set_data (bio, NULL);
+ BIO_clear_flags (bio, ~0);
+#endif
+ return 1;
+}
+
+static int
+gtls_bio_destroy (BIO *bio)
+{
+ if (bio == NULL)
+ return 0;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ if (bio->shutdown)
+ {
+ if (bio->ptr != NULL)
+ {
+ free_gbio (bio->ptr);
+ bio->ptr = NULL;
+ }
+ bio->init = 0;
+ bio->flags = 0;
+ }
+#else
+ if (BIO_get_shutdown (bio))
+ {
+ if (BIO_get_data (bio) != NULL)
+ {
+ free_gbio (BIO_get_data (bio));
+ BIO_set_data (bio, NULL);
+ }
+ BIO_clear_flags (bio, ~0);
+ BIO_set_init (bio, 0);
+ }
+#endif
+
+ 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:
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ ret = b->shutdown;
+#else
+ ret = BIO_get_shutdown (b);
+#endif
+ break;
+ case BIO_CTRL_SET_CLOSE:
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ b->shutdown = (int)num;
+#else
+ BIO_set_shutdown (b, (int)num);
+#endif
+ 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 (
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ !bio->init ||
+#else
+ !BIO_get_init (bio) ||
+#endif
+ in == NULL || inl == 0)
+ return 0;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ gbio = (GTlsBio *)bio->ptr;
+#else
+ gbio = BIO_get_data (bio);
+#endif
+
+ 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 (
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ !bio->init ||
+#else
+ !BIO_get_init (bio) ||
+#endif
+ out == NULL || outl == 0)
+ return 0;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ gbio = (GTlsBio *)bio->ptr;
+#else
+ gbio = BIO_get_data (bio);
+#endif
+
+ 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;
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+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
+};
+#else
+static BIO_METHOD *methods_gtls = NULL;
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+static BIO_METHOD *
+BIO_s_gtls (void)
+{
+ return &methods_gtls;
+}
+#else
+static const BIO_METHOD *
+BIO_s_gtls (void)
+{
+ if (methods_gtls == NULL)
+ {
+ methods_gtls = BIO_meth_new (BIO_TYPE_SOURCE_SINK | BIO_get_new_index (), "gtls");
+ if (methods_gtls == NULL ||
+ !BIO_meth_set_write (methods_gtls, gtls_bio_write) ||
+ !BIO_meth_set_read (methods_gtls, gtls_bio_read) ||
+ !BIO_meth_set_puts (methods_gtls, gtls_bio_puts) ||
+ !BIO_meth_set_gets (methods_gtls, gtls_bio_gets) ||
+ !BIO_meth_set_ctrl (methods_gtls, gtls_bio_ctrl) ||
+ !BIO_meth_set_create (methods_gtls, gtls_bio_create) ||
+ !BIO_meth_set_destroy (methods_gtls, gtls_bio_destroy))
+ return NULL;
+ }
+ return methods_gtls;
+}
+#endif
+
+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);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ ret->ptr = gbio;
+ ret->init = 1;
+#else
+ BIO_set_data (ret, gbio);
+ BIO_set_init (ret, 1);
+#endif
+
+ return ret;
+}
+
+void
+g_tls_bio_set_read_cancellable (BIO *bio,
+ GCancellable *cancellable)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ gbio = (GTlsBio *)bio->ptr;
+#else
+ gbio = BIO_get_data (bio);
+#endif
+ gbio->read_cancellable = cancellable;
+}
+
+void
+g_tls_bio_set_read_blocking (BIO *bio,
+ gboolean blocking)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ gbio = (GTlsBio *)bio->ptr;
+#else
+ gbio = BIO_get_data (bio);
+#endif
+ gbio->read_blocking = blocking;
+}
+
+void
+g_tls_bio_set_read_error (BIO *bio,
+ GError **error)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ gbio = (GTlsBio *)bio->ptr;
+#else
+ gbio = BIO_get_data (bio);
+#endif
+ gbio->read_error = error;
+}
+
+void
+g_tls_bio_set_write_cancellable (BIO *bio,
+ GCancellable *cancellable)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ gbio = (GTlsBio *)bio->ptr;
+#else
+ gbio = BIO_get_data (bio);
+#endif
+ gbio->write_cancellable = cancellable;
+}
+
+void
+g_tls_bio_set_write_blocking (BIO *bio,
+ gboolean blocking)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ gbio = (GTlsBio *)bio->ptr;
+#else
+ gbio = BIO_get_data (bio);
+#endif
+ gbio->write_blocking = blocking;
+}
+
+void
+g_tls_bio_set_write_error (BIO *bio,
+ GError **error)
+{
+ GTlsBio *gbio;
+
+ g_return_if_fail (bio != NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ gbio = (GTlsBio *)bio->ptr;
+#else
+ gbio = BIO_get_data (bio);
+#endif
+ gbio->write_error = error;
+}
diff --git a/tls/openssl/gtlsbio.h b/tls/openssl/gtlsbio.h
new file mode 100644
index 0000000..caa1e9b
--- /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-include.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..83e6e28
--- /dev/null
+++ b/tls/openssl/gtlscertificate-openssl.c
@@ -0,0 +1,701 @@
+/*
+ * 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-include.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 ();
+ csc = X509_STORE_CTX_new ();
+
+ if (!X509_STORE_CTX_init (csc, store, x, untrusted))
+ {
+ sk_X509_free (untrusted);
+ X509_STORE_CTX_free (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_free (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 ();
+ csc = X509_STORE_CTX_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;
+
+ sk_X509_free (trusted);
+
+end:
+ X509_STORE_CTX_free (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;
+
+ /* Check if the cert issued itself */
+ if (is_issuer (glib_certs->pdata[i], glib_certs->pdata[i]))
+ continue;
+
+ 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..0827c0f
--- /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-include.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..b1560c1
--- /dev/null
+++ b/tls/openssl/gtlsclientconnection-openssl.c
@@ -0,0 +1,573 @@
+/*
+ * 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"
+
+#include <errno.h>
+#include <string.h>
+
+#include "openssl-include.h"
+#include "gtlsconnection-base.h"
+#include "gtlsclientconnection-openssl.h"
+#include "gtlsbackend-openssl.h"
+#include "gtlscertificate-openssl.h"
+#include <glib/gi18n-lib.h>
+
+#define DEFAULT_CIPHER_LIST "HIGH:!DSS:!aNULL@STRENGTH"
+
+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);
+ SSL_SESSION_free (priv->session);
+
+ 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 = -1;
+
+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 */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+ CRYPTO_add (&key->references, 1, CRYPTO_LOCK_EVP_PKEY);
+#else
+ EVP_PKEY_up_ref (key);
+#endif
+ *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 void
+set_cipher_list (GTlsClientConnectionOpenssl *client)
+{
+ GTlsClientConnectionOpensslPrivate *priv;
+ const gchar *cipher_list;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (client);
+
+ cipher_list = g_getenv ("G_TLS_OPENSSL_CIPHER_LIST");
+ if (cipher_list == NULL)
+ cipher_list = DEFAULT_CIPHER_LIST;
+
+ SSL_CTX_set_cipher_list (priv->ssl_ctx, cipher_list);
+}
+
+#ifdef SSL_CTX_set1_sigalgs_list
+static void
+set_signature_algorithm_list (GTlsClientConnectionOpenssl *client)
+{
+ GTlsClientConnectionOpensslPrivate *priv;
+ const gchar *signature_algorithm_list;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (client);
+
+ signature_algorithm_list = g_getenv ("G_TLS_OPENSSL_SIGNATURE_ALGORITHM_LIST");
+ if (signature_algorithm_list == NULL)
+ return;
+
+ SSL_CTX_set1_sigalgs_list (priv->ssl_ctx, signature_algorithm_list);
+}
+#endif
+
+#ifdef SSL_CTX_set1_curves_list
+static void
+set_curve_list (GTlsClientConnectionOpenssl *client)
+{
+ GTlsClientConnectionOpensslPrivate *priv;
+ const gchar *curve_list;
+
+ priv = g_tls_client_connection_openssl_get_instance_private (client);
+
+ curve_list = g_getenv ("G_TLS_OPENSSL_CURVE_LIST");
+ if (curve_list == NULL)
+ return;
+
+ SSL_CTX_set1_curves_list (priv->ssl_ctx, curve_list);
+}
+#endif
+
+static gboolean
+use_ocsp (void)
+{
+ return g_getenv ("G_TLS_OPENSSL_OCSP_ENABLED") != NULL;
+}
+
+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;
+ const char *hostname;
+
+ 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;
+ }
+
+ /* Only TLS 1.2 or higher */
+ options = SSL_OP_NO_TICKET |
+ SSL_OP_NO_COMPRESSION |
+#ifdef SSL_OP_NO_TLSv1_1
+ SSL_OP_NO_TLSv1_1 |
+#endif
+ SSL_OP_NO_SSLv2 |
+ SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1;
+ SSL_CTX_set_options (priv->ssl_ctx, options);
+
+ SSL_CTX_clear_options (priv->ssl_ctx, SSL_OP_LEGACY_SERVER_CONNECT);
+
+ hostname = get_server_identity (client);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10200000L && !defined (LIBRESSL_VERSION_NUMBER)
+ 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);
+
+ set_cipher_list (client);
+
+#ifdef SSL_CTX_set1_sigalgs_list
+ set_signature_algorithm_list (client);
+#endif
+
+#ifdef SSL_CTX_set1_curves_list
+ set_curve_list (client);
+#endif
+
+ 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;
+ }
+
+ if (data_index == -1) {
+ data_index = SSL_get_ex_new_index (0, (void *)"gtlsclientconnection", NULL, NULL, NULL);
+ }
+ SSL_set_ex_data (priv->ssl, data_index, client);
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ if (hostname)
+ SSL_set_tlsext_host_name (priv->ssl, hostname);
+#endif
+
+ SSL_set_connect_state (priv->ssl);
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+ if (use_ocsp())
+ SSL_set_tlsext_status_type (priv->ssl, TLSEXT_STATUSTYPE_ocsp);
+#endif
+
+ 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..fbcb869
--- /dev/null
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -0,0 +1,644 @@
+/*
+ * 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-include.h"
+
+#include "gtlsconnection-openssl.h"
+#include "gtlsbackend-openssl.h"
+#include "gtlscertificate-openssl.h"
+#include "gtlsfiledatabase-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, err_lib, 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)
+ {
+ if (my_error)
+ g_error_free (my_error);
+ 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 ();
+ err_lib = ERR_GET_LIB (err);
+ 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;
+ }
+ }
+
+#ifdef SSL_R_SHUTDOWN_WHILE_IN_INIT
+ /* XXX: this error happens on ubuntu when shutting down the connection, it
+ * seems to be a bug in a specific version of openssl, so let's handle it
+ * gracefully
+ */
+ if (reason == SSL_R_SHUTDOWN_WHILE_IN_INIT)
+ {
+ g_clear_error (&my_error);
+ return G_TLS_CONNECTION_BASE_OK;
+ }
+#endif
+
+ if (reason == SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+ _("TLS connection peer did not send a certificate"));
+ return status;
+ }
+
+ if (err_lib == ERR_LIB_RSA && reason == RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY)
+ {
+ g_clear_error (&my_error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Digest too big for RSA key"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ 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, %d", G_IS_TLS_CLIENT_CONNECTION (openssl) ? "client" : "server",
err_code, err_lib, 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 { \
+ char error_str[256];
+
+#define END_OPENSSL_IO(openssl, direction, ret, status, errmsg, err) \
+ ERR_error_string_n (SSL_get_error (ssl, ret), error_str, sizeof(error_str)); \
+ status = end_openssl_io (openssl, direction, ret, err, errmsg, error_str); \
+ } 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);
+
+ if (tls->rehandshake_mode == G_TLS_REHANDSHAKE_NEVER)
+ {
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Peer requested illegal TLS rehandshake"));
+ return G_TLS_CONNECTION_BASE_ERROR;
+ }
+
+ 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_ocsp_response (GTlsConnectionOpenssl *openssl,
+ GTlsDatabase *database,
+ GTlsCertificate *peer_certificate)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+ SSL *ssl = NULL;
+ OCSP_RESPONSE *resp = NULL;
+ long len = 0;
+ const unsigned char *p = NULL;
+
+ ssl = g_tls_connection_openssl_get_ssl (openssl);
+ len = SSL_get_tlsext_status_ocsp_resp (ssl, &p);
+ /* Soft fail in case of no response is the best we can do */
+ if (p == NULL)
+ return 0;
+
+ resp = d2i_OCSP_RESPONSE (NULL, &p, len);
+ if (resp == NULL)
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ return g_tls_file_database_openssl_verify_ocsp_response (database,
+ peer_certificate,
+ resp);
+#else
+ return 0;
+#endif
+}
+
+static GTlsCertificateFlags
+verify_peer_certificate (GTlsConnectionOpenssl *openssl,
+ GTlsCertificate *peer_certificate)
+{
+ GTlsConnection *conn = G_TLS_CONNECTION (openssl);
+ GSocketConnectable *peer_identity;
+ GTlsDatabase *database;
+ GTlsCertificateFlags errors;
+ gboolean is_client;
+
+ is_client = G_IS_TLS_CLIENT_CONNECTION (openssl);
+ if (is_client)
+ peer_identity = g_tls_client_connection_get_server_identity (G_TLS_CLIENT_CONNECTION (openssl));
+ else
+ peer_identity = NULL;
+
+ errors = 0;
+
+ database = g_tls_connection_get_database (conn);
+ if (database == NULL)
+ {
+ errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+ errors |= g_tls_certificate_verify (peer_certificate, peer_identity, NULL);
+ }
+ else
+ {
+ GError *error = NULL;
+
+ errors |= g_tls_database_verify_chain (database, peer_certificate,
+ is_client ?
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER :
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT,
+ peer_identity,
+ g_tls_connection_get_interaction (conn),
+ G_TLS_DATABASE_VERIFY_NONE,
+ NULL, &error);
+ if (error)
+ {
+ g_warning ("failure verifying certificate chain: %s",
+ error->message);
+ g_assert (errors != 0);
+ g_clear_error (&error);
+ }
+ }
+
+ if (is_client && (errors == 0))
+ errors = verify_ocsp_response (openssl, database, peer_certificate);
+
+ return errors;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_openssl_handshake (GTlsConnectionBase *tls,
+ 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);
+ g_clear_object (&peer_certificate);
+ }
+
+ 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..31eeab3
--- /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-include.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..e45a619
--- /dev/null
+++ b/tls/openssl/gtlsfiledatabase-openssl.c
@@ -0,0 +1,852 @@
+/*
+ * 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-include.h"
+
+typedef struct _GTlsFileDatabaseOpensslPrivate
+{
+ /* read-only after construct */
+ gchar *anchor_filename;
+ STACK_OF(X509) *trusted;
+
+ /* 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;
+
+ if (priv->trusted != NULL)
+ sk_X509_pop_free (priv->trusted, X509_free);
+
+ 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 STACK_OF(X509) *
+load_certs (const gchar *file_name)
+{
+ BIO *bio;
+ STACK_OF(X509) *certs;
+ STACK_OF(X509_INFO) *xis = NULL;
+ gint i;
+
+ if (file_name == NULL)
+ return NULL;
+
+ bio = BIO_new_file (file_name, "rb");
+ if (bio == NULL)
+ return NULL;
+
+ xis = PEM_X509_INFO_read_bio (bio, NULL, NULL, NULL);
+
+ BIO_free (bio);
+
+ certs = sk_X509_new_null ();
+ if (certs == NULL)
+ goto end;
+
+ for (i = 0; i < sk_X509_INFO_num (xis); i++)
+ {
+ X509_INFO *xi;
+
+ xi = sk_X509_INFO_value (xis, i);
+ if (xi->x509 != NULL)
+ {
+ if (!sk_X509_push (certs, xi->x509))
+ goto end;
+ xi->x509 = NULL;
+ }
+ }
+
+end:
+ sk_X509_INFO_pop_free (xis, X509_INFO_free);
+
+ if (sk_X509_num (certs) == 0)
+ {
+ sk_X509_pop_free (certs, X509_free);
+ certs = NULL;
+ }
+
+ return certs;
+}
+
+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;
+ const gchar *anchor_path;
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ switch (prop_id)
+ {
+ case PROP_ANCHORS:
+ anchor_path = g_value_get_string (value);
+ if (anchor_path && !g_path_is_absolute (anchor_path))
+ {
+ g_warning ("The anchor file name used with a GTlsFileDatabase "
+ "must be an absolute path, and not relative: %s", anchor_path);
+ return;
+ }
+
+ if (priv->anchor_filename)
+ {
+ g_free (priv->anchor_filename);
+ if (priv->trusted != NULL)
+ sk_X509_pop_free (priv->trusted, X509_free);
+ }
+
+ priv->anchor_filename = g_strdup (anchor_path);
+ priv->trusted = load_certs (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 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)
+{
+ GTlsFileDatabaseOpenssl *file_database;
+ GTlsFileDatabaseOpensslPrivate *priv;
+ STACK_OF(X509) *certs;
+ X509_STORE *store;
+ X509_STORE_CTX *csc;
+ X509 *x;
+ GTlsCertificateFlags result = 0;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_OPENSSL (chain),
+ G_TLS_CERTIFICATE_GENERIC_ERROR);
+
+ file_database = G_TLS_FILE_DATABASE_OPENSSL (database);
+
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+
+ 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 ();
+ csc = X509_STORE_CTX_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_free (csc);
+ X509_STORE_free (store);
+ sk_X509_free (certs);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ if (priv->trusted)
+ {
+ X509_STORE_CTX_trusted_stack (csc, priv->trusted);
+ }
+
+ if (X509_verify_cert (csc) <= 0)
+ result = g_tls_certificate_openssl_convert_error (X509_STORE_CTX_get_error (csc));
+
+ X509_STORE_CTX_free (csc);
+ X509_STORE_free (store);
+ sk_X509_free (certs);
+
+ 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;
+}
+
+GTlsCertificateFlags
+g_tls_file_database_openssl_verify_ocsp_response (GTlsDatabase *database,
+ GTlsCertificate *chain,
+ OCSP_RESPONSE *resp)
+{
+ GTlsCertificateFlags errors = 0;
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+ GTlsFileDatabaseOpenssl *file_database;
+ GTlsFileDatabaseOpensslPrivate *priv;
+ STACK_OF(X509) *chain_openssl = NULL;
+ X509_STORE *store = NULL;
+ OCSP_BASICRESP *basic_resp = NULL;
+ int ocsp_status = 0;
+ int i;
+
+ ocsp_status = OCSP_response_status (resp);
+ if (ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL)
+ {
+ errors = G_TLS_CERTIFICATE_GENERIC_ERROR;
+ goto end;
+ }
+
+ basic_resp = OCSP_response_get1_basic (resp);
+ if (basic_resp == NULL)
+ {
+ errors = G_TLS_CERTIFICATE_GENERIC_ERROR;
+ goto end;
+ }
+
+ chain_openssl = convert_certificate_chain_to_openssl (G_TLS_CERTIFICATE_OPENSSL (chain));
+ file_database = G_TLS_FILE_DATABASE_OPENSSL (database);
+ priv = g_tls_file_database_openssl_get_instance_private (file_database);
+ store = X509_STORE_new ();
+ if ((chain_openssl == NULL) ||
+ (file_database == NULL) ||
+ (priv == NULL) ||
+ (priv->trusted == NULL) ||
+ (store == NULL))
+ {
+ errors = G_TLS_CERTIFICATE_GENERIC_ERROR;
+ goto end;
+ }
+
+ for (i = 0; i < sk_X509_num (priv->trusted); i++)
+ {
+ X509_STORE_add_cert (store, sk_X509_value (priv->trusted, i));
+ }
+
+ if (OCSP_basic_verify (basic_resp, chain_openssl, store, 0) <= 0)
+ {
+ errors = G_TLS_CERTIFICATE_GENERIC_ERROR;
+ goto end;
+ }
+
+ for (i = 0; i < OCSP_resp_count (basic_resp); i++)
+ {
+ OCSP_SINGLERESP *single_resp = OCSP_resp_get0 (basic_resp, i);
+ ASN1_GENERALIZEDTIME *revocation_time = NULL;
+ ASN1_GENERALIZEDTIME *this_update_time = NULL;
+ ASN1_GENERALIZEDTIME *next_update_time = NULL;
+ int crl_reason = 0;
+ int cert_status = 0;
+
+ if (single_resp == NULL)
+ continue;
+
+ cert_status = OCSP_single_get0_status (single_resp,
+ &crl_reason,
+ &revocation_time,
+ &this_update_time,
+ &next_update_time);
+ if (!OCSP_check_validity (this_update_time,
+ next_update_time,
+ 300L,
+ -1L))
+ {
+ errors = G_TLS_CERTIFICATE_GENERIC_ERROR;
+ goto end;
+ }
+
+ switch (cert_status)
+ {
+ case V_OCSP_CERTSTATUS_GOOD:
+ break;
+ case V_OCSP_CERTSTATUS_REVOKED:
+ errors = G_TLS_CERTIFICATE_REVOKED;
+ goto end;
+ case V_OCSP_CERTSTATUS_UNKNOWN:
+ errors = G_TLS_CERTIFICATE_GENERIC_ERROR;
+ goto end;
+ }
+ }
+
+end:
+ if (store != NULL)
+ X509_STORE_free (store);
+
+ if (basic_resp != NULL)
+ OCSP_BASICRESP_free (basic_resp);
+
+ if (resp != NULL)
+ OCSP_RESPONSE_free (resp);
+
+#endif
+ return errors;
+}
diff --git a/tls/openssl/gtlsfiledatabase-openssl.h b/tls/openssl/gtlsfiledatabase-openssl.h
new file mode 100644
index 0000000..67086db
--- /dev/null
+++ b/tls/openssl/gtlsfiledatabase-openssl.h
@@ -0,0 +1,62 @@
+/*
+ * 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>
+
+#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;
+
+GTlsCertificateFlags g_tls_file_database_openssl_verify_ocsp_response (GTlsDatabase *database,
+ GTlsCertificate *chain,
+ OCSP_RESPONSE *resp);
+
+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..6a99574
--- /dev/null
+++ b/tls/openssl/gtlsserverconnection-openssl.c
@@ -0,0 +1,421 @@
+/*
+ * 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-include.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
+};
+
+#define DEFAULT_CIPHER_LIST "HIGH:!DSS:!aNULL@STRENGTH"
+
+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)
+{
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+static void
+ssl_info_callback (const SSL *ssl,
+ int type,
+ int val)
+{
+ if ((type & SSL_CB_HANDSHAKE_DONE) != 0)
+ {
+ /* Disable renegotiation (CVE-2009-3555) */
+ ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
+ }
+}
+#endif
+
+static void
+set_cipher_list (GTlsServerConnectionOpenssl *server)
+{
+ GTlsServerConnectionOpensslPrivate *priv;
+ const gchar *cipher_list;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (server);
+
+ cipher_list = g_getenv ("G_TLS_OPENSSL_CIPHER_LIST");
+ if (cipher_list == NULL)
+ cipher_list = DEFAULT_CIPHER_LIST;
+
+ SSL_CTX_set_cipher_list (priv->ssl_ctx, cipher_list);
+}
+
+#ifdef SSL_CTX_set1_sigalgs_list
+static void
+set_signature_algorithm_list (GTlsServerConnectionOpenssl *server)
+{
+ GTlsServerConnectionOpensslPrivate *priv;
+ const gchar *signature_algorithm_list;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (server);
+
+ signature_algorithm_list = g_getenv ("G_TLS_OPENSSL_SIGNATURE_ALGORITHM_LIST");
+ if (signature_algorithm_list == NULL)
+ return;
+
+ SSL_CTX_set1_sigalgs_list (priv->ssl_ctx, signature_algorithm_list);
+}
+#endif
+
+#ifdef SSL_CTX_set1_curves_list
+static void
+set_curve_list (GTlsServerConnectionOpenssl *server)
+{
+ GTlsServerConnectionOpensslPrivate *priv;
+ const gchar *curve_list;
+
+ priv = g_tls_server_connection_openssl_get_instance_private (server);
+
+ curve_list = g_getenv ("G_TLS_OPENSSL_CURVE_LIST");
+ if (curve_list == NULL)
+ return;
+
+ SSL_CTX_set1_curves_list (priv->ssl_ctx, curve_list);
+}
+#endif
+
+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;
+ }
+
+ /* Only TLS 1.2 or higher */
+ options = SSL_OP_NO_TICKET |
+ SSL_OP_NO_COMPRESSION |
+ SSL_OP_CIPHER_SERVER_PREFERENCE |
+ SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
+ SSL_OP_SINGLE_ECDH_USE |
+#ifdef SSL_OP_NO_TLSv1_1
+ SSL_OP_NO_TLSv1_1 |
+#endif
+ SSL_OP_NO_SSLv2 |
+ SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10200000L && !defined (LIBRESSL_VERSION_NUMBER)
+ options |= SSL_OP_NO_RENEGOTIATION;
+#endif
+
+ 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);
+
+ set_cipher_list (server);
+
+#ifdef SSL_CTX_set1_sigalgs_list
+ set_signature_algorithm_list (server);
+#endif
+
+#ifdef SSL_CTX_set1_curves_list
+ set_curve_list (server);
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined (LIBRESSL_VERSION_NUMBER)
+# ifdef SSL_CTX_set_ecdh_auto
+ SSL_CTX_set_ecdh_auto (priv->ssl_ctx, 1);
+# else
+ {
+ EC_KEY *ecdh;
+
+ ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
+ if (ecdh != NULL)
+ {
+ SSL_CTX_set_tmp_ecdh (priv->ssl_ctx, ecdh);
+ EC_KEY_free (ecdh);
+ }
+ }
+# endif
+
+ SSL_CTX_set_info_callback (priv->ssl_ctx, ssl_info_callback);
+#endif
+
+ 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/meson.build b/tls/openssl/meson.build
new file mode 100644
index 0000000..5bec3b1
--- /dev/null
+++ b/tls/openssl/meson.build
@@ -0,0 +1,44 @@
+sources = files(
+ 'openssl-module.c',
+ 'gtlsbackend-openssl.c',
+ 'gtlscertificate-openssl.c',
+ 'gtlsconnection-openssl.c',
+ 'gtlsserverconnection-openssl.c',
+ 'gtlsclientconnection-openssl.c',
+ 'gtlsdatabase-openssl.c',
+ 'gtlsfiledatabase-openssl.c',
+ 'gtlsbio.c',
+ 'openssl-util.c',
+)
+
+incs = [top_inc]
+
+deps = [
+ gio_dep,
+ glib_dep,
+ gmodule_dep,
+ gobject_dep,
+ tlsbase_dep,
+ openssl_dep,
+]
+
+module = shared_module(
+ 'gioopenssl',
+ sources: sources,
+ include_directories: incs,
+ dependencies: deps,
+ link_args: module_ldflags,
+ link_depends: symbol_map,
+ name_suffix: module_suffix,
+ install: true,
+ install_dir: gio_module_dir
+)
+
+if get_option('static_modules')
+ static_library('gioopenssl',
+ objects: module.extract_all_objects(),
+ install: true,
+ install_dir: gio_module_dir
+ )
+ pkg.generate(module)
+endif
diff --git a/tls/openssl/openssl-include.h b/tls/openssl/openssl-include.h
new file mode 100644
index 0000000..7a6a460
--- /dev/null
+++ b/tls/openssl/openssl-include.h
@@ -0,0 +1,58 @@
+/*
+ * 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
+ * Christoph Reiter
+ */
+
+/* Due to name clashes between Windows and openssl headers we have to
+ * make sure windows.h is included before openssl and that we undef the
+ * clashing macros.
+ */
+
+#ifndef __G_TLS_OPENSSL_INCLUDE_H__
+#define __G_TLS_OPENSSL_INCLUDE_H__
+
+#include "glib.h"
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+/* These are defined by the Windows headers, but clash with openssl */
+#undef X509_NAME
+#undef X509_CERT_PAIR
+#undef X509_EXTENSIONS
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/x509v3.h>
+#include <openssl/crypto.h>
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
+#include <openssl/ocsp.h>
+#endif
+
+#endif /* __G_TLS_OPENSSL_INCLUDE_H__ */
diff --git a/tls/openssl/openssl-module.c b/tls/openssl/openssl-module.c
new file mode 100644
index 0000000..5cc57ed
--- /dev/null
+++ b/tls/openssl/openssl-module.c
@@ -0,0 +1,65 @@
+/*
+ * 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 <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include "gtlsbackend-openssl.h"
+
+
+void
+g_io_openssl_load (GIOModule *module)
+{
+ gchar *locale_dir;
+#ifdef G_OS_WIN32
+ gchar *base_dir;
+#endif
+
+ g_tls_backend_openssl_register (module);
+
+#ifdef G_OS_WIN32
+ base_dir = g_win32_get_package_installation_directory_of_module (NULL);
+ locale_dir = g_build_filename (base_dir, "share", "locale", NULL);
+ g_free (base_dir);
+#else
+ locale_dir = g_strdup (LOCALE_DIR);
+#endif
+
+ bindtextdomain (GETTEXT_PACKAGE, locale_dir);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ g_free (locale_dir);
+}
+
+void
+g_io_openssl_unload (GIOModule *module)
+{
+}
+
+gchar **
+g_io_openssl_query (void)
+{
+ return g_strsplit (G_TLS_BACKEND_EXTENSION_POINT_NAME, "!", -1);
+}
diff --git a/tls/openssl/openssl-util.c b/tls/openssl/openssl-util.c
new file mode 100644
index 0000000..4c8cffe
--- /dev/null
+++ b/tls/openssl/openssl-util.c
@@ -0,0 +1,488 @@
+/* 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 <strings.h>
+#include "openssl-include.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..10618cc
--- /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-include.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__ */
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]