~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/mappers/mod_userdir.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
 
2
 * contributor license agreements.  See the NOTICE file distributed with
 
3
 * this work for additional information regarding copyright ownership.
 
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
 
5
 * (the "License"); you may not use this file except in compliance with
 
6
 * the License.  You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
/*
 
18
 * mod_userdir... implement the UserDir command.  Broken away from the
 
19
 * Alias stuff for a couple of good and not-so-good reasons:
 
20
 *
 
21
 * 1) It shows a real minimal working example of how to do something like
 
22
 *    this.
 
23
 * 2) I know people who are actually interested in changing this *particular*
 
24
 *    aspect of server functionality without changing the rest of it.  That's
 
25
 *    what this whole modular arrangement is supposed to be good at...
 
26
 *
 
27
 * Modified by Alexei Kosut to support the following constructs
 
28
 * (server running at www.foo.com, request for /~bar/one/two.html)
 
29
 *
 
30
 * UserDir public_html      -> ~bar/public_html/one/two.html
 
31
 * UserDir /usr/web         -> /usr/web/bar/one/two.html
 
32
 * UserDir /home/ * /www     -> /home/bar/www/one/two.html
 
33
 *  NOTE: theses ^ ^ space only added allow it to work in a comment, ignore
 
34
 * UserDir http://x/users   -> (302) http://x/users/bar/one/two.html
 
35
 * UserDir http://x/ * /y     -> (302) http://x/bar/y/one/two.html
 
36
 *  NOTE: here also ^ ^
 
37
 *
 
38
 * In addition, you can use multiple entries, to specify alternate
 
39
 * user directories (a la Directory Index). For example:
 
40
 *
 
41
 * UserDir public_html /usr/web http://www.xyz.com/users
 
42
 *
 
43
 * Modified by Ken Coar to provide for the following:
 
44
 *
 
45
 * UserDir disable[d] username ...
 
46
 * UserDir enable[d] username ...
 
47
 *
 
48
 * If "disabled" has no other arguments, *all* ~<username> references are
 
49
 * disabled, except those explicitly turned on with the "enabled" keyword.
 
50
 */
 
51
 
 
52
#include "apr_strings.h"
 
53
#include "apr_user.h"
 
54
 
 
55
#define APR_WANT_STRFUNC
 
56
#include "apr_want.h"
 
57
 
 
58
#if APR_HAVE_UNISTD_H
 
59
#include <unistd.h>
 
60
#endif
 
61
 
 
62
#include "ap_config.h"
 
63
#include "httpd.h"
 
64
#include "http_config.h"
 
65
#include "http_request.h"
 
66
 
 
67
#if !defined(WIN32) && !defined(OS2) && !defined(BEOS) && !defined(NETWARE)
 
68
#define HAVE_UNIX_SUEXEC
 
69
#endif
 
70
 
 
71
#ifdef HAVE_UNIX_SUEXEC
 
72
#include "unixd.h"        /* Contains the suexec_identity hook used on Unix */
 
73
#endif
 
74
 
 
75
 
 
76
/*
 
77
 * The default directory in user's home dir
 
78
 * In the default install, the module is disabled
 
79
 */
 
80
#ifndef DEFAULT_USER_DIR
 
81
#define DEFAULT_USER_DIR NULL
 
82
#endif
 
83
 
 
84
module AP_MODULE_DECLARE_DATA userdir_module;
 
85
 
 
86
typedef struct {
 
87
    int globally_disabled;
 
88
    char *userdir;
 
89
    apr_table_t *enabled_users;
 
90
    apr_table_t *disabled_users;
 
91
} userdir_config;
 
92
 
 
93
/*
 
94
 * Server config for this module: global disablement flag, a list of usernames
 
95
 * ineligible for UserDir access, a list of those immune to global (but not
 
96
 * explicit) disablement, and the replacement string for all others.
 
97
 */
 
98
 
 
99
static void *create_userdir_config(apr_pool_t *p, server_rec *s)
 
