[glib-networking/tls-database: 2/2] gnutls: Add initial implemention of GTlsDatabaseGnutls
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/tls-database: 2/2] gnutls: Add initial implemention of GTlsDatabaseGnutls
- Date: Thu, 16 Dec 2010 03:11:52 +0000 (UTC)
commit 8b014927bd8f07f36491a2eaf8f0be1aab539256
Author: Stef Walter <stefw collabora co uk>
Date: Thu Dec 16 03:10:51 2010 +0000
gnutls: Add initial implemention of GTlsDatabaseGnutls
This is a GTlsDatabase which uses gnutls to verify and build
certificate chains. An abstract base class.
https://bugzilla.gnome.org/show_bug.cgi?id=636572
tls/gnutls/Makefile.am | 2 +
tls/gnutls/gtlsdatabase-gnutls.c | 351 ++++++++++++++++++++++++++++++++++++++
tls/gnutls/gtlsdatabase-gnutls.h | 69 ++++++++
3 files changed, 422 insertions(+), 0 deletions(-)
---
diff --git a/tls/gnutls/Makefile.am b/tls/gnutls/Makefile.am
index a206b08..9b8c0f6 100644
--- a/tls/gnutls/Makefile.am
+++ b/tls/gnutls/Makefile.am
@@ -28,6 +28,8 @@ libgiognutls_la_SOURCES = \
gtlsclientconnection-gnutls.h \
gtlsconnection-gnutls.c \
gtlsconnection-gnutls.h \
+ gtlsdatabase-gnutls.c \
+ gtlsdatabase-gnutls.h \
gtlsinputstream-gnutls.c \
gtlsinputstream-gnutls.h \
gtlsoutputstream-gnutls.c \
diff --git a/tls/gnutls/gtlsdatabase-gnutls.c b/tls/gnutls/gtlsdatabase-gnutls.c
new file mode 100644
index 0000000..aeacf27
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls.c
@@ -0,0 +1,351 @@
+/* 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 <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "gtlsdatabase-gnutls.h"
+
+#include "gtlscertificate-gnutls.h"
+
+#include <glib/gi18n-lib.h>
+
+G_DEFINE_ABSTRACT_TYPE (GTlsDatabaseGnutls, g_tls_database_gnutls, G_TYPE_TLS_DATABASE);
+
+enum {
+ STATUS_FAILURE,
+ STATUS_INCOMPLETE,
+ STATUS_SELFSIGNED,
+ STATUS_PINNED,
+ STATUS_ANCHORED,
+};
+
+static void
+g_tls_database_gnutls_init (GTlsDatabaseGnutls *self)
+{
+
+}
+
+static GList*
+list_unref_free (GList *list)
+{
+ GList *l;
+ for (l = list; l; l = g_list_next (l))
+ g_object_unref (l->data);
+ g_list_free (list);
+ return NULL;
+}
+
+static GTlsCertificateGnutls*
+lookup_best_issuer (GTlsDatabaseGnutls *self,
+ GTlsCertificateGnutls *certificate,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsCertificateGnutls *result;
+ gnutls_datum_t dn = { NULL, 0 };
+ gnutls_x509_crt_t cert;
+ GList *list;
+ int gerr;
+
+ g_assert (error);
+ g_assert (!*error);
+
+ /* Dig out the issuer of this certificate */
+ cert = g_tls_certificate_gnutls_get_cert (certificate);
+ gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
+ if (gerr < 0)
+ {
+ g_warning ("failed to get issuer of certificate: %s", gnutls_strerror (gerr));
+ return NULL;
+ }
+
+ list = g_tls_database_lookup_certificates (G_TLS_DATABASE (self),
+ G_TLS_DATABASE_LOOKUP_ISSUER,
+ dn.data,
+ dn.size,
+ cancellable,
+ error);
+
+ gnutls_free (dn.data);
+
+ if (*error)
+ return NULL;
+
+ /*
+ * TODO: For now we just select the first in the list, but do we need
+ * to select the best issuer based on notBefore/notAfter date, and
+ * perhaps the signature. Need to check RFC 5280
+ */
+ result = list ? list->data : NULL;
+ list_unref_free (list);
+ return result;
+}
+
+static gboolean
+is_self_signed (GTlsCertificateGnutls *certificate)
+{
+ const gnutls_x509_crt_t cert = g_tls_certificate_gnutls_get_cert (certificate);
+ return (gnutls_x509_crt_check_issuer (cert, cert) > 0);
+}
+
+static gint
+build_certificate_chain (GTlsDatabaseGnutls *self,
+ GList *input,
+ GSocketConnectable *identity,
+ gint flags,
+ GCancellable *cancellable,
+ GList **chain,
+ GList **anchors,
+ GError **error)
+{
+
+ GTlsCertificateGnutls *certificate;
+
+ g_assert (anchors);
+ g_assert (chain);
+ g_assert (error);
+ g_assert (!*error);
+
+ *chain = NULL;
+ *anchors = NULL;
+
+ /* Get the first certificate */
+ certificate = g_object_ref (G_TLS_CERTIFICATE_GNUTLS (input->data));
+ input = g_list_next (input);
+
+ /* Add this to the output chain */
+ *chain = g_list_append (*chain, certificate);
+
+ /* First check for pinned certificate */
+ if (g_tls_database_gnutls_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE,
+ identity, cancellable, error))
+ {
+ return STATUS_PINNED;
+ }
+ else if(*error)
+ {
+ *chain = list_unref_free (*chain);
+ return STATUS_FAILURE;
+ }
+
+ for (;;)
+ {
+ /* Was the last certificate self-signed? */
+ if (is_self_signed (certificate))
+ return STATUS_SELFSIGNED;
+
+ /* Bring over the next certificate in the chain */
+ if (input)
+ {
+ /* XXXX should we be skipping certs with wrong issuer here? */
+ certificate = g_object_ref (G_TLS_CERTIFICATE_GNUTLS (input->data));
+ input = g_list_next (input);
+ }
+
+ /* No more in chain, try to lookup */
+ else
+ {
+ certificate = lookup_best_issuer (self, certificate, cancellable, error);
+ if (*error)
+ {
+ *chain = list_unref_free (*chain);
+ return STATUS_FAILURE;
+ }
+ else if (!certificate)
+ {
+ return STATUS_INCOMPLETE;
+ }
+ }
+
+ /* Now look up whether this certificate is an anchor */
+ if (g_tls_database_gnutls_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE,
+ identity, cancellable, error))
+ {
+ *anchors = g_list_append (NULL, certificate);
+ return STATUS_ANCHORED;
+ }
+ else if (error)
+ {
+ g_object_unref (certificate);
+ *chain = list_unref_free (*chain);
+ return STATUS_FAILURE;
+ }
+ else
+ {
+ *chain = g_list_append (*chain, certificate);
+ }
+ }
+
+ g_assert_not_reached ();
+}
+
+static GTlsCertificateFlags
+double_check_before_after_dates (GList *chain)
+{
+ GTlsCertificateFlags gtls_flags = 0;
+ gnutls_x509_crt_t cert;
+ time_t t, now;
+ GList *l;
+
+ now = time (NULL);
+ for (l = chain; l; l = g_list_next (l))
+ {
+ cert = g_tls_certificate_gnutls_get_cert (l->data);
+ t = gnutls_x509_crt_get_activation_time (cert);
+ if (t == (time_t) -1 || t > now)
+ gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
+
+ t = gnutls_x509_crt_get_expiration_time (cert);
+ if (t == (time_t) -1 || t < now)
+ gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
+ }
+
+ return gtls_flags;
+}
+
+static void
+convert_certificate_list_to_gnutls (GList *chain,
+ gnutls_x509_crt_t **gnutls_chain,
+ guint *n_chain)
+{
+ guint i;
+
+ g_assert (gnutls_chain);
+ g_assert (n_chain);
+
+ *n_chain = g_list_length (chain);
+ if (*n_chain)
+ *gnutls_chain = g_new0 (gnutls_x509_crt_t, *n_chain);
+ else
+ *gnutls_chain = NULL;
+
+ for (i = 0; i < *n_chain; ++i, chain = g_list_next (chain))
+ (*gnutls_chain)[i] = g_tls_certificate_gnutls_get_cert (chain->data);
+}
+
+static GList*
+g_tls_database_gnutls_build_chain_and_verify (GTlsDatabase *database,
+ GList *input,
+ GSocketConnectable *identity,
+ gint flags,
+ GCancellable *cancellable,
+ GTlsCertificateFlags *verify_result,
+ GError **error)
+{
+ GTlsDatabaseGnutls *self;
+ gnutls_x509_crt_t *gnutls_chain;
+ gnutls_x509_crt_t *gnutls_anchors;
+ GError *err = NULL;
+ GList *chain, *anchors;
+ guint gnutls_result;
+ guint n_anchors, n_chain;
+ gint status, gerr;
+
+ g_return_val_if_fail (G_IS_TLS_DATABASE_GNUTLS (database), NULL);
+ g_return_val_if_fail (input, NULL);
+ g_return_val_if_fail (G_IS_SOCKET_CONNECTABLE (identity), NULL);
+ g_return_val_if_fail (verify_result, NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
+
+ self = G_TLS_DATABASE_GNUTLS (database);
+ chain = anchors = NULL;
+
+ status = build_certificate_chain (self, input, identity, flags, cancellable,
+ &chain, &anchors, &err);
+ if (err)
+ {
+ g_propagate_error (error, err);
+ return NULL;
+ }
+
+ /* A pinned certificate is verified on its own */
+ if (status == STATUS_PINNED)
+ {
+ list_unref_free (anchors);
+ *verify_result = 0;
+ return chain;
+ }
+
+ /* The certificate chain for validation by gnutls */
+ convert_certificate_list_to_gnutls (chain, &gnutls_chain, &n_chain);
+ convert_certificate_list_to_gnutls (anchors, &gnutls_anchors, &n_anchors);
+
+ gerr = gnutls_x509_crt_list_verify (gnutls_chain, n_chain,
+ gnutls_anchors, n_anchors,
+ NULL, 0, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+ &gnutls_result);
+
+ list_unref_free (anchors);
+ g_free (gnutls_chain);
+ g_free (gnutls_anchors);
+
+ if (gerr != 0)
+ {
+ g_free (gnutls_chain);
+ g_free (gnutls_anchors);
+ *verify_result = G_TLS_CERTIFICATE_GENERIC_ERROR;
+ return chain;
+ }
+
+ *verify_result = g_tls_certificate_gnutls_convert_flags (gnutls_result);
+
+ /*
+ * We have to check these ourselves since gnutls_x509_crt_list_verify
+ * won't bother if it gets an UNKNOWN_CA.
+ */
+ *verify_result |= double_check_before_after_dates (chain);
+
+ if (identity)
+ *verify_result |= g_tls_certificate_gnutls_verify_identity (chain->data, identity);
+
+ return chain;
+}
+
+static void
+g_tls_database_gnutls_class_init (GTlsDatabaseGnutlsClass *klass)
+{
+ GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+ database_class->build_chain_and_verify = g_tls_database_gnutls_build_chain_and_verify;
+}
+
+gboolean
+g_tls_database_gnutls_lookup_assertion (GTlsDatabaseGnutls *self,
+ GTlsCertificateGnutls *certificate,
+ GTlsDatabaseGnutlsAssertion assertion,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_TLS_DATABASE (self), FALSE);
+ g_return_val_if_fail (G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->lookup_assertion, FALSE);
+ return G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->lookup_assertion (self,
+ certificate,
+ assertion,
+ connectable,
+ cancellable,
+ error);
+}
diff --git a/tls/gnutls/gtlsdatabase-gnutls.h b/tls/gnutls/gtlsdatabase-gnutls.h
new file mode 100644
index 0000000..840265b
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls.h
@@ -0,0 +1,69 @@
+/* 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_DATABASE_GNUTLS_H__
+#define __G_TLS_DATABASE_GNUTLS_H__
+
+#include <gio/gio.h>
+
+#include "gtlscertificate-gnutls.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE = 1,
+ G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE = 2,
+} GTlsDatabaseGnutlsAssertion;
+
+#define G_TYPE_TLS_DATABASE_GNUTLS (g_tls_database_gnutls_get_type ())
+#define G_TLS_DATABASE_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_DATABASE_GNUTLS, GTlsDatabaseGnutls))
+#define G_TLS_DATABASE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_DATABASE_GNUTLS, GTlsDatabaseGnutlsClass))
+#define G_IS_TLS_DATABASE_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_DATABASE_GNUTLS))
+#define G_IS_TLS_DATABASE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_DATABASE_GNUTLS))
+#define G_TLS_DATABASE_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_DATABASE_GNUTLS, GTlsDatabaseGnutlsClass))
+
+typedef struct _GTlsDatabaseGnutlsPrivate GTlsDatabaseGnutlsPrivate;
+typedef struct _GTlsDatabaseGnutlsClass GTlsDatabaseGnutlsClass;
+typedef struct _GTlsDatabaseGnutls GTlsDatabaseGnutls;
+
+struct _GTlsDatabaseGnutlsClass
+{
+ GTlsDatabaseClass parent_class;
+
+ gboolean (*lookup_assertion) (GTlsDatabaseGnutls *self,
+ GTlsCertificateGnutls *certificate,
+ GTlsDatabaseGnutlsAssertion assertion,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GError **error);
+};
+
+struct _GTlsDatabaseGnutls
+{
+ GTlsDatabase parent_instance;
+ GTlsDatabaseGnutlsPrivate *priv;
+};
+
+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,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_DATABASE_GNUTLS_H___ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]