diff src/sslconn.c @ 6738:6c95f01aaf49

[gaim-migrate @ 7270] Added optional GNUTLS support, which will also be used as a fallback if Mozilla NSS is not installed. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Thu, 04 Sep 2003 06:19:25 +0000
parents b0913ab92893
children 57a24492434b
line wrap: on
line diff
--- a/src/sslconn.c	Thu Sep 04 05:36:09 2003 +0000
+++ b/src/sslconn.c	Thu Sep 04 06:19:25 2003 +0000
@@ -25,235 +25,41 @@
 #include "debug.h"
 #include "sslconn.h"
 
+/* Pre-installed SSL op functions. */
 #ifdef HAVE_NSS
-# include <nspr.h>
-# include <nss.h>
-# include <pk11func.h>
-# include <prio.h>
-# include <secerr.h>
-# include <secmod.h>
-# include <ssl.h>
-# include <sslerr.h>
-# include <sslproto.h>
-
-typedef struct
-{
-	char *host;
-	int port;
-	void *user_data;
-	GaimSslInputFunction input_func;
-
-	int fd;
-	int inpa;
-
-	PRFileDesc *nss_fd;
-	PRFileDesc *nss_in;
-
-} GaimSslData;
-
-static gboolean _nss_initialized = FALSE;
-static const PRIOMethods *_nss_methods = NULL;
-static PRDescIdentity _identity;
-
-static void
-destroy_ssl_data(GaimSslData *data)
-{
-	if (data->inpa)   gaim_input_remove(data->inpa);
-	if (data->nss_in) PR_Close(data->nss_in);
-	if (data->nss_fd) PR_Close(data->nss_fd);
-	if (data->fd)     close(data->fd);
-
-	if (data->host != NULL)
-		g_free(data->host);
-
-	g_free(data);
-}
-
-static void
-init_nss(void)
-{
-	if (_nss_initialized)
-		return;
-
-	PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
-	NSS_NoDB_Init(NULL);
-
-	/* TODO: Fix this so autoconf does the work trying to find this lib. */
-	SECMOD_AddNewModule("Builtins", LIBDIR "/libnssckbi.so", 0, 0);
-	NSS_SetDomesticPolicy();
-
-	_identity = PR_GetUniqueIdentity("Gaim");
-	_nss_methods = PR_GetDefaultIOMethods();
-
-	_nss_initialized = TRUE;
-}
-
-static SECStatus
-ssl_auth_cert(void *arg, PRFileDesc *socket, PRBool checksig, PRBool is_server)
-{
-	return SECSuccess;
-
-#if 0
-	CERTCertificate *cert;
-	void *pinArg;
-	SECStatus status;
-
-	cert = SSL_PeerCertificate(socket);
-	pinArg = SSL_RevealPinArg(socket);
-
-	status = CERT_VerifyCertNow((CERTCertDBHandle *)arg, cert, checksig,
-								certUsageSSLClient, pinArg);
-
-	if (status != SECSuccess) {
-		gaim_debug(GAIM_DEBUG_ERROR, "msn", "CERT_VerifyCertNow failed\n");
-		CERT_DestroyCertificate(cert);
-		return status;
-	}
-
-	CERT_DestroyCertificate(cert);
-	return SECSuccess;
+GaimSslOps *gaim_ssl_nss_get_ops();
 #endif
-}
-
-SECStatus
-ssl_bad_cert(void *arg, PRFileDesc *socket)
-{
-	SECStatus status = SECFailure;
-	PRErrorCode err;
-
-	if (arg == NULL)
-		return status;
-
-	*(PRErrorCode *)arg = err = PORT_GetError();
 
-	switch (err)
-	{
-		case SEC_ERROR_INVALID_AVA:
-		case SEC_ERROR_INVALID_TIME:
-		case SEC_ERROR_BAD_SIGNATURE:
-		case SEC_ERROR_EXPIRED_CERTIFICATE:
-		case SEC_ERROR_UNKNOWN_ISSUER:
-		case SEC_ERROR_UNTRUSTED_CERT:
-		case SEC_ERROR_CERT_VALID:
-		case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
-		case SEC_ERROR_CRL_EXPIRED:
-		case SEC_ERROR_CRL_BAD_SIGNATURE:
-		case SEC_ERROR_EXTENSION_VALUE_INVALID:
-		case SEC_ERROR_CA_CERT_INVALID:
-		case SEC_ERROR_CERT_USAGES_INVALID:
-		case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
-			status = SECSuccess;
-			break;
-
-		default:
-			status = SECFailure;
-			break;
-	}
-
-	gaim_debug(GAIM_DEBUG_ERROR, "msn",
-			   "Bad certificate: %d\n");
-
-	return status;
-}
-
-static void
-input_func(gpointer data, gint source, GaimInputCondition cond)
-{
-	GaimSslData *ssl_data = (GaimSslData *)data;
-	char *cp, *ip, *sp;
-	int op, kp0, kp1;
-	int result;
-
-	result = SSL_SecurityStatus(ssl_data->nss_in, &op, &cp, &kp0,
-								&kp1, &ip, &sp);
-
-	gaim_debug(GAIM_DEBUG_MISC, "msn",
-		"bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
-		"subject DN: %s\n"
-		"issuer  DN: %s\n",
-		cp, kp1, kp0, op, sp, ip);
-
-	PR_Free(cp);
-	PR_Free(ip);
-	PR_Free(sp);
-
-	ssl_data->input_func(ssl_data->user_data, (GaimSslConnection *)ssl_data,
-						 cond);
-}
-
-static void
-ssl_connect_cb(gpointer data, gint source, GaimInputCondition cond)
-{
-	PRSocketOptionData socket_opt;
-	GaimSslData *ssl_data = (GaimSslData *)data;
-
-	if (!_nss_initialized)
-		init_nss();
-
-	ssl_data->fd = source;
-
-	ssl_data->nss_fd = PR_ImportTCPSocket(ssl_data->fd);
-
-	if (ssl_data->nss_fd == NULL)
-	{
-		gaim_debug(GAIM_DEBUG_ERROR, "ssl", "nss_fd == NULL!\n");
-
-		destroy_ssl_data(ssl_data);
-
-		return;
-	}
-
-	socket_opt.option = PR_SockOpt_Nonblocking;
-	socket_opt.value.non_blocking = PR_FALSE;
-
-	PR_SetSocketOption(ssl_data->nss_fd, &socket_opt);
-
-	ssl_data->nss_in = SSL_ImportFD(NULL, ssl_data->nss_fd);
-
-	if (ssl_data->nss_in == NULL)
-	{
-		gaim_debug(GAIM_DEBUG_ERROR, "ssl", "nss_in == NUL!\n");
-
-		destroy_ssl_data(ssl_data);
-
-		return;
-	}
-
-	SSL_OptionSet(ssl_data->nss_in, SSL_SECURITY,            PR_TRUE);
-	SSL_OptionSet(ssl_data->nss_in, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
-
-	SSL_AuthCertificateHook(ssl_data->nss_in,
-							(SSLAuthCertificate)ssl_auth_cert,
-							(void *)CERT_GetDefaultCertDB());
-	SSL_BadCertHook(ssl_data->nss_in, (SSLBadCertHandler)ssl_bad_cert, NULL);
-
-	SSL_SetURL(ssl_data->nss_in, ssl_data->host);
-
-	SSL_ResetHandshake(ssl_data->nss_in, PR_FALSE);
-
-	if (SSL_ForceHandshake(ssl_data->nss_in))
-	{
-		gaim_debug(GAIM_DEBUG_ERROR, "ssl", "Handshake failed\n");
-
-		destroy_ssl_data(ssl_data);
-
-		return;
-	}
-
-#if 0
-	ssl_data->input_func(ssl_data->user_data, (GaimSslConnection *)ssl_data,
-						 cond);
+#ifdef HAVE_GNUTLS
+GaimSslOps *gaim_ssl_gnutls_get_ops();
 #endif
 
-	input_func(ssl_data, source, cond);
+
+static gboolean _ssl_initialized = FALSE;
+static GaimSslOps *_ssl_ops = NULL;
+
+static gboolean
+ssl_init(void)
+{
+	GaimSslOps *ops = gaim_ssl_get_ops();
+	gboolean success = FALSE;
+
+	if (_ssl_initialized)
+		return FALSE;
+
+	if (ops != NULL && ops->init != NULL)
+		success = ops->init();
+
+	_ssl_initialized = success;
+
+	return success;
 }
-#endif /* HAVE_NSS */
 
 gboolean
 gaim_ssl_is_supported(void)
 {
-#ifdef HAVE_NSS
-	return TRUE;
+#ifdef HAVE_SSL
+	return (gaim_ssl_get_ops() != NULL);
 #else
 	return FALSE;
 #endif
@@ -263,95 +69,138 @@
 gaim_ssl_connect(GaimAccount *account, const char *host, int port,
 				 GaimSslInputFunction func, void *data)
 {
-#ifdef HAVE_NSS
+	GaimSslConnection *gsc;
+	GaimSslOps *ops;
 	int i;
-	GaimSslData *ssl_data;
 
 	g_return_val_if_fail(host != NULL,            NULL);
 	g_return_val_if_fail(port != 0 && port != -1, NULL);
 	g_return_val_if_fail(func != NULL,            NULL);
 	g_return_val_if_fail(gaim_ssl_is_supported(), NULL);
 
-	ssl_data = g_new0(GaimSslData, 1);
+	ops = gaim_ssl_get_ops();
+
+	g_return_val_if_fail(ops != NULL, NULL);
+	g_return_val_if_fail(ops->connect_cb != NULL, NULL);
 
-	ssl_data->host       = g_strdup(host);
-	ssl_data->port       = port;
-	ssl_data->user_data  = data;
-	ssl_data->input_func = func;
+	if (!_ssl_initialized)
+	{
+		if (!ssl_init())
+			return NULL;
+	}
 
-	i = gaim_proxy_connect(account, host, port, ssl_connect_cb, ssl_data);
+	gsc = g_new0(GaimSslConnection, 1);
+
+	gsc->host       = g_strdup(host);
+	gsc->port       = port;
+	gsc->user_data  = data;
+	gsc->input_func = func;
+
+	i = gaim_proxy_connect(account, host, port, ops->connect_cb, gsc);
 
 	if (i < 0)
 	{
-		g_free(ssl_data->host);
-		g_free(ssl_data);
+		g_free(gsc->host);
+		g_free(gsc);
 
 		return NULL;
 	}
 
-	return (GaimSslConnection)ssl_data;
-#else
-	return GINT_TO_POINTER(-1);
-#endif
+	return (GaimSslConnection *)gsc;
 }
 
 void
 gaim_ssl_close(GaimSslConnection *gsc)
 {
+	GaimSslOps *ops;
+
 	g_return_if_fail(gsc != NULL);
 
-#ifdef HAVE_NSS
-	destroy_ssl_data((GaimSslData *)gsc);
-#endif
+	ops = gaim_ssl_get_ops();
+
+	if (gsc->inpa)
+		gaim_input_remove(gsc->inpa);
+
+	if (ops != NULL && ops->close != NULL)
+		ops->close(gsc);
+
+	if (gsc->fd)
+		close(gsc->fd);
+
+	if (gsc->host != NULL)
+		g_free(gsc->host);
+
+	g_free(gsc);
 }
 
 size_t
 gaim_ssl_read(GaimSslConnection *gsc, void *data, size_t len)
 {
-#ifdef HAVE_NSS
-	GaimSslData *ssl_data = (GaimSslData *)gsc;
+	GaimSslOps *ops;
 
 	g_return_val_if_fail(gsc  != NULL, 0);
 	g_return_val_if_fail(data != NULL, 0);
 	g_return_val_if_fail(len  >  0,    0);
 
-	return PR_Read(ssl_data->nss_in, data, len);
-#else
+	ops = gaim_ssl_get_ops();
+
+	if (ops != NULL && ops->read != NULL)
+		return ops->read(gsc, data, len);
+
 	return 0;
-#endif
 }
 
 size_t
 gaim_ssl_write(GaimSslConnection *gsc, const void *data, size_t len)
 {
-#ifdef HAVE_NSS
-	GaimSslData *ssl_data = (GaimSslData *)gsc;
+	GaimSslOps *ops;
 
 	g_return_val_if_fail(gsc  != NULL, 0);
 	g_return_val_if_fail(data != NULL, 0);
 	g_return_val_if_fail(len  >  0,    0);
 
-	return PR_Write(ssl_data->nss_in, data, len);
-#else
+	ops = gaim_ssl_get_ops();
+
+	if (ops != NULL && ops->write != NULL)
+		return ops->write(gsc, data, len);
+
 	return 0;
-#endif
+}
+
+void
+gaim_ssl_set_ops(GaimSslOps *ops)
+{
+	_ssl_ops = ops;
+}
+
+GaimSslOps *
+gaim_ssl_get_ops(void)
+{
+	return _ssl_ops;
 }
 
 void
 gaim_ssl_init(void)
 {
+#if defined(HAVE_NSS)
+	gaim_ssl_set_ops(gaim_ssl_nss_get_ops());
+#elif defined(HAVE_GNUTLS)
+	gaim_ssl_set_ops(gaim_ssl_gnutls_get_ops());
+#endif
 }
 
 void
 gaim_ssl_uninit(void)
 {
-#ifdef HAVE_NSS
-	if (!_nss_initialized)
+	GaimSslOps *ops;
+
+	if (!_ssl_initialized)
 		return;
 
-	PR_Cleanup();
+	ops = gaim_ssl_get_ops();
 
-	_nss_initialized = FALSE;
-	_nss_methods     = NULL;
-#endif
+	if (ops != NULL && ops->uninit != NULL)
+		ops->uninit();
+
+	_ssl_initialized = FALSE;
 }