Mercurial > dovecot > original-hg > dovecot-1.2
changeset 7927:2351a81ce699 HEAD
If commands are pipelined after the login command, pass them to the
IMAP/POP3 process so it can process the command instead of discarding it.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 21 Jun 2008 12:23:08 +0300 |
parents | 187183c360dd |
children | 9e226056a208 |
files | src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/client.h src/imap-login/imap-proxy.c src/imap/client.c src/imap/client.h src/imap/main.c src/login-common/client-common.h src/login-common/master.c src/master/login-process.c src/master/mail-process.c src/master/mail-process.h src/master/master-login-interface.h src/master/master-settings.c src/pop3-login/client-authenticate.c src/pop3-login/client.c src/pop3-login/client.h src/pop3-login/pop3-proxy.c src/pop3/client.c src/pop3/client.h src/pop3/main.c |
diffstat | 21 files changed, 204 insertions(+), 93 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap-login/client-authenticate.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/imap-login/client-authenticate.c Sat Jun 21 12:23:08 2008 +0300 @@ -52,14 +52,14 @@ return; if (client->skip_line) { - if (i_stream_next_line(client->input) == NULL) + if (i_stream_next_line(client->common.input) == NULL) return; client->skip_line = FALSE; } /* @UNSAFE */ - line = i_stream_next_line(client->input); + line = i_stream_next_line(client->common.input); if (line == NULL) return;
--- a/src/imap-login/client.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/imap-login/client.c Sat Jun 21 12:23:08 2008 +0300 @@ -19,10 +19,6 @@ #include <stdlib.h> -/* max. size of one parameter in line, or max reply length in SASL - authentication */ -#define MAX_INBUF_SIZE 4096 - /* max. size of output buffer. if it gets full, the client is disconnected. SASL authentication gives the largest output. */ #define MAX_OUTBUF_SIZE 4096 @@ -72,10 +68,11 @@ static void client_open_streams(struct imap_client *client, int fd) { - client->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); + 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->input, client->output, - MAX_IMAP_LINE); + client->parser = imap_parser_create(client->common.input, + client->output, MAX_IMAP_LINE); } /* Skip incoming data until newline is found, @@ -85,11 +82,11 @@ const unsigned char *data; size_t i, data_size; - data = i_stream_get_data(client->input, &data_size); + data = i_stream_get_data(client->common.input, &data_size); for (i = 0; i < data_size; i++) { if (data[i] == '\n') { - i_stream_skip(client->input, i+1); + i_stream_skip(client->common.input, i+1); return TRUE; } } @@ -141,7 +138,7 @@ client_set_title(client); client->common.fd = fd_ssl; - i_stream_unref(&client->input); + i_stream_unref(&client->common.input); o_stream_unref(&client->output); imap_parser_destroy(&client->parser); @@ -360,7 +357,9 @@ /* not enough data */ return FALSE; } - client->skip_line = TRUE; + /* we read the entire line - skip over the CRLF */ + if (!client_skip_line(client)) + i_unreached(); if (*client->cmd_tag == '\0') ret = -1; @@ -387,7 +386,7 @@ bool client_read(struct imap_client *client) { - switch (i_stream_read(client->input)) { + switch (i_stream_read(client->common.input)) { case -2: /* buffer full */ client_send_line(client, "* BYE Input buffer full, aborting"); @@ -557,8 +556,8 @@ client_unlink(&client->common); - if (client->input != NULL) - i_stream_close(client->input); + if (client->common.input != NULL) + i_stream_close(client->common.input); if (client->output != NULL) o_stream_close(client->output); @@ -637,8 +636,8 @@ imap_parser_destroy(&client->parser); - if (client->input != NULL) - i_stream_unref(&client->input); + if (client->common.input != NULL) + i_stream_unref(&client->common.input); if (client->output != NULL) o_stream_unref(&client->output); @@ -665,7 +664,7 @@ 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); + i_stream_close(client->common.input); } }
--- a/src/imap-login/client.h Sat Jun 21 12:22:01 2008 +0300 +++ b/src/imap-login/client.h Sat Jun 21 12:23:08 2008 +0300 @@ -12,7 +12,6 @@ int refcount; struct io *io; - struct istream *input; struct ostream *output; struct imap_parser *parser; struct timeout *to_idle_disconnect, *to_auth_waiting;
--- a/src/imap-login/imap-proxy.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/imap-login/imap-proxy.c Sat Jun 21 12:23:08 2008 +0300 @@ -97,11 +97,11 @@ login_proxy_get_port(client->proxy)); (void)client_skip_line(client); - login_proxy_detach(client->proxy, client->input, + login_proxy_detach(client->proxy, client->common.input, client->output); client->proxy = NULL; - client->input = NULL; + client->common.input = NULL; client->output = NULL; client->common.fd = -1; client_destroy_success(client, msg);
--- a/src/imap/client.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/imap/client.c Sat Jun 21 12:23:08 2008 +0300 @@ -18,8 +18,6 @@ static struct client *my_client; /* we don't need more than one currently */ -static bool client_handle_input(struct client *client); - static void client_idle_timeout(struct client *client) { if (client->output_lock == NULL) @@ -684,7 +682,7 @@ return client_command_input(client->input_lock); } -static bool client_handle_input(struct client *client) +bool client_handle_input(struct client *client) { bool ret, remove_io, handled_commands = FALSE;
--- a/src/imap/client.h Sat Jun 21 12:22:01 2008 +0300 +++ b/src/imap/client.h Sat Jun 21 12:23:08 2008 +0300 @@ -171,6 +171,7 @@ void client_continue_pending_input(struct client **_client); void client_input(struct client *client); +bool client_handle_input(struct client *client); int client_output(struct client *client); #endif
--- a/src/imap/main.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/imap/main.c Sat Jun 21 12:23:08 2008 +0300 @@ -5,6 +5,8 @@ #include "network.h" #include "ostream.h" #include "str.h" +#include "base64.h" +#include "istream.h" #include "lib-signals.h" #include "restrict-access.h" #include "fd-close-on-exec.h" @@ -163,6 +165,7 @@ static void main_init(void) { struct client *client; + struct ostream *output; struct mail_namespace *ns; const char *user, *str; @@ -238,7 +241,9 @@ i_fatal("Namespace initialization failed"); client = client_create(0, 1, ns); - o_stream_cork(client->output); + output = client->output; + o_stream_ref(output); + o_stream_cork(output); if (IS_STANDALONE()) { client_send_line(client, t_strconcat( "* PREAUTH [CAPABILITY ", @@ -249,7 +254,18 @@ client_send_line(client, t_strconcat(getenv("IMAPLOGINTAG"), " OK Logged in.", NULL)); } - o_stream_uncork(client->output); + str = getenv("CLIENT_INPUT"); + if (str != NULL) T_BEGIN { + buffer_t *buf = t_base64_decode_str(str); + if (buf->used > 0) { + if (!i_stream_add_data(client->input, buf->data, + buf->used)) + i_panic("Couldn't add client input to stream"); + (void)client_handle_input(client); + } + } T_END; + o_stream_uncork(output); + o_stream_unref(&output); } static void main_deinit(void) @@ -292,8 +308,12 @@ process_title_init(argv, envp); ioloop = io_loop_create(); + /* fake that we're running, so we know if client was destroyed + while initializing */ + io_loop_set_running(ioloop); main_init(); - io_loop_run(ioloop); + if (io_loop_is_running(ioloop)) + io_loop_run(ioloop); main_deinit(); io_loop_destroy(&ioloop);
--- a/src/login-common/client-common.h Sat Jun 21 12:22:01 2008 +0300 +++ b/src/login-common/client-common.h Sat Jun 21 12:23:08 2008 +0300 @@ -5,6 +5,14 @@ #include "master.h" #include "sasl-server.h" +/* max. size of input buffer. this means: + + SASL: Max SASL request length from client + IMAP: Max. length of a single parameter + POP3: Max. length of a command line (spec says 512 would be enough) +*/ +#define LOGIN_MAX_INBUF_SIZE 4096 + struct client { struct client *prev, *next; @@ -14,6 +22,7 @@ struct ssl_proxy *proxy; int fd; + struct istream *input; char *auth_mech_name; struct auth_request *auth_request;
--- a/src/login-common/master.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/login-common/master.c Sat Jun 21 12:23:08 2008 +0300 @@ -2,6 +2,7 @@ #include "common.h" #include "hash.h" +#include "buffer.h" #include "ioloop.h" #include "network.h" #include "fdpass.h" @@ -59,32 +60,50 @@ void master_request_login(struct client *client, master_callback_t *callback, unsigned int auth_pid, unsigned int auth_id) { - struct master_login_request req; + buffer_t *buf; + struct master_login_request *req; struct stat st; + const unsigned char *data; + size_t size; + ssize_t ret; i_assert(auth_pid != 0); - memset(&req, 0, sizeof(req)); - req.version = MASTER_LOGIN_PROTOCOL_VERSION; - req.tag = ++master_tag_counter; - if (req.tag == 0) - req.tag = ++master_tag_counter; - req.auth_pid = auth_pid; - req.auth_id = auth_id; - req.local_ip = client->local_ip; - req.remote_ip = client->ip; + data = i_stream_get_data(client->input, &size); + buf = buffer_create_dynamic(pool_datastack_create(), + sizeof(*req) + size); + buffer_write(buf, sizeof(*req), data, size); + req = buffer_get_space_unsafe(buf, 0, sizeof(*req)); + req->version = MASTER_LOGIN_PROTOCOL_VERSION; + req->tag = ++master_tag_counter; + if (req->tag == 0) + req->tag = ++master_tag_counter; + req->auth_pid = auth_pid; + req->auth_id = auth_id; + req->local_ip = client->local_ip; + req->remote_ip = client->ip; + req->data_size = size; +#if LOGIN_MAX_INBUF_SIZE != MASTER_LOGIN_MAX_DATA_SIZE +# error buffer max sizes unsynced +#endif + i_assert(req->data_size <= LOGIN_MAX_INBUF_SIZE); if (fstat(client->fd, &st) < 0) i_fatal("fstat(client) failed: %m"); - req.ino = st.st_ino; + req->ino = st.st_ino; - if (fd_send(master_fd, client->fd, &req, sizeof(req)) != sizeof(req)) + ret = fd_send(master_fd, client->fd, buf->data, buf->used); + if (ret < 0) i_fatal("fd_send(%d) failed: %m", client->fd); + if ((size_t)ret != buf->used) { + i_fatal("fd_send() sent only %d of %d bytes", + (int)ret, (int)buf->used); + } - client->master_tag = req.tag; + client->master_tag = req->tag; client->master_callback = callback; - hash_insert(master_requests, POINTER_CAST(req.tag), client); + hash_insert(master_requests, POINTER_CAST(req->tag), client); } void master_request_abort(struct client *client)
--- a/src/master/login-process.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/master/login-process.c Sat Jun 21 12:23:08 2008 +0300 @@ -48,7 +48,9 @@ unsigned int login_tag; int fd; + unsigned int data_size; struct ip_addr local_ip, remote_ip; + unsigned char data[]; }; static unsigned int auth_id_counter, login_pid_counter; @@ -105,6 +107,7 @@ group->set, request->fd, &request->local_ip, &request->remote_ip, user, args, + request->data_size, request->data, FALSE); } T_END; @@ -304,6 +307,7 @@ static int login_read_request(struct login_process *p, struct master_login_request *req, + unsigned char data[MASTER_LOGIN_MAX_DATA_SIZE], int *client_fd_r) { struct stat st; @@ -343,6 +347,26 @@ } return 1; } + if (req->data_size != 0) { + if (req->data_size > MASTER_LOGIN_MAX_DATA_SIZE) { + i_error("login: Too large data_size sent"); + return -1; + } + /* @UNSAFE */ + ret = read(p->fd, data, req->data_size); + if (ret != req->data_size) { + if (ret == 0) { + /* disconnected */ + } else if (ret > 0) { + /* request wasn't fully read */ + i_error("login: Data read partially %d/%u", + (int)ret, req->data_size); + } else { + i_error("login: read(data) failed: %m"); + } + return -1; + } + } if (*client_fd_r == -1) { i_error("login: Login request missing a file descriptor"); @@ -353,7 +377,6 @@ i_error("login: fstat(mail client) failed: %m"); return -1; } - if (st.st_ino != req->ino) { i_error("login: Login request inode mismatch: %s != %s", dec2str(st.st_ino), dec2str(req->ino)); @@ -367,6 +390,7 @@ struct auth_process *auth_process; struct login_auth_request *authreq; struct master_login_request req; + unsigned char data[MASTER_LOGIN_MAX_DATA_SIZE]; int client_fd; ssize_t ret; @@ -377,7 +401,7 @@ return; } - ret = login_read_request(p, &req, &client_fd); + ret = login_read_request(p, &req, data, &client_fd); if (ret == 0) return; if (ret < 0) { @@ -412,7 +436,7 @@ fd_close_on_exec(client_fd, TRUE); /* ask the cookie from the auth process */ - authreq = i_new(struct login_auth_request, 1); + authreq = i_malloc(sizeof(*authreq) + req.data_size); p->refcount++; authreq->process = p; authreq->tag = ++auth_id_counter; @@ -420,6 +444,8 @@ authreq->fd = client_fd; authreq->local_ip = req.local_ip; authreq->remote_ip = req.remote_ip; + authreq->data_size = req.data_size; + memcpy(authreq->data, data, req.data_size); auth_process = auth_process_find(req.auth_pid); if (auth_process == NULL) {
--- a/src/master/mail-process.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/master/mail-process.c Sat Jun 21 12:23:08 2008 +0300 @@ -5,6 +5,7 @@ #include "hash.h" #include "fd-close-on-exec.h" #include "env-util.h" +#include "base64.h" #include "str.h" #include "network.h" #include "mountpoint.h" @@ -524,6 +525,7 @@ int socket_fd, const struct ip_addr *local_ip, const struct ip_addr *remote_ip, const char *user, const char *const *args, + unsigned int input_size, const unsigned char *input, bool dump_capability) { const struct var_expand_table *var_expand_table; @@ -832,6 +834,13 @@ addr = net_ip2addr(remote_ip); env_put(t_strconcat("IP=", addr, NULL)); + if (input_size > 0) { + str_truncate(str, 0); + str_append(str, "CLIENT_INPUT="); + base64_encode(input, input_size, str); + env_put(str_c(str)); + } + if (!set->verbose_proctitle) title[0] = '\0'; else {
--- a/src/master/mail-process.h Sat Jun 21 12:22:01 2008 +0300 +++ b/src/master/mail-process.h Sat Jun 21 12:23:08 2008 +0300 @@ -13,6 +13,7 @@ int socket_fd, const struct ip_addr *local_ip, const struct ip_addr *remote_ip, const char *user, const char *const *args, + unsigned int input_size, const unsigned char *input, bool dump_capability); void mail_processes_init(void);
--- a/src/master/master-login-interface.h Sat Jun 21 12:22:01 2008 +0300 +++ b/src/master/master-login-interface.h Sat Jun 21 12:23:08 2008 +0300 @@ -9,6 +9,9 @@ (or something else) is changed. */ #define MASTER_LOGIN_PROTOCOL_VERSION 3 +/* This should be kept in sync with LOGIN_MAX_INBUF_SIZE */ +#define MASTER_LOGIN_MAX_DATA_SIZE 4096 + enum master_login_state { /* process is accepting new connections */ LOGIN_STATE_LISTENING = 0, @@ -28,6 +31,8 @@ uint32_t auth_pid; uint32_t auth_id; + /* request follows this many bytes of client input */ + uint32_t data_size; ino_t ino;
--- a/src/master/master-settings.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/master/master-settings.c Sat Jun 21 12:23:08 2008 +0300 @@ -647,7 +647,7 @@ fd_close_on_exec(fd[1], TRUE); login_status = create_mail_process(PROCESS_TYPE_IMAP, set, fd[1], &ip, &ip, "dump-capability", - args, TRUE); + args, 0, NULL, TRUE); if (login_status != MASTER_LOGIN_STATUS_OK) { (void)close(fd[0]); (void)close(fd[1]);
--- a/src/pop3-login/client-authenticate.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/pop3-login/client-authenticate.c Sat Jun 21 12:23:08 2008 +0300 @@ -66,7 +66,7 @@ return; /* @UNSAFE */ - line = i_stream_next_line(client->input); + line = i_stream_next_line(client->common.input); if (line == NULL) return;
--- a/src/pop3-login/client.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/pop3-login/client.c Sat Jun 21 12:23:08 2008 +0300 @@ -17,10 +17,6 @@ #include "pop3-proxy.h" #include "hostpid.h" -/* max. length of input command line (spec says 512), or max reply length in - SASL authentication */ -#define MAX_INBUF_SIZE 4096 - /* max. size of output buffer. if it gets full, the client is disconnected. SASL authentication gives the largest output. */ #define MAX_OUTBUF_SIZE 4096 @@ -59,7 +55,8 @@ static void client_open_streams(struct pop3_client *client, int fd) { - client->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); + client->common.input = + i_stream_create_fd(fd, LOGIN_MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE); } @@ -87,7 +84,7 @@ client->common.fd = fd_ssl; - i_stream_unref(&client->input); + i_stream_unref(&client->common.input); o_stream_unref(&client->output); client_open_streams(client, fd_ssl); @@ -180,7 +177,7 @@ bool client_read(struct pop3_client *client) { - switch (i_stream_read(client->input)) { + switch (i_stream_read(client->common.input)) { case -2: /* buffer full */ client_send_line(client, "-ERR Input line too long, aborting"); @@ -208,8 +205,8 @@ client_ref(client); o_stream_cork(client->output); - while (!client->output->closed && - (line = i_stream_next_line(client->input)) != NULL) { + while (!client->output->closed && !client->common.authenticating && + (line = i_stream_next_line(client->common.input)) != NULL) { args = strchr(line, ' '); if (args != NULL) *args++ = '\0'; @@ -363,8 +360,8 @@ client_unlink(&client->common); - if (client->input != NULL) - i_stream_close(client->input); + if (client->common.input != NULL) + i_stream_close(client->common.input); if (client->output != NULL) o_stream_close(client->output); @@ -433,8 +430,8 @@ i_assert(client->destroyed); - if (client->input != NULL) - i_stream_unref(&client->input); + if (client->common.input != NULL) + i_stream_unref(&client->common.input); if (client->output != NULL) o_stream_unref(&client->output); @@ -463,7 +460,7 @@ 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); + i_stream_close(client->common.input); } }
--- a/src/pop3-login/client.h Sat Jun 21 12:22:01 2008 +0300 +++ b/src/pop3-login/client.h Sat Jun 21 12:23:08 2008 +0300 @@ -13,7 +13,6 @@ int refcount; struct io *io; - struct istream *input; struct ostream *output; struct timeout *to_idle_disconnect; @@ -29,7 +28,6 @@ struct auth_connect_id auth_id; unsigned int login_success:1; - unsigned int authenticating:1; unsigned int auth_connected:1; unsigned int destroyed:1; };
--- a/src/pop3-login/pop3-proxy.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/pop3-login/pop3-proxy.c Sat Jun 21 12:23:08 2008 +0300 @@ -107,11 +107,11 @@ login_proxy_get_host(client->proxy), login_proxy_get_port(client->proxy)); - login_proxy_detach(client->proxy, client->input, + login_proxy_detach(client->proxy, client->common.input, client->output); client->proxy = NULL; - client->input = NULL; + client->common.input = NULL; client->output = NULL; client->common.fd = -1; client_destroy_success(client, msg);
--- a/src/pop3/client.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/pop3/client.c Sat Jun 21 12:23:08 2008 +0300 @@ -375,35 +375,11 @@ &error)); } -static void client_input(struct client *client) +bool client_handle_input(struct client *client) { char *line, *args; int ret; - if (client->cmd != NULL) { - /* we're still processing a command. wait until it's - finished. */ - io_remove(&client->io); - client->waiting_input = TRUE; - return; - } - - client->waiting_input = FALSE; - client->last_input = ioloop_time; - timeout_reset(client->to_idle); - - switch (i_stream_read(client->input)) { - case -1: - /* disconnected */ - client_destroy(client, NULL); - return; - case -2: - /* line too long, kill it */ - client_send_line(client, "-ERR Input line too long."); - client_destroy(client, "Input line too long"); - return; - } - o_stream_cork(client->output); while (!client->output->closed && (line = i_stream_next_line(client->input)) != NULL) { @@ -430,8 +406,40 @@ } o_stream_uncork(client->output); - if (client->output->closed) + if (client->output->closed) { client_destroy(client, NULL); + return FALSE; + } + return TRUE; +} + +static void client_input(struct client *client) +{ + if (client->cmd != NULL) { + /* we're still processing a command. wait until it's + finished. */ + io_remove(&client->io); + client->waiting_input = TRUE; + return; + } + + client->waiting_input = FALSE; + client->last_input = ioloop_time; + timeout_reset(client->to_idle); + + switch (i_stream_read(client->input)) { + case -1: + /* disconnected */ + client_destroy(client, NULL); + return; + case -2: + /* line too long, kill it */ + client_send_line(client, "-ERR Input line too long."); + client_destroy(client, "Input line too long"); + return; + } + + (void)client_handle_input(client); } static int client_output(struct client *client)
--- a/src/pop3/client.h Sat Jun 21 12:22:01 2008 +0300 +++ b/src/pop3/client.h Sat Jun 21 12:23:08 2008 +0300 @@ -61,6 +61,8 @@ ATTR_FORMAT(2, 3); void client_send_storage_error(struct client *client); +bool client_handle_input(struct client *client); + void clients_init(void); void clients_deinit(void);
--- a/src/pop3/main.c Sat Jun 21 12:22:01 2008 +0300 +++ b/src/pop3/main.c Sat Jun 21 12:23:08 2008 +0300 @@ -7,6 +7,9 @@ #include "lib-signals.h" #include "restrict-access.h" #include "fd-close-on-exec.h" +#include "base64.h" +#include "buffer.h" +#include "istream.h" #include "process-title.h" #include "randgen.h" #include "module-dir.h" @@ -180,9 +183,12 @@ restrict_access_by_env(!IS_STANDALONE()); } -static int main_init(void) +static bool main_init(void) { struct mail_namespace *ns; + struct client *client; + const char *str; + bool ret = TRUE; lib_signals_init(); lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL); @@ -232,7 +238,21 @@ namespace_pool = pool_alloconly_create("namespaces", 1024); if (mail_namespaces_init(namespace_pool, getenv("USER"), &ns) < 0) i_fatal("Namespace initialization failed"); - return client_create(0, 1, ns) != NULL; + client = client_create(0, 1, ns); + if (client == NULL) + return FALSE; + + str = getenv("CLIENT_INPUT"); + if (str != NULL) T_BEGIN { + buffer_t *buf = t_base64_decode_str(str); + if (buf->used > 0) { + if (!i_stream_add_data(client->input, buf->data, + buf->used)) + i_panic("Couldn't add client input to stream"); + ret = client_handle_input(client); + } + } T_END; + return ret; } static void main_deinit(void)