]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: Added support for enforcing a payload limit for incoming HTTP messages.
authorStephan Bosch <stephan@rename-it.nl>
Sun, 15 Sep 2013 00:52:01 +0000 (03:52 +0300)
committerStephan Bosch <stephan@rename-it.nl>
Sun, 15 Sep 2013 00:52:01 +0000 (03:52 +0300)
src/lib-http/http-message-parser.c
src/lib-http/http-message-parser.h
src/lib-http/http-request-parser.c
src/lib-http/http-response-parser.c
src/lib-http/http-transfer-chunked.c
src/lib-http/http-transfer.h
src/lib-http/test-http-transfer.c

index 5cf381895e6d3acf1e6aa15975251b2dcf18c3d5..2b881dc356d300f0c09f0ddef5010c3913570790 100644 (file)
 #include <ctype.h>
 
 void http_message_parser_init(struct http_message_parser *parser,
-       struct istream *input, const struct http_header_limits *hdr_limits)
+       struct istream *input, const struct http_header_limits *hdr_limits,
+       uoff_t max_payload_size)
 {
        memset(parser, 0, sizeof(*parser));
        parser->input = input;
        if (hdr_limits != NULL)
                parser->header_limits = *hdr_limits;
+       parser->max_payload_size = max_payload_size;
 }
 
 void http_message_parser_deinit(struct http_message_parser *parser)
@@ -98,8 +100,16 @@ int http_message_parse_finish_payload(struct http_message_parser *parser)
                i_stream_skip(parser->payload, size);
        if (ret == 0 || parser->payload->stream_errno != 0) {
                if (ret < 0) {
-                       parser->error = "Stream error while skipping payload";
-                       parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM;
+                       if (parser->payload->stream_errno == EMSGSIZE) {
+                               parser->error_code = HTTP_MESSAGE_PARSE_ERROR_PAYLOAD_TOO_LARGE;
+                               parser->error = "Payload is too large";
+                       } else if (parser->payload->stream_errno == EIO) {
+                               parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE;
+                               parser->error = "Invalid payload";
+                       } else {
+                               parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM;
+                               parser->error = "Stream error while skipping payload";
+                       }
                }
                return ret;
        }
