[glib-networking/mcatanzaro/gnutls-creds] WIP: rewrite gnutls certificate verification




commit 12e593855cc95a2a6189953da30298e53b67a04e
Author: Michael Catanzaro <mcatanzaro redhat com>
Date:   Tue Jun 22 19:22:42 2021 -0500

    WIP: rewrite gnutls certificate verification
    
      /* There are several different ways to perform certificate verification with
       * GnuTLS, but they all fall into one of two categories:
       *
       * (a) outside the context of a TLS session
       * (b) within the context of a TLS session
       *
       * (a) is done by g_tls_database_verify_chain() and implemented using one of
       * several different functions of gnutls_x509_trust_list_t, e.g.
       * gnutls_x509_trust_list_verify_crt2() or one of the related functions.
       *
       * (b) is what we're doing here. The recommended way is to use
       * gnutls_session_set_verify_cert(), but we can't do that because that would
       * leave no way to implement the accept-certificate signal. The other way is
       * to use gnutls_certificate_verify_peers3() or one of the related functions.
       * This adds additional smarts that are not possible when using GTlsDatabase
       * directly. For example, it checks name constraints, key usage, and basic
       * constraints. It also checks for stapled OCSP responses. Verification will
       * fail if the OCSP response indicates the certificate has been revoked.
       * Verification will also fail if the Must-Staple flag is set but the OCSP
       * response is missing. Nice! This uses the gnutls_certificate_credentials_t
       * set on the gnutls_session_t by gnutls_credentials_set().
       */

 tls/base/gtlsconnection-base.c           |  18 ++--
 tls/base/gtlsconnection-base.h           |  40 +++++---
 tls/gnutls/gtlsclientconnection-gnutls.c |  10 ++
 tls/gnutls/gtlsconnection-gnutls.c       | 169 +++++++++++++++++++++++++++----
 tls/gnutls/gtlsconnection-gnutls.h       |   3 +
 tls/gnutls/gtlsdatabase-gnutls.c         | 112 ++++++++++++++------
 tls/gnutls/gtlsdatabase-gnutls.h         |  13 ++-
 tls/gnutls/gtlsserverconnection-gnutls.c |  10 ++
 tls/openssl/gtlsconnection-openssl.c     |  40 ++++++++
 9 files changed, 335 insertions(+), 80 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 72551dc..541e2a4 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -1240,6 +1240,7 @@ verify_peer_certificate (GTlsConnectionBase *tls,
                          GTlsCertificate    *peer_certificate)
 {
   GSocketConnectable *peer_identity = NULL;
+  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
   GTlsDatabase *database;
   GTlsCertificateFlags errors = 0;
   gboolean is_client;
@@ -1267,14 +1268,15 @@ verify_peer_certificate (GTlsConnectionBase *tls,
     {
       GError *error = NULL;
 
-      errors |= g_tls_database_verify_chain (database, peer_certificate,
-                                             is_client ?
-                                             G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER :
-                                             G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT,
-                                             peer_identity,
-                                             g_tls_connection_get_interaction (G_TLS_CONNECTION (tls)),
-                                             G_TLS_DATABASE_VERIFY_NONE,
-                                             NULL, &error);
+      g_assert (tls_class->verify_chain);
+      errors |= tls_class->verify_chain (tls,
+                                         peer_certificate,
+                                         is_client ? G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER : 
G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT,
+                                         peer_identity,
+                                         g_tls_connection_get_interaction (G_TLS_CONNECTION (tls)),
+                                         G_TLS_DATABASE_VERIFY_NONE,
+                                         NULL,
+                                         &error);
       if (error)
         {
           g_tls_log_debug (tls, "failure verifying certificate chain: %s", error->message);
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index a89122e..e9f3242 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -58,24 +58,32 @@ struct _GTlsConnectionBaseClass
 {
   GTlsConnectionClass parent_class;
 
-  void                        (*prepare_handshake)          (GTlsConnectionBase   *tls,
-                                                             gchar               **advertised_protocols);
+  void                        (*prepare_handshake)          (GTlsConnectionBase       *tls,
+                                                             gchar                   **advertised_protocols);
   GTlsSafeRenegotiationStatus (*handshake_thread_safe_renegotiation_status)
-                                                            (GTlsConnectionBase    *tls);
+                                                            (GTlsConnectionBase        *tls);
   GTlsConnectionBaseStatus    (*handshake_thread_request_rehandshake)
-                                                            (GTlsConnectionBase   *tls,
-                                                             gint64                timeout,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-  GTlsConnectionBaseStatus    (*handshake_thread_handshake) (GTlsConnectionBase   *tls,
-                                                             gint64                timeout,
-                                                             GCancellable         *cancellable,
-                                                             GError              **error);
-  GTlsCertificate            *(*retrieve_peer_certificate)  (GTlsConnectionBase   *tls);
-  void                        (*complete_handshake)         (GTlsConnectionBase   *tls,
-                                                             gboolean              handshake_succeeded,
-                                                             gchar               **negotiated_protocol,
-                                                             GError              **error);
+                                                            (GTlsConnectionBase       *tls,
+                                                             gint64                    timeout,
+                                                             GCancellable             *cancellable,
+                                                             GError                  **error);
+  GTlsConnectionBaseStatus    (*handshake_thread_handshake) (GTlsConnectionBase       *tls,
+                                                             gint64                    timeout,
+                                                             GCancellable             *cancellable,
+                                                             GError                  **error);
+  GTlsCertificate            *(*retrieve_peer_certificate)  (GTlsConnectionBase       *tls);
+  GTlsCertificateFlags        (*verify_chain)               (GTlsConnectionBase       *tls,
+                                                             GTlsCertificate          *chain,
+                                                             const gchar              *purpose,
+                                                             GSocketConnectable       *identity,
+                                                             GTlsInteraction          *interaction,
+                                                             GTlsDatabaseVerifyFlags   flags,
+                                                             GCancellable             *cancellable,
+                                                             GError                  **error);
+  void                        (*complete_handshake)         (GTlsConnectionBase       *tls,
+                                                             gboolean                  handshake_succeeded,
+                                                             gchar                   **negotiated_protocol,
+                                                             GError                  **error);
 
   gboolean                    (*is_session_resumed)         (GTlsConnectionBase   *tls);
 
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 6f4cd23..3c432da 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -566,11 +566,19 @@ g_tls_client_connection_gnutls_copy_session_state (GTlsClientConnection *conn,
   gnutls->session_data_override = !!gnutls->session_data;
 }
 
+static void
+g_tls_client_connection_gnutls_update_credentials (GTlsConnectionGnutls             *gnutls,
+                                                   gnutls_certificate_credentials_t  credentials)
+{
+  gnutls_certificate_set_retrieve_function2 (credentials, 
g_tls_client_connection_gnutls_handshake_thread_retrieve_function);
+}
+
 static void
 g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+  GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
 
   gobject_class->get_property = g_tls_client_connection_gnutls_get_property;
   gobject_class->set_property = g_tls_client_connection_gnutls_set_property;
@@ -579,6 +587,8 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
   base_class->prepare_handshake  = g_tls_client_connection_gnutls_prepare_handshake;
   base_class->complete_handshake = g_tls_client_connection_gnutls_complete_handshake;
 
+  gnutls_class->update_credentials = g_tls_client_connection_gnutls_update_credentials;
+
   g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
   g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
   g_object_class_override_property (gobject_class, PROP_USE_SSL3, "use-ssl3");
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index f99ba40..c4f5b1d 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -36,6 +36,7 @@
 #include "gtlsbackend-gnutls.h"
 #include "gtlscertificate-gnutls.h"
 #include "gtlsclientconnection-gnutls.h"
+#include "gtlsdatabase-gnutls.h"
 #include "gtlslog.h"
 
 #ifdef G_OS_WIN32
@@ -111,6 +112,55 @@ g_tls_connection_gnutls_set_handshake_priority (GTlsConnectionGnutls *gnutls)
     g_warning ("Failed to set GnuTLS session priority: %s", gnutls_strerror (ret));
 }
 
+static void
+update_credentials_cb (GObject    *gobject,
+                       GParamSpec *pspec,
+                       gpointer    user_data)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (gobject);
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+  GTlsConnectionGnutlsClass *connection_class = G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls);
+  gnutls_certificate_credentials_t credentials;
+  GTlsDatabase *database;
+  GError *error = NULL;
+  int ret;
+
+  database = g_tls_connection_get_database (G_TLS_CONNECTION (gnutls));
+  if (database)
+    {
+      credentials = g_tls_database_gnutls_get_credentials (G_TLS_DATABASE_GNUTLS (database), &error);
+      if (!credentials)
+        {
+          g_warning ("Failed to update credentials: %s", error->message);
+          g_error_free (error);
+          return;
+        }
+    }
+  else
+    {
+      ret = gnutls_certificate_allocate_credentials (&credentials);
+      if (ret != 0)
+        {
+          g_warning ("Failed to update credentials: %s", gnutls_strerror (ret));
+          return;
+        }
+    }
+
+  ret = gnutls_credentials_set (priv->session, GNUTLS_CRD_CERTIFICATE, credentials);
+  if (ret != 0)
+    {
+      g_warning ("Failed to update credentials: %s", gnutls_strerror (ret));
+      gnutls_certificate_free_credentials (credentials);
+      return;
+    }
+
+  gnutls_certificate_free_credentials (priv->creds);
+  priv->creds = credentials;
+
+  g_assert (connection_class->update_credentials);
+  connection_class->update_credentials (gnutls, credentials);
+}
+
 static gboolean
 g_tls_connection_gnutls_initable_init (GInitable     *initable,
                                        GCancellable  *cancellable,
@@ -118,11 +168,13 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
 {
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+  GTlsDatabase *database;
   GIOStream *base_io_stream = NULL;
   GDatagramBased *base_socket = NULL;
   gboolean client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
   guint flags = client ? GNUTLS_CLIENT : GNUTLS_SERVER;
-  int status;
+  GError *my_error = NULL;
+  gboolean success = FALSE;
   int ret;
 
   g_object_get (gnutls,
@@ -136,33 +188,47 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
   if (base_socket)
     flags |= GNUTLS_DATAGRAM;
 
-  ret = gnutls_certificate_allocate_credentials (&priv->creds);
-  if (ret != GNUTLS_E_SUCCESS)
+  database = g_tls_connection_get_database (G_TLS_CONNECTION (gnutls));
+  if (database)
     {
-      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
-                   _("Could not create TLS connection: %s"),
-                   gnutls_strerror (ret));
-      g_clear_object (&base_io_stream);
-      g_clear_object (&base_socket);
-      return FALSE;
+      priv->creds = g_tls_database_gnutls_get_credentials (G_TLS_DATABASE_GNUTLS (database), &my_error);
+      if (!priv->creds)
+        {
+          g_propagate_prefixed_error (error, my_error, ("Could not create TLS connection:"));
+          goto out;
+        }
+    }
+  else
+    {
+      ret = gnutls_certificate_allocate_credentials (&priv->creds);
+      if (ret != 0)
+        {
+          g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                       _("Could not create TLS connection: %s"),
+                       gnutls_strerror (ret));
+          goto out;
+        }
     }
 
+  if (!client)
+    g_signal_connect (gnutls, "notify::authentication-mode", G_CALLBACK (update_credentials_cb), NULL);
+  g_signal_connect (gnutls, "notify::database", G_CALLBACK (update_credentials_cb), NULL);
+  g_signal_connect (gnutls, "notify::use-system-certdb", G_CALLBACK (update_credentials_cb), NULL);
+
   gnutls_init (&priv->session, flags);
 
   gnutls_session_set_ptr (priv->session, gnutls);
   gnutls_session_set_verify_function (priv->session, verify_certificate_cb);
 
-  status = gnutls_credentials_set (priv->session,
-                                   GNUTLS_CRD_CERTIFICATE,
-                                   priv->creds);
-  if (status != 0)
+  ret = gnutls_credentials_set (priv->session,
+                                GNUTLS_CRD_CERTIFICATE,
+                                priv->creds);
+  if (ret != 0)
     {
       g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
                    _("Could not create TLS connection: %s"),
-                   gnutls_strerror (status));
-      g_clear_object (&base_io_stream);
-      g_clear_object (&base_socket);
-      return FALSE;
+                   gnutls_strerror (ret));
+      goto out;
     }
 
   gnutls_transport_set_push_function (priv->session,
@@ -184,10 +250,13 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
   if (flags & GNUTLS_DATAGRAM)
     gnutls_dtls_set_mtu (priv->session, 1400);
 
+  success = TRUE;
+
+out:
   g_clear_object (&base_io_stream);
   g_clear_object (&base_socket);
 
-  return TRUE;
+  return success;
 }
 
 static void
@@ -900,6 +969,69 @@ g_tls_connection_gnutls_handshake_thread_handshake (GTlsConnectionBase  *tls,
   return status;
 }
 
+static GTlsCertificateFlags
+g_tls_connection_gnutls_verify_chain (GTlsConnectionBase       *tls,
+                                      GTlsCertificate          *chain,
+                                      const gchar              *purpose,
+                                      GSocketConnectable       *identity,
+                                      GTlsInteraction          *interaction,
+                                      GTlsDatabaseVerifyFlags   flags,
+                                      GCancellable             *cancellable,
+                                      GError                  **error)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+  GTlsCertificateFlags errors = 0;
+  const char *hostname = NULL;
+  char *free_hostname = NULL;
+  guint gnutls_result;
+  int ret;
+
+  /* There are several different ways to perform certificate verification with
+   * GnuTLS, but they all fall into one of two categories:
+   *
+   * (a) outside the context of a TLS session
+   * (b) within the context of a TLS session
+   *
+   * (a) is done by g_tls_database_verify_chain() and implemented using one of
+   * several different functions of gnutls_x509_trust_list_t, e.g.
+   * gnutls_x509_trust_list_verify_crt2() or one of the related functions.
+   *
+   * (b) is what we're doing here. The recommended way is to use
+   * gnutls_session_set_verify_cert(), but we can't do that because that would
+   * leave no way to implement the accept-certificate signal. The other way is
+   * to use gnutls_certificate_verify_peers3() or one of the related functions.
+   * This adds additional smarts that are not possible when using GTlsDatabase
+   * directly. For example, it checks name constraints, key usage, and basic
+   * constraints. It also checks for stapled OCSP responses. Verification will
+   * fail if the OCSP response indicates the certificate has been revoked.
+   * Verification will also fail if the Must-Staple flag is set but the OCSP
+   * response is missing. Nice! This uses the gnutls_certificate_credentials_t
+   * set on the gnutls_session_t by gnutls_credentials_set().
+   */
+
+  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))
+    {
+      GInetAddress *addr;
+
+      addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (identity));
+      hostname = free_hostname = g_inet_address_to_string (addr);
+    }
+
+  ret = gnutls_certificate_verify_peers3 (priv->session, hostname, &gnutls_result);
+  if (ret != 0)
+    errors = G_TLS_CERTIFICATE_GENERIC_ERROR;
+  else
+    errors = g_tls_certificate_gnutls_convert_flags (gnutls_result);
+
+  g_free (free_hostname);
+  return errors;
+}
+
 static void
 g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
                                             gboolean             handshake_succeeded,
