File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_userdir.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 10:32:47 2013 UTC (10 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_33, HEAD
1.4.33

    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>