@@ -370,8 +380,8 @@ int http_message_parse_body(struct http_message_parser *parser, bool request)
        }
 
                if (chunked_last) {     
-                       parser->payload =
-                               http_transfer_chunked_istream_create(parser->input);
+                       parser->payload = http_transfer_chunked_istream_create
+                               (parser->input, parser->max_payload_size);
                } else if (!request) {
                        /*  https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
                              Section 3.3.3.:
@@ -381,7 +391,8 @@ int http_message_parse_body(struct http_message_parser *parser, bool request)
                            message body length is determined by reading the connection until
                            it is closed by the server.
                         */
-                       parser->payload =
+                       /* FIXME: enforce max payload size (relevant to http-client only) */
+                       parser->payload = 
                                        i_stream_create_limit(parser->input, (size_t)-1);
                } else {
                        /* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
@@ -412,6 +423,13 @@ int http_message_parse_body(struct http_message_parser *parser, bool request)
                        http_header_field_delete(parser->msg.header, "Content-Length");
 
        } else if (parser->msg.content_length > 0) {
+               if (parser->max_payload_size > 0
+                       && parser->msg.content_length > parser->max_payload_size) {
+                       parser->error_code = HTTP_MESSAGE_PARSE_ERROR_PAYLOAD_TOO_LARGE;
+                       parser->error = "Payload is too large";
+                       return -1;
+               }
+
                /* Got explicit message size from Content-Length: header */
                parser->payload =
                        i_stream_create_limit(parser->input,
@@ -427,6 +445,7 @@ int http_message_parse_body(struct http_message_parser *parser, bool request)
                   body length, so the message body length is determined by the
                   number of octets received prior to the server closing the connection.
                 */
+               /* FIXME: enforce max payload size (relevant to http-client only) */
                parser->payload =
                        i_stream_create_limit(parser->input, (size_t)-1);
        }
index 487b4ed0a6051cf66bff4ae4d7976e38d496d9e8..879c094b4fb30d6361ffaf5239ac8e39da247718 100644 (file)
@@ -7,12 +7,14 @@
 #include "http-header.h"
 
 enum http_message_parse_error {
-       HTTP_MESSAGE_PARSE_ERROR_NONE = 0,        /* no error */
-       HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM,   /* stream error */
-       HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE,  /* unrecoverable generic error */
-       HTTP_MESSAGE_PARSE_ERROR_BAD_MESSAGE,     /* recoverable generic error */
-       HTTP_MESSAGE_PARSE_ERROR_NOT_IMPLEMENTED, /* used unimplemented feature
-                                                   (recoverable) */
+       HTTP_MESSAGE_PARSE_ERROR_NONE = 0,          /* no error */
+       HTTP_MESSAGE_PARSE_ERROR_BROKEN_STREAM,     /* stream error */
+       HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE,    /* unrecoverable generic error */
+       HTTP_MESSAGE_PARSE_ERROR_BAD_MESSAGE,       /* recoverable generic error */
+       HTTP_MESSAGE_PARSE_ERROR_NOT_IMPLEMENTED,   /* used unimplemented feature
+                                                     (recoverable) */
+       HTTP_MESSAGE_PARSE_ERROR_PAYLOAD_TOO_LARGE  /* message payload is too large
+                                                     (fatal) */
 };
 
 struct http_message {
@@ -35,7 +37,9 @@ struct http_message {
 
 struct http_message_parser {
        struct istream *input;
+
        struct http_header_limits header_limits;
+       uoff_t max_payload_size;
 
        const unsigned char *cur, *end;
 
@@ -50,8 +54,8 @@ struct http_message_parser {
 };
 
 void http_message_parser_init(struct http_message_parser *parser,
-       struct istream *input, const struct http_header_limits *hdr_limits)
-       ATTR_NULL(3);
+       struct istream *input, const struct http_header_limits *hdr_limits,
+       uoff_t max_payload_size) ATTR_NULL(3);
 void http_message_parser_deinit(struct http_message_parser *parser);
 void http_message_parser_restart(struct http_message_parser *parser,
        pool_t pool);
index 654a3641c410e35b98ba18d27bc84cc1907654d0..28284d2fa9a39e40c93c2218294fc54d1d820310 100644 (file)
@@ -39,7 +39,7 @@ http_request_parser_init(struct istream *input,
        struct http_request_parser *parser;
 
        parser = i_new(struct http_request_parser, 1);
-       http_message_parser_init(&parser->parser, input, hdr_limits);
+       http_message_parser_init(&parser->parser, input, hdr_limits, 0);
        return parser;
 }
 
index cdff4fa18aeddd8a97e767c6298d0beb410b5136..ea4fa5e1cb8e1d93d962d0ec6a6b06cd25f0e85b 100644 (file)
@@ -36,7 +36,7 @@ http_response_parser_init(struct istream *input,
 
        /* FIXME: implement status line limit */
        parser = i_new(struct http_response_parser, 1);
-       http_message_parser_init(&parser->parser, input, hdr_limits);
+       http_message_parser_init(&parser->parser, input, hdr_limits, 0);
        return parser;
 }
 
index 5d1d01226f9a49b3ba6499bb5e025e4ac15196f7..414027849d6505083f7837606f2a74254f95dc95 100644 (file)
@@ -43,6 +43,7 @@ struct http_transfer_chunked_istream {
        unsigned int parsed_chars;
 
        uoff_t chunk_size, chunk_v_offset, chunk_pos;
+       uoff_t size, max_size;
        const char *error;
 
        struct http_header_parser *header_parser;
@@ -327,8 +328,16 @@ static int http_transfer_chunked_parse_next(
                i_stream_skip(input, tcstream->cur - tcstream->begin);
 
                if (ret > 0) {
-                       if (tcstream->state == HTTP_CHUNKED_PARSE_STATE_DATA)
+                       if (tcstream->state == HTTP_CHUNKED_PARSE_STATE_DATA) {
                                tcstream->chunk_v_offset = input->v_offset;
+
+                               tcstream->size += tcstream->chunk_size;
+                               if (tcstream->max_size > 0 && tcstream->size > tcstream->max_size) {
+                                       tcstream->error = "Total chunked payload size exceeds maximum";
+                                       stream->istream.stream_errno = EMSGSIZE;
+                                       return -1;
+                               }
+                       }
                        return ret;
                }
        }
@@ -495,11 +504,12 @@ http_transfer_chunked_istream_destroy(struct iostream_private *stream)
 }
 
 struct istream *
-http_transfer_chunked_istream_create(struct istream *input)
+http_transfer_chunked_istream_create(struct istream *input, uoff_t max_size)
 {
        struct http_transfer_chunked_istream *tcstream;
 
        tcstream = i_new(struct http_transfer_chunked_istream, 1);
+       tcstream->max_size = max_size;
 
        tcstream->istream.max_buffer_size =
                input->real_stream->max_buffer_size;
index c7da0daf464bff9d618dcba6175cd27db173afb0..f3f679108e5dd9002dc3b511c32645d009dff7c0 100644 (file)
@@ -18,7 +18,7 @@ ARRAY_DEFINE_TYPE(http_transfer_coding, struct http_transfer_coding);
 // FIXME: we currently lack a means to get error strings from the input stream
 
 struct istream *
-       http_transfer_chunked_istream_create(struct istream *input);
+http_transfer_chunked_istream_create(struct istream *input, uoff_t max_size);
 struct ostream *
        http_transfer_chunked_ostream_create(struct ostream *output);
 
index 9403edd37897e106bc1e86e4ced94bbac31decd5..643ddc47a33f0b7268f61dfee3c53842ea492c22 100644 (file)
@@ -99,7 +99,7 @@ static void test_http_transfer_chunked_input_valid(void)
                test_begin(t_strdup_printf("http transfer_chunked input valid [%d]", i));
 
                input = i_stream_create_from_data(in, strlen(in));
-               chunked = http_transfer_chunked_istream_create(input);
+               chunked = http_transfer_chunked_istream_create(input, 0);
 
                buffer_set_used_size(payload_buffer, 0);
                output = o_stream_create_buffer(payload_buffer);
@@ -193,7 +193,7 @@ static void test_http_transfer_chunked_input_invalid(void)
                test_begin(t_strdup_printf("http transfer_chunked input invalid [%d]", i));
 
                input = i_stream_create_from_data(in, strlen(in));
-               chunked = http_transfer_chunked_istream_create(input);
+               chunked = http_transfer_chunked_istream_create(input, 0);
 
                buffer_set_used_size(payload_buffer, 0);
                output = o_stream_create_buffer(payload_buffer);
@@ -306,7 +306,7 @@ static void test_http_transfer_chunked_output_valid(void)
                /* create chunked input stream */
                input = i_stream_create_from_data
                        (chunked_buffer->data, chunked_buffer->used);
-               ichunked = http_transfer_chunked_istream_create(input);
+               ichunked = http_transfer_chunked_istream_create(input, 0);
 
                /* read back chunk */
                buffer_set_used_size(plain_buffer, 0);