"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /* ====================================================================
2 * Copyright (c) 2000 The Apache Group. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * 3. All advertising materials mentioning features or use of this
17 * software must display the following acknowledgment:
18 * "This product includes software developed by the Apache Group
19 * for use in the Apache HTTP server project (http://www.apache.org/)."
20 *
21 * 4. The names "Apache Server" and "Apache Group" must not be used to
22 * endorse or promote products derived from this software without
23 * prior written permission. For written permission, please contact
24 * apache@apache.org.
25 *
26 * 5. Products derived from this software may not be called "Apache"
27 * nor may "Apache" appear in their names without prior written
28 * permission of the Apache Group.
29 *
30 * 6. Redistributions of any form whatsoever must retain the following
31 * acknowledgment:
32 * "This product includes software developed by the Apache Group
33 * for use in the Apache HTTP server project (http://www.apache.org/)."
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
36 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46 * OF THE POSSIBILITY OF SUCH DAMAGE.
47 * ====================================================================
48 *
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Group and was originally based
51 * on public domain software written at the National Center for
52 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
53 * For more information on the Apache Group and the Apache HTTP server
54 * project, please see <http://www.apache.org/>.
55 *
56 */
57
58 /* Version 0.1
59 * Based on mod_extract_forwarded 1.4
60 * Authors: Adrian Hosey alh@warhound.org
61 * Richard Barrett <R.Barrett@ftel.co.uk>
62 * Apache 2.0.x port (tested on FreeBSD only) Clement Laforet <sheepkiller@cultdeadsheep.org>
63 */
64
65 #include <unistd.h>
66 #include <netdb.h>
67 #include <sys/types.h>
68 #include <sys/socket.h>
69 #include <netinet/in.h>
70 #include <arpa/inet.h>
71
72 #include "ap_config.h"
73 #include "ap_mmn.h"
74 #include "apr_buckets.h"
75 #include "apr_strings.h"
76
77 #include "httpd.h"
78 #include "http_core.h"
79 #include "http_config.h"
80 #include "http_connection.h"
81 #include "http_log.h"
82 #include "http_protocol.h"
83 #include "http_vhost.h"
84
85 #include "util_filter.h"
86
87
88 module AP_MODULE_DECLARE_DATA extract_forwarded_module;
89
90 typedef struct {
91 int allow_cache;
92 apr_table_t *allowed_proxies;
93 apr_table_t *denied_proxies;
94 } fwd_dir_conf;
95
96
97 /* Given an IP as "arg", make sure it is in the allowed_proxies
98 * table. It will only ever exist once in a given table.
99 */
100 static const char *add_forwarder(cmd_parms *cmd, void *mconfig,
101 const char *arg)
102 {
103 struct hostent *hptr = NULL;
104 char** haddr;
105 fwd_dir_conf *conf = (fwd_dir_conf*)mconfig;
106
107 /* "all" keyword replaces everything with just itself. */
108 if (strcasecmp(arg, "all") == 0) {
109 apr_table_clear(conf->allowed_proxies);
110 apr_table_set(conf->allowed_proxies, arg, "t");
111 } else {
112 hptr = gethostbyname(arg);
113 if (hptr) {
114 for (haddr=hptr->h_addr_list; *haddr; haddr++)
115 apr_table_set(conf->allowed_proxies,
116 inet_ntoa(*((struct in_addr*)(*haddr))), "t");
117 }
118 }
119 return NULL;
120 }
121
122
123 /* Given an IP as "arg", make sure it is NOT in the allowed_proxies table.
124 */
125 static const char *rm_forwarder(cmd_parms *cmd, void *mconfig,
126 const char *arg)
127 {
128 struct hostent *hptr = NULL;
129 char** haddr;
130 server_rec *r = cmd->server;
131 fwd_dir_conf *conf = (fwd_dir_conf*)mconfig;
132
133 if (strcasecmp(arg, "all") == 0) {
134 apr_table_clear(conf->denied_proxies);
135 apr_table_set(conf->denied_proxies, arg, "t");
136 } else {
137 hptr = gethostbyname(arg);
138 if (hptr) {
139 for (haddr=hptr->h_addr_list; *haddr; haddr++)
140 apr_table_set(conf->denied_proxies,
141 inet_ntoa(*((struct in_addr*)(*haddr))), "t");
142 }
143 }
144 return NULL;
145 }
146
147
148 /* Set allow_cache in this fwd_dir_conf to be whatever FLAG we are given.
149 */
150 static const char *toggle_caching(cmd_parms *cmd, void *mconfig, int flag)
151 {
152 fwd_dir_conf *conf = (fwd_dir_conf*)mconfig;
153 conf->allow_cache = flag;
154 return NULL;
155 }
156
157 /* Hook up the cmd functions we have lovingly defined above. */
158 static const command_rec extract_forwarded_cmds[] = {
159 AP_INIT_FLAG(
160 "AllowForwarderCaching",
161 toggle_caching,
162 NULL,
163 OR_OPTIONS,
164 "Allow caching of this page if fetched by proxy - On or Off"
165 ),
166 AP_INIT_ITERATE(
167 "AddAcceptForwarder",
168 add_forwarder,
169 NULL,
170 OR_OPTIONS,
171 "One or more proxy IPs to add to the accept list"
172 ),
173 AP_INIT_ITERATE(
174 "RemoveAcceptForwarder",
175 rm_forwarder,
176 NULL,
177 OR_OPTIONS,
178 "One or more proxy IPs to remove from the accept list"
179 ),
180 { NULL }
181 };
182
183
184 /* This is a callback function used by merge_fwd_dir_conf(). See that
185 * function for more details. */
186 static int take_out_proxies(void *allows, const char *key, const char* val)
187 {
188 /* If key exists in the allows list take it out. */
189 apr_table_unset((apr_table_t*)allows, key);
190 return 1;
191 }
192
193
194 /* The next two functions are the fwd_dir_conf creator and merger. */
195 static void *create_fwd_dir_conf(apr_pool_t *p, char *dir)
196 {
197 fwd_dir_conf *conf = (fwd_dir_conf*)apr_pcalloc(p, sizeof(fwd_dir_conf));
198 /* This defaults to a 2, meaning "unspecified" */
199 conf->allow_cache = 2;
200 /* We start with an empty table, which means ignore all
201 * Forwarded-For headers. */
202 conf->allowed_proxies = apr_table_make(p, 0);
203 conf->denied_proxies = apr_table_make(p, 0);
204 return (void*)conf;
205 }
206
207
208 static void *merge_fwd_dir_conf(apr_pool_t *p, void *base_conf, void* new_conf)
209 {
210 fwd_dir_conf *parent = (fwd_dir_conf*)base_conf;
211 fwd_dir_conf *child = (fwd_dir_conf*)new_conf;
212 fwd_dir_conf *merged = (fwd_dir_conf*)apr_pcalloc(p, sizeof(fwd_dir_conf));
213 int altered_child_denied = 0;
214 int altered_child_allowed = 0;
215
216 /* If child->allow_cache was explicitly set in this dir, use
217 * that. Else use the parent value. If the parent value is unset,
218 * set to "On" */
219 if (child->allow_cache != 2)
220 merged->allow_cache = child->allow_cache;
221 else if (parent->allow_cache == 2)
222 merged->allow_cache = 1;
223 else
224 merged->allow_cache = parent->allow_cache;
225
226 /* The new allowed list starts as the parent list. */
227 merged->allowed_proxies = apr_table_copy(p, parent->allowed_proxies);
228 merged->denied_proxies = apr_table_copy(p, parent->denied_proxies);
229
230 /* Process "all"s before anything else, and process Removes before
231 * Adds. */
232 if (apr_table_get(child->denied_proxies, "all")) {
233 apr_table_clear(merged->allowed_proxies);
234 /* Flag to remind us we need to undo this change. */
235 altered_child_denied = 1;
236 apr_table_unset(child->denied_proxies, "all");
237 }
238 if (apr_table_get(child->allowed_proxies, "all")) {
239 apr_table_clear(merged->allowed_proxies);
240 apr_table_set(merged->allowed_proxies, "all", "t");
241 /* Flag to remind us we need to undo this change. */
242 altered_child_allowed = 1;
243 apr_table_unset(child->allowed_proxies, "all");
244 }
245
246 /* If we have an allow "all" then the denied list becomes a
247 * "mask" meaning "allow everything except these". Otherwise we just
248 * remove the IPs from the allowed list. */
249 if (apr_table_get(merged->allowed_proxies, "all")) {
250 merged->denied_proxies =
251 apr_table_overlay(p, child->denied_proxies,
252 merged->denied_proxies);
253 } else {
254 apr_table_do(take_out_proxies, (void*)merged->allowed_proxies,
255 child->denied_proxies, NULL);
256 }
257 /* Now handle the allows, which is easy for a change. */
258 merged->allowed_proxies =
259 apr_table_overlay(p, child->allowed_proxies, merged->allowed_proxies);
260
261 /* If we altered the child tables then set them back. */
262 if (altered_child_denied)
263 apr_table_set(child->denied_proxies, "all", "t");
264 if (altered_child_allowed)
265 apr_table_set(child->allowed_proxies, "all", "t");
266
267 return (void*)merged;
268 }
269
270
271 /* Make sure the given proxy IP is allowed with the conf we are given. */
272 static int proxy_is_kosher(fwd_dir_conf *conf, char *proxy_ip) {
273
274 /* If the allowed list is set to "all", then we "mask out" any
275 * proxies that might be in the denied list. */
276 if (apr_table_get(conf->allowed_proxies, "all")) {
277 if (apr_table_get(conf->denied_proxies, proxy_ip))
278 return 0;
279 }
280 /* Otherwise we just need to make sure this IP is in the allowed list. */
281 else if (!apr_table_get(conf->allowed_proxies, proxy_ip)) {
282 /* Oops. It's not. */
283 return 0;
284 }
285 return 1;
286 }
287
288 /* Request cleanup handler and associated data structure.
289 *
290 * This restores the remote_ip of the connection over which requests
291 * are being made after a request transaction has completed, if the
292 * conn_rec was changed for that request. This is needed if our
293 * incoming connection is from a proxy server which is making multiple
294 * requests, potentially for different clients, down a persistent
295 * connection.
296 *
297 * If we do not restore the proxy server's IP in the conn_rec then all
298 * subsequent requests down the connection will be misattributed to
299 * the same IP as the first request.
300 */
301 typedef struct proxy_save_rec proxy_save_rec;
302
303 struct proxy_save_rec {
304 conn_rec *saved_connection; /* connection record being used */
305 char *saved_remote_ip; /* original remote_ip */
306 char *saved_remote_host; /* original remote_host */
307 };
308
309 static apr_status_t restore_proxy_remote_addr(void *data)
310 {
311 proxy_save_rec *proxy_saved = (proxy_save_rec *)data;
312
313 conn_rec *conn = proxy_saved->saved_connection;
314
315 conn->remote_ip = proxy_saved->saved_remote_ip;
316 conn->remote_addr->sa.sin.sin_addr.s_addr = apr_inet_addr(conn->remote_ip);
317 conn->remote_host = proxy_saved->saved_remote_host;
318 }
319
320
321 /* If a proxy has provided us with an X-Forwarded-For: header we want
322 * to set the remote IP of the request to the one provided.
323 */
324 static int real_set_proxy_remote_addr(request_rec *r)
325 {
326 const char *fwded_for;
327 char *val, *client_ip;
328 proxy_save_rec *proxy_saved;
329 fwd_dir_conf *conf;
330 apr_array_header_t *ary;
331 int ctr, start_ptr;
332
333 conf = (fwd_dir_conf*)ap_get_module_config(r->per_dir_config,
334 &extract_forwarded_module);
335
336 if (!(conf->allow_cache)) {
337 apr_table_set(r->headers_out, "Pragma", "no-cache");
338 apr_table_set(r->headers_out, "Cache-Control", "no-cache");
339 }
340
341 /* First make sure the proxy actually making this request is kosher. */
342 if (! proxy_is_kosher(conf, r->connection->remote_ip))
343 return OK;
344 /* Okay now let's look for the header. */
345 if ((fwded_for = apr_table_get(r->headers_in, "X-Forwarded-For")) == NULL &&
346 (fwded_for = apr_table_get(r->headers_in, "Forwarded-For")) == NULL)
347 return OK;
348
349 ary = apr_array_make(r->pool, 1, sizeof(char*));
350 ctr = 0;
351 while (*fwded_for &&
352 (val = ap_get_token(r->pool, &fwded_for, 0))) {
353 *(char**)apr_array_push(ary) = apr_pstrdup(r->pool, val);
354 if (*fwded_for == ',' || *fwded_for == ';') {
355 ++fwded_for;
356 }
357 /* This is a little "anti-suicide" clause if someone tries to
358 * feed us a monster string. */
359 if (++ctr > 64) break;
360 }
361
362 /* Scan back from the end of the list of forwarded-fors until we
363 * find one that isn't kosher, that is, it isn't one of our proxy
364 * servers. This allows us to back out any sequence of our proxy
365 * servers and find the first IP that isn't, which is the IP we're
366 * interested in. What we want is the IP number of the machine
367 * that made the connection to first of, potentially a sequence
368 * of, our trusted proxies. We don't care about any external
369 * proxies that may precede our trusted proxies because we cannot
370 * trust what they say.
371 *
372 * Do not search back beyond the 2nd forwarded-for IP number. Even
373 * if the first is from a trusted proxy's IP number it must have
374 * been acting as a client not a proxy if it appears in that
375 * position.
376 */
377 for (ctr = ary->nelts - 1; ctr >= 1; ctr--)
378 if (! proxy_is_kosher(conf, ((char**)ary->elts)[ctr] ))
379 break;
380 client_ip = ((char**)ary->elts)[ctr];
381
382 /* Preserve the proxy's IP etc so we can reset the conn_rec in our
383 * cleanup handler. We pass the saved data in the cleanup handler
384 * registration. */
385 proxy_saved = apr_pcalloc(r->pool, sizeof(proxy_save_rec));
386 proxy_saved->saved_connection = r->connection;
387 proxy_saved->saved_remote_ip = r->connection->remote_ip;
388 proxy_saved->saved_remote_host = r->connection->remote_host;
389 apr_pool_cleanup_register(r->pool, (void *)proxy_saved,
390 restore_proxy_remote_addr, apr_pool_cleanup_null);
391 /* Put the proxy IP in an env var so that subsequent modules, or
392 * CGIs, can know who really sent the request (if they care), as
393 * well as on who's behalf. The presence of this var also serves
394 * to tell other modules in other phases (including this module!)
395 * that the conn_rec has already been tampered with so don't do it
396 * again. */
397 apr_table_set(r->subprocess_env, "PROXY_ADDR", r->connection->remote_ip);
398 /* Here's the spoof. */
399 r->connection->remote_ip = client_ip;
400 /* To allow .htaccess files to work, we really need to alter this
401 * value as well. - David Hayes <dave@jetcafe.org> */
402 r->connection->remote_addr->sa.sin.sin_addr.s_addr = apr_inet_addr(client_ip);
403 r->connection->remote_host =
404 apr_pstrdup(r->pool,
405 ap_get_remote_host(r->connection, r->per_dir_config,
406 REMOTE_HOST, NULL));
407
408 return OK;
409 }
410
411
412 /* This is what we export as our handler. It checks for the presence
413 * of PROXY_ADDR and calls real_set_proxy_remote_addr() to do the
414 * work, only if necessary. */
415 static int set_proxy_remote_addr(request_rec *r)
416 {
417 if (apr_table_get(r->subprocess_env, "PROXY_ADDR") == NULL)
418 real_set_proxy_remote_addr(r);
419 return OK;
420 }
421
422
423 /* Yet another wrapper, this one is hooked to the URI translation
424 * phase where the return code needs to be different from above. */
425 static int ft_set_proxy_remote_addr(request_rec *r)
426 {
427 if (apr_table_get(r->subprocess_env, "PROXY_ADDR") == NULL)
428 real_set_proxy_remote_addr(r);
429 return DECLINED;
430 }
431
432
433 static void register_hooks(apr_pool_t *p) {
434 ap_hook_post_read_request(set_proxy_remote_addr, NULL, NULL, APR_HOOK_MIDDLE);
435 }
436
437 module AP_MODULE_DECLARE_DATA extract_forwarded_module = {
438 STANDARD20_MODULE_STUFF,
439 create_fwd_dir_conf, /* create per-directory config structure */
440 merge_fwd_dir_conf, /* merge per-directory config structures */
441 NULL, /* create per-server config structure */
442 NULL, /* merge per-server config structures */
443 extract_forwarded_cmds, /* command apr_table_t */
444 register_hooks /* register hooks */
445 };