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