Blob Blame History Raw
From 7712a50a4c3dfecda6b6401ba5a9dff52a583ecb Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Wed, 15 Mar 2017 20:23:43 +0100
Subject: [PATCH 1/5] tls-verifier: Handle GNUTLS_CERT_REVOKED

... by mapping it to TP_TLS_CERTIFICATE_REJECT_REASON_REVOKED.

https://bugzilla.gnome.org/show_bug.cgi?id=780160
---
 libempathy/empathy-tls-verifier.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libempathy/empathy-tls-verifier.c b/libempathy/empathy-tls-verifier.c
index fcbc559b3f97..8f80b4372de1 100644
--- a/libempathy/empathy-tls-verifier.c
+++ b/libempathy/empathy-tls-verifier.c
@@ -98,6 +98,8 @@ verification_output_to_reason (gint res,
         *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;
 
-- 
2.14.4


From 8c5dc77f406308b77b4a6c7274ff8096091267a6 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Mon, 20 Mar 2017 19:20:11 +0100
Subject: [PATCH 2/5] tests: Fix comment

The existing comment was mistakenly copied from
test_certificate_verify_success_with_full_chain.

This test case is about a certificate that has been pinned against a
specific peer. The mock TLS connection doesn't have the full chain,
but just the leaf-level certificate that has been pinned.

https://bugzilla.gnome.org/show_bug.cgi?id=780160
---
 tests/empathy-tls-test.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/empathy-tls-test.c b/tests/empathy-tls-test.c
index 91b05761f9b9..0752e1b328c5 100644
--- a/tests/empathy-tls-test.c
+++ b/tests/empathy-tls-test.c
@@ -654,8 +654,8 @@ test_certificate_verify_success_with_pinned (Test *test,
   };
 
   /*
-   * In this test the mock TLS connection has a full certificate
-   * chain. We look for an anchor certificate in the chain.
+   * In this test the mock TLS connection has a certificate that has
+   * been pinned for the test-server.empathy.gnome.org peer.
    */
 
   test->mock = mock_tls_certificate_new_and_register (test->dbus,
-- 
2.14.4


From 6fe06a78a7538cefa2333b180d58b330325796ab Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Mon, 20 Mar 2017 19:31:39 +0100
Subject: [PATCH 3/5] tests: Actually test that hostnames of pinned
 certificates are verified

This test case is about ensuring that a pinned certificate won't be
validated if the wrong hostname is used.

If we don't add the pinned certificate to our database, then checks for
pinning are going to fail regardless of the hostname being used. The
correct certificate-hostname pair needs to be in the database to ensure
that the hostnames are being matched as advertised.

https://bugzilla.gnome.org/show_bug.cgi?id=780160
---
 tests/empathy-tls-test.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/empathy-tls-test.c b/tests/empathy-tls-test.c
index 0752e1b328c5..422909e7cc2a 100644
--- a/tests/empathy-tls-test.c
+++ b/tests/empathy-tls-test.c
@@ -695,7 +695,8 @@ test_certificate_verify_pinned_wrong_host (Test *test,
   test->mock = mock_tls_certificate_new_and_register (test->dbus,
           "server-cert.cer", NULL);
 
-  /* Note that we're not adding any place to find root certs */
+  /* We add the collabora directory with the collabora root */
+  add_certificate_to_mock (test, "server-cert.cer", "test-server.empathy.gnome.org");
 
   ensure_certificate_proxy (test);
 
-- 
2.14.4


From f07492434449bcdd74a61aa74596884ef5700d88 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Wed, 15 Mar 2017 20:24:08 +0100
Subject: [PATCH 4/5] 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.

However, GIO doesn't support certificate pinning. Gcr continues to
provide that feature.

Note:

(a) We don't set "certificate-hostname" when we encounter
TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH. The resulting loss
of verbosity in EmpathyTLSDialog is balanced by no longer relying on a
specific encryption library.

(b) glib-networking doesn't differentiate between
GNUTLS_CERT_SIGNER_NOT_FOUND and GNUTLS_CERT_SIGNER_NOT_CA. Hence, we
club them together as TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED and we
no longer return TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED.

(c) Unlike Gcr, GnuTLS doesn't seem to provide a way to load a PKCS#11
module that's built into the code, as opposed to being a shared object.
This makes it hard for us to load our mock PKCS#11 module. Therefore,
we have disabled the test case that relies on using PKCS#11 storage to
complete the certificate chain.

Bump required GLib version to 2.48. We really do need 2.48 because we
rely on the improvements to GIO's GnuTLS backend.

https://bugzilla.gnome.org/show_bug.cgi?id=780160
---
 configure.ac                      |   6 +-
 libempathy/empathy-tls-verifier.c | 419 ++++++++++++++++++--------------------
 libempathy/empathy-tls-verifier.h |   3 +
 tests/empathy-tls-test.c          |  35 +++-
 4 files changed, 232 insertions(+), 231 deletions(-)

diff --git a/configure.ac b/configure.ac
index a427eba3af56..cd6f371de799 100644
--- a/configure.ac
+++ b/configure.ac
@@ -37,9 +37,9 @@ AC_COPYRIGHT([
 FOLKS_REQUIRED=0.9.5
 GNUTLS_REQUIRED=2.8.5
 
-GLIB_REQUIRED=2.37.6
-AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_30, [Ignore post 2.30 deprecations])
-AC_DEFINE(GLIB_VERSION_MAX_ALLOWED, GLIB_VERSION_2_38, [Prevent post 2.38 APIs])
+GLIB_REQUIRED=2.48.0
+AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_48, [Ignore post 2.48 deprecations])
+AC_DEFINE(GLIB_VERSION_MAX_ALLOWED, GLIB_VERSION_2_48, [Prevent post 2.48 APIs])
 
 GTK_REQUIRED=3.9.4
 AC_DEFINE(GDK_VERSION_MIN_REQUIRED, GDK_VERSION_3_8, [Ignore post 3.8 deprecations])
diff --git a/libempathy/empathy-tls-verifier.c b/libempathy/empathy-tls-verifier.c
index 8f80b4372de1..a8306bb569ea 100644
--- a/libempathy/empathy-tls-verifier.c
+++ b/libempathy/empathy-tls-verifier.c
@@ -1,7 +1,9 @@
 /*
  * empathy-tls-verifier.c - Source for EmpathyTLSVerifier
  * Copyright (C) 2010 Collabora Ltd.
+ * Copyright (C) 2017 Red Hat, Inc.
  * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
+ * @author Debarshi Ray <debarshir@gnome.org>
  * @author Stef Walter <stefw@collabora.co.uk>
  *
  * This library is free software; you can redistribute it and/or
@@ -43,6 +45,8 @@ enum {
 };
 
 typedef struct {
+  GTlsCertificate *g_certificate;
+  GTlsDatabase *database;
   TpTLSCertificate *certificate;
   gchar *hostname;
   gchar **reference_identities;
@@ -53,135 +57,86 @@ typedef struct {
   gboolean dispose_run;
 } EmpathyTLSVerifierPriv;
 
-static gboolean
-verification_output_to_reason (gint res,
-    guint verify_output,
-    TpTLSCertificateRejectReason *reason)
+static GTlsCertificate *
+tls_certificate_new_from_der (GPtrArray *data, GError **error)
 {
-  gboolean retval = TRUE;
+  GTlsBackend *tls_backend;
+  GTlsCertificate *cert = NULL;
+  GTlsCertificate *issuer = NULL;
+  GTlsCertificate *retval = NULL;
+  GType tls_certificate_type;
+  gint i;
 
-  g_assert (reason != NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  if (res != GNUTLS_E_SUCCESS)
-    {
-      retval = FALSE;
+  tls_backend = g_tls_backend_get_default ();
+  tls_certificate_type = g_tls_backend_get_certificate_type (tls_backend);
 
-      /* 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;
+  for (i = (gint) data->len - 1; i >= 0; --i)
+    {
+      GArray *cert_data;
+
+      cert_data = g_ptr_array_index (data, i);
+      cert = g_initable_new (tls_certificate_type,
+          NULL,
+          error,
+          "certificate", (GByteArray *) cert_data,
+          "issuer", issuer,
+          NULL);
+
+      if (cert == NULL)
+        goto out;
+
+      g_clear_object (&issuer);
+      issuer = g_object_ref (cert);
+      g_clear_object (&cert);
     }
 
-  /* 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;
+  g_assert_null (cert);
+  g_assert_true (G_IS_TLS_CERTIFICATE (issuer));
 
-      goto out;
-    }
+  retval = g_object_ref (issuer);
 
  out:
+  g_clear_object (&cert);
+  g_clear_object (&issuer);
   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)
+static TpTLSCertificateRejectReason
+verification_output_to_reason (GTlsCertificateFlags flags)
 {
-  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);
+  TpTLSCertificateRejectReason retval;
 
-  *list = *anchors = NULL;
-  *n_list = *n_anchors = 0;
+  g_assert (flags != 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)
+  switch (flags)
     {
-      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;
+      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_GENERIC_ERROR:
+      default:
+        retval = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
+        break;
     }
-}
 
-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);
+  return retval;
 }
 
 static void
@@ -193,6 +148,7 @@ complete_verification (EmpathyTLSVerifier *self)
 
   g_simple_async_result_complete_in_idle (priv->verify_result);
 
+  g_clear_object (&priv->g_certificate);
   tp_clear_object (&priv->verify_result);
 }
 
@@ -209,6 +165,7 @@ abort_verification (EmpathyTLSVerifier *self,
       reason);
   g_simple_async_result_complete_in_idle (priv->verify_result);
 
+  g_clear_object (&priv->g_certificate);
   tp_clear_object (&priv->verify_result);
 }
 
@@ -221,142 +178,137 @@ debug_certificate (GcrCertificate *cert)
 }
 
 static void
-debug_certificate_chain (GcrCertificateChain *chain)
+verify_chain_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;
+  GTlsCertificateFlags flags;
+  GTlsDatabase *tls_database = G_TLS_DATABASE (object);
   gint i;
-  gboolean matched = FALSE;
+  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.
+  /* FIXME: g_tls_database_verify_chain doesn't set the GError if the
+   * certificate chain couldn't be verified. See:
+   * https://bugzilla.gnome.org/show_bug.cgi?id=780310
    */
-  if (gcr_certificate_chain_get_status (chain) == GCR_CERTIFICATE_CHAIN_PINNED)
+  flags = g_tls_database_verify_chain_finish (tls_database, res, &error);
+  if (flags != 0)
     {
-      DEBUG ("Found pinned certificate for %s", priv->hostname);
-      complete_verification (self);
-      goto out;
-  }
-
-  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;
-  }
+      TpTLSCertificateRejectReason reason;
 
-  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);
+      /* We don't pass the identity to g_tls_database_verify. */
+      g_assert_false (flags & G_TLS_CERTIFICATE_BAD_IDENTITY);
 
-  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);
+      g_clear_error (&error);
       goto out;
