From 18002a419b23cdb53534f9b9da07ae4d6a7c6a96 Mon Sep 17 00:00:00 2001 From: guoxiaoqi Date: Wed, 17 Mar 2021 14:04:11 +0800 Subject: [PATCH 1/2] fix CVE-2020-14058,CVE-2020-15049,CVE-2020-15810,CVE-2020-15811,CVE-2020-24606 (cherry picked from commit 9311c7ccaa00ae53c0e9886b214589265b04c565) --- CVE-2020-14058.patch | 295 +++++++++++++++++++++++++++++++++++++++++++ CVE-2020-15049.patch | 105 +++++++++++++++ CVE-2020-15810.patch | 63 +++++++++ CVE-2020-15811.patch | 161 +++++++++++++++++++++++ CVE-2020-24606.patch | 34 +++++ squid.spec | 13 +- 6 files changed, 670 insertions(+), 1 deletion(-) create mode 100644 CVE-2020-14058.patch create mode 100644 CVE-2020-15049.patch create mode 100644 CVE-2020-15810.patch create mode 100644 CVE-2020-15811.patch create mode 100644 CVE-2020-24606.patch diff --git a/CVE-2020-14058.patch b/CVE-2020-14058.patch new file mode 100644 index 0000000..1567477 --- /dev/null +++ b/CVE-2020-14058.patch @@ -0,0 +1,295 @@ +commit 93f5fda134a2a010b84ffedbe833d670e63ba4be +Author: Christos Tsantilas +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 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); diff --git a/CVE-2020-15049.patch b/CVE-2020-15049.patch new file mode 100644 index 0000000..5f7151d --- /dev/null +++ b/CVE-2020-15049.patch @@ -0,0 +1,105 @@ +commit ea12a34d338b962707d5078d6d1fc7c6eb119a22 +Author: Alex Rousskov +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 , 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 + Alin Nastac + Alter ++ Amit Klein + Amos Jeffries + Amos Jeffries + Amos Jeffries +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); diff --git a/CVE-2020-15810.patch b/CVE-2020-15810.patch new file mode 100644 index 0000000..5b676a7 --- /dev/null +++ b/CVE-2020-15810.patch @@ -0,0 +1,63 @@ +From 9c8e2a71aa1d3c159a319d9365c346c48dc783a5 Mon Sep 17 00:00:00 2001 +From: Amos Jeffries +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) << "'"); diff --git a/CVE-2020-15811.patch b/CVE-2020-15811.patch new file mode 100644 index 0000000..42d802a --- /dev/null +++ b/CVE-2020-15811.patch @@ -0,0 +1,161 @@ +From fd68382860633aca92065e6c343cfd1b12b126e7 Mon Sep 17 00:00:00 2001 +From: Amos Jeffries +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 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(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 + } diff --git a/CVE-2020-24606.patch b/CVE-2020-24606.patch new file mode 100644 index 0000000..d277507 --- /dev/null +++ b/CVE-2020-24606.patch @@ -0,0 +1,34 @@ +commit b789e719affbb0a6ff9c22095f6ca8db6a5f4926 +Author: Eduard Bagdasaryan +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; + diff --git a/squid.spec b/squid.spec index 9a0d241..32134fe 100644 --- a/squid.spec +++ b/squid.spec @@ -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 - 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 - 4.9-5 - Type:cves - ID:CVE-2020-11945 -- Gitee From 82318c8f76dbcaef294badee149499a7e6fbfc51 Mon Sep 17 00:00:00 2001 From: guoxiaoqi Date: Fri, 19 Mar 2021 11:13:09 +0800 Subject: [PATCH 2/2] fix --- squid.spec | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/squid.spec b/squid.spec index 32134fe..ecb81b7 100644 --- a/squid.spec +++ b/squid.spec @@ -25,14 +25,13 @@ Patch4: squid-4.0.21-large-acl.patch Patch5: CVE-2019-12528.patch Patch6: CVE-2020-8517.patch 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 +Patch8: CVE-2019-12519.patch +Patch9:CVE-2020-11945.patch +Patch10:CVE-2020-14058.patch +Patch11:CVE-2020-15049.patch +Patch12:CVE-2020-15810.patch +Patch13:CVE-2020-15811.patch +Patch14:CVE-2020-24606.patch Buildroot: %{_tmppath}/squid-4.9-1-root-%(%{__id_u} -n) Requires: bash >= 2.0 -- Gitee