lighttpd 1.4.x https://www.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1704 lines
61 KiB

#include "first.h"
#include "base.h"
#include "log.h"
#include "buffer.h"
#include "http_header.h"
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( "Forwarded" ) or extforward.headers = ( "Forwarded", "X-Forwarded-For" ) or extforward.headers = ( "Forwarded", "X-Forwarded-For", "Forwarded-For" ) The default remains: extforward.headers = ( "X-Forwarded-For", "Forwarded-For" ) Support for "Forwarded" is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite "Host" with Forwarded value #"host" => 1 # set REMOTE_USER with Forwarded value #"remote_user" => 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove "Forwarded" from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( "Forwarded" => "" ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( "X-Forwarded-For" => "", "X-Forwarded-By" => "", "X-Forwarded-Server" => "", "X-Origin-IP" => "", "Via" => "", #... ) x-ref: "Forwarded HTTP Extension" https://tools.ietf.org/html/rfc7239 "Forward authenticated user to proxied requests" https://redmine.lighttpd.net/issues/2703
5 years ago
#include "request.h"
#include "sock_addr.h"
#include "plugin.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "sys-socket.h"
/**
* mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com
* extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu
* support chained proxies by glen@delfi.ee, #1528
*
*
* Mostly rewritten
* Portions:
* Copyright(c) 2017 Glenn Strauss gstrauss()gluelogic.com All rights reserved
* License: BSD 3-clause (same as lighttpd)
*
* Config example:
*
* Trust proxy 10.0.0.232 and 10.0.0.232
* extforward.forwarder = ( "10.0.0.232" => "trust",
* "10.0.0.233" => "trust" )
*
* Trust all proxies (NOT RECOMMENDED!)
* extforward.forwarder = ( "all" => "trust")
*
* Note that "all" has precedence over specific entries,
* so "all except" setups will not work.
*
* In case you have chained proxies, you can add all their IP's to the
* config. However "all" has effect only on connecting IP, as the
* X-Forwarded-For header can not be trusted.
*
* Note: The effect of this module is variable on $HTTP["remotip"] directives and
* other module's remote ip dependent actions.
* Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP.
* Things done in between these two moments will match on the real client's IP.
* The moment things are done by a module depends on in which hook it does things and within the same hook
* on whether they are before/after us in the module loading order
* (order in the server.modules directive in the config file).
*
* Tested behaviours:
*
* mod_access: Will match on the real client.
*
* mod_accesslog:
* In order to see the "real" ip address in access log ,
* you'll have to load mod_extforward after mod_accesslog.
* like this:
*
* server.modules = (
* .....
* mod_accesslog,
* mod_extforward
* )
*/
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
typedef enum {
PROXY_FORWARDED_NONE = 0x00,
PROXY_FORWARDED_FOR = 0x01,
PROXY_FORWARDED_PROTO = 0x02,
PROXY_FORWARDED_HOST = 0x04,
PROXY_FORWARDED_BY = 0x08,
PROXY_FORWARDED_REMOTE_USER = 0x10
} proxy_forwarded_t;
struct sock_addr_mask {
sock_addr addr;
int bits;
};
struct forwarder_cfg {
const array *forwarder;
int forward_all;
uint32_t addrs_used;
#if defined(__STDC_VERSION__) && __STDC_VERSION__-0 >= 199901L /* C99 */
struct sock_addr_mask addrs[];
#else
struct sock_addr_mask addrs[1];
#endif
};
typedef struct {
const array *forwarder;
int forward_all;
uint32_t forward_masks_used;
const struct sock_addr_mask *forward_masks;
const array *headers;
unsigned int opts;
char hap_PROXY;
char hap_PROXY_ssl_client_verify;
} plugin_config;
typedef struct {
PLUGIN_DATA;
plugin_config defaults;
plugin_config conf;
array *default_headers;
} plugin_data;
static plugin_data *mod_extforward_plugin_data_singleton;
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
static int extforward_check_proxy;
/* context , used for restore remote ip */
typedef struct {
/* per-request state */
sock_addr saved_remote_addr;
buffer *saved_remote_addr_buf;
/* hap-PROXY protocol prior to receiving first request */
int(*saved_network_read)(connection *, chunkqueue *, off_t);
/* connection-level state applied to requests in handle_request_env */
array *env;
int ssl_client_verify;
} handler_ctx;
static handler_ctx * handler_ctx_init(void) {
handler_ctx * hctx;
hctx = calloc(1, sizeof(*hctx));
force_assert(hctx);
return hctx;
}
static void handler_ctx_free(handler_ctx *hctx) {
free(hctx);
}
INIT_FUNC(mod_extforward_init) {
return calloc(1, sizeof(plugin_data));
}
FREE_FUNC(mod_extforward_free) {
plugin_data * const p = p_d;
array_free(p->default_headers);
if (NULL == p->cvlist) return;
/* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0: /* extforward.forwarder */
if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v);
break;
default:
break;
}
}
}
}
static void mod_extforward_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
case 0: /* extforward.forwarder */
if (cpv->vtype == T_CONFIG_LOCAL) {
const struct forwarder_cfg * const fwd = cpv->v.v;
pconf->forwarder = fwd->forwarder;
pconf->forward_all = fwd->forward_all;
pconf->forward_masks_used = fwd->addrs_used;
pconf->forward_masks = fwd->addrs;
}
break;
case 1: /* extforward.headers */
pconf->headers = cpv->v.a;
break;
case 2: /* extforward.params */
if (cpv->vtype == T_CONFIG_LOCAL)
pconf->opts = cpv->v.u;
break;
case 3: /* extforward.hap-PROXY */
pconf->hap_PROXY = (char)cpv->v.u;
break;
case 4: /* extforward.hap-PROXY-ssl-client-verify */
pconf->hap_PROXY_ssl_client_verify = (char)cpv->v.u;
break;
default:/* should not happen */
return;
}
}
static void mod_extforward_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
do {
mod_extforward_merge_config_cpv(pconf, cpv);
} while ((++cpv)->k_id != -1);
}
static void mod_extforward_patch_config(request_st * const r, plugin_data * const p) {
memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
for (int i = 1, used = p->nconfig; i < used; ++i) {
if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
mod_extforward_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
}
}
static void * mod_extforward_parse_forwarder(server *srv, const array *forwarder) {
const data_string * const allds = (const data_string *)
array_get_element_klen(forwarder, CONST_STR_LEN("all"));
const int forward_all = (NULL == allds)
? 0
: buffer_eq_icase_slen(&allds->value, CONST_STR_LEN("trust")) ? 1 : -1;
uint32_t nmasks = 0;
for (uint32_t j = 0; j < forwarder->used; ++j) {
data_string * const ds = (data_string *)forwarder->data[j];
char * const nm_slash = strchr(ds->key.ptr, '/');
if (NULL != nm_slash) ++nmasks;
if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("trust"))) {
if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("untrusted")))
log_error(srv->errh, __FILE__, __LINE__,
"ERROR: expect \"trust\", not \"%s\" => \"%s\"; "
"treating as untrusted", ds->key.ptr, ds->value.ptr);
if (NULL != nm_slash) {
/* future: consider adding member next to bits in sock_addr_mask
* with bool trusted/untrusted member */
--nmasks;
log_error(srv->errh, __FILE__, __LINE__,
"ERROR: untrusted CIDR masks are ignored (\"%s\" => \"%s\")",
ds->key.ptr, ds->value.ptr);
}
buffer_clear(&ds->value); /* empty is untrusted */
continue;
}
}
struct forwarder_cfg * const fwd =
malloc(sizeof(struct forwarder_cfg)+sizeof(struct sock_addr_mask)*nmasks);
force_assert(fwd);
memset(fwd, 0,
sizeof(struct forwarder_cfg) + sizeof(struct sock_addr_mask)*nmasks);
fwd->forwarder = forwarder;
fwd->forward_all = forward_all;
fwd->addrs_used = 0;
for (uint32_t j = 0; j < forwarder->used; ++j) {
data_string * const ds = (data_string *)forwarder->data[j];
char * const nm_slash = strchr(ds->key.ptr, '/');
if (NULL == nm_slash) continue;
if (buffer_string_is_empty(&ds->value)) continue; /* ignored */
char *err;
const int nm_bits = strtol(nm_slash + 1, &err, 10);
int rc;
if (*err || nm_bits <= 0 || !light_isdigit(nm_slash[1])) {
log_error(srv->errh, __FILE__, __LINE__,
"ERROR: invalid netmask: %s %s", ds->key.ptr, err);
free(fwd);
return NULL;
}
struct sock_addr_mask * const sm = fwd->addrs + fwd->addrs_used++;
sm->bits = nm_bits;
*nm_slash = '\0';
rc = sock_addr_from_str_numeric(&sm->addr, ds->key.ptr, srv->errh);
*nm_slash = '/';
if (1 != rc) {
free(fwd);
return NULL;
}
buffer_clear(&ds->value);
/* empty is untrusted,
* e.g. if subnet (incorrectly) appears in X-Forwarded-For */
}
return fwd;
}
static unsigned int mod_extforward_parse_opts(server *srv, const array *opts_params) {
unsigned int opts = 0;
for (uint32_t j = 0, used = opts_params->used; j < used; ++j) {
proxy_forwarded_t param;
data_unset *du = opts_params->data[j];
#if 0 /*("for" and "proto" historical behavior: always enabled)*/
if (buffer_eq_slen(&du->key, CONST_STR_LEN("by")))
param = PROXY_FORWARDED_BY;
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for")))
param = PROXY_FORWARDED_FOR;
else
#endif
if (buffer_eq_slen(&du->key, CONST_STR_LEN("host")))
param = PROXY_FORWARDED_HOST;
#if 0
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto")))
param = PROXY_FORWARDED_PROTO;
#endif
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user")))
param = PROXY_FORWARDED_REMOTE_USER;
else {
log_error(srv->errh, __FILE__, __LINE__,
"extforward.params keys must be one of: "
"host, remote_user, but not: %s", du->key.ptr);
return HANDLER_ERROR;
}
if (du->type == TYPE_STRING) {
data_string *ds = (data_string *)du;
if (buffer_eq_slen(&ds->value, CONST_STR_LEN("enable"))) {
opts |= param;
}
else if (!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))) {
log_error(srv->errh, __FILE__, __LINE__,
"extforward.params values must be one of: "
"0, 1, enable, disable; error for key: %s", du->key.ptr);
return UINT_MAX;
}
}
else if (du->type == TYPE_INTEGER) {
data_integer *di = (data_integer *)du;
if (di->value) opts |= param;
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"extforward.params values must be one of: "
"0, 1, enable, disable; error for key: %s", du->key.ptr);
return UINT_MAX;
}
}
return opts;
}
SETDEFAULTS_FUNC(mod_extforward_set_defaults) {
static const config_plugin_keys_t cpk[] = {
{ CONST_STR_LEN("extforward.forwarder"),
T_CONFIG_ARRAY_KVSTRING,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("extforward.headers"),
T_CONFIG_ARRAY_VLIST,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("extforward.params"),
T_CONFIG_ARRAY_KVANY,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("extforward.hap-PROXY"),
T_CONFIG_BOOL,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("extforward.hap-PROXY-ssl-client-verify"),
T_CONFIG_BOOL,
T_CONFIG_SCOPE_CONNECTION }
,{ NULL, 0,
T_CONFIG_UNSET,
T_CONFIG_SCOPE_UNSET }
};
plugin_data * const p = p_d;
if (!config_plugin_values_init(srv, p, cpk, "mod_extforward"))
return HANDLER_ERROR;
int hap_PROXY = 0;
/* process and validate config directives
* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0: /* extforward.forwarder */
cpv->v.v = mod_extforward_parse_forwarder(srv, cpv->v.a);
if (NULL == cpv->v.v) {
log_error(srv->errh, __FILE__, __LINE__,
"unexpected value for %s", cpk[cpv->k_id].k);
return HANDLER_ERROR;
}
cpv->vtype = T_CONFIG_LOCAL;
break;
case 1: /* extforward.headers */
break;
case 2: /* extforward.params */
cpv->v.u = mod_extforward_parse_opts(srv, cpv->v.a);
if (UINT_MAX == cpv->v.u)
return HANDLER_ERROR;
break;
case 3: /* extforward.hap-PROXY */
if (cpv->v.u) hap_PROXY = 1;
break;
case 4: /* extforward.hap-PROXY-ssl-client-verify */
break;
default:/* should not happen */
break;
}
}
}
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
mod_extforward_plugin_data_singleton = p;
p->defaults.opts = PROXY_FORWARDED_NONE;
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
/* initialize p->defaults from global config context */
if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
if (-1 != cpv->k_id)
mod_extforward_merge_config(&p->defaults, cpv);
}
/* default to "X-Forwarded-For" or "Forwarded-For" if extforward.headers
* is not specified or is empty (and not using hap_PROXY) */
if (!p->defaults.hap_PROXY
&& (NULL == p->defaults.headers || 0 == p->defaults.headers->used)) {
p->defaults.headers = p->default_headers = array_init(2);
array_insert_value(p->default_headers,CONST_STR_LEN("X-Forwarded-For"));
array_insert_value(p->default_headers,CONST_STR_LEN("Forwarded-For"));
}
/* attempt to warn if mod_extforward is not last module loaded to hook
* handle_connection_accept. (Nice to have, but remove this check if
* it reaches too far into internals and prevents other code changes.)
* While it would be nice to check connection_handle_accept plugin slot
* to make sure mod_extforward is last, that info is private to plugin.c
* so merely warn if mod_openssl is loaded after mod_extforward, though
* future modules which hook connection_handle_accept might be missed.*/
if (hap_PROXY) {
uint32_t i;
for (i = 0; i < srv->srvconf.modules->used; ++i) {
data_string *ds = (data_string *)srv->srvconf.modules->data[i];
if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_extforward")))
break;
}
for (; i < srv->srvconf.modules->used; ++i) {
data_string *ds = (data_string *)srv->srvconf.modules->data[i];
if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_openssl"))
|| buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_mbedtls"))
|| buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_nss"))
|| buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_gnutls"))) {
log_error(srv->errh, __FILE__, __LINE__,
"mod_extforward must be loaded after %s in "
"server.modules when extforward.hap-PROXY = \"enable\"",
ds->value.ptr);
break;
}
}
}
for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
data_string *ds = (data_string *)srv->srvconf.modules->data[i];
if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_proxy"))) {
extforward_check_proxy = 1;
break;
}
}
return HANDLER_GO_ON;
}
/*
extract a forward array from the environment
*/
static array *extract_forward_array(const buffer *pbuffer)
{
array *result = array_init(8);
if (!buffer_string_is_empty(pbuffer)) {
const char *base, *curr;
/* state variable, 0 means not in string, 1 means in string */
int in_str = 0;
for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) {
int hex_or_colon = (light_isxdigit(*curr) || *curr == ':');
if (in_str) {
if (!hex_or_colon && *curr != '.') {
/* found an separator , insert value into result array */
array_insert_value(result, base, curr - base);
/* change state to not in string */
in_str = 0;
}
} else {
if (hex_or_colon) {
/* found leading char of an IP address, move base pointer and change state */
base = curr;
in_str = 1;
}
}
}
/* if breaking out while in str, we got to the end of string, so add it */
if (in_str) {
array_insert_value(result, base, curr - base);
}
}
return result;
}
/*
* check whether ip is trusted, return 1 for trusted , 0 for untrusted
*/
static int is_proxy_trusted(plugin_data *p, const char * const ip, size_t iplen)
{
const data_string *ds =
(const data_string *)array_get_element_klen(p->conf.forwarder, ip, iplen);
if (NULL != ds) return !buffer_string_is_empty(&ds->value);
if (p->conf.forward_masks_used) {
const struct sock_addr_mask * const addrs = p->conf.forward_masks;
const uint32_t aused = p->conf.forward_masks_used;
sock_addr addr;
/* C funcs inet_aton(), inet_pton() require '\0'-terminated IP str */
char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/
if (iplen >= sizeof(addrstr)) return 0;
memcpy(addrstr, ip, iplen);
addrstr[iplen] = '\0';
if (1 != sock_addr_inet_pton(&addr, addrstr, AF_INET, 0)
&& 1 != sock_addr_inet_pton(&addr, addrstr, AF_INET6, 0)) return 0;
for (uint32_t i = 0; i < aused; ++i) {
if (sock_addr_is_addr_eq_bits(&addr, &addrs[i].addr, addrs[i].bits))
return 1;
}
}
return 0;
}
static int is_connection_trusted(connection * const con, plugin_data *p)
{
if (p->conf.forward_all) return (1 == p->conf.forward_all);
return is_proxy_trusted(p, CONST_BUF_LEN(con->dst_addr_buf));
}
/*
* Return last address of proxy that is not trusted.
* Do not accept "all" keyword here.
*/
static const char *last_not_in_array(array *a, plugin_data *p)
{
int i;
for (i = a->used - 1; i >= 0; i--) {
data_string *ds = (data_string *)a->data[i];
if (!is_proxy_trusted(p, CONST_BUF_LEN(&ds->value))) {
return ds->value.ptr;
}
}
return NULL;
}
static int mod_extforward_set_addr(request_st * const r, plugin_data *p, const char *addr) {
connection * const con = r->con;
sock_addr sock;
handler_ctx *hctx = r->plugin_ctx[p->id];
if (r->conf.log_request_handling) {
log_error(r->conf.errh, __FILE__, __LINE__, "using address: %s", addr);
}
sock.plain.sa_family = AF_UNSPEC;
if (1 != sock_addr_from_str_numeric(&sock, addr, r->conf.errh)) return 0;
if (sock.plain.sa_family == AF_UNSPEC) return 0;
/* we found the remote address, modify current connection and save the old address */
if (hctx) {
if (hctx->saved_remote_addr_buf) {
if (r->conf.log_request_handling) {
log_error(r->conf.errh, __FILE__, __LINE__,
"-- mod_extforward_uri_handler already patched this connection, resetting state");
}
con->dst_addr = hctx->saved_remote_addr;
buffer_free(con->dst_addr_buf);
con->dst_addr_buf = hctx->saved_remote_addr_buf;
hctx->saved_remote_addr_buf = NULL;
}
} else {
r->plugin_ctx[p->id] = hctx = handler_ctx_init();
}
/* save old address */
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
if (extforward_check_proxy) {
http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR"), CONST_BUF_LEN(con->dst_addr_buf));
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
}
hctx->saved_remote_addr = con->dst_addr;
hctx->saved_remote_addr_buf = con->dst_addr_buf;
/* patch connection address */
con->dst_addr = sock;
con->dst_addr_buf = buffer_init_string(addr);
if (r->conf.log_request_handling) {
log_error(r->conf.errh, __FILE__, __LINE__,
"patching con->dst_addr_buf for the accesslog: %s", addr);
}
/* Now, clean the conf_cond cache, because we may have changed the results of tests */
config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP);
return 1;
}
static void mod_extforward_set_proto(request_st * const r, const char * const proto, size_t protolen) {
if (0 != protolen && !buffer_is_equal_caseless_string(&r->uri.scheme, proto, protolen)) {
/* update scheme if X-Forwarded-Proto is set
* Limitations:
* - Only "http" or "https" are currently accepted since the request to lighttpd currently has to
* be HTTP/1.0 or HTTP/1.1 using http or https. If this is changed, then the scheme from this
* untrusted header must be checked to contain only alphanumeric characters, and to be a
* reasonable length, e.g. < 256 chars.
* - r->uri.scheme is not reset in mod_extforward_restore() but is currently not an issues since
* r->uri.scheme will be reset by next request. If a new module uses r->uri.scheme in the
* handle_request_done hook, then should evaluate if that module should use the forwarded value
* (probably) or the original value.
*/
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
if (extforward_check_proxy) {
http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO"), CONST_BUF_LEN(&r->uri.scheme));
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
}
if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("https"))) {
buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("https"));
config_cond_cache_reset_item(r, COMP_HTTP_SCHEME);
} else if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("http"))) {
buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("http"));
config_cond_cache_reset_item(r, COMP_HTTP_SCHEME);
}
}
}
static handler_t mod_extforward_X_Forwarded_For(request_st * const r, plugin_data * const p, const buffer * const x_forwarded_for) {
/* build forward_array from forwarded data_string */
array *forward_array = extract_forward_array(x_forwarded_for);
const char *real_remote_addr = last_not_in_array(forward_array, p);
if (real_remote_addr != NULL) { /* parsed */
/* get scheme if X-Forwarded-Proto is set
* Limitations:
* - X-Forwarded-Proto may or may not be set by proxies, even if X-Forwarded-For is set
* - X-Forwarded-Proto may be a comma-separated list if there are multiple proxies,
* but the historical behavior of the code below only honored it if there was exactly one value
* (not done: walking backwards in X-Forwarded-Proto the same num of steps
* as in X-Forwarded-For to find proto set by last trusted proxy)
*/
const buffer *x_forwarded_proto = http_header_request_get(r, HTTP_HEADER_X_FORWARDED_PROTO, CONST_STR_LEN("X-Forwarded-Proto"));
if (mod_extforward_set_addr(r, p, real_remote_addr) && NULL != x_forwarded_proto) {
mod_extforward_set_proto(r, CONST_BUF_LEN(x_forwarded_proto));
}
}
array_free(forward_array);
return HANDLER_GO_ON;
}
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
static int find_end_quoted_string (const char * const s, int i) {
do {
++i;
} while (s[i] != '"' && s[i] != '\0' && (s[i] != '\\' || s[++i] != '\0'));
return i;
}
static int find_next_semicolon_or_comma_or_eq (const char * const s, int i) {
for (; s[i] != '=' && s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) {
if (s[i] == '"') {
i = find_end_quoted_string(s, i);
if (s[i] == '\0') return -1;
}
}
return i;
}
static int find_next_semicolon_or_comma (const char * const s, int i) {
for (; s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) {
if (s[i] == '"') {
i = find_end_quoted_string(s, i);
if (s[i] == '\0') return -1;
}
}
return i;
}
static int buffer_backslash_unescape (buffer * const b) {
/* (future: might move to buffer.c) */
size_t j = 0;
size_t len = buffer_string_length(b);
char *p = memchr(b->ptr, '\\', len);
if (NULL == p) return 1; /*(nothing to do)*/
len -= (size_t)(p - b->ptr);
for (size_t i = 0; i < len; ++i) {
if (p[i] == '\\') {
if (++i == len) return 0; /*(invalid trailing backslash)*/
}
p[j++] = p[i];
}
buffer_string_set_length(b, (size_t)(p+j - b->ptr));
return 1;
}
static handler_t mod_extforward_Forwarded (request_st * const r, plugin_data * const p, const buffer * const forwarded) {
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
/* HTTP list need not consist of param=value tokens,
* but this routine expect such for HTTP Forwarded header
* Since info in each set of params is only used if from
* admin-specified trusted proxy:
* - invalid param=value tokens are ignored and skipped
* - not checking "for" exists in each set of params
* - not checking for duplicated params in each set of params
* - not checking canonical form of addr (also might be obfuscated)
* - obfuscated tokens permitted in chain, though end of trust is expected
* to be non-obfuscated IP for mod_extforward to masquerade as remote IP
* future: since (potentially) trusted proxies begin at end of string,
* it might be better to parse from end of string rather than parsing from
* beginning. Doing so would also allow reducing arbitrary param limit
* to number of params permitted per proxy.
*/
char * const s = forwarded->ptr;
int i = 0, j = -1, v, vlen, k, klen;
int used = (int)buffer_string_length(forwarded);
int ofor = -1, oproto, ohost, oby, oremote_user;
int offsets[256];/*(~50 params is more than reasonably expected to handle)*/
while (i < used) {
while (s[i] == ' ' || s[i] == '\t') ++i;
if (s[i] == ';') { ++i; continue; }
if (s[i] == ',') {
if (j >= (int)(sizeof(offsets)/sizeof(int))) break;
offsets[++j] = -1; /*("offset" separating params from next proxy)*/
++i;
continue;
}
if (s[i] == '\0') break;
k = i;
i = find_next_semicolon_or_comma_or_eq(s, i);
if (i < 0) {
/*(reject IP spoofing if attacker sets improper quoted-string)*/
log_error(r->conf.errh, __FILE__, __LINE__,
"invalid quoted-string in Forwarded header");
r->http_status = 400; /* Bad Request */
r->handler_module = NULL;
[mod_extforward] support Forwarded HTTP Extension (#2703) enable with, e.g.: extforward.headers = ( &#34;Forwarded&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34; ) or extforward.headers = ( &#34;Forwarded&#34;, &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) The default remains: extforward.headers = ( &#34;X-Forwarded-For&#34;, &#34;Forwarded-For&#34; ) Support for &#34;Forwarded&#34; is not enabled by default since intermediate proxies might not be aware of Forwarded, and might therefore pass spoofed Forwarded header received from client. extforward.params = ( # overwrite &#34;Host&#34; with Forwarded value #&#34;host&#34; =&gt; 1 # set REMOTE_USER with Forwarded value #&#34;remote_user&#34; =&gt; 1 ) Note: be cautious configuring trusted proxies if enabling these options since Forwarded header may be spoofed and passed along indescriminantly by proxies which do not handle Forwarded. To remove &#34;Forwarded&#34; from incoming requests, do not enable these options and instead use mod_setenv to clear the request header: setenv.set-request-header = ( &#34;Forwarded&#34; =&gt; &#34;&#34; ) Other proxy-related headers which admin might evaluate to keep or clear: setenv.set-request-header = ( &#34;X-Forwarded-For&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-By&#34; =&gt; &#34;&#34;, &#34;X-Forwarded-Server&#34; =&gt; &#34;&#34;, &#34;X-Origin-IP&#34; =&gt; &#34;&#34;, &#34;Via&#34; =&gt; &#34;&#34;, #... ) x-ref: &#34;Forwarded HTTP Extension&#34; https://tools.ietf.org/html/rfc7239 &#34;Forward authenticated user to proxied requests&#34; https://redmine.lighttpd.net/issues/2703
5 years ago
return HANDLER_FINISHED;
}
if (s[i] != '=') continue;
klen = i - k;
v = ++i;
i = find_next_semicolon_or_comma(s, i);
if (i < 0) {
/*(reject IP spoofing if attacker sets improper quoted-string)*/
log_error(r->conf.errh, __FILE__, __LINE__,
"invalid quoted-string in Forwarded header");