[glib-networking/tls-database] gnutls: Initial implementation of GTlsFileDatabaseGnutls



commit 2d26b9d67e35422176f56def057bb77f3281677d
Author: Stef Walter <stefw collabora co uk>
Date:   Thu Dec 16 20:01:37 2010 +0000

    gnutls: Initial implementation of GTlsFileDatabaseGnutls
    
    A database which loads certificates from a filename and exposes
    them all as anchored certificates. Not yet tested or plugged
    into anything.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=636572

 tls/gnutls/Makefile.am               |    2 +
 tls/gnutls/gtlsdatabase-gnutls.h     |    4 +-
 tls/gnutls/gtlsfiledatabase-gnutls.c |  348 ++++++++++++++++++++++++++++++++++
 tls/gnutls/gtlsfiledatabase-gnutls.h |   52 +++++
 4 files changed, 404 insertions(+), 2 deletions(-)
---
diff --git a/tls/gnutls/Makefile.am b/tls/gnutls/Makefile.am
index 9b8c0f6..f9f1965 100644
--- a/tls/gnutls/Makefile.am
+++ b/tls/gnutls/Makefile.am
@@ -30,6 +30,8 @@ libgiognutls_la_SOURCES = 		\
 	gtlsconnection-gnutls.h		\
 	gtlsdatabase-gnutls.c		\
 	gtlsdatabase-gnutls.h		\
+	gtlsfiledatabase-gnutls.c	\
+	gtlsfiledatabase-gnutls.h	\
 	gtlsinputstream-gnutls.c	\
 	gtlsinputstream-gnutls.h	\
 	gtlsoutputstream-gnutls.c	\
diff --git a/tls/gnutls/gtlsdatabase-gnutls.h b/tls/gnutls/gtlsdatabase-gnutls.h
index 840265b..fbb575e 100644
--- a/tls/gnutls/gtlsdatabase-gnutls.h
+++ b/tls/gnutls/gtlsdatabase-gnutls.h
@@ -44,7 +44,7 @@ struct _GTlsDatabaseGnutlsClass
   gboolean       (*lookup_assertion)      (GTlsDatabaseGnutls          *self,
                                            GTlsCertificateGnutls       *certificate,
                                            GTlsDatabaseGnutlsAssertion  assertion,
-                                           GSocketConnectable          *connectable,
+                                           GSocketConnectable          *identity,
                                            GCancellable                *cancellable,
                                            GError                     **error);
 };
@@ -60,7 +60,7 @@ GType          g_tls_database_gnutls_get_type              (void) G_GNUC_CONST;
 gboolean       g_tls_database_gnutls_lookup_assertion      (GTlsDatabaseGnutls          *self,
                                                             GTlsCertificateGnutls       *certificate,
                                                             GTlsDatabaseGnutlsAssertion  assertion,
-                                                            GSocketConnectable          *connectable,
+                                                            GSocketConnectable          *identity,
                                                             GCancellable                *cancellable,
                                                             GError                     **error);
 
