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