changeset 27997:4c5f35f2b1ff

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.
author Paul Aurich <paul@darkrain42.org>
date Wed, 22 Jul 2009 06:03:31 +0000
parents b29eac4769e9
children 31905a0d1c9d
files libpurple/certificate.c
diffstat 1 files changed, 37 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- 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) ) {