diff --git a/tls/gnutls/gtlsfiledatabase-gnutls.c b/tls/gnutls/gtlsfiledatabase-gnutls.c
new file mode 100644
index 0000000..8aadd65
--- /dev/null
+++ b/tls/gnutls/gtlsfiledatabase-gnutls.c
@@ -0,0 +1,348 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gtlsfiledatabase-gnutls.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <gnutls/x509.h>
+
+G_DEFINE_TYPE (GTlsFileDatabaseGnutls, g_tls_file_database_gnutls,
+               G_TYPE_TLS_DATABASE_GNUTLS);
+
+enum
+{
+  PROP_0,
+  PROP_ANCHOR_FILENAME,
+};
+
+struct _GTlsFileDatabaseGnutlsPrivate
+{
+  /* read-only after construct */
+  gchar *anchor_filename;
+
+  /* protected by mutex */
+  GStaticMutex mutex;
+  GHashTable *anchors;
+};
+
+static guint
+byte_array_hash (gconstpointer v)
+{
+  const GByteArray *array = v;
+  const signed char *p;
+  guint32 h = 0;
+  gsize i;
+
+  g_assert (array);
+  g_assert (array->data);
+  p = (signed char*)array->data;
+
+  /* 31 bit hash function */
+  for (i = 0; i < array->len; ++i, ++p)
+    h = (h << 5) - h + *p;
+
+  return h;
+}
+
+static gboolean
+byte_array_equal (gconstpointer v1, gconstpointer v2)
+{
+  const GByteArray *array1 = v1;
+  const GByteArray *array2 = v2;
+
+  if (array1 == array2)
+    return TRUE;
+  if (!array1 || !array2)
+    return FALSE;
+
+  if (array1->len != array2->len)
+    return FALSE;
+
+  if (array1->data == array2->data)
+    return TRUE;
+  if (!array1->data || !array2->data)
+    return FALSE;
+
+  return (memcmp (array1->data, array2->data, array1->len) == 0) ? TRUE : FALSE;
+}
+
+static gboolean
+load_anchor_file (const gchar *filename,
+                  GHashTable  *anchors,
+                  GError     **error)
+{
+  GList *list, *l;
+  gnutls_x509_crt_t cert;
+  gnutls_datum_t dn;
+  GByteArray *der, *subject;
+  gint gerr;
+
+  g_assert (error);
+
+  list = g_tls_certificate_list_new_from_file (filename, error);
+  if (*error)
+    return FALSE;
+
+  for (l = list; l; l = g_list_next (l))
+    {
+      cert = g_tls_certificate_gnutls_get_cert (l->data);
+      gerr = gnutls_x509_crt_get_raw_dn (cert, &dn);
+      if (gerr < 0)
+        {
+          g_warning ("failed to get subject of anchor certificate: %s",
+                     gnutls_strerror (gerr));
+          continue;
+        }
+
+      /* Dig out the full value of this certificate's DER encoding */
+      der = NULL;
+      g_object_get (l->data, "certificate", &der, NULL);
+      g_return_val_if_fail (der, FALSE);
+
+      subject = g_byte_array_new ();
+      g_byte_array_append (subject, dn.data, dn.size);
+      gnutls_free (dn.data);
+
+      /* Insert two different ways into our anchors hash table */
+      g_hash_table_replace (anchors, g_byte_array_ref (der), g_byte_array_ref (der));
+      g_hash_table_replace (anchors, g_byte_array_ref (subject), g_byte_array_ref (der));
+
+      /*
+       * TODO: Do we need to be able to handle multiple certificates with the
+       * same subject DN? It seems like having such certificates would be bad
+       * bad practice...
+       */
+
+      g_byte_array_unref (der);
+      g_byte_array_unref (subject);
+    }
+
+  return TRUE;
+}
+
+static GHashTable*
+ensure_and_ref_anchors (GTlsFileDatabaseGnutls *self)
+{
+  GHashTable *anchors;
+  GError *error = NULL;
+
+  g_static_mutex_lock (&self->priv->mutex);
+
+  if (self->priv->anchors)
+    {
+      anchors = g_hash_table_ref (self->priv->anchors);
+    }
+  else
+    {
+      anchors = g_hash_table_new_full (byte_array_hash, byte_array_equal,
+                                       (GDestroyNotify)g_byte_array_unref,
+                                       (GDestroyNotify)g_byte_array_unref);
+
+      if (load_anchor_file (self->priv->anchor_filename, anchors, &error))
+        {
+          self->priv->anchors = g_hash_table_ref (anchors);
+        }
+      else
+        {
+          /*
+           * Note that this is not a security problem, since if
+           * G_TLS_VALIDATE_CA is set, then this just means validation
+           * will always fail, and if it isn't set, then it doesn't
+           * matter that we couldn't read the CAs.
+           */
+          g_warning ("couldn't load certificate anchor file: %s: %s",
+                     self->priv->anchor_filename,
+                     error && error->message ? error->message : "");
+        }
+    }
+
+  g_static_mutex_unlock (&self->priv->mutex);
+
+  /* We always return one, even if it's just empty */
+  return anchors;
+}
+
+static void
+g_tls_file_database_gnutls_finalize (GObject *object)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
+
+  g_hash_table_destroy (self->priv->anchors);
+  self->priv->anchors = NULL;
+
+  g_free (self->priv->anchor_filename);
+  self->priv->anchor_filename = NULL;
+
+  g_static_mutex_free (&self->priv->mutex);
+
+  G_OBJECT_CLASS (g_tls_file_database_gnutls_parent_class)->finalize (object);
+}
+
+static void
+g_tls_file_database_gnutls_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_ANCHOR_FILENAME:
+      g_value_set_string (value, self->priv->anchor_filename);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_file_database_gnutls_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
+
+  switch (prop_id)
+    {
+    case PROP_ANCHOR_FILENAME:
+      self->priv->anchor_filename = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_file_database_gnutls_init (GTlsFileDatabaseGnutls *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                            G_TYPE_TLS_FILE_DATABASE_GNUTLS,
+                                            GTlsFileDatabaseGnutlsPrivate);
+  g_static_mutex_init (&self->priv->mutex);
+}
+
+static gboolean
+g_tls_file_database_gnutls_lookup_assertion (GTlsDatabaseGnutls          *database,
+                                             GTlsCertificateGnutls       *certificate,
+                                             GTlsDatabaseGnutlsAssertion  assertion,
+                                             GSocketConnectable          *identity,
+                                             GCancellable                *cancellable,
+                                             GError                     **error)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+  GByteArray *der = NULL;
+  gboolean contains;
+  GHashTable *anchors;
+
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), FALSE);
+  g_return_val_if_fail (!identity || G_IS_SOCKET_CONNECTABLE (identity), FALSE);
+  g_return_val_if_fail (!error || !*error, FALSE);
+
+  /* We only have anchored certificate assertions here */
+  if (assertion != G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE)
+    return FALSE;
+
+  g_object_get (certificate, "certificate", &der, NULL);
+  g_return_val_if_fail (der, FALSE);
+
+  anchors = ensure_and_ref_anchors (self);
+  contains = g_hash_table_lookup (anchors, der) ? TRUE : FALSE;
+  g_byte_array_unref (der);
+  g_hash_table_unref (anchors);
+
+  /* All certificates in our file are anchored certificates */
+  return contains;
+}
+
+static GList*
+g_tls_file_database_gnutls_lookup_certificates (GTlsDatabase          *database,
+                                                GTlsDatabaseLookupType lookup_type,
+                                                gconstpointer          identifier,
+                                                gsize                  identifier_length,
+                                                GCancellable          *cancellable,
+                                                GError               **error)
+{
+  GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+  GByteArray *subject, *der;
+  GHashTable *anchors;
+  gnutls_datum_t datum;
+  GList *results = NULL;
+
+  g_return_val_if_fail (!error || !*error, NULL);
+
+  /* We can only perform lookups for issuer */
+  if (lookup_type != G_TLS_DATABASE_LOOKUP_ISSUER)
+    return NULL;
+
+  /* The value is the subject DN of the issuer */
+  g_return_val_if_fail (identifier, NULL);
+  g_return_val_if_fail (identifier_length, NULL);
+  subject = g_byte_array_new ();
+  g_byte_array_append (subject, identifier, identifier_length);
+
+  /* Find the full DER value of the certificate */
+  anchors = ensure_and_ref_anchors (self);
+  der = g_hash_table_lookup (anchors, subject);
+
+  g_byte_array_unref (subject);
+
+  if (der != NULL)
+    {
+      datum.data = der->data;
+      datum.size = der->len;
+      results = g_list_append (results, g_tls_certificate_gnutls_new (&datum, NULL));
+    }
+
+  g_hash_table_unref (anchors);
+  return results;
+}
+
+static void
+g_tls_file_database_gnutls_class_init (GTlsFileDatabaseGnutlsClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+  GTlsDatabaseGnutlsClass *gnutls_class = G_TLS_DATABASE_GNUTLS_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsFileDatabaseGnutlsPrivate));
+
+  gobject_class->get_property = g_tls_file_database_gnutls_get_property;
+  gobject_class->set_property = g_tls_file_database_gnutls_set_property;
+  gobject_class->finalize     = g_tls_file_database_gnutls_finalize;
+
+  database_class->lookup_certificates = g_tls_file_database_gnutls_lookup_certificates;
+  gnutls_class->lookup_assertion = g_tls_file_database_gnutls_lookup_assertion;
+
+  g_object_class_install_property (gobject_class, PROP_ANCHOR_FILENAME,
+                                   g_param_spec_string ("anchor-filename",
+                                                         N_("Anchor Filename"),
+                                                         N_("Path to system certificate anchor file"),
+                                                         NULL,
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT |
+                                                         G_PARAM_STATIC_STRINGS));
+}
diff --git a/tls/gnutls/gtlsfiledatabase-gnutls.h b/tls/gnutls/gtlsfiledatabase-gnutls.h
new file mode 100644
index 0000000..faa2c7d
--- /dev/null
+++ b/tls/gnutls/gtlsfiledatabase-gnutls.h
@@ -0,0 +1,52 @@
+/* GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright © 2010 Collabora, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_TLS_FILE_DATABASE_GNUTLS_H__
+#define __G_TLS_FILE_DATABASE_GNUTLS_H__
+
+#include <gio/gio.h>
+
+#include "gtlsdatabase-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_FILE_DATABASE_GNUTLS            (g_tls_file_database_gnutls_get_type ())
+#define G_TLS_FILE_DATABASE_GNUTLS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_FILE_DATABASE_GNUTLS, GTlsFileDatabaseGnutls))
+#define G_TLS_FILE_DATABASE_GNUTLS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_FILE_DATABASE_GNUTLS, GTlsFileDatabaseGnutlsClass))
+#define G_IS_TLS_FILE_DATABASE_GNUTLS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_FILE_DATABASE_GNUTLS))
+#define G_IS_TLS_FILE_DATABASE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_FILE_DATABASE_GNUTLS))
+#define G_TLS_FILE_DATABASE_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_FILE_DATABASE_GNUTLS, GTlsFileDatabaseGnutlsClass))
+
+typedef struct _GTlsFileDatabaseGnutlsPrivate                   GTlsFileDatabaseGnutlsPrivate;
+typedef struct _GTlsFileDatabaseGnutlsClass                     GTlsFileDatabaseGnutlsClass;
+typedef struct _GTlsFileDatabaseGnutls                          GTlsFileDatabaseGnutls;
+
+struct _GTlsFileDatabaseGnutlsClass
+{
+  GTlsDatabaseGnutlsClass parent_class;
+};
+
+struct _GTlsFileDatabaseGnutls
+{
+  GTlsDatabaseGnutls parent_instance;
+  GTlsFileDatabaseGnutlsPrivate *priv;
+};
+
+GType                        g_tls_file_database_gnutls_get_type              (void) G_GNUC_CONST;
+
+GTlsDatabase*                g_tls_file_database_gnutls_new                   (const gchar *anchor_file);
+
+G_END_DECLS
+
+#endif /* __G_TLS_FILE_DATABASE_GNUTLS_H___ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]