e2987f
From 9f7136105bff920413042a8806cc5de3f6086d6d Mon Sep 17 00:00:00 2001
e2987f
From: Thomas Leroy <32497783+p4zuu@users.noreply.github.com>
e2987f
Date: Tue, 28 Nov 2023 07:35:46 +0000
e2987f
Subject: [PATCH] Limit the number of allowed X-Forwarded-For hops (#1589)
e2987f
e2987f
Squid will ignore all X-Forwarded-For elements listed after the first 64
e2987f
addresses allowed by the follow_x_forwarded_for directive. A different
e2987f
limit can be specified by defining a C++ SQUID_X_FORWARDED_FOR_HOP_MAX
e2987f
macro, but that macro is not a supported Squid configuration interface
e2987f
and may change or disappear at any time.
e2987f
e2987f
Squid will log a cache.log ERROR if the hop limit has been reached.
e2987f
e2987f
This change works around problematic ACLChecklist and/or slow ACLs
e2987f
implementation that results in immediate nonBlockingCheck() callbacks.
e2987f
Such callbacks have caused many bugs and development complications. In
e2987f
clientFollowXForwardedForCheck() context, they lead to indirect
e2987f
recursion that was bound only by the number of allowed XFF entries,
e2987f
which could reach thousands and exhaust Squid process call stack.
e2987f
e2987f
This recursion bug was discovered and detailed by Joshua Rogers at
e2987f
https://megamansec.github.io/Squid-Security-Audit/xff-stackoverflow.html
e2987f
where it was filed as "X-Forwarded-For Stack Overflow".
e2987f
---
e2987f
 src/ClientRequestContext.h |  4 ++++
e2987f
 src/client_side_request.cc | 17 +++++++++++++++--
e2987f
 2 files changed, 19 insertions(+), 2 deletions(-)
e2987f
e2987f
diff --git a/src/ClientRequestContext.h b/src/ClientRequestContext.h
e2987f
index 7dde9bc..775e308 100644
e2987f
--- a/src/ClientRequestContext.h
e2987f
+++ b/src/ClientRequestContext.h
e2987f
@@ -90,6 +90,10 @@ public:
e2987f
     ErrorState *error; ///< saved error page for centralized/delayed processing
e2987f
     bool readNextRequest; ///< whether Squid should read after error handling
e2987f
 
e2987f
+#if FOLLOW_X_FORWARDED_FOR
e2987f
+    size_t currentXffHopNumber = 0; ///< number of X-Forwarded-For header values processed so far
e2987f
+#endif
e2987f
+
e2987f
 private:
e2987f
     CBDATA_CLASS2(ClientRequestContext);
e2987f
 };
e2987f
diff --git a/src/client_side_request.cc b/src/client_side_request.cc
e2987f
index 57d8975..9b77271 100644
e2987f
--- a/src/client_side_request.cc
e2987f
+++ b/src/client_side_request.cc
e2987f
@@ -362,6 +362,11 @@ clientBeginRequest(const HttpRequestMethod& method, char const *url, CSCB * stre
e2987f
     request->client_addr.setNoAddr();
e2987f
 
e2987f
 #if FOLLOW_X_FORWARDED_FOR
e2987f
+
e2987f
+#if !defined(SQUID_X_FORWARDED_FOR_HOP_MAX)
e2987f
+#define SQUID_X_FORWARDED_FOR_HOP_MAX 64
e2987f
+#endif
e2987f
+
e2987f
     request->indirect_client_addr.setNoAddr();
e2987f
 #endif /* FOLLOW_X_FORWARDED_FOR */
e2987f
 
e2987f
@@ -475,8 +480,15 @@ clientFollowXForwardedForCheck(allow_t answer, void *data)
e2987f
                 /* override the default src_addr tested if we have to go deeper than one level into XFF */
e2987f
                 Filled(calloutContext->acl_checklist)->src_addr = request->indirect_client_addr;
e2987f
             }
e2987f
-            calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
e2987f
-            return;
e2987f
+            if (++calloutContext->currentXffHopNumber < SQUID_X_FORWARDED_FOR_HOP_MAX) {
e2987f
+                calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
e2987f
+                return;
e2987f
+            }
e2987f
+            debugs(28, DBG_CRITICAL, "ERROR: Ignoring trailing X-Forwarded-For addresses" <<
e2987f
+                   Debug::Extra << "addresses allowed by follow_x_forwarded_for: " << calloutContext->currentXffHopNumber <<
e2987f
+                   Debug::Extra << "last/accepted address: " << request->indirect_client_addr <<
e2987f
+                   Debug::Extra << "ignored trailing addresses: " << request->x_forwarded_for_iterator);
e2987f
+            // fall through to resume clientAccessCheck() processing
e2987f
         }
e2987f
     } /*if (answer == ACCESS_ALLOWED &&
e2987f
         request->x_forwarded_for_iterator.size () != 0)*/
e2987f
-- 
e2987f
2.43.0
e2987f