Compare commits

...

No commits in common. "imports/c8s-stream-4/squid-4.11-4.module+el8.4.0+9038+276d2d9f" and "c8-stream-4" have entirely different histories.

30 changed files with 8960 additions and 823 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/squid-4.11.tar.xz
SOURCES/squid-4.15.tar.xz

View File

@ -1 +1 @@
053277bf5497163ffc9261b9807abda5959bb6fc SOURCES/squid-4.11.tar.xz
60bda34ba39657e2d870c8c1d2acece8a69c3075 SOURCES/squid-4.15.tar.xz

View File

@ -1,295 +0,0 @@
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);

View File

@ -1,105 +0,0 @@
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);

View File

@ -1,44 +0,0 @@
diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc
index 67543a4..19efc6d 100644
--- a/src/HttpHeader.cc
+++ b/src/HttpHeader.cc
@@ -445,18 +445,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);
}
@@ -1451,6 +1439,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) << "'");

View File

@ -1,139 +0,0 @@
diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc
index dc6e0ff..67543a4 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);
}
@@ -464,11 +466,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 e3553a4..64f294a 100644
--- a/src/HttpHeader.h
+++ b/src/HttpHeader.h
@@ -140,7 +140,13 @@ public:
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 @@ protected:
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 5f5a79e..000a00b 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 9654c4a..6f4d3b2 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
}

View File

@ -1,34 +0,0 @@
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

@ -1,27 +0,0 @@
diff --git a/tools/cachemgr.cc b/tools/cachemgr.cc
index 36d8e38..50bb9b6 100644
--- a/tools/cachemgr.cc
+++ b/tools/cachemgr.cc
@@ -1092,14 +1092,20 @@ make_pub_auth(cachemgr_request * req)
if (!req->passwd || !strlen(req->passwd))
return;
+ auto *rfc1738_username = xstrdup(rfc1738_escape(safe_str(req->user_name)));
+ auto *rfc1738_passwd = xstrdup(rfc1738_escape(req->passwd));
+
/* host | time | user | passwd */
const int bufLen = snprintf(buf, sizeof(buf), "%s|%d|%s|%s",
req->hostname,
(int) now,
- rfc1738_escape(safe_str(req->user_name)),
- rfc1738_escape(req->passwd));
+ rfc1738_username,
+ rfc1738_passwd);
debug("cmgr: pre-encoded for pub: %s\n", buf);
+ safe_free(rfc1738_username);
+ safe_free(rfc1738_passwd);
+
const int encodedLen = base64_encode_len(bufLen);
req->pub_auth = (char *) xmalloc(encodedLen);
struct base64_encode_ctx ctx;

View File

@ -14,131 +14,129 @@ Move client_ip_max_connections test later in the TCP accept process to ensure ds
1 file changed, 39 insertions(+), 43 deletions(-)
diff --git a/src/comm/TcpAcceptor.cc b/src/comm/TcpAcceptor.cc
index cae92a7b1e..2109913008 100644
index d4b576d..936aa30 100644
--- a/src/comm/TcpAcceptor.cc
+++ b/src/comm/TcpAcceptor.cc
@@ -282,16 +282,7 @@ Comm::TcpAcceptor::acceptOne()
@@ -282,7 +282,16 @@ Comm::TcpAcceptor::acceptOne()
ConnectionPointer newConnDetails = new Connection();
const Comm::Flag flag = oldAccept(newConnDetails);
- /* Check for errors */
- if (!newConnDetails->isOpen()) {
-
- if (flag == Comm::NOMESSAGE) {
- /* register interest again */
- debugs(5, 5, HERE << "try later: " << conn << " handler Subscription: " << theCallSub);
- SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
- return;
- }
-
+ if (flag == Comm::COMM_ERROR) {
- if (flag == Comm::COMM_ERROR) {
+ /* Check for errors */
+ if (!newConnDetails->isOpen()) {
+
+ if (flag == Comm::NOMESSAGE) {
+ /* register interest again */
+ debugs(5, 5, HERE << "try later: " << conn << " handler Subscription: " << theCallSub);
+ SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
+ return;
+ }
+
// A non-recoverable error; notify the caller */
debugs(5, 5, HERE << "non-recoverable error:" << status() << " handler Subscription: " << theCallSub);
if (intendedForUserConnections())
@@ -301,12 +292,16 @@ Comm::TcpAcceptor::acceptOne()
@@ -292,16 +301,12 @@ Comm::TcpAcceptor::acceptOne()
return;
}
- newConnDetails->nfmark = Ip::Qos::getNfmarkFromConnection(newConnDetails, Ip::Qos::dirAccepted);
+ if (flag == Comm::NOMESSAGE) {
+ /* register interest again */
+ debugs(5, 5, "try later: " << conn << " handler Subscription: " << theCallSub);
+ } else {
+ debugs(5, 5, "Listener: " << conn <<
+ " accepted new connection " << newConnDetails <<
+ " handler Subscription: " << theCallSub);
+ notify(flag, newConnDetails);
+ }
- if (flag == Comm::NOMESSAGE) {
- /* register interest again */
- debugs(5, 5, "try later: " << conn << " handler Subscription: " << theCallSub);
- } else {
- debugs(5, 5, "Listener: " << conn <<
- " accepted new connection " << newConnDetails <<
- " handler Subscription: " << theCallSub);
- notify(flag, newConnDetails);
- }
+ newConnDetails->nfmark = Ip::Qos::getNfmarkFromConnection(newConnDetails, Ip::Qos::dirAccepted);
- debugs(5, 5, HERE << "Listener: " << conn <<
- " accepted new connection " << newConnDetails <<
- " handler Subscription: " << theCallSub);
- notify(flag, newConnDetails);
+ debugs(5, 5, HERE << "Listener: " << conn <<
+ " accepted new connection " << newConnDetails <<
+ " handler Subscription: " << theCallSub);
+ notify(flag, newConnDetails);
SetSelect(conn->fd, COMM_SELECT_READ, doAccept, this, 0);
}
@@ -346,8 +341,8 @@ Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &
@@ -341,8 +346,8 @@ Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &
*
* \retval Comm::OK success. details parameter filled.
* \retval Comm::NOMESSAGE attempted accept() but nothing useful came in.
- * \retval Comm::COMM_ERROR an outright failure occurred.
* Or this client has too many connections already.
+ * \retval Comm::COMM_ERROR an outright failure occurred.
- * Or this client has too many connections already.
* \retval Comm::COMM_ERROR an outright failure occurred.
+ * Or this client has too many connections already.
*/
Comm::Flag
Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
@@ -382,15 +377,6 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
details->fd = sock;
@@ -383,6 +388,15 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
details->remote = *gai;
- if ( Config.client_ip_max_connections >= 0) {
- if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) {
- debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
- Ip::Address::FreeAddr(gai);
- PROF_stop(comm_accept);
- return Comm::COMM_ERROR;
- }
- }
-
+ if ( Config.client_ip_max_connections >= 0) {
+ if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) {
+ debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
+ Ip::Address::FreeAddr(gai);
+ PROF_stop(comm_accept);
+ return Comm::COMM_ERROR;
+ }
+ }
+
// lookup the local-end details of this new connection
Ip::Address::InitAddr(gai);
details->local.setEmpty();
@@ -404,6 +390,34 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
@@ -396,6 +410,23 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
details->local = *gai;
Ip::Address::FreeAddr(gai);
+ // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
+ if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
+ debugs(50, DBG_IMPORTANT, "ERROR: NAT/TPROXY lookup failed to locate original IPs on " << details);
+ // Failed.
+ PROF_stop(comm_accept);
+ return Comm::COMM_ERROR;
+ }
+ /* fdstat update */
+ fdd_table[sock].close_file = NULL;
+ fdd_table[sock].close_line = 0;
+
+#if USE_SQUID_EUI
+ if (Eui::TheConfig.euiLookup) {
+ if (details->remote.isIPv4()) {
+ details->remoteEui48.lookup(details->remote);
+ } else if (details->remote.isIPv6()) {
+ details->remoteEui64.lookup(details->remote);
+ }
+ }
+#endif
+ fde *F = &fd_table[sock];
+ details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
+ F->remote_port = details->remote.port();
+ F->local_addr = details->local;
+ F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET;
+
+ details->nfmark = Ip::Qos::getNfmarkFromConnection(details, Ip::Qos::dirAccepted);
+ // set socket flags
+ commSetCloseOnExec(sock);
+ commSetNonBlocking(sock);
+
+ if (Config.client_ip_max_connections >= 0) {
+ if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) {
+ debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
+ PROF_stop(comm_accept);
+ return Comm::NOMESSAGE;
+ }
+ }
+ /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
+ F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
+
/* fdstat update */
// XXX : these are not all HTTP requests. use a note about type and ip:port details->
// so we end up with a uniform "(HTTP|FTP-data|HTTPS|...) remote-ip:remote-port"
@@ -425,24 +439,6 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
/* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
// Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
debugs(50, DBG_IMPORTANT, "ERROR: NAT/TPROXY lookup failed to locate original IPs on " << details);
@@ -414,33 +445,6 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
}
#endif
- // Perform NAT or TPROXY operations to retrieve the real client/dest IP addresses
- if (conn->flags&(COMM_TRANSPARENT|COMM_INTERCEPTION) && !Ip::Interceptor.Lookup(details, conn)) {
- debugs(50, DBG_IMPORTANT, "ERROR: NAT/TPROXY lookup failed to locate original IPs on " << details);
- // Failed.
- PROF_stop(comm_accept);
- return Comm::COMM_ERROR;
- }
- details->nfmark = Ip::Qos::getNfmarkFromConnection(details, Ip::Qos::dirAccepted);
-
-#if USE_SQUID_EUI
- if (Eui::TheConfig.euiLookup) {
- if (details->remote.isIPv4()) {
- details->remoteEui48.lookup(details->remote);
- } else if (details->remote.isIPv6()) {
- details->remoteEui64.lookup(details->remote);
- if (Config.client_ip_max_connections >= 0) {
- if (clientdbEstablished(details->remote, 0) > Config.client_ip_max_connections) {
- debugs(50, DBG_IMPORTANT, "WARNING: " << details->remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
- PROF_stop(comm_accept);
- return Comm::NOMESSAGE;
- }
- }
-#endif
-
- /* fdstat update */
- fdd_table[sock].close_file = NULL;
- fdd_table[sock].close_line = 0;
-
- fde *F = &fd_table[sock];
- details->remote.toStr(F->ipaddr,MAX_IPSTRLEN);
- F->remote_port = details->remote.port();
- F->local_addr = details->local;
- F->sock_family = details->local.isIPv6()?AF_INET6:AF_INET;
-
- // set socket flags
- commSetCloseOnExec(sock);
- commSetNonBlocking(sock);
-
- /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
- F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
-
PROF_stop(comm_accept);
return Comm::OK;

