changeset 622:235188ee7a05 HEAD

Support for OpenSSL.
author Timo Sirainen <tss@iki.fi>
date Wed, 20 Nov 2002 16:05:13 +0200
parents 5690ce7151c9
children 3fa9e363cd3d
files acconfig.h configure.in src/login/Makefile.am src/login/ssl-proxy-gnutls.c src/login/ssl-proxy-openssl.c src/login/ssl-proxy.c src/master/Makefile.am src/master/ssl-init-gnutls.c src/master/ssl-init-openssl.c src/master/ssl-init.c src/master/ssl-init.h
diffstat 11 files changed, 1057 insertions(+), 618 deletions(-) [+]
line wrap: on
line diff
--- a/acconfig.h	Wed Nov 20 16:04:04 2002 +0200
+++ b/acconfig.h	Wed Nov 20 16:05:13 2002 +0200
@@ -9,6 +9,8 @@
 
 /* Build with SSL/TLS support */
 #undef HAVE_SSL
+#undef HAVE_GNUTLS
+#undef HAVE_OPENSSL
 
 /* build with IPv6 support */
 #undef HAVE_IPV6
--- a/configure.in	Wed Nov 20 16:04:04 2002 +0200
+++ b/configure.in	Wed Nov 20 16:05:13 2002 +0200
@@ -102,14 +102,24 @@
 		AC_DEFINE(BUILD_RAWLOG)
 	fi)
 
-AC_ARG_WITH(gnutls,
-[  --with-gnutls           Build with GNUTLS (default)],
+AC_ARG_WITH(ssl,
+[  --with-ssl=[gnutls|openssl] Build with GNUTLS (default) or OpenSSL],
 	if test x$withval = xno; then
 		want_gnutls=no
+		want_openssl=no
+	elif test x$withval = xgnutls; then
+		want_gnutls=yes
+		want_openssl=no
+	elif test x$withval = xopenssl; then
+		want_gnutls=no
+		want_openssl=yes
 	else
 		want_gnutls=yes
-	fi,
-	want_gnutls=yes)
+		want_openssl=yes
+	fi, [
+		want_gnutls=yes
+		want_openssl=yes
+	])
 
 dnl **
 dnl ** just some generic stuff...
@@ -346,22 +356,42 @@
 ])
 
 dnl **
-dnl ** SSL (gnutls)
+dnl ** SSL
 dnl **
 
 if test $want_gnutls = yes; then
-  AC_CHECK_LIB(gnutls, gnutls_global_init, [
-    AC_DEFINE(HAVE_SSL)
-    SSL_LIBS="-lgnutls -lgcrypt"
-    AC_SUBST(SSL_LIBS)
-    have_ssl=yes
-  ], [
-    have_ssl=no
-  ], -lgcrypt)
+	AC_CHECK_LIB(gnutls, gnutls_global_init, [
+		AC_DEFINE(HAVE_SSL)
+		AC_DEFINE(HAVE_GNUTLS)
+		SSL_LIBS="-lgnutls -lgcrypt"
+		AC_SUBST(SSL_LIBS)
+		have_ssl="yes (GNUTLS)"
+		have_gnutls=yes
+	], [
+		have_ssl=no
+	], -lgcrypt)
 else
-  have_ssl=no
+	have_ssl=no
 fi
 
+if test "$want_openssl" = "yes" -a "$have_ssl" = "no"; then
+	AC_CHECK_LIB(ssl, SSL_read, [
+		AC_CHECK_LIB(crypto, X509_new, [
+			AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h, [
+				AC_DEFINE(HAVE_SSL)
+				AC_DEFINE(HAVE_OPENSSL)
+				SSL_LIBS="-lssl -lcrypto"
+				AC_SUBST(SSL_LIBS)
+				have_ssl="yes (OpenSSL)"
+				have_openssl=yes
+			])
+		])
+	])
+fi
+
+AM_CONDITIONAL(SSL_GNUTLS, test "$have_gnutls" = "yes")
+AM_CONDITIONAL(SSL_OPENSSL, test "$have_openssl" = "yes")
+
 dnl **
 dnl ** shadow/pam support
 dnl **
@@ -481,7 +511,7 @@
 dnl **
 
 capability="IMAP4rev1"
-if test "$have_ssl" = "yes"; then
+if test "$have_ssl" != "no"; then
 	capability="$capability STARTTLS"
 fi
 AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability")
--- a/src/login/Makefile.am	Wed Nov 20 16:04:04 2002 +0200
+++ b/src/login/Makefile.am	Wed Nov 20 16:05:13 2002 +0200
@@ -13,7 +13,9 @@
 	client-authenticate.c \
 	main.c \
 	master.c \
-	ssl-proxy.c
+	ssl-proxy.c \
+	ssl-proxy-gnutls.c \
+	ssl-proxy-openssl.c
 
 noinst_HEADERS = \
 	auth-connection.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login/ssl-proxy-gnutls.c	Wed Nov 20 16:05:13 2002 +0200
