File SQUID-2020_8.patch of Package squid.26147
from:
commit fd68382860633aca92065e6c343cfd1b12b126e7
Author: Amos Jeffries <yadij@users.noreply.github.com>
Date: Sun Aug 16 02:21:22 2020 +0000
Improve Transfer-Encoding handling (#702)
Reject messages containing Transfer-Encoding header with coding other
than chunked or identity. Squid does not support other codings.
For simplicity and security sake, also reject messages where
Transfer-Encoding contains unnecessary complex values that are
technically equivalent to "chunked" or "identity" (e.g., ",,chunked" or
"identity, chunked").
RFC 7230 formally deprecated and removed identity coding, but it is
still used by some agents.
Index: squid-3.5.21/src/HttpHeader.cc
===================================================================
--- squid-3.5.21.orig/src/HttpHeader.cc
+++ squid-3.5.21/src/HttpHeader.cc
@@ -469,6 +469,7 @@ HttpHeader::operator =(const HttpHeader
update(&other, NULL); // will update the mask as well
len = other.len;
conflictingContentLength_ = other.conflictingContentLength_;
+ teUnsupported_ = other.teUnsupported_;
}
return *this;
}
@@ -518,6 +519,7 @@ HttpHeader::clean()
httpHeaderMaskInit(&mask, 0);
len = 0;
conflictingContentLength_ = false;
+ teUnsupported_ = false;
PROF_stop(HttpHeaderClean);
}
@@ -723,11 +725,22 @@ HttpHeader::parse(const char *header_sta
Raw("header", header_start, header_end - header_start));
}
- if (chunked()) {
+ if (has(HDR_TRANSFER_ENCODING)) {
+ const String rawTe = getStrOrList(HDR_TRANSFER_ENCODING);
// RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
delById(HDR_CONTENT_LENGTH);
// RFC 7230 section 3.3.3 #4: ignore Content-Length conflicts with Transfer-Encoding
conflictingContentLength_ = false;
+
+ if (rawTe == "chunked") {
+ ; // leave header present for chunked() method
+ } else if (rawTe == "identity") { // deprecated. no coding
+ delById(HDR_TRANSFER_ENCODING);
+ } else {
+ // This also rejects multiple encodings until we support them properly.
+ debugs(55, warnOnError, "WARNING: unsupported Transfer-Encoding used by client: " << rawTe);
+ teUnsupported_ = true;
+ }
} else if (clen.sawBad) {
// ensure our callers do not accidentally see bad Content-Length values
delById(HDR_CONTENT_LENGTH);
Index: squid-3.5.21/src/HttpHeader.h
===================================================================
--- squid-3.5.21.orig/src/HttpHeader.h
+++ squid-3.5.21/src/HttpHeader.h
@@ -267,7 +267,13 @@ public:
int hasListMember(http_hdr_type id, const char *member, const char separator) const;
int hasByNameListMember(const char *name, const char *member, const char separator) const;
void removeHopByHopEntries();
- inline bool chunked() const; ///< whether message uses chunked Transfer-Encoding
+
+ /// whether the message uses chunked Transfer-Encoding
+ /// optimized implementation relies on us rejecting/removing other codings
+ bool chunked() const { return has(HDR_TRANSFER_ENCODING); }
+
+ /// whether message used an unsupported and/or invalid Transfer-Encoding
+ bool unsupportedTe() const { return teUnsupported_; }
/* protected, do not use these, use interface functions instead */
std::vector<HttpHeaderEntry *> entries; /**< parsed fields in raw format */
@@ -282,6 +288,9 @@ protected:
private:
HttpHeaderEntry *findLastEntry(http_hdr_type id) const;
bool conflictingContentLength_; ///< found different Content-Length fields
+ /// unsupported encoding, unnecessary syntax characters, and/or
+ /// invalid field-value found in Transfer-Encoding header
+ bool teUnsupported_ = false;
};
int httpHeaderParseQuotedString(const char *start, const int len, String *val);
@@ -293,13 +302,6 @@ int httpHeaderHasByNameListMember(const
void httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask);
void httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count);
-inline bool
-HttpHeader::chunked() const
-{
- return has(HDR_TRANSFER_ENCODING) &&
- hasListMember(HDR_TRANSFER_ENCODING, "chunked", ',');
-}
-
void httpHeaderInitModule(void);
void httpHeaderCleanModule(void);
Index: squid-3.5.21/src/client_side.cc
===================================================================
--- squid-3.5.21.orig/src/client_side.cc
+++ squid-3.5.21/src/client_side.cc
@@ -2581,9 +2581,7 @@ clientProcessRequest(ConnStateData *conn
ClientHttpRequest *http = context->http;
HttpRequest::Pointer request;
bool notedUseOfBuffer = false;
- bool chunked = false;
bool mustReplyToOptions = false;
- bool unsupportedTe = false;
bool expectBody = false;
// temporary hack to avoid splitting this huge function with sensitive code
@@ -2767,13 +2765,7 @@ clientProcessRequest(ConnStateData *conn
// TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
request->clientConnectionManager = conn;
- if (request->header.chunked()) {
- chunked = true;
- } else if (request->header.has(HDR_TRANSFER_ENCODING)) {
- const String te = request->header.getList(HDR_TRANSFER_ENCODING);
- // HTTP/1.1 requires chunking to be the last encoding if there is one
- unsupportedTe = te.size() && te != "identity";
- } // else implied identity coding
+ const auto unsupportedTe = request->header.unsupportedTe();
mustReplyToOptions = (method == Http::METHOD_OPTIONS) &&
(request->header.getInt64(HDR_MAX_FORWARDS) == 0);
@@ -2791,6 +2783,7 @@ clientProcessRequest(ConnStateData *conn
return;
}
+ const auto chunked = request->header.chunked();
if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
clientStreamNode *node = context->getClientReplyContext();
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
Index: squid-3.5.21/src/http.cc
===================================================================
--- squid-3.5.21.orig/src/http.cc
+++ squid-3.5.21/src/http.cc
@@ -1265,6 +1265,9 @@ HttpStateData::continueAfterParsingHeade
} else if (vrep->header.conflictingContentLength()) {
fwd->dontRetry(true);
error = ERR_INVALID_RESP;
+ } else if (vrep->header.unsupportedTe()) {
+ fwd->dontRetry(true);
+ error = ERR_INVALID_RESP;
} else {
return true; // done parsing, got reply, and no error
}