--- a/libpurple/certificate.c Tue Mar 07 20:27:59 2017 -0600
+++ b/libpurple/certificate.c Tue Mar 07 20:29:05 2017 -0600
@@ -84,6 +84,29 @@
+get_ascii_fingerprints (PurpleCertificate *crt, gchar **sha1, gchar **sha256) + sha_bin = purple_certificate_get_fingerprint_sha1(crt); + *sha1 = purple_base16_encode_chunked(sha_bin->data, sha_bin->len); + g_byte_array_free(sha_bin, TRUE); + sha_bin = purple_certificate_get_fingerprint_sha256(crt, FALSE); + *sha256 = (sha_bin == NULL) ? g_strdup("(null)") : + purple_base16_encode_chunked(sha_bin->data, sha_bin->len); + g_byte_array_free(sha_bin, TRUE); purple_certificate_verify (PurpleCertificateVerifier *verifier,
const gchar *subject_name, GList *cert_chain,
@@ -388,6 +411,30 @@
+purple_certificate_get_fingerprint_sha256(PurpleCertificate *crt, gboolean sha1_fallback) + PurpleCertificateScheme *scheme; + GByteArray *fpr = NULL; + g_return_val_if_fail(crt, NULL); + g_return_val_if_fail(crt->scheme, NULL); + if (!PURPLE_CERTIFICATE_SCHEME_HAS_FUNC(scheme, get_fingerprint_sha256)) { + /* outdated ssl module? fallback to sha1 and print a warning */ + fpr = purple_certificate_get_fingerprint_sha1(crt); + g_return_val_if_reached(fpr); + fpr = (scheme->get_fingerprint_sha256)(crt); purple_certificate_get_unique_id(PurpleCertificate *crt)
@@ -461,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)
@@ -630,18 +695,13 @@
x509_singleuse_start_verify (PurpleCertificateVerificationRequest *vrq)
+ gchar *sha1_asc, *sha256_asc; - gchar *primary, *secondary;
+ gchar *primary, *secondary, *secondary_extra; PurpleCertificate *crt = (PurpleCertificate *) vrq->cert_chain->data;
- /* Pull out the SHA1 checksum */
- sha_bin = purple_certificate_get_fingerprint_sha1(crt);
- /* Now decode it for display */
- sha_asc = purple_base16_encode_chunked(sha_bin->data,
+ get_ascii_fingerprints(crt, &sha1_asc, &sha256_asc); /* Get the cert Common Name */
cn = purple_certificate_get_subject_name(crt);
@@ -655,14 +715,17 @@
primary = g_strdup_printf(_("%s has presented the following certificate for just-this-once use:"), vrq->subject_name);
- secondary = g_strdup_printf(_("Common name: %s %s\nFingerprint (SHA1): %s"), cn, cn_match, sha_asc);
+ secondary = g_strdup_printf(_("Common name: %s %s\nFingerprint (SHA1): %s"), cn, cn_match, sha1_asc); + /* TODO: make this part of the translatable string above */ + secondary_extra = g_strdup_printf("%s\nSHA256: %s", secondary, sha256_asc); /* Make a semi-pretty display */
purple_request_accept_cancel(
vrq->cb_data, /* TODO: Find what the handle ought to be */
_("Single-use Certificate Verification"),
0, /* Accept by default */
NULL, /* No other user */
@@ -675,8 +738,9 @@
- g_byte_array_free(sha_bin, TRUE);
+ g_free(secondary_extra); @@ -1506,10 +1570,10 @@
- /* Now get SHA1 sums for both and compare them */
+ /* Now get SHA256 sums for both and compare them */ /* TODO: This is not an elegant way to compare certs */
- peer_fpr = purple_certificate_get_fingerprint_sha1(peer_crt);
- cached_fpr = purple_certificate_get_fingerprint_sha1(cached_crt);
+ peer_fpr = purple_certificate_get_fingerprint_sha256(peer_crt, TRUE); + cached_fpr = purple_certificate_get_fingerprint_sha256(cached_crt, TRUE); if (!memcmp(peer_fpr->data, cached_fpr->data, peer_fpr->len)) {
purple_debug_info("certificate/x509/tls_cached",
"Peer cert matched cached\n");
@@ -1616,8 +1680,8 @@
- failing_fpr = purple_certificate_get_fingerprint_sha1(failing_crt);
- ca_fpr = purple_certificate_get_fingerprint_sha1(ca_crt);
+ failing_fpr = purple_certificate_get_fingerprint_sha256(failing_crt, TRUE); + ca_fpr = purple_certificate_get_fingerprint_sha256(ca_crt, TRUE); if (byte_arrays_equal(failing_fpr, ca_fpr)) {
purple_debug_info("certificate/x509/tls_cached",
"Full chain verification failed (probably a bad "
@@ -1699,12 +1763,18 @@
* If the fingerprints don't match, we'll fall back to checking the
- last_fpr = purple_certificate_get_fingerprint_sha1(end_crt);
+ 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_sha1(ca_crt);
+ 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
@@ -1714,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;
@@ -2152,19 +2225,14 @@
purple_certificate_display_x509(PurpleCertificate *crt)
+ gchar *sha1_asc, *sha256_asc; time_t activation, expiration;
gchar *activ_str, *expir_str;
+ gchar *secondary, *secondary_extra; - /* Pull out the SHA1 checksum */
- sha_bin = purple_certificate_get_fingerprint_sha1(crt);
- /* Now decode it for display */
- sha_asc = purple_base16_encode_chunked(sha_bin->data,
+ get_ascii_fingerprints(crt, &sha1_asc, &sha256_asc); /* Get the cert Common Name */
/* TODO: Will break on CA certs */
@@ -2193,20 +2261,23 @@
"Expiration date: %s\n"),
self_signed ? _("(self-signed)") : (issuer_id ? issuer_id : "(null)"),
- sha_asc ? sha_asc : "(null)",
+ sha1_asc ? sha1_asc : "(null)", activ_str ? activ_str : "(null)",
expir_str ? expir_str : "(null)");
+ /* TODO: make this part of the translatable string above */ + secondary_extra = g_strdup_printf("%sSHA256: %s", secondary, sha256_asc); /* Make a semi-pretty display */
purple_notify_info(NULL, /* TODO: Find what the handle ought to be */
_("Certificate Information"),
purple_request_action(NULL, /* TODO: Find what the handle ought to be */
_("Certificate Information"), _("Certificate Information"),
- secondary, 2, NULL, NULL, NULL,
+ secondary_extra, 2, NULL, NULL, NULL, _("View Issuer Certificate"), PURPLE_CALLBACK(display_x509_issuer),
_("Close"), PURPLE_CALLBACK(g_free));
@@ -2219,10 +2290,11 @@
+ g_free(secondary_extra); - g_byte_array_free(sha_bin, TRUE);
void purple_certificate_add_ca_search_path(const char *path)
--- a/libpurple/certificate.h Tue Mar 07 20:27:59 2017 -0600
+++ b/libpurple/certificate.h Tue Mar 07 20:29:05 2017 -0600
@@ -315,9 +315,41 @@
void (* verify_cert)(PurpleCertificateVerificationRequest *vrq, PurpleCertificateInvalidityFlags *flags);
- void (*_purple_reserved3)(void);
+ * The size of the PurpleCertificateScheme. This should always be sizeof(PurpleCertificateScheme). + * This allows adding more functions to this struct without requiring a major version bump. + * PURPLE_CERTIFICATE_SCHEME_HAS_FUNC() should be used for functions after this point. + unsigned long struct_size; + * Retrieves the certificate public key fingerprint using SHA256 + * @param crt Certificate instance + * @return Binary representation of SHA256 hash - must be freed using + 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) \ + (((G_STRUCT_OFFSET(PurpleCertificateScheme, member) < G_STRUCT_OFFSET(PurpleCertificateScheme, struct_size)) \ + || (G_STRUCT_OFFSET(PurpleCertificateScheme, member) < obj->struct_size)) && \ /** A set of operations used to provide logic for verifying a Certificate's
@@ -578,12 +610,26 @@
* Retrieves the certificate public key fingerprint using SHA1.
* @param crt Certificate instance
- * @return Binary representation of the hash. You are responsible for free()ing
+ * @return Binary representation of the hash. You are responsible for freeing + * this with g_byte_array_free(). + * @see purple_base16_encode_chunked() + * @see purple_certificate_get_fingerprint_sha256() +purple_certificate_get_fingerprint_sha1(PurpleCertificate *crt); + * Retrieves the certificate public key fingerprint using SHA256. + * @param crt Certificate instance + * @param sha1_fallback If true, return SHA1 if the SSL module doesn't + * implement SHA256. Otherwise, return NULL. + * @return Binary representation of the hash. You are responsible for freeing + * this with g_byte_array_free(). * @see purple_base16_encode_chunked()
-purple_certificate_get_fingerprint_sha1(PurpleCertificate *crt);
+purple_certificate_get_fingerprint_sha256(PurpleCertificate *crt, gboolean sha1_fallback); * Get a unique identifier for the certificate
@@ -638,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 Tue Mar 07 20:27:59 2017 -0600
+++ b/libpurple/plugins/ssl/ssl-gnutls.c Tue Mar 07 20:29:05 2017 -0600
@@ -1037,9 +1037,9 @@
-x509_sha1sum(PurpleCertificate *crt)
+x509_shasum(PurpleCertificate *crt, gnutls_digest_algorithm_t algo) - size_t hashlen = 20; /* SHA1 hashes are 20 bytes */
+ size_t hashlen = (algo == GNUTLS_DIG_SHA1) ? 20 : 32; size_t tmpsz = hashlen; /* Throw-away variable for GnuTLS to stomp on*/
gnutls_x509_crt_t crt_dat;
GByteArray *hash; /**< Final hash container */
@@ -1051,7 +1051,7 @@
/* Extract the fingerprint */
- 0 == gnutls_x509_crt_get_fingerprint(crt_dat, GNUTLS_DIG_SHA,
+ 0 == gnutls_x509_crt_get_fingerprint(crt_dat, algo, @@ -1065,6 +1065,18 @@
+x509_sha1sum(PurpleCertificate *crt) + return x509_shasum(crt, GNUTLS_DIG_SHA1); +x509_sha256sum(PurpleCertificate *crt) + return x509_shasum(crt, GNUTLS_DIG_SHA256); x509_cert_dn (PurpleCertificate *crt)
@@ -1220,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 */
@@ -1239,8 +1291,9 @@
+ 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 Tue Mar 07 20:27:59 2017 -0600
+++ b/libpurple/plugins/ssl/ssl-nss.c Tue Mar 07 20:29:05 2017 -0600
@@ -881,11 +881,11 @@
-x509_sha1sum(PurpleCertificate *crt)
+x509_shasum(PurpleCertificate *crt, SECOidTag algo) CERTCertificate *crt_dat;
- size_t hashlen = 20; /* Size of an sha1sum */
+ size_t hashlen = (algo == SEC_OID_SHA1) ? 20 : 32; SECItem *derCert; /* DER representation of the cert */
@@ -899,22 +899,34 @@
derCert = &(crt_dat->derCert);
- sha1sum = g_byte_array_sized_new(hashlen);
+ hash = g_byte_array_sized_new(hashlen); /* glib leaves the size as 0 by default */
- sha1sum->len = hashlen;
- st = PK11_HashBuf(SEC_OID_SHA1, sha1sum->data,
+ st = PK11_HashBuf(algo, hash->data, derCert->data, derCert->len);
- g_byte_array_free(sha1sum, TRUE);
+ g_byte_array_free(hash, TRUE); purple_debug_error("nss/x509",
"Error: hashing failed!\n");
+x509_sha1sum(PurpleCertificate *crt) + return x509_shasum(crt, SEC_OID_SHA1); +x509_sha256sum(PurpleCertificate *crt) + return x509_shasum(crt, SEC_OID_SHA256); @@ -1211,7 +1223,9 @@
x509_importcerts_from_file, /* Multiple certificate import function */
x509_register_trusted_tls_cert, /* Register a certificate as trusted for TLS */
x509_verify_cert, /* Verify that the specified cert chain is trusted */
+ sizeof(PurpleCertificateScheme), /* struct_size */ + x509_sha256sum, /* SHA256 fingerprint */ static PurpleSslOps ssl_ops =
--- a/libpurple/protocols/novell/nmrtf.c Tue Mar 07 20:27:59 2017 -0600
+++ b/libpurple/protocols/novell/nmrtf.c Tue Mar 07 20:29:05 2017 -0600
@@ -474,23 +474,23 @@
rtf_pop_state(NMRtfContext *ctx)
- NMRtfStateSave *save_old;
+ NMRtfStateSave *save_old; - if (ctx->saved == NULL)
- return NMRTF_STACK_UNDERFLOW;
+ if (ctx->saved == NULL) + return NMRTF_STACK_UNDERFLOW; save_old = ctx->saved->data;
- ctx->chp = save_old->chp;
- ctx->rds = save_old->rds;
- ctx->ris = save_old->ris;
+ ctx->chp = save_old->chp; + ctx->rds = save_old->rds; + ctx->ris = save_old->ris;
ctx->saved = g_slist_remove_link(ctx->saved, link_old);
g_slist_free_1(link_old);
@@ -671,13 +671,13 @@
rtf_apply_property(NMRtfContext *ctx, NMRtfProperty prop, int val)
- if (ctx->rds == NMRTF_STATE_SKIP) /* If we're skipping text, */
- return NMRTF_OK; /* don't do anything. */
+ if (ctx->rds == NMRTF_STATE_SKIP) /* If we're skipping text, */ + return NMRTF_OK; /* don't do anything. */ /* Need to flush any temporary data before a property change*/
case NMRTF_PROP_FONT_IDX:
@@ -686,9 +686,9 @@