dovecot-2.2: lib-http: Implemented limits on overall HTTP header...

dovecot at dovecot.org dovecot at dovecot.org
Sun Sep 15 03:50:34 EEST 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/572b9a9031e7
changeset: 16745:572b9a9031e7
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Sun Sep 15 03:46:12 2013 +0300
description:
lib-http: Implemented limits on overall HTTP header size, size of individual header fields and the number of fields in the header.

diffstat:

 src/lib-http/http-client-connection.c    |    3 +-
 src/lib-http/http-client.c               |    1 +
 src/lib-http/http-client.h               |    3 +
 src/lib-http/http-header-parser.c        |   61 ++++++++++++-
 src/lib-http/http-header-parser.h        |    5 +-
 src/lib-http/http-header.h               |    6 +
 src/lib-http/http-message-parser.c       |   12 +-
 src/lib-http/http-message-parser.h       |    6 +-
 src/lib-http/http-request-parser.c       |    6 +-
 src/lib-http/http-request-parser.h       |    3 +-
 src/lib-http/http-response-parser.c      |    7 +-
 src/lib-http/http-response-parser.h      |    4 +-
 src/lib-http/http-transfer-chunked.c     |    4 +-
 src/lib-http/test-http-header-parser.c   |  134 +++++++++++++++++++++++-------
 src/lib-http/test-http-response-parser.c |    6 +-
 src/lib-http/test-http-server.c          |    2 +-
 16 files changed, 207 insertions(+), 56 deletions(-)

diffs (truncated from 605 to 300 lines):

diff -r dca140149d80 -r 572b9a9031e7 src/lib-http/http-client-connection.c
--- a/src/lib-http/http-client-connection.c	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-client-connection.c	Sun Sep 15 03:46:12 2013 +0300
@@ -721,7 +721,8 @@
 	}
 
 	/* start protocol I/O */
-	conn->http_parser = http_response_parser_init(conn->conn.input);
+	conn->http_parser = http_response_parser_init
+		(conn->conn.input, &conn->client->set.response_hdr_limits);
 	o_stream_set_flush_callback(conn->conn.output,
     http_client_connection_output, conn);
 }
diff -r dca140149d80 -r 572b9a9031e7 src/lib-http/http-client.c
--- a/src/lib-http/http-client.c	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-client.c	Sun Sep 15 03:46:12 2013 +0300
@@ -96,6 +96,7 @@
 		(set->max_pipelined_requests > 0 ? set->max_pipelined_requests : 1);
 	client->set.max_attempts = set->max_attempts;
 	client->set.max_redirects = set->max_redirects;
+	client->set.response_hdr_limits = set->response_hdr_limits;
 	client->set.request_timeout_msecs = set->request_timeout_msecs;
 	client->set.connect_timeout_msecs = set->connect_timeout_msecs;
 	client->set.soft_connect_timeout_msecs = set->soft_connect_timeout_msecs;
diff -r dca140149d80 -r 572b9a9031e7 src/lib-http/http-client.h
--- a/src/lib-http/http-client.h	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-client.h	Sun Sep 15 03:46:12 2013 +0300
@@ -59,6 +59,9 @@
 	/* maximum number of attempts for a request */
 	unsigned int max_attempts;
 
+	/* response header limits */
+	struct http_header_limits response_hdr_limits;
+
 	/* max time to wait for HTTP request to finish before retrying
 	   (default = unlimited) */
 	unsigned int request_timeout_msecs;
diff -r dca140149d80 -r 572b9a9031e7 src/lib-http/http-header-parser.c
--- a/src/lib-http/http-header-parser.c	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-header-parser.c	Sun Sep 15 03:46:12 2013 +0300
@@ -6,6 +6,7 @@
 #include "str.h"
 #include "str-sanitize.h"
 #include "http-parser.h"
+#include "http-header.h"
 
 #include "http-header-parser.h"
 
@@ -25,6 +26,10 @@
 struct http_header_parser {
 	struct istream *input;
 
+	struct http_header_limits limits;
+	uoff_t size, field_size;
+	unsigned int field_count;
+
 	const unsigned char *begin, *cur, *end;
 	const char *error;
 
@@ -34,9 +39,9 @@
 	enum http_header_parse_state state;
 };
 
