Imported Upstream version 2.34.0
[platform/upstream/glib-networking.git] / tls / gnutls / gtlsfiledatabase-gnutls.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright 2010 Collabora, Ltd
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  * Author: Stef Walter <stefw@collabora.co.uk>
20  */
21
22 #include "config.h"
23
24 #include "gtlsfiledatabase-gnutls.h"
25
26 #include <gio/gio.h>
27 #include <glib/gi18n-lib.h>
28 #include <gnutls/x509.h>
29
30 static void g_tls_file_database_gnutls_file_database_interface_init (GTlsFileDatabaseInterface *iface);
31
32 static void g_tls_file_database_gnutls_initable_interface_init (GInitableIface *iface);
33
34 G_DEFINE_TYPE_WITH_CODE (GTlsFileDatabaseGnutls, g_tls_file_database_gnutls, G_TYPE_TLS_DATABASE_GNUTLS,
35                          G_IMPLEMENT_INTERFACE (G_TYPE_TLS_FILE_DATABASE,
36                                                 g_tls_file_database_gnutls_file_database_interface_init);
37                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
38                                                 g_tls_file_database_gnutls_initable_interface_init);
39 );
40
41 enum
42 {
43   PROP_0,
44   PROP_ANCHORS,
45 };
46
47 struct _GTlsFileDatabaseGnutlsPrivate
48 {
49   /* read-only after construct */
50   gchar *anchor_filename;
51
52   /* protected by mutex */
53   GMutex mutex;
54
55   /*
56    * These are hash tables of GBytes -> GPtrArray<GBytes>. The values of
57    * the ptr array are full DER encoded certificate values. The keys are byte
58    * arrays containing either subject DNs, issuer DNs, or full DER encoded certs
59    */
60   GHashTable *subjects;
61   GHashTable *issuers;
62
63   /*
64    * This is a table of GBytes -> GBytes. The values and keys are
65    * DER encoded certificate values.
66    */
67   GHashTable *complete;
68
69   /*
70    * This is a table of gchar * -> GPtrArray<GBytes>. The values of
71    * the ptr array are full DER encoded certificate values. The keys are the
72    * string handles. This array is populated on demand.
73    */
74   GHashTable *handles;
75 };
76
77 static GHashTable *
78 bytes_multi_table_new (void)
79 {
80   return g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
81                                 (GDestroyNotify)g_bytes_unref,
82                                 (GDestroyNotify)g_ptr_array_unref);
83 }
84
85 static void
86 bytes_multi_table_insert (GHashTable *table,
87                           GBytes     *key,
88                           GBytes     *value)
89 {
90   GPtrArray *multi;
91
92   multi = g_hash_table_lookup (table, key);
93   if (multi == NULL)
94     {
95       multi = g_ptr_array_new_with_free_func ((GDestroyNotify)g_bytes_unref);
96       g_hash_table_insert (table, g_bytes_ref (key), multi);
97     }
98   g_ptr_array_add (multi, g_bytes_ref (value));
99 }
100
101 static GBytes *
102 bytes_multi_table_lookup_ref_one (GHashTable *table,
103                                   GBytes     *key)
104 {
105   GPtrArray *multi;
106
107   multi = g_hash_table_lookup (table, key);
108   if (multi == NULL)
109     return NULL;
110
111   g_assert (multi->len > 0);
112   return g_bytes_ref (multi->pdata[0]);
113 }
114
115 static GList *
116 bytes_multi_table_lookup_ref_all (GHashTable *table,
117                                   GBytes     *key)
118 {
119   GPtrArray *multi;
120   GList *list = NULL;
121   gint i;
122
123   multi = g_hash_table_lookup (table, key);
124   if (multi == NULL)
125     return NULL;
126
127   for (i = 0; i < multi->len; i++)
128     list = g_list_prepend (list, g_bytes_ref (multi->pdata[i]));
129
130   return g_list_reverse (list);
131 }
132
133 static gchar *
134 create_handle_for_certificate (const gchar *filename,
135                                GBytes      *der)
136 {
137   gchar *bookmark;
138   gchar *uri_part;
139   gchar *uri;
140
141   /*
142    * Here we create a URI that looks like:
143    * file:///etc/ssl/certs/ca-certificates.crt#11b2641821252596420e468c275771f5e51022c121a17bd7a89a2f37b6336c8f
144    */
145
146   uri_part = g_filename_to_uri (filename, NULL, NULL);
147   if (!uri_part)
148     return NULL;
149
150   bookmark = g_compute_checksum_for_bytes (G_CHECKSUM_SHA256, der);
151   uri = g_strconcat (uri_part, "#", bookmark, NULL);
152
153   g_free (bookmark);
154   g_free (uri_part);
155
156   return uri;
157 }
158
159 static GHashTable *
160 create_handles_array_unlocked (const gchar *filename,
161                                GHashTable  *complete)
162 {
163   GHashTable *handles;
164   GHashTableIter iter;
165   GBytes *der;
166   gchar *handle;
167
168   handles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
169                                    (GDestroyNotify)g_bytes_unref);
170
171   g_hash_table_iter_init (&iter, complete);
172   while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&der))
173     {
174       handle = create_handle_for_certificate (filename, der);
175       if (handle != NULL)
176         g_hash_table_insert (handles, handle, g_bytes_ref (der));
177     }
178
179   return handles;
180 }
181
182 static gboolean
183 load_anchor_file (const gchar *filename,
184                   GHashTable  *subjects,
185                   GHashTable  *issuers,
186                   GHashTable  *complete,
187                   GError     **error)
188 {
189   GList *list, *l;
190   gnutls_x509_crt_t cert;
191   gnutls_datum_t dn;
192   GBytes *der;
193   GBytes *subject;
194   GBytes *issuer;
195   gint gerr;
196   GError *my_error = NULL;
197
198   list = g_tls_certificate_list_new_from_file (filename, &my_error);
199   if (my_error)
200     {
201       g_propagate_error (error, my_error);
202       return FALSE;
203     }
204
205   for (l = list; l; l = l->next)
206     {
207       cert = g_tls_certificate_gnutls_get_cert (l->data);
208       gerr = gnutls_x509_crt_get_raw_dn (cert, &dn);
209       if (gerr < 0)
210         {
211           g_warning ("failed to get subject of anchor certificate: %s",
212                      gnutls_strerror (gerr));
213           continue;
214         }
215
216       subject = g_bytes_new_with_free_func (dn.data, dn.size, gnutls_free, dn.data);
217
218       gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
219       if (gerr < 0)
220         {
221           g_warning ("failed to get subject of anchor certificate: %s",
222                      gnutls_strerror (gerr));
223           continue;
224         }
225
226       issuer = g_bytes_new_with_free_func (dn.data, dn.size, gnutls_free, dn.data);
227
228       der = g_tls_certificate_gnutls_get_bytes (l->data);
229       g_return_val_if_fail (der != NULL, FALSE);
230
231       /* Three different ways of looking up same certificate */
232       bytes_multi_table_insert (subjects, subject, der);
233       bytes_multi_table_insert (issuers, issuer, der);
234
235       g_hash_table_insert (complete, g_bytes_ref (der),
236                            g_bytes_ref (der));
237
238       g_bytes_unref (der);
239       g_bytes_unref (subject);
240       g_bytes_unref (issuer);
241
242       g_object_unref (l->data);
243     }
244   g_list_free (list);
245
246   return TRUE;
247 }
248
249
250
251 static void
252 g_tls_file_database_gnutls_finalize (GObject *object)
253 {
254   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
255
256   if (self->priv->subjects)
257     g_hash_table_destroy (self->priv->subjects);
258   self->priv->subjects = NULL;
259
260   if (self->priv->issuers)
261     g_hash_table_destroy (self->priv->issuers);
262   self->priv->issuers = NULL;
263
264   if (self->priv->complete)
265     g_hash_table_destroy (self->priv->complete);
266   self->priv->complete = NULL;
267
268   if (self->priv->handles)
269     g_hash_table_destroy (self->priv->handles);
270   self->priv->handles = NULL;
271
272   g_free (self->priv->anchor_filename);
273   self->priv->anchor_filename = NULL;
274
275   g_mutex_clear (&self->priv->mutex);
276
277   G_OBJECT_CLASS (g_tls_file_database_gnutls_parent_class)->finalize (object);
278 }
279
280 static void
281 g_tls_file_database_gnutls_get_property (GObject    *object,
282                                          guint       prop_id,
283                                          GValue     *value,
284                                          GParamSpec *pspec)
285 {
286   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
287
288   switch (prop_id)
289     {
290     case PROP_ANCHORS:
291       g_value_set_string (value, self->priv->anchor_filename);
292       break;
293     default:
294       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
295     }
296 }
297
298 static void
299 g_tls_file_database_gnutls_set_property (GObject      *object,
300                                          guint         prop_id,
301                                          const GValue *value,
302                                          GParamSpec   *pspec)
303 {
304   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
305   gchar *anchor_path;
306
307   switch (prop_id)
308     {
309     case PROP_ANCHORS:
310       anchor_path = g_value_dup_string (value);
311       if (anchor_path && !g_path_is_absolute (anchor_path))
312         {
313           g_warning ("The anchor file name for used with a GTlsFileDatabase "
314                      "must be an absolute path, and not relative: %s", anchor_path);
315         }
316       else
317         {
318           self->priv->anchor_filename = anchor_path;
319         }
320       break;
321     default:
322       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
323     }
324 }
325
326 static void
327 g_tls_file_database_gnutls_init (GTlsFileDatabaseGnutls *self)
328 {
329   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
330                                             G_TYPE_TLS_FILE_DATABASE_GNUTLS,
331                                             GTlsFileDatabaseGnutlsPrivate);
332   g_mutex_init (&self->priv->mutex);
333 }
334
335 static gchar*
336 g_tls_file_database_gnutls_create_certificate_handle (GTlsDatabase            *database,
337                                                       GTlsCertificate         *certificate)
338 {
339   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
340   GBytes *der;
341   gboolean contains;
342   gchar *handle = NULL;
343
344   der = g_tls_certificate_gnutls_get_bytes (G_TLS_CERTIFICATE_GNUTLS (certificate));
345   g_return_val_if_fail (der != NULL, FALSE);
346
347   g_mutex_lock (&self->priv->mutex);
348
349   /* At the same time look up whether this certificate is in list */
350   contains = g_hash_table_lookup (self->priv->complete, der) ? TRUE : FALSE;
351
352   g_mutex_unlock (&self->priv->mutex);
353
354   /* Certificate is in the database */
355   if (contains)
356     handle = create_handle_for_certificate (self->priv->anchor_filename, der);
357
358   g_bytes_unref (der);
359   return handle;
360 }
361
362 static GTlsCertificate*
363 g_tls_file_database_gnutls_lookup_certificate_for_handle (GTlsDatabase            *database,
364                                                           const gchar             *handle,
365                                                           GTlsInteraction         *interaction,
366                                                           GTlsDatabaseLookupFlags  flags,
367                                                           GCancellable            *cancellable,
368                                                           GError                 **error)
369 {
370   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
371   GTlsCertificate *cert;
372   GBytes *der;
373   gnutls_datum_t datum;
374   gsize length;
375
376   if (g_cancellable_set_error_if_cancelled (cancellable, error))
377     return NULL;
378
379   if (!handle)
380     return NULL;
381
382   g_mutex_lock (&self->priv->mutex);
383
384   /* Create the handles table if not already done */
385   if (!self->priv->handles)
386     self->priv->handles = create_handles_array_unlocked (self->priv->anchor_filename,
387                                                          self->priv->complete);
388
389     der = g_hash_table_lookup (self->priv->handles, handle);
390     if (der != NULL)
391       g_bytes_ref (der);
392
393   g_mutex_unlock (&self->priv->mutex);
394
395   if (der == NULL)
396     return NULL;
397
398   datum.data = (unsigned char *)g_bytes_get_data (der, &length);
399   datum.size = length;
400
401   if (g_cancellable_set_error_if_cancelled (cancellable, error))
402     cert = NULL;
403   else
404     cert = g_tls_certificate_gnutls_new (&datum, NULL);
405
406   g_bytes_unref (der);
407   return cert;
408 }
409
410 static gboolean
411 g_tls_file_database_gnutls_lookup_assertion (GTlsDatabaseGnutls          *database,
412                                              GTlsCertificateGnutls       *certificate,
413                                              GTlsDatabaseGnutlsAssertion  assertion,
414                                              const gchar                 *purpose,
415                                              GSocketConnectable          *identity,
416                                              GCancellable                *cancellable,
417                                              GError                     **error)
418 {
419   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
420   GBytes *der = NULL;
421   gboolean contains;
422
423   if (g_cancellable_set_error_if_cancelled (cancellable, error))
424     return FALSE;
425
426   /* We only have anchored certificate assertions here */
427   if (assertion != G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE)
428     return FALSE;
429
430   /*
431    * TODO: We should be parsing any Extended Key Usage attributes and
432    * comparing them to the purpose.
433    */
434
435   der = g_tls_certificate_gnutls_get_bytes (certificate);
436
437   g_mutex_lock (&self->priv->mutex);
438   contains = g_hash_table_lookup (self->priv->complete, der) ? TRUE : FALSE;
439   g_mutex_unlock (&self->priv->mutex);
440
441   g_bytes_unref (der);
442
443   if (g_cancellable_set_error_if_cancelled (cancellable, error))
444     return FALSE;
445
446   /* All certificates in our file are anchored certificates */
447   return contains;
448 }
449
450 static GTlsCertificate*
451 g_tls_file_database_gnutls_lookup_certificate_issuer (GTlsDatabase           *database,
452                                                       GTlsCertificate        *certificate,
453                                                       GTlsInteraction        *interaction,
454                                                       GTlsDatabaseLookupFlags flags,
455                                                       GCancellable           *cancellable,
456                                                       GError                **error)
457 {
458   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
459   gnutls_datum_t dn = { NULL, 0 };
460   GBytes *subject, *der;
461   gnutls_datum_t datum;
462   GTlsCertificate *issuer = NULL;
463   gnutls_x509_crt_t cert;
464   gsize length;
465   int gerr;
466
467   g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), NULL);
468
469   if (g_cancellable_set_error_if_cancelled (cancellable, error))
470     return NULL;
471
472   if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
473     return NULL;
474
475   /* Dig out the issuer of this certificate */
476   cert = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (certificate));
477   gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
478   if (gerr < 0)
479     {
480       g_warning ("failed to get issuer of certificate: %s", gnutls_strerror (gerr));
481       return NULL;
482     }
483
484   subject = g_bytes_new_with_free_func (dn.data, dn.size, gnutls_free, dn.data);
485
486   /* Find the full DER value of the certificate */
487   g_mutex_lock (&self->priv->mutex);
488   der = bytes_multi_table_lookup_ref_one (self->priv->subjects, subject);
489   g_mutex_unlock (&self->priv->mutex);
490
491   g_bytes_unref (subject);
492
493   if (g_cancellable_set_error_if_cancelled (cancellable, error))
494     {
495       issuer = NULL;
496     }
497   else if (der != NULL)
498     {
499       datum.data = (unsigned char *)g_bytes_get_data (der, &length);
500       datum.size = length;
501       issuer = g_tls_certificate_gnutls_new (&datum, NULL);
502     }
503
504   if (der != NULL)
505     g_bytes_unref (der);
506   return issuer;
507 }
508
509 static GList*
510 g_tls_file_database_gnutls_lookup_certificates_issued_by (GTlsDatabase           *database,
511                                                           GByteArray             *issuer_raw_dn,
512                                                           GTlsInteraction        *interaction,
513                                                           GTlsDatabaseLookupFlags flags,
514                                                           GCancellable           *cancellable,
515                                                           GError                **error)
516 {
517   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
518   GBytes *issuer;
519   gnutls_datum_t datum;
520   GList *issued = NULL;
521   GList *ders;
522   gsize length;
523   GList *l;
524
525   if (g_cancellable_set_error_if_cancelled (cancellable, error))
526     return NULL;
527
528   /* We don't have any private keys here */
529   if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
530     return NULL;
531
532   issuer = g_bytes_new_static (issuer_raw_dn->data, issuer_raw_dn->len);
533
534   /* Find the full DER value of the certificate */
535   g_mutex_lock (&self->priv->mutex);
536   ders = bytes_multi_table_lookup_ref_all (self->priv->issuers, issuer);
537   g_mutex_unlock (&self->priv->mutex);
538
539   g_bytes_unref (issuer);
540
541   for (l = ders; l != NULL; l = g_list_next (l))
542     {
543       if (g_cancellable_set_error_if_cancelled (cancellable, error))
544         {
545           g_list_free_full (issued, g_object_unref);
546           issued = NULL;
547           break;
548         }
549
550       datum.data = (unsigned char *)g_bytes_get_data (l->data, &length);
551       datum.size = length;
552       issued = g_list_prepend (issued, g_tls_certificate_gnutls_new (&datum, NULL));
553     }
554
555   g_list_free_full (ders, (GDestroyNotify)g_bytes_unref);
556   return issued;
557 }
558
559 static void
560 g_tls_file_database_gnutls_class_init (GTlsFileDatabaseGnutlsClass *klass)
561 {
562   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
563   GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
564   GTlsDatabaseGnutlsClass *gnutls_class = G_TLS_DATABASE_GNUTLS_CLASS (klass);
565
566   g_type_class_add_private (klass, sizeof (GTlsFileDatabaseGnutlsPrivate));
567
568   gobject_class->get_property = g_tls_file_database_gnutls_get_property;
569   gobject_class->set_property = g_tls_file_database_gnutls_set_property;
570   gobject_class->finalize     = g_tls_file_database_gnutls_finalize;
571
572   database_class->create_certificate_handle = g_tls_file_database_gnutls_create_certificate_handle;
573   database_class->lookup_certificate_for_handle = g_tls_file_database_gnutls_lookup_certificate_for_handle;
574   database_class->lookup_certificate_issuer = g_tls_file_database_gnutls_lookup_certificate_issuer;
575   database_class->lookup_certificates_issued_by = g_tls_file_database_gnutls_lookup_certificates_issued_by;
576   gnutls_class->lookup_assertion = g_tls_file_database_gnutls_lookup_assertion;
577
578   g_object_class_override_property (gobject_class, PROP_ANCHORS, "anchors");
579 }
580
581 static void
582 g_tls_file_database_gnutls_file_database_interface_init (GTlsFileDatabaseInterface *iface)
583 {
584
585 }
586
587 static gboolean
588 g_tls_file_database_gnutls_initable_init (GInitable    *initable,
589                                           GCancellable *cancellable,
590                                           GError      **error)
591 {
592   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (initable);
593   GHashTable *subjects, *issuers, *complete;
594   gboolean result;
595
596   if (g_cancellable_set_error_if_cancelled (cancellable, error))
597     return FALSE;
598
599   subjects = bytes_multi_table_new ();
600   issuers = bytes_multi_table_new ();
601
602   complete = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
603                                     (GDestroyNotify)g_bytes_unref,
604                                     (GDestroyNotify)g_bytes_unref);
605
606   result = load_anchor_file (self->priv->anchor_filename, subjects, issuers,
607                              complete, error);
608
609   if (g_cancellable_set_error_if_cancelled (cancellable, error))
610     result = FALSE;
611
612   if (result)
613     {
614       g_mutex_lock (&self->priv->mutex);
615       if (!self->priv->subjects)
616         {
617           self->priv->subjects = subjects;
618           subjects = NULL;
619         }
620       if (!self->priv->issuers)
621         {
622           self->priv->issuers = issuers;
623           issuers = NULL;
624         }
625       if (!self->priv->complete)
626         {
627           self->priv->complete = complete;
628           complete = NULL;
629         }
630       g_mutex_unlock (&self->priv->mutex);
631     }
632
633   if (subjects != NULL)
634     g_hash_table_unref (subjects);
635   if (issuers != NULL)
636     g_hash_table_unref (issuers);
637   if (complete != NULL)
638     g_hash_table_unref (complete);
639   return result;
640 }
641
642 static void
643 g_tls_file_database_gnutls_initable_interface_init (GInitableIface *iface)
644 {
645   iface->init = g_tls_file_database_gnutls_initable_init;
646 }