pidgin: 44e2c86f: Our certificate code is generally design...

nosnilmot at pidgin.im nosnilmot at pidgin.im
Mon Nov 22 21:01:02 EST 2010


----------------------------------------------------------------------
Revision: 44e2c86fa3250a09c12de48785f224c5244d4819
Parent:   0be86888d82fc0d9bd61c1426b73e52196b35817
Author:   nosnilmot at pidgin.im
Date:     11/22/10 20:56:12
Branch:   im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/44e2c86fa3250a09c12de48785f224c5244d4819

Changelog: 

Our certificate code is generally designed around no two CA
certificates having the same DN. Unfortunately this breaks when have
multiple distinct intermediate certificates with the same DN, such as
when we want to validate against MSN intermediate CAs. This change
allows us to verify against any one of multiple CA certificates with
the same DN, instead of relying on a) luck from reading from disk in
the "right" order or b) black magic from NSS reconstructing a valid
chain on connection attempts after CA pool initialization is complete.

Changes against parent 0be86888d82fc0d9bd61c1426b73e52196b35817

  patched  libpurple/certificate.c

-------------- next part --------------
============================================================
--- libpurple/certificate.c	27fbde12deb7cdfe30354e7b120ddd7ad0f57895
+++ libpurple/certificate.c	08da1f3ecd79b6e752e0e915e08dce45e0114b39
@@ -947,6 +947,22 @@ x509_ca_locate_cert(GList *lst, const gc
 	return NULL;
 }
 
+static GSList *
+x509_ca_locate_certs(GList *lst, const gchar *dn)
+{
+	GList *cur;
+	GSList *crts = NULL;
+
+	for (cur = lst; cur; cur = cur->next) {
+		x509_ca_element *el = cur->data;
+		if (purple_strequal(dn, el->dn)) {
+			crts = g_slist_prepend(crts, el);
+		}
+	}
+	return crts;
+}
+
+
 static gboolean
 x509_ca_cert_in_pool(const gchar *id)
 {
@@ -985,6 +1001,31 @@ x509_ca_get_cert(const gchar *id)
 	return crt;
 }
 
+static GSList *
+x509_ca_get_certs(const gchar *id)
+{
+	GSList *crts = NULL, *els = NULL;
+
+	g_return_val_if_fail(x509_ca_lazy_init(), NULL);
+	g_return_val_if_fail(id, NULL);
+
+	/* Search the memory-cached pool */
+	els = x509_ca_locate_certs(x509_ca_certs, id);
+
+	if (els != NULL) {
+		GSList *cur;
+		/* Make a copy of the memcached ones for the function caller
+		   to play with */
+		for (cur = els; cur; cur = cur->next) {
+			x509_ca_element *el = cur->data;
+			crts = g_slist_prepend(crts, purple_certificate_copy(el->crt));
+		}
+		g_slist_free(els);
+	}
+
+	return crts;
+}
+
 static gboolean
 x509_ca_put_cert(const gchar *id, PurpleCertificate *crt)
 {
@@ -1558,7 +1599,9 @@ x509_tls_cached_unknown_peer(PurpleCerti
 	PurpleCertificate *ca_crt, *end_crt;
 	PurpleCertificate *failing_crt;
 	GList *chain = vrq->cert_chain;
+	GSList *ca_crts, *cur;
 	GByteArray *last_fpr, *ca_fpr;
+	gboolean valid = FALSE;
 	gchar *ca_id;
 
 	peer_crt = (PurpleCertificate *) chain->data;
@@ -1646,8 +1689,8 @@ x509_tls_cached_unknown_peer(PurpleCerti
 	purple_debug_info("certificate/x509/tls_cached",
 			  "Checking for a CA with DN=%s\n",
 			  ca_id);
-	ca_crt = purple_certificate_pool_retrieve(ca, ca_id);
-	if ( NULL == ca_crt ) {
+	ca_crts = x509_ca_get_certs(ca_id);
+	if ( NULL == ca_crts ) {
 		flags |= PURPLE_CERTIFICATE_CA_UNKNOWN;
 
 		purple_debug_warning("certificate/x509/tls_cached",
@@ -1677,23 +1720,32 @@ x509_tls_cached_unknown_peer(PurpleCerti
 	 * the list, so here we are.
 	 */
 	last_fpr = purple_certificate_get_fingerprint_sha1(end_crt);
-	ca_fpr   = purple_certificate_get_fingerprint_sha1(ca_crt);
+	for (cur = ca_crts; cur; cur = cur->next) {
+		ca_crt = cur->data;
+		ca_fpr = purple_certificate_get_fingerprint_sha1(ca_crt);
 
-	if ( !byte_arrays_equal(last_fpr, ca_fpr) &&
-			!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
-		   now, a full DN is a little much with which to assault the
-		   user's poor, leaky eyes. */
-		flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
+		if ( byte_arrays_equal(last_fpr, ca_fpr) ||
+				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
+			   now, a full DN is a little much with which to assault the
+			   user's poor, leaky eyes. */
+			valid = TRUE;
+			g_byte_array_free(ca_fpr, TRUE);
+			break;
+		}
+
+		g_byte_array_free(ca_fpr, TRUE);
 	}
 
-	g_byte_array_free(ca_fpr, TRUE);
-	g_byte_array_free(last_fpr, TRUE);
+	if (valid == FALSE)
+		flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
 
-	purple_certificate_destroy(ca_crt);
+	g_slist_foreach(ca_crts, (GFunc)purple_certificate_destroy, NULL);
+	g_slist_free(ca_crts);
+	g_byte_array_free(last_fpr, TRUE);
 
 	x509_tls_cached_check_subject_name(vrq, flags);
 }


More information about the Commits mailing list