Blame SOURCES/squid-4.11-CVE-2020-15811.patch

08f08e
diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc
08f08e
index dc6e0ff..67543a4 100644
08f08e
--- a/src/HttpHeader.cc
08f08e
+++ b/src/HttpHeader.cc
08f08e
@@ -174,6 +174,7 @@ HttpHeader::operator =(const HttpHeader &other)
08f08e
         update(&other); // will update the mask as well
08f08e
         len = other.len;
08f08e
         conflictingContentLength_ = other.conflictingContentLength_;
08f08e
+        teUnsupported_ = other.teUnsupported_;
08f08e
     }
08f08e
     return *this;
08f08e
 }
08f08e
@@ -222,6 +223,7 @@ HttpHeader::clean()
08f08e
     httpHeaderMaskInit(&mask, 0);
08f08e
     len = 0;
08f08e
     conflictingContentLength_ = false;
08f08e
+    teUnsupported_ = false;
08f08e
     PROF_stop(HttpHeaderClean);
08f08e
 }
08f08e
 
08f08e
@@ -464,11 +466,23 @@ HttpHeader::parse(const char *header_start, size_t hdrLen)
08f08e
                Raw("header", header_start, hdrLen));
08f08e
     }
08f08e
 
08f08e
-    if (chunked()) {
08f08e
+    String rawTe;
08f08e
+    if (getByIdIfPresent(Http::HdrType::TRANSFER_ENCODING, &rawTe)) {
08f08e
         // RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
08f08e
         // RFC 7230 section 3.3.3 #3: Transfer-Encoding overwrites Content-Length
08f08e
         delById(Http::HdrType::CONTENT_LENGTH);
08f08e
         // and clen state becomes irrelevant
08f08e
+
08f08e
+        if (rawTe == "chunked") {
08f08e
+            ; // leave header present for chunked() method
08f08e
+        } else if (rawTe == "identity") { // deprecated. no coding
08f08e
+            delById(Http::HdrType::TRANSFER_ENCODING);
08f08e
+        } else {
08f08e
+            // This also rejects multiple encodings until we support them properly.
08f08e
+            debugs(55, warnOnError, "WARNING: unsupported Transfer-Encoding used by client: " << rawTe);
08f08e
+            teUnsupported_ = true;
08f08e
+        }
08f08e
+
08f08e
     } else if (clen.sawBad) {
08f08e
         // ensure our callers do not accidentally see bad Content-Length values
08f08e
         delById(Http::HdrType::CONTENT_LENGTH);
08f08e
diff --git a/src/HttpHeader.h b/src/HttpHeader.h
08f08e
index e3553a4..64f294a 100644
08f08e
--- a/src/HttpHeader.h
08f08e
+++ b/src/HttpHeader.h
08f08e
@@ -140,7 +140,13 @@ public:
08f08e
     int hasListMember(Http::HdrType id, const char *member, const char separator) const;
08f08e
     int hasByNameListMember(const char *name, const char *member, const char separator) const;
08f08e
     void removeHopByHopEntries();
08f08e
-    inline bool chunked() const; ///< whether message uses chunked Transfer-Encoding
08f08e
+
08f08e
+    /// whether the message uses chunked Transfer-Encoding
08f08e
+    /// optimized implementation relies on us rejecting/removing other codings
08f08e
+    bool chunked() const { return has(Http::HdrType::TRANSFER_ENCODING); }
08f08e
+
08f08e
+    /// whether message used an unsupported and/or invalid Transfer-Encoding
08f08e
+    bool unsupportedTe() const { return teUnsupported_; }
08f08e
 
08f08e
     /* protected, do not use these, use interface functions instead */
08f08e
     std::vector<HttpHeaderEntry *> entries;     /**< parsed fields in raw format */
08f08e
@@ -158,6 +164,9 @@ protected:
08f08e
 private:
08f08e
     HttpHeaderEntry *findLastEntry(Http::HdrType id) const;
08f08e
     bool conflictingContentLength_; ///< found different Content-Length fields
08f08e
+    /// unsupported encoding, unnecessary syntax characters, and/or
08f08e
+    /// invalid field-value found in Transfer-Encoding header
08f08e
+    bool teUnsupported_ = false;
08f08e
 };
08f08e
 
08f08e
 int httpHeaderParseQuotedString(const char *start, const int len, String *val);
08f08e
@@ -167,13 +176,6 @@ SBuf httpHeaderQuoteString(const char *raw);
08f08e
 
08f08e
 void httpHeaderCalcMask(HttpHeaderMask * mask, Http::HdrType http_hdr_type_enums[], size_t count);
08f08e
 
08f08e
-inline bool
08f08e
-HttpHeader::chunked() const
08f08e
-{
08f08e
-    return has(Http::HdrType::TRANSFER_ENCODING) &&
08f08e
-           hasListMember(Http::HdrType::TRANSFER_ENCODING, "chunked", ',');
08f08e
-}
08f08e
-
08f08e
 void httpHeaderInitModule(void);
08f08e
 
08f08e
 #endif /* SQUID_HTTPHEADER_H */
08f08e
diff --git a/src/client_side.cc b/src/client_side.cc
08f08e
index 5f5a79e..000a00b 100644
08f08e
--- a/src/client_side.cc
08f08e
+++ b/src/client_side.cc
08f08e
@@ -1600,9 +1600,7 @@ void
08f08e
 clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
08f08e
 {
08f08e
     ClientHttpRequest *http = context->http;
08f08e
-    bool chunked = false;
08f08e
     bool mustReplyToOptions = false;
08f08e
-    bool unsupportedTe = false;
08f08e
     bool expectBody = false;
08f08e
 
08f08e
     // We already have the request parsed and checked, so we
08f08e
@@ -1659,13 +1657,7 @@ clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp,
08f08e
         request->http_ver.minor = http_ver.minor;
08f08e
     }
08f08e
 
08f08e
-    if (request->header.chunked()) {
08f08e
-        chunked = true;
08f08e
-    } else if (request->header.has(Http::HdrType::TRANSFER_ENCODING)) {
08f08e
-        const String te = request->header.getList(Http::HdrType::TRANSFER_ENCODING);
08f08e
-        // HTTP/1.1 requires chunking to be the last encoding if there is one
08f08e
-        unsupportedTe = te.size() && te != "identity";
08f08e
-    } // else implied identity coding
08f08e
+    const auto unsupportedTe = request->header.unsupportedTe();
08f08e
 
08f08e
     mustReplyToOptions = (request->method == Http::METHOD_OPTIONS) &&
08f08e
                          (request->header.getInt64(Http::HdrType::MAX_FORWARDS) == 0);
08f08e
@@ -1682,6 +1674,7 @@ clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp,
08f08e
         return;
08f08e
     }
08f08e
 
08f08e
+    const auto chunked = request->header.chunked();
08f08e
     if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
08f08e
         clientStreamNode *node = context->getClientReplyContext();
08f08e
         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
08f08e
diff --git a/src/http.cc b/src/http.cc
08f08e
index 9654c4a..6f4d3b2 100644
08f08e
--- a/src/http.cc
08f08e
+++ b/src/http.cc
08f08e
@@ -1292,6 +1292,9 @@ HttpStateData::continueAfterParsingHeader()
08f08e
             } else if (vrep->header.conflictingContentLength()) {
08f08e
                 fwd->dontRetry(true);
08f08e
                 error = ERR_INVALID_RESP;
08f08e
+            } else if (vrep->header.unsupportedTe()) {
08f08e
+                fwd->dontRetry(true);
08f08e
+                error = ERR_INVALID_RESP;
08f08e
             } else {
08f08e
                 return true; // done parsing, got reply, and no error
08f08e
             }