[dovecot-cvs] dovecot/src/login-common .cvsignore,NONE,1.1 Makefile.am,NONE,1.1 auth-connection.c,NONE,1.1 auth-connection.h,NONE,1.1 client-common.h,NONE,1.1 common.h,NONE,1.1 main.c,NONE,1.1 master.c,NONE,1.1 master.h,NONE,1.1 ssl-proxy-gnutls.c,NONE,1.1 Message-Id: <20030128213528.5C5EA238C5@danu.procontrol.fi>

cras at procontrol.fi cras at procontrol.fi
Tue Jan 28 23:35:28 EET 2003


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

Added Files:
	.cvsignore Makefile.am auth-connection.c auth-connection.h 
	client-common.h common.h main.c master.c master.h 
	ssl-proxy-gnutls.c ssl-proxy-openssl.c ssl-proxy.c ssl-proxy.h 
Log Message:
Moved common login process code to login-common, created pop3-login.



--- NEW FILE: .cvsignore ---
*.la
*.lo
*.o
.deps
.libs
Makefile
Makefile.in
so_locations

--- NEW FILE: Makefile.am ---
noinst_LIBRARIES = liblogin-common.a

INCLUDES = \
	-I$(top_srcdir)/src/lib

liblogin_common_a_SOURCES = \
	auth-connection.c \
	main.c \
	master.c \
	ssl-proxy.c \
	ssl-proxy-gnutls.c \
	ssl-proxy-openssl.c

noinst_HEADERS = \
	auth-connection.h \
	common.h \
	client-common.h \
	master.h \
	ssl-proxy.h

--- NEW FILE: auth-connection.c ---
/* Copyright (C) 2002 Timo Sirainen */

#include "common.h"
#include "hash.h"
#include "ioloop.h"
#include "network.h"
#include "istream.h"
#include "ostream.h"
#include "auth-connection.h"

#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>

/* Maximum size for an auth reply. 50kB should be more than enough. */
#define MAX_INBUF_SIZE (1024*50)

#define MAX_OUTBUF_SIZE \
	(sizeof(struct auth_login_request_continue) + \
	 AUTH_LOGIN_MAX_REQUEST_DATA_SIZE)

enum auth_mech available_auth_mechs;

static int auth_reconnect;
static unsigned int request_id_counter;
static struct auth_connection *auth_connections;
static struct timeout *to;

static void auth_connection_destroy(struct auth_connection *conn);
static void auth_input(void *context);
static void auth_connect_missing(void);

static struct auth_connection *auth_connection_find(const char *path)
{
	struct auth_connection *conn;

	for (conn = auth_connections; conn != NULL; conn = conn->next) {
		if (strcmp(conn->path, path) == 0)
			return conn;
	}

	return NULL;
}

static struct auth_connection *auth_connection_new(const char *path)
{
	struct auth_connection *conn;
        struct auth_login_handshake_input handshake;
	int fd;

	fd = net_connect_unix(path);
	if (fd == -1) {
		i_error("Can't connect to imap-auth at %s: %m", path);
                auth_reconnect = TRUE;
		return NULL;
	}

	/* we depend on auth process - if it's slow, just wait */
        net_set_nonblock(fd, FALSE);

	conn = i_new(struct auth_connection, 1);
	conn->path = i_strdup(path);
	conn->fd = fd;
	conn->io = io_add(fd, IO_READ, auth_input, conn);
	conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE,
					   FALSE);
	conn->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
					    IO_PRIORITY_DEFAULT, FALSE);
	conn->requests = hash_create(default_pool, default_pool, 100,
				     NULL, NULL);

	conn->next = auth_connections;
	auth_connections = conn;

	/* send our handshake */
	memset(&handshake, 0, sizeof(handshake));
	handshake.pid = login_process_uid;
	if (o_stream_send(conn->output, &handshake, sizeof(handshake)) < 0) {
                auth_connection_destroy(conn);
		return NULL;
	}
	return conn;
}

static void request_destroy(struct auth_request *request)
{
	hash_remove(request->conn->requests, POINTER_CAST(request->id));
	i_free(request);
}

