"Fossies" - the Fresh Open Source Software Archive

Member "mod_extract_forwarded2-0.1/mod_extract_forwarded2.c" (9 Nov 2003, 15985 Bytes) of package /linux/www/apache_httpd_modules/old/mod_extract_forwarded2-0.1.tar.gz:


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 };