[glib-networking] gnutls: use gnutls's verification code for GTlsFileDatabaseGnutls



commit a2cc9b8e08063745d9ba1091e030fbe43fc5a055
Author: Dan Winship <danw gnome org>
Date:   Tue Nov 24 12:40:02 2015 -0500

    gnutls: use gnutls's verification code for GTlsFileDatabaseGnutls
    
    Should mostly fix https://bugzilla.gnome.org/show_bug.cgi?id=753260
    and some other bugs.
    
    (Redone to avoid post-3.0.0 API.)

 tls/gnutls/gtlscertificate-gnutls.c  |    3 +-
 tls/gnutls/gtlsfiledatabase-gnutls.c |  344 ++++++----------------------------
 2 files changed, 59 insertions(+), 288 deletions(-)
---
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
index 7aaa6f6..4ff996c 100644
--- a/tls/gnutls/gtlscertificate-gnutls.c
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -511,7 +511,8 @@ static const struct {
   { GNUTLS_CERT_NOT_ACTIVATED, G_TLS_CERTIFICATE_NOT_ACTIVATED },
   { GNUTLS_CERT_EXPIRED, G_TLS_CERTIFICATE_EXPIRED },
   { GNUTLS_CERT_REVOKED, G_TLS_CERTIFICATE_REVOKED },
-  { GNUTLS_CERT_INSECURE_ALGORITHM, G_TLS_CERTIFICATE_INSECURE }
+  { GNUTLS_CERT_INSECURE_ALGORITHM, G_TLS_CERTIFICATE_INSECURE },
+  { GNUTLS_CERT_UNEXPECTED_OWNER, G_TLS_CERTIFICATE_BAD_IDENTITY }
 };
 static const int flags_map_size = G_N_ELEMENTS (flags_map);
 
