changeset 8985:f43bebab3dac HEAD

imap/pop3 proxy: Support SSL/TLS connections to remote servers. passdb can return ssl=yes, ssl=any-cert and starttls options.
author Timo Sirainen <tss@iki.fi>
date Tue, 28 Apr 2009 22:31:40 -0400
parents e7bfb3c134f9
children d475e17d01a3
files src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/client.h src/imap-login/imap-proxy.c src/imap-login/imap-proxy.h src/login-common/login-proxy.c src/login-common/login-proxy.h src/login-common/ssl-proxy-openssl.c src/login-common/ssl-proxy.c src/login-common/ssl-proxy.h src/pop3-login/client-authenticate.c src/pop3-login/client.c src/pop3-login/client.h src/pop3-login/pop3-proxy.c src/pop3-login/pop3-proxy.h
diffstat 15 files changed, 520 insertions(+), 214 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap-login/client-authenticate.c	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/imap-login/client-authenticate.c	Tue Apr 28 22:31:40 2009 -0400
@@ -124,6 +124,8 @@
 {
 	const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL;
 	const char *master_user = NULL;
+	const char *key, *value, *p;
+	enum login_proxy_ssl_flags ssl_flags = 0;
 	string_t *reply;
 	unsigned int port = 143;
 	bool proxy = FALSE, temp = FALSE, nologin = !success;
@@ -131,33 +133,49 @@
 
 	*nodelay_r = FALSE;
 	for (; *args != NULL; args++) {
-		if (strcmp(*args, "nologin") == 0)
+		p = strchr(*args, '=');
+		if (p == NULL) {
+			key = *args;
+			value = "";
+		} else {
+			key = t_strdup_until(*args, p);
+			value = p + 1;
+		}
+		if (strcmp(key, "nologin") == 0)
 			nologin = TRUE;
-		else if (strcmp(*args, "nodelay") == 0)
+		else if (strcmp(key, "nodelay") == 0)
 			*nodelay_r = TRUE;
-		else if (strcmp(*args, "proxy") == 0)
+		else if (strcmp(key, "proxy") == 0)
 			proxy = TRUE;
-		else if (strcmp(*args, "temp") == 0)
+		else if (strcmp(key, "temp") == 0)
 			temp = TRUE;
-		else if (strcmp(*args, "authz") == 0)
+		else if (strcmp(key, "authz") == 0)
 			authz_failure = TRUE;
-		else if (strncmp(*args, "reason=", 7) == 0)
-			reason = *args + 7;
-		else if (strncmp(*args, "host=", 5) == 0)
-			host = *args + 5;
-		else if (strncmp(*args, "port=", 5) == 0)
-			port = atoi(*args + 5);
-		else if (strncmp(*args, "destuser=", 9) == 0)
-			destuser = *args + 9;
-		else if (strncmp(*args, "pass=", 5) == 0)
-			pass = *args + 5;
-		else if (strncmp(*args, "master=", 7) == 0)
-			master_user = *args + 7;
-		else if (strncmp(*args, "user=", 5) == 0) {
+		else if (strcmp(key, "reason") == 0)
+			reason = value + 7;
+		else if (strcmp(key, "host") == 0)
+			host = value;
+		else if (strcmp(key, "port") == 0)
+			port = atoi(value);
+		else if (strcmp(key, "destuser") == 0)
+			destuser = value;
+		else if (strcmp(key, "pass") == 0)
+			pass = value;
+		else if (strcmp(key, "master") == 0)
+			master_user = value;
+		else if (strcmp(key, "ssl") == 0) {
+			if (strcmp(value, "yes") == 0)
+				ssl_flags |= PROXY_SSL_FLAG_YES;
+			else if (strcmp(value, "any-cert") == 0) {
+				ssl_flags |= PROXY_SSL_FLAG_YES |
+					PROXY_SSL_FLAG_ANY_CERT;
+			}
+		} else if (strcmp(key, "starttls") == 0) {
+			ssl_flags |= PROXY_SSL_FLAG_STARTTLS;
+		} else if (strcmp(key, "user") == 0) {
 			/* already handled in login-common */
 		} else if (auth_debug) {
-			i_info("Ignoring unknown passdb extra field: %s",
-			       *args);
+			i_info("Ignoring unknown passdb extra field: %s", key);
 		}
 	}
 
@@ -173,7 +191,7 @@
 		if (!success)
 			return FALSE;
 		if (imap_proxy_new(client, host, port, destuser, master_user,
-				   pass) < 0)
+				   pass, ssl_flags) < 0)
 			client_auth_failed(client, TRUE);
 		return TRUE;
 	}
--- a/src/imap-login/client.c	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/imap-login/client.c	Tue Apr 28 22:31:40 2009 -0400
@@ -556,10 +556,11 @@
 	if (client->output != NULL)
 		o_stream_close(client->output);
 