@@ -0,0 +1,521 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "ioloop.h"
+#include "network.h"
+#include "ssl-proxy.h"
+
+#ifdef HAVE_GNUTLS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <gcrypt.h>
+#include <gnutls/gnutls.h>
+
+typedef struct {
+	int refcount;
+
+	gnutls_session session;
+	int fd_ssl, fd_plain;
+	IO io_ssl, io_plain;
+	int io_ssl_dir;
+
+	unsigned char outbuf_plain[1024];
+	unsigned int outbuf_pos_plain;
+
+	size_t send_left_ssl, send_left_plain;
+} SSLProxy;
+
+const int protocol_priority[] =
+	{ GNUTLS_TLS1, GNUTLS_SSL3, 0 };
+const int kx_priority[] =
+	{ GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, 0 };
+const int cipher_priority[] =
+	{ GNUTLS_CIPHER_RIJNDAEL_CBC, GNUTLS_CIPHER_3DES_CBC,
+	  GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_ARCFOUR_40, 0 };
+const int comp_priority[] =
+	{ GNUTLS_COMP_LZO, GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 };
+const int mac_priority[] =
+	{ GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 };
+const int cert_type_priority[] =
+	{ GNUTLS_CRT_X509, 0 };
+
+static gnutls_certificate_credentials x509_cred;
+static gnutls_dh_params dh_params;
+static gnutls_rsa_params rsa_params;
+
+static void ssl_input(void *context, int handle, IO io);
+static void plain_input(void *context, int handle, IO io);
+static int ssl_proxy_destroy(SSLProxy *proxy);
+
+static const char *get_alert_text(SSLProxy *proxy)
+{
+	return gnutls_alert_get_name(gnutls_alert_get(proxy->session));
+}
+
+static int handle_ssl_error(SSLProxy *proxy, int error)
+{
+	if (!gnutls_error_is_fatal(error)) {
+		if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) {
+			i_warning("Received SSL warning alert: %s",
+				  get_alert_text(proxy));
+		}
+		return 0;
+	}
+
+	/* fatal error occured */
+	if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+		i_warning("Received SSL fatal alert: %s",
+			  get_alert_text(proxy));
+	} else {
+		i_warning("Error reading from SSL client: %s",
+			  gnutls_strerror(error));
+	}
+
+        gnutls_alert_send_appropriate(proxy->session, error);
+	ssl_proxy_destroy(proxy);
+	return -1;
+}
+
+static int proxy_recv_ssl(SSLProxy *proxy, void *data, size_t size)
+{
+	int rcvd;
+
+	rcvd = gnutls_record_recv(proxy->session, data, size);
+	if (rcvd > 0)
+		return rcvd;
+
+	if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
+		/* disconnected, either by nicely telling us that we'll
+		   close the connection, or by simply killing the
+		   connection which gives us the packet length error. */
+		ssl_proxy_destroy(proxy);
+		return -1;
+	}
+
+	return handle_ssl_error(proxy, rcvd);
+}
+
+static int proxy_send_ssl(SSLProxy *proxy, const void *data, size_t size)
+{
+	int sent;
+
+	sent = gnutls_record_send(proxy->session, data, size);
+	if (sent >= 0)
+		return sent;
+
+	if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) {
+		/* don't warn about errors related to unexpected disconnection */
+		ssl_proxy_destroy(proxy);
+		return -1;
+	}
+
+	return handle_ssl_error(proxy, sent);
+}
+
+static int ssl_proxy_destroy(SSLProxy *proxy)
+{
+	if (--proxy->refcount > 0)
+		return TRUE;
+
+	gnutls_deinit(proxy->session);
+
+	(void)net_disconnect(proxy->fd_ssl);
+	(void)net_disconnect(proxy->fd_plain);
+
+	if (proxy->io_ssl != NULL)
+		io_remove(proxy->io_ssl);
+	if (proxy->io_plain != NULL)
+		io_remove(proxy->io_plain);
+
+	i_free(proxy);
+
+	main_unref();
+	return FALSE;
+}
+
+static void ssl_output(void *context, int fd __attr_unused__,
+		       IO io __attr_unused__)
+{
+        SSLProxy *proxy = context;
+	int sent;
+
+	sent = net_transmit(proxy->fd_plain,
+			    proxy->outbuf_plain + proxy->outbuf_pos_plain,
+			    proxy->send_left_plain);
+	if (sent < 0) {
+		/* disconnected */
+		ssl_proxy_destroy(proxy);
+		return;
+	}
+
+	proxy->send_left_plain -= sent;
+	proxy->outbuf_pos_plain += sent;
+
+	if (proxy->send_left_plain > 0)
+		return;
+
+	/* everything is sent, start reading again */
+	io_remove(proxy->io_ssl);
+	proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy);
+}
+
+static void ssl_input(void *context, int fd __attr_unused__,
+		      IO io __attr_unused__)
+{
+        SSLProxy *proxy = context;
+	int rcvd, sent;
+
+	rcvd = proxy_recv_ssl(proxy, proxy->outbuf_plain,
+			      sizeof(proxy->outbuf_plain));
+	if (rcvd <= 0)
+		return;
+
+	sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain, (size_t)rcvd);
+	if (sent == rcvd)
+		return;
+
+	if (sent < 0) {
+		/* disconnected */
+		ssl_proxy_destroy(proxy);
+		return;
+	}
+
+	/* everything wasn't sent - don't read anything until we've
+	   sent it all */
+        proxy->outbuf_pos_plain = 0;
+	proxy->send_left_plain = rcvd - sent;
+
+	io_remove(proxy->io_ssl);
+	proxy->io_ssl = io_add(proxy->fd_ssl, IO_WRITE, ssl_output, proxy);
+}
+
+static void plain_output(void *context, int fd __attr_unused__,
+			 IO io __attr_unused__)
+{
+	SSLProxy *proxy = context;
+	int sent;
+
+	sent = proxy_send_ssl(proxy, NULL, proxy->send_left_ssl);
+	if (sent <= 0)
+		return;
+
+	proxy->send_left_ssl -= sent;
+	if (proxy->send_left_ssl > 0)
+		return;
+
+	/* everything is sent, start reading again */
+	io_remove(proxy->io_plain);
+	proxy->io_plain = io_add(proxy->fd_plain, IO_READ, plain_input, proxy);
+}
+
+static void plain_input(void *context, int fd __attr_unused__,
+			IO io __attr_unused__)
+{
+	SSLProxy *proxy = context;
+	char buf[1024];
+	ssize_t rcvd, sent;
+
+	rcvd = net_receive(proxy->fd_plain, buf, sizeof(buf));
+	if (rcvd < 0) {
+		/* disconnected */
+		gnutls_bye(proxy->session, 1);
+		ssl_proxy_destroy(proxy);
+		return;
+	}
+
+	sent = proxy_send_ssl(proxy, buf, (size_t)rcvd);
+	if (sent < 0 || sent == rcvd)
+		return;
+
+	/* everything wasn't sent - don't read anything until we've
+	   sent it all */
+	proxy->send_left_ssl = rcvd - sent;
+
+	io_remove(proxy->io_plain);
+	proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy);
+}
+
+static void ssl_handshake(void *context, int fd __attr_unused__,
+			  IO io __attr_unused__)
+{
+	SSLProxy *proxy = context;
+	int ret, dir;
+
+        ret = gnutls_handshake(proxy->session);
+	if (ret >= 0) {
+		/* handshake done, now we can start reading */
+		if (proxy->io_ssl != NULL)
+			io_remove(proxy->io_ssl);
+
+		proxy->io_plain = io_add(proxy->fd_plain, IO_READ,
+					 plain_input, proxy);
+		proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ,
+				       ssl_input, proxy);
+		return;
+	}
+
+	if (handle_ssl_error(proxy, ret) < 0)
+		return;
+
+	/* i/o interrupted */
+	dir = gnutls_handshake_get_direction(proxy->session) == 0 ?
+		IO_READ : IO_WRITE;
+	if (proxy->io_ssl_dir != dir) {
+		if (proxy->io_ssl != NULL)
+			io_remove(proxy->io_ssl);
+		proxy->io_ssl = io_add(proxy->fd_ssl, dir,
+				       ssl_handshake, proxy);
+		proxy->io_ssl_dir = dir;
+	}
+}
+
+static gnutls_session initialize_state(void)
+{
+	gnutls_session session;
+
+	gnutls_init(&session, GNUTLS_SERVER);
+
+	gnutls_protocol_set_priority(session, protocol_priority);
+	gnutls_cipher_set_priority(session, cipher_priority);
+	gnutls_compression_set_priority(session, comp_priority);
+	gnutls_kx_set_priority(session, kx_priority);
+	gnutls_mac_set_priority(session, mac_priority);
+	gnutls_cert_type_set_priority(session, cert_type_priority);
+
+	gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+	return session;
+}
+
+int ssl_proxy_new(int fd)
+{
+        SSLProxy *proxy;
+	gnutls_session session;
+	int sfd[2];
+
+	if (!ssl_initialized)
+		return -1;
+
+	session = initialize_state();
+	gnutls_transport_set_ptr(session, fd);
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
+		i_error("socketpair() failed: %m");
+		gnutls_deinit(session);
+		return -1;
+	}
+
+	net_set_nonblock(sfd[0], TRUE);
+	net_set_nonblock(sfd[1], TRUE);
+
+	proxy = i_new(SSLProxy, 1);
+	proxy->refcount = 1;
+	proxy->session = session;
+	proxy->fd_ssl = fd;
+	proxy->fd_plain = sfd[0];
+
+	proxy->refcount++;
+	ssl_handshake(proxy, -1, NULL);
+	if (!ssl_proxy_destroy(proxy))
+		return -1;
+
+        main_ref();
+	return sfd[1];
+}
+
+static void read_next_field(int fd, gnutls_datum *datum,
+			    const char *fname, const char *field_name)
+{
+        ssize_t ret;
+
+	/* get size */
+	ret = read(fd, &datum->size, sizeof(datum->size));
+	if (ret < 0)
+		i_fatal("read() failed for %s: %m", fname);
+
+	if (ret != sizeof(datum->size)) {
+		(void)unlink(fname);
+		i_fatal("Corrupted SSL parameter file %s: File too small",
+			fname);
+	}
+
+	if (datum->size > 10240) {
+		(void)unlink(fname);
+		i_fatal("Corrupted SSL parameter file %s: "
+			"Field '%s' too large (%u)",
+			fname, field_name, datum->size);
+	}
+
+	/* read the actual data */
+	datum->data = t_malloc(datum->size);
+	ret = read(fd, datum->data, datum->size);
+	if (ret < 0)
+		i_fatal("read() failed for %s: %m", fname);
+
+	if ((size_t)ret != datum->size) {
+		(void)unlink(fname);
+		i_fatal("Corrupted SSL parameter file %s: "
+			"Field '%s' not fully in file (%u < %u)",
+			fname, field_name, datum->size - ret, datum->size);
+	}
+}
+
+static void read_dh_parameters(int fd, const char *fname)
+{
+	gnutls_datum dbits, prime, generator;
+	int ret, bits;
+
+	if ((ret = gnutls_dh_params_init(&dh_params)) < 0) {
+		i_fatal("gnutls_dh_params_init() failed: %s",
+			gnutls_strerror(ret));
+	}
+
+	/* read until bits field is 0 */
+	for (;;) {
+		read_next_field(fd, &dbits, fname, "DH bits");
+
+		if (dbits.size != sizeof(int)) {
+			(void)unlink(fname);
+			i_fatal("Corrupted SSL parameter file %s: "
+				"Field 'DH bits' has invalid size %u",
+				fname, dbits.size);
+		}
+
+		bits = *((int *) dbits.data);
+		if (bits == 0)
+			break;
+
+		read_next_field(fd, &prime, fname, "DH prime");
+		read_next_field(fd, &generator, fname, "DH generator");
+
+		ret = gnutls_dh_params_set(dh_params, prime, generator, bits);
+		if (ret < 0) {
+			i_fatal("gnutls_dh_params_set() failed: %s",
+				gnutls_strerror(ret));
+		}
+	}
+}
+
+static void read_rsa_parameters(int fd, const char *fname)
+{
+	gnutls_datum m, e, d, p, q, u;
+	int ret;
+
+	read_next_field(fd, &m, fname, "RSA m");
+	read_next_field(fd, &e, fname, "RSA e");
+	read_next_field(fd, &d, fname, "RSA d");
+	read_next_field(fd, &p, fname, "RSA p");
+	read_next_field(fd, &q, fname, "RSA q");
+	read_next_field(fd, &u, fname, "RSA u");
+
+	if ((ret = gnutls_rsa_params_init(&rsa_params)) < 0) {
+		i_fatal("gnutls_rsa_params_init() failed: %s",
+			gnutls_strerror(ret));
+	}
+
+	/* only 512bit is allowed */
+	ret = gnutls_rsa_params_set(rsa_params, m, e, d, p, q, u, 512);
+	if (ret < 0) {
+		i_fatal("gnutls_rsa_params_set() failed: %s",
+			gnutls_strerror(ret));
+	}
+}
+
+static void read_parameters(const char *fname)
+{
+	int fd;
+
+	/* we'll wait until parameter file exists */
+	for (;;) {
+		fd = open(fname, O_RDONLY);
+		if (fd != -1)
+			break;
+
+		if (errno != ENOENT)
+			i_fatal("Can't open SSL parameter file %s: %m", fname);
+
+		sleep(1);
+	}
+
+	read_dh_parameters(fd, fname);
+	read_rsa_parameters(fd, fname);
+
+	(void)close(fd);
+}
+
+static void gcrypt_log_handler(void *context __attr_unused__, int level,
+			       const char *fmt, va_list args)
+{
+	char *buf;
+
+	t_push();
+
+	buf = t_malloc(printf_string_upper_bound(fmt, args));
+	vsprintf(buf, fmt, args);
+
+	if (level == GCRY_LOG_FATAL)
+		i_error("gcrypt fatal: %s", buf);
+
+	t_pop();
+}
+
+void ssl_proxy_init(void)
+{
+	const char *certfile, *keyfile, *paramfile;
+	char buf[4];
+	int ret;
+
+	certfile = getenv("SSL_CERT_FILE");
+	keyfile = getenv("SSL_KEY_FILE");
+	paramfile = getenv("SSL_PARAM_FILE");
+
+	if (certfile == NULL || keyfile == NULL || paramfile == NULL) {
+		/* SSL support is disabled */
+		return;
+	}
+
+	if ((ret = gnutls_global_init() < 0)) {
+		i_fatal("gnu_tls_global_init() failed: %s",
+			gnutls_strerror(ret));
+	}
+
+	/* gcrypt initialization - set log handler and make sure randomizer
+	   opens /dev/urandom now instead of after we've chrooted */
+	gcry_set_log_handler(gcrypt_log_handler, NULL);
+	gcry_randomize(buf, sizeof(buf), GCRY_STRONG_RANDOM);
+
+	read_parameters(paramfile);
+
+	if ((ret = gnutls_certificate_allocate_cred(&x509_cred)) < 0) {
+		i_fatal("gnutls_certificate_allocate_cred() failed: %s",
+			gnutls_strerror(ret));
+	}
+
+	ret = gnutls_certificate_set_x509_key_file(x509_cred, certfile, keyfile,
+						   GNUTLS_X509_FMT_PEM);
+	if (ret < 0) {
+		i_fatal("Can't load certificate files %s and %s: %s",
+			certfile, keyfile, gnutls_strerror(ret));
+	}
+
+        ret = gnutls_certificate_set_dh_params(x509_cred, dh_params);
+	if (ret < 0)
+		i_fatal("Can't set DH parameters: %s", gnutls_strerror(ret));
+	ret = gnutls_certificate_set_rsa_params(x509_cred, rsa_params);
+	if (ret < 0)
+		i_fatal("Can't set RSA parameters: %s", gnutls_strerror(ret));
+
+	ssl_initialized = TRUE;
+}
+
+void ssl_proxy_deinit(void)
+{
+	if (ssl_initialized) {
+		gnutls_certificate_free_cred(x509_cred);
+		gnutls_global_deinit();
+	}
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login/ssl-proxy-openssl.c	Wed Nov 20 16:05:13 2002 +0200
@@ -0,0 +1,376 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "ioloop.h"
+#include "network.h"
+#include "ssl-proxy.h"
+
+#ifdef HAVE_OPENSSL
+
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+typedef enum {
+	SSL_STATE_HANDSHAKE,
+	SSL_STATE_READ,
+	SSL_STATE_WRITE
+} SSLState;
+
+typedef struct {
+	int refcount;
+
+	SSL *ssl;
+        SSLState state;
+
+	int fd_ssl, fd_plain;
+	IO io_ssl, io_plain_read, io_plain_write;
+	int io_ssl_dir;
+
+	unsigned char plainout_buf[1024];
+	unsigned int plainout_pos, plainout_size;
+
+	unsigned char sslout_buf[1024];
+	unsigned int sslout_pos, sslout_size;
+} SSLProxy;
+
+static SSL_CTX *ssl_ctx;
+
+static void plain_read(SSLProxy *proxy);
+static void plain_write(SSLProxy *proxy);
+
+static int ssl_proxy_destroy(SSLProxy *proxy);
+static void ssl_set_direction(SSLProxy *proxy, int dir);
+
+static void plain_block_input(SSLProxy *proxy, int block)
+{
+	if (block) {
+		if (proxy->io_plain_read != NULL) {
+			io_remove(proxy->io_plain_read);
+			proxy->io_plain_read = NULL;
+		}
+	} else {
+		if (proxy->io_plain_read == NULL) {
+			proxy->io_plain_read =
+				io_add(proxy->fd_plain, IO_READ,
+				       (IOFunc) plain_read, proxy);
+		}
+	}
+}
+
+static void ssl_block(SSLProxy *proxy, int block)
+{
+	i_assert(proxy->state == SSL_STATE_READ);
+
+	if (block) {
+		if (proxy->io_ssl != NULL) {
+			io_remove(proxy->io_ssl);
+			proxy->io_ssl = NULL;
+		}
+
+		proxy->io_ssl_dir = -2;
+	} else {
+		proxy->io_ssl_dir = -1;
+		ssl_set_direction(proxy, IO_READ);
+	}
+}
+
+static void plain_read(SSLProxy *proxy)
+{
+	ssize_t ret;
+
+	i_assert(proxy->sslout_size == 0);
+
+	ret = net_receive(proxy->fd_plain, proxy->sslout_buf,
+			  sizeof(proxy->sslout_buf));
+	if (ret < 0)
+		ssl_proxy_destroy(proxy);
+	else {
+		proxy->sslout_size = ret;
+		proxy->sslout_pos = 0;
+
+		proxy->state = SSL_STATE_WRITE;
+		ssl_set_direction(proxy, IO_WRITE);
+
+		plain_block_input(proxy, TRUE);
+	}
+}
+
+static void plain_write(SSLProxy *proxy)
+{
+	ssize_t ret;
+
+	ret = net_transmit(proxy->fd_plain,
+			   proxy->plainout_buf + proxy->plainout_pos,
+			   proxy->plainout_size);
+	if (ret < 0)
+		ssl_proxy_destroy(proxy);
+	else {
+		proxy->plainout_size -= ret;
+		proxy->plainout_pos += ret;
+
+		if (proxy->plainout_size > 0) {
+			ssl_block(proxy, TRUE);
+			if (proxy->io_plain_write == NULL) {
+				proxy->io_plain_write =
+					io_add(proxy->fd_plain, IO_WRITE,
+					       (IOFunc) plain_write, proxy);
+			}
+		} else {
+			proxy->plainout_pos = 0;
+			ssl_block(proxy, FALSE);
+
+			if (proxy->io_plain_write != NULL) {
+				io_remove(proxy->io_plain_write);
+                                proxy->io_plain_write = NULL;
+			}
+		}
+	}
+
+}
+
+const char *ssl_last_error(void)
+{
+	unsigned long err;
+	char *buf;
+
+	err = ERR_get_error();
+	if (err == 0)
+		return strerror(errno);
+
+	buf = t_malloc(256);
+	buf[255] = '\0';
+	ERR_error_string_n(err, buf, 255);
+	return buf;
+}
+
+static void ssl_handle_error(SSLProxy *proxy, int err, const char *func)
+{
+	err = SSL_get_error(proxy->ssl, err);
+	switch (err) {
+	case SSL_ERROR_WANT_READ:
+		ssl_set_direction(proxy, IO_READ);
+		break;
+	case SSL_ERROR_WANT_WRITE:
+		ssl_set_direction(proxy, IO_WRITE);
+		break;
+	case SSL_ERROR_SYSCALL:
+		/* eat up the error queue */
+		i_error("%s failed: %s", func, ssl_last_error());
+		ssl_proxy_destroy(proxy);
+		break;
+	case SSL_ERROR_ZERO_RETURN:
+		/* clean connection closing */
+	default:
+		ssl_proxy_destroy(proxy);
+		break;
+	}
+}
+
+static void ssl_handshake_step(SSLProxy *proxy)
+{
+	int ret;
+
+	ret = SSL_accept(proxy->ssl);
+	if (ret != 1) {
+		plain_block_input(proxy, TRUE);
+		ssl_handle_error(proxy, ret, "SSL_accept()");
+	} else {
+		plain_block_input(proxy, FALSE);
+		ssl_set_direction(proxy, IO_READ);
+		proxy->state = SSL_STATE_READ;
+	}
+}
+
+static void ssl_read_step(SSLProxy *proxy)
+{
+	int ret;
+
+	i_assert(proxy->plainout_size == 0);
+
+	ret = SSL_read(proxy->ssl, proxy->plainout_buf,
+		       sizeof(proxy->plainout_buf));
+	if (ret <= 0) {
+		plain_block_input(proxy, TRUE);
+		ssl_handle_error(proxy, ret, "SSL_read()");
+	} else {
+		plain_block_input(proxy, FALSE);
+		ssl_set_direction(proxy, IO_READ);
+
+		proxy->plainout_pos = 0;
+		proxy->plainout_size = ret;
+		plain_write(proxy);
+	}
+}
+
+static void ssl_write_step(SSLProxy *proxy)
+{
+	int ret;
+
+	ret = SSL_write(proxy->ssl, proxy->sslout_buf + proxy->sslout_pos,
+			proxy->sslout_size);
+	if (ret <= 0) {
+		plain_block_input(proxy, TRUE);
+		ssl_handle_error(proxy, ret, "SSL_write()");
+	} else {
+		proxy->sslout_size -= ret;
+		proxy->sslout_pos += ret;
+
+		if (proxy->sslout_size > 0) {
+			plain_block_input(proxy, TRUE);
+			ssl_set_direction(proxy, IO_WRITE);
+			proxy->state = SSL_STATE_WRITE;
+		} else {
+			plain_block_input(proxy, FALSE);
+			ssl_set_direction(proxy, IO_READ);
+			proxy->state = SSL_STATE_READ;
+			proxy->sslout_pos = 0;
+		}
+	}
+}
+
+static void ssl_step(void *context, int fd __attr_unused__,
+		     IO io __attr_unused__)
+{
+        SSLProxy *proxy = context;
+
+	switch (proxy->state) {
+	case SSL_STATE_HANDSHAKE:
+		ssl_handshake_step(proxy);
+		break;
+	case SSL_STATE_READ:
+		ssl_read_step(proxy);
+		break;
+	case SSL_STATE_WRITE:
+		ssl_write_step(proxy);
+		break;
+	}
+}
+
+static void ssl_set_direction(SSLProxy *proxy, int dir)
+{
+	i_assert(proxy->io_ssl_dir != -2);
+
+	if (proxy->io_ssl_dir == dir)
+		return;
+
+	if (proxy->io_ssl != NULL)
+		io_remove(proxy->io_ssl);
+	proxy->io_ssl = io_add(proxy->fd_ssl, dir, ssl_step, proxy);
+}
+
+int ssl_proxy_new(int fd)
+{
+	SSLProxy *proxy;
+	SSL *ssl;
+	int sfd[2];
+
+	if (!ssl_initialized)
+		return -1;
+
+	ssl = SSL_new(ssl_ctx);
+	if (ssl == NULL) {
+		i_error("SSL_new() failed: %s", ssl_last_error());
+		return -1;
+	}
+
+	SSL_set_accept_state(ssl);
+	if (SSL_set_fd(ssl, fd) != 1) {
+		i_error("SSL_set_fd() failed: %s", ssl_last_error());
+		return -1;
+	}
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
+		i_error("socketpair() failed: %m");
+		SSL_free(ssl);
+		return -1;
+	}
+
+	net_set_nonblock(sfd[0], TRUE);
+	net_set_nonblock(sfd[1], TRUE);
+
+	proxy = i_new(SSLProxy, 1);
+	proxy->refcount = 1;
+	proxy->ssl = ssl;
+	proxy->fd_ssl = fd;
+	proxy->fd_plain = sfd[0];
+
+	proxy->state = SSL_STATE_HANDSHAKE;
+	ssl_set_direction(proxy, IO_READ);
+
+	proxy->refcount++;
+	ssl_handshake_step(proxy);
+	if (!ssl_proxy_destroy(proxy))
+		return -1;
+
+        main_ref();
+	return sfd[1];
+}
+
+static int ssl_proxy_destroy(SSLProxy *proxy)
+{
+	if (--proxy->refcount > 0)
+		return TRUE;
+
+	SSL_free(proxy->ssl);
+
+	(void)net_disconnect(proxy->fd_ssl);
+	(void)net_disconnect(proxy->fd_plain);
+
+	if (proxy->io_ssl != NULL)
+		io_remove(proxy->io_ssl);
+	if (proxy->io_plain_read != NULL)
+		io_remove(proxy->io_plain_read);
+	if (proxy->io_plain_write != NULL)
+		io_remove(proxy->io_plain_write);
+
+	i_free(proxy);
+
+	main_unref();
+	return FALSE;
+}
+
+void ssl_proxy_init(void)
+{
+	const char *certfile, *keyfile, *paramfile;
+	int ret;
+
+	certfile = getenv("SSL_CERT_FILE");
+	keyfile = getenv("SSL_KEY_FILE");
+	paramfile = getenv("SSL_PARAM_FILE");
+
+	if (certfile == NULL || keyfile == NULL || paramfile == NULL) {
+		/* SSL support is disabled */
+		return;
+	}
+
+	SSL_library_init();
+	SSL_load_error_strings();
+
+	if ((ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
+		i_fatal("SSL_CTX_new() failed");
+
+        ret = SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile);
+	if (ret != 1) {
+		i_fatal("Can't load certificate file %s: %s",
+			certfile, ssl_last_error());
+	}
+
+	ret = SSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile, SSL_FILETYPE_PEM);
+	if (ret != 1) {
+		i_fatal("Can't load private key file %s: %s",
+			keyfile, ssl_last_error());
+	}
+
+	ssl_initialized = TRUE;
+}
+
+void ssl_proxy_deinit(void)
+{
+	if (ssl_initialized)
+                SSL_CTX_free(ssl_ctx);
+}
+
+#endif
--- a/src/login/ssl-proxy.c	Wed Nov 20 16:04:04 2002 +0200
+++ b/src/login/ssl-proxy.c	Wed Nov 20 16:05:13 2002 +0200
@@ -1,526 +1,10 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
-#include "common.h"
-#include "ioloop.h"
-#include "network.h"
-#include "ssl-proxy.h"
+#include "lib.h"
 
 int ssl_initialized = FALSE;
 