static void request_hash_destroy(void *key __attr_unused__, void *value,
				 void *context __attr_unused__)
{
	struct auth_request *request = value;

	request->callback(request, NULL, NULL, request->context);
	request_destroy(request);
}

static void auth_connection_destroy(struct auth_connection *conn)
{
	struct auth_connection **pos;

	for (pos = &auth_connections; *pos != NULL; pos = &(*pos)->next) {
		if (*pos == conn) {
			*pos = conn->next;
			break;
		}
	}

	hash_foreach(conn->requests, request_hash_destroy, NULL);
	hash_destroy(conn->requests);

	if (close(conn->fd) < 0)
		i_error("close(imap-auth) failed: %m");
	io_remove(conn->io);
	i_stream_unref(conn->input);
	o_stream_unref(conn->output);
	i_free(conn->path);
	i_free(conn);
}

static struct auth_connection *
auth_connection_get(enum auth_mech mech, size_t size, const char **error)
{
	struct auth_connection *conn;
	int found;

	found = FALSE;
	for (conn = auth_connections; conn != NULL; conn = conn->next) {
		if ((conn->available_auth_mechs & mech)) {
			if (o_stream_have_space(conn->output, size) > 0)
				return conn;

			found = TRUE;
		}
	}

	if (!found) {
		if ((available_auth_mechs & mech) == 0)
			*error = "Unsupported authentication mechanism";
		else {
			*error = "Authentication server isn't connected, "
				"try again later..";
			auth_reconnect = TRUE;
		}
	} else {
		*error = "Authentication servers are busy, wait..";
		i_warning("Authentication servers are busy");
	}

	return NULL;
}

static void update_available_auth_mechs(void)
{
	struct auth_connection *conn;

        available_auth_mechs = 0;
	for (conn = auth_connections; conn != NULL; conn = conn->next)
                available_auth_mechs |= conn->available_auth_mechs;
}

static void auth_handle_handshake(struct auth_connection *conn,
				  struct auth_login_handshake_output *handshake)
{
	conn->pid = handshake->pid;
	conn->available_auth_mechs = handshake->auth_mechanisms;
	conn->handshake_received = TRUE;

	update_available_auth_mechs();
}

static void auth_handle_reply(struct auth_connection *conn,
			      struct auth_login_reply *reply,
			      const unsigned char *data)
{
	struct auth_request *request;

	request = hash_lookup(conn->requests, POINTER_CAST(reply->id));
	if (request == NULL) {
		i_error("BUG: imap-auth sent us reply with unknown ID %u",
			reply->id);
		return;
	}

	request->callback(request, reply, data, request->context);

	if (reply->result != AUTH_LOGIN_RESULT_CONTINUE)
		request_destroy(request);
}

static void auth_input(void *context)
{
	struct auth_connection *conn = context;
        struct auth_login_handshake_output handshake;
	const unsigned char *data;
	size_t size;

	switch (i_stream_read(conn->input)) {
	case 0:
		return;
	case -1:
		/* disconnected */
                auth_reconnect = TRUE;
		auth_connection_destroy(conn);
		return;
	case -2:
		/* buffer full - can't happen unless imap-auth is buggy */
		i_error("BUG: imap-auth sent us more than %d bytes of data",
			MAX_INBUF_SIZE);
		auth_connection_destroy(conn);
		return;
	}

	if (!conn->handshake_received) {
		data = i_stream_get_data(conn->input, &size);
		if (size == sizeof(handshake)) {
			memcpy(&handshake, data, sizeof(handshake));
			i_stream_skip(conn->input, sizeof(handshake));

			auth_handle_handshake(conn, &handshake);
		} else if (size > sizeof(handshake)) {
			i_error("BUG: imap-auth sent us too large handshake "
				"(%"PRIuSIZE_T " vs %"PRIuSIZE_T")", size,
				sizeof(handshake));
			auth_connection_destroy(conn);
		}
		return;
	}

	if (!conn->reply_received) {
		data = i_stream_get_data(conn->input, &size);
		if (size < sizeof(conn->reply))
			return;

		memcpy(&conn->reply, data, sizeof(conn->reply));
		i_stream_skip(conn->input, sizeof(conn->reply));
		conn->reply_received = TRUE;
	}

	data = i_stream_get_data(conn->input, &size);
	if (size < conn->reply.data_size)
		return;

	/* we've got a full reply */
	conn->reply_received = FALSE;
	auth_handle_reply(conn, &conn->reply, data);
	i_stream_skip(conn->input, conn->reply.data_size);
}

