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>