View File

@ -6,5 +6,5 @@ index 90ac6a4..8dbed90 100755
-#!/usr/local/bin/perl -Tw
+#!/usr/bin/perl -Tw
#
# * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
# * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
# *

View File

@ -1,39 +0,0 @@
diff --git a/configure b/configure
index 17b2ebf..9530f6b 100755
--- a/configure
+++ b/configure
@@ -33915,6 +33915,7 @@ done
fi
if test "x$SYSTEMD_LIBS" != "x" ; then
CXXFLAGS="$SYSTEMD_CFLAGS $CXXFLAGS"
+ LDFLAGS="$SYSTEMD_LIBS $LDFLAGS"
$as_echo "#define USE_SYSTEMD 1" >>confdefs.h
diff --git a/src/Debug.h b/src/Debug.h
index 6eecd01..ddd9e38 100644
--- a/src/Debug.h
+++ b/src/Debug.h
@@ -99,6 +99,10 @@ public:
/// configures the active debugging context to write syslog ALERT
static void ForceAlert();
+
+ /// prefixes each grouped debugs() line after the first one in the group
+ static std::ostream& Extra(std::ostream &os) { return os << "\n "; }
+
private:
static Context *Current; ///< deepest active context; nil outside debugs()
};
diff --git a/configure.ac b/configure.ac
index d3c5da8..806302c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2162,6 +2162,7 @@ if test "x$with_systemd" != "xno" -a "x$squid_host_os" = "xlinux"; then
fi
if test "x$SYSTEMD_LIBS" != "x" ; then
CXXFLAGS="$SYSTEMD_CFLAGS $CXXFLAGS"
+ LDFLAGS="$SYSTEMD_LIBS $LDFLAGS"
AC_DEFINE(USE_SYSTEMD,1,[systemd support is available])
else
with_systemd=no

View File

@ -1,25 +0,0 @@
File: squid-4.11.tar.xz
Date: Sun Apr 19 12:56:37 UTC 2020
Size: 2447700
MD5 : 10f34e852153a9996aa4614670e2bda1
SHA1: 053277bf5497163ffc9261b9807abda5959bb6fc
Key : CD6DBF8EF3B17D3E <squid3@treenet.co.nz>
B068 84ED B779 C89B 044E 64E3 CD6D BF8E F3B1 7D3E
keyring = http://www.squid-cache.org/pgp.asc
keyserver = pool.sks-keyservers.net
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEsGiE7bd5yJsETmTjzW2/jvOxfT4FAl6cSpEACgkQzW2/jvOx
fT6YbA/6A+IbIbNBJUW45oj23Io9Tw/CzAcTeLHR+McKwV77qMbR+L+kQ+fUdM5F
rHAmd8bVVlyHc4WanVfWItEmzBzHA/ifTNvVpefSGGEbDb80RF66k7ACiZUokg1b
kkPwc/SjDhe2wvketIaBiVVd7pylrlCdVvazcF8gE9MWDOIlJND5mnHXidXvwkbJ
T2//8JZVEmcmN9pdFGNAUVckFm+AnwWXcRM1SQPYDGSVUtjVlqido8snLTA1mZwl
rIpjppujMV54OOWlj+Gqa3MZkpNzIaMCAfphzUFlsQY+/sRUYAOv1wmxw2WclxlK
WlWM+fw8OsYNDMwkOScKZZWceoAkq6UsUHzCAdJIdLqV/R6mZ9nfuZ6BHIr0+2dP
bDf9MU4KXbwEuXiRD/KPziUxxOZwSPivbm3wy9DqTTZfO9V+Iq6FVHX+ahxJ0XbM
JWRYA3GW+DRLjorfsWxU5r4UJsrnBfhItPUAfGPjPjEGZ/pn8r9G6MGenNGPLMKy
wP1rMlOhrZPwerzokzAvKx8G0WWkfN+IPv2JK3rDot6RiJIOuvnZZd4RIuVNTGbh
liO7M24JlWX3WD2wHBzxQag46+plb3VvrrVChwIQnZ2Qzpf50w0Bife/wtNBGpK0
k/Xi/nocO796YS8GZBnmhS1lEGEwp/YpJBFWmIjTWMUMEOcswVA=
=PKl0
-----END PGP SIGNATURE-----

View File

