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>