changeset 12617:7b7434fef6ff

imapc: Added initial support for SSL.
author Timo Sirainen <tss@iki.fi>
date Mon, 31 Jan 2011 18:41:04 +0200
parents bd23d4e10fa1
children ac84693374ad
files configure.in src/lib-storage/index/imapc/Makefile.am src/lib-storage/index/imapc/imapc-client-private.h src/lib-storage/index/imapc/imapc-client.c src/lib-storage/index/imapc/imapc-client.h src/lib-storage/index/imapc/imapc-connection.c src/lib-storage/index/imapc/imapc-storage.c
diffstat 7 files changed, 120 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Mon Jan 31 18:40:27 2011 +0200
+++ b/configure.in	Mon Jan 31 18:41:04 2011 +0200
@@ -2429,7 +2429,7 @@
 sdbox_libs='$(top_builddir)/src/lib-storage/index/dbox-single/libstorage_dbox_single.la'
 mdbox_libs='$(top_builddir)/src/lib-storage/index/dbox-multi/libstorage_dbox_multi.la'
 cydir_libs='$(top_builddir)/src/lib-storage/index/cydir/libstorage_cydir.la'
-imapc_libs='$(top_builddir)/src/lib-storage/index/imapc/libstorage_imapc.la'
+imapc_libs='$(top_builddir)/src/lib-storage/index/imapc/libstorage_imapc.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la'
 raw_libs='$(top_builddir)/src/lib-storage/index/raw/libstorage_raw.la'
 shared_libs='$(top_builddir)/src/lib-storage/index/shared/libstorage_shared.la'
 
--- a/src/lib-storage/index/imapc/Makefile.am	Mon Jan 31 18:40:27 2011 +0200
+++ b/src/lib-storage/index/imapc/Makefile.am	Mon Jan 31 18:41:04 2011 +0200
@@ -4,6 +4,7 @@
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-test \
 	-I$(top_srcdir)/src/lib-dns \
+	-I$(top_srcdir)/src/lib-ssl-iostream \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
 	-I$(top_srcdir)/src/lib-index \
--- a/src/lib-storage/index/imapc/imapc-client-private.h	Mon Jan 31 18:40:27 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-client-private.h	Mon Jan 31 18:41:04 2011 +0200
@@ -11,6 +11,7 @@
 struct imapc_client {
 	pool_t pool;
 	struct imapc_client_settings set;
+	struct ssl_iostream_context *ssl_ctx;
 
 	imapc_untagged_callback_t *untagged_callback;
 	void *untagged_context;
--- a/src/lib-storage/index/imapc/imapc-client.c	Mon Jan 31 18:40:27 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-client.c	Mon Jan 31 18:41:04 2011 +0200
@@ -5,6 +5,7 @@
 #include "str.h"
 #include "ioloop.h"
 #include "safe-mkstemp.h"
+#include "iostream-ssl.h"
 #include "imapc-seqmap.h"
 #include "imapc-connection.h"
 #include "imapc-client-private.h"
@@ -33,6 +34,8 @@
 imapc_client_init(const struct imapc_client_settings *set)
 {
 	struct imapc_client *client;
+	struct ssl_iostream_settings ssl_set;
+	const char *source;
 	pool_t pool;
 
 	pool = pool_alloconly_create("imapc client", 1024);
@@ -48,6 +51,23 @@
 		p_strdup(pool, set->dns_client_socket_path);
 	client->set.temp_path_prefix =
 		p_strdup(pool, set->temp_path_prefix);
+
+	if (set->ssl_mode != IMAPC_CLIENT_SSL_MODE_NONE) {
+		client->set.ssl_mode = set->ssl_mode;
+		client->set.ssl_ca_dir = p_strdup(pool, set->ssl_ca_dir);
+
+		memset(&ssl_set, 0, sizeof(ssl_set));
+		ssl_set.ca_dir = set->ssl_ca_dir;
+		ssl_set.verify_remote_cert = TRUE;
+
+		source = t_strdup_printf("%s:%u", set->host, set->port);
+		if (ssl_iostream_context_init_client(source, &ssl_set,
+						     &client->ssl_ctx) < 0) {
+			i_error("imapc(%s): Couldn't initialize SSL context",
+				source);
+		}
+	}
+
 	p_array_init(&client->conns, pool, 8);
 	return client;
 }
@@ -59,6 +79,8 @@
 
 	*_client = NULL;
 
+	if (client->ssl_ctx != NULL)
+		ssl_iostream_context_deinit(&client->ssl_ctx);
 	array_foreach_modifiable(&client->conns, connp)
 		imapc_connection_deinit(&(*connp)->conn);
 	pool_unref(&client->pool);
--- a/src/lib-storage/index/imapc/imapc-client.h	Mon Jan 31 18:40:27 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-client.h	Mon Jan 31 18:41:04 2011 +0200
@@ -23,6 +23,12 @@
 };
 extern const struct imapc_capability_name imapc_capability_names[];
 
