From fb08ecf008d314a6c5ba4814efbb41abb0284dbf Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Thu, 12 Dec 2019 16:22:40 -0800 Subject: [PATCH] Properly enforce max_request_header_size Previously this would not be properly enforced if the request was passed to waitress in one go, and the request headers could be larger than the administrator had allowed. --- waitress/parser.py | 48 ++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/waitress/parser.py b/waitress/parser.py index 17994cd5..945e6808 100644 --- a/waitress/parser.py +++ b/waitress/parser.py @@ -75,16 +75,41 @@ def received(self, data): """ if self.completed: return 0 # Can't consume any more. + datalen = len(data) br = self.body_rcv if br is None: # In header. + max_header = self.adj.max_request_header_size + s = self.header_plus + data index = find_double_newline(s) + consumed = 0 + + if index >= 0: + # If the headers have ended, and we also have part of the body + # message in data we still want to validate we aren't going + # over our limit for received headers. + self.header_bytes_received += index + consumed = datalen - (len(s) - index) + else: + self.header_bytes_received += datalen + consumed = datalen + + # If the first line + headers is over the max length, we return a + # RequestHeaderFieldsTooLarge error rather than continuing to + # attempt to parse the headers. + if self.header_bytes_received >= max_header: + self.parse_header(b"GET / HTTP/1.0\r\n") + self.error = RequestHeaderFieldsTooLarge( + "exceeds max_header of %s" % max_header + ) + self.completed = True + return consumed + if index >= 0: # Header finished. header_plus = s[:index] - consumed = len(data) - (len(s) - index) # Remove preceeding blank lines. This is suggested by # https://tools.ietf.org/html/rfc7230#section-3.5 to support @@ -117,22 +142,10 @@ def received(self, data): self.completed = True self.headers_finished = True return consumed - else: - # Header not finished yet. - self.header_bytes_received += datalen - max_header = self.adj.max_request_header_size - if self.header_bytes_received >= max_header: - # malformed header, we need to construct some request - # on our own. we disregard the incoming(?) requests HTTP - # version and just use 1.0. IOW someone just sent garbage - # over the wire - self.parse_header(b"GET / HTTP/1.0\n") - self.error = RequestHeaderFieldsTooLarge( - "exceeds max_header of %s" % max_header - ) - self.completed = True - self.header_plus = s - return datalen + + # Header not finished yet. + self.header_plus = s + return datalen else: # In body. consumed = br.received(data) @@ -157,6 +170,7 @@ def received(self, data): # appear to the client to be an entirely non-chunked HTTP # request with a valid content-length. self.headers["CONTENT_LENGTH"] = str(br.__len__()) + return consumed def parse_header(self, header_plus):