-	if (client->common.master_tag != 0)
+	if (client->common.master_tag != 0) {
+		i_assert(client->common.auth_request == NULL);
+		i_assert(client->common.authenticating);
 		master_request_abort(&client->common);
-
-	if (client->common.auth_request != NULL) {
+	} else if (client->common.auth_request != NULL) {
 		i_assert(client->common.authenticating);
 		sasl_server_auth_client_error(&client->common, NULL);
 	} else {
--- a/src/imap-login/client.h	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/imap-login/client.h	Tue Apr 28 22:31:40 2009 -0400
@@ -30,7 +30,8 @@
 
 	unsigned int login_success:1;
 	unsigned int cmd_finished:1;
-	unsigned int proxy_login_sent:1;
+	unsigned int proxy_sasl_ir:1;
+	unsigned int proxy_seen_banner:1;
 	unsigned int skip_line:1;
 	unsigned int input_blocked:1;
 	unsigned int destroyed:1;
--- a/src/imap-login/imap-proxy.c	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/imap-login/imap-proxy.c	Tue Apr 28 22:31:40 2009 -0400
@@ -136,9 +136,35 @@
 	str_printfa(str, "* CAPABILITY %s\r\n", capability);
 }
 
+static void proxy_write_login(struct imap_client *client, string_t *str)
+{
+	if (client->capability_command_used)
+		str_append(str, "C CAPABILITY\r\n");
+
+	if (client->proxy_master_user == NULL) {
+		/* logging in normally - use LOGIN command */
+		str_append(str, "L LOGIN ");
+		imap_quote_append_string(str, client->proxy_user, FALSE);
+		str_append_c(str, ' ');
+		imap_quote_append_string(str, client->proxy_password, FALSE);
+
+		proxy_free_password(client);
+	} else if (client->proxy_sasl_ir) {
+		/* master user login with SASL initial response support */
+		str_append(str, "L AUTHENTICATE PLAIN ");
+		get_plain_auth(client, str);
+		proxy_free_password(client);
+	} else {
+		/* master user login without SASL initial response */
+		str_append(str, "L AUTHENTICATE PLAIN");
+	}
+	str_append(str, "\r\n");
+}
+
 static int proxy_input_banner(struct imap_client *client,
 			      struct ostream *output, const char *line)
 {
+	enum login_proxy_ssl_flags ssl_flags;
 	const char *const *capabilities = NULL;
 	string_t *str;
 
@@ -154,44 +180,39 @@
 		capabilities = t_strsplit(t_strcut(line + 5 + 12, ']'), " ");
 		if (str_array_icase_find(capabilities, "ID"))
 			proxy_write_id(client, str);
+		if (str_array_icase_find(capabilities, "SASL-IR"))
+			client->proxy_sasl_ir = TRUE;
 	}
-	if (client->capability_command_used)
-		str_append(str, "C CAPABILITY\r\n");
-
-	if (client->proxy_master_user == NULL) {
-		/* logging in normally - use LOGIN command */
-		str_append(str, "L LOGIN ");
-		imap_quote_append_string(str, client->proxy_user, FALSE);
-		str_append_c(str, ' ');
-		imap_quote_append_string(str, client->proxy_password, FALSE);
 
-		proxy_free_password(client);
-	} else if (capabilities != NULL &&
-		   str_array_icase_find(capabilities, "SASL-IR")) {
-		/* master user login with SASL initial response support */
-		str_append(str, "L AUTHENTICATE PLAIN ");
-		get_plain_auth(client, str);
-		proxy_free_password(client);
+	ssl_flags = login_proxy_get_ssl_flags(client->proxy);
+	if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) != 0) {
+		if (capabilities != NULL &&
+		    !str_array_icase_find(capabilities, "STARTTLS")) {
+			client_syslog_err(&client->common,
+				"proxy: Remote doesn't support STARTTLS");
+			return -1;
+		}
+		str_append(str, "S STARTTLS\r\n");
 	} else {
-		/* master user login without SASL initial response */
-		str_append(str, "L AUTHENTICATE PLAIN");
+		proxy_write_login(client, str);
 	}
-	str_append(str, "\r\n");
+
 	(void)o_stream_send(output, str_data(str), str_len(str));
-	client->proxy_login_sent = TRUE;
 	return 0;
 }
 
