1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright 2010 Collabora, Ltd
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.
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.
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/>.
19 * Author: Stef Walter <stefw@collabora.co.uk>
24 #include "gtlsfiledatabase-gnutls.h"
27 #include <glib/gi18n-lib.h>
28 #include <gnutls/x509.h>
30 static void g_tls_file_database_gnutls_file_database_interface_init (GTlsFileDatabaseInterface *iface);
32 static void g_tls_file_database_gnutls_initable_interface_init (GInitableIface *iface);
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);
47 struct _GTlsFileDatabaseGnutlsPrivate
49 /* read-only after construct */
50 gchar *anchor_filename;
52 /* protected by mutex */
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
64 * This is a table of GBytes -> GBytes. The values and keys are
65 * DER encoded certificate values.
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.
78 bytes_multi_table_new (void)
80 return g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
81 (GDestroyNotify)g_bytes_unref,
82 (GDestroyNotify)g_ptr_array_unref);
86 bytes_multi_table_insert (GHashTable *table,
92 multi = g_hash_table_lookup (table, key);
95 multi = g_ptr_array_new_with_free_func ((GDestroyNotify)g_bytes_unref);
96 g_hash_table_insert (table, g_bytes_ref (key), multi);
98 g_ptr_array_add (multi, g_bytes_ref (value));
102 bytes_multi_table_lookup_ref_one (GHashTable *table,
107 multi = g_hash_table_lookup (table, key);
111 g_assert (multi->len > 0);
112 return g_bytes_ref (multi->pdata[0]);
116 bytes_multi_table_lookup_ref_all (GHashTable *table,
123 multi = g_hash_table_lookup (table, key);
127 for (i = 0; i < multi->len; i++)
128 list = g_list_prepend (list, g_bytes_ref (multi->pdata[i]));
130 return g_list_reverse (list);
134 create_handle_for_certificate (const gchar *filename,
142 * Here we create a URI that looks like:
143 * file:///etc/ssl/certs/ca-certificates.crt#11b2641821252596420e468c275771f5e51022c121a17bd7a89a2f37b6336c8f
146 uri_part = g_filename_to_uri (filename, NULL, NULL);
150 bookmark = g_compute_checksum_for_bytes (G_CHECKSUM_SHA256, der);
151 uri = g_strconcat (uri_part, "#", bookmark, NULL);
160 create_handles_array_unlocked (const gchar *filename,
161 GHashTable *complete)
168 handles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
169 (GDestroyNotify)g_bytes_unref);
171 g_hash_table_iter_init (&iter, complete);
172 while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&der))
174 handle = create_handle_for_certificate (filename, der);
176 g_hash_table_insert (handles, handle, g_bytes_ref (der));
183 load_anchor_file (const gchar *filename,
184 GHashTable *subjects,
186 GHashTable *complete,
190 gnutls_x509_crt_t cert;
196 GError *my_error = NULL;
198 list = g_tls_certificate_list_new_from_file (filename, &my_error);
201 g_propagate_error (error, my_error);
205 for (l = list; l; l = l->next)
207 cert = g_tls_certificate_gnutls_get_cert (l->data);
208 gerr = gnutls_x509_crt_get_raw_dn (cert, &dn);
211 g_warning ("failed to get subject of anchor certificate: %s",
212 gnutls_strerror (gerr));
216 subject = g_bytes_new_with_free_func (dn.data, dn.size, gnutls_free, dn.data);
218 gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
221 g_warning ("failed to get subject of anchor certificate: %s",
222 gnutls_strerror (gerr));
226 issuer = g_bytes_new_with_free_func (dn.data, dn.size, gnutls_free, dn.data);
228 der = g_tls_certificate_gnutls_get_bytes (l->data);
229 g_return_val_if_fail (der != NULL, FALSE);
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);
235 g_hash_table_insert (complete, g_bytes_ref (der),
239 g_bytes_unref (subject);
240 g_bytes_unref (issuer);
242 g_object_unref (l->data);
252 g_tls_file_database_gnutls_finalize (GObject *object)
254 GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
256 if (self->priv->subjects)
257 g_hash_table_destroy (self->priv->subjects);
258 self->priv->subjects = NULL;
260 if (self->priv->issuers)
261 g_hash_table_destroy (self->priv->issuers);
262 self->priv->issuers = NULL;
264 if (self->priv->complete)
265 g_hash_table_destroy (self->priv->complete);
266 self->priv->complete = NULL;
268 if (self->priv->handles)
269 g_hash_table_destroy (self->priv->handles);
270 self->priv->handles = NULL;
272 g_free (self->priv->anchor_filename);
273 self->priv->anchor_filename = NULL;
275 g_mutex_clear (&self->priv->mutex);
277 G_OBJECT_CLASS (g_tls_file_database_gnutls_parent_class)->finalize (object);
281 g_tls_file_database_gnutls_get_property (GObject *object,
286 GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
291 g_value_set_string (value, self->priv->anchor_filename);
294 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
299 g_tls_file_database_gnutls_set_property (GObject *object,
304 GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
310 anchor_path = g_value_dup_string (value);
311 if (anchor_path && !g_path_is_absolute (anchor_path))
313 g_warning ("The anchor file name for used with a GTlsFileDatabase "
314 "must be an absolute path, and not relative: %s", anchor_path);
318 self->priv->anchor_filename = anchor_path;
322 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
327 g_tls_file_database_gnutls_init (GTlsFileDatabaseGnutls *self)
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);
336 g_tls_file_database_gnutls_create_certificate_handle (GTlsDatabase *database,
337 GTlsCertificate *certificate)
339 GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
342 gchar *handle = NULL;
344 der = g_tls_certificate_gnutls_get_bytes (G_TLS_CERTIFICATE_GNUTLS (certificate));
345 g_return_val_if_fail (der != NULL, FALSE);
347 g_mutex_lock (&self->priv->mutex);
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;
352 g_mutex_unlock (&self->priv->mutex);
354 /* Certificate is in the database */
356 handle = create_handle_for_certificate (self->priv->anchor_filename, der);
362 static GTlsCertificate*
363 g_tls_file_database_gnutls_lookup_certificate_for_handle (GTlsDatabase *database,
365 GTlsInteraction *interaction,
366 GTlsDatabaseLookupFlags flags,
367 GCancellable *cancellable,
370 GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
371 GTlsCertificate *cert;
373 gnutls_datum_t datum;
376 if (g_cancellable_set_error_if_cancelled (cancellable, error))
382 g_mutex_lock (&self->priv->mutex);
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);
389 der = g_hash_table_lookup (self->priv->handles, handle);
393 g_mutex_unlock (&self->priv->mutex);
398 datum.data = (unsigned char *)g_bytes_get_data (der, &length);
401 if (g_cancellable_set_error_if_cancelled (cancellable, error))
404 cert = g_tls_certificate_gnutls_new (&datum, NULL);
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,
419 GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
423 if (g_cancellable_set_error_if_cancelled (cancellable, error))
426 /* We only have anchored certificate assertions here */
427 if (assertion != G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE)
431 * TODO: We should be parsing any Extended Key Usage attributes and
432 * comparing them to the purpose.
435 der = g_tls_certificate_gnutls_get_bytes (certificate);
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);
443 if (g_cancellable_set_error_if_cancelled (cancellable, error))
446 /* All certificates in our file are anchored certificates */
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,
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;
467 g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), NULL);
469 if (g_cancellable_set_error_if_cancelled (cancellable, error))
472 if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
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);
480 g_warning ("failed to get issuer of certificate: %s", gnutls_strerror (gerr));
484 subject = g_bytes_new_with_free_func (dn.data, dn.size, gnutls_free, dn.data);
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);
491 g_bytes_unref (subject);
493 if (g_cancellable_set_error_if_cancelled (cancellable, error))
497 else if (der != NULL)
499 datum.data = (unsigned char *)g_bytes_get_data (der, &length);
501 issuer = g_tls_certificate_gnutls_new (&datum, NULL);
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,
517 GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
519 gnutls_datum_t datum;
520 GList *issued = NULL;
525 if (g_cancellable_set_error_if_cancelled (cancellable, error))
528 /* We don't have any private keys here */
529 if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
532 issuer = g_bytes_new_static (issuer_raw_dn->data, issuer_raw_dn->len);
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);
539 g_bytes_unref (issuer);
541 for (l = ders; l != NULL; l = g_list_next (l))
543 if (g_cancellable_set_error_if_cancelled (cancellable, error))
545 g_list_free_full (issued, g_object_unref);
550 datum.data = (unsigned char *)g_bytes_get_data (l->data, &length);
552 issued = g_list_prepend (issued, g_tls_certificate_gnutls_new (&datum, NULL));
555 g_list_free_full (ders, (GDestroyNotify)g_bytes_unref);
560 g_tls_file_database_gnutls_class_init (GTlsFileDatabaseGnutlsClass *klass)
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);
566 g_type_class_add_private (klass, sizeof (GTlsFileDatabaseGnutlsPrivate));
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;
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;
578 g_object_class_override_property (gobject_class, PROP_ANCHORS, "anchors");
582 g_tls_file_database_gnutls_file_database_interface_init (GTlsFileDatabaseInterface *iface)
588 g_tls_file_database_gnutls_initable_init (GInitable *initable,
589 GCancellable *cancellable,
592 GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (initable);
593 GHashTable *subjects, *issuers, *complete;
596 if (g_cancellable_set_error_if_cancelled (cancellable, error))
599 subjects = bytes_multi_table_new ();
600 issuers = bytes_multi_table_new ();
602 complete = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
603 (GDestroyNotify)g_bytes_unref,
604 (GDestroyNotify)g_bytes_unref);
606 result = load_anchor_file (self->priv->anchor_filename, subjects, issuers,
609 if (g_cancellable_set_error_if_cancelled (cancellable, error))
614 g_mutex_lock (&self->priv->mutex);
615 if (!self->priv->subjects)
617 self->priv->subjects = subjects;
620 if (!self->priv->issuers)
622 self->priv->issuers = issuers;
625 if (!self->priv->complete)
627 self->priv->complete = complete;
630 g_mutex_unlock (&self->priv->mutex);
633 if (subjects != NULL)
634 g_hash_table_unref (subjects);
636 g_hash_table_unref (issuers);
637 if (complete != NULL)
638 g_hash_table_unref (complete);
643 g_tls_file_database_gnutls_initable_interface_init (GInitableIface *iface)
645 iface->init = g_tls_file_database_gnutls_initable_init;