--- a/ChangeLog Wed Mar 08 22:26:46 2017 -0600
+++ b/ChangeLog Wed Mar 08 22:28:29 2017 -0600
@@ -29,6 +29,7 @@
ended April 30th, 2015. A new protocol plugin has been written,
using a different method, to support Facebook. It can be found at
https://github.com/dequis/purple-facebook/wiki
+ * Fixed gnutls certificate validation errors that mainly affected google (Dequis) * Replaced instances of d.pidgin.im with developer.pidgin.im and updated the
--- a/ChangeLog.API Wed Mar 08 22:26:46 2017 -0600
+++ b/ChangeLog.API Wed Mar 08 22:28:29 2017 -0600
@@ -6,7 +6,9 @@
* PURPLE_MESSAGE_REMOTE_SEND in PurpleMessageFlags, to specify
messages like _SEND that were sent from another location.
* purple_certificate_get_fingerprint_sha256
+ * purple_certificate_compare_pubkeys * PurpleCertificateScheme.get_fingerprint_sha256
+ * PurpleCertificateScheme.compare_pubkeys * PURPLE_CERTIFICATE_SCHEME_HAS_FUNC
--- a/libpurple/certificate.c Wed Mar 08 22:26:46 2017 -0600
+++ b/libpurple/certificate.c Wed Mar 08 22:28:29 2017 -0600
@@ -508,6 +508,24 @@
return (scheme->get_times)(crt, activation, expiration);
+purple_certificate_compare_pubkeys(PurpleCertificate *crt1, PurpleCertificate *crt2) + PurpleCertificateScheme *scheme; + g_return_val_if_fail(crt1 && crt2, FALSE); + g_return_val_if_fail(crt1->scheme && crt2->scheme, FALSE); + g_return_val_if_fail(crt1->scheme == crt2->scheme, FALSE); + if (!(PURPLE_CERTIFICATE_SCHEME_HAS_FUNC(scheme, compare_pubkeys))) { + return (scheme->compare_pubkeys)(crt1, crt2); purple_certificate_pool_mkpath(PurpleCertificatePool *pool, const gchar *id)
@@ -1746,11 +1764,17 @@
last_fpr = purple_certificate_get_fingerprint_sha256(end_crt, TRUE);
+ ca_id = purple_certificate_get_unique_id(end_crt); for (cur = ca_crts; cur; cur = cur->next) {
ca_fpr = purple_certificate_get_fingerprint_sha256(ca_crt, TRUE);
+ ca2_id = purple_certificate_get_unique_id(ca_crt); if ( byte_arrays_equal(last_fpr, ca_fpr) ||
+ (purple_strequal(ca_id, ca2_id) && + purple_certificate_compare_pubkeys(end_crt, ca_crt)) || purple_certificate_signed_by(end_crt, ca_crt) )
/* TODO: If signed_by ever returns a reason, maybe mention
@@ -1760,11 +1784,14 @@
user's poor, leaky eyes. */
g_byte_array_free(ca_fpr, TRUE);
g_byte_array_free(ca_fpr, TRUE);
flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
--- a/libpurple/certificate.h Wed Mar 08 22:26:46 2017 -0600
+++ b/libpurple/certificate.h Wed Mar 08 22:28:29 2017 -0600
@@ -332,6 +332,16 @@
GByteArray * (* get_fingerprint_sha256)(PurpleCertificate *crt);
+ * Compares the public keys of two certificates + * @param crt1 A certificate instance + * @param crt2 Another certificate instance + * @return TRUE if both certificates have the same key, otherwise FALSE + gboolean (* compare_pubkeys)(PurpleCertificate *crt1, PurpleCertificate *crt2); #define PURPLE_CERTIFICATE_SCHEME_HAS_FUNC(obj, member) \
@@ -674,6 +684,20 @@
purple_certificate_get_times(PurpleCertificate *crt, time_t *activation, time_t *expiration);
+ * Compares the public keys of two certificates. + * If the SSL backend does not implement this function, it may return FALSE + * every time. This is the case with the NSS plugin, which doesn't need it. + * @param crt1 A certificate instance + * @param crt2 Another certificate instance + * @return TRUE if both certificates have the same key, otherwise FALSE +purple_certificate_compare_pubkeys(PurpleCertificate *crt1, PurpleCertificate *crt2); /*****************************************************************************/
--- a/libpurple/plugins/ssl/ssl-gnutls.c Wed Mar 08 22:26:46 2017 -0600
+++ b/libpurple/plugins/ssl/ssl-gnutls.c Wed Mar 08 22:28:29 2017 -0600
@@ -1232,6 +1232,46 @@
+/* GNUTLS_KEYID_USE_BEST_KNOWN was added in gnutls 3.4.1, but can't ifdef it + * because it's an enum member. Older versions will ignore it, which means + * using SHA1 instead of SHA256 to compare pubkeys. But hey, not my fault. */ +#if GNUTLS_VERSION_NUMBER < 0x030401 +#define KEYID_FLAG (1<<30) +#define KEYID_FLAG GNUTLS_KEYID_USE_BEST_KNOWN +x509_compare_pubkeys (PurpleCertificate *crt1, PurpleCertificate *crt2) + gnutls_x509_crt_t crt_dat1, crt_dat2; + unsigned char buffer1[64], buffer2[64]; + size1 = size2 = sizeof(buffer1); + g_return_val_if_fail(crt1 && crt2, FALSE); + g_return_val_if_fail(crt1->scheme == &x509_gnutls, FALSE); + g_return_val_if_fail(crt2->scheme == &x509_gnutls, FALSE); + crt_dat1 = X509_GET_GNUTLS_DATA(crt1); + if (gnutls_x509_crt_get_key_id(crt_dat1, KEYID_FLAG, buffer1, &size1) != 0) { + crt_dat2 = X509_GET_GNUTLS_DATA(crt2); + if (gnutls_x509_crt_get_key_id(crt_dat2, KEYID_FLAG, buffer2, &size2) != 0) { + return memcmp(buffer1, buffer2, size1) == 0; /* X.509 certificate operations provided by this plugin */
static PurpleCertificateScheme x509_gnutls = {
"x509", /* Scheme name */
@@ -1253,6 +1293,7 @@
sizeof(PurpleCertificateScheme), /* struct_size */
x509_sha256sum, /* SHA256 fingerprint */
+ x509_compare_pubkeys, /* Compare public keys */ static PurpleSslOps ssl_ops =
--- a/libpurple/plugins/ssl/ssl-nss.c Wed Mar 08 22:26:46 2017 -0600
+++ b/libpurple/plugins/ssl/ssl-nss.c Wed Mar 08 22:28:29 2017 -0600
@@ -1225,6 +1225,7 @@
x509_verify_cert, /* Verify that the specified cert chain is trusted */
sizeof(PurpleCertificateScheme), /* struct_size */
x509_sha256sum, /* SHA256 fingerprint */
static PurpleSslOps ssl_ops =