dovecot-2.2: lib-http: Added support for enforcing a payload lim...

dovecot at dovecot.org dovecot at dovecot.org
Sun Sep 15 03:57:22 EEST 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/a335db9dca6a
changeset: 16750:a335db9dca6a
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Sun Sep 15 03:52:01 2013 +0300
description:
lib-http: Added support for enforcing a payload limit for incoming HTTP messages.

diffstat:

 src/lib-http/http-message-parser.c   |  31 +++++++++++++++++++++++++------
 src/lib-http/http-message-parser.h   |  20 ++++++++++++--------
 src/lib-http/http-request-parser.c   |   2 +-
 src/lib-http/http-response-parser.c  |   2 +-
 src/lib-http/http-transfer-chunked.c |  14 ++++++++++++--
 src/lib-http/http-transfer.h         |   2 +-
 src/lib-http/test-http-transfer.c    |   6 +++---
 7 files changed, 55 insertions(+), 22 deletions(-)

diffs (235 lines):

diff -r 28e5d58e49dc -r a335db9dca6a src/lib-http/http-message-parser.c
--- 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);
 	}
diff -r 28e5d58e49dc -r a335db9dca6a src/lib-http/http-message-parser.h
--- 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);
diff -r 28e5d58e49dc -r a335db9dca6a src/lib-http/http-request-parser.c
--- 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;
 }
 
diff -r 28e5d58e49dc -r a335db9dca6a src/lib-http/http-response-parser.c
--- 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;
 }
 
diff -r 28e5d58e49dc -r a335db9dca6a src/lib-http/http-transfer-chunked.c
--- 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;
diff -r 28e5d58e49dc -r a335db9dca6a src/lib-http/http-transfer.h
--- 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);
 
diff -r 28e5d58e49dc -r a335db9dca6a src/lib-http/test-http-transfer.c
--- 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);


More information about the dovecot-cvs mailing list