[dovecot-cvs] dovecot/src/login ssl-proxy-gnutls.c,NONE,1.1 ssl-proxy-openssl.c,NONE,1.1 Makefile.am,1.1.1.1,1.2 ssl-proxy.c,1.19,1.20

cras at procontrol.fi cras at procontrol.fi
Wed Nov 20 16:05:16 EET 2002


Update of /home/cvs/dovecot/src/login
In directory danu:/tmp/cvs-serv18612/src/login

Modified Files:
	Makefile.am ssl-proxy.c 
Added Files:
	ssl-proxy-gnutls.c ssl-proxy-openssl.c 
Log Message:
Support for OpenSSL.



--- NEW FILE: ssl-proxy-gnutls.c ---
/* 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

--- NEW FILE: ssl-proxy-openssl.c ---
/* 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

Index: Makefile.am
===================================================================
RCS file: /home/cvs/dovecot/src/login/Makefile.am,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -d -r1.1.1.1 -r1.2
--- Makefile.am	9 Aug 2002 09:15:53 -0000	1.1.1.1
+++ Makefile.am	20 Nov 2002 14:05:13 -0000	1.2
@@ -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 \

Index: ssl-proxy.c
===================================================================
RCS file: /home/cvs/dovecot/src/login/ssl-proxy.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- ssl-proxy.c	17 Nov 2002 09:42:08 -0000	1.19
+++ ssl-proxy.c	20 Nov 2002 14:05:13 -0000	1.20
@@ -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 */
 




More information about the dovecot-cvs mailing list