c22530ede355daecf7d555ab1ea29f4e2e73955b
[platform/upstream/glib-networking.git] / tls / gnutls / gtlsbackend-gnutls.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  * GIO - GLib Input, Output and Streaming Library
4  *
5  * Copyright 2010 Red Hat, Inc
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, see
19  * <http://www.gnu.org/licenses/>.
20  *
21  * In addition, when the library is used with OpenSSL, a special
22  * exception applies. Refer to the LICENSE_EXCEPTION file for details.
23  */
24
25 #include "config.h"
26 #include "glib.h"
27
28 #include <errno.h>
29 #include <string.h>
30
31 #include <gnutls/gnutls.h>
32
33 #include "gtlsbackend-gnutls.h"
34 #include "gtlscertificate-gnutls.h"
35 #include "gtlsclientconnection-gnutls.h"
36 #include "gtlsfiledatabase-gnutls.h"
37 #include "gtlsserverconnection-gnutls.h"
38
39 #include "TIZEN.h"
40
41 struct _GTlsBackendGnutls
42 {
43   GObject parent_instance;
44
45   GMutex mutex;
46   GTlsDatabase *default_database;
47 };
48
49 static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
50
51 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendGnutls, g_tls_backend_gnutls, G_TYPE_OBJECT, 0,
52                                 G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_TLS_BACKEND,
53                                                                g_tls_backend_gnutls_interface_init);)
54
55 #ifdef GTLS_GNUTLS_DEBUG
56 static void
57 gtls_log_func (int level, const char *msg)
58 {
59 #if ENABLE(TIZEN_DLOG)
60         TIZEN_LOGI("GTLS: %s", msg);
61 #else
62   g_print ("GTLS: %s", msg);
63 #endif
64 }
65 #endif
66
67 static gpointer
68 gtls_gnutls_init (gpointer data)
69 {
70   GTypePlugin *plugin;
71
72   gnutls_global_init ();
73
74 #ifdef GTLS_GNUTLS_DEBUG
75   gnutls_global_set_log_function (gtls_log_func);
76   gnutls_global_set_log_level (9);
77 #endif
78
79   /* Leak the module to keep it from being unloaded. */
80   plugin = g_type_get_plugin (G_TYPE_TLS_BACKEND_GNUTLS);
81   if (plugin != NULL)
82     g_type_plugin_use (plugin);
83   return NULL;
84 }
85
86 GNUTLS_SKIP_GLOBAL_INIT
87
88 static GOnce gnutls_inited = G_ONCE_INIT;
89
90 static void
91 g_tls_backend_gnutls_init (GTlsBackendGnutls *backend)
92 {
93   /* Once we call gtls_gnutls_init(), we can't allow the module to be
94    * unloaded (since if gnutls gets unloaded but gcrypt doesn't, then
95    * gcrypt will have dangling pointers to gnutls's mutex functions).
96    * So we initialize it from here rather than at class init time so
97    * that it doesn't happen unless the app is actually using TLS (as
98    * opposed to just calling g_io_modules_scan_all_in_directory()).
99    */
100   g_once (&gnutls_inited, gtls_gnutls_init, NULL);
101
102   g_mutex_init (&backend->mutex);
103 }
104
105 static void
106 g_tls_backend_gnutls_finalize (GObject *object)
107 {
108   GTlsBackendGnutls *backend = G_TLS_BACKEND_GNUTLS (object);
109
110   g_clear_object (&backend->default_database);
111   g_mutex_clear (&backend->mutex);
112
113   G_OBJECT_CLASS (g_tls_backend_gnutls_parent_class)->finalize (object);
114 }
115
116 static void
117 g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
118 {
119   GObjectClass *gobject_class = G_OBJECT_CLASS (backend_class);
120
121   gobject_class->finalize = g_tls_backend_gnutls_finalize;
122 }
123
124 static void
125 g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
126 {
127 }
128
129 static GTlsDatabase*
130 g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
131 {
132   GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
133   GTlsDatabase *result;
134   GError *error = NULL;
135
136   g_mutex_lock (&self->mutex);
137
138   if (self->default_database)
139     {
140       result = g_object_ref (self->default_database);
141     }
142   else
143     {
144       result = G_TLS_DATABASE (g_tls_database_gnutls_new (&error));
145       if (error)
146         {
147           g_warning ("Failed to load TLS database: %s", error->message);
148           g_clear_error (&error);
149         }
150       else
151         {
152           g_assert (result);
153           self->default_database = g_object_ref (result);
154         }
155     }
156
157   g_mutex_unlock (&self->mutex);
158
159   return result;
160 }
161
162 static void
163 g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
164 {
165   iface->get_certificate_type       = g_tls_certificate_gnutls_get_type;
166   iface->get_client_connection_type = g_tls_client_connection_gnutls_get_type;
167   iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
168   iface->get_file_database_type =     g_tls_file_database_gnutls_get_type;
169   iface->get_default_database =       g_tls_backend_gnutls_get_default_database;
170   iface->get_dtls_client_connection_type = g_tls_client_connection_gnutls_get_type;
171   iface->get_dtls_server_connection_type = g_tls_server_connection_gnutls_get_type;
172 }
173
174 /* Session cache support; all the details are sort of arbitrary. Note
175  * that having session_cache_cleanup() be a little bit slow isn't the
176  * end of the world, since it will still be faster than the network
177  * is. (NSS uses a linked list for its cache...)
178  */
179
180 G_LOCK_DEFINE_STATIC (session_cache_lock);
181 GHashTable *client_session_cache, *server_session_cache;
182
183 #define SESSION_CACHE_MAX_SIZE 50
184 #define SESSION_CACHE_MAX_AGE (60 * 60) /* one hour */
185
186 typedef struct {
187   GBytes *session_id;
188   GBytes *session_data;
189   time_t  last_used;
190 } GTlsBackendGnutlsCacheData;
191
192 static void
193 session_cache_cleanup (GHashTable *cache)
194 {
195   GHashTableIter iter;
196   gpointer key, value;
197   GTlsBackendGnutlsCacheData *cache_data;
198   time_t expired = time (NULL) - SESSION_CACHE_MAX_AGE;
199
200   g_hash_table_iter_init (&iter, cache);
201   while (g_hash_table_iter_next (&iter, &key, &value))
202     {
203       cache_data = value;
204       if (cache_data->last_used < expired)
205         g_hash_table_iter_remove (&iter);
206     }
207 }
208
209 static void
210 cache_data_free (gpointer data)
211 {
212   GTlsBackendGnutlsCacheData *cache_data = data;
213
214   g_bytes_unref (cache_data->session_id);
215   g_bytes_unref (cache_data->session_data);
216   g_free (cache_data);
217 }
218
219 static GHashTable *
220 get_session_cache (unsigned int            type,
221                    gboolean                create)
222 {
223   GHashTable **cache_p;
224
225   cache_p = (type == GNUTLS_CLIENT) ? &client_session_cache : &server_session_cache;
226   if (!*cache_p && create)
227     {
228       *cache_p = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
229                                         NULL, cache_data_free);
230     }
231   return *cache_p;
232 }
233
234 void
235 g_tls_backend_gnutls_store_session (unsigned int             type,
236                                     GBytes                  *session_id,
237                                     GBytes                  *session_data)
238 {
239   GTlsBackendGnutlsCacheData *cache_data;
240   GHashTable *cache;
241
242   G_LOCK (session_cache_lock);
243
244   cache = get_session_cache (type, TRUE);
245   cache_data = g_hash_table_lookup (cache, session_id);
246   if (cache_data)
247     {
248       if (!g_bytes_equal (cache_data->session_data, session_data))
249         {
250           g_bytes_unref (cache_data->session_data);
251           cache_data->session_data = g_bytes_ref (session_data);
252         }
253     }
254   else
255     {
256       if (g_hash_table_size (cache) >= SESSION_CACHE_MAX_SIZE)
257         session_cache_cleanup (cache);
258
259       cache_data = g_new (GTlsBackendGnutlsCacheData, 1);
260       cache_data->session_id = g_bytes_ref (session_id);
261       cache_data->session_data = g_bytes_ref (session_data);
262
263       g_hash_table_insert (cache, cache_data->session_id, cache_data);
264     }
265   cache_data->last_used = time (NULL);
266
267   G_UNLOCK (session_cache_lock);
268 }
269
270 void
271 g_tls_backend_gnutls_remove_session (unsigned int             type,
272                                      GBytes                  *session_id)
273 {
274   GHashTable *cache;
275
276   G_LOCK (session_cache_lock);
277
278   cache = get_session_cache (type, FALSE);
279   if (cache)
280     g_hash_table_remove (cache, session_id);
281
282   G_UNLOCK (session_cache_lock);
283 }
284
285 GBytes *
286 g_tls_backend_gnutls_lookup_session (unsigned int             type,
287                                      GBytes                  *session_id)
288 {
289   GTlsBackendGnutlsCacheData *cache_data;
290   GBytes *session_data = NULL;
291   GHashTable *cache;
292
293   G_LOCK (session_cache_lock);
294
295   cache = get_session_cache (type, FALSE);
296   if (cache)
297     {
298       cache_data = g_hash_table_lookup (cache, session_id);
299       if (cache_data)
300         {
301           cache_data->last_used = time (NULL);
302           session_data = g_bytes_ref (cache_data->session_data);
303         }
304     }
305
306   G_UNLOCK (session_cache_lock);
307
308   return session_data;
309 }
310
311 void
312 g_tls_backend_gnutls_register (GIOModule *module)
313 {
314   g_tls_backend_gnutls_register_type (G_TYPE_MODULE (module));
315   if (module == NULL)
316     g_io_extension_point_register (G_TLS_BACKEND_EXTENSION_POINT_NAME);
317   g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
318                                   g_tls_backend_gnutls_get_type(),
319                                   "gnutls",
320                                   0);
321 }