#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=");
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)
-{
- 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)
+bool imap_client_auth_handle_reply(struct client *client,
+ const struct client_auth_reply *reply)
{
- 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;
-
- 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;
- }
+ struct imap_client *imap_client = (struct imap_client *)client;
+ string_t *str;
- if (host != NULL) {
+ if (reply->host != NULL) {
/* IMAP referral
[nologin] referral host=.. [port=..] [destuser=..]
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 {
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);
+ imap_client->client_ignores_capability_resp_code,
+ imap_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;
+ 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;
- 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;
-
- /* 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 */
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;
}
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;
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);
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));
}
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);
#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. */
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;
}
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;
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;
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;
}
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;
}
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;
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)
-{
- 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;
-
- 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");
- }
-}
-
-static void client_send_greeting(struct imap_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, "\r\n");
-
- client_send_raw(client, str_c(greet));
- client->greeting_sent = TRUE;
-}
-
-static void client_idle_disconnect_timeout(struct imap_client *client)
+static struct client *imap_client_alloc(pool_t pool)
{
- client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
- "Disconnected for inactivity.");
- client_destroy(client, "Disconnected: Inactivity");
-}
+ struct imap_client *imap_client;
-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);
+ imap_client = p_new(pool, struct imap_client, 1);
+ return &imap_client->common;
}
-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_create(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);
+ struct imap_client *imap_client = (struct imap_client *)client;
- client->to_idle_disconnect =
- timeout_add(CLIENT_LOGIN_IDLE_TIMEOUT_MSECS,
- client_idle_disconnect_timeout, client);
- return &client->common;
+ 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);
}
-void client_destroy(struct imap_client *client, const char *reason)
+static void imap_client_destroy(struct client *client)
{
- 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_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);
+ struct imap_client *imap_client = (struct imap_client *)client;
- if (client->common.proxy != NULL) {
- ssl_proxy_free(client->common.proxy);
- client->common.proxy = NULL;
- }
- client_unref(client);
+ i_free_and_null(imap_client->proxy_backend_capability);
+ imap_parser_destroy(&imap_client->parser);
}
-void client_destroy_success(struct imap_client *client, const char *reason)
+static void imap_client_send_greeting(struct client *client)
{
- client->login_success = TRUE;
- client_destroy(client, reason);
-}
+ string_t *greet;
-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");
-}
+ greet = t_str_new(128);
+ str_append(greet, "* OK ");
+ str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
+ str_append(greet, client->set->login_greeting);
+ str_append(greet, "\r\n");
-void client_ref(struct imap_client *client)
-{
- client->refcount++;
+ client_send_raw(client, str_c(greet));
+ client->greeting_sent = TRUE;
}
-bool client_unref(struct imap_client *client)
+static void imap_client_starttls(struct 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);
+ struct imap_client *imap_client = (struct imap_client *)client;
- if (!client->common.proxying) {
- i_assert(client->common.proxy == NULL);
- master_service_client_connection_destroyed(master_service);
- }
+ imap_parser_destroy(&imap_client->parser);
+ imap_client->parser =
+ imap_parser_create(imap_client->common.input,
+ imap_client->common.output, MAX_IMAP_LINE);
- 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;
prefix = "OK";
tagged = FALSE;
break;
+ case CLIENT_CMD_REPLY_STATUS_BAD:
+ prefix = "BAD";
+ tagged = FALSE;
+ resp_code = "ALERT";
+ break;
}
T_BEGIN {
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 */
{
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
+};
#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
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;
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;
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");
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;
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;
}
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;
} 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,
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
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) {
}
}
-static void proxy_input(struct imap_client *client)
+void imap_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);
+ struct imap_client *imap_client = (struct imap_client *)client;
- 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)
-{
- 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_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;
}
#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
liblogin_la_SOURCES = \
client-common.c \
+ client-common-auth.c \
login-proxy.c \
login-settings.c \
main.c \
--- /dev/null
+/* 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);
+ }
+ }
+}
#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_unlink(struct client *client)
+void client_destroy(struct client *client, const char *reason)
{
- i_assert(clients_count > 0);
+ 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++;
+}
+
+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)
+{
+ 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;
+ }
+
+ 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)
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)
{
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;
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));
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)";
}
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);
+}
#define CLIENT_COMMON_H
#include "network.h"
+#include "login-proxy.h"
#include "sasl-server.h"
/* max. size of input buffer. this means:
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,
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_ref(struct client *client);
+bool client_unref(struct client *client);
+
+void client_cmd_starttls(struct client *client);
-void client_link(struct client *client);
-void client_unlink(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);
"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;
}
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);
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));
}
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;
#ifndef LOGIN_PROXY_H
#define LOGIN_PROXY_H
+struct client;
struct login_proxy;
enum login_proxy_ssl_flags {
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;
{
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;
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;
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;
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);
}
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)
{
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;
#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)
}
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)
+bool pop3_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 pop3_client *client, bool nodelay)
-{
- 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)
-{
- 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)
+bool cmd_auth(struct pop3_client *pop3_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;
}
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;
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)
-{
- if (client->common.secured ||
- !client->common.set->disable_plaintext_auth)
- 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;
-}
-
-bool cmd_user(struct pop3_client *client, const char *args)
+bool cmd_user(struct pop3_client *pop3_client, const char *args)
{
- if (!check_plaintext_auth(client))
+ if (!client_check_plaintext_auth(&pop3_client->common, FALSE))
return TRUE;
- i_free(client->last_user);
- client->last_user = i_strdup(args);
+ i_free(pop3_client->last_user);
+ pop3_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;
}
/* 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;
}
/* <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;
}
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;
}
#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);
#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"
#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;
}
return FALSE;
}
-bool client_read(struct pop3_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)
+static void pop3_client_input(struct 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;
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");
}
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;
-
- /* 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;
-
- 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;
- }
- }
- }
+ struct pop3_client *pop3_client;
- /* then kill them */
- for (i = 0; i < destroy_count; i++) {
- if (destroy_buf[i] == NULL)
- break;
+ pop3_client = p_new(pool, struct pop3_client, 1);
+ return &pop3_client->common;
+}
- client_destroy(destroy_buf[i],
- "Disconnected: Connection queue full");
- }
+static void pop3_client_create(struct client *client ATTR_UNUSED)
+{
+}
+
+static void pop3_client_destroy(struct client *client)
+{
+ struct pop3_client *pop3_client = (struct pop3_client *)client;
+
+ i_free(pop3_client->last_user);
+ i_free(pop3_client->apop_challenge);
}
static char *get_apop_challenge(struct pop3_client *client)
(const char *)buf->data, my_hostname);
}
-static void client_auth_ready(struct pop3_client *client)
+static void pop3_client_send_greeting(struct 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)
-{
- client_destroy(client, "Disconnected: Inactivity");
-}
-
-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();
- }
-
- /* 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);
-}
+ struct pop3_client *pop3_client = (struct pop3_client *)client;
-void client_destroy(struct pop3_client *client, const char *reason)
-{
- if (client->destroyed)
- return;
- client->destroyed = TRUE;
+ client->io = io_add(client->fd, IO_READ, client_input, client);
- 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);
+ 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 {
- 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;
+ client_send_line(client, CLIENT_CMD_REPLY_OK,
+ t_strconcat(client->set->login_greeting, " ",
+ pop3_client->apop_challenge, 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++;
+ client->greeting_sent = TRUE;
}
-bool client_unref(struct pop3_client *client)
+static void pop3_client_starttls(struct client *client ATTR_UNUSED)
{
- 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) {
case CLIENT_CMD_REPLY_BYE:
break;
case CLIENT_CMD_REPLY_STATUS:
+ case CLIENT_CMD_REPLY_STATUS_BAD:
/* can't send status notifications */
return;
}
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
+};
#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,
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
#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;
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;
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. */
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);
}
(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)
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;
}
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;
- }
-}
-
-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);
+ struct pop3_client *pop3_client = (struct pop3_client *)client;
- /* disable input until authentication is finished */
- if (client->io != NULL)
- io_remove(&client->io);
- return 0;
+ pop3_client->proxy_state = POP3_PROXY_BANNER;
}
#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