Imported Upstream version 2.50.0
[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  * In addition, when the library is used with OpenSSL, a special
20  * exception applies. Refer to the LICENSE_EXCEPTION file for details.
21  */
22
23 #include "config.h"
24 #include "glib.h"
25
26 #include <errno.h>
27 #include <string.h>
28
29 #include <gnutls/gnutls.h>
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 #ifdef GTLS_GNUTLS_DEBUG
50 static void
51 gtls_log_func (int level, const char *msg)
52 {
53   g_print ("GTLS: %s", msg);
54 }
55 #endif
56
57 static gpointer
58 gtls_gnutls_init (gpointer data)
59 {
60   gnutls_global_init ();
61
62 #ifdef GTLS_GNUTLS_DEBUG
63   gnutls_global_set_log_function (gtls_log_func);
64   gnutls_global_set_log_level (9);
65 #endif
66
67   /* Leak the module to keep it from being unloaded. */
68   g_type_plugin_use (g_type_get_plugin (G_TYPE_TLS_BACKEND_GNUTLS));
69   return NULL;
70 }
71
72 static GOnce gnutls_inited = G_ONCE_INIT;
73
74 static void
75 g_tls_backend_gnutls_init (GTlsBackendGnutls *backend)
76 {
77   /* Once we call gtls_gnutls_init(), we can't allow the module to be
78    * unloaded (since if gnutls gets unloaded but gcrypt doesn't, then
79    * gcrypt will have dangling pointers to gnutls's mutex functions).
80    * So we initialize it from here rather than at class init time so
81    * that it doesn't happen unless the app is actually using TLS (as
82    * opposed to just calling g_io_modules_scan_all_in_directory()).
83    */
84   g_once (&gnutls_inited, gtls_gnutls_init, NULL);
85
86   backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsPrivate);
87   g_mutex_init (&backend->priv->mutex);
88 }
89
90 static void
91 g_tls_backend_gnutls_finalize (GObject *object)
92 {
93   GTlsBackendGnutls *backend = G_TLS_BACKEND_GNUTLS (object);
94
95   if (backend->priv->default_database)
96     g_object_unref (backend->priv->default_database);
97   g_mutex_clear (&backend->priv->mutex);
98
99   G_OBJECT_CLASS (g_tls_backend_gnutls_parent_class)->finalize (object);
100 }
101
102 static GTlsDatabase*
103 g_tls_backend_gnutls_real_create_database (GTlsBackendGnutls  *self,
104                                            GError            **error)
105 {
106   const gchar *anchor_file = NULL;
107 #ifdef GTLS_SYSTEM_CA_FILE
108   anchor_file = GTLS_SYSTEM_CA_FILE;
109 #endif
110   return g_tls_file_database_new (anchor_file, error);
111 }
112
113 static void
114 g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
115 {
116   GObjectClass *gobject_class = G_OBJECT_CLASS (backend_class);
117   gobject_class->finalize = g_tls_backend_gnutls_finalize;
118   backend_class->create_database = g_tls_backend_gnutls_real_create_database;
119   g_type_class_add_private (backend_class, sizeof (GTlsBackendGnutlsPrivate));
120 }
121
122 static void
123 g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
124 {
125 }
126
127 static GTlsDatabase*
128 g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
129 {
130   GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
131   GTlsDatabase *result;
132   GError *error = NULL;
133
134   g_mutex_lock (&self->priv->mutex);
135
136   if (self->priv->default_database)
137     {
138       result = g_object_ref (self->priv->default_database);
139     }
140   else
141     {
142       g_assert (G_TLS_BACKEND_GNUTLS_GET_CLASS (self)->create_database);
143       result = G_TLS_BACKEND_GNUTLS_GET_CLASS (self)->create_database (self, &error);
144       if (error)
145         {
146           g_warning ("couldn't load TLS file database: %s",
147                      error->message);
148           g_clear_error (&error);
149         }
150       else
151         {
152           g_assert (result);
153           self->priv->default_database = g_object_ref (result);
154         }
155     }
156
157   g_mutex_unlock (&self->priv->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 }
171
172 /* Session cache support; all the details are sort of arbitrary. Note
173  * that having session_cache_cleanup() be a little bit slow isn't the
174  * end of the world, since it will still be faster than the network
175  * is. (NSS uses a linked list for its cache...)
176  */
177
178 G_LOCK_DEFINE_STATIC (session_cache_lock);
179 GHashTable *client_session_cache, *server_session_cache;
180
181 #define SESSION_CACHE_MAX_SIZE 50
182 #define SESSION_CACHE_MAX_AGE (60 * 60) /* one hour */
183
184 typedef struct {
185   GBytes *session_id;
186   GBytes *session_data;
187   time_t  last_used;
188 } GTlsBackendGnutlsCacheData;
189
190 static void
191 session_cache_cleanup (GHashTable *cache)
192 {
193   GHashTableIter iter;
194   gpointer key, value;
195   GTlsBackendGnutlsCacheData *cache_data;
196   time_t expired = time (NULL) - SESSION_CACHE_MAX_AGE;
197
198   g_hash_table_iter_init (&iter, cache);
199   while (g_hash_table_iter_next (&iter, &key, &value))
200     {
201       cache_data = value;
202       if (cache_data->last_used < expired)
203         g_hash_table_iter_remove (&iter);
204     }
205 }
206
207 static void
208 cache_data_free (gpointer data)
209 {
210   GTlsBackendGnutlsCacheData *cache_data = data;
211
212   g_bytes_unref (cache_data->session_id);
213   g_bytes_unref (cache_data->session_data);
214   g_slice_free (GTlsBackendGnutlsCacheData, cache_data);
215 }
216
217 static GHashTable *
218 get_session_cache (unsigned int            type,
219                    gboolean                create)
220 {
221   GHashTable **cache_p;
222
223   cache_p = (type == GNUTLS_CLIENT) ? &client_session_cache : &server_session_cache;
224   if (!*cache_p && create)
225     {
226       *cache_p = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
227                                         NULL, cache_data_free);
228     }
229   return *cache_p;
230 }
231
232 void
233 g_tls_backend_gnutls_store_session (unsigned int             type,
234                                     GBytes                  *session_id,
235                                     GBytes                  *session_data)
236 {
237   GTlsBackendGnutlsCacheData *cache_data;
238   GHashTable *cache;
239
240   G_LOCK (session_cache_lock);
241
242   cache = get_session_cache (type, TRUE);
243   cache_data = g_hash_table_lookup (cache, session_id);
244   if (cache_data)
245     {
246       if (!g_bytes_equal (cache_data->session_data, session_data))
247         {
248           g_bytes_unref (cache_data->session_data);
249           cache_data->session_data = g_bytes_ref (session_data);
250         }
251     }
252   else
253     {
254       if (g_hash_table_size (cache) >= SESSION_CACHE_MAX_SIZE)
255         session_cache_cleanup (cache);
256
257       cache_data = g_slice_new (GTlsBackendGnutlsCacheData);
258       cache_data->session_id = g_bytes_ref (session_id);
259       cache_data->session_data = g_bytes_ref (session_data);
260
261       g_hash_table_insert (cache, cache_data->session_id, cache_data);
262     }
263   cache_data->last_used = time (NULL);
264
265   G_UNLOCK (session_cache_lock);
266 }
267
268 void
269 g_tls_backend_gnutls_remove_session (unsigned int             type,
270                                      GBytes                  *session_id)
271 {
272   GHashTable *cache;
273
274   G_LOCK (session_cache_lock);
275
276   cache = get_session_cache (type, FALSE);
277   if (cache)
278     g_hash_table_remove (cache, session_id);
279
280   G_UNLOCK (session_cache_lock);
281 }
282
283 GBytes *
284 g_tls_backend_gnutls_lookup_session (unsigned int             type,
285                                      GBytes                  *session_id)
286 {
287   GTlsBackendGnutlsCacheData *cache_data;
288   GBytes *session_data = NULL;
289   GHashTable *cache;
290
291   G_LOCK (session_cache_lock);
292
293   cache = get_session_cache (type, FALSE);
294   if (cache)
295     {
296       cache_data = g_hash_table_lookup (cache, session_id);
297       if (cache_data)
298         {
299           cache_data->last_used = time (NULL);
300           session_data = g_bytes_ref (cache_data->session_data);
301         }
302     }
303
304   G_UNLOCK (session_cache_lock);
305
306   return session_data;
307 }
308
309 void
310 g_tls_backend_gnutls_register (GIOModule *module)
311 {
312   g_tls_backend_gnutls_register_type (G_TYPE_MODULE (module));
313   g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
314                                   g_tls_backend_gnutls_get_type(),
315                                   "gnutls",
316                                   0);
317 }