int auth_init_request(enum auth_mech mech, auth_callback_t callback,
		      void *context, const char **error)
{
	struct auth_connection *conn;
	struct auth_request *request;
	struct auth_login_request_new auth_request;

	if (auth_reconnect)
		auth_connect_missing();

	conn = auth_connection_get(mech, sizeof(auth_request), error);
	if (conn == NULL)
		return FALSE;

	/* create internal request structure */
	request = i_new(struct auth_request, 1);
	request->mech = mech;
	request->conn = conn;
	request->id = ++request_id_counter;
	if (request->id == 0) {
		/* wrapped - ID 0 not allowed */
		request->id = ++request_id_counter;
	}
	request->callback = callback;
	request->context = context;

	hash_insert(conn->requests, POINTER_CAST(request->id), request);

	/* send request to auth */
	auth_request.type = AUTH_LOGIN_REQUEST_NEW;
	auth_request.mech = request->mech;
	auth_request.id = request->id;
	if (o_stream_send(request->conn->output, &auth_request,
			  sizeof(auth_request)) < 0)
		auth_connection_destroy(request->conn);
	return TRUE;
}

void auth_continue_request(struct auth_request *request,
			   const unsigned char *data, size_t data_size)
{
	struct auth_login_request_continue auth_request;

	/* send continued request to auth */
	auth_request.type = AUTH_LOGIN_REQUEST_CONTINUE;
	auth_request.id = request->id;
	auth_request.data_size = data_size;

	if (o_stream_send(request->conn->output, &auth_request,
			  sizeof(auth_request)) < 0)
		auth_connection_destroy(request->conn);
	else if (o_stream_send(request->conn->output, data, data_size) < 0)
		auth_connection_destroy(request->conn);
}

void auth_abort_request(struct auth_request *request)
{
        request_destroy(request);
}

static void auth_connect_missing(void)
{
	DIR *dirp;
	struct dirent *dp;
	struct stat st;

	auth_reconnect = TRUE;

	/* we're chrooted into */
	dirp = opendir(".");
	if (dirp == NULL) {
		i_error("opendir(\".\") failed when trying to get list of "
			"authentication servers: %m");
		return;
	}

	while ((dp = readdir(dirp)) != NULL) {
		if (dp->d_name[0] == '.')
			continue;

		if (auth_connection_find(dp->d_name) != NULL) {
			/* already connected */
			continue;
		}

		if (stat(dp->d_name, &st) == 0 && S_ISSOCK(st.st_mode)) {
			if (auth_connection_new(dp->d_name) != NULL)
				auth_reconnect = FALSE;
		}
	}

	(void)closedir(dirp);
}

static void
auth_connect_missing_timeout(void *context __attr_unused__)
{
	if (auth_reconnect)
                auth_connect_missing();
}

void auth_connection_init(void)
{
	auth_connections = NULL;
	request_id_counter = 0;
        auth_reconnect = FALSE;

	auth_connect_missing();
	to = timeout_add(1000, auth_connect_missing_timeout, NULL);
}

void auth_connection_deinit(void)
{
	struct auth_connection *next;

	while (auth_connections != NULL) {
		next = auth_connections->next;
		auth_connection_destroy(auth_connections);
		auth_connections = next;
	}

	timeout_remove(to);
}

--- NEW FILE: auth-connection.h ---
#ifndef __AUTH_CONNECTION_H
#define __AUTH_CONNECTION_H

struct client;
struct auth_request;

/* reply is NULL if auth connection died */
typedef void auth_callback_t(struct auth_request *request,
			     struct auth_login_reply *reply,
			     const unsigned char *data, struct client *client);