-  }
+    }
 
-  /* now check if the certificate matches one of the reference identities. */
-  if (priv->reference_identities != NULL)
+  for (i = 0; priv->reference_identities[i] != NULL; i++)
     {
-      for (i = 0, matched = FALSE; priv->reference_identities[i] != NULL; ++i)
-        {
-          if (gnutls_x509_crt_check_hostname (list[0],
-                  priv->reference_identities[i]) == 1)
-            {
-              matched = TRUE;
-              break;
-            }
-        }
+      GSocketConnectable *identity = NULL;
+
+      identity = g_network_address_new (priv->reference_identities[i], 0);
+      flags = g_tls_certificate_verify (priv->g_certificate, identity, NULL);
+
+      g_object_unref (identity);
+      if (flags == 0)
+        break;
     }
 
-  if (!matched)
+  if (flags != 0)
     {
-      gchar *certified_hostname;
+      TpTLSCertificateRejectReason reason;
+
+      g_assert_cmpint (flags, ==, G_TLS_CERTIFICATE_BAD_IDENTITY);
+
+      reason = verification_output_to_reason (flags);
+      DEBUG ("Certificate verification gave flags %d with reason %u",
+          (gint) flags,
+          reason);
 
-      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);
+      /* FIXME: We don't set "certificate-hostname" because
+       * GTlsCertificate doesn't expose the hostname used in the
+       * certificate. We will temporarily lose some verbosity in
+       * EmpathyTLSDialog, but that's balanced by no longer
+       * relying on a specific encryption library.
+       */
+      tp_asv_set_string (priv->details, "expected-hostname", priv->hostname);
 
