75b3e935dde30deb3ea1cb0d70150a23083f6b3e
[platform/upstream/glib-networking.git] / tls / gnutls / gtlsbackend-gnutls.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright © 2010 Red Hat, Inc
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22 #include "glib.h"
23
24 #include <errno.h>
25
26 #include <gnutls/gnutls.h>
27 #include <gcrypt.h>
28 #ifndef G_OS_WIN32
29 #include <pthread.h>
30 #endif
31
32 #include "gtlsbackend-gnutls.h"
33 #include "gtlscertificate-gnutls.h"
34 #include "gtlsclientconnection-gnutls.h"
35 #include "gtlsfiledatabase-gnutls.h"
36 #include "gtlsserverconnection-gnutls.h"
37
38 struct _GTlsBackendGnutlsPrivate
39 {
40   GMutex *mutex;
41   GTlsDatabase *default_database;
42 };
43
44 static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
45
46 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendGnutls, g_tls_backend_gnutls, G_TYPE_OBJECT, 0,
47                                 G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_TLS_BACKEND,
48                                                                g_tls_backend_gnutls_interface_init);)
49
50 #if defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) && !defined(G_OS_WIN32)
51 GCRY_THREAD_OPTION_PTHREAD_IMPL;
52 #endif
53
54 #ifdef G_OS_WIN32
55
56 static int
57 gtls_gcry_win32_mutex_init (void **priv)
58 {
59         int err = 0;
60         CRITICAL_SECTION *lock = (CRITICAL_SECTION*)malloc (sizeof (CRITICAL_SECTION));
61
62         if (!lock)
63                 err = ENOMEM;
64         if (!err) {
65                 InitializeCriticalSection (lock);
66                 *priv = lock;
67         }
68         return err;
69 }
70
71 static int
72 gtls_gcry_win32_mutex_destroy (void **lock)
73 {
74         DeleteCriticalSection ((CRITICAL_SECTION*)*lock);
75         free (*lock);
76         return 0;
77 }
78
79 static int
80 gtls_gcry_win32_mutex_lock (void **lock)
81 {
82         EnterCriticalSection ((CRITICAL_SECTION*)*lock);
83         return 0;
84 }
85
86 static int
87 gtls_gcry_win32_mutex_unlock (void **lock)
88 {
89         LeaveCriticalSection ((CRITICAL_SECTION*)*lock);
90         return 0;
91 }
92
93
94 static struct gcry_thread_cbs gtls_gcry_threads_win32 = {                \
95         (GCRY_THREAD_OPTION_USER | (GCRY_THREAD_OPTION_VERSION << 8)),   \
96         NULL, gtls_gcry_win32_mutex_init, gtls_gcry_win32_mutex_destroy, \
97         gtls_gcry_win32_mutex_lock, gtls_gcry_win32_mutex_unlock,        \
98         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
99
100 #endif
101
102 static gpointer
103 gtls_gnutls_init (gpointer data)
104 {
105 #if defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) && !defined(G_OS_WIN32)
106   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
107 #elif defined(G_OS_WIN32)
108   gcry_control (GCRYCTL_SET_THREAD_CBS, &gtls_gcry_threads_win32);
109 #endif
110   gnutls_global_init ();
111
112   /* Leak the module to keep it from being unloaded. */
113   g_type_plugin_use (g_type_get_plugin (G_TYPE_TLS_BACKEND_GNUTLS));
114   return NULL;
115 }
116
117 static GOnce gnutls_inited = G_ONCE_INIT;
118
119 static void
120 g_tls_backend_gnutls_init (GTlsBackendGnutls *backend)
121 {
122   /* Once we call gtls_gnutls_init(), we can't allow the module to be
123    * unloaded, since that would break the pointers to the mutex
124    * functions we set for gcrypt. So we initialize it from here rather
125    * than at class init time so that it doesn't happen unless the app
126    * is actually using TLS (as opposed to just calling
127    * g_io_modules_scan_all_in_directory()).
128    */
129   g_once (&gnutls_inited, gtls_gnutls_init, NULL);
130
131   backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsPrivate);
132   backend->priv->mutex = g_mutex_new ();
133 }
134
135 static void
136 g_tls_backend_gnutls_finalize (GObject *object)
137 {
138   GTlsBackendGnutls *backend = G_TLS_BACKEND_GNUTLS (object);
139
140   if (backend->priv->default_database)
141     g_object_unref (backend->priv->default_database);
142   g_mutex_free (backend->priv->mutex);
143
144   G_OBJECT_CLASS (g_tls_backend_gnutls_parent_class)->finalize (object);
145 }
146
147 static void
148 g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
149 {
150   GObjectClass *gobject_class = G_OBJECT_CLASS (backend_class);
151   gobject_class->finalize = g_tls_backend_gnutls_finalize;
152   g_type_class_add_private (backend_class, sizeof (GTlsBackendGnutlsPrivate));
153 }
154
155 static void
156 g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
157 {
158 }
159
160 static GTlsDatabase*
161 g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
162 {
163   GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
164   const gchar *anchor_file = NULL;
165   GTlsDatabase *result;
166   GError *error = NULL;
167
168   g_mutex_lock (self->priv->mutex);
169
170   if (self->priv->default_database)
171     {
172       result = g_object_ref (self->priv->default_database);
173     }
174   else
175     {
176 #ifdef GTLS_SYSTEM_CA_FILE
177       anchor_file = GTLS_SYSTEM_CA_FILE;
178 #endif
179       result = g_tls_file_database_new (anchor_file, &error);
180       if (error)
181         {
182           g_warning ("couldn't load TLS file database: %s",
183                      error->message);
184           g_clear_error (&error);
185         }
186       else
187         {
188           self->priv->default_database = g_object_ref (result);
189         }
190     }
191
192   g_mutex_unlock (self->priv->mutex);
193
194   return result;
195 }
196
197 static void
198 g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
199 {
200   iface->get_certificate_type       = g_tls_certificate_gnutls_get_type;
201   iface->get_client_connection_type = g_tls_client_connection_gnutls_get_type;
202   iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
203   iface->get_file_database_type =     g_tls_file_database_gnutls_get_type;
204   iface->get_default_database =       g_tls_backend_gnutls_get_default_database;
205 }
206
207 /* Session cache support; all the details are sort of arbitrary. Note
208  * that having session_cache_cleanup() be a little bit slow isn't the
209  * end of the world, since it will still be faster than the network
210  * is. (NSS uses a linked list for its cache...)
211  */
212
213 G_LOCK_DEFINE_STATIC (session_cache_lock);
214 GHashTable *session_cache;
215
216 #define SESSION_CACHE_MAX_SIZE 50
217 #define SESSION_CACHE_MAX_AGE (60 * 60) /* one hour */
218
219 typedef struct {
220   gchar      *session_id;
221   GByteArray *session_data;
222   time_t      last_used;
223 } GTlsBackendGnutlsCacheData;
224
225 static void
226 session_cache_cleanup (void)
227 {
228   GHashTableIter iter;
229   gpointer key, value;
230   GTlsBackendGnutlsCacheData *cache_data;
231   time_t expired = time (NULL) - SESSION_CACHE_MAX_AGE;
232
233   g_hash_table_iter_init (&iter, session_cache);
234   while (g_hash_table_iter_next (&iter, &key, &value))
235     {
236       cache_data = value;
237       if (cache_data->last_used < expired)
238         g_hash_table_iter_remove (&iter);
239     }
240 }
241
242 static void
243 cache_data_free (gpointer data)
244 {
245   GTlsBackendGnutlsCacheData *cache_data = data;
246
247   g_free (cache_data->session_id);
248   g_byte_array_unref (cache_data->session_data);
249   g_slice_free (GTlsBackendGnutlsCacheData, cache_data);
250 }
251
252 void
253 g_tls_backend_gnutls_cache_session_data (const gchar *session_id,
254                                          guchar      *session_data,
255                                          gsize        session_data_length)
256 {
257   GTlsBackendGnutlsCacheData *cache_data;
258
259   G_LOCK (session_cache_lock);
260
261   if (!session_cache)
262     session_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
263                                            NULL, cache_data_free);
264
265   cache_data = g_hash_table_lookup (session_cache, session_id);
266   if (cache_data)
267     {
268       if (cache_data->session_data->len == session_data_length &&
269           memcmp (cache_data->session_data->data,
270                   session_data, session_data_length) == 0)
271         {
272           cache_data->last_used = time (NULL);
273           G_UNLOCK (session_cache_lock);
274           return;
275         }
276
277       g_byte_array_set_size (cache_data->session_data, 0);
278     }
279   else
280     {
281       if (g_hash_table_size (session_cache) >= SESSION_CACHE_MAX_SIZE)
282         session_cache_cleanup ();
283
284       cache_data = g_slice_new (GTlsBackendGnutlsCacheData);
285       cache_data->session_id = g_strdup (session_id);
286       cache_data->session_data = g_byte_array_sized_new (session_data_length);
287
288       g_hash_table_insert (session_cache, cache_data->session_id, cache_data);
289     }
290
291   g_byte_array_append (cache_data->session_data,
292                        session_data, session_data_length);
293   cache_data->last_used = time (NULL);
294   G_UNLOCK (session_cache_lock);
295 }
296
297 void
298 g_tls_backend_gnutls_uncache_session_data (const gchar *session_id)
299 {
300   G_LOCK (session_cache_lock);
301   if (session_cache)
302     g_hash_table_remove (session_cache, session_id);
303   G_UNLOCK (session_cache_lock);
304 }
305
306 GByteArray *
307 g_tls_backend_gnutls_lookup_session_data (const gchar *session_id)
308 {
309   GTlsBackendGnutlsCacheData *cache_data;
310   GByteArray *session_data = NULL;
311
312   G_LOCK (session_cache_lock);
313   if (session_cache)
314     {
315       cache_data = g_hash_table_lookup (session_cache, session_id);
316       if (cache_data)
317         {
318           cache_data->last_used = time (NULL);
319           session_data = g_byte_array_ref (cache_data->session_data);
320         }
321     }
322   G_UNLOCK (session_cache_lock);
323
324   return session_data;
325 }
326
327 void
328 g_tls_backend_gnutls_register (GIOModule *module)
329 {
330   g_tls_backend_gnutls_register_type (G_TYPE_MODULE (module));
331   g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
332                                   g_tls_backend_gnutls_get_type(),
333                                   "gnutls",
334                                   0);
335 }