-#ifdef HAVE_SSL
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <gcrypt.h>
-#include <gnutls/gnutls.h>
-
-typedef struct {
-	int refcount;
-
-	gnutls_session session;
-	int fd_ssl, fd_plain;
-	IO io_ssl, io_plain;
-	int io_ssl_dir;
-
-	unsigned char outbuf_plain[1024];
-	unsigned int outbuf_pos_plain;
-
-	size_t send_left_ssl, send_left_plain;
-} SSLProxy;
-
-const int protocol_priority[] =
-	{ GNUTLS_TLS1, GNUTLS_SSL3, 0 };
-const int kx_priority[] =
-	{ GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, 0 };
-const int cipher_priority[] =
-	{ GNUTLS_CIPHER_RIJNDAEL_CBC, GNUTLS_CIPHER_3DES_CBC,
-	  GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_ARCFOUR_40, 0 };
-const int comp_priority[] =
-	{ GNUTLS_COMP_LZO, GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 };
-const int mac_priority[] =
-	{ GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 };
-const int cert_type_priority[] =
-	{ GNUTLS_CRT_X509, 0 };
-
-static gnutls_certificate_credentials x509_cred;
-static gnutls_dh_params dh_params;
-static gnutls_rsa_params rsa_params;
-
-static void ssl_input(void *context, int handle, IO io);
-static void plain_input(void *context, int handle, IO io);
-static int ssl_proxy_destroy(SSLProxy *proxy);
-
-static const char *get_alert_text(SSLProxy *proxy)
-{
-	return gnutls_alert_get_name(gnutls_alert_get(proxy->session));
-}
-
-static int handle_ssl_error(SSLProxy *proxy, int error)
-{
-	if (!gnutls_error_is_fatal(error)) {
-		if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) {
-			i_warning("Received SSL warning alert: %s",
-				  get_alert_text(proxy));
-		}
-		return 0;
-	}
-
-	/* fatal error occured */
-	if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) {
-		i_warning("Received SSL fatal alert: %s",
-			  get_alert_text(proxy));
-	} else {
-		i_warning("Error reading from SSL client: %s",
-			  gnutls_strerror(error));
-	}
-
-        gnutls_alert_send_appropriate(proxy->session, error);
-	ssl_proxy_destroy(proxy);
-	return -1;
-}
-
-static int proxy_recv_ssl(SSLProxy *proxy, void *data, size_t size)
-{
-	int rcvd;
-
-	rcvd = gnutls_record_recv(proxy->session, data, size);
-	if (rcvd > 0)
-		return rcvd;
-
-	if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
-		/* disconnected, either by nicely telling us that we'll
-		   close the connection, or by simply killing the
-		   connection which gives us the packet length error. */
-		ssl_proxy_destroy(proxy);
-		return -1;
-	}
-
-	return handle_ssl_error(proxy, rcvd);
-}
-
-static int proxy_send_ssl(SSLProxy *proxy, const void *data, size_t size)
-{
-	int sent;
-
-	sent = gnutls_record_send(proxy->session, data, size);
-	if (sent >= 0)
-		return sent;
-
-	if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) {
-		/* don't warn about errors related to unexpected disconnection */
-		ssl_proxy_destroy(proxy);
-		return -1;
-	}
-
-	return handle_ssl_error(proxy, sent);
-}
-
-static int ssl_proxy_destroy(SSLProxy *proxy)
-{
-	if (--proxy->refcount > 0)
-		return TRUE;
-
-	gnutls_deinit(proxy->session);
-
-	(void)net_disconnect(proxy->fd_ssl);
-	(void)net_disconnect(proxy->fd_plain);
-
-	if (proxy->io_ssl != NULL)
-		io_remove(proxy->io_ssl);
-	if (proxy->io_plain != NULL)
-		io_remove(proxy->io_plain);
-
-	i_free(proxy);
-
-	main_unref();
-	return FALSE;
-}
-
-static void ssl_output(void *context, int fd __attr_unused__,
-		       IO io __attr_unused__)
-{
-        SSLProxy *proxy = context;
-	int sent;
-
-	sent = net_transmit(proxy->fd_plain,
-			    proxy->outbuf_plain + proxy->outbuf_pos_plain,
-			    proxy->send_left_plain);
-	if (sent < 0) {
-		/* disconnected */
-		ssl_proxy_destroy(proxy);
-		return;
-	}
-
-	proxy->send_left_plain -= sent;
-	proxy->outbuf_pos_plain += sent;
-
-	if (proxy->send_left_plain > 0)
-		return;
-
-	/* everything is sent, start reading again */
-	io_remove(proxy->io_ssl);
-	proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy);
-}
-
-static void ssl_input(void *context, int fd __attr_unused__,
-		      IO io __attr_unused__)
-{
-        SSLProxy *proxy = context;
-	int rcvd, sent;
-
-	rcvd = proxy_recv_ssl(proxy, proxy->outbuf_plain,
-			      sizeof(proxy->outbuf_plain));
-	if (rcvd <= 0)
-		return;
-
-	sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain, (size_t)rcvd);
-	if (sent == rcvd)
-		return;
-
-	if (sent < 0) {
-		/* disconnected */
-		ssl_proxy_destroy(proxy);
-		return;
-	}
-
-	/* everything wasn't sent - don't read anything until we've
-	   sent it all */
-        proxy->outbuf_pos_plain = 0;
-	proxy->send_left_plain = rcvd - sent;
-
-	io_remove(proxy->io_ssl);
-	proxy->io_ssl = io_add(proxy->fd_ssl, IO_WRITE, ssl_output, proxy);
-}
-
-static void plain_output(void *context, int fd __attr_unused__,
-			 IO io __attr_unused__)
-{
-	SSLProxy *proxy = context;
-	int sent;
-
-	sent = proxy_send_ssl(proxy, NULL, proxy->send_left_ssl);
-	if (sent <= 0)
-		return;
-
-	proxy->send_left_ssl -= sent;
-	if (proxy->send_left_ssl > 0)
-		return;
-
-	/* everything is sent, start reading again */
-	io_remove(proxy->io_plain);
-	proxy->io_plain = io_add(proxy->fd_plain, IO_READ, plain_input, proxy);
-}
-
-static void plain_input(void *context, int fd __attr_unused__,
-			IO io __attr_unused__)
-{
-	SSLProxy *proxy = context;
-	char buf[1024];
-	ssize_t rcvd, sent;
-
-	rcvd = net_receive(proxy->fd_plain, buf, sizeof(buf));
-	if (rcvd < 0) {
-		/* disconnected */
-		gnutls_bye(proxy->session, 1);
-		ssl_proxy_destroy(proxy);
-		return;
-	}
-
-	sent = proxy_send_ssl(proxy, buf, (size_t)rcvd);
-	if (sent < 0 || sent == rcvd)
-		return;
-
-	/* everything wasn't sent - don't read anything until we've
-	   sent it all */
-	proxy->send_left_ssl = rcvd - sent;
-
-	io_remove(proxy->io_plain);
-	proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy);
-}
-
-static void ssl_handshake(void *context, int fd __attr_unused__,
-			  IO io __attr_unused__)
-{
-	SSLProxy *proxy = context;
-	int ret, dir;
-
-        ret = gnutls_handshake(proxy->session);
-	if (ret >= 0) {
-		/* handshake done, now we can start reading */
-		if (proxy->io_ssl != NULL)
-			io_remove(proxy->io_ssl);
-
-		proxy->io_plain = io_add(proxy->fd_plain, IO_READ,
-					 plain_input, proxy);
-		proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ,
-				       ssl_input, proxy);
-		return;
-	}
-
-	if (handle_ssl_error(proxy, ret) < 0)
-		return;
-
-	/* i/o interrupted */
-	dir = gnutls_handshake_get_direction(proxy->session) == 0 ?
-		IO_READ : IO_WRITE;
-	if (proxy->io_ssl_dir != dir) {
-		if (proxy->io_ssl != NULL)
-			io_remove(proxy->io_ssl);
-		proxy->io_ssl = io_add(proxy->fd_ssl, dir,
-				       ssl_handshake, proxy);
-		proxy->io_ssl_dir = dir;
-	}
-}
-
-static gnutls_session initialize_state(void)
-{
-	gnutls_session session;
-
-	gnutls_init(&session, GNUTLS_SERVER);
-
-	gnutls_protocol_set_priority(session, protocol_priority);
-	gnutls_cipher_set_priority(session, cipher_priority);
-	gnutls_compression_set_priority(session, comp_priority);
-	gnutls_kx_set_priority(session, kx_priority);
-	gnutls_mac_set_priority(session, mac_priority);
-	gnutls_cert_type_set_priority(session, cert_type_priority);
-
-	gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
-	return session;
-}
-
-int ssl_proxy_new(int fd)
-{
-        SSLProxy *proxy;
-	gnutls_session session;
-	int sfd[2];
-
-	if (!ssl_initialized)
-		return -1;
-
-	session = initialize_state();
-	gnutls_transport_set_ptr(session, fd);
-
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
-		i_error("socketpair() failed: %m");
-		gnutls_deinit(session);
-		return -1;
-	}
-
-	net_set_nonblock(sfd[0], TRUE);
-	net_set_nonblock(sfd[1], TRUE);
-
-	proxy = i_new(SSLProxy, 1);
-	proxy->refcount = 1;
-	proxy->session = session;
-	proxy->fd_ssl = fd;
-	proxy->fd_plain = sfd[0];
-
-	proxy->refcount++;
-	ssl_handshake(proxy, -1, NULL);
-	if (!ssl_proxy_destroy(proxy))
-		return -1;
-
-        main_ref();
-	return sfd[1];
-}
-
-static void read_next_field(int fd, gnutls_datum *datum,
-			    const char *fname, const char *field_name)
-{
-        ssize_t ret;
-
-	/* get size */
-	ret = read(fd, &datum->size, sizeof(datum->size));
-	if (ret < 0)
-		i_fatal("read() failed for %s: %m", fname);
-
-	if (ret != sizeof(datum->size)) {
-		(void)unlink(fname);
-		i_fatal("Corrupted SSL parameter file %s: File too small",
-			fname);
-	}
-
-	if (datum->size > 10240) {
-		(void)unlink(fname);
-		i_fatal("Corrupted SSL parameter file %s: "
-			"Field '%s' too large (%u)",
-			fname, field_name, datum->size);
-	}
-
-	/* read the actual data */
-	datum->data = t_malloc(datum->size);
-	ret = read(fd, datum->data, datum->size);
-	if (ret < 0)
-		i_fatal("read() failed for %s: %m", fname);
-
-	if ((size_t)ret != datum->size) {
-		(void)unlink(fname);
-		i_fatal("Corrupted SSL parameter file %s: "
-			"Field '%s' not fully in file (%u < %u)",
-			fname, field_name, datum->size - ret, datum->size);
-	}
-}
-
-static void read_dh_parameters(int fd, const char *fname)
-{
-	gnutls_datum dbits, prime, generator;
-	int ret, bits;
-
-	if ((ret = gnutls_dh_params_init(&dh_params)) < 0) {
-		i_fatal("gnutls_dh_params_init() failed: %s",
-			gnutls_strerror(ret));
-	}
-
-	/* read until bits field is 0 */
-	for (;;) {
-		read_next_field(fd, &dbits, fname, "DH bits");
-
-		if (dbits.size != sizeof(int)) {
-			(void)unlink(fname);
-			i_fatal("Corrupted SSL parameter file %s: "
-				"Field 'DH bits' has invalid size %u",
-				fname, dbits.size);
-		}
-
-		bits = *((int *) dbits.data);
-		if (bits == 0)
-			break;
-
-		read_next_field(fd, &prime, fname, "DH prime");
-		read_next_field(fd, &generator, fname, "DH generator");
-
-		ret = gnutls_dh_params_set(dh_params, prime, generator, bits);
-		if (ret < 0) {
-			i_fatal("gnutls_dh_params_set() failed: %s",
-				gnutls_strerror(ret));
-		}
-	}
-}
-
-static void read_rsa_parameters(int fd, const char *fname)
-{
-	gnutls_datum m, e, d, p, q, u;
-	int ret;
-
-	read_next_field(fd, &m, fname, "RSA m");
-	read_next_field(fd, &e, fname, "RSA e");
-	read_next_field(fd, &d, fname, "RSA d");
-	read_next_field(fd, &p, fname, "RSA p");
-	read_next_field(fd, &q, fname, "RSA q");
-	read_next_field(fd, &u, fname, "RSA u");
-
-	if ((ret = gnutls_rsa_params_init(&rsa_params)) < 0) {
-		i_fatal("gnutls_rsa_params_init() failed: %s",
-			gnutls_strerror(ret));
-	}
-
-	/* only 512bit is allowed */
-	ret = gnutls_rsa_params_set(rsa_params, m, e, d, p, q, u, 512);
-	if (ret < 0) {
-		i_fatal("gnutls_rsa_params_set() failed: %s",
-			gnutls_strerror(ret));
-	}
-}
-
-static void read_parameters(const char *fname)
-{
-	int fd;
-
-	/* we'll wait until parameter file exists */
-	for (;;) {
-		fd = open(fname, O_RDONLY);
-		if (fd != -1)
-			break;
-
-		if (errno != ENOENT)
-			i_fatal("Can't open SSL parameter file %s: %m", fname);
-
-		sleep(1);
-	}
-
-	read_dh_parameters(fd, fname);
-	read_rsa_parameters(fd, fname);
-
-	(void)close(fd);
-}
-
-static void gcrypt_log_handler(void *context __attr_unused__, int level,
-			       const char *fmt, va_list args)
-{
-	char *buf;
-
-	t_push();
-
-	buf = t_malloc(printf_string_upper_bound(fmt, args));
-	vsprintf(buf, fmt, args);
-
-	if (level == GCRY_LOG_FATAL)
-		i_error("gcrypt fatal: %s", buf);
-
-	t_pop();
-}
-
-void ssl_proxy_init(void)
-{
-	const char *certfile, *keyfile, *paramfile;
-	char buf[4];
-	int ret;
-
-	certfile = getenv("SSL_CERT_FILE");
-	keyfile = getenv("SSL_KEY_FILE");
-	paramfile = getenv("SSL_PARAM_FILE");
-
-	if (certfile == NULL || keyfile == NULL || paramfile == NULL) {
-		/* SSL support is disabled */
-		return;
-	}
-
-	if ((ret = gnutls_global_init() < 0)) {
-		i_fatal("gnu_tls_global_init() failed: %s",
-			gnutls_strerror(ret));
-	}
-
-	/* gcrypt initialization - set log handler and make sure randomizer
-	   opens /dev/urandom now instead of after we've chrooted */
-	gcry_set_log_handler(gcrypt_log_handler, NULL);
-	gcry_randomize(buf, sizeof(buf), GCRY_STRONG_RANDOM);
-
-	read_parameters(paramfile);
-
-	if ((ret = gnutls_certificate_allocate_cred(&x509_cred)) < 0) {
-		i_fatal("gnutls_certificate_allocate_cred() failed: %s",
-			gnutls_strerror(ret));
-	}
-
-	ret = gnutls_certificate_set_x509_key_file(x509_cred, certfile, keyfile,
-						   GNUTLS_X509_FMT_PEM);
-	if (ret < 0) {
-		i_fatal("Can't load certificate files %s and %s: %s",
-			certfile, keyfile, gnutls_strerror(ret));
-	}
-
-        ret = gnutls_certificate_set_dh_params(x509_cred, dh_params);
-	if (ret < 0)
-		i_fatal("Can't set DH parameters: %s", gnutls_strerror(ret));
-	ret = gnutls_certificate_set_rsa_params(x509_cred, rsa_params);
-	if (ret < 0)
-		i_fatal("Can't set RSA parameters: %s", gnutls_strerror(ret));
-
-	ssl_initialized = TRUE;
-}
-
-void ssl_proxy_deinit(void)
-{
-	if (ssl_initialized) {
-		gnutls_certificate_free_cred(x509_cred);
-		gnutls_global_deinit();
-	}
-}
-
-#else
+#ifndef HAVE_SSL
 
 /* no SSL support */
 