@ -0,0 +1,424 @@
commit b003a0da7865caa25b5d1e70c79329b32409b02a (HEAD -> refs/heads/v4, refs/remotes/origin/v4)
Author: Amos Jeffries <yadij@users.noreply.github.com>
Date: 2021-09-24 21:53:11 +0000
WCCP: Validate packets better (#899)
Update WCCP to support exception based error handling for
parsing and processing we are moving Squid to for protocol
handling.
Update the main WCCPv2 parsing checks to throw meaningful
exceptions when detected.
diff --git a/src/wccp2.cc b/src/wccp2.cc
index ee592449c..6ef469e91 100644
--- a/src/wccp2.cc
+++ b/src/wccp2.cc
@@ -1108,6 +1108,59 @@ wccp2ConnectionClose(void)
* Functions for handling the requests.
*/
+/// Checks that the given area section ends inside the given (whole) area.
+/// \param error the message to throw when the section does not fit
+static void
+CheckSectionLength(const void *sectionStart, const size_t sectionLength, const void *wholeStart, const size_t wholeSize, const char *error)
+{
+ assert(sectionStart);
+ assert(wholeStart);
+
+ const auto wholeEnd = static_cast<const char*>(wholeStart) + wholeSize;
+ assert(sectionStart >= wholeStart && "we never go backwards");
+ assert(sectionStart <= wholeEnd && "we never go beyond our whole (but zero-sized fields are OK)");
+ static_assert(sizeof(wccp2_i_see_you_t) <= PTRDIFF_MAX, "paranoid: no UB when subtracting in-whole pointers");
+ // subtraction safe due to the three assertions above
+ const auto remainderDiff = wholeEnd - static_cast<const char*>(sectionStart);
+
+ // casting safe due to the assertions above (and size_t definition)
+ assert(remainderDiff >= 0);
+ const auto remainderSize = static_cast<size_t>(remainderDiff);
+
+ if (sectionLength <= remainderSize)
+ return;
+
+ throw TextException(error, Here());
+}
+
+/// Checks that the area contains at least dataLength bytes after the header.
+/// The size of the field header itself is not included in dataLength.
+/// \returns the total field size -- the field header and field data combined
+template<class FieldHeader>
+static size_t
+CheckFieldDataLength(const FieldHeader *header, const size_t dataLength, const void *areaStart, const size_t areaSize, const char *error)
+{
+ assert(header);
+ const auto dataStart = reinterpret_cast<const char*>(header) + sizeof(header);
+ CheckSectionLength(dataStart, dataLength, areaStart, areaSize, error);
+ return sizeof(header) + dataLength; // no overflow after CheckSectionLength()
+}
+
+/// Positions the given field at a given start within a given packet area.
+/// The Field type determines the correct field size (used for bounds checking).
+/// \param field the field pointer the function should set
+/// \param areaStart the start of a packet (sub)structure containing the field
+/// \param areaSize the size of the packet (sub)structure starting at areaStart
+/// \param fieldStart the start of a field within the given area
+/// \param error the message to throw when the field does not fit the area
+template<class Field>
+static void
+SetField(Field *&field, const void *fieldStart, const void *areaStart, const size_t areaSize, const char *error)
+{
+ CheckSectionLength(fieldStart, sizeof(Field), areaStart, areaSize, error);
+ field = static_cast<Field*>(const_cast<void*>(fieldStart));
+}
+
/*
* Accept the UDP packet
*/
@@ -1124,8 +1177,6 @@ wccp2HandleUdp(int sock, void *)
/* These structs form the parts of the packet */
- struct wccp2_item_header_t *header = NULL;
-
struct wccp2_security_none_t *security_info = NULL;
struct wccp2_service_info_t *service_info = NULL;
@@ -1141,14 +1192,13 @@ wccp2HandleUdp(int sock, void *)
struct wccp2_cache_identity_info_t *cache_identity = NULL;
struct wccp2_capability_info_header_t *router_capability_header = NULL;
+ char *router_capability_data_start = nullptr;
struct wccp2_capability_element_t *router_capability_element;
struct sockaddr_in from;
struct in_addr cache_address;
- int len, found;
- short int data_length, offset;
uint32_t tmp;
char *ptr;
int num_caches;
@@ -1161,20 +1211,18 @@ wccp2HandleUdp(int sock, void *)
Ip::Address from_tmp;
from_tmp.setIPv4();
- len = comm_udp_recvfrom(sock,
- &wccp2_i_see_you,
- WCCP_RESPONSE_SIZE,
- 0,
- from_tmp);
+ const auto lenOrError = comm_udp_recvfrom(sock, &wccp2_i_see_you, WCCP_RESPONSE_SIZE, 0, from_tmp);
- if (len < 0)
+ if (lenOrError < 0)
return;
+ const auto len = static_cast<size_t>(lenOrError);
- if (ntohs(wccp2_i_see_you.version) != WCCP2_VERSION)
- return;
-
- if (ntohl(wccp2_i_see_you.type) != WCCP2_I_SEE_YOU)
- return;
+ try {
+ // TODO: Remove wccp2_i_see_you.data and use a buffer to read messages.
+ const auto message_header_size = sizeof(wccp2_i_see_you) - sizeof(wccp2_i_see_you.data);
+ Must2(len >= message_header_size, "incomplete WCCP message header");
+ Must2(ntohs(wccp2_i_see_you.version) == WCCP2_VERSION, "WCCP version unsupported");
+ Must2(ntohl(wccp2_i_see_you.type) == WCCP2_I_SEE_YOU, "WCCP packet type unsupported");
/* FIXME INET6 : drop conversion boundary */
from_tmp.getSockAddr(from);
@@ -1182,73 +1230,60 @@ wccp2HandleUdp(int sock, void *)
debugs(80, 3, "Incoming WCCPv2 I_SEE_YOU length " << ntohs(wccp2_i_see_you.length) << ".");
/* Record the total data length */
- data_length = ntohs(wccp2_i_see_you.length);
+ const auto data_length = ntohs(wccp2_i_see_you.length);
+ Must2(data_length <= len - message_header_size,
+ "malformed packet claiming it's bigger than received data");
- offset = 0;
-
- if (data_length > len) {
- debugs(80, DBG_IMPORTANT, "ERROR: Malformed WCCPv2 packet claiming it's bigger than received data");
- return;
- }
+ size_t offset = 0;
/* Go through the data structure */
- while (data_length > offset) {
+ while (offset + sizeof(struct wccp2_item_header_t) <= data_length) {
char *data = wccp2_i_see_you.data;
- header = (struct wccp2_item_header_t *) &data[offset];
+ const auto itemHeader = reinterpret_cast<const wccp2_item_header_t*>(&data[offset]);
+ const auto itemSize = CheckFieldDataLength(itemHeader, ntohs(itemHeader->length),
+ data, data_length, "truncated record");
+ // XXX: Check "The specified length must be a multiple of 4 octets"
+ // requirement to avoid unaligned memory reads after the first item.
- switch (ntohs(header->type)) {
+ switch (ntohs(itemHeader->type)) {
case WCCP2_SECURITY_INFO:
-
- if (security_info != NULL) {
- debugs(80, DBG_IMPORTANT, "Duplicate security definition");
- return;
- }
-
- security_info = (struct wccp2_security_none_t *) &wccp2_i_see_you.data[offset];
+ Must2(!security_info, "duplicate security definition");
+ SetField(security_info, itemHeader, itemHeader, itemSize,
+ "security definition truncated");
break;
case WCCP2_SERVICE_INFO:
-
- if (service_info != NULL) {
- debugs(80, DBG_IMPORTANT, "Duplicate service_info definition");
- return;
- }
-
- service_info = (struct wccp2_service_info_t *) &wccp2_i_see_you.data[offset];
+ Must2(!service_info, "duplicate service_info definition");
+ SetField(service_info, itemHeader, itemHeader, itemSize,
+ "service_info definition truncated");
break;
case WCCP2_ROUTER_ID_INFO:
-
- if (router_identity_info != NULL) {
- debugs(80, DBG_IMPORTANT, "Duplicate router_identity_info definition");
- return;
- }
-
- router_identity_info = (struct router_identity_info_t *) &wccp2_i_see_you.data[offset];
+ Must2(!router_identity_info, "duplicate router_identity_info definition");
+ SetField(router_identity_info, itemHeader, itemHeader, itemSize,
+ "router_identity_info definition truncated");
break;
case WCCP2_RTR_VIEW_INFO:
-
- if (router_view_header != NULL) {
- debugs(80, DBG_IMPORTANT, "Duplicate router_view definition");
- return;
- }
-
- router_view_header = (struct router_view_t *) &wccp2_i_see_you.data[offset];
+ Must2(!router_view_header, "duplicate router_view definition");
+ SetField(router_view_header, itemHeader, itemHeader, itemSize,
+ "router_view definition truncated");
break;
- case WCCP2_CAPABILITY_INFO:
-
- if (router_capability_header != NULL) {
- debugs(80, DBG_IMPORTANT, "Duplicate router_capability definition");
- return;
- }
+ case WCCP2_CAPABILITY_INFO: {
+ Must2(!router_capability_header, "duplicate router_capability definition");
+ SetField(router_capability_header, itemHeader, itemHeader, itemSize,
+ "router_capability definition truncated");
- router_capability_header = (struct wccp2_capability_info_header_t *) &wccp2_i_see_you.data[offset];
+ CheckFieldDataLength(router_capability_header, ntohs(router_capability_header->capability_info_length),
+ itemHeader, itemSize, "capability info truncated");
+ router_capability_data_start = reinterpret_cast<char*>(router_capability_header) +
+ sizeof(*router_capability_header);
break;
+ }
/* Nothing to do for the types below */
@@ -1257,22 +1292,17 @@ wccp2HandleUdp(int sock, void *)
break;
default:
- debugs(80, DBG_IMPORTANT, "Unknown record type in WCCPv2 Packet (" << ntohs(header->type) << ").");
+ debugs(80, DBG_IMPORTANT, "Unknown record type in WCCPv2 Packet (" << ntohs(itemHeader->type) << ").");
}
- offset += sizeof(struct wccp2_item_header_t);
- offset += ntohs(header->length);
-
- if (offset > data_length) {
- debugs(80, DBG_IMPORTANT, "Error: WCCPv2 packet tried to tell us there is data beyond the end of the packet");
- return;
- }
+ offset += itemSize;
+ assert(offset <= data_length && "CheckFieldDataLength(itemHeader...) established that");
}
- if ((security_info == NULL) || (service_info == NULL) || (router_identity_info == NULL) || (router_view_header == NULL)) {
- debugs(80, DBG_IMPORTANT, "Incomplete WCCPv2 Packet");
- return;
- }
+ Must2(security_info, "packet missing security definition");
+ Must2(service_info, "packet missing service_info definition");
+ Must2(router_identity_info, "packet missing router_identity_info definition");
+ Must2(router_view_header, "packet missing router_view definition");
debugs(80, 5, "Complete packet received");
@@ -1308,10 +1338,7 @@ wccp2HandleUdp(int sock, void *)
break;
}
- if (router_list_ptr->next == NULL) {
- debugs(80, DBG_IMPORTANT, "WCCPv2 Packet received from unknown router");
- return;
- }
+ Must2(router_list_ptr->next, "packet received from unknown router");
/* Set the router id */
router_list_ptr->info->router_address = router_identity_info->router_id_element.router_address;
@@ -1331,11 +1358,20 @@ wccp2HandleUdp(int sock, void *)
}
} else {
- char *end = ((char *) router_capability_header) + sizeof(*router_capability_header) + ntohs(router_capability_header->capability_info_length) - sizeof(struct wccp2_capability_info_header_t);
-
- router_capability_element = (struct wccp2_capability_element_t *) (((char *) router_capability_header) + sizeof(*router_capability_header));
-
- while ((char *) router_capability_element <= end) {
+ const auto router_capability_data_length = ntohs(router_capability_header->capability_info_length);
+ assert(router_capability_data_start);
+ const auto router_capability_data_end = router_capability_data_start +
+ router_capability_data_length;
+ for (auto router_capability_data_current = router_capability_data_start;
+ router_capability_data_current < router_capability_data_end;) {
+
+ SetField(router_capability_element, router_capability_data_current,
+ router_capability_data_start, router_capability_data_length,
+ "capability element header truncated");
+ const auto elementSize = CheckFieldDataLength(
+ router_capability_element, ntohs(router_capability_element->capability_length),
+ router_capability_data_start, router_capability_data_length,
+ "capability element truncated");
switch (ntohs(router_capability_element->capability_type)) {
@@ -1377,7 +1413,7 @@ wccp2HandleUdp(int sock, void *)
debugs(80, DBG_IMPORTANT, "Unknown capability type in WCCPv2 Packet (" << ntohs(router_capability_element->capability_type) << ").");
}
- router_capability_element = (struct wccp2_capability_element_t *) (((char *) router_capability_element) + sizeof(struct wccp2_item_header_t) + ntohs(router_capability_element->capability_length));
+ router_capability_data_current += elementSize;
}
}
@@ -1396,23 +1432,34 @@ wccp2HandleUdp(int sock, void *)
num_caches = 0;
/* Check to see if we're the master cache and update the cache list */
- found = 0;
+ bool found = false;
service_list_ptr->lowest_ip = 1;
cache_list_ptr = &router_list_ptr->cache_list_head;
/* to find the list of caches, we start at the end of the router view header */
ptr = (char *) (router_view_header) + sizeof(struct router_view_t);
+ const auto router_view_size = sizeof(struct router_view_t) +
+ ntohs(router_view_header->header.length);
/* Then we read the number of routers */
- memcpy(&tmp, ptr, sizeof(tmp));
+ const uint32_t *routerCountRaw = nullptr;
+ SetField(routerCountRaw, ptr, router_view_header, router_view_size,
+ "malformed packet (truncated router view info w/o number of routers)");
/* skip the number plus all the ip's */
-
- ptr += sizeof(tmp) + (ntohl(tmp) * sizeof(struct in_addr));
+ ptr += sizeof(*routerCountRaw);
+ const auto ipCount = ntohl(*routerCountRaw);
+ const auto ipsSize = ipCount * sizeof(struct in_addr); // we check for unsigned overflow below
+ Must2(ipsSize / sizeof(struct in_addr) != ipCount, "huge IP address count");
+ CheckSectionLength(ptr, ipsSize, router_view_header, router_view_size, "invalid IP address count");
+ ptr += ipsSize;
/* Then read the number of caches */
- memcpy(&tmp, ptr, sizeof(tmp));
+ const uint32_t *cacheCountRaw = nullptr;
+ SetField(cacheCountRaw, ptr, router_view_header, router_view_size,
+ "malformed packet (truncated router view info w/o cache count)");
+ memcpy(&tmp, cacheCountRaw, sizeof(tmp)); // TODO: Replace tmp with cacheCount
ptr += sizeof(tmp);
if (ntohl(tmp) != 0) {
@@ -1426,7 +1473,8 @@ wccp2HandleUdp(int sock, void *)
case WCCP2_ASSIGNMENT_METHOD_HASH:
- cache_identity = (struct wccp2_cache_identity_info_t *) ptr;
+ SetField(cache_identity, ptr, router_view_header, router_view_size,
+ "malformed packet (truncated router view info cache w/o assignment hash)");
ptr += sizeof(struct wccp2_cache_identity_info_t);
@@ -1437,13 +1485,15 @@ wccp2HandleUdp(int sock, void *)
case WCCP2_ASSIGNMENT_METHOD_MASK:
- cache_mask_info = (struct cache_mask_info_t *) ptr;
+ SetField(cache_mask_info, ptr, router_view_header, router_view_size,
+ "malformed packet (truncated router view info cache w/o assignment mask)");
/* The mask assignment has an undocumented variable length entry here */
if (ntohl(cache_mask_info->num1) == 3) {
- cache_mask_identity = (struct wccp2_cache_mask_identity_info_t *) ptr;
+ SetField(cache_mask_identity, ptr, router_view_header, router_view_size,
+ "malformed packet (truncated router view info cache w/o assignment mask identity)");
ptr += sizeof(struct wccp2_cache_mask_identity_info_t);
@@ -1474,10 +1524,7 @@ wccp2HandleUdp(int sock, void *)
debugs (80, 5, "checking cache list: (" << std::hex << cache_address.s_addr << ":" << router_list_ptr->local_ip.s_addr << ")");
/* Check to see if it's the master, or us */
-
- if (cache_address.s_addr == router_list_ptr->local_ip.s_addr) {
- found = 1;
- }
+ found = found || (cache_address.s_addr == router_list_ptr->local_ip.s_addr);
if (cache_address.s_addr < router_list_ptr->local_ip.s_addr) {
service_list_ptr->lowest_ip = 0;
@@ -1494,7 +1541,7 @@ wccp2HandleUdp(int sock, void *)
cache_list_ptr->next = NULL;
service_list_ptr->lowest_ip = 1;
- found = 1;
+ found = true;
num_caches = 1;
}
@@ -1502,7 +1549,7 @@ wccp2HandleUdp(int sock, void *)
router_list_ptr->num_caches = htonl(num_caches);
- if ((found == 1) && (service_list_ptr->lowest_ip == 1)) {
+ if (found && (service_list_ptr->lowest_ip == 1)) {
if (ntohl(router_view_header->change_number) != router_list_ptr->member_change) {
debugs(80, 4, "Change detected - queueing up new assignment");
router_list_ptr->member_change = ntohl(router_view_header->change_number);
@@ -1515,6 +1562,10 @@ wccp2HandleUdp(int sock, void *)
eventDelete(wccp2AssignBuckets, NULL);
debugs(80, 5, "I am not the lowest ip cache - not assigning buckets");
}
+
+ } catch (...) {
+ debugs(80, DBG_IMPORTANT, "ERROR: Ignoring WCCPv2 message: " << CurrentException);
+ }
}
static void

View File

@ -0,0 +1,129 @@
From 780c4ea1b4c9d2fb41f6962aa6ed73ae57f74b2b Mon Sep 17 00:00:00 2001
From: Joshua Rogers <MegaManSec@users.noreply.github.com>
Date: Mon, 18 Apr 2022 13:42:36 +0000
Subject: [PATCH] Improve handling of Gopher responses (#1022)
---
src/gopher.cc | 45 ++++++++++++++++++++-------------------------
1 file changed, 20 insertions(+), 25 deletions(-)
diff --git a/src/gopher.cc b/src/gopher.cc
index 169b0e18299..6187da18bcd 100644
--- a/src/gopher.cc
+++ b/src/gopher.cc
@@ -371,7 +371,6 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
char *lpos = NULL;
char *tline = NULL;
LOCAL_ARRAY(char, line, TEMP_BUF_SIZE);
- LOCAL_ARRAY(char, tmpbuf, TEMP_BUF_SIZE);
char *name = NULL;
char *selector = NULL;
char *host = NULL;
@@ -381,7 +380,6 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
char gtype;
StoreEntry *entry = NULL;
- memset(tmpbuf, '\0', TEMP_BUF_SIZE);
memset(line, '\0', TEMP_BUF_SIZE);
entry = gopherState->entry;
@@ -416,7 +414,7 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
return;
}
- String outbuf;
+ SBuf outbuf;
if (!gopherState->HTML_header_added) {
if (gopherState->conversion == GopherStateData::HTML_CSO_RESULT)
@@ -583,34 +581,34 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
break;
}
- memset(tmpbuf, '\0', TEMP_BUF_SIZE);
-
if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) {
if (strlen(escaped_selector) != 0)
- snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s@%s%s%s/\">%s</A>\n",
- icon_url, escaped_selector, rfc1738_escape_part(host),
- *port ? ":" : "", port, html_quote(name));
+ outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s@%s%s%s/\">%s</A>\n",
+ icon_url, escaped_selector, rfc1738_escape_part(host),
+ *port ? ":" : "", port, html_quote(name));
else
- snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s%s%s/\">%s</A>\n",
- icon_url, rfc1738_escape_part(host), *port ? ":" : "",
- port, html_quote(name));
+ outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s%s%s/\">%s</A>\n",
+ icon_url, rfc1738_escape_part(host), *port ? ":" : "",
+ port, html_quote(name));
} else if (gtype == GOPHER_INFO) {
- snprintf(tmpbuf, TEMP_BUF_SIZE, "\t%s\n", html_quote(name));
+ outbuf.appendf("\t%s\n", html_quote(name));
} else {
if (strncmp(selector, "GET /", 5) == 0) {
/* WWW link */
- snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"http://%s/%s\">%s</A>\n",
- icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name));
+ outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"http://%s/%s\">%s</A>\n",
+ icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name));
+ } else if (gtype == GOPHER_WWW) {
+ outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
+ icon_url, rfc1738_escape_unescaped(selector), html_quote(name));
} else {
/* Standard link */
- snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
- icon_url, host, gtype, escaped_selector, html_quote(name));
+ outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
+ icon_url, host, gtype, escaped_selector, html_quote(name));
}
}
safe_free(escaped_selector);
- outbuf.append(tmpbuf);
} else {
memset(line, '\0', TEMP_BUF_SIZE);
continue;
@@ -643,13 +641,12 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
break;
if (gopherState->cso_recno != recno) {
- snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>Record# %d<br><i>%s</i></H2>\n<PRE>", recno, html_quote(result));
+ outbuf.appendf("</PRE><HR noshade size=\"1px\"><H2>Record# %d<br><i>%s</i></H2>\n<PRE>", recno, html_quote(result));
gopherState->cso_recno = recno;
} else {
- snprintf(tmpbuf, TEMP_BUF_SIZE, "%s\n", html_quote(result));
+ outbuf.appendf("%s\n", html_quote(result));
}
- outbuf.append(tmpbuf);
break;
} else {
int code;
@@ -677,8 +674,7 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
case 502: { /* Too Many Matches */
/* Print the message the server returns */
- snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>%s</H2>\n<PRE>", html_quote(result));
- outbuf.append(tmpbuf);
+ outbuf.appendf("</PRE><HR noshade size=\"1px\"><H2>%s</H2>\n<PRE>", html_quote(result));
break;
}
@@ -694,13 +690,12 @@ gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
} /* while loop */
- if (outbuf.size() > 0) {
- entry->append(outbuf.rawBuf(), outbuf.size());
+ if (outbuf.length() > 0) {
+ entry->append(outbuf.rawContent(), outbuf.length());
/* now let start sending stuff to client */
entry->flush();
}
- outbuf.clean();
return;
}