struct auth_connection {
	struct auth_connection *next;

	char *path;
	int fd;
	struct io *io;
	struct istream *input;
	struct ostream *output;

	unsigned int pid;
	enum auth_mech available_auth_mechs;
        struct auth_login_reply reply;

        struct hash_table *requests;

	unsigned int handshake_received:1;
	unsigned int reply_received:1;
};

struct auth_request {
        enum auth_mech mech;
        struct auth_connection *conn;

	unsigned int id;

	auth_callback_t *callback;
	void *context;

	unsigned int init_sent:1;
};

extern enum auth_mech available_auth_mechs;

int auth_init_request(enum auth_mech mech, auth_callback_t *callback,
		      void *context, const char **error);

void auth_continue_request(struct auth_request *request,
			   const unsigned char *data, size_t data_size);

void auth_abort_request(struct auth_request *request);

void auth_connection_init(void);
void auth_connection_deinit(void);

#endif

--- NEW FILE: client-common.h ---
#ifndef __CLIENT_COMMON_H
#define __CLIENT_COMMON_H

#include "network.h"

struct client {
	struct ip_addr ip;
	int fd;

	master_callback_t *master_callback;
	/* ... */
};

struct client *client_create(int fd, struct ip_addr *ip, int ssl);

unsigned int clients_get_count(void);
void clients_destroy_all(void);

void clients_init(void);
void clients_deinit(void);

#endif

--- NEW FILE: common.h ---
#ifndef __COMMON_H
#define __COMMON_H

#include "lib.h"
#include "../auth/auth-login-interface.h"

extern int disable_plaintext_auth, process_per_connection, verbose_proctitle;
extern unsigned int max_logging_users;
extern unsigned int login_process_uid;

void main_ref(void);
void main_unref(void);

void main_close_listen(void);

#endif

--- NEW FILE: main.c ---
/* Copyright (C) 2002 Timo Sirainen */

#include "common.h"
#include "ioloop.h"
#include "lib-signals.h"
#include "restrict-access.h"
#include "process-title.h"
#include "fd-close-on-exec.h"
#include "auth-connection.h"
#include "master.h"
#include "client-common.h"
#include "ssl-proxy.h"

#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>

int disable_plaintext_auth, process_per_connection, verbose_proctitle;
unsigned int max_logging_users;
unsigned int login_process_uid;

static struct ioloop *ioloop;
static struct io *io_listen, *io_listen_ssl;
static int main_refcount;
static int closing_down;

void main_ref(void)
{
	main_refcount++;
}

void main_unref(void)
{
	if (--main_refcount == 0) {
		/* nothing to do, quit */
		io_loop_stop(ioloop);
	} else if (closing_down && clients_get_count() == 0) {
		/* last login finished, close all communications
		   to master process */
		master_close();
	}
}

void main_close_listen(void)
{
	if (closing_down)
		return;

	if (io_listen != NULL) {
		if (close(LOGIN_LISTEN_FD) < 0)
			i_fatal("can't close() IMAP listen handle");

		io_remove(io_listen);
		io_listen = NULL;
	}

	if (io_listen_ssl != NULL) {
		if (close(LOGIN_SSL_LISTEN_FD) < 0)
			i_fatal("can't close() IMAPS listen handle");

		io_remove(io_listen_ssl);
		io_listen_ssl = NULL;
	}

	closing_down = TRUE;
	master_notify_finished();
}

static void sig_quit(int signo __attr_unused__)
{
	io_loop_stop(ioloop);
}

static void login_accept(void *context __attr_unused__)
{
	struct ip_addr ip;
	int fd;

	fd = net_accept(LOGIN_LISTEN_FD, &ip, NULL);
	if (fd < 0) {
		if (fd < -1)
			i_fatal("accept() failed: %m");
		return;
	}

	if (process_per_connection)
		main_close_listen();

	(void)client_create(fd, &ip, FALSE);
}

