[empathy/wip/rishi/tls: 2/2] tls-verifier: Use GIO to verify the chain of TLS certificates



commit 45b87deec7f25c76c29811913765e31ae48b45bd
Author: Debarshi Ray <debarshir gnome org>
Date:   Wed Mar 15 20:24:08 2017 +0100

    tls-verifier: Use GIO to verify the chain of TLS certificates
    
    Gcr has its own hand rolled code to complete the certificate chain and
    validate it, which predates the equivalent functionality in GIO. These
    days, GIO's GnuTLS backend is a better option because it defers to
    GnuTLS to do the right thing. It benefits automatically from any
    improvements made to GnuTLS itself.

 libempathy/empathy-tls-verifier.c |  352 ++++++++++---------------------------
 1 files changed, 91 insertions(+), 261 deletions(-)
---
diff --git a/libempathy/empathy-tls-verifier.c b/libempathy/empathy-tls-verifier.c
index 8f80b43..bbe49c4 100644
--- a/libempathy/empathy-tls-verifier.c
+++ b/libempathy/empathy-tls-verifier.c
@@ -53,138 +53,44 @@ typedef struct {
   gboolean dispose_run;
 } EmpathyTLSVerifierPriv;
 
-static gboolean
-verification_output_to_reason (gint res,
-    guint verify_output,
-    TpTLSCertificateRejectReason *reason)
+static TpTLSCertificateRejectReason
+verification_output_to_reason (GTlsCertificateFlags flags)
 {
-  gboolean retval = TRUE;
+  TpTLSCertificateRejectReason retval;
 
-  g_assert (reason != NULL);
+  g_assert (flags != 0);
 
-  if (res != GNUTLS_E_SUCCESS)
+  switch (flags)
     {
-      retval = FALSE;
-
-      /* the certificate is not structurally valid */
-      switch (res)
-        {
-        case GNUTLS_E_INSUFFICIENT_CREDENTIALS:
-          *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
-          break;
-        case GNUTLS_E_CONSTRAINT_ERROR:
-          *reason = TP_TLS_CERTIFICATE_REJECT_REASON_LIMIT_EXCEEDED;
-          break;
-        default:
-          *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
-          break;
-        }
-
-      goto out;
+      case G_TLS_CERTIFICATE_UNKNOWN_CA:
+        retval = TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
+        break;
+      case G_TLS_CERTIFICATE_BAD_IDENTITY:
+        retval = TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH;
+        break;
+      case G_TLS_CERTIFICATE_NOT_ACTIVATED:
+        retval = TP_TLS_CERTIFICATE_REJECT_REASON_NOT_ACTIVATED;
+        break;
+      case G_TLS_CERTIFICATE_EXPIRED:
+        retval = TP_TLS_CERTIFICATE_REJECT_REASON_EXPIRED;
+        break;
+      case G_TLS_CERTIFICATE_REVOKED:
+        retval = TP_TLS_CERTIFICATE_REJECT_REASON_REVOKED;
+        break;
+      case G_TLS_CERTIFICATE_INSECURE:
+        retval = TP_TLS_CERTIFICATE_REJECT_REASON_INSECURE;
+        break;
+      case G_TLS_CERTIFICATE_BAD_IDENTITY:
+      case G_TLS_CERTIFICATE_GENERIC_ERROR:
+      default:
+        retval = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
+        break;
     }
 
-  /* the certificate is structurally valid, check for other errors. */
-  if (verify_output & GNUTLS_CERT_INVALID)
-    {
-      retval = FALSE;
-
-      if (verify_output & GNUTLS_CERT_SIGNER_NOT_FOUND)
-        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED;
-      else if (verify_output & GNUTLS_CERT_SIGNER_NOT_CA)
-        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
-      else if (verify_output & GNUTLS_CERT_INSECURE_ALGORITHM)
-        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_INSECURE;
-      else if (verify_output & GNUTLS_CERT_NOT_ACTIVATED)
-        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_NOT_ACTIVATED;
-      else if (verify_output & GNUTLS_CERT_EXPIRED)
-        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_EXPIRED;
-      else if (verify_output & GNUTLS_CERT_REVOKED)
-        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_REVOKED;
-      else
-        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
-
-      goto out;
-    }
-
- out:
   return retval;
 }
 
 static void
