changeset 23737:d7c62b220ed0

Patch to fully enable NSS SSL Certificates from #6500. Thanks to Lou Cipher for the patch
author William Ehlhardt <williamehlhardt@gmail.com>
date Thu, 14 Aug 2008 22:54:29 +0000
parents 79346eb75cca
children 82bc9f6fa234
files libpurple/plugins/ssl/ssl-nss.c
diffstat 1 files changed, 115 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/plugins/ssl/ssl-nss.c	Thu Aug 14 21:39:12 2008 +0000
+++ b/libpurple/plugins/ssl/ssl-nss.c	Thu Aug 14 22:54:29 2008 +0000
@@ -60,6 +60,7 @@
 
 static const PRIOMethods *_nss_methods = NULL;
 static PRDescIdentity _identity;
+static PurpleCertificateScheme x509_nss;
 
 /* Thank you, Evolution */
 static void
@@ -172,6 +173,7 @@
 #endif
 }
 
+#if 0
 static SECStatus
 ssl_bad_cert(void *arg, PRFileDesc *socket)
 {
@@ -211,6 +213,7 @@
 
 	return status;
 }
+#endif
 
 static gboolean
 ssl_nss_init(void)
@@ -227,6 +230,82 @@
 }
 
 static void
+ssl_nss_verified_cb(PurpleCertificateVerificationStatus st,
+		       gpointer userdata)
+{
+	PurpleSslConnection *gsc = (PurpleSslConnection *) userdata;
+
+	if (st == PURPLE_CERTIFICATE_VALID) {
+		/* Certificate valid? Good! Do the connection! */
+		gsc->connect_cb(gsc->connect_cb_data, gsc, PURPLE_INPUT_READ);
+	} else {
+		/* Otherwise, signal an error */
+		if(gsc->error_cb != NULL)
+			gsc->error_cb(gsc, PURPLE_SSL_CERTIFICATE_INVALID,
+				      gsc->connect_cb_data);
+		purple_ssl_close(gsc);
+	}
+}
+
+/** Transforms an NSS containing an X.509 certificate into a Certificate instance
+ *
+ * @param cert   Certificate to transform
+ * @return A newly allocated Certificate
+ */
+static PurpleCertificate *
+x509_import_from_nss(CERTCertificate* cert)
+{
+	/* New certificate to return */
+	PurpleCertificate * crt;
+
+	/* Allocate the certificate and load it with data */
+	crt = g_new0(PurpleCertificate, 1);
+	crt->scheme = &x509_nss;
+	crt->data = CERT_DupCertificate(cert);
+
+	return crt;
+}
+
+static GList *
+ssl_nss_get_peer_certificates(PRFileDesc *socket, PurpleSslConnection * gsc)
+{
+	CERTCertificate *curcert;
+	CERTCertificate *issuerCert;
+	PurpleCertificate * newcrt;
+
+	/* List of Certificate instances to return */
+	GList * peer_certs = NULL;
+	int count;
+	int64 now = PR_Now();
+	
+	curcert = SSL_PeerCertificate(socket);
+	if (curcert == NULL) {
+		purple_debug_error("nss", "could not DupCertificate\n");
+		return NULL;
+	}
+	
+	for (count = 0 ; count < CERT_MAX_CERT_CHAIN ; count++) {
+		purple_debug_info("nss", "subject=%s issuer=%s\n", curcert->subjectName, curcert->issuerName);
+		newcrt = x509_import_from_nss(curcert);
+		peer_certs = g_list_append(peer_certs, newcrt);
+
+		if (curcert->isRoot) {
+			break;
+		}
+		issuerCert = CERT_FindCertIssuer(curcert, now, certUsageSSLServer);
+		if (!issuerCert) {
+			purple_debug_error("nss", "partial certificate chain\n");
+			break;
+		}
+		CERT_DestroyCertificate(curcert);
+		curcert = issuerCert;
+	}
+	CERT_DestroyCertificate(curcert);
+
+	return peer_certs;
+}
+
+static void
 ssl_nss_handshake_cb(gpointer data, int fd, PurpleInputCondition cond)
 {
 	PurpleSslConnection *gsc = (PurpleSslConnection *)data;
@@ -256,7 +335,25 @@
 	purple_input_remove(nss_data->handshake_handler);
 	nss_data->handshake_handler = 0;
 
-	gsc->connect_cb(gsc->connect_cb_data, gsc, cond);
+	/* If a Verifier was given, hand control over to it */
+	if (gsc->verifier) {
+		GList *peers;
+		/* First, get the peer cert chain */
+		peers = ssl_nss_get_peer_certificates(nss_data->in, gsc);
+
+		/* Now kick off the verification process */
+		purple_certificate_verify(gsc->verifier,
+				gsc->host,
+				peers,
+				ssl_nss_verified_cb,
+				gsc);
+
+		purple_certificate_destroy_list(peers);
+	} else {
+		/* Otherwise, just call the "connection complete"
+		   callback */
+		gsc->connect_cb(gsc->connect_cb_data, gsc, cond);
+	}
 }
 
 static void
@@ -310,7 +407,10 @@
 	SSL_AuthCertificateHook(nss_data->in,
 							(SSLAuthCertificate)ssl_auth_cert,
 							(void *)CERT_GetDefaultCertDB());
+#if 0
+	/* No point in hooking BadCert, since ssl_auth_cert always succeeds */
 	SSL_BadCertHook(nss_data->in, (SSLBadCertHandler)ssl_bad_cert, NULL);
+#endif
 
 	if(gsc->host)
 		SSL_SetURL(nss_data->in, gsc->host);
@@ -566,7 +666,20 @@
 x509_signed_by(PurpleCertificate * crt,
 	       PurpleCertificate * issuer)
 {
-	return TRUE;
+	CERTCertificate *subjectCert;
+	CERTCertificate *issuerCert;
+	SECStatus st;
+	
+	issuerCert = X509_NSS_DATA(issuer);
+	g_return_val_if_fail(issuerCert, FALSE);
+
+	subjectCert = X509_NSS_DATA(crt);
+	g_return_val_if_fail(subjectCert, FALSE);
+
+	if ( PORT_Strcmp(subjectCert->issuerName, issuerCert->subjectName) != 0 )
+		return FALSE;
+	st = CERT_VerifySignedData(&subjectCert->signatureWrap, issuerCert, PR_Now(), NULL);
+	return st == SECSuccess;
 }
 
 static GByteArray *