aboutsummaryrefslogblamecommitdiff
path: root/www/squid26/files/follow_xff-2.5.patch
blob: 3c08e0a7c398301bb2a4d158af1db000441f92f2 (plain) (tree)






















































































































































































































































































































































                                                                                                       

                                                
               

                

                                   
                                   








                                            

                









                                       

                  

                               
                                    








                                               

                  









                                                                                

                  
                                                                                                                      

                                







                                                                    
! This is a reduced part of the original follow-XFF patchset from
! devel.squid-cache.org for use with the FreeBSD squid-2.5 port.
Index: src/acl.c
--- src/acl.c	13 May 2003 02:14:12 -0000	1.43.2.16
+++ src/acl.c	23 Nov 2003 14:20:12 -0000
@@ -2001,6 +2001,11 @@
     cbdataLock(A);
     if (request != NULL) {
 	checklist->request = requestLink(request);
+#if FOLLOW_X_FORWARDED_FOR
+	if (Config.onoff.acl_uses_indirect_client) {
+	    checklist->src_addr = request->indirect_client_addr;
+	} else
+#endif /* FOLLOW_X_FORWARDED_FOR */
 	checklist->src_addr = request->client_addr;
 	checklist->my_addr = request->my_addr;
 	checklist->my_port = request->my_port;
Index: src/cf.data.pre
--- src/cf.data.pre	7 Nov 2003 03:14:30 -0000	1.49.2.46
+++ src/cf.data.pre	23 Nov 2003 14:20:17 -0000
@@ -2065,6 +2065,92 @@
 NOCOMMENT_END
 DOC_END
 
+NAME: follow_x_forwarded_for
+TYPE: acl_access
+IFDEF: FOLLOW_X_FORWARDED_FOR
+LOC: Config.accessList.followXFF
+DEFAULT: none
+DEFAULT_IF_NONE: deny all
+DOC_START
+	Allowing or Denying the X-Forwarded-For header to be followed to
+	find the original source of a request.
+
+	Requests may pass through a chain of several other proxies
+	before reaching us.  The X-Forwarded-For header will contain a
+	comma-separated list of the IP addresses in the chain, with the
+	rightmost address being the most recent.
+
+	If a request reaches us from a source that is allowed by this
+	configuration item, then we consult the X-Forwarded-For header
+	to see where that host received the request from.  If the
+	X-Forwarded-For header contains multiple addresses, and if
+	acl_uses_indirect_client is on, then we continue backtracking
+	until we reach an address for which we are not allowed to
+	follow the X-Forwarded-For header, or until we reach the first
+	address in the list.  (If acl_uses_indirect_client is off, then
+	it's impossible to backtrack through more than one level of
+	X-Forwarded-For addresses.)
+
+	The end result of this process is an IP address that we will
+	refer to as the indirect client address.  This address may
+	be treated as the client address for access control, delay
+	pools and logging, depending on the acl_uses_indirect_client,
+	delay_pool_uses_indirect_client and log_uses_indirect_client
+	options.
+
+	SECURITY CONSIDERATIONS:
+
+		Any host for which we follow the X-Forwarded-For header
+		can place incorrect information in the header, and Squid
+		will use the incorrect information as if it were the
+		source address of the request.  This may enable remote
+		hosts to bypass any access control restrictions that are
+		based on the client's source addresses.
+
+	For example:
+
+		acl localhost src 127.0.0.1
+		acl my_other_proxy srcdomain .proxy.example.com
+		follow_x_forwarded_for allow localhost
+		follow_x_forwarded_for allow my_other_proxy
+DOC_END
+
+NAME: acl_uses_indirect_client
+COMMENT: on|off
+TYPE: onoff
+IFDEF: FOLLOW_X_FORWARDED_FOR
+DEFAULT: on
+LOC: Config.onoff.acl_uses_indirect_client
+DOC_START
+	Controls whether the indirect client address
+	(see follow_x_forwarded_for) is used instead of the
+	direct client address in acl matching.
+DOC_END
+
+NAME: delay_pool_uses_indirect_client
+COMMENT: on|off
+TYPE: onoff
+IFDEF: FOLLOW_X_FORWARDED_FOR && DELAY_POOLS
+DEFAULT: on
+LOC: Config.onoff.delay_pool_uses_indirect_client
+DOC_START
+	Controls whether the indirect client address
+	(see follow_x_forwarded_for) is used instead of the
+	direct client address in delay pools.
+DOC_END
+
+NAME: log_uses_indirect_client
+COMMENT: on|off
+TYPE: onoff
+IFDEF: FOLLOW_X_FORWARDED_FOR
+DEFAULT: on
+LOC: Config.onoff.log_uses_indirect_client
+DOC_START
+	Controls whether the indirect client address
+	(see follow_x_forwarded_for) is used instead of the
+	direct client address in the access log.
+DOC_END
+
 NAME: http_access
 TYPE: acl_access
 LOC: Config.accessList.http
Index: src/client_side.c
--- src/client_side.c	2 Sep 2003 02:13:45 -0000	1.47.2.39
+++ src/client_side.c	23 Nov 2003 14:20:22 -0000
@@ -109,6 +109,11 @@
 #if USE_IDENT
 static IDCB clientIdentDone;
 #endif
+#if FOLLOW_X_FORWARDED_FOR
+static void clientFollowXForwardedForStart(void *data);
+static void clientFollowXForwardedForNext(void *data);
+static void clientFollowXForwardedForDone(int answer, void *data);
+#endif /* FOLLOW_X_FORWARDED_FOR */
 static int clientOnlyIfCached(clientHttpRequest * http);
 static STCB clientSendMoreData;
 static STCB clientCacheHit;
@@ -177,10 +182,179 @@
     return ch;
 }
 
