[mod_extforward] CIDR support for trusted proxies (fixes #2860)
x-ref: "RFE: mod_extforward CIDR support" https://redmine.lighttpd.net/issues/2860personal/stbuehler/fix-fdevent
parent
cd2b51cb1a
commit
fc7edb3946
|
@ -73,13 +73,26 @@ typedef enum {
|
|||
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;
|
||||
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 {
|
||||
|
@ -148,6 +161,11 @@ FREE_FUNC(mod_extforward_free) {
|
|||
array_free(s->headers);
|
||||
array_free(s->opts_params);
|
||||
|
||||
if (s->forward_masks) {
|
||||
free(s->forward_masks->addrs);
|
||||
free(s->forward_masks);
|
||||
}
|
||||
|
||||
free(s);
|
||||
}
|
||||
free(p->config_storage);
|
||||
|
@ -206,6 +224,39 @@ SETDEFAULTS_FUNC(mod_extforward_set_defaults) {
|
|||
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 (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!array_is_vlist(s->headers)) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "s",
|
||||
"unexpected value for extforward.headers; expected list of \"headername\"");
|
||||
|
@ -318,10 +369,12 @@ static int mod_extforward_patch_connection(server *srv, connection *con, plugin_
|
|||
plugin_config *s = p->config_storage[0];
|
||||
|
||||
PATCH(forwarder);
|
||||
PATCH(forward_masks);
|
||||
PATCH(headers);
|
||||
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++) {
|
||||
|
@ -337,6 +390,8 @@ static int mod_extforward_patch_connection(server *srv, connection *con, plugin_
|
|||
|
||||
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);
|
||||
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.params"))) {
|
||||
|
@ -397,24 +452,40 @@ static array *extract_forward_array(buffer *pbuffer)
|
|||
return result;
|
||||
}
|
||||
|
||||
#define IP_TRUSTED 1
|
||||
#define IP_UNTRUSTED 0
|
||||
/*
|
||||
* check whether ip is trusted, return 1 for trusted , 0 for untrusted
|
||||
*/
|
||||
static int is_proxy_trusted(const buffer *ipstr, plugin_data *p)
|
||||
static int is_proxy_trusted(plugin_data *p, const char * const ip, size_t iplen)
|
||||
{
|
||||
data_string* allds = (data_string *)array_get_element(p->conf.forwarder, "all");
|
||||
if (NULL != array_get_element_klen(p->conf.forwarder, ip, iplen))
|
||||
return 1;
|
||||
|
||||
if (allds) {
|
||||
if (strcasecmp(allds->value->ptr, "trust") == 0) {
|
||||
return IP_TRUSTED;
|
||||
} else {
|
||||
return IP_UNTRUSTED;
|
||||
}
|
||||
}
|
||||
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';
|
||||
|
||||
return (data_string *)array_get_element_klen(p->conf.forwarder, CONST_BUF_LEN(ipstr)) ? IP_TRUSTED : IP_UNTRUSTED;
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -423,12 +494,11 @@ static int is_proxy_trusted(const buffer *ipstr, plugin_data *p)
|
|||
*/
|
||||
static const char *last_not_in_array(array *a, plugin_data *p)
|
||||
{
|
||||
array *forwarder = p->conf.forwarder;
|
||||
int i;
|
||||
|
||||
for (i = a->used - 1; i >= 0; i--) {
|
||||
data_string *ds = (data_string *)a->data[i];
|
||||
if (!array_get_element_klen(forwarder, CONST_BUF_LEN(ds->value))) {
|
||||
if (!is_proxy_trusted(p, CONST_BUF_LEN(ds->value))) {
|
||||
return ds->value->ptr;
|
||||
}
|
||||
}
|
||||
|
@ -706,7 +776,7 @@ static handler_t mod_extforward_Forwarded (server *srv, connection *con, plugin_
|
|||
* attempted by this module. */
|
||||
|
||||
if (v != vlen) {
|
||||
int trusted = (NULL != array_get_element_klen(p->conf.forwarder, s+v, vlen-v));
|
||||
int trusted = is_proxy_trusted(p, s+v, vlen-v);
|
||||
|
||||
if (s[v] != '_' && s[v] != '/'
|
||||
&& (7 != (vlen - v) || 0 != memcmp(s+v, "unknown", 7))) {
|
||||
|
@ -980,7 +1050,7 @@ URIHANDLER_FUNC(mod_extforward_uri_handler) {
|
|||
}
|
||||
|
||||
/* if the remote ip itself is not trusted, then do nothing */
|
||||
if (IP_UNTRUSTED == is_proxy_trusted(con->dst_addr_buf, p)) {
|
||||
if (!is_connection_trusted(con, p)) {
|
||||
if (con->conf.log_request_handling) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sbs",
|
||||
"remote address", con->dst_addr_buf, "is NOT a trusted proxy, skipping");
|
||||
|
@ -1074,7 +1144,7 @@ CONNECTION_FUNC(mod_extforward_handle_con_accept)
|
|||
plugin_data *p = p_d;
|
||||
mod_extforward_patch_connection(srv, con, p);
|
||||
if (!p->conf.hap_PROXY) return HANDLER_GO_ON;
|
||||
if (IP_TRUSTED == is_proxy_trusted(con->dst_addr_buf, p)) {
|
||||
if (is_connection_trusted(con, p)) {
|
||||
handler_ctx *hctx = handler_ctx_init();
|
||||
con->plugin_ctx[p->id] = hctx;
|
||||
hctx->saved_network_read = con->network_read;
|
||||
|
|
Loading…
Reference in New Issue