-      DEBUG ("Hostname mismatch: got %s but expected %s",
-          certified_hostname, priv->hostname);
+      DEBUG ("Hostname mismatch: expected %s", priv->hostname);
 
-      g_free (certified_hostname);
-      abort_verification (self,
-              TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
+      abort_verification (self, reason);
       goto out;
     }
 
-  DEBUG ("Hostname matched");
+  DEBUG ("Verified certificate chain");
   complete_verification (self);
 
- out:
-  free_certificate_list_for_gnutls (list, n_list);
-  free_certificate_list_for_gnutls (anchors, n_anchors);
+out:
+  /* Matches ref when starting verify chain */
+  g_object_unref (self);
 }
 
 static void
-perform_verification_cb (GObject *object,
-        GAsyncResult *res,
-        gpointer user_data)
+is_certificate_pinned_cb (GObject *object,
+    GAsyncResult *res,
+    gpointer user_data)
 {
   GError *error = NULL;
-
-  GcrCertificateChain *chain = GCR_CERTIFICATE_CHAIN (object);
+  GPtrArray *cert_data;
   EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (user_data);
+  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
+
+  if (gcr_trust_is_certificate_pinned_finish (res, &error))
+    {
+      DEBUG ("Found pinned certificate for %s", priv->hostname);
+      complete_verification (self);
+      goto out;
+    }
+
+  /* error is set only when there is an actual failure. It won't be
+   * set, if it successfully determined that the ceritificate was not
+   * pinned. */
+  if (error != NULL)
+    {
+      DEBUG ("Failed to determine if certificate is pinned: %s",
+          error->message);
+      g_clear_error (&error);
+    }
 
-  /* Even if building the chain fails, try verifying what we have */
-  if (!gcr_certificate_chain_build_finish (chain, res, &error))
+  cert_data = tp_tls_certificate_get_cert_data (priv->certificate);
+  priv->g_certificate = tls_certificate_new_from_der (cert_data, &error);
+  if (error != NULL)
     {
-      DEBUG ("Building of certificate chain failed: %s", error->message);
+      DEBUG ("Verification of certificate chain failed: %s", error->message);
+
+      abort_verification (self, TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
       g_clear_error (&error);
+      goto out;
     }
 
-  perform_verification (self, chain);
+  DEBUG ("Performing verification");
+
+  g_tls_database_verify_chain_async (priv->database,
+      priv->g_certificate,
+      G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+      NULL,
+      NULL,
+      G_TLS_DATABASE_VERIFY_NONE,
+      NULL,
+      verify_chain_cb,
+      g_object_ref (self));
 
-  /* Matches ref when staring chain build */
+out:
+  /* Matches ref when starting is certificate pinned */
   g_object_unref (self);
 }
 
@@ -420,6 +372,8 @@ empathy_tls_verifier_dispose (GObject *object)
 
   priv->dispose_run = TRUE;
 
+  g_clear_object (&priv->g_certificate);
+  g_clear_object (&priv->database);
   tp_clear_object (&priv->certificate);
 
   G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->dispose (object);
@@ -443,10 +397,14 @@ static void
 empathy_tls_verifier_init (EmpathyTLSVerifier *self)
 {
   EmpathyTLSVerifierPriv *priv;
+  GTlsBackend *tls_backend;
 
   priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
       EMPATHY_TYPE_TLS_VERIFIER, EmpathyTLSVerifierPriv);
   priv->details = tp_asv_new (NULL, NULL);
+
+  tls_backend = g_tls_backend_get_default ();
+  priv->database = g_tls_backend_get_default_database (tls_backend);
 }
 
 static void