100
{
 
101
    userdir_config *newcfg = apr_pcalloc(p, sizeof(*newcfg));
 
102
 
 
103
    newcfg->globally_disabled = 0;
 
104
    newcfg->userdir = DEFAULT_USER_DIR;
 
105
    newcfg->enabled_users = apr_table_make(p, 4);
 
106
    newcfg->disabled_users = apr_table_make(p, 4);
 
107
 
 
108
    return newcfg;
 
109
}
 
110
 
 
111
#define O_DEFAULT 0
 
112
#define O_ENABLE 1
 
113
#define O_DISABLE 2
 
114
 
 
115
static const char *set_user_dir(cmd_parms *cmd, void *dummy, const char *arg)
 
116
{
 
117
    userdir_config *s_cfg = ap_get_module_config(cmd->server->module_config,
 
118
                                                 &userdir_module);
 
119
    char *username;
 
120
    const char *usernames = arg;
 
121
    char *kw = ap_getword_conf(cmd->pool, &usernames);
 
122
    apr_table_t *usertable;
 
123
 
 
124
    /* Since we are a raw argument, it is possible for us to be called with
 
125
     * zero arguments.  So that we aren't ambiguous, flat out reject this.
 
126
     */
 
127
    if (*kw == '\0') {
 
128
        return "UserDir requires an argument.";
 
129
    }
 
130
 
 
131
    /*
 
132
     * Let's do the comparisons once.
 
133
     */
 
134
    if ((!strcasecmp(kw, "disable")) || (!strcasecmp(kw, "disabled"))) {
 
135
        /*
 
136
         * If there are no usernames specified, this is a global disable - we
 
137
         * need do no more at this point than record the fact.
 
138
         */
 
139
        if (strlen(usernames) == 0) {
 
140
            s_cfg->globally_disabled = 1;
 
141
            return NULL;
 
142
        }
 
143
        usertable = s_cfg->disabled_users;
 
144
    }
 
145
    else if ((!strcasecmp(kw, "enable")) || (!strcasecmp(kw, "enabled"))) {
 
146
        /*
 
147
         * The "disable" keyword can stand alone or take a list of names, but
 
148
         * the "enable" keyword requires the list.  Whinge if it doesn't have
 
149
         * it.
 
150
         */
 
151
        if (strlen(usernames) == 0) {
 
152
            return "UserDir \"enable\" keyword requires a list of usernames";
 
153
        }
 
154
        usertable = s_cfg->enabled_users;
 
155
    }
 
156
    else {
 
157
        /*
 
158
         * If the first (only?) value isn't one of our keywords, just copy
 
159
         * the string to the userdir string.
 
160
         */
 
161
        s_cfg->userdir = apr_pstrdup(cmd->pool, arg);
 
162
        return NULL;
 
163
    }
 
164
    /*
 
165
     * Now we just take each word in turn from the command line and add it to
 
166
     * the appropriate table.
 
167
     */
 
168
    while (*usernames) {
 
169
        username = ap_getword_conf(cmd->pool, &usernames);
 
170
        apr_table_setn(usertable, username, kw);
 
171
    }
 
172
    return NULL;
 
173
}
 
174
 
 
175
static const command_rec userdir_cmds[] = {
 
176
    AP_INIT_RAW_ARGS("UserDir", set_user_dir, NULL, RSRC_CONF,
 
177
                     "the public subdirectory in users' home directories, or "
 
178
                     "'disabled', or 'disabled username username...', or "
 
179
                     "'enabled username username...'"),
 
180
    {NULL}
 
181
};
 
182
 
 
183
static int translate_userdir(request_rec *r)
 
