summaryrefslogtreecommitdiff
path: root/www/squid30/files/follow_xff-2.5.patch
blob: 767de5d2dfbcb5b3d464bcef4b91f1ed79abfe1d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
! 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;
Index: src/structs.h
*** src/structs.h.orig	Wed Oct  6 00:56:14 2004
--- src/structs.h	Mon Oct 18 20:23:13 2004
***************
*** 606,611 ****
--- 606,616 ----
  	int request_entities;
  	int detect_broken_server_pconns;
  	int balance_on_multiple_ip;
+ #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 {
***************
*** 627,632 ****
--- 632,640 ----
  	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 internal:1;
      unsigned int body_sent:1;
      unsigned int reset_tcp: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 {
***************
*** 1669,1674 ****
--- 1682,1690 ----
      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;
***************
*** 1680,1685 ****
--- 1696,1706 ----
      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 {