Remove explicit gcrypt references
[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 struct _GTlsBackendGnutlsPrivate
35 {
36   GMutex mutex;
37   GTlsDatabase *default_database;
38 };
39
40 static void gtls_gnutls_init (void);
41 static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
42
43 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendGnutls, g_tls_backend_gnutls, G_TYPE_OBJECT, 0,
44                                 G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_TLS_BACKEND,
45                                                                g_tls_backend_gnutls_interface_init);
46                                 gtls_gnutls_init ();
47                                 )
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 void
58 gtls_gnutls_init (void)
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
66   /* Leak the module to keep it from being unloaded and breaking
67    * the pointer to gtls_log_func().
68    */
69   g_type_plugin_use (g_type_get_plugin (G_TYPE_TLS_BACKEND_GNUTLS));
70 #endif
71 }
72
73 static void
74 g_tls_backend_gnutls_init (GTlsBackendGnutls *backend)
75 {
76   backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsPrivate);
77   g_mutex_init (&backend->priv->mutex);
78 }
79
80 static void
81 g_tls_backend_gnutls_finalize (GObject *object)
82 {
83   GTlsBackendGnutls *backend = G_TLS_BACKEND_GNUTLS (object);
84
85   if (backend->priv->default_database)
86     g_object_unref (backend->priv->default_database);
87   g_mutex_clear (&backend->priv->mutex);
88
89   G_OBJECT_CLASS (g_tls_backend_gnutls_parent_class)->finalize (object);
90 }
91
92 static GTlsDatabase*
93 g_tls_backend_gnutls_real_create_database (GTlsBackendGnutls  *self,
94                                            GError            **error)
95 {
96   const gchar *anchor_file = NULL;
97 #ifdef GTLS_SYSTEM_CA_FILE
98   anchor_file = GTLS_SYSTEM_CA_FILE;
99 #endif
100   return g_tls_file_database_new (anchor_file, error);
101 }
102
103 static void
104 g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
105 {
106   GObjectClass *gobject_class = G_OBJECT_CLASS (backend_class);
107   gobject_class->finalize = g_tls_backend_gnutls_finalize;
108   backend_class->create_database = g_tls_backend_gnutls_real_create_database;
109   g_type_class_add_private (backend_class, sizeof (GTlsBackendGnutlsPrivate));
110 }
111
112 static void
113 g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
114 {
115 }
116
117 static GTlsDatabase*
118 g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
119 {
120   GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
121   GTlsDatabase *result;
122   GError *error = NULL;
123
124   g_mutex_lock (&self->priv->mutex);
125
126   if (self->priv->default_database)
127     {
128       result = g_object_ref (self->priv->default_database);
129     }
130   else
131     {
132       g_assert (G_TLS_BACKEND_GNUTLS_GET_CLASS (self)->create_database);
133       result = G_TLS_BACKEND_GNUTLS_GET_CLASS (self)->create_database (self, &error);
134       if (error)
135         {
136           g_warning ("couldn't load TLS file database: %s",
137                      error->message);
138           g_clear_error (&error);
139         }
140       else
141         {
142           g_assert (result);
143           self->priv->default_database = g_object_ref (result);
144         }
145     }
146
147   g_mutex_unlock (&self->priv->mutex);
148
149   return result;
150 }
151
152 static void
153 g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
154 {
155   iface->get_certificate_type       = g_tls_certificate_gnutls_get_type;
156   iface->get_client_connection_type = g_tls_client_connection_gnutls_get_type;
157   iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
158   iface->get_file_database_type =     g_tls_file_database_gnutls_get_type;
159   iface->get_default_database =       g_tls_backend_gnutls_get_default_database;
160 }
161
162 /* Session cache support; all the details are sort of arbitrary. Note
163  * that having session_cache_cleanup() be a little bit slow isn't the
164  * end of the world, since it will still be faster than the network
165  * is. (NSS uses a linked list for its cache...)
166  */
167
168 G_LOCK_DEFINE_STATIC (session_cache_lock);
169 GHashTable *session_cache;
170
171 #define SESSION_CACHE_MAX_SIZE 50
172 #define SESSION_CACHE_MAX_AGE (60 * 60) /* one hour */
173
174 typedef struct {
175   gchar      *session_id;
176   GByteArray *session_data;
177   time_t      last_used;
178 } GTlsBackendGnutlsCacheData;
179
180 static void
181 session_cache_cleanup (void)
182 {
183   GHashTableIter iter;
184   gpointer key, value;
185   GTlsBackendGnutlsCacheData *cache_data;
186   time_t expired = time (NULL) - SESSION_CACHE_MAX_AGE;
187
188   g_hash_table_iter_init (&iter, session_cache);
189   while (g_hash_table_iter_next (&iter, &key, &value))
190     {
191       cache_data = value;
192       if (cache_data->last_used < expired)
193         g_hash_table_iter_remove (&iter);
194     }
195 }
196
197 static void
198 cache_data_free (gpointer data)
199 {
200   GTlsBackendGnutlsCacheData *cache_data = data;
201
202   g_free (cache_data->session_id);
203   g_byte_array_unref (cache_data->session_data);
204   g_slice_free (GTlsBackendGnutlsCacheData, cache_data);
205 }
206
207 void
208 g_tls_backend_gnutls_cache_session_data (const gchar *session_id,
209                                          guchar      *session_data,
210                                          gsize        session_data_length)
211 {
212   GTlsBackendGnutlsCacheData *cache_data;
213
214   G_LOCK (session_cache_lock);
215
216   if (!session_cache)
217     session_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
218                                            NULL, cache_data_free);
219
220   cache_data = g_hash_table_lookup (session_cache, session_id);
221   if (cache_data)
222     {
223       if (cache_data->session_data->len == session_data_length &&
224           memcmp (cache_data->session_data->data,
225                   session_data, session_data_length) == 0)
226         {
227           cache_data->last_used = time (NULL);
228           G_UNLOCK (session_cache_lock);
229           return;
230         }
231
232       g_byte_array_set_size (cache_data->session_data, 0);
233     }
234   else
235     {
236       if (g_hash_table_size (session_cache) >= SESSION_CACHE_MAX_SIZE)
237         session_cache_cleanup ();
238
239       cache_data = g_slice_new (GTlsBackendGnutlsCacheData);
240       cache_data->session_id = g_strdup (session_id);
241       cache_data->session_data = g_byte_array_sized_new (session_data_length);
242
243       g_hash_table_insert (session_cache, cache_data->session_id, cache_data);
244     }
245
246   g_byte_array_append (cache_data->session_data,
247                        session_data, session_data_length);
248   cache_data->last_used = time (NULL);
249   G_UNLOCK (session_cache_lock);
250 }
251
252 void
253 g_tls_backend_gnutls_uncache_session_data (const gchar *session_id)
254 {
255   G_LOCK (session_cache_lock);
256   if (session_cache)
257     g_hash_table_remove (session_cache, session_id);
258   G_UNLOCK (session_cache_lock);
259 }
260
261 GByteArray *
262 g_tls_backend_gnutls_lookup_session_data (const gchar *session_id)
263 {
264   GTlsBackendGnutlsCacheData *cache_data;
265   GByteArray *session_data = NULL;
266
267   G_LOCK (session_cache_lock);
268   if (session_cache)
269     {
270       cache_data = g_hash_table_lookup (session_cache, session_id);
271       if (cache_data)
272         {
273           cache_data->last_used = time (NULL);
274           session_data = g_byte_array_ref (cache_data->session_data);
275         }
276     }
277   G_UNLOCK (session_cache_lock);
278
279   return session_data;
280 }
281
282 void
283 g_tls_backend_gnutls_register (GIOModule *module)
284 {
285   g_tls_backend_gnutls_register_type (G_TYPE_MODULE (module));
286   g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
287                                   g_tls_backend_gnutls_get_type(),
288                                   "gnutls",
289                                   0);
290 }