[mod_access] new directive url.access-allow (fixes #1421)

url.access-allow is list of allowed url suffixes (e.g. file extensions)
If url.access-allow has been set, then deny any URL that does not match
the explicitly listed suffixes.

(thx japc)

x-ref:
  "access_allow directive for lighttpd"
  https://redmine.lighttpd.net/issues/1421
personal/stbuehler/mod-csrf-old
Glenn Strauss 7 years ago
parent 5e76b284df
commit ce7d040bf3

@ -11,6 +11,7 @@
#include <string.h>
typedef struct {
array *access_allow;
array *access_deny;
} plugin_config;
@ -44,6 +45,7 @@ FREE_FUNC(mod_access_free) {
if (NULL == s) continue;
array_free(s->access_allow);
array_free(s->access_deny);
free(s);
@ -62,6 +64,7 @@ SETDEFAULTS_FUNC(mod_access_set_defaults) {
config_values_t cv[] = {
{ "url.access-deny", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
{ "url.access-allow", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
@ -73,8 +76,10 @@ SETDEFAULTS_FUNC(mod_access_set_defaults) {
s = calloc(1, sizeof(plugin_config));
s->access_deny = array_init();
s->access_allow = array_init();
cv[0].destination = s->access_deny;
cv[1].destination = s->access_allow;
p->config_storage[i] = s;
@ -92,6 +97,7 @@ static int mod_access_patch_connection(server *srv, connection *con, plugin_data
size_t i, j;
plugin_config *s = p->config_storage[0];
PATCH(access_allow);
PATCH(access_deny);
/* skip the first, the global context */
@ -108,6 +114,8 @@ static int mod_access_patch_connection(server *srv, connection *con, plugin_data
if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-deny"))) {
PATCH(access_deny);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-allow"))) {
PATCH(access_allow);
}
}
}
@ -141,6 +149,43 @@ URIHANDLER_FUNC(mod_access_uri_handler) {
"-- mod_access_uri_handler called");
}
for (k = 0; k < p->conf.access_allow->used; ++k) {
data_string *ds = (data_string *)p->conf.access_allow->data[k];
int ct_len = buffer_string_length(ds->value);
int allowed = 0;
if (ct_len > s_len) continue;
if (buffer_is_empty(ds->value)) continue;
/* if we have a case-insensitive FS we have to lower-case the URI here too */
if (con->conf.force_lowercase_filenames) {
if (0 == strncasecmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
allowed = 1;
}
} else {
if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
allowed = 1;
}
}
if (allowed) {
return HANDLER_GO_ON;
}
}
if (k > 0) { /* have access_allow but none matched */
con->http_status = 403;
con->mode = DIRECT;
if (con->conf.log_request_handling) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"url denied as failed to match any from access_allow", con->uri.path);
}
return HANDLER_FINISHED;
}
for (k = 0; k < p->conf.access_deny->used; k++) {
data_string *ds = (data_string *)p->conf.access_deny->data[k];
int ct_len = buffer_string_length(ds->value);

@ -292,6 +292,11 @@ $HTTP["cookie"] =~ "empty-ref" {
}
}
$HTTP["host"] =~ "allow\.example\.org$" {
url.access-allow = ( ".txt" ) # allow takes precedence over deny
url.access-deny = ( ".txt" )
}
$HTTP["host"] == "etag.example.org" {
static-file.etags = "disable"
compress.filetype = ()

@ -8,7 +8,7 @@ BEGIN {
use strict;
use IO::Socket;
use Test::More tests => 4;
use Test::More tests => 6;
use LightyTest;
my $tf = LightyTest->new();
@ -30,5 +30,21 @@ EOF
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
ok($tf->handle_http($t) == 0, '#1230 - forbid access to ...~ - trailing slash');
$t->{REQUEST} = ( <<EOF
GET /ssi-include.txt HTTP/1.0
Host: allow.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
ok($tf->handle_http($t) == 0, 'explicitly allowed');
$t->{REQUEST} = ( <<EOF
GET /cgi.pl HTTP/1.0
Host: allow.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
ok($tf->handle_http($t) == 0, 'not explicitly allowed');
ok($tf->stop_proc == 0, "Stopping lighttpd");

Loading…
Cancel
Save