@@ -1348,6 +1480,7 @@ g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
   base_class->handshake_thread_request_rehandshake       = 
g_tls_connection_gnutls_handshake_thread_request_rehandshake;
   base_class->handshake_thread_handshake                 = 
g_tls_connection_gnutls_handshake_thread_handshake;
   base_class->retrieve_peer_certificate                  = g_tls_connection_gnutls_retrieve_peer_certificate;
+  base_class->verify_chain                               = g_tls_connection_gnutls_verify_chain;
   base_class->complete_handshake                         = g_tls_connection_gnutls_complete_handshake;
   base_class->is_session_resumed                         = g_tls_connection_gnutls_is_session_resumed;
   base_class->get_channel_binding_data                   = g_tls_connection_gnutls_get_channel_binding_data;
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index 55ae5ee..16ec747 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -39,6 +39,9 @@ G_DECLARE_DERIVABLE_TYPE (GTlsConnectionGnutls, g_tls_connection_gnutls, G, TLS_
 struct _GTlsConnectionGnutlsClass
 {
   GTlsConnectionBaseClass parent_class;
+
+  void (*update_credentials) (GTlsConnectionGnutls             *gnutls,
+                              gnutls_certificate_credentials_t  credentials);
 };
 
 gnutls_certificate_credentials_t g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
diff --git a/tls/gnutls/gtlsdatabase-gnutls.c b/tls/gnutls/gtlsdatabase-gnutls.c
index 4354d48..5499ae5 100644
--- a/tls/gnutls/gtlsdatabase-gnutls.c
+++ b/tls/gnutls/gtlsdatabase-gnutls.c
@@ -566,26 +566,6 @@ g_tls_database_gnutls_create_handle_for_certificate (GTlsDatabaseGnutls *self,
   return uri;
 }
 
-static gboolean
-g_tls_database_gnutls_populate_trust_list (GTlsDatabaseGnutls        *self,
-                                           gnutls_x509_trust_list_t   trust_list,
-                                           GError                   **error)
-{
-  int gerr = gnutls_x509_trust_list_add_system_trust (trust_list, 0, 0);
-  if (gerr == GNUTLS_E_UNIMPLEMENTED_FEATURE)
-    {
-      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
-                           _("Failed to load system trust store: GnuTLS was not configured with a system 
trust"));
-    }
-  else if (gerr < 0)
-    {
-      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
-                   _("Failed to load system trust store: %s"),
-                   gnutls_strerror (gerr));
-    }
-  return gerr >= 0;
-}
-
 #if GNUTLS_VERSION_MAJOR > 3 || GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 7
 static int
 issuer_missing_cb (gnutls_x509_trust_list_t   tlist,
@@ -710,6 +690,82 @@ out:
 }
 #endif
 
+static gboolean
+g_tls_database_gnutls_populate_trust_list (GTlsDatabaseGnutls        *self,
+                                           gnutls_x509_trust_list_t   trust_list,
+                                           GError                   **error)
+{
+  int gerr = gnutls_x509_trust_list_add_system_trust (trust_list, 0, 0);
+  if (gerr == GNUTLS_E_UNIMPLEMENTED_FEATURE)
+    {
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                           _("Failed to load system trust store: GnuTLS was not configured with a system 
trust"));
+    }
+  else if (gerr < 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                   _("Failed to load system trust store: %s"),
+                   gnutls_strerror (gerr));
+    }
+  return gerr >= 0;
+}
+
+static gnutls_x509_trust_list_t
+create_trust_list (GTlsDatabaseGnutls  *self,
+                   GError             **error)
+{
+  GTlsDatabaseGnutlsClass *database_class = G_TLS_DATABASE_GNUTLS_GET_CLASS (self);
+  gnutls_x509_trust_list_t trust_list;
+  int ret;
+
+  ret = gnutls_x509_trust_list_init (&trust_list, 0);
+  if (ret != 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC, "Failed to initialize trust list: %s", 
gnutls_strerror (ret));
+      return NULL;
+    }
+
+#if GNUTLS_VERSION_MAJOR > 3 || GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 7
+  gnutls_x509_trust_list_set_getissuer_function (trust_list, issuer_missing_cb);
+  gnutls_x509_trust_list_set_ptr (trust_list, self);
+#endif
+
+  g_assert (database_class->populate_trust_list);
+  if (!database_class->populate_trust_list (self, trust_list, error))
+    {
+      gnutls_x509_trust_list_deinit (trust_list, TRUE);
+      return NULL;
+    }
+
+  return trust_list;
+}
+
+gnutls_certificate_credentials_t
+g_tls_database_gnutls_get_credentials (GTlsDatabaseGnutls  *self,
+                                       GError             **error)
+{
+  gnutls_certificate_credentials_t credentials;
+  gnutls_x509_trust_list_t trust_list = NULL;
+  int ret;
+
+  ret = gnutls_certificate_allocate_credentials (&credentials);
+  if (ret != 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC, "Failed to allocate credentials: %s", 
gnutls_strerror (ret));
+      return NULL;
+    }
+
+  trust_list = create_trust_list (self, error);
+  if (!trust_list)
+    {
+      gnutls_certificate_free_credentials (credentials);
+      return NULL;
+    }
+
+  gnutls_certificate_set_trust_list (credentials, trust_list, 0);
+  return credentials;
+}
+
 static void
 g_tls_database_gnutls_class_init (GTlsDatabaseGnutlsClass *klass)
 {
@@ -744,18 +800,9 @@ g_tls_database_gnutls_initable_init (GInitable     *initable,
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return FALSE;
 
-  gnutls_x509_trust_list_init (&trust_list, 0);
-#if GNUTLS_VERSION_MAJOR > 3 || GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 7
-  gnutls_x509_trust_list_set_getissuer_function (trust_list, issuer_missing_cb);
-  gnutls_x509_trust_list_set_ptr (trust_list, self);
-#endif
-
-  g_assert (G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->populate_trust_list);
-  if (!G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->populate_trust_list (self, trust_list, error))
-    {
-      result = FALSE;
-      goto out;
-    }
+  trust_list = create_trust_list (self, error);
+  if (!trust_list)
+    return FALSE;
 
   subjects = bytes_multi_table_new ();
   issuers = bytes_multi_table_new ();
@@ -795,7 +842,6 @@ g_tls_database_gnutls_initable_init (GInitable     *initable,
       g_mutex_unlock (&priv->mutex);
     }
 
-out:
   if (trust_list)
     gnutls_x509_trust_list_deinit (trust_list, 1);
   if (subjects)
diff --git a/tls/gnutls/gtlsdatabase-gnutls.h b/tls/gnutls/gtlsdatabase-gnutls.h
index 0a31dd5..0f9cd93 100644
--- a/tls/gnutls/gtlsdatabase-gnutls.h
+++ b/tls/gnutls/gtlsdatabase-gnutls.h
@@ -41,13 +41,16 @@ struct _GTlsDatabaseGnutlsClass
 {
   GTlsDatabaseClass parent_class;
 
-  gchar    *(*create_handle_for_certificate)  (GTlsDatabaseGnutls        *self,
-                                               GBytes                    *der);
-  gboolean  (*populate_trust_list)            (GTlsDatabaseGnutls        *self,
-                                               gnutls_x509_trust_list_t   trust_list,
-                                               GError                   **error);
+  gchar    *(*create_handle_for_certificate)  (GTlsDatabaseGnutls                *self,
+                                               GBytes                            *der);
+  gboolean  (*populate_trust_list)            (GTlsDatabaseGnutls                *self,
+                                               gnutls_x509_trust_list_t           trust_list,
+                                               GError                           **error);
 };
 
 GTlsDatabaseGnutls *g_tls_database_gnutls_new (GError **error);
 
+gnutls_certificate_credentials_t g_tls_database_gnutls_get_credentials (GTlsDatabaseGnutls  *self,
+                                                                        GError             **error);
+
 G_END_DECLS
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index 090b57d..2530376 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -218,11 +218,19 @@ g_tls_server_connection_gnutls_prepare_handshake (GTlsConnectionBase  *tls,
   G_TLS_CONNECTION_BASE_CLASS (g_tls_server_connection_gnutls_parent_class)->prepare_handshake (tls, 
advertised_protocols);
 }
 
+static void
+g_tls_server_connection_gnutls_update_credentials (GTlsConnectionGnutls             *gnutls,
+                                                   gnutls_certificate_credentials_t  credentials)
+{
+  gnutls_certificate_set_retrieve_function2 (credentials, 
g_tls_server_connection_gnutls_handshake_thread_retrieve_function);
+}
+
 static void
 g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+  GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
 
   gobject_class->finalize     = g_tls_server_connection_gnutls_finalize;
   gobject_class->get_property = g_tls_server_connection_gnutls_get_property;
@@ -230,6 +238,8 @@ g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klas
 
   base_class->prepare_handshake  = g_tls_server_connection_gnutls_prepare_handshake;
 
+  gnutls_class->update_credentials = g_tls_server_connection_gnutls_update_credentials;
+
   g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
 }
 
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index e756fa7..8829d28 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -482,7 +482,44 @@ g_tls_connection_openssl_prepare_handshake (GTlsConnectionBase  *tls,
       g_byte_array_unref (protocols);
     }
 }
