xref: /lighttpd1.4/src/mod_access.c (revision 49e9f0ac)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "log.h"
5 #include "buffer.h"
6 
7 #include "plugin.h"
8 
9 #include <stdlib.h>
10 #include <string.h>
11 
12 typedef struct {
13 	array *access_allow;
14 	array *access_deny;
15 } plugin_config;
16 
17 typedef struct {
18 	PLUGIN_DATA;
19 
20 	plugin_config **config_storage;
21 
22 	plugin_config conf;
23 } plugin_data;
24 
25 INIT_FUNC(mod_access_init) {
26 	plugin_data *p;
27 
28 	p = calloc(1, sizeof(*p));
29 
30 	return p;
31 }
32 
33 FREE_FUNC(mod_access_free) {
34 	plugin_data *p = p_d;
35 
36 	UNUSED(srv);
37 
38 	if (!p) return HANDLER_GO_ON;
39 
40 	if (p->config_storage) {
41 		size_t i;
42 		for (i = 0; i < srv->config_context->used; i++) {
43 			plugin_config *s = p->config_storage[i];
44 
45 			if (NULL == s) continue;
46 
47 			array_free(s->access_allow);
48 			array_free(s->access_deny);
49 
50 			free(s);
51 		}
52 		free(p->config_storage);
53 	}
54 
55 	free(p);
56 
57 	return HANDLER_GO_ON;
58 }
59 
60 SETDEFAULTS_FUNC(mod_access_set_defaults) {
61 	plugin_data *p = p_d;
62 	size_t i = 0;
63 
64 	config_values_t cv[] = {
65 		{ "url.access-deny",             NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
66 		{ "url.access-allow",            NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
67 		{ NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
68 	};
69 
70 	p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
71 
72 	for (i = 0; i < srv->config_context->used; i++) {
73 		data_config const* config = (data_config const*)srv->config_context->data[i];
74 		plugin_config *s;
75 
76 		s = calloc(1, sizeof(plugin_config));
77 		s->access_deny    = array_init();
78 		s->access_allow   = array_init();
79 
80 		cv[0].destination = s->access_deny;
81 		cv[1].destination = s->access_allow;
82 
83 		p->config_storage[i] = s;
84 
85 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
86 			return HANDLER_ERROR;
87 		}
88 
89 		if (!array_is_vlist(s->access_deny)) {
90 			log_error_write(srv, __FILE__, __LINE__, "s",
91 					"unexpected value for url.access-deny; expected list of \"suffix\"");
92 			return HANDLER_ERROR;
93 		}
94 
95 		if (!array_is_vlist(s->access_allow)) {
96 			log_error_write(srv, __FILE__, __LINE__, "s",
97 					"unexpected value for url.access-allow; expected list of \"suffix\"");
98 			return HANDLER_ERROR;
99 		}
100 	}
101 
102 	return HANDLER_GO_ON;
103 }
104 
105 #define PATCH(x) \
106 	p->conf.x = s->x;
107 static int mod_access_patch_connection(server *srv, connection *con, plugin_data *p) {
108 	size_t i, j;
109 	plugin_config *s = p->config_storage[0];
110 
111 	PATCH(access_allow);
112 	PATCH(access_deny);
113 
114 	/* skip the first, the global context */
115 	for (i = 1; i < srv->config_context->used; i++) {
116 		data_config *dc = (data_config *)srv->config_context->data[i];
117 		s = p->config_storage[i];
118 
119 		/* condition didn't match */
120 		if (!config_check_cond(srv, con, dc)) continue;
121 
122 		/* merge config */
123 		for (j = 0; j < dc->value->used; j++) {
124 			data_unset *du = dc->value->data[j];
125 
126 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-deny"))) {
127 				PATCH(access_deny);
128 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-allow"))) {
129 				PATCH(access_allow);
130 			}
131 		}
132 	}
133 
134 	return 0;
135 }
136 #undef PATCH
137 
138 static int mod_access_check (const array *allow, const array *deny, const buffer *urlpath, const int lc) {
139 
140     if (allow->used) {
141         const buffer *match = (!lc)
142           ? array_match_value_suffix(allow, urlpath)
143           : array_match_value_suffix_nc(allow, urlpath);
144         return (match != NULL); /* allowed if match; denied if none matched */
145     }
146 
147     if (deny->used) {
148         const buffer *match = (!lc)
149           ? array_match_value_suffix(deny, urlpath)
150           : array_match_value_suffix_nc(deny, urlpath);
151         return (match == NULL); /* deny if match; allow if none matched */
152     }
153 
154     return 1; /* allowed (not denied) */
155 }
156 
157 /**
158  * URI handler
159  *
160  * we will get called twice:
161  * - after the clean up of the URL and
162  * - after the pathinfo checks are done
163  *
164  * this handles the issue of trailing slashes
165  */
166 URIHANDLER_FUNC(mod_access_uri_handler) {
167 	plugin_data *p = p_d;
168 	if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
169 
170 	mod_access_patch_connection(srv, con, p);
171 
172 	if (0 == p->conf.access_allow->used && 0 == p->conf.access_deny->used) {
173 		return HANDLER_GO_ON; /* access allowed; nothing to match */
174 	}
175 
176 	if (con->conf.log_request_handling) {
177 		log_error_write(srv, __FILE__, __LINE__, "s",
178 				"-- mod_access_uri_handler called");
179 	}
180 
181 	if (mod_access_check(p->conf.access_allow, p->conf.access_deny,
182 			     con->uri.path, con->conf.force_lowercase_filenames)) {
183 		return HANDLER_GO_ON; /* access allowed */
184 	}
185 
186 	/* (else) access denied */
187 	if (con->conf.log_request_handling) {
188 		if (p->conf.access_allow->used) {
189 			log_error_write(srv, __FILE__, __LINE__, "sb", "url denied as failed to match any from access_allow", con->uri.path);
190 		}
191 		else {
192 			log_error_write(srv, __FILE__, __LINE__, "sb", "url denied as we match access_deny", con->uri.path);
193 		}
194 	}
195 
196 	con->http_status = 403;
197 	con->mode = DIRECT;
198 	return HANDLER_FINISHED;
199 }
200 
201 
202 int mod_access_plugin_init(plugin *p);
203 int mod_access_plugin_init(plugin *p) {
204 	p->version     = LIGHTTPD_VERSION_ID;
205 	p->name        = buffer_init_string("access");
206 
207 	p->init        = mod_access_init;
208 	p->set_defaults = mod_access_set_defaults;
209 	p->handle_uri_clean = mod_access_uri_handler;
210 	p->handle_subrequest_start  = mod_access_uri_handler;
211 	p->cleanup     = mod_access_free;
212 
213 	p->data        = NULL;
214 
215 	return 0;
216 }
217