+enum imapc_client_ssl_mode {
+	IMAPC_CLIENT_SSL_MODE_NONE,
+	IMAPC_CLIENT_SSL_MODE_IMMEDIATE,
+	IMAPC_CLIENT_SSL_MODE_STARTTLS
+};
+
 struct imapc_client_settings {
 	const char *host;
 	unsigned int port;
@@ -33,6 +39,9 @@
 
 	const char *dns_client_socket_path;
 	const char *temp_path_prefix;
+
+	enum imapc_client_ssl_mode ssl_mode;
+	const char *ssl_ca_dir;
 };
 
 struct imapc_command_reply {
--- a/src/lib-storage/index/imapc/imapc-connection.c	Mon Jan 31 18:40:27 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-connection.c	Mon Jan 31 18:41:04 2011 +0200
@@ -9,6 +9,7 @@
 #include "write-full.h"
 #include "str.h"
 #include "dns-lookup.h"
+#include "iostream-ssl.h"
 #include "imap-quote.h"
 #include "imap-util.h"
 #include "imap-parser.h"
@@ -68,6 +69,8 @@
 	struct imap_parser *parser;
 	struct timeout *to;
 
+	struct ssl_iostream *ssl_iostream;
+
 	int (*input_callback)(struct imapc_connection *conn);
 	enum imapc_input_state input_state;
 	unsigned int cur_tag;
@@ -123,7 +126,8 @@
 	*_conn = NULL;
 
 	imapc_connection_disconnect(conn);
-	p_strsplit_free(default_pool, conn->capabilities_list);
+	if (conn->capabilities_list != NULL)
+		p_strsplit_free(default_pool, conn->capabilities_list);
 	array_free(&conn->cmd_send_queue);
 	array_free(&conn->cmd_wait_list);
 	array_free(&conn->literal_files);
@@ -215,6 +219,8 @@
 		timeout_remove(&conn->to);
 	imap_parser_destroy(&conn->parser);
 	io_remove(&conn->io);
+	if (conn->ssl_iostream != NULL)
+		ssl_iostream_unref(&conn->ssl_iostream);
 	i_stream_destroy(&conn->input);
 	o_stream_destroy(&conn->output);
 	net_disconnect(conn->fd);
@@ -835,15 +841,73 @@
 
 static void imapc_connection_input(struct imapc_connection *conn)
 {
-	if (i_stream_read(conn->input) == -1) {
+	ssize_t ret;
+
+	if ((ret = i_stream_read(conn->input)) > 0)
+		imapc_connection_input_pending(conn);
+	else if (ret < 0) {
 		/* disconnected */
-		i_error("imapc(%s): Server disconnected unexpectedly",
-			conn->name);
+		if (conn->ssl_iostream == NULL) {
+			i_error("imapc(%s): Server disconnected unexpectedly",
+				conn->name);
+		} else {
+			i_error("imapc(%s): Server disconnected: %s", conn->name,
+				ssl_iostream_get_last_error(conn->ssl_iostream));
+		}
 		imapc_connection_disconnect(conn);
 		return;
 	}
+}
 
-	imapc_connection_input_pending(conn);
+static int imapc_connection_ssl_handshaked(void *context)
+{
+	struct imapc_connection *conn = context;
+
+	if (ssl_iostream_has_valid_client_cert(conn->ssl_iostream))
+		return 0;
+
+	if (!ssl_iostream_has_broken_client_cert(conn->ssl_iostream)) {
+		i_error("imapc(%s): SSL certificate not received", conn->name);
+	} else {
+		i_error("imapc(%s): Received invalid SSL certificate",
+			conn->name);
+	}
+	i_stream_close(conn->input);
+	return -1;
+}
+
+static int imapc_connection_ssl_init(struct imapc_connection *conn)
+{
+	struct ssl_iostream_settings ssl_set;
+	const char *source;
+
+	if (conn->client->ssl_ctx == NULL) {
+		i_error("imapc(%s): No SSL context", conn->name);
+		return -1;
+	}
+
+	memset(&ssl_set, 0, sizeof(ssl_set));
+	ssl_set.verbose_invalid_cert = TRUE;
+	ssl_set.verify_remote_cert = TRUE;
+	ssl_set.require_valid_cert = TRUE;
+
+	source = t_strdup_printf("imapc(%s): ", conn->name);
+	if (io_stream_create_ssl(conn->client->ssl_ctx, source, &ssl_set,
+				 &conn->input, &conn->output,
+				 &conn->ssl_iostream) < 0) {
+		i_error("imapc(%s): Couldn't initialize SSL client",
+			conn->name);
+		return -1;
+	}
+	ssl_iostream_set_handshake_callback(conn->ssl_iostream,
+					    imapc_connection_ssl_handshaked,
+					    conn);
+	if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
+		i_error("imapc(%s): SSL handshake failed", conn->name);
+		return -1;
+	}
+	imap_parser_set_streams(conn->parser, conn->input, NULL);
+	return 0;
 }
 
 static void imapc_connection_connected(struct imapc_connection *conn)
@@ -861,6 +925,11 @@
 	}
 	io_remove(&conn->io);
 	conn->io = io_add(conn->fd, IO_READ, imapc_connection_input, conn);
