Mercurial > dovecot > core-2.2
changeset 9756:e30495ae11de HEAD
*-login: Moved most of the common code to login-common.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 09 Aug 2009 21:53:14 -0400 |
parents | c2037505a66b |
children | 1c01e6bf1090 |
files | src/imap-login/client-authenticate.c src/imap-login/client-authenticate.h 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/Makefile.am src/login-common/client-common-auth.c src/login-common/client-common.c src/login-common/client-common.h src/login-common/common.h src/login-common/login-proxy.c src/login-common/login-proxy.h src/login-common/main.c src/login-common/sasl-server.c 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-authenticate.h src/pop3-login/client.c src/pop3-login/client.h src/pop3-login/pop3-proxy.c src/pop3-login/pop3-proxy.h |
diffstat | 24 files changed, 1314 insertions(+), 1936 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap-login/client-authenticate.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/imap-login/client-authenticate.c Sun Aug 09 21:53:14 2009 -0400 @@ -18,18 +18,14 @@ #include <stdlib.h> -#define AUTH_FAILURE_DELAY_INCREASE_MSECS 5000 - -#define IMAP_SERVICE_NAME "imap" - -const char *client_authenticate_get_capabilities(struct imap_client *client) +const char *client_authenticate_get_capabilities(struct client *client) { const struct auth_mech_desc *mech; unsigned int i, count; string_t *str; str = t_str_new(128); - mech = sasl_server_get_advertised_mechs(&client->common, &count); + mech = sasl_server_get_advertised_mechs(client, &count); for (i = 0; i < count; i++) { str_append_c(str, ' '); str_append(str, "AUTH="); @@ -39,146 +35,13 @@ return str_c(str); } -static void client_auth_input(struct imap_client *client) -{ - char *line; - - if (!client_read(client)) - return; - - if (client->skip_line) { - if (i_stream_next_line(client->common.input) == NULL) - return; - - client->skip_line = FALSE; - } - - /* @UNSAFE */ - line = i_stream_next_line(client->common.input); - if (line == NULL) - return; - - if (strcmp(line, "*") == 0) - sasl_server_auth_abort(&client->common); - else { - client_set_auth_waiting(client); - auth_client_request_continue(client->common.auth_request, line); - io_remove(&client->io); - - /* clear sensitive data */ - safe_memset(line, 0, strlen(line)); - } -} - -static void client_authfail_delay_timeout(struct imap_client *client) +bool imap_client_auth_handle_reply(struct client *client, + const struct client_auth_reply *reply) { - timeout_remove(&client->to_authfail_delay); - - /* get back to normal client input. */ - i_assert(client->io == NULL); - client->io = io_add(client->common.fd, IO_READ, client_input, client); - client_input(client); -} - -void client_auth_failed(struct imap_client *client, bool nodelay) -{ - unsigned int delay_msecs; - - i_free_and_null(client->common.master_data_prefix); - - if (client->auth_initializing) - return; - - if (client->io != NULL) - io_remove(&client->io); - if (nodelay) { - client->io = io_add(client->common.fd, IO_READ, - client_input, client); - client_input(client); - return; - } - - /* increase the timeout after each unsuccessful attempt, but don't - increase it so high that the idle timeout would be triggered */ - delay_msecs = client->common.auth_attempts * - AUTH_FAILURE_DELAY_INCREASE_MSECS; - if (delay_msecs > CLIENT_LOGIN_IDLE_TIMEOUT_MSECS) - delay_msecs = CLIENT_LOGIN_IDLE_TIMEOUT_MSECS - 1000; + struct imap_client *imap_client = (struct imap_client *)client; + string_t *str; - i_assert(client->to_authfail_delay == NULL); - client->to_authfail_delay = - timeout_add(delay_msecs, client_authfail_delay_timeout, client); -} - -static bool client_handle_args(struct imap_client *client, - const char *const *args, bool success, - bool *nodelay_r) -{ - 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; - bool authz_failure = FALSE; - - *nodelay_r = FALSE; - for (; *args != NULL; args++) { - 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(key, "nodelay") == 0) - *nodelay_r = TRUE; - else if (strcmp(key, "proxy") == 0) - proxy = TRUE; - else if (strcmp(key, "temp") == 0) - temp = TRUE; - else if (strcmp(key, "authz") == 0) - authz_failure = TRUE; - 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 (client->common.set->auth_debug) - i_info("Ignoring unknown passdb extra field: %s", key); - } - - if (destuser == NULL) - destuser = client->common.virtual_user; - - if (proxy) { - /* we want to proxy the connection to another server. - don't do this unless authentication succeeded. with - master user proxying we can get FAIL with proxy still set. - - proxy host=.. [port=..] [destuser=..] pass=.. */ - if (!success) - return FALSE; - if (imap_proxy_new(client, host, port, destuser, master_user, - pass, ssl_flags) < 0) - client_auth_failed(client, TRUE); - return TRUE; - } - - if (host != NULL) { + if (reply->host != NULL) { /* IMAP referral [nologin] referral host=.. [port=..] [destuser=..] @@ -188,47 +51,46 @@ OK [...] Logged in, but you should use this server instead. .. [REFERRAL ..] (Reason from auth server) */ - reply = t_str_new(128); - str_append(reply, client->cmd_tag); - str_append_c(reply, ' '); - str_append(reply, nologin ? "NO " : "OK "); - str_printfa(reply, "[REFERRAL imap://%s;AUTH=%s@%s", - destuser, client->common.auth_mech_name, host); - if (port != 143) - str_printfa(reply, ":%u", port); - str_append(reply, "/] "); - if (reason != NULL) - str_append(reply, reason); - else if (nologin) - str_append(reply, "Try this server instead."); + str = t_str_new(128); + str_append(str, imap_client->cmd_tag); + str_append_c(str, ' '); + str_append(str, reply->nologin ? "NO " : "OK "); + str_printfa(str, "[REFERRAL imap://%s;AUTH=%s@%s", + reply->destuser, client->auth_mech_name, + reply->host); + if (reply->port != 143) + str_printfa(str, ":%u", reply->port); + str_append(str, "/] "); + if (reply->reason != NULL) + str_append(str, reply->reason); + else if (reply->nologin) + str_append(str, "Try this server instead."); else { - str_append(reply, "Logged in, but you should use " + str_append(str, "Logged in, but you should use " "this server instead."); } - str_append(reply, "\r\n"); - client_send_raw(client, str_c(reply)); - if (!nologin) { + str_append(str, "\r\n"); + client_send_raw(client, str_c(str)); + if (!reply->nologin) { client_destroy_success(client, "Login with referral"); return TRUE; } - } else if (nologin) { + } else if (reply->nologin) { /* Authentication went ok, but for some reason user isn't allowed to log in. Shouldn't probably happen. */ - if (reason != NULL) { - client_send_line(&client->common, + if (reply->reason != NULL) { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_REASON, - reason); - } else if (temp) { - client_send_line(&client->common, + reply->reason); + } else if (reply->temp) { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, AUTH_TEMP_FAILED_MSG); - } else if (authz_failure) { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTHZ_FAILED, + } else if (reply->authz_failure) { + client_send_line(client, CLIENT_CMD_REPLY_AUTHZ_FAILED, "Authorization failed"); } else { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAILED, + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, AUTH_FAILED_MSG); } } else { @@ -236,127 +98,34 @@ return FALSE; } - i_assert(nologin); + i_assert(reply->nologin); if (!client->destroyed) - client_auth_failed(client, *nodelay_r); + client_auth_failed(client, reply->nodelay); return TRUE; } -static void sasl_callback(struct client *_client, enum sasl_server_reply reply, - const char *data, const char *const *args) -{ - struct imap_client *client = (struct imap_client *)_client; - struct const_iovec iov[3]; - size_t data_len; - bool nodelay; - - i_assert(!client->destroyed || - reply == SASL_SERVER_REPLY_AUTH_ABORTED || - reply == SASL_SERVER_REPLY_MASTER_FAILED); - - switch (reply) { - case SASL_SERVER_REPLY_SUCCESS: - if (client->to_auth_waiting != NULL) - timeout_remove(&client->to_auth_waiting); - if (args != NULL) { - if (client_handle_args(client, args, TRUE, &nodelay)) - break; - } - client_destroy_success(client, "Login"); - break; - case SASL_SERVER_REPLY_AUTH_FAILED: - case SASL_SERVER_REPLY_AUTH_ABORTED: - if (client->to_auth_waiting != NULL) - timeout_remove(&client->to_auth_waiting); - if (args != NULL) { - if (client_handle_args(client, args, FALSE, &nodelay)) - break; - } - - if (reply == SASL_SERVER_REPLY_AUTH_ABORTED) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "Authentication aborted by client."); - } else if (data == NULL) { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAILED, - AUTH_FAILED_MSG); - } else { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_REASON, - data); - } - - if (!client->destroyed) - client_auth_failed(client, nodelay); - break; - case SASL_SERVER_REPLY_MASTER_FAILED: - if (data == NULL) - client_destroy_internal_failure(client); - else { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data); - /* authentication itself succeeded, we just hit some - internal failure. */ - client_destroy_success(client, data); - } - break; - case SASL_SERVER_REPLY_CONTINUE: - data_len = strlen(data); - iov[0].iov_base = "+ "; - iov[0].iov_len = 2; - iov[1].iov_base = data; - iov[1].iov_len = data_len; - iov[2].iov_base = "\r\n"; - iov[2].iov_len = 2; - - /* don't check return value here. it gets tricky if we try - to call client_destroy() in here. */ - (void)o_stream_sendv(client->output, iov, 3); - - if (client->to_auth_waiting != NULL) - timeout_remove(&client->to_auth_waiting); - - i_assert(client->io == NULL); - client->io = io_add(client->common.fd, IO_READ, - client_auth_input, client); - client_auth_input(client); - return; - } - - client_unref(client); -} - -static int client_auth_begin(struct imap_client *client, const char *mech_name, - const char *init_resp) +static int +imap_client_auth_begin(struct imap_client *imap_client, const char *mech_name, + const char *init_resp) { char *prefix; prefix = i_strdup_printf("%d%s", - client->client_ignores_capability_resp_code, - client->cmd_tag); - - i_free(client->common.master_data_prefix); - client->common.master_data_prefix = (void *)prefix; - client->common.master_data_prefix_len = strlen(prefix)+1; + imap_client->client_ignores_capability_resp_code, + imap_client->cmd_tag); - client_ref(client); - client->auth_initializing = TRUE; - sasl_server_auth_begin(&client->common, IMAP_SERVICE_NAME, mech_name, - init_resp, sasl_callback); - client->auth_initializing = FALSE; - if (!client->common.authenticating) - return 1; + i_free(imap_client->common.master_data_prefix); + imap_client->common.master_data_prefix = (void *)prefix; + imap_client->common.master_data_prefix_len = strlen(prefix)+1; - /* don't handle input until we get the initial auth reply */ - if (client->io != NULL) - io_remove(&client->io); - client_set_auth_waiting(client); - return 0; + return client_auth_begin(&imap_client->common, mech_name, init_resp); } -int cmd_authenticate(struct imap_client *client, const struct imap_arg *args) +int cmd_authenticate(struct imap_client *imap_client, + const struct imap_arg *args) { + struct client *client = &imap_client->common; const char *mech_name, *init_resp = NULL; /* we want only one argument: authentication mechanism name */ @@ -370,15 +139,13 @@ init_resp = IMAP_ARG_STR(&args[1]); } - if (!client->common.secured && - strcmp(client->common.set->ssl, "required") == 0) { - if (client->common.set->verbose_auth) { - client_syslog(&client->common, "Login failed: " - "SSL required for authentication"); + if (!client->secured && strcmp(client->set->ssl, "required") == 0) { + if (client->set->verbose_auth) { + client_log(client, "Login failed: " + "SSL required for authentication"); } - client->common.auth_attempts++; - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, + client->auth_attempts++; + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, "Authentication not allowed until SSL/TLS is enabled."); return 1; } @@ -386,11 +153,12 @@ mech_name = IMAP_ARG_STR(&args[0]); if (*mech_name == '\0') return -1; - return client_auth_begin(client, mech_name, init_resp); + return imap_client_auth_begin(imap_client, mech_name, init_resp); } -int cmd_login(struct imap_client *client, const struct imap_arg *args) +int cmd_login(struct imap_client *imap_client, const struct imap_arg *args) { + struct client *client = &imap_client->common; const char *user, *pass; string_t *plain_login, *base64; @@ -405,23 +173,8 @@ user = IMAP_ARG_STR(&args[0]); pass = IMAP_ARG_STR(&args[1]); - if (!client->common.secured && - client->common.set->disable_plaintext_auth) { - if (client->common.set->verbose_auth) { - client_syslog(&client->common, "Login failed: " - "Plaintext authentication disabled"); - } - client->common.auth_tried_disabled_plaintext = TRUE; - client->common.auth_attempts++; - client_send_raw(client, - "* BAD [ALERT] Plaintext authentication not allowed " - "without SSL/TLS, but your client did it anyway. " - "If anyone was listening, the password was exposed.\r\n"); - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, - AUTH_PLAINTEXT_DISABLED_MSG); + if (!client_check_plaintext_auth(client, TRUE)) return 1; - } /* authorization ID \0 authentication ID \0 pass */ plain_login = buffer_create_dynamic(pool_datastack_create(), 64); @@ -433,5 +186,5 @@ base64 = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_ENCODED_SIZE(plain_login->used)); base64_encode(plain_login->data, plain_login->used, base64); - return client_auth_begin(client, "PLAIN", str_c(base64)); + return imap_client_auth_begin(imap_client, "PLAIN", str_c(base64)); }
--- a/src/imap-login/client-authenticate.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/imap-login/client-authenticate.h Sun Aug 09 21:53:14 2009 -0400 @@ -3,7 +3,10 @@ struct imap_arg; -const char *client_authenticate_get_capabilities(struct imap_client *client); +const char *client_authenticate_get_capabilities(struct client *client); + +bool imap_client_auth_handle_reply(struct client *client, + const struct client_auth_reply *reply); int cmd_login(struct imap_client *client, const struct imap_arg *args); int cmd_authenticate(struct imap_client *client, const struct imap_arg *args);
--- a/src/imap-login/client.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/imap-login/client.c Sun Aug 09 21:53:14 2009 -0400 @@ -22,61 +22,15 @@ #include <stdlib.h> -/* max. size of output buffer. if it gets full, the client is disconnected. - SASL authentication gives the largest output. */ -#define MAX_OUTBUF_SIZE 4096 - /* maximum length for IMAP command line. */ #define MAX_IMAP_LINE 8192 /* Disconnect client when it sends too many bad commands */ #define CLIENT_MAX_BAD_COMMANDS 10 -/* When max. number of simultaneous connections is reached, few of the - oldest connections are disconnected. Since we have to go through all of the - clients, it's faster if we disconnect multiple clients. */ -#define CLIENT_DESTROY_OLDEST_COUNT 16 - -/* If we've been waiting auth server to respond for over this many milliseconds, - send a "waiting" message. */ -#define AUTH_WAITING_TIMEOUT_MSECS (30*1000) - -#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000 -# error client idle timeout must be larger than authentication timeout -#endif - -#define AUTH_SERVER_WAITING_MSG \ - "Waiting for authentication process to respond.." -#define AUTH_MASTER_WAITING_MSG \ - "Waiting for authentication master process to respond.." - const char *login_protocol = "imap"; const char *login_process_name = "imap-login"; - -static void client_set_title(struct imap_client *client) -{ - const char *addr; - - if (!client->common.set->verbose_proctitle || - !client->common.set->login_process_per_connection) - return; - - addr = net_ip2addr(&client->common.ip); - if (addr == NULL) - addr = "??"; - - process_title_set(t_strdup_printf(client->common.tls ? - "[%s TLS]" : "[%s]", addr)); -} - -static void client_open_streams(struct imap_client *client, int fd) -{ - client->common.input = - i_stream_create_fd(fd, LOGIN_MAX_INBUF_SIZE, FALSE); - client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE); - client->parser = imap_parser_create(client->common.input, - client->output, MAX_IMAP_LINE); -} +unsigned int login_default_port = 143; /* Skip incoming data until newline is found, returns TRUE if newline was found. */ @@ -97,118 +51,36 @@ return FALSE; } -static const char *get_capability(struct imap_client *client) +static const char *get_capability(struct client *client) { const char *auths; auths = client_authenticate_get_capabilities(client); return t_strconcat(CAPABILITY_BANNER_STRING, - (ssl_initialized && !client->common.tls) ? - " STARTTLS" : "", - client->common.set->disable_plaintext_auth && - !client->common.secured ? - " LOGINDISABLED" : "", auths, NULL); + (ssl_initialized && !client->tls) ? " STARTTLS" : "", + client->set->disable_plaintext_auth && + !client->secured ? " LOGINDISABLED" : "", + auths, NULL); } -static int cmd_capability(struct imap_client *client) +static int cmd_capability(struct imap_client *imap_client) { + struct client *client = &imap_client->common; + /* Client is required to send CAPABILITY after STARTTLS, so the capability resp-code workaround checks only pre-STARTTLS CAPABILITY commands. */ if (!client->starttls) - client->client_ignores_capability_resp_code = TRUE; + imap_client->client_ignores_capability_resp_code = TRUE; client_send_raw(client, t_strconcat( "* CAPABILITY ", get_capability(client), "\r\n", NULL)); - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, - "Capability completed."); - return 1; -} - -static void client_start_tls(struct imap_client *client) -{ - int fd_ssl; - - client_ref(client); - if (!client_unref(client) || client->destroyed) - return; - - fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip, - client->common.set, &client->common.proxy); - if (fd_ssl == -1) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, - "TLS initialization failed."); - client_destroy(client, - "Disconnected: TLS initialization failed."); - return; - } - - client->starttls = TRUE; - client->common.proxying = TRUE; - client->common.tls = TRUE; - client->common.secured = TRUE; - client_set_title(client); - - client->common.fd = fd_ssl; - i_stream_unref(&client->common.input); - o_stream_unref(&client->output); - imap_parser_destroy(&client->parser); - - /* CRLF is lost from buffer when streams are reopened. */ - client->skip_line = FALSE; - - client_open_streams(client, fd_ssl); - client->io = io_add(client->common.fd, IO_READ, client_input, client); -} - -static int client_output_starttls(struct imap_client *client) -{ - int ret; - - if ((ret = o_stream_flush(client->output)) < 0) { - client_destroy(client, "Disconnected"); - return 1; - } - - if (ret > 0) { - o_stream_unset_flush_callback(client->output); - client_start_tls(client); - } + client_send_line(client, CLIENT_CMD_REPLY_OK, "Capability completed."); return 1; } static int cmd_starttls(struct imap_client *client) { - if (client->common.tls) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "TLS is already active."); - return 1; - } - - if (!ssl_initialized) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "TLS support isn't enabled."); - return 1; - } - - /* remove input handler, SSL proxy gives us a new fd. we also have to - remove it in case we have to wait for buffer to be flushed */ - if (client->io != NULL) - io_remove(&client->io); - - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, - "Begin TLS negotiation now."); - - /* uncork the old fd */ - o_stream_uncork(client->output); - - if (o_stream_flush(client->output) <= 0) { - /* the buffer has to be flushed */ - o_stream_set_flush_pending(client->output, TRUE); - o_stream_set_flush_callback(client->output, - client_output_starttls, client); - } else { - client_start_tls(client); - } + client_cmd_starttls(&client->common); return 1; } @@ -249,13 +121,13 @@ env = getenv("IMAP_ID_LOG"); value = imap_id_args_get_log_reply(args, env); if (value != NULL) { - client_syslog(&client->common, - t_strdup_printf("ID sent: %s", value)); + client_log(&client->common, + t_strdup_printf("ID sent: %s", value)); } } env = getenv("IMAP_ID_SEND"); - client_send_raw(client, + client_send_raw(&client->common, t_strdup_printf("* ID %s\r\n", imap_id_reply_generate(env))); client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "ID completed."); return 1; @@ -270,17 +142,16 @@ static int cmd_logout(struct imap_client *client) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, - "Logging out"); + client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, "Logging out"); client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "Logout completed."); - client_destroy(client, "Aborted login"); + client_destroy(&client->common, "Aborted login"); return 1; } static int cmd_enable(struct imap_client *client) { - client_send_raw(client, "* ENABLED\r\n"); + client_send_raw(&client->common, "* ENABLED\r\n"); client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "ENABLE ignored in non-authenticated state."); return 1; @@ -356,7 +227,7 @@ if (fatal) { client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, msg); - client_destroy(client, + client_destroy(&client->common, t_strconcat("Disconnected: ", msg, NULL)); return FALSE; } @@ -382,10 +253,10 @@ if (ret < 0) { if (*client->cmd_tag == '\0') client->cmd_tag = "*"; - if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) { + if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) { client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, "Too many invalid IMAP commands."); - client_destroy(client, + client_destroy(&client->common, "Disconnected: Too many invalid commands"); return FALSE; } @@ -396,31 +267,10 @@ return ret != 0; } -bool client_read(struct imap_client *client) +static void imap_client_input(struct client *client) { - switch (i_stream_read(client->common.input)) { - case -2: - /* buffer full */ - client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, - "Input buffer full, aborting"); - client_destroy(client, "Disconnected: Input buffer full"); - return FALSE; - case -1: - /* disconnected */ - client_destroy(client, "Disconnected"); - return FALSE; - case 0: - /* nothing new read */ - return TRUE; - default: - /* something was read */ - timeout_reset(client->to_idle_disconnect); - return TRUE; - } -} + struct imap_client *imap_client = (struct imap_client *)client; -void client_input(struct imap_client *client) -{ if (!client_read(client)) return; @@ -429,280 +279,76 @@ if (!auth_client_is_connected(auth_client)) { /* we're not yet connected to auth process - don't allow any commands */ - client_send_line(&client->common, CLIENT_CMD_REPLY_STATUS, + client_send_line(client, CLIENT_CMD_REPLY_STATUS, AUTH_SERVER_WAITING_MSG); if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); client->input_blocked = TRUE; } else { - o_stream_cork(client->output); - while (client_handle_input(client)) ; - o_stream_uncork(client->output); + o_stream_cork(imap_client->common.output); + while (client_handle_input(imap_client)) ; + o_stream_uncork(imap_client->common.output); } client_unref(client); } -void client_destroy_oldest(void) +static struct client *imap_client_alloc(pool_t pool) { - unsigned int max_connections = - global_login_settings->login_max_connections; - struct client *client; - struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT]; - unsigned int i, destroy_count; - - /* find the oldest clients and put them to destroy-buffer */ - memset(destroy_buf, 0, sizeof(destroy_buf)); - - destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ? - CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1); - for (client = clients; client != NULL; client = client->next) { - struct imap_client *imap_client = (struct imap_client *)client; + struct imap_client *imap_client; - for (i = 0; i < destroy_count; i++) { - if (destroy_buf[i] == NULL || - destroy_buf[i]->created > imap_client->created) { - /* @UNSAFE */ - memmove(destroy_buf+i+1, destroy_buf+i, - sizeof(destroy_buf) - - (i+1) * sizeof(struct imap_client *)); - destroy_buf[i] = imap_client; - break; - } - } - } - - /* then kill them */ - for (i = 0; i < destroy_count; i++) { - if (destroy_buf[i] == NULL) - break; - - client_destroy(destroy_buf[i], - "Disconnected: Connection queue full"); - } + imap_client = p_new(pool, struct imap_client, 1); + return &imap_client->common; } -static void client_send_greeting(struct imap_client *client) +static void imap_client_create(struct client *client) +{ + struct imap_client *imap_client = (struct imap_client *)client; + + imap_client->parser = + imap_parser_create(imap_client->common.input, + imap_client->common.output, MAX_IMAP_LINE); + client->io = io_add(client->fd, IO_READ, client_input, client); +} + +static void imap_client_destroy(struct client *client) +{ + struct imap_client *imap_client = (struct imap_client *)client; + + i_free_and_null(imap_client->proxy_backend_capability); + imap_parser_destroy(&imap_client->parser); +} + +static void imap_client_send_greeting(struct client *client) { string_t *greet; greet = t_str_new(128); str_append(greet, "* OK "); str_printfa(greet, "[CAPABILITY %s] ", get_capability(client)); - str_append(greet, client->common.set->login_greeting); + str_append(greet, client->set->login_greeting); str_append(greet, "\r\n"); client_send_raw(client, str_c(greet)); client->greeting_sent = TRUE; } -static void client_idle_disconnect_timeout(struct imap_client *client) -{ - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "Disconnected for inactivity."); - client_destroy(client, "Disconnected: Inactivity"); -} - -static void client_auth_waiting_timeout(struct imap_client *client) -{ - client_send_line(&client->common, CLIENT_CMD_REPLY_STATUS, - client->common.master_tag == 0 ? - AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG); - timeout_remove(&client->to_auth_waiting); -} - -void client_set_auth_waiting(struct imap_client *client) -{ - i_assert(client->to_auth_waiting == NULL); - client->to_auth_waiting = - timeout_add(AUTH_WAITING_TIMEOUT_MSECS, - client_auth_waiting_timeout, client); -} - -struct client *client_create(int fd, bool ssl, pool_t pool, - const struct login_settings *set, - const struct ip_addr *local_ip, - const struct ip_addr *remote_ip) +static void imap_client_starttls(struct client *client) { - struct imap_client *client; - - i_assert(fd != -1); - - if (clients_get_count() >= set->login_max_connections) { - /* reached max. users count, kill few of the - oldest connections */ - client_destroy_oldest(); - } - - /* always use nonblocking I/O */ - net_set_nonblock(fd, TRUE); - - client = p_new(pool, struct imap_client, 1); - client->created = ioloop_time; - client->refcount = 1; - - client->common.pool = pool; - client->common.set = set; - client->common.local_ip = *local_ip; - client->common.ip = *remote_ip; - client->common.fd = fd; - client->common.tls = ssl; - client->common.trusted = client_is_trusted(&client->common); - client->common.secured = ssl || client->common.trusted || - net_ip_compare(remote_ip, local_ip); - - client_open_streams(client, fd); - client->io = io_add(fd, IO_READ, client_input, client); - - client_link(&client->common); - - if (auth_client_is_connected(auth_client)) - client_send_greeting(client); - else - client_set_auth_waiting(client); - client_set_title(client); - - client->to_idle_disconnect = - timeout_add(CLIENT_LOGIN_IDLE_TIMEOUT_MSECS, - client_idle_disconnect_timeout, client); - return &client->common; -} - -void client_destroy(struct imap_client *client, const char *reason) -{ - if (client->destroyed) - return; - client->destroyed = TRUE; - - if (!client->login_success && reason != NULL) { - reason = t_strconcat(reason, " ", - client_get_extra_disconnect_reason(&client->common), - NULL); - } - if (reason != NULL) - client_syslog(&client->common, reason); - - client_unlink(&client->common); + struct imap_client *imap_client = (struct imap_client *)client; - if (client->common.input != NULL) - i_stream_close(client->common.input); - if (client->output != NULL) - o_stream_close(client->output); - - if (client->common.master_tag != 0) { - i_assert(client->common.auth_request == NULL); - i_assert(client->common.authenticating); - master_auth_request_abort(master_service, - client->common.master_tag); - } else if (client->common.auth_request != NULL) { - i_assert(client->common.authenticating); - sasl_server_auth_abort(&client->common); - } else { - i_assert(!client->common.authenticating); - } - - if (client->io != NULL) - io_remove(&client->io); - if (client->to_idle_disconnect != NULL) - timeout_remove(&client->to_idle_disconnect); - if (client->to_auth_waiting != NULL) - timeout_remove(&client->to_auth_waiting); - if (client->to_authfail_delay != NULL) - timeout_remove(&client->to_authfail_delay); - - if (client->common.fd != -1) { - net_disconnect(client->common.fd); - client->common.fd = -1; - } - - if (client->proxy_password != NULL) { - safe_memset(client->proxy_password, 0, - strlen(client->proxy_password)); - i_free(client->proxy_password); - client->proxy_password = NULL; - } - - i_free_and_null(client->proxy_user); - i_free_and_null(client->proxy_master_user); - i_free_and_null(client->proxy_backend_capability); - - if (client->proxy != NULL) - login_proxy_free(&client->proxy); + imap_parser_destroy(&imap_client->parser); + imap_client->parser = + imap_parser_create(imap_client->common.input, + imap_client->common.output, MAX_IMAP_LINE); - if (client->common.proxy != NULL) { - ssl_proxy_free(client->common.proxy); - client->common.proxy = NULL; - } - client_unref(client); -} - -void client_destroy_success(struct imap_client *client, const char *reason) -{ - client->login_success = TRUE; - client_destroy(client, reason); -} - -void client_destroy_internal_failure(struct imap_client *client) -{ - client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, - "Internal login failure. " - "Refer to server log for more information."); - client_destroy(client, "Internal login failure"); -} - -void client_ref(struct imap_client *client) -{ - client->refcount++; -} - -bool client_unref(struct imap_client *client) -{ - i_assert(client->refcount > 0); - if (--client->refcount > 0) - return TRUE; - - i_assert(client->destroyed); - - imap_parser_destroy(&client->parser); - - if (client->common.input != NULL) - i_stream_unref(&client->common.input); - if (client->output != NULL) - o_stream_unref(&client->output); - - if (!client->common.proxying) { - i_assert(client->common.proxy == NULL); - master_service_client_connection_destroyed(master_service); - } - - i_free(client->common.virtual_user); - i_free(client->common.auth_mech_name); - pool_unref(&client->common.pool); - return FALSE; + /* CRLF is lost from buffer when streams are reopened. */ + imap_client->skip_line = FALSE; } static void -client_send_raw_data(struct imap_client *client, const void *data, size_t size) -{ - ssize_t ret; - - ret = o_stream_send(client->output, data, size); - if (ret < 0 || (size_t)ret != size) { - /* either disconnection or buffer full. in either case we want - this connection destroyed. however destroying it here might - break things if client is still tried to be accessed without - being referenced.. */ - i_stream_close(client->common.input); - } -} - -void client_send_raw(struct imap_client *client, const char *data) -{ - client_send_raw_data(client, data, strlen(data)); -} - -void client_send_line(struct client *client, enum client_cmd_reply reply, +imap_client_send_line(struct client *client, enum client_cmd_reply reply, const char *text) { struct imap_client *imap_client = (struct imap_client *)client; @@ -740,6 +386,11 @@ prefix = "OK"; tagged = FALSE; break; + case CLIENT_CMD_REPLY_STATUS_BAD: + prefix = "BAD"; + tagged = FALSE; + resp_code = "ALERT"; + break; } T_BEGIN { @@ -757,41 +408,11 @@ str_append(line, text); str_append(line, "\r\n"); - client_send_raw_data(imap_client, str_data(line), + client_send_raw_data(client, str_data(line), str_len(line)); } T_END; } -void clients_notify_auth_connected(void) -{ - struct client *client; - - for (client = clients; client != NULL; client = client->next) { - struct imap_client *imap_client = (struct imap_client *)client; - - if (imap_client->to_auth_waiting != NULL) - timeout_remove(&imap_client->to_auth_waiting); - if (!imap_client->greeting_sent) - client_send_greeting(imap_client); - if (imap_client->input_blocked) { - imap_client->input_blocked = FALSE; - client_input(imap_client); - } - } -} - -void clients_destroy_all(void) -{ - struct client *client, *next; - - for (client = clients; client != NULL; client = next) { - struct imap_client *imap_client = (struct imap_client *)client; - - next = client->next; - client_destroy(imap_client, "Disconnected: Shutting down"); - } -} - void clients_init(void) { /* Nothing to initialize for IMAP */ @@ -801,3 +422,16 @@ { clients_destroy_all(); } + +struct client_vfuncs client_vfuncs = { + imap_client_alloc, + imap_client_create, + imap_client_destroy, + imap_client_send_greeting, + imap_client_starttls, + imap_client_input, + imap_client_send_line, + imap_client_auth_handle_reply, + imap_proxy_reset, + imap_proxy_parse_line +};
--- a/src/imap-login/client.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/imap-login/client.h Sun Aug 09 21:53:14 2009 -0400 @@ -4,56 +4,22 @@ #include "network.h" #include "client-common.h" -/* Disconnect client after idling this many milliseconds */ -#define CLIENT_LOGIN_IDLE_TIMEOUT_MSECS (3*60*1000) - struct imap_client { struct client common; - time_t created; - int refcount; - - struct io *io; - struct ostream *output; struct imap_parser *parser; - struct timeout *to_idle_disconnect, *to_auth_waiting; - struct timeout *to_authfail_delay; - - struct login_proxy *proxy; - char *proxy_user, *proxy_master_user, *proxy_password; char *proxy_backend_capability; - unsigned int bad_counter; - const char *cmd_tag, *cmd_name; - unsigned int starttls:1; - unsigned int login_success:1; unsigned int cmd_finished: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; - unsigned int greeting_sent:1; unsigned int id_logged:1; - unsigned int auth_initializing:1; unsigned int client_ignores_capability_resp_code:1; }; -void client_destroy(struct imap_client *client, const char *reason); -void client_destroy_success(struct imap_client *client, const char *reason); -void client_destroy_internal_failure(struct imap_client *client); - -bool client_read(struct imap_client *client); bool client_skip_line(struct imap_client *client); -void client_input(struct imap_client *client); - -void client_send_raw(struct imap_client *client, const char *data); -void client_ref(struct imap_client *client); -bool client_unref(struct imap_client *client); - -void client_set_auth_waiting(struct imap_client *client); -void client_auth_failed(struct imap_client *client, bool nodelay); #endif
--- a/src/imap-login/imap-proxy.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/imap-login/imap-proxy.c Sun Aug 09 21:53:14 2009 -0400 @@ -30,7 +30,7 @@ client->common.local_port); } -static void proxy_free_password(struct imap_client *client) +static void proxy_free_password(struct client *client) { if (client->proxy_password == NULL) return; @@ -39,24 +39,7 @@ i_free_and_null(client->proxy_password); } -static void proxy_failed(struct imap_client *client, bool send_tagline) -{ - if (send_tagline) { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - AUTH_TEMP_FAILED_MSG); - } - - login_proxy_free(&client->proxy); - proxy_free_password(client); - i_free_and_null(client->proxy_user); - i_free_and_null(client->proxy_master_user); - - /* call this last - it may destroy the client */ - client_auth_failed(client, TRUE); -} - -static void get_plain_auth(struct imap_client *client, string_t *dest) +static void get_plain_auth(struct client *client, string_t *dest) { string_t *str; @@ -91,19 +74,20 @@ if (client->client_ignores_capability_resp_code) str_append(str, "C CAPABILITY\r\n"); - if (client->proxy_master_user == NULL) { + if (client->common.proxy_master_user == NULL) { /* logging in normally - use LOGIN command */ str_append(str, "L LOGIN "); - imap_quote_append_string(str, client->proxy_user, FALSE); + imap_quote_append_string(str, client->common.proxy_user, FALSE); str_append_c(str, ' '); - imap_quote_append_string(str, client->proxy_password, FALSE); + imap_quote_append_string(str, client->common.proxy_password, + FALSE); - proxy_free_password(client); + proxy_free_password(&client->common); } 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); + get_plain_auth(&client->common, str); + proxy_free_password(&client->common); } else { /* master user login without SASL initial response */ str_append(str, "L AUTHENTICATE PLAIN"); @@ -119,7 +103,7 @@ string_t *str; if (strncmp(line, "* OK ", 5) != 0) { - client_syslog_err(&client->common, t_strdup_printf( + client_log_err(&client->common, t_strdup_printf( "proxy: Remote returned invalid banner: %s", str_sanitize(line, 160))); return -1; @@ -134,11 +118,11 @@ client->proxy_sasl_ir = TRUE; } - ssl_flags = login_proxy_get_ssl_flags(client->proxy); + ssl_flags = login_proxy_get_ssl_flags(client->common.login_proxy); if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) != 0) { if (capabilities != NULL && !str_array_icase_find(capabilities, "STARTTLS")) { - client_syslog_err(&client->common, + client_log_err(&client->common, "proxy: Remote doesn't support STARTTLS"); return -1; } @@ -151,20 +135,21 @@ return 0; } -static int proxy_input_line(struct imap_client *client, const char *line) +int imap_proxy_parse_line(struct client *client, const char *line) { + struct imap_client *imap_client = (struct imap_client *)client; struct ostream *output; const char *capability; string_t *str; i_assert(!client->destroyed); - output = login_proxy_get_ostream(client->proxy); - if (!client->proxy_seen_banner) { + output = login_proxy_get_ostream(client->login_proxy); + if (!imap_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); + imap_client->proxy_seen_banner = TRUE; + if (proxy_input_banner(imap_client, output, line) < 0) { + client_proxy_failed(client, TRUE); return -1; } return 0; @@ -180,88 +165,48 @@ } else if (strncmp(line, "S ", 2) == 0) { if (strncmp(line, "S OK ", 5) != 0) { /* STARTTLS failed */ - client_syslog_err(&client->common, t_strdup_printf( + client_log_err(client, t_strdup_printf( "proxy: Remote STARTTLS failed: %s", str_sanitize(line + 5, 160))); - proxy_failed(client, TRUE); + client_proxy_failed(client, TRUE); return -1; } /* STARTTLS successful, begin TLS negotiation. */ - if (login_proxy_starttls(client->proxy) < 0) { - proxy_failed(client, TRUE); + if (login_proxy_starttls(client->login_proxy) < 0) { + client_proxy_failed(client, TRUE); return -1; } /* i/ostreams changed. */ - output = login_proxy_get_ostream(client->proxy); + output = login_proxy_get_ostream(client->login_proxy); str = t_str_new(128); - proxy_write_login(client, str); + proxy_write_login(imap_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; + capability = imap_client->proxy_backend_capability; if (strncmp(line + 5, "[CAPABILITY ", 12) == 0) capability = t_strcut(line + 5 + 12, ']'); str = t_str_new(128); - client_send_capability_if_needed(client, str, capability); - str_append(str, client->cmd_tag); + client_send_capability_if_needed(imap_client, str, capability); + str_append(str, imap_client->cmd_tag); str_append(str, line + 1); str_append(str, "\r\n"); (void)o_stream_send(client->output, str_data(str), str_len(str)); - str_truncate(str, 0); - str_printfa(str, "proxy(%s): started proxying to %s:%u", - client->common.virtual_user, - login_proxy_get_host(client->proxy), - login_proxy_get_port(client->proxy)); - if (strcmp(client->common.virtual_user, - client->proxy_user) != 0) { - /* remote username is different, log it */ - str_append_c(str, '/'); - str_append(str, client->proxy_user); - } - if (client->proxy_master_user != NULL) { - str_printfa(str, " (master %s)", - client->proxy_master_user); - } - - (void)client_skip_line(client); - login_proxy_detach(client->proxy, client->common.input, - client->output); - - client->proxy = NULL; - client->common.input = NULL; - client->output = NULL; - client->common.fd = -1; - client->common.proxying = TRUE; - client_destroy_success(client, str_c(str)); + (void)client_skip_line(imap_client); + client_proxy_finish_destroy_client(client); return 1; } else if (strncmp(line, "L ", 2) == 0) { line += 2; - if (client->common.set->verbose_auth) { - str = t_str_new(128); - str_printfa(str, "proxy(%s): Login failed to %s:%u", - client->common.virtual_user, - login_proxy_get_host(client->proxy), - login_proxy_get_port(client->proxy)); - if (strcmp(client->common.virtual_user, - client->proxy_user) != 0) { - /* remote username is different, log it */ - str_append_c(str, '/'); - str_append(str, client->proxy_user); - } - if (client->proxy_master_user != NULL) { - str_printfa(str, " (master %s)", - client->proxy_master_user); - } - str_append(str, ": "); - if (strncasecmp(line, "NO ", 3) == 0) - str_append(str, line + 3); - else - str_append(str, line); - i_info("%s", str_c(str)); + if (client->set->verbose_auth) { + const char *log_line = line; + + if (strncasecmp(log_line, "NO ", 3) == 0) + log_line += 3; + client_proxy_log_failure(client, log_line); } #define STR_NO_IMAP_RESP_CODE_AUTHFAILED "NO ["IMAP_RESP_CODE_AUTHFAILED"]" if (strncmp(line, STR_NO_IMAP_RESP_CODE_AUTHFAILED, @@ -271,13 +216,12 @@ the remote is sending a different error message an attacker can't find out what users exist in the system. */ - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAILED, + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, AUTH_FAILED_MSG); } else if (strncmp(line, "NO [", 4) == 0) { /* remote sent some other resp-code. forward it. */ client_send_raw(client, t_strconcat( - client->cmd_tag, " ", line, "\r\n", NULL)); + imap_client->cmd_tag, " ", line, "\r\n", NULL)); } else { /* there was no [resp-code], so remote isn't Dovecot v1.2+. we could either forward the line as-is and @@ -286,16 +230,15 @@ failures. since other errors are pretty rare, it's safer to just hide them. they're still available in logs though. */ - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAILED, + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, AUTH_FAILED_MSG); } - proxy_failed(client, FALSE); + client_proxy_failed(client, FALSE); return -1; } else if (strncasecmp(line, "* CAPABILITY ", 13) == 0) { - i_free(client->proxy_backend_capability); - client->proxy_backend_capability = i_strdup(line + 13); + i_free(imap_client->proxy_backend_capability); + imap_client->proxy_backend_capability = i_strdup(line + 13); return 0; } else if (strncasecmp(line, "I ", 2) == 0 || strncasecmp(line, "* ID ", 5) == 0) { @@ -311,96 +254,10 @@ } } -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->destroyed) { - /* we came here from client_destroy() */ - return; - } - - /* failed for some reason, probably server disconnected */ - proxy_failed(client, TRUE); - return; - } - - i_assert(!client->destroyed); - - switch (i_stream_read(input)) { - case -2: - client_syslog_err(&client->common, - "proxy: Remote input buffer full"); - proxy_failed(client, TRUE); - return; - case -1: - client_syslog_err(&client->common, - "proxy: Remote disconnected"); - proxy_failed(client, TRUE); - return; - } - - while ((line = i_stream_next_line(input)) != NULL) { - 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, enum login_proxy_ssl_flags ssl_flags) +void imap_proxy_reset(struct client *client) { - i_assert(user != NULL); - i_assert(!client->destroyed); - - if (password == NULL) { - client_syslog_err(&client->common, "proxy: password not given"); - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - AUTH_TEMP_FAILED_MSG); - return -1; - } - - i_assert(client->refcount > 1); + struct imap_client *imap_client = (struct imap_client *)client; - if (client->destroyed) { - /* connection_queue_add() decided that we were the oldest - connection and killed us. */ - return -1; - } - if (login_proxy_is_ourself(&client->common, host, port, user)) { - client_syslog_err(&client->common, "Proxying loops to itself"); - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - AUTH_TEMP_FAILED_MSG); - return -1; - } - - client->proxy = login_proxy_new(&client->common, host, port, ssl_flags, - proxy_input, client); - if (client->proxy == NULL) { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - AUTH_TEMP_FAILED_MSG); - return -1; - } - - 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); - - /* disable input until authentication is finished */ - if (client->io != NULL) - io_remove(&client->io); - return 0; + imap_client->proxy_sasl_ir = FALSE; + imap_client->proxy_seen_banner = FALSE; }
--- a/src/imap-login/imap-proxy.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/imap-login/imap-proxy.h Sun Aug 09 21:53:14 2009 -0400 @@ -1,10 +1,7 @@ #ifndef IMAP_PROXY_H #define IMAP_PROXY_H -#include "login-proxy.h" - -int imap_proxy_new(struct imap_client *client, const char *hosts, - unsigned int port, const char *user, const char *master_user, - const char *password, enum login_proxy_ssl_flags ssl_flags); +void imap_proxy_reset(struct client *client); +int imap_proxy_parse_line(struct client *client, const char *line); #endif
--- a/src/login-common/Makefile.am Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/Makefile.am Sun Aug 09 21:53:14 2009 -0400 @@ -9,6 +9,7 @@ liblogin_la_SOURCES = \ client-common.c \ + client-common-auth.c \ login-proxy.c \ login-settings.c \ main.c \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/login-common/client-common-auth.c Sun Aug 09 21:53:14 2009 -0400 @@ -0,0 +1,480 @@ +/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */ + +#include "common.h" +#include "istream.h" +#include "ostream.h" +#include "str.h" +#include "safe-memset.h" +#include "login-proxy.h" +#include "auth-client.h" +#include "client-common.h" + +#include <stdlib.h> + +/* If we've been waiting auth server to respond for over this many milliseconds, + send a "waiting" message. */ +#define AUTH_WAITING_TIMEOUT_MSECS (30*1000) +#define AUTH_FAILURE_DELAY_INCREASE_MSECS 5000 + +#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000 +# error client idle timeout must be larger than authentication timeout +#endif + +static void client_authfail_delay_timeout(struct client *client) +{ + timeout_remove(&client->to_authfail_delay); + + /* get back to normal client input. */ + i_assert(client->io == NULL); + client->io = io_add(client->fd, IO_READ, client_input, client); + client_input(client); +} + +void client_auth_failed(struct client *client, bool nodelay) +{ + unsigned int delay_msecs; + + i_free_and_null(client->master_data_prefix); + + if (client->auth_initializing) + return; + + if (client->io != NULL) + io_remove(&client->io); + if (nodelay) { + client->io = io_add(client->fd, IO_READ, client_input, client); + client_input(client); + return; + } + + /* increase the timeout after each unsuccessful attempt, but don't + increase it so high that the idle timeout would be triggered */ + delay_msecs = client->auth_attempts * AUTH_FAILURE_DELAY_INCREASE_MSECS; + if (delay_msecs > CLIENT_LOGIN_IDLE_TIMEOUT_MSECS) + delay_msecs = CLIENT_LOGIN_IDLE_TIMEOUT_MSECS - 1000; + + i_assert(client->to_authfail_delay == NULL); + client->to_authfail_delay = + timeout_add(delay_msecs, client_authfail_delay_timeout, client); +} + +static void client_auth_waiting_timeout(struct client *client) +{ + client_send_line(client, CLIENT_CMD_REPLY_STATUS, + client->master_tag == 0 ? + AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG); + timeout_remove(&client->to_auth_waiting); +} + +void client_set_auth_waiting(struct client *client) +{ + i_assert(client->to_auth_waiting == NULL); + client->to_auth_waiting = + timeout_add(AUTH_WAITING_TIMEOUT_MSECS, + client_auth_waiting_timeout, client); +} + +static void client_auth_parse_args(struct client *client, + const char *const *args, + struct client_auth_reply *reply_r) +{ + const char *key, *value, *p; + + memset(reply_r, 0, sizeof(*reply_r)); + reply_r->port = login_default_port; + + for (; *args != NULL; args++) { + p = strchr(*args, '='); + if (p == NULL) { + key = *args; + value = ""; + } else { + key = t_strdup_until(*args, p); + value = p + 1; + } + if (strcmp(key, "nologin") == 0) + reply_r->nologin = TRUE; + else if (strcmp(key, "nodelay") == 0) + reply_r->nodelay = TRUE; + else if (strcmp(key, "proxy") == 0) + reply_r->proxy = TRUE; + else if (strcmp(key, "temp") == 0) + reply_r->temp = TRUE; + else if (strcmp(key, "authz") == 0) + reply_r->authz_failure = TRUE; + else if (strcmp(key, "reason") == 0) + reply_r->reason = value; + else if (strcmp(key, "host") == 0) + reply_r->host = value; + else if (strcmp(key, "port") == 0) + reply_r->port = atoi(value); + else if (strcmp(key, "destuser") == 0) + reply_r->destuser = value; + else if (strcmp(key, "pass") == 0) + reply_r->password = value; + else if (strcmp(key, "master") == 0) + reply_r->master_user = value; + else if (strcmp(key, "ssl") == 0) { + if (strcmp(value, "yes") == 0) + reply_r->ssl_flags |= PROXY_SSL_FLAG_YES; + else if (strcmp(value, "any-cert") == 0) { + reply_r->ssl_flags |= PROXY_SSL_FLAG_YES | + PROXY_SSL_FLAG_ANY_CERT; + } + } else if (strcmp(key, "starttls") == 0) { + reply_r->ssl_flags |= PROXY_SSL_FLAG_STARTTLS; + } else if (strcmp(key, "user") == 0) { + /* already handled in login-common */ + } else if (client->set->auth_debug) + i_info("Ignoring unknown passdb extra field: %s", key); + } + + if (reply_r->destuser == NULL) + reply_r->destuser = client->virtual_user; +} + +static void proxy_free_password(struct client *client) +{ + if (client->proxy_password == NULL) + return; + + safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); + i_free_and_null(client->proxy_password); +} + +void client_proxy_finish_destroy_client(struct client *client) +{ + string_t *str = t_str_new(128); + + str_printfa(str, "proxy(%s): started proxying to %s:%u", + client->virtual_user, + login_proxy_get_host(client->login_proxy), + login_proxy_get_port(client->login_proxy)); + if (strcmp(client->virtual_user, client->proxy_user) != 0) { + /* remote username is different, log it */ + str_append_c(str, '/'); + str_append(str, client->proxy_user); + } + if (client->proxy_master_user != NULL) + str_printfa(str, " (master %s)", client->proxy_master_user); + + login_proxy_detach(client->login_proxy, client->input, client->output); + + client->login_proxy = NULL; + client->input = NULL; + client->output = NULL; + client->fd = -1; + client->proxying = TRUE; + client_destroy_success(client, str_c(str)); +} + +void client_proxy_log_failure(struct client *client, const char *line) +{ + string_t *str = t_str_new(128); + + str_printfa(str, "proxy(%s): Login failed to %s:%u", + client->virtual_user, + login_proxy_get_host(client->login_proxy), + login_proxy_get_port(client->login_proxy)); + if (strcmp(client->virtual_user, client->proxy_user) != 0) { + /* remote username is different, log it */ + str_append_c(str, '/'); + str_append(str, client->proxy_user); + } + if (client->proxy_master_user != NULL) + str_printfa(str, " (master %s)", client->proxy_master_user); + str_append(str, ": "); + str_append(str, line); + i_info("%s", str_c(str)); +} + +void client_proxy_failed(struct client *client, bool send_line) +{ + if (send_line) { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); + } + + login_proxy_free(&client->login_proxy); + proxy_free_password(client); + i_free_and_null(client->proxy_user); + i_free_and_null(client->proxy_master_user); + + /* call this last - it may destroy the client */ + client_auth_failed(client, TRUE); +} + +static void proxy_input(struct client *client) +{ + struct istream *input; + const char *line; + + if (client->login_proxy == NULL) { + /* we're just freeing the proxy */ + return; + } + + input = login_proxy_get_istream(client->login_proxy); + if (input == NULL) { + if (client->destroyed) { + /* we came here from client_destroy() */ + return; + } + + /* failed for some reason, probably server disconnected */ + client_proxy_failed(client, TRUE); + return; + } + + i_assert(!client->destroyed); + + switch (i_stream_read(input)) { + case -2: + client_log_err(client, "proxy: Remote input buffer full"); + client_proxy_failed(client, TRUE); + return; + case -1: + client_log_err(client, "proxy: Remote disconnected"); + client_proxy_failed(client, TRUE); + return; + } + + while ((line = i_stream_next_line(input)) != NULL) { + if (client->v.proxy_parse_line(client, line) != 0) + break; + } +} + +static int proxy_start(struct client *client, + const struct client_auth_reply *reply) +{ + i_assert(reply->destuser != NULL); + i_assert(!client->destroyed); + + client->v.proxy_reset(client); + + if (reply->password == NULL) { + client_log_err(client, "proxy: password not given"); + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); + return -1; + } + + i_assert(client->refcount > 1); + + if (client->destroyed) { + /* connection_queue_add() decided that we were the oldest + connection and killed us. */ + return -1; + } + if (login_proxy_is_ourself(client, reply->host, reply->port, + reply->destuser)) { + client_log_err(client, "Proxying loops to itself"); + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); + return -1; + } + + client->login_proxy = + login_proxy_new(client, reply->host, reply->port, + reply->ssl_flags, proxy_input, client); + if (client->login_proxy == NULL) { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); + return -1; + } + + client->proxy_user = i_strdup(reply->destuser); + client->proxy_master_user = i_strdup(reply->master_user); + client->proxy_password = i_strdup(reply->password); + + /* disable input until authentication is finished */ + if (client->io != NULL) + io_remove(&client->io); + return 0; +} + +static bool +client_auth_handle_reply(struct client *client, + const struct client_auth_reply *reply, bool success) +{ + if (reply->proxy) { + /* we want to proxy the connection to another server. + don't do this unless authentication succeeded. with + master user proxying we can get FAIL with proxy still set. + + proxy host=.. [port=..] [destuser=..] pass=.. */ + if (!success) + return FALSE; + if (proxy_start(client, reply) < 0) + client_auth_failed(client, TRUE); + return TRUE; + } + return client->v.auth_handle_reply(client, reply); +} + +static void client_auth_input(struct client *client) +{ + char *line; + + if (!client_read(client)) + return; + + /* @UNSAFE */ + line = i_stream_next_line(client->input); + if (line == NULL) + return; + + if (strcmp(line, "*") == 0) + sasl_server_auth_abort(client); + else { + client_set_auth_waiting(client); + auth_client_request_continue(client->auth_request, line); + io_remove(&client->io); + + /* clear sensitive data */ + safe_memset(line, 0, strlen(line)); + } +} + +static void +sasl_callback(struct client *client, enum sasl_server_reply sasl_reply, + const char *data, const char *const *args) +{ + struct const_iovec iov[3]; + struct client_auth_reply reply; + size_t data_len; + + i_assert(!client->destroyed || + sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED || + sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED); + + switch (sasl_reply) { + case SASL_SERVER_REPLY_SUCCESS: + if (client->to_auth_waiting != NULL) + timeout_remove(&client->to_auth_waiting); + if (args != NULL) { + client_auth_parse_args(client, args, &reply); + if (client_auth_handle_reply(client, &reply, TRUE)) + break; + } + client_destroy_success(client, "Login"); + break; + case SASL_SERVER_REPLY_AUTH_FAILED: + case SASL_SERVER_REPLY_AUTH_ABORTED: + if (client->to_auth_waiting != NULL) + timeout_remove(&client->to_auth_waiting); + if (args != NULL) { + client_auth_parse_args(client, args, &reply); + reply.nologin = TRUE; + if (client_auth_handle_reply(client, &reply, FALSE)) + break; + } + + if (sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED) { + client_send_line(client, CLIENT_CMD_REPLY_BAD, + "Authentication aborted by client."); + } else if (data == NULL) { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, + AUTH_FAILED_MSG); + } else { + client_send_line(client, + CLIENT_CMD_REPLY_AUTH_FAIL_REASON, + data); + } + + if (!client->destroyed) + client_auth_failed(client, reply.nodelay); + break; + case SASL_SERVER_REPLY_MASTER_FAILED: + if (data == NULL) + client_destroy_internal_failure(client); + else { + client_send_line(client, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data); + /* authentication itself succeeded, we just hit some + internal failure. */ + client_destroy_success(client, data); + } + break; + case SASL_SERVER_REPLY_CONTINUE: + data_len = strlen(data); + iov[0].iov_base = "+ "; + iov[0].iov_len = 2; + iov[1].iov_base = data; + iov[1].iov_len = data_len; + iov[2].iov_base = "\r\n"; + iov[2].iov_len = 2; + + /* don't check return value here. it gets tricky if we try + to call client_destroy() in here. */ + (void)o_stream_sendv(client->output, iov, 3); + + if (client->to_auth_waiting != NULL) + timeout_remove(&client->to_auth_waiting); + + i_assert(client->io == NULL); + client->io = io_add(client->fd, IO_READ, + client_auth_input, client); + client_auth_input(client); + return; + } + + client_unref(client); +} + +int client_auth_begin(struct client *client, const char *mech_name, + const char *init_resp) +{ + client_ref(client); + client->auth_initializing = TRUE; + sasl_server_auth_begin(client, login_protocol, mech_name, + init_resp, sasl_callback); + client->auth_initializing = FALSE; + if (!client->authenticating) + return 1; + + /* don't handle input until we get the initial auth reply */ + if (client->io != NULL) + io_remove(&client->io); + client_set_auth_waiting(client); + return 0; +} + +bool client_check_plaintext_auth(struct client *client, bool pass_sent) +{ + if (client->secured || !client->set->disable_plaintext_auth) + return TRUE; + + if (client->set->verbose_auth) { + client_log(client, "Login failed: " + "Plaintext authentication disabled"); + } + if (pass_sent) { + client_send_line(client, CLIENT_CMD_REPLY_STATUS_BAD, + "Plaintext authentication not allowed " + "without SSL/TLS, but your client did it anyway. " + "If anyone was listening, the password was exposed."); + } + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, + AUTH_PLAINTEXT_DISABLED_MSG); + client->auth_tried_disabled_plaintext = TRUE; + client->auth_attempts++; + return FALSE; +} + +void clients_notify_auth_connected(void) +{ + struct client *client; + + for (client = clients; client != NULL; client = client->next) { + if (client->to_auth_waiting != NULL) + timeout_remove(&client->to_auth_waiting); + if (!client->greeting_sent) + client->v.send_greeting(client); + if (client->input_blocked) { + client->input_blocked = FALSE; + client_input(client); + } + } +}
--- a/src/login-common/client-common.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/client-common.c Sun Aug 09 21:53:14 2009 -0400 @@ -3,29 +3,333 @@ #include "common.h" #include "hostpid.h" #include "llist.h" +#include "istream.h" +#include "ostream.h" +#include "process-title.h" #include "str.h" #include "str-sanitize.h" +#include "safe-memset.h" #include "var-expand.h" +#include "master-service.h" +#include "master-auth.h" +#include "auth-client.h" +#include "login-proxy.h" #include "ssl-proxy.h" #include "client-common.h" #include <stdlib.h> +/* When max. number of simultaneous connections is reached, few of the + oldest connections are disconnected. Since we have to go through all of the + clients, it's faster if we disconnect multiple clients. */ +#define CLIENT_DESTROY_OLDEST_COUNT 16 + struct client *clients = NULL; static unsigned int clients_count = 0; -void client_link(struct client *client) +static void client_idle_disconnect_timeout(struct client *client) +{ + client_send_line(client, CLIENT_CMD_REPLY_BAD, + "Disconnected for inactivity."); + client_destroy(client, "Disconnected: Inactivity"); +} + +static void client_open_streams(struct client *client) +{ + client->input = + i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE); + client->output = + o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE); +} + +struct client *client_create(int fd, bool ssl, pool_t pool, + const struct login_settings *set, + const struct ip_addr *local_ip, + const struct ip_addr *remote_ip) { + struct client *client; + + i_assert(fd != -1); + + if (clients_get_count() >= set->login_max_connections) { + /* reached max. users count, kill few of the + oldest connections */ + client_destroy_oldest(); + } + + /* always use nonblocking I/O */ + net_set_nonblock(fd, TRUE); + + client = client_vfuncs.alloc(pool); + client->v = client_vfuncs; + client->created = ioloop_time; + client->refcount = 1; + + client->pool = pool; + client->set = set; + client->local_ip = *local_ip; + client->ip = *remote_ip; + client->fd = fd; + client->tls = ssl; + client->trusted = client_is_trusted(client); + client->secured = ssl || client->trusted || + net_ip_compare(remote_ip, local_ip); + DLLIST_PREPEND(&clients, client); clients_count++; + + client_set_title(client); + + client->to_idle_disconnect = + timeout_add(CLIENT_LOGIN_IDLE_TIMEOUT_MSECS, + client_idle_disconnect_timeout, client); + client_open_streams(client); + + client->v.create(client); + + if (auth_client_is_connected(auth_client)) + client->v.send_greeting(client); + else + client_set_auth_waiting(client); + return client; +} + +void client_destroy(struct client *client, const char *reason) +{ + if (client->destroyed) + return; + client->destroyed = TRUE; + + if (!client->login_success && reason != NULL) { + reason = t_strconcat(reason, " ", + client_get_extra_disconnect_reason(client), NULL); + } + if (reason != NULL) + client_log(client, reason); + + i_assert(clients_count > 0); + clients_count--; + DLLIST_REMOVE(&clients, client); + + if (client->input != NULL) + i_stream_close(client->input); + if (client->output != NULL) + o_stream_close(client->output); + + if (client->master_tag != 0) { + i_assert(client->auth_request == NULL); + i_assert(client->authenticating); + master_auth_request_abort(master_service, client->master_tag); + } else if (client->auth_request != NULL) { + i_assert(client->authenticating); + sasl_server_auth_abort(client); + } else { + i_assert(!client->authenticating); + } + + if (client->io != NULL) + io_remove(&client->io); + if (client->to_idle_disconnect != NULL) + timeout_remove(&client->to_idle_disconnect); + if (client->to_auth_waiting != NULL) + timeout_remove(&client->to_auth_waiting); + if (client->to_authfail_delay != NULL) + timeout_remove(&client->to_authfail_delay); + + if (client->fd != -1) { + net_disconnect(client->fd); + client->fd = -1; + } + + if (client->proxy_password != NULL) { + safe_memset(client->proxy_password, 0, + strlen(client->proxy_password)); + i_free_and_null(client->proxy_password); + } + + i_free_and_null(client->proxy_user); + i_free_and_null(client->proxy_master_user); + + if (client->login_proxy != NULL) + login_proxy_free(&client->login_proxy); + if (client->ssl_proxy != NULL) + ssl_proxy_free(&client->ssl_proxy); + client_unref(client); +} + +void client_destroy_success(struct client *client, const char *reason) +{ + client->login_success = TRUE; + client_destroy(client, reason); +} + +void client_destroy_internal_failure(struct client *client) +{ + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + "Internal login failure. " + "Refer to server log for more information."); + client_destroy(client, "Internal login failure"); +} + +void client_ref(struct client *client) +{ + client->refcount++; } -void client_unlink(struct client *client) +bool client_unref(struct client *client) +{ + i_assert(client->refcount > 0); + if (--client->refcount > 0) + return TRUE; + + i_assert(client->destroyed); + + if (client->input != NULL) + i_stream_unref(&client->input); + if (client->output != NULL) + o_stream_unref(&client->output); + + if (!client->proxying) { + i_assert(client->ssl_proxy == NULL); + master_service_client_connection_destroyed(master_service); + } + + i_free(client->virtual_user); + i_free(client->auth_mech_name); + client->v.destroy(client); + pool_unref(&client->pool); + return FALSE; +} + +void client_destroy_oldest(void) +{ + unsigned int max_connections = + global_login_settings->login_max_connections; + struct client *client; + struct client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT]; + unsigned int i, destroy_count; + + /* find the oldest clients and put them to destroy-buffer */ + memset(destroy_buf, 0, sizeof(destroy_buf)); + + destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ? + CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1); + for (client = clients; client != NULL; client = client->next) { + for (i = 0; i < destroy_count; i++) { + if (destroy_buf[i] == NULL || + destroy_buf[i]->created > client->created) { + /* @UNSAFE */ + memmove(destroy_buf+i+1, destroy_buf+i, + sizeof(destroy_buf) - + (i+1) * sizeof(destroy_buf[0])); + destroy_buf[i] = client; + break; + } + } + } + + /* then kill them */ + for (i = 0; i < destroy_count; i++) { + if (destroy_buf[i] == NULL) + break; + + client_destroy(destroy_buf[i], + "Disconnected: Connection queue full"); + } +} + +void clients_destroy_all(void) +{ + struct client *client, *next; + + for (client = clients; client != NULL; client = next) { + next = client->next; + client_destroy(client, "Disconnected: Shutting down"); + } +} + +static void client_start_tls(struct client *client) { - i_assert(clients_count > 0); + int fd_ssl; + + client_ref(client); + if (!client_unref(client) || client->destroyed) + return; + + fd_ssl = ssl_proxy_new(client->fd, &client->ip, + client->set, &client->ssl_proxy); + if (fd_ssl == -1) { + client_send_line(client, CLIENT_CMD_REPLY_BYE, + "TLS initialization failed."); + client_destroy(client, + "Disconnected: TLS initialization failed."); + return; + } + + client->starttls = TRUE; + client->proxying = TRUE; + client->tls = TRUE; + client->secured = TRUE; + client_set_title(client); + + client->fd = fd_ssl; + client->io = io_add(client->fd, IO_READ, client_input, client); + i_stream_unref(&client->input); + o_stream_unref(&client->output); + client_open_streams(client); + + client->v.starttls(client); +} + +static int client_output_starttls(struct client *client) +{ + int ret; + + if ((ret = o_stream_flush(client->output)) < 0) { + client_destroy(client, "Disconnected"); + return 1; + } - clients_count--; - DLLIST_REMOVE(&clients, client); + if (ret > 0) { + o_stream_unset_flush_callback(client->output); + client_start_tls(client); + } + return 1; +} + +void client_cmd_starttls(struct client *client) +{ + if (client->tls) { + client_send_line(client, CLIENT_CMD_REPLY_BAD, + "TLS is already active."); + return; + } + + if (!ssl_initialized) { + client_send_line(client, CLIENT_CMD_REPLY_BAD, + "TLS support isn't enabled."); + return; + } + + /* remove input handler, SSL proxy gives us a new fd. we also have to + remove it in case we have to wait for buffer to be flushed */ + if (client->io != NULL) + io_remove(&client->io); + + client_send_line(client, CLIENT_CMD_REPLY_OK, + "Begin TLS negotiation now."); + + /* uncork the old fd */ + o_stream_uncork(client->output); + + if (o_stream_flush(client->output) <= 0) { + /* the buffer has to be flushed */ + o_stream_set_flush_pending(client->output, TRUE); + o_stream_set_flush_callback(client->output, + client_output_starttls, client); + } else { + client_start_tls(client); + } } unsigned int clients_get_count(void) @@ -33,6 +337,22 @@ return clients_count; } +void client_set_title(struct client *client) +{ + const char *addr; + + if (!client->set->verbose_proctitle || + !client->set->login_process_per_connection) + return; + + addr = net_ip2addr(&client->ip); + if (addr == NULL) + addr = "??"; + + process_title_set(t_strdup_printf(client->tls ? + "[%s TLS]" : "[%s]", addr)); +} + static const struct var_expand_table * get_var_expand_table(struct client *client) { @@ -81,13 +401,16 @@ tab[11].value = client->secured ? "secured" : NULL; tab[12].value = ""; } else { - const char *ssl_state = ssl_proxy_is_handshaked(client->proxy) ? + const char *ssl_state = + ssl_proxy_is_handshaked(client->ssl_proxy) ? "TLS" : "TLS handshaking"; - const char *ssl_error = ssl_proxy_get_last_error(client->proxy); + const char *ssl_error = + ssl_proxy_get_last_error(client->ssl_proxy); tab[11].value = ssl_error == NULL ? ssl_state : t_strdup_printf("%s: %s", ssl_state, ssl_error); - tab[12].value = ssl_proxy_get_security_string(client->proxy); + tab[12].value = + ssl_proxy_get_security_string(client->ssl_proxy); } tab[13].value = dec2str(client->mail_pid); return tab; @@ -151,14 +474,14 @@ return str_c(str); } -void client_syslog(struct client *client, const char *msg) +void client_log(struct client *client, const char *msg) { T_BEGIN { i_info("%s", client_get_log_str(client, msg)); } T_END; } -void client_syslog_err(struct client *client, const char *msg) +void client_log_err(struct client *client, const char *msg) { T_BEGIN { i_error("%s", client_get_log_str(client, msg)); @@ -190,10 +513,10 @@ const char *client_get_extra_disconnect_reason(struct client *client) { - if (client->set->ssl_require_client_cert && client->proxy != NULL) { - if (ssl_proxy_has_broken_client_cert(client->proxy)) + if (client->set->ssl_require_client_cert && client->ssl_proxy != NULL) { + if (ssl_proxy_has_broken_client_cert(client->ssl_proxy)) return "(client sent an invalid cert)"; - if (!ssl_proxy_has_valid_client_cert(client->proxy)) + if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy)) return "(client didn't send a cert)"; } @@ -209,3 +532,56 @@ return t_strdup_printf("(auth failed, %u attempts)", client->auth_attempts); } + +void client_send_line(struct client *client, enum client_cmd_reply reply, + const char *text) +{ + client->v.send_line(client, reply, text); +} + +void client_send_raw_data(struct client *client, const void *data, size_t size) +{ + ssize_t ret; + + ret = o_stream_send(client->output, data, size); + if (ret < 0 || (size_t)ret != size) { + /* either disconnection or buffer full. in either case we want + this connection destroyed. however destroying it here might + break things if client is still tried to be accessed without + being referenced.. */ + i_stream_close(client->input); + } +} + +void client_send_raw(struct client *client, const char *data) +{ + client_send_raw_data(client, data, strlen(data)); +} + +bool client_read(struct client *client) +{ + switch (i_stream_read(client->input)) { + case -2: + /* buffer full */ + client_send_line(client, CLIENT_CMD_REPLY_BYE, + "Input buffer full, aborting"); + client_destroy(client, "Disconnected: Input buffer full"); + return FALSE; + case -1: + /* disconnected */ + client_destroy(client, "Disconnected"); + return FALSE; + case 0: + /* nothing new read */ + return TRUE; + default: + /* something was read */ + timeout_reset(client->to_idle_disconnect); + return TRUE; + } +} + +void client_input(struct client *client) +{ + client->v.input(client); +}
--- a/src/login-common/client-common.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/client-common.h Sun Aug 09 21:53:14 2009 -0400 @@ -2,6 +2,7 @@ #define CLIENT_COMMON_H #include "network.h" +#include "login-proxy.h" #include "sasl-server.h" /* max. size of input buffer. this means: @@ -11,6 +12,17 @@ POP3: Max. length of a command line (spec says 512 would be enough) */ #define LOGIN_MAX_INBUF_SIZE 4096 +/* max. size of output buffer. if it gets full, the client is disconnected. + SASL authentication gives the largest output. */ +#define LOGIN_MAX_OUTBUF_SIZE 4096 + +/* Disconnect client after idling this many milliseconds */ +#define CLIENT_LOGIN_IDLE_TIMEOUT_MSECS (3*60*1000) + +#define AUTH_SERVER_WAITING_MSG \ + "Waiting for authentication process to respond.." +#define AUTH_MASTER_WAITING_MSG \ + "Waiting for authentication master process to respond.." enum client_cmd_reply { CLIENT_CMD_REPLY_OK, @@ -21,61 +33,133 @@ CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, CLIENT_CMD_REPLY_BAD, CLIENT_CMD_REPLY_BYE, - CLIENT_CMD_REPLY_STATUS + CLIENT_CMD_REPLY_STATUS, + CLIENT_CMD_REPLY_STATUS_BAD +}; + +struct client_auth_reply { + const char *master_user, *reason; + /* for proxying */ + const char *host, *destuser, *password; + unsigned int port; + enum login_proxy_ssl_flags ssl_flags; + + unsigned int proxy:1; + unsigned int temp:1; + unsigned int nologin:1; + unsigned int nodelay:1; + unsigned int authz_failure:1; +}; + +struct client_vfuncs { + struct client *(*alloc)(pool_t pool); + void (*create)(struct client *client); + void (*destroy)(struct client *client); + void (*send_greeting)(struct client *client); + void (*starttls)(struct client *client); + void (*input)(struct client *client); + void (*send_line)(struct client *client, enum client_cmd_reply reply, + const char *text); + bool (*auth_handle_reply)(struct client *client, + const struct client_auth_reply *reply); + void (*proxy_reset)(struct client *client); + int (*proxy_parse_line)(struct client *client, const char *line); }; struct client { struct client *prev, *next; pool_t pool; + struct client_vfuncs v; + + time_t created; + int refcount; struct ip_addr local_ip; struct ip_addr ip; unsigned int local_port, remote_port; - struct ssl_proxy *proxy; + struct ssl_proxy *ssl_proxy; const struct login_settings *set; int fd; struct istream *input; + struct ostream *output; + struct io *io; + struct timeout *to_authfail_delay, *to_auth_waiting; + struct timeout *to_idle_disconnect; + unsigned char *master_data_prefix; unsigned int master_data_prefix_len; + struct login_proxy *login_proxy; + char *proxy_user, *proxy_master_user, *proxy_password; + char *auth_mech_name; struct auth_request *auth_request; unsigned int master_tag; sasl_server_callback_t *sasl_callback; + unsigned int bad_counter; unsigned int auth_attempts; pid_t mail_pid; char *virtual_user; + unsigned int destroyed:1; + unsigned int input_blocked:1; + unsigned int login_success:1; + unsigned int greeting_sent:1; + unsigned int starttls:1; unsigned int tls:1; unsigned int secured:1; unsigned int trusted:1; unsigned int proxying:1; unsigned int authenticating:1; unsigned int auth_tried_disabled_plaintext:1; + unsigned int auth_initializing:1; /* ... */ }; extern struct client *clients; +extern struct client_vfuncs client_vfuncs; struct client *client_create(int fd, bool ssl, pool_t pool, const struct login_settings *set, const struct ip_addr *local_ip, const struct ip_addr *remote_ip); +void client_destroy(struct client *client, const char *reason); +void client_destroy_success(struct client *client, const char *reason); +void client_destroy_internal_failure(struct client *client); -void client_link(struct client *client); -void client_unlink(struct client *client); +void client_ref(struct client *client); +bool client_unref(struct client *client); + +void client_cmd_starttls(struct client *client); + unsigned int clients_get_count(void) ATTR_PURE; +void client_set_title(struct client *client); +void client_log(struct client *client, const char *msg); +void client_log_err(struct client *client, const char *msg); +const char *client_get_extra_disconnect_reason(struct client *client); +bool client_is_trusted(struct client *client); +void client_auth_failed(struct client *client, bool nodelay); + +bool client_read(struct client *client); +void client_input(struct client *client); + void client_send_line(struct client *client, enum client_cmd_reply reply, const char *text); +void client_send_raw_data(struct client *client, const void *data, size_t size); +void client_send_raw(struct client *client, const char *data); -void client_syslog(struct client *client, const char *msg); -void client_syslog_err(struct client *client, const char *msg); -const char *client_get_extra_disconnect_reason(struct client *client); -bool client_is_trusted(struct client *client); +void client_set_auth_waiting(struct client *client); +int client_auth_begin(struct client *client, const char *mech_name, + const char *init_resp); +bool client_check_plaintext_auth(struct client *client, bool pass_sent); + +void client_proxy_finish_destroy_client(struct client *client); +void client_proxy_log_failure(struct client *client, const char *line); +void client_proxy_failed(struct client *client, bool send_line); void clients_notify_auth_connected(void); void client_destroy_oldest(void);
--- a/src/login-common/common.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/common.h Sun Aug 09 21:53:14 2009 -0400 @@ -13,6 +13,7 @@ "Plaintext authentication disallowed on non-secure (SSL/TLS) connections." extern const char *login_protocol, *login_process_name; +extern unsigned int login_default_port; extern struct auth_client *auth_client; extern bool closing_down;
--- a/src/login-common/login-proxy.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/login-proxy.c Sun Aug 09 21:53:14 2009 -0400 @@ -237,7 +237,7 @@ } if (proxy->ssl_proxy != NULL) - ssl_proxy_free(proxy->ssl_proxy); + ssl_proxy_free(&proxy->ssl_proxy); net_disconnect(proxy->server_fd); i_free(proxy->host); @@ -335,11 +335,11 @@ return 0; if (!ssl_proxy_has_broken_client_cert(proxy->ssl_proxy)) { - client_syslog_err(proxy->prelogin_client, t_strdup_printf( + client_log_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( + client_log_err(proxy->prelogin_client, t_strdup_printf( "proxy: Received invalid SSL certificate from %s:%u", proxy->host, proxy->port)); } @@ -362,7 +362,7 @@ login_proxy_ssl_handshaked, proxy, &proxy->ssl_proxy); if (fd < 0) { - client_syslog_err(proxy->prelogin_client, t_strdup_printf( + client_log_err(proxy->prelogin_client, t_strdup_printf( "proxy: SSL handshake failed to %s:%u", proxy->host, proxy->port)); return -1;
--- a/src/login-common/login-proxy.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/login-proxy.h Sun Aug 09 21:53:14 2009 -0400 @@ -1,6 +1,7 @@ #ifndef LOGIN_PROXY_H #define LOGIN_PROXY_H +struct client; struct login_proxy; enum login_proxy_ssl_flags {
--- a/src/login-common/main.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/main.c Sun Aug 09 21:53:14 2009 -0400 @@ -59,7 +59,7 @@ client = client_create(fd_ssl, TRUE, pool, set, &local_ip, &conn->remote_ip); client->proxying = TRUE; - client->proxy = proxy; + client->ssl_proxy = proxy; } client->remote_port = conn->remote_port;
--- a/src/login-common/sasl-server.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/sasl-server.c Sun Aug 09 21:53:14 2009 -0400 @@ -54,8 +54,8 @@ { enum auth_request_flags auth_flags = 0; - if (client->proxy != NULL && - ssl_proxy_has_valid_client_cert(client->proxy)) + if (client->ssl_proxy != NULL && + ssl_proxy_has_valid_client_cert(client->ssl_proxy)) auth_flags |= AUTH_REQUEST_FLAG_VALID_CLIENT_CERT; if (client->secured) auth_flags |= AUTH_REQUEST_FLAG_SECURED; @@ -256,8 +256,8 @@ memset(&info, 0, sizeof(info)); info.mech = mech->name; info.service = service; - info.cert_username = client->proxy == NULL ? NULL : - ssl_proxy_get_peer_name(client->proxy); + info.cert_username = client->ssl_proxy == NULL ? NULL : + ssl_proxy_get_peer_name(client->ssl_proxy); info.flags = client_get_auth_flags(client); info.local_ip = client->local_ip; info.remote_ip = client->ip; @@ -282,9 +282,8 @@ if (client->set->verbose_auth && reason != NULL) { const char *auth_name = str_sanitize(client->auth_mech_name, MAX_MECH_NAME); - client_syslog(client, - t_strdup_printf("Authenticate %s failed: %s", - auth_name, reason)); + client_log(client, t_strdup_printf( + "Authenticate %s failed: %s", auth_name, reason)); } client->authenticating = FALSE;
--- a/src/login-common/ssl-proxy-openssl.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/ssl-proxy-openssl.c Sun Aug 09 21:53:14 2009 -0400 @@ -669,8 +669,11 @@ bits, alg_bits); } -void ssl_proxy_free(struct ssl_proxy *proxy) +void ssl_proxy_free(struct ssl_proxy **_proxy) { + struct ssl_proxy *proxy = *_proxy; + + *_proxy = NULL; ssl_proxy_unref(proxy); }
--- a/src/login-common/ssl-proxy.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/ssl-proxy.c Sun Aug 09 21:53:14 2009 -0400 @@ -57,7 +57,7 @@ return ""; } -void ssl_proxy_free(struct ssl_proxy *proxy ATTR_UNUSED) {} +void ssl_proxy_free(struct ssl_proxy **proxy ATTR_UNUSED) {} unsigned int ssl_proxy_get_count(void) {
--- a/src/login-common/ssl-proxy.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/login-common/ssl-proxy.h Sun Aug 09 21:53:14 2009 -0400 @@ -26,7 +26,7 @@ bool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy) ATTR_PURE; const char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy) ATTR_PURE; const char *ssl_proxy_get_security_string(struct ssl_proxy *proxy); -void ssl_proxy_free(struct ssl_proxy *proxy); +void ssl_proxy_free(struct ssl_proxy **proxy); /* Return number of active SSL proxies */ unsigned int ssl_proxy_get_count(void) ATTR_PURE;
--- a/src/pop3-login/client-authenticate.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/pop3-login/client-authenticate.c Sun Aug 09 21:53:14 2009 -0400 @@ -19,9 +19,6 @@ #include <stdlib.h> -#define POP3_SERVICE_NAME "pop3" -#define AUTH_FAILURE_DELAY_INCREASE_MSECS 5000 - const char *capability_string = POP3_CAPABILITY_REPLY; bool cmd_capa(struct pop3_client *client, const char *args ATTR_UNUSED) @@ -48,247 +45,45 @@ } str_append(str, "\r\n.\r\n"); - client_send_raw(client, str_c(str)); + client_send_raw(&client->common, str_c(str)); return TRUE; } -static void client_auth_input(struct pop3_client *client) -{ - char *line; - - if (!client_read(client)) - return; - - /* @UNSAFE */ - line = i_stream_next_line(client->common.input); - if (line == NULL) - return; - - if (strcmp(line, "*") == 0) - sasl_server_auth_abort(&client->common); - else { - auth_client_request_continue(client->common.auth_request, line); - io_remove(&client->io); - - /* clear sensitive data */ - safe_memset(line, 0, strlen(line)); - } -} - -static void client_authfail_delay_timeout(struct pop3_client *client) -{ - timeout_remove(&client->to_authfail_delay); - - /* get back to normal client input. */ - i_assert(client->io == NULL); - client->io = io_add(client->common.fd, IO_READ, client_input, client); - client_input(client); -} - -void client_auth_failed(struct pop3_client *client, bool nodelay) +bool pop3_client_auth_handle_reply(struct client *client, + const struct client_auth_reply *reply) { - unsigned int delay_msecs; - - if (client->auth_initializing) - return; - - if (client->io != NULL) - io_remove(&client->io); - if (nodelay) { - client->io = io_add(client->common.fd, IO_READ, - client_input, client); - client_input(client); - return; - } - - /* increase the timeout after each unsuccessful attempt, but don't - increase it so high that the idle timeout would be triggered */ - delay_msecs = client->common.auth_attempts * - AUTH_FAILURE_DELAY_INCREASE_MSECS; - if (delay_msecs > CLIENT_LOGIN_IDLE_TIMEOUT_MSECS) - delay_msecs = CLIENT_LOGIN_IDLE_TIMEOUT_MSECS - 1000; - - i_assert(client->to_authfail_delay == NULL); - client->to_authfail_delay = - timeout_add(delay_msecs, client_authfail_delay_timeout, client); -} - -static bool client_handle_args(struct pop3_client *client, - const char *const *args, bool success, - bool *nodelay_r) -{ - 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; - unsigned int port = 110; - bool proxy = FALSE, temp = FALSE, nologin = !success; - - *nodelay_r = FALSE; - for (; *args != NULL; args++) { - 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(key, "nodelay") == 0) - *nodelay_r = TRUE; - else if (strcmp(key, "proxy") == 0) - proxy = TRUE; - else if (strcmp(key, "temp") == 0) - temp = TRUE; - 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 (client->common.set->auth_debug) - i_info("Ignoring unknown passdb extra field: %s", key); - } - - if (destuser == NULL) - destuser = client->common.virtual_user; - - if (proxy) { - /* we want to proxy the connection to another server. - don't do this unless authentication succeeded. with - master user proxying we can get FAIL with proxy still set. - - proxy host=.. [port=..] [destuser=..] pass=.. */ - if (!success) - return FALSE; - if (pop3_proxy_new(client, host, port, destuser, master_user, - pass, ssl_flags) < 0) - client_auth_failed(client, TRUE); - return TRUE; - } - - if (!nologin) + if (!reply->nologin) return FALSE; - if (reason != NULL) { - client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAILED, - reason); - } else if (temp) { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + if (reply->reason != NULL) { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, + reply->reason); + } else if (reply->temp) { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, AUTH_TEMP_FAILED_MSG); } else { - client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAILED, + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, AUTH_FAILED_MSG); } if (!client->destroyed) - client_auth_failed(client, *nodelay_r); + client_auth_failed(client, reply->nodelay); return TRUE; } -static void sasl_callback(struct client *_client, enum sasl_server_reply reply, - const char *data, const char *const *args) +bool cmd_auth(struct pop3_client *pop3_client, const char *args) { - struct pop3_client *client = (struct pop3_client *)_client; - struct const_iovec iov[3]; - size_t data_len; - bool nodelay; - - i_assert(!client->destroyed || - reply == SASL_SERVER_REPLY_AUTH_ABORTED || - reply == SASL_SERVER_REPLY_MASTER_FAILED); - - switch (reply) { - case SASL_SERVER_REPLY_SUCCESS: - if (args != NULL) { - if (client_handle_args(client, args, TRUE, &nodelay)) - break; - } - - client_destroy_success(client, "Login"); - break; - case SASL_SERVER_REPLY_AUTH_FAILED: - case SASL_SERVER_REPLY_AUTH_ABORTED: - if (args != NULL) { - if (client_handle_args(client, args, FALSE, &nodelay)) - break; - } - - if (reply == SASL_SERVER_REPLY_AUTH_ABORTED) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "Authentication aborted by client."); - } else if (data == NULL) { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAILED, - AUTH_FAILED_MSG); - } else { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_REASON, - data); - } - - if (!client->destroyed) - client_auth_failed(client, nodelay); - break; - case SASL_SERVER_REPLY_MASTER_FAILED: - if (data == NULL) - client_destroy_internal_failure(client); - else { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data); - /* authentication itself succeeded, we just hit some - internal failure. */ - client_destroy_success(client, data); - } - break; - case SASL_SERVER_REPLY_CONTINUE: - data_len = strlen(data); - iov[0].iov_base = "+ "; - iov[0].iov_len = 2; - iov[1].iov_base = data; - iov[1].iov_len = data_len; - iov[2].iov_base = "\r\n"; - iov[2].iov_len = 2; - - /* don't check return value here. it gets tricky if we try - to call client_destroy() in here. */ - (void)o_stream_sendv(client->output, iov, 3); - - i_assert(client->io == NULL); - client->io = io_add(client->common.fd, IO_READ, - client_auth_input, client); - client_auth_input(client); - return; - } - - client_unref(client); -} - -bool cmd_auth(struct pop3_client *client, const char *args) -{ + struct client *client = &pop3_client->common; const struct auth_mech_desc *mech; const char *mech_name, *p; - if (!client->common.secured && - strcmp(client->common.set->ssl, "required") == 0) { - if (client->common.set->verbose_auth) { - client_syslog(&client->common, "Login failed: " - "SSL required for authentication"); + if (!client->secured && strcmp(client->set->ssl, "required") == 0) { + if (client->set->verbose_auth) { + client_log(client, "Login failed: " + "SSL required for authentication"); } - client->common.auth_attempts++; - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, + client->auth_attempts++; + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, "Authentication not allowed until SSL/TLS is enabled."); return TRUE; } @@ -298,15 +93,10 @@ unsigned int i, count; client_send_raw(client, "+OK\r\n"); - mech = auth_client_get_available_mechs(auth_client, &count); + mech = sasl_server_get_advertised_mechs(client, &count); for (i = 0; i < count; i++) { - if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 && - (client->common.secured || - client->common.set->disable_plaintext_auth || - (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) { - client_send_raw(client, mech[i].name); - client_send_raw(client, "\r\n"); - } + client_send_raw(client, mech[i].name); + client_send_raw(client, "\r\n"); } client_send_raw(client, ".\r\n"); return TRUE; @@ -322,58 +112,34 @@ args = p+1; } - client_ref(client); - sasl_server_auth_begin(&client->common, POP3_SERVICE_NAME, mech_name, - args, sasl_callback); - if (!client->common.authenticating) - return TRUE; - - /* don't handle input until we get the initial auth reply */ - if (client->io != NULL) - io_remove(&client->io); + (void)client_auth_begin(client, mech_name, args); return TRUE; } -static bool check_plaintext_auth(struct pop3_client *client) +bool cmd_user(struct pop3_client *pop3_client, const char *args) { - if (client->common.secured || - !client->common.set->disable_plaintext_auth) + if (!client_check_plaintext_auth(&pop3_client->common, FALSE)) return TRUE; - if (client->common.set->verbose_auth) { - client_syslog(&client->common, "Login failed: " - "Plaintext authentication disabled"); - } - client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, - AUTH_PLAINTEXT_DISABLED_MSG); - client->common.auth_tried_disabled_plaintext = TRUE; - client->common.auth_attempts++; - return FALSE; -} + i_free(pop3_client->last_user); + pop3_client->last_user = i_strdup(args); -bool cmd_user(struct pop3_client *client, const char *args) -{ - if (!check_plaintext_auth(client)) - return TRUE; - - i_free(client->last_user); - client->last_user = i_strdup(args); - - client_send_raw(client, "+OK\r\n"); + client_send_raw(&pop3_client->common, "+OK\r\n"); return TRUE; } -bool cmd_pass(struct pop3_client *client, const char *args) +bool cmd_pass(struct pop3_client *pop3_client, const char *args) { + struct client *client = &pop3_client->common; string_t *plain_login, *base64; - if (client->last_user == NULL) { + if (pop3_client->last_user == NULL) { /* client may ignore the USER reply and only display the error message from PASS */ - if (!check_plaintext_auth(client)) + if (!client_check_plaintext_auth(client, TRUE)) return TRUE; - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + client_send_line(client, CLIENT_CMD_REPLY_BAD, "No username given."); return TRUE; } @@ -381,42 +147,30 @@ /* authorization ID \0 authentication ID \0 pass */ plain_login = t_str_new(128); str_append_c(plain_login, '\0'); - str_append(plain_login, client->last_user); + str_append(plain_login, pop3_client->last_user); str_append_c(plain_login, '\0'); str_append(plain_login, args); - i_free(client->last_user); - client->last_user = NULL; + i_free_and_null(pop3_client->last_user); base64 = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_ENCODED_SIZE(plain_login->used)); base64_encode(plain_login->data, plain_login->used, base64); - client_ref(client); - client->auth_initializing = TRUE; - sasl_server_auth_begin(&client->common, POP3_SERVICE_NAME, "PLAIN", - str_c(base64), sasl_callback); - client->auth_initializing = FALSE; - if (!client->common.authenticating) - return TRUE; - - /* don't read any input from client until login is finished */ - if (client->io != NULL) - io_remove(&client->io); + (void)client_auth_begin(client, "PLAIN", str_c(base64)); return TRUE; } -bool cmd_apop(struct pop3_client *client, const char *args) +bool cmd_apop(struct pop3_client *pop3_client, const char *args) { + struct client *client = &pop3_client->common; buffer_t *apop_data, *base64; const char *p; - if (client->apop_challenge == NULL) { - if (client->common.set->verbose_auth) { - client_syslog(&client->common, - "APOP failed: APOP not enabled"); - } - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + if (pop3_client->apop_challenge == NULL) { + if (client->set->verbose_auth) + client_log(client, "APOP failed: APOP not enabled"); + client_send_line(client, CLIENT_CMD_REPLY_BAD, "APOP not enabled."); return TRUE; } @@ -424,28 +178,26 @@ /* <username> <md5 sum in hex> */ p = strchr(args, ' '); if (p == NULL || strlen(p+1) != 32) { - if (client->common.set->verbose_auth) { - client_syslog(&client->common, - "APOP failed: Invalid parameters"); - } - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + if (client->set->verbose_auth) + client_log(client, "APOP failed: Invalid parameters"); + client_send_line(client, CLIENT_CMD_REPLY_BAD, "Invalid parameters."); return TRUE; } /* APOP challenge \0 username \0 APOP response */ apop_data = buffer_create_dynamic(pool_datastack_create(), 128); - buffer_append(apop_data, client->apop_challenge, - strlen(client->apop_challenge)+1); + buffer_append(apop_data, pop3_client->apop_challenge, + strlen(pop3_client->apop_challenge)+1); buffer_append(apop_data, args, (size_t)(p-args)); buffer_append_c(apop_data, '\0'); if (hex_to_binary(p+1, apop_data) < 0) { - if (client->common.set->verbose_auth) { - client_syslog(&client->common, "APOP failed: " - "Invalid characters in MD5 response"); + if (client->set->verbose_auth) { + client_log(client, "APOP failed: " + "Invalid characters in MD5 response"); } - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + client_send_line(client, CLIENT_CMD_REPLY_BAD, "Invalid characters in MD5 response."); return TRUE; } @@ -454,14 +206,6 @@ MAX_BASE64_ENCODED_SIZE(apop_data->used)); base64_encode(apop_data->data, apop_data->used, base64); - client_ref(client); - sasl_server_auth_begin(&client->common, POP3_SERVICE_NAME, "APOP", - str_c(base64), sasl_callback); - if (!client->common.authenticating) - return TRUE; - - /* don't read any input from client until login is finished */ - if (client->io != NULL) - io_remove(&client->io); + (void)client_auth_begin(client, "APOP", str_c(base64)); return TRUE; }
--- a/src/pop3-login/client-authenticate.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/pop3-login/client-authenticate.h Sun Aug 09 21:53:14 2009 -0400 @@ -1,6 +1,9 @@ #ifndef CLIENT_AUTHENTICATE_H #define CLIENT_AUTHENTICATE_H +bool pop3_client_auth_handle_reply(struct client *client, + const struct client_auth_reply *reply); + bool cmd_capa(struct pop3_client *client, const char *args); bool cmd_user(struct pop3_client *client, const char *args); bool cmd_pass(struct pop3_client *client, const char *args);
--- a/src/pop3-login/client.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/pop3-login/client.c Sun Aug 09 21:53:14 2009 -0400 @@ -7,12 +7,9 @@ #include "istream.h" #include "ostream.h" #include "randgen.h" -#include "process-title.h" #include "safe-memset.h" #include "str.h" #include "strescape.h" -#include "master-service.h" -#include "master-auth.h" #include "client.h" #include "client-authenticate.h" #include "auth-client.h" @@ -20,136 +17,23 @@ #include "pop3-proxy.h" #include "hostpid.h" -/* max. size of output buffer. if it gets full, the client is disconnected. - SASL authentication gives the largest output. */ -#define MAX_OUTBUF_SIZE 4096 - /* Disconnect client when it sends too many bad commands */ #define CLIENT_MAX_BAD_COMMANDS 10 -/* When max. number of simultaneous connections is reached, few of the - oldest connections are disconnected. Since we have to go through all of the - clients, it's faster if we disconnect multiple clients. */ -#define CLIENT_DESTROY_OLDEST_COUNT 16 - -#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000 -# error client idle timeout must be larger than authentication timeout -#endif - const char *login_protocol = "pop3"; const char *login_process_name = "pop3-login"; - -static void client_set_title(struct pop3_client *client) -{ - const char *addr; - - if (!client->common.set->verbose_proctitle || - !client->common.set->login_process_per_connection) - return; - - addr = net_ip2addr(&client->common.ip); - if (addr == NULL) - addr = "??"; - - process_title_set(t_strdup_printf(client->common.tls ? - "[%s TLS]" : "[%s]", addr)); -} - -static void client_open_streams(struct pop3_client *client, int fd) -{ - client->common.input = - i_stream_create_fd(fd, LOGIN_MAX_INBUF_SIZE, FALSE); - client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE); -} - -static void client_start_tls(struct pop3_client *client) -{ - int fd_ssl; - - client_ref(client); - if (!client_unref(client) || client->destroyed) - return; - - fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip, - client->common.set, &client->common.proxy); - if (fd_ssl == -1) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, - "TLS initialization failed."); - client_destroy(client, - "Disconnected: TLS initialization failed."); - return; - } - - client->common.proxying = TRUE; - client->common.tls = TRUE; - client->common.secured = TRUE; - client_set_title(client); - - client->common.fd = fd_ssl; - - i_stream_unref(&client->common.input); - o_stream_unref(&client->output); - - client_open_streams(client, fd_ssl); - client->io = io_add(client->common.fd, IO_READ, client_input, client); -} - -static int client_output_starttls(struct pop3_client *client) -{ - int ret; - - if ((ret = o_stream_flush(client->output)) < 0) { - client_destroy(client, "Disconnected"); - return 1; - } - - if (ret > 0) { - o_stream_unset_flush_callback(client->output); - client_start_tls(client); - } - return 1; -} +unsigned int login_default_port = 110; static bool cmd_stls(struct pop3_client *client) { - if (client->common.tls) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "TLS is already active."); - return TRUE; - } - - if (!ssl_initialized) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "TLS support isn't enabled."); - return TRUE; - } - - /* remove input handler, SSL proxy gives us a new fd. we also have to - remove it in case we have to wait for buffer to be flushed */ - if (client->io != NULL) - io_remove(&client->io); - - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, - "Begin TLS negotiation now."); - - /* uncork the old fd */ - o_stream_uncork(client->output); - - if (o_stream_flush(client->output) <= 0) { - /* the buffer has to be flushed */ - o_stream_set_flush_pending(client->output, TRUE); - o_stream_set_flush_callback(client->output, - client_output_starttls, client); - } else { - client_start_tls(client); - } + client_cmd_starttls(&client->common); return TRUE; } static bool cmd_quit(struct pop3_client *client) { client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "Logging out"); - client_destroy(client, "Aborted login"); + client_destroy(&client->common, "Aborted login"); return TRUE; } @@ -177,34 +61,12 @@ return FALSE; } -bool client_read(struct pop3_client *client) +static void pop3_client_input(struct client *client) { - switch (i_stream_read(client->common.input)) { - case -2: - /* buffer full */ - client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, - "Input buffer full, aborting"); - client_destroy(client, "Disconnected: Input buffer full"); - return FALSE; - case -1: - /* disconnected */ - client_destroy(client, "Disconnected"); - return FALSE; - case 0: - /* nothing new read */ - return TRUE; - default: - /* something was read */ - timeout_reset(client->to_idle_disconnect); - return TRUE; - } -} - -void client_input(struct pop3_client *client) -{ + struct pop3_client *pop3_client = (struct pop3_client *)client; char *line, *args; - i_assert(!client->common.authenticating); + i_assert(!client->authenticating); if (!client_read(client)) return; @@ -214,18 +76,18 @@ o_stream_cork(client->output); /* if a command starts an authentication, stop processing further commands until the authentication is finished. */ - while (!client->output->closed && !client->common.authenticating && - (line = i_stream_next_line(client->common.input)) != NULL) { + while (!client->output->closed && !client->authenticating && + (line = i_stream_next_line(client->input)) != NULL) { args = strchr(line, ' '); if (args != NULL) *args++ = '\0'; - if (client_command_execute(client, line, + if (client_command_execute(pop3_client, line, args != NULL ? args : "")) client->bad_counter = 0; else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, - "Too many invalid IMAP commands."); + client_send_line(client, CLIENT_CMD_REPLY_BYE, + "Too many invalid bad commands."); client_destroy(client, "Disconnected: Too many bad commands"); } @@ -235,43 +97,24 @@ o_stream_uncork(client->output); } -void client_destroy_oldest(void) +static struct client *pop3_client_alloc(pool_t pool) { - unsigned int max_connections = - global_login_settings->login_max_connections; - struct client *client; - struct pop3_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT]; - unsigned int i, destroy_count; + struct pop3_client *pop3_client; - /* find the oldest clients and put them to destroy-buffer */ - memset(destroy_buf, 0, sizeof(destroy_buf)); - - destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ? - CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1); - for (client = clients; client != NULL; client = client->next) { - struct pop3_client *pop3_client = (struct pop3_client *)client; + pop3_client = p_new(pool, struct pop3_client, 1); + return &pop3_client->common; +} - for (i = 0; i < destroy_count; i++) { - if (destroy_buf[i] == NULL || - destroy_buf[i]->created > pop3_client->created) { - /* @UNSAFE */ - memmove(destroy_buf+i+1, destroy_buf+i, - sizeof(destroy_buf) - - (i+1) * sizeof(struct pop3_client *)); - destroy_buf[i] = pop3_client; - break; - } - } - } +static void pop3_client_create(struct client *client ATTR_UNUSED) +{ +} - /* then kill them */ - for (i = 0; i < destroy_count; i++) { - if (destroy_buf[i] == NULL) - break; +static void pop3_client_destroy(struct client *client) +{ + struct pop3_client *pop3_client = (struct pop3_client *)client; - client_destroy(destroy_buf[i], - "Disconnected: Connection queue full"); - } + i_free(pop3_client->last_user); + i_free(pop3_client->apop_challenge); } static char *get_apop_challenge(struct pop3_client *client) @@ -295,206 +138,32 @@ (const char *)buf->data, my_hostname); } -static void client_auth_ready(struct pop3_client *client) -{ - client->io = io_add(client->common.fd, IO_READ, client_input, client); - - client->apop_challenge = get_apop_challenge(client); - if (client->apop_challenge == NULL) { - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, - client->common.set->login_greeting); - } else { - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, - t_strconcat(client->common.set->login_greeting, " ", - client->apop_challenge, NULL)); - } -} - -static void client_idle_disconnect_timeout(struct pop3_client *client) +static void pop3_client_send_greeting(struct client *client) { - client_destroy(client, "Disconnected: Inactivity"); -} + struct pop3_client *pop3_client = (struct pop3_client *)client; -struct client *client_create(int fd, bool ssl, pool_t pool, - const struct login_settings *set, - const struct ip_addr *local_ip, - const struct ip_addr *remote_ip) -{ - struct pop3_client *client; - - i_assert(fd != -1); - - if (clients_get_count() >= set->login_max_connections) { - /* reached max. users count, kill few of the - oldest connections */ - client_destroy_oldest(); - } + client->io = io_add(client->fd, IO_READ, client_input, client); - /* always use nonblocking I/O */ - net_set_nonblock(fd, TRUE); - - client = p_new(pool, struct pop3_client, 1); - client->created = ioloop_time; - client->refcount = 1; - - client->common.pool = pool; - client->common.set = set; - client->common.local_ip = *local_ip; - client->common.ip = *remote_ip; - client->common.fd = fd; - client->common.tls = ssl; - client->common.trusted = client_is_trusted(&client->common); - client->common.secured = ssl || client->common.trusted || - net_ip_compare(remote_ip, local_ip); - - client_open_streams(client, fd); - client_link(&client->common); - - client->auth_connected = auth_client_is_connected(auth_client); - if (client->auth_connected) - client_auth_ready(client); - client_set_title(client); - - client->to_idle_disconnect = - timeout_add(CLIENT_LOGIN_IDLE_TIMEOUT_MSECS, - client_idle_disconnect_timeout, client); - return &client->common; -} - -void client_destroy_success(struct pop3_client *client, const char *reason) -{ - client->login_success = TRUE; - client_destroy(client, reason); + pop3_client->apop_challenge = get_apop_challenge(pop3_client); + if (pop3_client->apop_challenge == NULL) { + client_send_line(client, CLIENT_CMD_REPLY_OK, + client->set->login_greeting); + } else { + client_send_line(client, CLIENT_CMD_REPLY_OK, + t_strconcat(client->set->login_greeting, " ", + pop3_client->apop_challenge, NULL)); + } + client->greeting_sent = TRUE; } -void client_destroy(struct pop3_client *client, const char *reason) +static void pop3_client_starttls(struct client *client ATTR_UNUSED) { - if (client->destroyed) - return; - client->destroyed = TRUE; - - if (!client->login_success && reason != NULL) { - reason = t_strconcat(reason, " ", - client_get_extra_disconnect_reason(&client->common), - NULL); - } - if (reason != NULL) - client_syslog(&client->common, reason); - - client_unlink(&client->common); - - if (client->common.input != NULL) - i_stream_close(client->common.input); - if (client->output != NULL) - o_stream_close(client->output); - - if (client->common.master_tag != 0) { - i_assert(client->common.auth_request == NULL); - i_assert(client->common.authenticating); - master_auth_request_abort(master_service, - client->common.master_tag); - } else if (client->common.auth_request != NULL) { - i_assert(client->common.authenticating); - sasl_server_auth_abort(&client->common); - } else { - i_assert(!client->common.authenticating); - } - - if (client->io != NULL) - io_remove(&client->io); - if (client->to_idle_disconnect != NULL) - timeout_remove(&client->to_idle_disconnect); - if (client->to_authfail_delay != NULL) - timeout_remove(&client->to_authfail_delay); - - if (client->common.fd != -1) { - net_disconnect(client->common.fd); - client->common.fd = -1; - } - - if (client->proxy_password != NULL) { - safe_memset(client->proxy_password, 0, - strlen(client->proxy_password)); - i_free(client->proxy_password); - client->proxy_password = NULL; - } - - i_free(client->proxy_user); - client->proxy_user = NULL; - - if (client->proxy != NULL) - login_proxy_free(&client->proxy); - - if (client->common.proxy != NULL) { - ssl_proxy_free(client->common.proxy); - client->common.proxy = NULL; - } - client_unref(client); -} - -void client_destroy_internal_failure(struct pop3_client *client) -{ - client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - "Internal login failure. " - "Refer to server log for more information."); - client_destroy(client, "Internal login failure"); -} - -void client_ref(struct pop3_client *client) -{ - client->refcount++; -} - -bool client_unref(struct pop3_client *client) -{ - i_assert(client->refcount > 0); - if (--client->refcount > 0) - return TRUE; - - i_assert(client->destroyed); - - if (client->common.input != NULL) - i_stream_unref(&client->common.input); - if (client->output != NULL) - o_stream_unref(&client->output); - - if (!client->common.proxying) { - i_assert(client->common.proxy == NULL); - master_service_client_connection_destroyed(master_service); - } - - i_free(client->last_user); - i_free(client->apop_challenge); - i_free(client->common.virtual_user); - i_free(client->common.auth_mech_name); - pool_unref(&client->common.pool); - return FALSE; } static void -client_send_raw_data(struct pop3_client *client, const void *data, size_t size) -{ - ssize_t ret; - - ret = o_stream_send(client->output, data, size); - if (ret < 0 || (size_t)ret != size) { - /* either disconnection or buffer full. in either case we want - this connection destroyed. however destroying it here might - break things if client is still tried to be accessed without - being referenced.. */ - i_stream_close(client->common.input); - } -} - -void client_send_raw(struct pop3_client *client, const char *data) -{ - client_send_raw_data(client, data, strlen(data)); -} - -void client_send_line(struct client *client, enum client_cmd_reply reply, +pop3_client_send_line(struct client *client, enum client_cmd_reply reply, const char *text) { - struct pop3_client *pop3_client = (struct pop3_client *)client; const char *prefix = "-ERR"; switch (reply) { @@ -512,6 +181,7 @@ case CLIENT_CMD_REPLY_BYE: break; case CLIENT_CMD_REPLY_STATUS: + case CLIENT_CMD_REPLY_STATUS_BAD: /* can't send status notifications */ return; } @@ -524,44 +194,31 @@ str_append(line, text); str_append(line, "\r\n"); - client_send_raw_data(pop3_client, str_data(line), + client_send_raw_data(client, str_data(line), str_len(line)); } T_END; } -void clients_notify_auth_connected(void) -{ - struct client *client; - - for (client = clients; client != NULL; client = client->next) { - struct pop3_client *pop3_client = (struct pop3_client *)client; - - if (!pop3_client->auth_connected) { - pop3_client->auth_connected = TRUE; - client_auth_ready(pop3_client); - } - } -} - -void clients_destroy_all(void) -{ - struct client *client, *next; - - for (client = clients; client != NULL; client = next) { - struct pop3_client *pop3_client = (struct pop3_client *)client; - - next = client->next; - client_destroy(pop3_client, "Disconnected: Shutting down"); - } -} - void clients_init(void) { - /* Nothing to initialize for POP3 */ + /* Nothing to initialize for POP3 */ } void clients_deinit(void) { clients_destroy_all(); } + +struct client_vfuncs client_vfuncs = { + pop3_client_alloc, + pop3_client_create, + pop3_client_destroy, + pop3_client_send_greeting, + pop3_client_starttls, + pop3_client_input, + pop3_client_send_line, + pop3_client_auth_handle_reply, + pop3_proxy_reset, + pop3_proxy_parse_line +};
--- a/src/pop3-login/client.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/pop3-login/client.h Sun Aug 09 21:53:14 2009 -0400 @@ -5,9 +5,6 @@ #include "client-common.h" #include "auth-client.h" -/* 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, @@ -18,42 +15,11 @@ struct pop3_client { struct client common; - time_t created; - int refcount; - - struct io *io; - struct ostream *output; - struct timeout *to_idle_disconnect, *to_authfail_delay; - - struct login_proxy *proxy; - char *proxy_user, *proxy_master_user, *proxy_password; enum pop3_proxy_state proxy_state; - unsigned int bad_counter; - char *last_user; - char *apop_challenge; struct auth_connect_id auth_id; - - unsigned int login_success:1; - unsigned int auth_connected:1; - unsigned int auth_initializing:1; - unsigned int destroyed:1; }; -void client_destroy(struct pop3_client *client, const char *reason); -void client_destroy_success(struct pop3_client *client, const char *reason); -void client_destroy_internal_failure(struct pop3_client *client); - -bool client_read(struct pop3_client *client); -void client_input(struct pop3_client *client); - -void client_send_raw(struct pop3_client *client, const char *data); - -void client_ref(struct pop3_client *client); -bool client_unref(struct pop3_client *client); - -void client_auth_failed(struct pop3_client *client, bool nodelay); - #endif
--- a/src/pop3-login/pop3-proxy.c Sun Aug 09 21:48:45 2009 -0400 +++ b/src/pop3-login/pop3-proxy.c Sun Aug 09 21:53:14 2009 -0400 @@ -11,7 +11,7 @@ #include "client.h" #include "pop3-proxy.h" -static void proxy_free_password(struct pop3_client *client) +static void proxy_free_password(struct client *client) { if (client->proxy_password == NULL) return; @@ -20,24 +20,7 @@ i_free_and_null(client->proxy_password); } -static void proxy_failed(struct pop3_client *client, bool send_line) -{ - if (send_line) { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - AUTH_TEMP_FAILED_MSG); - } - - login_proxy_free(&client->proxy); - proxy_free_password(client); - i_free_and_null(client->proxy_user); - i_free_and_null(client->proxy_master_user); - - /* call this last - it may destroy the client */ - client_auth_failed(client, TRUE); -} - -static void get_plain_auth(struct pop3_client *client, string_t *dest) +static void get_plain_auth(struct client *client, string_t *dest) { string_t *str; @@ -55,10 +38,10 @@ string_t *str; str = t_str_new(128); - if (client->proxy_master_user == NULL) { + if (client->common.proxy_master_user == NULL) { /* send USER command */ str_append(str, "USER "); - str_append(str, client->proxy_user); + str_append(str, client->common.proxy_user); str_append(str, "\r\n"); } else { /* master user login - use AUTH PLAIN. */ @@ -68,49 +51,50 @@ client->proxy_state = POP3_PROXY_LOGIN1; } -static int proxy_input_line(struct pop3_client *client, const char *line) +int pop3_proxy_parse_line(struct client *client, const char *line) { + struct pop3_client *pop3_client = (struct pop3_client *)client; 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) { + output = login_proxy_get_ostream(client->login_proxy); + switch (pop3_client->proxy_state) { case POP3_PROXY_BANNER: /* this is a banner */ if (strncmp(line, "+OK", 3) != 0) { - client_syslog_err(&client->common, t_strdup_printf( + client_log_err(client, t_strdup_printf( "proxy: Remote returned invalid banner: %s", str_sanitize(line, 160))); - proxy_failed(client, TRUE); + client_proxy_failed(client, TRUE); return -1; } - ssl_flags = login_proxy_get_ssl_flags(client->proxy); + ssl_flags = login_proxy_get_ssl_flags(client->login_proxy); if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) { - proxy_send_login(client, output); + proxy_send_login(pop3_client, output); } else { (void)o_stream_send_str(output, "STLS\r\n"); - client->proxy_state = POP3_PROXY_STARTTLS; + pop3_client->proxy_state = POP3_PROXY_STARTTLS; } return 0; case POP3_PROXY_STARTTLS: if (strncmp(line, "+OK", 3) != 0) { - client_syslog_err(&client->common, t_strdup_printf( + client_log_err(client, t_strdup_printf( "proxy: Remote STLS failed: %s", str_sanitize(line, 160))); - proxy_failed(client, TRUE); + client_proxy_failed(client, TRUE); return -1; } - if (login_proxy_starttls(client->proxy) < 0) { - proxy_failed(client, TRUE); + if (login_proxy_starttls(client->login_proxy) < 0) { + client_proxy_failed(client, TRUE); return -1; } /* i/ostreams changed. */ - output = login_proxy_get_ostream(client->proxy); - proxy_send_login(client, output); + output = login_proxy_get_ostream(client->login_proxy); + proxy_send_login(pop3_client, output); return 1; case POP3_PROXY_LOGIN1: str = t_str_new(128); @@ -131,7 +115,7 @@ } (void)o_stream_send(output, str_data(str), str_len(str)); proxy_free_password(client); - client->proxy_state = POP3_PROXY_LOGIN2; + pop3_client->proxy_state = POP3_PROXY_LOGIN2; return 0; case POP3_PROXY_LOGIN2: if (strncmp(line, "+OK", 3) != 0) @@ -141,31 +125,7 @@ line = t_strconcat(line, "\r\n", NULL); (void)o_stream_send_str(client->output, line); - str = t_str_new(128); - str_printfa(str, "proxy(%s): started proxying to %s:%u", - client->common.virtual_user, - login_proxy_get_host(client->proxy), - login_proxy_get_port(client->proxy)); - if (strcmp(client->common.virtual_user, - client->proxy_user) != 0) { - /* remote username is different, log it */ - str_append_c(str, '/'); - str_append(str, client->proxy_user); - } - if (client->proxy_master_user != NULL) { - str_printfa(str, " (master %s)", - client->proxy_master_user); - } - - login_proxy_detach(client->proxy, client->common.input, - client->output); - - client->proxy = NULL; - client->common.input = NULL; - client->output = NULL; - client->common.fd = -1; - client->common.proxying = TRUE; - client_destroy_success(client, str_c(str)); + client_proxy_finish_destroy_client(client); return 1; } @@ -184,129 +144,25 @@ So for now we'll just forward the error message. This shouldn't be a real problem since of course everyone will be using only Dovecot as their backend :) */ - if (strncmp(line, "-ERR ", 5) != 0) - client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAILED, + if (strncmp(line, "-ERR ", 5) != 0) { + client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, AUTH_FAILED_MSG); - else { + } else { client_send_raw(client, t_strconcat(line, "\r\n", NULL)); } - if (client->common.set->verbose_auth) { - str = t_str_new(128); - str_printfa(str, "proxy(%s): Login failed to %s:%u", - client->common.virtual_user, - login_proxy_get_host(client->proxy), - login_proxy_get_port(client->proxy)); - if (strcmp(client->common.virtual_user, - client->proxy_user) != 0) { - /* remote username is different, log it */ - str_append_c(str, '/'); - str_append(str, client->proxy_user); - } - if (client->proxy_master_user != NULL) { - str_printfa(str, " (master %s)", - client->proxy_master_user); - } - str_append(str, ": "); + if (client->set->verbose_auth) { if (strncmp(line, "-ERR ", 5) == 0) - str_append(str, line + 5); - else - str_append(str, line); - i_info("%s", str_c(str)); + line += 5; + client_proxy_log_failure(client, line); } - proxy_failed(client, FALSE); + client_proxy_failed(client, FALSE); return -1; } -static void proxy_input(struct pop3_client *client) +void pop3_proxy_reset(struct 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->destroyed) { - /* we came here from client_destroy() */ - return; - } - - /* failed for some reason, probably server disconnected */ - proxy_failed(client, TRUE); - return; - } - - i_assert(!client->destroyed); - - switch (i_stream_read(input)) { - case -2: - client_syslog_err(&client->common, - "proxy: Remote input buffer full"); - proxy_failed(client, TRUE); - return; - case -1: - client_syslog_err(&client->common, - "proxy: Remote disconnected"); - proxy_failed(client, TRUE); - return; - } - - while ((line = i_stream_next_line(input)) != NULL) { - if (proxy_input_line(client, line) != 0) - break; - } -} + struct pop3_client *pop3_client = (struct pop3_client *)client; -int pop3_proxy_new(struct pop3_client *client, const char *host, - unsigned int port, const char *user, const char *master_user, - const char *password, enum login_proxy_ssl_flags ssl_flags) -{ - i_assert(user != NULL); - i_assert(!client->destroyed); - - if (password == NULL) { - client_syslog_err(&client->common, "proxy: password not given"); - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - AUTH_TEMP_FAILED_MSG); - return -1; - } - - i_assert(client->refcount > 1); - - if (client->destroyed) { - /* connection_queue_add() decided that we were the oldest - connection and killed us. */ - return -1; - } - if (login_proxy_is_ourself(&client->common, host, port, user)) { - client_syslog_err(&client->common, "Proxying loops to itself"); - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - AUTH_TEMP_FAILED_MSG); - return -1; - } - - client->proxy = login_proxy_new(&client->common, host, port, ssl_flags, - proxy_input, client); - if (client->proxy == NULL) { - client_send_line(&client->common, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - AUTH_TEMP_FAILED_MSG); - return -1; - } - - 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); - - /* disable input until authentication is finished */ - if (client->io != NULL) - io_remove(&client->io); - return 0; + pop3_client->proxy_state = POP3_PROXY_BANNER; }
--- a/src/pop3-login/pop3-proxy.h Sun Aug 09 21:48:45 2009 -0400 +++ b/src/pop3-login/pop3-proxy.h Sun Aug 09 21:53:14 2009 -0400 @@ -1,10 +1,7 @@ #ifndef POP3_PROXY_H #define POP3_PROXY_H -#include "login-proxy.h" - -int pop3_proxy_new(struct pop3_client *client, const char *host, - unsigned int port, const char *user, const char *master_user, - const char *password, enum login_proxy_ssl_flags ssl_flags); +void pop3_proxy_reset(struct client *client); +int pop3_proxy_parse_line(struct client *client, const char *line); #endif