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