]> granicus.if.org Git - apache/blob - modules/metadata/mod_remoteip.c
c72e8d8ad9e33550f57d3f0f795186d9954a0cf2
[apache] / modules / metadata / mod_remoteip.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "ap_config.h"
18 #include "ap_mmn.h"
19 #include "httpd.h"
20 #include "http_config.h"
21 #include "http_connection.h"
22 #include "http_protocol.h"
23 #include "http_log.h"
24 #include "apr_strings.h"
25 #include "apr_lib.h"
26 #define APR_WANT_BYTEFUNC
27 #include "apr_want.h"
28 #include "apr_network_io.h"
29
30 module AP_MODULE_DECLARE_DATA remoteip_module;
31
32 typedef struct {
33     /** A proxy IP mask to match */
34     apr_ipsubnet_t *ip;
35     /** Flagged if internal, otherwise an external trusted proxy */
36     void  *internal;
37 } remoteip_proxymatch_t;
38
39 typedef struct {
40     /** The header to retrieve a proxy-via ip list */
41     const char *header_name;
42     /** A header to record the proxied IP's
43      * (removed as the physical connection and
44      * from the proxy-via ip header value list)
45      */
46     const char *proxies_header_name;
47     /** A list of trusted proxies, ideally configured
48      *  with the most commonly encountered listed first
49      */
50     apr_array_header_t *proxymatch_ip;
51 } remoteip_config_t;
52
53 typedef struct {
54     apr_sockaddr_t *client_addr;
55     char *client_ip;
56     /** The list of proxy ip's ignored as remote ip's */
57     const char *proxy_ips;
58     /** The remaining list of untrusted proxied remote ip's */
59     const char *proxied_remote;
60 } remoteip_req_t;
61
62 static void *create_remoteip_server_config(apr_pool_t *p, server_rec *s)
63 {
64     remoteip_config_t *config = apr_pcalloc(p, sizeof *config);
65     /* config->header_name = NULL;
66      * config->proxies_header_name = NULL;
67      */
68     return config;
69 }
70
71 static void *merge_remoteip_server_config(apr_pool_t *p, void *globalv,
72                                           void *serverv)
73 {
74     remoteip_config_t *global = (remoteip_config_t *) globalv;
75     remoteip_config_t *server = (remoteip_config_t *) serverv;
76     remoteip_config_t *config;
77
78     config = (remoteip_config_t *) apr_palloc(p, sizeof(*config));
79     config->header_name = server->header_name
80                         ? server->header_name
81                         : global->header_name;
82     config->proxies_header_name = server->proxies_header_name
83                                 ? server->proxies_header_name
84                                 : global->proxies_header_name;
85     config->proxymatch_ip = server->proxymatch_ip
86                           ? server->proxymatch_ip
87                           : global->proxymatch_ip;
88     return config;
89 }
90
91 static const char *header_name_set(cmd_parms *cmd, void *dummy,
92                                    const char *arg)
93 {
94     remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
95                                                      &remoteip_module);
96     config->header_name = arg;
97     return NULL;
98 }
99
100 static const char *proxies_header_name_set(cmd_parms *cmd, void *dummy,
101                                            const char *arg)
102 {
103     remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
104                                                      &remoteip_module);
105     config->proxies_header_name = arg;
106     return NULL;
107 }
108
109 /* Would be quite nice if APR exported this */
110 /* apr:network_io/unix/sockaddr.c */
111 static int looks_like_ip(const char *ipstr)
112 {
113     if (ap_strchr_c(ipstr, ':')) {
114         /* definitely not a hostname; assume it is intended to be an IPv6 address */
115         return 1;
116     }
117
118     /* simple IPv4 address string check */
119     while ((*ipstr == '.') || apr_isdigit(*ipstr))
120         ipstr++;
121     return (*ipstr == '\0');
122 }
123
124 static const char *proxies_set(cmd_parms *cmd, void *cfg,
125                                const char *arg)
126 {
127     remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
128                                                      &remoteip_module);
129     remoteip_proxymatch_t *match;
130     apr_status_t rv;
131     char *ip = apr_pstrdup(cmd->temp_pool, arg);
132     char *s = ap_strchr(ip, '/');
133     if (s) {
134         *s++ = '\0';
135     }
136
137     if (!config->proxymatch_ip) {
138         config->proxymatch_ip = apr_array_make(cmd->pool, 1, sizeof(*match));
139     }
140     match = (remoteip_proxymatch_t *) apr_array_push(config->proxymatch_ip);
141     match->internal = cmd->info;
142
143     if (looks_like_ip(ip)) {
144         /* Note s may be null, that's fine (explicit host) */
145         rv = apr_ipsubnet_create(&match->ip, ip, s, cmd->pool);
146     }
147     else
148     {
149         apr_sockaddr_t *temp_sa;
150
151         if (s) {
152             return apr_pstrcat(cmd->pool, "RemoteIP: Error parsing IP ", arg,
153                                " the subnet /", s, " is invalid for ",
154                                cmd->cmd->name, NULL);
155         }
156
157         rv = apr_sockaddr_info_get(&temp_sa,  ip, APR_UNSPEC, 0,
158                                    APR_IPV4_ADDR_OK, cmd->temp_pool);
159         while (rv == APR_SUCCESS)
160         {
161             apr_sockaddr_ip_get(&ip, temp_sa);
162             rv = apr_ipsubnet_create(&match->ip, ip, NULL, cmd->pool);
163             if (!(temp_sa = temp_sa->next)) {
164                 break;
165             }
166             match = (remoteip_proxymatch_t *)
167                     apr_array_push(config->proxymatch_ip);
168             match->internal = cmd->info;
169         }
170     }
171
172     if (rv != APR_SUCCESS) {
173         char msgbuf[128];
174         apr_strerror(rv, msgbuf, sizeof(msgbuf));
175         return apr_pstrcat(cmd->pool, "RemoteIP: Error parsing IP ", arg,
176                            " (", msgbuf, " error) for ", cmd->cmd->name, NULL);
177     }
178
179     return NULL;
180 }
181
182 static const char *proxylist_read(cmd_parms *cmd, void *cfg,
183                                   const char *filename)
184 {
185     char lbuf[MAX_STRING_LEN];
186     char *arg;
187     const char *args;
188     const char *errmsg;
189     ap_configfile_t *cfp;
190     apr_status_t rv;
191
192     filename = ap_server_root_relative(cmd->temp_pool, filename);
193     rv = ap_pcfg_openfile(&cfp, cmd->temp_pool, filename);
194     if (rv != APR_SUCCESS) {
195         return apr_psprintf(cmd->pool, "%s: Could not open file %s: %s",
196                             cmd->cmd->name, filename,
197                             apr_strerror(rv, lbuf, sizeof(lbuf)));
198     }
199
200     while (!(ap_cfg_getline(lbuf, MAX_STRING_LEN, cfp))) {
201         args = lbuf;
202         while (*(arg = ap_getword_conf(cmd->temp_pool, &args)) != '\0') {
203             if (*arg == '#' || *arg == '\0') {
204                 break;
205             }
206             errmsg = proxies_set(cmd, cfg, arg);
207             if (errmsg) {
208                 errmsg = apr_psprintf(cmd->pool, "%s at line %d of %s",
209                                       errmsg, cfp->line_number, filename);
210                 return errmsg;
211             }
212         }
213     }
214
215     ap_cfg_closefile(cfp);
216     return NULL;
217 }
218
219 static int remoteip_modify_request(request_rec *r)
220 {
221     conn_rec *c = r->connection;
222     remoteip_config_t *config = (remoteip_config_t *)
223         ap_get_module_config(r->server->module_config, &remoteip_module);
224     remoteip_req_t *req = NULL;
225
226     apr_sockaddr_t *temp_sa;
227
228     apr_status_t rv;
229     char *remote;
230     char *proxy_ips = NULL;
231     char *parse_remote;
232     char *eos;
233     unsigned char *addrbyte;
234     void *internal = NULL;
235
236     if (!config->header_name) {
237         return DECLINED;
238     }
239
240     remote = (char *) apr_table_get(r->headers_in, config->header_name);
241     if (!remote) {
242         return OK;
243     }
244     remote = apr_pstrdup(r->pool, remote);
245
246     temp_sa = c->peer_addr;
247
248     while (remote) {
249
250         /* verify c->peer_addr is trusted if there is a trusted proxy list
251          */
252         if (config->proxymatch_ip) {
253             int i;
254             remoteip_proxymatch_t *match;
255             match = (remoteip_proxymatch_t *)config->proxymatch_ip->elts;
256             for (i = 0; i < config->proxymatch_ip->nelts; ++i) {
257                 if (apr_ipsubnet_test(match[i].ip, c->peer_addr)) {
258                     internal = match[i].internal;
259                     break;
260                 }
261             }
262             if (i && i >= config->proxymatch_ip->nelts) {
263                 break;
264             }
265         }
266
267         if ((parse_remote = strrchr(remote, ',')) == NULL) {
268             parse_remote = remote;
269             remote = NULL;
270         }
271         else {
272             *(parse_remote++) = '\0';
273         }
274
275         while (*parse_remote == ' ') {
276             ++parse_remote;
277         }
278
279         eos = parse_remote + strlen(parse_remote) - 1;
280         while (eos >= parse_remote && *eos == ' ') {
281             *(eos--) = '\0';
282         }
283
284         if (eos < parse_remote) {
285             if (remote) {
286                 *(remote + strlen(remote)) = ',';
287             }
288             else {
289                 remote = parse_remote;
290             }
291             break;
292         }
293
294         /* We map as IPv4 rather than IPv6 for equivilant host names
295          * or IPV4OVERIPV6
296          */
297         rv = apr_sockaddr_info_get(&temp_sa,  parse_remote,
298                                    APR_UNSPEC, temp_sa->port,
299                                    APR_IPV4_ADDR_OK, r->pool);
300         if (rv != APR_SUCCESS) {
301             ap_log_rerror(APLOG_MARK, APLOG_DEBUG,  rv, r,
302                           "RemoteIP: Header %s value of %s cannot be parsed "
303                           "as a client IP",
304                           config->header_name, parse_remote);
305
306             if (remote) {
307                 *(remote + strlen(remote)) = ',';
308             }
309             else {
310                 remote = parse_remote;
311             }
312             break;
313
314         }
315
316         addrbyte = (unsigned char *) &temp_sa->sa.sin.sin_addr;
317
318         /* For intranet (Internal proxies) ignore all restrictions below */
319         if (!internal
320               && ((temp_sa->family == APR_INET
321                    /* For internet (non-Internal proxies) deny all
322                     * RFC3330 designated local/private subnets:
323                     * 10.0.0.0/8   169.254.0.0/16  192.168.0.0/16
324                     * 127.0.0.0/8  172.16.0.0/12
325                     */
326                       && (addrbyte[0] == 10
327                        || addrbyte[0] == 127
328                        || (addrbyte[0] == 169 && addrbyte[1] == 254)
329                        || (addrbyte[0] == 172 && (addrbyte[1] & 0xf0) == 16)
330                        || (addrbyte[0] == 192 && addrbyte[1] == 168)))
331 #if APR_HAVE_IPV6
332                || (temp_sa->family == APR_INET6
333                    /* For internet (non-Internal proxies) we translated
334                     * IPv4-over-IPv6-mapped addresses as IPv4, above.
335                     * Accept only Global Unicast 2000::/3 defined by RFC4291
336                     */
337                       && ((temp_sa->sa.sin6.sin6_addr.s6_addr[0] & 0xe0) != 0x20))
338 #endif
339         )) {
340             ap_log_rerror(APLOG_MARK, APLOG_DEBUG,  rv, r,
341                           "RemoteIP: Header %s value of %s appears to be "
342                           "a private IP or nonsensical.  Ignored",
343                           config->header_name, parse_remote);
344             if (remote) {
345                 *(remote + strlen(remote)) = ',';
346             }
347             else {
348                 remote = parse_remote;
349             }
350
351             break;
352         }
353
354         /* save away our results */
355         if (!req) {
356             req = (remoteip_req_t *) apr_palloc(r->pool, sizeof(remoteip_req_t));
357         }
358
359         /* Set peer_ip string */
360         if (!internal) {
361             if (proxy_ips) {
362                 proxy_ips = apr_pstrcat(r->pool, proxy_ips, ", ",
363                                         c->peer_ip, NULL);
364             }
365             else {
366                 proxy_ips = c->peer_ip;
367             }
368         }
369
370         req->client_addr = temp_sa;
371         apr_sockaddr_ip_get(&req->client_ip, req->client_addr);
372     }
373
374     /* Nothing happened? */
375     if (!req) {
376         return OK;
377     }
378
379     req->proxied_remote = remote;
380     req->proxy_ips = proxy_ips;
381
382     if (req->proxied_remote) {
383         apr_table_setn(r->headers_in, config->header_name,
384                        req->proxied_remote);
385     }
386     else {
387         apr_table_unset(r->headers_in, config->header_name);
388     }
389     if (req->proxy_ips) {
390         apr_table_setn(r->notes, "remoteip-proxy-ip-list", req->proxy_ips);
391         if (config->proxies_header_name) {
392             apr_table_setn(r->headers_in, config->proxies_header_name,
393                            req->proxy_ips);
394         }
395     }
396
397     r->client_addr = req->client_addr;
398     r->client_ip = req->client_ip;
399
400     ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, 0, r,
401                   req->proxy_ips
402                       ? "Using %s as client's IP by proxies %s"
403                       : "Using %s as client's IP by internal proxies",
404                   req->client_ip, req->proxy_ips);
405     return OK;
406 }
407
408 static const command_rec remoteip_cmds[] =
409 {
410     AP_INIT_TAKE1("RemoteIPHeader", header_name_set, NULL, RSRC_CONF,
411                   "Specifies a request header to trust as the client IP, "
412                   "e.g. X-Forwarded-For"),
413     AP_INIT_TAKE1("RemoteIPProxiesHeader", proxies_header_name_set,
414                   NULL, RSRC_CONF,
415                   "Specifies a request header to record proxy IP's, "
416                   "e.g. X-Forwarded-By; if not given then do not record"),
417     AP_INIT_ITERATE("RemoteIPTrustedProxy", proxies_set, 0, RSRC_CONF,
418                     "Specifies one or more proxies which are trusted "
419                     "to present IP headers"),
420     AP_INIT_ITERATE("RemoteIPInternalProxy", proxies_set, (void*)1, RSRC_CONF,
421                     "Specifies one or more internal (transparent) proxies "
422                     "which are trusted to present IP headers"),
423     AP_INIT_TAKE1("RemoteIPTrustedProxyList", proxylist_read, 0,
424                   RSRC_CONF | EXEC_ON_READ,
425                   "The filename to read the list of trusted proxies, "
426                   "see the RemoteIPTrustedProxy directive"),
427     AP_INIT_TAKE1("RemoteIPInternalProxyList", proxylist_read, (void*)1,
428                   RSRC_CONF | EXEC_ON_READ,
429                   "The filename to read the list of internal proxies, "
430                   "see the RemoteIPInternalProxy directive"),
431     { NULL }
432 };
433
434 static void register_hooks(apr_pool_t *p)
435 {
436     ap_hook_post_read_request(remoteip_modify_request, NULL, NULL, APR_HOOK_FIRST);
437 }
438
439 AP_DECLARE_MODULE(remoteip) = {
440     STANDARD20_MODULE_STUFF,
441     NULL,                          /* create per-directory config structure */
442     NULL,                          /* merge per-directory config structures */
443     create_remoteip_server_config, /* create per-server config structure */
444     merge_remoteip_server_config,  /* merge per-server config structures */
445     remoteip_cmds,                 /* command apr_table_t */
446     register_hooks                 /* register hooks */
447 };