-static int proxy_input_line(struct imap_client *client,
-			    struct ostream *output, const char *line)
+static int proxy_input_line(struct imap_client *client, const char *line)
 {
+	struct ostream *output;
 	const char *capability;
 	string_t *str;
 
 	i_assert(!client->destroyed);
 
-	if (!client->proxy_login_sent) {
+	output = login_proxy_get_ostream(client->proxy);
+	if (!client->proxy_seen_banner) {
 		/* this is a banner */
+		client->proxy_seen_banner = TRUE;
 		if (proxy_input_banner(client, output, line) < 0) {
 			proxy_failed(client, TRUE);
 			return -1;
@@ -206,6 +227,26 @@
 
 		(void)o_stream_send(output, str_data(str), str_len(str));
 		return 0;
+	} else if (strncmp(line, "S ", 2) == 0) {
+		if (strncmp(line, "S OK ", 5) != 0) {
+			/* STARTTLS failed */
+			client_syslog_err(&client->common, t_strdup_printf(
+				"proxy: Remote STARTTLS failed: %s",
+				str_sanitize(line + 5, 160)));
+			proxy_failed(client, TRUE);
+			return -1;
+		}
+		/* STARTTLS successful, begin TLS negotiation. */
+		if (login_proxy_starttls(client->proxy) < 0) {
+			proxy_failed(client, TRUE);
+			return -1;
+		}
+		/* i/ostreams changed. */
+		output = login_proxy_get_ostream(client->proxy);
+		str = t_str_new(128);
+		proxy_write_login(client, str);
+		(void)o_stream_send(output, str_data(str), str_len(str));
+		return 1;
 	} else if (strncmp(line, "L OK ", 5) == 0) {
 		/* Login successful. Send this line to client. */
 		capability = client->proxy_backend_capability;
@@ -306,17 +347,18 @@
 	}
 }
 
-static void proxy_input(struct istream *input, struct ostream *output,
-			struct imap_client *client)
+static void proxy_input(struct imap_client *client)
 {
+	struct istream *input;
 	const char *line;
 
+	if (client->proxy == NULL) {
+		/* we're just freeing the proxy */
+		return;
+	}
+
+	input = login_proxy_get_istream(client->proxy);
 	if (input == NULL) {
-		if (client->proxy == NULL) {
-			/* we're just freeing the proxy */
-			return;
-		}
-
 		if (client->destroyed) {
 			/* we came here from client_destroy() */
 			return;
@@ -343,14 +385,14 @@
 	}
 
 	while ((line = i_stream_next_line(input)) != NULL) {
-		if (proxy_input_line(client, output, line) != 0)
+		if (proxy_input_line(client, line) != 0)
 			break;
 	}
 }
 
 int imap_proxy_new(struct imap_client *client, const char *host,
 		   unsigned int port, const char *user, const char *master_user,
-		   const char *password)
+		   const char *password, enum login_proxy_ssl_flags ssl_flags)
 {
 	i_assert(user != NULL);
 	i_assert(!client->destroyed);
@@ -375,14 +417,15 @@
 		return -1;
 	}
 
-	client->proxy = login_proxy_new(&client->common, host, port,
+	client->proxy = login_proxy_new(&client->common, host, port, ssl_flags,
 					proxy_input, client);
 	if (client->proxy == NULL) {
 		client_send_tagline(client, PROXY_FAILURE_MSG);
 		return -1;
 	}
 
-	client->proxy_login_sent = FALSE;
+	client->proxy_sasl_ir = FALSE;
+	client->proxy_seen_banner = FALSE;
 	client->proxy_user = i_strdup(user);
 	client->proxy_master_user = i_strdup(master_user);
 	client->proxy_password = i_strdup(password);
--- a/src/imap-login/imap-proxy.h	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/imap-login/imap-proxy.h	Tue Apr 28 22:31:40 2009 -0400
@@ -5,6 +5,6 @@
 
 int imap_proxy_new(struct imap_client *client, const char *hosts,
 		   unsigned int port, const char *user, const char *master_user,
-		   const char *password);
+		   const char *password, enum login_proxy_ssl_flags ssl_flags);
 
 #endif
--- a/src/login-common/login-proxy.c	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/login-common/login-proxy.c	Tue Apr 28 22:31:40 2009 -0400
@@ -7,6 +7,7 @@
 #include "llist.h"
 #include "str-sanitize.h"
 #include "client-common.h"
+#include "ssl-proxy.h"
 #include "login-proxy.h"
 
 #define MAX_PROXY_INPUT_SIZE 4096
@@ -15,19 +16,23 @@
 struct login_proxy {
 	struct login_proxy *prev, *next;
 
+	struct client *prelogin_client;
 	int client_fd, server_fd;
 	struct io *client_io, *server_io;
 	struct istream *server_input;
 	struct ostream *client_output, *server_output;
 	struct ip_addr ip;
+	struct ssl_proxy *ssl_proxy;
 
 	char *host, *user;
 	unsigned int port;
+	enum login_proxy_ssl_flags ssl_flags;
 
 	proxy_callback_t *callback;
 	void *context;
 
 	unsigned int destroying:1;
+	unsigned int disconnecting:1;
 };
 
 static struct login_proxy *login_proxies = NULL;
@@ -107,8 +112,19 @@
 
 static void proxy_prelogin_input(struct login_proxy *proxy)
 {
-	proxy->callback(proxy->server_input, proxy->server_output,
-			proxy->context);
+	proxy->callback(proxy->context);
+}
+
+static void proxy_plain_connected(struct login_proxy *proxy)
+{
+	proxy->server_input =
+		i_stream_create_fd(proxy->server_fd, MAX_PROXY_INPUT_SIZE,
+				   FALSE);
+	proxy->server_output =
+		o_stream_create_fd(proxy->server_fd, (size_t)-1, FALSE);
+
+	proxy->server_io =
+		io_add(proxy->server_fd, IO_READ, proxy_prelogin_input, proxy);
 }
 
 static void proxy_wait_connect(struct login_proxy *proxy)
@@ -123,21 +139,22 @@
 		return;
 	}
 
-	/* connect successful */
-	proxy->server_input =
-		i_stream_create_fd(proxy->server_fd, MAX_PROXY_INPUT_SIZE,
-				   FALSE);
-	proxy->server_output =
-		o_stream_create_fd(proxy->server_fd, (size_t)-1, FALSE);
-
-	io_remove(&proxy->server_io);
-	proxy->server_io =
-		io_add(proxy->server_fd, IO_READ, proxy_prelogin_input, proxy);
+	if ((proxy->ssl_flags & PROXY_SSL_FLAG_YES) != 0 &&
+	    (proxy->ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) {
+		if (login_proxy_starttls(proxy) < 0) {
+			login_proxy_free(&proxy);
+			return;
+		}
+	} else {
+		io_remove(&proxy->server_io);
+		proxy_plain_connected(proxy);
+	}
 }
 
 #undef login_proxy_new
 struct login_proxy *
 login_proxy_new(struct client *client, const char *host, unsigned int port,
+		enum login_proxy_ssl_flags ssl_flags,
 		proxy_callback_t *callback, void *context)
 {
 	struct login_proxy *proxy;
@@ -166,6 +183,8 @@
 	proxy->host = i_strdup(host);
 	proxy->user = i_strdup(client->virtual_user);
 	proxy->port = port;
+	proxy->ssl_flags = ssl_flags;
+	proxy->prelogin_client = client;
 
 	proxy->server_fd = fd;
 	proxy->server_io = io_add(fd, IO_WRITE, proxy_wait_connect, proxy);
@@ -189,6 +208,13 @@
 		return;
 	proxy->destroying = TRUE;
 
+	if (proxy->server_io != NULL)
+		io_remove(&proxy->server_io);
+	if (proxy->server_input != NULL)
+		i_stream_destroy(&proxy->server_input);
+	if (proxy->server_output != NULL)
+		o_stream_destroy(&proxy->server_output);
+
 	if (proxy->client_fd != -1) {
 		/* detached proxy */
 		main_unref();
@@ -209,15 +235,11 @@
 		i_assert(proxy->client_io == NULL);
 		i_assert(proxy->client_output == NULL);
 
-		proxy->callback(NULL, NULL, proxy->context);
+		proxy->callback(proxy->context);
 	}
 
-	if (proxy->server_io != NULL)
-		io_remove(&proxy->server_io);
-	if (proxy->server_input != NULL)
-		i_stream_destroy(&proxy->server_input);
-	if (proxy->server_output != NULL)
-		o_stream_destroy(&proxy->server_output);
+	if (proxy->ssl_proxy != NULL)
+		ssl_proxy_free(proxy->ssl_proxy);
 	net_disconnect(proxy->server_fd);
 
 	i_free(proxy->host);
@@ -243,6 +265,16 @@
 	return strcmp(client->virtual_user, destuser) == 0;
 }
 
+struct istream *login_proxy_get_istream(struct login_proxy *proxy)
+{
+	return proxy->disconnecting ? NULL : proxy->server_input;
+}
+
+struct ostream *login_proxy_get_ostream(struct login_proxy *proxy)
+{
+	return proxy->server_output;
+}
+
 const char *login_proxy_get_host(const struct login_proxy *proxy)
 {
 	return proxy->host;
@@ -253,6 +285,12 @@
 	return proxy->port;
 }
 
+enum login_proxy_ssl_flags
+login_proxy_get_ssl_flags(const struct login_proxy *proxy)
+{
+	return proxy->ssl_flags;
+}
+
 unsigned int login_proxy_get_count(void)
 {
 	return login_proxy_count;
@@ -267,6 +305,7 @@
 	i_assert(proxy->client_fd == -1);
 	i_assert(proxy->server_output != NULL);
 
+	proxy->prelogin_client = NULL;
 	proxy->client_fd = i_stream_get_fd(client_input);
 	proxy->client_output = client_output;
 
@@ -296,6 +335,52 @@
 	main_ref();
 }
 
+static int login_proxy_ssl_handshaked(void *context)
+{
+	struct login_proxy *proxy = context;
+
+	if ((proxy->ssl_flags & PROXY_SSL_FLAG_ANY_CERT) != 0 ||
+	    ssl_proxy_has_valid_client_cert(proxy->ssl_proxy))
+		return 0;
+
+	if (!ssl_proxy_has_broken_client_cert(proxy->ssl_proxy)) {
+		client_syslog_err(proxy->prelogin_client, t_strdup_printf(
+			"proxy: SSL certificate not received from %s:%u",
+			proxy->host, proxy->port));
+	} else {
+		client_syslog_err(proxy->prelogin_client, t_strdup_printf(
+			"proxy: Received invalid SSL certificate from %s:%u",
+			proxy->host, proxy->port));
+	}
+	proxy->disconnecting = TRUE;
+	return -1;
+}
+
+int login_proxy_starttls(struct login_proxy *proxy)
+{
+	int fd;
+
+	if (proxy->server_input != NULL)
+		i_stream_destroy(&proxy->server_input);
+	if (proxy->server_output != NULL)
+		o_stream_destroy(&proxy->server_output);
+	io_remove(&proxy->server_io);
+
+	fd = ssl_proxy_client_new(proxy->server_fd, &proxy->ip,
+				  login_proxy_ssl_handshaked, proxy,
+				  &proxy->ssl_proxy);
+	if (fd < 0) {
+		client_syslog_err(proxy->prelogin_client, t_strdup_printf(
+			"proxy: SSL handshake failed to %s:%u",
+			proxy->host, proxy->port));
+		return -1;
+	}
+
+	proxy->server_fd = fd;
+	proxy_plain_connected(proxy);
+	return 0;
+}
+
 void login_proxy_deinit(void)
 {
 	struct login_proxy *proxy;
--- a/src/login-common/login-proxy.h	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/login-common/login-proxy.h	Tue Apr 28 22:31:40 2009 -0400
@@ -3,24 +3,32 @@
 
 struct login_proxy;
 
+enum login_proxy_ssl_flags {
+	/* Use SSL/TLS enabled */
+	PROXY_SSL_FLAG_YES	= 0x01,
+	/* Don't do SSL handshake immediately after connected */
+	PROXY_SSL_FLAG_STARTTLS	= 0x02,
+	/* Don't require that the received certificate is valid */
+	PROXY_SSL_FLAG_ANY_CERT	= 0x04
+};
+
 /* Called when new input comes from proxy. */
-typedef void proxy_callback_t(struct istream *input, struct ostream *output,
-			      void *context);
+typedef void proxy_callback_t(void *context);
 
 /* Create a proxy to given host. Returns NULL if failed. Given callback is
    called when new input is available from proxy. */
 struct login_proxy *
 login_proxy_new(struct client *client, const char *host, unsigned int port,
+		enum login_proxy_ssl_flags ssl_flags,
 		proxy_callback_t *callback, void *context);
 #ifdef CONTEXT_TYPE_SAFETY
-#  define login_proxy_new(client, host, port, callback, context) \
-	({(void)(1 ? 0 : callback((struct istream *)NULL, \
-				  (struct ostream *)NULL, context)); \
-	  login_proxy_new(client, host, port, \
+#  define login_proxy_new(client, host, port, ssl_flags, callback, context) \
+	({(void)(1 ? 0 : callback(context)); \
+	  login_proxy_new(client, host, port, ssl_flags, \
 		(proxy_callback_t *)callback, context); })
 #else
-#  define login_proxy_new(client, host, port, callback, context) \
-	  login_proxy_new(client, host, port, \
+#  define login_proxy_new(client, host, port, ssl_flags, callback, context) \
+	  login_proxy_new(client, host, port, ssl_flags, \
 		(proxy_callback_t *)callback, context)
 #endif
 /* Free the proxy. This should be called if authentication fails. */
@@ -36,8 +44,16 @@
 void login_proxy_detach(struct login_proxy *proxy, struct istream *client_input,
 			struct ostream *client_output);
 
+/* STARTTLS command was issued. */
+int login_proxy_starttls(struct login_proxy *proxy);
+
+struct istream *login_proxy_get_istream(struct login_proxy *proxy);
+struct ostream *login_proxy_get_ostream(struct login_proxy *proxy);
+
 const char *login_proxy_get_host(const struct login_proxy *proxy) ATTR_PURE;
 unsigned int login_proxy_get_port(const struct login_proxy *proxy) ATTR_PURE;
+enum login_proxy_ssl_flags
+login_proxy_get_ssl_flags(const struct login_proxy *proxy) ATTR_PURE;
 
 /* Return number of active detached login proxies */
 unsigned int login_proxy_get_count(void) ATTR_PURE;
--- a/src/login-common/ssl-proxy-openssl.c	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/login-common/ssl-proxy-openssl.c	Tue Apr 28 22:31:40 2009 -0400
@@ -49,11 +49,15 @@
 	unsigned char sslout_buf[1024];
 	unsigned int sslout_size;
 
+	ssl_handshake_callback_t *handshake_callback;
+	void *handshake_callback_context;
+
 	char *last_error;
 	unsigned int handshaked:1;
 	unsigned int destroyed:1;
 	unsigned int cert_received:1;
 	unsigned int cert_broken:1;
+	unsigned int client:1;
 };
 
 struct ssl_parameters {
@@ -65,7 +69,8 @@
 };
 
 static int extdata_index;
-static SSL_CTX *ssl_ctx;
+static SSL_CTX *ssl_server_ctx;
+static SSL_CTX *ssl_client_ctx;
 static unsigned int ssl_proxy_count;
 static struct ssl_proxy *ssl_proxies;
 static struct ssl_parameters ssl_params;
@@ -396,16 +401,27 @@
 {
 	int ret;
 
-	ret = SSL_accept(proxy->ssl);
-	if (ret != 1)
-		ssl_handle_error(proxy, ret, "SSL_accept()");
-	else {
-		i_free_and_null(proxy->last_error);
-		proxy->handshaked = TRUE;
+	if (proxy->client) {
+		ret = SSL_connect(proxy->ssl);
+		if (ret != 1) {
+			ssl_handle_error(proxy, ret, "SSL_connect()");
+			return;
+		}
+	} else {
+		ret = SSL_accept(proxy->ssl);
+		if (ret != 1) {
+			ssl_handle_error(proxy, ret, "SSL_accept()");
+			return;
+		}
+	}
+	i_free_and_null(proxy->last_error);
+	proxy->handshaked = TRUE;
 
-		ssl_set_io(proxy, SSL_ADD_INPUT);
-		plain_block_input(proxy, FALSE);
-	}
+	ssl_set_io(proxy, SSL_ADD_INPUT);
+	plain_block_input(proxy, FALSE);
+
+	if (proxy->handshake_callback(proxy->handshake_callback_context) < 0)
+		ssl_proxy_destroy(proxy);
 }
 
 static void ssl_read(struct ssl_proxy *proxy)
@@ -473,7 +489,8 @@
 	ssl_proxy_unref(proxy);
 }
 
-int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r)
+static int ssl_proxy_new_common(SSL_CTX *ssl_ctx, int fd, struct ip_addr *ip,
+				struct ssl_proxy **proxy_r)
 {
 	struct ssl_proxy *proxy;
 	SSL *ssl;
@@ -523,13 +540,39 @@
 	ssl_proxy_count++;
 	DLLIST_PREPEND(&ssl_proxies, proxy);
 
-	ssl_step(proxy);
 	main_ref();
 
 	*proxy_r = proxy;
 	return sfd[1];
 }
 
+int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r)
+{
+	int ret;
+
+	if ((ret = ssl_proxy_new_common(ssl_server_ctx, fd, ip, proxy_r)) < 0)
+		return -1;
+
+	ssl_step(*proxy_r);
+	return ret;
+}
+
+int ssl_proxy_client_new(int fd, struct ip_addr *ip,
+			 ssl_handshake_callback_t *callback, void *context,
+			 struct ssl_proxy **proxy_r)
+{
+	int ret;
+
+	if ((ret = ssl_proxy_new_common(ssl_client_ctx, fd, ip, proxy_r)) < 0)
+		return -1;
+
+	(*proxy_r)->handshake_callback = callback;
+	(*proxy_r)->handshake_callback_context = context;
+	(*proxy_r)->client = TRUE;
+	ssl_step(*proxy_r);
+	return ret;
+}
+
 bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy)
 {
 	return proxy->cert_received && !proxy->cert_broken;
@@ -737,53 +780,63 @@
 	return strstr(buf, "PRIVATE KEY---") != NULL;
 }
 
-void ssl_proxy_init(void)
+static void ssl_proxy_ctx_init(SSL_CTX *ssl_ctx)
 {
-	static char dovecot[] = "dovecot";
-	const char *cafile, *certfile, *keyfile, *cipher_list, *username_field;
-	char *password;
-	unsigned char buf;
-	unsigned long err;
-
-	memset(&ssl_params, 0, sizeof(ssl_params));
-
-	cafile = getenv("SSL_CA_FILE");
-	certfile = getenv("SSL_CERT_FILE");
-	keyfile = getenv("SSL_KEY_FILE");
-	ssl_params.fname = getenv("SSL_PARAM_FILE");
-	password = getenv("SSL_KEY_PASSWORD");
-
-	if (certfile == NULL || keyfile == NULL || ssl_params.fname == NULL) {
-		/* SSL support is disabled */
-		return;
-	}
-
-	SSL_library_init();
-	SSL_load_error_strings();
-
-	extdata_index = SSL_get_ex_new_index(0, dovecot, NULL, NULL, NULL);
-
-	if ((ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
-		i_fatal("SSL_CTX_new() failed");
+	const char *cafile;
 
 	SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL);
 
-	cipher_list = getenv("SSL_CIPHER_LIST");
-	if (cipher_list == NULL)
-		cipher_list = DOVECOT_SSL_DEFAULT_CIPHER_LIST;
-	if (SSL_CTX_set_cipher_list(ssl_ctx, cipher_list) != 1) {
-		i_fatal("Can't set cipher list to '%s': %s",
-			cipher_list, ssl_last_error());
-	}
-
+	cafile = getenv("SSL_CA_FILE");
 	if (cafile != NULL) {
 		if (SSL_CTX_load_verify_locations(ssl_ctx, cafile, NULL) != 1) {
 			i_fatal("Can't load CA file %s: %s",
 				cafile, ssl_last_error());
 		}
 	}
+	if (verbose_ssl)
+		SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback);
+	if (SSL_CTX_need_tmp_RSA(ssl_ctx))
+		SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key);
+	SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback);
+}
 
