diff --git a/NEWS b/NEWS index fe5f504c..2c2b7c04 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ NEWS * detect "x-gzip"/"x-bzip2" as separate encodings, more strict encoding matching (fixes #2443) * tests: make sure mod_proxy doesn't leave running processes (fixes #2435, thx kibi) * mod_extforward: log address of untrusted proxy with debug.log-request-handling + * fix DoS in Connection header value split (reported by Jesse Sipprell, CVE-2012-5533) - 1.4.31 - 2012-05-31 * [ssl] fix segfault in counting renegotiations for openssl versions without TLSEXT/SNI (thx carpii for reporting) diff --git a/src/request.c b/src/request.c index de1a7826..0d749e68 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; }