18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss
322e8b456SStefan Bühler #include "base.h"
422e8b456SStefan Bühler #include "log.h"
522e8b456SStefan Bühler #include "buffer.h"
63dd3cde9SGlenn Strauss #include "http_header.h"
7371e1bf7SGlenn Strauss #include "request.h"
81367f606SGlenn Strauss #include "sock_addr.h"
922e8b456SStefan Bühler
1022e8b456SStefan Bühler #include "plugin.h"
1122e8b456SStefan Bühler
1204d76e7aSGlenn Strauss #include <limits.h>
13f68fd405SElan Ruusamäe #include <stdlib.h>
14f68fd405SElan Ruusamäe #include <string.h>
1538f2d1ddSStefan Bühler #include <errno.h>
16f68fd405SElan Ruusamäe
178cf6e908SGlenn Strauss #include "sys-socket.h"
188cf6e908SGlenn Strauss
19f68fd405SElan Ruusamäe /**
20f68fd405SElan Ruusamäe * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com
21f68fd405SElan Ruusamäe * extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu
22cde46f6aSElan Ruusamäe * support chained proxies by [email protected], #1528
23f68fd405SElan Ruusamäe *
24c18f442aSGlenn Strauss *
25c18f442aSGlenn Strauss * Mostly rewritten
26c18f442aSGlenn Strauss * Portions:
27c18f442aSGlenn Strauss * Copyright(c) 2017 Glenn Strauss gstrauss()gluelogic.com All rights reserved
28c18f442aSGlenn Strauss * License: BSD 3-clause (same as lighttpd)
29c18f442aSGlenn Strauss *
30f68fd405SElan Ruusamäe * Config example:
31f68fd405SElan Ruusamäe *
32f68fd405SElan Ruusamäe * Trust proxy 10.0.0.232 and 10.0.0.232
33f68fd405SElan Ruusamäe * extforward.forwarder = ( "10.0.0.232" => "trust",
34f68fd405SElan Ruusamäe * "10.0.0.233" => "trust" )
35f68fd405SElan Ruusamäe *
36f68fd405SElan Ruusamäe * Trust all proxies (NOT RECOMMENDED!)
37f68fd405SElan Ruusamäe * extforward.forwarder = ( "all" => "trust")
38f68fd405SElan Ruusamäe *
39f68fd405SElan Ruusamäe * Note that "all" has precedence over specific entries,
40f68fd405SElan Ruusamäe * so "all except" setups will not work.
41f68fd405SElan Ruusamäe *
42cde46f6aSElan Ruusamäe * In case you have chained proxies, you can add all their IP's to the
43cde46f6aSElan Ruusamäe * config. However "all" has effect only on connecting IP, as the
44cde46f6aSElan Ruusamäe * X-Forwarded-For header can not be trusted.
45cde46f6aSElan Ruusamäe *
460e093d66SGlenn Strauss * Note: The effect of this module is variable on $HTTP["remoteip"] directives and
47f68fd405SElan Ruusamäe * other module's remote ip dependent actions.
48f68fd405SElan Ruusamäe * Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP.
49f68fd405SElan Ruusamäe * Things done in between these two moments will match on the real client's IP.
50f68fd405SElan Ruusamäe * The moment things are done by a module depends on in which hook it does things and within the same hook
51f68fd405SElan Ruusamäe * on whether they are before/after us in the module loading order
52f68fd405SElan Ruusamäe * (order in the server.modules directive in the config file).
53f68fd405SElan Ruusamäe */
54f68fd405SElan Ruusamäe
55f68fd405SElan Ruusamäe
56371e1bf7SGlenn Strauss typedef enum {
57371e1bf7SGlenn Strauss PROXY_FORWARDED_NONE = 0x00,
58371e1bf7SGlenn Strauss PROXY_FORWARDED_FOR = 0x01,
59371e1bf7SGlenn Strauss PROXY_FORWARDED_PROTO = 0x02,
60371e1bf7SGlenn Strauss PROXY_FORWARDED_HOST = 0x04,
61371e1bf7SGlenn Strauss PROXY_FORWARDED_BY = 0x08,
62371e1bf7SGlenn Strauss PROXY_FORWARDED_REMOTE_USER = 0x10
63371e1bf7SGlenn Strauss } proxy_forwarded_t;
64371e1bf7SGlenn Strauss
65fc7edb39SGlenn Strauss struct sock_addr_mask {
66fc7edb39SGlenn Strauss sock_addr addr;
67fc7edb39SGlenn Strauss int bits;
68fc7edb39SGlenn Strauss };
69fc7edb39SGlenn Strauss
70e38e907fSGlenn Strauss struct forwarder_cfg {
71e38e907fSGlenn Strauss const array *forwarder;
72e38e907fSGlenn Strauss int forward_all;
73e38e907fSGlenn Strauss uint32_t addrs_used;
74e38e907fSGlenn Strauss #if defined(__STDC_VERSION__) && __STDC_VERSION__-0 >= 199901L /* C99 */
75e38e907fSGlenn Strauss struct sock_addr_mask addrs[];
76e38e907fSGlenn Strauss #else
77e38e907fSGlenn Strauss struct sock_addr_mask addrs[1];
78e38e907fSGlenn Strauss #endif
79fc7edb39SGlenn Strauss };
80fc7edb39SGlenn Strauss
81f68fd405SElan Ruusamäe typedef struct {
82e38e907fSGlenn Strauss const array *forwarder;
83e38e907fSGlenn Strauss int forward_all;
84e38e907fSGlenn Strauss uint32_t forward_masks_used;
85e38e907fSGlenn Strauss const struct sock_addr_mask *forward_masks;
86e38e907fSGlenn Strauss const array *headers;
87371e1bf7SGlenn Strauss unsigned int opts;
88e38e907fSGlenn Strauss char hap_PROXY;
89e38e907fSGlenn Strauss char hap_PROXY_ssl_client_verify;
90f68fd405SElan Ruusamäe } plugin_config;
91f68fd405SElan Ruusamäe
92f68fd405SElan Ruusamäe typedef struct {
93f68fd405SElan Ruusamäe PLUGIN_DATA;
94e38e907fSGlenn Strauss plugin_config defaults;
95f68fd405SElan Ruusamäe plugin_config conf;
96e38e907fSGlenn Strauss array *default_headers;
972d9bf806SGlenn Strauss array tokens;
98f68fd405SElan Ruusamäe } plugin_data;
99f68fd405SElan Ruusamäe
10078cc7272SGlenn Strauss static plugin_data *mod_extforward_plugin_data_singleton;
101371e1bf7SGlenn Strauss static int extforward_check_proxy;
102371e1bf7SGlenn Strauss
103f68fd405SElan Ruusamäe
104f68fd405SElan Ruusamäe /* context , used for restore remote ip */
105f68fd405SElan Ruusamäe
106f68fd405SElan Ruusamäe typedef struct {
10778cc7272SGlenn Strauss /* per-request state */
108*e0817646SGlenn Strauss sock_addr dst_addr;
109*e0817646SGlenn Strauss buffer dst_addr_buf;
110*e0817646SGlenn Strauss } handler_rctx;
111*e0817646SGlenn Strauss
112*e0817646SGlenn Strauss typedef struct {
113*e0817646SGlenn Strauss int con_is_trusted;
114*e0817646SGlenn Strauss
115*e0817646SGlenn Strauss /* connection-level state applied to requests in handle_request_env */
116*e0817646SGlenn Strauss int ssl_client_verify;
117*e0817646SGlenn Strauss array *env;
11878cc7272SGlenn Strauss
11978cc7272SGlenn Strauss /* hap-PROXY protocol prior to receiving first request */
120010c2894SGlenn Strauss int(*saved_network_read)(connection *, chunkqueue *, off_t);
121f68fd405SElan Ruusamäe } handler_ctx;
122f68fd405SElan Ruusamäe
123f68fd405SElan Ruusamäe
handler_rctx_init(void)124*e0817646SGlenn Strauss static handler_rctx * handler_rctx_init(void) {
125*e0817646SGlenn Strauss return ck_calloc(1, sizeof(handler_rctx));
126*e0817646SGlenn Strauss }
127*e0817646SGlenn Strauss
handler_rctx_free(handler_rctx * rctx)128*e0817646SGlenn Strauss static void handler_rctx_free(handler_rctx *rctx) {
129*e0817646SGlenn Strauss free(rctx->dst_addr_buf.ptr);
130*e0817646SGlenn Strauss free(rctx);
131*e0817646SGlenn Strauss }
132*e0817646SGlenn Strauss
handler_ctx_init(void)13336adf0d9SGlenn Strauss static handler_ctx * handler_ctx_init(void) {
1345e14db43SGlenn Strauss return ck_calloc(1, sizeof(handler_ctx));
135f68fd405SElan Ruusamäe }
136f68fd405SElan Ruusamäe
handler_ctx_free(handler_ctx * hctx)137f68fd405SElan Ruusamäe static void handler_ctx_free(handler_ctx *hctx) {
138*e0817646SGlenn Strauss if (NULL != hctx->env)
139*e0817646SGlenn Strauss array_free(hctx->env);
140f68fd405SElan Ruusamäe free(hctx);
141f68fd405SElan Ruusamäe }
142f68fd405SElan Ruusamäe
INIT_FUNC(mod_extforward_init)143f68fd405SElan Ruusamäe INIT_FUNC(mod_extforward_init) {
1445e14db43SGlenn Strauss return ck_calloc(1, sizeof(plugin_data));
145f68fd405SElan Ruusamäe }
146f68fd405SElan Ruusamäe
FREE_FUNC(mod_extforward_free)147b73949e0SGlenn Strauss FREE_FUNC(mod_extforward_free) {
148b73949e0SGlenn Strauss plugin_data * const p = p_d;
149b73949e0SGlenn Strauss array_free(p->default_headers);
1502d9bf806SGlenn Strauss array_free_data(&p->tokens);
151e38e907fSGlenn Strauss if (NULL == p->cvlist) return;
152e38e907fSGlenn Strauss /* (init i to 0 if global context; to 1 to skip empty global context) */
153e38e907fSGlenn Strauss for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
154e38e907fSGlenn Strauss config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
155e38e907fSGlenn Strauss for (; -1 != cpv->k_id; ++cpv) {
156e38e907fSGlenn Strauss switch (cpv->k_id) {
157e38e907fSGlenn Strauss case 0: /* extforward.forwarder */
158e38e907fSGlenn Strauss if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v);
159e38e907fSGlenn Strauss break;
160e38e907fSGlenn Strauss default:
161e38e907fSGlenn Strauss break;
162e38e907fSGlenn Strauss }
163e38e907fSGlenn Strauss }
164e38e907fSGlenn Strauss }
165e38e907fSGlenn Strauss }
166e38e907fSGlenn Strauss
mod_extforward_merge_config_cpv(plugin_config * const pconf,const config_plugin_value_t * const cpv)167e38e907fSGlenn Strauss static void mod_extforward_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
168e38e907fSGlenn Strauss switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
169e38e907fSGlenn Strauss case 0: /* extforward.forwarder */
170e38e907fSGlenn Strauss if (cpv->vtype == T_CONFIG_LOCAL) {
171e38e907fSGlenn Strauss const struct forwarder_cfg * const fwd = cpv->v.v;
172e38e907fSGlenn Strauss pconf->forwarder = fwd->forwarder;
173e38e907fSGlenn Strauss pconf->forward_all = fwd->forward_all;
174e38e907fSGlenn Strauss pconf->forward_masks_used = fwd->addrs_used;
175e38e907fSGlenn Strauss pconf->forward_masks = fwd->addrs;
176e38e907fSGlenn Strauss }
177e38e907fSGlenn Strauss break;
178e38e907fSGlenn Strauss case 1: /* extforward.headers */
179e38e907fSGlenn Strauss pconf->headers = cpv->v.a;
180e38e907fSGlenn Strauss break;
181e38e907fSGlenn Strauss case 2: /* extforward.params */
182e38e907fSGlenn Strauss if (cpv->vtype == T_CONFIG_LOCAL)
183e38e907fSGlenn Strauss pconf->opts = cpv->v.u;
184e38e907fSGlenn Strauss break;
185e38e907fSGlenn Strauss case 3: /* extforward.hap-PROXY */
186e38e907fSGlenn Strauss pconf->hap_PROXY = (char)cpv->v.u;
187e38e907fSGlenn Strauss break;
188e38e907fSGlenn Strauss case 4: /* extforward.hap-PROXY-ssl-client-verify */
189e38e907fSGlenn Strauss pconf->hap_PROXY_ssl_client_verify = (char)cpv->v.u;
190e38e907fSGlenn Strauss break;
191e38e907fSGlenn Strauss default:/* should not happen */
192e38e907fSGlenn Strauss return;
193e38e907fSGlenn Strauss }
194f68fd405SElan Ruusamäe }
195bd77abe0SGlenn Strauss
mod_extforward_merge_config(plugin_config * const pconf,const config_plugin_value_t * cpv)196e38e907fSGlenn Strauss static void mod_extforward_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
197e38e907fSGlenn Strauss do {
198e38e907fSGlenn Strauss mod_extforward_merge_config_cpv(pconf, cpv);
199e38e907fSGlenn Strauss } while ((++cpv)->k_id != -1);
200bd77abe0SGlenn Strauss }
201bd77abe0SGlenn Strauss
mod_extforward_patch_config(request_st * const r,plugin_data * const p)2027c7f8c46SGlenn Strauss static void mod_extforward_patch_config(request_st * const r, plugin_data * const p) {
203e38e907fSGlenn Strauss memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
204e38e907fSGlenn Strauss for (int i = 1, used = p->nconfig; i < used; ++i) {
2057c7f8c46SGlenn Strauss if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
206e38e907fSGlenn Strauss mod_extforward_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
207e38e907fSGlenn Strauss }
208e38e907fSGlenn Strauss }
209e38e907fSGlenn Strauss
mod_extforward_parse_forwarder(server * srv,const array * forwarder)210e38e907fSGlenn Strauss static void * mod_extforward_parse_forwarder(server *srv, const array *forwarder) {
211e38e907fSGlenn Strauss const data_string * const allds = (const data_string *)
212e38e907fSGlenn Strauss array_get_element_klen(forwarder, CONST_STR_LEN("all"));
213e38e907fSGlenn Strauss const int forward_all = (NULL == allds)
214e38e907fSGlenn Strauss ? 0
215e38e907fSGlenn Strauss : buffer_eq_icase_slen(&allds->value, CONST_STR_LEN("trust")) ? 1 : -1;
216e38e907fSGlenn Strauss uint32_t nmasks = 0;
217e38e907fSGlenn Strauss for (uint32_t j = 0; j < forwarder->used; ++j) {
218e38e907fSGlenn Strauss data_string * const ds = (data_string *)forwarder->data[j];
219ad9b7e00SGlenn Strauss char * const nm_slash = strchr(ds->key.ptr, '/');
220e38e907fSGlenn Strauss if (NULL != nm_slash) ++nmasks;
221601c572cSGlenn Strauss if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("trust"))) {
222e38e907fSGlenn Strauss if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("untrusted")))
223e38e907fSGlenn Strauss log_error(srv->errh, __FILE__, __LINE__,
224e38e907fSGlenn Strauss "ERROR: expect \"trust\", not \"%s\" => \"%s\"; "
225e38e907fSGlenn Strauss "treating as untrusted", ds->key.ptr, ds->value.ptr);
22678e25f0fSGlenn Strauss if (NULL != nm_slash) {
227e38e907fSGlenn Strauss /* future: consider adding member next to bits in sock_addr_mask
228e38e907fSGlenn Strauss * with bool trusted/untrusted member */
229e38e907fSGlenn Strauss --nmasks;
230e38e907fSGlenn Strauss log_error(srv->errh, __FILE__, __LINE__,
231e38e907fSGlenn Strauss "ERROR: untrusted CIDR masks are ignored (\"%s\" => \"%s\")",
232e38e907fSGlenn Strauss ds->key.ptr, ds->value.ptr);
23378e25f0fSGlenn Strauss }
234601c572cSGlenn Strauss buffer_clear(&ds->value); /* empty is untrusted */
23578e25f0fSGlenn Strauss continue;
23678e25f0fSGlenn Strauss }
237e38e907fSGlenn Strauss }
238e38e907fSGlenn Strauss
239e38e907fSGlenn Strauss struct forwarder_cfg * const fwd =
2405e14db43SGlenn Strauss ck_calloc(1, sizeof(struct forwarder_cfg)
2415e14db43SGlenn Strauss + sizeof(struct sock_addr_mask)*nmasks);
242e38e907fSGlenn Strauss fwd->forwarder = forwarder;
243e38e907fSGlenn Strauss fwd->forward_all = forward_all;
244e38e907fSGlenn Strauss fwd->addrs_used = 0;
245e38e907fSGlenn Strauss for (uint32_t j = 0; j < forwarder->used; ++j) {
246e38e907fSGlenn Strauss data_string * const ds = (data_string *)forwarder->data[j];
247e38e907fSGlenn Strauss char * const nm_slash = strchr(ds->key.ptr, '/');
248e38e907fSGlenn Strauss if (NULL == nm_slash) continue;
249af3df29aSGlenn Strauss if (buffer_is_blank(&ds->value)) continue; /* ignored */
250e38e907fSGlenn Strauss
251fc7edb39SGlenn Strauss char *err;
252fc7edb39SGlenn Strauss const int nm_bits = strtol(nm_slash + 1, &err, 10);
253fc7edb39SGlenn Strauss int rc;
25410dbe38aSGlenn Strauss if (*err || nm_bits <= 0 || !light_isdigit(nm_slash[1])) {
255e38e907fSGlenn Strauss log_error(srv->errh, __FILE__, __LINE__,
256e38e907fSGlenn Strauss "ERROR: invalid netmask: %s %s", ds->key.ptr, err);
257e38e907fSGlenn Strauss free(fwd);
258e38e907fSGlenn Strauss return NULL;
259fc7edb39SGlenn Strauss }
260e38e907fSGlenn Strauss struct sock_addr_mask * const sm = fwd->addrs + fwd->addrs_used++;
261fc7edb39SGlenn Strauss sm->bits = nm_bits;
262fc7edb39SGlenn Strauss *nm_slash = '\0';
263e298e868SGlenn Strauss if (ds->key.ptr[0] == '['
264e298e868SGlenn Strauss && ds->key.ptr+1 < nm_slash && nm_slash[-1] == ']') {
265e298e868SGlenn Strauss nm_slash[-1] = '\0';
266e298e868SGlenn Strauss rc = sock_addr_from_str_numeric(&sm->addr,ds->key.ptr+1,srv->errh);
267e298e868SGlenn Strauss nm_slash[-1] = ']';
268e298e868SGlenn Strauss }
269e298e868SGlenn Strauss else
270010c2894SGlenn Strauss rc = sock_addr_from_str_numeric(&sm->addr,ds->key.ptr, srv->errh);
271fc7edb39SGlenn Strauss *nm_slash = '/';
272e38e907fSGlenn Strauss if (1 != rc) {
273e38e907fSGlenn Strauss free(fwd);
274e38e907fSGlenn Strauss return NULL;
275fc7edb39SGlenn Strauss }
276e38e907fSGlenn Strauss buffer_clear(&ds->value);
277e38e907fSGlenn Strauss /* empty is untrusted,
278e38e907fSGlenn Strauss * e.g. if subnet (incorrectly) appears in X-Forwarded-For */
279fc7edb39SGlenn Strauss }
280fc7edb39SGlenn Strauss
281e38e907fSGlenn Strauss return fwd;
282bd77abe0SGlenn Strauss }
283c3c78599SGlenn Strauss
mod_extforward_parse_opts(server * srv,const array * opts_params)284e38e907fSGlenn Strauss static unsigned int mod_extforward_parse_opts(server *srv, const array *opts_params) {
285e38e907fSGlenn Strauss unsigned int opts = 0;
286e38e907fSGlenn Strauss for (uint32_t j = 0, used = opts_params->used; j < used; ++j) {
287371e1bf7SGlenn Strauss proxy_forwarded_t param;
288e38e907fSGlenn Strauss data_unset *du = opts_params->data[j];
289371e1bf7SGlenn Strauss #if 0 /*("for" and "proto" historical behavior: always enabled)*/
290e38e907fSGlenn Strauss if (buffer_eq_slen(&du->key, CONST_STR_LEN("by")))
291371e1bf7SGlenn Strauss param = PROXY_FORWARDED_BY;
292e38e907fSGlenn Strauss else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for")))
293371e1bf7SGlenn Strauss param = PROXY_FORWARDED_FOR;
294e38e907fSGlenn Strauss else
295371e1bf7SGlenn Strauss #endif
296e38e907fSGlenn Strauss if (buffer_eq_slen(&du->key, CONST_STR_LEN("host")))
297371e1bf7SGlenn Strauss param = PROXY_FORWARDED_HOST;
298371e1bf7SGlenn Strauss #if 0
299e38e907fSGlenn Strauss else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto")))
300371e1bf7SGlenn Strauss param = PROXY_FORWARDED_PROTO;
301371e1bf7SGlenn Strauss #endif
302e38e907fSGlenn Strauss else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user")))
303371e1bf7SGlenn Strauss param = PROXY_FORWARDED_REMOTE_USER;
304e38e907fSGlenn Strauss else {
305e38e907fSGlenn Strauss log_error(srv->errh, __FILE__, __LINE__,
306e38e907fSGlenn Strauss "extforward.params keys must be one of: "
307e38e907fSGlenn Strauss "host, remote_user, but not: %s", du->key.ptr);
308730c932eSGlenn Strauss return UINT_MAX;
309371e1bf7SGlenn Strauss }
310e38e907fSGlenn Strauss
311730c932eSGlenn Strauss int val = config_plugin_value_tobool(du, 2);
312730c932eSGlenn Strauss if (2 == val) {
313730c932eSGlenn Strauss log_error(srv->errh, __FILE__, __LINE__,
314730c932eSGlenn Strauss "extforward.params values must be one of: "
315730c932eSGlenn Strauss "0, 1, enable, disable; error for key: %s", du->key.ptr);
316730c932eSGlenn Strauss return UINT_MAX;
317730c932eSGlenn Strauss }
318730c932eSGlenn Strauss if (val)
319e38e907fSGlenn Strauss opts |= param;
320371e1bf7SGlenn Strauss }
321e38e907fSGlenn Strauss return opts;
322e38e907fSGlenn Strauss }
323e38e907fSGlenn Strauss
SETDEFAULTS_FUNC(mod_extforward_set_defaults)324e38e907fSGlenn Strauss SETDEFAULTS_FUNC(mod_extforward_set_defaults) {
325e38e907fSGlenn Strauss static const config_plugin_keys_t cpk[] = {
326e38e907fSGlenn Strauss { CONST_STR_LEN("extforward.forwarder"),
32703b4c993SGlenn Strauss T_CONFIG_ARRAY_KVSTRING,
328e38e907fSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
329e38e907fSGlenn Strauss ,{ CONST_STR_LEN("extforward.headers"),
33003b4c993SGlenn Strauss T_CONFIG_ARRAY_VLIST,
331e38e907fSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
332e38e907fSGlenn Strauss ,{ CONST_STR_LEN("extforward.params"),
33303b4c993SGlenn Strauss T_CONFIG_ARRAY_KVANY,
334e38e907fSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
335e38e907fSGlenn Strauss ,{ CONST_STR_LEN("extforward.hap-PROXY"),
336e38e907fSGlenn Strauss T_CONFIG_BOOL,
337e38e907fSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
338e38e907fSGlenn Strauss ,{ CONST_STR_LEN("extforward.hap-PROXY-ssl-client-verify"),
339e38e907fSGlenn Strauss T_CONFIG_BOOL,
340e38e907fSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
341e38e907fSGlenn Strauss ,{ NULL, 0,
342e38e907fSGlenn Strauss T_CONFIG_UNSET,
343e38e907fSGlenn Strauss T_CONFIG_SCOPE_UNSET }
344e38e907fSGlenn Strauss };
345e38e907fSGlenn Strauss
346e38e907fSGlenn Strauss plugin_data * const p = p_d;
347e38e907fSGlenn Strauss if (!config_plugin_values_init(srv, p, cpk, "mod_extforward"))
348e38e907fSGlenn Strauss return HANDLER_ERROR;
349e38e907fSGlenn Strauss
350e38e907fSGlenn Strauss int hap_PROXY = 0;
351e38e907fSGlenn Strauss
352e38e907fSGlenn Strauss /* process and validate config directives
353e38e907fSGlenn Strauss * (init i to 0 if global context; to 1 to skip empty global context) */
354e38e907fSGlenn Strauss for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
355e38e907fSGlenn Strauss config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
356e38e907fSGlenn Strauss for (; -1 != cpv->k_id; ++cpv) {
357e38e907fSGlenn Strauss switch (cpv->k_id) {
358e38e907fSGlenn Strauss case 0: /* extforward.forwarder */
359e38e907fSGlenn Strauss cpv->v.v = mod_extforward_parse_forwarder(srv, cpv->v.a);
360e38e907fSGlenn Strauss if (NULL == cpv->v.v) {
361e38e907fSGlenn Strauss log_error(srv->errh, __FILE__, __LINE__,
362e38e907fSGlenn Strauss "unexpected value for %s", cpk[cpv->k_id].k);
363e38e907fSGlenn Strauss return HANDLER_ERROR;
364371e1bf7SGlenn Strauss }
365e38e907fSGlenn Strauss cpv->vtype = T_CONFIG_LOCAL;
366e38e907fSGlenn Strauss break;
367e38e907fSGlenn Strauss case 1: /* extforward.headers */
3682e0676fdSGlenn Strauss if (cpv->v.a->used) {
3692e0676fdSGlenn Strauss array *a;
3702e0676fdSGlenn Strauss *(const array **)&a = cpv->v.a;
3712e0676fdSGlenn Strauss for (uint32_t j = 0; j < a->used; ++j) {
3722e0676fdSGlenn Strauss data_string * const ds = (data_string *)a->data[j];
3732e0676fdSGlenn Strauss ds->ext =
374af3df29aSGlenn Strauss http_header_hkey_get(BUF_PTR_LEN(&ds->value));
3752e0676fdSGlenn Strauss }
3762e0676fdSGlenn Strauss }
377e38e907fSGlenn Strauss break;
378e38e907fSGlenn Strauss case 2: /* extforward.params */
379e38e907fSGlenn Strauss cpv->v.u = mod_extforward_parse_opts(srv, cpv->v.a);
380e38e907fSGlenn Strauss if (UINT_MAX == cpv->v.u)
381e38e907fSGlenn Strauss return HANDLER_ERROR;
382e38e907fSGlenn Strauss break;
383e38e907fSGlenn Strauss case 3: /* extforward.hap-PROXY */
384e38e907fSGlenn Strauss if (cpv->v.u) hap_PROXY = 1;
385e38e907fSGlenn Strauss break;
386e38e907fSGlenn Strauss case 4: /* extforward.hap-PROXY-ssl-client-verify */
387e38e907fSGlenn Strauss break;
388e38e907fSGlenn Strauss default:/* should not happen */
389e38e907fSGlenn Strauss break;
390e38e907fSGlenn Strauss }
391e38e907fSGlenn Strauss }
392e38e907fSGlenn Strauss }
393e38e907fSGlenn Strauss
394e38e907fSGlenn Strauss mod_extforward_plugin_data_singleton = p;
395e38e907fSGlenn Strauss p->defaults.opts = PROXY_FORWARDED_NONE;
396e38e907fSGlenn Strauss
397e38e907fSGlenn Strauss /* initialize p->defaults from global config context */
398e38e907fSGlenn Strauss if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
399e38e907fSGlenn Strauss const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
400e38e907fSGlenn Strauss if (-1 != cpv->k_id)
401e38e907fSGlenn Strauss mod_extforward_merge_config(&p->defaults, cpv);
402e38e907fSGlenn Strauss }
403e38e907fSGlenn Strauss
404e38e907fSGlenn Strauss /* default to "X-Forwarded-For" or "Forwarded-For" if extforward.headers
405e38e907fSGlenn Strauss * is not specified or is empty (and not using hap_PROXY) */
406e38e907fSGlenn Strauss if (!p->defaults.hap_PROXY
407e38e907fSGlenn Strauss && (NULL == p->defaults.headers || 0 == p->defaults.headers->used)) {
40824680a91SGlenn Strauss p->defaults.headers = p->default_headers = array_init(2);
409e38e907fSGlenn Strauss array_insert_value(p->default_headers,CONST_STR_LEN("X-Forwarded-For"));
410e38e907fSGlenn Strauss array_insert_value(p->default_headers,CONST_STR_LEN("Forwarded-For"));
411b6892fb2SGlenn Strauss for (uint32_t i = 0; i < p->default_headers->used; ++i) {
412b6892fb2SGlenn Strauss data_string * const ds = (data_string *)p->default_headers->data[i];
413af3df29aSGlenn Strauss ds->ext = http_header_hkey_get(BUF_PTR_LEN(&ds->value));
414b6892fb2SGlenn Strauss }
415371e1bf7SGlenn Strauss }
416371e1bf7SGlenn Strauss
41778cc7272SGlenn Strauss /* attempt to warn if mod_extforward is not last module loaded to hook
41878cc7272SGlenn Strauss * handle_connection_accept. (Nice to have, but remove this check if
419cdfddce7SGlenn Strauss * it reaches too far into internals and prevents other code changes.)
420164f7600SGlenn Strauss * While it would be nice to check handle_connection_accept plugin slot
42178cc7272SGlenn Strauss * to make sure mod_extforward is last, that info is private to plugin.c
42278cc7272SGlenn Strauss * so merely warn if mod_openssl is loaded after mod_extforward, though
423164f7600SGlenn Strauss * future modules which hook handle_connection_accept might be missed.*/
424e38e907fSGlenn Strauss if (hap_PROXY) {
425e38e907fSGlenn Strauss uint32_t i;
426e38e907fSGlenn Strauss for (i = 0; i < srv->srvconf.modules->used; ++i) {
427e38e907fSGlenn Strauss data_string *ds = (data_string *)srv->srvconf.modules->data[i];
428e38e907fSGlenn Strauss if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_extforward")))
42978cc7272SGlenn Strauss break;
43078cc7272SGlenn Strauss }
431e38e907fSGlenn Strauss for (; i < srv->srvconf.modules->used; ++i) {
432e38e907fSGlenn Strauss data_string *ds = (data_string *)srv->srvconf.modules->data[i];
433a479d08bSGlenn Strauss if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_openssl"))
434a479d08bSGlenn Strauss || buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_mbedtls"))
435a99550d7SGlenn Strauss || buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_wolfssl"))
436e00deb55SGlenn Strauss || buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_nss"))
437a479d08bSGlenn Strauss || buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_gnutls"))) {
438010c2894SGlenn Strauss log_error(srv->errh, __FILE__, __LINE__,
439a479d08bSGlenn Strauss "mod_extforward must be loaded after %s in "
440a479d08bSGlenn Strauss "server.modules when extforward.hap-PROXY = \"enable\"",
441a479d08bSGlenn Strauss ds->value.ptr);
44278cc7272SGlenn Strauss break;
44378cc7272SGlenn Strauss }
44478cc7272SGlenn Strauss }
44578cc7272SGlenn Strauss }
44678cc7272SGlenn Strauss
447e38e907fSGlenn Strauss for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
448371e1bf7SGlenn Strauss data_string *ds = (data_string *)srv->srvconf.modules->data[i];
449601c572cSGlenn Strauss if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_proxy"))) {
450371e1bf7SGlenn Strauss extforward_check_proxy = 1;
451371e1bf7SGlenn Strauss break;
452371e1bf7SGlenn Strauss }
453f68fd405SElan Ruusamäe }
454f68fd405SElan Ruusamäe
455f68fd405SElan Ruusamäe return HANDLER_GO_ON;
456f68fd405SElan Ruusamäe }
457f68fd405SElan Ruusamäe
458f68fd405SElan Ruusamäe
459f68fd405SElan Ruusamäe /*
460f68fd405SElan Ruusamäe extract a forward array from the environment
461f68fd405SElan Ruusamäe */
extract_forward_array(array * const result,const buffer * pbuffer)4622d9bf806SGlenn Strauss static void extract_forward_array(array * const result, const buffer *pbuffer)
463f68fd405SElan Ruusamäe {
4642d9bf806SGlenn Strauss /*force_assert(!buffer_is_blank(pbuffer));*/
4652fd0faf1SGlenn Strauss const char *base, *curr;
466f68fd405SElan Ruusamäe /* state variable, 0 means not in string, 1 means in string */
467f68fd405SElan Ruusamäe int in_str = 0;
468cde46f6aSElan Ruusamäe for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) {
4692fd0faf1SGlenn Strauss int hex_or_colon = (light_isxdigit(*curr) || *curr == ':');
470f68fd405SElan Ruusamäe if (in_str) {
4712fd0faf1SGlenn Strauss if (!hex_or_colon && *curr != '.') {
472f68fd405SElan Ruusamäe /* found an separator , insert value into result array */
473810109ccSGlenn Strauss array_insert_value(result, base, curr - base);
474f68fd405SElan Ruusamäe /* change state to not in string */
475f68fd405SElan Ruusamäe in_str = 0;
476f68fd405SElan Ruusamäe }
477f68fd405SElan Ruusamäe } else {
4782fd0faf1SGlenn Strauss if (hex_or_colon) {
479f68fd405SElan Ruusamäe /* found leading char of an IP address, move base pointer and change state */
480f68fd405SElan Ruusamäe base = curr;
481f68fd405SElan Ruusamäe in_str = 1;
482f68fd405SElan Ruusamäe }
483f68fd405SElan Ruusamäe }
484f68fd405SElan Ruusamäe }
485f68fd405SElan Ruusamäe /* if breaking out while in str, we got to the end of string, so add it */
486cde46f6aSElan Ruusamäe if (in_str) {
487810109ccSGlenn Strauss array_insert_value(result, base, curr - base);
488f68fd405SElan Ruusamäe }
489f68fd405SElan Ruusamäe }
490f68fd405SElan Ruusamäe
491f68fd405SElan Ruusamäe /*
492cde46f6aSElan Ruusamäe * check whether ip is trusted, return 1 for trusted , 0 for untrusted
493f68fd405SElan Ruusamäe */
is_proxy_trusted(plugin_data * p,const char * const ip,size_t iplen)494fc7edb39SGlenn Strauss static int is_proxy_trusted(plugin_data *p, const char * const ip, size_t iplen)
495f68fd405SElan Ruusamäe {
49683535bbeSGlenn Strauss const data_string *ds =
49783535bbeSGlenn Strauss (const data_string *)array_get_element_klen(p->conf.forwarder, ip, iplen);
498af3df29aSGlenn Strauss if (NULL != ds) return !buffer_is_blank(&ds->value);
499cde46f6aSElan Ruusamäe
500e38e907fSGlenn Strauss if (p->conf.forward_masks_used) {
501e38e907fSGlenn Strauss const struct sock_addr_mask * const addrs = p->conf.forward_masks;
502e38e907fSGlenn Strauss const uint32_t aused = p->conf.forward_masks_used;
503fc7edb39SGlenn Strauss sock_addr addr;
504fc7edb39SGlenn Strauss /* C funcs inet_aton(), inet_pton() require '\0'-terminated IP str */
505fc7edb39SGlenn Strauss char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/
50628f1867cSGlenn Strauss if (0 == iplen || iplen >= sizeof(addrstr)) return 0;
507fc7edb39SGlenn Strauss memcpy(addrstr, ip, iplen);
508fc7edb39SGlenn Strauss addrstr[iplen] = '\0';
509fc7edb39SGlenn Strauss
510fc7edb39SGlenn Strauss if (1 != sock_addr_inet_pton(&addr, addrstr, AF_INET, 0)
511fc7edb39SGlenn Strauss && 1 != sock_addr_inet_pton(&addr, addrstr, AF_INET6, 0)) return 0;
512fc7edb39SGlenn Strauss
513e38e907fSGlenn Strauss for (uint32_t i = 0; i < aused; ++i) {
514fc7edb39SGlenn Strauss if (sock_addr_is_addr_eq_bits(&addr, &addrs[i].addr, addrs[i].bits))
515fc7edb39SGlenn Strauss return 1;
516f68fd405SElan Ruusamäe }
517cde46f6aSElan Ruusamäe }
518cde46f6aSElan Ruusamäe
519fc7edb39SGlenn Strauss return 0;
520fc7edb39SGlenn Strauss }
521fc7edb39SGlenn Strauss
is_connection_trusted(connection * const con,plugin_data * p)522fc7edb39SGlenn Strauss static int is_connection_trusted(connection * const con, plugin_data *p)
523fc7edb39SGlenn Strauss {
524fc7edb39SGlenn Strauss if (p->conf.forward_all) return (1 == p->conf.forward_all);
525f1e8a82fSGlenn Strauss return is_proxy_trusted(p, BUF_PTR_LEN(&con->dst_addr_buf));
526f68fd405SElan Ruusamäe }
527f68fd405SElan Ruusamäe
is_connection_trusted_cached(connection * const con,plugin_data * p)528*e0817646SGlenn Strauss static int is_connection_trusted_cached(connection * const con, plugin_data *p)
529*e0817646SGlenn Strauss {
530*e0817646SGlenn Strauss if (p->conf.forward_all) return (1 == p->conf.forward_all);
531*e0817646SGlenn Strauss
532*e0817646SGlenn Strauss handler_ctx ** const hctx = (handler_ctx **)&con->plugin_ctx[p->id];
533*e0817646SGlenn Strauss if (!*hctx)
534*e0817646SGlenn Strauss *hctx = handler_ctx_init();
535*e0817646SGlenn Strauss else if ((*hctx)->con_is_trusted != -1)
536*e0817646SGlenn Strauss return (*hctx)->con_is_trusted;
537*e0817646SGlenn Strauss return ((*hctx)->con_is_trusted =
538*e0817646SGlenn Strauss is_proxy_trusted(p, BUF_PTR_LEN(&con->dst_addr_buf)));
539*e0817646SGlenn Strauss }
540*e0817646SGlenn Strauss
541cde46f6aSElan Ruusamäe /*
54286bb8be2SGlenn Strauss * Return last address of proxy that is not trusted.
543cde46f6aSElan Ruusamäe * Do not accept "all" keyword here.
544cde46f6aSElan Ruusamäe */
last_not_in_array(array * a,plugin_data * p)5452d9bf806SGlenn Strauss static const buffer *last_not_in_array(array *a, plugin_data *p)
546cde46f6aSElan Ruusamäe {
5472772e62dSStefan Bühler int i;
548cde46f6aSElan Ruusamäe
5492772e62dSStefan Bühler for (i = a->used - 1; i >= 0; i--) {
550cde46f6aSElan Ruusamäe data_string *ds = (data_string *)a->data[i];
551af3df29aSGlenn Strauss if (!is_proxy_trusted(p, BUF_PTR_LEN(&ds->value))) {
5522d9bf806SGlenn Strauss return &ds->value;
553cde46f6aSElan Ruusamäe }
554cde46f6aSElan Ruusamäe }
555cde46f6aSElan Ruusamäe return NULL;
556cde46f6aSElan Ruusamäe }
557cde46f6aSElan Ruusamäe
mod_extforward_set_addr(request_st * const r,plugin_data * p,const char * addr,size_t addrlen)5582d9bf806SGlenn Strauss static int mod_extforward_set_addr(request_st * const r, plugin_data *p, const char *addr, size_t addrlen) {
55936adf0d9SGlenn Strauss sock_addr sock;
56036adf0d9SGlenn Strauss sock.plain.sa_family = AF_UNSPEC;
5617c7f8c46SGlenn Strauss if (1 != sock_addr_from_str_numeric(&sock, addr, r->conf.errh)) return 0;
56236adf0d9SGlenn Strauss if (sock.plain.sa_family == AF_UNSPEC) return 0;
56336adf0d9SGlenn Strauss
564*e0817646SGlenn Strauss if (!r->plugin_ctx[p->id]) {
565*e0817646SGlenn Strauss handler_rctx * const rctx = r->plugin_ctx[p->id] = handler_rctx_init();
566*e0817646SGlenn Strauss r->dst_addr = &rctx->dst_addr;
567*e0817646SGlenn Strauss r->dst_addr_buf = &rctx->dst_addr_buf;
568*e0817646SGlenn Strauss }
569*e0817646SGlenn Strauss #if 0 /*(not expected)*/
570*e0817646SGlenn Strauss else if (r->conf.log_request_handling)
5717c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__,
57236adf0d9SGlenn Strauss "-- mod_extforward_uri_handler already patched this connection, resetting state");
573*e0817646SGlenn Strauss #endif
574*e0817646SGlenn Strauss
575*e0817646SGlenn Strauss if (r->conf.log_request_handling)
576*e0817646SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, "using address: %s", addr);
577*e0817646SGlenn Strauss
578*e0817646SGlenn Strauss #if 0 /*(no longer necessary since not overwriting con->dst_addr_buf)*/
57936adf0d9SGlenn Strauss /* save old address */
580371e1bf7SGlenn Strauss if (extforward_check_proxy) {
581f1e8a82fSGlenn Strauss http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR"),
582f1e8a82fSGlenn Strauss BUF_PTR_LEN(&con->dst_addr_buf));
583371e1bf7SGlenn Strauss }
584*e0817646SGlenn Strauss #endif
58536adf0d9SGlenn Strauss
586*e0817646SGlenn Strauss /* set (virtual) remote address for request */
587*e0817646SGlenn Strauss *(sock_addr *)r->dst_addr = sock;
588*e0817646SGlenn Strauss buffer_copy_string_len(r->dst_addr_buf, addr, addrlen);
589*e0817646SGlenn Strauss /* reset conf_cond cache; results may change */
5907c7f8c46SGlenn Strauss config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP);
59136adf0d9SGlenn Strauss
59236adf0d9SGlenn Strauss return 1;
59336adf0d9SGlenn Strauss }
59436adf0d9SGlenn Strauss
mod_extforward_set_proto(request_st * const r,const char * const proto,size_t protolen)5957c7f8c46SGlenn Strauss static void mod_extforward_set_proto(request_st * const r, const char * const proto, size_t protolen) {
59621f0dabeSGlenn Strauss if (0 != protolen && !buffer_eq_icase_slen(&r->uri.scheme, proto, protolen)) {
59736adf0d9SGlenn Strauss /* update scheme if X-Forwarded-Proto is set
59836adf0d9SGlenn Strauss * Limitations:
59936adf0d9SGlenn Strauss * - Only "http" or "https" are currently accepted since the request to lighttpd currently has to
60036adf0d9SGlenn Strauss * be HTTP/1.0 or HTTP/1.1 using http or https. If this is changed, then the scheme from this
60136adf0d9SGlenn Strauss * untrusted header must be checked to contain only alphanumeric characters, and to be a
60236adf0d9SGlenn Strauss * reasonable length, e.g. < 256 chars.
6037c7f8c46SGlenn Strauss * - r->uri.scheme is not reset in mod_extforward_restore() but is currently not an issues since
6047c7f8c46SGlenn Strauss * r->uri.scheme will be reset by next request. If a new module uses r->uri.scheme in the
60536adf0d9SGlenn Strauss * handle_request_done hook, then should evaluate if that module should use the forwarded value
60636adf0d9SGlenn Strauss * (probably) or the original value.
60736adf0d9SGlenn Strauss */
608371e1bf7SGlenn Strauss if (extforward_check_proxy) {
609af3df29aSGlenn Strauss http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO"), BUF_PTR_LEN(&r->uri.scheme));
610371e1bf7SGlenn Strauss }
611e3874a20SGlenn Strauss if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("https"))) {
6120c7d2500SGlenn Strauss r->con->proto_default_port = 443; /* "https" */
6137c7f8c46SGlenn Strauss buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("https"));
6147c7f8c46SGlenn Strauss config_cond_cache_reset_item(r, COMP_HTTP_SCHEME);
615e3874a20SGlenn Strauss } else if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("http"))) {
6160c7d2500SGlenn Strauss r->con->proto_default_port = 80; /* "http" */
6177c7f8c46SGlenn Strauss buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("http"));
6187c7f8c46SGlenn Strauss config_cond_cache_reset_item(r, COMP_HTTP_SCHEME);
61936adf0d9SGlenn Strauss }
62036adf0d9SGlenn Strauss }
62136adf0d9SGlenn Strauss }
62236adf0d9SGlenn Strauss
mod_extforward_X_Forwarded_For(request_st * const r,plugin_data * const p,const buffer * const x_forwarded_for)6237c7f8c46SGlenn Strauss static handler_t mod_extforward_X_Forwarded_For(request_st * const r, plugin_data * const p, const buffer * const x_forwarded_for) {
62436adf0d9SGlenn Strauss /* build forward_array from forwarded data_string */
6252d9bf806SGlenn Strauss array * const forward_array = &p->tokens;
6262d9bf806SGlenn Strauss extract_forward_array(forward_array, x_forwarded_for);
6272d9bf806SGlenn Strauss const buffer *real_remote_addr = last_not_in_array(forward_array, p);
62836adf0d9SGlenn Strauss if (real_remote_addr != NULL) { /* parsed */
62936adf0d9SGlenn Strauss /* get scheme if X-Forwarded-Proto is set
63036adf0d9SGlenn Strauss * Limitations:
63136adf0d9SGlenn Strauss * - X-Forwarded-Proto may or may not be set by proxies, even if X-Forwarded-For is set
63236adf0d9SGlenn Strauss * - X-Forwarded-Proto may be a comma-separated list if there are multiple proxies,
63336adf0d9SGlenn Strauss * but the historical behavior of the code below only honored it if there was exactly one value
63436adf0d9SGlenn Strauss * (not done: walking backwards in X-Forwarded-Proto the same num of steps
63536adf0d9SGlenn Strauss * as in X-Forwarded-For to find proto set by last trusted proxy)
63636adf0d9SGlenn Strauss */
6377c7f8c46SGlenn Strauss const buffer *x_forwarded_proto = http_header_request_get(r, HTTP_HEADER_X_FORWARDED_PROTO, CONST_STR_LEN("X-Forwarded-Proto"));
6382d9bf806SGlenn Strauss if (mod_extforward_set_addr(r, p, BUF_PTR_LEN(real_remote_addr)) && NULL != x_forwarded_proto) {
639af3df29aSGlenn Strauss mod_extforward_set_proto(r, BUF_PTR_LEN(x_forwarded_proto));
64036adf0d9SGlenn Strauss }
64136adf0d9SGlenn Strauss }
6422d9bf806SGlenn Strauss array_reset_data_strings(forward_array);
64336adf0d9SGlenn Strauss return HANDLER_GO_ON;
64436adf0d9SGlenn Strauss }
64536adf0d9SGlenn Strauss
64619bc8885SGlenn Strauss __attribute_pure__
find_end_quoted_string(const char * const s,int i)647371e1bf7SGlenn Strauss static int find_end_quoted_string (const char * const s, int i) {
648371e1bf7SGlenn Strauss do {
649371e1bf7SGlenn Strauss ++i;
650371e1bf7SGlenn Strauss } while (s[i] != '"' && s[i] != '\0' && (s[i] != '\\' || s[++i] != '\0'));
651371e1bf7SGlenn Strauss return i;
652371e1bf7SGlenn Strauss }
653371e1bf7SGlenn Strauss
65419bc8885SGlenn Strauss __attribute_pure__
find_next_semicolon_or_comma_or_eq(const char * const s,int i)655371e1bf7SGlenn Strauss static int find_next_semicolon_or_comma_or_eq (const char * const s, int i) {
656371e1bf7SGlenn Strauss for (; s[i] != '=' && s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) {
657371e1bf7SGlenn Strauss if (s[i] == '"') {
658371e1bf7SGlenn Strauss i = find_end_quoted_string(s, i);
659371e1bf7SGlenn Strauss if (s[i] == '\0') return -1;
660371e1bf7SGlenn Strauss }
661371e1bf7SGlenn Strauss }
662371e1bf7SGlenn Strauss return i;
663371e1bf7SGlenn Strauss }
664371e1bf7SGlenn Strauss
66519bc8885SGlenn Strauss __attribute_pure__
find_next_semicolon_or_comma(const char * const s,int i)666371e1bf7SGlenn Strauss static int find_next_semicolon_or_comma (const char * const s, int i) {
667371e1bf7SGlenn Strauss for (; s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) {
668371e1bf7SGlenn Strauss if (s[i] == '"') {
669371e1bf7SGlenn Strauss i = find_end_quoted_string(s, i);
670371e1bf7SGlenn Strauss if (s[i] == '\0') return -1;
671371e1bf7SGlenn Strauss }
672371e1bf7SGlenn Strauss }
673371e1bf7SGlenn Strauss return i;
674371e1bf7SGlenn Strauss }
675371e1bf7SGlenn Strauss
buffer_backslash_unescape(buffer * const b)676371e1bf7SGlenn Strauss static int buffer_backslash_unescape (buffer * const b) {
677371e1bf7SGlenn Strauss /* (future: might move to buffer.c) */
678371e1bf7SGlenn Strauss size_t j = 0;
679af3df29aSGlenn Strauss size_t len = buffer_clen(b);
680371e1bf7SGlenn Strauss char *p = memchr(b->ptr, '\\', len);
681371e1bf7SGlenn Strauss
682371e1bf7SGlenn Strauss if (NULL == p) return 1; /*(nothing to do)*/
683371e1bf7SGlenn Strauss
684371e1bf7SGlenn Strauss len -= (size_t)(p - b->ptr);
685371e1bf7SGlenn Strauss for (size_t i = 0; i < len; ++i) {
686371e1bf7SGlenn Strauss if (p[i] == '\\') {
687371e1bf7SGlenn Strauss if (++i == len) return 0; /*(invalid trailing backslash)*/
688371e1bf7SGlenn Strauss }
689371e1bf7SGlenn Strauss p[j++] = p[i];
690371e1bf7SGlenn Strauss }
691af3df29aSGlenn Strauss buffer_truncate(b, (size_t)(p+j - b->ptr));
692371e1bf7SGlenn Strauss return 1;
693371e1bf7SGlenn Strauss }
694371e1bf7SGlenn Strauss
6952097fe44SGlenn Strauss __attribute_cold__
mod_extforward_bad_request(request_st * const r,const unsigned int line,const char * const msg)6962097fe44SGlenn Strauss static handler_t mod_extforward_bad_request (request_st * const r, const unsigned int line, const char * const msg)
6972097fe44SGlenn Strauss {
6982097fe44SGlenn Strauss r->http_status = 400; /* Bad Request */
6992097fe44SGlenn Strauss r->handler_module = NULL;
7002097fe44SGlenn Strauss log_error(r->conf.errh, __FILE__, line, "%s", msg);
7012097fe44SGlenn Strauss return HANDLER_FINISHED;
7022097fe44SGlenn Strauss }
7032097fe44SGlenn Strauss
mod_extforward_Forwarded(request_st * const r,plugin_data * const p,const buffer * const forwarded)7047c7f8c46SGlenn Strauss static handler_t mod_extforward_Forwarded (request_st * const r, plugin_data * const p, const buffer * const forwarded) {
705371e1bf7SGlenn Strauss /* HTTP list need not consist of param=value tokens,
706371e1bf7SGlenn Strauss * but this routine expect such for HTTP Forwarded header
707371e1bf7SGlenn Strauss * Since info in each set of params is only used if from
708371e1bf7SGlenn Strauss * admin-specified trusted proxy:
709371e1bf7SGlenn Strauss * - invalid param=value tokens are ignored and skipped
710371e1bf7SGlenn Strauss * - not checking "for" exists in each set of params
711371e1bf7SGlenn Strauss * - not checking for duplicated params in each set of params
712371e1bf7SGlenn Strauss * - not checking canonical form of addr (also might be obfuscated)
713371e1bf7SGlenn Strauss * - obfuscated tokens permitted in chain, though end of trust is expected
714371e1bf7SGlenn Strauss * to be non-obfuscated IP for mod_extforward to masquerade as remote IP
715371e1bf7SGlenn Strauss * future: since (potentially) trusted proxies begin at end of string,
716371e1bf7SGlenn Strauss * it might be better to parse from end of string rather than parsing from
717371e1bf7SGlenn Strauss * beginning. Doing so would also allow reducing arbitrary param limit
718371e1bf7SGlenn Strauss * to number of params permitted per proxy.
719371e1bf7SGlenn Strauss */
720371e1bf7SGlenn Strauss char * const s = forwarded->ptr;
721371e1bf7SGlenn Strauss int i = 0, j = -1, v, vlen, k, klen;
722af3df29aSGlenn Strauss int used = (int)buffer_clen(forwarded);
723371e1bf7SGlenn Strauss int ofor = -1, oproto, ohost, oby, oremote_user;
724371e1bf7SGlenn Strauss int offsets[256];/*(~50 params is more than reasonably expected to handle)*/
725371e1bf7SGlenn Strauss while (i < used) {
726371e1bf7SGlenn Strauss while (s[i] == ' ' || s[i] == '\t') ++i;
727371e1bf7SGlenn Strauss if (s[i] == ';') { ++i; continue; }
728371e1bf7SGlenn Strauss if (s[i] == ',') {
7298c62a890Spovcfe if (j >= (int)(sizeof(offsets)/sizeof(int))-1) break;
730371e1bf7SGlenn Strauss offsets[++j] = -1; /*("offset" separating params from next proxy)*/
731371e1bf7SGlenn Strauss ++i;
732371e1bf7SGlenn Strauss continue;
733371e1bf7SGlenn Strauss }
734371e1bf7SGlenn Strauss if (s[i] == '\0') break;
735371e1bf7SGlenn Strauss
736371e1bf7SGlenn Strauss k = i;
737371e1bf7SGlenn Strauss i = find_next_semicolon_or_comma_or_eq(s, i);
738371e1bf7SGlenn Strauss if (i < 0) {
739371e1bf7SGlenn Strauss /*(reject IP spoofing if attacker sets improper quoted-string)*/
7402097fe44SGlenn Strauss return mod_extforward_bad_request(r, __LINE__,
741371e1bf7SGlenn Strauss "invalid quoted-string in Forwarded header");
742371e1bf7SGlenn Strauss }
743371e1bf7SGlenn Strauss if (s[i] != '=') continue;
744371e1bf7SGlenn Strauss klen = i - k;
745371e1bf7SGlenn Strauss v = ++i;
746371e1bf7SGlenn Strauss i = find_next_semicolon_or_comma(s, i);
747371e1bf7SGlenn Strauss if (i < 0) {
748371e1bf7SGlenn Strauss /*(reject IP spoofing if attacker sets improper quoted-string)*/
7492097fe44SGlenn Strauss return mod_extforward_bad_request(r, __LINE__,
750371e1bf7SGlenn Strauss "invalid quoted-string in Forwarded header");
751371e1bf7SGlenn Strauss }
752371e1bf7SGlenn Strauss vlen = i - v; /* might be 0 */
753371e1bf7SGlenn Strauss
754371e1bf7SGlenn Strauss /* have k, klen, v, vlen
755371e1bf7SGlenn Strauss * (might contain quoted string) (contents not validated or decoded)
756371e1bf7SGlenn Strauss * (might be repeated k)
757371e1bf7SGlenn Strauss */
758371e1bf7SGlenn Strauss if (0 == klen) continue; /* invalid k */
759371e1bf7SGlenn Strauss if (j >= (int)(sizeof(offsets)/sizeof(int))-4) break;
760371e1bf7SGlenn Strauss offsets[j+1] = k;
761371e1bf7SGlenn Strauss offsets[j+2] = klen;
762371e1bf7SGlenn Strauss offsets[j+3] = v;
763371e1bf7SGlenn Strauss offsets[j+4] = vlen;
764371e1bf7SGlenn Strauss j += 4;
765371e1bf7SGlenn Strauss }
766371e1bf7SGlenn Strauss
767371e1bf7SGlenn Strauss if (j >= (int)(sizeof(offsets)/sizeof(int))-4) {
768371e1bf7SGlenn Strauss /* error processing Forwarded; too many params; fail closed */
7692097fe44SGlenn Strauss return mod_extforward_bad_request(r, __LINE__,
770371e1bf7SGlenn Strauss "Too many params in Forwarded header");
771371e1bf7SGlenn Strauss }
772371e1bf7SGlenn Strauss
773371e1bf7SGlenn Strauss if (-1 == j) return HANDLER_GO_ON; /* make no changes */
774371e1bf7SGlenn Strauss used = j+1;
775371e1bf7SGlenn Strauss offsets[used] = -1; /* mark end of last set of params */
776371e1bf7SGlenn Strauss
777b31e7840SGlenn Strauss while (j >= 4) { /*(param=value pairs)*/
778371e1bf7SGlenn Strauss if (-1 == offsets[j]) { --j; continue; }
779371e1bf7SGlenn Strauss do {
780371e1bf7SGlenn Strauss j -= 3; /*(k, klen, v, vlen come in sets of 4)*/
781371e1bf7SGlenn Strauss } while ((3 != offsets[j+1] /* 3 == sizeof("for")-1 */
782e3874a20SGlenn Strauss || !buffer_eq_icase_ssn(s+offsets[j], "for", 3))
783371e1bf7SGlenn Strauss && 0 != j-- && -1 != offsets[j]);
784371e1bf7SGlenn Strauss if (j < 0) break;
785371e1bf7SGlenn Strauss if (-1 == offsets[j]) { --j; continue; }
786371e1bf7SGlenn Strauss
787371e1bf7SGlenn Strauss /* remove trailing spaces/tabs and double-quotes from string
788371e1bf7SGlenn Strauss * (note: not unescaping backslash escapes in quoted string) */
789371e1bf7SGlenn Strauss v = offsets[j+2];
790371e1bf7SGlenn Strauss vlen = v + offsets[j+3];
791371e1bf7SGlenn Strauss while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen;
792371e1bf7SGlenn Strauss if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') {
793371e1bf7SGlenn Strauss offsets[j+2] = ++v;
794371e1bf7SGlenn Strauss --vlen;
795371e1bf7SGlenn Strauss if (s[v] == '[') {
796371e1bf7SGlenn Strauss /* remove "[]" surrounding IPv6, as well as (optional) port
797371e1bf7SGlenn Strauss * (assumes properly formatted IPv6 addr from trusted proxy) */
798371e1bf7SGlenn Strauss ++v;
799371e1bf7SGlenn Strauss do { --vlen; } while (vlen > v && s[vlen] != ']');
800371e1bf7SGlenn Strauss if (v == vlen) {
8012097fe44SGlenn Strauss return mod_extforward_bad_request(r, __LINE__,
802371e1bf7SGlenn Strauss "Invalid IPv6 addr in Forwarded header");
803371e1bf7SGlenn Strauss }
804371e1bf7SGlenn Strauss }
805371e1bf7SGlenn Strauss else if (s[v] != '_' && s[v] != '/' && s[v] != 'u') {
806371e1bf7SGlenn Strauss /* remove (optional) port from non-obfuscated IPv4 */
807371e1bf7SGlenn Strauss for (klen=vlen, vlen=v; vlen < klen && s[vlen] != ':'; ++vlen) ;
808371e1bf7SGlenn Strauss }
809371e1bf7SGlenn Strauss offsets[j+2] = v;
810371e1bf7SGlenn Strauss }
811371e1bf7SGlenn Strauss offsets[j+3] = vlen - v;
812371e1bf7SGlenn Strauss
813371e1bf7SGlenn Strauss /* obfuscated ipstr and obfuscated port are also accepted here, as
814371e1bf7SGlenn Strauss * is path to unix domain socket, but note that backslash escapes
815371e1bf7SGlenn Strauss * in quoted-string were not unescaped above. Also, if obfuscated
816371e1bf7SGlenn Strauss * identifiers are rotated by proxies as recommended by RFC, then
817371e1bf7SGlenn Strauss * maintaining list of trusted identifiers is non-trivial and is not
818371e1bf7SGlenn Strauss * attempted by this module. */
819371e1bf7SGlenn Strauss
820371e1bf7SGlenn Strauss if (v != vlen) {
821fc7edb39SGlenn Strauss int trusted = is_proxy_trusted(p, s+v, vlen-v);
822371e1bf7SGlenn Strauss
823371e1bf7SGlenn Strauss if (s[v] != '_' && s[v] != '/'
824371e1bf7SGlenn Strauss && (7 != (vlen - v) || 0 != memcmp(s+v, "unknown", 7))) {
825371e1bf7SGlenn Strauss ofor = j; /* save most recent non-obfuscated ipstr */
826371e1bf7SGlenn Strauss }
827371e1bf7SGlenn Strauss
828371e1bf7SGlenn Strauss if (!trusted) break;
829371e1bf7SGlenn Strauss }
830371e1bf7SGlenn Strauss
831371e1bf7SGlenn Strauss do { --j; } while (j > 0 && -1 != offsets[j]);
832371e1bf7SGlenn Strauss if (j <= 0) break;
833371e1bf7SGlenn Strauss --j;
834371e1bf7SGlenn Strauss }
835371e1bf7SGlenn Strauss
836371e1bf7SGlenn Strauss if (-1 != ofor) {
837371e1bf7SGlenn Strauss /* C funcs getaddrinfo(), inet_addr() require '\0'-terminated IP str */
838371e1bf7SGlenn Strauss char *ipend = s+offsets[ofor+2]+offsets[ofor+3];
839371e1bf7SGlenn Strauss char c = *ipend;
840371e1bf7SGlenn Strauss int rc;
841371e1bf7SGlenn Strauss *ipend = '\0';
8422d9bf806SGlenn Strauss rc = mod_extforward_set_addr(r, p, s+offsets[ofor+2], offsets[ofor+3]);
843371e1bf7SGlenn Strauss *ipend = c;
844371e1bf7SGlenn Strauss if (!rc) return HANDLER_GO_ON; /* invalid addr; make no changes */
845371e1bf7SGlenn Strauss }
846371e1bf7SGlenn Strauss else {
847371e1bf7SGlenn Strauss return HANDLER_GO_ON; /* make no changes */
848371e1bf7SGlenn Strauss }
849371e1bf7SGlenn Strauss
850371e1bf7SGlenn Strauss /* parse out params associated with for=<ip> addr set above */
851371e1bf7SGlenn Strauss oproto = ohost = oby = oremote_user = -1;
85228f1867cSGlenn Strauss UNUSED(oby);
853371e1bf7SGlenn Strauss j = ofor;
854371e1bf7SGlenn Strauss if (j > 0) { do { --j; } while (j > 0 && -1 != offsets[j]); }
855371e1bf7SGlenn Strauss if (-1 == offsets[j]) ++j;
856371e1bf7SGlenn Strauss if (j == ofor) j += 4;
857371e1bf7SGlenn Strauss for (; -1 != offsets[j]; j+=4) { /*(k, klen, v, vlen come in sets of 4)*/
858371e1bf7SGlenn Strauss switch (offsets[j+1]) {
859371e1bf7SGlenn Strauss #if 0
860371e1bf7SGlenn Strauss case 2:
861e3874a20SGlenn Strauss if (buffer_eq_icase_ssn(s+offsets[j], "by", 2))
862bd0b2592SGlenn Strauss oby = j;
863371e1bf7SGlenn Strauss break;
864371e1bf7SGlenn Strauss #endif
865371e1bf7SGlenn Strauss #if 0
866371e1bf7SGlenn Strauss /*(already handled above to find IP prior to earliest trusted proxy)*/
867371e1bf7SGlenn Strauss case 3:
868e3874a20SGlenn Strauss if (buffer_eq_icase_ssn(s+offsets[j], "for", 3))
869371e1bf7SGlenn Strauss ofor = j;
870371e1bf7SGlenn Strauss break;
871371e1bf7SGlenn Strauss #endif
872371e1bf7SGlenn Strauss case 4:
873e3874a20SGlenn Strauss if (buffer_eq_icase_ssn(s+offsets[j], "host", 4))
874371e1bf7SGlenn Strauss ohost = j;
875371e1bf7SGlenn Strauss break;
876371e1bf7SGlenn Strauss case 5:
877e3874a20SGlenn Strauss if (buffer_eq_icase_ssn(s+offsets[j], "proto", 5))
878371e1bf7SGlenn Strauss oproto = j;
879371e1bf7SGlenn Strauss break;
880371e1bf7SGlenn Strauss case 11:
881e3874a20SGlenn Strauss if (buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11))
882371e1bf7SGlenn Strauss oremote_user = j;
883371e1bf7SGlenn Strauss break;
884371e1bf7SGlenn Strauss default:
885371e1bf7SGlenn Strauss break;
886371e1bf7SGlenn Strauss }
887371e1bf7SGlenn Strauss }
8889a2404ceSGlenn Strauss i = j+1;
889371e1bf7SGlenn Strauss
890371e1bf7SGlenn Strauss if (-1 != oproto) {
891371e1bf7SGlenn Strauss /* remove trailing spaces/tabs, and double-quotes from proto
892371e1bf7SGlenn Strauss * (note: not unescaping backslash escapes in quoted string) */
893371e1bf7SGlenn Strauss v = offsets[oproto+2];
894371e1bf7SGlenn Strauss vlen = v + offsets[oproto+3];
895371e1bf7SGlenn Strauss while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen;
896371e1bf7SGlenn Strauss if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { ++v; --vlen; }
8977c7f8c46SGlenn Strauss mod_extforward_set_proto(r, s+v, vlen-v);
898371e1bf7SGlenn Strauss }
899371e1bf7SGlenn Strauss
900371e1bf7SGlenn Strauss if (p->conf.opts & PROXY_FORWARDED_HOST) {
901371e1bf7SGlenn Strauss /* Limitations:
9027c7f8c46SGlenn Strauss * - r->http_host is not reset in mod_extforward_restore()
9037c7f8c46SGlenn Strauss * but is currently not an issues since r->http_host will be
9047c7f8c46SGlenn Strauss * reset by next request. If a new module uses r->http_host
905371e1bf7SGlenn Strauss * in the handle_request_done hook, then should evaluate if that
906371e1bf7SGlenn Strauss * module should use the forwarded value (probably) or original value.
907371e1bf7SGlenn Strauss * - due to need to decode and unescape host=..., some extra work is
908371e1bf7SGlenn Strauss * done in the case where host matches current Host header.
909371e1bf7SGlenn Strauss * future: might add code to check if Host has actually changed or not
91040f72a41SGlenn Strauss *
91140f72a41SGlenn Strauss * note: change host after mod_extforward_set_proto() since that may
91240f72a41SGlenn Strauss * affect scheme port used in http_request_host_policy() host
91340f72a41SGlenn Strauss * normalization
914371e1bf7SGlenn Strauss */
915371e1bf7SGlenn Strauss
916371e1bf7SGlenn Strauss /* find host param set by earliest trusted proxy in proxy chain
917371e1bf7SGlenn Strauss * (host might be changed anywhere along the chain) */
918371e1bf7SGlenn Strauss for (j = i; j < used && -1 == ohost; ) {
919371e1bf7SGlenn Strauss if (-1 == offsets[j]) { ++j; continue; }
920371e1bf7SGlenn Strauss if (4 == offsets[j+1]
921e3874a20SGlenn Strauss && buffer_eq_icase_ssn(s+offsets[j], "host", 4))
922371e1bf7SGlenn Strauss ohost = j;
923371e1bf7SGlenn Strauss j += 4; /*(k, klen, v, vlen come in sets of 4)*/
924371e1bf7SGlenn Strauss }
925371e1bf7SGlenn Strauss if (-1 != ohost) {
926af3df29aSGlenn Strauss if (r->http_host && !buffer_is_blank(r->http_host)) {
9273a9a3716SGlenn Strauss if (extforward_check_proxy)
9287c7f8c46SGlenn Strauss http_header_env_set(r,
929371e1bf7SGlenn Strauss CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST"),
930af3df29aSGlenn Strauss BUF_PTR_LEN(r->http_host));
931371e1bf7SGlenn Strauss }
9323a9a3716SGlenn Strauss else {
9333a9a3716SGlenn Strauss r->http_host =
9343a9a3716SGlenn Strauss http_header_request_set_ptr(r, HTTP_HEADER_HOST,
9353a9a3716SGlenn Strauss CONST_STR_LEN("Host"));
9363a9a3716SGlenn Strauss }
937371e1bf7SGlenn Strauss /* remove trailing spaces/tabs, and double-quotes from host */
938371e1bf7SGlenn Strauss v = offsets[ohost+2];
939371e1bf7SGlenn Strauss vlen = v + offsets[ohost+3];
940371e1bf7SGlenn Strauss while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen;
941371e1bf7SGlenn Strauss if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') {
942371e1bf7SGlenn Strauss ++v; --vlen;
943f490078dSGlenn Strauss buffer_copy_string_len_lc(r->http_host, s+v, vlen-v);
9447c7f8c46SGlenn Strauss if (!buffer_backslash_unescape(r->http_host)) {
9452097fe44SGlenn Strauss return mod_extforward_bad_request(r, __LINE__,
946371e1bf7SGlenn Strauss "invalid host= value in Forwarded header");
947371e1bf7SGlenn Strauss }
948371e1bf7SGlenn Strauss }
949371e1bf7SGlenn Strauss else {
950f490078dSGlenn Strauss buffer_copy_string_len_lc(r->http_host, s+v, vlen-v);
951371e1bf7SGlenn Strauss }
952371e1bf7SGlenn Strauss
9537c7f8c46SGlenn Strauss if (0 != http_request_host_policy(r->http_host,
9547c7f8c46SGlenn Strauss r->conf.http_parseopts,
9550c7d2500SGlenn Strauss r->con->proto_default_port)) {
956371e1bf7SGlenn Strauss /*(reject invalid chars in Host)*/
9572097fe44SGlenn Strauss return mod_extforward_bad_request(r, __LINE__,
958371e1bf7SGlenn Strauss "invalid host= value in Forwarded header");
959371e1bf7SGlenn Strauss }
960371e1bf7SGlenn Strauss
9617c7f8c46SGlenn Strauss config_cond_cache_reset_item(r, COMP_HTTP_HOST);
962371e1bf7SGlenn Strauss }
963371e1bf7SGlenn Strauss }
964371e1bf7SGlenn Strauss
965371e1bf7SGlenn Strauss if (p->conf.opts & PROXY_FORWARDED_REMOTE_USER) {
966371e1bf7SGlenn Strauss /* find remote_user param set by closest proxy
967371e1bf7SGlenn Strauss * (auth may have been handled by any trusted proxy in proxy chain) */
968371e1bf7SGlenn Strauss for (j = i; j < used; ) {
969371e1bf7SGlenn Strauss if (-1 == offsets[j]) { ++j; continue; }
970371e1bf7SGlenn Strauss if (11 == offsets[j+1]
971e3874a20SGlenn Strauss && buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11))
972371e1bf7SGlenn Strauss oremote_user = j;
973371e1bf7SGlenn Strauss j += 4; /*(k, klen, v, vlen come in sets of 4)*/
974371e1bf7SGlenn Strauss }
975371e1bf7SGlenn Strauss if (-1 != oremote_user) {
976371e1bf7SGlenn Strauss /* ???: should we also support param for auth_type ??? */
977371e1bf7SGlenn Strauss /* remove trailing spaces/tabs, and double-quotes from remote_user*/
978bd0b2592SGlenn Strauss v = offsets[oremote_user+2];
979bd0b2592SGlenn Strauss vlen = v + offsets[oremote_user+3];
980371e1bf7SGlenn Strauss while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen;
981371e1bf7SGlenn Strauss if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') {
9823dd3cde9SGlenn Strauss buffer *euser;
983371e1bf7SGlenn Strauss ++v; --vlen;
9847c7f8c46SGlenn Strauss http_header_env_set(r,
985371e1bf7SGlenn Strauss CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v);
9867c7f8c46SGlenn Strauss euser = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER"));
9873dd3cde9SGlenn Strauss force_assert(NULL != euser);
9883dd3cde9SGlenn Strauss if (!buffer_backslash_unescape(euser)) {
9892097fe44SGlenn Strauss return mod_extforward_bad_request(r, __LINE__,
990371e1bf7SGlenn Strauss "invalid remote_user= value in Forwarded header");
991371e1bf7SGlenn Strauss }
992371e1bf7SGlenn Strauss }
993371e1bf7SGlenn Strauss else {
9947c7f8c46SGlenn Strauss http_header_env_set(r,
995371e1bf7SGlenn Strauss CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v);
996371e1bf7SGlenn Strauss }
997371e1bf7SGlenn Strauss }
998371e1bf7SGlenn Strauss }
999371e1bf7SGlenn Strauss
1000bd0b2592SGlenn Strauss #if 0
1001bd0b2592SGlenn Strauss if ((p->conf.opts & PROXY_FORWARDED_CREATE_XFF)
10024d6d1e79SGlenn Strauss && !light_btst(r->rqst_htags, HTTP_HEADER_X_FORWARDED_FOR)) {
1003bd0b2592SGlenn Strauss /* create X-Forwarded-For if not present
1004bd0b2592SGlenn Strauss * (and at least original connecting IP is a trusted proxy) */
100526f354cbSGlenn Strauss buffer * const xff =
100626f354cbSGlenn Strauss http_header_request_set_ptr(r, HTTP_HEADER_X_FORWARDED_FOR,
100726f354cbSGlenn Strauss CONST_STR_LEN("X-Forwarded-For"));
1008bd0b2592SGlenn Strauss for (j = 0; j < used; ) {
1009bd0b2592SGlenn Strauss if (-1 == offsets[j]) { ++j; continue; }
1010bd0b2592SGlenn Strauss if (3 == offsets[j+1]
1011e3874a20SGlenn Strauss && buffer_eq_icase_ssn(s+offsets[j], "for", 3)) {
1012af3df29aSGlenn Strauss if (!buffer_is_blank(xff))
1013bd0b2592SGlenn Strauss buffer_append_string_len(xff, CONST_STR_LEN(", "));
1014bd0b2592SGlenn Strauss /* quoted-string, IPv6 brackets, and :port already removed */
1015bd0b2592SGlenn Strauss v = offsets[j+2];
1016bd0b2592SGlenn Strauss vlen = offsets[j+3];
1017bd0b2592SGlenn Strauss buffer_append_string_len(xff, s+v, vlen);
1018bd0b2592SGlenn Strauss if (s[v-1] != '=') { /*(must have been quoted-string)*/
1019bd0b2592SGlenn Strauss char *x =
1020af3df29aSGlenn Strauss memchr(xff->ptr + buffer_clen(xff) - vlen, '\\', vlen);
1021bd0b2592SGlenn Strauss if (NULL != x) { /* backslash unescape in-place */
1022bd0b2592SGlenn Strauss for (v = 0; x[v]; ++x) {
1023bd0b2592SGlenn Strauss if (x[v] == '\\' && x[++v] == '\0')
1024bd0b2592SGlenn Strauss break; /*(invalid trailing backslash)*/
1025bd0b2592SGlenn Strauss *x = x[v];
1026bd0b2592SGlenn Strauss }
1027af3df29aSGlenn Strauss buffer_truncate(xff, x - xff->ptr);
1028bd0b2592SGlenn Strauss }
1029bd0b2592SGlenn Strauss }
1030bd0b2592SGlenn Strauss /* skip to next group; take first "for=..." in group
1031bd0b2592SGlenn Strauss * (should be 0 or 1 "for=..." per group, but not trusted) */
1032bd0b2592SGlenn Strauss do { j += 4; } while (-1 != offsets[j]);
1033bd0b2592SGlenn Strauss ++j;
1034bd0b2592SGlenn Strauss continue;
1035bd0b2592SGlenn Strauss }
1036bd0b2592SGlenn Strauss j += 4; /*(k, klen, v, vlen come in sets of 4)*/
1037bd0b2592SGlenn Strauss }
1038bd0b2592SGlenn Strauss }
1039bd0b2592SGlenn Strauss #endif
1040bd0b2592SGlenn Strauss
1041371e1bf7SGlenn Strauss return HANDLER_GO_ON;
1042371e1bf7SGlenn Strauss }
1043371e1bf7SGlenn Strauss
URIHANDLER_FUNC(mod_extforward_uri_handler)1044f68fd405SElan Ruusamäe URIHANDLER_FUNC(mod_extforward_uri_handler) {
1045f68fd405SElan Ruusamäe plugin_data *p = p_d;
10467c7f8c46SGlenn Strauss mod_extforward_patch_config(r, p);
104796324fbbSGlenn Strauss if (NULL == p->conf.forwarder) return HANDLER_GO_ON;
1048f68fd405SElan Ruusamäe
1049b6bd4d3dSGlenn Strauss if (p->conf.hap_PROXY_ssl_client_verify) {
105083535bbeSGlenn Strauss const data_string *ds;
1051164f7600SGlenn Strauss handler_ctx *hctx = r->con->plugin_ctx[p->id];
105278cc7272SGlenn Strauss if (NULL != hctx && hctx->ssl_client_verify && NULL != hctx->env
105383535bbeSGlenn Strauss && NULL != (ds = (const data_string *)array_get_element_klen(hctx->env, CONST_STR_LEN("SSL_CLIENT_S_DN_CN")))) {
10547c7f8c46SGlenn Strauss http_header_env_set(r,
105578cc7272SGlenn Strauss CONST_STR_LEN("SSL_CLIENT_VERIFY"),
105678cc7272SGlenn Strauss CONST_STR_LEN("SUCCESS"));
10577c7f8c46SGlenn Strauss http_header_env_set(r,
105878cc7272SGlenn Strauss CONST_STR_LEN("REMOTE_USER"),
1059af3df29aSGlenn Strauss BUF_PTR_LEN(&ds->value));
10607c7f8c46SGlenn Strauss http_header_env_set(r,
106178cc7272SGlenn Strauss CONST_STR_LEN("AUTH_TYPE"),
106278cc7272SGlenn Strauss CONST_STR_LEN("SSL_CLIENT_VERIFY"));
106378cc7272SGlenn Strauss } else {
10647c7f8c46SGlenn Strauss http_header_env_set(r,
106578cc7272SGlenn Strauss CONST_STR_LEN("SSL_CLIENT_VERIFY"),
106678cc7272SGlenn Strauss CONST_STR_LEN("NONE"));
106778cc7272SGlenn Strauss }
106878cc7272SGlenn Strauss }
106978cc7272SGlenn Strauss
107096324fbbSGlenn Strauss /* Note: headers are parsed per-request even when using HAProxy PROXY
107196324fbbSGlenn Strauss * protocol since Forwarded header might provide additional info and
107296324fbbSGlenn Strauss * internal _L_ vars might be set for later use by mod_proxy or others*/
107396324fbbSGlenn Strauss /*if (p->conf.hap_PROXY) return HANDLER_GO_ON;*/
107496324fbbSGlenn Strauss
1075e38e907fSGlenn Strauss if (NULL == p->conf.headers) return HANDLER_GO_ON;
107696324fbbSGlenn Strauss
107718c5f3ebSGlenn Strauss /* Do not reparse headers for same request, e.g. after HANDER_COMEBACK
107896324fbbSGlenn Strauss * from mod_rewrite, mod_magnet MAGNET_RESTART_REQUEST, mod_cgi
107996324fbbSGlenn Strauss * cgi.local-redir, or gw_backend reconnect. This has the implication
108096324fbbSGlenn Strauss * that mod_magnet and mod_cgi with local-redir should not modify
1081*e0817646SGlenn Strauss * Forwarded or related headers and expect effects here */
1082*e0817646SGlenn Strauss if (r->plugin_ctx[p->id]) return HANDLER_GO_ON;
108396324fbbSGlenn Strauss
10842d9bf806SGlenn Strauss const buffer *forwarded = NULL;
10852d9bf806SGlenn Strauss int is_forwarded_header = 0;
108696324fbbSGlenn Strauss for (uint32_t k = 0; k < p->conf.headers->used; ++k) {
10872e0676fdSGlenn Strauss const data_string * const ds = (data_string *)p->conf.headers->data[k];
10882e0676fdSGlenn Strauss const buffer * const hdr = &ds->value;
1089af3df29aSGlenn Strauss forwarded = http_header_request_get(r, ds->ext, BUF_PTR_LEN(hdr));
10903dd3cde9SGlenn Strauss if (forwarded) {
1091891007fbSGlenn Strauss is_forwarded_header = (ds->ext == HTTP_HEADER_FORWARDED);
10923dd3cde9SGlenn Strauss break;
10933dd3cde9SGlenn Strauss }
10944c7c0b81SStefan Bühler }
10952d9bf806SGlenn Strauss
1096*e0817646SGlenn Strauss if (forwarded && is_connection_trusted_cached(r->con, p)) {
10972d9bf806SGlenn Strauss return (is_forwarded_header)
10982d9bf806SGlenn Strauss ? mod_extforward_Forwarded(r, p, forwarded)
10992d9bf806SGlenn Strauss : mod_extforward_X_Forwarded_For(r, p, forwarded);
11002d9bf806SGlenn Strauss }
11012d9bf806SGlenn Strauss else {
11027c7f8c46SGlenn Strauss if (r->conf.log_request_handling) {
11037c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__,
11042d9bf806SGlenn Strauss "no forward header found or "
1105010c2894SGlenn Strauss "remote address %s is NOT a trusted proxy, skipping",
1106f1e8a82fSGlenn Strauss r->con->dst_addr_buf.ptr);
11077f5aabdaSJan Kneschke }
11087f5aabdaSJan Kneschke return HANDLER_GO_ON;
11097f5aabdaSJan Kneschke }
1110f68fd405SElan Ruusamäe }
1111f68fd405SElan Ruusamäe
111278cc7272SGlenn Strauss
REQUEST_FUNC(mod_extforward_handle_request_env)11137c7f8c46SGlenn Strauss REQUEST_FUNC(mod_extforward_handle_request_env) {
1114*e0817646SGlenn Strauss handler_ctx * const hctx = r->con->plugin_ctx[((plugin_data *)p_d)->id];
111578cc7272SGlenn Strauss if (NULL == hctx || NULL == hctx->env) return HANDLER_GO_ON;
1116*e0817646SGlenn Strauss const array * restrict env = hctx->env;
1117*e0817646SGlenn Strauss for (uint32_t i = 0; i < env->used; ++i) {
111878cc7272SGlenn Strauss /* note: replaces values which may have been set by mod_openssl
111978cc7272SGlenn Strauss * (when mod_extforward is listed after mod_openssl in server.modules)*/
1120*e0817646SGlenn Strauss const data_string *ds = (const data_string *)env->data[i];
1121af3df29aSGlenn Strauss http_header_env_set(r, BUF_PTR_LEN(&ds->key), BUF_PTR_LEN(&ds->value));
112278cc7272SGlenn Strauss }
112378cc7272SGlenn Strauss return HANDLER_GO_ON;
112478cc7272SGlenn Strauss }
112578cc7272SGlenn Strauss
112678cc7272SGlenn Strauss
REQUEST_FUNC(mod_extforward_restore)11277c7f8c46SGlenn Strauss REQUEST_FUNC(mod_extforward_restore) {
1128*e0817646SGlenn Strauss handler_rctx **rctx =
1129*e0817646SGlenn Strauss (handler_rctx **)&r->plugin_ctx[((plugin_data *)p_d)->id];
1130*e0817646SGlenn Strauss if (*rctx) {
1131*e0817646SGlenn Strauss handler_rctx_free(*rctx);
1132*e0817646SGlenn Strauss *rctx = NULL;
1133*e0817646SGlenn Strauss
1134164f7600SGlenn Strauss connection * const con = r->con;
1135*e0817646SGlenn Strauss r->dst_addr = &con->dst_addr;
1136*e0817646SGlenn Strauss r->dst_addr_buf = &con->dst_addr_buf;
1137*e0817646SGlenn Strauss /* reset conf_cond cache; results may change */
1138*e0817646SGlenn Strauss /* (even though other mods not expected to parse config in reset hook)*/
11397c7f8c46SGlenn Strauss config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP);
114078cc7272SGlenn Strauss }
11413940c60eSJan Kneschke
114278cc7272SGlenn Strauss return HANDLER_GO_ON;
114378cc7272SGlenn Strauss }
114478cc7272SGlenn Strauss
114578cc7272SGlenn Strauss
CONNECTION_FUNC(mod_extforward_handle_con_close)114678cc7272SGlenn Strauss CONNECTION_FUNC(mod_extforward_handle_con_close)
114778cc7272SGlenn Strauss {
114878cc7272SGlenn Strauss plugin_data *p = p_d;
1149164f7600SGlenn Strauss handler_ctx *hctx = con->plugin_ctx[p->id];
115078cc7272SGlenn Strauss if (NULL != hctx) {
1151164f7600SGlenn Strauss con->plugin_ctx[p->id] = NULL;
1152*e0817646SGlenn Strauss if (NULL != hctx->saved_network_read)
1153*e0817646SGlenn Strauss con->network_read = hctx->saved_network_read;
1154*e0817646SGlenn Strauss handler_ctx_free(hctx);
115578cc7272SGlenn Strauss }
115678cc7272SGlenn Strauss
115778cc7272SGlenn Strauss return HANDLER_GO_ON;
115878cc7272SGlenn Strauss }
115978cc7272SGlenn Strauss
116078cc7272SGlenn Strauss
1161010c2894SGlenn Strauss static int mod_extforward_network_read (connection *con, chunkqueue *cq, off_t max_bytes);
116278cc7272SGlenn Strauss
CONNECTION_FUNC(mod_extforward_handle_con_accept)116378cc7272SGlenn Strauss CONNECTION_FUNC(mod_extforward_handle_con_accept)
116478cc7272SGlenn Strauss {
11657c7f8c46SGlenn Strauss request_st * const r = &con->request;
116678cc7272SGlenn Strauss plugin_data *p = p_d;
11677c7f8c46SGlenn Strauss mod_extforward_patch_config(r, p);
116878cc7272SGlenn Strauss if (!p->conf.hap_PROXY) return HANDLER_GO_ON;
1169e38e907fSGlenn Strauss if (NULL == p->conf.forwarder) return HANDLER_GO_ON;
1170fc7edb39SGlenn Strauss if (is_connection_trusted(con, p)) {
117178cc7272SGlenn Strauss handler_ctx *hctx = handler_ctx_init();
1172164f7600SGlenn Strauss con->plugin_ctx[p->id] = hctx;
1173*e0817646SGlenn Strauss hctx->con_is_trusted = -1; /*(masquerade IP not yet known/checked)*/
117478cc7272SGlenn Strauss hctx->saved_network_read = con->network_read;
117578cc7272SGlenn Strauss con->network_read = mod_extforward_network_read;
117678cc7272SGlenn Strauss }
117778cc7272SGlenn Strauss else {
11787c7f8c46SGlenn Strauss if (r->conf.log_request_handling) {
11797c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__,
1180010c2894SGlenn Strauss "remote address %s is NOT a trusted proxy, skipping",
1181f1e8a82fSGlenn Strauss con->dst_addr_buf.ptr);
118278cc7272SGlenn Strauss }
118378cc7272SGlenn Strauss }
1184f68fd405SElan Ruusamäe return HANDLER_GO_ON;
1185f68fd405SElan Ruusamäe }
1186f68fd405SElan Ruusamäe
1187f68fd405SElan Ruusamäe
1188b82d7b8aSGlenn Strauss __attribute_cold__
118963f785a2SStefan Bühler int mod_extforward_plugin_init(plugin *p);
mod_extforward_plugin_init(plugin * p)1190f68fd405SElan Ruusamäe int mod_extforward_plugin_init(plugin *p) {
1191f68fd405SElan Ruusamäe p->version = LIGHTTPD_VERSION_ID;
1192e2de4e58SGlenn Strauss p->name = "extforward";
1193f68fd405SElan Ruusamäe
1194f68fd405SElan Ruusamäe p->init = mod_extforward_init;
119578cc7272SGlenn Strauss p->handle_connection_accept = mod_extforward_handle_con_accept;
1196f68fd405SElan Ruusamäe p->handle_uri_raw = mod_extforward_uri_handler;
119778cc7272SGlenn Strauss p->handle_request_env = mod_extforward_handle_request_env;
119833c8cf41SGlenn Strauss p->handle_request_reset = mod_extforward_restore;
119978cc7272SGlenn Strauss p->handle_connection_close = mod_extforward_handle_con_close;
1200f68fd405SElan Ruusamäe p->set_defaults = mod_extforward_set_defaults;
1201f68fd405SElan Ruusamäe p->cleanup = mod_extforward_free;
1202f68fd405SElan Ruusamäe
1203f68fd405SElan Ruusamäe return 0;
1204f68fd405SElan Ruusamäe }
1205f68fd405SElan Ruusamäe
120678cc7272SGlenn Strauss
120778cc7272SGlenn Strauss
120878cc7272SGlenn Strauss
120978cc7272SGlenn Strauss /* Modified from:
121078cc7272SGlenn Strauss * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
121178cc7272SGlenn Strauss *
121278cc7272SGlenn Strauss 9. Sample code
121378cc7272SGlenn Strauss
121478cc7272SGlenn Strauss The code below is an example of how a receiver may deal with both versions of
121578cc7272SGlenn Strauss the protocol header for TCP over IPv4 or IPv6. The function is supposed to be
121678cc7272SGlenn Strauss called upon a read event. Addresses may be directly copied into their final
121778cc7272SGlenn Strauss memory location since they're transported in network byte order. The sending
121878cc7272SGlenn Strauss side is even simpler and can easily be deduced from this sample code.
121978cc7272SGlenn Strauss *
122078cc7272SGlenn Strauss */
122178cc7272SGlenn Strauss
122278cc7272SGlenn Strauss union hap_PROXY_hdr {
122378cc7272SGlenn Strauss struct {
122478cc7272SGlenn Strauss char line[108];
122578cc7272SGlenn Strauss } v1;
122678cc7272SGlenn Strauss struct {
122778cc7272SGlenn Strauss uint8_t sig[12];
122878cc7272SGlenn Strauss uint8_t ver_cmd;
122978cc7272SGlenn Strauss uint8_t fam;
123078cc7272SGlenn Strauss uint16_t len;
123178cc7272SGlenn Strauss union {
123278cc7272SGlenn Strauss struct { /* for TCP/UDP over IPv4, len = 12 */
123378cc7272SGlenn Strauss uint32_t src_addr;
123478cc7272SGlenn Strauss uint32_t dst_addr;
123578cc7272SGlenn Strauss uint16_t src_port;
123678cc7272SGlenn Strauss uint16_t dst_port;
123778cc7272SGlenn Strauss } ip4;
123878cc7272SGlenn Strauss struct { /* for TCP/UDP over IPv6, len = 36 */
123978cc7272SGlenn Strauss uint8_t src_addr[16];
124078cc7272SGlenn Strauss uint8_t dst_addr[16];
124178cc7272SGlenn Strauss uint16_t src_port;
124278cc7272SGlenn Strauss uint16_t dst_port;
124378cc7272SGlenn Strauss } ip6;
124478cc7272SGlenn Strauss struct { /* for AF_UNIX sockets, len = 216 */
124578cc7272SGlenn Strauss uint8_t src_addr[108];
124678cc7272SGlenn Strauss uint8_t dst_addr[108];
124778cc7272SGlenn Strauss } unx;
124878cc7272SGlenn Strauss } addr;
124978cc7272SGlenn Strauss } v2;
12505b310b36SGlenn Strauss uint64_t ext[32]; /* 2k (- hdr) for v2 TLV extensions (at least 1536 MTU) */
125178cc7272SGlenn Strauss };
125278cc7272SGlenn Strauss
125378cc7272SGlenn Strauss /*
125478cc7272SGlenn Strauss If the length specified in the PROXY protocol header indicates that additional
125578cc7272SGlenn Strauss bytes are part of the header beyond the address information, a receiver may
125678cc7272SGlenn Strauss choose to skip over and ignore those bytes, or attempt to interpret those
125778cc7272SGlenn Strauss bytes.
125878cc7272SGlenn Strauss
125978cc7272SGlenn Strauss The information in those bytes will be arranged in Type-Length-Value (TLV
126078cc7272SGlenn Strauss vectors) in the following format. The first byte is the Type of the vector.
126178cc7272SGlenn Strauss The second two bytes represent the length in bytes of the value (not included
126278cc7272SGlenn Strauss the Type and Length bytes), and following the length field is the number of
126378cc7272SGlenn Strauss bytes specified by the length.
126478cc7272SGlenn Strauss */
126578cc7272SGlenn Strauss struct pp2_tlv {
126678cc7272SGlenn Strauss uint8_t type;
126778cc7272SGlenn Strauss uint8_t length_hi;
126878cc7272SGlenn Strauss uint8_t length_lo;
126978cc7272SGlenn Strauss /*uint8_t value[0];*//* C99 zero-length array */
127078cc7272SGlenn Strauss };
127178cc7272SGlenn Strauss
127278cc7272SGlenn Strauss /*
127378cc7272SGlenn Strauss The following types have already been registered for the <type> field :
127478cc7272SGlenn Strauss */
127578cc7272SGlenn Strauss
127678cc7272SGlenn Strauss #define PP2_TYPE_ALPN 0x01
127778cc7272SGlenn Strauss #define PP2_TYPE_AUTHORITY 0x02
127878cc7272SGlenn Strauss #define PP2_TYPE_CRC32C 0x03
127978cc7272SGlenn Strauss #define PP2_TYPE_NOOP 0x04
128077ea7d8aSGlenn Strauss #define PP2_TYPE_UNIQUE_ID 0x05
128178cc7272SGlenn Strauss #define PP2_TYPE_SSL 0x20
128278cc7272SGlenn Strauss #define PP2_SUBTYPE_SSL_VERSION 0x21
128378cc7272SGlenn Strauss #define PP2_SUBTYPE_SSL_CN 0x22
128478cc7272SGlenn Strauss #define PP2_SUBTYPE_SSL_CIPHER 0x23
128578cc7272SGlenn Strauss #define PP2_SUBTYPE_SSL_SIG_ALG 0x24
128678cc7272SGlenn Strauss #define PP2_SUBTYPE_SSL_KEY_ALG 0x25
128778cc7272SGlenn Strauss #define PP2_TYPE_NETNS 0x30
128878cc7272SGlenn Strauss
128978cc7272SGlenn Strauss /*
1290e5f9e94dSGlenn Strauss For the type PP2_TYPE_SSL, the value is itself defined like this :
129178cc7272SGlenn Strauss */
129278cc7272SGlenn Strauss
129378cc7272SGlenn Strauss struct pp2_tlv_ssl {
129478cc7272SGlenn Strauss uint8_t client;
129578cc7272SGlenn Strauss uint32_t verify;
129678cc7272SGlenn Strauss /*struct pp2_tlv sub_tlv[0];*//* C99 zero-length array */
129778cc7272SGlenn Strauss };
129878cc7272SGlenn Strauss
129978cc7272SGlenn Strauss /*
130078cc7272SGlenn Strauss And the <client> field is made of a bit field from the following values,
130178cc7272SGlenn Strauss indicating which element is present :
130278cc7272SGlenn Strauss */
130378cc7272SGlenn Strauss
130478cc7272SGlenn Strauss #define PP2_CLIENT_SSL 0x01
130578cc7272SGlenn Strauss #define PP2_CLIENT_CERT_CONN 0x02
130678cc7272SGlenn Strauss #define PP2_CLIENT_CERT_SESS 0x04
130778cc7272SGlenn Strauss
130878cc7272SGlenn Strauss
130978cc7272SGlenn Strauss
131078cc7272SGlenn Strauss
131178cc7272SGlenn Strauss #ifndef MSG_DONTWAIT
13128913dc4eSGlenn Strauss #define MSG_DONTWAIT 0
131378cc7272SGlenn Strauss #endif
131478cc7272SGlenn Strauss #ifndef MSG_NOSIGNAL
13158913dc4eSGlenn Strauss #define MSG_NOSIGNAL 0
131678cc7272SGlenn Strauss #endif
131778cc7272SGlenn Strauss
131878cc7272SGlenn Strauss /* 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)131984b5064dSGlenn Strauss static int hap_PROXY_recv (const int fd, union hap_PROXY_hdr * const hdr, const int family, const int so_type)
132078cc7272SGlenn Strauss {
132178cc7272SGlenn Strauss static const char v2sig[12] =
132278cc7272SGlenn Strauss "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
132378cc7272SGlenn Strauss
132478cc7272SGlenn Strauss ssize_t ret;
132578cc7272SGlenn Strauss size_t sz;
132678cc7272SGlenn Strauss int ver;
132778cc7272SGlenn Strauss
132878cc7272SGlenn Strauss do {
132978cc7272SGlenn Strauss ret = recv(fd, hdr, sizeof(*hdr), MSG_PEEK|MSG_DONTWAIT|MSG_NOSIGNAL);
133078cc7272SGlenn Strauss } while (-1 == ret && errno == EINTR);
133178cc7272SGlenn Strauss
133278cc7272SGlenn Strauss if (-1 == ret)
133378cc7272SGlenn Strauss return (errno == EAGAIN
133478cc7272SGlenn Strauss #ifdef EWOULDBLOCK
133578cc7272SGlenn Strauss #if EAGAIN != EWOULDBLOCK
133678cc7272SGlenn Strauss || errno == EWOULDBLOCK
133778cc7272SGlenn Strauss #endif
133878cc7272SGlenn Strauss #endif
133978cc7272SGlenn Strauss ) ? 0 : -1;
134078cc7272SGlenn Strauss
134178cc7272SGlenn Strauss if (ret >= 16 && 0 == memcmp(&hdr->v2, v2sig, 12)
134278cc7272SGlenn Strauss && (hdr->v2.ver_cmd & 0xF0) == 0x20) {
134378cc7272SGlenn Strauss ver = 2;
134478cc7272SGlenn Strauss sz = 16 + (size_t)ntohs(hdr->v2.len);
134578cc7272SGlenn Strauss if ((size_t)ret < sz)
134678cc7272SGlenn Strauss return -2; /* truncated or too large header */
134778cc7272SGlenn Strauss
134878cc7272SGlenn Strauss switch (hdr->v2.ver_cmd & 0xF) {
134978cc7272SGlenn Strauss case 0x01: break; /* PROXY command */
135078cc7272SGlenn Strauss case 0x00: break; /* LOCAL command */
135178cc7272SGlenn Strauss default: return -2; /* not a supported command */
135278cc7272SGlenn Strauss }
135378cc7272SGlenn Strauss }
135478cc7272SGlenn Strauss else if (ret >= 8 && 0 == memcmp(hdr->v1.line, "PROXY", 5)) {
135578cc7272SGlenn Strauss const char *end = memchr(hdr->v1.line, '\r', ret - 1);
135678cc7272SGlenn Strauss if (!end || end[1] != '\n')
135778cc7272SGlenn Strauss return -2; /* partial or invalid header */
135878cc7272SGlenn Strauss ver = 1;
135978cc7272SGlenn Strauss sz = (size_t)(end + 2 - hdr->v1.line); /* skip header + CRLF */
136078cc7272SGlenn Strauss }
136178cc7272SGlenn Strauss else {
136278cc7272SGlenn Strauss /* Wrong protocol */
136378cc7272SGlenn Strauss return -2;
136478cc7272SGlenn Strauss }
136578cc7272SGlenn Strauss
136678cc7272SGlenn Strauss /* we need to consume the appropriate amount of data from the socket
136778cc7272SGlenn Strauss * (overwrites existing contents of hdr with same data) */
136884b5064dSGlenn Strauss UNUSED(family);
136984b5064dSGlenn Strauss UNUSED(so_type);
137078cc7272SGlenn Strauss do {
137184b5064dSGlenn Strauss #if defined(MSG_TRUNC) && defined(__linux__)
137284b5064dSGlenn Strauss if ((family==AF_INET || family==AF_INET6) && so_type == SOCK_STREAM) {
137384b5064dSGlenn Strauss ret = recv(fd, hdr, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL);
137484b5064dSGlenn Strauss if (ret >= 0 || errno != EINVAL) continue;
137584b5064dSGlenn Strauss }
137684b5064dSGlenn Strauss #endif
137778cc7272SGlenn Strauss ret = recv(fd, hdr, sz, MSG_DONTWAIT|MSG_NOSIGNAL);
137878cc7272SGlenn Strauss } while (-1 == ret && errno == EINTR);
137978cc7272SGlenn Strauss if (ret < 0) return -1;
138084b5064dSGlenn Strauss if (ret != (ssize_t)sz) {
138184b5064dSGlenn Strauss errno = EIO; /*(partial read; valid but unexpected; not handled)*/
138284b5064dSGlenn Strauss return -1;
138384b5064dSGlenn Strauss }
138478cc7272SGlenn Strauss if (1 == ver) hdr->v1.line[sz-2] = '\0'; /*terminate str to ease parsing*/
138578cc7272SGlenn Strauss return ver;
138678cc7272SGlenn Strauss }
138778cc7272SGlenn Strauss
138878cc7272SGlenn Strauss
138910dbe38aSGlenn Strauss __attribute_pure__
mod_extforward_str_to_port(const char * const s)139010dbe38aSGlenn Strauss static int mod_extforward_str_to_port (const char * const s)
139110dbe38aSGlenn Strauss {
139210dbe38aSGlenn Strauss /*(more strict than strtol(); digits only)*/
139310dbe38aSGlenn Strauss int port = 0;
139410dbe38aSGlenn Strauss for (int i = 0; i < 5; ++i, port *= 10) {
139510dbe38aSGlenn Strauss if (!light_isdigit(s[i])) return -1;
139610dbe38aSGlenn Strauss port += (s[i] - '0');
139710dbe38aSGlenn Strauss if (s[i+1] == '\0') return port;
139810dbe38aSGlenn Strauss }
139910dbe38aSGlenn Strauss return -1;
140010dbe38aSGlenn Strauss }
140110dbe38aSGlenn Strauss
14027bc7082dSGlenn Strauss /* coverity[-tainted_data_sink: arg-1] */
mod_extforward_hap_PROXY_v1(connection * const con,union hap_PROXY_hdr * const hdr)140378cc7272SGlenn Strauss static int mod_extforward_hap_PROXY_v1 (connection * const con,
140478cc7272SGlenn Strauss union hap_PROXY_hdr * const hdr)
140578cc7272SGlenn Strauss {
1406dadfb5fcSGlenn Strauss #ifdef __COVERITY__
1407dadfb5fcSGlenn Strauss __coverity_tainted_data_sink__(hdr);
1408dadfb5fcSGlenn Strauss #endif
1409dadfb5fcSGlenn Strauss
141078cc7272SGlenn Strauss /* samples
141178cc7272SGlenn Strauss * "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n"
141278cc7272SGlenn Strauss * "PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
141378cc7272SGlenn Strauss * "PROXY UNKNOWN\r\n"
141478cc7272SGlenn Strauss * "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
141578cc7272SGlenn Strauss */
141678cc7272SGlenn Strauss char *s = hdr->v1.line + sizeof("PROXY")-1; /*checked in hap_PROXY_recv()*/
141710dbe38aSGlenn Strauss char *src_addr, *dst_addr, *src_port, *dst_port;
141878cc7272SGlenn Strauss int family;
141910dbe38aSGlenn Strauss int src_lport, dst_lport;
142078cc7272SGlenn Strauss if (*s != ' ') return -1;
142178cc7272SGlenn Strauss ++s;
142278cc7272SGlenn Strauss if (s[0] == 'T' && s[1] == 'C' && s[2] == 'P' && s[4] == ' ') {
142378cc7272SGlenn Strauss if (s[3] == '4') {
142478cc7272SGlenn Strauss family = AF_INET;
142578cc7272SGlenn Strauss } else if (s[3] == '6') {
142678cc7272SGlenn Strauss family = AF_INET6;
142778cc7272SGlenn Strauss }
142878cc7272SGlenn Strauss else {
142978cc7272SGlenn Strauss return -1;
143078cc7272SGlenn Strauss }
143178cc7272SGlenn Strauss s += 5;
143278cc7272SGlenn Strauss }
143378cc7272SGlenn Strauss else if (0 == memcmp(s, "UNKNOWN", sizeof("UNKNOWN")-1)
143478cc7272SGlenn Strauss && (s[7] == '\0' || s[7] == ' ')) {
143578cc7272SGlenn Strauss return 0; /* keep local connection address */
143678cc7272SGlenn Strauss }
143778cc7272SGlenn Strauss else {
143878cc7272SGlenn Strauss return -1;
143978cc7272SGlenn Strauss }
144078cc7272SGlenn Strauss
144178cc7272SGlenn Strauss /*(strsep() should be fairly portable, but is not standard)*/
144278cc7272SGlenn Strauss src_addr = s;
144378cc7272SGlenn Strauss dst_addr = strchr(src_addr, ' ');
144478cc7272SGlenn Strauss if (NULL == dst_addr) return -1;
144578cc7272SGlenn Strauss *dst_addr++ = '\0';
144678cc7272SGlenn Strauss src_port = strchr(dst_addr, ' ');
144778cc7272SGlenn Strauss if (NULL == src_port) return -1;
144878cc7272SGlenn Strauss *src_port++ = '\0';
144978cc7272SGlenn Strauss dst_port = strchr(src_port, ' ');
145078cc7272SGlenn Strauss if (NULL == dst_port) return -1;
145178cc7272SGlenn Strauss *dst_port++ = '\0';
145278cc7272SGlenn Strauss
145310dbe38aSGlenn Strauss src_lport = mod_extforward_str_to_port(src_port);
145410dbe38aSGlenn Strauss if (src_lport <= 0) return -1;
145510dbe38aSGlenn Strauss dst_lport = mod_extforward_str_to_port(dst_port);
145610dbe38aSGlenn Strauss if (dst_lport <= 0) return -1;
145778cc7272SGlenn Strauss
145878cc7272SGlenn Strauss if (1 != sock_addr_inet_pton(&con->dst_addr,
145978cc7272SGlenn Strauss src_addr, family, (unsigned short)src_lport))
146078cc7272SGlenn Strauss return -1;
146178cc7272SGlenn Strauss /* Forwarded by=... could be saved here.
146278cc7272SGlenn Strauss * (see additional comments in mod_extforward_hap_PROXY_v2()) */
146378cc7272SGlenn Strauss
146478cc7272SGlenn Strauss /* re-parse addr to string to normalize
146578cc7272SGlenn Strauss * (instead of trusting PROXY to provide canonicalized src_addr string)
146678cc7272SGlenn Strauss * (should prefer PROXY v2 protocol if concerned about performance) */
1467f1e8a82fSGlenn Strauss sock_addr_inet_ntop_copy_buffer(&con->dst_addr_buf, &con->dst_addr);
146878cc7272SGlenn Strauss
146978cc7272SGlenn Strauss return 0;
147078cc7272SGlenn Strauss }
147178cc7272SGlenn Strauss
147278cc7272SGlenn Strauss
14737bc7082dSGlenn Strauss /* coverity[-tainted_data_sink: arg-1] */
mod_extforward_hap_PROXY_v2(connection * const con,union hap_PROXY_hdr * const hdr)147478cc7272SGlenn Strauss static int mod_extforward_hap_PROXY_v2 (connection * const con,
147578cc7272SGlenn Strauss union hap_PROXY_hdr * const hdr)
147678cc7272SGlenn Strauss {
1477dadfb5fcSGlenn Strauss #ifdef __COVERITY__
1478dadfb5fcSGlenn Strauss __coverity_tainted_data_sink__(hdr);
1479dadfb5fcSGlenn Strauss #endif
1480dadfb5fcSGlenn Strauss
148178cc7272SGlenn Strauss /* If HAProxy-PROXY protocol used, then lighttpd acts as transparent proxy,
148278cc7272SGlenn Strauss * masquerading as servicing the client IP provided in by HAProxy-PROXY hdr.
148378cc7272SGlenn Strauss * The connecting con->dst_addr and con->dst_addr_buf are not saved here,
148478cc7272SGlenn Strauss * so that info is lost unless getsockname() and getpeername() are used.
148578cc7272SGlenn Strauss * One result is that mod_proxy will use the masqueraded IP instead of the
148678cc7272SGlenn Strauss * actual IP when updated Forwarded and X-Forwarded-For (but if actual
148778cc7272SGlenn Strauss * connection IPs needed, better to save the info here rather than use
148878cc7272SGlenn Strauss * syscalls to retrieve the info later).
148978cc7272SGlenn Strauss * (Exception: con->dst_addr can be further changed if mod_extforward parses
1490c752d469SGlenn Strauss * Forwarded or X-Forwarded-For request headers later, after request headers
149178cc7272SGlenn Strauss * have been received.)
149278cc7272SGlenn Strauss */
149378cc7272SGlenn Strauss
149478cc7272SGlenn Strauss /* Forwarded by=... could be saved here. The by param is for backends to be
149578cc7272SGlenn Strauss * able to construct URIs for that interface (interface on server which
149678cc7272SGlenn Strauss * received request and made PROXY connection here), though that server
149778cc7272SGlenn Strauss * should provide that information in updated Forwarded or X-Forwarded-For
149878cc7272SGlenn Strauss * HTTP headers */
149978cc7272SGlenn Strauss /*struct sockaddr_storage by;*/
150078cc7272SGlenn Strauss
150178cc7272SGlenn Strauss /* Addresses provided by HAProxy-PROXY protocol are in network byte order.
150278cc7272SGlenn Strauss * Note: addr info is not validated, so do not accept HAProxy-PROXY
150378cc7272SGlenn Strauss * protocol from untrusted servers. For example, untrusted servers from
150478cc7272SGlenn Strauss * which HAProxy-PROXY protocol is accepted (don't do that) could pretend
150578cc7272SGlenn Strauss * to be from the internal network and might thereby bypass security policy.
150678cc7272SGlenn Strauss */
150778cc7272SGlenn Strauss
150878cc7272SGlenn Strauss /* (Clear con->dst_addr with memset() in case actual and proxies IPs
150978cc7272SGlenn Strauss * are different domains, e.g. one is IPv4 and the other is IPv6) */
151078cc7272SGlenn Strauss
151178cc7272SGlenn Strauss struct pp2_tlv *tlv;
151278cc7272SGlenn Strauss uint32_t sz = ntohs(hdr->v2.len);
151378cc7272SGlenn Strauss uint32_t len = 0;
151478cc7272SGlenn Strauss
151578cc7272SGlenn Strauss switch (hdr->v2.ver_cmd & 0xF) {
151678cc7272SGlenn Strauss case 0x01: break; /* PROXY command */
151778cc7272SGlenn Strauss case 0x00: return 0;/* LOCAL command; keep local connection address */
151878cc7272SGlenn Strauss default: return -1;/* should not happen; validated in hap_PROXY_recv()*/
151978cc7272SGlenn Strauss }
152078cc7272SGlenn Strauss
152178cc7272SGlenn Strauss /* PROXY command */
152278cc7272SGlenn Strauss
152378cc7272SGlenn Strauss switch (hdr->v2.fam) {
152478cc7272SGlenn Strauss case 0x11: /* TCPv4 */
15251367f606SGlenn Strauss sock_addr_assign(&con->dst_addr, AF_INET, hdr->v2.addr.ip4.src_port,
15261367f606SGlenn Strauss &hdr->v2.addr.ip4.src_addr);
1527f1e8a82fSGlenn Strauss sock_addr_inet_ntop_copy_buffer(&con->dst_addr_buf, &con->dst_addr);
152878cc7272SGlenn Strauss #if 0
152978cc7272SGlenn Strauss ((struct sockaddr_in *)&by)->sin_family = AF_INET;
153078cc7272SGlenn Strauss ((struct sockaddr_in *)&by)->sin_addr.s_addr =
153178cc7272SGlenn Strauss hdr->v2.addr.ip4.dst_addr;
153278cc7272SGlenn Strauss ((struct sockaddr_in *)&by)->sin_port =
153378cc7272SGlenn Strauss hdr->v2.addr.ip4.dst_port;
153478cc7272SGlenn Strauss #endif
153578cc7272SGlenn Strauss len = (uint32_t)sizeof(hdr->v2.addr.ip4);
153678cc7272SGlenn Strauss break;
153778cc7272SGlenn Strauss #ifdef HAVE_IPV6
153878cc7272SGlenn Strauss case 0x21: /* TCPv6 */
15391367f606SGlenn Strauss sock_addr_assign(&con->dst_addr, AF_INET6, hdr->v2.addr.ip6.src_port,
15401367f606SGlenn Strauss &hdr->v2.addr.ip6.src_addr);
1541f1e8a82fSGlenn Strauss sock_addr_inet_ntop_copy_buffer(&con->dst_addr_buf, &con->dst_addr);
154278cc7272SGlenn Strauss #if 0
154378cc7272SGlenn Strauss ((struct sockaddr_in6 *)&by)->sin6_family = AF_INET6;
154478cc7272SGlenn Strauss memcpy(&((struct sockaddr_in6 *)&by)->sin6_addr,
154578cc7272SGlenn Strauss hdr->v2.addr.ip6.dst_addr, 16);
154678cc7272SGlenn Strauss ((struct sockaddr_in6 *)&by)->sin6_port =
154778cc7272SGlenn Strauss hdr->v2.addr.ip6.dst_port;
154878cc7272SGlenn Strauss #endif
154978cc7272SGlenn Strauss len = (uint32_t)sizeof(hdr->v2.addr.ip6);
155078cc7272SGlenn Strauss break;
155178cc7272SGlenn Strauss #endif
155278cc7272SGlenn Strauss #ifdef HAVE_SYS_UN_H
155378cc7272SGlenn Strauss case 0x31: /* UNIX domain socket */
155478cc7272SGlenn Strauss {
155578cc7272SGlenn Strauss char *src_addr = (char *)hdr->v2.addr.unx.src_addr;
1556609f9209SGlenn Strauss char *z = memchr(src_addr, '\0', sizeof(hdr->v2.addr.unx.src_addr));
155778cc7272SGlenn Strauss if (NULL == z) return -1; /* invalid addr; too long */
1558609f9209SGlenn Strauss len = (uint32_t)(z - src_addr);
1559609f9209SGlenn Strauss /*if (0 == len) return -1;*//* abstract socket not supported; err?*/
1560609f9209SGlenn Strauss if (0 != sock_addr_assign(&con->dst_addr, AF_UNIX, 0, src_addr))
1561609f9209SGlenn Strauss return -1; /* invalid addr; too long */
1562f1e8a82fSGlenn Strauss buffer_copy_string_len(&con->dst_addr_buf, src_addr, len);
156378cc7272SGlenn Strauss }
156478cc7272SGlenn Strauss #if 0 /*(dst_addr should be identical to src_addr for AF_UNIX)*/
156578cc7272SGlenn Strauss ((struct sockaddr_un *)&by)->sun_family = AF_UNIX;
156678cc7272SGlenn Strauss memcpy(&((struct sockaddr_un *)&by)->sun_path,
156778cc7272SGlenn Strauss hdr->v2.addr.unx.dst_addr, 108);
156878cc7272SGlenn Strauss #endif
156978cc7272SGlenn Strauss len = (uint32_t)sizeof(hdr->v2.addr.unx);
157078cc7272SGlenn Strauss break;
157178cc7272SGlenn Strauss #endif
157278cc7272SGlenn Strauss default: /* keep local connection address; unsupported protocol */
157378cc7272SGlenn Strauss return 0;
157478cc7272SGlenn Strauss }
157578cc7272SGlenn Strauss
157678cc7272SGlenn Strauss /* (optional) Type-Length-Value (TLV vectors) follow addresses */
157778cc7272SGlenn Strauss
157877ea7d8aSGlenn Strauss if (3 + len > sz) return 0;
157977ea7d8aSGlenn Strauss
158077ea7d8aSGlenn Strauss handler_ctx * const hctx =
158177ea7d8aSGlenn Strauss con->plugin_ctx[mod_extforward_plugin_data_singleton->id];
158278cc7272SGlenn Strauss tlv = (struct pp2_tlv *)((char *)hdr + 16);
158378cc7272SGlenn Strauss for (sz -= len, len -= 3; sz >= 3; sz -= 3 + len) {
158478cc7272SGlenn Strauss tlv = (struct pp2_tlv *)((char *)tlv + 3 + len);
158578cc7272SGlenn Strauss len = ((uint32_t)tlv->length_hi << 8) | tlv->length_lo;
158678cc7272SGlenn Strauss if (3 + len > sz) break; /*(invalid TLV)*/
158777ea7d8aSGlenn Strauss const char *k;
158877ea7d8aSGlenn Strauss uint32_t klen;
158978cc7272SGlenn Strauss switch (tlv->type) {
159078cc7272SGlenn Strauss #if 0 /*(not implemented here)*/
159178cc7272SGlenn Strauss case PP2_TYPE_ALPN:
159278cc7272SGlenn Strauss case PP2_TYPE_AUTHORITY:
159378cc7272SGlenn Strauss case PP2_TYPE_CRC32C:
159478cc7272SGlenn Strauss #endif
159578cc7272SGlenn Strauss case PP2_TYPE_SSL: {
159677ea7d8aSGlenn Strauss if (len < 5) continue;
159778cc7272SGlenn Strauss static const uint32_t zero = 0;
1598b298e2acSGlenn Strauss struct pp2_tlv_ssl *tlv_ssl =
1599b298e2acSGlenn Strauss (struct pp2_tlv_ssl *)(void *)((char *)tlv+3);
160078cc7272SGlenn Strauss struct pp2_tlv *subtlv = tlv;
160178cc7272SGlenn Strauss if (tlv_ssl->client & PP2_CLIENT_SSL) {
16021dd58c5aSGlenn Strauss con->proto_default_port = 443; /* "https" */
160378cc7272SGlenn Strauss }
160478cc7272SGlenn Strauss if ((tlv_ssl->client & (PP2_CLIENT_CERT_CONN|PP2_CLIENT_CERT_SESS))
160578cc7272SGlenn Strauss && 0 == memcmp(&tlv_ssl->verify, &zero, 4)) { /* misaligned */
160678cc7272SGlenn Strauss hctx->ssl_client_verify = 1;
160778cc7272SGlenn Strauss }
160877ea7d8aSGlenn Strauss if (len < 5 + 3) continue;
160977ea7d8aSGlenn Strauss if (NULL == hctx->env) hctx->env = array_init(8);
161078cc7272SGlenn Strauss for (uint32_t subsz = len-5, n = 5; subsz >= 3; subsz -= 3 + n) {
161178cc7272SGlenn Strauss subtlv = (struct pp2_tlv *)((char *)subtlv + 3 + n);
161278cc7272SGlenn Strauss n = ((uint32_t)subtlv->length_hi << 8) | subtlv->length_lo;
161378cc7272SGlenn Strauss if (3 + n > subsz) break; /*(invalid TLV)*/
161478cc7272SGlenn Strauss switch (subtlv->type) {
161578cc7272SGlenn Strauss case PP2_SUBTYPE_SSL_VERSION:
161677ea7d8aSGlenn Strauss k = "SSL_PROTOCOL";
161777ea7d8aSGlenn Strauss klen = sizeof("SSL_PROTOCOL")-1;
161878cc7272SGlenn Strauss break;
161978cc7272SGlenn Strauss case PP2_SUBTYPE_SSL_CN:
162078cc7272SGlenn Strauss /* (tlv_ssl->client & PP2_CLIENT_CERT_CONN)
162178cc7272SGlenn Strauss * or
162278cc7272SGlenn Strauss * (tlv_ssl->client & PP2_CLIENT_CERT_SESS) */
162377ea7d8aSGlenn Strauss k = "SSL_CLIENT_S_DN_CN";
162477ea7d8aSGlenn Strauss klen = sizeof("SSL_CLIENT_S_DN_CN")-1;
162578cc7272SGlenn Strauss break;
162678cc7272SGlenn Strauss case PP2_SUBTYPE_SSL_CIPHER:
162777ea7d8aSGlenn Strauss k = "SSL_CIPHER";
162877ea7d8aSGlenn Strauss klen = sizeof("SSL_CIPHER")-1;
162978cc7272SGlenn Strauss break;
163078cc7272SGlenn Strauss case PP2_SUBTYPE_SSL_SIG_ALG:
163177ea7d8aSGlenn Strauss k = "SSL_SERVER_A_SIG";
163277ea7d8aSGlenn Strauss klen = sizeof("SSL_SERVER_A_SIG")-1;
163378cc7272SGlenn Strauss break;
163478cc7272SGlenn Strauss case PP2_SUBTYPE_SSL_KEY_ALG:
163577ea7d8aSGlenn Strauss k = "SSL_SERVER_A_KEY";
163677ea7d8aSGlenn Strauss klen = sizeof("SSL_SERVER_A_KEY")-1;
163778cc7272SGlenn Strauss break;
163878cc7272SGlenn Strauss default:
163977ea7d8aSGlenn Strauss continue;
164077ea7d8aSGlenn Strauss }
164177ea7d8aSGlenn Strauss array_set_key_value(hctx->env, k, klen, (char *)subtlv+3, n);
164277ea7d8aSGlenn Strauss }
164377ea7d8aSGlenn Strauss continue;
164477ea7d8aSGlenn Strauss }
164577ea7d8aSGlenn Strauss case PP2_TYPE_UNIQUE_ID:
164677ea7d8aSGlenn Strauss k = "PP2_UNIQUE_ID";
164777ea7d8aSGlenn Strauss klen = sizeof("PP2_UNIQUE_ID")-1;
164878cc7272SGlenn Strauss break;
164978cc7272SGlenn Strauss #if 0 /*(not implemented here)*/
165078cc7272SGlenn Strauss case PP2_TYPE_NETNS:
165178cc7272SGlenn Strauss #endif
165278cc7272SGlenn Strauss /*case PP2_TYPE_NOOP:*//* no-op */
165378cc7272SGlenn Strauss default:
165477ea7d8aSGlenn Strauss continue;
165578cc7272SGlenn Strauss }
165677ea7d8aSGlenn Strauss if (NULL == hctx->env) hctx->env = array_init(8);
165777ea7d8aSGlenn Strauss array_set_key_value(hctx->env, k, klen, (char *)tlv+3, len);
165878cc7272SGlenn Strauss }
165978cc7272SGlenn Strauss
166078cc7272SGlenn Strauss return 0;
166178cc7272SGlenn Strauss }
166278cc7272SGlenn Strauss
166378cc7272SGlenn Strauss
mod_extforward_network_read(connection * con,chunkqueue * cq,off_t max_bytes)1664010c2894SGlenn Strauss static int mod_extforward_network_read (connection *con,
166578cc7272SGlenn Strauss chunkqueue *cq, off_t max_bytes)
166678cc7272SGlenn Strauss {
166778cc7272SGlenn Strauss /* XXX: when using hap-PROXY protocol, currently avoid overhead of setting
166878cc7272SGlenn Strauss * _L_ environment variables for mod_proxy to accurately set Forwarded hdr
166978cc7272SGlenn Strauss * In the future, might add config switch to enable doing this extra work */
167078cc7272SGlenn Strauss
167178cc7272SGlenn Strauss union hap_PROXY_hdr hdr;
16727c7f8c46SGlenn Strauss log_error_st *errh;
16738b382a81SGlenn Strauss const int family = sock_addr_get_family(&con->dst_addr);
16748b382a81SGlenn Strauss int rc = hap_PROXY_recv(con->fd, &hdr, family, SOCK_STREAM);
16759bc61f16SGlenn Strauss switch (rc) {
167678cc7272SGlenn Strauss case 2: rc = mod_extforward_hap_PROXY_v2(con, &hdr); break;
167778cc7272SGlenn Strauss case 1: rc = mod_extforward_hap_PROXY_v1(con, &hdr); break;
167878cc7272SGlenn Strauss case 0: return 0; /*(errno == EAGAIN || errno == EWOULDBLOCK)*/
167921987c86SGlenn Strauss case -1: errh = con->srv->errh;
16807c7f8c46SGlenn Strauss log_perror(errh,__FILE__,__LINE__,"hap-PROXY recv()");
168178cc7272SGlenn Strauss rc = -1; break;
168221987c86SGlenn Strauss case -2: errh = con->srv->errh;
16837c7f8c46SGlenn Strauss log_error(errh,__FILE__,__LINE__,
1684010c2894SGlenn Strauss "hap-PROXY proto received invalid/unsupported request");
168576faed91SGlenn Strauss __attribute_fallthrough__
168678cc7272SGlenn Strauss default: rc = -1; break;
168778cc7272SGlenn Strauss }
168878cc7272SGlenn Strauss
168921987c86SGlenn Strauss handler_ctx *hctx =
169021987c86SGlenn Strauss con->plugin_ctx[mod_extforward_plugin_data_singleton->id];
169121987c86SGlenn Strauss con->network_read = hctx->saved_network_read;
169221987c86SGlenn Strauss hctx->saved_network_read = NULL;
1693010c2894SGlenn Strauss return (0 == rc) ? con->network_read(con, cq, max_bytes) : rc;
169478cc7272SGlenn Strauss }
1695