-	if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile) != 1) {
+static void ssl_proxy_ctx_verify_client(SSL_CTX *ssl_ctx)
+{
+	const char *cafile;
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+	X509_STORE *store;
+
+	store = SSL_CTX_get_cert_store(ssl_ctx);
+	X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
+			     X509_V_FLAG_CRL_CHECK_ALL);
+#endif
+	cafile = getenv("SSL_CA_FILE");
+	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,
+			   ssl_verify_client_cert);
+	SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(cafile));
+}
+
+static void ssl_proxy_init_server(const char *certfile, const char *keyfile)
+{
+	const char *cipher_list, *username_field;
+	char *password;
+	unsigned long err;
+
+	password = getenv("SSL_KEY_PASSWORD");
+
+	if ((ssl_server_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
+		i_fatal("SSL_CTX_new() failed");
+	ssl_proxy_ctx_init(ssl_server_ctx);
+
+	cipher_list = getenv("SSL_CIPHER_LIST");
+	if (cipher_list == NULL)
+		cipher_list = DOVECOT_SSL_DEFAULT_CIPHER_LIST;
+	if (SSL_CTX_set_cipher_list(ssl_server_ctx, cipher_list) != 1) {
+		i_fatal("Can't set cipher list to '%s': %s",
+			cipher_list, ssl_last_error());
+	}
+
+	if (SSL_CTX_use_certificate_chain_file(ssl_server_ctx, certfile) != 1) {
 		err = ERR_peek_error();
 		if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
 		    ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
@@ -801,35 +854,16 @@
 		}
 	}
 
-        SSL_CTX_set_default_passwd_cb(ssl_ctx, pem_password_callback);
-        SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, password);
-	if (SSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile,
+        SSL_CTX_set_default_passwd_cb(ssl_server_ctx, pem_password_callback);
+        SSL_CTX_set_default_passwd_cb_userdata(ssl_server_ctx, password);
+	if (SSL_CTX_use_PrivateKey_file(ssl_server_ctx, keyfile,
 					SSL_FILETYPE_PEM) != 1) {
 		i_fatal("Can't load private key file %s: %s",
 			keyfile, ssl_last_error());
 	}
 
-	if (SSL_CTX_need_tmp_RSA(ssl_ctx))
-		SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key);
-	SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback);
-
-	if (verbose_ssl)
-		SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback);
-
-	if (getenv("SSL_VERIFY_CLIENT_CERT") != NULL) {
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L
-		X509_STORE *store;
-
-		store = SSL_CTX_get_cert_store(ssl_ctx);
-		X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
-				     X509_V_FLAG_CRL_CHECK_ALL);
-#endif
-		SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER |
-				   SSL_VERIFY_CLIENT_ONCE,
-				   ssl_verify_client_cert);
-		SSL_CTX_set_client_CA_list(ssl_ctx,
-					   SSL_load_client_CA_file(cafile));
-	}
+	if (getenv("SSL_VERIFY_CLIENT_CERT") != NULL)
+		ssl_proxy_ctx_verify_client(ssl_server_ctx);
 
 	username_field = getenv("SSL_CERT_USERNAME_FIELD");
 	if (username_field == NULL)
