2009-05-29 13:05:31 +00:00
|
|
|
/*
|
|
|
|
* mod_access - restrict access to the webserver for certain clients
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* mod_access lets you filter clients by IP address.
|
|
|
|
*
|
|
|
|
* Setups:
|
|
|
|
* access.load "file";
|
|
|
|
* - Loads access rules from a file. One rule per line in the format <action> <ip>.
|
|
|
|
* Example file (\n is newline): allow 127.0.0.1\ndeny 10.0.0.0/8\nallow 192.168.0.0/24
|
|
|
|
* Options:
|
|
|
|
* access.redirect_url = "url";
|
|
|
|
* - if set, clients are redirected to this url if access is refused
|
|
|
|
* Actions:
|
|
|
|
* access.deny;
|
|
|
|
* - Denies access by returning a 403 status code
|
|
|
|
* access.check ("allow" => iplist, "deny" => iplist);
|
|
|
|
* - "allow" and "deny" are optional. If left out, they default to "all"
|
|
|
|
* - iplists are lists of strings representing IP addresses with optional CIDR suffix
|
|
|
|
* - To represent all IPs, you can either use "x.x.x.x/0" or "all"
|
|
|
|
*
|
|
|
|
* Example config:
|
|
|
|
* access.redirect_url = "http://www.example.tld/denied.html";
|
|
|
|
* access.check (
|
2009-12-14 13:29:59 +00:00
|
|
|
* "allow" => ("127.0.0.0/24", "192.168.0.0/16", "::1"),
|
|
|
|
* "deny" => ( "all" )
|
2009-05-29 13:05:31 +00:00
|
|
|
* );
|
|
|
|
* if req.path =$ ".inc" { access.deny; }
|
|
|
|
*
|
|
|
|
* This config snippet will grant access only to clients from the local network (127.0.0.* or 192.168.*.*).
|
|
|
|
* Additionally it will deny access to any file ending with ".inc", no matter what client.
|
|
|
|
* Clients that are denied access, will be redirected to http://www.example.tld/denied.html
|
|
|
|
*
|
|
|
|
* Tip:
|
|
|
|
* none
|
|
|
|
*
|
|
|
|
* Todo:
|
|
|
|
* - access.redirect_url
|
|
|
|
*
|
|
|
|
* Author:
|
|
|
|
* Copyright (c) 2009 Thomas Porzelt
|
|
|
|
* License:
|
|
|
|
* MIT, see COPYING file in the lighttpd 2 tree
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <lighttpd/base.h>
|
2009-07-07 16:45:47 +00:00
|
|
|
#include <lighttpd/radix.h>
|
2009-05-29 13:05:31 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
LI_API gboolean mod_access_init(liModules *mods, liModule *mod);
|
|
|
|
LI_API gboolean mod_access_free(liModules *mods, liModule *mod);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
|
|
|
struct access_check_data {
|
2009-07-08 19:06:07 +00:00
|
|
|
liPlugin *p;
|
2009-12-14 13:29:59 +00:00
|
|
|
liRadixTree *ipv4, *ipv6;
|
2009-05-29 13:05:31 +00:00
|
|
|
};
|
|
|
|
typedef struct access_check_data access_check_data;
|
|
|
|
|
2010-01-24 20:30:41 +00:00
|
|
|
enum { ACCESS_DENY = 1, ACCESS_ALLOW = 2 };
|
2009-12-14 13:29:59 +00:00
|
|
|
|
2010-01-24 20:30:41 +00:00
|
|
|
enum {
|
|
|
|
OPTION_LOG_BLOCKED = 0
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
OPTION_REDIRECT_URL = 0
|
|
|
|
};
|
2009-05-29 13:05:31 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
static liHandlerResult access_check(liVRequest *vr, gpointer param, gpointer *context) {
|
2009-05-29 13:05:31 +00:00
|
|
|
access_check_data *acd = param;
|
2010-07-31 12:44:45 +00:00
|
|
|
liSockAddr *addr = vr->coninfo->remote_addr.addr;
|
2010-01-24 20:30:41 +00:00
|
|
|
gboolean log_blocked = _OPTION(vr, acd->p, OPTION_LOG_BLOCKED).boolean;
|
|
|
|
GString *redirect_url = _OPTIONPTR(vr, acd->p, OPTION_REDIRECT_URL).string;
|
2009-05-29 13:05:31 +00:00
|
|
|
|
|
|
|
UNUSED(context);
|
|
|
|
UNUSED(redirect_url);
|
|
|
|
|
|
|
|
if (addr->plain.sa_family == AF_INET) {
|
2009-12-14 13:29:59 +00:00
|
|
|
if (GINT_TO_POINTER(ACCESS_DENY) == li_radixtree_lookup(acd->ipv4, &addr->ipv4.sin_addr.s_addr, 32)) {
|
2009-09-02 19:11:48 +00:00
|
|
|
if (!li_vrequest_handle_direct(vr))
|
|
|
|
return LI_HANDLER_GO_ON;
|
|
|
|
|
2009-05-29 13:05:31 +00:00
|
|
|
vr->response.http_status = 403;
|
|
|
|
|
2009-09-02 19:11:48 +00:00
|
|
|
if (log_blocked)
|
2010-07-31 12:44:45 +00:00
|
|
|
VR_INFO(vr, "access.check: blocked %s", vr->coninfo->remote_addr_str->str);
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
2009-12-14 13:29:59 +00:00
|
|
|
#ifdef HAVE_IPV6
|
2009-05-29 13:05:31 +00:00
|
|
|
} else if (addr->plain.sa_family == AF_INET6) {
|
2009-12-14 13:29:59 +00:00
|
|
|
if (GINT_TO_POINTER(ACCESS_DENY) == li_radixtree_lookup(acd->ipv6, &addr->ipv6.sin6_addr.s6_addr, 128)) {
|
|
|
|
if (!li_vrequest_handle_direct(vr))
|
|
|
|
return LI_HANDLER_GO_ON;
|
|
|
|
|
|
|
|
vr->response.http_status = 403;
|
|
|
|
|
|
|
|
if (log_blocked)
|
2010-07-31 12:44:45 +00:00
|
|
|
VR_INFO(vr, "access.check: blocked %s", vr->coninfo->remote_addr_str->str);
|
2009-12-14 13:29:59 +00:00
|
|
|
}
|
|
|
|
#endif
|
2009-05-29 13:05:31 +00:00
|
|
|
} else {
|
|
|
|
VR_ERROR(vr, "%s", "access.check only supports ipv4 or ipv6 clients");
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_HANDLER_ERROR;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_HANDLER_GO_ON;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
static void access_check_free(liServer *srv, gpointer param) {
|
2009-05-29 13:05:31 +00:00
|
|
|
access_check_data *acd = param;
|
|
|
|
|
|
|
|
UNUSED(srv);
|
|
|
|
|
2009-12-14 13:29:59 +00:00
|
|
|
li_radixtree_free(acd->ipv4, NULL, NULL);
|
|
|
|
li_radixtree_free(acd->ipv6, NULL, NULL);
|
2009-05-29 13:05:31 +00:00
|
|
|
g_slice_free(access_check_data, acd);
|
|
|
|
}
|
|
|
|
|
2010-05-07 18:54:50 +00:00
|
|
|
static liAction* access_check_create(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
|
2009-05-29 13:05:31 +00:00
|
|
|
GArray *arr;
|
2009-07-08 19:06:07 +00:00
|
|
|
liValue *v, *ip;
|
2009-05-29 13:05:31 +00:00
|
|
|
guint i, j;
|
|
|
|
guint32 ipv4, netmaskv4;
|
|
|
|
gboolean deny = FALSE;
|
|
|
|
access_check_data *acd = NULL;
|
|
|
|
|
|
|
|
UNUSED(srv);
|
2010-05-07 18:54:50 +00:00
|
|
|
UNUSED(wrk);
|
2009-12-21 11:29:14 +00:00
|
|
|
UNUSED(userdata);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
if (!val || val->type != LI_VALUE_LIST || (val->data.list->len != 1 && val->data.list->len != 2)) {
|
2009-05-29 13:05:31 +00:00
|
|
|
ERROR(srv, "%s", "access_check expects a list of one or two string,list tuples as parameter");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
arr = val->data.list;
|
|
|
|
|
|
|
|
acd = g_slice_new0(access_check_data);
|
|
|
|
acd->p = p;
|
2010-08-23 14:53:03 +00:00
|
|
|
acd->ipv4 = li_radixtree_new();
|
|
|
|
acd->ipv6 = li_radixtree_new();
|
2009-12-14 13:29:59 +00:00
|
|
|
li_radixtree_insert(acd->ipv4, NULL, 0, GINT_TO_POINTER(ACCESS_DENY));
|
|
|
|
li_radixtree_insert(acd->ipv6, NULL, 0, GINT_TO_POINTER(ACCESS_DENY));
|
2009-05-29 13:05:31 +00:00
|
|
|
|
|
|
|
for (i = 0; i < arr->len; i++) {
|
2009-07-08 19:06:07 +00:00
|
|
|
v = g_array_index(arr, liValue*, i);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
if (v->type != LI_VALUE_LIST || v->data.list->len != 2) {
|
2009-05-29 13:05:31 +00:00
|
|
|
ERROR(srv, "%s", "access_check expects a list of one or two string,list tuples as parameter");
|
2009-12-14 13:29:59 +00:00
|
|
|
goto failed_free_acd;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
v = g_array_index(v->data.list, liValue*, 0);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
if (v->type != LI_VALUE_STRING) {
|
2009-05-29 13:05:31 +00:00
|
|
|
ERROR(srv, "%s", "access_check expects a list of one or two string,list tuples as parameter");
|
2009-12-14 13:29:59 +00:00
|
|
|
goto failed_free_acd;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (g_str_equal(v->data.string->str, "allow")) {
|
|
|
|
deny = FALSE;
|
|
|
|
} else if (g_str_equal(v->data.string->str, "deny")) {
|
|
|
|
deny = TRUE;
|
|
|
|
} else {
|
|
|
|
ERROR(srv, "access_check: invalid option \"%s\"", v->data.string->str);
|
2009-12-14 13:29:59 +00:00
|
|
|
goto failed_free_acd;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
v = g_array_index(g_array_index(arr, liValue*, i)->data.list, liValue*, 1);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
if (v->type != LI_VALUE_LIST) {
|
2009-05-29 13:05:31 +00:00
|
|
|
ERROR(srv, "%s", "access_check expects a list of one or two string,list tuples as parameter");
|
2009-12-14 13:29:59 +00:00
|
|
|
goto failed_free_acd;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < v->data.list->len; j++) {
|
2009-12-14 13:29:59 +00:00
|
|
|
guint8 ipv6_addr[16];
|
|
|
|
guint ipv6_network;
|
2009-07-08 19:06:07 +00:00
|
|
|
ip = g_array_index(v->data.list, liValue*, j);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
if (ip->type != LI_VALUE_STRING) {
|
2009-05-29 13:05:31 +00:00
|
|
|
ERROR(srv, "%s", "access_check expects a list of one or two string,list tuples as parameter");
|
2009-12-14 13:29:59 +00:00
|
|
|
goto failed_free_acd;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (g_str_equal(ip->data.string->str, "all")) {
|
2009-12-14 13:29:59 +00:00
|
|
|
li_radixtree_insert(acd->ipv4, NULL, 0, GINT_TO_POINTER(deny ? ACCESS_DENY : ACCESS_ALLOW));
|
|
|
|
li_radixtree_insert(acd->ipv6, NULL, 0, GINT_TO_POINTER(deny ? ACCESS_DENY : ACCESS_ALLOW));
|
2009-07-09 20:17:24 +00:00
|
|
|
} else if (li_parse_ipv4(ip->data.string->str, &ipv4, &netmaskv4, NULL)) {
|
2009-12-14 13:29:59 +00:00
|
|
|
gint prefixlen;
|
|
|
|
netmaskv4 = ntohl(netmaskv4);
|
|
|
|
prefixlen = 32 - g_bit_nth_lsf(netmaskv4, -1);
|
|
|
|
if (prefixlen < 0 || prefixlen > 32) prefixlen = 0;
|
|
|
|
li_radixtree_insert(acd->ipv4, &ipv4, prefixlen, GINT_TO_POINTER(deny ? ACCESS_DENY : ACCESS_ALLOW));
|
|
|
|
} else if (li_parse_ipv6(ip->data.string->str, ipv6_addr, &ipv6_network, NULL)) {
|
|
|
|
li_radixtree_insert(acd->ipv6, ipv6_addr, ipv6_network, GINT_TO_POINTER(deny ? ACCESS_DENY : ACCESS_ALLOW));
|
2009-05-29 13:05:31 +00:00
|
|
|
} else {
|
|
|
|
ERROR(srv, "access_check: error parsing ip: %s", ip->data.string->str);
|
2009-12-14 13:29:59 +00:00
|
|
|
goto failed_free_acd;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-09 20:17:24 +00:00
|
|
|
return li_action_new_function(access_check, NULL, access_check_free, acd);
|
2009-12-14 13:29:59 +00:00
|
|
|
|
|
|
|
failed_free_acd:
|
|
|
|
li_radixtree_free(acd->ipv4, NULL, NULL);
|
|
|
|
li_radixtree_free(acd->ipv6, NULL, NULL);
|
|
|
|
g_slice_free(access_check_data, acd);
|
|
|
|
return NULL;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
static liHandlerResult access_deny(liVRequest *vr, gpointer param, gpointer *context) {
|
2010-01-24 20:30:41 +00:00
|
|
|
gboolean log_blocked = _OPTION(vr, ((liPlugin*)param), OPTION_LOG_BLOCKED).boolean;
|
|
|
|
GString *redirect_url = _OPTIONPTR(vr, ((liPlugin*)param), OPTION_REDIRECT_URL).string;
|
2009-05-29 13:05:31 +00:00
|
|
|
|
|
|
|
UNUSED(context);
|
|
|
|
UNUSED(redirect_url);
|
|
|
|
|
2009-09-02 19:11:48 +00:00
|
|
|
if (!li_vrequest_handle_direct(vr))
|
|
|
|
return LI_HANDLER_GO_ON;
|
|
|
|
|
2009-05-29 13:05:31 +00:00
|
|
|
vr->response.http_status = 403;
|
|
|
|
|
|
|
|
if (log_blocked) {
|
2010-07-31 12:44:45 +00:00
|
|
|
VR_INFO(vr, "access.deny: blocked %s", vr->coninfo->remote_addr_str->str);
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_HANDLER_GO_ON;
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
2010-05-07 18:54:50 +00:00
|
|
|
static liAction* access_deny_create(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
|
|
|
|
UNUSED(srv); UNUSED(wrk); UNUSED(userdata);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
|
|
|
if (val) {
|
|
|
|
ERROR(srv, "%s", "access.deny doesn't expect any parameters");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-07-09 20:17:24 +00:00
|
|
|
return li_action_new_function(access_deny, NULL, NULL, p);
|
2009-05-29 13:05:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
static const liPluginOption options[] = {
|
2010-01-24 20:30:41 +00:00
|
|
|
{ "access.log_blocked", LI_VALUE_BOOLEAN, 0, NULL },
|
|
|
|
|
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const liPluginOptionPtr optionptrs[] = {
|
2009-07-08 19:06:07 +00:00
|
|
|
{ "access.redirect_url", LI_VALUE_STRING, NULL, NULL, NULL },
|
2009-05-29 13:05:31 +00:00
|
|
|
|
|
|
|
{ NULL, 0, NULL, NULL, NULL }
|
|
|
|
};
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
static const liPluginAction actions[] = {
|
2009-12-21 11:29:14 +00:00
|
|
|
{ "access.check", access_check_create, NULL },
|
|
|
|
{ "access.deny", access_deny_create, NULL },
|
2009-05-29 13:05:31 +00:00
|
|
|
|
2009-12-21 11:29:14 +00:00
|
|
|
{ NULL, NULL, NULL }
|
2009-05-29 13:05:31 +00:00
|
|
|
};
|
|
|
|
|
2009-08-30 17:25:01 +00:00
|
|
|
static const liPluginSetup setups[] = {
|
2009-12-21 11:29:14 +00:00
|
|
|
{ NULL, NULL, NULL }
|
2009-05-29 13:05:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2009-12-21 11:29:14 +00:00
|
|
|
static void plugin_access_init(liServer *srv, liPlugin *p, gpointer userdata) {
|
|
|
|
UNUSED(srv); UNUSED(userdata);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
|
|
|
p->options = options;
|
2010-01-24 20:30:41 +00:00
|
|
|
p->optionptrs = optionptrs;
|
2009-05-29 13:05:31 +00:00
|
|
|
p->actions = actions;
|
|
|
|
p->setups = setups;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
gboolean mod_access_init(liModules *mods, liModule *mod) {
|
2009-05-29 13:05:31 +00:00
|
|
|
UNUSED(mod);
|
|
|
|
|
|
|
|
MODULE_VERSION_CHECK(mods);
|
|
|
|
|
2009-12-21 11:29:14 +00:00
|
|
|
mod->config = li_plugin_register(mods->main, "mod_access", plugin_access_init, NULL);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
|
|
|
return mod->config != NULL;
|
|
|
|
}
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
gboolean mod_access_free(liModules *mods, liModule *mod) {
|
2009-05-29 13:05:31 +00:00
|
|
|
if (mod->config)
|
2009-07-09 20:17:24 +00:00
|
|
|
li_plugin_free(mods->main, mod->config);
|
2009-05-29 13:05:31 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|