From 162bb2fe51552c43192799d4514c4d8b0de3265b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20B=C3=BChler?= <stbuehler@web.de>
Date: Sat, 17 Nov 2012 11:54:18 +0100
Subject: [PATCH] Fix DoS in header value split (reported by Jesse Sipprell, CVE-2012-5533)

---
 src/request.c |   75 +++++++++++++++++++++++++++++----------------------------
 1 file changed, 38 insertions(+), 37 deletions(-)

diff --git a/src/request.c b/src/request.c
index de1a782..0d749e6 100644
--- a/src/request.c
+++ b/src/request.c
@@ -209,9 +209,11 @@ static int request_check_hostname(server *srv, connection *con, buffer *host) {
 #endif
 
 static int http_request_split_value(array *vals, buffer *b) {
-	char *s;
 	size_t i;
 	int state = 0;
+
+	const char *current;
+	const char *token_start = NULL, *token_end = NULL;
 	/*
 	 * parse
 	 *
@@ -222,53 +224,52 @@ static int http_request_split_value(array *vals, buffer *b) {
 
 	if (b->used == 0) return 0;
 
-	s = b->ptr;
-
-	for (i =0; i < b->used - 1; ) {
-		char *start = NULL, *end = NULL;
+	current = b->ptr;
+	for (i =  0; i < b->used; ++i, ++current) {
 		data_string *ds;
 
 		switch (state) {
-		case 0: /* ws */
-
-			/* skip ws */
-			for (; (*s == ' ' || *s == '\t') && i < b->used - 1; i++, s++);
-
-
-			state = 1;
-			break;
-		case 1: /* value */
-			start = s;
-
-			for (; *s != ',' && i < b->used - 1; i++, s++);
-			if (start == s) break; /* empty fields are skipped */
-			end = s - 1;
-
-			for (; end > start && (*end == ' ' || *end == '\t'); end--);
-			if (start == end) break; /* empty fields are skipped */
-
-			if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) {
-				ds = data_string_init();
+		case 0: /* find start of a token */
+			switch (*current) {
+			case ' ':
+			case '\t': /* skip white space */
+			case ',': /* skip empty token */
+				break;
+			case '\0': /* end of string */
+				return 0;
+			default:
+				/* found real data, switch to state 1 to find the end of the token */
+				token_start = token_end = current;
+				state = 1;
+				break;
 			}
+			break;
+		case 1: /* find end of token and last non white space character */
+			switch (*current) {
+			case ' ':
+			case '\t':
+				/* space - don't update token_end */
+				break;
+			case ',':
+			case '\0': /* end of string also marks the end of a token */
+				if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) {
+					ds = data_string_init();
+				}
 
-			buffer_copy_string_len(ds->value, start, end-start+1);
-			array_insert_unique(vals, (data_unset *)ds);
+				buffer_copy_string_len(ds->value, token_start, token_end-token_start+1);
+				array_insert_unique(vals, (data_unset *)ds);
 
-			if (*s == ',') {
 				state = 0;
-				i++;
-				s++;
-			} else {
-				/* end of string */
-
-				state = 2;
+				break;
+			default:
+				/* no white space, update token_end to include current character */
+				token_end = current;
+				break;
 			}
 			break;
-		default:
-			i++;
-			break;
 		}
 	}
+
 	return 0;
 }
 
-- 
1.7.10.4

