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

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: 
1.1.1.2 ! misho     168:        p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1       misho     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));
1.1.1.2 ! misho     339:                force_assert(AF_INET == sock->plain.sa_family);
1.1       misho     340:                break;
                    341:        case AF_INET6:
                    342:                memcpy(&sock->ipv6, addrlist->ai_addr, sizeof(sock->ipv6));
1.1.1.2 ! misho     343:                force_assert(AF_INET6 == sock->plain.sa_family);
1.1       misho     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:                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]) {
                    448:                                log_error_write(srv, __FILE__, __LINE__, "s", 
                    449:                                                "patching an already patched connection!");
                    450:                                handler_ctx_free(con->plugin_ctx[p->id]);
                    451:                                con->plugin_ctx[p->id] = NULL;
                    452:                        }
                    453:                        /* save old address */
                    454:                        con->plugin_ctx[p->id] = handler_ctx_init(con->dst_addr, con->dst_addr_buf);
                    455:                        /* patch connection address */
                    456:                        con->dst_addr = sock;
                    457:                        con->dst_addr_buf = buffer_init();
                    458:                        buffer_copy_string(con->dst_addr_buf, real_remote_addr);
                    459:                
                    460:                        if (con->conf.log_request_handling) {
                    461:                                log_error_write(srv, __FILE__, __LINE__, "ss",
                    462:                                                "patching con->dst_addr_buf for the accesslog:", real_remote_addr);
                    463:                        }
                    464:                        /* Now, clean the conf_cond cache, because we may have changed the results of tests */
                    465:                        clean_cond_cache(srv, con);
                    466:                }
                    467:        }
                    468:        array_free(forward_array);
                    469: 
                    470:        /* not found */
                    471:        return HANDLER_GO_ON;
                    472: }
                    473: 
                    474: CONNECTION_FUNC(mod_extforward_restore) {
                    475:        plugin_data *p = p_d;
                    476:        handler_ctx *hctx = con->plugin_ctx[p->id];
                    477: 
                    478:        if (!hctx) return HANDLER_GO_ON;
                    479:        
                    480:        con->dst_addr = hctx->saved_remote_addr;
                    481:        buffer_free(con->dst_addr_buf);
                    482: 
                    483:        con->dst_addr_buf = hctx->saved_remote_addr_buf;
                    484:        
                    485:        handler_ctx_free(hctx);
                    486: 
                    487:        con->plugin_ctx[p->id] = NULL;
                    488: 
                    489:        /* Now, clean the conf_cond cache, because we may have changed the results of tests */
                    490:        clean_cond_cache(srv, con);
                    491: 
                    492:        return HANDLER_GO_ON;
                    493: }
                    494: 
                    495: 
                    496: /* this function is called at dlopen() time and inits the callbacks */
                    497: 
                    498: int mod_extforward_plugin_init(plugin *p);
                    499: int mod_extforward_plugin_init(plugin *p) {
                    500:        p->version     = LIGHTTPD_VERSION_ID;
                    501:        p->name        = buffer_init_string("extforward");
                    502: 
                    503:        p->init        = mod_extforward_init;
                    504:        p->handle_uri_raw = mod_extforward_uri_handler;
                    505:        p->handle_request_done = mod_extforward_restore;
                    506:        p->connection_reset = mod_extforward_restore;
                    507:        p->set_defaults  = mod_extforward_set_defaults;
                    508:        p->cleanup     = mod_extforward_free;
                    509: 
                    510:        p->data        = NULL;
                    511: 
                    512:        return 0;
                    513: }
                    514: 

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