Mercurial > pidgin
changeset 27734:d0654dea0575
certs: Clean up the code a little, since I made it hard to follow.
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Thu, 30 Jul 2009 05:24:44 +0000 |
parents | 82ac0bef7d89 |
children | 29e07dedfacc |
files | libpurple/certificate.c |
diffstat | 1 files changed, 213 insertions(+), 198 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/certificate.c Wed Jul 29 19:16:02 2009 +0000 +++ b/libpurple/certificate.c Thu Jul 30 05:24:44 2009 +0000 @@ -1318,211 +1318,20 @@ g_byte_array_free(cached_fpr, TRUE); } -/* For when we've never communicated with this party before */ -/* TODO: Need ways to specify possibly multiple problems with a cert, or at - least reprioritize them. For example, maybe the signature ought to be - checked BEFORE the hostname checking? - Stu thinks we should check the signature before the name, so we do now. - The above TODO still stands. */ +/* + * This is called from two points in x509_tls_cached_unknown_peer below + * once we've verified the signature chain is valid. Now we need to verify + * the subject name of the certificate. + */ static void -x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq) +x509_tls_cached_check_subject_name(PurpleCertificateVerificationRequest *vrq) { - PurpleCertificatePool *ca, *tls_peers; + PurpleCertificatePool *tls_peers; PurpleCertificate *peer_crt; - PurpleCertificate *failing_crt; GList *chain = vrq->cert_chain; - gboolean chain_validated = FALSE; peer_crt = (PurpleCertificate *) chain->data; - /* TODO: Figure out a way to check for a bad signature, as opposed to - "not self-signed" */ - if ( purple_certificate_signed_by(peer_crt, peer_crt) ) { - gchar *msg; - - purple_debug_info("certificate/x509/tls_cached", - "Certificate for %s is self-signed.\n", - vrq->subject_name); - - /* Prompt the user to authenticate the certificate */ - /* vrq will be completed by user_auth */ - msg = g_strdup_printf(_("The certificate presented by \"%s\" " - "is self-signed. It cannot be " - "automatically checked."), - vrq->subject_name); - - x509_tls_cached_user_auth(vrq,msg); - - g_free(msg); - return; - } /* if (self signed) */ - - /* Next, attempt to verify the last certificate against a CA */ - ca = purple_certificate_find_pool(x509_tls_cached.scheme_name, "ca"); - - /* Next, check that the certificate chain is valid */ - if (purple_certificate_check_signature_chain_with_failing(chain, - &failing_crt)) - chain_validated = TRUE; - else { - /* - * Check if the failing certificate is in the CA store. If it is, then - * consider this fully validated. This works around issues with some - * prominent intermediate CAs whose signature is md5WithRSAEncryption. - * I'm looking at CACert Class 3 here. See #4458 for details. - */ - if (ca) { - gchar *uid = purple_certificate_get_unique_id(failing_crt); - PurpleCertificate *ca_crt = purple_certificate_pool_retrieve(ca, uid); - if (ca_crt != NULL) { - GByteArray *failing_fpr; - GByteArray *ca_fpr; - failing_fpr = purple_certificate_get_fingerprint_sha1(failing_crt); - ca_fpr = purple_certificate_get_fingerprint_sha1(ca_crt); - if (byte_arrays_equal(failing_fpr, ca_fpr)) { - purple_debug_info("certificate/x509/tls_cached", - "Full chain verification failed (probably a bad " - "signature algorithm), but found the last " - "certificate %s in the CA pool.\n", uid); - chain_validated = TRUE; - } - - g_byte_array_free(failing_fpr, TRUE); - g_byte_array_free(ca_fpr, TRUE); - } - - purple_certificate_destroy(ca_crt); - g_free(uid); - } - - /* - * If we get here, either the cert matched the stuff right above - * or it didn't, in which case we give up and complain to the user. - */ - if (!chain_validated) { - /* TODO: Tell the user where the chain broke? */ - /* TODO: This error will hopelessly confuse any - non-elite user. */ - gchar *secondary; - - secondary = g_strdup_printf(_("The certificate chain presented" - " for %s is not valid."), - vrq->subject_name); - - /* TODO: Make this error either block the ensuing SSL - connection error until the user dismisses this one, or - stifle it. */ - purple_notify_error(NULL, /* TODO: Probably wrong. */ - _("SSL Certificate Error"), - _("Invalid certificate chain"), - secondary ); - g_free(secondary); - - /* Okay, we're done here */ - purple_certificate_verify_complete(vrq, - PURPLE_CERTIFICATE_INVALID); - return; - } - } /* if (signature chain not good) */ - - /* If, for whatever reason, there is no Certificate Authority pool - loaded, we will simply present it to the user for checking. */ - if ( !ca ) { - purple_debug_error("certificate/x509/tls_cached", - "No X.509 Certificate Authority pool " - "could be found!\n"); - - /* vrq will be completed by user_auth */ - x509_tls_cached_user_auth(vrq,_("You have no database of root " - "certificates, so this " - "certificate cannot be " - "validated.")); - return; - } - - if (!chain_validated) { - GByteArray *last_fpr, *ca_fpr; - PurpleCertificate *ca_crt, *end_crt; - gchar *ca_id; - - end_crt = g_list_last(chain)->data; - - /* Attempt to look up the last certificate's issuer */ - ca_id = purple_certificate_get_issuer_unique_id(end_crt); - 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 ) { - purple_debug_warning("certificate/x509/tls_cached", - "Certificate Authority with DN='%s' not " - "found. I'll prompt the user, I guess.\n", - ca_id); - g_free(ca_id); - /* vrq will be completed by user_auth */ - x509_tls_cached_user_auth(vrq,_("The root certificate this " - "one claims to be issued by " - "is unknown to Pidgin.")); - return; - } - - g_free(ca_id); - - /* - * 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_fpr = purple_certificate_get_fingerprint_sha1(end_crt); - 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. */ - /* TODO: This error message makes my eyes cross, and I wrote it */ - gchar * secondary = - g_strdup_printf(_("The certificate chain presented by " - "%s does not have a valid digital " - "signature from the Certificate " - "Authority from which it claims to " - "have a signature."), - vrq->subject_name); - - purple_notify_error(NULL, /* TODO: Probably wrong */ - _("SSL Certificate Error"), - _("Invalid certificate authority" - " signature"), - secondary); - g_free(secondary); - - /* Signal "bad cert" */ - purple_certificate_verify_complete(vrq, - PURPLE_CERTIFICATE_INVALID); - - purple_certificate_destroy(ca_crt); - g_byte_array_free(ca_fpr, TRUE); - g_byte_array_free(last_fpr, TRUE); - return; - } /* if (CA signature not good) */ - - g_byte_array_free(ca_fpr, TRUE); - g_byte_array_free(last_fpr, TRUE); - } - /* Last, check that the hostname matches */ if ( ! purple_certificate_check_subject_name(peer_crt, vrq->subject_name) ) { @@ -1571,6 +1380,212 @@ /* Whew! Done! */ purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID); + +} + +/* For when we've never communicated with this party before */ +/* TODO: Need ways to specify possibly multiple problems with a cert, or at + least reprioritize them. + */ +static void +x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq) +{ + PurpleCertificatePool *ca; + PurpleCertificate *peer_crt; + PurpleCertificate *ca_crt, *end_crt; + PurpleCertificate *failing_crt; + GList *chain = vrq->cert_chain; + GByteArray *last_fpr, *ca_fpr; + gchar *ca_id; + + peer_crt = (PurpleCertificate *) chain->data; + + /* TODO: Figure out a way to check for a bad signature, as opposed to + "not self-signed" */ + if ( purple_certificate_signed_by(peer_crt, peer_crt) ) { + gchar *msg; + + purple_debug_info("certificate/x509/tls_cached", + "Certificate for %s is self-signed.\n", + vrq->subject_name); + + /* Prompt the user to authenticate the certificate */ + /* vrq will be completed by user_auth */ + msg = g_strdup_printf(_("The certificate presented by \"%s\" " + "is self-signed. It cannot be " + "automatically checked."), + vrq->subject_name); + + x509_tls_cached_user_auth(vrq,msg); + + g_free(msg); + return; + } /* if (self signed) */ + + /* Next, attempt to verify the last certificate against a CA */ + ca = purple_certificate_find_pool(x509_tls_cached.scheme_name, "ca"); + + /* Next, check that the certificate chain is valid */ + if (!purple_certificate_check_signature_chain_with_failing(chain, + &failing_crt)) + { + gboolean chain_validated = FALSE; + /* + * Check if the failing certificate is in the CA store. If it is, then + * consider this fully validated. This works around issues with some + * prominent intermediate CAs whose signature is md5WithRSAEncryption. + * I'm looking at CACert Class 3 here. See #4458 for details. + */ + if (ca) { + gchar *uid = purple_certificate_get_unique_id(failing_crt); + PurpleCertificate *ca_crt = purple_certificate_pool_retrieve(ca, uid); + if (ca_crt != NULL) { + GByteArray *failing_fpr; + GByteArray *ca_fpr; + failing_fpr = purple_certificate_get_fingerprint_sha1(failing_crt); + ca_fpr = purple_certificate_get_fingerprint_sha1(ca_crt); + if (byte_arrays_equal(failing_fpr, ca_fpr)) { + purple_debug_info("certificate/x509/tls_cached", + "Full chain verification failed (probably a bad " + "signature algorithm), but found the last " + "certificate %s in the CA pool.\n", uid); + chain_validated = TRUE; + } + + g_byte_array_free(failing_fpr, TRUE); + g_byte_array_free(ca_fpr, TRUE); + } + + purple_certificate_destroy(ca_crt); + g_free(uid); + } + + /* + * If we get here, either the cert matched the stuff right above + * or it didn't, in which case we give up and complain to the user. + */ + if (chain_validated) { + x509_tls_cached_check_subject_name(vrq); + } else { + /* TODO: Tell the user where the chain broke? */ + /* TODO: This error will hopelessly confuse any + non-elite user. */ + gchar *secondary; + + secondary = g_strdup_printf(_("The certificate chain presented" + " for %s is not valid."), + vrq->subject_name); + + /* TODO: Make this error either block the ensuing SSL + connection error until the user dismisses this one, or + stifle it. */ + purple_notify_error(NULL, /* TODO: Probably wrong. */ + _("SSL Certificate Error"), + _("Invalid certificate chain"), + secondary ); + g_free(secondary); + + /* Okay, we're done here */ + purple_certificate_verify_complete(vrq, + PURPLE_CERTIFICATE_INVALID); + } + + return; + } /* if (signature chain not good) */ + + /* If, for whatever reason, there is no Certificate Authority pool + loaded, we will simply present it to the user for checking. */ + if ( !ca ) { + purple_debug_error("certificate/x509/tls_cached", + "No X.509 Certificate Authority pool " + "could be found!\n"); + + /* vrq will be completed by user_auth */ + x509_tls_cached_user_auth(vrq,_("You have no database of root " + "certificates, so this " + "certificate cannot be " + "validated.")); + return; + } + + end_crt = g_list_last(chain)->data; + + /* Attempt to look up the last certificate's issuer */ + ca_id = purple_certificate_get_issuer_unique_id(end_crt); + 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 ) { + purple_debug_warning("certificate/x509/tls_cached", + "Certificate Authority with DN='%s' not " + "found. I'll prompt the user, I guess.\n", + ca_id); + g_free(ca_id); + /* vrq will be completed by user_auth */ + x509_tls_cached_user_auth(vrq,_("The root certificate this " + "one claims to be issued by " + "is unknown to Pidgin.")); + return; + } + + g_free(ca_id); + + /* + * 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_fpr = purple_certificate_get_fingerprint_sha1(end_crt); + 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. */ + /* TODO: This error message makes my eyes cross, and I wrote it */ + gchar * secondary = + g_strdup_printf(_("The certificate chain presented by " + "%s does not have a valid digital " + "signature from the Certificate " + "Authority from which it claims to " + "have a signature."), + vrq->subject_name); + + purple_notify_error(NULL, /* TODO: Probably wrong */ + _("SSL Certificate Error"), + _("Invalid certificate authority" + " signature"), + secondary); + g_free(secondary); + + /* Signal "bad cert" */ + purple_certificate_verify_complete(vrq, + PURPLE_CERTIFICATE_INVALID); + + purple_certificate_destroy(ca_crt); + g_byte_array_free(ca_fpr, TRUE); + g_byte_array_free(last_fpr, TRUE); + return; + } /* if (CA signature not good) */ + + g_byte_array_free(ca_fpr, TRUE); + g_byte_array_free(last_fpr, TRUE); + + x509_tls_cached_check_subject_name(vrq); } static void