static void login_accept_ssl(void *context __attr_unused__)
{
	struct client *client;
	struct ip_addr ip;
	int fd, fd_ssl;

	fd = net_accept(LOGIN_SSL_LISTEN_FD, &ip, NULL);
	if (fd < 0) {
		if (fd < -1)
			i_fatal("accept() failed: %m");
		return;
	}

	if (process_per_connection)
		main_close_listen();

	fd_ssl = ssl_proxy_new(fd);
	if (fd_ssl == -1)
		net_disconnect(fd);
	else
		client = client_create(fd_ssl, &ip, TRUE);
}

static void open_logfile(const char *name)
{
	if (getenv("IMAP_USE_SYSLOG") != NULL)
		i_set_failure_syslog(name, LOG_NDELAY, LOG_MAIL);
	else {
		/* log to file or stderr */
		i_set_failure_file(getenv("LOGFILE"), name);
	}

	if (getenv("INFOLOGFILE") != NULL)
		i_set_info_file(getenv("INFOLOGFILE"));

	i_set_failure_timestamp_format(getenv("LOGSTAMP"));
}

static void drop_privileges(const char *name)
{
	/* Log file or syslog opening probably requires roots */
	open_logfile(name);

	/* Initialize SSL proxy so it can read certificate and private
	   key file. */
	ssl_proxy_init();

	/* Refuse to run as root - we should never need it and it's
	   dangerous with SSL. */
	restrict_access_by_env(TRUE);
}

static void main_init(void)
{
	const char *value;

	lib_init_signals(sig_quit);

	disable_plaintext_auth = getenv("DISABLE_PLAINTEXT_AUTH") != NULL;
	process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL;
        verbose_proctitle = getenv("VERBOSE_PROCTITLE") != NULL;

	value = getenv("MAX_LOGGING_USERS");
	max_logging_users = value == NULL ? 0 : strtoul(value, NULL, 10);

	value = getenv("PROCESS_UID");
	if (value == NULL)
		i_fatal("BUG: PROCESS_UID environment not given");
        login_process_uid = strtoul(value, NULL, 10);
	if (login_process_uid == 0)
		i_fatal("BUG: PROCESS_UID environment is 0");

        closing_down = FALSE;
	main_refcount = 0;

	auth_connection_init();
	clients_init();

	io_listen = io_listen_ssl = NULL;

	if (net_getsockname(LOGIN_LISTEN_FD, NULL, NULL) == 0) {
		/* we're listening for imap */
		io_listen = io_add(LOGIN_LISTEN_FD, IO_READ,
				   login_accept, NULL);
	}

	if (net_getsockname(LOGIN_SSL_LISTEN_FD, NULL, NULL) == 0) {
		/* we're listening for imaps */
		if (!ssl_initialized) {
			/* this shouldn't happen, master should have
			   disabled the imaps socket.. */
			i_fatal("BUG: SSL initialization parameters not given "
				"while they should have been");
		}

		io_listen_ssl = io_add(LOGIN_SSL_LISTEN_FD, IO_READ,
				       login_accept_ssl, NULL);
	}

	/* initialize master last - it sends the "we're ok" notification */
	master_init();
}

static void main_deinit(void)
{
        if (lib_signal_kill != 0)
		i_warning("Killed with signal %d", lib_signal_kill);

	if (io_listen != NULL) io_remove(io_listen);
	if (io_listen_ssl != NULL) io_remove(io_listen_ssl);

	clients_deinit();
	master_deinit();
	auth_connection_deinit();

	ssl_proxy_deinit();

	closelog();
}

int main(int argc __attr_unused__, char *argv[], char *envp[])
{
	const char *name;

#ifdef DEBUG
        fd_debug_verify_leaks(3, 1024);
#endif
	/* NOTE: we start rooted, so keep the code minimal until
	   restrict_access_by_env() is called */
	lib_init();

	name = strrchr(argv[0], '/');
	drop_privileges(name == NULL ? argv[0] : name+1);

	process_title_init(argv, envp);
	ioloop = io_loop_create(system_pool);

	main_init();
        io_loop_run(ioloop);
	main_deinit();

	io_loop_destroy(ioloop);
	lib_deinit();

        return 0;
}