+
+	if (conn->client->set.ssl_mode == IMAPC_CLIENT_SSL_MODE_IMMEDIATE) {
+		if (imapc_connection_ssl_init(conn) < 0)
+			imapc_connection_disconnect(conn);
+	}
 }
 
 static void imapc_connection_timeout(struct imapc_connection *conn)
@@ -956,7 +1025,7 @@
 		return;
 
 	o_stream_cork(conn->output);
-	while (ret > 0 && !conn->client->stop_now) {
+	while (ret > 0 && !conn->client->stop_now && conn->input != NULL) {
 		T_BEGIN {
 			ret = imapc_connection_input_one(conn);
 		} T_END;
--- a/src/lib-storage/index/imapc/imapc-storage.c	Mon Jan 31 18:40:27 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-storage.c	Mon Jan 31 18:41:04 2011 +0200
@@ -163,7 +163,7 @@
 {
 	struct imapc_storage *storage = (struct imapc_storage *)_storage;
 	struct imapc_client_settings set;
-	const char *port;
+	const char *port, *value;
 	string_t *str;
 
 	memset(&set, 0, sizeof(set));
@@ -196,6 +196,16 @@
 	mail_user_set_get_temp_prefix(str, _storage->user->set);
 	set.temp_path_prefix = str_c(str);
 
+	set.ssl_ca_dir = mail_user_plugin_getenv(_storage->user,
+						 "imapc_ssl_ca_dir");
+	if (set.ssl_ca_dir != NULL) {
+		value = mail_user_plugin_getenv(_storage->user,
+						"imapc_ssl_starttls");
+		set.ssl_mode = value != NULL ?
+			IMAPC_CLIENT_SSL_MODE_STARTTLS :
+			IMAPC_CLIENT_SSL_MODE_IMMEDIATE;
+	}
+
 	storage->list = (struct imapc_mailbox_list *)ns->list;
 	storage->list->storage = storage;
 	storage->client = imapc_client_init(&set);