passdb can return ssl=yes, ssl=any-cert and starttls options.
--HG--
branch : HEAD
{
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;
*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;
+ 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, "user") == 0) {
/* already handled in login-common */
- } else if (login_settings->auth_debug) {
- i_info("Ignoring unknown passdb extra field: %s",
- *args);
- }
+ } else if (login_settings->auth_debug)
+ i_info("Ignoring unknown passdb extra field: %s", key);
}
if (destuser == NULL)
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;
}
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;
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;
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;
(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;
}
}
-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 (input == NULL) {
- if (client->proxy == NULL) {
- /* we're just freeing the proxy */
- return;
- }
+ if (client->proxy == NULL) {
+ /* we're just freeing the proxy */
+ return;
+ }
+ input = login_proxy_get_istream(client->proxy);
+ if (input == NULL) {
if (client->destroyed) {
/* we came here from client_destroy() */
return;
}
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);
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);
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
#include "str-sanitize.h"
#include "master-service.h"
#include "client-common.h"
+#include "ssl-proxy.h"
#include "login-proxy.h"
#define MAX_PROXY_INPUT_SIZE 4096
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;
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)
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;
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);
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 */
DLLIST_REMOVE(&login_proxies, proxy);
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);
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;
return proxy->port;
}
+enum login_proxy_ssl_flags
+login_proxy_get_ssl_flags(const struct login_proxy *proxy)
+{
+ return proxy->ssl_flags;
+}
+
void login_proxy_detach(struct login_proxy *proxy, struct istream *client_input,
struct ostream *client_output)
{
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;
DLLIST_PREPEND(&login_proxies, proxy);
}
+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;
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. */
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;
void login_proxy_deinit(void);
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 {
};
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;
{
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;
-
- ssl_set_io(proxy, SSL_ADD_INPUT);
- plain_block_input(proxy, FALSE);
+ 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);
+
+ if (proxy->handshake_callback(proxy->handshake_callback_context) < 0)
+ ssl_proxy_destroy(proxy);
}
static void ssl_read(struct ssl_proxy *proxy)
ssl_proxy_unref(proxy);
}
-int ssl_proxy_new(int fd, const struct ip_addr *ip, struct ssl_proxy **proxy_r)
+static int
+ssl_proxy_new_common(SSL_CTX *ssl_ctx, int fd, const struct ip_addr *ip,
+ struct ssl_proxy **proxy_r)
{
struct ssl_proxy *proxy;
SSL *ssl;
ssl_proxy_count++;
DLLIST_PREPEND(&ssl_proxies, proxy);
- ssl_step(proxy);
-
*proxy_r = proxy;
return sfd[1];
}
+int ssl_proxy_new(int fd, const 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;
return strstr(buf, "PRIVATE KEY---") != NULL;
}
-void ssl_proxy_init(void)
+static void
+ssl_proxy_ctx_init(SSL_CTX *ssl_ctx, const struct login_settings *set)
{
- static char dovecot[] = "dovecot";
- const struct login_settings *set = login_settings;
- unsigned char buf;
- char *password;
- unsigned long err;
-
- if (strcmp(set->ssl, "no") == 0)
- 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");
-
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL);
- if (SSL_CTX_set_cipher_list(ssl_ctx, set->ssl_cipher_list) != 1) {
- i_fatal("Can't set cipher list to '%s': %s",
- set->ssl_cipher_list, ssl_last_error());
- }
-
if (*set->ssl_ca_file != '\0') {
if (SSL_CTX_load_verify_locations(ssl_ctx, set->ssl_ca_file,
NULL) != 1) {
set->ssl_ca_file, ssl_last_error());
}
}
+ if (set->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);
+}
+
+static void
+ssl_proxy_ctx_verify_client(SSL_CTX *ssl_ctx, const struct login_settings *set)
+{
+#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(set->ssl_ca_file));
+}
+
+static void ssl_proxy_init_server(const struct login_settings *set)
+{
+ char *password;
+ unsigned long err;
+
+ if ((ssl_server_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
+ i_fatal("SSL_CTX_new() failed");
+ ssl_proxy_ctx_init(ssl_server_ctx, set);
+
+ if (SSL_CTX_set_cipher_list(ssl_server_ctx, set->ssl_cipher_list) != 1) {
+ i_fatal("Can't set cipher list to '%s': %s",
+ set->ssl_cipher_list, ssl_last_error());
+ }
- if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
+ if (SSL_CTX_use_certificate_chain_file(ssl_server_ctx,
set->ssl_cert_file) != 1) {
err = ERR_peek_error();
if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
}
password = t_strdup_noconst(set->ssl_key_password);
- 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, set->ssl_key_file,
+ 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, set->ssl_key_file,
SSL_FILETYPE_PEM) != 1) {
i_fatal("Can't load private key file %s: %s",
set->ssl_key_file, ssl_last_error());
}
safe_memset(password, 0, strlen(password));
- 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 (set->verbose_ssl)
- SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback);
+ SSL_CTX_set_info_callback(ssl_server_ctx, ssl_info_callback);
- if (set->ssl_verify_client_cert) {
-#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(set->ssl_ca_file));
- }
+ if (set->ssl_verify_client_cert)
+ ssl_proxy_ctx_verify_client(ssl_server_ctx, set);
ssl_username_nid = OBJ_txt2nid(set->ssl_cert_username_field);
if (ssl_username_nid == NID_undef) {
i_fatal("Invalid ssl_cert_username_field: %s",
set->ssl_cert_username_field);
}
+}
+
+static void ssl_proxy_init_client(const struct login_settings *set)
+{
+ if ((ssl_client_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
+ i_fatal("SSL_CTX_new() failed");
+ ssl_proxy_ctx_init(ssl_client_ctx, set);
+ ssl_proxy_ctx_verify_client(ssl_client_ctx, set);
+}
+
+void ssl_proxy_init(void)
+{
+ const struct login_settings *set = login_settings;
+ static char dovecot[] = "dovecot";
+ unsigned char buf;
+
+ if (strcmp(set->ssl, "no") == 0)
+ return;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ extdata_index = SSL_get_ex_new_index(0, dovecot, NULL, NULL, NULL);
+ ssl_proxy_init_server(set);
+ ssl_proxy_init_client(set);
/* PRNG initialization might want to use /dev/urandom, make sure it
does it before chrooting. We might not have enough entropy at
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();
}
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;
#ifndef SSL_PROXY_H
#define SSL_PROXY_H
+#include "ioloop.h"
+
struct ip_addr;
struct ssl_proxy;
extern bool ssl_initialized;
+typedef int ssl_handshake_callback_t(void *context);
+
/* establish SSL 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, const struct ip_addr *ip, struct ssl_proxy **proxy_r);
+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);
{
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, "user") == 0) {
/* already handled in login-common */
- } else if (login_settings->auth_debug) {
- i_info("Ignoring unknown passdb extra field: %s",
- *args);
- }
+ } else if (login_settings->auth_debug)
+ i_info("Ignoring unknown passdb extra field: %s", key);
}
if (destuser == NULL)
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;
}
/* 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;
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;
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(
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)
}
(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;
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 (input == NULL) {
- if (client->proxy == NULL) {
- /* we're just freeing the proxy */
- return;
- }
+ if (client->proxy == NULL) {
+ /* we're just freeing the proxy */
+ return;
+ }
+ input = login_proxy_get_istream(client->proxy);
+ if (input == NULL) {
if (client->destroyed) {
/* we came here from client_destroy() */
return;
}
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);
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);
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