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