Annotation of embedaddon/lighttpd/src/mod_extforward.c, revision 1.1.1.3

1.1.1.3 ! misho       1: #include "first.h"
        !             2: 
1.1       misho       3: #include "base.h"
                      4: #include "log.h"
                      5: #include "buffer.h"
                      6: 
                      7: #include "plugin.h"
                      8: 
                      9: #include "inet_ntop_cache.h"
                     10: #include "configfile.h"
                     11: 
                     12: #include <assert.h>
                     13: #include <ctype.h>
                     14: #include <stdlib.h>
                     15: #include <string.h>
                     16: #include <stdio.h>
                     17: #include <netinet/in.h>
                     18: #include <errno.h>
                     19: 
                     20: /**
                     21:  * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com
                     22:  *                  extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu
                     23:  *                  support chained proxies by glen@delfi.ee, #1528
                     24:  *
                     25:  * Config example:
                     26:  *
                     27:  *       Trust proxy 10.0.0.232 and 10.0.0.232
                     28:  *       extforward.forwarder = ( "10.0.0.232" => "trust",
                     29:  *                                "10.0.0.233" => "trust" )
                     30:  *
                     31:  *       Trust all proxies  (NOT RECOMMENDED!)
                     32:  *       extforward.forwarder = ( "all" => "trust")
                     33:  *
                     34:  *       Note that "all" has precedence over specific entries,
                     35:  *       so "all except" setups will not work.
                     36:  *
                     37:  *       In case you have chained proxies, you can add all their IP's to the
                     38:  *       config. However "all" has effect only on connecting IP, as the
                     39:  *       X-Forwarded-For header can not be trusted.
                     40:  *
                     41:  * Note: The effect of this module is variable on $HTTP["remotip"] directives and
                     42:  *       other module's remote ip dependent actions.
                     43:  *  Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP.
                     44:  *  Things done in between these two moments will match on the real client's IP.
                     45:  *  The moment things are done by a module depends on in which hook it does things and within the same hook
                     46:  *  on whether they are before/after us in the module loading order
                     47:  *  (order in the server.modules directive in the config file).
                     48:  *
                     49:  * Tested behaviours:
                     50:  *
                     51:  *  mod_access: Will match on the real client.
                     52:  *
                     53:  *  mod_accesslog:
                     54:  *   In order to see the "real" ip address in access log ,
                     55:  *   you'll have to load mod_extforward after mod_accesslog.
                     56:  *   like this:
                     57:  *
                     58:  *    server.modules  = (
                     59:  *       .....
                     60:  *       mod_accesslog,
                     61:  *       mod_extforward
                     62:  *    )
                     63:  *
                     64:  * Known issues:
                     65:  *      seems causing segfault with mod_ssl and $HTTP{"socket"} directives
                     66:  *      LEM 2006.05.26: Fixed segfault $SERVER["socket"] directive. Untested with SSL.
                     67:  *
                     68:  * ChangeLog:
                     69:  *     2005.12.19   Initial Version
                     70:  *     2005.12.19   fixed conflict with conditional directives
                     71:  *     2006.05.26   LEM: IPv6 support
                     72:  *     2006.05.26   LEM: Fix a segfault with $SERVER["socket"] directive.
                     73:  *     2006.05.26   LEM: Run at uri_raw time, as we don't need to see the URI
                     74:  *                       In this manner, we run before mod_access and $HTTP["remoteip"] directives work!
                     75:  *     2006.05.26   LEM: Clean config_cond cache of tests whose result we probably change.
                     76:  */
                     77: 
                     78: 
                     79: /* plugin config for all request/connections */
                     80: 
                     81: typedef struct {
                     82:        array *forwarder;
                     83:        array *headers;
                     84: } plugin_config;
                     85: 
                     86: typedef struct {
                     87:        PLUGIN_DATA;
                     88: 
                     89:        plugin_config **config_storage;
                     90: 
                     91:        plugin_config conf;
                     92: } plugin_data;
                     93: 
                     94: 
                     95: /* context , used for restore remote ip */
                     96: 
                     97: typedef struct {
                     98:        sock_addr saved_remote_addr;
                     99:        buffer *saved_remote_addr_buf;
                    100: } handler_ctx;
                    101: 
                    102: 
                    103: static handler_ctx * handler_ctx_init(sock_addr oldaddr, buffer *oldaddr_buf) {
                    104:        handler_ctx * hctx;
                    105:        hctx = calloc(1, sizeof(*hctx));
                    106:        hctx->saved_remote_addr = oldaddr;
                    107:        hctx->saved_remote_addr_buf = oldaddr_buf;
                    108:        return hctx;
                    109: }
                    110: 
                    111: static void handler_ctx_free(handler_ctx *hctx) {
                    112:        free(hctx);
                    113: }
                    114: 
                    115: /* init the plugin data */
                    116: INIT_FUNC(mod_extforward_init) {
                    117:        plugin_data *p;
                    118:        p = calloc(1, sizeof(*p));
                    119:        return p;
                    120: }
                    121: 
                    122: /* destroy the plugin data */
                    123: FREE_FUNC(mod_extforward_free) {
                    124:        plugin_data *p = p_d;
                    125: 
                    126:        UNUSED(srv);
                    127: 
                    128:        if (!p) return HANDLER_GO_ON;
                    129: 
                    130:        if (p->config_storage) {
                    131:                size_t i;
                    132: 
                    133:                for (i = 0; i < srv->config_context->used; i++) {
                    134:                        plugin_config *s = p->config_storage[i];
                    135: 
1.1.1.3 ! misho     136:                        if (NULL == s) continue;
1.1       misho     137: 
                    138:                        array_free(s->forwarder);
                    139:                        array_free(s->headers);
                    140: 
                    141:                        free(s);
                    142:                }
                    143:                free(p->config_storage);
                    144:        }
                    145: 
                    146: 
                    147:        free(p);
                    148: 
                    149:        return HANDLER_GO_ON;
                    150: }
                    151: 
                    152: /* handle plugin config and check values */
                    153: 
                    154: SETDEFAULTS_FUNC(mod_extforward_set_defaults) {
                    155:        plugin_data *p = p_d;
                    156:        size_t i = 0;
                    157: 
                    158:        config_values_t cv[] = {
                    159:                { "extforward.forwarder",       NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
                    160:                { "extforward.headers",         NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
                    161:                { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
                    162:        };
                    163: 
                    164:        if (!p) return HANDLER_ERROR;
                    165: 
1.1.1.2   misho     166:        p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1       misho     167: 
                    168:        for (i = 0; i < srv->config_context->used; i++) {
1.1.1.3 ! misho     169:                data_config const* config = (data_config const*)srv->config_context->data[i];
1.1       misho     170:                plugin_config *s;
                    171: 
                    172:                s = calloc(1, sizeof(plugin_config));
                    173:                s->forwarder    = array_init();
                    174:                s->headers      = array_init();
                    175: 
                    176:                cv[0].destination = s->forwarder;
                    177:                cv[1].destination = s->headers;
                    178: 
                    179:                p->config_storage[i] = s;
                    180: 
1.1.1.3 ! misho     181:                if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1.1       misho     182:                        return HANDLER_ERROR;
                    183:                }
                    184:        }
                    185: 
                    186:        return HANDLER_GO_ON;
                    187: }
                    188: 
                    189: #define PATCH(x) \
                    190:        p->conf.x = s->x;
                    191: static int mod_extforward_patch_connection(server *srv, connection *con, plugin_data *p) {
                    192:        size_t i, j;
                    193:        plugin_config *s = p->config_storage[0];
                    194: 
                    195:        PATCH(forwarder);
                    196:        PATCH(headers);
                    197: 
                    198:        /* skip the first, the global context */
                    199:        for (i = 1; i < srv->config_context->used; i++) {
                    200:                data_config *dc = (data_config *)srv->config_context->data[i];
                    201:                s = p->config_storage[i];
                    202: 
                    203:                /* condition didn't match */
                    204:                if (!config_check_cond(srv, con, dc)) continue;
                    205: 
                    206:                /* merge config */
                    207:                for (j = 0; j < dc->value->used; j++) {
                    208:                        data_unset *du = dc->value->data[j];
                    209: 
                    210:                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.forwarder"))) {
                    211:                                PATCH(forwarder);
                    212:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.headers"))) {
                    213:                                PATCH(headers);
                    214:                        }
                    215:                }
                    216:        }
                    217: 
                    218:        return 0;
                    219: }
                    220: #undef PATCH
                    221: 
                    222: 
                    223: static void put_string_into_array_len(array *ary, const char *str, int len)
                    224: {
                    225:        data_string *tempdata;
                    226:        if (len == 0)
                    227:                return;
                    228:        tempdata = data_string_init();
                    229:        buffer_copy_string_len(tempdata->value,str,len);
                    230:        array_insert_unique(ary,(data_unset *)tempdata);
                    231: }
                    232: /*
                    233:    extract a forward array from the environment
                    234: */
                    235: static array *extract_forward_array(buffer *pbuffer)
                    236: {
                    237:        array *result = array_init();
1.1.1.3 ! misho     238:        if (!buffer_string_is_empty(pbuffer)) {
1.1       misho     239:                char *base, *curr;
                    240:                /* state variable, 0 means not in string, 1 means in string */
                    241:                int in_str = 0;
                    242:                for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) {
                    243:                        if (in_str) {
                    244:                                if ((*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':' && (*curr < 'a' || *curr > 'f') && (*curr < 'A' || *curr > 'F')) {
                    245:                                        /* found an separator , insert value into result array */
                    246:                                        put_string_into_array_len(result, base, curr - base);
                    247:                                        /* change state to not in string */
                    248:                                        in_str = 0;
                    249:                                }
                    250:                        } else {
                    251:                                if ((*curr >= '0' && *curr <= '9') || *curr == ':' || (*curr >= 'a' && *curr <= 'f') || (*curr >= 'A' && *curr <= 'F')) {
                    252:                                        /* found leading char of an IP address, move base pointer and change state */
                    253:                                        base = curr;
                    254:                                        in_str = 1;
                    255:                                }
                    256:                        }
                    257:                }
                    258:                /* if breaking out while in str, we got to the end of string, so add it */
                    259:                if (in_str) {
                    260:                        put_string_into_array_len(result, base, curr - base);
                    261:                }
                    262:        }
                    263:        return result;
                    264: }
                    265: 
                    266: #define IP_TRUSTED 1
                    267: #define IP_UNTRUSTED 0
                    268: /*
                    269:  * check whether ip is trusted, return 1 for trusted , 0 for untrusted
                    270:  */
                    271: static int is_proxy_trusted(const char *ipstr, plugin_data *p)
                    272: {
                    273:        data_string* allds = (data_string *)array_get_element(p->conf.forwarder, "all");
                    274: 
                    275:        if (allds) {
                    276:                if (strcasecmp(allds->value->ptr, "trust") == 0) {
                    277:                        return IP_TRUSTED;
                    278:                } else {
                    279:                        return IP_UNTRUSTED;
                    280:                }
                    281:        }
                    282: 
                    283:        return (data_string *)array_get_element(p->conf.forwarder, ipstr) ? IP_TRUSTED : IP_UNTRUSTED;
                    284: }
                    285: 
                    286: /*
                    287:  * Return char *ip of last address of proxy that is not trusted.
                    288:  * Do not accept "all" keyword here.
                    289:  */
                    290: static const char *last_not_in_array(array *a, plugin_data *p)
                    291: {
                    292:        array *forwarder = p->conf.forwarder;
                    293:        int i;
                    294: 
                    295:        for (i = a->used - 1; i >= 0; i--) {
                    296:                data_string *ds = (data_string *)a->data[i];
                    297:                const char *ip = ds->value->ptr;
                    298: 
                    299:                if (!array_get_element(forwarder, ip)) {
                    300:                        return ip;
                    301:                }
                    302:        }
                    303:        return NULL;
                    304: }
                    305: 
                    306: #ifdef HAVE_IPV6
                    307: static void ipstr_to_sockaddr(server *srv, const char *host, sock_addr *sock) {
                    308:        struct addrinfo hints, *addrlist = NULL;
                    309:        int result;
                    310: 
                    311:        memset(&hints, 0, sizeof(hints));
                    312:        sock->plain.sa_family = AF_UNSPEC;
                    313: 
                    314: #ifndef AI_NUMERICSERV
                    315:        /**
                    316:          * quoting $ man getaddrinfo
                    317:          *
                    318:          * NOTES
                    319:          *        AI_ADDRCONFIG, AI_ALL, and AI_V4MAPPED are available since glibc 2.3.3.
                    320:          *        AI_NUMERICSERV is available since glibc 2.3.4.
                    321:          */
                    322: #define AI_NUMERICSERV 0
                    323: #endif
                    324:        hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
                    325: 
                    326:        errno = 0;
                    327:        result = getaddrinfo(host, NULL, &hints, &addrlist);
                    328: 
                    329:        if (result != 0) {
                    330:                log_error_write(srv, __FILE__, __LINE__, "SSSs(S)",
                    331:                        "could not parse ip address ", host, " because ", gai_strerror(result), strerror(errno));
                    332:        } else if (addrlist == NULL) {
                    333:                log_error_write(srv, __FILE__, __LINE__, "SSS",
                    334:                        "Problem in parsing ip address ", host, ": succeeded, but no information returned");
                    335:        } else switch (addrlist->ai_family) {
                    336:        case AF_INET:
                    337:                memcpy(&sock->ipv4, addrlist->ai_addr, sizeof(sock->ipv4));
1.1.1.2   misho     338:                force_assert(AF_INET == sock->plain.sa_family);
1.1       misho     339:                break;
                    340:        case AF_INET6:
                    341:                memcpy(&sock->ipv6, addrlist->ai_addr, sizeof(sock->ipv6));
1.1.1.2   misho     342:                force_assert(AF_INET6 == sock->plain.sa_family);
1.1       misho     343:                break;
                    344:        default:
                    345:                log_error_write(srv, __FILE__, __LINE__, "SSS",
                    346:                        "Problem in parsing ip address ", host, ": succeeded, but unknown family");
                    347:        }
                    348: 
                    349:        freeaddrinfo(addrlist);
                    350: }
                    351: #endif
                    352: 
                    353: static void clean_cond_cache(server *srv, connection *con) {
                    354:        config_cond_cache_reset_item(srv, con, COMP_HTTP_REMOTE_IP);
1.1.1.3 ! misho     355:        config_cond_cache_reset_item(srv, con, COMP_HTTP_SCHEME);
1.1       misho     356: }
                    357: 
                    358: URIHANDLER_FUNC(mod_extforward_uri_handler) {
                    359:        plugin_data *p = p_d;
                    360:        data_string *forwarded = NULL;
                    361: #ifdef HAVE_IPV6
                    362:        char b2[INET6_ADDRSTRLEN + 1];
                    363: #endif
                    364:        const char *dst_addr_str = NULL;
                    365:        array *forward_array = NULL;
                    366:        const char *real_remote_addr = NULL;
                    367: #ifdef HAVE_IPV6
                    368: #endif
                    369: 
                    370:        if (!con->request.headers) return HANDLER_GO_ON;
                    371: 
                    372:        mod_extforward_patch_connection(srv, con, p);
                    373: 
                    374:        if (con->conf.log_request_handling) {
                    375:                log_error_write(srv, __FILE__, __LINE__, "s",
                    376:                        "-- mod_extforward_uri_handler called");
                    377:        }
                    378: 
                    379:        if (p->conf.headers->used) {
                    380:                data_string *ds;
                    381:                size_t k;
                    382: 
                    383:                for(k = 0; k < p->conf.headers->used; k++) {
                    384:                        ds = (data_string *) p->conf.headers->data[k];
                    385:                        if (NULL != (forwarded = (data_string*) array_get_element(con->request.headers, ds->value->ptr))) break;
                    386:                }
                    387:        } else {
                    388:                forwarded = (data_string *) array_get_element(con->request.headers,"X-Forwarded-For");
                    389:                if (NULL == forwarded) forwarded = (data_string *) array_get_element(con->request.headers,  "Forwarded-For");
                    390:        }
                    391: 
                    392:        if (NULL == forwarded) {
                    393:                if (con->conf.log_request_handling) {
                    394:                        log_error_write(srv, __FILE__, __LINE__, "s", "no forward header found, skipping");
                    395:                }
                    396: 
                    397:                return HANDLER_GO_ON;
                    398:        }
                    399: 
                    400: #ifdef HAVE_IPV6
                    401:        dst_addr_str = inet_ntop(con->dst_addr.plain.sa_family,
                    402:                con->dst_addr.plain.sa_family == AF_INET6 ?
                    403:                        (struct sockaddr *)&(con->dst_addr.ipv6.sin6_addr) :
                    404:                        (struct sockaddr *)&(con->dst_addr.ipv4.sin_addr),
                    405:                b2, (sizeof b2) - 1);
                    406: #else
                    407:        dst_addr_str = inet_ntoa(con->dst_addr.ipv4.sin_addr);
                    408: #endif
                    409: 
                    410:        /* if the remote ip itself is not trusted, then do nothing */
                    411:        if (IP_UNTRUSTED == is_proxy_trusted(dst_addr_str, p)) {
                    412:                if (con->conf.log_request_handling) {
                    413:                        log_error_write(srv, __FILE__, __LINE__, "sss",
                    414:                                        "remote address", dst_addr_str, "is NOT a trusted proxy, skipping");
                    415:                }
                    416: 
                    417:                return HANDLER_GO_ON;
                    418:        }
                    419: 
                    420:        /* build forward_array from forwarded data_string */
                    421:        forward_array = extract_forward_array(forwarded->value);
                    422:        real_remote_addr = last_not_in_array(forward_array, p);
                    423: 
                    424:        if (real_remote_addr != NULL) { /* parsed */
                    425:                sock_addr sock;
                    426:                data_string *forwarded_proto = (data_string *)array_get_element(con->request.headers, "X-Forwarded-Proto");
                    427: 
                    428:                if (NULL != forwarded_proto) {
                    429:                        if (buffer_is_equal_caseless_string(forwarded_proto->value, CONST_STR_LEN("https"))) {
                    430:                                buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("https"));
                    431:                        } else if (buffer_is_equal_caseless_string(forwarded_proto->value, CONST_STR_LEN("http"))) {
                    432:                                buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("http"));
                    433:                        }
                    434:                }
                    435: 
                    436:                if (con->conf.log_request_handling) {
                    437:                        log_error_write(srv, __FILE__, __LINE__, "ss", "using address:", real_remote_addr);
                    438:                }
                    439: #ifdef HAVE_IPV6
                    440:                ipstr_to_sockaddr(srv, real_remote_addr, &sock);
                    441: #else
                    442:                sock.ipv4.sin_addr.s_addr = inet_addr(real_remote_addr);
                    443:                sock.plain.sa_family = (sock.ipv4.sin_addr.s_addr == 0xFFFFFFFF) ? AF_UNSPEC : AF_INET;
                    444: #endif
                    445:                if (sock.plain.sa_family != AF_UNSPEC) {
                    446:                        /* we found the remote address, modify current connection and save the old address */
                    447:                        if (con->plugin_ctx[p->id]) {
1.1.1.3 ! misho     448:                                if (con->conf.log_request_handling) {
        !           449:                                        log_error_write(srv, __FILE__, __LINE__, "s",
        !           450:                                                "-- mod_extforward_uri_handler already patched this connection, resetting state");
        !           451:                                }
1.1       misho     452:                                handler_ctx_free(con->plugin_ctx[p->id]);
                    453:                                con->plugin_ctx[p->id] = NULL;
                    454:                        }
                    455:                        /* save old address */
                    456:                        con->plugin_ctx[p->id] = handler_ctx_init(con->dst_addr, con->dst_addr_buf);
                    457:                        /* patch connection address */
                    458:                        con->dst_addr = sock;
                    459:                        con->dst_addr_buf = buffer_init();
                    460:                        buffer_copy_string(con->dst_addr_buf, real_remote_addr);
1.1.1.3 ! misho     461: 
1.1       misho     462:                        if (con->conf.log_request_handling) {
                    463:                                log_error_write(srv, __FILE__, __LINE__, "ss",
                    464:                                                "patching con->dst_addr_buf for the accesslog:", real_remote_addr);
                    465:                        }
                    466:                        /* Now, clean the conf_cond cache, because we may have changed the results of tests */
                    467:                        clean_cond_cache(srv, con);
                    468:                }
                    469:        }
                    470:        array_free(forward_array);
                    471: 
                    472:        /* not found */
                    473:        return HANDLER_GO_ON;
                    474: }
                    475: 
                    476: CONNECTION_FUNC(mod_extforward_restore) {
                    477:        plugin_data *p = p_d;
                    478:        handler_ctx *hctx = con->plugin_ctx[p->id];
                    479: 
                    480:        if (!hctx) return HANDLER_GO_ON;
                    481:        
                    482:        con->dst_addr = hctx->saved_remote_addr;
                    483:        buffer_free(con->dst_addr_buf);
                    484: 
                    485:        con->dst_addr_buf = hctx->saved_remote_addr_buf;
                    486:        
                    487:        handler_ctx_free(hctx);
                    488: 
                    489:        con->plugin_ctx[p->id] = NULL;
                    490: 
                    491:        /* Now, clean the conf_cond cache, because we may have changed the results of tests */
                    492:        clean_cond_cache(srv, con);
                    493: 
                    494:        return HANDLER_GO_ON;
                    495: }
                    496: 
                    497: 
                    498: /* this function is called at dlopen() time and inits the callbacks */
                    499: 
                    500: int mod_extforward_plugin_init(plugin *p);
                    501: int mod_extforward_plugin_init(plugin *p) {
                    502:        p->version     = LIGHTTPD_VERSION_ID;
                    503:        p->name        = buffer_init_string("extforward");
                    504: 
                    505:        p->init        = mod_extforward_init;
                    506:        p->handle_uri_raw = mod_extforward_uri_handler;
                    507:        p->handle_request_done = mod_extforward_restore;
                    508:        p->connection_reset = mod_extforward_restore;
                    509:        p->set_defaults  = mod_extforward_set_defaults;
                    510:        p->cleanup     = mod_extforward_free;
                    511: 
                    512:        p->data        = NULL;
                    513: 
                    514:        return 0;
                    515: }
                    516: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>