]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
*-login: Moved most of the common code to login-common.
authorTimo Sirainen <tss@iki.fi>
Mon, 10 Aug 2009 01:53:14 +0000 (21:53 -0400)
committerTimo Sirainen <tss@iki.fi>
Mon, 10 Aug 2009 01:53:14 +0000 (21:53 -0400)
--HG--
branch : HEAD

24 files changed:
src/imap-login/client-authenticate.c
src/imap-login/client-authenticate.h
src/imap-login/client.c
src/imap-login/client.h
src/imap-login/imap-proxy.c
src/imap-login/imap-proxy.h
src/login-common/Makefile.am
src/login-common/client-common-auth.c [new file with mode: 0644]
src/login-common/client-common.c
src/login-common/client-common.h
src/login-common/common.h
src/login-common/login-proxy.c
src/login-common/login-proxy.h
src/login-common/main.c
src/login-common/sasl-server.c
src/login-common/ssl-proxy-openssl.c
src/login-common/ssl-proxy.c
src/login-common/ssl-proxy.h
src/pop3-login/client-authenticate.c
src/pop3-login/client-authenticate.h
src/pop3-login/client.c
src/pop3-login/client.h
src/pop3-login/pop3-proxy.c
src/pop3-login/pop3-proxy.h

index 94caaf4914dcb124372eb98df27972bdc12fc038..0285bc415bcbba3437eb7741446c0de80ed0ff44 100644 (file)
 
 #include <stdlib.h>
 
-#define AUTH_FAILURE_DELAY_INCREASE_MSECS 5000
-
-#define IMAP_SERVICE_NAME "imap"
-
-const char *client_authenticate_get_capabilities(struct imap_client *client)
+const char *client_authenticate_get_capabilities(struct client *client)
 {
        const struct auth_mech_desc *mech;
        unsigned int i, count;
        string_t *str;
 
        str = t_str_new(128);
-       mech = sasl_server_get_advertised_mechs(&client->common, &count);
+       mech = sasl_server_get_advertised_mechs(client, &count);
        for (i = 0; i < count; i++) {
                str_append_c(str, ' ');
                str_append(str, "AUTH=");
@@ -39,146 +35,13 @@ const char *client_authenticate_get_capabilities(struct imap_client *client)
        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=..]
@@ -188,47 +51,46 @@ static bool client_handle_args(struct imap_client *client,
                   OK [...] Logged in, but you should use this server instead.
                   .. [REFERRAL ..] (Reason from auth server)
                */
-               reply = t_str_new(128);
-               str_append(reply, client->cmd_tag);
-               str_append_c(reply, ' ');
-               str_append(reply, nologin ? "NO " : "OK ");
-               str_printfa(reply, "[REFERRAL imap://%s;AUTH=%s@%s",
-                           destuser, client->common.auth_mech_name, host);
-               if (port != 143)
-                       str_printfa(reply, ":%u", port);
-               str_append(reply, "/] ");
-               if (reason != NULL)
-                       str_append(reply, reason);
-               else if (nologin)
-                       str_append(reply, "Try this server instead.");
+               str = t_str_new(128);
+               str_append(str, imap_client->cmd_tag);
+               str_append_c(str, ' ');
+               str_append(str, reply->nologin ? "NO " : "OK ");
+               str_printfa(str, "[REFERRAL imap://%s;AUTH=%s@%s",
+                           reply->destuser, client->auth_mech_name,
+                           reply->host);
+               if (reply->port != 143)
+                       str_printfa(str, ":%u", reply->port);
+               str_append(str, "/] ");
+               if (reply->reason != NULL)
+                       str_append(str, reply->reason);
+               else if (reply->nologin)
+                       str_append(str, "Try this server instead.");
                else {
-                       str_append(reply, "Logged in, but you should use "
+                       str_append(str, "Logged in, but you should use "
                                   "this server instead.");
                }
-               str_append(reply, "\r\n");
-               client_send_raw(client, str_c(reply));
-               if (!nologin) {
+               str_append(str, "\r\n");
+               client_send_raw(client, str_c(str));
+               if (!reply->nologin) {
                        client_destroy_success(client, "Login with referral");
                        return TRUE;
                }
-       } else if (nologin) {
+       } else if (reply->nologin) {
                /* Authentication went ok, but for some reason user isn't
                   allowed to log in. Shouldn't probably happen. */
-               if (reason != NULL) {
-                       client_send_line(&client->common,
+               if (reply->reason != NULL) {
+                       client_send_line(client,
                                         CLIENT_CMD_REPLY_AUTH_FAIL_REASON,
-                                        reason);
-               } else if (temp) {
-                       client_send_line(&client->common,
+                                        reply->reason);
+               } else if (reply->temp) {
+                       client_send_line(client,
                                         CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
                                         AUTH_TEMP_FAILED_MSG);
-               } else if (authz_failure) {
-                       client_send_line(&client->common,
-                                        CLIENT_CMD_REPLY_AUTHZ_FAILED,
+               } else if (reply->authz_failure) {
+                       client_send_line(client, CLIENT_CMD_REPLY_AUTHZ_FAILED,
                                         "Authorization failed");
                } else {
-                       client_send_line(&client->common,
-                                        CLIENT_CMD_REPLY_AUTH_FAILED,
+                       client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED,
                                         AUTH_FAILED_MSG);
                }
        } else {
@@ -236,127 +98,34 @@ static bool client_handle_args(struct imap_client *client,
                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 */
@@ -370,15 +139,13 @@ int cmd_authenticate(struct imap_client *client, const struct imap_arg *args)
                init_resp = IMAP_ARG_STR(&args[1]);
        }
 
-       if (!client->common.secured &&
-           strcmp(client->common.set->ssl, "required") == 0) {
-               if (client->common.set->verbose_auth) {
-                       client_syslog(&client->common, "Login failed: "
-                                     "SSL required for authentication");
+       if (!client->secured && strcmp(client->set->ssl, "required") == 0) {
+               if (client->set->verbose_auth) {
+                       client_log(client, "Login failed: "
+                                  "SSL required for authentication");
                }
-               client->common.auth_attempts++;
-               client_send_line(&client->common,
-                       CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
+               client->auth_attempts++;
+               client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
                        "Authentication not allowed until SSL/TLS is enabled.");
                return 1;
        }
@@ -386,11 +153,12 @@ int cmd_authenticate(struct imap_client *client, const struct imap_arg *args)
        mech_name = IMAP_ARG_STR(&args[0]);
        if (*mech_name == '\0')
                return -1;
-       return client_auth_begin(client, mech_name, init_resp);
+       return imap_client_auth_begin(imap_client, mech_name, init_resp);
 }
 
-int cmd_login(struct imap_client *client, const struct imap_arg *args)
+int cmd_login(struct imap_client *imap_client, const struct imap_arg *args)
 {
+       struct client *client = &imap_client->common;
        const char *user, *pass;
        string_t *plain_login, *base64;
 
@@ -405,23 +173,8 @@ int cmd_login(struct imap_client *client, const struct imap_arg *args)
        user = IMAP_ARG_STR(&args[0]);
        pass = IMAP_ARG_STR(&args[1]);
 
-       if (!client->common.secured &&
-           client->common.set->disable_plaintext_auth) {
-               if (client->common.set->verbose_auth) {
-                       client_syslog(&client->common, "Login failed: "
-                                     "Plaintext authentication disabled");
-               }
-               client->common.auth_tried_disabled_plaintext = TRUE;
-               client->common.auth_attempts++;
-               client_send_raw(client,
-                       "* BAD [ALERT] Plaintext authentication not allowed "
-                       "without SSL/TLS, but your client did it anyway. "
-                       "If anyone was listening, the password was exposed.\r\n");
-               client_send_line(&client->common,
-                                CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
-                                AUTH_PLAINTEXT_DISABLED_MSG);
+       if (!client_check_plaintext_auth(client, TRUE))
                return 1;
-       }
 
        /* authorization ID \0 authentication ID \0 pass */
        plain_login = buffer_create_dynamic(pool_datastack_create(), 64);
@@ -433,5 +186,5 @@ int cmd_login(struct imap_client *client, const struct imap_arg *args)
        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));
 }
index 1f2a347e476dcec2089809b08155e63857d891c2..7d9f4226d853be34b4d70eedf2037ac462aeb697 100644 (file)
@@ -3,7 +3,10 @@
 
 struct imap_arg;
 
-const char *client_authenticate_get_capabilities(struct imap_client *client);
+const char *client_authenticate_get_capabilities(struct client *client);
+
+bool imap_client_auth_handle_reply(struct client *client,
+                                  const struct client_auth_reply *reply);
 
 int cmd_login(struct imap_client *client, const struct imap_arg *args);
 int cmd_authenticate(struct imap_client *client, const struct imap_arg *args);
index fbafcf1027cebe1569a31ca98d6b1e9b5c2e938c..c9c9dcf099f1fcbf28279098c224b1379a116b69 100644 (file)
 
 #include <stdlib.h>
 
-/* max. size of output buffer. if it gets full, the client is disconnected.
-   SASL authentication gives the largest output. */
-#define MAX_OUTBUF_SIZE 4096
-
 /* maximum length for IMAP command line. */
 #define MAX_IMAP_LINE 8192
 
 /* Disconnect client when it sends too many bad commands */
 #define CLIENT_MAX_BAD_COMMANDS 10
 
-/* When max. number of simultaneous connections is reached, few of the
-   oldest connections are disconnected. Since we have to go through all of the
-   clients, it's faster if we disconnect multiple clients. */
-#define CLIENT_DESTROY_OLDEST_COUNT 16
-
-/* If we've been waiting auth server to respond for over this many milliseconds,
-   send a "waiting" message. */
-#define AUTH_WAITING_TIMEOUT_MSECS (30*1000)
-
-#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000
-#  error client idle timeout must be larger than authentication timeout
-#endif
-
-#define AUTH_SERVER_WAITING_MSG \
-       "Waiting for authentication process to respond.."
-#define AUTH_MASTER_WAITING_MSG \
-       "Waiting for authentication master process to respond.."
-
 const char *login_protocol = "imap";
 const char *login_process_name = "imap-login";
-
-static void client_set_title(struct imap_client *client)
-{
-       const char *addr;
-
-       if (!client->common.set->verbose_proctitle ||
-           !client->common.set->login_process_per_connection)
-               return;
-
-       addr = net_ip2addr(&client->common.ip);
-       if (addr == NULL)
-               addr = "??";
-
-       process_title_set(t_strdup_printf(client->common.tls ?
-                                         "[%s TLS]" : "[%s]", addr));
-}
-
-static void client_open_streams(struct imap_client *client, int fd)
-{
-       client->common.input =
-               i_stream_create_fd(fd, LOGIN_MAX_INBUF_SIZE, FALSE);
-       client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
-       client->parser = imap_parser_create(client->common.input,
-                                           client->output, MAX_IMAP_LINE);
-}
+unsigned int login_default_port = 143;
 
 /* Skip incoming data until newline is found,
    returns TRUE if newline was found. */
@@ -97,118 +51,36 @@ bool client_skip_line(struct imap_client *client)
        return FALSE;
 }
 
-static const char *get_capability(struct imap_client *client)
+static const char *get_capability(struct client *client)
 {
        const char *auths;
 
        auths = client_authenticate_get_capabilities(client);
        return t_strconcat(CAPABILITY_BANNER_STRING,
-                          (ssl_initialized && !client->common.tls) ?
-                          " STARTTLS" : "",
-                          client->common.set->disable_plaintext_auth &&
-                          !client->common.secured ?
-                          " LOGINDISABLED" : "", auths, NULL);
+                          (ssl_initialized && !client->tls) ? " STARTTLS" : "",
+                          client->set->disable_plaintext_auth &&
+                          !client->secured ? " LOGINDISABLED" : "",
+                          auths, NULL);
 }
 
-static int cmd_capability(struct imap_client *client)
+static int cmd_capability(struct imap_client *imap_client)
 {
+       struct client *client = &imap_client->common;
+
        /* Client is required to send CAPABILITY after STARTTLS, so the
           capability resp-code workaround checks only pre-STARTTLS
           CAPABILITY commands. */
        if (!client->starttls)
-               client->client_ignores_capability_resp_code = TRUE;
+               imap_client->client_ignores_capability_resp_code = TRUE;
        client_send_raw(client, t_strconcat(
                "* CAPABILITY ", get_capability(client), "\r\n", NULL));
-       client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
-                        "Capability completed.");
-       return 1;
-}
-
-static void client_start_tls(struct imap_client *client)
-{
-       int fd_ssl;
-
-       client_ref(client);
-       if (!client_unref(client) || client->destroyed)
-               return;
-
-       fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
-                              client->common.set, &client->common.proxy);
-       if (fd_ssl == -1) {
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
-                                "TLS initialization failed.");
-               client_destroy(client,
-                              "Disconnected: TLS initialization failed.");
-               return;
-       }
-
-       client->starttls = TRUE;
-       client->common.proxying = TRUE;
-       client->common.tls = TRUE;
-       client->common.secured = TRUE;
-       client_set_title(client);
-
-       client->common.fd = fd_ssl;
-       i_stream_unref(&client->common.input);
-       o_stream_unref(&client->output);
-       imap_parser_destroy(&client->parser);
-
-       /* CRLF is lost from buffer when streams are reopened. */
-       client->skip_line = FALSE;
-
-       client_open_streams(client, fd_ssl);
-       client->io = io_add(client->common.fd, IO_READ, client_input, client);
-}
-
-static int client_output_starttls(struct imap_client *client)
-{
-       int ret;
-
-       if ((ret = o_stream_flush(client->output)) < 0) {
-               client_destroy(client, "Disconnected");
-               return 1;
-       }
-
-       if (ret > 0) {
-               o_stream_unset_flush_callback(client->output);
-               client_start_tls(client);
-       }
+       client_send_line(client, CLIENT_CMD_REPLY_OK, "Capability completed.");
        return 1;
 }
 
 static int cmd_starttls(struct imap_client *client)
 {
-       if (client->common.tls) {
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
-                                "TLS is already active.");
-               return 1;
-       }
-
-       if (!ssl_initialized) {
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
-                                "TLS support isn't enabled.");
-               return 1;
-       }
-
-       /* remove input handler, SSL proxy gives us a new fd. we also have to
-          remove it in case we have to wait for buffer to be flushed */
-       if (client->io != NULL)
-               io_remove(&client->io);
-
-       client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
-                        "Begin TLS negotiation now.");
-
-       /* uncork the old fd */
-       o_stream_uncork(client->output);
-
-       if (o_stream_flush(client->output) <= 0) {
-               /* the buffer has to be flushed */
-               o_stream_set_flush_pending(client->output, TRUE);
-               o_stream_set_flush_callback(client->output,
-                                           client_output_starttls, client);
-       } else {
-               client_start_tls(client);
-       }
+       client_cmd_starttls(&client->common);
        return 1;
 }
 
