[glib-networking] gnutls: reorganize certificate handling



commit e4e47d270eaeacef010a9ee41c8a3f1c0aabdad6
Author: Dan Winship <danw gnome org>
Date:   Tue Jul 17 18:47:26 2012 -0400

    gnutls: reorganize certificate handling
    
    Move some of the code in handshake_internal() out into helper
    functions, and move the code from the client and server verify_peer()
    implementations into a helper function in gtlsconnection-gnutls.c as
    well, to reduce duplication between them.

 tls/gnutls/gtlsclientconnection-gnutls.c |   49 +--------
 tls/gnutls/gtlsconnection-gnutls.c       |  179 ++++++++++++++++++++++--------
 tls/gnutls/gtlsconnection-gnutls.h       |    4 -
 tls/gnutls/gtlsserverconnection-gnutls.c |   35 ------
 4 files changed, 136 insertions(+), 131 deletions(-)
---
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 6ff6383..478be2b 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -293,55 +293,15 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
   gnutls->priv->cert_requested = FALSE;
 }
 
-static gboolean
-g_tls_client_connection_gnutls_verify_peer (GTlsConnectionGnutls  *conn_gnutls,
-					    GTlsCertificate       *peer_certificate,
-					    GTlsCertificateFlags  *errors)
-{
-  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn_gnutls);
-  GTlsDatabase *database;
-  gboolean accepted;
-  GError *error = NULL;
-
-  database = g_tls_connection_get_database (G_TLS_CONNECTION (conn_gnutls));
-  if (database == NULL)
-    {
-      *errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
-      *errors |= g_tls_certificate_verify (peer_certificate, gnutls->priv->server_identity, NULL);
-    }
-  else
-    {
-      *errors |= g_tls_database_verify_chain (database, peer_certificate,
-                                              G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
-                                              gnutls->priv->server_identity,
-                                              g_tls_connection_get_interaction (G_TLS_CONNECTION (gnutls)),
-                                              G_TLS_DATABASE_VERIFY_NONE,
-                                              NULL, &error);
-      if (error)
-        {
-          g_warning ("failure verifying certificate chain: %s",
-                     error->message);
-          g_clear_error (&error);
-        }
-    }
-
-  if (*errors & gnutls->priv->validation_flags)
-    accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls), peer_certificate, *errors);
-  else
-    accepted = TRUE;
-
-  return accepted;
-}
-
 static void
 g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
