client.c revision 00fa8dcbc66f56daa737487c9dec7166c37de79e
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "common.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "base64.h"
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen#include "buffer.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "ioloop.h"
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen#include "istream.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "ostream.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "randgen.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "process-title.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "safe-memset.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "str.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "strescape.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "master-service.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "master-auth.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "client.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "client-authenticate.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "auth-client.h"
c8b5a21a139992e66b4ad02adb69eaf929b3d024Timo Sirainen#include "ssl-proxy.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "pop3-proxy.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "hostpid.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen/* max. size of output buffer. if it gets full, the client is disconnected.
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen SASL authentication gives the largest output. */
c8b5a21a139992e66b4ad02adb69eaf929b3d024Timo Sirainen#define MAX_OUTBUF_SIZE 4096
c8b5a21a139992e66b4ad02adb69eaf929b3d024Timo Sirainen
c8b5a21a139992e66b4ad02adb69eaf929b3d024Timo Sirainen/* Disconnect client when it sends too many bad commands */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#define CLIENT_MAX_BAD_COMMANDS 10
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen/* When max. number of simultaneous connections is reached, few of the
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen oldest connections are disconnected. Since we have to go through all of the
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen clients, it's faster if we disconnect multiple clients. */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#define CLIENT_DESTROY_OLDEST_COUNT 16
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen# error client idle timeout must be larger than authentication timeout
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#endif
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenconst char *login_protocol = "pop3";
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenconst char *login_process_name = "pop3-login";
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic void client_set_title(struct pop3_client *client)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen const char *addr;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (!client->common.set->verbose_proctitle ||
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen !client->common.set->login_process_per_connection)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen addr = net_ip2addr(&client->common.ip);
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen if (addr == NULL)
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen addr = "??";
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen process_title_set(t_strdup_printf(client->common.tls ?
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen "[%s TLS]" : "[%s]", addr));
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic void client_open_streams(struct pop3_client *client, int fd)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client->common.input =
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen i_stream_create_fd(fd, LOGIN_MAX_INBUF_SIZE, FALSE);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic void client_start_tls(struct pop3_client *client)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen int fd_ssl;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_ref(client);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (!client_unref(client) || client->destroyed)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client->common.set, &client->common.proxy);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (fd_ssl == -1) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen "TLS initialization failed.");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_destroy(client,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen "Disconnected: TLS initialization failed.");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client->common.proxying = TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client->common.tls = TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client->common.secured = TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_set_title(client);
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen client->common.fd = fd_ssl;
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen i_stream_unref(&client->common.input);
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen o_stream_unref(&client->output);
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen client_open_streams(client, fd_ssl);
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen client->io = io_add(client->common.fd, IO_READ, client_input, client);
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen}
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainenstatic int client_output_starttls(struct pop3_client *client)
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen{
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen int ret;
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_destroy(client, "Disconnected");
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen return 1;
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen }
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (ret > 0) {
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen o_stream_unset_flush_callback(client->output);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_start_tls(client);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return 1;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic bool cmd_stls(struct pop3_client *client)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (client->common.tls) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen "TLS is already active.");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (!ssl_initialized) {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen "TLS support isn't enabled.");
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen return TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen remove it in case we have to wait for buffer to be flushed */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (client->io != NULL)
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen io_remove(&client->io);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen "Begin TLS negotiation now.");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* uncork the old fd */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen o_stream_uncork(client->output);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (o_stream_flush(client->output) <= 0) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* the buffer has to be flushed */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen o_stream_set_flush_callback(client->output,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_output_starttls, client);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen } else {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_start_tls(client);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic bool cmd_quit(struct pop3_client *client)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "Logging out");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_destroy(client, "Aborted login");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic bool client_command_execute(struct pop3_client *client, const char *cmd,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen const char *args)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen cmd = t_str_ucase(cmd);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (strcmp(cmd, "CAPA") == 0)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return cmd_capa(client, args);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (strcmp(cmd, "USER") == 0)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return cmd_user(client, args);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (strcmp(cmd, "PASS") == 0)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return cmd_pass(client, args);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (strcmp(cmd, "AUTH") == 0)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return cmd_auth(client, args);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (strcmp(cmd, "APOP") == 0)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return cmd_apop(client, args);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (strcmp(cmd, "STLS") == 0)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return cmd_stls(client);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (strcmp(cmd, "QUIT") == 0)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return cmd_quit(client);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen "Unknown command.");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return FALSE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenbool client_read(struct pop3_client *client)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen switch (i_stream_read(client->common.input)) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case -2:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* buffer full */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen "Input buffer full, aborting");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return FALSE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case -1:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* disconnected */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen client_destroy(client, "Disconnected");
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return FALSE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case 0:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* nothing new read */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen default:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* something was read */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen timeout_reset(client->to_idle_disconnect);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenvoid client_input(struct pop3_client *client)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen char *line, *args;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen i_assert(!client->common.authenticating);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (!client_read(client))
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return;
client_ref(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) {
args = strchr(line, ' ');
if (args != NULL)
*args++ = '\0';
if (client_command_execute(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_destroy(client,
"Disconnected: Too many bad commands");
}
}
if (client_unref(client))
o_stream_uncork(client->output);
}
void client_destroy_oldest(void)
{
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;
}
}
}
/* 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 char *get_apop_challenge(struct pop3_client *client)
{
struct auth_connect_id *id = &client->auth_id;
unsigned char buffer[16];
buffer_t *buf;
if (!auth_client_reserve_connection(auth_client, "APOP", id))
return NULL;
random_fill(buffer, sizeof(buffer));
buf = buffer_create_static_hard(pool_datastack_create(),
MAX_BASE64_ENCODED_SIZE(sizeof(buffer)) + 1);
base64_encode(buffer, sizeof(buffer), buf);
buffer_append_c(buf, '\0');
return i_strdup_printf("<%x.%x.%lx.%s@%s>",
id->server_pid, id->connect_uid,
(unsigned long)ioloop_time,
(const char *)buf->data, my_hostname);
}
static void client_auth_ready(struct pop3_client *client)
{
client->io = io_add(client->common.fd, IO_READ, client_input, client);
client->apop_challenge = get_apop_challenge(client);
if (client->apop_challenge == NULL) {
client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
client->common.set->login_greeting);
} else {
client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
t_strconcat(client->common.set->login_greeting, " ",
client->apop_challenge, NULL));
}
}
static void client_idle_disconnect_timeout(struct pop3_client *client)
{
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);
}
void client_destroy(struct pop3_client *client, const char *reason)
{
if (client->destroyed)
return;
client->destroyed = TRUE;
if (!client->login_success && reason != NULL) {
reason = t_strconcat(reason, " ",
client_get_extra_disconnect_reason(&client->common),
NULL);
}
if (reason != NULL)
client_syslog(&client->common, reason);
client_unlink(&client->common);
if (client->common.input != NULL)
i_stream_close(client->common.input);
if (client->output != NULL)
o_stream_close(client->output);
if (client->common.master_tag != 0) {
i_assert(client->common.auth_request == NULL);
i_assert(client->common.authenticating);
master_auth_request_abort(master_service,
client->common.master_tag);
} else if (client->common.auth_request != NULL) {
i_assert(client->common.authenticating);
sasl_server_auth_abort(&client->common);
} else {
i_assert(!client->common.authenticating);
}
if (client->io != NULL)
io_remove(&client->io);
if (client->to_idle_disconnect != NULL)
timeout_remove(&client->to_idle_disconnect);
if (client->to_authfail_delay != NULL)
timeout_remove(&client->to_authfail_delay);
if (client->common.fd != -1) {
net_disconnect(client->common.fd);
client->common.fd = -1;
}
if (client->proxy_password != NULL) {
safe_memset(client->proxy_password, 0,
strlen(client->proxy_password));
i_free(client->proxy_password);
client->proxy_password = NULL;
}
i_free(client->proxy_user);
client->proxy_user = NULL;
if (client->proxy != NULL)
login_proxy_free(&client->proxy);
if (client->common.proxy != NULL) {
ssl_proxy_free(client->common.proxy);
client->common.proxy = NULL;
}
client_unref(client);
}
void client_destroy_internal_failure(struct pop3_client *client)
{
client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
"Internal login failure. "
"Refer to server log for more information.");
client_destroy(client, "Internal login failure");
}
void client_ref(struct pop3_client *client)
{
client->refcount++;
}
bool client_unref(struct pop3_client *client)
{
i_assert(client->refcount > 0);
if (--client->refcount > 0)
return TRUE;
i_assert(client->destroyed);
if (client->common.input != NULL)
i_stream_unref(&client->common.input);
if (client->output != NULL)
o_stream_unref(&client->output);
if (!client->common.proxying) {
i_assert(client->common.proxy == NULL);
master_service_client_connection_destroyed(master_service);
}
i_free(client->last_user);
i_free(client->apop_challenge);
i_free(client->common.virtual_user);
i_free(client->common.auth_mech_name);
pool_unref(&client->common.pool);
return FALSE;
}
static void
client_send_raw_data(struct pop3_client *client, const void *data, size_t size)
{
ssize_t ret;
ret = o_stream_send(client->output, data, size);
if (ret < 0 || (size_t)ret != size) {
/* either disconnection or buffer full. in either case we want
this connection destroyed. however destroying it here might
break things if client is still tried to be accessed without
being referenced.. */
i_stream_close(client->common.input);
}
}
void client_send_raw(struct pop3_client *client, const char *data)
{
client_send_raw_data(client, data, strlen(data));
}
void client_send_line(struct client *client, enum client_cmd_reply reply,
const char *text)
{
struct pop3_client *pop3_client = (struct pop3_client *)client;
const char *prefix = "-ERR";
switch (reply) {
case CLIENT_CMD_REPLY_OK:
prefix = "+OK";
break;
case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP:
prefix = "-ERR [IN-USE]";
break;
case CLIENT_CMD_REPLY_AUTH_FAILED:
case CLIENT_CMD_REPLY_AUTHZ_FAILED:
case CLIENT_CMD_REPLY_AUTH_FAIL_REASON:
case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL:
case CLIENT_CMD_REPLY_BAD:
case CLIENT_CMD_REPLY_BYE:
break;
case CLIENT_CMD_REPLY_STATUS:
/* can't send status notifications */
return;
}
T_BEGIN {
string_t *line = t_str_new(256);
str_append(line, prefix);
str_append_c(line, ' ');
str_append(line, text);
str_append(line, "\r\n");
client_send_raw_data(pop3_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 */
}
void clients_deinit(void)
{
clients_destroy_all();
}