@@ -503,16 +461,15 @@ empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
     GAsyncReadyCallback callback,
     gpointer user_data)
 {
-  GcrCertificateChain *chain;
   GcrCertificate *cert;
   GPtrArray *cert_data;
   GArray *data;
-  guint idx;
   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
 
   DEBUG ("Starting verification");
 
   g_return_if_fail (priv->verify_result == NULL);
+  g_return_if_fail (priv->g_certificate == NULL);
 
   cert_data = tp_tls_certificate_get_cert_data (priv->certificate);
   g_return_if_fail (cert_data);
@@ -520,19 +477,22 @@ empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
   priv->verify_result = g_simple_async_result_new (G_OBJECT (self),
       callback, user_data, NULL);
 
-  /* Create a certificate chain */
-  chain = gcr_certificate_chain_new ();
-  for (idx = 0; idx < cert_data->len; ++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);
-  }
+  /* The first certificate in the chain is for the host */
+  data = g_ptr_array_index (cert_data, 0);
+  cert = gcr_simple_certificate_new ((gpointer) data->data,
+      (gsize) data->len);
+
+  DEBUG ("Checking if certificate is pinned:");
+  debug_certificate (cert);
 
-  gcr_certificate_chain_build_async (chain, GCR_PURPOSE_SERVER_AUTH, priv->hostname, 0,
-          NULL, perform_verification_cb, g_object_ref (self));
+  gcr_trust_is_certificate_pinned_async (cert,
+      GCR_PURPOSE_SERVER_AUTH,
+      priv->hostname,
+      NULL,
+      is_certificate_pinned_cb,
+      g_object_ref (self));
 