@@ -249,13 +121,13 @@ static int cmd_id(struct imap_client *client, const struct imap_arg *args)
                env = getenv("IMAP_ID_LOG");
                value = imap_id_args_get_log_reply(args, env);
                if (value != NULL) {
-                       client_syslog(&client->common,
-                                     t_strdup_printf("ID sent: %s", value));
+                       client_log(&client->common,
+                                  t_strdup_printf("ID sent: %s", value));
                }
        }
 
        env = getenv("IMAP_ID_SEND");
-       client_send_raw(client,
+       client_send_raw(&client->common,
                t_strdup_printf("* ID %s\r\n", imap_id_reply_generate(env)));
        client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "ID completed.");
        return 1;
@@ -270,17 +142,16 @@ static int cmd_noop(struct imap_client *client)
 
 static int cmd_logout(struct imap_client *client)
 {
-       client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
-                        "Logging out");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, "Logging out");
        client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
                         "Logout completed.");
-       client_destroy(client, "Aborted login");
+       client_destroy(&client->common, "Aborted login");
        return 1;
 }
 
 static int cmd_enable(struct imap_client *client)
 {
-       client_send_raw(client, "* ENABLED\r\n");
+       client_send_raw(&client->common, "* ENABLED\r\n");
        client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
                         "ENABLE ignored in non-authenticated state.");
        return 1;
@@ -356,7 +227,7 @@ static bool client_handle_input(struct imap_client *client)
                if (fatal) {
                        client_send_line(&client->common,
                                         CLIENT_CMD_REPLY_BYE, msg);
-                       client_destroy(client,
+                       client_destroy(&client->common,
                                t_strconcat("Disconnected: ", msg, NULL));
                        return FALSE;
                }
@@ -382,10 +253,10 @@ static bool client_handle_input(struct imap_client *client)
        if (ret < 0) {
                if (*client->cmd_tag == '\0')
                        client->cmd_tag = "*";
-               if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
+               if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
                        client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
                                "Too many invalid IMAP commands.");
-                       client_destroy(client,
+                       client_destroy(&client->common,
                                "Disconnected: Too many invalid commands");
                        return FALSE;
                }  
@@ -396,31 +267,10 @@ static bool client_handle_input(struct imap_client *client)
        return ret != 0;
 }
 
-bool client_read(struct imap_client *client)
+static void imap_client_input(struct client *client)
 {
-       switch (i_stream_read(client->common.input)) {
-       case -2:
-               /* buffer full */
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
-                                "Input buffer full, aborting");
-               client_destroy(client, "Disconnected: Input buffer full");
-               return FALSE;
-       case -1:
-               /* disconnected */
-               client_destroy(client, "Disconnected");
-               return FALSE;
-       case 0:
-               /* nothing new read */
-               return TRUE;
-       default:
-               /* something was read */
-               timeout_reset(client->to_idle_disconnect);
-               return TRUE;
-       }
-}
+       struct imap_client *imap_client = (struct imap_client *)client;
 