-						 gboolean               success,
 						 GError               **inout_error)
 {
   GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
 
-  if (inout_error &&
-      g_error_matches (*inout_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS) &&
+  g_assert (inout_error != NULL);
+
+  if (g_error_matches (*inout_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS) &&
       gnutls->priv->cert_requested)
     {
       g_clear_error (inout_error);
@@ -353,7 +313,7 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
     {
       gnutls_datum session_datum;
 
-      if (success &&
+      if (!*inout_error &&
 	  gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (conn),
 				    &session_datum) == 0)
 	{
@@ -384,7 +344,6 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
 
   connection_gnutls_class->failed           = g_tls_client_connection_gnutls_failed;
   connection_gnutls_class->begin_handshake  = g_tls_client_connection_gnutls_begin_handshake;
-  connection_gnutls_class->verify_peer      = g_tls_client_connection_gnutls_verify_peer;
   connection_gnutls_class->finish_handshake = g_tls_client_connection_gnutls_finish_handshake;
 
   g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 65658e3..46db1c7 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -769,6 +769,110 @@ g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
   return ret;
 }
 
+
+static GTlsCertificate *
+get_peer_certificate_from_session (GTlsConnectionGnutls *gnutls)
+{
+  GTlsCertificate *chain, *cert;
+  const gnutls_datum_t *certs;
+  unsigned int num_certs;
+  int i;
+
+  certs = gnutls_certificate_get_peers (gnutls->priv->session, &num_certs);
+  if (!certs || !num_certs)
+    return NULL;
+
+  chain = NULL;
+  for (i = num_certs - 1; i >= 0; i--)
+    {
+      cert = g_tls_certificate_gnutls_new (&certs[i], chain);
+      if (chain)
+	g_object_unref (chain);
+      chain = cert;
+    }
+
+  return chain;
+}
+
+static GTlsCertificateFlags
+verify_peer_certificate (GTlsConnectionGnutls *gnutls,
+			 GTlsCertificate      *peer_certificate)
+{
+  GTlsConnection *conn = G_TLS_CONNECTION (gnutls);
+  GSocketConnectable *peer_identity;
+  GTlsDatabase *database;
+  GTlsCertificateFlags errors;
+  gboolean is_client;
+
+  is_client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
+  if (is_client)
+    peer_identity = g_tls_client_connection_get_server_identity (G_TLS_CLIENT_CONNECTION (gnutls));
+  else
+    peer_identity = NULL;
+
+  errors = 0;
+
+  database = g_tls_connection_get_database (conn);
+  if (database == NULL)
+    {
+      errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+      errors |= g_tls_certificate_verify (peer_certificate, peer_identity, NULL);
+    }
+  else
+    {
+      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 (conn),
+					     G_TLS_DATABASE_VERIFY_NONE,
+					     NULL, &error);
+      if (error)
+	{
+	  g_warning ("failure verifying certificate chain: %s",
+		     error->message);
+	  g_assert (errors != 0);
+	  g_clear_error (&error);
+	}
+    }
+
+  return errors;
+}
+
+static gboolean
+accept_peer_certificate (GTlsConnectionGnutls *gnutls,
+			 GTlsCertificate      *peer_certificate,
+			 GTlsCertificateFlags  peer_certificate_errors)
+{
+  gboolean accepted;
+
+  if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
+    {
+      GTlsCertificateFlags validation_flags =
+	g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (gnutls));
+
+      if ((peer_certificate_errors & validation_flags) == 0)
+	accepted = TRUE;
+      else
+	{
+	  accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls),
+							       peer_certificate,
+							       peer_certificate_errors);
+	}
+    }
+  else
+    {
+      accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls),
+							   peer_certificate,
+							   peer_certificate_errors);
+    }
+
+  return accepted;
+}
+
 static gboolean
 handshake_internal (GTlsConnectionGnutls  *gnutls,
 		    gboolean               blocking,
@@ -797,14 +901,8 @@ handshake_internal (GTlsConnectionGnutls  *gnutls,
     {
       gnutls->priv->handshaking = TRUE;
 
-      if (gnutls->priv->peer_certificate)
-	{
-	  g_clear_object (&gnutls->priv->peer_certificate);
-	  gnutls->priv->peer_certificate_errors = 0;
-
-	  g_object_notify (G_OBJECT (gnutls), "peer-certificate");
-	  g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
-	}
+      g_clear_object (&gnutls->priv->peer_certificate);
+      gnutls->priv->peer_certificate_errors = 0;
 
       g_tls_connection_gnutls_set_handshake_priority (gnutls);
       G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
@@ -828,57 +926,44 @@ handshake_internal (GTlsConnectionGnutls  *gnutls,
 
   if (ret == 0 &&
       gnutls_certificate_type_get (gnutls->priv->session) == GNUTLS_CRT_X509)
-    {
-      GTlsCertificate *chain, *cert;
-      const gnutls_datum_t *certs;
-      unsigned int num_certs;
-      int i;
-
-      certs = gnutls_certificate_get_peers (gnutls->priv->session, &num_certs);
-      chain = NULL;
-      if (certs)
-	{
-	  for (i = num_certs - 1; i >= 0; i--)
-	    {
-	      cert = g_tls_certificate_gnutls_new (&certs[i], chain);
-	      if (chain)
-		g_object_unref (chain);
-	      chain = cert;
-	    }
-	}
-
-      peer_certificate = chain;
-    }
+    peer_certificate = get_peer_certificate_from_session (gnutls);
+  else
+    peer_certificate = NULL;
 
   if (peer_certificate)
     {
-      gboolean accepted;
-
-      accepted = G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->verify_peer (gnutls, peer_certificate, &peer_certificate_errors);
-
-      gnutls->priv->peer_certificate = peer_certificate;
-      gnutls->priv->peer_certificate_errors = peer_certificate_errors;
-
-      g_object_notify (G_OBJECT (gnutls), "peer-certificate");
-      g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
-
-      if (!accepted)
+      peer_certificate_errors = verify_peer_certificate (gnutls, peer_certificate);
+      if (!accept_peer_certificate (gnutls, peer_certificate,
+				    peer_certificate_errors))
 	{
 	  g_set_error_literal (&gnutls->priv->handshake_error,
 			       G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
 			       _("Unacceptable TLS certificate"));
-	  if (error)
-	    *error = g_error_copy (gnutls->priv->handshake_error);
-	  return FALSE;
 	}
+
+      gnutls->priv->peer_certificate = peer_certificate;
+      gnutls->priv->peer_certificate_errors = peer_certificate_errors;
+      g_object_notify (G_OBJECT (gnutls), "peer-certificate");
+      g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
+    }
+  else if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
+    {
+      g_set_error_literal (&gnutls->priv->handshake_error,
+			   G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			   _("Server did not return a valid TLS certificate"));
     }
 
   G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->
-    finish_handshake (gnutls, ret == 0, &gnutls->priv->handshake_error);
+    finish_handshake (gnutls, &gnutls->priv->handshake_error);
 
-  if (gnutls->priv->handshake_error && error)
-    *error = g_error_copy (gnutls->priv->handshake_error);
-  return (ret == 0);
+  if (gnutls->priv->handshake_error)
+    {
+      if (error)
+	*error = g_error_copy (gnutls->priv->handshake_error);
+      return FALSE;
+    }
+  else
+    return TRUE;
 }
 
 static gboolean
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index b69eca1..b217f97 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -36,11 +36,7 @@ struct _GTlsConnectionGnutlsClass
   void     (*failed)           (GTlsConnectionGnutls  *gnutls);
 
   void     (*begin_handshake)  (GTlsConnectionGnutls  *gnutls);
-  gboolean (*verify_peer)      (GTlsConnectionGnutls  *gnutls,
-				GTlsCertificate       *peer_certificate,
-				GTlsCertificateFlags  *errors);
   void     (*finish_handshake) (GTlsConnectionGnutls  *gnutls,
-				gboolean               success,
 				GError               **inout_error);
 };
 
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index 2f05ecf..566b922 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -187,42 +187,8 @@ g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
   gnutls_certificate_server_set_request (session, req_mode);
 }
 
-static gboolean
-g_tls_server_connection_gnutls_verify_peer (GTlsConnectionGnutls  *gnutls,
-					    GTlsCertificate       *peer_certificate,
-					    GTlsCertificateFlags  *errors)
-{
-  GTlsDatabase *database;
-  GError *error = NULL;
-
-  database = g_tls_connection_get_database (G_TLS_CONNECTION (gnutls));
-  if (database == NULL)
-    {
-      *errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
-      *errors |= g_tls_certificate_verify (peer_certificate, NULL, NULL);
-    }
-  else
-    {
-      *errors |= g_tls_database_verify_chain (database, peer_certificate,
-                                              G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT, NULL,
-                                              g_tls_connection_get_interaction (G_TLS_CONNECTION (gnutls)),
-                                              G_TLS_DATABASE_VERIFY_NONE,
-                                              NULL, &error);
-      if (error)
-        {
-          g_warning ("failure verifying certificate chain: %s",
-                     error->message);
-          g_clear_error (&error);
-        }
-    }
-
-  return g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls),
-                                                   peer_certificate, *errors);
-}
-
 static void
 g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *gnutls,
-						 gboolean               success,
 						 GError               **inout_error)
 {
 }
@@ -298,7 +264,6 @@ g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klas
 
   connection_gnutls_class->failed           = g_tls_server_connection_gnutls_failed;
   connection_gnutls_class->begin_handshake  = g_tls_server_connection_gnutls_begin_handshake;
-  connection_gnutls_class->verify_peer      = g_tls_server_connection_gnutls_verify_peer;
   connection_gnutls_class->finish_handshake = g_tls_server_connection_gnutls_finish_handshake;
 
   g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");



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