--- NEW FILE: master.c ---
/* Copyright (C) 2002 Timo Sirainen */

#include "common.h"
#include "hash.h"
#include "ioloop.h"
#include "network.h"
#include "fdpass.h"
#include "master.h"
#include "client-common.h"

#include <unistd.h>

static struct io *io_master;
static struct hash_table *master_requests;

static unsigned int master_pos;
static char master_buf[sizeof(struct master_login_reply)];

static void request_handle(struct master_login_reply *reply)
{
	struct client *client;

	client = hash_lookup(master_requests, POINTER_CAST(reply->tag));
	if (client == NULL)
		i_fatal("Master sent reply with unknown tag %u", reply->tag);

	client->master_callback(client, reply->success);

	hash_remove(master_requests, POINTER_CAST(reply->tag));
}

void master_request_imap(struct client *client, master_callback_t *callback,
			 unsigned int auth_pid, unsigned int auth_id)
{
	struct master_login_request req;

	memset(&req, 0, sizeof(req));
	req.tag = client->fd;
	req.auth_pid = auth_pid;
	req.auth_id = auth_id;
	req.ip = client->ip;

	if (fd_send(LOGIN_MASTER_SOCKET_FD,
		    client->fd, &req, sizeof(req)) != sizeof(req))
		i_fatal("fd_send() failed: %m");

	client->master_callback = callback;
	hash_insert(master_requests, POINTER_CAST(req.tag), client);
}

void master_notify_finished(void)
{
	struct master_login_request req;

	if (io_master == NULL)
		return;

	memset(&req, 0, sizeof(req));

	/* sending -1 as fd does the notification */
	if (fd_send(LOGIN_MASTER_SOCKET_FD,
		    -1, &req, sizeof(req)) != sizeof(req))
		i_fatal("fd_send() failed: %m");
}

void master_close(void)
{
	if (io_master == NULL)
		return;

	clients_destroy_all();

	if (close(LOGIN_MASTER_SOCKET_FD) < 0)
		i_fatal("close(master) failed: %m");

	io_remove(io_master);
	io_master = NULL;

        main_close_listen();
	main_unref();
}

static void master_input(void *context __attr_unused__)
{
	int ret;

	ret = net_receive(LOGIN_MASTER_SOCKET_FD, master_buf + master_pos,
			  sizeof(master_buf) - master_pos);
	if (ret < 0) {
		/* master died, kill all clients logging in */
		master_close();
		return;
	}

	master_pos += ret;
	if (master_pos < sizeof(master_buf))
		return;

	/* reply is now read */
	request_handle((struct master_login_reply *) master_buf);
	master_pos = 0;
}

void master_init(void)
{
	main_ref();

	master_requests = hash_create(default_pool, default_pool,
				      0, NULL, NULL);

        master_pos = 0;
	io_master = io_add(LOGIN_MASTER_SOCKET_FD, IO_READ, master_input, NULL);

	/* just a note to master that we're ok. if we die before,
	   master should shutdown itself. */
        master_notify_finished();
}

void master_deinit(void)
{
	hash_destroy(master_requests);

	if (io_master != NULL)
		io_remove(io_master);
}

--- NEW FILE: master.h ---
#ifndef __MASTER_H
#define __MASTER_H

struct client;

#include "../master/master-login-interface.h"

typedef void master_callback_t(struct client *client, int success);

void master_request_imap(struct client *client, master_callback_t *callback,
			 unsigned int auth_pid, unsigned int auth_id);

/* Notify master that we're not listening for new connections anymore. */
void master_notify_finished(void);

/* Close connection to master process */
void master_close(void);

void master_init(void);
void master_deinit(void);

#endif

--- 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>

struct ssl_proxy {
	int refcount;

	gnutls_session session;
	int fd_ssl, fd_plain;
	struct 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;
};

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);
static void plain_input(void *context);
static int ssl_proxy_destroy(struct ssl_proxy *proxy);

static const char *get_alert_text(struct ssl_proxy *proxy)
{
	return gnutls_alert_get_name(gnutls_alert_get(proxy->session));
}

