[packages/lighttpd] use-after-free header unfolding fix
glen
glen at pld-linux.org
Mon Aug 27 09:44:31 CEST 2018
commit f5fbcdd0f12f01f0a9d849079c407ef5cf45775d
Author: Elan Ruusamäe <glen at pld-linux.org>
Date: Mon Aug 27 10:37:26 2018 +0300
use-after-free header unfolding fix
a9e131..df8e4f.patch | 976 +++++++++++++++++++++++++++++++++++++++++++++++++++
lighttpd.spec | 4 +-
2 files changed, 979 insertions(+), 1 deletion(-)
---
diff --git a/lighttpd.spec b/lighttpd.spec
index dc77eff..5ac2ff0 100644
--- a/lighttpd.spec
+++ b/lighttpd.spec
@@ -42,7 +42,7 @@ Summary: Fast and light HTTP server
Summary(pl.UTF-8): Szybki i lekki serwer HTTP
Name: lighttpd
Version: 1.4.50
-Release: 1
+Release: 2
License: BSD
Group: Networking/Daemons/HTTP
Source0: https://download.lighttpd.net/lighttpd/releases-1.4.x/%{name}-%{version}.tar.xz
@@ -120,6 +120,7 @@ Patch2: %{name}-mod_h264_streaming.patch
Patch3: %{name}-branding.patch
Patch4: systemd.patch
Patch5: test-port-setup.patch
+Patch6: a9e131..df8e4f.patch
URL: https://www.lighttpd.net/
%{?with_geoip:BuildRequires: GeoIP-devel}
%{?with_xattr:BuildRequires: attr-devel}
@@ -952,6 +953,7 @@ Plik monitrc do monitorowania serwera www lighttpd.
%patch3 -p1
%patch4 -p1
%patch5 -p1
+%patch6 -p1
rm -f src/mod_ssi_exprparser.h # bad patching: should be removed by is emptied instead
diff --git a/a9e131..df8e4f.patch b/a9e131..df8e4f.patch
new file mode 100644
index 0000000..12ea77c
--- /dev/null
+++ b/a9e131..df8e4f.patch
@@ -0,0 +1,976 @@
+From: =?UTF-8?Q?Stefan_B=c3=bchler?= <stbuehler at web.de>
+To: lighttpd-announce at lists.lighttpd.net
+Message-ID: <8cbf0c87-1037-8f70-aa24-b2f14c95170d at web.de>
+Date: Sun, 26 Aug 2018 19:06:30 +0200
+Subject: use-after-free bug in header folding
+
+Hi,
+
+Or Peles reported some use-after-free scenarios related to header
+folding (continuing a header value on a whitespace indented line).
+
+The fix required some preparation and therefor consists of a series of 4
+patches, see git:
+
+https://git.lighttpd.net/lighttpd/lighttpd1.4.git/log/?qt=range&q=a9e131..df8e4f&showmsg=1
+
+There are currently no plans for a new release.
+
+cheers,
+Stefan
+
+diff --git a/src/request.c b/src/request.c
+index 213a87e1..e94d8591 100644
+--- a/src/request.c
++++ b/src/request.c
+@@ -408,26 +408,179 @@ static int request_uri_is_valid_char(unsigned char c) {
+ return 1;
+ }
+
+-static int http_request_missing_CR_before_LF(server *srv, connection *con) {
++static void http_request_missing_CR_before_LF(server *srv, connection *con) {
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "s", "missing CR before LF in header -> 400");
+ log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request);
+ }
++}
+
+- con->http_status = 400;
+- con->keep_alive = 0;
+- con->response.keep_alive = 0;
++enum keep_alive_set {
++ HTTP_CONNECTION_UNSET,
++ HTTP_CONNECTION_KEEPALIVE,
++ HTTP_CONNECTION_CLOSE,
++};
++
++typedef struct {
++ enum keep_alive_set keep_alive_set;
++ char con_length_set;
++ char *reqline_host;
++ int reqline_hostlen;
++} parse_header_state;
++
++static void init_parse_header_state(parse_header_state* state) {
++ state->keep_alive_set = HTTP_CONNECTION_UNSET;
++ state->con_length_set = 0;
++ state->reqline_host = NULL;
++ state->reqline_hostlen = 0;
++}
++
++/* add a header to the list of headers; certain headers are also parsed in this state.
++ *
++ * Also might drop a header if deemed unnecessary/broken.
++ *
++ * returns 0 on error
++ */
++static int parse_single_header(server *srv, connection *con, parse_header_state *state, data_string *ds) {
++ int cmp = 0;
++
++ /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
++ if (buffer_string_is_empty(ds->value)) {
++ goto drop_header;
++ }
++
++ /* retreive values
++ *
++ *
++ * the list of options is sorted to simplify the search
++ */
++
++ if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) {
++ array *vals;
++ size_t vi;
++
++ /* split on , */
++
++ vals = srv->split_vals;
++
++ array_reset(vals);
++
++ http_request_split_value(vals, ds->value);
++
++ for (vi = 0; vi < vals->used; vi++) {
++ data_string *dsv = (data_string *)vals->data[vi];
++
++ if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("keep-alive"))) {
++ state->keep_alive_set = HTTP_CONNECTION_KEEPALIVE;
++
++ break;
++ } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) {
++ state->keep_alive_set = HTTP_CONNECTION_CLOSE;
++
++ break;
++ }
++ }
++
++ } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) {
++ char *err;
++ off_t r;
++
++ if (state->con_length_set) {
++ if (srv->srvconf.log_request_header_on_error) {
++ log_error_write(srv, __FILE__, __LINE__, "s",
++ "duplicate Content-Length-header -> 400");
++ log_error_write(srv, __FILE__, __LINE__, "Sb",
++ "request-header:\n",
++ con->request.request);
++ }
++ goto invalid_header;
++ }
++
++ r = strtoll(ds->value->ptr, &err, 10);
++
++ if (*err == '\0' && r >= 0) {
++ state->con_length_set = 1;
++ con->request.content_length = r;
++ } else {
++ log_error_write(srv, __FILE__, __LINE__, "sbs",
++ "content-length broken:", ds->value, "-> 400");
++ goto invalid_header;
++ }
++ } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Type")))) {
++ /* if dup, only the first one will survive */
++ if (!con->request.http_content_type) {
++ con->request.http_content_type = ds->value->ptr;
++ } else {
++ if (srv->srvconf.log_request_header_on_error) {
++ log_error_write(srv, __FILE__, __LINE__, "s",
++ "duplicate Content-Type-header -> 400");
++ log_error_write(srv, __FILE__, __LINE__, "Sb",
++ "request-header:\n",
++ con->request.request);
++ }
++ goto invalid_header;
++ }
++ } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) {
++ if (state->reqline_host) {
++ /* ignore all host: headers as we got the host in the request line */
++ goto drop_header;
++ } else if (!con->request.http_host) {
++ con->request.http_host = ds->value;
++ } else {
++ if (srv->srvconf.log_request_header_on_error) {
++ log_error_write(srv, __FILE__, __LINE__, "s",
++ "duplicate Host-header -> 400");
++ log_error_write(srv, __FILE__, __LINE__, "Sb",
++ "request-header:\n",
++ con->request.request);
++ }
++ goto invalid_header;
++ }
++ } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) {
++ /* Proxies sometimes send dup headers
++ * if they are the same we ignore the second
++ * if not, we raise an error */
++ if (!con->request.http_if_modified_since) {
++ con->request.http_if_modified_since = ds->value->ptr;
++ } else if (0 == strcasecmp(con->request.http_if_modified_since, ds->value->ptr)) {
++ /* ignore it if they are the same */
++ goto drop_header;
++ } else {
++ if (srv->srvconf.log_request_header_on_error) {
++ log_error_write(srv, __FILE__, __LINE__, "s",
++ "duplicate If-Modified-Since header -> 400");
++ log_error_write(srv, __FILE__, __LINE__, "Sb",
++ "request-header:\n",
++ con->request.request);
++ }
++ goto invalid_header;
++ }
++ } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) {
++ /* if dup, only the first one will survive */
++ if (!con->request.http_if_none_match) {
++ con->request.http_if_none_match = ds->value->ptr;
++ } else {
++ goto drop_header;
++ }
++ }
++
++ array_insert_unique(con->request.headers, (data_unset *)ds);
++ return 1;
++
++drop_header:
++ ds->free((data_unset *)ds);
++ return 1;
++
++invalid_header:
++ ds->free((data_unset *)ds);
+ return 0;
+ }
+
+ int http_request_parse(server *srv, connection *con) {
+- char *uri = NULL, *proto = NULL, *method = NULL, con_length_set;
++ char *uri = NULL, *proto = NULL, *method = NULL;
+ int is_key = 1, key_len = 0, is_ws_after_key = 0, in_folding;
+ char *value = NULL, *key = NULL;
+- char *reqline_host = NULL;
+- int reqline_hostlen = 0;
+-
+- enum { HTTP_CONNECTION_UNSET, HTTP_CONNECTION_KEEPALIVE, HTTP_CONNECTION_CLOSE } keep_alive_set = HTTP_CONNECTION_UNSET;
++ data_string *current_header = NULL;
+
+ int line = 0;
+
+@@ -437,6 +590,9 @@ int http_request_parse(server *srv, connection *con) {
+ int done = 0;
+ const unsigned int http_header_strict = (con->conf.http_parseopts & HTTP_PARSEOPT_HEADER_STRICT);
+
++ parse_header_state state;
++ init_parse_header_state(&state);
++
+ /*
+ * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$"
+ * Option : "^([-a-zA-Z]+): (.+)$"
+@@ -457,9 +613,7 @@ int http_request_parse(server *srv, connection *con) {
+
+ #ifdef __COVERITY__
+ if (buffer_string_length(con->request.request) < 2) {
+- con->keep_alive = 0;
+- con->http_status = 400;
+- return 0;
++ goto failure;
+ }
+ #endif
+ /* coverity[overflow_sink : FALSE] */
+@@ -467,12 +621,13 @@ int http_request_parse(server *srv, connection *con) {
+ } else if (con->request_count > 0 &&
+ con->request.request->ptr[1] == '\n') {
+ /* we are in keep-alive and might get \n after a previous POST request.*/
+- if (http_header_strict) return http_request_missing_CR_before_LF(srv, con);
++ if (http_header_strict) {
++ http_request_missing_CR_before_LF(srv, con);
++ goto failure;
++ }
+ #ifdef __COVERITY__
+ if (buffer_string_length(con->request.request) < 1) {
+- con->keep_alive = 0;
+- con->http_status = 400;
+- return 0;
++ goto failure;
+ }
+ #endif
+ /* coverity[overflow_sink : FALSE] */
+@@ -482,9 +637,6 @@ int http_request_parse(server *srv, connection *con) {
+ buffer_copy_buffer(con->parse_request, con->request.request);
+ }
+
+- keep_alive_set = 0;
+- con_length_set = 0;
+-
+ /* parse the first line of the request
+ *
+ * should be:
+@@ -510,22 +662,19 @@ int http_request_parse(server *srv, connection *con) {
+ con->parse_request->ptr[i] = '\0';
+ ++i;
+ } else if (http_header_strict) { /* '\n' */
+- return http_request_missing_CR_before_LF(srv, con);
++ http_request_missing_CR_before_LF(srv, con);
++ goto failure;
+ }
+ con->parse_request->ptr[i] = '\0';
+
+ if (request_line_stage != 2) {
+- con->http_status = 400;
+- con->response.keep_alive = 0;
+- con->keep_alive = 0;
+-
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400");
+ log_error_write(srv, __FILE__, __LINE__, "Sb",
+ "request-header:\n",
+ con->request.request);
+ }
+- return 0;
++ goto failure;
+ }
+
+ proto = con->parse_request->ptr + first;
+@@ -536,8 +685,6 @@ int http_request_parse(server *srv, connection *con) {
+ /* we got the first one :) */
+ if (HTTP_METHOD_UNSET == (r = get_http_method_key(method))) {
+ con->http_status = 501;
+- con->response.keep_alive = 0;
+- con->keep_alive = 0;
+
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501");
+@@ -546,7 +693,7 @@ int http_request_parse(server *srv, connection *con) {
+ con->request.request);
+ }
+
+- return 0;
++ goto failure;
+ }
+
+ con->request.http_method = r;
+@@ -582,16 +729,13 @@ int http_request_parse(server *srv, connection *con) {
+ }
+
+ if (invalid_version) {
+- con->http_status = 400;
+- con->keep_alive = 0;
+-
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
+ log_error_write(srv, __FILE__, __LINE__, "Sb",
+ "request-header:\n",
+ con->request.request);
+ }
+- return 0;
++ goto failure;
+ }
+
+ if (major_num == 1 && minor_num == 1) {
+@@ -607,19 +751,16 @@ int http_request_parse(server *srv, connection *con) {
+ "request-header:\n",
+ con->request.request);
+ }
+- return 0;
++ goto failure;
+ }
+ } else {
+- con->http_status = 400;
+- con->keep_alive = 0;
+-
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
+ log_error_write(srv, __FILE__, __LINE__, "Sb",
+ "request-header:\n",
+ con->request.request);
+ }
+- return 0;
++ goto failure;
+ }
+
+ if (*uri == '/') {
+@@ -627,14 +768,14 @@ int http_request_parse(server *srv, connection *con) {
+ buffer_copy_string_len(con->request.uri, uri, proto - uri - 1);
+ } else if (0 == strncasecmp(uri, "http://", 7) &&
+ NULL != (nuri = strchr(uri + 7, '/'))) {
+- reqline_host = uri + 7;
+- reqline_hostlen = nuri - reqline_host;
++ state.reqline_host = uri + 7;
++ state.reqline_hostlen = nuri - state.reqline_host;
+
+ buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
+ } else if (0 == strncasecmp(uri, "https://", 8) &&
+ NULL != (nuri = strchr(uri + 8, '/'))) {
+- reqline_host = uri + 8;
+- reqline_hostlen = nuri - reqline_host;
++ state.reqline_host = uri + 8;
++ state.reqline_hostlen = nuri - state.reqline_host;
+
+ buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
+ } else if (!http_header_strict
+@@ -643,10 +784,8 @@ int http_request_parse(server *srv, connection *con) {
+ /* everything looks good so far */
+ buffer_copy_string_len(con->request.uri, uri, proto - uri - 1);
+ } else {
+- con->http_status = 400;
+- con->keep_alive = 0;
+ log_error_write(srv, __FILE__, __LINE__, "ss", "request-URI parse error -> 400 for:", uri);
+- return 0;
++ goto failure;
+ }
+
+ /* check uri for invalid characters */
+@@ -660,33 +799,30 @@ int http_request_parse(server *srv, connection *con) {
+ j = (NULL == z) ? jlen : (size_t)(z - con->request.uri->ptr);
+ }
+ if (j < jlen) {
+- con->http_status = 400;
+- con->keep_alive = 0;
+-
+- if (srv->srvconf.log_request_header_on_error) {
+- unsigned char buf[2];
+- buf[0] = con->request.uri->ptr[j];
+- buf[1] = '\0';
+-
+- if (con->request.uri->ptr[j] > 32 &&
+- con->request.uri->ptr[j] != 127) {
+- /* the character is printable -> print it */
+- log_error_write(srv, __FILE__, __LINE__, "ss",
+- "invalid character in URI -> 400",
+- buf);
+- } else {
+- /* a control-character, print ascii-code */
+- log_error_write(srv, __FILE__, __LINE__, "sd",
+- "invalid character in URI -> 400",
+- con->request.uri->ptr[j]);
+- }
+-
+- log_error_write(srv, __FILE__, __LINE__, "Sb",
+- "request-header:\n",
+- con->request.request);
++ if (srv->srvconf.log_request_header_on_error) {
++ unsigned char buf[2];
++ buf[0] = con->request.uri->ptr[j];
++ buf[1] = '\0';
++
++ if (con->request.uri->ptr[j] > 32 &&
++ con->request.uri->ptr[j] != 127) {
++ /* the character is printable -> print it */
++ log_error_write(srv, __FILE__, __LINE__, "ss",
++ "invalid character in URI -> 400",
++ buf);
++ } else {
++ /* a control-character, print ascii-code */
++ log_error_write(srv, __FILE__, __LINE__, "sd",
++ "invalid character in URI -> 400",
++ con->request.uri->ptr[j]);
+ }
+
+- return 0;
++ log_error_write(srv, __FILE__, __LINE__, "Sb",
++ "request-header:\n",
++ con->request.request);
++ }
++
++ goto failure;
+ }
+
+ buffer_copy_buffer(con->request.orig_uri, con->request.uri);
+@@ -711,17 +847,13 @@ int http_request_parse(server *srv, connection *con) {
+ break;
+ default:
+ /* ERROR, one space to much */
+- con->http_status = 400;
+- con->response.keep_alive = 0;
+- con->keep_alive = 0;
+-
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "s", "overlong request line -> 400");
+ log_error_write(srv, __FILE__, __LINE__, "Sb",
+ "request-header:\n",
+ con->request.request);
+ }
+- return 0;
++ goto failure;
+ }
+
+ request_line_stage++;
+@@ -732,20 +864,16 @@ int http_request_parse(server *srv, connection *con) {
+ in_folding = 0;
+
+ if (buffer_string_is_empty(con->request.uri)) {
+- con->http_status = 400;
+- con->response.keep_alive = 0;
+- con->keep_alive = 0;
+-
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "s", "no uri specified -> 400");
+ log_error_write(srv, __FILE__, __LINE__, "Sb",
+ "request-header:\n",
+ con->request.request);
+ }
+- return 0;
++ goto failure;
+ }
+
+- if (reqline_host) {
++ if (state.reqline_host) {
+ /* Insert as host header */
+ data_string *ds;
+
+@@ -754,7 +882,7 @@ int http_request_parse(server *srv, connection *con) {
+ }
+
+ buffer_copy_string_len(ds->key, CONST_STR_LEN("Host"));
+- buffer_copy_string_len(ds->value, reqline_host, reqline_hostlen);
++ buffer_copy_string_len(ds->value, state.reqline_host, state.reqline_hostlen);
+ array_insert_unique(con->request.headers, (data_unset *)ds);
+ con->request.http_host = ds->value;
+ }
+@@ -799,10 +927,6 @@ int http_request_parse(server *srv, connection *con) {
+ case '=':
+ case '{':
+ case '}':
+- con->http_status = 400;
+- con->keep_alive = 0;
+- con->response.keep_alive = 0;
+-
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "sbsds",
+ "invalid character in key", con->request.request, cur, *cur, "-> 400");
+@@ -811,7 +935,7 @@ int http_request_parse(server *srv, connection *con) {
+ "request-header:\n",
+ con->request.request);
+ }
+- return 0;
++ goto failure;
+ case ' ':
+ case '\t':
+ if (i == first) {
+@@ -850,11 +974,7 @@ int http_request_parse(server *srv, connection *con) {
+ con->request.request);
+ }
+
+- con->http_status = 400;
+- con->response.keep_alive = 0;
+- con->keep_alive = 0;
+-
+- return 0;
++ goto failure;
+ }
+ }
+
+@@ -876,15 +996,13 @@ int http_request_parse(server *srv, connection *con) {
+ con->request.request);
+ }
+
+- con->http_status = 400;
+- con->keep_alive = 0;
+- con->response.keep_alive = 0;
+- return 0;
++ goto failure;
+ }
+ break;
+ case '\n':
+ if (http_header_strict) {
+- return http_request_missing_CR_before_LF(srv, con);
++ http_request_missing_CR_before_LF(srv, con);
++ goto failure;
+ } else if (i == first) {
+ con->parse_request->ptr[i] = '\0';
+ done = 1;
+@@ -893,10 +1011,6 @@ int http_request_parse(server *srv, connection *con) {
+ /* fall through */
+ default:
+ if (http_header_strict ? (*cur < 32 || ((unsigned char)*cur) >= 127) : *cur == '\0') {
+- con->http_status = 400;
+- con->keep_alive = 0;
+- con->response.keep_alive = 0;
+-
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "sbsds",
+ "invalid character in key", con->request.request, cur, *cur, "-> 400");
+@@ -906,7 +1020,7 @@ int http_request_parse(server *srv, connection *con) {
+ con->request.request);
+ }
+
+- return 0;
++ goto failure;
+ }
+ /* ok */
+ break;
+@@ -916,9 +1030,13 @@ int http_request_parse(server *srv, connection *con) {
+ case '\r':
+ case '\n':
+ if (*cur == '\n' || con->parse_request->ptr[i+1] == '\n') {
+- data_string *ds = NULL;
++ int value_len;
++
+ if (*cur == '\n') {
+- if (http_header_strict) return http_request_missing_CR_before_LF(srv, con);
++ if (http_header_strict) {
++ http_request_missing_CR_before_LF(srv, con);
++ goto failure;
++ }
+ } else { /* (con->parse_request->ptr[i+1] == '\n') */
+ con->parse_request->ptr[i] = '\0';
+ ++i;
+@@ -927,17 +1045,15 @@ int http_request_parse(server *srv, connection *con) {
+ /* End of Headerline */
+ con->parse_request->ptr[i] = '\0';
+
++ value_len = cur - value;
++
++ /* strip trailing white-spaces */
++ while (value_len > 0 && (value[value_len - 1] == ' ' || value[value_len - 1] == '\t')) {
++ --value_len;
++ }
++
+ if (in_folding) {
+- /**
+- * we use a evil hack to handle the line-folding
+- *
+- * As array_insert_unique() deletes 'ds' in the case of a duplicate
+- * ds points somewhere and we get a evil crash. As a solution we keep the old
+- * "key" and get the current value from the hash and append us
+- *
+- * */
+-
+- if (!key || !key_len) {
++ if (!current_header) {
+ /* 400 */
+
+ if (srv->srvconf.log_request_header_on_error) {
+@@ -948,193 +1064,35 @@ int http_request_parse(server *srv, connection *con) {
+ con->request.request);
+ }
+
+-
+- con->http_status = 400;
+- con->keep_alive = 0;
+- con->response.keep_alive = 0;
+- return 0;
++ goto failure;
+ }
+
+- if (NULL != (ds = (data_string *)array_get_element_klen(con->request.headers, key, key_len))) {
+- buffer_append_string(ds->value, value);
+- }
++ buffer_append_string_len(current_header->value, value, value_len);
+ } else {
+- int s_len;
+- key = con->parse_request->ptr + first;
+-
+- s_len = cur - value;
+-
+- /* strip trailing white-spaces */
+- for (; s_len > 0 &&
+- (value[s_len - 1] == ' ' ||
+- value[s_len - 1] == '\t'); s_len--);
+-
+- value[s_len] = '\0';
+-
+- if (s_len > 0) {
+- int cmp = 0;
+- if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
+- ds = data_string_init();
+- }
+- buffer_copy_string_len(ds->key, key, key_len);
+- buffer_copy_string_len(ds->value, value, s_len);
+-
+- /* retreive values
+- *
+- *
+- * the list of options is sorted to simplify the search
+- */
+-
+- if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) {
+- array *vals;
+- size_t vi;
+-
+- /* split on , */
+-
+- vals = srv->split_vals;
+-
+- array_reset(vals);
+-
+- http_request_split_value(vals, ds->value);
+-
+- for (vi = 0; vi < vals->used; vi++) {
+- data_string *dsv = (data_string *)vals->data[vi];
+-
+- if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("keep-alive"))) {
+- keep_alive_set = HTTP_CONNECTION_KEEPALIVE;
+-
+- break;
+- } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) {
+- keep_alive_set = HTTP_CONNECTION_CLOSE;
+-
+- break;
+- }
+- }
+-
+- } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) {
+- char *err;
+- off_t r;
+-
+- if (con_length_set) {
+- con->http_status = 400;
+- con->keep_alive = 0;
+-
+- if (srv->srvconf.log_request_header_on_error) {
+- log_error_write(srv, __FILE__, __LINE__, "s",
+- "duplicate Content-Length-header -> 400");
+- log_error_write(srv, __FILE__, __LINE__, "Sb",
+- "request-header:\n",
+- con->request.request);
+- }
+- array_insert_unique(con->request.headers, (data_unset *)ds);
+- return 0;
+- }
+-
+- r = strtoll(ds->value->ptr, &err, 10);
+-
+- if (*err == '\0' && r >= 0) {
+- con_length_set = 1;
+- con->request.content_length = r;
+- } else {
+- log_error_write(srv, __FILE__, __LINE__, "sbs",
+- "content-length broken:", ds->value, "-> 400");
+-
+- con->http_status = 400;
+- con->keep_alive = 0;
+-
+- array_insert_unique(con->request.headers, (data_unset *)ds);
+- return 0;
+- }
+- } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Type")))) {
+- /* if dup, only the first one will survive */
+- if (!con->request.http_content_type) {
+- con->request.http_content_type = ds->value->ptr;
+- } else {
+- con->http_status = 400;
+- con->keep_alive = 0;
+-
+- if (srv->srvconf.log_request_header_on_error) {
+- log_error_write(srv, __FILE__, __LINE__, "s",
+- "duplicate Content-Type-header -> 400");
+- log_error_write(srv, __FILE__, __LINE__, "Sb",
+- "request-header:\n",
+- con->request.request);
+- }
+- array_insert_unique(con->request.headers, (data_unset *)ds);
+- return 0;
+- }
+- } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) {
+- if (reqline_host) {
+- /* ignore all host: headers as we got the host in the request line */
+- ds->free((data_unset*) ds);
+- ds = NULL;
+- } else if (!con->request.http_host) {
+- con->request.http_host = ds->value;
+- } else {
+- con->http_status = 400;
+- con->keep_alive = 0;
+-
+- if (srv->srvconf.log_request_header_on_error) {
+- log_error_write(srv, __FILE__, __LINE__, "s",
+- "duplicate Host-header -> 400");
+- log_error_write(srv, __FILE__, __LINE__, "Sb",
+- "request-header:\n",
+- con->request.request);
+- }
+- array_insert_unique(con->request.headers, (data_unset *)ds);
+- return 0;
+- }
+- } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) {
+- /* Proxies sometimes send dup headers
+- * if they are the same we ignore the second
+- * if not, we raise an error */
+- if (!con->request.http_if_modified_since) {
+- con->request.http_if_modified_since = ds->value->ptr;
+- } else if (0 == strcasecmp(con->request.http_if_modified_since,
+- ds->value->ptr)) {
+- /* ignore it if they are the same */
+-
+- ds->free((data_unset *)ds);
+- ds = NULL;
+- } else {
+- con->http_status = 400;
+- con->keep_alive = 0;
+-
+- if (srv->srvconf.log_request_header_on_error) {
+- log_error_write(srv, __FILE__, __LINE__, "s",
+- "duplicate If-Modified-Since header -> 400");
+- log_error_write(srv, __FILE__, __LINE__, "Sb",
+- "request-header:\n",
+- con->request.request);
+- }
+- array_insert_unique(con->request.headers, (data_unset *)ds);
+- return 0;
+- }
+- } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) {
+- /* if dup, only the first one will survive */
+- if (!con->request.http_if_none_match) {
+- con->request.http_if_none_match = ds->value->ptr;
+- } else {
+- ds->free((data_unset*) ds);
+- ds = NULL;
+- }
++ /* process previous header */
++ if (current_header) {
++ data_string *ds = current_header;
++ current_header = NULL;
++ if (!parse_single_header(srv, con, &state, ds)) {
++ /* parse_single_header should already have logged it */
++ goto failure;
+ }
++ }
+
+- if (ds) array_insert_unique(con->request.headers, (data_unset *)ds);
+- } else {
+- /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
++ key = con->parse_request->ptr + first;
++
++ if (NULL == (current_header = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
++ current_header = data_string_init();
+ }
++
++ buffer_copy_string_len(current_header->key, key, key_len);
++ buffer_copy_string_len(current_header->value, value, value_len);
+ }
+
+ first = i+1;
+ is_key = 1;
+ value = NULL;
+-#if 0
+- /**
+- * for Bug 1230 keep the key_len a live
+- */
+ key_len = 0;
+-#endif
+ in_folding = 0;
+ } else {
+ if (srv->srvconf.log_request_header_on_error) {
+@@ -1142,10 +1100,7 @@ int http_request_parse(server *srv, connection *con) {
+ "CR without LF", con->request.request, "-> 400");
+ }
+
+- con->http_status = 400;
+- con->keep_alive = 0;
+- con->response.keep_alive = 0;
+- return 0;
++ goto failure;
+ }
+ break;
+ case ' ':
+@@ -1160,22 +1115,29 @@ int http_request_parse(server *srv, connection *con) {
+ "invalid char in header", (int)*cur, "-> 400");
+ }
+
+- con->http_status = 400;
+- con->keep_alive = 0;
+-
+- return 0;
++ goto failure;
+ }
+ break;
+ }
+ }
+ }
+
++ /* process last header */
++ if (current_header) {
++ data_string* ds = current_header;
++ current_header = NULL;
++ if (!parse_single_header(srv, con, &state, ds)) {
++ /* parse_single_header should already have logged it */
++ goto failure;
++ }
++ }
++
+ con->header_len = i;
+
+ /* do some post-processing */
+
+ if (con->request.http_version == HTTP_VERSION_1_1) {
+- if (keep_alive_set != HTTP_CONNECTION_CLOSE) {
++ if (state.keep_alive_set != HTTP_CONNECTION_CLOSE) {
+ /* no Connection-Header sent */
+
+ /* HTTP/1.1 -> keep-alive default TRUE */
+@@ -1187,9 +1149,6 @@ int http_request_parse(server *srv, connection *con) {
+ /* RFC 2616, 14.23 */
+ if (con->request.http_host == NULL ||
+ buffer_string_is_empty(con->request.http_host)) {
+- con->http_status = 400;
+- con->response.keep_alive = 0;
+- con->keep_alive = 0;
+
+ if (srv->srvconf.log_request_header_on_error) {
+ log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400");
+@@ -1197,10 +1156,10 @@ int http_request_parse(server *srv, connection *con) {
+ "request-header:\n",
+ con->request.request);
+ }
+- return 0;
++ goto failure;
+ }
+ } else {
+- if (keep_alive_set == HTTP_CONNECTION_KEEPALIVE) {
++ if (state.keep_alive_set == HTTP_CONNECTION_KEEPALIVE) {
+ /* no Connection-Header sent */
+
+ /* HTTP/1.0 -> keep-alive default FALSE */
+@@ -1222,11 +1181,7 @@ int http_request_parse(server *srv, connection *con) {
+ con->request.request);
+ }
+
+- con->http_status = 400;
+- con->response.keep_alive = 0;
+- con->keep_alive = 0;
+-
+- return 0;
++ goto failure;
+ }
+
+ {
+@@ -1235,24 +1190,21 @@ int http_request_parse(server *srv, connection *con) {
+ if (con->request.http_version == HTTP_VERSION_1_0) {
+ log_error_write(srv, __FILE__, __LINE__, "s",
+ "HTTP/1.0 with Transfer-Encoding (bad HTTP/1.0 proxy?) -> 400");
+- con->keep_alive = 0;
+- con->http_status = 400; /* Bad Request */
+- return 0;
++ goto failure;
+ }
+
+ if (0 != strcasecmp(ds->value->ptr, "chunked")) {
+ /* Transfer-Encoding might contain additional encodings,
+ * which are not currently supported by lighttpd */
+- con->keep_alive = 0;
+ con->http_status = 501; /* Not Implemented */
+- return 0;
++ goto failure;
+ }
+
+ /* reset value for Transfer-Encoding, a hop-by-hop header,
+ * which must not be blindly forwarded to backends */
+ buffer_reset(ds->value); /* headers with empty values are ignored */
+
+- con_length_set = 1;
++ state.con_length_set = 1;
+ con->request.content_length = -1;
+
+ /*(note: ignore whether or not Content-Length was provided)*/
+@@ -1265,27 +1217,23 @@ int http_request_parse(server *srv, connection *con) {
+ case HTTP_METHOD_GET:
+ case HTTP_METHOD_HEAD:
+ /* content-length is forbidden for those */
+- if (con_length_set && con->request.content_length != 0) {
++ if (state.con_length_set && con->request.content_length != 0) {
+ /* content-length is missing */
+ log_error_write(srv, __FILE__, __LINE__, "s",
+ "GET/HEAD with content-length -> 400");
+
+- con->keep_alive = 0;
+- con->http_status = 400;
+- return 0;
++ goto failure;
+ }
+ break;
+ case HTTP_METHOD_POST:
+ /* content-length is required for them */
+- if (!con_length_set) {
++ if (!state.con_length_set) {
+ /* content-length is missing */
+ log_error_write(srv, __FILE__, __LINE__, "s",
+ "POST-request, but content-length missing -> 411");
+
+- con->keep_alive = 0;
+ con->http_status = 411;
+- return 0;
+-
++ goto failure;
+ }
+ break;
+ default:
+@@ -1294,12 +1242,21 @@ int http_request_parse(server *srv, connection *con) {
+
+
+ /* check if we have read post data */
+- if (con_length_set) {
++ if (state.con_length_set) {
+ /* we have content */
+ if (con->request.content_length != 0) {
+ return 1;
+ }
+ }
+
++ return 0;
++
++failure:
++ if (current_header) current_header->free((data_unset *)current_header);
++
++ con->keep_alive = 0;
++ con->response.keep_alive = 0;
++ if (!con->http_status) con->http_status = 400;
++
+ return 0;
+ }
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/lighttpd.git/commitdiff/f5fbcdd0f12f01f0a9d849079c407ef5cf45775d
More information about the pld-cvs-commit
mailing list