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

07d5a6
diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc
07d5a6
index ce55a6f..6ce06f2 100644
07d5a6
--- a/src/HttpHeader.cc
07d5a6
+++ b/src/HttpHeader.cc
07d5a6
@@ -470,6 +470,7 @@ HttpHeader::operator =(const HttpHeader &other)
07d5a6
         update(&other, NULL); // will update the mask as well
07d5a6
         len = other.len;
07d5a6
         conflictingContentLength_ = other.conflictingContentLength_;
07d5a6
+        teUnsupported_ = other.teUnsupported_;
07d5a6
     }
07d5a6
     return *this;
07d5a6
 }
07d5a6
@@ -519,6 +520,7 @@ HttpHeader::clean()
07d5a6
     httpHeaderMaskInit(&mask, 0);
07d5a6
     len = 0;
07d5a6
     conflictingContentLength_ = false;
07d5a6
+    teUnsupported_ = false;
07d5a6
     PROF_stop(HttpHeaderClean);
07d5a6
 }
07d5a6
 
07d5a6
@@ -717,12 +719,24 @@ HttpHeader::parse(const char *header_start, const char *header_end)
07d5a6
                Raw("header", header_start, (size_t)(header_end - header_start)));
07d5a6
     }
07d5a6
 
07d5a6
-    if (chunked()) {
07d5a6
+
07d5a6
+
07d5a6
+    String rawTe;
07d5a6
+    if (getByIdIfPresent(HDR_TRANSFER_ENCODING, &rawTe)) {
07d5a6
         // RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
07d5a6
         // RFC 7230 section 3.3.3 #3: Transfer-Encoding overwrites Content-Length
07d5a6
         delById(HDR_CONTENT_LENGTH);
07d5a6
-
07d5a6
         // and clen state becomes irrelevant
07d5a6
+
07d5a6
+        if (rawTe == "chunked") {
07d5a6
+            ; // leave header present for chunked() method
07d5a6
+        } else if (rawTe == "identity") { // deprecated. no coding
07d5a6
+            delById(HDR_TRANSFER_ENCODING);
07d5a6
+        } else {
07d5a6
+            // This also rejects multiple encodings until we support them properly.
07d5a6
+            debugs(55, warnOnError, "WARNING: unsupported Transfer-Encoding used by client: " << rawTe);
07d5a6
+            teUnsupported_ = true;
07d5a6
+        }
07d5a6
     } else if (clen.sawBad) {
07d5a6
         // ensure our callers do not accidentally see bad Content-Length values
07d5a6
         delById(HDR_CONTENT_LENGTH);
07d5a6
@@ -1084,6 +1098,18 @@ HttpHeader::getStrOrList(http_hdr_type id) const
07d5a6
     return String();
07d5a6
 }
07d5a6
 
07d5a6
+bool
07d5a6
+HttpHeader::getByIdIfPresent(http_hdr_type id, String *result) const
07d5a6
+{
07d5a6
+    if (id == HDR_BAD_HDR)
07d5a6
+        return false;
07d5a6
+    if (!has(id))
07d5a6
+        return false;
07d5a6
+    if (result)
07d5a6
+        *result = getStrOrList(id);
07d5a6
+    return true;
07d5a6
+}
07d5a6
+
07d5a6
 /*
07d5a6
  * Returns the value of the specified header and/or an undefined String.
07d5a6
  */
07d5a6
diff --git a/src/HttpHeader.h b/src/HttpHeader.h
07d5a6
index 836a26f..c49b105 100644
07d5a6
--- a/src/HttpHeader.h
07d5a6
+++ b/src/HttpHeader.h
07d5a6
@@ -239,6 +239,9 @@ public:
07d5a6
     bool getByNameIfPresent(const char *name, String &value) const;
07d5a6
     String getByNameListMember(const char *name, const char *member, const char separator) const;
07d5a6
     String getListMember(http_hdr_type id, const char *member, const char separator) const;
07d5a6
+    /// returns true iff a [possibly empty] field identified by id is there
07d5a6
+    /// when returning true, also sets the `result` parameter (if it is not nil)
07d5a6
+    bool getByIdIfPresent(http_hdr_type id, String *result) const;
07d5a6
     int has(http_hdr_type id) const;
07d5a6
     void putInt(http_hdr_type id, int number);
07d5a6
     void putInt64(http_hdr_type id, int64_t number);
07d5a6
@@ -267,7 +270,13 @@ public:
07d5a6
     int hasListMember(http_hdr_type id, const char *member, const char separator) const;
07d5a6
     int hasByNameListMember(const char *name, const char *member, const char separator) const;
07d5a6
     void removeHopByHopEntries();
07d5a6
-    inline bool chunked() const; ///< whether message uses chunked Transfer-Encoding
07d5a6
+
07d5a6
+    /// whether the message uses chunked Transfer-Encoding
07d5a6
+    /// optimized implementation relies on us rejecting/removing other codings
07d5a6
+    bool chunked() const { return has(HDR_TRANSFER_ENCODING); }
07d5a6
+
07d5a6
+    /// whether message used an unsupported and/or invalid Transfer-Encoding
07d5a6
+    bool unsupportedTe() const { return teUnsupported_; }
07d5a6
 
07d5a6
     /* protected, do not use these, use interface functions instead */
07d5a6
     std::vector<HttpHeaderEntry *> entries;     /**< parsed fields in raw format */
07d5a6
@@ -282,6 +291,9 @@ protected:
07d5a6
 private:
07d5a6
     HttpHeaderEntry *findLastEntry(http_hdr_type id) const;
07d5a6
     bool conflictingContentLength_; ///< found different Content-Length fields
07d5a6
+    /// unsupported encoding, unnecessary syntax characters, and/or
07d5a6
+    /// invalid field-value found in Transfer-Encoding header
07d5a6
+    bool teUnsupported_ = false;
07d5a6
 };
