File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_extforward.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:20:06 2014 UTC (10 years ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, HEAD
lighttpd 1.4.35

    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(plugin_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: 		force_assert(AF_INET == sock->plain.sa_family);
  340: 		break;
  341: 	case AF_INET6:
  342: 		memcpy(&sock->ipv6, addrlist->ai_addr, sizeof(sock->ipv6));
  343: 		force_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: 		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>