--- a/src/master/Makefile.am	Wed Nov 20 16:04:04 2002 +0200
+++ b/src/master/Makefile.am	Wed Nov 20 16:05:13 2002 +0200
@@ -16,7 +16,9 @@
 	login-process.c \
 	main.c \
 	settings.c \
-	ssl-init.c
+	ssl-init.c \
+	ssl-init-gnutls.c \
+	ssl-init-openssl.c
 
 noinst_HEADERS = \
 	auth-process.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/ssl-init-gnutls.c	Wed Nov 20 16:05:13 2002 +0200
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "write-full.h"
+#include "ssl-init.h"
+
+#ifdef HAVE_GNUTLS
+
+#include <gnutls/gnutls.h>
+
+static int prime_nums[] = { 768, 1024, 0 };
+
+static void write_datum(int fd, const char *fname, gnutls_datum *dbits)
+{
+	if (write_full(fd, &dbits->size, sizeof(dbits->size)) < 0)
+		i_fatal("write_full() failed for file %s: %m", fname);
+
+	if (write_full(fd, dbits->data, dbits->size) < 0)
+		i_fatal("write_full() failed for file %s: %m", fname);
+}
+
+static void generate_dh_parameters(int fd, const char *fname)
+{
+	gnutls_datum dbits, prime, generator;
+	int ret, bits, i;
+
+	dbits.size = sizeof(bits);
+	dbits.data = (unsigned char *) &bits;
+
+	for (i = 0; prime_nums[i] != 0; i++) {
+		bits = prime_nums[i];
+
+		ret = gnutls_dh_params_generate(&prime, &generator, bits);
+		if (ret < 0) {
+			i_fatal("gnutls_dh_params_generate(%d) failed: %s",
+				bits, gnutls_strerror(ret));
+		}
+
+		write_datum(fd, fname, &dbits);
+		write_datum(fd, fname, &prime);
+		write_datum(fd, fname, &generator);
+
+		free(prime.data);
+		free(generator.data);
+	}
+
+	bits = 0;
+	write_datum(fd, fname, &dbits);
+}
+
+static void generate_rsa_parameters(int fd, const char *fname)
+{
+	gnutls_datum m, e, d, p, q, u;
+	int ret;
+
+        ret = gnutls_rsa_params_generate(&m, &e, &d, &p, &q, &u, 512);
+	if (ret < 0) {
+		i_fatal("gnutls_rsa_params_generate() faile: %s",
+			strerror(ret));
+	}
+
+	write_datum(fd, fname, &m);
+	write_datum(fd, fname, &e);
+	write_datum(fd, fname, &d);
+	write_datum(fd, fname, &p);
+	write_datum(fd, fname, &q);
+	write_datum(fd, fname, &u);
+}
+
+void _ssl_generate_parameters(int fd, const char *fname)
+{
+	int ret;
+
+	if ((ret = gnutls_global_init() < 0)) {
+		i_fatal("gnu_tls_global_init() failed: %s",
+			gnutls_strerror(ret));
+	}
+
+	generate_dh_parameters(fd, temp_fname);
+	generate_rsa_parameters(fd, temp_fname);
+
+	gnutls_global_deinit();
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/ssl-init-openssl.c	Wed Nov 20 16:05:13 2002 +0200
@@ -0,0 +1,12 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "ssl-init.h"
+
+#ifdef HAVE_OPENSSL
+
+void _ssl_generate_parameters(int fd, const char *fname)
+{
+}
+
+#endif
--- a/src/master/ssl-init.c	Wed Nov 20 16:04:04 2002 +0200
+++ b/src/master/ssl-init.c	Wed Nov 20 16:05:13 2002 +0200
@@ -2,88 +2,22 @@
 
 #include "common.h"
 #include "ioloop.h"
-#include "write-full.h"
+#include "ssl-init.h"
 
 #ifdef HAVE_SSL
 
 #include <stdio.h>
-#include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <gnutls/gnutls.h>
-
-static int prime_nums[] = { 768, 1024, 0 };
 
 static Timeout to;
 static int generating;
 
-static void write_datum(int fd, const char *fname, gnutls_datum *dbits)
-{
-	if (write_full(fd, &dbits->size, sizeof(dbits->size)) < 0)
-		i_fatal("write_full() failed for file %s: %m", fname);
-
-	if (write_full(fd, dbits->data, dbits->size) < 0)
-		i_fatal("write_full() failed for file %s: %m", fname);
-}
-
-static void generate_dh_parameters(int fd, const char *fname)
-{
-	gnutls_datum dbits, prime, generator;
-	int ret, bits, i;
-
-	dbits.size = sizeof(bits);
-	dbits.data = (unsigned char *) &bits;
-
-	for (i = 0; prime_nums[i] != 0; i++) {
-		bits = prime_nums[i];
-
-		ret = gnutls_dh_params_generate(&prime, &generator, bits);
-		if (ret < 0) {
-			i_fatal("gnutls_dh_params_generate(%d) failed: %s",
-				bits, gnutls_strerror(ret));
-		}
-
-		write_datum(fd, fname, &dbits);
-		write_datum(fd, fname, &prime);
-		write_datum(fd, fname, &generator);
-
-		free(prime.data);
-		free(generator.data);
-	}
-
-	bits = 0;
-	write_datum(fd, fname, &dbits);
-}
-
-static void generate_rsa_parameters(int fd, const char *fname)
-{
-	gnutls_datum m, e, d, p, q, u;
-	int ret;
-
-        ret = gnutls_rsa_params_generate(&m, &e, &d, &p, &q, &u, 512);
-	if (ret < 0) {
-		i_fatal("gnutls_rsa_params_generate() faile: %s",
-			strerror(ret));
-	}
-
-	write_datum(fd, fname, &m);
-	write_datum(fd, fname, &e);
-	write_datum(fd, fname, &d);
-	write_datum(fd, fname, &p);
-	write_datum(fd, fname, &q);
-	write_datum(fd, fname, &u);
-}
-
 static void generate_parameters_file(const char *fname)
 {
 	const char *temp_fname;
-	int fd, ret;
-
-	if ((ret = gnutls_global_init() < 0)) {
-		i_fatal("gnu_tls_global_init() failed: %s",
-			gnutls_strerror(ret));
-	}
+	int fd;
 
 	temp_fname = t_strconcat(fname, ".tmp", NULL);
 	(void)unlink(temp_fname);
@@ -94,16 +28,13 @@
 			temp_fname);
 	}
 