diff --git a/tls/gnutls/gtlsfiledatabase-gnutls.c b/tls/gnutls/gtlsfiledatabase-gnutls.c
index c7cf147..f4d252f 100644
--- a/tls/gnutls/gtlsfiledatabase-gnutls.c
+++ b/tls/gnutls/gtlsfiledatabase-gnutls.c
@@ -53,6 +53,7 @@ struct _GTlsFileDatabaseGnutlsPrivate
 {
   /* read-only after construct */
   gchar *anchor_filename;
+  gnutls_x509_trust_list_t trust_list;
 
   /* protected by mutex */
   GMutex mutex;
@@ -258,25 +259,15 @@ g_tls_file_database_gnutls_finalize (GObject *object)
 {
   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
 
-  if (self->priv->subjects)
-    g_hash_table_destroy (self->priv->subjects);
-  self->priv->subjects = NULL;
-
-  if (self->priv->issuers)
-    g_hash_table_destroy (self->priv->issuers);
-  self->priv->issuers = NULL;
-
-  if (self->priv->complete)
-    g_hash_table_destroy (self->priv->complete);
-  self->priv->complete = NULL;
-
-  if (self->priv->handles)
-    g_hash_table_destroy (self->priv->handles);
-  self->priv->handles = NULL;
-
-  g_free (self->priv->anchor_filename);
-  self->priv->anchor_filename = NULL;
-
+  g_clear_pointer (&self->priv->subjects, g_hash_table_destroy);
+  g_clear_pointer (&self->priv->issuers, g_hash_table_destroy);
+  g_clear_pointer (&self->priv->complete, g_hash_table_destroy);
+  g_clear_pointer (&self->priv->handles, g_hash_table_destroy);
+  if (self->priv->anchor_filename)
+    {
+      g_free (self->priv->anchor_filename);
+      gnutls_x509_trust_list_deinit (self->priv->trust_list, 1);
+    }
   g_mutex_clear (&self->priv->mutex);
 
   G_OBJECT_CLASS (g_tls_file_database_gnutls_parent_class)->finalize (object);
@@ -307,21 +298,29 @@ g_tls_file_database_gnutls_set_property (GObject      *object,
                                          GParamSpec   *pspec)
 {
   GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
-  gchar *anchor_path;
+  const char *anchor_path;
 
   switch (prop_id)
     {
     case PROP_ANCHORS:
-      anchor_path = g_value_dup_string (value);
+      anchor_path = g_value_get_string (value);
       if (anchor_path && !g_path_is_absolute (anchor_path))
-        {
-          g_warning ("The anchor file name for used with a GTlsFileDatabase "
-                     "must be an absolute path, and not relative: %s", anchor_path);
-        }
-      else
-        {
-          self->priv->anchor_filename = anchor_path;
-        }
+       {
+         g_warning ("The anchor file name used with a GTlsFileDatabase "
+                    "must be an absolute path, and not relative: %s", anchor_path);
+         return;
+       }
+
+      if (self->priv->anchor_filename)
+       {
+         g_free (self->priv->anchor_filename);
+         gnutls_x509_trust_list_deinit (self->priv->trust_list, 1);
+       }
+      self->priv->anchor_filename = g_strdup (anchor_path);
+      gnutls_x509_trust_list_init (&self->priv->trust_list, 0);
+      gnutls_x509_trust_list_add_trust_file (self->priv->trust_list,
+                                            anchor_path, NULL,
+                                            GNUTLS_X509_FMT_PEM, 0, 0);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -412,45 +411,6 @@ g_tls_file_database_gnutls_lookup_certificate_for_handle (GTlsDatabase
   return cert;
 }
 
-static gboolean
-g_tls_file_database_gnutls_lookup_assertion (GTlsFileDatabaseGnutls       *self,
-                                             GTlsCertificateGnutls        *certificate,
-                                             GTlsDatabaseGnutlsAssertion   assertion,
-                                             const gchar                  *purpose,
-                                             GSocketConnectable           *identity,
-                                             GCancellable                 *cancellable,
-                                             GError                      **error)
-{
-  GBytes *der = NULL;
-  gboolean contains;
-
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
-
-  /* We only have anchored certificate assertions here */
-  if (assertion != G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE)
-    return FALSE;
-
-  /*
-   * TODO: We should be parsing any Extended Key Usage attributes and
-   * comparing them to the purpose.
-   */
-
-  der = g_tls_certificate_gnutls_get_bytes (certificate);
-
-  g_mutex_lock (&self->priv->mutex);
-  contains = g_hash_table_lookup (self->priv->complete, der) ? TRUE : FALSE;
-  g_mutex_unlock (&self->priv->mutex);
-
-  g_bytes_unref (der);
-
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
-
-  /* All certificates in our file are anchored certificates */
-  return contains;
-}
-
 static GTlsCertificate *
 g_tls_file_database_gnutls_lookup_certificate_issuer (GTlsDatabase             *database,
                                                       GTlsCertificate          *certificate,
@@ -560,158 +520,6 @@ 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,
@@ -748,93 +556,55 @@ g_tls_file_database_gnutls_verify_chain (GTlsDatabase             *database,
 {
   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;
+  gnutls_x509_crt_t *certs;
+  guint certs_length;
+  const char *hostname = NULL;
+  char *free_hostname = NULL;
+  int gerr;
 
   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);
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return G_TLS_CERTIFICATE_GENERIC_ERROR;
 
-  /* 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;
-    }
+  self = G_TLS_FILE_DATABASE_GNUTLS (database);
 
-  if (err)
-    {
-      g_propagate_error (error, err);
-      return G_TLS_CERTIFICATE_GENERIC_ERROR;
-    }
+  convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain),
+                                       &certs, &certs_length);
+  gerr = gnutls_x509_trust_list_verify_crt (self->priv->trust_list,
+                                           certs, certs_length,
+                                           0, &gnutls_result, NULL);
 
-  anchor = NULL;
-  status = build_certificate_chain (self, certificate, NULL, FALSE, recursion_depth,
-                                    purpose, identity, interaction, cancellable, &anchor, &err);
-  if (status == STATUS_FAILURE)
+  if (gerr != 0 || g_cancellable_set_error_if_cancelled (cancellable, error))
     {
-      g_propagate_error (error, err);
+      g_free (certs);
       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);
+  result = g_tls_certificate_gnutls_convert_flags (gnutls_result);
 
-  if (anchor)
+  if (G_IS_NETWORK_ADDRESS (identity))
+    hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
+  else if (G_IS_NETWORK_SERVICE (identity))
+    hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
+  else if (G_IS_INET_SOCKET_ADDRESS (identity))
     {
-      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);
+      GInetAddress *addr;
+
+      addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (identity));
+      hostname = free_hostname = g_inet_address_to_string (addr);
     }
-  else
+  if (hostname)
     {
-      anchors = NULL;
-      anchors_length = 0;
+      if (!gnutls_x509_crt_check_hostname (certs[0], hostname))
+       result |= G_TLS_CERTIFICATE_BAD_IDENTITY;
+      g_free (free_hostname);
     }
 
-  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;
 }
 


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