@@ -841,6 +875,39 @@
 				username_field);
 		}
 	}
+}
+
+static void ssl_proxy_init_client(void)
+{
+	if ((ssl_client_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
+		i_fatal("SSL_CTX_new() failed");
+	ssl_proxy_ctx_init(ssl_client_ctx);
+	ssl_proxy_ctx_verify_client(ssl_client_ctx);
+}
+
+void ssl_proxy_init(void)
+{
+	static char dovecot[] = "dovecot";
+	const char *certfile, *keyfile;
+	unsigned char buf;
+
+	memset(&ssl_params, 0, sizeof(ssl_params));
+
+	certfile = getenv("SSL_CERT_FILE");
+	keyfile = getenv("SSL_KEY_FILE");
+	ssl_params.fname = getenv("SSL_PARAM_FILE");
+
+	if (certfile == NULL || keyfile == NULL || ssl_params.fname == NULL) {
+		/* SSL support is disabled */
+		return;
+	}
+
+	SSL_library_init();
+	SSL_load_error_strings();
+
+	extdata_index = SSL_get_ex_new_index(0, dovecot, NULL, NULL, NULL);
+	ssl_proxy_init_server(certfile, keyfile);
+	ssl_proxy_init_client();
 
 	/* PRNG initialization might want to use /dev/urandom, make sure it
 	   does it before chrooting. We might not have enough entropy at
@@ -862,7 +929,8 @@
 		ssl_proxy_destroy(ssl_proxies);
 
 	ssl_free_parameters(&ssl_params);
-	SSL_CTX_free(ssl_ctx);
+	SSL_CTX_free(ssl_server_ctx);
+	SSL_CTX_free(ssl_client_ctx);
 	EVP_cleanup();
 	ERR_free_strings();
 }
--- a/src/login-common/ssl-proxy.c	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/login-common/ssl-proxy.c	Tue Apr 28 22:31:40 2009 -0400
@@ -16,6 +16,15 @@
 	return -1;
 }
 
+int ssl_proxy_client_new(int fd ATTR_UNUSED, struct ip_addr *ip ATTR_UNUSED,
+			 ssl_handshake_callback_t *callback ATTR_UNUSED,
+			 void *context ATTR_UNUSED,
+			 struct ssl_proxy **proxy_r ATTR_UNUSED)
+{
+	i_error("Dovecot wasn't built with SSL support");
+	return -1;
+}
+
 bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy ATTR_UNUSED)
 {
 	return FALSE;
--- a/src/login-common/ssl-proxy.h	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/login-common/ssl-proxy.h	Tue Apr 28 22:31:40 2009 -0400
@@ -1,15 +1,23 @@
 #ifndef SSL_PROXY_H
 #define SSL_PROXY_H
 
+#include "ioloop.h"
+
 struct ip_addr;
 struct ssl_proxy;
 
 extern bool ssl_initialized;
 
-/* establish SSL connection with the given fd, returns a new fd which you
+typedef int ssl_handshake_callback_t(void *context);
+
+/* establish SSL server connection with the given fd, returns a new fd which you
    must use from now on, or -1 if error occurred. Unless -1 is returned,
    the given fd must be simply forgotten. */
 int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r);
+/* establish SSL client connection */
+int ssl_proxy_client_new(int fd, struct ip_addr *ip,
+			 ssl_handshake_callback_t *callback, void *context,
+			 struct ssl_proxy **proxy_r);
 bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy) ATTR_PURE;
 bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy);
 const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy);
