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