184
{
 
185
    ap_conf_vector_t *server_conf;
 
186
    const userdir_config *s_cfg;
 
187
    char *name = r->uri;
 
188
    const char *userdirs;
 
189
    const char *w, *dname;
 
190
    char *redirect;
 
191
    apr_finfo_t statbuf;
 
192
 
 
193
    /*
 
194
     * If the URI doesn't match our basic pattern, we've nothing to do with
 
195
     * it.
 
196
     */
 
197
    if (name[0] != '/' || name[1] != '~') {
 
198
        return DECLINED;
 
199
    }
 
200
    server_conf = r->server->module_config;
 
201
    s_cfg = ap_get_module_config(server_conf, &userdir_module);
 
202
    userdirs = s_cfg->userdir;
 
203
    if (userdirs == NULL) {
 
204
        return DECLINED;
 
205
    }
 
206
 
 
207
    dname = name + 2;
 
208
    w = ap_getword(r->pool, &dname, '/');
 
209
 
 
210
    /*
 
211
     * The 'dname' funny business involves backing it up to capture the '/'
 
212
     * delimiting the "/~user" part from the rest of the URL, in case there
 
213
     * was one (the case where there wasn't being just "GET /~user HTTP/1.0",
 
214
     * for which we don't want to tack on a '/' onto the filename).
 
215
     */
 
216
 
 
217
    if (dname[-1] == '/') {
 
218
        --dname;
 
219
    }
 
220
 
 
221
    /*
 
222
     * If there's no username, it's not for us.  Ignore . and .. as well.
 
223
     */
 
224
    if (w[0] == '\0' || (w[1] == '.' && (w[2] == '\0' || (w[2] == '.' && w[3] == '\0')))) {
 
225
        return DECLINED;
 
226
    }
 
227
    /*
 
228
     * Nor if there's an username but it's in the disabled list.
 
229
     */
 
230
    if (apr_table_get(s_cfg->disabled_users, w) != NULL) {
 
231
        return DECLINED;
 
232
    }
 
233
    /*
 
234
     * If there's a global interdiction on UserDirs, check to see if this
 
235
     * name is one of the Blessed.
 
236
     */
 
237
    if (s_cfg->globally_disabled
 
238
        && apr_table_get(s_cfg->enabled_users, w) == NULL) {
 
239
        return DECLINED;
 
240
    }
 
241
 
 
242
    /*
 
243
     * Special cases all checked, onward to normal substitution processing.
 
244
     */
 
245
 
 
246
    while (*userdirs) {
 
247
        const char *userdir = ap_getword_conf(r->pool, &userdirs);
 
248
        char *filename = NULL, *x = NULL;
 
249
        apr_status_t rv;
 
250
        int is_absolute = ap_os_is_path_absolute(r->pool, userdir);
 
251
 
 
252
        if (ap_strchr_c(userdir, '*'))
 
253
            x = ap_getword(r->pool, &userdir, '*');
 
254
 
 
255
        if (userdir[0] == '\0' || is_absolute) {
 
256
            if (x) {
 
257
#ifdef HAVE_DRIVE_LETTERS
 
258
                /*
 
259
                 * Crummy hack. Need to figure out whether we have been
 
260
                 * redirected to a URL or to a file on some drive. Since I
 
261
                 * know of no protocols that are a single letter, ignore
 
262
                 * a : as the first or second character, and assume a file
 
263
                 * was specified
 
264
                 */
 
265
                if (strchr(x + 2, ':'))
 
266
#else
 
267
                if (strchr(x, ':') && !is_absolute)
 
268
#endif /* HAVE_DRIVE_LETTERS */
 
269
                {
 
270
                    redirect = apr_pstrcat(r->pool, x, w, userdir, dname, NULL);
 
271
                    apr_table_setn(r->headers_out, "Location", redirect);
 
272
                    return HTTP_MOVED_TEMPORARILY;
 
273
                }
 
274
                else
 
275
                    filename = apr_pstrcat(r->pool, x, w, userdir, NULL);
 
276
            }
 
277
            else
 
278
                filename = apr_pstrcat(r->pool, userdir, "/", w, NULL);
 
279
        }
 
280
        else if (x && ap_strchr_c(x, ':')) {
 
281
            redirect = apr_pstrcat(r->pool, x, w, dname, NULL);
 
282
            apr_table_setn(r->headers_out, "Location", redirect);
 
283
            return HTTP_MOVED_TEMPORARILY;
 
284
        }
 
285
        else {
 
286
#if APR_HAS_USER
 
287
            char *homedir;
 
288
 
 
289
            if (apr_uid_homepath_get(&homedir, w, r->pool) == APR_SUCCESS) {
 
290
                filename = apr_pstrcat(r->pool, homedir, "/", userdir, NULL);
 
291
            }
 
292
#else
 
293
            return DECLINED;
 
294
#endif
 
295
        }
 
296
 
 
297
        /*
 
298
         * Now see if it exists, or we're at the last entry. If we are at the
 
299
         * last entry, then use the filename generated (if there is one)
 
300
         * anyway, in the hope that some handler might handle it. This can be
 
301
         * used, for example, to run a CGI script for the user.
 
302
         */
 
303
        if (filename && (!*userdirs
 
304
                      || ((rv = apr_stat(&statbuf, filename, APR_FINFO_MIN,
 
305
                                         r->pool)) == APR_SUCCESS
 
306
                                             || rv == APR_INCOMPLETE))) {
 
307
            r->filename = apr_pstrcat(r->pool, filename, dname, NULL);
 
308
            /* XXX: Does this walk us around FollowSymLink rules?
 
309
             * When statbuf contains info on r->filename we can save a syscall
 
310
             * by copying it to r->finfo
 
311
             */
 
312
            if (*userdirs && dname[0] == 0)
 
313
                r->finfo = statbuf;
 
314
 
 
315
            /* For use in the get_suexec_identity phase */
 
316
            apr_table_setn(r->notes, "mod_userdir_user", w);
 
317
 
 
318
            return OK;
 
319
        }
 
320
    }
 
321
 
 
322
    return DECLINED;
 
323
}
 