-// FIXME(Stephan): Add support for limiting maximum header size.
-
-struct http_header_parser *http_header_parser_init(struct istream *input)
+struct http_header_parser *
+http_header_parser_init(struct istream *input,
+	const struct http_header_limits *limits)
 {
 	struct http_header_parser *parser;
 
@@ -45,6 +50,16 @@
 	parser->name = str_new(default_pool, 128);
 	parser->value_buf = buffer_create_dynamic(default_pool, 4096);
 
+	if (limits != NULL)
+		parser->limits = *limits;
+
+	if (parser->limits.max_size == 0)
+		parser->limits.max_size = (uoff_t)-1;
+	if (parser->limits.max_field_size == 0)
+		parser->limits.max_field_size = (uoff_t)-1;
+	if (parser->limits.max_fields == 0)
+		parser->limits.max_fields = (unsigned int)-1;
+
 	return parser;
 }
 
@@ -62,6 +77,9 @@
 void http_header_parser_reset(struct http_header_parser *parser)
 {
 	parser->state = HTTP_HEADER_PARSE_STATE_INIT;
+	parser->size = 0;
+	parser->field_size = 0;
+	parser->field_count = 0;
 }
 
 static int http_header_parse_name(struct http_header_parser *parser)
@@ -144,7 +162,7 @@
 			if (http_char_is_token(*parser->cur)) {
 				if ((ret=http_header_parse_name(parser)) <= 0)
 					return ret;
-			} else if (str_len(parser->name) == 0) {
+			} else if (*parser->cur != ':' && str_len(parser->name) == 0) {
 				parser->state = HTTP_HEADER_PARSE_STATE_LAST_LINE;
 				break;
 			}
@@ -163,6 +181,10 @@
 				parser->error = "Empty header field name";
 				return -1;
 			}
+			if (++parser->field_count > parser->limits.max_fields) {
+				parser->error = "Excessive number of header fields";
+				return -1;
+			}
 			parser->state = HTTP_HEADER_PARSE_STATE_OWS;
 			/* fall through */
 		case HTTP_HEADER_PARSE_STATE_OWS:
@@ -203,7 +225,7 @@
 				parser->state = HTTP_HEADER_PARSE_STATE_OWS;
 				break;
 			}
