[glib-networking] gnutls: move code from GTlsDatabaseGnutls into subclasses
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking] gnutls: move code from GTlsDatabaseGnutls into subclasses
- Date: Mon, 8 Feb 2016 13:15:16 +0000 (UTC)
commit f90c5a49fdef8d35e57d3482f5d4888b47bd034a
Author: Dan Winship <danw gnome org>
Date: Tue Nov 24 12:38:14 2015 -0500
gnutls: move code from GTlsDatabaseGnutls into subclasses
tls/gnutls/gtlscertificate-gnutls-pkcs11.h | 2 +
tls/gnutls/gtlsdatabase-gnutls-pkcs11.c | 284 +++++++++++++++++++++++++-
tls/gnutls/gtlsdatabase-gnutls.c | 313 ----------------------------
tls/gnutls/gtlsdatabase-gnutls.h | 17 --
tls/gnutls/gtlsfiledatabase-gnutls.c | 288 +++++++++++++++++++++++++-
5 files changed, 565 insertions(+), 339 deletions(-)
---
diff --git a/tls/gnutls/gtlscertificate-gnutls-pkcs11.h b/tls/gnutls/gtlscertificate-gnutls-pkcs11.h
index 3ea8998..aaac044 100644
--- a/tls/gnutls/gtlscertificate-gnutls-pkcs11.h
+++ b/tls/gnutls/gtlscertificate-gnutls-pkcs11.h
@@ -21,6 +21,8 @@
#include <gio/gio.h>
#include <gnutls/gnutls.h>
+#include "gtlscertificate-gnutls.h"
+
G_BEGIN_DECLS
#define G_TYPE_TLS_CERTIFICATE_GNUTLS_PKCS11 (g_tls_certificate_gnutls_pkcs11_get_type ())
diff --git a/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c
index 74fda3d..919eccd 100644
--- a/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c
+++ b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c
@@ -386,7 +386,7 @@ enumerate_assertion_exists_in_database (GTlsDatabaseGnutlsPkcs11 *self,
}
static gboolean
-g_tls_database_gnutls_pkcs11_lookup_assertion (GTlsDatabaseGnutls *database,
+g_tls_database_gnutls_pkcs11_lookup_assertion (GTlsDatabaseGnutlsPkcs11 *self,
GTlsCertificateGnutls *certificate,
GTlsDatabaseGnutlsAssertion assertion,
const gchar *purpose,
@@ -394,7 +394,6 @@ g_tls_database_gnutls_pkcs11_lookup_assertion (GTlsDatabaseGnutls *dat
GCancellable *cancellable,
GError **error)
{
- GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
GByteArray *der = NULL;
gboolean found, ready;
GPkcs11Array *match;
@@ -790,12 +789,289 @@ g_tls_database_gnutls_pkcs11_lookup_certificate_for_handle (GTlsDatabase
return result;
}
+#define BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT 10
+
+enum {
+ STATUS_FAILURE,
+ STATUS_INCOMPLETE,
+ STATUS_SELFSIGNED,
+ STATUS_ANCHORED,
+ STATUS_RECURSION_LIMIT_REACHED
+};
+
+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 (GTlsDatabaseGnutlsPkcs11 *self,
+ GTlsCertificateGnutls *certificate,
+ GTlsCertificateGnutls *previous,
+ gboolean certificate_is_from_db,
+ guint recursion_depth,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GCancellable *cancellable,
+ GTlsCertificateGnutls **anchor,
+ GError **error)
+{
+ GTlsCertificate *issuer;
+ gint status;
+
+ if (recursion_depth++ > BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT)
+ return STATUS_RECURSION_LIMIT_REACHED;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return STATUS_FAILURE;
+
+ /* Look up whether this certificate is an anchor */
+ if (g_tls_database_gnutls_pkcs11_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE,
+ purpose, identity, cancellable, error))
+ {
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ *anchor = certificate;
+ return STATUS_ANCHORED;
+ }
+ else if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+
+ /* Is it self-signed? */
+ if (is_self_signed (certificate))
+ {
+ /*
+ * Since at this point we would fail with 'self-signed', can we replace
+ * this certificate with one from the database and do better?
+ */
+ if (previous && !certificate_is_from_db)
+ {
+ issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+ G_TLS_CERTIFICATE (previous),
+ interaction,
+ G_TLS_DATABASE_LOOKUP_NONE,
+ cancellable, error);
+ if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+ else if (issuer)
+ {
+ /* Replaced with certificate in the db, restart step again with this certificate */
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+ g_tls_certificate_gnutls_set_issuer (previous, certificate);
+ g_object_unref (issuer);
+
+ return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
+ purpose, identity, interaction, cancellable, anchor, error);
+ }
+ }
+
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ return STATUS_SELFSIGNED;
+ }
+
+ previous = certificate;
+
+ /* Bring over the next certificate in the chain */
+ issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (certificate));
+ if (issuer)
+ {
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+
+ status = build_certificate_chain (self, certificate, previous, FALSE, recursion_depth,
+ purpose, identity, interaction, cancellable, anchor, error);
+ if (status != STATUS_INCOMPLETE)
+ {
+ return status;
+ }
+ }
+
+ /* Search for the next certificate in chain */
+ issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+ G_TLS_CERTIFICATE (certificate),
+ interaction,
+ G_TLS_DATABASE_LOOKUP_NONE,
+ cancellable, error);
+ if (*error)
+ return STATUS_FAILURE;
+
+ if (!issuer)
+ return STATUS_INCOMPLETE;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ g_tls_certificate_gnutls_set_issuer (certificate, G_TLS_CERTIFICATE_GNUTLS (issuer));
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+ g_object_unref (issuer);
+
+ return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
+ purpose, identity, interaction, cancellable, anchor, error);
+}
+
+static GTlsCertificateFlags
+double_check_before_after_dates (GTlsCertificateGnutls *chain)
+{
+ GTlsCertificateFlags gtls_flags = 0;
+ gnutls_x509_crt_t cert;
+ time_t t, now;
+
+ now = time (NULL);
+ while (chain)
+ {
+ cert = g_tls_certificate_gnutls_get_cert (chain);
+ 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;
+
+ chain = G_TLS_CERTIFICATE_GNUTLS (g_tls_certificate_get_issuer
+ (G_TLS_CERTIFICATE (chain)));
+ }
+
+ return gtls_flags;
+}
+
+static void
+convert_certificate_chain_to_gnutls (GTlsCertificateGnutls *chain,
+ gnutls_x509_crt_t **gnutls_chain,
+ guint *gnutls_chain_length)
+{
+ GTlsCertificate *cert;
+ guint i;
+
+ g_assert (gnutls_chain);
+ g_assert (gnutls_chain_length);
+
+ for (*gnutls_chain_length = 0, cert = G_TLS_CERTIFICATE (chain);
+ cert; cert = g_tls_certificate_get_issuer (cert))
+ ++(*gnutls_chain_length);
+
+ *gnutls_chain = g_new0 (gnutls_x509_crt_t, *gnutls_chain_length);
+
+ for (i = 0, cert = G_TLS_CERTIFICATE (chain);
+ cert; cert = g_tls_certificate_get_issuer (cert), ++i)
+ (*gnutls_chain)[i] = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (cert));
+
+ g_assert (i == *gnutls_chain_length);
+}
+
+static GTlsCertificateFlags
+g_tls_database_gnutls_pkcs11_verify_chain (GTlsDatabase *database,
+ GTlsCertificate *chain,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GTlsDatabaseVerifyFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsDatabaseGnutlsPkcs11 *self;
+ GTlsCertificateFlags result;
+ GTlsCertificateGnutls *certificate;
+ GError *err = NULL;
+ GTlsCertificateGnutls *anchor;
+ guint gnutls_result;
+ gnutls_x509_crt_t *certs, *anchors;
+ guint certs_length, anchors_length;
+ gint status, gerr;
+ guint recursion_depth = 0;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (chain),
+ G_TLS_CERTIFICATE_GENERIC_ERROR);
+ g_assert (purpose);
+
+ self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (chain);
+
+ /* First check for pinned certificate */
+ if (g_tls_database_gnutls_pkcs11_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE,
+ purpose, identity, cancellable, &err))
+ {
+ /*
+ * A pinned certificate is verified on its own, without any further
+ * verification.
+ */
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ return 0;
+ }
+
+ if (err)
+ {
+ g_propagate_error (error, err);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ anchor = NULL;
+ status = build_certificate_chain (self, certificate, NULL, FALSE, recursion_depth,
+ purpose, identity, interaction, cancellable, &anchor, &err);
+ if (status == STATUS_FAILURE)
+ {
+ g_propagate_error (error, err);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain),
+ &certs, &certs_length);
+
+ if (anchor)
+ {
+ g_assert (g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (anchor)) == NULL);
+ convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (anchor),
+ &anchors, &anchors_length);
+ }
+ else
+ {
+ anchors = NULL;
+ anchors_length = 0;
+ }
+
+ gerr = gnutls_x509_crt_list_verify (certs, certs_length,
+ anchors, anchors_length,
+ NULL, 0, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+ &gnutls_result);
+
+ g_free (certs);
+ g_free (anchors);
+
+ if (gerr != 0)
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ else if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ 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.
+ */
+ result |= double_check_before_after_dates (G_TLS_CERTIFICATE_GNUTLS (chain));
+
+ if (identity)
+ result |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (chain),
+ identity);
+
+ return result;
+}
+
static void
g_tls_database_gnutls_pkcs11_class_init (GTlsDatabaseGnutlsPkcs11Class *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 (GTlsDatabaseGnutlsPkcs11Private));
@@ -805,7 +1081,7 @@ g_tls_database_gnutls_pkcs11_class_init (GTlsDatabaseGnutlsPkcs11Class *klass)
database_class->lookup_certificate_issuer = g_tls_database_gnutls_pkcs11_lookup_certificate_issuer;
database_class->lookup_certificates_issued_by = g_tls_database_gnutls_pkcs11_lookup_certificates_issued_by;
database_class->lookup_certificate_for_handle = g_tls_database_gnutls_pkcs11_lookup_certificate_for_handle;
- gnutls_class->lookup_assertion = g_tls_database_gnutls_pkcs11_lookup_assertion;
+ database_class->verify_chain = g_tls_database_gnutls_pkcs11_verify_chain;
}
static gboolean
diff --git a/tls/gnutls/gtlsdatabase-gnutls.c b/tls/gnutls/gtlsdatabase-gnutls.c
index 1c15adc..7d25f59 100644
--- a/tls/gnutls/gtlsdatabase-gnutls.c
+++ b/tls/gnutls/gtlsdatabase-gnutls.c
@@ -24,329 +24,16 @@
#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);
-#define BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT 10
-
-enum {
- STATUS_FAILURE,
- STATUS_INCOMPLETE,
- STATUS_SELFSIGNED,
- STATUS_ANCHORED,
- STATUS_RECURSION_LIMIT_REACHED
-};
-
static void
g_tls_database_gnutls_init (GTlsDatabaseGnutls *self)
{
-
-}
-
-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,
- GTlsCertificateGnutls *certificate,
- GTlsCertificateGnutls *previous,
- gboolean certificate_is_from_db,
- guint recursion_depth,
- const gchar *purpose,
- GSocketConnectable *identity,
- GTlsInteraction *interaction,
- GCancellable *cancellable,
- GTlsCertificateGnutls **anchor,
- GError **error)
-{
- GTlsCertificate *issuer;
- gint status;
-
- if (recursion_depth++ > BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT)
- return STATUS_RECURSION_LIMIT_REACHED;
-
- if (g_cancellable_set_error_if_cancelled (cancellable, error))
- return STATUS_FAILURE;
-
- /* Look up whether this certificate is an anchor */
- if (g_tls_database_gnutls_lookup_assertion (self, certificate,
- G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE,
- purpose, identity, cancellable, error))
- {
- g_tls_certificate_gnutls_set_issuer (certificate, NULL);
- *anchor = certificate;
- return STATUS_ANCHORED;
- }
- else if (*error)
- {
- return STATUS_FAILURE;
- }
-
- /* Is it self-signed? */
- if (is_self_signed (certificate))
- {
- /*
- * Since at this point we would fail with 'self-signed', can we replace
- * this certificate with one from the database and do better?
- */
- if (previous && !certificate_is_from_db)
- {
- issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
- G_TLS_CERTIFICATE (previous),
- interaction,
- G_TLS_DATABASE_LOOKUP_NONE,
- cancellable, error);
- if (*error)
- {
- return STATUS_FAILURE;
- }
- else if (issuer)
- {
- /* Replaced with certificate in the db, restart step again with this certificate */
- g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
- certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
- g_tls_certificate_gnutls_set_issuer (previous, certificate);
- g_object_unref (issuer);
-
- return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
- purpose, identity, interaction, cancellable, anchor, error);
- }
- }
-
- g_tls_certificate_gnutls_set_issuer (certificate, NULL);
- return STATUS_SELFSIGNED;
- }
-
- previous = certificate;
-
- /* Bring over the next certificate in the chain */
- issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (certificate));
- if (issuer)
- {
- g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
- certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
-
- status = build_certificate_chain (self, certificate, previous, FALSE, recursion_depth,
- purpose, identity, interaction, cancellable, anchor, error);
- if (status != STATUS_INCOMPLETE)
- {
- return status;
- }
- }
-
- /* Search for the next certificate in chain */
- issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
- G_TLS_CERTIFICATE (certificate),
- interaction,
- G_TLS_DATABASE_LOOKUP_NONE,
- cancellable, error);
- if (*error)
- return STATUS_FAILURE;
-
- if (!issuer)
- return STATUS_INCOMPLETE;
-
- g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
- g_tls_certificate_gnutls_set_issuer (certificate, G_TLS_CERTIFICATE_GNUTLS (issuer));
- certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
- g_object_unref (issuer);
-
- return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
- purpose, identity, interaction, cancellable, anchor, error);
-}
-
-static GTlsCertificateFlags
-double_check_before_after_dates (GTlsCertificateGnutls *chain)
-{
- GTlsCertificateFlags gtls_flags = 0;
- gnutls_x509_crt_t cert;
- time_t t, now;
-
- now = time (NULL);
- while (chain)
- {
- cert = g_tls_certificate_gnutls_get_cert (chain);
- 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;
-
- chain = G_TLS_CERTIFICATE_GNUTLS (g_tls_certificate_get_issuer
- (G_TLS_CERTIFICATE (chain)));
- }
-
- return gtls_flags;
-}
-
-static void
-convert_certificate_chain_to_gnutls (GTlsCertificateGnutls *chain,
- gnutls_x509_crt_t **gnutls_chain,
- guint *gnutls_chain_length)
-{
- GTlsCertificate *cert;
- guint i;
-
- g_assert (gnutls_chain);
- g_assert (gnutls_chain_length);
-
- for (*gnutls_chain_length = 0, cert = G_TLS_CERTIFICATE (chain);
- cert; cert = g_tls_certificate_get_issuer (cert))
- ++(*gnutls_chain_length);
-
- *gnutls_chain = g_new0 (gnutls_x509_crt_t, *gnutls_chain_length);
-
- for (i = 0, cert = G_TLS_CERTIFICATE (chain);
- cert; cert = g_tls_certificate_get_issuer (cert), ++i)
- (*gnutls_chain)[i] = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (cert));
-
- g_assert (i == *gnutls_chain_length);
-}
-
-static GTlsCertificateFlags
-g_tls_database_gnutls_verify_chain (GTlsDatabase *database,
- GTlsCertificate *chain,
- const gchar *purpose,
- GSocketConnectable *identity,
- GTlsInteraction *interaction,
- GTlsDatabaseVerifyFlags flags,
- GCancellable *cancellable,
- GError **error)
-{
- GTlsDatabaseGnutls *self;
- GTlsCertificateFlags result;
- GTlsCertificateGnutls *certificate;
- GError *err = NULL;
- GTlsCertificateGnutls *anchor;
- guint gnutls_result;
- gnutls_x509_crt_t *certs, *anchors;
- guint certs_length, anchors_length;
- gint status, gerr;
- guint recursion_depth = 0;
-
- g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (chain),
- G_TLS_CERTIFICATE_GENERIC_ERROR);
- g_assert (purpose);
-
- self = G_TLS_DATABASE_GNUTLS (database);
- certificate = G_TLS_CERTIFICATE_GNUTLS (chain);
-
- /* First check for pinned certificate */
- if (g_tls_database_gnutls_lookup_assertion (self, certificate,
- G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE,
- purpose, identity, cancellable, &err))
- {
- /*
- * A pinned certificate is verified on its own, without any further
- * verification.
- */
- g_tls_certificate_gnutls_set_issuer (certificate, NULL);
- return 0;
- }
-
- if (err)
- {
- g_propagate_error (error, err);
- return G_TLS_CERTIFICATE_GENERIC_ERROR;
- }
-
- anchor = NULL;
- status = build_certificate_chain (self, certificate, NULL, FALSE, recursion_depth,
- purpose, identity, interaction, cancellable, &anchor, &err);
- if (status == STATUS_FAILURE)
- {
- g_propagate_error (error, err);
- return G_TLS_CERTIFICATE_GENERIC_ERROR;
- }
-
- if (g_cancellable_set_error_if_cancelled (cancellable, error))
- return G_TLS_CERTIFICATE_GENERIC_ERROR;
-
- convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain),
- &certs, &certs_length);
-
- if (anchor)
- {
- g_assert (g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (anchor)) == NULL);
- convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (anchor),
- &anchors, &anchors_length);
- }
- else
- {
- anchors = NULL;
- anchors_length = 0;
- }
-
- gerr = gnutls_x509_crt_list_verify (certs, certs_length,
- anchors, anchors_length,
- NULL, 0, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
- &gnutls_result);
-
- g_free (certs);
- g_free (anchors);
-
- if (gerr != 0)
- return G_TLS_CERTIFICATE_GENERIC_ERROR;
- else if (g_cancellable_set_error_if_cancelled (cancellable, error))
- return G_TLS_CERTIFICATE_GENERIC_ERROR;
-
- 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.
- */
- result |= double_check_before_after_dates (G_TLS_CERTIFICATE_GNUTLS (chain));
-
- if (identity)
- result |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (chain),
- identity);
-
- return result;
}
static void
g_tls_database_gnutls_class_init (GTlsDatabaseGnutlsClass *klass)
{
- GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
- database_class->verify_chain = g_tls_database_gnutls_verify_chain;
-}
-
-gboolean
-g_tls_database_gnutls_lookup_assertion (GTlsDatabaseGnutls *self,
- GTlsCertificateGnutls *certificate,
- GTlsDatabaseGnutlsAssertion assertion,
- const gchar *purpose,
- GSocketConnectable *identity,
- GCancellable *cancellable,
- GError **error)
-{
- g_return_val_if_fail (G_IS_TLS_DATABASE_GNUTLS (self), FALSE);
- g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), FALSE);
- g_return_val_if_fail (purpose, FALSE);
- g_return_val_if_fail (!identity || G_IS_SOCKET_CONNECTABLE (identity), FALSE);
- g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
- g_return_val_if_fail (!error || !*error, 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,
- purpose,
- identity,
- cancellable,
- error);
}
diff --git a/tls/gnutls/gtlsdatabase-gnutls.h b/tls/gnutls/gtlsdatabase-gnutls.h
index 4aa6d13..ce668ff 100644
--- a/tls/gnutls/gtlsdatabase-gnutls.h
+++ b/tls/gnutls/gtlsdatabase-gnutls.h
@@ -43,32 +43,15 @@ typedef struct _GTlsDatabaseGnutls GTlsDatabaseGnutls;
struct _GTlsDatabaseGnutlsClass
{
GTlsDatabaseClass parent_class;
-
- gboolean (*lookup_assertion) (GTlsDatabaseGnutls *self,
- GTlsCertificateGnutls *certificate,
- GTlsDatabaseGnutlsAssertion assertion,
- const gchar *purpose,
- GSocketConnectable *identity,
- 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,
- const gchar *purpose,
- GSocketConnectable *identity,
- GCancellable *cancellable,
- GError **error);
-
G_END_DECLS
#endif /* __G_TLS_DATABASE_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsfiledatabase-gnutls.c b/tls/gnutls/gtlsfiledatabase-gnutls.c
index 24d54a9..c7cf147 100644
--- a/tls/gnutls/gtlsfiledatabase-gnutls.c
+++ b/tls/gnutls/gtlsfiledatabase-gnutls.c
@@ -30,6 +30,8 @@
#include <glib/gi18n-lib.h>
#include <gnutls/x509.h>
+#include "gtlscertificate-gnutls.h"
+
static void g_tls_file_database_gnutls_file_database_interface_init (GTlsFileDatabaseInterface *iface);
static void g_tls_file_database_gnutls_initable_interface_init (GInitableIface *iface);
@@ -39,7 +41,7 @@ G_DEFINE_TYPE_WITH_CODE (GTlsFileDatabaseGnutls, g_tls_file_database_gnutls, G_T
g_tls_file_database_gnutls_file_database_interface_init);
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
g_tls_file_database_gnutls_initable_interface_init);
-);
+ );
enum
{
@@ -411,7 +413,7 @@ g_tls_file_database_gnutls_lookup_certificate_for_handle (GTlsDatabase
}
static gboolean
-g_tls_file_database_gnutls_lookup_assertion (GTlsDatabaseGnutls *database,
+g_tls_file_database_gnutls_lookup_assertion (GTlsFileDatabaseGnutls *self,
GTlsCertificateGnutls *certificate,
GTlsDatabaseGnutlsAssertion assertion,
const gchar *purpose,
@@ -419,7 +421,6 @@ g_tls_file_database_gnutls_lookup_assertion (GTlsDatabaseGnutls *datab
GCancellable *cancellable,
GError **error)
{
- GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
GBytes *der = NULL;
gboolean contains;
@@ -559,12 +560,289 @@ g_tls_file_database_gnutls_lookup_certificates_issued_by (GTlsDatabase
return issued;
}
+#define BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT 10
+
+enum {
+ STATUS_FAILURE,
+ STATUS_INCOMPLETE,
+ STATUS_SELFSIGNED,
+ STATUS_ANCHORED,
+ STATUS_RECURSION_LIMIT_REACHED
+};
+
+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 (GTlsFileDatabaseGnutls *self,
+ GTlsCertificateGnutls *certificate,
+ GTlsCertificateGnutls *previous,
+ gboolean certificate_is_from_db,
+ guint recursion_depth,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GCancellable *cancellable,
+ GTlsCertificateGnutls **anchor,
+ GError **error)
+{
+ GTlsCertificate *issuer;
+ gint status;
+
+ if (recursion_depth++ > BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT)
+ return STATUS_RECURSION_LIMIT_REACHED;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return STATUS_FAILURE;
+
+ /* Look up whether this certificate is an anchor */
+ if (g_tls_file_database_gnutls_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE,
+ purpose, identity, cancellable, error))
+ {
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ *anchor = certificate;
+ return STATUS_ANCHORED;
+ }
+ else if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+
+ /* Is it self-signed? */
+ if (is_self_signed (certificate))
+ {
+ /*
+ * Since at this point we would fail with 'self-signed', can we replace
+ * this certificate with one from the database and do better?
+ */
+ if (previous && !certificate_is_from_db)
+ {
+ issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+ G_TLS_CERTIFICATE (previous),
+ interaction,
+ G_TLS_DATABASE_LOOKUP_NONE,
+ cancellable, error);
+ if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+ else if (issuer)
+ {
+ /* Replaced with certificate in the db, restart step again with this certificate */
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+ g_tls_certificate_gnutls_set_issuer (previous, certificate);
+ g_object_unref (issuer);
+
+ return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
+ purpose, identity, interaction, cancellable, anchor, error);
+ }
+ }
+
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ return STATUS_SELFSIGNED;
+ }
+
+ previous = certificate;
+
+ /* Bring over the next certificate in the chain */
+ issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (certificate));
+ if (issuer)
+ {
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+
+ status = build_certificate_chain (self, certificate, previous, FALSE, recursion_depth,
+ purpose, identity, interaction, cancellable, anchor, error);
+ if (status != STATUS_INCOMPLETE)
+ {
+ return status;
+ }
+ }
+
+ /* Search for the next certificate in chain */
+ issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+ G_TLS_CERTIFICATE (certificate),
+ interaction,
+ G_TLS_DATABASE_LOOKUP_NONE,
+ cancellable, error);
+ if (*error)
+ return STATUS_FAILURE;
+
+ if (!issuer)
+ return STATUS_INCOMPLETE;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ g_tls_certificate_gnutls_set_issuer (certificate, G_TLS_CERTIFICATE_GNUTLS (issuer));
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+ g_object_unref (issuer);
+
+ return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
+ purpose, identity, interaction, cancellable, anchor, error);
+}
+
+static GTlsCertificateFlags
+double_check_before_after_dates (GTlsCertificateGnutls *chain)
+{
+ GTlsCertificateFlags gtls_flags = 0;
+ gnutls_x509_crt_t cert;
+ time_t t, now;
+
+ now = time (NULL);
+ while (chain)
+ {
+ cert = g_tls_certificate_gnutls_get_cert (chain);
+ 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;
+
+ chain = G_TLS_CERTIFICATE_GNUTLS (g_tls_certificate_get_issuer
+ (G_TLS_CERTIFICATE (chain)));
+ }
+
+ return gtls_flags;
+}
+
+static void
+convert_certificate_chain_to_gnutls (GTlsCertificateGnutls *chain,
+ gnutls_x509_crt_t **gnutls_chain,
+ guint *gnutls_chain_length)
+{
+ GTlsCertificate *cert;
+ guint i;
+
+ g_assert (gnutls_chain);
+ g_assert (gnutls_chain_length);
+
+ for (*gnutls_chain_length = 0, cert = G_TLS_CERTIFICATE (chain);
+ cert; cert = g_tls_certificate_get_issuer (cert))
+ ++(*gnutls_chain_length);
+
+ *gnutls_chain = g_new0 (gnutls_x509_crt_t, *gnutls_chain_length);
+
+ for (i = 0, cert = G_TLS_CERTIFICATE (chain);
+ cert; cert = g_tls_certificate_get_issuer (cert), ++i)
+ (*gnutls_chain)[i] = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (cert));
+
+ g_assert (i == *gnutls_chain_length);
+}
+
+static GTlsCertificateFlags
+g_tls_file_database_gnutls_verify_chain (GTlsDatabase *database,
+ GTlsCertificate *chain,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GTlsDatabaseVerifyFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseGnutls *self;
+ GTlsCertificateFlags result;
+ GTlsCertificateGnutls *certificate;
+ GError *err = NULL;
+ GTlsCertificateGnutls *anchor;
+ guint gnutls_result;
+ gnutls_x509_crt_t *certs, *anchors;
+ guint certs_length, anchors_length;
+ gint status, gerr;
+ guint recursion_depth = 0;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (chain),
+ G_TLS_CERTIFICATE_GENERIC_ERROR);
+ g_assert (purpose);
+
+ self = G_TLS_FILE_DATABASE_GNUTLS (database);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (chain);
+
+ /* First check for pinned certificate */
+ if (g_tls_file_database_gnutls_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE,
+ purpose, identity, cancellable, &err))
+ {
+ /*
+ * A pinned certificate is verified on its own, without any further
+ * verification.
+ */
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ return 0;
+ }
+
+ if (err)
+ {
+ g_propagate_error (error, err);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ anchor = NULL;
+ status = build_certificate_chain (self, certificate, NULL, FALSE, recursion_depth,
+ purpose, identity, interaction, cancellable, &anchor, &err);
+ if (status == STATUS_FAILURE)
+ {
+ g_propagate_error (error, err);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain),
+ &certs, &certs_length);
+
+ if (anchor)
+ {
+ g_assert (g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (anchor)) == NULL);
+ convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (anchor),
+ &anchors, &anchors_length);
+ }
+ else
+ {
+ anchors = NULL;
+ anchors_length = 0;
+ }
+
+ gerr = gnutls_x509_crt_list_verify (certs, certs_length,
+ anchors, anchors_length,
+ NULL, 0, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+ &gnutls_result);
+
+ g_free (certs);
+ g_free (anchors);
+
+ if (gerr != 0)
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ else if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ 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.
+ */
+ result |= double_check_before_after_dates (G_TLS_CERTIFICATE_GNUTLS (chain));
+
+ if (identity)
+ result |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (chain),
+ identity);
+
+ return result;
+}
+
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));
@@ -576,7 +854,7 @@ g_tls_file_database_gnutls_class_init (GTlsFileDatabaseGnutlsClass *klass)
database_class->lookup_certificate_for_handle = g_tls_file_database_gnutls_lookup_certificate_for_handle;
database_class->lookup_certificate_issuer = g_tls_file_database_gnutls_lookup_certificate_issuer;
database_class->lookup_certificates_issued_by = g_tls_file_database_gnutls_lookup_certificates_issued_by;
- gnutls_class->lookup_assertion = g_tls_file_database_gnutls_lookup_assertion;
+ database_class->verify_chain = g_tls_file_database_gnutls_verify_chain;
g_object_class_override_property (gobject_class, PROP_ANCHORS, "anchors");
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]