-	generate_dh_parameters(fd, temp_fname);
-	generate_rsa_parameters(fd, temp_fname);
+	_ssl_generate_parameters(fd, temp_fname);
 
 	if (close(fd) < 0)
 		i_fatal("close() failed for %s: %m", temp_fname);
 
 	if (rename(temp_fname, fname) < 0)
 		i_fatal("rename(%s, %s) failed: %m", temp_fname, fname);
-
-	gnutls_global_deinit();
 }
 
 static void start_generate_process(void)
@@ -171,16 +102,8 @@
 
 #else
 
-void ssl_parameter_process_destroyed(pid_t pid __attr_unused__)
-{
-}
-
-void ssl_init(void)
-{
-}
-
-void ssl_deinit(void)
-{
-}
+void ssl_parameter_process_destroyed(pid_t pid __attr_unused__) {}
+void ssl_init(void) {}
+void ssl_deinit(void) {}
 
 #endif
--- a/src/master/ssl-init.h	Wed Nov 20 16:04:04 2002 +0200
+++ b/src/master/ssl-init.h	Wed Nov 20 16:05:13 2002 +0200
@@ -3,6 +3,8 @@
 
 void ssl_parameter_process_destroyed(pid_t pid);
 
+void _ssl_generate_parameters(int fd, const char *fname);
+
 void ssl_init(void);
 void ssl_deinit(void);