Annotation of embedaddon/lighttpd/src/mod_userdir.c, revision 1.1.1.3
1.1.1.3 ! misho 1: #include "first.h"
! 2:
1.1 misho 3: #include "base.h"
4: #include "log.h"
5: #include "buffer.h"
6:
7: #include "response.h"
8:
9: #include "plugin.h"
10:
11: #include <sys/types.h>
12:
13: #include <stdlib.h>
14: #include <string.h>
15:
16: #ifdef HAVE_PWD_H
17: # include <pwd.h>
18: #endif
19:
20: /* plugin config for all request/connections */
21: typedef struct {
22: array *exclude_user;
23: array *include_user;
24: buffer *path;
25: buffer *basepath;
26: unsigned short letterhomes;
27: unsigned short active;
28: } plugin_config;
29:
30: typedef struct {
31: PLUGIN_DATA;
32:
33: buffer *username;
34: buffer *temp_path;
35:
36: plugin_config **config_storage;
37:
38: plugin_config conf;
39: } plugin_data;
40:
41: /* init the plugin data */
42: INIT_FUNC(mod_userdir_init) {
43: plugin_data *p;
44:
45: p = calloc(1, sizeof(*p));
46:
47: p->username = buffer_init();
48: p->temp_path = buffer_init();
49:
50: return p;
51: }
52:
53: /* detroy the plugin data */
54: FREE_FUNC(mod_userdir_free) {
55: plugin_data *p = p_d;
56:
57: if (!p) return HANDLER_GO_ON;
58:
59: if (p->config_storage) {
60: size_t i;
61:
62: for (i = 0; i < srv->config_context->used; i++) {
63: plugin_config *s = p->config_storage[i];
64:
1.1.1.3 ! misho 65: if (NULL == s) continue;
! 66:
1.1 misho 67: array_free(s->include_user);
68: array_free(s->exclude_user);
69: buffer_free(s->path);
70: buffer_free(s->basepath);
71:
72: free(s);
73: }
74: free(p->config_storage);
75: }
76:
77: buffer_free(p->username);
78: buffer_free(p->temp_path);
79:
80: free(p);
81:
82: return HANDLER_GO_ON;
83: }
84:
85: /* handle plugin config and check values */
86:
87: SETDEFAULTS_FUNC(mod_userdir_set_defaults) {
88: plugin_data *p = p_d;
89: size_t i;
90:
91: config_values_t cv[] = {
92: { "userdir.path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
93: { "userdir.exclude-user", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
94: { "userdir.include-user", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
95: { "userdir.basepath", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
96: { "userdir.letterhomes", NULL, T_CONFIG_BOOLEAN,T_CONFIG_SCOPE_CONNECTION }, /* 4 */
97: { "userdir.active", NULL, T_CONFIG_BOOLEAN,T_CONFIG_SCOPE_CONNECTION }, /* 5 */
98: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
99: };
100:
101: if (!p) return HANDLER_ERROR;
102:
1.1.1.2 misho 103: p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1 misho 104:
105: for (i = 0; i < srv->config_context->used; i++) {
1.1.1.3 ! misho 106: data_config const* config = (data_config const*)srv->config_context->data[i];
1.1 misho 107: plugin_config *s;
108:
109: s = calloc(1, sizeof(plugin_config));
110: s->exclude_user = array_init();
111: s->include_user = array_init();
112: s->path = buffer_init();
113: s->basepath = buffer_init();
114: s->letterhomes = 0;
115: /* enabled by default for backward compatibility; if userdir.path isn't set userdir is disabled too,
116: * but you can't disable it by setting it to an empty string. */
117: s->active = 1;
118:
119: cv[0].destination = s->path;
120: cv[1].destination = s->exclude_user;
121: cv[2].destination = s->include_user;
122: cv[3].destination = s->basepath;
123: cv[4].destination = &(s->letterhomes);
124: cv[5].destination = &(s->active);
125:
126: p->config_storage[i] = s;
127:
1.1.1.3 ! misho 128: if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1.1 misho 129: return HANDLER_ERROR;
130: }
131: }
132:
133: return HANDLER_GO_ON;
134: }
135:
136: #define PATCH(x) \
137: p->conf.x = s->x;
138: static int mod_userdir_patch_connection(server *srv, connection *con, plugin_data *p) {
139: size_t i, j;
140: plugin_config *s = p->config_storage[0];
141:
142: PATCH(path);
143: PATCH(exclude_user);
144: PATCH(include_user);
145: PATCH(basepath);
146: PATCH(letterhomes);
147: PATCH(active);
148:
149: /* skip the first, the global context */
150: for (i = 1; i < srv->config_context->used; i++) {
151: data_config *dc = (data_config *)srv->config_context->data[i];
152: s = p->config_storage[i];
153:
154: /* condition didn't match */
155: if (!config_check_cond(srv, con, dc)) continue;
156:
157: /* merge config */
158: for (j = 0; j < dc->value->used; j++) {
159: data_unset *du = dc->value->data[j];
160:
161: if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.path"))) {
162: PATCH(path);
163: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.exclude-user"))) {
164: PATCH(exclude_user);
165: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.include-user"))) {
166: PATCH(include_user);
167: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.basepath"))) {
168: PATCH(basepath);
169: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.letterhomes"))) {
170: PATCH(letterhomes);
171: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.active"))) {
172: PATCH(active);
173: }
174: }
175: }
176:
177: return 0;
178: }
179: #undef PATCH
180:
181: URIHANDLER_FUNC(mod_userdir_docroot_handler) {
182: plugin_data *p = p_d;
183: size_t k;
184: char *rel_url;
185: #ifdef HAVE_PWD_H
186: struct passwd *pwd = NULL;
187: #endif
188:
1.1.1.3 ! misho 189: if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
1.1 misho 190:
191: mod_userdir_patch_connection(srv, con, p);
192:
193: /* enforce the userdir.path to be set in the config, ugly fix for #1587;
194: * should be replaced with a clean .enabled option in 1.5
195: */
1.1.1.3 ! misho 196: if (!p->conf.active || buffer_is_empty(p->conf.path)) return HANDLER_GO_ON;
1.1 misho 197:
198: /* /~user/foo.html -> /home/user/public_html/foo.html */
199:
200: if (con->uri.path->ptr[0] != '/' ||
201: con->uri.path->ptr[1] != '~') return HANDLER_GO_ON;
202:
203: if (NULL == (rel_url = strchr(con->uri.path->ptr + 2, '/'))) {
204: /* / is missing -> redirect to .../ as we are a user - DIRECTORY ! :) */
205: http_response_redirect_to_directory(srv, con);
206:
207: return HANDLER_FINISHED;
208: }
209:
210: /* /~/ is a empty username, catch it directly */
211: if (0 == rel_url - (con->uri.path->ptr + 2)) {
212: return HANDLER_GO_ON;
213: }
214:
215: buffer_copy_string_len(p->username, con->uri.path->ptr + 2, rel_url - (con->uri.path->ptr + 2));
216:
1.1.1.3 ! misho 217: if (buffer_string_is_empty(p->conf.basepath)
1.1 misho 218: #ifdef HAVE_PWD_H
219: && NULL == (pwd = getpwnam(p->username->ptr))
220: #endif
221: ) {
222: /* user not found */
223: return HANDLER_GO_ON;
224: }
225:
226:
227: for (k = 0; k < p->conf.exclude_user->used; k++) {
228: data_string *ds = (data_string *)p->conf.exclude_user->data[k];
229:
230: if (buffer_is_equal(ds->value, p->username)) {
231: /* user in exclude list */
232: return HANDLER_GO_ON;
233: }
234: }
235:
236: if (p->conf.include_user->used) {
237: int found_user = 0;
238: for (k = 0; k < p->conf.include_user->used; k++) {
239: data_string *ds = (data_string *)p->conf.include_user->data[k];
240:
241: if (buffer_is_equal(ds->value, p->username)) {
242: /* user in include list */
243: found_user = 1;
244: break;
245: }
246: }
247:
248: if (!found_user) return HANDLER_GO_ON;
249: }
250:
251: /* we build the physical path */
252:
1.1.1.3 ! misho 253: if (buffer_string_is_empty(p->conf.basepath)) {
1.1 misho 254: #ifdef HAVE_PWD_H
255: buffer_copy_string(p->temp_path, pwd->pw_dir);
256: #endif
257: } else {
258: char *cp;
259: /* check if the username is valid
260: * a request for /~../ should lead to a directory traversal
261: * limiting to [-_a-z0-9.] should fix it */
262:
263: for (cp = p->username->ptr; *cp; cp++) {
264: char c = *cp;
265:
266: if (!(c == '-' ||
267: c == '_' ||
268: c == '.' ||
269: (c >= 'a' && c <= 'z') ||
270: (c >= 'A' && c <= 'Z') ||
271: (c >= '0' && c <= '9'))) {
272:
273: return HANDLER_GO_ON;
274: }
275: }
276: if (con->conf.force_lowercase_filenames) {
277: buffer_to_lower(p->username);
278: }
279:
1.1.1.3 ! misho 280: buffer_copy_buffer(p->temp_path, p->conf.basepath);
! 281: buffer_append_slash(p->temp_path);
1.1 misho 282: if (p->conf.letterhomes) {
283: buffer_append_string_len(p->temp_path, p->username->ptr, 1);
1.1.1.3 ! misho 284: buffer_append_slash(p->temp_path);
1.1 misho 285: }
286: buffer_append_string_buffer(p->temp_path, p->username);
287: }
1.1.1.3 ! misho 288: buffer_append_slash(p->temp_path);
1.1 misho 289: buffer_append_string_buffer(p->temp_path, p->conf.path);
290:
1.1.1.3 ! misho 291: if (buffer_string_is_empty(p->conf.basepath)) {
1.1 misho 292: struct stat st;
293: int ret;
294:
295: ret = stat(p->temp_path->ptr, &st);
296: if (ret < 0 || S_ISDIR(st.st_mode) != 1) {
297: return HANDLER_GO_ON;
298: }
299: }
300:
1.1.1.3 ! misho 301: buffer_copy_buffer(con->physical.basedir, p->temp_path);
1.1.1.2 misho 302:
1.1 misho 303: /* the physical rel_path is basically the same as uri.path;
304: * but it is converted to lowercase in case of force_lowercase_filenames and some special handling
305: * for trailing '.', ' ' and '/' on windows
306: * we assume that no docroot/physical handler changed this
307: * (docroot should only set the docroot/server name, phyiscal should only change the phyiscal.path;
1.1.1.3 ! misho 308: * the exception mod_secdownload doesn't work with userdir anyway)
1.1 misho 309: */
1.1.1.3 ! misho 310: buffer_append_slash(p->temp_path);
1.1 misho 311: /* if no second '/' is found, we assume that it was stripped from the uri.path for the special handling
312: * on windows.
313: * we do not care about the trailing slash here on windows, as we already ensured it is a directory
314: *
315: * TODO: what to do with trailing dots in usernames on windows? they may result in the same directory
316: * as a username without them.
317: */
318: if (NULL != (rel_url = strchr(con->physical.rel_path->ptr + 2, '/'))) {
319: buffer_append_string(p->temp_path, rel_url + 1); /* skip the / */
320: }
1.1.1.3 ! misho 321: buffer_copy_buffer(con->physical.path, p->temp_path);
1.1 misho 322:
323: buffer_reset(p->temp_path);
324:
325: return HANDLER_GO_ON;
326: }
327:
328: /* this function is called at dlopen() time and inits the callbacks */
329:
330: int mod_userdir_plugin_init(plugin *p);
331: int mod_userdir_plugin_init(plugin *p) {
332: p->version = LIGHTTPD_VERSION_ID;
333: p->name = buffer_init_string("userdir");
334:
335: p->init = mod_userdir_init;
336: p->handle_physical = mod_userdir_docroot_handler;
337: p->set_defaults = mod_userdir_set_defaults;
338: p->cleanup = mod_userdir_free;
339:
340: p->data = NULL;
341:
342: return 0;
343: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>