From: Timo Sirainen Date: Mon, 31 Jan 2011 16:41:04 +0000 (+0200) Subject: imapc: Added initial support for SSL. X-Git-Tag: 2.1.alpha1~388 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bfc7fcde95de729c7e5f18cca7566a3cf754c0ce;p=thirdparty%2Fdovecot%2Fcore.git imapc: Added initial support for SSL. --- diff --git a/configure.in b/configure.in index e1e1b1ae20..021ce5e898 100644 --- a/configure.in +++ b/configure.in @@ -2429,7 +2429,7 @@ dbox_common_libs='$(top_builddir)/src/lib-storage/index/dbox-common/libstorage_d 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' diff --git a/src/lib-storage/index/imapc/Makefile.am b/src/lib-storage/index/imapc/Makefile.am index 4f47d890d0..5f9e375992 100644 --- a/src/lib-storage/index/imapc/Makefile.am +++ b/src/lib-storage/index/imapc/Makefile.am @@ -4,6 +4,7 @@ AM_CPPFLAGS = \ -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 \ diff --git a/src/lib-storage/index/imapc/imapc-client-private.h b/src/lib-storage/index/imapc/imapc-client-private.h index ff8e27ef85..d8442ab058 100644 --- a/src/lib-storage/index/imapc/imapc-client-private.h +++ b/src/lib-storage/index/imapc/imapc-client-private.h @@ -11,6 +11,7 @@ struct imapc_client_connection { 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; diff --git a/src/lib-storage/index/imapc/imapc-client.c b/src/lib-storage/index/imapc/imapc-client.c index 325fd440f0..4939560cb0 100644 --- a/src/lib-storage/index/imapc/imapc-client.c +++ b/src/lib-storage/index/imapc/imapc-client.c @@ -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 @@ struct imapc_client * 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 @@ imapc_client_init(const struct imapc_client_settings *set) 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 @@ void imapc_client_deinit(struct imapc_client **_client) *_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); diff --git a/src/lib-storage/index/imapc/imapc-client.h b/src/lib-storage/index/imapc/imapc-client.h index 072a4895d7..79026011c5 100644 --- a/src/lib-storage/index/imapc/imapc-client.h +++ b/src/lib-storage/index/imapc/imapc-client.h @@ -23,6 +23,12 @@ struct imapc_capability_name { }; 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 @@ struct imapc_client_settings { 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 { diff --git a/src/lib-storage/index/imapc/imapc-connection.c b/src/lib-storage/index/imapc/imapc-connection.c index a5f6e7f362..9f9bb1c7c9 100644 --- a/src/lib-storage/index/imapc/imapc-connection.c +++ b/src/lib-storage/index/imapc/imapc-connection.c @@ -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 imapc_connection { 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 @@ void imapc_connection_deinit(struct imapc_connection **_conn) *_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 @@ static void imapc_connection_disconnect(struct imapc_connection *conn) 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 int imapc_connection_input_one(struct imapc_connection *conn) 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 @@ static void imapc_connection_connected(struct imapc_connection *conn) } 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 @@ void imapc_connection_input_pending(struct imapc_connection *conn) 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; diff --git a/src/lib-storage/index/imapc/imapc-storage.c b/src/lib-storage/index/imapc/imapc-storage.c index 9177c22280..184c7ab1cb 100644 --- a/src/lib-storage/index/imapc/imapc-storage.c +++ b/src/lib-storage/index/imapc/imapc-storage.c @@ -163,7 +163,7 @@ imapc_storage_create(struct mail_storage *_storage, { 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 @@ imapc_storage_create(struct mail_storage *_storage, 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);