-void client_input(struct imap_client *client)
-{
        if (!client_read(client))
                return;
 
@@ -429,280 +279,76 @@ void client_input(struct imap_client *client)
        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;
@@ -740,6 +386,11 @@ void client_send_line(struct client *client, enum client_cmd_reply reply,
                prefix = "OK";
                tagged = FALSE;
                break;
+       case CLIENT_CMD_REPLY_STATUS_BAD:
+               prefix = "BAD";
+               tagged = FALSE;
+               resp_code = "ALERT";
+               break;
        }
 
        T_BEGIN {
@@ -757,41 +408,11 @@ void client_send_line(struct client *client, enum client_cmd_reply reply,
                str_append(line, text);
                str_append(line, "\r\n");
 
-               client_send_raw_data(imap_client, str_data(line),
+               client_send_raw_data(client, str_data(line),
                                     str_len(line));
        } T_END;
 }
 
-void clients_notify_auth_connected(void)
-{
-       struct client *client;
-
-       for (client = clients; client != NULL; client = client->next) {
-               struct imap_client *imap_client = (struct imap_client *)client;
-
-               if (imap_client->to_auth_waiting != NULL)
-                       timeout_remove(&imap_client->to_auth_waiting);
-               if (!imap_client->greeting_sent)
-                       client_send_greeting(imap_client);
-               if (imap_client->input_blocked) {
-                       imap_client->input_blocked = FALSE;
-                       client_input(imap_client);
-               }
-       }
-}
-
-void clients_destroy_all(void)
-{
-       struct client *client, *next;
-
-       for (client = clients; client != NULL; client = next) {
-               struct imap_client *imap_client = (struct imap_client *)client;
-
-               next = client->next;
-               client_destroy(imap_client, "Disconnected: Shutting down");
-       }
-}
-
 void clients_init(void)
 {
        /* Nothing to initialize for IMAP */
@@ -801,3 +422,16 @@ void clients_deinit(void)
 {
        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
+};
index 76f1f38294e1f9dc5d27cac0c1d9512601d64091..5dc3d34107e2e393edbd6313989cf911da01efb7 100644 (file)
@@ -4,56 +4,22 @@
 #include "network.h"
 #include "client-common.h"
 
-/* Disconnect client after idling this many milliseconds */
-#define CLIENT_LOGIN_IDLE_TIMEOUT_MSECS (3*60*1000)
-
 struct imap_client {
        struct client common;
 
-       time_t created;
-       int refcount;
-
-       struct io *io;
-       struct ostream *output;
        struct imap_parser *parser;
-       struct timeout *to_idle_disconnect, *to_auth_waiting;
-       struct timeout *to_authfail_delay;
-
-       struct login_proxy *proxy;
-       char *proxy_user, *proxy_master_user, *proxy_password;
        char *proxy_backend_capability;
 
-       unsigned int bad_counter;
-
        const char *cmd_tag, *cmd_name;
 
-       unsigned int starttls:1;
-       unsigned int login_success:1;
        unsigned int cmd_finished:1;
        unsigned int proxy_sasl_ir:1;
        unsigned int proxy_seen_banner:1;
        unsigned int skip_line:1;
-       unsigned int input_blocked:1;
-       unsigned int destroyed:1;
-       unsigned int greeting_sent:1;
        unsigned int id_logged:1;
-       unsigned int auth_initializing:1;
        unsigned int client_ignores_capability_resp_code:1;
 };
 
-void client_destroy(struct imap_client *client, const char *reason);
-void client_destroy_success(struct imap_client *client, const char *reason);
-void client_destroy_internal_failure(struct imap_client *client);
-
-bool client_read(struct imap_client *client);
 bool client_skip_line(struct imap_client *client);
-void client_input(struct imap_client *client);
-
-void client_send_raw(struct imap_client *client, const char *data);
-void client_ref(struct imap_client *client);
-bool client_unref(struct imap_client *client);
-
-void client_set_auth_waiting(struct imap_client *client);
-void client_auth_failed(struct imap_client *client, bool nodelay);
 
 #endif
index cb4d44da66158aaa4b365088bf85c43e35ab545f..a94f4511dacf286a6af291ee30b70b0be5085b3d 100644 (file)
@@ -30,7 +30,7 @@ static void proxy_write_id(struct imap_client *client, string_t *str)
                    client->common.local_port);
 }
 
-static void proxy_free_password(struct imap_client *client)
+static void proxy_free_password(struct client *client)
 {
        if (client->proxy_password == NULL)
                return;
@@ -39,24 +39,7 @@ static void proxy_free_password(struct imap_client *client)
        i_free_and_null(client->proxy_password);
 }
 
-static void proxy_failed(struct imap_client *client, bool send_tagline)
-{
-       if (send_tagline) {
-               client_send_line(&client->common,
-                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
-                                AUTH_TEMP_FAILED_MSG);
-       }
-
-       login_proxy_free(&client->proxy);
-       proxy_free_password(client);
-       i_free_and_null(client->proxy_user);
-       i_free_and_null(client->proxy_master_user);
-
-       /* call this last - it may destroy the client */
-       client_auth_failed(client, TRUE);
-}
-
-static void get_plain_auth(struct imap_client *client, string_t *dest)
+static void get_plain_auth(struct client *client, string_t *dest)
 {
        string_t *str;
 
@@ -91,19 +74,20 @@ static void proxy_write_login(struct imap_client *client, 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");
@@ -119,7 +103,7 @@ static int proxy_input_banner(struct imap_client *client,
        string_t *str;
 
        if (strncmp(line, "* OK ", 5) != 0) {
-               client_syslog_err(&client->common, t_strdup_printf(
+               client_log_err(&client->common, t_strdup_printf(
                        "proxy: Remote returned invalid banner: %s",
                        str_sanitize(line, 160)));
                return -1;
@@ -134,11 +118,11 @@ static int proxy_input_banner(struct imap_client *client,
                        client->proxy_sasl_ir = TRUE;
        }
 
-       ssl_flags = login_proxy_get_ssl_flags(client->proxy);
+       ssl_flags = login_proxy_get_ssl_flags(client->common.login_proxy);
        if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) != 0) {
                if (capabilities != NULL &&
                    !str_array_icase_find(capabilities, "STARTTLS")) {
-                       client_syslog_err(&client->common,
+                       client_log_err(&client->common,
                                "proxy: Remote doesn't support STARTTLS");
                        return -1;
                }
@@ -151,20 +135,21 @@ static int proxy_input_banner(struct imap_client *client,
        return 0;
 }
 
-static int proxy_input_line(struct imap_client *client, const char *line)
+int imap_proxy_parse_line(struct client *client, const char *line)
 {
+       struct imap_client *imap_client = (struct imap_client *)client;
        struct ostream *output;
        const char *capability;
        string_t *str;
 
        i_assert(!client->destroyed);
 
-       output = login_proxy_get_ostream(client->proxy);
-       if (!client->proxy_seen_banner) {
+       output = login_proxy_get_ostream(client->login_proxy);
+       if (!imap_client->proxy_seen_banner) {
                /* this is a banner */
-               client->proxy_seen_banner = TRUE;
-               if (proxy_input_banner(client, output, line) < 0) {
-                       proxy_failed(client, TRUE);
+               imap_client->proxy_seen_banner = TRUE;
+               if (proxy_input_banner(imap_client, output, line) < 0) {
+                       client_proxy_failed(client, TRUE);
                        return -1;
                }
                return 0;
@@ -180,88 +165,48 @@ static int proxy_input_line(struct imap_client *client, const char *line)
        } else if (strncmp(line, "S ", 2) == 0) {
                if (strncmp(line, "S OK ", 5) != 0) {
                        /* STARTTLS failed */
-                       client_syslog_err(&client->common, t_strdup_printf(
+                       client_log_err(client, t_strdup_printf(
                                "proxy: Remote STARTTLS failed: %s",
                                str_sanitize(line + 5, 160)));
-                       proxy_failed(client, TRUE);
+                       client_proxy_failed(client, TRUE);
                        return -1;
                }
                /* STARTTLS successful, begin TLS negotiation. */
-               if (login_proxy_starttls(client->proxy) < 0) {
-                       proxy_failed(client, TRUE);
+               if (login_proxy_starttls(client->login_proxy) < 0) {
+                       client_proxy_failed(client, TRUE);
                        return -1;
                }
                /* i/ostreams changed. */
-               output = login_proxy_get_ostream(client->proxy);
+               output = login_proxy_get_ostream(client->login_proxy);
                str = t_str_new(128);
-               proxy_write_login(client, str);
+               proxy_write_login(imap_client, str);
                (void)o_stream_send(output, str_data(str), str_len(str));
                return 1;
        } else if (strncmp(line, "L OK ", 5) == 0) {
                /* Login successful. Send this line to client. */
-               capability = client->proxy_backend_capability;
+               capability = imap_client->proxy_backend_capability;
                if (strncmp(line + 5, "[CAPABILITY ", 12) == 0)
                        capability = t_strcut(line + 5 + 12, ']');
 
                str = t_str_new(128);
-               client_send_capability_if_needed(client, str, capability);
-               str_append(str, client->cmd_tag);
+               client_send_capability_if_needed(imap_client, str, capability);
+               str_append(str, imap_client->cmd_tag);
                str_append(str, line + 1);
                str_append(str, "\r\n");
                (void)o_stream_send(client->output,
                                    str_data(str), str_len(str));
 
-               str_truncate(str, 0);
-               str_printfa(str, "proxy(%s): started proxying to %s:%u",
-                           client->common.virtual_user,
-                           login_proxy_get_host(client->proxy),
-                           login_proxy_get_port(client->proxy));
-               if (strcmp(client->common.virtual_user,
-                          client->proxy_user) != 0) {
-                       /* remote username is different, log it */
-                       str_append_c(str, '/');
-                       str_append(str, client->proxy_user);
-               }
-               if (client->proxy_master_user != NULL) {
-                       str_printfa(str, " (master %s)",
-                                   client->proxy_master_user);
-               }
-
-               (void)client_skip_line(client);
-               login_proxy_detach(client->proxy, client->common.input,
-                                  client->output);
-
-               client->proxy = NULL;
-               client->common.input = NULL;
-               client->output = NULL;
-               client->common.fd = -1;
-               client->common.proxying = TRUE;
-               client_destroy_success(client, str_c(str));
+               (void)client_skip_line(imap_client);
+               client_proxy_finish_destroy_client(client);
                return 1;
        } else if (strncmp(line, "L ", 2) == 0) {
                line += 2;
-               if (client->common.set->verbose_auth) {
-                       str = t_str_new(128);
-                       str_printfa(str, "proxy(%s): Login failed to %s:%u",
-                                   client->common.virtual_user,
-                                   login_proxy_get_host(client->proxy),
-                                   login_proxy_get_port(client->proxy));
-                       if (strcmp(client->common.virtual_user,
-                                  client->proxy_user) != 0) {
-                               /* remote username is different, log it */
-                               str_append_c(str, '/');
-                               str_append(str, client->proxy_user);
-                       }
-                       if (client->proxy_master_user != NULL) {
-                               str_printfa(str, " (master %s)",
-                                           client->proxy_master_user);
-                       }
-                       str_append(str, ": ");
-                       if (strncasecmp(line, "NO ", 3) == 0)
-                               str_append(str, line + 3);
-                       else
-                               str_append(str, line);
-                       i_info("%s", str_c(str));
+               if (client->set->verbose_auth) {
+                       const char *log_line = line;
+
+                       if (strncasecmp(log_line, "NO ", 3) == 0)
+                               log_line += 3;
+                       client_proxy_log_failure(client, log_line);
                }
 #define STR_NO_IMAP_RESP_CODE_AUTHFAILED "NO ["IMAP_RESP_CODE_AUTHFAILED"]"
                if (strncmp(line, STR_NO_IMAP_RESP_CODE_AUTHFAILED,
@@ -271,13 +216,12 @@ static int proxy_input_line(struct imap_client *client, const char *line)
                           the remote is sending a different error message
                           an attacker can't find out what users exist in
                           the system. */
-                       client_send_line(&client->common,
-                                        CLIENT_CMD_REPLY_AUTH_FAILED,
+                       client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED,
                                         AUTH_FAILED_MSG);
                } else if (strncmp(line, "NO [", 4) == 0) {
                        /* remote sent some other resp-code. forward it. */
                        client_send_raw(client, t_strconcat(
-                               client->cmd_tag, " ", line, "\r\n", NULL));
+                               imap_client->cmd_tag, " ", line, "\r\n", NULL));
                } else {
                        /* there was no [resp-code], so remote isn't Dovecot
                           v1.2+. we could either forward the line as-is and
@@ -286,16 +230,15 @@ static int proxy_input_line(struct imap_client *client, const char *line)
                           failures. since other errors are pretty rare,
                           it's safer to just hide them. they're still
                           available in logs though. */
-                       client_send_line(&client->common,
-                                        CLIENT_CMD_REPLY_AUTH_FAILED,
+                       client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED,
                                         AUTH_FAILED_MSG);
                }
 
-               proxy_failed(client, FALSE);
+               client_proxy_failed(client, FALSE);
                return -1;
        } else if (strncasecmp(line, "* CAPABILITY ", 13) == 0) {
-               i_free(client->proxy_backend_capability);
-               client->proxy_backend_capability = i_strdup(line + 13);
+               i_free(imap_client->proxy_backend_capability);
+               imap_client->proxy_backend_capability = i_strdup(line + 13);
                return 0;
        } else if (strncasecmp(line, "I ", 2) == 0 ||
                   strncasecmp(line, "* ID ", 5) == 0) {
@@ -311,96 +254,10 @@ static int proxy_input_line(struct imap_client *client, const char *line)
        }
 }
 
-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;
 }
index 1a5b708e2774523792938d4f18f9073627232840..e0f32240c284a798d963caa206a6e7db63085df6 100644 (file)
@@ -1,10 +1,7 @@
 #ifndef IMAP_PROXY_H
 #define IMAP_PROXY_H
 
-#include "login-proxy.h"
-
-int imap_proxy_new(struct imap_client *client, const char *hosts,
-                  unsigned int port, const char *user, const char *master_user,
-                  const char *password, enum login_proxy_ssl_flags ssl_flags);
+void imap_proxy_reset(struct client *client);
+int imap_proxy_parse_line(struct client *client, const char *line);
 
 #endif
index 289b9b73e3a9cb2b04e69802675f709a6288ee9b..4f475856baf2a030a1dc712ade08b8f43acd1219 100644 (file)
@@ -9,6 +9,7 @@ AM_CPPFLAGS = \
 
 liblogin_la_SOURCES = \
        client-common.c \
+       client-common-auth.c \
        login-proxy.c \
        login-settings.c \
        main.c \
diff --git a/src/login-common/client-common-auth.c b/src/login-common/client-common-auth.c
new file mode 100644 (file)
index 0000000..6277c2e
--- /dev/null
@@ -0,0 +1,480 @@
+/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "istream.h"
+#include "ostream.h"
+#include "str.h"
+#include "safe-memset.h"
+#include "login-proxy.h"
+#include "auth-client.h"
+#include "client-common.h"
+
+#include <stdlib.h>
+
+/* If we've been waiting auth server to respond for over this many milliseconds,
+   send a "waiting" message. */
+#define AUTH_WAITING_TIMEOUT_MSECS (30*1000)
+#define AUTH_FAILURE_DELAY_INCREASE_MSECS 5000
+
+#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000
+#  error client idle timeout must be larger than authentication timeout
+#endif
+
+static void client_authfail_delay_timeout(struct client *client)
+{
+       timeout_remove(&client->to_authfail_delay);
+
+       /* get back to normal client input. */
+       i_assert(client->io == NULL);
+       client->io = io_add(client->fd, IO_READ, client_input, client);
+       client_input(client);
+}
+
+void client_auth_failed(struct client *client, bool nodelay)
+{
+       unsigned int delay_msecs;
+
+       i_free_and_null(client->master_data_prefix);
+
+       if (client->auth_initializing)
+               return;
+
+       if (client->io != NULL)
+               io_remove(&client->io);
+       if (nodelay) {
+               client->io = io_add(client->fd, IO_READ, client_input, client);
+               client_input(client);
+               return;
+       }
+
+       /* increase the timeout after each unsuccessful attempt, but don't
+          increase it so high that the idle timeout would be triggered */
+       delay_msecs = client->auth_attempts * AUTH_FAILURE_DELAY_INCREASE_MSECS;
+       if (delay_msecs > CLIENT_LOGIN_IDLE_TIMEOUT_MSECS)
+               delay_msecs = CLIENT_LOGIN_IDLE_TIMEOUT_MSECS - 1000;
+
+       i_assert(client->to_authfail_delay == NULL);
+       client->to_authfail_delay =
+               timeout_add(delay_msecs, client_authfail_delay_timeout, client);
+}
+
+static void client_auth_waiting_timeout(struct client *client)
+{
+       client_send_line(client, CLIENT_CMD_REPLY_STATUS,
+                        client->master_tag == 0 ?
+                        AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
+       timeout_remove(&client->to_auth_waiting);
+}
+
+void client_set_auth_waiting(struct client *client)
+{
+       i_assert(client->to_auth_waiting == NULL);
+       client->to_auth_waiting =
+               timeout_add(AUTH_WAITING_TIMEOUT_MSECS,
+                           client_auth_waiting_timeout, client);
+}
+
+static void client_auth_parse_args(struct client *client,
+                                  const char *const *args,
+                                  struct client_auth_reply *reply_r)
+{
+       const char *key, *value, *p;
+
+       memset(reply_r, 0, sizeof(*reply_r));
+       reply_r->port = login_default_port;
+
+       for (; *args != NULL; args++) {
+               p = strchr(*args, '=');
+               if (p == NULL) {
+                       key = *args;
+                       value = "";
+               } else {
+                       key = t_strdup_until(*args, p);
+                       value = p + 1;
+               }
+               if (strcmp(key, "nologin") == 0)
+                       reply_r->nologin = TRUE;
+               else if (strcmp(key, "nodelay") == 0)
+                       reply_r->nodelay = TRUE;
+               else if (strcmp(key, "proxy") == 0)
+                       reply_r->proxy = TRUE;
+               else if (strcmp(key, "temp") == 0)
+                       reply_r->temp = TRUE;
+               else if (strcmp(key, "authz") == 0)
+                       reply_r->authz_failure = TRUE;
+               else if (strcmp(key, "reason") == 0)
+                       reply_r->reason = value;
+               else if (strcmp(key, "host") == 0)
+                       reply_r->host = value;
+               else if (strcmp(key, "port") == 0)
+                       reply_r->port = atoi(value);
+               else if (strcmp(key, "destuser") == 0)
+                       reply_r->destuser = value;
+               else if (strcmp(key, "pass") == 0)
+                       reply_r->password = value;
+               else if (strcmp(key, "master") == 0)
+                       reply_r->master_user = value;
+               else if (strcmp(key, "ssl") == 0) {
+                       if (strcmp(value, "yes") == 0)
+                               reply_r->ssl_flags |= PROXY_SSL_FLAG_YES;
+                       else if (strcmp(value, "any-cert") == 0) {
+                               reply_r->ssl_flags |= PROXY_SSL_FLAG_YES |
+                                       PROXY_SSL_FLAG_ANY_CERT;
+                       }
+               } else if (strcmp(key, "starttls") == 0) {
+                       reply_r->ssl_flags |= PROXY_SSL_FLAG_STARTTLS;
+               } else if (strcmp(key, "user") == 0) {
+                       /* already handled in login-common */
+               } else if (client->set->auth_debug)
+                       i_info("Ignoring unknown passdb extra field: %s", key);
+       }
+
+       if (reply_r->destuser == NULL)
+               reply_r->destuser = client->virtual_user;
+}
+
+static void proxy_free_password(struct client *client)
+{
+       if (client->proxy_password == NULL)
+               return;
+
+       safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
+       i_free_and_null(client->proxy_password);
+}
+
+void client_proxy_finish_destroy_client(struct client *client)
+{
+       string_t *str = t_str_new(128);
+
+       str_printfa(str, "proxy(%s): started proxying to %s:%u",
+                   client->virtual_user,
+                   login_proxy_get_host(client->login_proxy),
+                   login_proxy_get_port(client->login_proxy));
+       if (strcmp(client->virtual_user, client->proxy_user) != 0) {
+               /* remote username is different, log it */
+               str_append_c(str, '/');
+               str_append(str, client->proxy_user);
+       }
+       if (client->proxy_master_user != NULL)
+               str_printfa(str, " (master %s)", client->proxy_master_user);
+
+       login_proxy_detach(client->login_proxy, client->input, client->output);
+
+       client->login_proxy = NULL;
+       client->input = NULL;
+       client->output = NULL;
+       client->fd = -1;
+       client->proxying = TRUE;
+       client_destroy_success(client, str_c(str));
+}
+
+void client_proxy_log_failure(struct client *client, const char *line)
+{
+       string_t *str = t_str_new(128);
+
+       str_printfa(str, "proxy(%s): Login failed to %s:%u",
+                   client->virtual_user,
+                   login_proxy_get_host(client->login_proxy),
+                   login_proxy_get_port(client->login_proxy));
+       if (strcmp(client->virtual_user, client->proxy_user) != 0) {
+               /* remote username is different, log it */
+               str_append_c(str, '/');
+               str_append(str, client->proxy_user);
+       }
+       if (client->proxy_master_user != NULL)
+               str_printfa(str, " (master %s)", client->proxy_master_user);
+       str_append(str, ": ");
+       str_append(str, line);
+       i_info("%s", str_c(str));
+}
+
+void client_proxy_failed(struct client *client, bool send_line)
+{
+       if (send_line) {
+               client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
+       }
+
+       login_proxy_free(&client->login_proxy);
+       proxy_free_password(client);
+       i_free_and_null(client->proxy_user);
+       i_free_and_null(client->proxy_master_user);
+
+       /* call this last - it may destroy the client */
+       client_auth_failed(client, TRUE);
+}
+
+static void proxy_input(struct client *client)
+{
+       struct istream *input;
+       const char *line;
+
+       if (client->login_proxy == NULL) {
+               /* we're just freeing the proxy */
+               return;
+       }
+
+       input = login_proxy_get_istream(client->login_proxy);
+       if (input == NULL) {
+               if (client->destroyed) {
+                       /* we came here from client_destroy() */
+                       return;
+               }
+
+               /* failed for some reason, probably server disconnected */
+               client_proxy_failed(client, TRUE);
+               return;
+       }
+
+       i_assert(!client->destroyed);
+
+       switch (i_stream_read(input)) {
+       case -2:
+               client_log_err(client, "proxy: Remote input buffer full");
+               client_proxy_failed(client, TRUE);
+               return;
+       case -1:
+               client_log_err(client, "proxy: Remote disconnected");
+               client_proxy_failed(client, TRUE);
+               return;
+       }
+
+       while ((line = i_stream_next_line(input)) != NULL) {
+               if (client->v.proxy_parse_line(client, line) != 0)
+                       break;
+       }
+}
+
+static int proxy_start(struct client *client,
+                      const struct client_auth_reply *reply)
+{
+       i_assert(reply->destuser != NULL);
+       i_assert(!client->destroyed);
+
+       client->v.proxy_reset(client);
+
+       if (reply->password == NULL) {
+               client_log_err(client, "proxy: password not given");
+               client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
+               return -1;
+       }
+
+       i_assert(client->refcount > 1);
+
+       if (client->destroyed) {
+               /* connection_queue_add() decided that we were the oldest
+                  connection and killed us. */
+               return -1;
+       }
+       if (login_proxy_is_ourself(client, reply->host, reply->port,
+                                  reply->destuser)) {
+               client_log_err(client, "Proxying loops to itself");
+               client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
+               return -1;
+       }
+
+       client->login_proxy =
+               login_proxy_new(client, reply->host, reply->port,
+                               reply->ssl_flags, proxy_input, client);
+       if (client->login_proxy == NULL) {
+               client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
+               return -1;
+       }
+
+       client->proxy_user = i_strdup(reply->destuser);
+       client->proxy_master_user = i_strdup(reply->master_user);
+       client->proxy_password = i_strdup(reply->password);
+
+       /* disable input until authentication is finished */
+       if (client->io != NULL)
+               io_remove(&client->io);
+       return 0;
+}
+
+static bool
+client_auth_handle_reply(struct client *client,
+                        const struct client_auth_reply *reply, bool success)
+{
+       if (reply->proxy) {
+               /* we want to proxy the connection to another server.
+                  don't do this unless authentication succeeded. with
+                  master user proxying we can get FAIL with proxy still set.
+
+                  proxy host=.. [port=..] [destuser=..] pass=.. */
+               if (!success)
+                       return FALSE;
+               if (proxy_start(client, reply) < 0)
+                       client_auth_failed(client, TRUE);
+               return TRUE;
+       }
+       return client->v.auth_handle_reply(client, reply);
+}
+
+static void client_auth_input(struct client *client)
+{
+       char *line;
+
+       if (!client_read(client))
+               return;
+
+       /* @UNSAFE */
+       line = i_stream_next_line(client->input);
+       if (line == NULL)
+               return;
+
+       if (strcmp(line, "*") == 0)
+               sasl_server_auth_abort(client);
+       else {
+               client_set_auth_waiting(client);
+               auth_client_request_continue(client->auth_request, line);
+               io_remove(&client->io);
+
+               /* clear sensitive data */
+               safe_memset(line, 0, strlen(line));
+       }
+}
+
+static void
+sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
+             const char *data, const char *const *args)
+{
+       struct const_iovec iov[3];
+       struct client_auth_reply reply;
+       size_t data_len;
+
+       i_assert(!client->destroyed ||
+                sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED ||
+                sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED);
+
+       switch (sasl_reply) {
+       case SASL_SERVER_REPLY_SUCCESS:
+               if (client->to_auth_waiting != NULL)
+                       timeout_remove(&client->to_auth_waiting);
+               if (args != NULL) {
+                       client_auth_parse_args(client, args, &reply);
+                       if (client_auth_handle_reply(client, &reply, TRUE))
+                               break;
+               }
+               client_destroy_success(client, "Login");
+               break;
+       case SASL_SERVER_REPLY_AUTH_FAILED:
+       case SASL_SERVER_REPLY_AUTH_ABORTED:
+               if (client->to_auth_waiting != NULL)
+                       timeout_remove(&client->to_auth_waiting);
+               if (args != NULL) {
+                       client_auth_parse_args(client, args, &reply);
+                       reply.nologin = TRUE;
+                       if (client_auth_handle_reply(client, &reply, FALSE))
+                               break;
+               }
+
+               if (sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED) {
+                       client_send_line(client, CLIENT_CMD_REPLY_BAD,
+                                        "Authentication aborted by client.");
+               } else if (data == NULL) {
+                       client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED,
+                                        AUTH_FAILED_MSG);
+               } else {
+                       client_send_line(client,
+                                        CLIENT_CMD_REPLY_AUTH_FAIL_REASON,
+                                        data);
+               }
+
+               if (!client->destroyed)
+                       client_auth_failed(client, reply.nodelay);
+               break;
+       case SASL_SERVER_REPLY_MASTER_FAILED:
+               if (data == NULL)
+                       client_destroy_internal_failure(client);
+               else {
+                       client_send_line(client,
+                                        CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data);
+                       /* authentication itself succeeded, we just hit some
+                          internal failure. */
+                       client_destroy_success(client, data);
+               }
+               break;
+       case SASL_SERVER_REPLY_CONTINUE:
+               data_len = strlen(data);
+               iov[0].iov_base = "+ ";
+               iov[0].iov_len = 2;
+               iov[1].iov_base = data;
+               iov[1].iov_len = data_len;
+               iov[2].iov_base = "\r\n";
+               iov[2].iov_len = 2;
+
+               /* don't check return value here. it gets tricky if we try
+                  to call client_destroy() in here. */
+               (void)o_stream_sendv(client->output, iov, 3);
+
+               if (client->to_auth_waiting != NULL)
+                       timeout_remove(&client->to_auth_waiting);
+
+               i_assert(client->io == NULL);
+               client->io = io_add(client->fd, IO_READ,
+                                   client_auth_input, client);
+               client_auth_input(client);
+               return;
+       }
+
+       client_unref(client);
+}
+
+int client_auth_begin(struct client *client, const char *mech_name,
+                     const char *init_resp)
+{
+       client_ref(client);
+       client->auth_initializing = TRUE;
+       sasl_server_auth_begin(client, login_protocol, mech_name,
+                              init_resp, sasl_callback);
+       client->auth_initializing = FALSE;
+       if (!client->authenticating)
+               return 1;
+
+       /* don't handle input until we get the initial auth reply */
+       if (client->io != NULL)
+               io_remove(&client->io);
+       client_set_auth_waiting(client);
+       return 0;
+}
+
+bool client_check_plaintext_auth(struct client *client, bool pass_sent)
+{
+       if (client->secured || !client->set->disable_plaintext_auth)
+               return TRUE;
+
+       if (client->set->verbose_auth) {
+               client_log(client, "Login failed: "
+                          "Plaintext authentication disabled");
+       }
+       if (pass_sent) {
+               client_send_line(client, CLIENT_CMD_REPLY_STATUS_BAD,
+                        "Plaintext authentication not allowed "
+                        "without SSL/TLS, but your client did it anyway. "
+                        "If anyone was listening, the password was exposed.");
+       }
+       client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
+                        AUTH_PLAINTEXT_DISABLED_MSG);
+       client->auth_tried_disabled_plaintext = TRUE;
+       client->auth_attempts++;
+       return FALSE;
+}
+
+void clients_notify_auth_connected(void)
+{
+       struct client *client;
+
+       for (client = clients; client != NULL; client = client->next) {
+               if (client->to_auth_waiting != NULL)
+                       timeout_remove(&client->to_auth_waiting);
+               if (!client->greeting_sent)
+                       client->v.send_greeting(client);
+               if (client->input_blocked) {
+                       client->input_blocked = FALSE;
+                       client_input(client);
+               }
+       }
+}
index 80836475d6136a68694d4c7e422fa5ff6c73bd0f..92c8aef665247e0d44e675a235182ce9b250efc1 100644 (file)
 #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)
@@ -33,6 +337,22 @@ 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)
 {
@@ -81,13 +401,16 @@ 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;
@@ -151,14 +474,14 @@ client_get_log_str(struct client *client, const char *msg)
        return str_c(str);
 }
 
-void client_syslog(struct client *client, const char *msg)
+void client_log(struct client *client, const char *msg)
 {
        T_BEGIN {
                i_info("%s", client_get_log_str(client, msg));
        } T_END;
 }
 
-void client_syslog_err(struct client *client, const char *msg)
+void client_log_err(struct client *client, const char *msg)
 {
        T_BEGIN {
                i_error("%s", client_get_log_str(client, msg));
@@ -190,10 +513,10 @@ bool client_is_trusted(struct client *client)
 
 const char *client_get_extra_disconnect_reason(struct client *client)
 {
-       if (client->set->ssl_require_client_cert && client->proxy != NULL) {
-               if (ssl_proxy_has_broken_client_cert(client->proxy))
+       if (client->set->ssl_require_client_cert && client->ssl_proxy != NULL) {
+               if (ssl_proxy_has_broken_client_cert(client->ssl_proxy))
                        return "(client sent an invalid cert)";
-               if (!ssl_proxy_has_valid_client_cert(client->proxy))
+               if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy))
                        return "(client didn't send a cert)";
        }
 
@@ -209,3 +532,56 @@ const char *client_get_extra_disconnect_reason(struct client *client)
        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);
+}
index 2a0f1f39d4cb2802760c01a5ed26ba030a788a8c..ae1d01cc2c3eb8d5b45950000d434d94e78dcf92 100644 (file)
@@ -2,6 +2,7 @@
 #define CLIENT_COMMON_H
 
 #include "network.h"
+#include "login-proxy.h"
 #include "sasl-server.h"
 
 /* max. size of input buffer. this means:
    POP3: Max. length of a command line (spec says 512 would be enough)
 */
 #define LOGIN_MAX_INBUF_SIZE 4096
+/* max. size of output buffer. if it gets full, the client is disconnected.
+   SASL authentication gives the largest output. */
+#define LOGIN_MAX_OUTBUF_SIZE 4096
+
+/* Disconnect client after idling this many milliseconds */
+#define CLIENT_LOGIN_IDLE_TIMEOUT_MSECS (3*60*1000)
+
+#define AUTH_SERVER_WAITING_MSG \
+       "Waiting for authentication process to respond.."
+#define AUTH_MASTER_WAITING_MSG \
+       "Waiting for authentication master process to respond.."
 
 enum client_cmd_reply {
        CLIENT_CMD_REPLY_OK,
@@ -21,61 +33,133 @@ enum client_cmd_reply {
        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);
index e39e780ddf51c0d8d3830491c59cbd9de26eb53b..6df3ddc8103cd20c75af02acf5fd38d319ef6485 100644 (file)
@@ -13,6 +13,7 @@
        "Plaintext authentication disallowed on non-secure (SSL/TLS) connections."
 
 extern const char *login_protocol, *login_process_name;
+extern unsigned int login_default_port;
 
 extern struct auth_client *auth_client;
 extern bool closing_down;
index a259f8d0ddcf57e34d6c228b91eb5051795261b6..37ecada92acdaaedd9712ad5c607534ac9c974e7 100644 (file)
@@ -237,7 +237,7 @@ void login_proxy_free(struct login_proxy **_proxy)
        }
 
        if (proxy->ssl_proxy != NULL)
-               ssl_proxy_free(proxy->ssl_proxy);
+               ssl_proxy_free(&proxy->ssl_proxy);
        net_disconnect(proxy->server_fd);
 
        i_free(proxy->host);
@@ -335,11 +335,11 @@ static int login_proxy_ssl_handshaked(void *context)
                return 0;
 
        if (!ssl_proxy_has_broken_client_cert(proxy->ssl_proxy)) {
-               client_syslog_err(proxy->prelogin_client, t_strdup_printf(
+               client_log_err(proxy->prelogin_client, t_strdup_printf(
                        "proxy: SSL certificate not received from %s:%u",
                        proxy->host, proxy->port));
        } else {
-               client_syslog_err(proxy->prelogin_client, t_strdup_printf(
+               client_log_err(proxy->prelogin_client, t_strdup_printf(
                        "proxy: Received invalid SSL certificate from %s:%u",
                        proxy->host, proxy->port));
        }
@@ -362,7 +362,7 @@ int login_proxy_starttls(struct login_proxy *proxy)
                                  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;
index 25d034c31453bf45cf5529e9d5024ff0389585e2..87a6702e68dfa0439247fe932dd56007999be057 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef LOGIN_PROXY_H
 #define LOGIN_PROXY_H
 
+struct client;
 struct login_proxy;
 
 enum login_proxy_ssl_flags {
index 3401f4f65791d4c8a02d34c2c2035737c843b3d9..9f91313c76c530be277eb76305c721f9b5a7fa40 100644 (file)
@@ -59,7 +59,7 @@ static void client_connected(const struct master_service_connection *conn)
                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;
index 1bbbb630a6f4c085b42293c7c44a4ca3fa5dfb8e..0796b2eeea47caf309c4b61b07950f40d5938fe6 100644 (file)
@@ -54,8 +54,8 @@ client_get_auth_flags(struct client *client)
 {
         enum auth_request_flags auth_flags = 0;
 
-       if (client->proxy != NULL &&
-           ssl_proxy_has_valid_client_cert(client->proxy))
+       if (client->ssl_proxy != NULL &&
+           ssl_proxy_has_valid_client_cert(client->ssl_proxy))
                auth_flags |= AUTH_REQUEST_FLAG_VALID_CLIENT_CERT;
        if (client->secured)
                auth_flags |= AUTH_REQUEST_FLAG_SECURED;
@@ -256,8 +256,8 @@ void sasl_server_auth_begin(struct client *client,
        memset(&info, 0, sizeof(info));
        info.mech = mech->name;
        info.service = service;
-       info.cert_username = client->proxy == NULL ? NULL :
-               ssl_proxy_get_peer_name(client->proxy);
+       info.cert_username = client->ssl_proxy == NULL ? NULL :
+               ssl_proxy_get_peer_name(client->ssl_proxy);
        info.flags = client_get_auth_flags(client);
        info.local_ip = client->local_ip;
        info.remote_ip = client->ip;
@@ -282,9 +282,8 @@ static void sasl_server_auth_cancel(struct client *client, const char *reason,
        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;
index d741fa49ba5c7e1beb25a862d984fc6578536cc1..35badb91e7dba72d36ddf294481be5088f132d42 100644 (file)
@@ -669,8 +669,11 @@ const char *ssl_proxy_get_security_string(struct ssl_proxy *proxy)
                               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);
 }
 
index 1042596bde4d26c66cacafcc7d1475eb4ae1e16f..5670844b88c39687c187d89da4f09b02417ad48c 100644 (file)
@@ -57,7 +57,7 @@ const char *ssl_proxy_get_security_string(struct ssl_proxy *proxy ATTR_UNUSED)
        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)
 {
index a2ee5324738d6cef6b1fcbcab5de003185a6fa6e..0a2f15b90cffc50bb86deec50837bca8f3d9d9cb 100644 (file)
@@ -26,7 +26,7 @@ const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy);
 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;
index 2c3ec1d6ff95cbefc3addff1da62cb1f795834fc..2f08dec5807e0fd85df90eedd04c999e7408ed37 100644 (file)
@@ -19,9 +19,6 @@
 
 #include <stdlib.h>
 
-#define POP3_SERVICE_NAME "pop3"
-#define AUTH_FAILURE_DELAY_INCREASE_MSECS 5000
-
 const char *capability_string = POP3_CAPABILITY_REPLY;
 
 bool cmd_capa(struct pop3_client *client, const char *args ATTR_UNUSED)
@@ -48,247 +45,45 @@ 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;
        }
@@ -298,15 +93,10 @@ bool cmd_auth(struct pop3_client *client, const char *args)
                unsigned int i, count;
 
                client_send_raw(client, "+OK\r\n");
-               mech = auth_client_get_available_mechs(auth_client, &count);
+               mech = sasl_server_get_advertised_mechs(client, &count);
                for (i = 0; i < count; i++) {
-                       if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
-                           (client->common.secured ||
-                            client->common.set->disable_plaintext_auth ||
-                            (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) {
-                               client_send_raw(client, mech[i].name);
-                               client_send_raw(client, "\r\n");
-                       }
+                       client_send_raw(client, mech[i].name);
+                       client_send_raw(client, "\r\n");
                }
                client_send_raw(client, ".\r\n");
                return TRUE;
@@ -322,58 +112,34 @@ bool cmd_auth(struct pop3_client *client, const char *args)
                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;
        }
@@ -381,42 +147,30 @@ bool cmd_pass(struct pop3_client *client, const char *args)
        /* authorization ID \0 authentication ID \0 pass */
        plain_login = t_str_new(128);
        str_append_c(plain_login, '\0');
-       str_append(plain_login, client->last_user);
+       str_append(plain_login, pop3_client->last_user);
        str_append_c(plain_login, '\0');
        str_append(plain_login, args);
 
-       i_free(client->last_user);
-       client->last_user = NULL;
+       i_free_and_null(pop3_client->last_user);
 
        base64 = buffer_create_dynamic(pool_datastack_create(),
                                MAX_BASE64_ENCODED_SIZE(plain_login->used));
        base64_encode(plain_login->data, plain_login->used, base64);
 
-       client_ref(client);
-       client->auth_initializing = TRUE;
-       sasl_server_auth_begin(&client->common, POP3_SERVICE_NAME, "PLAIN",
-                              str_c(base64), sasl_callback);
-       client->auth_initializing = FALSE;
-       if (!client->common.authenticating)
-               return TRUE;
-
-       /* don't read any input from client until login is finished */
-       if (client->io != NULL)
-               io_remove(&client->io);
+       (void)client_auth_begin(client, "PLAIN", str_c(base64));
        return TRUE;
 }
 
-bool cmd_apop(struct pop3_client *client, const char *args)
+bool cmd_apop(struct pop3_client *pop3_client, const char *args)
 {
+       struct client *client = &pop3_client->common;
        buffer_t *apop_data, *base64;
        const char *p;
 
-       if (client->apop_challenge == NULL) {
-               if (client->common.set->verbose_auth) {
-                       client_syslog(&client->common,
-                                     "APOP failed: APOP not enabled");
-               }
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+       if (pop3_client->apop_challenge == NULL) {
+               if (client->set->verbose_auth)
+                       client_log(client, "APOP failed: APOP not enabled");
+               client_send_line(client, CLIENT_CMD_REPLY_BAD,
                                 "APOP not enabled.");
                return TRUE;
        }
@@ -424,28 +178,26 @@ bool cmd_apop(struct pop3_client *client, const char *args)
        /* <username> <md5 sum in hex> */
        p = strchr(args, ' ');
        if (p == NULL || strlen(p+1) != 32) {
-               if (client->common.set->verbose_auth) {
-                       client_syslog(&client->common,
-                                     "APOP failed: Invalid parameters");
-               }
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+               if (client->set->verbose_auth)
+                       client_log(client, "APOP failed: Invalid parameters");
+               client_send_line(client, CLIENT_CMD_REPLY_BAD,
                                 "Invalid parameters.");
                return TRUE;
        }
 
        /* APOP challenge \0 username \0 APOP response */
        apop_data = buffer_create_dynamic(pool_datastack_create(), 128);
-       buffer_append(apop_data, client->apop_challenge,
-                     strlen(client->apop_challenge)+1);
+       buffer_append(apop_data, pop3_client->apop_challenge,
+                     strlen(pop3_client->apop_challenge)+1);
        buffer_append(apop_data, args, (size_t)(p-args));
        buffer_append_c(apop_data, '\0');
 
        if (hex_to_binary(p+1, apop_data) < 0) {
-               if (client->common.set->verbose_auth) {
-                       client_syslog(&client->common, "APOP failed: "
-                                     "Invalid characters in MD5 response");
+               if (client->set->verbose_auth) {
+                       client_log(client, "APOP failed: "
+                                  "Invalid characters in MD5 response");
                }
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+               client_send_line(client, CLIENT_CMD_REPLY_BAD,
                                 "Invalid characters in MD5 response.");
                return TRUE;
        }
@@ -454,14 +206,6 @@ bool cmd_apop(struct pop3_client *client, const char *args)
                                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;
 }
index 183304ae7946de43484293400e30526e6a327983..977112b65e1a40eed7b4bfb2c85e90eaabfdd716 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef CLIENT_AUTHENTICATE_H
 #define CLIENT_AUTHENTICATE_H
 
+bool pop3_client_auth_handle_reply(struct client *client,
+                                  const struct client_auth_reply *reply);
+
 bool cmd_capa(struct pop3_client *client, const char *args);
 bool cmd_user(struct pop3_client *client, const char *args);
 bool cmd_pass(struct pop3_client *client, const char *args);
index d8683233bf5a0bb96e9d3e57fbfdc7e80df62935..891fdb4d57dbb76059ab875f5918c31c053220a0 100644 (file)
@@ -7,12 +7,9 @@
 #include "istream.h"
 #include "ostream.h"
 #include "randgen.h"
-#include "process-title.h"
 #include "safe-memset.h"
 #include "str.h"
 #include "strescape.h"
-#include "master-service.h"
-#include "master-auth.h"
 #include "client.h"
 #include "client-authenticate.h"
 #include "auth-client.h"
 #include "pop3-proxy.h"
 #include "hostpid.h"
 
-/* max. size of output buffer. if it gets full, the client is disconnected.
-   SASL authentication gives the largest output. */
-#define MAX_OUTBUF_SIZE 4096
-
 /* Disconnect client when it sends too many bad commands */
 #define CLIENT_MAX_BAD_COMMANDS 10
 
-/* When max. number of simultaneous connections is reached, few of the
-   oldest connections are disconnected. Since we have to go through all of the
-   clients, it's faster if we disconnect multiple clients. */
-#define CLIENT_DESTROY_OLDEST_COUNT 16
-
-#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000
-#  error client idle timeout must be larger than authentication timeout
-#endif
-
 const char *login_protocol = "pop3";
 const char *login_process_name = "pop3-login";
-
-static void client_set_title(struct pop3_client *client)
-{
-       const char *addr;
-
-       if (!client->common.set->verbose_proctitle ||
-           !client->common.set->login_process_per_connection)
-               return;
-
-       addr = net_ip2addr(&client->common.ip);
-       if (addr == NULL)
-               addr = "??";
-
-       process_title_set(t_strdup_printf(client->common.tls ?
-                                         "[%s TLS]" : "[%s]", addr));
-}
-
-static void client_open_streams(struct pop3_client *client, int fd)
-{
-       client->common.input =
-               i_stream_create_fd(fd, LOGIN_MAX_INBUF_SIZE, FALSE);
-       client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
-}
-
-static void client_start_tls(struct pop3_client *client)
-{
-       int fd_ssl;
-
-       client_ref(client);
-       if (!client_unref(client) || client->destroyed)
-               return;
-
-       fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
-                              client->common.set, &client->common.proxy);
-       if (fd_ssl == -1) {
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
-                                "TLS initialization failed.");
-               client_destroy(client,
-                              "Disconnected: TLS initialization failed.");
-               return;
-       }
-
-       client->common.proxying = TRUE;
-       client->common.tls = TRUE;
-       client->common.secured = TRUE;
-       client_set_title(client);
-
-       client->common.fd = fd_ssl;
-
-       i_stream_unref(&client->common.input);
-       o_stream_unref(&client->output);
-
-       client_open_streams(client, fd_ssl);
-       client->io = io_add(client->common.fd, IO_READ, client_input, client);
-}
-
-static int client_output_starttls(struct pop3_client *client)
-{
-       int ret;
-
-       if ((ret = o_stream_flush(client->output)) < 0) {
-               client_destroy(client, "Disconnected");
-               return 1;
-       }
-
-       if (ret > 0) {
-               o_stream_unset_flush_callback(client->output);
-               client_start_tls(client);
-       }
-       return 1;
-}
+unsigned int login_default_port = 110;
 
 static bool cmd_stls(struct pop3_client *client)
 {
-       if (client->common.tls) {
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
-                                "TLS is already active.");
-               return TRUE;
-       }
-
-       if (!ssl_initialized) {
-               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
-                                "TLS support isn't enabled.");
-               return TRUE;
-       }
-
-       /* remove input handler, SSL proxy gives us a new fd. we also have to
-          remove it in case we have to wait for buffer to be flushed */
-       if (client->io != NULL)
-               io_remove(&client->io);
-
-       client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
-                        "Begin TLS negotiation now.");
-
-       /* uncork the old fd */
-       o_stream_uncork(client->output);
-
-       if (o_stream_flush(client->output) <= 0) {
-               /* the buffer has to be flushed */
-               o_stream_set_flush_pending(client->output, TRUE);
-               o_stream_set_flush_callback(client->output,
-                                           client_output_starttls, client);
-       } else {
-               client_start_tls(client);
-       }
+       client_cmd_starttls(&client->common);
        return TRUE;
 }
 
 static bool cmd_quit(struct pop3_client *client)
 {
        client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "Logging out");
-       client_destroy(client, "Aborted login");
+       client_destroy(&client->common, "Aborted login");
        return TRUE;
 }
 
@@ -177,34 +61,12 @@ static bool client_command_execute(struct pop3_client *client, const char *cmd,
        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;
@@ -214,18 +76,18 @@ void client_input(struct pop3_client *client)
        o_stream_cork(client->output);
        /* if a command starts an authentication, stop processing further
           commands until the authentication is finished. */
-       while (!client->output->closed && !client->common.authenticating &&
-              (line = i_stream_next_line(client->common.input)) != NULL) {
+       while (!client->output->closed && !client->authenticating &&
+              (line = i_stream_next_line(client->input)) != NULL) {
                args = strchr(line, ' ');
                if (args != NULL)
                        *args++ = '\0';
 
-               if (client_command_execute(client, line,
+               if (client_command_execute(pop3_client, line,
                                           args != NULL ? args : ""))
                        client->bad_counter = 0;
                else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
-                       client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
-                               "Too many invalid IMAP commands.");
+                       client_send_line(client, CLIENT_CMD_REPLY_BYE,
+                               "Too many invalid bad commands.");
                        client_destroy(client,
                                       "Disconnected: Too many bad commands");
                }
@@ -235,43 +97,24 @@ void client_input(struct pop3_client *client)
                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)
@@ -295,206 +138,32 @@ 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) {
@@ -512,6 +181,7 @@ void client_send_line(struct client *client, enum client_cmd_reply reply,
        case CLIENT_CMD_REPLY_BYE:
                break;
        case CLIENT_CMD_REPLY_STATUS:
+       case CLIENT_CMD_REPLY_STATUS_BAD:
                /* can't send status notifications */
                return;
        }
@@ -524,44 +194,31 @@ void client_send_line(struct client *client, enum client_cmd_reply reply,
                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
+};
index 00763e55ae17aee46d27f6cfdbb07bf1860dc035..06dfbb20f681b1349e3703670a88cd0d1a84abce 100644 (file)
@@ -5,9 +5,6 @@
 #include "client-common.h"
 #include "auth-client.h"
 
-/* Disconnect client after idling this many milliseconds */
-#define CLIENT_LOGIN_IDLE_TIMEOUT_MSECS (3*60*1000)
-
 enum pop3_proxy_state {
        POP3_PROXY_BANNER = 0,
        POP3_PROXY_STARTTLS,
@@ -18,42 +15,11 @@ enum pop3_proxy_state {
 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
index 805b7b4ddf65717048b5c912a9906b14ba97ab0b..0e14b28994c81dfbc3e5839c5629f5ba76b5c590 100644 (file)
@@ -11,7 +11,7 @@
 #include "client.h"
 #include "pop3-proxy.h"
 
-static void proxy_free_password(struct pop3_client *client)
+static void proxy_free_password(struct client *client)
 {
        if (client->proxy_password == NULL)
                return;
@@ -20,24 +20,7 @@ static void proxy_free_password(struct pop3_client *client)
        i_free_and_null(client->proxy_password);
 }
 
-static void proxy_failed(struct pop3_client *client, bool send_line)
-{
-       if (send_line) {
-               client_send_line(&client->common,
-                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
-                                AUTH_TEMP_FAILED_MSG);
-       }
-
-       login_proxy_free(&client->proxy);
-       proxy_free_password(client);
-       i_free_and_null(client->proxy_user);
-       i_free_and_null(client->proxy_master_user);
-
-       /* call this last - it may destroy the client */
-       client_auth_failed(client, TRUE);
-}
-
-static void get_plain_auth(struct pop3_client *client, string_t *dest)
+static void get_plain_auth(struct client *client, string_t *dest)
 {
        string_t *str;
 
@@ -55,10 +38,10 @@ static void proxy_send_login(struct pop3_client *client, struct ostream *output)
        string_t *str;
 
        str = t_str_new(128);
-       if (client->proxy_master_user == NULL) {
+       if (client->common.proxy_master_user == NULL) {
                /* send USER command */
                str_append(str, "USER ");
-               str_append(str, client->proxy_user);
+               str_append(str, client->common.proxy_user);
                str_append(str, "\r\n");
        } else {
                /* master user login - use AUTH PLAIN. */
@@ -68,49 +51,50 @@ static void proxy_send_login(struct pop3_client *client, struct ostream *output)
        client->proxy_state = POP3_PROXY_LOGIN1;
 }
 
-static int proxy_input_line(struct pop3_client *client, const char *line)
+int pop3_proxy_parse_line(struct client *client, const char *line)
 {
+       struct pop3_client *pop3_client = (struct pop3_client *)client;
        struct ostream *output;
        enum login_proxy_ssl_flags ssl_flags;
        string_t *str;
 
        i_assert(!client->destroyed);
 
-       output = login_proxy_get_ostream(client->proxy);
-       switch (client->proxy_state) {
+       output = login_proxy_get_ostream(client->login_proxy);
+       switch (pop3_client->proxy_state) {
        case POP3_PROXY_BANNER:
                /* this is a banner */
                if (strncmp(line, "+OK", 3) != 0) {
-                       client_syslog_err(&client->common, t_strdup_printf(
+                       client_log_err(client, t_strdup_printf(
                                "proxy: Remote returned invalid banner: %s",
                                str_sanitize(line, 160)));
-                       proxy_failed(client, TRUE);
+                       client_proxy_failed(client, TRUE);
                        return -1;
                }
 
-               ssl_flags = login_proxy_get_ssl_flags(client->proxy);
+               ssl_flags = login_proxy_get_ssl_flags(client->login_proxy);
                if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) {
-                       proxy_send_login(client, output);
+                       proxy_send_login(pop3_client, output);
                } else {
                        (void)o_stream_send_str(output, "STLS\r\n");
-                       client->proxy_state = POP3_PROXY_STARTTLS;
+                       pop3_client->proxy_state = POP3_PROXY_STARTTLS;
                }
                return 0;
        case POP3_PROXY_STARTTLS:
                if (strncmp(line, "+OK", 3) != 0) {
-                       client_syslog_err(&client->common, t_strdup_printf(
+                       client_log_err(client, t_strdup_printf(
                                "proxy: Remote STLS failed: %s",
                                str_sanitize(line, 160)));
-                       proxy_failed(client, TRUE);
+                       client_proxy_failed(client, TRUE);
                        return -1;
                }
-               if (login_proxy_starttls(client->proxy) < 0) {
-                       proxy_failed(client, TRUE);
+               if (login_proxy_starttls(client->login_proxy) < 0) {
+                       client_proxy_failed(client, TRUE);
                        return -1;
                }
                /* i/ostreams changed. */
-               output = login_proxy_get_ostream(client->proxy);
-               proxy_send_login(client, output);
+               output = login_proxy_get_ostream(client->login_proxy);
+               proxy_send_login(pop3_client, output);
                return 1;
        case POP3_PROXY_LOGIN1:
                str = t_str_new(128);
@@ -131,7 +115,7 @@ static int proxy_input_line(struct pop3_client *client, const char *line)
                }
                (void)o_stream_send(output, str_data(str), str_len(str));
                proxy_free_password(client);
-               client->proxy_state = POP3_PROXY_LOGIN2;
+               pop3_client->proxy_state = POP3_PROXY_LOGIN2;
                return 0;
        case POP3_PROXY_LOGIN2:
                if (strncmp(line, "+OK", 3) != 0)
@@ -141,31 +125,7 @@ static int proxy_input_line(struct pop3_client *client, const char *line)
                line = t_strconcat(line, "\r\n", NULL);
                (void)o_stream_send_str(client->output, line);
 
-               str = t_str_new(128);
-               str_printfa(str, "proxy(%s): started proxying to %s:%u",
-                           client->common.virtual_user,
-                           login_proxy_get_host(client->proxy),
-                           login_proxy_get_port(client->proxy));
-               if (strcmp(client->common.virtual_user,
-                          client->proxy_user) != 0) {
-                       /* remote username is different, log it */
-                       str_append_c(str, '/');
-                       str_append(str, client->proxy_user);
-               }
-               if (client->proxy_master_user != NULL) {
-                       str_printfa(str, " (master %s)",
-                                   client->proxy_master_user);
-               }
-
-               login_proxy_detach(client->proxy, client->common.input,
-                                  client->output);
-
-               client->proxy = NULL;
-               client->common.input = NULL;
-               client->output = NULL;
-               client->common.fd = -1;
-               client->common.proxying = TRUE;
-               client_destroy_success(client, str_c(str));
+               client_proxy_finish_destroy_client(client);
                return 1;
        }
 
@@ -184,129 +144,25 @@ static int proxy_input_line(struct pop3_client *client, const char *line)
           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;
 }
index 035f40ab6ad18610bb3ad78fe2f1e39a8a78b458..e99b9e40ce89f0c1b88e0065908280796312f73f 100644 (file)
@@ -1,10 +1,7 @@
 #ifndef POP3_PROXY_H
 #define POP3_PROXY_H
 
-#include "login-proxy.h"
-
-int pop3_proxy_new(struct pop3_client *client, const char *host,
-                  unsigned int port, const char *user, const char *master_user,
-                  const char *password, enum login_proxy_ssl_flags ssl_flags);
+void pop3_proxy_reset(struct client *client);
+int pop3_proxy_parse_line(struct client *client, const char *line);
 
 #endif