diff --git a/INSTALL b/INSTALL index fa06d8a..d1f6d56 100644 --- a/INSTALL +++ b/INSTALL @@ -13,11 +13,12 @@ to build and install a DSO version of the module: /usr/local/apache2/bin/apxs -c -i -a mod_extract_forwarded.c -If the Apache instance you are adding mod_extract_forwarded to will not -have mod_proxy and proxy_http loaded then you will get an error when -mod_extract_forwarded is loaded. In that case edit -mod_extract_forwarded.c and comment out the #define for -USING_proxy_http_module or change it to an an #undef. If you -subsequently run Apache with proxy_http do not forget to reinstate the -#define; failure to do so will mean that any X-Fowarded-For header -inserted by proxy_http will use the spoofed IP number in error. +If the Apache instance you are adding mod_extract_forwarded to will +have mod_proxy and proxy_http loaded then you need to build and +install a DSO version of the module: + +/usr/local/apache2/bin/apxs -c -i -a mod_extract_forwarded_proxy.c + +Without this module, any X-Fowarded-For header inserted by proxy_http +will use the spoofed IP number. + diff --git a/mod_extract_forwarded.c b/mod_extract_forwarded.c index afecbcf..fa94493 100644 --- a/mod_extract_forwarded.c +++ b/mod_extract_forwarded.c @@ -84,6 +84,9 @@ * of what is done by the module as an aid to understanding fixing problems * with this code; this is NOT for production use because of the volume of * output it will generate and the way it flushes stderr + * 8. Support IPv6: If real connection is served by IPv6 socket, both IPv4 and + * IPv6 clients are supported. If real connection is served by IPv4 socket, + * only IPv4 clients are supported. [yoshfuji] * * History of Apache 2 compatible mod_extract_forwarded * ---------------------------------------------------- @@ -98,7 +101,8 @@ * Cleaned up interpretation of per_der_config * for internal redirect and subrequests and * other logic tidying - * + * 12 Apr 2009 Apache 2.2 support. [yoshfuji] + * 16 Apr 2009 IPv6 support. [yoshfuji] */ #include "httpd.h" @@ -110,22 +114,13 @@ #include "http_request.h" #include "util_script.h" #include "http_connection.h" -#include "mod_proxy.h" #include "apr_strings.h" #include #include #include -/* - * #define USING_proxy_http_module if proxy_http.c module is either - * compiled into Apache 2 or it is being loaded as a DSO. If proxy_http.c - * module is not loaded then this module will generate an error when and - * if it is loaded as a DSO. In that case comment out the #define, recompile - * and reinstall this module. BUT do not forget to change things back if - * proxy_http.c module is reinstated - */ -#define USING_proxy_http_module 1 +#include "mod_extract_forwarded.h" /*--------------------------------------------------------------------------*/ /* */ @@ -139,17 +134,6 @@ */ module AP_MODULE_DECLARE_DATA extract_forwarded_module; -/* - * Per directory configuration record. - */ -typedef struct { - int order; /* order in which the accept and refuse specs are applied */ - int debug; /* debug output to error log flag */ - const char *envar; /* name of env var to add */ - apr_table_t *accept_proxies; /* proxies to trust */ - apr_table_t *refuse_proxies; /* proxies to distrust */ -} mef_config; - /* * Two possible orders in which the accept and refuse specs are applied */ @@ -279,72 +263,41 @@ static const char *mef_debug_control(cmd_parms *cparms, void *mconfig, } /* - * Given an IP or 'all' as "arg", add it to the accept_proxies table + * Given an IP or 'all' as "arg", add it to the accept_proxies/refuse_proxies + * table */ -static const char *mef_accept_proxy(cmd_parms *cparms, void *mconfig, +static const char *mef_config_proxy(cmd_parms *cparms, void *mconfig, const char *arg) { mef_config *conf = (mef_config *)mconfig; - struct hostent *hptr = NULL; - char** haddr; + apr_table_t *conf_proxies = cparms->info ? + conf->accept_proxies : conf->refuse_proxies; if (strcasecmp(arg, "all") == 0) /* "all" keyword replaces everything with just itself */ { - apr_table_clear(conf->accept_proxies); - apr_table_set(conf->accept_proxies, arg, "t"); + apr_table_clear(conf_proxies); + apr_table_set(conf_proxies, arg, "t"); } else - /* Add IP to list of accepted proxies */ + /* Add IP to list of accepted/refuse proxies */ { - hptr = gethostbyname(arg); - if (hptr) + apr_sockaddr_t *sa0, *sa; + if (apr_sockaddr_info_get(&sa0, arg, APR_UNSPEC, 0, 0, + cparms->temp_pool) == APR_SUCCESS) { - apr_table_unset(conf->accept_proxies, "all"); - for (haddr=hptr->h_addr_list; *haddr; haddr++) + apr_table_unset(conf_proxies, "all"); + for (sa = sa0; sa; sa = sa->next) { - apr_table_set(conf->accept_proxies, - inet_ntoa(*((struct in_addr*)(*haddr))), "t"); + char *ipaddr; + if (apr_sockaddr_ip_get(&ipaddr, sa) == APR_SUCCESS) + { + apr_table_set(conf_proxies, ipaddr, "t"); + } } - } - else - { - return "No 'all' or valid IP identified by MEFaccept"; } - } - return NULL; -} - -/* - * Given an IP or 'all' as "arg", add it to the refused_proxies table - */ -static const char *mef_refuse_proxy(cmd_parms *cparms, void *mconfig, - const char *arg) -{ - mef_config *conf = (mef_config *) mconfig; - struct hostent *hptr = NULL; - char** haddr; - if (strcasecmp(arg, "all") == 0) - /* "all" keyword replaces everything with just itself */ - { - apr_table_clear(conf->refuse_proxies); - apr_table_set(conf->refuse_proxies, arg, "t"); - } - else - /* Add IP to list of refused proxies */ - { - hptr = gethostbyname(arg); - if (hptr) - { - apr_table_unset(conf->refuse_proxies, "all"); - for (haddr=hptr->h_addr_list; *haddr; haddr++) - { - apr_table_set(conf->refuse_proxies, - inet_ntoa(*((struct in_addr*)(*haddr))), "t"); - } - } - else + else { - return "No 'all' or valid IP identified by MEFrefuse"; + return "No 'all' or valid IP identified by MEFaccept / MEFrefuse"; } } return NULL; @@ -385,54 +338,6 @@ static int acceptable_proxy(mef_config *conf, char *proxy_ip) return 0; } -/* - * The MEFsave_rec data structure is used to preserve information that - * this module has modified in the conn_rec associated with a request - * so that the conn_rec can be restored to its original state as needed. - * It also carries information between transaction phases and internal - * redirects and subrequests - */ -typedef struct MEFsave_rec MEFsave_rec; - -struct MEFsave_rec { - conn_rec *connection; /* connection record being used */ - in_addr_t orig_in_addr; /* original remote in_addr_t */ - in_addr_t new_in_addr; /* modified remote in_addr_t */ - char *orig_remote_ip; /* original remote_ip */ - char *new_remote_ip; /* modified remote_ip */ - int conn_rec_mod_state; /* conn_rec modification state */ - int debug; /* are we printing MEF debug */ - const char *envar; /* name of env var to add */ - void *per_dir_config; /* per_dir_config applicable */ - MEFsave_rec *other_saved; /* any preceding req's save_rec */ - request_rec *other_r; /* any preceding req's request_rec */ -}; - -#define CONN_REC_MODIFIED 1 -#define CONN_REC_RESTORED 0 - -/* - * remote_in_addr returns a pointer to the in_addr_t which specifes - * the IP of the remote end of the connection supporting the specified - * request. NULL is returned if this cannot be determined. - */ -static in_addr_t *get_remote_in_addr(conn_rec *conn) -{ - in_addr_t *result = NULL; - if (conn->remote_addr->family == AF_INET) - { - result = &(conn->remote_addr->sa.sin.sin_addr.s_addr); - } -#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) - if (conn->remote_addr->family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&conn->remote_addr->sa.sin6.sin6_addr)) - { - result = &(((uint32_t *)conn->remote_addr->ipaddr_ptr)[3]); - } -#endif - return result; -} - /* Forward declared for convenience */ static apr_status_t cleanup_initial(void *data); static apr_status_t cleanup_not_initial(void *data); @@ -446,22 +351,12 @@ static apr_status_t cleanup_not_initial(void *data); */ static int spoof_initial(request_rec *r, char *spoof_ip, char *phase) { - in_addr_t *remote_in_addr; MEFsave_rec *saved; mef_config *conf = ap_get_module_config(r->per_dir_config, &extract_forwarded_module); - /* Validate and acquire pointer to the remote in_addr_t */ - if ((remote_in_addr = get_remote_in_addr(r->connection)) == NULL) - { - /* Could not get a valid value so give up */ - if (conf->debug == MEF_DEBUG_ON) - { - fprintf(stderr,"MEF: phase:%s, si problem acquiring remote_in_addr\n", - phase); - fflush(stderr); - } - return DECLINED; - } + apr_sockaddr_t *sa; + apr_port_t port; + /* * We can proceed to do the spoof * @@ -470,9 +365,40 @@ static int spoof_initial(request_rec *r, char *spoof_ip, char *phase) saved = apr_pcalloc(r->pool, sizeof(MEFsave_rec)); /* Then the saving */ saved->connection = r->connection; - saved->orig_in_addr = *remote_in_addr; + saved->orig_addr = *r->connection->remote_addr; saved->orig_remote_ip = r->connection->remote_ip; - saved->new_in_addr = inet_addr(spoof_ip); + + /* setup new address + * it's easy if address family is not changed. + */ + port = r->connection->remote_addr->port; + if (apr_sockaddr_info_get(&sa, spoof_ip, + r->connection->remote_addr->family, port, + 0, r->pool) != APR_SUCCESS) + { +#if APR_HAVE_IPV6 + /* Try inter-family conversion. + * If if the real connection is served via ipv4, it + * implies Give up if connection is ipv4 and real client is ipv6. + */ + char *buf; + if (r->connection->remote_addr->family != APR_INET6) + { + return DECLINED; + } + buf = apr_palloc(r->pool, strlen(spoof_ip) + sizeof("::ffff:")); + sprintf(buf, "::ffff:%s", spoof_ip); + if (apr_sockaddr_info_get(&sa, buf, APR_INET6, port, + 0, r->pool) != APR_SUCCESS) + { + return DECLINED; + } +#else + return DECLINED; +#endif + } + + saved->new_addr = *sa; saved->new_remote_ip = spoof_ip; saved->per_dir_config = r->per_dir_config; saved->debug = conf->debug; @@ -480,7 +406,7 @@ static int spoof_initial(request_rec *r, char *spoof_ip, char *phase) saved->other_saved = NULL; saved->other_r = NULL; /* Then the modifying of the conn_rec */ - *remote_in_addr = saved->new_in_addr; + *saved->connection->remote_addr = saved->new_addr; saved->connection->remote_ip = saved->new_remote_ip; saved->conn_rec_mod_state = CONN_REC_MODIFIED; /* Force re-evaluation of the remote_host value */ @@ -520,7 +446,6 @@ static int spoof_initial(request_rec *r, char *spoof_ip, char *phase) static int spoof_not_initial(request_rec *this_r, request_rec *other_r, char *phase) { - in_addr_t *remote_in_addr; MEFsave_rec *saved; MEFsave_rec *other_saved; /* @@ -540,10 +465,9 @@ static int spoof_not_initial(request_rec *this_r, request_rec *other_r, saved = apr_pcalloc(this_r->pool, sizeof(MEFsave_rec)); /* Then the copying */ saved->connection = other_saved->connection; - remote_in_addr = get_remote_in_addr(saved->connection); - saved->orig_in_addr = other_saved->orig_in_addr; + saved->orig_addr = other_saved->orig_addr; saved->orig_remote_ip = other_saved->orig_remote_ip; - saved->new_in_addr = other_saved->new_in_addr; + saved->new_addr = other_saved->new_addr; saved->new_remote_ip = other_saved->new_remote_ip; saved->per_dir_config = other_saved->per_dir_config; saved->debug = other_saved->debug; @@ -552,7 +476,11 @@ static int spoof_not_initial(request_rec *this_r, request_rec *other_r, saved->other_saved = other_saved; saved->other_r = other_r; /* Ensure the conn_rec is spoofed */ - *remote_in_addr = saved->new_in_addr; + if (saved->connection->remote_addr->family != saved->new_addr.family) + { + return DECLINED; + } + *saved->connection->remote_addr = saved->new_addr; this_r->connection->remote_ip = saved->new_remote_ip; saved->conn_rec_mod_state = CONN_REC_MODIFIED; /* Force re-evaluation of the remote_host value */ @@ -589,16 +517,11 @@ static int spoof_not_initial(request_rec *this_r, request_rec *other_r, * The undo_spoof() function undoes the changes made to a conn_rec * by spoof_initial() or spoof_not_initial() */ -static int undo_spoof(MEFsave_rec *saved, request_rec *r, char *phase) +int extract_forwarded_undo_spoof(MEFsave_rec *saved, request_rec *r, + char *phase) { - in_addr_t *remote_in_addr; - if ((remote_in_addr = get_remote_in_addr(saved->connection)) == NULL) - { - /* Could not get a valid value so give up */ - return DECLINED; - } /* Do the restoring */ - *remote_in_addr = saved->orig_in_addr; + *saved->connection->remote_addr = saved->orig_addr; saved->connection->remote_ip = saved->orig_remote_ip; saved->connection->remote_host = NULL; ap_get_remote_host(saved->connection, saved->per_dir_config, @@ -641,16 +564,11 @@ static int undo_spoof(MEFsave_rec *saved, request_rec *r, char *phase) * subordinate, request_rec which is (should be) using the same * conn_rec as the primary request */ -static int redo_spoof(MEFsave_rec *saved, request_rec *r, char *phase) +int extract_forwarded_redo_spoof(MEFsave_rec *saved, request_rec *r, + char *phase) { - in_addr_t *remote_in_addr; - if ((remote_in_addr = get_remote_in_addr(saved->connection)) == NULL) - { - /* Could not get a valid value so give up */ - return DECLINED; - } /* Modify it all again */ - *remote_in_addr = saved->new_in_addr; + *saved->connection->remote_addr = saved->new_addr; saved->connection->remote_ip = saved->new_remote_ip; saved->connection->remote_host = NULL; ap_get_remote_host(saved->connection, saved->per_dir_config, @@ -704,7 +622,7 @@ static int redo_spoof(MEFsave_rec *saved, request_rec *r, char *phase) static int cleanup_initial(void *data) { MEFsave_rec *saved = (MEFsave_rec *)data; - return undo_spoof(saved, NULL, "cleanup initial"); + return extract_forwarded_undo_spoof(saved, NULL, "cleanup initial"); } static int cleanup_not_initial(void *data) @@ -712,13 +630,15 @@ static int cleanup_not_initial(void *data) MEFsave_rec *saved = (MEFsave_rec *)data; if (saved->other_saved->conn_rec_mod_state == CONN_REC_MODIFIED) { - return redo_spoof(saved->other_saved, saved->other_r, - "cleanup not initial"); + return extract_forwarded_redo_spoof(saved->other_saved, + saved->other_r, + "cleanup not initial"); } else { - return undo_spoof(saved->other_saved, saved->other_r, - "cleanup not initial"); + return extract_forwarded_undo_spoof(saved->other_saved, + saved->other_r, + "cleanup not initial"); } } @@ -758,7 +678,7 @@ static int primary_request(request_rec *r, char *phase) { if (conf->debug == MEF_DEBUG_ON) { - fprintf(stderr,"MEF: phase:%s, $s not acceptabler proxy, %s\n", + fprintf(stderr,"MEF: phase:%s, %s not acceptabler proxy, %s\n", phase, conn->remote_ip, r->unparsed_uri); fflush(stderr); } @@ -917,66 +837,6 @@ static int mef_access_check(request_rec *r) return mef_composite(r, "access check"); } -/* - * mef_before_proxy_http() is called if Apache 2's HTTP proxy_http handler - * is about to act and undoes the spoofing of the conn_rec associated with - * the incoming request if the proxy is about to add information to the - * request's X-Forwarded-For header. Without this the wrong IP (the - * spoof one) is added to the X-Forwarded-For header. - */ -static int mef_before_proxy_http(request_rec *r, - proxy_server_conf *pconf, - char *url, const char *proxyname, - apr_port_t proxyport) -{ - MEFsave_rec *saved; - /* - * If our post-read-request handler did something we may have to too - */ - if ((saved = (MEFsave_rec *)ap_get_module_config(r->request_config, - &extract_forwarded_module)) != NULL) - { - /* - * If proxy_http is going to add X-Forwarded-For info then we have - * have to undo the changes we made earlier so proxy_http can get - * it right - */ - if (PROXYREQ_REVERSE == r->proxyreq) - { - undo_spoof(saved, r, "before proxy http"); - } - } - return DECLINED; -} - -/* - * mef_logging() is used to redo the spoofing of the conn_rec associated - * with the incoming request if was undone. - * Redoing the spoof is to ensure that the spoof IP is used for logging - * information about the request - */ -static int mef_logging(request_rec *r) -{ - MEFsave_rec *saved; - /* - * If our post-read-request handler did something we may have to too - */ - if ((saved = (MEFsave_rec *)ap_get_module_config(r->request_config, - &extract_forwarded_module)) != NULL) - { - /* - * If we undid the spoof, probably because proxy_http was adding - * X-Forwarded-For info, then we want to redo the changes we - * undid so the spook IP is logged - */ - if (saved->conn_rec_mod_state == CONN_REC_RESTORED) - { - redo_spoof(saved, r, "logging"); - } - } - return DECLINED; -} - /*--------------------------------------------------------------------------*/ /* */ /* Data structures pulling all the mef module's bits together */ @@ -996,16 +856,6 @@ static void mef_register_hooks(apr_pool_t *p) * ap_hook_header_parser(mef_header_parser, NULL, NULL, APR_HOOK_FIRST); */ ap_hook_access_checker(mef_access_check, NULL, NULL, APR_HOOK_FIRST); -#ifdef USING_proxy_http_module - /* - * Only need to register the following handlers if proxy_http_module - * is going to be loaded - */ - static const char *const mef_proxy_b4[] = { "proxy_http.c", NULL }; - proxy_hook_scheme_handler(mef_before_proxy_http, NULL, mef_proxy_b4, - APR_HOOK_FIRST); - ap_hook_log_transaction(mef_logging, NULL, NULL, APR_HOOK_FIRST); -#endif /* USING_proxy_http_module */ } /* @@ -1015,6 +865,8 @@ static void mef_register_hooks(apr_pool_t *p) * translation and hence directory information is unavailable for the * request. */ +static char its_an_accept; + static const command_rec mef_cmds[] = { AP_INIT_TAKE1( @@ -1043,15 +895,15 @@ static const command_rec mef_cmds[] = ), AP_INIT_ITERATE( "MEFaccept", /* directive name */ - mef_accept_proxy, /* config action routine */ - NULL, /* argument to include in call */ + mef_config_proxy, /* config action routine */ + &its_an_accept, /* argument to include in call */ RSRC_CONF, /* where available */ /* description */ "One or more proxy names or IPs to accept, or 'all'" ), AP_INIT_ITERATE( "MEFrefuse", /* directive name */ - mef_refuse_proxy, /* config action routine */ + mef_config_proxy, /* config action routine */ NULL, /* argument to include in call */ RSRC_CONF, /* where available */ /* description */ diff --git a/mod_extract_forwarded.conf b/mod_extract_forwarded.conf new file mode 100644 index 0000000..b5606a5 --- /dev/null +++ b/mod_extract_forwarded.conf @@ -0,0 +1,52 @@ + + # MEForder: + # This can have either of two value 'refuse,accept' or + # 'accept,refuse' and specifies the order in which the + # information in two associated directives, MEFaccept + # and MEFrefuse, are intepreted. The MEFaccept and + # MEFrefuse directives are each used to spcifiy one or + # more IP numbers. + MEForder refuse,accept + + # MEFrefuse: + # This can be 'all' OR a space separated list of IP numbers + # and/or domain names of trusted proxy servers whose IP number + # can be derived by DNS from the domain name. The presence of + # 'all' overrides any particular IP numbers and means + # that no proxy servers are to be trusted. Individual IP + # numbers mean that those the proxy servers having them + # are not to be trusted. This defaults to 'all'. + MEFrefuse all + + # MEFaccept: + # This can be 'all' OR a space separated list of IP numbers + # and/or domain names of trusted proxy servers whose IP number + # can be derived by DNS from the domain name. The presence of + # 'all' overrides any particular IP numbers and means + # that all proxy servers are to be trusted. Individual IP + # numbers mean that those the proxy servers having them + # are to be trusted. This defaults to an empty list of + # trusted IP numbers. + # MEFaccept + + # MEFaddenv: + # This can be 'off', 'on' (the default) or a string. 'off' + # means that when spoofing, do not add an environment variable + # whose value is the IP number of the connecting machine. + # 'on' means that when spoofing, add an environment variable + # called 'MEF_RPROXY_ADDR' whose value is the IP number of the + # connecting machine. A string means that when spoofing, add + # an environment variable named by the string supplied whose + # value is the IP number of the connecting machine. + + # MEFdebug: + # This can be 'on' or 'off' (the default). When turned 'on' + # information about how the mod_extract_forwarded module is + # processing every request to your Apache 2 server, and any + # associated internal redirects or subsrequests, is written + # to the server's error_log. Thhe amount of output written + # and the way it is generated is such that you would never + # normally want to turn this feature on. This feature is + # intended for debugging operation of the mod_extract_forwarded + # mod_module and it is unlikely you will want to do that. + diff --git a/mod_extract_forwarded.h b/mod_extract_forwarded.h new file mode 100644 index 0000000..96b49a1 --- /dev/null +++ b/mod_extract_forwarded.h @@ -0,0 +1,131 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +#ifndef MOD_EXTRACT_FORWARDED_H +#define MOD_EXTRACT_FORWARDED_H + +#include "mod_proxy.h" +#include "mod_extract_forwarded.h" + +extern module AP_MODULE_DECLARE_DATA extract_forwarded_module; + +/* + * Per directory configuration record. + */ +typedef struct { + int order; /* order in which the accept and refuse specs are applied */ + int debug; /* debug output to error log flag */ + const char *envar; /* name of env var to add */ + apr_table_t *accept_proxies; /* proxies to trust */ + apr_table_t *refuse_proxies; /* proxies to distrust */ +} mef_config; + +/* + * Two possible orders in which the accept and refuse specs are applied + */ +#define REFUSE_THEN_ACCEPT 0 +#define ACCEPT_THEN_REFUSE 1 + +/* + * To output debug info to error log or not + */ +#define MEF_DEBUG_OFF 0 +#define MEF_DEBUG_ON 1 + +/* + * Maximum number of IPs in an X-Forwarded-For header of a request before + * it is treated a excessive and hence absurd + */ +#define MEF_ABSURD_PROXY_LIMIT 32 +/* + * Default environment variable name + */ +#define MEF_PROXY_ADDR "MEF_PROXY_ADDR" + +/* + * The MEFsave_rec data structure is used to preserve information that + * this module has modified in the conn_rec associated with a request + * so that the conn_rec can be restored to its original state as needed. + * It also carries information between transaction phases and internal + * redirects and subrequests + */ +typedef struct MEFsave_rec MEFsave_rec; + +struct MEFsave_rec { + conn_rec *connection; /* connection record being used */ + apr_sockaddr_t orig_addr; /* original remote address */ + apr_sockaddr_t new_addr; /* modified remote address */ + char *orig_remote_ip; /* original remote_ip */ + char *new_remote_ip; /* modified remote_ip */ + int conn_rec_mod_state; /* conn_rec modification state */ + int debug; /* are we printing MEF debug */ + const char *envar; /* name of env var to add */ + void *per_dir_config; /* per_dir_config applicable */ + MEFsave_rec *other_saved; /* any preceding req's save_rec */ + request_rec *other_r; /* any preceding req's request_rec */ +}; + +#define CONN_REC_MODIFIED 1 +#define CONN_REC_RESTORED 0 + +/* + * Functions provided by mod_extract_forwarded + */ +extern int extract_forwarded_undo_spoof(MEFsave_rec *saved, request_rec *r, + char *phase); +extern int extract_forwarded_redo_spoof(MEFsave_rec *saved, request_rec *r, + char *phase); + +#endif diff --git a/mod_extract_forwarded_proxy.c b/mod_extract_forwarded_proxy.c new file mode 100644 index 0000000..1b53d36 --- /dev/null +++ b/mod_extract_forwarded_proxy.c @@ -0,0 +1,175 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +#include "mod_proxy.h" + +#include "mod_extract_forwarded.h" + +/* + * The undo_spoof() function undoes the changes made to a conn_rec by + * spoof_initial() or spoof_not_initial() + */ +extern int extract_forwarded_undo_spoof(MEFsave_rec *saved, request_rec *r, + char *phase); + +/* + * The redo_spoof() function reapplies the changes made to a + * conn_rec by spoof_initial() or spoof_not_initial(): + * + * 1. after a prior call to undo_spoof has removed them, typically + * because of proxy_http reverse-proxy X-Forwarded-For issue + * 2. when an internal redirect or subrequest has generated a new + * subordinate, request_rec which is (should be) using the same + * conn_rec as the primary request + */ +extern int extract_forwarded_redo_spoof(MEFsave_rec *saved, request_rec *r, + char *phase); + +/* + * mef_before_proxy_http() is called if Apache 2's HTTP proxy_http handler + * is about to act and undoes the spoofing of the conn_rec associated with + * the incoming request if the proxy is about to add information to the + * request's X-Forwarded-For header. Without this the wrong IP (the + * spoof one) is added to the X-Forwarded-For header. + */ +static int mef_before_proxy_http(request_rec *r, +#if AP_SERVER_MINORVERSION_NUMBER >= 2 + proxy_worker *worker, +#endif + proxy_server_conf *pconf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + MEFsave_rec *saved; + /* + * If our post-read-request handler did something we may have to too + */ + if ((saved = (MEFsave_rec *)ap_get_module_config(r->request_config, + &extract_forwarded_module)) != NULL) + { + /* + * If proxy_http is going to add X-Forwarded-For info then we have + * have to undo the changes we made earlier so proxy_http can get + * it right + */ + if (PROXYREQ_REVERSE == r->proxyreq) + { + extract_forwarded_undo_spoof(saved, r, "before proxy http"); + } + } + return DECLINED; +} + +/* + * mef_logging() is used to redo the spoofing of the conn_rec associated + * with the incoming request if was undone. + * Redoing the spoof is to ensure that the spoof IP is used for logging + * information about the request + */ +static int mef_logging(request_rec *r) +{ + MEFsave_rec *saved; + /* + * If our post-read-request handler did something we may have to too + */ + if ((saved = (MEFsave_rec *)ap_get_module_config(r->request_config, + &extract_forwarded_module)) != NULL) + { + /* + * If we undid the spoof, probably because proxy_http was adding + * X-Forwarded-For info, then we want to redo the changes we + * undid so the spook IP is logged + */ + if (saved->conn_rec_mod_state == CONN_REC_RESTORED) + { + redo_spoof(saved, r, "logging"); + } + } + return DECLINED; +} + +/*--------------------------------------------------------------------------*/ +/* */ +/* Data structures pulling all the mef module's bits together */ +/* */ +/*--------------------------------------------------------------------------*/ +/* + * mef module's functions attached to request processing hooks. + */ +static void mef_register_hooks(apr_pool_t *p) +{ + /* + * Only need to register the following handlers if proxy_http_module + * is going to be loaded + */ + static const char *const mef_proxy_b4[] = { "proxy_http.c", NULL }; + proxy_hook_scheme_handler(mef_before_proxy_http, NULL, mef_proxy_b4, + APR_HOOK_FIRST); + ap_hook_log_transaction(mef_logging, NULL, NULL, APR_HOOK_FIRST); +} + +/* + * mef module's definition for configuration. + */ +module AP_MODULE_DECLARE_DATA extract_forwarded_module = +{ + STANDARD20_MODULE_STUFF, + NULL, /* per-directory config creator */ + NULL, /* dir config merger */ + NULL, /* server config creator */ + NULL, /* server config merger */ + NULL, /* command table */ + mef_register_hooks, /* set up other request processing hooks */ +};