static int handle_ssl_error(struct ssl_proxy *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(struct ssl_proxy *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(struct ssl_proxy *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(struct ssl_proxy *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)
{
        struct ssl_proxy *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)
{
        struct ssl_proxy *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)
{
	struct ssl_proxy *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)
{
	struct ssl_proxy *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)
{
	struct ssl_proxy *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)
{
        struct ssl_proxy *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(struct ssl_proxy, 1);
	proxy->refcount = 1;
	proxy->session = session;
	proxy->fd_ssl = fd;
	proxy->fd_plain = sfd[0];

	proxy->refcount++;
	ssl_handshake(proxy);
	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)
{
	if (level == GCRY_LOG_FATAL) {
		t_push();
		i_error("gcrypt fatal: %s", t_strdup_vprintf(fmt, args));
		t_pop();
	}
}

void ssl_proxy_init(void)
{
	const char *certfile, *keyfile, *paramfile;
	unsigned 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>

enum ssl_state {
	SSL_STATE_HANDSHAKE,
	SSL_STATE_READ,
	SSL_STATE_WRITE
};

struct ssl_proxy {
	int refcount;

	SSL *ssl;
        enum ssl_state state;

	int fd_ssl, fd_plain;
	struct 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;
};

static SSL_CTX *ssl_ctx;

static void plain_read(struct ssl_proxy *proxy);
static void plain_write(struct ssl_proxy *proxy);

static int ssl_proxy_destroy(struct ssl_proxy *proxy);
static void ssl_set_direction(struct ssl_proxy *proxy, int dir);

static void plain_block_input(struct ssl_proxy *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,
				       (io_callback_t *)plain_read, proxy);
		}
	}
}

static void ssl_block(struct ssl_proxy *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(struct ssl_proxy *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 if (ret > 0) {
		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(struct ssl_proxy *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,
					       (io_callback_t *)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;
			}
		}
	}

}

static const char *ssl_last_error(void)
{
	unsigned long err;
	char *buf;
	size_t err_size = 256;

	err = ERR_get_error();
	if (err == 0)
		return strerror(errno);

	buf = t_malloc(err_size);
	buf[err_size-1] = '\0';
	ERR_error_string_n(err, buf, err_size-1);
	return buf;
}

static void ssl_handle_error(struct ssl_proxy *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_warning("%s failed: %s", func, ssl_last_error());*/
		ssl_proxy_destroy(proxy);
		break;
	case SSL_ERROR_ZERO_RETURN:
		/* clean connection closing */
		ssl_proxy_destroy(proxy);
		break;
	case SSL_ERROR_SSL:
		/*i_warning("%s failed: %s", func, ssl_last_error());*/
		ssl_proxy_destroy(proxy);
		break;
	default:
		i_warning("%s failed: unknown failure %d (%s)",
			  func, err, ssl_last_error());
		ssl_proxy_destroy(proxy);
		break;
	}
}

static void ssl_handshake_step(struct ssl_proxy *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(struct ssl_proxy *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(struct ssl_proxy *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)
{
        struct ssl_proxy *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(struct ssl_proxy *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)
{
	struct ssl_proxy *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(struct ssl_proxy, 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(struct ssl_proxy *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

--- NEW FILE: ssl-proxy.c ---
/* Copyright (C) 2002 Timo Sirainen */

#include "lib.h"
#include "ssl-proxy.h"

int ssl_initialized = FALSE;

#ifndef HAVE_SSL

/* no SSL support */

int ssl_proxy_new(int fd __attr_unused__) { return -1; }
void ssl_proxy_init(void) {}
void ssl_proxy_deinit(void) {}

#endif

--- NEW FILE: ssl-proxy.h ---
#ifndef __SSL_PROXY_H
#define __SSL_PROXY_H

extern int ssl_initialized;

/* establish SSL connection with the given fd, returns a new fd which you
   must use from now on, or -1 if error occured. Unless -1 is returned,
   the given fd must be simply forgotten. */
int ssl_proxy_new(int fd);

void ssl_proxy_init(void);
void ssl_proxy_deinit(void);

#endif




More information about the dovecot-cvs mailing list