--- a/src/pop3-login/client-authenticate.c	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/pop3-login/client-authenticate.c	Tue Apr 28 22:31:40 2009 -0400
@@ -127,37 +127,55 @@
 {
 	const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL;
 	const char *master_user = NULL;
+	const char *key, *value, *p;
+	enum login_proxy_ssl_flags ssl_flags = 0;
 	string_t *reply;
 	unsigned int port = 110;
 	bool proxy = FALSE, temp = FALSE, nologin = !success;
 
 	*nodelay_r = FALSE;
 	for (; *args != NULL; args++) {
-		if (strcmp(*args, "nologin") == 0)
+		p = strchr(*args, '=');
+		if (p == NULL) {
+			key = *args;
+			value = "";
+		} else {
+			key = t_strdup_until(*args, p);
+			value = p + 1;
+		}
+		if (strcmp(key, "nologin") == 0)
 			nologin = TRUE;
-		else if (strcmp(*args, "nodelay") == 0)
+		else if (strcmp(key, "nodelay") == 0)
 			*nodelay_r = TRUE;
-		else if (strcmp(*args, "proxy") == 0)
+		else if (strcmp(key, "proxy") == 0)
 			proxy = TRUE;
-		else if (strcmp(*args, "temp") == 0)
+		else if (strcmp(key, "temp") == 0)
 			temp = TRUE;
-		else if (strncmp(*args, "reason=", 7) == 0)
-			reason = *args + 7;
-		else if (strncmp(*args, "host=", 5) == 0)
-			host = *args + 5;
-		else if (strncmp(*args, "port=", 5) == 0)
-			port = atoi(*args + 5);
-		else if (strncmp(*args, "destuser=", 9) == 0)
-			destuser = *args + 9;
-		else if (strncmp(*args, "pass=", 5) == 0)
-			pass = *args + 5;
-		else if (strncmp(*args, "master=", 7) == 0)
-			master_user = *args + 7;
-		else if (strncmp(*args, "user=", 5) == 0) {
+		else if (strcmp(key, "reason") == 0)
+			reason = value;
+		else if (strcmp(key, "host") == 0)
+			host = value;
+		else if (strcmp(key, "port") == 0)
+			port = atoi(value);
+		else if (strcmp(key, "destuser") == 0)
+			destuser = value;
+		else if (strcmp(key, "pass") == 0)
+			pass = value;
+		else if (strcmp(key, "master") == 0)
+			master_user = value;
+		else if (strcmp(key, "ssl") == 0) {
+			if (strcmp(value, "yes") == 0)
+				ssl_flags |= PROXY_SSL_FLAG_YES;
+			else if (strcmp(value, "any-cert") == 0) {
+				ssl_flags |= PROXY_SSL_FLAG_YES |
+					PROXY_SSL_FLAG_ANY_CERT;
+			}
+		} else if (strcmp(key, "starttls") == 0) {
+			ssl_flags |= PROXY_SSL_FLAG_STARTTLS;
+		} else if (strcmp(key, "user") == 0) {
 			/* already handled in login-common */
 		} else if (auth_debug) {
-			i_info("Ignoring unknown passdb extra field: %s",
-			       *args);
+			i_info("Ignoring unknown passdb extra field: %s", key);
 		}
 	}
 
@@ -173,7 +191,7 @@
 		if (!success)
 			return FALSE;
 		if (pop3_proxy_new(client, host, port, destuser, master_user,
-				   pass) < 0)
+				   pass, ssl_flags) < 0)
 			client_auth_failed(client, TRUE);
 		return TRUE;
 	}