View File

@ -0,0 +1,38 @@
commit 4031c6c2b004190fdffbc19dab7cd0305a2025b7 (refs/remotes/origin/v4, refs/remotes/github/v4, refs/heads/v4)
Author: Amos Jeffries <yadij@users.noreply.github.com>
Date: 2022-08-09 23:34:54 +0000
Bug 3193 pt2: NTLM decoder truncating strings (#1114)
The initial bug fix overlooked large 'offset' causing integer
wrap to extract a too-short length string.
Improve debugs and checks sequence to clarify cases and ensure
that all are handled correctly.
diff --git a/lib/ntlmauth/ntlmauth.cc b/lib/ntlmauth/ntlmauth.cc
index 5d9637290..f00fd51f8 100644
--- a/lib/ntlmauth/ntlmauth.cc
+++ b/lib/ntlmauth/ntlmauth.cc
@@ -107,10 +107,19 @@ ntlm_fetch_string(const ntlmhdr *packet, const int32_t packet_size, const strhdr
int32_t o = le32toh(str->offset);
// debug("ntlm_fetch_string(plength=%d,l=%d,o=%d)\n",packet_size,l,o);
- if (l < 0 || l > NTLM_MAX_FIELD_LENGTH || o + l > packet_size || o == 0) {
- debug("ntlm_fetch_string: insane data (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
+ if (l < 0 || l > NTLM_MAX_FIELD_LENGTH) {
+ debug("ntlm_fetch_string: insane string length (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
return rv;
}
+ else if (o <= 0 || o > packet_size) {
+ debug("ntlm_fetch_string: insane string offset (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
+ return rv;
+ }
+ else if (l > packet_size - o) {
+ debug("ntlm_fetch_string: truncated string data (pkt-sz: %d, fetch len: %d, offset: %d)\n", packet_size,l,o);
+ return rv;
+ }
+
rv.str = (char *)packet + o;
rv.l = 0;
if ((flags & NTLM_NEGOTIATE_ASCII) == 0) {

View File

@ -0,0 +1,24 @@
diff --git a/src/anyp/Uri.cc b/src/anyp/Uri.cc
index 20b9bf1..81ebb18 100644
--- a/src/anyp/Uri.cc
+++ b/src/anyp/Uri.cc
@@ -173,6 +173,10 @@ urlInitialize(void)
assert(0 == matchDomainName("*.foo.com", ".foo.com", mdnHonorWildcards));
assert(0 != matchDomainName("*.foo.com", "foo.com", mdnHonorWildcards));
+ assert(0 != matchDomainName("foo.com", ""));
+ assert(0 != matchDomainName("foo.com", "", mdnHonorWildcards));
+ assert(0 != matchDomainName("foo.com", "", mdnRejectSubsubDomains));
+
/* more cases? */
}
@@ -756,6 +760,8 @@ matchDomainName(const char *h, const char *d, MatchDomainNameFlags flags)
return -1;
dl = strlen(d);
+ if (dl == 0)
+ return 1;
/*
* Start at the ends of the two strings and work towards the

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
diff --git a/src/auth/digest/Config.cc b/src/auth/digest/Config.cc
index 6a9736f..0a883fa 100644
--- a/src/auth/digest/Config.cc
+++ b/src/auth/digest/Config.cc
@@ -847,11 +847,15 @@ Auth::Digest::Config::decode(char const *proxy_auth, const char *aRequestRealm)
break;
case DIGEST_NC:
- if (value.size() != 8) {
+ if (value.size() == 8) {
+ // for historical reasons, the nc value MUST be exactly 8 bytes
+ static_assert(sizeof(digest_request->nc) == 8 + 1, "bad nc buffer size");
+ xstrncpy(digest_request->nc, value.rawBuf(), value.size() + 1);
+ debugs(29, 9, "Found noncecount '" << digest_request->nc << "'");
+ } else {
debugs(29, 9, "Invalid nc '" << value << "' in '" << temp << "'");
+ digest_request->nc[0] = 0;
}
- xstrncpy(digest_request->nc, value.rawBuf(), value.size() + 1);
- debugs(29, 9, "Found noncecount '" << digest_request->nc << "'");
break;
case DIGEST_CNONCE:

View File

@ -0,0 +1,30 @@
commit 77b3fb4df0f126784d5fd4967c28ed40eb8d521b
Author: Alex Rousskov <rousskov@measurement-factory.com>
Date: Wed Oct 25 19:41:45 2023 +0000
RFC 1123: Fix date parsing (#1538)
The bug was discovered and detailed by Joshua Rogers at
https://megamansec.github.io/Squid-Security-Audit/datetime-overflow.html
where it was filed as "1-Byte Buffer OverRead in RFC 1123 date/time
Handling".
diff --git a/lib/rfc1123.c b/lib/rfc1123.c
index e5bf9a4d7..cb484cc00 100644
--- a/lib/rfc1123.c
+++ b/lib/rfc1123.c
@@ -50,7 +50,13 @@ make_month(const char *s)
char month[3];
month[0] = xtoupper(*s);
+ if (!month[0])
+ return -1; // protects *(s + 1) below
+
month[1] = xtolower(*(s + 1));
+ if (!month[1])
+ return -1; // protects *(s + 2) below
+
month[2] = xtolower(*(s + 2));
for (i = 0; i < 12; i++)

View File

@ -0,0 +1,62 @@
diff --git a/src/ipc.cc b/src/ipc.cc
index 42e11e6..a68e623 100644
--- a/src/ipc.cc
+++ b/src/ipc.cc
@@ -19,6 +19,11 @@
#include "SquidConfig.h"
#include "SquidIpc.h"
#include "tools.h"
+#include <cstdlib>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
static const char *hello_string = "hi there\n";
#ifndef HELLO_BUF_SZ
@@ -365,6 +370,22 @@ ipcCreate(int type, const char *prog, const char *const args[], const char *name
}
PutEnvironment();
+
+ // A dup(2) wrapper that reports and exits the process on errors. The
+ // exiting logic is only suitable for this child process context.
+ const auto dupOrExit = [prog,name](const int oldFd) {
+ const auto newFd = dup(oldFd);
+ if (newFd < 0) {
+ const auto savedErrno = errno;
+ debugs(54, DBG_CRITICAL, "ERROR: Helper process initialization failure: " << name <<
+ Debug::Extra << "helper (CHILD) PID: " << getpid() <<
+ Debug::Extra << "helper program name: " << prog <<
+ Debug::Extra << "dup(2) system call error for FD " << oldFd << ": " << xstrerr(savedErrno));
+ _exit(EXIT_FAILURE);
+ }
+ return newFd;
+ };
+
/*
* This double-dup stuff avoids problems when one of
* crfd, cwfd, or debug_log are in the rage 0-2.
@@ -372,17 +393,16 @@ ipcCreate(int type, const char *prog, const char *const args[], const char *name
do {
/* First make sure 0-2 is occupied by something. Gets cleaned up later */
- x = dup(crfd);
- assert(x > -1);
- } while (x < 3 && x > -1);
+ x = dupOrExit(crfd);
+ } while (x < 3);
close(x);
- t1 = dup(crfd);
+ t1 = dupOrExit(crfd);
- t2 = dup(cwfd);
+ t2 = dupOrExit(cwfd);
- t3 = dup(fileno(debug_log));
+ t3 = dupOrExit(fileno(debug_log));
assert(t1 > 2 && t2 > 2 && t3 > 2);

View File

@ -0,0 +1,50 @@
diff --git a/src/ClientRequestContext.h b/src/ClientRequestContext.h
index fe2edf6..47aa935 100644
--- a/src/ClientRequestContext.h
+++ b/src/ClientRequestContext.h
@@ -81,6 +81,10 @@ public:
#endif
ErrorState *error; ///< saved error page for centralized/delayed processing
bool readNextRequest; ///< whether Squid should read after error handling
+
+#if FOLLOW_X_FORWARDED_FOR
+ size_t currentXffHopNumber = 0; ///< number of X-Forwarded-For header values processed so far
+#endif
};
#endif /* SQUID_CLIENTREQUESTCONTEXT_H */
diff --git a/src/client_side_request.cc b/src/client_side_request.cc
index 1c6ff62..b758f6f 100644
--- a/src/client_side_request.cc
+++ b/src/client_side_request.cc
@@ -78,6 +78,11 @@
static const char *const crlf = "\r\n";
#if FOLLOW_X_FORWARDED_FOR
+
+#if !defined(SQUID_X_FORWARDED_FOR_HOP_MAX)
+#define SQUID_X_FORWARDED_FOR_HOP_MAX 64
+#endif
+
static void clientFollowXForwardedForCheck(allow_t answer, void *data);
#endif /* FOLLOW_X_FORWARDED_FOR */
@@ -485,8 +490,16 @@ clientFollowXForwardedForCheck(allow_t answer, void *data)
/* override the default src_addr tested if we have to go deeper than one level into XFF */
Filled(calloutContext->acl_checklist)->src_addr = request->indirect_client_addr;
}
- calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
- return;
+ if (++calloutContext->currentXffHopNumber < SQUID_X_FORWARDED_FOR_HOP_MAX) {
+ calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
+ return;
+ }
+ const auto headerName = Http::HeaderLookupTable.lookup(Http::HdrType::X_FORWARDED_FOR).name;
+ debugs(28, DBG_CRITICAL, "ERROR: Ignoring trailing " << headerName << " addresses" <<
+ Debug::Extra << "addresses allowed by follow_x_forwarded_for: " << calloutContext->currentXffHopNumber <<
+ Debug::Extra << "last/accepted address: " << request->indirect_client_addr <<
+ Debug::Extra << "ignored trailing addresses: " << request->x_forwarded_for_iterator);
+ // fall through to resume clientAccessCheck() processing
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,193 @@
diff --git a/src/http.cc b/src/http.cc
index b006300..023e411 100644
--- a/src/http.cc
+++ b/src/http.cc
@@ -52,6 +52,7 @@
#include "rfc1738.h"
#include "SquidConfig.h"
#include "SquidTime.h"
+#include "SquidMath.h"
#include "StatCounters.h"
#include "Store.h"
#include "StrList.h"
@@ -1150,18 +1151,26 @@ HttpStateData::readReply(const CommIoCbParams &io)
* Plus, it breaks our lame *HalfClosed() detection
*/
- Must(maybeMakeSpaceAvailable(true));
- CommIoCbParams rd(this); // will be expanded with ReadNow results
- rd.conn = io.conn;
- rd.size = entry->bytesWanted(Range<size_t>(0, inBuf.spaceSize()));
+ size_t moreDataPermission = 0;
+ if ((!canBufferMoreReplyBytes(&moreDataPermission) || !moreDataPermission)) {
+ abortTransaction("ready to read required data, but the read buffer is full and cannot be drained");
+ return;
+ }
+
+ const auto readSizeMax = maybeMakeSpaceAvailable(moreDataPermission);
+ // TODO: Move this logic inside maybeMakeSpaceAvailable():
+ const auto readSizeWanted = readSizeMax ? entry->bytesWanted(Range<size_t>(0, readSizeMax)) : 0;
- if (rd.size <= 0) {
+ if (readSizeWanted <= 0) {
assert(entry->mem_obj);
AsyncCall::Pointer nilCall;
entry->mem_obj->delayRead(DeferredRead(readDelayed, this, CommRead(io.conn, NULL, 0, nilCall)));
return;
}
+ CommIoCbParams rd(this); // will be expanded with ReadNow results
+ rd.conn = io.conn;
+ rd.size = readSizeWanted;
switch (Comm::ReadNow(rd, inBuf)) {
case Comm::INPROGRESS:
if (inBuf.isEmpty())
@@ -1520,8 +1529,11 @@ HttpStateData::maybeReadVirginBody()
if (!Comm::IsConnOpen(serverConnection) || fd_table[serverConnection->fd].closing())
return;
- if (!maybeMakeSpaceAvailable(false))
+ size_t moreDataPermission = 0;
+ if ((!canBufferMoreReplyBytes(&moreDataPermission)) || !moreDataPermission) {
+ abortTransaction("more response bytes required, but the read buffer is full and cannot be drained");
return;
+ }
// XXX: get rid of the do_next_read flag
// check for the proper reasons preventing read(2)
@@ -1539,40 +1551,79 @@ HttpStateData::maybeReadVirginBody()
Comm::Read(serverConnection, call);
}
+/// Desired inBuf capacity based on various capacity preferences/limits:
+/// * a smaller buffer may not hold enough for look-ahead header/body parsers;
+/// * a smaller buffer may result in inefficient tiny network reads;
+/// * a bigger buffer may waste memory;
+/// * a bigger buffer may exceed SBuf storage capabilities (SBuf::maxSize);
+size_t
+HttpStateData::calcReadBufferCapacityLimit() const
+{
+ if (!flags.headers_parsed)
+ return Config.maxReplyHeaderSize;
+
+ // XXX: Our inBuf is not used to maintain the read-ahead gap, and using
+ // Config.readAheadGap like this creates huge read buffers for large
+ // read_ahead_gap values. TODO: Switch to using tcp_recv_bufsize as the
+ // primary read buffer capacity factor.
+ //
+ // TODO: Cannot reuse throwing NaturalCast() here. Consider removing
+ // .value() dereference in NaturalCast() or add/use NaturalCastOrMax().
+ const auto configurationPreferences = NaturalSum<size_t>(Config.readAheadGap).second ? NaturalSum<size_t>(Config.readAheadGap).first : SBuf::maxSize;
+
+ // TODO: Honor TeChunkedParser look-ahead and trailer parsing requirements
+ // (when explicit configurationPreferences are set too low).
+
+ return std::min<size_t>(configurationPreferences, SBuf::maxSize);
+}
+
+/// The maximum number of virgin reply bytes we may buffer before we violate
+/// the currently configured response buffering limits.
+/// \retval std::nullopt means that no more virgin response bytes can be read
+/// \retval 0 means that more virgin response bytes may be read later
+/// \retval >0 is the number of bytes that can be read now (subject to other constraints)
bool
-HttpStateData::maybeMakeSpaceAvailable(bool doGrow)
+HttpStateData::canBufferMoreReplyBytes(size_t *maxReadSize) const
{
- // how much we are allowed to buffer
- const int limitBuffer = (flags.headers_parsed ? Config.readAheadGap : Config.maxReplyHeaderSize);
-
- if (limitBuffer < 0 || inBuf.length() >= (SBuf::size_type)limitBuffer) {
- // when buffer is at or over limit already
- debugs(11, 7, "will not read up to " << limitBuffer << ". buffer has (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
- debugs(11, DBG_DATA, "buffer has {" << inBuf << "}");
- // Process next response from buffer
- processReply();
- return false;
+#if USE_ADAPTATION
+ // If we do not check this now, we may say the final "no" prematurely below
+ // because inBuf.length() will decrease as adaptation drains buffered bytes.
+ if (responseBodyBuffer) {
+ debugs(11, 3, "yes, but waiting for adaptation to drain read buffer");
+ *maxReadSize = 0; // yes, we may be able to buffer more (but later)
+ return true;
+ }
+#endif
+
+ const auto maxCapacity = calcReadBufferCapacityLimit();
+ if (inBuf.length() >= maxCapacity) {
+ debugs(11, 3, "no, due to a full buffer: " << inBuf.length() << '/' << inBuf.spaceSize() << "; limit: " << maxCapacity);
+ return false; // no, configuration prohibits buffering more
}
+ *maxReadSize = (maxCapacity - inBuf.length()); // positive
+ debugs(11, 7, "yes, may read up to " << *maxReadSize << " into " << inBuf.length() << '/' << inBuf.spaceSize());
+ return true; // yes, can read up to this many bytes (subject to other constraints)
+}
+
+/// prepare read buffer for reading
+/// \return the maximum number of bytes the caller should attempt to read
+/// \retval 0 means that the caller should delay reading
+size_t
+HttpStateData::maybeMakeSpaceAvailable(const size_t maxReadSize)
+{
// how much we want to read
- const size_t read_size = calcBufferSpaceToReserve(inBuf.spaceSize(), (limitBuffer - inBuf.length()));
+ const size_t read_size = calcBufferSpaceToReserve(inBuf.spaceSize(), maxReadSize);
- if (!read_size) {
+ if (read_size < 2) {
debugs(11, 7, "will not read up to " << read_size << " into buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
- return false;
+ return 0;
}
- // just report whether we could grow or not, do not actually do it
- if (doGrow)
- return (read_size >= 2);
-
// we may need to grow the buffer
inBuf.reserveSpace(read_size);
- debugs(11, 8, (!flags.do_next_read ? "will not" : "may") <<
- " read up to " << read_size << " bytes info buf(" << inBuf.length() << "/" << inBuf.spaceSize() <<
- ") from " << serverConnection);
-
- return (inBuf.spaceSize() >= 2); // only read if there is 1+ bytes of space available
+ debugs(11, 7, "may read up to " << read_size << " bytes info buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
+ return read_size;
}
/// called after writing the very last request byte (body, last-chunk, etc)
diff --git a/src/http.h b/src/http.h
index 8965b77..007d2e6 100644
--- a/src/http.h
+++ b/src/http.h
@@ -15,6 +15,8 @@
#include "http/StateFlags.h"
#include "sbuf/SBuf.h"
+#include <optional>
+
class FwdState;
class HttpHeader;
@@ -107,16 +109,9 @@ private:
void abortTransaction(const char *reason) { abortAll(reason); } // abnormal termination
- /**
- * determine if read buffer can have space made available
- * for a read.
- *
- * \param grow whether to actually expand the buffer
- *
- * \return whether the buffer can be grown to provide space
- * regardless of whether the grow actually happened.
- */
- bool maybeMakeSpaceAvailable(bool grow);
+ size_t calcReadBufferCapacityLimit() const;
+ bool canBufferMoreReplyBytes(size_t *maxReadSize) const;
+ size_t maybeMakeSpaceAvailable(size_t maxReadSize);
// consuming request body
virtual void handleMoreRequestBodyAvailable();

View File

@ -0,0 +1,105 @@
diff --git a/src/SquidString.h b/src/SquidString.h
index a791885..b9aef38 100644
--- a/src/SquidString.h
+++ b/src/SquidString.h
@@ -114,7 +114,16 @@ private:
size_type len_; /* current length */
- static const size_type SizeMax_ = 65535; ///< 64K limit protects some fixed-size buffers
+ /// An earlier 64KB limit was meant to protect some fixed-size buffers, but
+ /// (a) we do not know where those buffers are (or whether they still exist)
+ /// (b) too many String users unknowingly exceeded that limit and asserted.
+ /// We are now using a larger limit to reduce the number of (b) cases,
+ /// especially cases where "compact" lists of items grow 50% in size when we
+ /// convert them to canonical form. The new limit is selected to withstand
+ /// concatenation and ~50% expansion of two HTTP headers limited by default
+ /// request_header_max_size and reply_header_max_size settings.
+ static const size_type SizeMax_ = 3*64*1024 - 1;
+
/// returns true after increasing the first argument by extra if the sum does not exceed SizeMax_
static bool SafeAdd(size_type &base, size_type extra) { if (extra <= SizeMax_ && base <= SizeMax_ - extra) { base += extra; return true; } return false; }
diff --git a/src/cache_cf.cc b/src/cache_cf.cc
index a9c1b7e..46f07bb 100644
--- a/src/cache_cf.cc
+++ b/src/cache_cf.cc
@@ -935,6 +935,18 @@ configDoConfigure(void)
(uint32_t)Config.maxRequestBufferSize, (uint32_t)Config.maxRequestHeaderSize);
}
+ // Warn about the dangers of exceeding String limits when manipulating HTTP
+ // headers. Technically, we do not concatenate _requests_, so we could relax
+ // their check, but we keep the two checks the same for simplicity sake.
+ const auto safeRawHeaderValueSizeMax = (String::SizeMaxXXX()+1)/3;
+ // TODO: static_assert(safeRawHeaderValueSizeMax >= 64*1024); // no WARNINGs for default settings
+ if (Config.maxRequestHeaderSize > safeRawHeaderValueSizeMax)
+ debugs(3, DBG_CRITICAL, "WARNING: Increasing request_header_max_size beyond " << safeRawHeaderValueSizeMax <<
+ " bytes makes Squid more vulnerable to denial-of-service attacks; configured value: " << Config.maxRequestHeaderSize << " bytes");
+ if (Config.maxReplyHeaderSize > safeRawHeaderValueSizeMax)
+ debugs(3, DBG_CRITICAL, "WARNING: Increasing reply_header_max_size beyond " << safeRawHeaderValueSizeMax <<
+ " bytes makes Squid more vulnerable to denial-of-service attacks; configured value: " << Config.maxReplyHeaderSize << " bytes");
+
/*
* Disable client side request pipelining if client_persistent_connections OFF.
* Waste of resources queueing any pipelined requests when the first will close the connection.
diff --git a/src/cf.data.pre b/src/cf.data.pre
index bc2ddcd..d55b870 100644
--- a/src/cf.data.pre
+++ b/src/cf.data.pre
@@ -6196,11 +6196,14 @@ TYPE: b_size_t
DEFAULT: 64 KB
LOC: Config.maxRequestHeaderSize
DOC_START
- This specifies the maximum size for HTTP headers in a request.
- Request headers are usually relatively small (about 512 bytes).
- Placing a limit on the request header size will catch certain
- bugs (for example with persistent connections) and possibly
- buffer-overflow or denial-of-service attacks.
+ This directives limits the header size of a received HTTP request
+ (including request-line). Increasing this limit beyond its 64 KB default
+ exposes certain old Squid code to various denial-of-service attacks. This
+ limit also applies to received FTP commands.
+
+ This limit has no direct affect on Squid memory consumption.
+
+ Squid does not check this limit when sending requests.
DOC_END
NAME: reply_header_max_size
@@ -6209,11 +6212,14 @@ TYPE: b_size_t
DEFAULT: 64 KB
LOC: Config.maxReplyHeaderSize
DOC_START
- This specifies the maximum size for HTTP headers in a reply.
- Reply headers are usually relatively small (about 512 bytes).
- Placing a limit on the reply header size will catch certain
- bugs (for example with persistent connections) and possibly
- buffer-overflow or denial-of-service attacks.
+ This directives limits the header size of a received HTTP response
+ (including status-line). Increasing this limit beyond its 64 KB default
+ exposes certain old Squid code to various denial-of-service attacks. This
+ limit also applies to FTP command responses.
+
+ Squid also checks this limit when loading hit responses from disk cache.
+
+ Squid does not check this limit when sending responses.
DOC_END
NAME: request_body_max_size
diff --git a/src/http.cc b/src/http.cc
index 877172d..b006300 100644
--- a/src/http.cc
+++ b/src/http.cc
@@ -1820,8 +1820,9 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request,
String strFwd = hdr_in->getList(Http::HdrType::X_FORWARDED_FOR);
- // if we cannot double strFwd size, then it grew past 50% of the limit
- if (!strFwd.canGrowBy(strFwd.size())) {
+ // Detect unreasonably long header values. And paranoidly check String
+ // limits: a String ought to accommodate two reasonable-length values.
+ if (strFwd.size() > 32*1024 || !strFwd.canGrowBy(strFwd.size())) {
// There is probably a forwarding loop with Via detection disabled.
// If we do nothing, String will assert on overflow soon.
// TODO: Terminate all transactions with huge XFF?

View File

@ -0,0 +1,32 @@
diff --git a/src/clients/FtpGateway.cc b/src/clients/FtpGateway.cc
index da9867f..e992638 100644
--- a/src/clients/FtpGateway.cc
+++ b/src/clients/FtpGateway.cc
@@ -1084,16 +1084,17 @@ Ftp::Gateway::checkAuth(const HttpHeader * req_hdr)
void
Ftp::Gateway::checkUrlpath()
{
- static SBuf str_type_eq("type=");
- auto t = request->url.path().rfind(';');
-
- if (t != SBuf::npos) {
- auto filenameEnd = t-1;
- if (request->url.path().substr(++t).cmp(str_type_eq, str_type_eq.length()) == 0) {
- t += str_type_eq.length();
- typecode = (char)xtoupper(request->url.path()[t]);
- request->url.path(request->url.path().substr(0,filenameEnd));
- }
+ // If typecode was specified, extract it and leave just the filename in
+ // url.path. Tolerate trailing garbage or missing typecode value. Roughly:
+ // [filename] ;type=[typecode char] [trailing garbage]
+ static const SBuf middle(";type=");
+ const auto typeSpecStart = request->url.path().find(middle);
+ if (typeSpecStart != SBuf::npos) {
+ const auto fullPath = request->url.path();
+ const auto typecodePos = typeSpecStart + middle.length();
+ typecode = (typecodePos < fullPath.length()) ?
+ static_cast<char>(xtoupper(fullPath[typecodePos])) : '\0';
+ request->url.path(fullPath.substr(0, typeSpecStart));
}
int l = request->url.path().length();

View File

@ -0,0 +1,163 @@
diff --git a/src/client_side.cc b/src/client_side.cc
index f57f3f7..ab393e4 100644
--- a/src/client_side.cc
+++ b/src/client_side.cc
@@ -906,7 +906,7 @@ ConnStateData::kick()
* We are done with the response, and we are either still receiving request
* body (early response!) or have already stopped receiving anything.
*
- * If we are still receiving, then clientParseRequest() below will fail.
+ * If we are still receiving, then parseRequests() below will fail.
* (XXX: but then we will call readNextRequest() which may succeed and
* execute a smuggled request as we are not done with the current request).
*
@@ -926,28 +926,12 @@ ConnStateData::kick()
* Attempt to parse a request from the request buffer.
* If we've been fed a pipelined request it may already
* be in our read buffer.
- *
- \par
- * This needs to fall through - if we're unlucky and parse the _last_ request
- * from our read buffer we may never re-register for another client read.
*/
- if (clientParseRequests()) {
- debugs(33, 3, clientConnection << ": parsed next request from buffer");
- }
+ parseRequests();
- /** \par
- * Either we need to kick-start another read or, if we have
- * a half-closed connection, kill it after the last request.
- * This saves waiting for half-closed connections to finished being
- * half-closed _AND_ then, sometimes, spending "Timeout" time in
- * the keepalive "Waiting for next request" state.
- */
- if (commIsHalfClosed(clientConnection->fd) && pipeline.empty()) {
- debugs(33, 3, "half-closed client with no pending requests, closing");
- clientConnection->close();
+ if (!isOpen())
return;
- }
/** \par
* At this point we either have a parsed request (which we've
@@ -2058,16 +2042,11 @@ ConnStateData::receivedFirstByte()
commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
}
-/**
- * Attempt to parse one or more requests from the input buffer.
- * Returns true after completing parsing of at least one request [header]. That
- * includes cases where parsing ended with an error (e.g., a huge request).
- */
-bool
-ConnStateData::clientParseRequests()
+/// Attempt to parse one or more requests from the input buffer.
+/// May close the connection.
+void
+ConnStateData::parseRequests()
{
- bool parsed_req = false;
-
debugs(33, 5, HERE << clientConnection << ": attempting to parse");
// Loop while we have read bytes that are not needed for producing the body
@@ -2116,8 +2095,6 @@ ConnStateData::clientParseRequests()
processParsedRequest(context);
- parsed_req = true; // XXX: do we really need to parse everything right NOW ?
-
if (context->mayUseConnection()) {
debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection");
break;
@@ -2130,8 +2107,19 @@ ConnStateData::clientParseRequests()
}
}
- /* XXX where to 'finish' the parsing pass? */
- return parsed_req;
+ debugs(33, 7, "buffered leftovers: " << inBuf.length());
+
+ if (isOpen() && commIsHalfClosed(clientConnection->fd)) {
+ if (pipeline.empty()) {
+ // we processed what we could parse, and no more data is coming
+ debugs(33, 5, "closing half-closed without parsed requests: " << clientConnection);
+ clientConnection->close();
+ } else {
+ // we parsed what we could, and no more data is coming
+ debugs(33, 5, "monitoring half-closed while processing parsed requests: " << clientConnection);
+ flags.readMore = false; // may already be false
+ }
+ }
}
void
@@ -2148,23 +2136,7 @@ ConnStateData::afterClientRead()
if (pipeline.empty())
fd_note(clientConnection->fd, "Reading next request");
- if (!clientParseRequests()) {
- if (!isOpen())
- return;
- /*
- * If the client here is half closed and we failed
- * to parse a request, close the connection.
- * The above check with connFinishedWithConn() only
- * succeeds _if_ the buffer is empty which it won't
- * be if we have an incomplete request.
- * XXX: This duplicates ConnStateData::kick
- */
- if (pipeline.empty() && commIsHalfClosed(clientConnection->fd)) {
- debugs(33, 5, clientConnection << ": half-closed connection, no completed request parsed, connection closing.");
- clientConnection->close();
- return;
- }
- }
+ parseRequests();
if (!isOpen())
return;
@@ -3945,7 +3917,7 @@ ConnStateData::notePinnedConnectionBecameIdle(PinnedIdleContext pic)
startPinnedConnectionMonitoring();
if (pipeline.empty())
- kick(); // in case clientParseRequests() was blocked by a busy pic.connection
+ kick(); // in case parseRequests() was blocked by a busy pic.connection
}
/// Forward future client requests using the given server connection.
diff --git a/src/client_side.h b/src/client_side.h
index 9fe8463..dfb4d8e 100644
--- a/src/client_side.h
+++ b/src/client_side.h
@@ -85,7 +85,6 @@ public:
virtual void doneWithControlMsg();
/// Traffic parsing
- bool clientParseRequests();
void readNextRequest();
/// try to make progress on a transaction or read more I/O
@@ -373,6 +372,7 @@ private:
virtual bool connFinishedWithConn(int size);
virtual void checkLogging();
+ void parseRequests();
void clientAfterReadingRequests();
bool concurrentRequestQueueFilled() const;
diff --git a/src/tests/stub_client_side.cc b/src/tests/stub_client_side.cc
index d7efb0f..655ed83 100644
--- a/src/tests/stub_client_side.cc
+++ b/src/tests/stub_client_side.cc
@@ -14,7 +14,7 @@
#include "tests/STUB.h"
#include "client_side.h"
-bool ConnStateData::clientParseRequests() STUB_RETVAL(false)
+void ConnStateData::parseRequests() STUB
void ConnStateData::readNextRequest() STUB
bool ConnStateData::isOpen() const STUB_RETVAL(false)
void ConnStateData::kick() STUB

View File

@ -0,0 +1,156 @@
commit c08948c8b831a2ba73c676b48aa11ba1b58cc542
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Thu Dec 8 11:03:08 2022 +0100
Backport adding IP_BIND_ADDRESS_NO_PORT flag to outgoing connections
diff --git a/src/comm.cc b/src/comm.cc
index 0d5f34d..6811b54 100644
--- a/src/comm.cc
+++ b/src/comm.cc
@@ -58,6 +58,7 @@
*/
static IOCB commHalfClosedReader;
+static int comm_openex(int sock_type, int proto, Ip::Address &, int flags, const char *note);
static void comm_init_opened(const Comm::ConnectionPointer &conn, const char *note, struct addrinfo *AI);
static int comm_apply_flags(int new_socket, Ip::Address &addr, int flags, struct addrinfo *AI);
@@ -75,6 +76,7 @@ static EVH commHalfClosedCheck;
static void commPlanHalfClosedCheck();
static Comm::Flag commBind(int s, struct addrinfo &);
+static void commSetBindAddressNoPort(int);
static void commSetReuseAddr(int);
static void commSetNoLinger(int);
#ifdef TCP_NODELAY
@@ -201,6 +203,22 @@ comm_local_port(int fd)
return F->local_addr.port();
}
+/// sets the IP_BIND_ADDRESS_NO_PORT socket option to optimize ephemeral port
+/// reuse by outgoing TCP connections that must bind(2) to a source IP address
+static void
+commSetBindAddressNoPort(const int fd)
+{
+#if defined(IP_BIND_ADDRESS_NO_PORT)
+ int flag = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, reinterpret_cast<char*>(&flag), sizeof(flag)) < 0) {
+ const auto savedErrno = errno;
+ debugs(50, DBG_IMPORTANT, "ERROR: setsockopt(IP_BIND_ADDRESS_NO_PORT) failure: " << xstrerr(savedErrno));
+ }
+#else
+ (void)fd;
+#endif
+}
+
static Comm::Flag
commBind(int s, struct addrinfo &inaddr)
{
@@ -227,6 +245,10 @@ comm_open(int sock_type,
int flags,
const char *note)
{
+ // assume zero-port callers do not need to know the assigned port right away
+ if (sock_type == SOCK_STREAM && addr.port() == 0 && ((flags & COMM_DOBIND) || !addr.isAnyAddr()))
+ flags |= COMM_DOBIND_PORT_LATER;
+
return comm_openex(sock_type, proto, addr, flags, note);
}
@@ -328,7 +350,7 @@ comm_set_transparent(int fd)
* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE
* is OR of flags specified in defines.h:COMM_*
*/
-int
+static int
comm_openex(int sock_type,
int proto,
Ip::Address &addr,
@@ -476,6 +498,9 @@ comm_apply_flags(int new_socket,
if ( addr.isNoAddr() )
debugs(5,0,"CRITICAL: Squid is attempting to bind() port " << addr << "!!");
+ if ((flags & COMM_DOBIND_PORT_LATER))
+ commSetBindAddressNoPort(new_socket);
+
if (commBind(new_socket, *AI) != Comm::OK) {
comm_close(new_socket);
return -1;
diff --git a/src/comm.h b/src/comm.h
index c963e1c..9ff201d 100644
--- a/src/comm.h
+++ b/src/comm.h
@@ -43,7 +43,6 @@ void comm_import_opened(const Comm::ConnectionPointer &, const char *note, struc
/**
* Open a port specially bound for listening or sending through a specific port.
- * This is a wrapper providing IPv4/IPv6 failover around comm_openex().
* Please use for all listening sockets and bind() outbound sockets.
*
* It will open a socket bound for:
@@ -59,7 +58,6 @@ void comm_import_opened(const Comm::ConnectionPointer &, const char *note, struc
int comm_open_listener(int sock_type, int proto, Ip::Address &addr, int flags, const char *note);
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note);
-int comm_openex(int, int, Ip::Address &, int, const char *);
unsigned short comm_local_port(int fd);
int comm_udp_sendto(int sock, const Ip::Address &to, const void *buf, int buflen);
diff --git a/src/comm/ConnOpener.cc b/src/comm/ConnOpener.cc
index 25a30e4..2082214 100644
--- a/src/comm/ConnOpener.cc
+++ b/src/comm/ConnOpener.cc
@@ -263,7 +263,7 @@ Comm::ConnOpener::createFd()
if (callback_ == NULL || callback_->canceled())
return false;
- temporaryFd_ = comm_openex(SOCK_STREAM, IPPROTO_TCP, conn_->local, conn_->flags, host_);
+ temporaryFd_ = comm_open(SOCK_STREAM, IPPROTO_TCP, conn_->local, conn_->flags, host_);
if (temporaryFd_ < 0) {
sendAnswer(Comm::ERR_CONNECT, 0, "Comm::ConnOpener::createFd");
return false;
diff --git a/src/comm/Connection.h b/src/comm/Connection.h
index 4f2f23a..1e32c22 100644
--- a/src/comm/Connection.h
+++ b/src/comm/Connection.h
@@ -47,6 +47,8 @@ namespace Comm
#define COMM_DOBIND 0x08 // requires a bind()
#define COMM_TRANSPARENT 0x10 // arrived via TPROXY
#define COMM_INTERCEPTION 0x20 // arrived via NAT
+/// Internal Comm optimization: Keep the source port unassigned until connect(2)
+#define COMM_DOBIND_PORT_LATER 0x100
/**
* Store data about the physical and logical attributes of a connection.
diff --git a/src/ipc.cc b/src/ipc.cc
index e1d48fc..e92a27f 100644
--- a/src/ipc.cc
+++ b/src/ipc.cc
@@ -95,12 +95,12 @@ ipcCreate(int type, const char *prog, const char *const args[], const char *name
} else void(0)
if (type == IPC_TCP_SOCKET) {
- crfd = cwfd = comm_open(SOCK_STREAM,
+ crfd = cwfd = comm_open_listener(SOCK_STREAM,
0,
local_addr,
COMM_NOCLOEXEC,
name);
- prfd = pwfd = comm_open(SOCK_STREAM,
+ prfd = pwfd = comm_open_listener(SOCK_STREAM,
0, /* protocol */
local_addr,
0, /* blocking */
diff --git a/src/tests/stub_comm.cc b/src/tests/stub_comm.cc
index 58f85e4..5381ab2 100644
--- a/src/tests/stub_comm.cc
+++ b/src/tests/stub_comm.cc
@@ -46,7 +46,6 @@ int comm_open_uds(int sock_type, int proto, struct sockaddr_un* addr, int flags)
void comm_import_opened(const Comm::ConnectionPointer &, const char *note, struct addrinfo *AI) STUB
int comm_open_listener(int sock_type, int proto, Ip::Address &addr, int flags, const char *note) STUB_RETVAL(-1)
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note) STUB
-int comm_openex(int, int, Ip::Address &, int, tos_t tos, nfmark_t nfmark, const char *) STUB_RETVAL(-1)
unsigned short comm_local_port(int fd) STUB_RETVAL(0)
int comm_udp_sendto(int sock, const Ip::Address &to, const void *buf, int buflen) STUB_RETVAL(-1)
void commCallCloseHandlers(int fd) STUB

View File

@ -0,0 +1,25 @@
File: squid-4.15.tar.xz
Date: Mon 10 May 2021 10:50:22 UTC
Size: 2454176
MD5 : a593de9dc888dfeca4f1f7db2cd7d3b9
SHA1: 60bda34ba39657e2d870c8c1d2acece8a69c3075
Key : CD6DBF8EF3B17D3E <squid3@treenet.co.nz>
B068 84ED B779 C89B 044E 64E3 CD6D BF8E F3B1 7D3E
keyring = http://www.squid-cache.org/pgp.asc
keyserver = pool.sks-keyservers.net
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEsGiE7bd5yJsETmTjzW2/jvOxfT4FAmCZD/UACgkQzW2/jvOx
fT6zZg/+N8JMIYpmVJ7jm4lF0Ub2kEHGTOrc+tnlA3LGnlMQuTm61+BYk58g0SKW
96NbJ0cycW215Q34L+Y0tWuxEbIU01vIc3AA7rQd0LKy+fQU0OtBuhk5Vf4bKilW
uHEVIQZs9HmY6bqC+kgtCf49tVZvR8FZYNuilg/68+i/pQdwaDDmVb+j2oF7w+y2
dgkTFWtM5NTL6bqUVC0E7lLFPjzMefKfxkkpWFdV/VrAhU25jN24kpnjcfotQhdW
LDFy5okduz3ljso9pBYJfLeMXM1FZPpceC91zj32x3tcUyrD3yIoXob58rEKvfe4
RDXN4SuClsNe4UQ4oNoGIES9XtaYlOzPR1PlbqPUrdp1cDnhgLJ+1fkAixlMqCml
wuI1VIKSEY+nvRzQzFHnXJK9otV8QwMF76AHaytO9y+X6JuZmu/CcV1pq61qY9qv
t1/8z99wWSxpu17zthZgq64J225GF/hkBedaFlYoS5k5YUMDLPlRSCC0yPmb8JBF
Cns5i/aq2PmOx2ZhQ2RQIF416J3HK8Galw8ytFOjnEcn4ux9yzKNjL38p4+PJJA0
7GCMAqYYNjok3LSkGbiR7cPgbHnkqRfYbPFLMj4FtruoFlZ9L5MIU3oFvqA3ZR6l
Az6LaKLsAYPUmukAOPUSIrqpKXZHc7hdBWkT+7RYA4qaoU+9oIo=
=1Re1
-----END PGP SIGNATURE-----

View File

@ -1,8 +1,8 @@
%define __perl_requires %{SOURCE98}
Name: squid
Version: 4.11
Release: 4%{?dist}
Version: 4.15
Release: 10%{?dist}
Summary: The Squid proxy caching server
Epoch: 7
# See CREDITS for breakdown of non GPLv2+ code
@ -22,6 +22,8 @@ Source98: perl-requires-squid.sh
# Upstream patches
# Backported patches
Patch101: squid-4.15-ip-bind-address-no-port.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2072988
# Local patches
# Applying upstream patches first makes it less likely that local patches
@ -33,23 +35,40 @@ Patch204: squid-4.11-include-guards.patch
Patch205: squid-4.11-large-acl.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=980511
Patch206: squid-4.11-active-ftp.patch
# https://github.com/squid-cache/squid/commit/c26cd1cb6a60ff196ef13c00e82576d3bfeb2e30
Patch207: squid-4.11-systemd.patch
Patch208: squid-4.11-convert-ipv4.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=1890606
Patch209: squid-4.11-cache-mgr-auth-strdup.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2006121
Patch209: squid-4.15-ftp-filename-extraction.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2076717
Patch210: squid-4.15-halfclosed.patch
# Security fixes
# https://bugzilla.redhat.com/show_bug.cgi?id=1852554
Patch500: squid-4.11-CVE-2020-14058.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=1852550
Patch501: squid-4.11-CVE-2020-15049.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=1871705
Patch502: squid-4.11-CVE-2020-24606.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=1871702
Patch503: squid-4.11-CVE-2020-15811.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=1871700
Patch504: squid-4.11-CVE-2020-15810.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=1941506
Patch300: squid-4.15-CVE-2021-28116.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2100721
Patch301: squid-4.15-CVE-2021-46784.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2129771
Patch302: squid-4.15-CVE-2022-41318.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2245910
# +backported: https://github.com/squid-cache/squid/commit/417da4006cf5c97d44e74431b816fc58fec9e270
Patch303: squid-4.15-CVE-2023-46846.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2245916
Patch304: squid-4.15-CVE-2023-46847.patch
# https://issues.redhat.com/browse/RHEL-14792
Patch305: squid-4.15-CVE-2023-5824.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2248521
Patch306: squid-4.15-CVE-2023-46728.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2247567
Patch307: squid-4.15-CVE-2023-46724.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2252926
Patch308: squid-4.15-CVE-2023-49285.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2252923
Patch309: squid-4.15-CVE-2023-49286.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2254663
Patch310: squid-4.15-CVE-2023-50269.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2264309
Patch311: squid-4.15-CVE-2024-25617.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2268366
Patch312: squid-4.15-CVE-2024-25111.patch
Requires: bash >= 2.0
@ -101,6 +120,7 @@ lookup program (dnsserver), a program for retrieving FTP data
# Upstream patches
# Backported patches
%patch101 -p1 -b .ip-bind-address-no-port
# Local patches
%patch201 -p1 -b .config
@ -109,16 +129,25 @@ lookup program (dnsserver), a program for retrieving FTP data
%patch204 -p1 -b .include-guards
%patch205 -p1 -b .large_acl
%patch206 -p1 -b .active-ftp
%patch207 -p1 -b .systemd
%patch208 -p1 -R -b .convert-ipv4
%patch209 -p1 -b .cachemgr-strdup
%patch208 -p1 -b .convert-ipv4
%patch209 -p1 -b .ftp-fn-extraction
%patch210 -p1 -b .halfclosed
# Security patches
%patch500 -p1 -b .cve-2020-14058
%patch501 -p1 -b .cve-2020-15049
%patch502 -p1 -b .cve-2020-24606
%patch503 -p1 -b .CVE-2020-15811
%patch504 -p1 -b .CVE-2020-15810
%patch300 -p1 -b .CVE-2021-28116
%patch301 -p1 -b .CVE-2021-46784
%patch302 -p1 -b .CVE-2022-41318
%patch303 -p1 -b .CVE-2023-46846
%patch304 -p1 -b .CVE-2023-46847
%patch305 -p1 -b .CVE-2023-5824
%patch306 -p1 -b .CVE-2023-46728
%patch307 -p1 -b .CVE-2023-46724
%patch308 -p1 -b .CVE-2023-49285
%patch309 -p1 -b .CVE-2023-49286
%patch310 -p1 -b .CVE-2023-50269
%patch311 -p1 -b .CVE-2024-25617
%patch312 -p1 -b .CVE-2024-25111
# https://bugzilla.redhat.com/show_bug.cgi?id=1679526
# Patch in the vendor documentation and used different location for documentation
@ -335,6 +364,64 @@ fi
%changelog
* Tue Mar 19 2024 Luboš Uhliarik <luhliari@redhat.com> - 7:4.15-10
- Resolves: RHEL-28529 - squid:4/squid: Denial of Service in HTTP Chunked
Decoding (CVE-2024-25111)
- Resolves: RHEL-26088 - squid:4/squid: denial of service in HTTP header
parser (CVE-2024-25617)
* Fri Feb 02 2024 Luboš Uhliarik <luhliari@redhat.com> - 7:4.15-9
- Resolves: RHEL-19552 - squid:4/squid: denial of service in HTTP request
parsing (CVE-2023-50269)
* Fri Feb 02 2024 Luboš Uhliarik <luhliari@redhat.com> - 7:4.15-8
- Resolves: RHEL-18351 - squid:4/squid: Buffer over-read in the HTTP Message
processing feature (CVE-2023-49285)
- Resolves: RHEL-18342 - squid:4/squid: Incorrect Check of Function Return
Value In Helper Process management (CVE-2023-49286)
- Resolves: RHEL-18230 - squid:4/squid: Denial of Service in SSL Certificate
validation (CVE-2023-46724)
- Resolves: RHEL-15911 - squid:4/squid: NULL pointer dereference in the gopher
protocol code (CVE-2023-46728)
- Resolves: RHEL-18251 - squid crashes in assertion when a parent peer exists
- Resolves: RHEL-14794 - squid: squid multiple issues in HTTP response caching
(CVE-2023-5824)
- Resolves: RHEL-14803 - squid: squid: Denial of Service in HTTP Digest
Authentication (CVE-2023-46847)
- Resolves: RHEL-14777 - squid: squid: Request/Response smuggling in HTTP/1.1
and ICAP (CVE-2023-46846)
* Wed Aug 16 2023 Luboš Uhliarik <luhliari@redhat.com> - 7:4.15-7
- Resolves: #2076717 - Crash with half_closed_client on
* Thu Dec 08 2022 Tomas Korbar <tkorbar@redhat.com> - 4.15-6
- Resolves: #2072988 - [RFE] Add the "IP_BIND_ADDRESS_NO_PORT"
flag to sockets created for outgoing connections in the squid source code.
* Wed Sep 28 2022 Luboš Uhliarik <luhliari@redhat.com> - 7:4.15-5
- Resolves: #2130260 - CVE-2022-41318 squid:4/squid: buffer-over-read in SSPI and SMB
authentication
* Tue Jun 28 2022 Luboš Uhliarik <luhliari@redhat.com> - 7:4.15-4
- Resolves: #2100783 - CVE-2021-46784 squid:4/squid: DoS when processing gopher
server responses
* Wed Feb 09 2022 Luboš Uhliarik <luhliari@redhat.com> - 7:4.15-3
- Resolves: #1941506 - CVE-2021-28116 squid:4/squid: out-of-bounds read in WCCP
protocol data may lead to information disclosure
* Tue Jan 25 2022 Luboš Uhliarik <luhliari@redhat.com> - 7:4.15-2
- Resolves: #2006121 - SQUID shortens FTP Link wrong that contains a semi-colon
and as a result is not able to download zip file.CODE 404 TO CLIENT)
* Fri Jun 18 2021 Luboš Uhliarik <luhliari@redhat.com> - 7:4.15-1
- new version 4.15
- Resolves: #1964384 - squid:4 rebase to 4.15
* Wed Mar 31 2021 Lubos Uhliarik <luhliari@redhat.com> - 7:4.11-5
- Resolves: #1944261 - CVE-2020-25097 squid:4/squid: improper input validation
may allow a trusted client to perform HTTP Request Smuggling
* Mon Oct 26 2020 Lubos Uhliarik <luhliari@redhat.com> - 7:4.11-4
- Resolves: #1890606 - Fix for CVE 2019-13345 breaks authentication in
cachemgr.cgi