324
 
 
325
#ifdef HAVE_UNIX_SUEXEC
 
326
static ap_unix_identity_t *get_suexec_id_doer(const request_rec *r)
 
327
{
 
328
    ap_unix_identity_t *ugid = NULL;
 
329
#if APR_HAS_USER
 
330
    const char *username = apr_table_get(r->notes, "mod_userdir_user");
 
331
 
 
332
    if (username == NULL) {
 
333
        return NULL;
 
334
    }
 
335
 
 
336
    if ((ugid = apr_palloc(r->pool, sizeof(*ugid))) == NULL) {
 
337
        return NULL;
 
338
    }
 
339
 
 
340
    if (apr_uid_get(&ugid->uid, &ugid->gid, username, r->pool) != APR_SUCCESS) {
 
341
        return NULL;
 
342
    }
 
343
 
 
344
    ugid->userdir = 1;
 
345
#endif
 
346
    return ugid;
 
347
}
 
348
#endif /* HAVE_UNIX_SUEXEC */
 
349
 
 
350
static void register_hooks(apr_pool_t *p)
 
351
{
 
352
    static const char * const aszPre[]={ "mod_alias.c",NULL };
 
353
    static const char * const aszSucc[]={ "mod_vhost_alias.c",NULL };
 
354
 
 
355
    ap_hook_translate_name(translate_userdir,aszPre,aszSucc,APR_HOOK_MIDDLE);
 
356
#ifdef HAVE_UNIX_SUEXEC
 
357
    ap_hook_get_suexec_identity(get_suexec_id_doer,NULL,NULL,APR_HOOK_FIRST);
 
358
#endif
 
359
}
 
360
 
 
361
module AP_MODULE_DECLARE_DATA userdir_module = {
 
362
    STANDARD20_MODULE_STUFF,
 
363
    NULL,                       /* dir config creater */
 
364
    NULL,                       /* dir merger --- default is to override */
 
365
    create_userdir_config,      /* server config */
 
366
    NULL,                       /* merge server config */
 
367
    userdir_cmds,               /* command apr_table_t */
 
368
    register_hooks              /* register hooks */
 
369
};