From: Timo Sirainen Date: Wed, 2 Jul 2003 00:57:24 +0000 (+0300) Subject: IMAP parser memory limits are now enforced by bytes per line rather than X-Git-Tag: 1.1.alpha1~4515 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1733dc0970314e157c001dd21c39b275d515c259;p=thirdparty%2Fdovecot%2Fcore.git IMAP parser memory limits are now enforced by bytes per line rather than limiting maximum amount of tokens per line. Default is 64k now, which should help with the huge message sets generated by some clients. --HG-- branch : HEAD --- diff --git a/src/imap-login/client.c b/src/imap-login/client.c index 241fb5f461..2455ee0cb3 100644 --- a/src/imap-login/client.c +++ b/src/imap-login/client.c @@ -20,9 +20,8 @@ #define MAX_OUTBUF_SIZE 1024 -/* max. number of IMAP argument elements to accept. The maximum memory usage - for command from user is around MAX_INBUF_SIZE * MAX_IMAP_ARG_ELEMENTS */ -#define MAX_IMAP_ARG_ELEMENTS 4 +/* maximum length for IMAP command line. */ +#define MAX_IMAP_LINE 8192 /* Disconnect client after idling this many seconds */ #define CLIENT_LOGIN_IDLE_TIMEOUT 60 @@ -64,8 +63,7 @@ static void client_open_streams(struct imap_client *client, int fd) client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE, FALSE); client->parser = imap_parser_create(client->input, client->output, - MAX_INBUF_SIZE, - MAX_IMAP_ARG_ELEMENTS); + MAX_IMAP_LINE); } /* Skip incoming data until newline is found, diff --git a/src/imap/client.c b/src/imap/client.c index 85ca413814..98baef3563 100644 --- a/src/imap/client.c +++ b/src/imap/client.c @@ -9,9 +9,6 @@ #include -/* max. size of one parameter in line */ -#define MAX_INBUF_SIZE 8192 - /* If we can't send a buffer in a minute, disconnect the client */ #define CLIENT_OUTPUT_TIMEOUT (60*1000) @@ -49,7 +46,7 @@ struct client *client_create(int hin, int hout, struct mail_storage *storage) client = i_new(struct client, 1); client->input = i_stream_create_file(hin, default_pool, - MAX_INBUF_SIZE, FALSE); + imap_max_line_length, FALSE); client->output = o_stream_create_file(hout, default_pool, 4096, FALSE); /* set timeout for reading expected data (eg. APPEND). This is @@ -63,8 +60,7 @@ struct client *client_create(int hin, int hout, struct mail_storage *storage) client->io = io_add(hin, IO_READ, _client_input, client); client->parser = imap_parser_create(client->input, client->output, - MAX_INBUF_SIZE, - MAX_IMAP_ARG_ELEMENTS); + imap_max_line_length); client->last_input = ioloop_time; client->mailbox_flags.pool = diff --git a/src/imap/cmd-append.c b/src/imap/cmd-append.c index 408db83e73..d84759bebe 100644 --- a/src/imap/cmd-append.c +++ b/src/imap/cmd-append.c @@ -95,7 +95,7 @@ int cmd_append(struct client *client) count = 0; failed = TRUE; save_parser = imap_parser_create(client->input, client->output, - 0, MAX_IMAP_ARG_ELEMENTS); + imap_max_line_length); for (;;) { /* [] [] */ diff --git a/src/imap/common.h b/src/imap/common.h index 7c351d0c8a..edcb7979af 100644 --- a/src/imap/common.h +++ b/src/imap/common.h @@ -4,17 +4,19 @@ #include "lib.h" #include "client.h" -/* max. number of IMAP argument elements to accept. The maximum memory usage - for command from user is around MAX_INBUF_SIZE * MAX_IMAP_ARG_ELEMENTS */ -#define MAX_IMAP_ARG_ELEMENTS 128 - /* Disconnect client after idling this many seconds */ #define CLIENT_IDLE_TIMEOUT (60*30) +/* RFC-2683 recommends at least 8000 bytes. Some clients however don't + break large message sets to multiple commands, so we're pretty liberal + by default. */ +#define DEFAULT_IMAP_MAX_LINE_LENGTH 65536 + #define DEFAULT_MAX_CUSTOM_FLAG_LENGTH 50 extern struct ioloop *ioloop; extern unsigned int max_custom_flag_length, mailbox_check_interval; +extern unsigned int imap_max_line_length; extern string_t *capability_string; diff --git a/src/imap/main.c b/src/imap/main.c index 8918cba327..2142abfb34 100644 --- a/src/imap/main.c +++ b/src/imap/main.c @@ -22,6 +22,7 @@ struct ioloop *ioloop; unsigned int max_custom_flag_length, mailbox_check_interval; +unsigned int imap_max_line_length; static struct module *modules; static char log_prefix[128]; /* syslog() needs this to be permanent */ @@ -127,6 +128,11 @@ static void main_init(void) } } + str = getenv("IMAP_MAX_LINE_LENGTH"); + imap_max_line_length = str != NULL ? + (unsigned int)strtoul(str, NULL, 10) : + DEFAULT_IMAP_MAX_LINE_LENGTH; + str = getenv("MAIL_MAX_FLAG_LENGTH"); max_custom_flag_length = str != NULL ? (unsigned int)strtoul(str, NULL, 10) : diff --git a/src/lib-imap/imap-bodystructure.c b/src/lib-imap/imap-bodystructure.c index 96bde77325..639f281506 100644 --- a/src/lib-imap/imap-bodystructure.c +++ b/src/lib-imap/imap-bodystructure.c @@ -679,7 +679,7 @@ const char *imap_body_parse_from_bodystructure(const char *bodystructure) input = i_stream_create_from_data(data_stack_pool, bodystructure, len); (void)i_stream_read(input); - parser = imap_parser_create(input, NULL, 0, (size_t)-1); + parser = imap_parser_create(input, NULL, (size_t)-1); ret = imap_parser_read_args(parser, 0, IMAP_PARSE_FLAG_NO_UNESCAPE | IMAP_PARSE_FLAG_LITERAL_TYPE, &args); diff --git a/src/lib-imap/imap-envelope.c b/src/lib-imap/imap-envelope.c index b0c4ab14ef..e6d7a0d771 100644 --- a/src/lib-imap/imap-envelope.c +++ b/src/lib-imap/imap-envelope.c @@ -368,7 +368,7 @@ int imap_envelope_parse(const char *envelope, enum imap_envelope_field field, input = i_stream_create_from_data(data_stack_pool, envelope, strlen(envelope)); - parser = imap_parser_create(input, NULL, (size_t)-1, (size_t)-1); + parser = imap_parser_create(input, NULL, (size_t)-1); (void)i_stream_read(input); ret = imap_parser_read_args(parser, field+1, 0, &args); diff --git a/src/lib-imap/imap-parser.c b/src/lib-imap/imap-parser.c index e59815c904..467a774cdf 100644 --- a/src/lib-imap/imap-parser.c +++ b/src/lib-imap/imap-parser.c @@ -28,11 +28,12 @@ struct imap_parser { pool_t pool; struct istream *input; struct ostream *output; - size_t max_literal_size, max_elements; + size_t max_line_size; enum imap_parser_flags flags; /* reset by imap_parser_reset(): */ - struct imap_arg_list *root_list; + size_t line_size; + struct imap_arg_list *root_list; struct imap_arg_list *cur_list; struct imap_arg *list_arg; size_t element_count; @@ -73,7 +74,7 @@ static void imap_args_realloc(struct imap_parser *parser, size_t size) struct imap_parser * imap_parser_create(struct istream *input, struct ostream *output, - size_t max_literal_size, size_t max_elements) + size_t max_line_size) { struct imap_parser *parser; @@ -81,8 +82,7 @@ imap_parser_create(struct istream *input, struct ostream *output, parser->pool = pool_alloconly_create("IMAP parser", 8192); parser->input = input; parser->output = output; - parser->max_literal_size = max_literal_size; - parser->max_elements = max_elements; + parser->max_line_size = max_line_size; imap_args_realloc(parser, LIST_ALLOC_SIZE); return parser; @@ -98,6 +98,8 @@ void imap_parser_reset(struct imap_parser *parser) { p_clear(parser->pool); + parser->line_size = 0; + parser->root_list = NULL; parser->cur_list = NULL; parser->list_arg = NULL; @@ -136,6 +138,7 @@ static int imap_parser_skip_to_next(struct imap_parser *parser, break; } + parser->line_size += i; i_stream_skip(parser->input, i); parser->cur_pos = 0; @@ -352,7 +355,9 @@ static int imap_parser_read_string(struct imap_parser *parser, static int imap_parser_literal_end(struct imap_parser *parser) { if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_SIZE) == 0) { - if (parser->literal_size > parser->max_literal_size) { + if (parser->line_size >= parser->max_line_size || + parser->literal_size > + parser->max_line_size - parser->line_size) { /* too long string, abort. */ parser->error = "Literal size too large"; parser->fatal_error = TRUE; @@ -381,6 +386,7 @@ static int imap_parser_read_literal(struct imap_parser *parser, /* expecting digits + "}" */ for (i = parser->cur_pos; i < data_size; i++) { if (data[i] == '}') { + parser->line_size += i+1; i_stream_skip(parser->input, i+1); return imap_parser_literal_end(parser); } @@ -424,6 +430,7 @@ static int imap_parser_read_literal_data(struct imap_parser *parser, return FALSE; if (*data == '\r') { + parser->line_size++; data++; data_size--; i_stream_skip(parser->input, 1); @@ -436,8 +443,10 @@ static int imap_parser_read_literal_data(struct imap_parser *parser, return FALSE; } + parser->line_size++; data++; data_size--; i_stream_skip(parser->input, 1); + parser->literal_skip_crlf = FALSE; i_assert(parser->cur_pos == 0); @@ -564,14 +573,15 @@ int imap_parser_read_args(struct imap_parser *parser, unsigned int count, if (!imap_parser_read_arg(parser)) break; - if (parser->element_count > parser->max_elements) { - parser->error = "Too many argument elements"; + if (parser->line_size > parser->max_line_size) { + parser->error = "IMAP command line too large"; break; } } if (parser->error != NULL) { /* error, abort */ + parser->line_size += parser->cur_pos; i_stream_skip(parser->input, parser->cur_pos); parser->cur_pos = 0; *args = NULL; @@ -579,6 +589,7 @@ int imap_parser_read_args(struct imap_parser *parser, unsigned int count, } else if ((!IS_UNFINISHED(parser) && count > 0 && parser->root_list->size >= count) || parser->eol) { /* all arguments read / end of line. */ + parser->line_size += parser->cur_pos; i_stream_skip(parser->input, parser->cur_pos); parser->cur_pos = 0; @@ -621,27 +632,9 @@ const char *imap_parser_read_word(struct imap_parser *parser) } if (i < data_size) { - i_stream_skip(parser->input, i + (data[i] == ' ' ? 1 : 0)); - return p_strndup(parser->pool, data, i); - } else { - return NULL; - } -} - -const char *imap_parser_read_line(struct imap_parser *parser) -{ - const unsigned char *data; - size_t i, data_size; - - data = i_stream_get_data(parser->input, &data_size); - - for (i = 0; i < data_size; i++) { - if (data[i] == '\r' || data[i] == '\n') - break; - } - - if (i < data_size) { - i_stream_skip(parser->input, i); + data_size = i + (data[i] == ' ' ? 1 : 0); + parser->line_size += data_size; + i_stream_skip(parser->input, data_size); return p_strndup(parser->pool, data, i); } else { return NULL; diff --git a/src/lib-imap/imap-parser.h b/src/lib-imap/imap-parser.h index 4f83050f3d..331d59e2e8 100644 --- a/src/lib-imap/imap-parser.h +++ b/src/lib-imap/imap-parser.h @@ -60,15 +60,20 @@ struct imap_arg_list { struct imap_arg args[1]; /* variable size */ }; -/* Create new IMAP argument parser. There's no limit in argument sizes, only - the maximum buffer size of input stream limits it. max_literal_size limits - the maximum size of internally handled literals (ie. FLAG_LITERAL_SIZE is - unset). max_elements sets the number of elements we allow entirely so that - user can't give huge lists or lists inside lists. output is used for sending - command continuation requests for literals. */ +/* Create new IMAP argument parser. output is used for sending command + continuation requests for literals. + + max_line_size can be used to approximately limit the maximum amount of + memory that gets allocated when parsing a line. Input buffer size limits + the maximum size of each parsed token. + + Usually the largest lines are large only because they have a one huge + message set token, so you'll probably want to keep input buffer size the + same as max_line_size. That means the maximum memory usage is around + 2 * max_line_size. */ struct imap_parser * imap_parser_create(struct istream *input, struct ostream *output, - size_t max_literal_size, size_t max_elements); + size_t max_line_size); void imap_parser_destroy(struct imap_parser *parser); /* Reset the parser to initial state. */ @@ -94,9 +99,6 @@ int imap_parser_read_args(struct imap_parser *parser, unsigned int count, Returns NULL if more data is needed. */ const char *imap_parser_read_word(struct imap_parser *parser); -/* Read the rest of the line. Returns NULL if more data is needed. */ -const char *imap_parser_read_line(struct imap_parser *parser); - /* Returns the imap argument as string. NIL returns "" and list returns NULL. */ const char *imap_arg_string(struct imap_arg *arg);