--- a/src/pop3-login/client.c	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/pop3-login/client.c	Tue Apr 28 22:31:40 2009 -0400
@@ -364,10 +364,11 @@
 	if (client->output != NULL)
 		o_stream_close(client->output);
 
-	if (client->common.master_tag != 0)
+	if (client->common.master_tag != 0) {
+		i_assert(client->common.auth_request == NULL);
+		i_assert(client->common.authenticating);
 		master_request_abort(&client->common);
-
-	if (client->common.auth_request != NULL) {
+	} else if (client->common.auth_request != NULL) {
 		i_assert(client->common.authenticating);
 		sasl_server_auth_client_error(&client->common, NULL);
 	} else {
--- a/src/pop3-login/client.h	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/pop3-login/client.h	Tue Apr 28 22:31:40 2009 -0400
@@ -9,6 +9,13 @@
 /* Disconnect client after idling this many milliseconds */
 #define CLIENT_LOGIN_IDLE_TIMEOUT_MSECS (3*60*1000)
 
+enum pop3_proxy_state {
+	POP3_PROXY_BANNER = 0,
+	POP3_PROXY_STARTTLS,
+	POP3_PROXY_LOGIN1,
+	POP3_PROXY_LOGIN2
+};
+
 struct pop3_client {
 	struct client common;
 
@@ -21,7 +28,7 @@
 
 	struct login_proxy *proxy;
 	char *proxy_user, *proxy_master_user, *proxy_password;
-	int proxy_state;
+	enum pop3_proxy_state proxy_state;
 
 	unsigned int bad_counter;
 
--- a/src/pop3-login/pop3-proxy.c	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/pop3-login/pop3-proxy.c	Tue Apr 28 22:31:40 2009 -0400
@@ -49,15 +49,35 @@
 	base64_encode(str_data(str), str_len(str), dest);
 }
 
-static int proxy_input_line(struct pop3_client *client,
-			    struct ostream *output, const char *line)
+static void proxy_send_login(struct pop3_client *client, struct ostream *output)
 {
 	string_t *str;
 
+	str = t_str_new(128);
+	if (client->proxy_master_user == NULL) {
+		/* send USER command */
+		str_append(str, "USER ");
+		str_append(str, client->proxy_user);
+		str_append(str, "\r\n");
+	} else {
+		/* master user login - use AUTH PLAIN. */
+		str_append(str, "AUTH PLAIN\r\n");
+	}
+	(void)o_stream_send(output, str_data(str), str_len(str));
+	client->proxy_state = POP3_PROXY_LOGIN1;
+}
+
+static int proxy_input_line(struct pop3_client *client, const char *line)
+{
+	struct ostream *output;
+	enum login_proxy_ssl_flags ssl_flags;
+	string_t *str;
+
 	i_assert(!client->destroyed);
 
+	output = login_proxy_get_ostream(client->proxy);
 	switch (client->proxy_state) {
-	case 0:
+	case POP3_PROXY_BANNER:
 		/* this is a banner */
 		if (strncmp(line, "+OK", 3) != 0) {
 			client_syslog_err(&client->common, t_strdup_printf(
@@ -67,21 +87,31 @@
 			return -1;
 		}
 
-		str = t_str_new(128);
-		if (client->proxy_master_user == NULL) {
-			/* send USER command */
-			str_append(str, "USER ");
-			str_append(str, client->proxy_user);
-			str_append(str, "\r\n");
+		ssl_flags = login_proxy_get_ssl_flags(client->proxy);
+		if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) {
+			proxy_send_login(client, output);
 		} else {
-			/* master user login - use AUTH PLAIN. */
-			str_append(str, "AUTH PLAIN\r\n");
+			(void)o_stream_send_str(output, "STLS\r\n");
+			client->proxy_state = POP3_PROXY_STARTTLS;
 		}
-		(void)o_stream_send(output, str_data(str), str_len(str));
-
-		client->proxy_state++;
 		return 0;
-	case 1:
+	case POP3_PROXY_STARTTLS:
+		if (strncmp(line, "+OK", 3) != 0) {
+			client_syslog_err(&client->common, t_strdup_printf(
+				"proxy: Remote STLS failed: %s",
+				str_sanitize(line, 160)));
+			proxy_failed(client, TRUE);
+			return -1;
+		}
+		if (login_proxy_starttls(client->proxy) < 0) {
+			proxy_failed(client, TRUE);
+			return -1;
+		}
+		/* i/ostreams changed. */
+		output = login_proxy_get_ostream(client->proxy);
+		proxy_send_login(client, output);
+		return 1;
+	case POP3_PROXY_LOGIN1:
 		str = t_str_new(128);
 		if (client->proxy_master_user == NULL) {
 			if (strncmp(line, "+OK", 3) != 0)
@@ -100,9 +130,9 @@
 		}
 		(void)o_stream_send(output, str_data(str), str_len(str));
 		proxy_free_password(client);
-		client->proxy_state++;
+		client->proxy_state = POP3_PROXY_LOGIN2;
 		return 0;
-	case 2:
+	case POP3_PROXY_LOGIN2:
 		if (strncmp(line, "+OK", 3) != 0)
 			break;
 
@@ -184,17 +214,18 @@
 	return -1;
 }
 
-static void proxy_input(struct istream *input, struct ostream *output,
-			struct pop3_client *client)
+static void proxy_input(struct pop3_client *client)
 {
+	struct istream *input;
 	const char *line;
 
+	if (client->proxy == NULL) {
+		/* we're just freeing the proxy */
+		return;
+	}
+
+	input = login_proxy_get_istream(client->proxy);
 	if (input == NULL) {
-		if (client->proxy == NULL) {
-			/* we're just freeing the proxy */
-			return;
-		}
-
 		if (client->destroyed) {
 			/* we came here from client_destroy() */
 			return;
@@ -221,14 +252,14 @@
 	}
 
 	while ((line = i_stream_next_line(input)) != NULL) {
-		if (proxy_input_line(client, output, line) != 0)
+		if (proxy_input_line(client, line) != 0)
 			break;
 	}
 }
 
 int pop3_proxy_new(struct pop3_client *client, const char *host,
 		   unsigned int port, const char *user, const char *master_user,
-		   const char *password)
+		   const char *password, enum login_proxy_ssl_flags ssl_flags)
 {
 	i_assert(user != NULL);
 	i_assert(!client->destroyed);
@@ -253,14 +284,14 @@
 		return -1;
 	}
 
-	client->proxy = login_proxy_new(&client->common, host, port,
+	client->proxy = login_proxy_new(&client->common, host, port, ssl_flags,
 					proxy_input, client);
 	if (client->proxy == NULL) {
 		client_send_line(client, PROXY_FAILURE_MSG);
 		return -1;
 	}
 
-	client->proxy_state = 0;
+	client->proxy_state = POP3_PROXY_BANNER;
 	client->proxy_user = i_strdup(user);
 	client->proxy_master_user = i_strdup(master_user);
 	client->proxy_password = i_strdup(password);
--- a/src/pop3-login/pop3-proxy.h	Tue Apr 28 19:57:10 2009 -0400
+++ b/src/pop3-login/pop3-proxy.h	Tue Apr 28 22:31:40 2009 -0400
@@ -5,6 +5,6 @@
 
 int pop3_proxy_new(struct pop3_client *client, const char *host,
 		   unsigned int port, const char *user, const char *master_user,
-		   const char *password);
+		   const char *password, enum login_proxy_ssl_flags ssl_flags);
 
 #endif