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

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

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