xref: /lighttpd1.4/src/mod_extforward.c (revision e11f72b1)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "log.h"
5 #include "buffer.h"
6 #include "http_header.h"
7 #include "request.h"
8 #include "sock_addr.h"
9 
10 #include "plugin.h"
11 
12 #include <limits.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <errno.h>
16 
17 #include "sys-socket.h"
18 
19 /**
20  * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com
21  *                  extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu
22  *                  support chained proxies by [email protected], #1528
23  *
24  * Config example:
25  *
26  *       Trust proxy 10.0.0.232 and 10.0.0.232
27  *       extforward.forwarder = ( "10.0.0.232" => "trust",
28  *                                "10.0.0.233" => "trust" )
29  *
30  *       Trust all proxies  (NOT RECOMMENDED!)
31  *       extforward.forwarder = ( "all" => "trust")
32  *
33  *       Note that "all" has precedence over specific entries,
34  *       so "all except" setups will not work.
35  *
36  *       In case you have chained proxies, you can add all their IP's to the
37  *       config. However "all" has effect only on connecting IP, as the
38  *       X-Forwarded-For header can not be trusted.
39  *
40  * Note: The effect of this module is variable on $HTTP["remotip"] directives and
41  *       other module's remote ip dependent actions.
42  *  Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP.
43  *  Things done in between these two moments will match on the real client's IP.
44  *  The moment things are done by a module depends on in which hook it does things and within the same hook
45  *  on whether they are before/after us in the module loading order
46  *  (order in the server.modules directive in the config file).
47  *
48  * Tested behaviours:
49  *
50  *  mod_access: Will match on the real client.
51  *
52  *  mod_accesslog:
53  *   In order to see the "real" ip address in access log ,
54  *   you'll have to load mod_extforward after mod_accesslog.
55  *   like this:
56  *
57  *    server.modules  = (
58  *       .....
59  *       mod_accesslog,
60  *       mod_extforward
61  *    )
62  */
63 
64 
65 typedef enum {
66 	PROXY_FORWARDED_NONE         = 0x00,
67 	PROXY_FORWARDED_FOR          = 0x01,
68 	PROXY_FORWARDED_PROTO        = 0x02,
69 	PROXY_FORWARDED_HOST         = 0x04,
70 	PROXY_FORWARDED_BY           = 0x08,
71 	PROXY_FORWARDED_REMOTE_USER  = 0x10
72 } proxy_forwarded_t;
73 
74 struct sock_addr_mask {
75   sock_addr addr;
76   int bits;
77 };
78 
79 struct forwarder_cfg {
80   const array *forwarder;
81   int forward_all;
82   uint32_t addrs_used;
83  #if defined(__STDC_VERSION__) && __STDC_VERSION__-0 >= 199901L /* C99 */
84   struct sock_addr_mask addrs[];
85  #else
86   struct sock_addr_mask addrs[1];
87  #endif
88 };
89 
90 typedef struct {
91     const array *forwarder;
92     int forward_all;
93     uint32_t forward_masks_used;
94     const struct sock_addr_mask *forward_masks;
95     const array *headers;
96     unsigned int opts;
97     char hap_PROXY;
98     char hap_PROXY_ssl_client_verify;
99 } plugin_config;
100 
101 typedef struct {
102     PLUGIN_DATA;
103     plugin_config defaults;
104     plugin_config conf;
105     array *default_headers;
106 } plugin_data;
107 
108 static plugin_data *mod_extforward_plugin_data_singleton;
109 static int extforward_check_proxy;
110 
111 
112 /* context , used for restore remote ip */
113 
114 typedef struct {
115 	/* per-request state */
116 	sock_addr saved_remote_addr;
117 	buffer *saved_remote_addr_buf;
118 
119 	/* hap-PROXY protocol prior to receiving first request */
120 	int(*saved_network_read)(connection *, chunkqueue *, off_t);
121 
122 	/* connection-level state applied to requests in handle_request_env */
123 	array *env;
124 	int ssl_client_verify;
125 } handler_ctx;
126 
127 
128 static handler_ctx * handler_ctx_init(void) {
129 	handler_ctx * hctx;
130 	hctx = calloc(1, sizeof(*hctx));
131 	force_assert(hctx);
132 	return hctx;
133 }
134 
135 static void handler_ctx_free(handler_ctx *hctx) {
136 	free(hctx);
137 }
138 
139 INIT_FUNC(mod_extforward_init) {
140 	return calloc(1, sizeof(plugin_data));
141 }
142 
143 FREE_FUNC(mod_extforward_free) {
144     plugin_data * const p = p_d;
145     array_free(p->default_headers);
146     if (NULL == p->cvlist) return;
147     /* (init i to 0 if global context; to 1 to skip empty global context) */
148     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
149         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
150         for (; -1 != cpv->k_id; ++cpv) {
151             switch (cpv->k_id) {
152               case 0: /* extforward.forwarder */
153                 if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v);
154                 break;
155               default:
156                 break;
157             }
158         }
159     }
160 }
161 
162 static void mod_extforward_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
163     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
164       case 0: /* extforward.forwarder */
165         if (cpv->vtype == T_CONFIG_LOCAL) {
166             const struct forwarder_cfg * const fwd = cpv->v.v;
167             pconf->forwarder = fwd->forwarder;
168             pconf->forward_all = fwd->forward_all;
169             pconf->forward_masks_used = fwd->addrs_used;
170             pconf->forward_masks = fwd->addrs;
171         }
172         break;
173       case 1: /* extforward.headers */
174         pconf->headers = cpv->v.a;
175         break;
176       case 2: /* extforward.params */
177         if (cpv->vtype == T_CONFIG_LOCAL)
178             pconf->opts = cpv->v.u;
179         break;
180       case 3: /* extforward.hap-PROXY */
181         pconf->hap_PROXY = (char)cpv->v.u;
182         break;
183       case 4: /* extforward.hap-PROXY-ssl-client-verify */
184         pconf->hap_PROXY_ssl_client_verify = (char)cpv->v.u;
185         break;
186       default:/* should not happen */
187         return;
188     }
189 }
190 
191 static void mod_extforward_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
192     do {
193         mod_extforward_merge_config_cpv(pconf, cpv);
194     } while ((++cpv)->k_id != -1);
195 }
196 
197 static void mod_extforward_patch_config(request_st * const r, plugin_data * const p) {
198     memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
199     for (int i = 1, used = p->nconfig; i < used; ++i) {
200         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
201             mod_extforward_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
202     }
203 }
204 
205 static void * mod_extforward_parse_forwarder(server *srv, const array *forwarder) {
206     const data_string * const allds = (const data_string *)
207       array_get_element_klen(forwarder, CONST_STR_LEN("all"));
208     const int forward_all = (NULL == allds)
209       ? 0
210       : buffer_eq_icase_slen(&allds->value, CONST_STR_LEN("trust")) ? 1 : -1;
211     uint32_t nmasks = 0;
212     for (uint32_t j = 0; j < forwarder->used; ++j) {
213         data_string * const ds = (data_string *)forwarder->data[j];
214         char * const nm_slash = strchr(ds->key.ptr, '/');
215         if (NULL != nm_slash) ++nmasks;
216         if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("trust"))) {
217             if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("untrusted")))
218                 log_error(srv->errh, __FILE__, __LINE__,
219                   "ERROR: expect \"trust\", not \"%s\" => \"%s\"; "
220                   "treating as untrusted", ds->key.ptr, ds->value.ptr);
221             if (NULL != nm_slash) {
222                 /* future: consider adding member next to bits in sock_addr_mask
223                  *         with bool trusted/untrusted member */
224                 --nmasks;
225                 log_error(srv->errh, __FILE__, __LINE__,
226                   "ERROR: untrusted CIDR masks are ignored (\"%s\" => \"%s\")",
227                   ds->key.ptr, ds->value.ptr);
228             }
229             buffer_clear(&ds->value); /* empty is untrusted */
230             continue;
231         }
232     }
233 
234     struct forwarder_cfg * const fwd =
235       malloc(sizeof(struct forwarder_cfg)+sizeof(struct sock_addr_mask)*nmasks);
236     force_assert(fwd);
237     memset(fwd, 0,
238            sizeof(struct forwarder_cfg) + sizeof(struct sock_addr_mask)*nmasks);
239     fwd->forwarder = forwarder;
240     fwd->forward_all = forward_all;
241     fwd->addrs_used = 0;
242     for (uint32_t j = 0; j < forwarder->used; ++j) {
243         data_string * const ds = (data_string *)forwarder->data[j];
244         char * const nm_slash = strchr(ds->key.ptr, '/');
245         if (NULL == nm_slash) continue;
246         if (buffer_string_is_empty(&ds->value)) continue; /* ignored */
247 
248         char *err;
249         const int nm_bits = strtol(nm_slash + 1, &err, 10);
250         int rc;
251         if (*err || nm_bits <= 0) {
252             log_error(srv->errh, __FILE__, __LINE__,
253               "ERROR: invalid netmask: %s %s", ds->key.ptr, err);
254             free(fwd);
255             return NULL;
256         }
257         struct sock_addr_mask * const sm = fwd->addrs + fwd->addrs_used++;
258         sm->bits = nm_bits;
259         *nm_slash = '\0';
260         rc = sock_addr_from_str_numeric(&sm->addr, ds->key.ptr, srv->errh);
261         *nm_slash = '/';
262         if (1 != rc) {
263             free(fwd);
264             return NULL;
265         }
266         buffer_clear(&ds->value);
267         /* empty is untrusted,
268          * e.g. if subnet (incorrectly) appears in X-Forwarded-For */
269     }
270 
271     return fwd;
272 }
273 
274 static unsigned int mod_extforward_parse_opts(server *srv, const array *opts_params) {
275     unsigned int opts = 0;
276     for (uint32_t j = 0, used = opts_params->used; j < used; ++j) {
277         proxy_forwarded_t param;
278         data_unset *du = opts_params->data[j];
279       #if 0  /*("for" and "proto" historical behavior: always enabled)*/
280         if (buffer_eq_slen(&du->key, CONST_STR_LEN("by")))
281             param = PROXY_FORWARDED_BY;
282         else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for")))
283             param = PROXY_FORWARDED_FOR;
284         else
285       #endif
286         if (buffer_eq_slen(&du->key, CONST_STR_LEN("host")))
287             param = PROXY_FORWARDED_HOST;
288       #if 0
289         else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto")))
290             param = PROXY_FORWARDED_PROTO;
291       #endif
292         else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user")))
293             param = PROXY_FORWARDED_REMOTE_USER;
294         else {
295             log_error(srv->errh, __FILE__, __LINE__,
296               "extforward.params keys must be one of: "
297               "host, remote_user, but not: %s", du->key.ptr);
298             return HANDLER_ERROR;
299         }
300 
301         if (du->type == TYPE_STRING) {
302             data_string *ds = (data_string *)du;
303             if (buffer_eq_slen(&ds->value, CONST_STR_LEN("enable"))) {
304                 opts |= param;
305             }
306             else if (!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))) {
307                 log_error(srv->errh, __FILE__, __LINE__,
308                   "extforward.params values must be one of: "
309                   "0, 1, enable, disable; error for key: %s", du->key.ptr);
310                 return UINT_MAX;
311             }
312         }
313         else if (du->type == TYPE_INTEGER) {
314             data_integer *di = (data_integer *)du;
315             if (di->value) opts |= param;
316         }
317         else {
318             log_error(srv->errh, __FILE__, __LINE__,
319               "extforward.params values must be one of: "
320               "0, 1, enable, disable; error for key: %s", du->key.ptr);
321             return UINT_MAX;
322         }
323     }
324     return opts;
325 }
326 
327 SETDEFAULTS_FUNC(mod_extforward_set_defaults) {
328     static const config_plugin_keys_t cpk[] = {
329       { CONST_STR_LEN("extforward.forwarder"),
330         T_CONFIG_ARRAY_KVSTRING,
331         T_CONFIG_SCOPE_CONNECTION }
332      ,{ CONST_STR_LEN("extforward.headers"),
333         T_CONFIG_ARRAY_VLIST,
334         T_CONFIG_SCOPE_CONNECTION }
335      ,{ CONST_STR_LEN("extforward.params"),
336         T_CONFIG_ARRAY_KVANY,
337         T_CONFIG_SCOPE_CONNECTION }
338      ,{ CONST_STR_LEN("extforward.hap-PROXY"),
339         T_CONFIG_BOOL,
340         T_CONFIG_SCOPE_CONNECTION }
341      ,{ CONST_STR_LEN("extforward.hap-PROXY-ssl-client-verify"),
342         T_CONFIG_BOOL,
343         T_CONFIG_SCOPE_CONNECTION }
344      ,{ NULL, 0,
345         T_CONFIG_UNSET,
346         T_CONFIG_SCOPE_UNSET }
347     };
348 
349     plugin_data * const p = p_d;
350     if (!config_plugin_values_init(srv, p, cpk, "mod_extforward"))
351         return HANDLER_ERROR;
352 
353     int hap_PROXY = 0;
354 
355     /* process and validate config directives
356      * (init i to 0 if global context; to 1 to skip empty global context) */
357     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
358         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
359         for (; -1 != cpv->k_id; ++cpv) {
360             switch (cpv->k_id) {
361               case 0: /* extforward.forwarder */
362                 cpv->v.v = mod_extforward_parse_forwarder(srv, cpv->v.a);
363                 if (NULL == cpv->v.v) {
364                     log_error(srv->errh, __FILE__, __LINE__,
365                       "unexpected value for %s", cpk[cpv->k_id].k);
366                     return HANDLER_ERROR;
367                 }
368                 cpv->vtype = T_CONFIG_LOCAL;
369                 break;
370               case 1: /* extforward.headers */
371                 break;
372               case 2: /* extforward.params */
373                 cpv->v.u = mod_extforward_parse_opts(srv, cpv->v.a);
374                 if (UINT_MAX == cpv->v.u)
375                     return HANDLER_ERROR;
376                 break;
377               case 3: /* extforward.hap-PROXY */
378                 if (cpv->v.u) hap_PROXY = 1;
379                 break;
380               case 4: /* extforward.hap-PROXY-ssl-client-verify */
381                 break;
382               default:/* should not happen */
383                 break;
384             }
385         }
386     }
387 
388     mod_extforward_plugin_data_singleton = p;
389     p->defaults.opts = PROXY_FORWARDED_NONE;
390 
391     /* initialize p->defaults from global config context */
392     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
393         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
394         if (-1 != cpv->k_id)
395             mod_extforward_merge_config(&p->defaults, cpv);
396     }
397 
398     /* default to "X-Forwarded-For" or "Forwarded-For" if extforward.headers
399      * is not specified or is empty (and not using hap_PROXY) */
400     if (!p->defaults.hap_PROXY
401         && (NULL == p->defaults.headers || 0 == p->defaults.headers->used)) {
402         p->defaults.headers = p->default_headers = array_init(2);
403         array_insert_value(p->default_headers,CONST_STR_LEN("X-Forwarded-For"));
404         array_insert_value(p->default_headers,CONST_STR_LEN("Forwarded-For"));
405     }
406 
407     /* attempt to warn if mod_extforward is not last module loaded to hook
408      * handle_connection_accept.  (Nice to have, but remove this check if
409      * it reaches too far into internals and prevents other code changes.)
410      * While it would be nice to check connection_handle_accept plugin slot
411      * to make sure mod_extforward is last, that info is private to plugin.c
412      * so merely warn if mod_openssl is loaded after mod_extforward, though
413      * future modules which hook connection_handle_accept might be missed.*/
414     if (hap_PROXY) {
415         uint32_t i;
416         for (i = 0; i < srv->srvconf.modules->used; ++i) {
417             data_string *ds = (data_string *)srv->srvconf.modules->data[i];
418             if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_extforward")))
419                 break;
420         }
421         for (; i < srv->srvconf.modules->used; ++i) {
422             data_string *ds = (data_string *)srv->srvconf.modules->data[i];
423             if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_openssl"))) {
424                 log_error(srv->errh, __FILE__, __LINE__,
425                   "mod_extforward must be loaded after mod_openssl in "
426                   "server.modules when extforward.hap-PROXY = \"enable\"");
427                 break;
428             }
429         }
430     }
431 
432     for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
433         data_string *ds = (data_string *)srv->srvconf.modules->data[i];
434         if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_proxy"))) {
435             extforward_check_proxy = 1;
436             break;
437         }
438     }
439 
440     return HANDLER_GO_ON;
441 }
442 
443 
444 /*
445    extract a forward array from the environment
446 */
447 static array *extract_forward_array(const buffer *pbuffer)
448 {
449 	array *result = array_init(8);
450 	if (!buffer_string_is_empty(pbuffer)) {
451 		const char *base, *curr;
452 		/* state variable, 0 means not in string, 1 means in string */
453 		int in_str = 0;
454 		for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) {
455 			int hex_or_colon = (light_isxdigit(*curr) || *curr == ':');
456 			if (in_str) {
457 				if (!hex_or_colon && *curr != '.') {
458 					/* found an separator , insert value into result array */
459 					array_insert_value(result, base, curr - base);
460 					/* change state to not in string */
461 					in_str = 0;
462 				}
463 			} else {
464 				if (hex_or_colon) {
465 					/* found leading char of an IP address, move base pointer and change state */
466 					base = curr;
467 					in_str = 1;
468 				}
469 			}
470 		}
471 		/* if breaking out while in str, we got to the end of string, so add it */
472 		if (in_str) {
473 			array_insert_value(result, base, curr - base);
474 		}
475 	}
476 	return result;
477 }
478 
479 /*
480  * check whether ip is trusted, return 1 for trusted , 0 for untrusted
481  */
482 static int is_proxy_trusted(plugin_data *p, const char * const ip, size_t iplen)
483 {
484     const data_string *ds =
485       (const data_string *)array_get_element_klen(p->conf.forwarder, ip, iplen);
486     if (NULL != ds) return !buffer_string_is_empty(&ds->value);
487 
488     if (p->conf.forward_masks_used) {
489         const struct sock_addr_mask * const addrs = p->conf.forward_masks;
490         const uint32_t aused = p->conf.forward_masks_used;
491         sock_addr addr;
492         /* C funcs inet_aton(), inet_pton() require '\0'-terminated IP str */
493         char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/
494         if (iplen >= sizeof(addrstr)) return 0;
495         memcpy(addrstr, ip, iplen);
496         addrstr[iplen] = '\0';
497 
498         if (1 != sock_addr_inet_pton(&addr, addrstr, AF_INET,  0)
499          && 1 != sock_addr_inet_pton(&addr, addrstr, AF_INET6, 0)) return 0;
500 
501         for (uint32_t i = 0; i < aused; ++i) {
502             if (sock_addr_is_addr_eq_bits(&addr, &addrs[i].addr, addrs[i].bits))
503                 return 1;
504         }
505     }
506 
507     return 0;
508 }
509 
510 static int is_connection_trusted(connection * const con, plugin_data *p)
511 {
512     if (p->conf.forward_all) return (1 == p->conf.forward_all);
513     return is_proxy_trusted(p, CONST_BUF_LEN(con->dst_addr_buf));
514 }
515 
516 /*
517  * Return last address of proxy that is not trusted.
518  * Do not accept "all" keyword here.
519  */
520 static const char *last_not_in_array(array *a, plugin_data *p)
521 {
522 	int i;
523 
524 	for (i = a->used - 1; i >= 0; i--) {
525 		data_string *ds = (data_string *)a->data[i];
526 		if (!is_proxy_trusted(p, CONST_BUF_LEN(&ds->value))) {
527 			return ds->value.ptr;
528 		}
529 	}
530 	return NULL;
531 }
532 
533 static int mod_extforward_set_addr(request_st * const r, plugin_data *p, const char *addr) {
534 	connection * const con = r->con;
535 	sock_addr sock;
536 	handler_ctx *hctx = r->plugin_ctx[p->id];
537 
538 	if (r->conf.log_request_handling) {
539 		log_error(r->conf.errh, __FILE__, __LINE__, "using address: %s", addr);
540 	}
541 
542 	sock.plain.sa_family = AF_UNSPEC;
543 	if (1 != sock_addr_from_str_numeric(&sock, addr, r->conf.errh)) return 0;
544 	if (sock.plain.sa_family == AF_UNSPEC) return 0;
545 
546 	/* we found the remote address, modify current connection and save the old address */
547 	if (hctx) {
548 		if (hctx->saved_remote_addr_buf) {
549 			if (r->conf.log_request_handling) {
550 				log_error(r->conf.errh, __FILE__, __LINE__,
551 				  "-- mod_extforward_uri_handler already patched this connection, resetting state");
552 			}
553 			con->dst_addr = hctx->saved_remote_addr;
554 			buffer_free(con->dst_addr_buf);
555 			con->dst_addr_buf = hctx->saved_remote_addr_buf;
556 			hctx->saved_remote_addr_buf = NULL;
557 		}
558 	} else {
559 		r->plugin_ctx[p->id] = hctx = handler_ctx_init();
560 	}
561 	/* save old address */
562 	if (extforward_check_proxy) {
563 		http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR"), CONST_BUF_LEN(con->dst_addr_buf));
564 	}
565 	hctx->saved_remote_addr = con->dst_addr;
566 	hctx->saved_remote_addr_buf = con->dst_addr_buf;
567 	/* patch connection address */
568 	con->dst_addr = sock;
569 	con->dst_addr_buf = buffer_init_string(addr);
570 
571 	if (r->conf.log_request_handling) {
572 		log_error(r->conf.errh, __FILE__, __LINE__,
573 		  "patching con->dst_addr_buf for the accesslog: %s", addr);
574 	}
575 
576 	/* Now, clean the conf_cond cache, because we may have changed the results of tests */
577 	config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP);
578 
579 	return 1;
580 }
581 
582 static void mod_extforward_set_proto(request_st * const r, const char * const proto, size_t protolen) {
583 	if (0 != protolen && !buffer_is_equal_caseless_string(&r->uri.scheme, proto, protolen)) {
584 		/* update scheme if X-Forwarded-Proto is set
585 		 * Limitations:
586 		 * - Only "http" or "https" are currently accepted since the request to lighttpd currently has to
587 		 *   be HTTP/1.0 or HTTP/1.1 using http or https.  If this is changed, then the scheme from this
588 		 *   untrusted header must be checked to contain only alphanumeric characters, and to be a
589 		 *   reasonable length, e.g. < 256 chars.
590 		 * - r->uri.scheme is not reset in mod_extforward_restore() but is currently not an issues since
591 		 *   r->uri.scheme will be reset by next request.  If a new module uses r->uri.scheme in the
592 		 *   handle_request_done hook, then should evaluate if that module should use the forwarded value
593 		 *   (probably) or the original value.
594 		 */
595 		if (extforward_check_proxy) {
596 			http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO"), CONST_BUF_LEN(&r->uri.scheme));
597 		}
598 		if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("https"))) {
599 			buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("https"));
600 			config_cond_cache_reset_item(r, COMP_HTTP_SCHEME);
601 		} else if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("http"))) {
602 			buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("http"));
603 			config_cond_cache_reset_item(r, COMP_HTTP_SCHEME);
604 		}
605 	}
606 }
607 
608 static handler_t mod_extforward_X_Forwarded_For(request_st * const r, plugin_data * const p, const buffer * const x_forwarded_for) {
609 	/* build forward_array from forwarded data_string */
610 	array *forward_array = extract_forward_array(x_forwarded_for);
611 	const char *real_remote_addr = last_not_in_array(forward_array, p);
612 	if (real_remote_addr != NULL) { /* parsed */
613 		/* get scheme if X-Forwarded-Proto is set
614 		 * Limitations:
615 		 * - X-Forwarded-Proto may or may not be set by proxies, even if X-Forwarded-For is set
616 		 * - X-Forwarded-Proto may be a comma-separated list if there are multiple proxies,
617 		 *   but the historical behavior of the code below only honored it if there was exactly one value
618 		 *   (not done: walking backwards in X-Forwarded-Proto the same num of steps
619 		 *    as in X-Forwarded-For to find proto set by last trusted proxy)
620 		 */
621 		const buffer *x_forwarded_proto = http_header_request_get(r, HTTP_HEADER_X_FORWARDED_PROTO, CONST_STR_LEN("X-Forwarded-Proto"));
622 		if (mod_extforward_set_addr(r, p, real_remote_addr) && NULL != x_forwarded_proto) {
623 			mod_extforward_set_proto(r, CONST_BUF_LEN(x_forwarded_proto));
624 		}
625 	}
626 	array_free(forward_array);
627 	return HANDLER_GO_ON;
628 }
629 
630 static int find_end_quoted_string (const char * const s, int i) {
631     do {
632         ++i;
633     } while (s[i] != '"' && s[i] != '\0' && (s[i] != '\\' || s[++i] != '\0'));
634     return i;
635 }
636 
637 static int find_next_semicolon_or_comma_or_eq (const char * const s, int i) {
638     for (; s[i] != '=' && s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) {
639         if (s[i] == '"') {
640             i = find_end_quoted_string(s, i);
641             if (s[i] == '\0') return -1;
642         }
643     }
644     return i;
645 }
646 
647 static int find_next_semicolon_or_comma (const char * const s, int i) {
648     for (; s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) {
649         if (s[i] == '"') {
650             i = find_end_quoted_string(s, i);
651             if (s[i] == '\0') return -1;
652         }
653     }
654     return i;
655 }
656 
657 static int buffer_backslash_unescape (buffer * const b) {
658     /* (future: might move to buffer.c) */
659     size_t j = 0;
660     size_t len = buffer_string_length(b);
661     char *p = memchr(b->ptr, '\\', len);
662 
663     if (NULL == p) return 1; /*(nothing to do)*/
664 
665     len -= (size_t)(p - b->ptr);
666     for (size_t i = 0; i < len; ++i) {
667         if (p[i] == '\\') {
668             if (++i == len) return 0; /*(invalid trailing backslash)*/
669         }
670         p[j++] = p[i];
671     }
672     buffer_string_set_length(b, (size_t)(p+j - b->ptr));
673     return 1;
674 }
675 
676 static handler_t mod_extforward_Forwarded (request_st * const r, plugin_data * const p, const buffer * const forwarded) {
677     /* HTTP list need not consist of param=value tokens,
678      * but this routine expect such for HTTP Forwarded header
679      * Since info in each set of params is only used if from
680      * admin-specified trusted proxy:
681      * - invalid param=value tokens are ignored and skipped
682      * - not checking "for" exists in each set of params
683      * - not checking for duplicated params in each set of params
684      * - not checking canonical form of addr (also might be obfuscated)
685      * - obfuscated tokens permitted in chain, though end of trust is expected
686      *   to be non-obfuscated IP for mod_extforward to masquerade as remote IP
687      * future: since (potentially) trusted proxies begin at end of string,
688      *   it might be better to parse from end of string rather than parsing from
689      *   beginning.  Doing so would also allow reducing arbitrary param limit
690      *   to number of params permitted per proxy.
691      */
692     char * const s = forwarded->ptr;
693     int i = 0, j = -1, v, vlen, k, klen;
694     int used = (int)buffer_string_length(forwarded);
695     int ofor = -1, oproto, ohost, oby, oremote_user;
696     int offsets[256];/*(~50 params is more than reasonably expected to handle)*/
697     while (i < used) {
698         while (s[i] == ' ' || s[i] == '\t') ++i;
699         if (s[i] == ';') { ++i; continue; }
700         if (s[i] == ',') {
701             if (j >= (int)(sizeof(offsets)/sizeof(int))) break;
702             offsets[++j] = -1; /*("offset" separating params from next proxy)*/
703             ++i;
704             continue;
705         }
706         if (s[i] == '\0') break;
707 
708         k = i;
709         i = find_next_semicolon_or_comma_or_eq(s, i);
710         if (i < 0) {
711             /*(reject IP spoofing if attacker sets improper quoted-string)*/
712             log_error(r->conf.errh, __FILE__, __LINE__,
713               "invalid quoted-string in Forwarded header");
714             r->http_status = 400; /* Bad Request */
715             r->handler_module = NULL;
716             return HANDLER_FINISHED;
717         }
718         if (s[i] != '=') continue;
719         klen = i - k;
720         v = ++i;
721         i = find_next_semicolon_or_comma(s, i);
722         if (i < 0) {
723             /*(reject IP spoofing if attacker sets improper quoted-string)*/
724             log_error(r->conf.errh, __FILE__, __LINE__,
725               "invalid quoted-string in Forwarded header");
726             r->http_status = 400; /* Bad Request */
727             r->handler_module = NULL;
728             return HANDLER_FINISHED;
729         }
730         vlen = i - v;              /* might be 0 */
731 
732         /* have k, klen, v, vlen
733          * (might contain quoted string) (contents not validated or decoded)
734          * (might be repeated k)
735          */
736         if (0 == klen) continue;   /* invalid k */
737         if (j >= (int)(sizeof(offsets)/sizeof(int))-4) break;
738         offsets[j+1] = k;
739         offsets[j+2] = klen;
740         offsets[j+3] = v;
741         offsets[j+4] = vlen;
742         j += 4;
743     }
744 
745     if (j >= (int)(sizeof(offsets)/sizeof(int))-4) {
746         /* error processing Forwarded; too many params; fail closed */
747         log_error(r->conf.errh, __FILE__, __LINE__,
748           "Too many params in Forwarded header");
749         r->http_status = 400; /* Bad Request */
750         r->handler_module = NULL;
751         return HANDLER_FINISHED;
752     }
753 
754     if (-1 == j) return HANDLER_GO_ON;  /* make no changes */
755     used = j+1;
756     offsets[used] = -1; /* mark end of last set of params */
757 
758     while (j >= 4) { /*(param=value pairs)*/
759         if (-1 == offsets[j]) { --j; continue; }
760         do {
761             j -= 3; /*(k, klen, v, vlen come in sets of 4)*/
762         } while ((3 != offsets[j+1]  /* 3 == sizeof("for")-1 */
763                   || !buffer_eq_icase_ssn(s+offsets[j], "for", 3))
764                  && 0 != j-- && -1 != offsets[j]);
765         if (j < 0) break;
766         if (-1 == offsets[j]) { --j; continue; }
767 
768         /* remove trailing spaces/tabs and double-quotes from string
769          * (note: not unescaping backslash escapes in quoted string) */
770         v = offsets[j+2];
771         vlen = v + offsets[j+3];
772         while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen;
773         if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') {
774             offsets[j+2] = ++v;
775             --vlen;
776             if (s[v] == '[') {
777                 /* remove "[]" surrounding IPv6, as well as (optional) port
778                  * (assumes properly formatted IPv6 addr from trusted proxy) */
779                 ++v;
780                 do { --vlen; } while (vlen > v && s[vlen] != ']');
781                 if (v == vlen) {
782                     log_error(r->conf.errh, __FILE__, __LINE__,
783                       "Invalid IPv6 addr in Forwarded header");
784                     r->http_status = 400; /* Bad Request */
785                     r->handler_module = NULL;
786                     return HANDLER_FINISHED;
787                 }
788             }
789             else if (s[v] != '_' && s[v] != '/' && s[v] != 'u') {
790                 /* remove (optional) port from non-obfuscated IPv4 */
791                 for (klen=vlen, vlen=v; vlen < klen && s[vlen] != ':'; ++vlen) ;
792             }
793             offsets[j+2] = v;
794         }
795         offsets[j+3] = vlen - v;
796 
797         /* obfuscated ipstr and obfuscated port are also accepted here, as
798          * is path to unix domain socket, but note that backslash escapes
799          * in quoted-string were not unescaped above.  Also, if obfuscated
800          * identifiers are rotated by proxies as recommended by RFC, then
801          * maintaining list of trusted identifiers is non-trivial and is not
802          * attempted by this module. */
803 
804         if (v != vlen) {
805             int trusted = is_proxy_trusted(p, s+v, vlen-v);
806 
807             if (s[v] != '_' && s[v] != '/'
808                 && (7 != (vlen - v) || 0 != memcmp(s+v, "unknown", 7))) {
809                 ofor = j; /* save most recent non-obfuscated ipstr */
810             }
811 
812             if (!trusted) break;
813         }
814 
815         do { --j; } while (j > 0 && -1 != offsets[j]);
816         if (j <= 0) break;
817         --j;
818     }
819 
820     if (-1 != ofor) {
821         /* C funcs getaddrinfo(), inet_addr() require '\0'-terminated IP str */
822         char *ipend = s+offsets[ofor+2]+offsets[ofor+3];
823         char c = *ipend;
824         int rc;
825         *ipend = '\0';
826         rc = mod_extforward_set_addr(r, p, s+offsets[ofor+2]);
827         *ipend = c;
828         if (!rc) return HANDLER_GO_ON; /* invalid addr; make no changes */
829     }
830     else {
831         return HANDLER_GO_ON; /* make no changes */
832     }
833 
834     /* parse out params associated with for=<ip> addr set above */
835     oproto = ohost = oby = oremote_user = -1;
836     j = ofor;
837     if (j > 0) { do { --j; } while (j > 0 && -1 != offsets[j]); }
838     if (-1 == offsets[j]) ++j;
839     if (j == ofor) j += 4;
840     for (; -1 != offsets[j]; j+=4) { /*(k, klen, v, vlen come in sets of 4)*/
841         switch (offsets[j+1]) {
842          #if 0
843           case 2:
844             if (buffer_eq_icase_ssn(s+offsets[j], "by", 2))
845                 oby = j;
846             break;
847          #endif
848          #if 0
849           /*(already handled above to find IP prior to earliest trusted proxy)*/
850           case 3:
851             if (buffer_eq_icase_ssn(s+offsets[j], "for", 3))
852                 ofor = j;
853             break;
854          #endif
855           case 4:
856             if (buffer_eq_icase_ssn(s+offsets[j], "host", 4))
857                 ohost = j;
858             break;
859           case 5:
860             if (buffer_eq_icase_ssn(s+offsets[j], "proto", 5))
861                 oproto = j;
862             break;
863           case 11:
864             if (buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11))
865                 oremote_user = j;
866             break;
867           default:
868             break;
869         }
870     }
871     i = ++j;
872 
873     if (-1 != oproto) {
874         /* remove trailing spaces/tabs, and double-quotes from proto
875          * (note: not unescaping backslash escapes in quoted string) */
876         v = offsets[oproto+2];
877         vlen = v + offsets[oproto+3];
878         while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen;
879         if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { ++v; --vlen; }
880         mod_extforward_set_proto(r, s+v, vlen-v);
881     }
882 
883     if (p->conf.opts & PROXY_FORWARDED_HOST) {
884         /* Limitations:
885          * - r->http_host is not reset in mod_extforward_restore()
886          *   but is currently not an issues since r->http_host will be
887          *   reset by next request.  If a new module uses r->http_host
888          *   in the handle_request_done hook, then should evaluate if that
889          *   module should use the forwarded value (probably) or original value.
890          * - due to need to decode and unescape host=..., some extra work is
891          *   done in the case where host matches current Host header.
892          *   future: might add code to check if Host has actually changed or not
893          *
894          * note: change host after mod_extforward_set_proto() since that may
895          *       affect scheme port used in http_request_host_policy() host
896          *       normalization
897          */
898 
899         /* find host param set by earliest trusted proxy in proxy chain
900          * (host might be changed anywhere along the chain) */
901         for (j = i; j < used && -1 == ohost; ) {
902             if (-1 == offsets[j]) { ++j; continue; }
903             if (4 == offsets[j+1]
904                 && buffer_eq_icase_ssn(s+offsets[j], "host", 4))
905                 ohost = j;
906             j += 4; /*(k, klen, v, vlen come in sets of 4)*/
907         }
908         if (-1 != ohost) {
909             if (extforward_check_proxy
910                 && !buffer_string_is_empty(r->http_host)) {
911                 http_header_env_set(r,
912                                     CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST"),
913                                     CONST_BUF_LEN(r->http_host));
914             }
915             /* remove trailing spaces/tabs, and double-quotes from host */
916             v = offsets[ohost+2];
917             vlen = v + offsets[ohost+3];
918             while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen;
919             if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') {
920                 ++v; --vlen;
921                 buffer_copy_string_len(r->http_host, s+v, vlen-v);
922                 if (!buffer_backslash_unescape(r->http_host)) {
923                     log_error(r->conf.errh, __FILE__, __LINE__,
924                       "invalid host= value in Forwarded header");
925                     r->http_status = 400; /* Bad Request */
926                     r->handler_module = NULL;
927                     return HANDLER_FINISHED;
928                 }
929             }
930             else {
931                 buffer_copy_string_len(r->http_host, s+v, vlen-v);
932             }
933 
934             int scheme_port =
935               buffer_eq_slen(&r->uri.scheme,CONST_STR_LEN("https")) ? 443 : 80;
936             if (0 != http_request_host_policy(r->http_host,
937                                               r->conf.http_parseopts,
938                                               scheme_port)) {
939                 /*(reject invalid chars in Host)*/
940                 log_error(r->conf.errh, __FILE__, __LINE__,
941                   "invalid host= value in Forwarded header");
942                 r->http_status = 400; /* Bad Request */
943                 r->handler_module = NULL;
944                 return HANDLER_FINISHED;
945             }
946 
947             config_cond_cache_reset_item(r, COMP_HTTP_HOST);
948         }
949     }
950 
951     if (p->conf.opts & PROXY_FORWARDED_REMOTE_USER) {
952         /* find remote_user param set by closest proxy
953          * (auth may have been handled by any trusted proxy in proxy chain) */
954         for (j = i; j < used; ) {
955             if (-1 == offsets[j]) { ++j; continue; }
956             if (11 == offsets[j+1]
957                 && buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11))
958                 oremote_user = j;
959             j += 4; /*(k, klen, v, vlen come in sets of 4)*/
960         }
961         if (-1 != oremote_user) {
962             /* ???: should we also support param for auth_type ??? */
963             /* remove trailing spaces/tabs, and double-quotes from remote_user*/
964             v = offsets[oremote_user+2];
965             vlen = v + offsets[oremote_user+3];
966             while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen;
967             if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') {
968                 buffer *euser;
969                 ++v; --vlen;
970                 http_header_env_set(r,
971                                     CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v);
972                 euser = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER"));
973                 force_assert(NULL != euser);
974                 if (!buffer_backslash_unescape(euser)) {
975                     log_error(r->conf.errh, __FILE__, __LINE__,
976                       "invalid remote_user= value in Forwarded header");
977                     r->http_status = 400; /* Bad Request */
978                     r->handler_module = NULL;
979                     return HANDLER_FINISHED;
980                 }
981             }
982             else {
983                 http_header_env_set(r,
984                                     CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v);
985             }
986         }
987     }
988 
989   #if 0
990     if ((p->conf.opts & PROXY_FORWARDED_CREATE_XFF)
991         && NULL == http_header_request_get(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"))) {
992         /* create X-Forwarded-For if not present
993          * (and at least original connecting IP is a trusted proxy) */
994         buffer *xff = r->tmp_buf;
995         buffer_clear(xff);
996         for (j = 0; j < used; ) {
997             if (-1 == offsets[j]) { ++j; continue; }
998             if (3 == offsets[j+1]
999                 && buffer_eq_icase_ssn(s+offsets[j], "for", 3)) {
1000                 if (!buffer_string_is_empty(xff))
1001                     buffer_append_string_len(xff, CONST_STR_LEN(", "));
1002                 /* quoted-string, IPv6 brackets, and :port already removed */
1003                 v = offsets[j+2];
1004                 vlen = offsets[j+3];
1005                 buffer_append_string_len(xff, s+v, vlen);
1006                 if (s[v-1] != '=') { /*(must have been quoted-string)*/
1007                     char *x =
1008                       memchr(xff->ptr+buffer_string_length(xff)-vlen,'\\',vlen);
1009                     if (NULL != x) { /* backslash unescape in-place */
1010                         for (v = 0; x[v]; ++x) {
1011                             if (x[v] == '\\' && x[++v] == '\0')
1012                                 break; /*(invalid trailing backslash)*/
1013                             *x = x[v];
1014                         }
1015                         buffer_string_set_length(xff, x - xff->ptr);
1016                     }
1017                 }
1018                 /* skip to next group; take first "for=..." in group
1019                  * (should be 0 or 1 "for=..." per group, but not trusted) */
1020                 do { j += 4; } while (-1 != offsets[j]);
1021                 ++j;
1022                 continue;
1023             }
1024             j += 4; /*(k, klen, v, vlen come in sets of 4)*/
1025         }
1026         http_header_request_set(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"), CONST_BUF_LEN(xff));
1027     }
1028   #endif
1029 
1030     return HANDLER_GO_ON;
1031 }
1032 
1033 URIHANDLER_FUNC(mod_extforward_uri_handler) {
1034 	plugin_data *p = p_d;
1035 	const buffer *forwarded = NULL;
1036 	handler_ctx *hctx = r->plugin_ctx[p->id];
1037 	int is_forwarded_header = 0;
1038 
1039 	mod_extforward_patch_config(r, p);
1040 
1041 	if (r->conf.log_request_handling) {
1042 		log_error(r->conf.errh, __FILE__, __LINE__,
1043 		  "-- mod_extforward_uri_handler called");
1044 	}
1045 
1046 	if (p->conf.hap_PROXY_ssl_client_verify) {
1047 		const data_string *ds;
1048 		if (NULL != hctx && hctx->ssl_client_verify && NULL != hctx->env
1049 		    && NULL != (ds = (const data_string *)array_get_element_klen(hctx->env, CONST_STR_LEN("SSL_CLIENT_S_DN_CN")))) {
1050 			http_header_env_set(r,
1051 					    CONST_STR_LEN("SSL_CLIENT_VERIFY"),
1052 					    CONST_STR_LEN("SUCCESS"));
1053 			http_header_env_set(r,
1054 					    CONST_STR_LEN("REMOTE_USER"),
1055 					    CONST_BUF_LEN(&ds->value));
1056 			http_header_env_set(r,
1057 					    CONST_STR_LEN("AUTH_TYPE"),
1058 					    CONST_STR_LEN("SSL_CLIENT_VERIFY"));
1059 		} else {
1060 			http_header_env_set(r,
1061 					    CONST_STR_LEN("SSL_CLIENT_VERIFY"),
1062 					    CONST_STR_LEN("NONE"));
1063 		}
1064 	}
1065 
1066 	if (NULL == p->conf.forwarder) return HANDLER_GO_ON;
1067 	if (NULL == p->conf.headers) return HANDLER_GO_ON;
1068 	for (uint32_t k = 0; k < p->conf.headers->used && NULL == forwarded; ++k) {
1069 		buffer *hdr = &((data_string *)p->conf.headers->data[k])->value;
1070 		forwarded = http_header_request_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(hdr));
1071 		if (forwarded) {
1072 			is_forwarded_header = buffer_is_equal_caseless_string(hdr, CONST_STR_LEN("Forwarded"));
1073 			break;
1074 		}
1075 	}
1076 	if (NULL == forwarded) {
1077 		if (r->conf.log_request_handling) {
1078 			log_error(r->conf.errh, __FILE__, __LINE__,
1079 			  "no forward header found, skipping");
1080 		}
1081 
1082 		return HANDLER_GO_ON;
1083 	}
1084 
1085 	/* if the remote ip itself is not trusted, then do nothing */
1086 	if (!is_connection_trusted(r->con, p)) {
1087 		if (r->conf.log_request_handling) {
1088 			log_error(r->conf.errh, __FILE__, __LINE__,
1089 			  "remote address %s is NOT a trusted proxy, skipping",
1090 			  r->con->dst_addr_buf->ptr);
1091 		}
1092 
1093 		return HANDLER_GO_ON;
1094 	}
1095 
1096 	if (is_forwarded_header) {
1097 		return mod_extforward_Forwarded(r, p, forwarded);
1098 	}
1099 
1100 	return mod_extforward_X_Forwarded_For(r, p, forwarded);
1101 }
1102 
1103 
1104 REQUEST_FUNC(mod_extforward_handle_request_env) {
1105     plugin_data *p = p_d;
1106     handler_ctx *hctx = r->plugin_ctx[p->id];
1107     if (NULL == hctx || NULL == hctx->env) return HANDLER_GO_ON;
1108     for (uint32_t i=0; i < hctx->env->used; ++i) {
1109         /* note: replaces values which may have been set by mod_openssl
1110          * (when mod_extforward is listed after mod_openssl in server.modules)*/
1111         data_string *ds = (data_string *)hctx->env->data[i];
1112         http_header_env_set(r,
1113                             CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value));
1114     }
1115     return HANDLER_GO_ON;
1116 }
1117 
1118 
1119 REQUEST_FUNC(mod_extforward_restore) {
1120 	/* XXX: should change this to not occur at request reset,
1121 	*       but instead at connection reset */
1122 	plugin_data *p = p_d;
1123 	handler_ctx *hctx = r->plugin_ctx[p->id];
1124 
1125 	if (!hctx) return HANDLER_GO_ON;
1126 
1127 	connection * const con = r->con;
1128 	if (NULL != hctx->saved_network_read) {
1129 		con->network_read = hctx->saved_network_read;
1130 		hctx->saved_network_read = NULL;
1131 	}
1132 
1133 	if (NULL != hctx->saved_remote_addr_buf) {
1134 		con->dst_addr = hctx->saved_remote_addr;
1135 		buffer_free(con->dst_addr_buf);
1136 		con->dst_addr_buf = hctx->saved_remote_addr_buf;
1137 		hctx->saved_remote_addr_buf = NULL;
1138 		/* Now, clean the conf_cond cache, because we may have changed the results of tests */
1139 		config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP);
1140 	}
1141 
1142 	if (NULL == hctx->env) {
1143 		handler_ctx_free(hctx);
1144 		r->plugin_ctx[p->id] = NULL;
1145 	}
1146 
1147 	return HANDLER_GO_ON;
1148 }
1149 
1150 
1151 CONNECTION_FUNC(mod_extforward_handle_con_close)
1152 {
1153     request_st * const r = &con->request;
1154     plugin_data *p = p_d;
1155     handler_ctx *hctx = r->plugin_ctx[p->id];
1156     if (NULL != hctx) {
1157         if (NULL != hctx->saved_network_read) {
1158             con->network_read = hctx->saved_network_read;
1159         }
1160         if (NULL != hctx->saved_remote_addr_buf) {
1161             con->dst_addr = hctx->saved_remote_addr;
1162             buffer_free(con->dst_addr_buf);
1163             con->dst_addr_buf = hctx->saved_remote_addr_buf;
1164         }
1165         if (NULL != hctx->env) {
1166             array_free(hctx->env);
1167         }
1168         handler_ctx_free(hctx);
1169         r->plugin_ctx[p->id] = NULL;
1170     }
1171 
1172     return HANDLER_GO_ON;
1173 }
1174 
1175 
1176 static int mod_extforward_network_read (connection *con, chunkqueue *cq, off_t max_bytes);
1177 
1178 CONNECTION_FUNC(mod_extforward_handle_con_accept)
1179 {
1180     request_st * const r = &con->request;
1181     plugin_data *p = p_d;
1182     mod_extforward_patch_config(r, p);
1183     if (!p->conf.hap_PROXY) return HANDLER_GO_ON;
1184     if (NULL == p->conf.forwarder) return HANDLER_GO_ON;
1185     if (is_connection_trusted(con, p)) {
1186         handler_ctx *hctx = handler_ctx_init();
1187         r->plugin_ctx[p->id] = hctx;
1188         hctx->saved_network_read = con->network_read;
1189         con->network_read = mod_extforward_network_read;
1190     }
1191     else {
1192         if (r->conf.log_request_handling) {
1193             log_error(r->conf.errh, __FILE__, __LINE__,
1194               "remote address %s is NOT a trusted proxy, skipping",
1195               con->dst_addr_buf->ptr);
1196         }
1197     }
1198     return HANDLER_GO_ON;
1199 }
1200 
1201 
1202 int mod_extforward_plugin_init(plugin *p);
1203 int mod_extforward_plugin_init(plugin *p) {
1204 	p->version     = LIGHTTPD_VERSION_ID;
1205 	p->name        = "extforward";
1206 
1207 	p->init        = mod_extforward_init;
1208 	p->handle_connection_accept = mod_extforward_handle_con_accept;
1209 	p->handle_uri_raw = mod_extforward_uri_handler;
1210 	p->handle_request_env = mod_extforward_handle_request_env;
1211 	p->handle_request_done = mod_extforward_restore;
1212 	p->connection_reset = mod_extforward_restore;
1213 	p->handle_connection_close = mod_extforward_handle_con_close;
1214 	p->set_defaults  = mod_extforward_set_defaults;
1215 	p->cleanup     = mod_extforward_free;
1216 
1217 	return 0;
1218 }
1219 
1220 
1221 
1222 
1223 /* Modified from:
1224  *   http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
1225  *
1226 9. Sample code
1227 
1228 The code below is an example of how a receiver may deal with both versions of
1229 the protocol header for TCP over IPv4 or IPv6. The function is supposed to be
1230 called upon a read event. Addresses may be directly copied into their final
1231 memory location since they're transported in network byte order. The sending
1232 side is even simpler and can easily be deduced from this sample code.
1233  *
1234  */
1235 
1236 union hap_PROXY_hdr {
1237     struct {
1238         char line[108];
1239     } v1;
1240     struct {
1241         uint8_t sig[12];
1242         uint8_t ver_cmd;
1243         uint8_t fam;
1244         uint16_t len;
1245         union {
1246             struct {  /* for TCP/UDP over IPv4, len = 12 */
1247                 uint32_t src_addr;
1248                 uint32_t dst_addr;
1249                 uint16_t src_port;
1250                 uint16_t dst_port;
1251             } ip4;
1252             struct {  /* for TCP/UDP over IPv6, len = 36 */
1253                  uint8_t  src_addr[16];
1254                  uint8_t  dst_addr[16];
1255                  uint16_t src_port;
1256                  uint16_t dst_port;
1257             } ip6;
1258             struct {  /* for AF_UNIX sockets, len = 216 */
1259                  uint8_t src_addr[108];
1260                  uint8_t dst_addr[108];
1261             } unx;
1262         } addr;
1263     } v2;
1264 };
1265 
1266 /*
1267 If the length specified in the PROXY protocol header indicates that additional
1268 bytes are part of the header beyond the address information, a receiver may
1269 choose to skip over and ignore those bytes, or attempt to interpret those
1270 bytes.
1271 
1272 The information in those bytes will be arranged in Type-Length-Value (TLV
1273 vectors) in the following format.  The first byte is the Type of the vector.
1274 The second two bytes represent the length in bytes of the value (not included
1275 the Type and Length bytes), and following the length field is the number of
1276 bytes specified by the length.
1277  */
1278 struct pp2_tlv {
1279     uint8_t type;
1280     uint8_t length_hi;
1281     uint8_t length_lo;
1282     /*uint8_t value[0];*//* C99 zero-length array */
1283 };
1284 
1285 /*
1286 The following types have already been registered for the <type> field :
1287  */
1288 
1289 #define PP2_TYPE_ALPN             0x01
1290 #define PP2_TYPE_AUTHORITY        0x02
1291 #define PP2_TYPE_CRC32C           0x03
1292 #define PP2_TYPE_NOOP             0x04
1293 #define PP2_TYPE_SSL              0x20
1294 #define PP2_SUBTYPE_SSL_VERSION   0x21
1295 #define PP2_SUBTYPE_SSL_CN        0x22
1296 #define PP2_SUBTYPE_SSL_CIPHER    0x23
1297 #define PP2_SUBTYPE_SSL_SIG_ALG   0x24
1298 #define PP2_SUBTYPE_SSL_KEY_ALG   0x25
1299 #define PP2_TYPE_NETNS            0x30
1300 
1301 /*
1302 For the type PP2_TYPE_SSL, the value is itselv a defined like this :
1303  */
1304 
1305 struct pp2_tlv_ssl {
1306     uint8_t  client;
1307     uint32_t verify;
1308     /*struct pp2_tlv sub_tlv[0];*//* C99 zero-length array */
1309 };
1310 
1311 /*
1312 And the <client> field is made of a bit field from the following values,
1313 indicating which element is present :
1314  */
1315 
1316 #define PP2_CLIENT_SSL            0x01
1317 #define PP2_CLIENT_CERT_CONN      0x02
1318 #define PP2_CLIENT_CERT_SESS      0x04
1319 
1320 
1321 
1322 
1323 #ifndef MSG_DONTWAIT
1324 #define MSG_DONTWAIT 0
1325 #endif
1326 #ifndef MSG_NOSIGNAL
1327 #define MSG_NOSIGNAL 0
1328 #endif
1329 
1330 /* returns 0 if needs to poll, <0 upon error or >0 is protocol vers (success) */
1331 static int hap_PROXY_recv (const int fd, union hap_PROXY_hdr * const hdr, const int family, const int so_type)
1332 {
1333     static const char v2sig[12] =
1334         "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
1335 
1336     ssize_t ret;
1337     size_t sz;
1338     int ver;
1339 
1340     do {
1341         ret = recv(fd, hdr, sizeof(*hdr), MSG_PEEK|MSG_DONTWAIT|MSG_NOSIGNAL);
1342     } while (-1 == ret && errno == EINTR);
1343 
1344     if (-1 == ret)
1345         return (errno == EAGAIN
1346                 #ifdef EWOULDBLOCK
1347                 #if EAGAIN != EWOULDBLOCK
1348                 || errno == EWOULDBLOCK
1349                 #endif
1350                 #endif
1351                ) ? 0 : -1;
1352 
1353     if (ret >= 16 && 0 == memcmp(&hdr->v2, v2sig, 12)
1354         && (hdr->v2.ver_cmd & 0xF0) == 0x20) {
1355         ver = 2;
1356         sz = 16 + (size_t)ntohs(hdr->v2.len);
1357         if ((size_t)ret < sz)
1358             return -2; /* truncated or too large header */
1359 
1360         switch (hdr->v2.ver_cmd & 0xF) {
1361           case 0x01: break; /* PROXY command */
1362           case 0x00: break; /* LOCAL command */
1363           default:   return -2; /* not a supported command */
1364         }
1365     }
1366     else if (ret >= 8 && 0 == memcmp(hdr->v1.line, "PROXY", 5)) {
1367         const char *end = memchr(hdr->v1.line, '\r', ret - 1);
1368         if (!end || end[1] != '\n')
1369             return -2; /* partial or invalid header */
1370         ver = 1;
1371         sz = (size_t)(end + 2 - hdr->v1.line); /* skip header + CRLF */
1372     }
1373     else {
1374         /* Wrong protocol */
1375         return -2;
1376     }
1377 
1378     /* we need to consume the appropriate amount of data from the socket
1379      * (overwrites existing contents of hdr with same data) */
1380     UNUSED(family);
1381     UNUSED(so_type);
1382     do {
1383       #if defined(MSG_TRUNC) && defined(__linux__)
1384         if ((family==AF_INET || family==AF_INET6) && so_type == SOCK_STREAM) {
1385             ret = recv(fd, hdr, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL);
1386             if (ret >= 0 || errno != EINVAL) continue;
1387         }
1388       #endif
1389         ret = recv(fd, hdr, sz, MSG_DONTWAIT|MSG_NOSIGNAL);
1390     } while (-1 == ret && errno == EINTR);
1391     if (ret < 0) return -1;
1392     if (ret != (ssize_t)sz) {
1393         errno = EIO; /*(partial read; valid but unexpected; not handled)*/
1394         return -1;
1395     }
1396     if (1 == ver) hdr->v1.line[sz-2] = '\0'; /*terminate str to ease parsing*/
1397     return ver;
1398 }
1399 
1400 
1401 static int mod_extforward_hap_PROXY_v1 (connection * const con,
1402                                         union hap_PROXY_hdr * const hdr)
1403 {
1404   #ifdef __COVERITY__
1405     __coverity_tainted_data_sink__(hdr);
1406   #endif
1407 
1408     /* samples
1409      *   "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n"
1410      *   "PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
1411      *   "PROXY UNKNOWN\r\n"
1412      *   "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
1413      */
1414     char *s = hdr->v1.line + sizeof("PROXY")-1; /*checked in hap_PROXY_recv()*/
1415     char *src_addr, *dst_addr, *src_port, *dst_port, *e;
1416     int family;
1417     long src_lport, dst_lport;
1418     if (*s != ' ') return -1;
1419     ++s;
1420     if (s[0] == 'T' && s[1] == 'C' && s[2] == 'P' && s[4] == ' ') {
1421         if (s[3] == '4') {
1422             family = AF_INET;
1423         } else if (s[3] == '6') {
1424             family = AF_INET6;
1425         }
1426         else {
1427             return -1;
1428         }
1429         s += 5;
1430     }
1431     else if (0 == memcmp(s, "UNKNOWN", sizeof("UNKNOWN")-1)
1432              && (s[7] == '\0' || s[7] == ' ')) {
1433         return 0;     /* keep local connection address */
1434     }
1435     else {
1436         return -1;
1437     }
1438 
1439     /*(strsep() should be fairly portable, but is not standard)*/
1440     src_addr = s;
1441     dst_addr = strchr(src_addr, ' ');
1442     if (NULL == dst_addr) return -1;
1443     *dst_addr++ = '\0';
1444     src_port = strchr(dst_addr, ' ');
1445     if (NULL == src_port) return -1;
1446     *src_port++ = '\0';
1447     dst_port = strchr(src_port, ' ');
1448     if (NULL == dst_port) return -1;
1449     *dst_port++ = '\0';
1450 
1451     src_lport = strtol(src_port, &e, 10);
1452     if (src_lport <= 0 || src_lport > USHRT_MAX || *e != '\0') return -1;
1453     dst_lport = strtol(dst_port, &e, 10);
1454     if (dst_lport <= 0 || dst_lport > USHRT_MAX || *e != '\0') return -1;
1455 
1456     if (1 != sock_addr_inet_pton(&con->dst_addr,
1457                                  src_addr, family, (unsigned short)src_lport))
1458         return -1;
1459     /* Forwarded by=... could be saved here.
1460      * (see additional comments in mod_extforward_hap_PROXY_v2()) */
1461 
1462     /* re-parse addr to string to normalize
1463      * (instead of trusting PROXY to provide canonicalized src_addr string)
1464      * (should prefer PROXY v2 protocol if concerned about performance) */
1465     sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr);
1466 
1467     return 0;
1468 }
1469 
1470 
1471 static int mod_extforward_hap_PROXY_v2 (connection * const con,
1472                                         union hap_PROXY_hdr * const hdr)
1473 {
1474   #ifdef __COVERITY__
1475     __coverity_tainted_data_sink__(hdr);
1476   #endif
1477 
1478     /* If HAProxy-PROXY protocol used, then lighttpd acts as transparent proxy,
1479      * masquerading as servicing the client IP provided in by HAProxy-PROXY hdr.
1480      * The connecting con->dst_addr and con->dst_addr_buf are not saved here,
1481      * so that info is lost unless getsockname() and getpeername() are used.
1482      * One result is that mod_proxy will use the masqueraded IP instead of the
1483      * actual IP when updated Forwarded and X-Forwarded-For (but if actual
1484      * connection IPs needed, better to save the info here rather than use
1485      * syscalls to retrieve the info later).
1486      * (Exception: con->dst_addr can be further changed if mod_extforward parses
1487      *  Forwaded or X-Forwarded-For request headers later, after request headers
1488      *  have been received.)
1489      */
1490 
1491     /* Forwarded by=... could be saved here.  The by param is for backends to be
1492      * able to construct URIs for that interface (interface on server which
1493      * received request and made PROXY connection here), though that server
1494      * should provide that information in updated Forwarded or X-Forwarded-For
1495      * HTTP headers */
1496     /*struct sockaddr_storage by;*/
1497 
1498     /* Addresses provided by HAProxy-PROXY protocol are in network byte order.
1499      * Note: addr info is not validated, so do not accept HAProxy-PROXY
1500      * protocol from untrusted servers.  For example, untrusted servers from
1501      * which HAProxy-PROXY protocol is accepted (don't do that) could pretend
1502      * to be from the internal network and might thereby bypass security policy.
1503      */
1504 
1505     /* (Clear con->dst_addr with memset() in case actual and proxies IPs
1506      *  are different domains, e.g. one is IPv4 and the other is IPv6) */
1507 
1508     struct pp2_tlv *tlv;
1509     uint32_t sz = ntohs(hdr->v2.len);
1510     uint32_t len = 0;
1511 
1512     switch (hdr->v2.ver_cmd & 0xF) {
1513       case 0x01: break;    /* PROXY command */
1514       case 0x00: return  0;/* LOCAL command; keep local connection address */
1515       default:   return -1;/* should not happen; validated in hap_PROXY_recv()*/
1516     }
1517 
1518     /* PROXY command */
1519 
1520     switch (hdr->v2.fam) {
1521       case 0x11:  /* TCPv4 */
1522         sock_addr_assign(&con->dst_addr, AF_INET, hdr->v2.addr.ip4.src_port,
1523                                                  &hdr->v2.addr.ip4.src_addr);
1524         sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr);
1525        #if 0
1526         ((struct sockaddr_in *)&by)->sin_family = AF_INET;
1527         ((struct sockaddr_in *)&by)->sin_addr.s_addr =
1528             hdr->v2.addr.ip4.dst_addr;
1529         ((struct sockaddr_in *)&by)->sin_port =
1530             hdr->v2.addr.ip4.dst_port;
1531        #endif
1532         len = (uint32_t)sizeof(hdr->v2.addr.ip4);
1533         break;
1534      #ifdef HAVE_IPV6
1535       case 0x21:  /* TCPv6 */
1536         sock_addr_assign(&con->dst_addr, AF_INET6, hdr->v2.addr.ip6.src_port,
1537                                                   &hdr->v2.addr.ip6.src_addr);
1538         sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr);
1539        #if 0
1540         ((struct sockaddr_in6 *)&by)->sin6_family = AF_INET6;
1541         memcpy(&((struct sockaddr_in6 *)&by)->sin6_addr,
1542             hdr->v2.addr.ip6.dst_addr, 16);
1543         ((struct sockaddr_in6 *)&by)->sin6_port =
1544             hdr->v2.addr.ip6.dst_port;
1545        #endif
1546         len = (uint32_t)sizeof(hdr->v2.addr.ip6);
1547         break;
1548      #endif
1549      #ifdef HAVE_SYS_UN_H
1550       case 0x31:  /* UNIX domain socket */
1551         {
1552             char *src_addr = (char *)hdr->v2.addr.unx.src_addr;
1553             char *z = memchr(src_addr, '\0', UNIX_PATH_MAX);
1554             if (NULL == z) return -1; /* invalid addr; too long */
1555             len = (uint32_t)(z - src_addr + 1); /*(+1 for '\0')*/
1556             sock_addr_assign(&con->dst_addr, AF_UNIX, 0, src_addr);
1557             buffer_copy_string_len(con->dst_addr_buf, src_addr, len);
1558         }
1559        #if 0 /*(dst_addr should be identical to src_addr for AF_UNIX)*/
1560         ((struct sockaddr_un *)&by)->sun_family = AF_UNIX;
1561         memcpy(&((struct sockaddr_un *)&by)->sun_path,
1562             hdr->v2.addr.unx.dst_addr, 108);
1563        #endif
1564         len = (uint32_t)sizeof(hdr->v2.addr.unx);
1565         break;
1566      #endif
1567       default:    /* keep local connection address; unsupported protocol */
1568         return 0;
1569     }
1570 
1571     /* (optional) Type-Length-Value (TLV vectors) follow addresses */
1572 
1573     tlv = (struct pp2_tlv *)((char *)hdr + 16);
1574     for (sz -= len, len -= 3; sz >= 3; sz -= 3 + len) {
1575         tlv = (struct pp2_tlv *)((char *)tlv + 3 + len);
1576         len = ((uint32_t)tlv->length_hi << 8) | tlv->length_lo;
1577         if (3 + len > sz) break; /*(invalid TLV)*/
1578         switch (tlv->type) {
1579          #if 0 /*(not implemented here)*/
1580           case PP2_TYPE_ALPN:
1581           case PP2_TYPE_AUTHORITY:
1582           case PP2_TYPE_CRC32C:
1583          #endif
1584           case PP2_TYPE_SSL: {
1585             request_st * const r = &con->request;
1586             static const uint32_t zero = 0;
1587             handler_ctx *hctx =
1588               r->plugin_ctx[mod_extforward_plugin_data_singleton->id];
1589             struct pp2_tlv_ssl *tlv_ssl =
1590               (struct pp2_tlv_ssl *)(void *)((char *)tlv+3);
1591             struct pp2_tlv *subtlv = tlv;
1592             if (tlv_ssl->client & PP2_CLIENT_SSL) {
1593                 con->proto_default_port = 443; /* "https" */
1594             }
1595             if ((tlv_ssl->client & (PP2_CLIENT_CERT_CONN|PP2_CLIENT_CERT_SESS))
1596                 && 0 == memcmp(&tlv_ssl->verify, &zero, 4)) { /* misaligned */
1597                 hctx->ssl_client_verify = 1;
1598             }
1599             for (uint32_t subsz = len-5, n = 5; subsz >= 3; subsz -= 3 + n) {
1600                 subtlv = (struct pp2_tlv *)((char *)subtlv + 3 + n);
1601                 n = ((uint32_t)subtlv->length_hi << 8) | subtlv->length_lo;
1602                 if (3 + n > subsz) break; /*(invalid TLV)*/
1603                 if (NULL == hctx->env) hctx->env = array_init(8);
1604                 switch (subtlv->type) {
1605                   case PP2_SUBTYPE_SSL_VERSION:
1606                     array_set_key_value(hctx->env,
1607                                         CONST_STR_LEN("SSL_PROTOCOL"),
1608                                         (char *)subtlv+3, n);
1609                     break;
1610                   case PP2_SUBTYPE_SSL_CN:
1611                     /* (tlv_ssl->client & PP2_CLIENT_CERT_CONN)
1612                      *   or
1613                      * (tlv_ssl->client & PP2_CLIENT_CERT_SESS) */
1614                     array_set_key_value(hctx->env,
1615                                         CONST_STR_LEN("SSL_CLIENT_S_DN_CN"),
1616                                         (char *)subtlv+3, n);
1617                     break;
1618                   case PP2_SUBTYPE_SSL_CIPHER:
1619                     array_set_key_value(hctx->env,
1620                                         CONST_STR_LEN("SSL_CIPHER"),
1621                                         (char *)subtlv+3, n);
1622                     break;
1623                   case PP2_SUBTYPE_SSL_SIG_ALG:
1624                     array_set_key_value(hctx->env,
1625                                         CONST_STR_LEN("SSL_SERVER_A_SIG"),
1626                                         (char *)subtlv+3, n);
1627                     break;
1628                   case PP2_SUBTYPE_SSL_KEY_ALG:
1629                     array_set_key_value(hctx->env,
1630                                         CONST_STR_LEN("SSL_SERVER_A_KEY"),
1631                                         (char *)subtlv+3, n);
1632                     break;
1633                   default:
1634                     break;
1635                 }
1636             }
1637             break;
1638           }
1639          #if 0 /*(not implemented here)*/
1640           case PP2_TYPE_NETNS:
1641          #endif
1642           /*case PP2_TYPE_NOOP:*//* no-op */
1643           default:
1644             break;
1645         }
1646     }
1647 
1648     return 0;
1649 }
1650 
1651 
1652 static int mod_extforward_network_read (connection *con,
1653                                         chunkqueue *cq, off_t max_bytes)
1654 {
1655     /* XXX: when using hap-PROXY protocol, currently avoid overhead of setting
1656      * _L_ environment variables for mod_proxy to accurately set Forwarded hdr
1657      * In the future, might add config switch to enable doing this extra work */
1658 
1659     union hap_PROXY_hdr hdr;
1660     log_error_st *errh;
1661     int rc = hap_PROXY_recv(con->fd, &hdr,
1662                             con->dst_addr.plain.sa_family, SOCK_STREAM);
1663     switch (rc) {
1664       case  2: rc = mod_extforward_hap_PROXY_v2(con, &hdr); break;
1665       case  1: rc = mod_extforward_hap_PROXY_v1(con, &hdr); break;
1666       case  0: return  0; /*(errno == EAGAIN || errno == EWOULDBLOCK)*/
1667       case -1: errh = con->request.conf.errh;
1668                log_perror(errh,__FILE__,__LINE__,"hap-PROXY recv()");
1669                rc = -1; break;
1670       case -2: errh = con->request.conf.errh;
1671                log_error(errh,__FILE__,__LINE__,
1672                  "hap-PROXY proto received invalid/unsupported request");
1673                /* fall through */
1674       default: rc = -1; break;
1675     }
1676 
1677     request_st * const r = &con->request;
1678     mod_extforward_restore(r, mod_extforward_plugin_data_singleton);
1679     return (0 == rc) ? con->network_read(con, cq, max_bytes) : rc;
1680 }
1681