use-after-free header unfolding fix auto/th/lighttpd-1.4.50-2
authorElan Ruusamäe <glen@pld-linux.org>
Mon, 27 Aug 2018 07:37:26 +0000 (10:37 +0300)
committerElan Ruusamäe <glen@pld-linux.org>
Mon, 27 Aug 2018 07:39:13 +0000 (10:39 +0300)
a9e131..df8e4f.patch [new file with mode: 0644]
lighttpd.spec

diff --git a/a9e131..df8e4f.patch b/a9e131..df8e4f.patch
new file mode 100644 (file)
index 0000000..12ea77c
--- /dev/null
@@ -0,0 +1,976 @@
+From: =?UTF-8?Q?Stefan_B=c3=bchler?= <stbuehler@web.de>
+To: lighttpd-announce@lists.lighttpd.net
+Message-ID: <8cbf0c87-1037-8f70-aa24-b2f14c95170d@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;
+ }
index dc77eff..5ac2ff0 100644 (file)
@@ -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
 
This page took 0.99593 seconds and 4 git commands to generate.