]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
IMAP parser memory limits are now enforced by bytes per line rather than
authorTimo Sirainen <tss@iki.fi>
Wed, 2 Jul 2003 00:57:24 +0000 (03:57 +0300)
committerTimo Sirainen <tss@iki.fi>
Wed, 2 Jul 2003 00:57:24 +0000 (03:57 +0300)
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

src/imap-login/client.c
src/imap/client.c
src/imap/cmd-append.c
src/imap/common.h
src/imap/main.c
src/lib-imap/imap-bodystructure.c
src/lib-imap/imap-envelope.c
src/lib-imap/imap-parser.c
src/lib-imap/imap-parser.h

index 241fb5f4616e2117b8337dcbe55b1ea3d629018b..2455ee0cb33fda4334e17912218282f9012c1558 100644 (file)
@@ -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,
index 85ca4138149478e2be5f6b3a6343075e1cdf7251..98baef3563bcf6254b91a3a488f495889547504a 100644 (file)
@@ -9,9 +9,6 @@
 
 #include <stdlib.h>
 
-/* 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 =
index 408db83e73b4a6c4d686ad113a73dfe06ca05c59..d84759bebe27de9d79cac7b5438204a37c02ffc8 100644 (file)
@@ -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 (;;) {
                /* [<flags>] [<internal date>] <message literal> */
index 7c351d0c8adcfc7c3a27e2d5b9b23f976f292af3..edcb7979af36f7d24850a399fef8cb967418ea97 100644 (file)
@@ -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;
 
index 8918cba3273743807a52216f83e63a6895dd3370..2142abfb34b64394793963a87476ebe691d1bc23 100644 (file)
@@ -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) :
index 96bde773250887ca3963d271925205da0b5e9049..639f2815060f2a63517db37812144cd60d636de3 100644 (file)
@@ -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);
 
index b0c4ab14ef48b013190b96b4bb0562fec5c48929..e6d7a0d77177548ba1a049f8584dc4ccc4ab36fa 100644 (file)
@@ -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);
index e59815c904fca403199da994e73b6d021926d18b..467a774cdff734bf2a403242e7ceda35965d417a 100644 (file)
@@ -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;
index 4f83050f3d7d14ee56c50103dd064c13afc01c12..331d59e2e8453bcdd933d086a8c1c482d6eec0b8 100644 (file)
@@ -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);