[glib-networking/mcatanzaro/gnutls-creds: 24/24] gnutls: perform certificate verification in TLS session context




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

    gnutls: perform certificate verification in TLS session context
    
    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.
    This is what we have historically always done.
    
    (b) is what we're now 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!
    
    Incidentally fixes #32

 tls/base/gtlsconnection-base.c           |  18 ++--
 tls/base/gtlsconnection-base.h           |  14 ++-
 tls/gnutls/gtlsclientconnection-gnutls.c |  10 ++
 tls/gnutls/gtlsconnection-gnutls.c       | 168 +++++++++++++++++++++++++++----
 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     |  36 +++++++
 9 files changed, 316 insertions(+), 68 deletions(-)
---
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index dd23c46..6608b9b 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -1258,6 +1258,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;
@@ -1285,14 +1286,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 4541f54..a8c9b93 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -58,10 +58,10 @@ 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,
@@ -72,6 +72,14 @@ struct _GTlsConnectionBaseClass
                                                              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,
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 7df0488..bd67fc1 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -573,11 +573,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;
@@ -586,6 +594,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 cc07c1f..c072a1d 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,45 @@ 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;
+        }
     }
 
+  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 +248,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
@@ -907,6 +974,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 GTlsProtocolVersion
 glib_protocol_version_from_gnutls (gnutls_protocol_t protocol_version)
 {
@@ -949,7 +1079,6 @@ get_ciphersuite_name (gnutls_session_t session)
   return g_strdup_printf ("TLS_%s_%s",
                           gnutls_cipher_get_name (gnutls_cipher_get (session)),
                           gnutls_digest_get_name (gnutls_prf_hash_get (session)));
-
 }
 
 static void
@@ -1427,6 +1556,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 648645c..4c49c56 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -484,6 +484,41 @@ g_tls_connection_openssl_prepare_handshake (GTlsConnectionBase  *tls,
 }
 #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;
+}
+
 static GTlsProtocolVersion
 glib_protocol_version_from_openssl (int protocol_version)
 {
@@ -1039,6 +1074,7 @@ 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;
   base_class->complete_handshake                         = g_tls_connection_openssl_complete_handshake;
   base_class->handshake_thread_safe_renegotiation_status = 
g_tls_connection_openssl_handshake_thread_safe_renegotiation_status;
   base_class->handshake_thread_request_rehandshake       = 
g_tls_connection_openssl_handshake_thread_request_rehandshake;


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