-build_certificate_list_for_gnutls (GcrCertificateChain *chain,
-        gnutls_x509_crt_t **list,
-        guint *n_list,
-        gnutls_x509_crt_t **anchors,
-        guint *n_anchors)
-{
-  GcrCertificate *cert;
-  guint idx, length;
-  gnutls_x509_crt_t *retval;
-  gnutls_x509_crt_t gcert;
-  gnutls_datum_t datum;
-  gsize n_data;
-
-  g_assert (list);
-  g_assert (n_list);
-  g_assert (anchors);
-  g_assert (n_anchors);
-
-  *list = *anchors = NULL;
-  *n_list = *n_anchors = 0;
-
-  length = gcr_certificate_chain_get_length (chain);
-  retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * length);
-
-  /* Convert the main body of the chain to gnutls */
-  for (idx = 0; idx < length; ++idx)
-    {
-      cert = gcr_certificate_chain_get_certificate (chain, idx);
-      datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
-      datum.size = n_data;
-
-      gnutls_x509_crt_init (&gcert);
-      if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
-        g_return_if_reached ();
-
-      retval[idx] = gcert;
-    }
-
-  *list = retval;
-  *n_list = length;
-
-  /* See if we have an anchor */
-  if (gcr_certificate_chain_get_status (chain) ==
-          GCR_CERTIFICATE_CHAIN_ANCHORED)
-    {
-      cert = gcr_certificate_chain_get_anchor (chain);
-      g_return_if_fail (cert);
-
-      datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
-      datum.size = n_data;
-
-      gnutls_x509_crt_init (&gcert);
-      if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
-        g_return_if_reached ();
-
-      retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * 1);
-      retval[0] = gcert;
-      *anchors = retval;
-      *n_anchors = 1;
-    }
-}
-
-static void
-free_certificate_list_for_gnutls (gnutls_x509_crt_t *list,
-        guint n_list)
-{
-  guint idx;
-
-  for (idx = 0; idx < n_list; idx++)
-    gnutls_x509_crt_deinit (list[idx]);
-  g_free (list);
-}
-
-static void
 complete_verification (EmpathyTLSVerifier *self)
 {
   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
@@ -213,150 +119,42 @@ abort_verification (EmpathyTLSVerifier *self,
 }
 
 static void
-debug_certificate (GcrCertificate *cert)
-{
-    gchar *subject = gcr_certificate_get_subject_dn (cert);
-    DEBUG ("Certificate: %s", subject);
-    g_free (subject);
-}
-
-static void
-debug_certificate_chain (GcrCertificateChain *chain)
+perform_verification_cb (GObject *object,
+        GAsyncResult *res,
+        gpointer user_data)
 {
-    GEnumClass *enum_class;
-    GEnumValue *enum_value;
-    gint idx, length;
-    GcrCertificate *cert;
-
-    enum_class = G_ENUM_CLASS
-            (g_type_class_peek (GCR_TYPE_CERTIFICATE_CHAIN_STATUS));
-    enum_value = g_enum_get_value (enum_class,
-            gcr_certificate_chain_get_status (chain));
-    length = gcr_certificate_chain_get_length (chain);
-    DEBUG ("Certificate chain: length %u status %s",
-            length, enum_value ? enum_value->value_nick : "XXX");
-
-    for (idx = 0; idx < length; ++idx)
-      {
-        cert = gcr_certificate_chain_get_certificate (chain, idx);
-        debug_certificate (cert);
-      }
-}
+  GError *error = NULL;
 
-static void
-perform_verification (EmpathyTLSVerifier *self,
-        GcrCertificateChain *chain)
-{
-  gboolean ret = FALSE;
-  TpTLSCertificateRejectReason reason =
-    TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
-  gnutls_x509_crt_t *list, *anchors;
-  guint n_list, n_anchors;
-  guint verify_output;
-  gint res;
-  gint i;
-  gboolean matched = FALSE;
+  GTlsCertificateFlags flags;
+  GTlsDatabase *tls_database = G_TLS_DATABASE (object);
+  EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (user_data);
   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
 
-  DEBUG ("Performing verification");
-  debug_certificate_chain (chain);
-
-  list = anchors = NULL;
-  n_list = n_anchors = 0;
-
-  /*
-   * If the first certificate is an pinned certificate then we completely
-   * ignore the rest of the verification process.
-   */
-  if (gcr_certificate_chain_get_status (chain) == GCR_CERTIFICATE_CHAIN_PINNED)
+  flags = g_tls_database_verify_chain_finish (chain, res, &error);
+  if (error != NULL)
     {
-      DEBUG ("Found pinned certificate for %s", priv->hostname);
-      complete_verification (self);
-      goto out;
-  }
+      TpTLSCertificateRejectReason reason;
 
-  build_certificate_list_for_gnutls (chain, &list, &n_list,
-          &anchors, &n_anchors);
-  if (list == NULL || n_list == 0) {
-      g_warn_if_reached ();
-      abort_verification (self, TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
-      goto out;
-  }
-
-  verify_output = 0;
-  res = gnutls_x509_crt_list_verify (list, n_list, anchors, n_anchors,
-           NULL, 0, 0, &verify_output);
-  ret = verification_output_to_reason (res, verify_output, &reason);
+      DEBUG ("Verification of certificate chain failed: %s", error->message);
 
-  DEBUG ("Certificate verification gave result %d with reason %u", ret,
+      reason = verification_output_to_reason (flags);
+      DEBUG ("Certificate verification gave flags %d with reason %u",
+          (gint) flags,
           reason);
 
-  if (!ret) {
-      abort_verification (self, reason);
-      goto out;
-  }
-
-  /* now check if the certificate matches one of the reference identities. */
-  if (priv->reference_identities != NULL)
-    {
-      for (i = 0, matched = FALSE; priv->reference_identities[i] != NULL; ++i)
+      if (flags & G_TLS_CERTIFICATE_BAD_IDENTITY)
         {
-          if (gnutls_x509_crt_check_hostname (list[0],
-                  priv->reference_identities[i]) == 1)
-            {
-              matched = TRUE;
-              break;
-            }
-        }
-    }
-
-  if (!matched)
-    {
-      gchar *certified_hostname;
-
-      certified_hostname = empathy_get_x509_certificate_hostname (list[0]);
-      tp_asv_set_string (priv->details,
-          "expected-hostname", priv->hostname);
-      tp_asv_set_string (priv->details,
-          "certificate-hostname", certified_hostname);
-
-      DEBUG ("Hostname mismatch: got %s but expected %s",
-          certified_hostname, priv->hostname);
-
-      g_free (certified_hostname);
-      abort_verification (self,
-              TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
-      goto out;
-    }
-
-  DEBUG ("Hostname matched");
-  complete_verification (self);
+          tp_asv_set_string (priv->details,
+              "expected-hostname", priv->hostname);
 
- out:
-  free_certificate_list_for_gnutls (list, n_list);
-  free_certificate_list_for_gnutls (anchors, n_anchors);
-}
-
-static void
-perform_verification_cb (GObject *object,
-        GAsyncResult *res,
-        gpointer user_data)
-{
-  GError *error = NULL;
-
-  GcrCertificateChain *chain = GCR_CERTIFICATE_CHAIN (object);
-  EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (user_data);
+          DEBUG ("Hostname mismatch: expected %s", priv->hostname);
+        }
 
-  /* Even if building the chain fails, try verifying what we have */
-  if (!gcr_certificate_chain_build_finish (chain, res, &error))
-    {
-      DEBUG ("Building of certificate chain failed: %s", error->message);
+      abort_verification (self, reason);
       g_clear_error (&error);
     }
 
-  perform_verification (self, chain);
-
-  /* Matches ref when staring chain build */
+  /* Matches ref when staring verify chain */
   g_object_unref (self);
 }
 
@@ -503,10 +301,14 @@ empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
     GAsyncReadyCallback callback,
     gpointer user_data)
 {
-  GcrCertificateChain *chain;
-  GcrCertificate *cert;
   GPtrArray *cert_data;
   GArray *data;
+  GSocketConnectable *identity = NULL;
+  GTlsCertificate *cert = NULL;
+  GTlsCertificate *issuer = NULL;
+  GTlsBackend *tls_backend;
+  GTlsDatabase *tls_database = NULL;
+  GType tls_certificate_type;
   guint idx;
   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
 
@@ -520,19 +322,47 @@ empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
   priv->verify_result = g_simple_async_result_new (G_OBJECT (self),
       callback, user_data, NULL);
 
+  tls_backend = g_tls_backend_get_default ();
+  tls_certificate_type = g_tls_backend_get_certificate_type (tls_backend);
+
   /* Create a certificate chain */
-  chain = gcr_certificate_chain_new ();
-  for (idx = 0; idx < cert_data->len; ++idx) {
+  for (idx = cert_data->len - 1; idx >= 0; ++idx) {
     data = g_ptr_array_index (cert_data, idx);
-    cert = gcr_simple_certificate_new ((guchar *) data->data, data->len);
-    gcr_certificate_chain_add (chain, cert);
-    g_object_unref (cert);
+    cert = g_initable_new (tls_certificate_type,
+        NULL,
+        error,
+        "certificate", (GByteArray *) data,
+        "issuer", issuer,
+        NULL);
+
+    g_clear_object (&issuer);
+    issuer = g_object_ref (cert);
+    g_clear_object (&cert);
   }
 
-  gcr_certificate_chain_build_async (chain, GCR_PURPOSE_SERVER_AUTH, priv->hostname, 0,
-          NULL, perform_verification_cb, g_object_ref (self));
+  g_assert_null (cert);
+  g_assert_true (G_IS_TLS_CERTIFICATE (issuer));
+
+  cert = g_object_ref (issuer);
+  g_clear_object (&issuer);
+
+  tls_database = g_tls_backend_get_default_database ();
+  identity = g_network_address_new (priv->hostname, 0);
+
+  g_tls_database_verify_chain_async (tls_database,
+      cert,
+      G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+      identity,
+      NULL,
+      G_TLS_DATABASE_VERIFY_NONE,
+      NULL,
+      perform_verification_cb,
+      g_object_ref (self));
 
-  g_object_unref (chain);
+  g_clear_object (&cert);
+  g_clear_object (&issuer);
+  g_object_unref (identity);
+  g_object_unref (tls_database);
 }
 
 gboolean


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