+#if FOLLOW_X_FORWARDED_FOR
+/*
+ * clientFollowXForwardedForStart() copies the X-Forwarded-For
+ * header into x_forwarded_for_iterator and passes control to
+ * clientFollowXForwardedForNext().
+ *
+ * clientFollowXForwardedForNext() checks the indirect_client_addr
+ * against the followXFF ACL and passes the result to
+ * clientFollowXForwardedForDone().
+ *
+ * clientFollowXForwardedForDone() either grabs the next address
+ * from the tail of x_forwarded_for_iterator and loops back to
+ * clientFollowXForwardedForNext(), or cleans up and passes control to
+ * clientAccessCheck().
+ */
+
+static void
+clientFollowXForwardedForStart(void *data)
+{
+    clientHttpRequest *http = data;
+    request_t *request = http->request;
+    if (Config.accessList.followXFF
+	&& httpHeaderHas(&request->header, HDR_X_FORWARDED_FOR))
+    {
+	request->x_forwarded_for_iterator = httpHeaderGetList(
+			&request->header, HDR_X_FORWARDED_FOR);
+	debug(33, 5) ("clientFollowXForwardedForStart: indirect_client_addr=%s XFF='%s'\n",
+			inet_ntoa(request->indirect_client_addr),
+			strBuf(request->x_forwarded_for_iterator));
+	clientFollowXForwardedForNext(http);
+    } else {
+	/* not configured to follow X-Forwarded-For, or nothing to follow */
+	debug(33, 5) ("clientFollowXForwardedForStart: nothing to do\n");
+	clientFollowXForwardedForDone(-1, http);
+    }
+}
+
+static void
+clientFollowXForwardedForNext(void *data)
+{
+    clientHttpRequest *http = data;
+    request_t *request = http->request;
+    debug(33, 5) ("clientFollowXForwardedForNext: indirect_client_addr=%s XFF='%s'\n",
+		    inet_ntoa(request->indirect_client_addr),
+		    strBuf(request->x_forwarded_for_iterator));
+    if (strLen(request->x_forwarded_for_iterator) != 0) {
+	/* check the acl to see whether to believe the X-Forwarded-For header */
+	http->acl_checklist = clientAclChecklistCreate(
+			Config.accessList.followXFF, http);
+	aclNBCheck(http->acl_checklist, clientFollowXForwardedForDone, http);
+    } else {
+	/* nothing left to follow */
+	debug(33, 5) ("clientFollowXForwardedForNext: nothing more to do\n");
+	clientFollowXForwardedForDone(-1, http);
+    }
+}
+
+static void
+clientFollowXForwardedForDone(int answer, void *data)
+{
+    clientHttpRequest *http = data;
+    request_t *request = http->request;
+    /*
+     * answer should be be ACCESS_ALLOWED or ACCESS_DENIED if we are
+     * called as a result of ACL checks, or -1 if we are called when
+     * there's nothing left to do.
+     */
+    if (answer == ACCESS_ALLOWED) {
+	/*
+	 * The IP address currently in request->indirect_client_addr
+	 * is trusted to use X-Forwarded-For.  Remove the last
+	 * comma-delimited element from x_forwarded_for_iterator and use
+	 * it to to replace indirect_client_addr, then repeat the cycle.
+	 */
+	const char *p;
+	const char *asciiaddr;
+	int l;
+	struct in_addr addr;
+	debug(33, 5) ("clientFollowXForwardedForDone: indirect_client_addr=%s is trusted\n",
+			inet_ntoa(request->indirect_client_addr));
+	p = strBuf(request->x_forwarded_for_iterator);
+	l = strLen(request->x_forwarded_for_iterator);
+
+	/*
+	 * XXX x_forwarded_for_iterator should really be a list of
+	 * IP addresses, but it's a String instead.  We have to
+	 * walk backwards through the String, biting off the last
+	 * comma-delimited part each time.  As long as the data is in
+	 * a String, we should probably implement and use a variant of
+	 * strListGetItem() that walks backwards instead of forwards
+	 * through a comma-separated list.  But we don't even do that;
+	 * we just do the work in-line here.
+	 */
+	/* skip trailing space and commas */
+	while (l > 0 && (p[l-1] == ',' || xisspace(p[l-1])))
+	    l--;
+	strCut(request->x_forwarded_for_iterator, l);
+	/* look for start of last item in list */
+	while (l > 0 && ! (p[l-1] == ',' || xisspace(p[l-1])))
+	    l--;
+	asciiaddr = p+l;
+	if (inet_aton(asciiaddr, &addr) == 0) {
+	    /* the address is not well formed; do not use it */
+	    debug(33, 3) ("clientFollowXForwardedForDone: malformed address '%s'\n",
+			    asciiaddr);
+	    goto done;
+	}
+	debug(33, 3) ("clientFollowXForwardedForDone: changing indirect_client_addr from %s to '%s'\n",
+		    inet_ntoa(request->indirect_client_addr),
+		    asciiaddr);
+	request->indirect_client_addr = addr;
+	strCut(request->x_forwarded_for_iterator, l);
+	if (! Config.onoff.acl_uses_indirect_client) {
+	    /*
+	     * If acl_uses_indirect_client is off, then it's impossible
+	     * to follow more than one level of X-Forwarded-For.
+	     */
+	    goto done;
+	}
+	clientFollowXForwardedForNext(http);
+	return;
+    } else if (answer == ACCESS_DENIED) {
+	debug(33, 5) ("clientFollowXForwardedForDone: indirect_client_addr=%s not trusted\n",
+			inet_ntoa(request->indirect_client_addr));
+    } else {
+	debug(33, 5) ("clientFollowXForwardedForDone: indirect_client_addr=%s nothing more to do\n",
+			inet_ntoa(request->indirect_client_addr));
+    }
+done:
+    /* clean up, and pass control to clientAccessCheck */
+    debug(33, 6) ("clientFollowXForwardedForDone: cleanup\n");
+    if (Config.onoff.log_uses_indirect_client) {
+	/*
+	 * Ensure that the access log shows the indirect client
+	 * instead of the direct client.
+	 */
+	ConnStateData *conn = http->conn;
+	conn->log_addr = request->indirect_client_addr;
+	conn->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
+	debug(33, 3) ("clientFollowXForwardedForDone: setting log_addr=%s\n",
+			inet_ntoa(conn->log_addr));
+    }
+    stringClean(&request->x_forwarded_for_iterator);
+    request->flags.done_follow_x_forwarded_for = 1;
+    http->acl_checklist = NULL; /* XXX do we need to aclChecklistFree() ? */
+    clientAccessCheck(http);
+}
+#endif /* FOLLOW_X_FORWARDED_FOR */
+
 void
 clientAccessCheck(void *data)
 {
     clientHttpRequest *http = data;
+#if FOLLOW_X_FORWARDED_FOR
+    if (! http->request->flags.done_follow_x_forwarded_for
+        && httpHeaderHas(&http->request->header, HDR_X_FORWARDED_FOR))
+    {
+	/*
+	 * There's an X-ForwardedFor header and we haven't yet tried
+	 * to follow it to find the indirect_client_addr.  Follow it now.
+	 * clientFollowXForwardedForDone() will eventually pass control
+	 * back to us.
+	 *
+	 * XXX perhaps our caller should have called
+	 *     clientFollowXForwardedForStart instead.  Then we wouldn't
+	 *     need to do this little dance transferring control over
+	 *     there and then back here, and we wouldn't need the
+	 *     done_follow_x_forwarded_for flag.
+	 */
+	clientFollowXForwardedForStart(data);
+	return;
+    }
+#endif /* FOLLOW_X_FORWARDED_FOR */
     if (checkAccelOnly(http)) {
 	/* deny proxy requests in accel_only mode */
 	debug(33, 1) ("clientAccessCheck: proxy request denied in accel_only mode\n");
@@ -325,6 +499,9 @@
 	new_request->http_ver = old_request->http_ver;
 	httpHeaderAppend(&new_request->header, &old_request->header);
 	new_request->client_addr = old_request->client_addr;
+#if FOLLOW_X_FORWARDED_FOR
+	new_request->indirect_client_addr = old_request->indirect_client_addr;
+#endif /* FOLLOW_X_FORWARDED_FOR */
 	new_request->my_addr = old_request->my_addr;
 	new_request->my_port = old_request->my_port;
 	new_request->flags.redirected = 1;
@@ -3051,6 +3228,9 @@
 	    safe_free(http->log_uri);
 	    http->log_uri = xstrdup(urlCanonicalClean(request));
 	    request->client_addr = conn->peer.sin_addr;
+#if FOLLOW_X_FORWARDED_FOR
+	    request->indirect_client_addr = request->client_addr;
+#endif /* FOLLOW_X_FORWARDED_FOR */
 	    request->my_addr = conn->me.sin_addr;
 	    request->my_port = ntohs(conn->me.sin_port);
 	    request->http_ver = http->http_ver;
Index: src/delay_pools.c
--- src/delay_pools.c	19 Jun 2003 02:13:57 -0000	1.5.54.6
+++ src/delay_pools.c	23 Nov 2003 14:20:23 -0000
@@ -318,6 +318,11 @@
     r = http->request;
 
     memset(&ch, '\0', sizeof(ch));
+#if FOLLOW_X_FORWARDED_FOR
+    if (Config.onoff.delay_pool_uses_indirect_client) {
+	ch.src_addr = r->indirect_client_addr;
+    } else
+#endif /* FOLLOW_X_FORWARDED_FOR */
     ch.src_addr = r->client_addr;
     ch.my_addr = r->my_addr;
     ch.my_port = r->my_port;
*** src/structs.h.orig	Sun Jun 26 12:45:58 2005
--- src/structs.h	Sun Jun 26 12:48:45 2005
***************
*** 610,615 ****
--- 610,620 ----
  	int accel_uses_host_header;
  	int accel_no_pmtu_disc;
  	int global_internal_static;
+ #if FOLLOW_X_FORWARDED_FOR
+ 	int acl_uses_indirect_client;
+ 	int delay_pool_uses_indirect_client;
+ 	int log_uses_indirect_client;
+ #endif /* FOLLOW_X_FORWARDED_FOR */
      } onoff;
      acl *aclList;
      struct {
***************
*** 631,636 ****
--- 636,644 ----
  	acl_access *reply;
  	acl_address *outgoing_address;
  	acl_tos *outgoing_tos;
+ #if FOLLOW_X_FORWARDED_FOR
+ 	acl_access *followXFF;
+ #endif /* FOLLOW_X_FORWARDED_FOR */
      } accessList;
      acl_deny_info_list *denyInfoList;
      struct _authConfig {
***************
*** 1623,1628 ****
--- 1631,1641 ----
      unsigned int body_sent:1;
      unsigned int reset_tcp:1;
      unsigned int must_keepalive:1;
+ #if FOLLOW_X_FORWARDED_FOR
+     /* XXX this flag could be eliminated;
+      * see comments in clientAccessCheck */
+     unsigned int done_follow_x_forwarded_for;
+ #endif /* FOLLOW_X_FORWARDED_FOR */
  };
  
  struct _link_list {
***************
*** 1666,1671 ****
--- 1679,1687 ----
      int max_forwards;
      /* these in_addr's could probably be sockaddr_in's */
      struct in_addr client_addr;
+ #if FOLLOW_X_FORWARDED_FOR
+     struct in_addr indirect_client_addr; /* after following X-Forwarded-For */
+ #endif /* FOLLOW_X_FORWARDED_FOR */
      struct in_addr my_addr;
      unsigned short my_port;
      HttpHeader header;
***************
*** 1677,1682 ****
--- 1693,1703 ----
      const char *vary_headers;	/* Used when varying entities are detected. Changes how the store key is calculated */
      BODY_HANDLER *body_reader;
      void *body_reader_data;
+ #if FOLLOW_X_FORWARDED_FOR
+     /* XXX a list of IP addresses would be a better data structure
+      * than this String */
+     String x_forwarded_for_iterator;
+ #endif /* FOLLOW_X_FORWARDED_FOR */
  };
  
  struct _cachemgr_passwd {