Fix basic constraints checking for both our SSL plugins. release-2.x.y
authorMark Doliner <mark@kingant.net>
Sun, 12 Oct 2014 23:28:58 -0700
branchrelease-2.x.y
changeset2e4475087f04 pushlog
parent db951baf06ac
child c890736a8d5a
Fix basic constraints checking for both our SSL plugins.

This was reported to our private security@pidgin.im mailing list
by an anonymous person and Jacob Appelbaum of the Tor project.

The general problem is described by Moxie Marlinspike here:
http://www.thoughtcrime.org/ie-ssl-chain.txt

Turns out BOTH of our SSL/TLS plugins are vulnerable to this. It allows
a malicious man-in-the-middle to impersonate an https server accessed by
Pidgin.

The fix for this was difficult. We'd really like to just delegate all cert
validate to the NSS or GnuTLS plugins and not do any of it ourselves, because
they're experts and we're not. And this is essentially the change we made for
NSS. However, this was difficult for GnuTLS because we need a context that we
don't have access to in the right function. We could have done it, but it
would have been a little hacky. So for our GnuTLS plugin we added basic
constraints checking ourselves. In Pidgin 3.0.0 would should clean this up
and remove a lot of internal cert validation and ALWAYS delegate to the
SSL/TLS library.

The NSS parts of this patch were written by Kai Engert and Daniel Atallah.
I wrote the GnuTLS parts.

We'll be requesting a CVE number for this.

