IMAP/POP3 process so it can process the command instead of discarding it.
--HG--
branch : HEAD
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;
#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
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,
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;
}
}
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);
/* 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;
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");
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);
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);
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);
}
}
int refcount;
struct io *io;
- struct istream *input;
struct ostream *output;
struct imap_parser *parser;
struct timeout *to_idle_disconnect, *to_auth_waiting;
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);
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)
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;
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
#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"
static void main_init(void)
{
struct client *client;
+ struct ostream *output;
struct mail_namespace *ns;
const char *user, *str;
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 ",
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)
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);
#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;
struct ssl_proxy *proxy;
int fd;
+ struct istream *input;
char *auth_mech_name;
struct auth_request *auth_request;
#include "common.h"
#include "hash.h"
+#include "buffer.h"
#include "ioloop.h"
#include "network.h"
#include "fdpass.h"
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)
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;
group->set,
request->fd, &request->local_ip,
&request->remote_ip, user, args,
+ request->data_size, request->data,
FALSE);
} T_END;
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;
}
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");
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));
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;
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) {
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;
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) {
#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"
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;
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 {
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);
(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,
uint32_t auth_pid;
uint32_t auth_id;
+ /* request follows this many bytes of client input */
+ uint32_t data_size;
ino_t ino;
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]);
return;
/* @UNSAFE */
- line = i_stream_next_line(client->input);
+ line = i_stream_next_line(client->common.input);
if (line == NULL)
return;
#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
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);
}
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);
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");
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';
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);
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);
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);
}
}
int refcount;
struct io *io;
- struct istream *input;
struct ostream *output;
struct timeout *to_idle_disconnect;
struct auth_connect_id auth_id;
unsigned int login_success:1;
- unsigned int authenticating:1;
unsigned int auth_connected:1;
unsigned int destroyed:1;
};
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);
&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) {
}
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)
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);
#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"
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);
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)