-			parser->state = HTTP_HEADER_PARSE_STATE_NAME;
+			parser->state = HTTP_HEADER_PARSE_STATE_INIT;
 			return 1;
 		case HTTP_HEADER_PARSE_STATE_LAST_LINE:
 			if (*parser->cur == '\r') {
@@ -247,12 +269,35 @@
 	const char **name_r, const unsigned char **data_r, size_t *size_r,
 	const char **error_r)
 {
+	const uoff_t max_size = parser->limits.max_size;
+	const uoff_t max_field_size = parser->limits.max_field_size;
 	const unsigned char *data;
-	size_t size;
+	uoff_t size;
 	int ret;
 
+	*error_r = NULL;
+
 	while ((ret=i_stream_read_data
 		(parser->input, &parser->begin, &size, 0)) > 0) {
+
+		/* check header size limits */
+		if (parser->size >= max_size) {
+			*error_r = "Excessive header size";
+			return -1;
+		}
+		if (parser->field_size > max_field_size) {
+			*error_r = "Excessive header field size";
+			return -1;
+		}
+
+		/* don't parse beyond header size limits */
+		if (size > (max_size - parser->size))
+			size = max_size - parser->size;
+		if (size > (max_field_size - parser->field_size)) {
+			size = max_field_size - parser->field_size;
+			size = (size == 0 ? 1 : size); /* need to parse one more byte */
+		}
+
 		parser->cur = parser->begin;
 		parser->end = parser->cur + size;
 
@@ -262,8 +307,12 @@
 		}
 
 		i_stream_skip(parser->input, parser->cur - parser->begin);
+		parser->size += parser->cur - parser->begin;
+		parser->field_size += parser->cur - parser->begin;
 
 		if (ret == 1) {
+			parser->field_size = 0;
+
 			if (parser->state != HTTP_HEADER_PARSE_STATE_EOH) {
 				data = buffer_get_data(parser->value_buf, &size);
 			
diff -r dca140149d80 -r 572b9a9031e7 src/lib-http/http-header-parser.h
--- a/src/lib-http/http-header-parser.h	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-header-parser.h	Sun Sep 15 03:46:12 2013 +0300
@@ -1,9 +1,12 @@
 #ifndef HTTP_HEADER_PARSER_H
 #define HTTP_HEADER_PARSER_H
 
+struct http_header_limits;
 struct http_header_parser;
 
-struct http_header_parser *http_header_parser_init(struct istream *input);
+struct http_header_parser *
+http_header_parser_init(struct istream *input,
+	const struct http_header_limits *limits);
 void http_header_parser_deinit(struct http_header_parser **_parser);
 
 void http_header_parser_reset(struct http_header_parser *parser);
diff -r dca140149d80 -r 572b9a9031e7 src/lib-http/http-header.h
--- a/src/lib-http/http-header.h	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-header.h	Sun Sep 15 03:46:12 2013 +0300
@@ -3,6 +3,12 @@
 
 struct http_header;
 
+struct http_header_limits {
+	uoff_t max_size;
+	uoff_t max_field_size;
+	unsigned int max_fields;
+};
+
 struct http_header_field {
 	const char *key; /* FIXME: rename to 'name' for v2.3 */
 	const char *value;
diff -r dca140149d80 -r 572b9a9031e7 src/lib-http/http-message-parser.c
--- a/src/lib-http/http-message-parser.c	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-message-parser.c	Sun Sep 15 03:46:12 2013 +0300
@@ -13,10 +13,12 @@
 #include <ctype.h>
 
 void http_message_parser_init(struct http_message_parser *parser,
-			      struct istream *input)
+	struct istream *input, const struct http_header_limits *hdr_limits)
 {
 	memset(parser, 0, sizeof(*parser));
 	parser->input = input;
+	if (hdr_limits != NULL)
+		parser->header_limits = *hdr_limits;
 }
 
 void http_message_parser_deinit(struct http_message_parser *parser)
@@ -34,10 +36,12 @@
 {
 	i_assert(parser->payload == NULL);
 
-	if (parser->header_parser == NULL)
-		parser->header_parser = http_header_parser_init(parser->input);
-	else
+	if (parser->header_parser == NULL) {
+		parser->header_parser =
+			http_header_parser_init(parser->input, &parser->header_limits);
+	} else {
 		http_header_parser_reset(parser->header_parser);
+	}
 
 	if (parser->msg.pool != NULL)
 		pool_unref(&parser->msg.pool);
diff -r dca140149d80 -r 572b9a9031e7 src/lib-http/http-message-parser.h
--- a/src/lib-http/http-message-parser.h	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-message-parser.h	Sun Sep 15 03:46:12 2013 +0300
@@ -4,7 +4,7 @@
 #include "http-response.h"
 #include "http-transfer.h"
 
-struct http_header;
+#include "http-header.h"
 
 struct http_message {
 	pool_t pool;
@@ -26,6 +26,7 @@
 
 struct http_message_parser {
 	struct istream *input;
+	struct http_header_limits header_limits;
 
 	const unsigned char *cur, *end;
 
@@ -37,7 +38,8 @@
 };
 
 void http_message_parser_init(struct http_message_parser *parser,
-			      struct istream *input);
+	struct istream *input, const struct http_header_limits *hdr_limits)
+	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 dca140149d80 -r 572b9a9031e7 src/lib-http/http-request-parser.c
--- a/src/lib-http/http-request-parser.c	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-request-parser.c	Sun Sep 15 03:46:12 2013 +0300
@@ -29,12 +29,14 @@
 	unsigned int skipping_line:1;
 };
 
-struct http_request_parser *http_request_parser_init(struct istream *input)
+struct http_request_parser *
+http_request_parser_init(struct istream *input,
+	const struct http_header_limits *hdr_limits)
 {
 	struct http_request_parser *parser;
 
 	parser = i_new(struct http_request_parser, 1);
-	http_message_parser_init(&parser->parser, input);
+	http_message_parser_init(&parser->parser, input, hdr_limits);
 	return parser;
 }
 
diff -r dca140149d80 -r 572b9a9031e7 src/lib-http/http-request-parser.h
--- a/src/lib-http/http-request-parser.h	Sun Sep 15 03:44:42 2013 +0300
+++ b/src/lib-http/http-request-parser.h	Sun Sep 15 03:46:12 2013 +0300
@@ -4,7 +4,8 @@
 #include "http-request.h"
 


More information about the dovecot-cvs mailing list