Also, my thanks to Jacob Appelbaum and Moxie Marlinspike for their efforts
over many years to improve the security of the software that we use on a
daily basis. They are both stand-out citizens who have made contributions
to protect the privacy of all internet users. Thanks, guys!
COPYRIGHT
ChangeLog
libpurple/certificate.c
libpurple/certificate.h
libpurple/plugins/ssl/ssl-gnutls.c
libpurple/plugins/ssl/ssl-nss.c
      1.1 --- a/COPYRIGHT
      1.2 +++ b/COPYRIGHT
      1.3 @@ -161,6 +161,7 @@
      1.4  Markus Elfring
      1.5  Nelson Elhage
      1.6  Ignacio J. Elia
      1.7 +Kai Engert
      1.8  Brian Enigma
      1.9  Mattias Eriksson
     1.10  Pat Erley
      2.1 --- a/ChangeLog
      2.2 +++ b/ChangeLog
      2.3 @@ -1,9 +1,17 @@
      2.4  Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
      2.5  
      2.6 -version 2.10.10 (?/?/?):
      2.7 +version 2.10.10 (10/22/14):
      2.8  	General:
      2.9 -	* Allow and prefer TLS 1.2 and 1.1 when using libnss. (Elrond and
     2.10 -	  Ashish Gupta) (#15909)
     2.11 +	* Check the basic constraints extension when validating SSL/TLS
     2.12 +	  certificates. This fixes a security hole that allowed a malicious
     2.13 +	  man-in-the-middle to impersonate an IM server or any other https
     2.14 +	  endpoint. This affected both the NSS and GnuTLS plugins. (Discovered
     2.15 +	  by an anonymous person and Jacob Appelbaum of the Tor Project, with
     2.16 +	  thanks to Moxie Marlinspike for first publishing about this type of
     2.17 +	  vulnerability. Thanks to Kai Engert for guidance and for some of the
     2.18 +	  NSS changes).
     2.19 +	* Allow and prefer TLS 1.2 and 1.1 when using the NSS plugin for SSL.
     2.20 +	  (Elrond and Ashish Gupta) (#15909)
     2.21  
     2.22  	libpurple3 compatibility:
     2.23  	* Encrypted account passwords are preserved until the new one is set.
      3.1 --- a/libpurple/certificate.c
      3.2 +++ b/libpurple/certificate.c
      3.3 @@ -41,50 +41,6 @@
      3.4  /** List of registered Pools */
      3.5  static GList *cert_pools = NULL;
      3.6  
      3.7 -/*
      3.8 - * TODO: Merge this with PurpleCertificateVerificationStatus for 3.0.0 */
      3.9 -typedef enum {
     3.10 -	PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1,
     3.11 -
     3.12 -	/* Not an error */
     3.13 -	PURPLE_CERTIFICATE_NO_PROBLEMS = 0,
     3.14 -
     3.15 -	/* Non-fatal */
     3.16 -	PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF,
     3.17 -
     3.18 -	/* The certificate is self-signed. */
     3.19 -	PURPLE_CERTIFICATE_SELF_SIGNED = 0x01,
     3.20 -
     3.21 -	/* The CA is not in libpurple's pool of certificates. */
     3.22 -	PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02,
     3.23 -
     3.24 -	/* The current time is before the certificate's specified
     3.25 -	 * activation time.
     3.26 -	 */
     3.27 -	PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04,
     3.28 -
     3.29 -	/* The current time is after the certificate's specified expiration time */
     3.30 -	PURPLE_CERTIFICATE_EXPIRED = 0x08,
     3.31 -
     3.32 -	/* The certificate's subject name doesn't match the expected */
     3.33 -	PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10,
     3.34 -
     3.35 -	/* No CA pool was found. This shouldn't happen... */
     3.36 -	PURPLE_CERTIFICATE_NO_CA_POOL = 0x20,
     3.37 -
     3.38 -	/* Fatal */
     3.39 -	PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000,
     3.40 -
     3.41 -	/* The signature chain could not be validated. Due to limitations in the
     3.42 -	 * the current API, this also indicates one of the CA certificates in the
     3.43 -	 * chain is expired (or not yet activated). FIXME 3.0.0 */
     3.44 -	PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000,
     3.45 -
     3.46 -	/* The signature has been revoked. */
     3.47 -	PURPLE_CERTIFICATE_REVOKED = 0x20000,
     3.48 -
     3.49 -	PURPLE_CERTIFICATE_LAST = 0x40000,
     3.50 -} PurpleCertificateInvalidityFlags;
     3.51  
     3.52  static const gchar *
     3.53  invalidity_reason_to_string(PurpleCertificateInvalidityFlags flag)
     3.54 @@ -776,10 +732,11 @@
     3.55  /** Used for lazy initialization purposes. */
     3.56  static gboolean x509_ca_initialized = FALSE;
     3.57  
     3.58 -/** Adds a certificate to the in-memory cache, doing nothing else */
     3.59 +/** Adds a certificate to the in-memory cache, and mark it as trusted */
     3.60  static gboolean
     3.61  x509_ca_quiet_put_cert(PurpleCertificate *crt)
     3.62  {
     3.63 +	gboolean ret;
     3.64  	x509_ca_element *el;
     3.65  
     3.66  	/* lazy_init calls this function, so calling lazy_init here is a
     3.67 @@ -791,12 +748,20 @@
     3.68  	/* TODO: Perhaps just check crt->scheme->name instead? */
     3.69  	g_return_val_if_fail(crt->scheme == purple_certificate_find_scheme(x509_ca.scheme_name), FALSE);
     3.70  
     3.71 -	el = g_new0(x509_ca_element, 1);
     3.72 -	el->dn = purple_certificate_get_unique_id(crt);
     3.73 -	el->crt = purple_certificate_copy(crt);
     3.74 -	x509_ca_certs = g_list_prepend(x509_ca_certs, el);
     3.75 +	ret = TRUE;
     3.76  
     3.77 -	return TRUE;
     3.78 +	if (crt->scheme->register_trusted_tls_cert) {
     3.79 +		ret = (crt->scheme->register_trusted_tls_cert)(crt, TRUE);
     3.80 +	}
     3.81 +
     3.82 +	if (ret) {
     3.83 +		el = g_new0(x509_ca_element, 1);
     3.84 +		el->dn = purple_certificate_get_unique_id(crt);
     3.85 +		el->crt = purple_certificate_copy(crt);
     3.86 +		x509_ca_certs = g_list_prepend(x509_ca_certs, el);
     3.87 +	}
     3.88 +
     3.89 +	return ret;
     3.90  }
     3.91  
     3.92  /* Since the libpurple CertificatePools get registered before plugins are
     3.93 @@ -927,6 +892,7 @@
     3.94  	g_list_free(x509_ca_certs);
     3.95  	x509_ca_certs = NULL;
     3.96  	x509_ca_initialized = FALSE;
     3.97 +	/** TODO: the cert store in the SSL implementation wouldn't be cleared by this */
     3.98  	g_list_foreach(x509_ca_paths, (GFunc)g_free, NULL);
     3.99  	g_list_free(x509_ca_paths);
    3.100  	x509_ca_paths = NULL;
    3.101 @@ -1185,6 +1151,10 @@
    3.102  	keypath = purple_certificate_pool_mkpath(&x509_tls_peers, id);
    3.103  	ret = purple_certificate_export(keypath, crt);
    3.104  
    3.105 +	if (crt->scheme->register_trusted_tls_cert) {
    3.106 +		ret = (crt->scheme->register_trusted_tls_cert)(crt, FALSE);
    3.107 +	}
    3.108 +
    3.109  	g_free(keypath);
    3.110  	return ret;
    3.111  }
    3.112 @@ -1606,6 +1576,14 @@
    3.113  
    3.114  	peer_crt = (PurpleCertificate *) chain->data;
    3.115  
    3.116 +	if (peer_crt->scheme->verify_cert) {
    3.117 +		/** Make sure we've loaded the CA certs (which causes NSS to trust them) */
    3.118 +		g_return_if_fail(x509_ca_lazy_init());
    3.119 +		peer_crt->scheme->verify_cert(vrq, &flags);
    3.120 +		x509_tls_cached_complete(vrq, flags);
    3.121 +		return;
    3.122 +	}
    3.123 +
    3.124  	/* TODO: Figure out a way to check for a bad signature, as opposed to
    3.125  	   "not self-signed" */
    3.126  	if ( purple_certificate_signed_by(peer_crt, peer_crt) ) {
      4.1 --- a/libpurple/certificate.h
      4.2 +++ b/libpurple/certificate.h
      4.3 @@ -46,6 +46,51 @@
      4.4  	PURPLE_CERTIFICATE_VALID = 1
      4.5  } PurpleCertificateVerificationStatus;
      4.6  
      4.7 +/*
      4.8 + * TODO: Merge this with PurpleCertificateVerificationStatus for 3.0.0 */
      4.9 +typedef enum {
     4.10 +	PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1,
     4.11 +
     4.12 +	/* Not an error */
     4.13 +	PURPLE_CERTIFICATE_NO_PROBLEMS = 0,
     4.14 +
     4.15 +	/* Non-fatal */
     4.16 +	PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF,
     4.17 +
     4.18 +	/* The certificate is self-signed. */
     4.19 +	PURPLE_CERTIFICATE_SELF_SIGNED = 0x01,
     4.20 +
     4.21 +	/* The CA is not in libpurple's pool of certificates. */
     4.22 +	PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02,
     4.23 +
     4.24 +	/* The current time is before the certificate's specified
     4.25 +	 * activation time.
     4.26 +	 */
     4.27 +	PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04,
     4.28 +
     4.29 +	/* The current time is after the certificate's specified expiration time */
     4.30 +	PURPLE_CERTIFICATE_EXPIRED = 0x08,
     4.31 +
     4.32 +	/* The certificate's subject name doesn't match the expected */
     4.33 +	PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10,
     4.34 +
     4.35 +	/* No CA pool was found. This shouldn't happen... */
     4.36 +	PURPLE_CERTIFICATE_NO_CA_POOL = 0x20,
     4.37 +
     4.38 +	/* Fatal */
     4.39 +	PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000,
     4.40 +
     4.41 +	/* The signature chain could not be validated. Due to limitations in the
     4.42 +	 * the current API, this also indicates one of the CA certificates in the
     4.43 +	 * chain is expired (or not yet activated). FIXME 3.0.0 */
     4.44 +	PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000,
     4.45 +
     4.46 +	/* The signature has been revoked. */
     4.47 +	PURPLE_CERTIFICATE_REVOKED = 0x20000,
     4.48 +
     4.49 +	PURPLE_CERTIFICATE_LAST = 0x40000,
     4.50 +} PurpleCertificateInvalidityFlags;
     4.51 +
     4.52  typedef struct _PurpleCertificate PurpleCertificate;
     4.53  typedef struct _PurpleCertificatePool PurpleCertificatePool;
     4.54  typedef struct _PurpleCertificateScheme PurpleCertificateScheme;
     4.55 @@ -197,7 +242,8 @@
     4.56  	 */
     4.57  	void (* destroy_certificate)(PurpleCertificate * crt);
     4.58  
     4.59 -	/** Find whether "crt" has a valid signature from issuer "issuer"
     4.60 +	/** Find whether "crt" has a valid signature from "issuer," including
     4.61 +	 * appropriate values for the CA flag in the basic constraints extension.
     4.62  	 *  @see purple_certificate_signed_by() */
     4.63  	gboolean (*signed_by)(PurpleCertificate *crt, PurpleCertificate *issuer);
     4.64  	/**
     4.65 @@ -258,8 +304,17 @@
     4.66  	 */
     4.67  	GSList * (* import_certificates)(const gchar * filename);
     4.68  
     4.69 -	void (*_purple_reserved1)(void);
     4.70 -	void (*_purple_reserved2)(void);
     4.71 +	/**
     4.72 +	 * Register a certificate as "trusted."
     4.73 +	 */
     4.74 +	gboolean (* register_trusted_tls_cert)(PurpleCertificate *crt, gboolean ca);
     4.75 +
     4.76 +	/**
     4.77 +	 * Verify that a certificate is valid, performing all necessary checks
     4.78 +	 * including date range, valid cert chain, recognized and valid CAs, etc.
     4.79 +	 */
     4.80 +	void (* verify_cert)(PurpleCertificateVerificationRequest *vrq, PurpleCertificateInvalidityFlags *flags);
     4.81 +
     4.82  	void (*_purple_reserved3)(void);
     4.83  };
     4.84  
      5.1 --- a/libpurple/plugins/ssl/ssl-gnutls.c
      5.2 +++ b/libpurple/plugins/ssl/ssl-gnutls.c
      5.3 @@ -925,7 +925,7 @@
      5.4  	crt_dat = X509_GET_GNUTLS_DATA(crt);
      5.5  	issuer_dat = X509_GET_GNUTLS_DATA(issuer);
      5.6  
      5.7 -	/* First, let's check that crt.issuer is actually issuer */
      5.8 +	/* Ensure crt issuer matches the name on the issuer cert. */
      5.9  	ret = gnutls_x509_crt_check_issuer(crt_dat, issuer_dat);
     5.10  	if (ret <= 0) {
     5.11  
     5.12 @@ -954,6 +954,41 @@
     5.13  		return FALSE;
     5.14  	}
     5.15  
     5.16 +	/* Check basic constraints extension (if it exists then the CA flag must
     5.17 +	   be set to true, and it must exist for certs with version 3 or higher. */
     5.18 +	ret = gnutls_x509_crt_get_basic_constraints(issuer_dat, NULL, NULL, NULL);
     5.19 +	if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
     5.20 +		if (gnutls_x509_crt_get_version(issuer_dat) >= 3) {
     5.21 +			/* Reject cert (no basic constraints and cert version is >= 3). */
     5.22 +			gchar *issuer_id = purple_certificate_get_unique_id(issuer);
     5.23 +			purple_debug_info("gnutls/x509", "Rejecting cert because the "
     5.24 +					"basic constraints extension is missing from issuer cert "
     5.25 +					"for %s. The basic constraints extension is required on "
     5.26 +					"all version 3 or higher certs (this cert is version %d).",
     5.27 +					issuer_id ? issuer_id : "(null)",
     5.28 +					gnutls_x509_crt_get_version(issuer_dat));
     5.29 +			g_free(issuer_id);
     5.30 +			return FALSE;
     5.31 +		} else {
     5.32 +			/* Allow cert (no basic constraints and cert version is < 3). */
     5.33 +			purple_debug_info("gnutls/x509", "Basic constraint extension is "
     5.34 +					"missing from issuer cert for %s. Allowing this because "
     5.35 +					"the cert is version %d and the basic constraints "
     5.36 +					"extension is only required for version 3 or higher "
     5.37 +					"certs.", issuer_id ? issuer_id : "(null)",
     5.38 +					gnutls_x509_crt_get_version(issuer_dat));
     5.39 +		}
     5.40 +	} else if (ret <= 0) {
     5.41 +		/* Reject cert (CA flag is false in basic constraints). */
     5.42 +		gchar *issuer_id = purple_certificate_get_unique_id(issuer);
     5.43 +		purple_debug_info("gnutls/x509", "Rejecting cert because the CA flag "
     5.44 +				"is set to false in the basic constraints extension for "
     5.45 +				"issuer cert %s. ret=%d\n",
     5.46 +				issuer_id ? issuer_id : "(null)", ret);
     5.47 +		g_free(issuer_id);
     5.48 +		return FALSE;
     5.49 +	}
     5.50 +
     5.51  	/* Now, check the signature */
     5.52  	/* The second argument is a ptr to an array of "trusted" issuer certs,
     5.53  	   but we're only using one trusted one */
      6.1 --- a/libpurple/plugins/ssl/ssl-nss.c
      6.2 +++ b/libpurple/plugins/ssl/ssl-nss.c
      6.3 @@ -45,6 +45,7 @@
      6.4  #include <nspr.h>
      6.5  #include <nss.h>
      6.6  #include <nssb64.h>
      6.7 +#include <ocsp.h>
      6.8  #include <pk11func.h>
      6.9  #include <prio.h>
     6.10  #include <secerr.h>
     6.11 @@ -53,6 +54,11 @@
     6.12  #include <sslerr.h>
     6.13  #include <sslproto.h>
     6.14  
     6.15 +/* There's a bug in some versions of this header that requires that some of
     6.16 +   the headers above be included first. This is true for at least libnss
     6.17 +   3.15.4. */
     6.18 +#include <certdb.h>
     6.19 +
     6.20  /* This is defined in NSPR's <private/pprio.h>, but to avoid including a
     6.21   * private header we duplicate the prototype here */
     6.22  NSPR_API(PRFileDesc*)  PR_ImportTCPSocket(PRInt32 osfd);
     6.23 @@ -182,6 +188,9 @@
     6.24  	}
     6.25  #endif /* NSS >= 3.14 */
     6.26  
     6.27 +	/** Disable OCSP Checking until we can make that use our HTTP & Proxy stuff */
     6.28 +	CERT_EnableOCSPChecking(PR_FALSE);
     6.29 +
     6.30  	_identity = PR_GetUniqueIdentity("Purple");
     6.31  	_nss_methods = PR_GetDefaultIOMethods();
     6.32  }
     6.33 @@ -952,7 +961,7 @@
     6.34  		    Handling is different for signed vs unsigned 32-bit types.
     6.35  		 */
     6.36  		if (*activation != nss_activ) {
     6.37 -		       	if (nss_activ < 0) {
     6.38 +			if (nss_activ < 0) {
     6.39  				purple_debug_warning("nss",
     6.40  					"Setting Activation Date to epoch to handle pre-epoch value\n");
     6.41  				*activation = 0;
     6.42 @@ -990,6 +999,108 @@
     6.43  	return TRUE;
     6.44  }
     6.45  
     6.46 +static gboolean
     6.47 +x509_register_trusted_tls_cert(PurpleCertificate *crt, gboolean ca)
     6.48 +{
     6.49 +	CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
     6.50 +	CERTCertificate *crt_dat;
     6.51 +	CERTCertTrust trust;
     6.52 +
     6.53 +	g_return_val_if_fail(crt, FALSE);
     6.54 +	g_return_val_if_fail(crt->scheme == &x509_nss, FALSE);
     6.55 +
     6.56 +	crt_dat = X509_NSS_DATA(crt);
     6.57 +	g_return_val_if_fail(crt_dat, FALSE);
     6.58 +
     6.59 +	purple_debug_info("nss", "Trusting %s\n", crt_dat->subjectName);
     6.60 +
     6.61 +	if (ca && !CERT_IsCACert(crt_dat, NULL)) {
     6.62 +		purple_debug_error("nss",
     6.63 +			"Refusing to set non-CA cert as trusted CA\n");
     6.64 +		return FALSE;
     6.65 +	}
     6.66 +
     6.67 +	if (crt_dat->isperm) {
     6.68 +		purple_debug_info("nss",
     6.69 +			"Skipping setting trust for cert in permanent DB\n");
     6.70 +		return TRUE;
     6.71 +	}
     6.72 +
     6.73 +	if (ca) {
     6.74 +		trust.sslFlags = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
     6.75 +	} else {
     6.76 +		trust.sslFlags = CERTDB_TRUSTED;
     6.77 +	}
     6.78 +	trust.emailFlags = 0;
     6.79 +	trust.objectSigningFlags = 0;
     6.80 +
     6.81 +	CERT_ChangeCertTrust(certdb, crt_dat, &trust);
     6.82 +
     6.83 +	return TRUE;
     6.84 +}
     6.85 +
     6.86 +static void x509_verify_cert(PurpleCertificateVerificationRequest *vrq, PurpleCertificateInvalidityFlags *flags)
     6.87 +{
     6.88 +	CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
     6.89 +	CERTCertificate *crt_dat;
     6.90 +	PRTime now = PR_Now();
     6.91 +	SECStatus          rv;
     6.92 +	PurpleCertificate *first_cert = vrq->cert_chain->data;
     6.93 +	CERTVerifyLog log;
     6.94 +
     6.95 +	crt_dat = X509_NSS_DATA(first_cert);
     6.96 +
     6.97 +	log.arena = PORT_NewArena(512);
     6.98 +	log.head = log.tail = NULL;
     6.99 +	log.count = 0;
    6.100 +	rv = CERT_VerifyCert(certdb, crt_dat, PR_TRUE, certUsageSSLServer, now, NULL, &log);
    6.101 +
    6.102 +	if (rv != SECSuccess || log.count > 0) {
    6.103 +		CERTVerifyLogNode *node   = NULL;
    6.104 +		unsigned int depth = (unsigned int)-1;
    6.105 +
    6.106 +		for (node = log.head; node; node = node->next) {
    6.107 +			if (depth != node->depth) {
    6.108 +				depth = node->depth;
    6.109 +				purple_debug_error("nss", "CERT %d. %s %s:\n", depth,
    6.110 +					node->cert->subjectName,
    6.111 +					depth ? "[Certificate Authority]": "");
    6.112 +			}
    6.113 +			purple_debug_error("nss", "  ERROR %ld: %s\n", node->error,
    6.114 +				PR_ErrorToName(node->error));
    6.115 +			switch (node->error) {
    6.116 +				case SEC_ERROR_EXPIRED_CERTIFICATE:
    6.117 +					*flags |= PURPLE_CERTIFICATE_EXPIRED;
    6.118 +					break;
    6.119 +				case SEC_ERROR_REVOKED_CERTIFICATE:
    6.120 +					*flags |= PURPLE_CERTIFICATE_REVOKED;
    6.121 +					break;
    6.122 +				case SEC_ERROR_UNTRUSTED_ISSUER:
    6.123 +					if (crt_dat->isRoot) {
    6.124 +						*flags |= PURPLE_CERTIFICATE_SELF_SIGNED;
    6.125 +					} else {
    6.126 +						*flags |= PURPLE_CERTIFICATE_CA_UNKNOWN;
    6.127 +					}
    6.128 +					break;
    6.129 +				case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
    6.130 +				case SEC_ERROR_BAD_SIGNATURE:
    6.131 +				default:
    6.132 +					*flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
    6.133 +			}
    6.134 +			if (node->cert)
    6.135 +				CERT_DestroyCertificate(node->cert);
    6.136 +		}
    6.137 +	} else {
    6.138 +		rv = CERT_VerifyCertName(crt_dat, vrq->subject_name);
    6.139 +		if (rv != SECSuccess) {
    6.140 +			purple_debug_error("nss", "Cert chain valid, but name not verified\n");
    6.141 +			*flags |= PURPLE_CERTIFICATE_NAME_MISMATCH;
    6.142 +		}
    6.143 +	}
    6.144 +
    6.145 +	PORT_FreeArena(log.arena, PR_FALSE);
    6.146 +}
    6.147 +
    6.148  static PurpleCertificateScheme x509_nss = {
    6.149  	"x509",                          /* Scheme name */
    6.150  	N_("X.509 Certificates"),        /* User-visible scheme name */
    6.151 @@ -1005,9 +1116,8 @@
    6.152  	x509_check_name,                 /* Check subject name */
    6.153  	x509_times,                      /* Activation/Expiration time */
    6.154  	x509_importcerts_from_file,      /* Multiple certificate import function */
    6.155 -
    6.156 -	NULL,
    6.157 -	NULL,
    6.158 +	x509_register_trusted_tls_cert,  /* Register a certificate as trusted for TLS */
    6.159 +	x509_verify_cert,                /* Verify that the specified cert chain is trusted */
    6.160  	NULL
    6.161  };
    6.162