07d5a6
 
07d5a6
 int httpHeaderParseQuotedString(const char *start, const int len, String *val);
07d5a6
@@ -293,13 +305,6 @@ int httpHeaderHasByNameListMember(const HttpHeader * hdr, const char *name, cons
07d5a6
 void httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask);
07d5a6
 void httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count);
07d5a6
 
07d5a6
-inline bool
07d5a6
-HttpHeader::chunked() const
07d5a6
-{
07d5a6
-    return has(HDR_TRANSFER_ENCODING) &&
07d5a6
-           hasListMember(HDR_TRANSFER_ENCODING, "chunked", ',');
07d5a6
-}
07d5a6
-
07d5a6
 void httpHeaderInitModule(void);
07d5a6
 void httpHeaderCleanModule(void);
07d5a6
 
07d5a6
diff --git a/src/client_side.cc b/src/client_side.cc
07d5a6
index 261abdf..6858eb4 100644
07d5a6
--- a/src/client_side.cc
07d5a6
+++ b/src/client_side.cc
07d5a6
@@ -2581,9 +2581,7 @@ clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *c
07d5a6
     ClientHttpRequest *http = context->http;
07d5a6
     HttpRequest::Pointer request;
07d5a6
     bool notedUseOfBuffer = false;
07d5a6
-    bool chunked = false;
07d5a6
     bool mustReplyToOptions = false;
07d5a6
-    bool unsupportedTe = false;
07d5a6
     bool expectBody = false;
07d5a6
 
07d5a6
     // temporary hack to avoid splitting this huge function with sensitive code
07d5a6
@@ -2767,13 +2765,7 @@ clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *c
07d5a6
     // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
07d5a6
     request->clientConnectionManager = conn;
07d5a6
 
07d5a6
-    if (request->header.chunked()) {
07d5a6
-        chunked = true;
07d5a6
-    } else if (request->header.has(HDR_TRANSFER_ENCODING)) {
07d5a6
-        const String te = request->header.getList(HDR_TRANSFER_ENCODING);
07d5a6
-        // HTTP/1.1 requires chunking to be the last encoding if there is one
07d5a6
-        unsupportedTe = te.size() && te != "identity";
07d5a6
-    } // else implied identity coding
07d5a6
+    const auto unsupportedTe = request->header.unsupportedTe();
07d5a6
 
07d5a6
     mustReplyToOptions = (method == Http::METHOD_OPTIONS) &&
07d5a6
                          (request->header.getInt64(HDR_MAX_FORWARDS) == 0);
07d5a6
@@ -2791,6 +2783,7 @@ clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *c
07d5a6
         return;
07d5a6
     }
07d5a6
 
07d5a6
+    const auto chunked = request->header.chunked();
07d5a6
     if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
07d5a6
         clientStreamNode *node = context->getClientReplyContext();
07d5a6
         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
07d5a6
diff --git a/src/http.cc b/src/http.cc
07d5a6
index 08531dc..f0fe648 100644
07d5a6
--- a/src/http.cc
07d5a6
+++ b/src/http.cc
07d5a6
@@ -1296,6 +1296,9 @@ HttpStateData::continueAfterParsingHeader()
07d5a6
             } else if (vrep->header.conflictingContentLength()) {
07d5a6
                 fwd->dontRetry(true);
07d5a6
                 error = ERR_INVALID_RESP;
07d5a6
+            } else if (vrep->header.unsupportedTe()) {
07d5a6
+                fwd->dontRetry(true);
07d5a6
+                error = ERR_INVALID_RESP;
07d5a6
             } else {
07d5a6
                 return true; // done parsing, got reply, and no error
07d5a6
             }