+#endif
+
+static GTlsCertificateFlags
+g_tls_connection_openssl_verify_chain (GTlsConnectionBase       *tls,
+                                       GTlsCertificate          *chain,
+                                       const gchar              *purpose,
+                                       GSocketConnectable       *identity,
+                                       GTlsInteraction          *interaction,
+                                       GTlsDatabaseVerifyFlags   flags,
+                                       GCancellable             *cancellable,
+                                       GError                  **error)
+{
+  GTlsDatabase *database;
+  GTlsCertificateFlags errors = 0;
+  gboolean is_client = G_IS_TLS_CLIENT_CONNECTION (tls);
 
+  database = g_tls_connection_get_database (G_TLS_CONNECTION (tls));
+  if (database)
+    {
+      errors |= g_tls_database_verify_chain (database,
+                                             chain,
+                                             is_client ? G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER : 
G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT,
+                                             identity,
+                                             g_tls_connection_get_interaction (G_TLS_CONNECTION (tls)),
+                                             G_TLS_DATABASE_VERIFY_NONE,
+                                             NULL,
+                                             error);
+    }
+  else
+    {
+      errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+      errors |= g_tls_certificate_verify (chain, identity, NULL);
+    }
+
+  return errors;
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L || defined (LIBRESSL_VERSION_NUMBER)
 static void
 g_tls_connection_openssl_complete_handshake (GTlsConnectionBase  *tls,
                                              gboolean             handshake_succeeded,
@@ -1005,6 +1042,9 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L || defined (LIBRESSL_VERSION_NUMBER)
   base_class->prepare_handshake                          = g_tls_connection_openssl_prepare_handshake;
+#endif
+  base_class->verify_chain                               = g_tls_connection_openssl_verify_chain;
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L || defined (LIBRESSL_VERSION_NUMBER)
   base_class->complete_handshake                         = g_tls_connection_openssl_complete_handshake;
 #endif
   base_class->handshake_thread_safe_renegotiation_status = 
g_tls_connection_openssl_handshake_thread_safe_renegotiation_status;


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