-/* GIO - GLib Input, Output and Streaming Library
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
*
- * Copyright © 2010 Red Hat, Inc
+ * Copyright 2010 Red Hat, Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
*/
#include "config.h"
#include "glib.h"
#include <errno.h>
+#include <string.h>
#include <gnutls/gnutls.h>
-#include <gcrypt.h>
-#ifndef G_OS_WIN32
-#include <pthread.h>
-#endif
#include "gtlsbackend-gnutls.h"
#include "gtlscertificate-gnutls.h"
#include "gtlsclientconnection-gnutls.h"
+#include "gtlsfiledatabase-gnutls.h"
#include "gtlsserverconnection-gnutls.h"
-static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
-
-G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendGnutls, g_tls_backend_gnutls, G_TYPE_OBJECT, 0,
- G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_TLS_BACKEND,
- g_tls_backend_gnutls_interface_init);)
+#include "TIZEN.h"
-#if defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) && !defined(G_OS_WIN32)
-GCRY_THREAD_OPTION_PTHREAD_IMPL;
-#endif
-
-#ifdef G_OS_WIN32
-
-static int
-gtls_gcry_win32_mutex_init (void **priv)
+struct _GTlsBackendGnutls
{
- int err = 0;
- CRITICAL_SECTION *lock = (CRITICAL_SECTION*)malloc (sizeof (CRITICAL_SECTION));
+ GObject parent_instance;
- if (!lock)
- err = ENOMEM;
- if (!err) {
- InitializeCriticalSection (lock);
- *priv = lock;
- }
- return err;
-}
+ GMutex mutex;
+ GTlsDatabase *default_database;
+};
-static int
-gtls_gcry_win32_mutex_destroy (void **lock)
-{
- DeleteCriticalSection ((CRITICAL_SECTION*)*lock);
- free (*lock);
- return 0;
-}
+static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
-static int
-gtls_gcry_win32_mutex_lock (void **lock)
-{
- EnterCriticalSection ((CRITICAL_SECTION*)*lock);
- return 0;
-}
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendGnutls, g_tls_backend_gnutls, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_TLS_BACKEND,
+ g_tls_backend_gnutls_interface_init);)
-static int
-gtls_gcry_win32_mutex_unlock (void **lock)
+#ifdef GTLS_GNUTLS_DEBUG
+static void
+gtls_log_func (int level, const char *msg)
{
- LeaveCriticalSection ((CRITICAL_SECTION*)*lock);
- return 0;
+ g_print ("GTLS: %s", msg);
}
-
-
-static struct gcry_thread_cbs gtls_gcry_threads_win32 = { \
- (GCRY_THREAD_OPTION_USER | (GCRY_THREAD_OPTION_VERSION << 8)), \
- NULL, gtls_gcry_win32_mutex_init, gtls_gcry_win32_mutex_destroy, \
- gtls_gcry_win32_mutex_lock, gtls_gcry_win32_mutex_unlock, \
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
-
#endif
static gpointer
gtls_gnutls_init (gpointer data)
{
-#if defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) && !defined(G_OS_WIN32)
- gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
-#elif defined(G_OS_WIN32)
- gcry_control (GCRYCTL_SET_THREAD_CBS, >ls_gcry_threads_win32);
-#endif
+ GTypePlugin *plugin;
+
gnutls_global_init ();
+#ifdef GTLS_GNUTLS_DEBUG
+ gnutls_global_set_log_function (gtls_log_func);
+ gnutls_global_set_log_level (9);
+#endif
+
/* Leak the module to keep it from being unloaded. */
- g_type_plugin_use (g_type_get_plugin (G_TYPE_TLS_BACKEND_GNUTLS));
+ plugin = g_type_get_plugin (G_TYPE_TLS_BACKEND_GNUTLS);
+ if (plugin != NULL)
+ g_type_plugin_use (plugin);
return NULL;
}
+GNUTLS_SKIP_GLOBAL_INIT
+
static GOnce gnutls_inited = G_ONCE_INIT;
static void
g_tls_backend_gnutls_init (GTlsBackendGnutls *backend)
{
/* Once we call gtls_gnutls_init(), we can't allow the module to be
- * unloaded, since that would break the pointers to the mutex
- * functions we set for gcrypt. 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()).
+ * unloaded (since if gnutls gets unloaded but gcrypt doesn't, then
+ * gcrypt will have dangling pointers to gnutls'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 (&gnutls_inited, gtls_gnutls_init, NULL);
+
+ g_mutex_init (&backend->mutex);
+}
+
+static void
+g_tls_backend_gnutls_finalize (GObject *object)
+{
+ GTlsBackendGnutls *backend = G_TLS_BACKEND_GNUTLS (object);
+
+ g_clear_object (&backend->default_database);
+ g_mutex_clear (&backend->mutex);
+
+ G_OBJECT_CLASS (g_tls_backend_gnutls_parent_class)->finalize (object);
}
static void
g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (backend_class);
+
+ gobject_class->finalize = g_tls_backend_gnutls_finalize;
}
static void
{
}
+static GTlsDatabase*
+g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
+{
+ GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
+ GTlsDatabase *result;
+ GError *error = NULL;
+
+ g_mutex_lock (&self->mutex);
+
+ if (self->default_database)
+ {
+ result = g_object_ref (self->default_database);
+ }
+ else
+ {
+ result = G_TLS_DATABASE (g_tls_database_gnutls_new (&error));
+ if (error)
+ {
+ g_warning ("Failed to load TLS database: %s", error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ g_assert (result);
+ self->default_database = g_object_ref (result);
+ }
+ }
+
+ g_mutex_unlock (&self->mutex);
+
+ return result;
+}
+
static void
g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
{
iface->get_certificate_type = g_tls_certificate_gnutls_get_type;
iface->get_client_connection_type = g_tls_client_connection_gnutls_get_type;
iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
+ iface->get_file_database_type = g_tls_file_database_gnutls_get_type;
+ iface->get_default_database = g_tls_backend_gnutls_get_default_database;
+ iface->get_dtls_client_connection_type = g_tls_client_connection_gnutls_get_type;
+ iface->get_dtls_server_connection_type = g_tls_server_connection_gnutls_get_type;
}
-#ifdef GTLS_SYSTEM_CA_FILE
-/* Parsing the system CA list takes a noticeable amount of time.
- * So we only do it once, and only when we actually need to see it.
+/* Session cache support; all the details are sort of arbitrary. Note
+ * that having session_cache_cleanup() be a little bit slow isn't the
+ * end of the world, since it will still be faster than the network
+ * is. (NSS uses a linked list for its cache...)
*/
-static const GList *
-get_ca_lists (gnutls_x509_crt_t **cas,
- int *num_cas)
+
+G_LOCK_DEFINE_STATIC (session_cache_lock);
+GHashTable *client_session_cache, *server_session_cache;
+
+#define SESSION_CACHE_MAX_SIZE 50
+#define SESSION_CACHE_MAX_AGE (60 * 60) /* one hour */
+
+typedef struct {
+ GBytes *session_id;
+ GBytes *session_data;
+ time_t last_used;
+} GTlsBackendGnutlsCacheData;
+
+static void
+session_cache_cleanup (GHashTable *cache)
{
- static gnutls_x509_crt_t *ca_list_gnutls;
- static int ca_list_length;
- static GList *ca_list;
+ GHashTableIter iter;
+ gpointer key, value;
+ GTlsBackendGnutlsCacheData *cache_data;
+ time_t expired = time (NULL) - SESSION_CACHE_MAX_AGE;
- if (g_once_init_enter ((volatile gsize *)&ca_list_gnutls))
+ g_hash_table_iter_init (&iter, cache);
+ while (g_hash_table_iter_next (&iter, &key, &value))
{
- GError *error = NULL;
- gnutls_x509_crt_t *x509_crts;
- GList *c;
- int i;
-
- ca_list = g_tls_certificate_list_new_from_file (GTLS_SYSTEM_CA_FILE, &error);
- if (error)
- {
- g_warning ("Failed to read system CA file %s: %s.",
- GTLS_SYSTEM_CA_FILE, error->message);
- g_error_free (error);
- /* Note that this is not a security problem, since if
- * G_TLS_VALIDATE_CA is set, then this just means validation
- * will always fail, and if it isn't set, then it doesn't
- * matter that we couldn't read the CAs.
- */
- }
-
- ca_list_length = g_list_length (ca_list);
- x509_crts = g_new (gnutls_x509_crt_t, ca_list_length);
- for (c = ca_list, i = 0; c; c = c->next, i++)
- x509_crts[i] = g_tls_certificate_gnutls_get_cert (c->data);
-
- g_once_init_leave ((volatile gsize *)&ca_list_gnutls, GPOINTER_TO_SIZE (x509_crts));
+ cache_data = value;
+ if (cache_data->last_used < expired)
+ g_hash_table_iter_remove (&iter);
}
+}
+
+static void
+cache_data_free (gpointer data)
+{
+ GTlsBackendGnutlsCacheData *cache_data = data;
- if (cas)
- *cas = ca_list_gnutls;
- if (num_cas)
- *num_cas = ca_list_length;
-
- return ca_list;
+ g_bytes_unref (cache_data->session_id);
+ g_bytes_unref (cache_data->session_data);
+ g_free (cache_data);
}
-#endif
-const GList *
-g_tls_backend_gnutls_get_system_ca_list_gtls (void)
+static GHashTable *
+get_session_cache (unsigned int type,
+ gboolean create)
{
-#ifdef GTLS_SYSTEM_CA_FILE
- return get_ca_lists (NULL, NULL);
-#else
- return NULL;
-#endif
+ GHashTable **cache_p;
+
+ cache_p = (type == GNUTLS_CLIENT) ? &client_session_cache : &server_session_cache;
+ if (!*cache_p && create)
+ {
+ *cache_p = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
+ NULL, cache_data_free);
+ }
+ return *cache_p;
}
void
-g_tls_backend_gnutls_get_system_ca_list_gnutls (gnutls_x509_crt_t **cas,
- int *num_cas)
-{
-#ifdef GTLS_SYSTEM_CA_FILE
- get_ca_lists (cas, num_cas);
-#else
- *cas = NULL;
- *num_cas = 0;
-#endif
+g_tls_backend_gnutls_store_session (unsigned int type,
+ GBytes *session_id,
+ GBytes *session_data)
+{
+ GTlsBackendGnutlsCacheData *cache_data;
+ GHashTable *cache;
+
+ G_LOCK (session_cache_lock);
+
+ cache = get_session_cache (type, TRUE);
+ cache_data = g_hash_table_lookup (cache, session_id);
+ if (cache_data)
+ {
+ if (!g_bytes_equal (cache_data->session_data, session_data))
+ {
+ g_bytes_unref (cache_data->session_data);
+ cache_data->session_data = g_bytes_ref (session_data);
+ }
+ }
+ else
+ {
+ if (g_hash_table_size (cache) >= SESSION_CACHE_MAX_SIZE)
+ session_cache_cleanup (cache);
+
+ cache_data = g_new (GTlsBackendGnutlsCacheData, 1);
+ cache_data->session_id = g_bytes_ref (session_id);
+ cache_data->session_data = g_bytes_ref (session_data);
+
+ g_hash_table_insert (cache, cache_data->session_id, cache_data);
+ }
+ cache_data->last_used = time (NULL);
+
+ G_UNLOCK (session_cache_lock);
+}
+
+void
+g_tls_backend_gnutls_remove_session (unsigned int type,
+ GBytes *session_id)
+{
+ GHashTable *cache;
+
+ G_LOCK (session_cache_lock);
+
+ cache = get_session_cache (type, FALSE);
+ if (cache)
+ g_hash_table_remove (cache, session_id);
+
+ G_UNLOCK (session_cache_lock);
+}
+
+GBytes *
+g_tls_backend_gnutls_lookup_session (unsigned int type,
+ GBytes *session_id)
+{
+ GTlsBackendGnutlsCacheData *cache_data;
+ GBytes *session_data = NULL;
+ GHashTable *cache;
+
+ G_LOCK (session_cache_lock);
+
+ cache = get_session_cache (type, FALSE);
+ if (cache)
+ {
+ cache_data = g_hash_table_lookup (cache, session_id);
+ if (cache_data)
+ {
+ cache_data->last_used = time (NULL);
+ session_data = g_bytes_ref (cache_data->session_data);
+ }
+ }
+
+ G_UNLOCK (session_cache_lock);
+
+ return session_data;
}
void
g_tls_backend_gnutls_register (GIOModule *module)
{
g_tls_backend_gnutls_register_type (G_TYPE_MODULE (module));
+ if (module == NULL)
+ g_io_extension_point_register (G_TLS_BACKEND_EXTENSION_POINT_NAME);
g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
- g_tls_backend_gnutls_get_type(),
- "gnutls",
- 0);
+ g_tls_backend_gnutls_get_type(),
+ "gnutls",
+ 0);
}