pidgin.2.7.7: 1dcd5e71: Apply Stu's specific certificate changes...
rekkanoryo at pidgin.im
rekkanoryo at pidgin.im
Wed Nov 24 01:06:34 EST 2010
----------------------------------------------------------------------
Revision: 1dcd5e71e9221567a04e9ed9b893bd2a4293a8ae
Parent: 587b2bb9fe22fc4c15ba1c1ef8175be06025ea9d
Author: rekkanoryo at pidgin.im
Date: 11/23/10 23:31:54
Branch: im.pidgin.pidgin.2.7.7
URL: http://d.pidgin.im/viewmtn/revision/info/1dcd5e71e9221567a04e9ed9b893bd2a4293a8ae
Changelog:
Apply Stu's specific certificate changes listed below to the 2.7.7 branch.
*** Plucked rev 0be86888d82fc0d9bd61c1426b73e52196b35817 (nosnilmot at pidgin.im):
NSS will not return invalid or irrelevant intermediate certificates
that the server presented as part of the certificate chain. GnuTLS,
however, will return them, which breaks our certificate validation
when the server is mis-configured.
This fixes our GnuTLS SSL plugin to discard any certificate (and
subsequent certs) in the chain if it did not sign the previous
certificate. This allows GnuTLS users to connect to
omega.contacts.msn.com while it is still misconfigured.
*** Plucked rev 44e2c86fa3250a09c12de48785f224c5244d4819 (nosnilmot at pidgin.im):
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 587b2bb9fe22fc4c15ba1c1ef8175be06025ea9d
patched libpurple/certificate.c
patched libpurple/plugins/ssl/ssl-gnutls.c
-------------- next part --------------
============================================================
--- libpurple/plugins/ssl/ssl-gnutls.c 2f98ac68d428449b43bc257c77a9fa96b1f1cf3b
+++ libpurple/plugins/ssl/ssl-gnutls.c 1da6bdaf868c12329feac7b2dd7141f296a984ea
@@ -520,11 +520,18 @@ x509_import_from_datum(const gnutls_datu
/* Forward declarations are fun! */
static PurpleCertificate *
x509_import_from_datum(const gnutls_datum dt, gnutls_x509_crt_fmt mode);
+/* indeed! */
+static gboolean
+x509_certificate_signed_by(PurpleCertificate * crt,
+ PurpleCertificate * issuer);
+static void
+x509_destroy_certificate(PurpleCertificate * crt);
static GList *
ssl_gnutls_get_peer_certificates(PurpleSslConnection * gsc)
{
PurpleSslGnutlsData *gnutls_data = PURPLE_SSL_GNUTLS_DATA(gsc);
+ PurpleCertificate *prvcrt = NULL;
/* List of Certificate instances to return */
GList * peer_certs = NULL;
@@ -550,7 +557,17 @@ ssl_gnutls_get_peer_certificates(PurpleS
/* Append is somewhat inefficient on linked lists, but is easy
to read. If someone complains, I'll change it.
TODO: Is anyone complaining? (Maybe elb?) */
- peer_certs = g_list_append(peer_certs, newcrt);
+ /* only append if previous cert was actually signed by this one.
+ * Thanks Microsoft. */
+ if ((prvcrt == NULL) || x509_certificate_signed_by(prvcrt, newcrt)) {
+ peer_certs = g_list_append(peer_certs, newcrt);
+ prvcrt = newcrt;
+ } else {
+ x509_destroy_certificate(newcrt);
+ purple_debug_error("gnutls", "Dropping further peer certificates "
+ "because the chain is broken!\n");
+ break;
+ }
}
/* cert_list doesn't need free()-ing */
============================================================
--- 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