changeset 30960:27c56e6b5fa6

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.
author Stu Tomlinson <stu@nosnilmot.com>
date Tue, 23 Nov 2010 01:56:12 +0000
parents 43af903bd816
children 04ffd3b52283
files libpurple/certificate.c
diffstat 1 files changed, 67 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/certificate.c	Tue Nov 23 01:50:30 2010 +0000
+++ b/libpurple/certificate.c	Tue Nov 23 01:56:12 2010 +0000
@@ -947,6 +947,22 @@
 	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 @@
 	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 @@
 	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 @@
 	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,24 +1720,33 @@
 	 * 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);
+	if (valid == FALSE)
+		flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
+
+	g_slist_foreach(ca_crts, (GFunc)purple_certificate_destroy, NULL);
+	g_slist_free(ca_crts);
 	g_byte_array_free(last_fpr, TRUE);
 
-	purple_certificate_destroy(ca_crt);
-
 	x509_tls_cached_check_subject_name(vrq, flags);
 }