Mercurial > dovecot > original-hg > dovecot-2.2
changeset 16750:a335db9dca6a
lib-http: Added support for enforcing a payload limit for incoming HTTP messages.
author | Stephan Bosch <stephan@rename-it.nl> |
---|---|
date | Sun, 15 Sep 2013 03:52:01 +0300 |
parents | 28e5d58e49dc |
children | 7c7e3aa13de7 |
files | 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 |
diffstat | 7 files changed, 55 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-http/http-message-parser.c Sun Sep 15 03:50:08 2013 +0300 +++ b/src/lib-http/http-message-parser.c Sun Sep 15 03:52:01 2013 +0300 @@ -13,12 +13,14 @@ #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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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); }
--- a/src/lib-http/http-message-parser.h Sun Sep 15 03:50:08 2013 +0300 +++ b/src/lib-http/http-message-parser.h Sun Sep 15 03:52:01 2013 +0300 @@ -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_parser { struct istream *input; + struct http_header_limits header_limits; + uoff_t max_payload_size; const unsigned char *cur, *end; @@ -50,8 +54,8 @@ }; 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);
--- a/src/lib-http/http-request-parser.c Sun Sep 15 03:50:08 2013 +0300 +++ b/src/lib-http/http-request-parser.c Sun Sep 15 03:52:01 2013 +0300 @@ -39,7 +39,7 @@ 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; }
--- a/src/lib-http/http-response-parser.c Sun Sep 15 03:50:08 2013 +0300 +++ b/src/lib-http/http-response-parser.c Sun Sep 15 03:52:01 2013 +0300 @@ -36,7 +36,7 @@ /* 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; }
--- a/src/lib-http/http-transfer-chunked.c Sun Sep 15 03:50:08 2013 +0300 +++ b/src/lib-http/http-transfer-chunked.c Sun Sep 15 03:52:01 2013 +0300 @@ -43,6 +43,7 @@ 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 @@ 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 @@ } 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;
--- a/src/lib-http/http-transfer.h Sun Sep 15 03:50:08 2013 +0300 +++ b/src/lib-http/http-transfer.h Sun Sep 15 03:52:01 2013 +0300 @@ -18,7 +18,7 @@ // 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);
--- a/src/lib-http/test-http-transfer.c Sun Sep 15 03:50:08 2013 +0300 +++ b/src/lib-http/test-http-transfer.c Sun Sep 15 03:52:01 2013 +0300 @@ -99,7 +99,7 @@ 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 @@ 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 @@ /* 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);