-  g_object_unref (chain);
+  g_object_unref (cert);
 }
 
 gboolean
@@ -567,6 +527,21 @@ empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
   return TRUE;
 }
 
+void empathy_tls_verifier_set_database (EmpathyTLSVerifier *self,
+    GTlsDatabase *database)
+{
+  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
+
+  g_return_if_fail (EMPATHY_IS_TLS_VERIFIER (self));
+  g_return_if_fail (G_IS_TLS_DATABASE (database));
+
+  if (database == priv->database)
+    return;
+
+  g_clear_object (&priv->database);
+  priv->database = g_object_ref (database);
+}
+
 void
 empathy_tls_verifier_store_exception (EmpathyTLSVerifier *self)
 {
diff --git a/libempathy/empathy-tls-verifier.h b/libempathy/empathy-tls-verifier.h
index c25d9756cb02..f9bf54a612f2 100644
--- a/libempathy/empathy-tls-verifier.h
+++ b/libempathy/empathy-tls-verifier.h
@@ -72,6 +72,9 @@ gboolean empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
     GHashTable **details,
     GError **error);
 
+void empathy_tls_verifier_set_database (EmpathyTLSVerifier *self,
+    GTlsDatabase *database);
+
 void empathy_tls_verifier_store_exception (EmpathyTLSVerifier *self);
 
 G_END_DECLS
diff --git a/tests/empathy-tls-test.c b/tests/empathy-tls-test.c
index 422909e7cc2a..b8f9ffcbb9af 100644
--- a/tests/empathy-tls-test.c
+++ b/tests/empathy-tls-test.c
@@ -270,6 +270,7 @@ mock_tls_certificate_new_and_register (TpDBusDaemon *dbus,
 
 typedef struct {
   GMainLoop *loop;
+  GTlsDatabase *database;
   TpDBusDaemon *dbus;
   const gchar *dbus_name;
   MockTLSCertificate *mock;
@@ -283,9 +284,18 @@ setup (Test *test, gconstpointer data)
   GError *error = NULL;
   GckModule *module;
   const gchar *trust_uris[2] = { MOCK_SLOT_ONE_URI, NULL };
+  gchar *path = NULL;
 
   test->loop = g_main_loop_new (NULL, FALSE);
 
+  path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
+      "tests",
+      "certificates",
+      "certificate-authority.pem",
+      NULL);
+  test->database = g_tls_file_database_new (path, &error);
+  g_assert_no_error (error);
+
   test->dbus = tp_dbus_daemon_dup (&error);
   g_assert_no_error (error);
 
@@ -301,6 +311,8 @@ setup (Test *test, gconstpointer data)
   gcr_pkcs11_set_modules (NULL);
   gcr_pkcs11_add_module (module);
   gcr_pkcs11_set_trust_lookup_uris (trust_uris);
+
+  g_free (path);
 }
 
 static void
@@ -325,6 +337,8 @@ teardown (Test *test, gconstpointer data)
     g_object_unref (test->cert);
   test->cert = NULL;
 
+  g_clear_object (&test->database);
+
   g_main_loop_unref (test->loop);
   test->loop = NULL;
 
@@ -418,6 +432,8 @@ test_certificate_mock_basics (Test *test,
   g_assert (test->mock->state == TP_TLS_CERTIFICATE_STATE_ACCEPTED);
 }
 
+#if 0
+
 static void
 test_certificate_verify_success_with_pkcs11_lookup (Test *test,
         gconstpointer data G_GNUC_UNUSED)
@@ -459,6 +475,8 @@ test_certificate_verify_success_with_pkcs11_lookup (Test *test,
   g_object_unref (verifier);
 }
 
