[glib-networking/tls-database] gnutls: Initial implementation of GTlsFileDatabaseGnutls
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/tls-database] gnutls: Initial implementation of GTlsFileDatabaseGnutls
- Date: Thu, 16 Dec 2010 20:03:25 +0000 (UTC)
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]