fix CVE-2020-14058,CVE-2020-15049,CVE-2020-15810,CVE-2020-15811,CVE-2020-24606

This commit is contained in:
guoxiaoqi 2021-03-17 14:04:11 +08:00
parent 85aac88a36
commit 9311c7ccaa
6 changed files with 670 additions and 1 deletions

295
CVE-2020-14058.patch Normal file
View File

@ -0,0 +1,295 @@
commit 93f5fda134a2a010b84ffedbe833d670e63ba4be
Author: Christos Tsantilas <christos@chtsanti.net>
Date: 2020-05-15 04:54:54 +0000
Fix sending of unknown validation errors to cert. validator (#633)
Squid may be compiled with an OpenSSL release introducing X509
validation errors that Squid does not have the names for. Send their
integer codes.
Also sync Squid certificate verification errors with OpenSSL v1.1.1g.
This is a Measurement Factory project.
diff --git a/src/format/Format.cc b/src/format/Format.cc
index 8c5574b..4b4ad42 100644
--- a/src/format/Format.cc
+++ b/src/format/Format.cc
@@ -322,15 +322,6 @@ log_quoted_string(const char *str, char *out)
*p = '\0';
}
-#if USE_OPENSSL
-static char *
-sslErrorName(Security::ErrorCode err, char *buf, size_t size)
-{
- snprintf(buf, size, "SSL_ERR=%d", err);
- return buf;
-}
-#endif
-
/// XXX: Misnamed. TODO: Split <h (and this function) to distinguish received
/// headers from sent headers rather than failing to distinguish requests from responses.
/// \retval HttpReply sent to the HTTP client (access.log and default context).
@@ -959,9 +950,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
case LFT_SQUID_ERROR_DETAIL:
#if USE_OPENSSL
if (al->request && al->request->errType == ERR_SECURE_CONNECT_FAIL) {
- out = Ssl::GetErrorName(al->request->errDetail);
- if (!out)
- out = sslErrorName(al->request->errDetail, tmp, sizeof(tmp));
+ out = Ssl::GetErrorName(al->request->errDetail, true);
} else
#endif
if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
@@ -1263,10 +1252,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
if (!sb.isEmpty())
sb.append(separator);
- if (const char *errorName = Ssl::GetErrorName(sslError->element.code))
- sb.append(errorName);
- else
- sb.append(sslErrorName(sslError->element.code, tmp, sizeof(tmp)));
+ sb.append(Ssl::GetErrorName(sslError->element.code, true));
if (sslError->element.depth >= 0)
sb.appendf("@depth=%d", sslError->element.depth);
}
diff --git a/src/ssl/ErrorDetail.cc b/src/ssl/ErrorDetail.cc
index ddd61fd..00eb0e2 100644
--- a/src/ssl/ErrorDetail.cc
+++ b/src/ssl/ErrorDetail.cc
@@ -233,6 +233,9 @@ static SslErrorEntry TheSslErrorArray[] = {
"X509_V_ERR_SUBTREE_MINMAX"
},
#endif
+ { X509_V_ERR_APPLICATION_VERIFICATION, //50
+ "X509_V_ERR_APPLICATION_VERIFICATION"
+ },
#if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE)
{
X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, //51
@@ -257,9 +260,132 @@ static SslErrorEntry TheSslErrorArray[] = {
"X509_V_ERR_CRL_PATH_VALIDATION_ERROR"
},
#endif
- { X509_V_ERR_APPLICATION_VERIFICATION,
- "X509_V_ERR_APPLICATION_VERIFICATION"
+#if defined(X509_V_ERR_PATH_LOOP)
+ {
+ X509_V_ERR_PATH_LOOP, //55
+ "X509_V_ERR_PATH_LOOP"
+ },
+#endif
+#if defined(X509_V_ERR_SUITE_B_INVALID_VERSION)
+ {
+ X509_V_ERR_SUITE_B_INVALID_VERSION, //56
+ "X509_V_ERR_SUITE_B_INVALID_VERSION"
+ },
+#endif
+#if defined(X509_V_ERR_SUITE_B_INVALID_ALGORITHM)
+ {
+ X509_V_ERR_SUITE_B_INVALID_ALGORITHM, //57
+ "X509_V_ERR_SUITE_B_INVALID_ALGORITHM"
+ },
+#endif
+#if defined(X509_V_ERR_SUITE_B_INVALID_CURVE)
+ {
+ X509_V_ERR_SUITE_B_INVALID_CURVE, //58
+ "X509_V_ERR_SUITE_B_INVALID_CURVE"
+ },
+#endif
+#if defined(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM)
+ {
+ X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM, //59
+ "X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM"
+ },
+#endif
+#if defined(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED)
+ {
+ X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED, //60
+ "X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED"
+ },
+#endif
+#if defined(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256)
+ {
+ X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256, //61
+ "X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256"
+ },
+#endif
+#if defined(X509_V_ERR_HOSTNAME_MISMATCH)
+ {
+ X509_V_ERR_HOSTNAME_MISMATCH, //62
+ "X509_V_ERR_HOSTNAME_MISMATCH"
+ },
+#endif
+#if defined(X509_V_ERR_EMAIL_MISMATCH)
+ {
+ X509_V_ERR_EMAIL_MISMATCH, //63
+ "X509_V_ERR_EMAIL_MISMATCH"
+ },
+#endif
+#if defined(X509_V_ERR_IP_ADDRESS_MISMATCH)
+ {
+ X509_V_ERR_IP_ADDRESS_MISMATCH, //64
+ "X509_V_ERR_IP_ADDRESS_MISMATCH"
+ },
+#endif
+#if defined(X509_V_ERR_DANE_NO_MATCH)
+ {
+ X509_V_ERR_DANE_NO_MATCH, //65
+ "X509_V_ERR_DANE_NO_MATCH"
},
+#endif
+#if defined(X509_V_ERR_EE_KEY_TOO_SMALL)
+ {
+ X509_V_ERR_EE_KEY_TOO_SMALL, //66
+ "X509_V_ERR_EE_KEY_TOO_SMALL"
+ },
+#endif
+#if defined(X509_V_ERR_CA_KEY_TOO_SMALL)
+ {
+ X509_V_ERR_CA_KEY_TOO_SMALL, //67
+ "X509_V_ERR_CA_KEY_TOO_SMALL"
+ },
+#endif
+#if defined(X509_V_ERR_CA_MD_TOO_WEAK)
+ {
+ X509_V_ERR_CA_MD_TOO_WEAK, //68
+ "X509_V_ERR_CA_MD_TOO_WEAK"
+ },
+#endif
+#if defined(X509_V_ERR_INVALID_CALL)
+ {
+ X509_V_ERR_INVALID_CALL, //69
+ "X509_V_ERR_INVALID_CALL"
+ },
+#endif
+#if defined(X509_V_ERR_STORE_LOOKUP)
+ {
+ X509_V_ERR_STORE_LOOKUP, //70
+ "X509_V_ERR_STORE_LOOKUP"
+ },
+#endif
+#if defined(X509_V_ERR_NO_VALID_SCTS)
+ {
+ X509_V_ERR_NO_VALID_SCTS, //71
+ "X509_V_ERR_NO_VALID_SCTS"
+ },
+#endif
+#if defined(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION)
+ {
+ X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION, //72
+ "X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION"
+ },
+#endif
+#if defined(X509_V_ERR_OCSP_VERIFY_NEEDED)
+ {
+ X509_V_ERR_OCSP_VERIFY_NEEDED, //73
+ "X509_V_ERR_OCSP_VERIFY_NEEDED"
+ },
+#endif
+#if defined(X509_V_ERR_OCSP_VERIFY_FAILED)
+ {
+ X509_V_ERR_OCSP_VERIFY_FAILED, //74
+ "X509_V_ERR_OCSP_VERIFY_FAILED"
+ },
+#endif
+#if defined(X509_V_ERR_OCSP_CERT_UNKNOWN)
+ {
+ X509_V_ERR_OCSP_CERT_UNKNOWN, //75
+ "X509_V_ERR_OCSP_CERT_UNKNOWN"
+ },
+#endif
{ SSL_ERROR_NONE, "SSL_ERROR_NONE"},
{SSL_ERROR_NONE, NULL}
};
@@ -286,6 +412,27 @@ static const char *OptionalSslErrors[] = {
"X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX",
"X509_V_ERR_UNSUPPORTED_NAME_SYNTAX",
"X509_V_ERR_CRL_PATH_VALIDATION_ERROR",
+ "X509_V_ERR_PATH_LOOP",
+ "X509_V_ERR_SUITE_B_INVALID_VERSION",
+ "X509_V_ERR_SUITE_B_INVALID_ALGORITHM",
+ "X509_V_ERR_SUITE_B_INVALID_CURVE",
+ "X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM",
+ "X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED",
+ "X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256",
+ "X509_V_ERR_HOSTNAME_MISMATCH",
+ "X509_V_ERR_EMAIL_MISMATCH",
+ "X509_V_ERR_IP_ADDRESS_MISMATCH",
+ "X509_V_ERR_DANE_NO_MATCH",
+ "X509_V_ERR_EE_KEY_TOO_SMALL",
+ "X509_V_ERR_CA_KEY_TOO_SMALL",
+ "X509_V_ERR_CA_MD_TOO_WEAK",
+ "X509_V_ERR_INVALID_CALL",
+ "X509_V_ERR_STORE_LOOKUP",
+ "X509_V_ERR_NO_VALID_SCTS",
+ "X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION",
+ "X509_V_ERR_OCSP_VERIFY_NEEDED",
+ "X509_V_ERR_OCSP_VERIFY_FAILED",
+ "X509_V_ERR_OCSP_CERT_UNKNOWN",
NULL
};
@@ -390,7 +537,7 @@ Ssl::ParseErrorString(const char *name, Security::Errors &errors)
return false; // not reached
}
-const char *Ssl::GetErrorName(Security::ErrorCode value)
+const char *Ssl::GetErrorName(Security::ErrorCode value, const bool prefixRawCode)
{
if (TheSslErrors.empty())
loadSslErrorMap();
@@ -399,7 +546,9 @@ const char *Ssl::GetErrorName(Security::ErrorCode value)
if (it != TheSslErrors.end())
return it->second->name;
- return NULL;
+ static char tmpBuffer[128];
+ snprintf(tmpBuffer, sizeof(tmpBuffer), "%s%d", prefixRawCode ? "SSL_ERR=" : "", (int)value);
+ return tmpBuffer;
}
bool
@@ -529,21 +678,14 @@ const char *Ssl::ErrorDetail::notafter() const
*/
const char *Ssl::ErrorDetail::err_code() const
{
- static char tmpBuffer[64];
// We can use the GetErrorName but using the detailEntry is faster,
// so try it first.
- const char *err = detailEntry.name.termedBuf();
+ if (const char *err = detailEntry.name.termedBuf())
+ return err;
// error details not loaded yet or not defined in error_details.txt,
// try the GetErrorName...
- if (!err)
- err = GetErrorName(error_no);
-
- if (!err) {
- snprintf(tmpBuffer, 64, "%d", (int)error_no);
- err = tmpBuffer;
- }
- return err;
+ return GetErrorName(error_no);
}
/**
diff --git a/src/ssl/ErrorDetail.h b/src/ssl/ErrorDetail.h
index 48dc405..0eec0a9 100644
--- a/src/ssl/ErrorDetail.h
+++ b/src/ssl/ErrorDetail.h
@@ -26,8 +26,9 @@ bool ParseErrorString(const char *name, Security::Errors &);
/// The Security::ErrorCode code of the error described by "name".
Security::ErrorCode GetErrorCode(const char *name);
-/// The string representation of the TLS error "value"
-const char *GetErrorName(Security::ErrorCode value);
+/// \return string representation of a known TLS error (or a raw error code)
+/// \param prefixRawCode whether to prefix raw codes with "SSL_ERR="
+const char *GetErrorName(Security::ErrorCode value, const bool prefixRawCode = false);
/// A short description of the TLS error "value"
const char *GetErrorDescr(Security::ErrorCode value);

105
CVE-2020-15049.patch Normal file
View File

@ -0,0 +1,105 @@
commit ea12a34d338b962707d5078d6d1fc7c6eb119a22
Author: Alex Rousskov <rousskov@measurement-factory.com>
Date: 2020-05-13 14:05:00 +0000
Validate Content-Length value prefix (#629)
The new code detects all invalid Content-Length prefixes but the old
code was already rejecting most invalid prefixes using strtoll(). The
newly covered (and now rejected) invalid characters are
* explicit "+" sign;
* explicit "-" sign in "-0" values;
* isspace(3) characters that are not (relaxed) OWS characters.
In most deployment environments, the last set is probably empty because
the relaxed OWS set has all the POSIX/C isspace(3) characters but the
new line, and the new line is unlikely to sneak in past other checks.
Thank you, Amit Klein <amit.klein@safebreach.com>, for elevating the
importance of this 2016 TODO (added in commit a1b9ec2).
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 36957f2..c10a221 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -25,6 +25,7 @@ Thank you!
Alex Wu <alex_wu2012@hotmail.com>
Alin Nastac <mrness@gentoo.org>
Alter <alter@alter.org.ua>
+ Amit Klein <amit.klein@safebreach.com>
Amos Jeffries
Amos Jeffries <amosjeffries@squid-cache.org>
Amos Jeffries <squid3@treenet.co.nz>
diff --git a/src/http/ContentLengthInterpreter.cc b/src/http/ContentLengthInterpreter.cc
index 3fdf7de..a3741eb 100644
--- a/src/http/ContentLengthInterpreter.cc
+++ b/src/http/ContentLengthInterpreter.cc
@@ -28,6 +28,24 @@ Http::ContentLengthInterpreter::ContentLengthInterpreter(const int aDebugLevel):
{
}
+/// checks whether all characters before the Content-Length number are allowed
+/// \returns the start of the digit sequence (or nil on errors)
+const char *
+Http::ContentLengthInterpreter::findDigits(const char *prefix, const char * const valueEnd) const
+{
+ // skip leading OWS in RFC 7230's `OWS field-value OWS`
+ const CharacterSet &whitespace = Http::One::Parser::WhitespaceCharacters();
+ while (prefix < valueEnd) {
+ const auto ch = *prefix;
+ if (CharacterSet::DIGIT[ch])
+ return prefix; // common case: a pre-trimmed field value
+ if (!whitespace[ch])
+ return nullptr; // (trimmed) length does not start with a digit
+ ++prefix;
+ }
+ return nullptr; // empty or whitespace-only value
+}
+
/// checks whether all characters after the Content-Length are allowed
bool
Http::ContentLengthInterpreter::goodSuffix(const char *suffix, const char * const end) const
@@ -52,10 +70,19 @@ Http::ContentLengthInterpreter::checkValue(const char *rawValue, const int value
{
Must(!sawBad);
+ const auto valueEnd = rawValue + valueSize;
+
+ const auto digits = findDigits(rawValue, valueEnd);
+ if (!digits) {
+ debugs(55, debugLevel, "WARNING: Leading garbage or empty value in" << Raw("Content-Length", rawValue, valueSize));
+ sawBad = true;
+ return false;
+ }
+
int64_t latestValue = -1;
char *suffix = nullptr;
- // TODO: Handle malformed values with leading signs (e.g., "-0" or "+1").
- if (!httpHeaderParseOffset(rawValue, &latestValue, &suffix)) {
+
+ if (!httpHeaderParseOffset(digits, &latestValue, &suffix)) {
debugs(55, DBG_IMPORTANT, "WARNING: Malformed" << Raw("Content-Length", rawValue, valueSize));
sawBad = true;
return false;
@@ -68,7 +95,7 @@ Http::ContentLengthInterpreter::checkValue(const char *rawValue, const int value
}
// check for garbage after the number
- if (!goodSuffix(suffix, rawValue + valueSize)) {
+ if (!goodSuffix(suffix, valueEnd)) {
debugs(55, debugLevel, "WARNING: Trailing garbage in" << Raw("Content-Length", rawValue, valueSize));
sawBad = true;
return false;
diff --git a/src/http/ContentLengthInterpreter.h b/src/http/ContentLengthInterpreter.h
index ce36e22..f22de91 100644
--- a/src/http/ContentLengthInterpreter.h
+++ b/src/http/ContentLengthInterpreter.h
@@ -46,6 +46,7 @@ public:
bool sawGood;
protected:
+ const char *findDigits(const char *prefix, const char *valueEnd) const;
bool goodSuffix(const char *suffix, const char * const end) const;
bool checkValue(const char *start, const int size);
bool checkList(const String &list);

63
CVE-2020-15810.patch Normal file
View File

@ -0,0 +1,63 @@
From 9c8e2a71aa1d3c159a319d9365c346c48dc783a5 Mon Sep 17 00:00:00 2001
From: Amos Jeffries <yadij@users.noreply.github.com>
Date: Tue, 4 Aug 2020 04:34:32 +0000
Subject: [PATCH] Enforce token characters for field-name (#700)
RFC 7230 defines field-name as a token. Request splitting and cache
poisoning attacks have used non-token characters to fool broken HTTP
agents behind or in front of Squid for years. This change should
significantly reduce that abuse.
If we discover exceptional situations that need special treatment, the
relaxed parser can allow them on a case-by-case basis (while being extra
careful about framing-related header fields), just like we already
tolerate some header whitespace (e.g., between the response header
field-name and colon).
---
src/HttpHeader.cc | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc
index dc6e0ffd63..9e5e47fb34 100644
--- a/src/HttpHeader.cc
+++ b/src/HttpHeader.cc
@@ -443,18 +443,6 @@ HttpHeader::parse(const char *header_start, size_t hdrLen)
return 0;
}
- if (e->id == Http::HdrType::OTHER && stringHasWhitespace(e->name.termedBuf())) {
- debugs(55, warnOnError, "WARNING: found whitespace in HTTP header name {" <<
- getStringPrefix(field_start, field_end-field_start) << "}");
-
- if (!Config.onoff.relaxed_header_parser) {
- delete e;
- PROF_stop(HttpHeaderParse);
- clean();
- return 0;
- }
- }
-
addEntry(e);
}
@@ -1437,6 +1425,20 @@ HttpHeaderEntry::parse(const char *field_start, const char *field_end, const htt
}
}
+ /* RFC 7230 section 3.2:
+ *
+ * header-field = field-name ":" OWS field-value OWS
+ * field-name = token
+ * token = 1*TCHAR
+ */
+ for (const char *pos = field_start; pos < (field_start+name_len); ++pos) {
+ if (!CharacterSet::TCHAR[*pos]) {
+ debugs(55, 2, "found header with invalid characters in " <<
+ Raw("field-name", field_start, min(name_len,100)) << "...");
+ return nullptr;
+ }
+ }
+
/* now we know we can parse it */
debugs(55, 9, "parsing HttpHeaderEntry: near '" << getStringPrefix(field_start, field_end-field_start) << "'");