+#endif
+
 static void
 test_certificate_verify_success_with_full_chain (Test *test,
         gconstpointer data G_GNUC_UNUSED)
@@ -486,6 +504,7 @@ test_certificate_verify_success_with_full_chain (Test *test,
 
   verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
       reference_identities);
+  empathy_tls_verifier_set_database (verifier, test->database);
   empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
   g_main_loop_run (test->loop);
   empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
@@ -525,9 +544,9 @@ test_certificate_verify_root_not_found (Test *test,
   empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
       NULL, &error);
 
-  /* And it should say we're self-signed (oddly enough) */
+  /* And it should say we're untrusted */
   g_assert_error (error, G_IO_ERROR,
-      TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
+      TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED);
 
   g_clear_error (&error);
   g_object_unref (verifier);
@@ -560,9 +579,9 @@ test_certificate_verify_root_not_anchored (Test *test,
   empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
       NULL, &error);
 
-  /* And it should say we're self-signed (oddly enough) */
+  /* And it should say we're untrusted */
   g_assert_error (error, G_IO_ERROR,
-      TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
+      TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED);
 
   g_clear_error (&error);
   g_object_unref (verifier);
@@ -590,6 +609,7 @@ test_certificate_verify_identities_invalid (Test *test,
 
   verifier = empathy_tls_verifier_new (test->cert, "invalid.host.name",
       reference_identities);
+  empathy_tls_verifier_set_database (verifier, test->database);
   empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
   g_main_loop_run (test->loop);
 
@@ -627,6 +647,7 @@ test_certificate_verify_uses_reference_identities (Test *test,
   /* Should be using the reference_identities and not host name for checks */
   verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
       reference_identities);
+  empathy_tls_verifier_set_database (verifier, test->database);
   empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
   g_main_loop_run (test->loop);
 
@@ -708,9 +729,9 @@ test_certificate_verify_pinned_wrong_host (Test *test,
   empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
       NULL, &error);
 
-  /* And it should say we're self-signed */
+  /* And it should say we're untrusted */
   g_assert_error (error, G_IO_ERROR,
-      TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
+      TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED);
 
   g_clear_error (&error);
   g_object_unref (verifier);
@@ -727,8 +748,10 @@ main (int argc,
 
   g_test_add ("/tls/certificate_basics", Test, NULL,
           setup, test_certificate_mock_basics, teardown);
+#if 0
   g_test_add ("/tls/certificate_verify_success_with_pkcs11_lookup", Test, NULL,
           setup, test_certificate_verify_success_with_pkcs11_lookup, teardown);
+#endif
   g_test_add ("/tls/certificate_verify_success_with_full_chain", Test, NULL,
           setup, test_certificate_verify_success_with_full_chain, teardown);
   g_test_add ("/tls/certificate_verify_root_not_found", Test, NULL,
-- 
2.14.4


From a5ef984c6219070253f382d41101de9f904563c6 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 16 Mar 2017 19:50:40 +0100
Subject: [PATCH 5/5] Remove the GnuTLS dependency

GIO, backed by glib-networking, has everything that we need.

https://bugzilla.gnome.org/show_bug.cgi?id=780160
---
 configure.ac               |  2 --
 libempathy/empathy-utils.c | 35 -----------------------------------
 libempathy/empathy-utils.h |  3 ---
 src/empathy-auth-client.c  |  2 --
 tests/empathy-tls-test.c   |  2 --
 5 files changed, 44 deletions(-)

diff --git a/configure.ac b/configure.ac
index cd6f371de799..a1cd48687e27 100644
--- a/configure.ac
+++ b/configure.ac
@@ -35,7 +35,6 @@ AC_COPYRIGHT([
 
 # Hardp deps
 FOLKS_REQUIRED=0.9.5
-GNUTLS_REQUIRED=2.8.5
 
 GLIB_REQUIRED=2.48.0
 AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_48, [Ignore post 2.48 deprecations])
@@ -219,7 +218,6 @@ PKG_CHECK_MODULES(EMPATHY,
    gio-2.0 >= $GLIB_REQUIRED
    gio-unix-2.0 >= $GLIB_REQUIRED
    libsecret-1 >= $LIBSECRET_REQUIRED
-   gnutls >= $GNUTLS_REQUIRED
    gmodule-export-2.0
    gobject-2.0
    gsettings-desktop-schemas
diff --git a/libempathy/empathy-utils.c b/libempathy/empathy-utils.c
index e8349373639f..88e28b8dd92b 100644
--- a/libempathy/empathy-utils.c
+++ b/libempathy/empathy-utils.c
@@ -20,10 +20,6 @@
  * Authors: Richard Hult <richard@imendio.com>
  *          Martyn Russell <martyn@imendio.com>
  *          Xavier Claessens <xclaesse@gmail.com>
- *
- * Some snippets are taken from GnuTLS 2.8.6, which is distributed under the
- * same GNU Lesser General Public License 2.1 (or later) version. See
- * empathy_get_x509_certified_hostname ().
  */
 
 #include "config.h"
@@ -648,37 +644,6 @@ empathy_folks_persona_is_interesting (FolksPersona *persona)
   return TRUE;
 }
 
-gchar *
-empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert)
-{
-  gchar dns_name[256];
-  gsize dns_name_size;
-  gint idx;
-  gint res = 0;
-
-  /* this snippet is taken from GnuTLS.
-   * see gnutls/lib/x509/rfc2818_hostname.c
-   */
-  for (idx = 0; res >= 0; idx++)
-    {
-      dns_name_size = sizeof (dns_name);
-      res = gnutls_x509_crt_get_subject_alt_name (cert, idx,
-          dns_name, &dns_name_size, NULL);
-
-      if (res == GNUTLS_SAN_DNSNAME || res == GNUTLS_SAN_IPADDRESS)
-        return g_strndup (dns_name, dns_name_size);
-    }
-
-  dns_name_size = sizeof (dns_name);
-  res = gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME,
-      0, 0, dns_name, &dns_name_size);
-
-  if (res >= 0)
-    return g_strndup (dns_name, dns_name_size);
-
-  return NULL;
-}
-
 gchar *
 empathy_format_currency (gint amount,
     guint scale,
diff --git a/libempathy/empathy-utils.h b/libempathy/empathy-utils.h
index a9ff0d89060d..deb3ae87b7aa 100644
--- a/libempathy/empathy-utils.h
+++ b/libempathy/empathy-utils.h
@@ -27,7 +27,6 @@
 
 #include <glib.h>
 #include <glib-object.h>
-#include <gnutls/x509.h>
 #include <libxml/tree.h>
 #include <folks/folks.h>
 #include <folks/folks-telepathy.h>
@@ -85,8 +84,6 @@ gboolean empathy_connection_can_group_personas (TpConnection *connection,
 						FolksIndividual *individual);
 gboolean empathy_folks_persona_is_interesting (FolksPersona *persona);
 
-gchar * empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert);
-
 gchar *empathy_format_currency (gint amount,
     guint scale,
     const gchar *currency);
diff --git a/src/empathy-auth-client.c b/src/empathy-auth-client.c
index 3ee478d3e29c..6b6482d4b23d 100644
--- a/src/empathy-auth-client.c
+++ b/src/empathy-auth-client.c
@@ -22,7 +22,6 @@
 #include "config.h"
 
 #include <glib/gi18n.h>
-#include <gnutls/gnutls.h>
 
 #include "empathy-auth-factory.h"
 #include "empathy-bad-password-dialog.h"
@@ -297,7 +296,6 @@ main (int argc,
   g_option_context_free (context);
 
   empathy_gtk_init ();
-  gnutls_global_init ();
   g_set_application_name (_("Empathy authentication client"));
 
   /* Make empathy and empathy-auth-client appear as the same app in
diff --git a/tests/empathy-tls-test.c b/tests/empathy-tls-test.c
index b8f9ffcbb9af..9b62ae4e0ec7 100644
--- a/tests/empathy-tls-test.c
+++ b/tests/empathy-tls-test.c
@@ -1,6 +1,5 @@
 #include "config.h"
 
-#include <gnutls/gnutls.h>
 #include <telepathy-glib/telepathy-glib.h>
 #include <telepathy-glib/telepathy-glib-dbus.h>
 
@@ -744,7 +743,6 @@ main (int argc,
   int result;
 
   test_init (argc, argv);
-  gnutls_global_init ();
 
   g_test_add ("/tls/certificate_basics", Test, NULL,
           setup, test_certificate_mock_basics, teardown);
-- 
2.14.4