# HG changeset patch # User Paul Aurich # Date 1248242611 0 # Node ID 4c5f35f2b1ff5a27533434f83fc85de09e96eca1 # Parent b29eac4769e99d6ab9b3684e3e0c4e7168d90f73 A better solution for verifying certificate chains with NSS 3.12.3. Instead of allowing weak certificate algorithms all over the place, which is what the NSS flag we are enabling, short-circuit a verification a step early if the fingerprint of the last-checked certificate matches its signer's certificate (retrieved from the trusted CA pool). Closes #9360. diff -r b29eac4769e9 -r 4c5f35f2b1ff libpurple/certificate.c --- a/libpurple/certificate.c Wed Jul 22 04:15:02 2009 +0000 +++ b/libpurple/certificate.c Wed Jul 22 06:03:31 2009 +0000 @@ -293,6 +293,16 @@ return (scheme->export_certificate)(filename, crt); } +static gboolean +byte_arrays_equal(const GByteArray *array1, const GByteArray *array2) +{ + g_return_val_if_fail(array1 != NULL, FALSE); + g_return_val_if_fail(array2 != NULL, FALSE); + + return (array1->len == array2->len) && + (0 == memcmp(array1->data, array2->data, array1->len)); +} + GByteArray * purple_certificate_get_fingerprint_sha1(PurpleCertificate *crt) { @@ -1306,6 +1316,7 @@ GList *chain = vrq->cert_chain; GList *last; gchar *ca_id; + GByteArray *last_fingerprint, *ca_fingerprint; peer_crt = (PurpleCertificate *) chain->data; @@ -1399,8 +1410,26 @@ g_free(ca_id); - /* Check the signature */ - if ( !purple_certificate_signed_by(end_crt, ca_crt) ) { + /* + * Check the fingerprints; if they match, then this certificate *is* one + * of the designated "trusted roots", and we don't need to verify the + * signature. This is good because some of the older roots are self-signed + * with bad hash algorithms that we don't want to allow in any other + * circumstances (one of Verisign's root CAs is self-signed with MD2). + * + * If the fingerprints don't match, we'll fall back to checking the + * signature. + * + * GnuTLS doesn't seem to include the final root in the verification + * list, so this check will never succeed. NSS *does* include it in + * the list, so here we are. + */ + last_fingerprint = purple_certificate_get_fingerprint_sha1(end_crt); + ca_fingerprint = purple_certificate_get_fingerprint_sha1(ca_crt); + + if ( !byte_arrays_equal(last_fingerprint, ca_fingerprint) && + !purple_certificate_signed_by(end_crt, ca_crt) ) + { /* TODO: If signed_by ever returns a reason, maybe mention that, too. */ /* TODO: Also mention the CA involved. While I could do this @@ -1425,9 +1454,15 @@ /* Signal "bad cert" */ purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID); + + g_byte_array_free(ca_fingerprint, TRUE); + g_byte_array_free(last_fingerprint, TRUE); return; } /* if (CA signature not good) */ + g_byte_array_free(ca_fingerprint, TRUE); + g_byte_array_free(last_fingerprint, TRUE); + /* Last, check that the hostname matches */ if ( ! purple_certificate_check_subject_name(peer_crt, vrq->subject_name) ) {