161
CVE-2020-15811.patch Normal file
View File

@ -0,0 +1,161 @@
From fd68382860633aca92065e6c343cfd1b12b126e7 Mon Sep 17 00:00:00 2001
From: Amos Jeffries <yadij@users.noreply.github.com>
Date: Sun, 16 Aug 2020 02:21:22 +0000
Subject: [PATCH] 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.
---
src/HttpHeader.cc | 16 +++++++++++++++-
src/HttpHeader.h | 18 ++++++++++--------
src/client_side.cc | 11 ++---------
src/http.cc | 3 +++
4 files changed, 30 insertions(+), 18 deletions(-)
diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc
index 80c23458eb..f30802eb79 100644
--- a/src/HttpHeader.cc
+++ b/src/HttpHeader.cc
@@ -174,6 +174,7 @@ HttpHeader::operator =(const HttpHeader &other)
update(&other); // will update the mask as well
len = other.len;
conflictingContentLength_ = other.conflictingContentLength_;
+ teUnsupported_ = other.teUnsupported_;
}
return *this;
}
@@ -222,6 +223,7 @@ HttpHeader::clean()
httpHeaderMaskInit(&mask, 0);
len = 0;
conflictingContentLength_ = false;
+ teUnsupported_ = false;
PROF_stop(HttpHeaderClean);
}
@@ -471,11 +473,23 @@ HttpHeader::parse(const char *header_start, size_t hdrLen)
Raw("header", header_start, hdrLen));
}
- if (chunked()) {
+ String rawTe;
+ if (getByIdIfPresent(Http::HdrType::TRANSFER_ENCODING, &rawTe)) {
// RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
// RFC 7230 section 3.3.3 #3: Transfer-Encoding overwrites Content-Length
delById(Http::HdrType::CONTENT_LENGTH);
// and clen state becomes irrelevant
+
+ if (rawTe == "chunked") {
+ ; // leave header present for chunked() method
+ } else if (rawTe == "identity") { // deprecated. no coding
+ delById(Http::HdrType::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(Http::HdrType::CONTENT_LENGTH);
diff --git a/src/HttpHeader.h b/src/HttpHeader.h
index e3553a4e4d..64f294a50a 100644
--- a/src/HttpHeader.h
+++ b/src/HttpHeader.h
@@ -140,7 +140,13 @@ class HttpHeader
int hasListMember(Http::HdrType 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(Http::HdrType::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 */
@@ -158,6 +164,9 @@ class HttpHeader
private:
HttpHeaderEntry *findLastEntry(Http::HdrType 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);
@@ -167,13 +176,6 @@ SBuf httpHeaderQuoteString(const char *raw);
void httpHeaderCalcMask(HttpHeaderMask * mask, Http::HdrType http_hdr_type_enums[], size_t count);
-inline bool
-HttpHeader::chunked() const
-{
- return has(Http::HdrType::TRANSFER_ENCODING) &&
- hasListMember(Http::HdrType::TRANSFER_ENCODING, "chunked", ',');
-}
-
void httpHeaderInitModule(void);
#endif /* SQUID_HTTPHEADER_H */
diff --git a/src/client_side.cc b/src/client_side.cc
index f7038ba983..547b0ca723 100644
--- a/src/client_side.cc
+++ b/src/client_side.cc
@@ -1600,9 +1600,7 @@ void
clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
{
ClientHttpRequest *http = context->http;
- bool chunked = false;
bool mustReplyToOptions = false;
- bool unsupportedTe = false;
bool expectBody = false;
// We already have the request parsed and checked, so we
@@ -1659,13 +1657,7 @@ clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp,
request->http_ver.minor = http_ver.minor;
}
- if (request->header.chunked()) {
- chunked = true;
- } else if (request->header.has(Http::HdrType::TRANSFER_ENCODING)) {
- const String te = request->header.getList(Http::HdrType::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 = (request->method == Http::METHOD_OPTIONS) &&
(request->header.getInt64(Http::HdrType::MAX_FORWARDS) == 0);
@@ -1682,6 +1674,7 @@ clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp,
return;
}
+ const auto chunked = request->header.chunked();
if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
clientStreamNode *node = context->getClientReplyContext();
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
diff --git a/src/http.cc b/src/http.cc
index 53f428a4d2..79ab2cf226 100644
--- a/src/http.cc
+++ b/src/http.cc
@@ -1292,6 +1292,9 @@ HttpStateData::continueAfterParsingHeader()
} 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
}

34
CVE-2020-24606.patch Normal file
View File

@ -0,0 +1,34 @@
commit b789e719affbb0a6ff9c22095f6ca8db6a5f4926
Author: Eduard Bagdasaryan <eduard.bagdasaryan@measurement-factory.com>
Date: 2020-07-27 15:28:31 +0000
Fix livelocking in peerDigestHandleReply (#698)
peerDigestHandleReply() was missing a premature EOF check. The existing
peerDigestFetchedEnough() cannot detect EOF because it does not have
access to receivedData.length used to indicate the EOF condition. We did
not adjust peerDigestFetchedEnough() because it is abused to check both
post-I/O state and the state after each digest processing step. The
latter invocations lack access to receivedData.length and should not
really bother with EOF anyway.
diff --git a/src/peer_digest.cc b/src/peer_digest.cc
index d48340f97..265f16183 100644
--- a/src/peer_digest.cc
+++ b/src/peer_digest.cc
@@ -483,6 +483,15 @@ peerDigestHandleReply(void *data, StoreIOBuffer receivedData)
} while (cbdataReferenceValid(fetch) && prevstate != fetch->state && fetch->bufofs > 0);
+ // Check for EOF here, thus giving the parser one extra run. We could avoid this overhead by
+ // checking at the beginning of this function. However, in this case, we would have to require
+ // that the parser does not regard EOF as a special condition (it is true now but may change
+ // in the future).
+ if (!receivedData.length) { // EOF
+ peerDigestFetchAbort(fetch, fetch->buf, "premature end of digest reply");
+ return;
+ }
+
/* Update the copy offset */
fetch->offset += receivedData.length;

View File

@ -2,7 +2,7 @@
Name: squid
Version: 4.9
Release: 5
Release: 6
Summary: The Squid proxy caching server
Epoch: 7
License: GPLv2+ and (LGPLv2+ and MIT and BSD and Public Domain)
@ -28,6 +28,11 @@ Patch7: CVE-2020-8449_CVE-2020-8450.patch
Patch8: squid-fix-detection-of-sys-sysctl.h-detection-511.patch
Patch9: CVE-2019-12519.patch
Patch10:CVE-2020-11945.patch
Patch11:CVE-2020-14058.patch
Patch12:CVE-2020-15049.patch
Patch13:CVE-2020-15810.patch
Patch14:CVE-2020-15811.patch
Patch15:CVE-2020-24606.patch
Buildroot: %{_tmppath}/squid-4.9-1-root-%(%{__id_u} -n)
Requires: bash >= 2.0
@ -206,6 +211,12 @@ fi
chgrp squid /var/cache/samba/winbindd_privileged >/dev/null 2>&1 || :
%changelog
* Wed Mar 17 2021 openEuler Buildteam <buildteam@openeuler.org> - 4.9-6
- Type:cves
- ID:CVE-2020-14058, CVE-2020-15049, CVE-2020-15810, CVE-2020-15811, CVE-2020-24606
- SUG:restart
- DESC:fix CVE-2020-14058,CVE-2020-15049,CVE-2020-15810,CVE-2020-15811,CVE-2020-24606
* Mon Mar 8 2021 openEuler Buildteam <buildteam@openeuler.org> - 4.9-5
- Type:cves
- ID:CVE-2020-11945