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.

1646 lines
59 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 "configfile.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
*
* 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
* )
*/
/* plugin config for all request/connections */
[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 sock_addr_masks {
struct sock_addr_mask *addrs;
size_t used;
size_t sz;
};
typedef struct {
array *forwarder;
struct sock_addr_masks *forward_masks;
array *headers;
[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
array *opts_params;
unsigned int opts;
unsigned short int hap_PROXY;
unsigned short int hap_PROXY_ssl_client_verify;
short int forward_all;
} plugin_config;
typedef struct {
PLUGIN_DATA;
plugin_config **config_storage;
plugin_config conf;
} 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)(server *, 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));
return hctx;
}
static void handler_ctx_free(handler_ctx *hctx) {
free(hctx);
}
/* init the plugin data */
INIT_FUNC(mod_extforward_init) {
plugin_data *p;
p = calloc(1, sizeof(*p));
mod_extforward_plugin_data_singleton = p;
return p;
}
/* destroy the plugin data */
FREE_FUNC(mod_extforward_free) {
plugin_data *p = p_d;
UNUSED(srv);
if (!p) return HANDLER_GO_ON;
if (p->config_storage) {
size_t i;
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s = p->config_storage[i];
if (NULL == s) continue;
array_free(s->forwarder);
array_free(s->headers);
[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
array_free(s->opts_params);
if (s->forward_masks) {
free(s->forward_masks->addrs);
free(s->forward_masks);
}
free(s);
}
free(p->config_storage);
}
free(p);
return HANDLER_GO_ON;
}
/* handle plugin config and check values */
SETDEFAULTS_FUNC(mod_extforward_set_defaults) {
plugin_data *p = p_d;
size_t i = 0;
config_values_t cv[] = {
{ "extforward.forwarder", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
{ "extforward.headers", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
[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
{ "extforward.params", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
{ "extforward.hap-PROXY", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
{ "extforward.hap-PROXY-ssl-client-verify", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
if (!p) return HANDLER_ERROR;
p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
for (i = 0; i < srv->config_context->used; i++) {
data_config const* config = (data_config const*)srv->config_context->data[i];
plugin_config *s;
s = calloc(1, sizeof(plugin_config));
s->forwarder = array_init();
s->headers = array_init();
[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
s->opts_params = array_init();
s->opts = PROXY_FORWARDED_NONE;
cv[0].destination = s->forwarder;
cv[1].destination = s->headers;
[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
cv[2].destination = s->opts_params;
cv[3].destination = &s->hap_PROXY;
cv[4].destination = &s->hap_PROXY_ssl_client_verify;
p->config_storage[i] = s;
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
return HANDLER_ERROR;
}
if (!array_is_kvstring(s->forwarder)) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for extforward.forwarder; expected list of \"IPaddr\" => \"trust\"");
return HANDLER_ERROR;
}
if (array_get_element(config->value, "extforward.forwarder")) {
const data_string * const allds = (data_string *)array_get_element(s->forwarder, "all");
s->forward_all = (NULL == allds) ? 0 : (0 == strcasecmp(allds->value->ptr, "trust")) ? 1 : -1;
for (size_t j = 0; j < s->forwarder->used; ++j) {
data_string * const ds = (data_string *)s->forwarder->data[j];
char * const nm_slash = strchr(ds->key->ptr, '/');
if (0 != strcasecmp(ds->value->ptr, "trust")) {
if (0 != strcasecmp(ds->value->ptr, "untrusted")) {
log_error_write(srv, __FILE__, __LINE__, "sbsbs", "ERROR: expect \"trust\", not \"", ds->key, "\" => \"", ds->value, "\"; treating as untrusted");
}
if (NULL != nm_slash) {
log_error_write(srv, __FILE__, __LINE__, "sbsbs", "ERROR: untrusted CIDR masks are ignored (\"", ds->key, "\" => \"", ds->value, "\")");
}
buffer_clear(ds->value); /* empty is untrusted */
continue;
}
if (NULL != nm_slash) {
struct sock_addr_mask *sm;
char *err;
const int nm_bits = strtol(nm_slash + 1, &err, 10);
int rc;
if (*err || nm_bits <= 0) {
log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: invalid netmask:", ds->key, err);
return HANDLER_ERROR;
}
if (NULL == s->forward_masks) {
s->forward_masks = calloc(1, sizeof(struct sock_addr_masks));
force_assert(s->forward_masks);
}
if (s->forward_masks->used == s->forward_masks->sz) {
s->forward_masks->sz += 2;
s->forward_masks->addrs = realloc(s->forward_masks->addrs, s->forward_masks->sz * sizeof(struct sock_addr_mask));
force_assert(s->forward_masks->addrs);
}
sm = s->forward_masks->addrs + s->forward_masks->used++;
sm->bits = nm_bits;
*nm_slash = '\0';
rc = sock_addr_from_str_numeric(srv, &sm->addr, ds->key->ptr);
*nm_slash = '/';
if (1 != rc) return HANDLER_ERROR;
buffer_clear(ds->value); /* empty is untrusted, e.g. if subnet (incorrectly) appears in X-Forwarded-For */
}
}
}
if (!array_is_vlist(s->headers)) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for extforward.headers; expected list of \"headername\"");
return HANDLER_ERROR;
}
/* default to "X-Forwarded-For" or "Forwarded-For" if extforward.headers not specified or empty */
if (!s->hap_PROXY && 0 == s->headers->used && (0 == i || NULL != array_get_element(config->value, "extforward.headers"))) {
array_insert_value(s->headers, CONST_STR_LEN("X-Forwarded-For"));
array_insert_value(s->headers, CONST_STR_LEN("Forwarded-For"));
}
[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 (!array_is_kvany(s->opts_params)) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for extforward.params; expected ( \"param\" => \"value\" )");
return HANDLER_ERROR;
}
for (size_t j = 0, used = s->opts_params->used; j < used; ++j) {
proxy_forwarded_t param;
data_unset *du = s->opts_params->data[j];
#if 0 /*("for" and "proto" historical behavior: always enabled)*/
if (buffer_is_equal_string(du->key, CONST_STR_LEN("by"))) {
param = PROXY_FORWARDED_BY;
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("for"))) {
param = PROXY_FORWARDED_FOR;
} else
#endif
if (buffer_is_equal_string(du->key, CONST_STR_LEN("host"))) {
param = PROXY_FORWARDED_HOST;
#if 0
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proto"))) {
param = PROXY_FORWARDED_PROTO;
#endif
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("remote_user"))) {
param = PROXY_FORWARDED_REMOTE_USER;
} else {
log_error_write(srv, __FILE__, __LINE__, "sb",
"extforward.params keys must be one of: host, remote_user, but not:", du->key);
return HANDLER_ERROR;
}
if (du->type == TYPE_STRING) {
data_string *ds = (data_string *)du;
if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) {
s->opts |= param;
} else if (!buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"extforward.params values must be one of: 0, 1, enable, disable; error for key:", du->key);
return HANDLER_ERROR;
}
} else if (du->type == TYPE_INTEGER) {
data_integer *di = (data_integer *)du;
if (di->value) s->opts |= param;
} else {
log_error_write(srv, __FILE__, __LINE__, "sb",
"extforward.params values must be one of: 0, 1, enable, disable; error for key:", du->key);
return HANDLER_ERROR;
}
}
}
/* 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.*/
for (i = 0; i < srv->config_context->used; ++i) {
plugin_config *s = p->config_storage[i];
if (s->hap_PROXY) {
size_t j;
for (j = 0; j < srv->srvconf.modules->used; ++j) {
data_string *ds = (data_string *)srv->srvconf.modules->data[j];
if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_extforward"))) {
break;
}
}
for (; j < srv->srvconf.modules->used; ++j) {
data_string *ds = (data_string *)srv->srvconf.modules->data[j];
if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_openssl"))) {
log_error_write(srv, __FILE__, __LINE__, "s",
"mod_extforward must be loaded after mod_openssl in server.modules when extforward.hap-PROXY = \"enable\"");
break;
}
}
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
for (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;
}
#define PATCH(x) \
p->conf.x = s->x;
static int mod_extforward_patch_connection(server *srv, connection *con, plugin_data *p) {
size_t i, j;
plugin_config *s = p->config_storage[0];
PATCH(forwarder);
PATCH(forward_masks);
PATCH(headers);
[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
PATCH(opts);
PATCH(hap_PROXY);
PATCH(hap_PROXY_ssl_client_verify);
PATCH(forward_all);
/* skip the first, the global context */
for (i = 1; i < srv->config_context->used; i++) {
data_config *dc = (data_config *)srv->config_context->data[i];
s = p->config_storage[i];
/* condition didn't match */
if (!config_check_cond(srv, con, dc)) continue;
/* merge config */
for (j = 0; j < dc->value->used; j++) {
data_unset *du = dc->value->data[j];
if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.forwarder"))) {
PATCH(forwarder);
PATCH(forward_masks);
PATCH(forward_all);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.headers"))) {
PATCH(headers);
[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
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.params"))) {
PATCH(opts);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.hap-PROXY"))) {
PATCH(hap_PROXY);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.hap-PROXY-ssl-client-verify"))) {
PATCH(hap_PROXY_ssl_client_verify);
}
}
}
return 0;
}
#undef PATCH
/*
extract a forward array from the environment
*/
static array *extract_forward_array(buffer *pbuffer)
{
array *result = array_init();
if (!buffer_string_is_empty(pbuffer)) {
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++) {
if (in_str) {
if ((*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':' && (*curr < 'a' || *curr > 'f') && (*curr < 'A' || *curr > 'F')) {
/* 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 ((*curr >= '0' && *curr <= '9') || *curr == ':' || (*curr >= 'a' && *curr <= 'f') || (*curr >= 'A' && *curr <= 'F')) {
/* 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)
{
data_string *ds =
(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) {
const struct sock_addr_mask * const addrs =p->conf.forward_masks->addrs;
const size_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 (size_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(server *srv, connection *con, plugin_data *p, const char *addr) {
sock_addr sock;
handler_ctx *hctx = con->plugin_ctx[p->id];
if (con->conf.log_request_handling) {
log_error_write(srv, __FILE__, __LINE__, "ss", "using address:", addr);
}
sock.plain.sa_family = AF_UNSPEC;
if (1 != sock_addr_from_str_numeric(srv, &sock, addr)) 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 (con->conf.log_request_handling) {
log_error_write(srv, __FILE__, __LINE__, "s",
"-- 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 {
con->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(con, 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 (con->conf.log_request_handling) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"patching con->dst_addr_buf for the accesslog:", addr);
}
/* Now, clean the conf_cond cache, because we may have changed the results of tests */
[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
config_cond_cache_reset_item(srv, con, COMP_HTTP_REMOTE_IP);
return 1;
}
[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 void mod_extforward_set_proto(server *srv, connection *con, const char *proto, size_t protolen) {
if (0 != protolen && !buffer_is_equal_caseless_string(con->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.
* - con->uri.scheme is not reset in mod_extforward_restore() but is currently not an issues since
* con->uri.scheme will be reset by next request. If a new module uses con->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(con, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO"), CONST_BUF_LEN(con->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 (0 == buffer_caseless_compare(proto, protolen, CONST_STR_LEN("https"))) {
buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("https"));
[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
config_cond_cache_reset_item(srv, con, COMP_HTTP_SCHEME);
} else if (0 == buffer_caseless_compare(proto, protolen, CONST_STR_LEN("http"))) {
buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("http"));
[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
config_cond_cache_reset_item(srv, con, COMP_HTTP_SCHEME);
}
}
}
static handler_t mod_extforward_X_Forwarded_For(server *srv, connection *con, plugin_data *p, buffer *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)
*/
buffer *x_forwarded_proto = http_header_request_get(con, HTTP_HEADER_X_FORWARDED_PROTO, CONST_STR_LEN("X-Forwarded-Proto"));
if (mod_extforward_set_addr(srv, con, p, real_remote_addr) && NULL != x_forwarded_proto) {
mod_extforward_set_proto(srv, con, 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 (server *srv, connection *con, plugin_data *p, buffer *forwarded) {
/* 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_write(srv, __FILE__, __LINE__, "s",
"invalid quoted-string in Forwarded header");
con->http_status = 400; /* Bad Request */
con->mode = DIRECT;
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_write(srv, __FILE__, __LINE__, "s",
"invalid quoted-string in Forwarded header");
con->http_status = 400; /* Bad Request */
con->mode = DIRECT;
return HANDLER_FINISHED;
}
vlen = i - v; /* might be 0 */
/* have k, klen, v, vlen
* (might contain quoted string) (contents not validated or decoded)
* (might be repeated k)
*/
if (0 == klen) continue; /* invalid k */
if (j >= (int)(sizeof(offsets)/sizeof(int))-4) break;
offsets[j+1] = k;
offsets[j+2] = klen;
offsets[j+3] = v;
offsets[j+4] = vlen;
j += 4;
}
if (j >= (int)(sizeof(offsets)/sizeof(int))-4) {
/* error processing Forwarded; too many params; fail closed */
log_error_write(srv, __FILE__, __LINE__, "s",
"Too many params in Forwarded header");
con->http_status = 400; /* Bad Request */
con->mode = DIRECT;
return HANDLER_FINISHED;
}
if (-1 == j) return HANDLER_GO_ON; /* make no changes */
used = j+1;
offsets[used] = -1; /* mark end of last set of params */
while (j >= 4) { /*(param=value pairs)*/
[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 (-1 == offsets[j]) { --j; continue; }