[core] avoid spurious trace and error abort
[lighttpd.git] / src / mod_access.c
blobc450e76c49a1192140af948fc01feaeb59962100
1 #include "first.h"
3 #include "base.h"
4 #include "log.h"
5 #include "buffer.h"
7 #include "plugin.h"
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <string.h>
13 typedef struct {
14 array *access_allow;
15 array *access_deny;
16 } plugin_config;
18 typedef struct {
19 PLUGIN_DATA;
21 plugin_config **config_storage;
23 plugin_config conf;
24 } plugin_data;
26 INIT_FUNC(mod_access_init) {
27 plugin_data *p;
29 p = calloc(1, sizeof(*p));
31 return p;
34 FREE_FUNC(mod_access_free) {
35 plugin_data *p = p_d;
37 UNUSED(srv);
39 if (!p) return HANDLER_GO_ON;
41 if (p->config_storage) {
42 size_t i;
43 for (i = 0; i < srv->config_context->used; i++) {
44 plugin_config *s = p->config_storage[i];
46 if (NULL == s) continue;
48 array_free(s->access_allow);
49 array_free(s->access_deny);
51 free(s);
53 free(p->config_storage);
56 free(p);
58 return HANDLER_GO_ON;
61 SETDEFAULTS_FUNC(mod_access_set_defaults) {
62 plugin_data *p = p_d;
63 size_t i = 0;
65 config_values_t cv[] = {
66 { "url.access-deny", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
67 { "url.access-allow", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
68 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
71 p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
73 for (i = 0; i < srv->config_context->used; i++) {
74 data_config const* config = (data_config const*)srv->config_context->data[i];
75 plugin_config *s;
77 s = calloc(1, sizeof(plugin_config));
78 s->access_deny = array_init();
79 s->access_allow = array_init();
81 cv[0].destination = s->access_deny;
82 cv[1].destination = s->access_allow;
84 p->config_storage[i] = s;
86 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
87 return HANDLER_ERROR;
91 return HANDLER_GO_ON;
94 #define PATCH(x) \
95 p->conf.x = s->x;
96 static int mod_access_patch_connection(server *srv, connection *con, plugin_data *p) {
97 size_t i, j;
98 plugin_config *s = p->config_storage[0];
100 PATCH(access_allow);
101 PATCH(access_deny);
103 /* skip the first, the global context */
104 for (i = 1; i < srv->config_context->used; i++) {
105 data_config *dc = (data_config *)srv->config_context->data[i];
106 s = p->config_storage[i];
108 /* condition didn't match */
109 if (!config_check_cond(srv, con, dc)) continue;
111 /* merge config */
112 for (j = 0; j < dc->value->used; j++) {
113 data_unset *du = dc->value->data[j];
115 if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-deny"))) {
116 PATCH(access_deny);
117 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-allow"))) {
118 PATCH(access_allow);
123 return 0;
125 #undef PATCH
128 * URI handler
130 * we will get called twice:
131 * - after the clean up of the URL and
132 * - after the pathinfo checks are done
134 * this handles the issue of trailing slashes
136 URIHANDLER_FUNC(mod_access_uri_handler) {
137 plugin_data *p = p_d;
138 int s_len;
139 size_t k;
141 if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
143 mod_access_patch_connection(srv, con, p);
145 s_len = buffer_string_length(con->uri.path);
147 if (con->conf.log_request_handling) {
148 log_error_write(srv, __FILE__, __LINE__, "s",
149 "-- mod_access_uri_handler called");
152 for (k = 0; k < p->conf.access_allow->used; ++k) {
153 data_string *ds = (data_string *)p->conf.access_allow->data[k];
154 int ct_len = buffer_string_length(ds->value);
155 int allowed = 0;
157 if (ct_len > s_len) continue;
158 if (buffer_is_empty(ds->value)) continue;
160 /* if we have a case-insensitive FS we have to lower-case the URI here too */
162 if (con->conf.force_lowercase_filenames) {
163 if (0 == strncasecmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
164 allowed = 1;
166 } else {
167 if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
168 allowed = 1;
172 if (allowed) {
173 return HANDLER_GO_ON;
177 if (k > 0) { /* have access_allow but none matched */
178 con->http_status = 403;
179 con->mode = DIRECT;
181 if (con->conf.log_request_handling) {
182 log_error_write(srv, __FILE__, __LINE__, "sb",
183 "url denied as failed to match any from access_allow", con->uri.path);
186 return HANDLER_FINISHED;
189 for (k = 0; k < p->conf.access_deny->used; k++) {
190 data_string *ds = (data_string *)p->conf.access_deny->data[k];
191 int ct_len = buffer_string_length(ds->value);
192 int denied = 0;
195 if (ct_len > s_len) continue;
196 if (buffer_is_empty(ds->value)) continue;
198 /* if we have a case-insensitive FS we have to lower-case the URI here too */
200 if (con->conf.force_lowercase_filenames) {
201 if (0 == strncasecmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
202 denied = 1;
204 } else {
205 if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
206 denied = 1;
210 if (denied) {
211 con->http_status = 403;
212 con->mode = DIRECT;
214 if (con->conf.log_request_handling) {
215 log_error_write(srv, __FILE__, __LINE__, "sb",
216 "url denied as we match:", ds->value);
219 return HANDLER_FINISHED;
223 /* not found */
224 return HANDLER_GO_ON;
228 int mod_access_plugin_init(plugin *p);
229 int mod_access_plugin_init(plugin *p) {
230 p->version = LIGHTTPD_VERSION_ID;
231 p->name = buffer_init_string("access");
233 p->init = mod_access_init;
234 p->set_defaults = mod_access_set_defaults;
235 p->handle_uri_clean = mod_access_uri_handler;
236 p->handle_subrequest_start = mod_access_uri_handler;
237 p->cleanup = mod_access_free;
239 p->data = NULL;
241 return 0;