diff --git a/empathy-fix-certificate-validation.patch b/empathy-fix-certificate-validation.patch new file mode 100644 index 0000000..8228ade --- /dev/null +++ b/empathy-fix-certificate-validation.patch @@ -0,0 +1,1042 @@ +From 73c173dfc567dcdac9c37bdaeea8b1d5ba4ccd5e Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +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.9.3 + + +From 54fb0e0550ba4664934e73e1de8793479b3370b8 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +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.9.3 + + +From 77be5d93d0c438acfa765f94f04fe9ee1f6eba6e Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +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.9.3 + + +From a9ce001092a2f2868c4926817b5946e1e93687d6 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +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 ++ * @author Debarshi Ray + * @author Stef Walter + * + * 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.9.3 + + +From 7df2664ae5996b03c00a48d58c8f05eeba34dd25 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +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 + * Martyn Russell + * Xavier Claessens +- * +- * 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" +@@ -649,37 +645,6 @@ empathy_folks_persona_is_interesting (FolksPersona *persona) + } + + 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, + const gchar *currency) +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 + #include +-#include + #include + #include + #include +@@ -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 +-#include + + #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 + #include + #include + +@@ -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.9.3 + diff --git a/empathy.spec b/empathy.spec index 19e1537..303db2c 100644 --- a/empathy.spec +++ b/empathy.spec @@ -1,6 +1,6 @@ ## Minimum required versions. %global gtk3_min_version 3.9.4 -%global glib2_min_version 2.37.6 +%global glib2_min_version 2.48.0 %global tp_mc_min_version 5.12.0 %global tp_glib_min_version 0.23.2 %global enchant_version 1.2.0 @@ -17,7 +17,7 @@ Name: empathy Version: 3.12.13 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Instant Messaging Client for GNOME License: GPLv2+ @@ -26,6 +26,12 @@ URL: https://wiki.gnome.org/Apps/Empathy Source0: http://download.gnome.org/sources/%{name}/3.12/%{name}-%{version}.tar.xz Source1: %{name}-README.ConnectionManagers +# https://bugzilla.redhat.com/show_bug.cgi?id=1381671 +Patch0: %{name}-fix-certificate-validation.patch + +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: libtool BuildRequires: enchant-devel >= %{enchant_version} BuildRequires: iso-codes-devel BuildRequires: desktop-file-utils @@ -60,6 +66,7 @@ BuildRequires: libsecret-devel >= %{libsecret_version} BuildRequires: gcr-devel >= %{gcr_version} BuildRequires: pkgconfig(gee-0.8) BuildRequires: itstool +BuildRequires: yelp-tools Requires: telepathy-filesystem Requires: telepathy-mission-control >= %{tp_mc_min_version} @@ -84,6 +91,7 @@ It is built on top of the Telepathy framework. %build +autoreconf -f -i %configure \ --disable-schemas-compile \ --disable-silent-rules \ @@ -189,6 +197,9 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : %{_datadir}/adium/message-styles/PlanetGNOME.AdiumMessageStyle/Contents/Resources/main.css %changelog +* Tue Mar 21 2017 Debarshi Ray - 3.12.13-2 +- Fix certificate validation to work without legacy CAs + * Mon Mar 13 2017 Debarshi Ray - 3.12.13-1 - Update to 3.12.13