Coverage Report

Created: 2023-03-26 06:28

/src/httpd/server/request.c
Line
Count
Source (jump to first uncovered line)
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
 * @file  request.c
19
 * @brief functions to get and process requests
20
 *
21
 * @author Rob McCool 3/21/93
22
 *
23
 * Thoroughly revamped by rst for Apache.  NB this file reads
24
 * best from the bottom up.
25
 *
26
 */
27
28
#include "apr_strings.h"
29
#include "apr_file_io.h"
30
#include "apr_fnmatch.h"
31
32
#define APR_WANT_STRFUNC
33
#include "apr_want.h"
34
35
#include "ap_config.h"
36
#include "ap_provider.h"
37
#include "httpd.h"
38
#include "http_config.h"
39
#include "http_request.h"
40
#include "http_core.h"
41
#include "http_protocol.h"
42
#include "http_log.h"
43
#include "http_main.h"
44
#include "util_filter.h"
45
#include "util_charset.h"
46
#include "util_script.h"
47
#include "ap_expr.h"
48
#include "mod_request.h"
49
50
#include "mod_core.h"
51
#include "mod_auth.h"
52
53
#if APR_HAVE_STDARG_H
54
#include <stdarg.h>
55
#endif
56
57
/* we know core's module_index is 0 */
58
#undef APLOG_MODULE_INDEX
59
#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
60
61
APR_HOOK_STRUCT(
62
    APR_HOOK_LINK(pre_translate_name)
63
    APR_HOOK_LINK(translate_name)
64
    APR_HOOK_LINK(map_to_storage)
65
    APR_HOOK_LINK(check_user_id)
66
    APR_HOOK_LINK(fixups)
67
    APR_HOOK_LINK(type_checker)
68
    APR_HOOK_LINK(access_checker)
69
    APR_HOOK_LINK(access_checker_ex)
70
    APR_HOOK_LINK(auth_checker)
71
    APR_HOOK_LINK(insert_filter)
72
    APR_HOOK_LINK(create_request)
73
    APR_HOOK_LINK(post_perdir_config)
74
    APR_HOOK_LINK(dirwalk_stat)
75
    APR_HOOK_LINK(force_authn)
76
)
77
78
AP_IMPLEMENT_HOOK_RUN_FIRST(int,pre_translate_name,
79
                            (request_rec *r), (r), DECLINED)
80
AP_IMPLEMENT_HOOK_RUN_FIRST(int,translate_name,
81
                            (request_rec *r), (r), DECLINED)
82
AP_IMPLEMENT_HOOK_RUN_FIRST(int,map_to_storage,
83
                            (request_rec *r), (r), DECLINED)
84
AP_IMPLEMENT_HOOK_RUN_FIRST(int,check_user_id,
85
                            (request_rec *r), (r), DECLINED)
86
AP_IMPLEMENT_HOOK_RUN_ALL(int,fixups,
87
                          (request_rec *r), (r), OK, DECLINED)
88
AP_IMPLEMENT_HOOK_RUN_FIRST(int,type_checker,
89
                            (request_rec *r), (r), DECLINED)
90
AP_IMPLEMENT_HOOK_RUN_ALL(int,access_checker,
91
                          (request_rec *r), (r), OK, DECLINED)
92
AP_IMPLEMENT_HOOK_RUN_FIRST(int,access_checker_ex,
93
                          (request_rec *r), (r), DECLINED)
94
AP_IMPLEMENT_HOOK_RUN_FIRST(int,auth_checker,
95
                            (request_rec *r), (r), DECLINED)
96
AP_IMPLEMENT_HOOK_VOID(insert_filter, (request_rec *r), (r))
97
AP_IMPLEMENT_HOOK_RUN_ALL(int, create_request,
98
                          (request_rec *r), (r), OK, DECLINED)
99
AP_IMPLEMENT_HOOK_RUN_ALL(int, post_perdir_config,
100
                          (request_rec *r), (r), OK, DECLINED)
101
AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t,dirwalk_stat,
102
                            (apr_finfo_t *finfo, request_rec *r, apr_int32_t wanted),
103
                            (finfo, r, wanted), AP_DECLINED)
104
AP_IMPLEMENT_HOOK_RUN_FIRST(int,force_authn,
105
                            (request_rec *r), (r), DECLINED)
106
107
static int auth_internal_per_conf = 0;
108
static int auth_internal_per_conf_hooks = 0;
109
static int auth_internal_per_conf_providers = 0;
110
111
112
static int decl_die(int status, const char *phase, request_rec *r)
113
0
{
114
0
    if (status == DECLINED) {
115
0
        ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(00025)
116
0
                      "configuration error:  couldn't %s: %s", phase, r->uri);
117
0
        return HTTP_INTERNAL_SERVER_ERROR;
118
0
    }
119
0
    else {
120
0
        ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
121
0
                      "auth phase '%s' gave status %d: %s", phase,
122
0
                      status, r->uri);
123
0
        return status;
124
0
    }
125
0
}
126
127
AP_DECLARE(int) ap_some_authn_required(request_rec *r)
128
0
{
129
0
    int access_status;
130
0
    char *olduser = r->user;
131
0
    int rv = FALSE;
132
133
0
    switch (ap_satisfies(r)) {
134
0
    case SATISFY_ALL:
135
0
    case SATISFY_NOSPEC:
136
0
        if ((access_status = ap_run_access_checker(r)) != OK) {
137
0
            break;
138
0
        }
139
140
0
        access_status = ap_run_access_checker_ex(r);
141
0
        if (access_status == DECLINED) {
142
0
            rv = TRUE;
143
0
        }
144
145
0
        break;
146
0
    case SATISFY_ANY:
147
0
        if ((access_status = ap_run_access_checker(r)) == OK) {
148
0
            break;
149
0
        }
150
151
0
        access_status = ap_run_access_checker_ex(r);
152
0
        if (access_status == DECLINED) {
153
0
            rv = TRUE;
154
0
        }
155
156
0
        break;
157
0
    }
158
159
0
    r->user = olduser;
160
0
    return rv;
161
0
}
162
163
static int walk_location_and_if(request_rec *r)
164
0
{
165
0
    int access_status;
166
167
0
    if ((access_status = ap_location_walk(r))) {
168
0
        return access_status;
169
0
    }
170
0
    if ((access_status = ap_if_walk(r))) {
171
0
        return access_status;
172
0
    }
173
174
    /* Don't set per-dir loglevel if LogLevelOverride is set */
175
0
    if (!r->connection->log) {
176
0
        core_dir_config *d = ap_get_core_module_config(r->per_dir_config);
177
0
        if (d->log)
178
0
            r->log = d->log;
179
0
    }
180
181
0
    return OK;
182
0
}
183
184
/* This is the master logic for processing requests.  Do NOT duplicate
185
 * this logic elsewhere, or the security model will be broken by future
186
 * API changes.  Each phase must be individually optimized to pick up
187
 * redundant/duplicate calls by subrequests, and redirects.
188
 */
189
AP_DECLARE(int) ap_process_request_internal(request_rec *r)
190
0
{
191
0
    int access_status = DECLINED;
192
0
    int file_req = (r->main && r->filename);
193
0
    core_server_config *sconf =
194
0
        ap_get_core_module_config(r->server->module_config);
195
0
    unsigned int normalize_flags;
196
197
0
    normalize_flags = AP_NORMALIZE_NOT_ABOVE_ROOT;
198
0
    if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) { 
199
0
        normalize_flags |= AP_NORMALIZE_MERGE_SLASHES;
200
0
    }
201
0
    if (file_req) {
202
        /* File subrequests can have a relative path. */
203
0
        normalize_flags |= AP_NORMALIZE_ALLOW_RELATIVE;
204
0
    }
205
206
0
    if (r->parsed_uri.path) {
207
        /* Normalize: remove /./ and shrink /../ segments, plus
208
         * decode unreserved chars (first time only to avoid
209
         * double decoding after ap_unescape_url() below).
210
         */
211
0
        if (!ap_normalize_path(r->parsed_uri.path,
212
0
                               normalize_flags |
213
0
                               AP_NORMALIZE_DECODE_UNRESERVED)) {
214
0
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10244)
215
0
                          "invalid URI path (%s)", r->unparsed_uri);
216
0
            return HTTP_BAD_REQUEST;
217
0
        }
218
0
    }
219
220
    /* All file subrequests are a huge pain... they cannot bubble through the
221
     * next several steps.  Only file subrequests are allowed an empty uri,
222
     * otherwise let (pre_)translate_name kill the request.
223
     */
224
0
    if (!file_req) {
225
0
        ap_conf_vector_t *per_dir_config = r->per_dir_config;
226
227
0
        if ((access_status = walk_location_and_if(r))) {
228
0
            return access_status;
229
0
        }
230
231
        /* Let pre_translate_name hooks work with non-decoded URIs, and
232
         * eventually prevent further URI transformations (return DONE).
233
         */
234
0
        access_status = ap_run_pre_translate_name(r);
235
0
        if (ap_is_HTTP_ERROR(access_status)) {
236
0
            return access_status;
237
0
        }
238
239
        /* Throw away pre_trans only merging */
240
0
        r->per_dir_config = per_dir_config;
241
0
    }
242
243
    /* Ignore URL unescaping for translated URIs already */
244
0
    if (access_status != DONE && r->parsed_uri.path) {
245
0
        core_dir_config *d = ap_get_core_module_config(r->per_dir_config);
246
        /* Unreserved chars were already decoded by ap_normalize_path() */
247
0
        unsigned int unescape_flags = AP_UNESCAPE_URL_KEEP_UNRESERVED;
248
0
        if (!d->allow_encoded_slashes) {
249
0
            unescape_flags |= AP_UNESCAPE_URL_FORBID_SLASHES;
250
0
        }
251
0
        else if (!d->decode_encoded_slashes) {
252
0
            unescape_flags |= AP_UNESCAPE_URL_KEEP_SLASHES;
253
0
        }
254
0
        access_status = ap_unescape_url_ex(r->parsed_uri.path, unescape_flags);
255
0
        if (access_status) {
256
0
            if (access_status == HTTP_NOT_FOUND) {
257
0
                if (! d->allow_encoded_slashes) {
258
0
                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00026)
259
0
                                  "found %%2f (encoded '/') in URI path (%s), "
260
0
                                  "returning 404", r->unparsed_uri);
261
0
                }
262
0
            }
263
0
            return access_status;
264
0
        }
265
266
0
        if (d->allow_encoded_slashes && d->decode_encoded_slashes) {
267
            /* Decoding slashes might have created new // or /./ or /../
268
             * segments (e.g. "/.%2F/"), so re-normalize.
269
             */
270
0
            ap_normalize_path(r->parsed_uri.path, normalize_flags);
271
0
        }
272
0
    }
273
274
    /* Same, translate_name is not suited for file subrequests */
275
0
    if (!file_req) {
276
0
        if ((access_status = walk_location_and_if(r))) {
277
0
            return access_status;
278
0
        }
279
280
0
        if ((access_status = ap_run_translate_name(r))) {
281
0
            return decl_die(access_status, "translate", r);
282
0
        }
283
0
    }
284
285
    /* Reset to the server default config prior to running map_to_storage
286
     */
287
0
    r->per_dir_config = r->server->lookup_defaults;
288
289
0
    if ((access_status = ap_run_map_to_storage(r))) {
290
        /* This request wasn't in storage (e.g. TRACE) */
291
0
        return access_status;
292
0
    }
293
294
    /* Rerun the location walk, which overrides any map_to_storage config.
295
     */
296
0
    if ((access_status = walk_location_and_if(r))) {
297
0
        return access_status;
298
0
    }
299
300
0
    if ((access_status = ap_run_post_perdir_config(r))) {
301
0
        return access_status;
302
0
    }
303
304
    /* Only on the main request! */
305
0
    if (r->main == NULL) {
306
0
        if ((access_status = ap_run_header_parser(r))) {
307
0
            return access_status;
308
0
        }
309
0
    }
310
311
    /* Skip authn/authz if the parent or prior request passed the authn/authz,
312
     * and that configuration didn't change (this requires optimized _walk()
313
     * functions in map_to_storage that use the same merge results given
314
     * identical input.)  If the config changes, we must re-auth.
315
     */
316
0
    if (r->prev && (r->prev->per_dir_config == r->per_dir_config)) {
317
0
        r->user = r->prev->user;
318
0
        r->ap_auth_type = r->prev->ap_auth_type;
319
0
    }
320
0
    else if (r->main && (r->main->per_dir_config == r->per_dir_config)) {
321
0
        r->user = r->main->user;
322
0
        r->ap_auth_type = r->main->ap_auth_type;
323
0
    }
324
0
    else {
325
        /* A module using a confusing API (ap_get_basic_auth_pw) caused
326
        ** r->user to be filled out prior to check_authn hook. We treat
327
        ** it is inadvertent.
328
        */
329
0
        if (r->user && apr_table_get(r->notes, AP_GET_BASIC_AUTH_PW_NOTE)) { 
330
0
            r->user = NULL;
331
0
        }
332
333
0
        switch (ap_satisfies(r)) {
334
0
        case SATISFY_ALL:
335
0
        case SATISFY_NOSPEC:
336
0
            if ((access_status = ap_run_access_checker(r)) != OK) {
337
0
                return decl_die(access_status,
338
0
                                "check access (with Satisfy All)", r);
339
0
            }
340
341
0
            access_status = ap_run_access_checker_ex(r);
342
0
            if (access_status == DECLINED
343
0
                || (access_status == OK && ap_run_force_authn(r) == OK)) {
344
0
                if ((access_status = ap_run_check_user_id(r)) != OK) {
345
0
                    return decl_die(access_status, "check user", r);
346
0
                }
347
0
                if (r->user == NULL) {
348
                    /* don't let buggy authn module crash us in authz */
349
0
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00027)
350
0
                                  "No authentication done but request not "
351
0
                                  "allowed without authentication for %s. "
352
0
                                  "Authentication not configured?",
353
0
                                  r->uri);
354
0
                    access_status = HTTP_INTERNAL_SERVER_ERROR;
355
0
                    return decl_die(access_status, "check user", r);
356
0
                }
357
0
                if ((access_status = ap_run_auth_checker(r)) != OK) {
358
0
                    return decl_die(access_status, "check authorization", r);
359
0
                }
360
0
            }
361
0
            else if (access_status == OK) {
362
0
                ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
363
0
                              "request authorized without authentication by "
364
0
                              "access_checker_ex hook: %s", r->uri);
365
0
            }
366
0
            else {
367
0
                return decl_die(access_status, "check access", r);
368
0
            }
369
0
            break;
370
0
        case SATISFY_ANY:
371
0
            if ((access_status = ap_run_access_checker(r)) == OK) {
372
0
                ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
373
0
                              "request authorized without authentication by "
374
0
                              "access_checker hook and 'Satisfy any': %s",
375
0
                              r->uri);
376
0
                break;
377
0
            }
378
379
0
            access_status = ap_run_access_checker_ex(r);
380
0
            if (access_status == DECLINED
381
0
                || (access_status == OK && ap_run_force_authn(r) == OK)) {
382
0
                if ((access_status = ap_run_check_user_id(r)) != OK) {
383
0
                    return decl_die(access_status, "check user", r);
384
0
                }
385
0
                if (r->user == NULL) {
386
                    /* don't let buggy authn module crash us in authz */
387
0
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00028)
388
0
                                  "No authentication done but request not "
389
0
                                  "allowed without authentication for %s. "
390
0
                                  "Authentication not configured?",
391
0
                                  r->uri);
392
0
                    access_status = HTTP_INTERNAL_SERVER_ERROR;
393
0
                    return decl_die(access_status, "check user", r);
394
0
                }
395
0
                if ((access_status = ap_run_auth_checker(r)) != OK) {
396
0
                    return decl_die(access_status, "check authorization", r);
397
0
                }
398
0
            }
399
0
            else if (access_status == OK) {
400
0
                ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
401
0
                              "request authorized without authentication by "
402
0
                              "access_checker_ex hook: %s", r->uri);
403
0
            }
404
0
            else {
405
0
                return decl_die(access_status, "check access", r);
406
0
            }
407
0
            break;
408
0
        }
409
0
    }
410
    /* XXX Must make certain the ap_run_type_checker short circuits mime
411
     * in mod-proxy for r->proxyreq && r->parsed_uri.scheme
412
     *                              && !strcmp(r->parsed_uri.scheme, "http")
413
     */
414
0
    if ((access_status = ap_run_type_checker(r)) != OK) {
415
0
        return decl_die(access_status, "find types", r);
416
0
    }
417
418
0
    if ((access_status = ap_run_fixups(r)) != OK) {
419
0
        ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "fixups hook gave %d: %s",
420
0
                      access_status, r->uri);
421
0
        return access_status;
422
0
    }
423
424
0
    return OK;
425
0
}
426
427
428
/* Useful caching structures to repeat _walk/merge sequences as required
429
 * when a subrequest or redirect reuses substantially the same config.
430
 *
431
 * Directive order in the httpd.conf file and its Includes significantly
432
 * impact this optimization.  Grouping common blocks at the front of the
433
 * config that are less likely to change between a request and
434
 * its subrequests, or between a request and its redirects reduced
435
 * the work of these functions significantly.
436
 */
437
438
typedef struct walk_walked_t {
439
    ap_conf_vector_t *matched; /* A dir_conf sections we matched */
440
    ap_conf_vector_t *merged;  /* The dir_conf merged result */
441
} walk_walked_t;
442
443
typedef struct walk_cache_t {
444
    const char         *cached;          /* The identifier we matched */
445
    ap_conf_vector_t  **dir_conf_tested; /* The sections we matched against */
446
    ap_conf_vector_t   *dir_conf_merged; /* Base per_dir_config */
447
    ap_conf_vector_t   *per_dir_result;  /* per_dir_config += walked result */
448
    apr_array_header_t *walked;          /* The list of walk_walked_t results */
449
    struct walk_cache_t *prev; /* Prev cache of same call in this (sub)req */
450
    int count; /* Number of prev invocations of same call in this (sub)req */
451
} walk_cache_t;
452
453
static walk_cache_t *prep_walk_cache(apr_size_t t, request_rec *r)
454
0
{
455
0
    void **note, **inherit_note;
456
0
    walk_cache_t *cache, *prev_cache, *copy_cache;
457
0
    int count;
458
459
    /* Find the most relevant, recent walk cache to work from and provide
460
     * a copy the caller is allowed to munge.  In the case of a sub-request
461
     * or internal redirect, this is the cache corresponding to the equivalent
462
     * invocation of the same function call in the "parent" request, if such
463
     * a cache exists.  Otherwise it is the walk cache of the previous
464
     * invocation of the same function call in the current request, if
465
     * that exists; if not, then create a new walk cache.
466
     */
467
0
    note = ap_get_request_note(r, t);
468
0
    AP_DEBUG_ASSERT(note != NULL);
469
470
0
    copy_cache = prev_cache = *note;
471
0
    count = prev_cache ? (prev_cache->count + 1) : 0;
472
473
0
    if ((r->prev
474
0
         && (inherit_note = ap_get_request_note(r->prev, t))
475
0
         && *inherit_note)
476
0
        || (r->main
477
0
            && (inherit_note = ap_get_request_note(r->main, t))
478
0
            && *inherit_note)) {
479
0
        walk_cache_t *inherit_cache = *inherit_note;
480
481
0
        while (inherit_cache->count > count) {
482
0
            inherit_cache = inherit_cache->prev;
483
0
        }
484
0
        if (inherit_cache->count == count) {
485
0
            copy_cache = inherit_cache;
486
0
        }
487
0
    }
488
489
0
    if (copy_cache) {
490
0
        cache = apr_pmemdup(r->pool, copy_cache, sizeof(*cache));
491
0
        cache->walked = apr_array_copy(r->pool, cache->walked);
492
0
        cache->prev = prev_cache;
493
0
        cache->count = count;
494
0
    }
495
0
    else {
496
0
        cache = apr_pcalloc(r->pool, sizeof(*cache));
497
0
        cache->walked = apr_array_make(r->pool, 4, sizeof(walk_walked_t));
498
0
    }
499
500
0
    *note = cache;
501
502
0
    return cache;
503
0
}
504
505
/*****************************************************************
506
 *
507
 * Getting and checking directory configuration.  Also checks the
508
 * FollowSymlinks and FollowSymOwner stuff, since this is really the
509
 * only place that can happen (barring a new mid_dir_walk callout).
510
 *
511
 * We can't do it as an access_checker module function which gets
512
 * called with the final per_dir_config, since we could have a directory
513
 * with FollowSymLinks disabled, which contains a symlink to another
514
 * with a .htaccess file which turns FollowSymLinks back on --- and
515
 * access in such a case must be denied.  So, whatever it is that
516
 * checks FollowSymLinks needs to know the state of the options as
517
 * they change, all the way down.
518
 */
519
520
521
/*
522
 * resolve_symlink must _always_ be called on an APR_LNK file type!
523
 * It will resolve the actual target file type, modification date, etc,
524
 * and provide any processing required for symlink evaluation.
525
 * Path must already be cleaned, no trailing slash, no multi-slashes,
526
 * and don't call this on the root!
527
 *
528
 * Simply, the number of times we deref a symlink are minimal compared
529
 * to the number of times we had an extra lstat() since we 'weren't sure'.
530
 *
531
 * To optimize, we stat() anything when given (opts & OPT_SYM_LINKS), otherwise
532
 * we start off with an lstat().  Every lstat() must be dereferenced in case
533
 * it points at a 'nasty' - we must always rerun check_safe_file (or similar.)
534
 */
535
static int resolve_symlink(char *d, apr_finfo_t *lfi, int opts, apr_pool_t *p)
536
0
{
537
0
    apr_finfo_t fi;
538
0
    const char *savename;
539
540
0
    if (!(opts & (OPT_SYM_OWNER | OPT_SYM_LINKS))) {
541
0
        return HTTP_FORBIDDEN;
542
0
    }
543
544
    /* Save the name from the valid bits. */
545
0
    savename = (lfi->valid & APR_FINFO_NAME) ? lfi->name : NULL;
546
547
    /* if OPT_SYM_OWNER is unset, we only need to check target accessible */
548
0
    if (!(opts & OPT_SYM_OWNER)) {
549
0
        if (apr_stat(&fi, d, lfi->valid & ~(APR_FINFO_NAME | APR_FINFO_LINK), p)
550
0
            != APR_SUCCESS)
551
0
        {
552
0
            return HTTP_FORBIDDEN;
553
0
        }
554
555
        /* Give back the target */
556
0
        memcpy(lfi, &fi, sizeof(fi));
557
0
        if (savename) {
558
0
            lfi->name = savename;
559
0
            lfi->valid |= APR_FINFO_NAME;
560
0
        }
561
562
0
        return OK;
563
0
    }
564
565
    /* OPT_SYM_OWNER only works if we can get the owner of
566
     * both the file and symlink.  First fill in a missing
567
     * owner of the symlink, then get the info of the target.
568
     */
569
0
    if (!(lfi->valid & APR_FINFO_OWNER)) {
570
0
        if (apr_stat(lfi, d, lfi->valid | APR_FINFO_LINK | APR_FINFO_OWNER, p)
571
0
            != APR_SUCCESS)
572
0
        {
573
0
            return HTTP_FORBIDDEN;
574
0
        }
575
0
    }
576
577
0
    if (apr_stat(&fi, d, lfi->valid & ~(APR_FINFO_NAME), p) != APR_SUCCESS) {
578
0
        return HTTP_FORBIDDEN;
579
0
    }
580
581
0
    if (apr_uid_compare(fi.user, lfi->user) != APR_SUCCESS) {
582
0
        return HTTP_FORBIDDEN;
583
0
    }
584
585
    /* Give back the target */
586
0
    memcpy(lfi, &fi, sizeof(fi));
587
0
    if (savename) {
588
0
        lfi->name = savename;
589
0
        lfi->valid |= APR_FINFO_NAME;
590
0
    }
591
592
0
    return OK;
593
0
}
594
595
596
/*
597
 * As we walk the directory configuration, the merged config won't
598
 * be 'rooted' to a specific vhost until the very end of the merge.
599
 *
600
 * We need a very fast mini-merge to a real, vhost-rooted merge
601
 * of core.opts and core.override, the only options tested within
602
 * directory_walk itself.
603
 *
604
 * See core.c::merge_core_dir_configs() for explanation.
605
 */
606
607
typedef struct core_opts_t {
608
        allow_options_t opts;
609
        allow_options_t add;
610
        allow_options_t remove;
611
        overrides_t override;
612
        overrides_t override_opts;
613
        apr_table_t *override_list;
614
} core_opts_t;
615
616
static void core_opts_merge(const ap_conf_vector_t *sec, core_opts_t *opts)
617
0
{
618
0
    core_dir_config *this_dir = ap_get_core_module_config(sec);
619
620
0
    if (!this_dir) {
621
0
        return;
622
0
    }
623
624
0
    if (this_dir->opts & OPT_UNSET) {
625
0
        opts->add = (opts->add & ~this_dir->opts_remove)
626
0
                   | this_dir->opts_add;
627
0
        opts->remove = (opts->remove & ~this_dir->opts_add)
628
0
                      | this_dir->opts_remove;
629
0
        opts->opts = (opts->opts & ~opts->remove) | opts->add;
630
0
    }
631
0
    else {
632
0
        opts->opts = this_dir->opts;
633
0
        opts->add = this_dir->opts_add;
634
0
        opts->remove = this_dir->opts_remove;
635
0
    }
636
637
0
    if (!(this_dir->override & OR_UNSET)) {
638
0
        opts->override = this_dir->override;
639
0
        opts->override_opts = this_dir->override_opts;
640
0
    }
641
642
0
    if (this_dir->override_list != NULL) {
643
0
        opts->override_list = this_dir->override_list;
644
0
    }
645
0
}
646
647
648
/*****************************************************************
649
 *
650
 * Getting and checking directory configuration.  Also checks the
651
 * FollowSymlinks and FollowSymOwner stuff, since this is really the
652
 * only place that can happen (barring a new mid_dir_walk callout).
653
 *
654
 * We can't do it as an access_checker module function which gets
655
 * called with the final per_dir_config, since we could have a directory
656
 * with FollowSymLinks disabled, which contains a symlink to another
657
 * with a .htaccess file which turns FollowSymLinks back on --- and
658
 * access in such a case must be denied.  So, whatever it is that
659
 * checks FollowSymLinks needs to know the state of the options as
660
 * they change, all the way down.
661
 */
662
663
AP_DECLARE(int) ap_directory_walk(request_rec *r)
664
0
{
665
0
    ap_conf_vector_t *now_merged = NULL;
666
0
    core_server_config *sconf =
667
0
        ap_get_core_module_config(r->server->module_config);
668
0
    ap_conf_vector_t **sec_ent = (ap_conf_vector_t **) sconf->sec_dir->elts;
669
0
    int num_sec = sconf->sec_dir->nelts;
670
0
    walk_cache_t *cache;
671
0
    char *entry_dir;
672
0
    apr_status_t rv;
673
0
    int cached;
674
675
    /* XXX: Better (faster) tests needed!!!
676
     *
677
     * "OK" as a response to a real problem is not _OK_, but to allow broken
678
     * modules to proceed, we will permit the not-a-path filename to pass the
679
     * following two tests.  This behavior may be revoked in future versions
680
     * of Apache.  We still must catch it later if it's heading for the core
681
     * handler.  Leave INFO notes here for module debugging.
682
     */
683
0
    if (r->filename == NULL) {
684
0
        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00029)
685
0
                      "Module bug?  Request filename is missing for URI %s",
686
0
                      r->uri);
687
0
        return OK;
688
0
    }
689
690
    /* Canonicalize the file path without resolving filename case or aliases
691
     * so we can begin by checking the cache for a recent directory walk.
692
     * This call will ensure we have an absolute path in the same pass.
693
     */
694
0
    if ((rv = apr_filepath_merge(&entry_dir, NULL, r->filename,
695
0
                                 APR_FILEPATH_NOTRELATIVE, r->pool))
696
0
                  != APR_SUCCESS) {
697
0
        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00030)
698
0
                      "Module bug?  Request filename path %s is invalid or "
699
0
                      "or not absolute for uri %s",
700
0
                      r->filename, r->uri);
701
0
        return OK;
702
0
    }
703
704
    /* XXX Notice that this forces path_info to be canonical.  That might
705
     * not be desired by all apps.  However, some of those same apps likely
706
     * have significant security holes.
707
     */
708
0
    r->filename = entry_dir;
709
710
0
    cache = prep_walk_cache(AP_NOTE_DIRECTORY_WALK, r);
711
0
    cached = (cache->cached != NULL);
712
713
    /* If this is not a dirent subrequest with a preconstructed
714
     * r->finfo value, then we can simply stat the filename to
715
     * save burning mega-cycles with unneeded stats - if this is
716
     * an exact file match.  We don't care about failure... we
717
     * will stat by component failing this meager attempt.
718
     *
719
     * It would be nice to distinguish APR_ENOENT from other
720
     * types of failure, such as APR_ENOTDIR.  We can do something
721
     * with APR_ENOENT, knowing that the path is good.
722
     */
723
0
    if (r->finfo.filetype == APR_NOFILE || r->finfo.filetype == APR_LNK) {
724
0
        rv = ap_run_dirwalk_stat(&r->finfo, r, APR_FINFO_MIN);
725
726
        /* some OSs will return APR_SUCCESS/APR_REG if we stat
727
         * a regular file but we have '/' at the end of the name;
728
         *
729
         * other OSs will return APR_ENOTDIR for that situation;
730
         *
731
         * handle it the same everywhere by simulating a failure
732
         * if it looks like a directory but really isn't
733
         *
734
         * Also reset if the stat failed, just for safety.
735
         */
736
0
        if ((rv != APR_SUCCESS) ||
737
0
            (r->finfo.filetype != APR_NOFILE &&
738
0
             (r->finfo.filetype != APR_DIR) &&
739
0
             (r->filename[strlen(r->filename) - 1] == '/'))) {
740
0
             r->finfo.filetype = APR_NOFILE; /* forget what we learned */
741
0
        }
742
0
    }
743
744
0
    if (r->finfo.filetype == APR_REG) {
745
0
        entry_dir = ap_make_dirstr_parent(r->pool, entry_dir);
746
0
    }
747
0
    else if (r->filename[strlen(r->filename) - 1] != '/') {
748
0
        entry_dir = apr_pstrcat(r->pool, r->filename, "/", NULL);
749
0
    }
750
751
    /* If we have a file already matches the path of r->filename,
752
     * and the vhost's list of directory sections hasn't changed,
753
     * we can skip rewalking the directory_walk entries.
754
     */
755
0
    if (cached
756
0
        && ((r->finfo.filetype == APR_REG)
757
0
            || ((r->finfo.filetype == APR_DIR)
758
0
                && (!r->path_info || !*r->path_info)))
759
0
        && (cache->dir_conf_tested == sec_ent)
760
0
        && (strcmp(entry_dir, cache->cached) == 0)) {
761
0
        int familiar = 0;
762
763
        /* Well this looks really familiar!  If our end-result (per_dir_result)
764
         * didn't change, we have absolutely nothing to do :)
765
         * Otherwise (as is the case with most dir_merged/file_merged requests)
766
         * we must merge our dir_conf_merged onto this new r->per_dir_config.
767
         */
768
0
        if (r->per_dir_config == cache->per_dir_result) {
769
0
            familiar = 1;
770
0
        }
771
772
0
        if (r->per_dir_config == cache->dir_conf_merged) {
773
0
            r->per_dir_config = cache->per_dir_result;
774
0
            familiar = 1;
775
0
        }
776
777
0
        if (familiar) {
778
0
            apr_finfo_t thisinfo;
779
0
            int res;
780
0
            allow_options_t opts;
781
0
            core_dir_config *this_dir;
782
783
0
            this_dir = ap_get_core_module_config(r->per_dir_config);
784
0
            opts = this_dir->opts;
785
            /*
786
             * If Symlinks are allowed in general we do not need the following
787
             * check.
788
             */
789
0
            if (!(opts & OPT_SYM_LINKS)) {
790
0
                rv = ap_run_dirwalk_stat(&thisinfo, r,
791
0
                                         APR_FINFO_MIN | APR_FINFO_NAME | APR_FINFO_LINK);
792
                /*
793
                 * APR_INCOMPLETE is as fine as result as APR_SUCCESS as we
794
                 * have added APR_FINFO_NAME to the wanted parameter of
795
                 * apr_stat above. On Unix platforms this means that apr_stat
796
                 * is always going to return APR_INCOMPLETE in the case that
797
                 * the call to the native stat / lstat did not fail.
798
                 */
799
0
                if ((rv != APR_INCOMPLETE) && (rv != APR_SUCCESS)) {
800
                    /*
801
                     * This should never happen, because we did a stat on the
802
                     * same file, resolving a possible symlink several lines
803
                     * above. Therefore do not make a detailed analysis of rv
804
                     * in this case for the reason of the failure, just bail out
805
                     * with a HTTP_FORBIDDEN in case we hit a race condition
806
                     * here.
807
                     */
808
0
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00031)
809
0
                                  "access to %s failed; stat of '%s' failed.",
810
0
                                  r->uri, r->filename);
811
0
                    return r->status = HTTP_FORBIDDEN;
812
0
                }
813
0
                if (thisinfo.filetype == APR_LNK) {
814
                    /* Is this a possibly acceptable symlink? */
815
0
                    if ((res = resolve_symlink(r->filename, &thisinfo,
816
0
                                               opts, r->pool)) != OK) {
817
0
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00032)
818
0
                                      "Symbolic link not allowed "
819
0
                                      "or link target not accessible: %s",
820
0
                                      r->filename);
821
0
                        return r->status = res;
822
0
                    }
823
0
                }
824
0
            }
825
0
            return OK;
826
0
        }
827
828
0
        if (cache->walked->nelts) {
829
0
            now_merged = ((walk_walked_t*)cache->walked->elts)
830
0
                [cache->walked->nelts - 1].merged;
831
0
        }
832
0
    }
833
0
    else {
834
        /* We start now_merged from NULL since we want to build
835
         * a locations list that can be merged to any vhost.
836
         */
837
0
        int sec_idx;
838
0
        int matches = cache->walked->nelts;
839
0
        int cached_matches = matches;
840
0
        walk_walked_t *last_walk = (walk_walked_t*)cache->walked->elts;
841
0
        core_dir_config *this_dir;
842
0
        core_opts_t opts;
843
0
        apr_finfo_t thisinfo;
844
0
        char *save_path_info;
845
0
        apr_size_t buflen;
846
0
        char *buf;
847
0
        unsigned int seg, startseg;
848
0
        apr_pool_t *rxpool = NULL;
849
850
        /* Invariant: from the first time filename_len is set until
851
         * it goes out of scope, filename_len==strlen(r->filename)
852
         */
853
0
        apr_size_t filename_len;
854
#ifdef CASE_BLIND_FILESYSTEM
855
        apr_size_t canonical_len;
856
#endif
857
858
0
        cached &= auth_internal_per_conf;
859
860
        /*
861
         * We must play our own mini-merge game here, for the few
862
         * running dir_config values we care about within dir_walk.
863
         * We didn't start the merge from r->per_dir_config, so we
864
         * accumulate opts and override as we merge, from the globals.
865
         */
866
0
        this_dir = ap_get_core_module_config(r->per_dir_config);
867
0
        opts.opts = this_dir->opts;
868
0
        opts.add = this_dir->opts_add;
869
0
        opts.remove = this_dir->opts_remove;
870
0
        opts.override = this_dir->override;
871
0
        opts.override_opts = this_dir->override_opts;
872
0
        opts.override_list = this_dir->override_list;
873
874
        /* Set aside path_info to merge back onto path_info later.
875
         * If r->filename is a directory, we must remerge the path_info,
876
         * before we continue!  [Directories cannot, by definition, have
877
         * path info.  Either the next segment is not-found, or a file.]
878
         *
879
         * r->path_info tracks the unconsumed source path.
880
         * r->filename  tracks the path as we process it
881
         */
882
0
        if ((r->finfo.filetype == APR_DIR) && r->path_info && *r->path_info)
883
0
        {
884
0
            if ((rv = apr_filepath_merge(&r->path_info, r->filename,
885
0
                                         r->path_info,
886
0
                                         APR_FILEPATH_NOTABOVEROOT, r->pool))
887
0
                != APR_SUCCESS) {
888
0
                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00033)
889
0
                              "dir_walk error, path_info %s is not relative "
890
0
                              "to the filename path %s for uri %s",
891
0
                              r->path_info, r->filename, r->uri);
892
0
                return HTTP_INTERNAL_SERVER_ERROR;
893
0
            }
894
895
0
            save_path_info = NULL;
896
0
        }
897
0
        else {
898
0
            save_path_info = r->path_info;
899
0
            r->path_info = r->filename;
900
0
        }
901
902
#ifdef CASE_BLIND_FILESYSTEM
903
904
        canonical_len = 0;
905
        while (r->canonical_filename && r->canonical_filename[canonical_len]
906
               && (r->canonical_filename[canonical_len]
907
                   == r->path_info[canonical_len])) {
908
             ++canonical_len;
909
        }
910
911
        while (canonical_len
912
               && ((r->canonical_filename[canonical_len - 1] != '/'
913
                   && r->canonical_filename[canonical_len - 1])
914
                   || (r->path_info[canonical_len - 1] != '/'
915
                       && r->path_info[canonical_len - 1]))) {
916
            --canonical_len;
917
        }
918
919
        /*
920
         * Now build r->filename component by component, starting
921
         * with the root (on Unix, simply "/").  We will make a huge
922
         * assumption here for efficiency, that any canonical path
923
         * already given included a canonical root.
924
         */
925
        rv = apr_filepath_root((const char **)&r->filename,
926
                               (const char **)&r->path_info,
927
                               canonical_len ? 0 : APR_FILEPATH_TRUENAME,
928
                               r->pool);
929
        filename_len = strlen(r->filename);
930
931
        /*
932
         * Bad assumption above?  If the root's length is longer
933
         * than the canonical length, then it cannot be trusted as
934
         * a truename.  So try again, this time more seriously.
935
         */
936
        if ((rv == APR_SUCCESS) && canonical_len
937
            && (filename_len > canonical_len)) {
938
            rv = apr_filepath_root((const char **)&r->filename,
939
                                   (const char **)&r->path_info,
940
                                   APR_FILEPATH_TRUENAME, r->pool);
941
            filename_len = strlen(r->filename);
942
            canonical_len = 0;
943
        }
944
945
#else /* ndef CASE_BLIND_FILESYSTEM, really this simple for Unix today; */
946
947
0
        rv = apr_filepath_root((const char **)&r->filename,
948
0
                               (const char **)&r->path_info,
949
0
                               0, r->pool);
950
0
        filename_len = strlen(r->filename);
951
952
0
#endif
953
954
0
        if (rv != APR_SUCCESS) {
955
0
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00034)
956
0
                          "dir_walk error, could not determine the root "
957
0
                          "path of filename %s%s for uri %s",
958
0
                          r->filename, r->path_info, r->uri);
959
0
            return HTTP_INTERNAL_SERVER_ERROR;
960
0
        }
961
962
        /* Working space for terminating null and an extra / is required.
963
         */
964
0
        buflen = filename_len + strlen(r->path_info) + 2;
965
0
        buf = apr_palloc(r->pool, buflen);
966
0
        memcpy(buf, r->filename, filename_len + 1);
967
0
        r->filename = buf;
968
0
        thisinfo.valid = APR_FINFO_TYPE;
969
0
        thisinfo.filetype = APR_DIR; /* It's the root, of course it's a dir */
970
971
        /*
972
         * seg keeps track of which segment we've copied.
973
         * sec_idx keeps track of which section we're on, since sections are
974
         *     ordered by number of segments. See core_reorder_directories
975
         * startseg tells us how many segments describe the root path
976
         *     e.g. the complete path "//host/foo/" to a UNC share (4)
977
         */
978
0
        startseg = seg = ap_count_dirs(r->filename);
979
0
        sec_idx = 0;
980
981
        /*
982
         * Go down the directory hierarchy.  Where we have to check for
983
         * symlinks, do so.  Where a .htaccess file has permission to
984
         * override anything, try to find one.
985
         */
986
0
        do {
987
0
            int res;
988
0
            char *seg_name;
989
0
            char *delim;
990
0
            int temp_slash=0;
991
992
            /* We have no trailing slash, but we sure would appreciate one.
993
             * However, we don't want to append a / our first time through.
994
             */
995
0
            if ((seg > startseg) && r->filename[filename_len-1] != '/') {
996
0
                r->filename[filename_len++] = '/';
997
0
                r->filename[filename_len] = 0;
998
0
                temp_slash=1;
999
0
            }
1000
1001
            /* Begin *this* level by looking for matching <Directory> sections
1002
             * from the server config.
1003
             */
1004
0
            for (; sec_idx < num_sec; ++sec_idx) {
1005
1006
0
                ap_conf_vector_t *entry_config = sec_ent[sec_idx];
1007
0
                core_dir_config *entry_core;
1008
0
                entry_core = ap_get_core_module_config(entry_config);
1009
1010
                /* No more possible matches for this many segments?
1011
                 * We are done when we find relative/regex/longer components.
1012
                 */
1013
0
                if (entry_core->r || entry_core->d_components > seg) {
1014
0
                    break;
1015
0
                }
1016
1017
                /* We will never skip '0' element components, e.g. plain old
1018
                 * <Directory >, and <Directory "/"> are classified as zero
1019
                 * so that Win32/Netware/OS2 etc all pick them up.
1020
                 * Otherwise, skip over the mismatches.
1021
                 */
1022
0
                if (entry_core->d_components
1023
0
                    && ((entry_core->d_components < seg)
1024
0
                     || (entry_core->d_is_fnmatch
1025
0
                         ? (apr_fnmatch(entry_core->d, r->filename,
1026
0
                                        APR_FNM_PATHNAME) != APR_SUCCESS)
1027
0
                         : (strcmp(r->filename, entry_core->d) != 0)))) {
1028
0
                    continue;
1029
0
                }
1030
1031
                /* If we haven't continue'd above, we have a match.
1032
                 *
1033
                 * Calculate our full-context core opts & override.
1034
                 */
1035
0
                core_opts_merge(sec_ent[sec_idx], &opts);
1036
1037
                /* If we merged this same section last time, reuse it
1038
                 */
1039
0
                if (matches) {
1040
0
                    if (last_walk->matched == sec_ent[sec_idx]) {
1041
0
                        now_merged = last_walk->merged;
1042
0
                        ++last_walk;
1043
0
                        --matches;
1044
0
                        continue;
1045
0
                    }
1046
1047
                    /* We fell out of sync.  This is our own copy of walked,
1048
                     * so truncate the remaining matches and reset remaining.
1049
                     */
1050
0
                    cache->walked->nelts -= matches;
1051
0
                    matches = 0;
1052
0
                    cached = 0;
1053
0
                }
1054
1055
0
                if (now_merged) {
1056
0
                    now_merged = ap_merge_per_dir_configs(r->pool,
1057
0
                                                          now_merged,
1058
0
                                                          sec_ent[sec_idx]);
1059
0
                }
1060
0
                else {
1061
0
                    now_merged = sec_ent[sec_idx];
1062
0
                }
1063
1064
0
                last_walk = (walk_walked_t*)apr_array_push(cache->walked);
1065
0
                last_walk->matched = sec_ent[sec_idx];
1066
0
                last_walk->merged = now_merged;
1067
0
            }
1068
1069
            /* If .htaccess files are enabled, check for one, provided we
1070
             * have reached a real path.
1071
             */
1072
0
            do {  /* Not really a loop, just a break'able code block */
1073
1074
0
                ap_conf_vector_t *htaccess_conf = NULL;
1075
1076
                /* No htaccess in an incomplete root path,
1077
                 * nor if it's disabled
1078
                 */
1079
0
                if (seg < startseg || (!opts.override 
1080
0
                    && apr_is_empty_table(opts.override_list)
1081
0
                    )) {
1082
0
                    break;
1083
0
                }
1084
1085
1086
0
                res = ap_parse_htaccess(&htaccess_conf, r, opts.override,
1087
0
                                        opts.override_opts, opts.override_list,
1088
0
                                        r->filename, sconf->access_name);
1089
0
                if (res) {
1090
0
                    return res;
1091
0
                }
1092
1093
0
                if (!htaccess_conf) {
1094
0
                    break;
1095
0
                }
1096
1097
                /* If we are still here, we found our htaccess.
1098
                 *
1099
                 * Calculate our full-context core opts & override.
1100
                 */
1101
0
                core_opts_merge(htaccess_conf, &opts);
1102
1103
                /* If we merged this same htaccess last time, reuse it...
1104
                 * this wouldn't work except that we cache the htaccess
1105
                 * sections for the lifetime of the request, so we match
1106
                 * the same conf.  Good planning (no, pure luck ;)
1107
                 */
1108
0
                if (matches) {
1109
0
                    if (last_walk->matched == htaccess_conf) {
1110
0
                        now_merged = last_walk->merged;
1111
0
                        ++last_walk;
1112
0
                        --matches;
1113
0
                        break;
1114
0
                    }
1115
1116
                    /* We fell out of sync.  This is our own copy of walked,
1117
                     * so truncate the remaining matches and reset
1118
                     * remaining.
1119
                     */
1120
0
                    cache->walked->nelts -= matches;
1121
0
                    matches = 0;
1122
0
                    cached = 0;
1123
0
                }
1124
1125
0
                if (now_merged) {
1126
0
                    now_merged = ap_merge_per_dir_configs(r->pool,
1127
0
                                                          now_merged,
1128
0
                                                          htaccess_conf);
1129
0
                }
1130
0
                else {
1131
0
                    now_merged = htaccess_conf;
1132
0
                }
1133
1134
0
                last_walk = (walk_walked_t*)apr_array_push(cache->walked);
1135
0
                last_walk->matched = htaccess_conf;
1136
0
                last_walk->merged = now_merged;
1137
1138
0
            } while (0); /* Only one htaccess, not a real loop */
1139
1140
            /* That temporary trailing slash was useful, now drop it.
1141
             */
1142
0
            if (temp_slash) {
1143
0
                r->filename[--filename_len] = '\0';
1144
0
            }
1145
1146
            /* Time for all good things to come to an end?
1147
             */
1148
0
            if (!r->path_info || !*r->path_info) {
1149
0
                break;
1150
0
            }
1151
1152
            /* Now it's time for the next segment...
1153
             * We will assume the next element is an end node, and fix it up
1154
             * below as necessary...
1155
             */
1156
1157
0
            seg_name = r->filename + filename_len;
1158
0
            delim = strchr(r->path_info + (*r->path_info == '/' ? 1 : 0), '/');
1159
0
            if (delim) {
1160
0
                apr_size_t path_info_len = delim - r->path_info;
1161
0
                *delim = '\0';
1162
0
                memcpy(seg_name, r->path_info, path_info_len + 1);
1163
0
                filename_len += path_info_len;
1164
0
                r->path_info = delim;
1165
0
                *delim = '/';
1166
0
            }
1167
0
            else {
1168
0
                apr_size_t path_info_len = strlen(r->path_info);
1169
0
                memcpy(seg_name, r->path_info, path_info_len + 1);
1170
0
                filename_len += path_info_len;
1171
0
                r->path_info += path_info_len;
1172
0
            }
1173
0
            if (*seg_name == '/')
1174
0
                ++seg_name;
1175
1176
            /* If nothing remained but a '/' string, we are finished
1177
             * XXX: NO WE ARE NOT!!!  Now process this puppy!!! */
1178
0
            if (!*seg_name) {
1179
0
                break;
1180
0
            }
1181
1182
            /* First optimization;
1183
             * If...we knew r->filename was a file, and
1184
             * if...we have strict (case-sensitive) filenames, or
1185
             *      we know the canonical_filename matches to _this_ name, and
1186
             * if...we have allowed symlinks
1187
             * skip the lstat and dummy up an APR_DIR value for thisinfo.
1188
             */
1189
0
            if (r->finfo.filetype != APR_NOFILE
1190
#ifdef CASE_BLIND_FILESYSTEM
1191
                && (filename_len <= canonical_len)
1192
#endif
1193
0
                && ((opts.opts & (OPT_SYM_OWNER | OPT_SYM_LINKS)) == OPT_SYM_LINKS))
1194
0
            {
1195
1196
0
                thisinfo.filetype = APR_DIR;
1197
0
                ++seg;
1198
0
                continue;
1199
0
            }
1200
1201
            /* We choose apr_stat with flag APR_FINFO_LINK here, rather that
1202
             * plain apr_stat, so that we capture this path object rather than
1203
             * its target.  We will replace the info with our target's info
1204
             * below.  We especially want the name of this 'link' object, not
1205
             * the name of its target, if we are fixing the filename
1206
             * case/resolving aliases.
1207
             */
1208
0
            rv = ap_run_dirwalk_stat(&thisinfo, r,
1209
0
                                     APR_FINFO_MIN | APR_FINFO_NAME | APR_FINFO_LINK);
1210
1211
0
            if (APR_STATUS_IS_ENOENT(rv)) {
1212
                /* Nothing?  That could be nice.  But our directory
1213
                 * walk is done.
1214
                 */
1215
0
                thisinfo.filetype = APR_NOFILE;
1216
0
                break;
1217
0
            }
1218
0
            else if (APR_STATUS_IS_EACCES(rv)) {
1219
0
                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00035)
1220
0
                              "access to %s denied (filesystem path '%s') "
1221
0
                              "because search permissions are missing on a "
1222
0
                              "component of the path", r->uri, r->filename);
1223
0
                return r->status = HTTP_FORBIDDEN;
1224
0
            }
1225
0
            else if ((rv != APR_SUCCESS && rv != APR_INCOMPLETE)
1226
0
                     || !(thisinfo.valid & APR_FINFO_TYPE)) {
1227
                /* If we hit ENOTDIR, we must have over-optimized, deny
1228
                 * rather than assume not found.
1229
                 */
1230
0
                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00036)
1231
0
                              "access to %s failed (filesystem path '%s')", 
1232
0
                              r->uri, r->filename);
1233
0
                return r->status = HTTP_FORBIDDEN;
1234
0
            }
1235
1236
            /* Fix up the path now if we have a name, and they don't agree
1237
             */
1238
0
            if ((thisinfo.valid & APR_FINFO_NAME)
1239
0
                && strcmp(seg_name, thisinfo.name)) {
1240
                /* TODO: provide users an option that an internal/external
1241
                 * redirect is required here?  We need to walk the URI and
1242
                 * filename in tandem to properly correlate these.
1243
                 */
1244
0
                strcpy(seg_name, thisinfo.name);
1245
0
                filename_len = strlen(r->filename);
1246
0
            }
1247
1248
0
            if (thisinfo.filetype == APR_LNK) {
1249
                /* Is this a possibly acceptable symlink?
1250
                 */
1251
0
                if ((res = resolve_symlink(r->filename, &thisinfo,
1252
0
                                           opts.opts, r->pool)) != OK) {
1253
0
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00037)
1254
0
                                  "Symbolic link not allowed "
1255
0
                                  "or link target not accessible: %s",
1256
0
                                  r->filename);
1257
0
                    return r->status = res;
1258
0
                }
1259
0
            }
1260
1261
            /* Ok, we are done with the link's info, test the real target
1262
             */
1263
0
            if (thisinfo.filetype == APR_REG ||
1264
0
                thisinfo.filetype == APR_NOFILE) {
1265
                /* That was fun, nothing left for us here
1266
                 */
1267
0
                break;
1268
0
            }
1269
0
            else if (thisinfo.filetype != APR_DIR) {
1270
0
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00038)
1271
0
                              "Forbidden: %s doesn't point to "
1272
0
                              "a file or directory",
1273
0
                              r->filename);
1274
0
                return r->status = HTTP_FORBIDDEN;
1275
0
            }
1276
1277
0
            ++seg;
1278
0
        } while (thisinfo.filetype == APR_DIR);
1279
1280
        /* If we have _not_ optimized, this is the time to recover
1281
         * the final stat result.
1282
         */
1283
0
        if (r->finfo.filetype == APR_NOFILE || r->finfo.filetype == APR_LNK) {
1284
0
            r->finfo = thisinfo;
1285
0
        }
1286
1287
        /* Now splice the saved path_info back onto any new path_info
1288
         */
1289
0
        if (save_path_info) {
1290
0
            if (r->path_info && *r->path_info) {
1291
0
                r->path_info = ap_make_full_path(r->pool, r->path_info,
1292
0
                                                 save_path_info);
1293
0
            }
1294
0
            else {
1295
0
                r->path_info = save_path_info;
1296
0
            }
1297
0
        }
1298
1299
        /*
1300
         * Now we'll deal with the regexes, note we pick up sec_idx
1301
         * where we left off (we gave up after we hit entry_core->r)
1302
         */
1303
0
        for (; sec_idx < num_sec; ++sec_idx) {
1304
1305
0
            int nmatch = 0;
1306
0
            int i;
1307
0
            ap_regmatch_t *pmatch = NULL;
1308
1309
0
            core_dir_config *entry_core;
1310
0
            entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
1311
1312
0
            if (!entry_core->r) {
1313
0
                continue;
1314
0
            }
1315
1316
0
            if (entry_core->refs && entry_core->refs->nelts) {
1317
0
                if (!rxpool) {
1318
0
                    apr_pool_create(&rxpool, r->pool);
1319
0
                    apr_pool_tag(rxpool, "directory_walk_rxpool");
1320
0
                }
1321
0
                nmatch = entry_core->refs->nelts;
1322
0
                pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
1323
0
            }
1324
1325
0
            if (ap_regexec(entry_core->r, r->filename, nmatch, pmatch, 0)) {
1326
0
                continue;
1327
0
            }
1328
1329
0
            for (i = 0; i < nmatch; i++) {
1330
0
                if (pmatch[i].rm_so >= 0 && pmatch[i].rm_eo >= 0 &&
1331
0
                    ((const char **)entry_core->refs->elts)[i]) {
1332
0
                    apr_table_setn(r->subprocess_env, 
1333
0
                                   ((const char **)entry_core->refs->elts)[i],
1334
0
                                   apr_pstrndup(r->pool,
1335
0
                                   r->filename + pmatch[i].rm_so,
1336
0
                                   pmatch[i].rm_eo - pmatch[i].rm_so));
1337
0
                }
1338
0
            }
1339
1340
            /* If we haven't already continue'd above, we have a match.
1341
             *
1342
             * Calculate our full-context core opts & override.
1343
             */
1344
0
            core_opts_merge(sec_ent[sec_idx], &opts);
1345
1346
            /* If we merged this same section last time, reuse it
1347
             */
1348
0
            if (matches) {
1349
0
                if (last_walk->matched == sec_ent[sec_idx]) {
1350
0
                    now_merged = last_walk->merged;
1351
0
                    ++last_walk;
1352
0
                    --matches;
1353
0
                    continue;
1354
0
                }
1355
1356
                /* We fell out of sync.  This is our own copy of walked,
1357
                 * so truncate the remaining matches and reset remaining.
1358
                 */
1359
0
                cache->walked->nelts -= matches;
1360
0
                matches = 0;
1361
0
                cached = 0;
1362
0
            }
1363
1364
0
            if (now_merged) {
1365
0
                now_merged = ap_merge_per_dir_configs(r->pool,
1366
0
                                                      now_merged,
1367
0
                                                      sec_ent[sec_idx]);
1368
0
            }
1369
0
            else {
1370
0
                now_merged = sec_ent[sec_idx];
1371
0
            }
1372
1373
0
            last_walk = (walk_walked_t*)apr_array_push(cache->walked);
1374
0
            last_walk->matched = sec_ent[sec_idx];
1375
0
            last_walk->merged = now_merged;
1376
0
        }
1377
1378
0
        if (rxpool) {
1379
0
            apr_pool_destroy(rxpool);
1380
0
        }
1381
1382
        /* Whoops - everything matched in sequence, but either the original
1383
         * walk found some additional matches (which we need to truncate), or
1384
         * this walk found some additional matches.
1385
         */
1386
0
        if (matches) {
1387
0
            cache->walked->nelts -= matches;
1388
0
            cached = 0;
1389
0
        }
1390
0
        else if (cache->walked->nelts > cached_matches) {
1391
0
            cached = 0;
1392
0
        }
1393
0
    }
1394
1395
/* It seems this shouldn't be needed anymore.  We translated the
1396
 x symlink above into a real resource, and should have died up there.
1397
 x Even if we keep this, it needs more thought (maybe an r->file_is_symlink)
1398
 x perhaps it should actually happen in file_walk, so we catch more
1399
 x obscure cases in autoindex subrequests, etc.
1400
 x
1401
 x    * Symlink permissions are determined by the parent.  If the request is
1402
 x    * for a directory then applying the symlink test here would use the
1403
 x    * permissions of the directory as opposed to its parent.  Consider a
1404
 x    * symlink pointing to a dir with a .htaccess disallowing symlinks.  If
1405
 x    * you access /symlink (or /symlink/) you would get a 403 without this
1406
 x    * APR_DIR test.  But if you accessed /symlink/index.html, for example,
1407
 x    * you would *not* get the 403.
1408
 x
1409
 x   if (r->finfo.filetype != APR_DIR
1410
 x       && (res = resolve_symlink(r->filename, r->info, ap_allow_options(r),
1411
 x                                 r->pool))) {
1412
 x       ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03295)
1413
 x                     "Symbolic link not allowed: %s", r->filename);
1414
 x       return res;
1415
 x   }
1416
 */
1417
1418
    /* Save future sub-requestors much angst in processing
1419
     * this subrequest.  If dir_walk couldn't canonicalize
1420
     * the file path, nothing can.
1421
     */
1422
0
    r->canonical_filename = r->filename;
1423
1424
0
    if (r->finfo.filetype == APR_DIR) {
1425
0
        cache->cached = apr_pstrdup(r->pool, r->filename);
1426
0
    }
1427
0
    else {
1428
0
        cache->cached = ap_make_dirstr_parent(r->pool, r->filename);
1429
0
    }
1430
1431
0
    if (cached
1432
0
        && r->per_dir_config == cache->dir_conf_merged) {
1433
0
        r->per_dir_config = cache->per_dir_result;
1434
0
        return OK;
1435
0
    }
1436
1437
0
    cache->dir_conf_tested = sec_ent;
1438
0
    cache->dir_conf_merged = r->per_dir_config;
1439
1440
    /* Merge our cache->dir_conf_merged construct with the r->per_dir_configs,
1441
     * and note the end result to (potentially) skip this step next time.
1442
     */
1443
0
    if (now_merged) {
1444
0
        r->per_dir_config = ap_merge_per_dir_configs(r->pool,
1445
0
                                                     r->per_dir_config,
1446
0
                                                     now_merged);
1447
0
    }
1448
0
    cache->per_dir_result = r->per_dir_config;
1449
1450
0
    return OK;
1451
0
}
1452
1453
1454
AP_DECLARE(int) ap_location_walk(request_rec *r)
1455
0
{
1456
0
    ap_conf_vector_t *now_merged = NULL;
1457
0
    core_server_config *sconf =
1458
0
        ap_get_core_module_config(r->server->module_config);
1459
0
    ap_conf_vector_t **sec_ent = (ap_conf_vector_t **)sconf->sec_url->elts;
1460
0
    int num_sec = sconf->sec_url->nelts;
1461
0
    walk_cache_t *cache;
1462
0
    const char *entry_uri;
1463
0
    int cached;
1464
1465
    /* No tricks here, there are no <Locations > to parse in this vhost.
1466
     * We won't destroy the cache, just in case _this_ redirect is later
1467
     * redirected again to a vhost with <Location > blocks to optimize.
1468
     */
1469
0
    if (!num_sec) {
1470
0
        return OK;
1471
0
    }
1472
1473
0
    cache = prep_walk_cache(AP_NOTE_LOCATION_WALK, r);
1474
0
    cached = (cache->cached != NULL);
1475
1476
   /*
1477
    * When merge_slashes is set to AP_CORE_CONFIG_OFF the slashes in r->uri
1478
    * have not been merged. But for Location walks we always go with merged
1479
    * slashes no matter what merge_slashes is set to.
1480
    */
1481
0
    if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
1482
0
        entry_uri = r->uri;
1483
0
    }
1484
0
    else {
1485
0
        char *uri = apr_pstrdup(r->pool, r->uri);
1486
0
        ap_no2slash(uri);
1487
0
        entry_uri = uri;
1488
0
    }
1489
1490
    /* If we have an cache->cached location that matches r->uri,
1491
     * and the vhost's list of locations hasn't changed, we can skip
1492
     * rewalking the location_walk entries.
1493
     */
1494
0
    if (cached
1495
0
        && (cache->dir_conf_tested == sec_ent)
1496
0
        && (strcmp(entry_uri, cache->cached) == 0)) {
1497
        /* Well this looks really familiar!  If our end-result (per_dir_result)
1498
         * didn't change, we have absolutely nothing to do :)
1499
         * Otherwise (as is the case with most dir_merged/file_merged requests)
1500
         * we must merge our dir_conf_merged onto this new r->per_dir_config.
1501
         */
1502
0
        if (r->per_dir_config == cache->per_dir_result) {
1503
0
            return OK;
1504
0
        }
1505
1506
0
        if (cache->walked->nelts) {
1507
0
            now_merged = ((walk_walked_t*)cache->walked->elts)
1508
0
                                            [cache->walked->nelts - 1].merged;
1509
0
        }
1510
0
    }
1511
0
    else {
1512
        /* We start now_merged from NULL since we want to build
1513
         * a locations list that can be merged to any vhost.
1514
         */
1515
0
        int len, sec_idx;
1516
0
        int matches = cache->walked->nelts;
1517
0
        int cached_matches = matches;
1518
0
        walk_walked_t *last_walk = (walk_walked_t*)cache->walked->elts;
1519
0
        apr_pool_t *rxpool = NULL;
1520
1521
0
        cached &= auth_internal_per_conf;
1522
0
        cache->cached = apr_pstrdup(r->pool, entry_uri);
1523
1524
        /* Go through the location entries, and check for matches.
1525
         * We apply the directive sections in given order, we should
1526
         * really try them with the most general first.
1527
         */
1528
0
        for (sec_idx = 0; sec_idx < num_sec; ++sec_idx) {
1529
1530
0
            core_dir_config *entry_core;
1531
0
            entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
1532
1533
            /* ### const strlen can be optimized in location config parsing */
1534
0
            len = strlen(entry_core->d);
1535
1536
            /* Test the regex, fnmatch or string as appropriate.
1537
             * If it's a strcmp, and the <Location > pattern was
1538
             * not slash terminated, then this uri must be slash
1539
             * terminated (or at the end of the string) to match.
1540
             */
1541
0
            if (entry_core->r) {
1542
1543
0
                int nmatch = 0;
1544
0
                int i;
1545
0
                ap_regmatch_t *pmatch = NULL;
1546
1547
0
                if (entry_core->refs && entry_core->refs->nelts) {
1548
0
                    if (!rxpool) {
1549
0
                        apr_pool_create(&rxpool, r->pool);
1550
0
                        apr_pool_tag(rxpool, "location_walk_rxpool");
1551
0
                    }
1552
0
                    nmatch = entry_core->refs->nelts;
1553
0
                    pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
1554
0
                }
1555
1556
0
                if (ap_regexec(entry_core->r, r->uri, nmatch, pmatch, 0)) {
1557
0
                    continue;
1558
0
                }
1559
1560
0
                for (i = 0; i < nmatch; i++) {
1561
0
                    if (pmatch[i].rm_so >= 0 && pmatch[i].rm_eo >= 0 && 
1562
0
                        ((const char **)entry_core->refs->elts)[i]) {
1563
0
                        apr_table_setn(r->subprocess_env,
1564
0
                                       ((const char **)entry_core->refs->elts)[i],
1565
0
                                       apr_pstrndup(r->pool,
1566
0
                                       r->uri + pmatch[i].rm_so,
1567
0
                                       pmatch[i].rm_eo - pmatch[i].rm_so));
1568
0
                    }
1569
0
                }
1570
1571
0
            }
1572
0
            else {
1573
1574
0
                if ((entry_core->d_is_fnmatch
1575
0
                   ? apr_fnmatch(entry_core->d, cache->cached, APR_FNM_PATHNAME)
1576
0
                   : (strncmp(entry_core->d, cache->cached, len)
1577
0
                      || (len > 0
1578
0
                          && entry_core->d[len - 1] != '/'
1579
0
                          && cache->cached[len] != '/'
1580
0
                          && cache->cached[len] != '\0')))) {
1581
0
                    continue;
1582
0
                }
1583
1584
0
            }
1585
1586
            /* If we merged this same section last time, reuse it
1587
             */
1588
0
            if (matches) {
1589
0
                if (last_walk->matched == sec_ent[sec_idx]) {
1590
0
                    now_merged = last_walk->merged;
1591
0
                    ++last_walk;
1592
0
                    --matches;
1593
0
                    continue;
1594
0
                }
1595
1596
                /* We fell out of sync.  This is our own copy of walked,
1597
                 * so truncate the remaining matches and reset remaining.
1598
                 */
1599
0
                cache->walked->nelts -= matches;
1600
0
                matches = 0;
1601
0
                cached = 0;
1602
0
            }
1603
1604
0
            if (now_merged) {
1605
0
                now_merged = ap_merge_per_dir_configs(r->pool,
1606
0
                                                      now_merged,
1607
0
                                                      sec_ent[sec_idx]);
1608
0
            }
1609
0
            else {
1610
0
                now_merged = sec_ent[sec_idx];
1611
0
            }
1612
1613
0
            last_walk = (walk_walked_t*)apr_array_push(cache->walked);
1614
0
            last_walk->matched = sec_ent[sec_idx];
1615
0
            last_walk->merged = now_merged;
1616
0
        }
1617
1618
0
        if (rxpool) {
1619
0
            apr_pool_destroy(rxpool);
1620
0
        }
1621
1622
        /* Whoops - everything matched in sequence, but either the original
1623
         * walk found some additional matches (which we need to truncate), or
1624
         * this walk found some additional matches.
1625
         */
1626
0
        if (matches) {
1627
0
            cache->walked->nelts -= matches;
1628
0
            cached = 0;
1629
0
        }
1630
0
        else if (cache->walked->nelts > cached_matches) {
1631
0
            cached = 0;
1632
0
        }
1633
0
    }
1634
1635
0
    if (cached
1636
0
        && r->per_dir_config == cache->dir_conf_merged) {
1637
0
        r->per_dir_config = cache->per_dir_result;
1638
0
        return OK;
1639
0
    }
1640
1641
0
    cache->dir_conf_tested = sec_ent;
1642
0
    cache->dir_conf_merged = r->per_dir_config;
1643
1644
    /* Merge our cache->dir_conf_merged construct with the r->per_dir_configs,
1645
     * and note the end result to (potentially) skip this step next time.
1646
     */
1647
0
    if (now_merged) {
1648
0
        r->per_dir_config = ap_merge_per_dir_configs(r->pool,
1649
0
                                                     r->per_dir_config,
1650
0
                                                     now_merged);
1651
0
    }
1652
0
    cache->per_dir_result = r->per_dir_config;
1653
1654
0
    return OK;
1655
0
}
1656
1657
AP_DECLARE(int) ap_file_walk(request_rec *r)
1658
0
{
1659
0
    ap_conf_vector_t *now_merged = NULL;
1660
0
    core_dir_config *dconf = ap_get_core_module_config(r->per_dir_config);
1661
0
    ap_conf_vector_t **sec_ent = NULL;
1662
0
    int num_sec = 0;
1663
0
    walk_cache_t *cache;
1664
0
    const char *test_file;
1665
0
    int cached;
1666
1667
0
    if (dconf->sec_file) {
1668
0
        sec_ent = (ap_conf_vector_t **)dconf->sec_file->elts;
1669
0
        num_sec = dconf->sec_file->nelts;
1670
0
    }
1671
1672
    /* To allow broken modules to proceed, we allow missing filenames to pass.
1673
     * We will catch it later if it's heading for the core handler.
1674
     * directory_walk already posted an INFO note for module debugging.
1675
     */
1676
0
    if (r->filename == NULL) {
1677
0
        return OK;
1678
0
    }
1679
1680
    /* No tricks here, there are just no <Files > to parse in this context.
1681
     * We won't destroy the cache, just in case _this_ redirect is later
1682
     * redirected again to a context containing the same or similar <Files >.
1683
     */
1684
0
    if (!num_sec) {
1685
0
        return OK;
1686
0
    }
1687
1688
0
    cache = prep_walk_cache(AP_NOTE_FILE_WALK, r);
1689
0
    cached = (cache->cached != NULL);
1690
1691
    /* Get the basename .. and copy for the cache just
1692
     * in case r->filename is munged by another module
1693
     */
1694
0
    test_file = strrchr(r->filename, '/');
1695
0
    if (test_file == NULL) {
1696
0
        test_file = apr_pstrdup(r->pool, r->filename);
1697
0
    }
1698
0
    else {
1699
0
        test_file = apr_pstrdup(r->pool, ++test_file);
1700
0
    }
1701
1702
    /* If we have an cache->cached file name that matches test_file,
1703
     * and the directory's list of file sections hasn't changed, we
1704
     * can skip rewalking the file_walk entries.
1705
     */
1706
0
    if (cached
1707
0
        && (cache->dir_conf_tested == sec_ent)
1708
0
        && (strcmp(test_file, cache->cached) == 0)) {
1709
        /* Well this looks really familiar!  If our end-result (per_dir_result)
1710
         * didn't change, we have absolutely nothing to do :)
1711
         * Otherwise (as is the case with most dir_merged requests)
1712
         * we must merge our dir_conf_merged onto this new r->per_dir_config.
1713
         */
1714
0
        if (r->per_dir_config == cache->per_dir_result) {
1715
0
            return OK;
1716
0
        }
1717
1718
0
        if (cache->walked->nelts) {
1719
0
            now_merged = ((walk_walked_t*)cache->walked->elts)
1720
0
                [cache->walked->nelts - 1].merged;
1721
0
        }
1722
0
    }
1723
0
    else {
1724
        /* We start now_merged from NULL since we want to build
1725
         * a file section list that can be merged to any dir_walk.
1726
         */
1727
0
        int sec_idx;
1728
0
        int matches = cache->walked->nelts;
1729
0
        int cached_matches = matches;
1730
0
        walk_walked_t *last_walk = (walk_walked_t*)cache->walked->elts;
1731
0
        apr_pool_t *rxpool = NULL;
1732
1733
0
        cached &= auth_internal_per_conf;
1734
0
        cache->cached = test_file;
1735
1736
        /* Go through the location entries, and check for matches.
1737
         * We apply the directive sections in given order, we should
1738
         * really try them with the most general first.
1739
         */
1740
0
        for (sec_idx = 0; sec_idx < num_sec; ++sec_idx) {
1741
0
            core_dir_config *entry_core;
1742
0
            entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
1743
1744
0
            if (entry_core->r) {
1745
1746
0
                int nmatch = 0;
1747
0
                int i;
1748
0
                ap_regmatch_t *pmatch = NULL;
1749
1750
0
                if (entry_core->refs && entry_core->refs->nelts) {
1751
0
                    if (!rxpool) {
1752
0
                        apr_pool_create(&rxpool, r->pool);
1753
0
                        apr_pool_tag(rxpool, "file_walk_rxpool");
1754
0
                    }
1755
0
                    nmatch = entry_core->refs->nelts;
1756
0
                    pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
1757
0
                }
1758
1759
0
                if (ap_regexec(entry_core->r, cache->cached, nmatch, pmatch, 0)) {
1760
0
                    continue;
1761
0
                }
1762
1763
0
                for (i = 0; i < nmatch; i++) {
1764
0
                    if (pmatch[i].rm_so >= 0 && pmatch[i].rm_eo >= 0 && 
1765
0
                        ((const char **)entry_core->refs->elts)[i]) {
1766
0
                        apr_table_setn(r->subprocess_env,
1767
0
                                       ((const char **)entry_core->refs->elts)[i],
1768
0
                                       apr_pstrndup(r->pool,
1769
0
                                       cache->cached + pmatch[i].rm_so,
1770
0
                                       pmatch[i].rm_eo - pmatch[i].rm_so));
1771
0
                    }
1772
0
                }
1773
1774
0
            }
1775
0
            else {
1776
0
                if ((entry_core->d_is_fnmatch
1777
0
                       ? apr_fnmatch(entry_core->d, cache->cached, APR_FNM_PATHNAME)
1778
0
                       : strcmp(entry_core->d, cache->cached))) {
1779
0
                    continue;
1780
0
                }
1781
0
            }
1782
1783
            /* If we merged this same section last time, reuse it
1784
             */
1785
0
            if (matches) {
1786
0
                if (last_walk->matched == sec_ent[sec_idx]) {
1787
0
                    now_merged = last_walk->merged;
1788
0
                    ++last_walk;
1789
0
                    --matches;
1790
0
                    continue;
1791
0
                }
1792
1793
                /* We fell out of sync.  This is our own copy of walked,
1794
                 * so truncate the remaining matches and reset remaining.
1795
                 */
1796
0
                cache->walked->nelts -= matches;
1797
0
                matches = 0;
1798
0
                cached = 0;
1799
0
            }
1800
1801
0
            if (now_merged) {
1802
0
                now_merged = ap_merge_per_dir_configs(r->pool,
1803
0
                                                      now_merged,
1804
0
                                                      sec_ent[sec_idx]);
1805
0
            }
1806
0
            else {
1807
0
                now_merged = sec_ent[sec_idx];
1808
0
            }
1809
1810
0
            last_walk = (walk_walked_t*)apr_array_push(cache->walked);
1811
0
            last_walk->matched = sec_ent[sec_idx];
1812
0
            last_walk->merged = now_merged;
1813
0
        }
1814
1815
0
        if (rxpool) {
1816
0
            apr_pool_destroy(rxpool);
1817
0
        }
1818
1819
        /* Whoops - everything matched in sequence, but either the original
1820
         * walk found some additional matches (which we need to truncate), or
1821
         * this walk found some additional matches.
1822
         */
1823
0
        if (matches) {
1824
0
            cache->walked->nelts -= matches;
1825
0
            cached = 0;
1826
0
        }
1827
0
        else if (cache->walked->nelts > cached_matches) {
1828
0
            cached = 0;
1829
0
        }
1830
0
    }
1831
1832
0
    if (cached
1833
0
        && r->per_dir_config == cache->dir_conf_merged) {
1834
0
        r->per_dir_config = cache->per_dir_result;
1835
0
        return OK;
1836
0
    }
1837
1838
0
    cache->dir_conf_tested = sec_ent;
1839
0
    cache->dir_conf_merged = r->per_dir_config;
1840
1841
    /* Merge our cache->dir_conf_merged construct with the r->per_dir_configs,
1842
     * and note the end result to (potentially) skip this step next time.
1843
     */
1844
0
    if (now_merged) {
1845
0
        r->per_dir_config = ap_merge_per_dir_configs(r->pool,
1846
0
                                                     r->per_dir_config,
1847
0
                                                     now_merged);
1848
0
    }
1849
0
    cache->per_dir_result = r->per_dir_config;
1850
1851
0
    return OK;
1852
0
}
1853
1854
static int ap_if_walk_sub(request_rec *r, core_dir_config* dconf)
1855
0
{
1856
0
    ap_conf_vector_t *now_merged = NULL;
1857
0
    ap_conf_vector_t **sec_ent = NULL;
1858
0
    int num_sec = 0;
1859
0
    walk_cache_t *cache;
1860
0
    int cached;
1861
0
    int sec_idx;
1862
0
    int matches;
1863
0
    int cached_matches;
1864
0
    int prev_result = -1;
1865
0
    walk_walked_t *last_walk;
1866
1867
0
    if (dconf && dconf->sec_if) {
1868
0
        sec_ent = (ap_conf_vector_t **)dconf->sec_if->elts;
1869
0
        num_sec = dconf->sec_if->nelts;
1870
0
    }
1871
1872
    /* No tricks here, there are just no <If > to parse in this context.
1873
     * We won't destroy the cache, just in case _this_ redirect is later
1874
     * redirected again to a context containing the same or similar <If >.
1875
     */
1876
0
    if (!num_sec) {
1877
0
        return OK;
1878
0
    }
1879
1880
0
    cache = prep_walk_cache(AP_NOTE_IF_WALK, r);
1881
0
    cached = (cache->cached != NULL);
1882
0
    cache->cached = (void *)1;
1883
0
    matches = cache->walked->nelts;
1884
0
    cached_matches = matches;
1885
0
    last_walk = (walk_walked_t*)cache->walked->elts;
1886
1887
0
    cached &= auth_internal_per_conf;
1888
1889
    /* Go through the if entries, and check for matches  */
1890
0
    for (sec_idx = 0; sec_idx < num_sec; ++sec_idx) {
1891
0
        const char *err = NULL;
1892
0
        core_dir_config *entry_core;
1893
0
        int rc;
1894
0
        entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
1895
1896
0
        AP_DEBUG_ASSERT(entry_core->condition_ifelse != 0);
1897
0
        if (entry_core->condition_ifelse & AP_CONDITION_ELSE) {
1898
0
            AP_DEBUG_ASSERT(prev_result != -1);
1899
0
            if (prev_result == 1)
1900
0
                continue;
1901
0
        }
1902
1903
0
        if (entry_core->condition_ifelse & AP_CONDITION_IF) {
1904
0
            rc = ap_expr_exec(r, entry_core->condition, &err);
1905
0
            if (rc <= 0) {
1906
0
                if (rc < 0)
1907
0
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00039)
1908
0
                                  "Failed to evaluate <If > condition: %s",
1909
0
                                  err);
1910
0
                prev_result = 0;
1911
0
                continue;
1912
0
            }
1913
0
            prev_result = 1;
1914
0
        }
1915
0
        else {
1916
0
            prev_result = -1;
1917
0
        }
1918
1919
        /* If we merged this same section last time, reuse it
1920
         */
1921
0
        if (matches) {
1922
0
            if (last_walk->matched == sec_ent[sec_idx]) {
1923
0
                now_merged = last_walk->merged;
1924
0
                ++last_walk;
1925
0
                --matches;
1926
0
                continue;
1927
0
            }
1928
1929
            /* We fell out of sync.  This is our own copy of walked,
1930
             * so truncate the remaining matches and reset remaining.
1931
             */
1932
0
            cache->walked->nelts -= matches;
1933
0
            matches = 0;
1934
0
            cached = 0;
1935
0
        }
1936
1937
0
        if (now_merged) {
1938
0
            now_merged = ap_merge_per_dir_configs(r->pool,
1939
0
                                                  now_merged,
1940
0
                                                  sec_ent[sec_idx]);
1941
0
        }
1942
0
        else {
1943
0
            now_merged = sec_ent[sec_idx];
1944
0
        }
1945
1946
0
        last_walk = (walk_walked_t*)apr_array_push(cache->walked);
1947
0
        last_walk->matched = sec_ent[sec_idx];
1948
0
        last_walk->merged = now_merged;
1949
0
    }
1950
1951
    /* Everything matched in sequence, but it may be that the original
1952
     * walk found some additional matches (which we need to truncate), or
1953
     * this walk found some additional matches.
1954
     */
1955
0
    if (matches) {
1956
0
        cache->walked->nelts -= matches;
1957
0
        cached = 0;
1958
0
    }
1959
0
    else if (cache->walked->nelts > cached_matches) {
1960
0
        cached = 0;
1961
0
    }
1962
1963
0
    if (cached
1964
0
        && r->per_dir_config == cache->dir_conf_merged) {
1965
0
        r->per_dir_config = cache->per_dir_result;
1966
0
        return OK;
1967
0
    }
1968
1969
0
    cache->dir_conf_tested = sec_ent;
1970
0
    cache->dir_conf_merged = r->per_dir_config;
1971
1972
    /* Merge our cache->dir_conf_merged construct with the r->per_dir_configs,
1973
     * and note the end result to (potentially) skip this step next time.
1974
     */
1975
0
    if (now_merged) {
1976
0
        r->per_dir_config = ap_merge_per_dir_configs(r->pool,
1977
0
                                                     r->per_dir_config,
1978
0
                                                     now_merged);
1979
0
    }
1980
0
    cache->per_dir_result = r->per_dir_config;
1981
1982
0
    if (now_merged) {
1983
0
        core_dir_config *dconf_merged = ap_get_core_module_config(now_merged);
1984
1985
        /* Allow nested <If>s and their configs to get merged
1986
         * with the current one.
1987
         */
1988
0
        return ap_if_walk_sub(r, dconf_merged);
1989
0
    }
1990
1991
0
    return OK;
1992
0
}
1993
1994
AP_DECLARE(int) ap_if_walk(request_rec *r)
1995
0
{
1996
0
    core_dir_config *dconf = ap_get_core_module_config(r->per_dir_config);
1997
0
    int status = ap_if_walk_sub(r, dconf);
1998
0
    return status;
1999
0
}
2000
2001
/*****************************************************************
2002
 *
2003
 * The sub_request mechanism.
2004
 *
2005
 * Fns to look up a relative URI from, e.g., a map file or SSI document.
2006
 * These do all access checks, etc., but don't actually run the transaction
2007
 * ... use run_sub_req below for that.  Also, be sure to use destroy_sub_req
2008
 * as appropriate if you're likely to be creating more than a few of these.
2009
 * (An early Apache version didn't destroy the sub_reqs used in directory
2010
 * indexing.  The result, when indexing a directory with 800-odd files in
2011
 * it, was massively excessive storage allocation).
2012
 *
2013
 * Note more manipulation of protocol-specific vars in the request
2014
 * structure...
2015
 */
2016
2017
static request_rec *make_sub_request(const request_rec *r,
2018
                                     ap_filter_t *next_filter)
2019
0
{
2020
0
    apr_pool_t *rrp;
2021
0
    request_rec *rnew;
2022
2023
0
    apr_pool_create(&rrp, r->pool);
2024
0
    apr_pool_tag(rrp, "subrequest");
2025
0
    rnew = apr_pcalloc(rrp, sizeof(request_rec));
2026
0
    rnew->pool = rrp;
2027
2028
0
    rnew->hostname       = r->hostname;
2029
0
    rnew->request_time   = r->request_time;
2030
0
    rnew->connection     = r->connection;
2031
0
    rnew->server         = r->server;
2032
0
    rnew->log            = r->log;
2033
2034
0
    rnew->request_config = ap_create_request_config(rnew->pool);
2035
2036
    /* Start a clean config from this subrequest's vhost.  Optimization in
2037
     * Location/File/Dir walks from the parent request assure that if the
2038
     * config blocks of the subrequest match the parent request, no merges
2039
     * will actually occur (and generally a minimal number of merges are
2040
     * required, even if the parent and subrequest aren't quite identical.)
2041
     */
2042
0
    rnew->per_dir_config = r->server->lookup_defaults;
2043
2044
0
    rnew->htaccess = r->htaccess;
2045
0
    rnew->allowed_methods = ap_make_method_list(rnew->pool, 2);
2046
2047
    /* make a copy of the allowed-methods list */
2048
0
    ap_copy_method_list(rnew->allowed_methods, r->allowed_methods);
2049
2050
    /* start with the same set of output filters */
2051
0
    if (next_filter) {
2052
0
        ap_filter_t *scan = next_filter;
2053
2054
        /* while there are no input filters for a subrequest, we will
2055
         * try to insert some, so if we don't have valid data, the code
2056
         * will seg fault.
2057
         */
2058
0
        rnew->input_filters = r->input_filters;
2059
0
        rnew->proto_input_filters = r->proto_input_filters;
2060
0
        rnew->output_filters = next_filter;
2061
0
        rnew->proto_output_filters = r->proto_output_filters;
2062
0
        while (scan && (scan != r->proto_output_filters)) {
2063
0
            if (scan->frec == ap_subreq_core_filter_handle) {
2064
0
                break;
2065
0
            }
2066
0
            scan = scan->next;
2067
0
        }
2068
0
        if (!scan || scan == r->proto_output_filters) {
2069
0
            ap_add_output_filter_handle(ap_subreq_core_filter_handle,
2070
0
                    NULL, rnew, rnew->connection);
2071
0
        }
2072
0
    }
2073
0
    else {
2074
        /* If NULL - we are expecting to be internal_fast_redirect'ed
2075
         * to this subrequest - or this request will never be invoked.
2076
         * Ignore the original request filter stack entirely, and
2077
         * drill the input and output stacks back to the connection.
2078
         */
2079
0
        rnew->proto_input_filters = r->proto_input_filters;
2080
0
        rnew->proto_output_filters = r->proto_output_filters;
2081
2082
0
        rnew->input_filters = r->proto_input_filters;
2083
0
        rnew->output_filters = r->proto_output_filters;
2084
0
    }
2085
2086
0
    rnew->useragent_addr = r->useragent_addr;
2087
0
    rnew->useragent_ip = r->useragent_ip;
2088
2089
    /* no input filters for a subrequest */
2090
2091
0
    ap_set_sub_req_protocol(rnew, r);
2092
2093
    /* We have to run this after we fill in sub req vars,
2094
     * or the r->main pointer won't be setup
2095
     */
2096
0
    ap_run_create_request(rnew);
2097
2098
    /* Begin by presuming any module can make its own path_info assumptions,
2099
     * until some module interjects and changes the value.
2100
     */
2101
0
    rnew->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
2102
2103
    /* Pass on the kept body (if any) into the new request. */
2104
0
    rnew->kept_body = r->kept_body;
2105
2106
0
    return rnew;
2107
0
}
2108
2109
AP_CORE_DECLARE_NONSTD(apr_status_t) ap_sub_req_output_filter(ap_filter_t *f,
2110
                                                              apr_bucket_brigade *bb)
2111
0
{
2112
0
    apr_bucket *e = APR_BRIGADE_LAST(bb);
2113
2114
0
    if (APR_BUCKET_IS_EOS(e)) {
2115
0
        apr_bucket_delete(e);
2116
0
    }
2117
2118
0
    if (!APR_BRIGADE_EMPTY(bb)) {
2119
0
        return ap_pass_brigade(f->next, bb);
2120
0
    }
2121
2122
0
    return APR_SUCCESS;
2123
0
}
2124
2125
extern APR_OPTIONAL_FN_TYPE(authz_some_auth_required) *ap__authz_ap_some_auth_required;
2126
2127
AP_DECLARE(int) ap_some_auth_required(request_rec *r)
2128
0
{
2129
    /* Is there a require line configured for the type of *this* req? */
2130
0
    if (ap__authz_ap_some_auth_required) {
2131
0
        return ap__authz_ap_some_auth_required(r);
2132
0
    }
2133
0
    else
2134
0
        return 0;
2135
0
}
2136
2137
AP_DECLARE(void) ap_clear_auth_internal(void)
2138
0
{
2139
0
    auth_internal_per_conf_hooks = 0;
2140
0
    auth_internal_per_conf_providers = 0;
2141
0
}
2142
2143
AP_DECLARE(void) ap_setup_auth_internal(apr_pool_t *ptemp)
2144
0
{
2145
0
    int total_auth_hooks = 0;
2146
0
    int total_auth_providers = 0;
2147
2148
0
    auth_internal_per_conf = 0;
2149
2150
0
    if (_hooks.link_access_checker) {
2151
0
        total_auth_hooks += _hooks.link_access_checker->nelts;
2152
0
    }
2153
0
    if (_hooks.link_access_checker_ex) {
2154
0
        total_auth_hooks += _hooks.link_access_checker_ex->nelts;
2155
0
    }
2156
0
    if (_hooks.link_check_user_id) {
2157
0
        total_auth_hooks += _hooks.link_check_user_id->nelts;
2158
0
    }
2159
0
    if (_hooks.link_auth_checker) {
2160
0
        total_auth_hooks += _hooks.link_auth_checker->nelts;
2161
0
    }
2162
2163
0
    if (total_auth_hooks > auth_internal_per_conf_hooks) {
2164
0
        return;
2165
0
    }
2166
2167
0
    total_auth_providers +=
2168
0
        ap_list_provider_names(ptemp, AUTHN_PROVIDER_GROUP,
2169
0
                               AUTHN_PROVIDER_VERSION)->nelts;
2170
0
    total_auth_providers +=
2171
0
        ap_list_provider_names(ptemp, AUTHZ_PROVIDER_GROUP,
2172
0
                               AUTHZ_PROVIDER_VERSION)->nelts;
2173
2174
0
    if (total_auth_providers > auth_internal_per_conf_providers) {
2175
0
        return;
2176
0
    }
2177
2178
0
    auth_internal_per_conf = 1;
2179
0
}
2180
2181
AP_DECLARE(apr_status_t) ap_register_auth_provider(apr_pool_t *pool,
2182
                                                   const char *provider_group,
2183
                                                   const char *provider_name,
2184
                                                   const char *provider_version,
2185
                                                   const void *provider,
2186
                                                   int type)
2187
0
{
2188
0
    if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
2189
0
        ++auth_internal_per_conf_providers;
2190
0
    }
2191
2192
0
    return ap_register_provider(pool, provider_group, provider_name,
2193
0
                                provider_version, provider);
2194
0
}
2195
2196
AP_DECLARE(void) ap_hook_check_access(ap_HOOK_access_checker_t *pf,
2197
                                      const char * const *aszPre,
2198
                                      const char * const *aszSucc,
2199
                                      int nOrder, int type)
2200
0
{
2201
0
    if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
2202
0
        ++auth_internal_per_conf_hooks;
2203
0
    }
2204
2205
0
    ap_hook_access_checker(pf, aszPre, aszSucc, nOrder);
2206
0
}
2207
2208
AP_DECLARE(void) ap_hook_check_access_ex(ap_HOOK_access_checker_ex_t *pf,
2209
                                      const char * const *aszPre,
2210
                                      const char * const *aszSucc,
2211
                                      int nOrder, int type)
2212
0
{
2213
0
    if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
2214
0
        ++auth_internal_per_conf_hooks;
2215
0
    }
2216
2217
0
    ap_hook_access_checker_ex(pf, aszPre, aszSucc, nOrder);
2218
0
}
2219
2220
AP_DECLARE(void) ap_hook_check_authn(ap_HOOK_check_user_id_t *pf,
2221
                                     const char * const *aszPre,
2222
                                     const char * const *aszSucc,
2223
                                     int nOrder, int type)
2224
0
{
2225
0
    if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
2226
0
        ++auth_internal_per_conf_hooks;
2227
0
    }
2228
2229
0
    ap_hook_check_user_id(pf, aszPre, aszSucc, nOrder);
2230
0
}
2231
2232
AP_DECLARE(void) ap_hook_check_authz(ap_HOOK_auth_checker_t *pf,
2233
                                     const char * const *aszPre,
2234
                                     const char * const *aszSucc,
2235
                                     int nOrder, int type)
2236
0
{
2237
0
    if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
2238
0
        ++auth_internal_per_conf_hooks;
2239
0
    }
2240
2241
0
    ap_hook_auth_checker(pf, aszPre, aszSucc, nOrder);
2242
0
}
2243
2244
AP_DECLARE(request_rec *) ap_sub_req_method_uri(const char *method,
2245
                                                const char *new_uri,
2246
                                                const request_rec *r,
2247
                                                ap_filter_t *next_filter)
2248
0
{
2249
0
    request_rec *rnew;
2250
0
    int res = DECLINED;
2251
0
    char *udir;
2252
2253
0
    rnew = make_sub_request(r, next_filter);
2254
2255
    /* would be nicer to pass "method" to ap_set_sub_req_protocol */
2256
0
    rnew->method = method;
2257
0
    rnew->method_number = ap_method_number_of(method);
2258
2259
0
    if (new_uri[0] == '/') {
2260
0
        ap_parse_uri(rnew, new_uri);
2261
0
    }
2262
0
    else {
2263
0
        udir = ap_make_dirstr_parent(rnew->pool, r->uri);
2264
0
        udir = ap_escape_uri(rnew->pool, udir);    /* re-escape it */
2265
0
        ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_uri));
2266
0
    }
2267
0
    if (ap_is_HTTP_ERROR(rnew->status)) {
2268
0
        return rnew;
2269
0
    }
2270
2271
    /* We cannot return NULL without violating the API. So just turn this
2272
     * subrequest into a 500 to indicate the failure. */
2273
0
    if (ap_is_recursion_limit_exceeded(r)) {
2274
0
        rnew->status = HTTP_INTERNAL_SERVER_ERROR;
2275
0
        return rnew;
2276
0
    }
2277
2278
    /* lookup_uri
2279
     * If the content can be served by the quick_handler, we can
2280
     * safely bypass request_internal processing.
2281
     *
2282
     * If next_filter is NULL we are expecting to be
2283
     * internal_fast_redirect'ed to the subrequest, or the subrequest will
2284
     * never be invoked. We need to make sure that the quickhandler is not
2285
     * invoked by any lookups. Since an internal_fast_redirect will always
2286
     * occur too late for the quickhandler to handle the request.
2287
     */
2288
0
    if (next_filter) {
2289
0
        res = ap_run_quick_handler(rnew, 1);
2290
0
    }
2291
0
    if (res == DECLINED) {
2292
0
        res = ap_process_request_internal(rnew);
2293
0
    }
2294
0
    if (res) {
2295
0
        rnew->status = res;
2296
0
    }
2297
2298
0
    return rnew;
2299
0
}
2300
2301
AP_DECLARE(request_rec *) ap_sub_req_lookup_uri(const char *new_uri,
2302
                                                const request_rec *r,
2303
                                                ap_filter_t *next_filter)
2304
0
{
2305
0
    return ap_sub_req_method_uri("GET", new_uri, r, next_filter);
2306
0
}
2307
2308
AP_DECLARE(request_rec *) ap_sub_req_lookup_dirent(const apr_finfo_t *dirent,
2309
                                                   const request_rec *r,
2310
                                                   int subtype,
2311
                                                   ap_filter_t *next_filter)
2312
0
{
2313
0
    request_rec *rnew;
2314
0
    int res;
2315
0
    char *fdir;
2316
0
    char *udir;
2317
2318
0
    rnew = make_sub_request(r, next_filter);
2319
2320
    /* Special case: we are looking at a relative lookup in the same directory.
2321
     * This is 100% safe, since dirent->name just came from the filesystem.
2322
     */
2323
0
    if (r->path_info && *r->path_info) {
2324
        /* strip path_info off the end of the uri to keep it in sync
2325
         * with r->filename, which has already been stripped by directory_walk,
2326
         * merge the dirent->name, and then, if the caller wants us to remerge
2327
         * the original path info, do so.  Note we never fix the path_info back
2328
         * to r->filename, since dir_walk would do so (but we don't expect it
2329
         * to happen in the usual cases)
2330
         */
2331
0
        udir = apr_pstrdup(rnew->pool, r->uri);
2332
0
        udir[ap_find_path_info(udir, r->path_info)] = '\0';
2333
0
        udir = ap_make_dirstr_parent(rnew->pool, udir);
2334
2335
0
        rnew->uri = ap_make_full_path(rnew->pool, udir, dirent->name);
2336
0
        if (subtype == AP_SUBREQ_MERGE_ARGS) {
2337
0
            rnew->uri = ap_make_full_path(rnew->pool, rnew->uri, r->path_info + 1);
2338
0
            rnew->path_info = apr_pstrdup(rnew->pool, r->path_info);
2339
0
        }
2340
0
        rnew->uri = ap_escape_uri(rnew->pool, rnew->uri);
2341
0
    }
2342
0
    else {
2343
0
        udir = ap_make_dirstr_parent(rnew->pool, r->uri);
2344
0
        rnew->uri = ap_escape_uri(rnew->pool, ap_make_full_path(rnew->pool,
2345
0
                                                                udir,
2346
0
                                                                dirent->name));
2347
0
    }
2348
2349
0
    fdir = ap_make_dirstr_parent(rnew->pool, r->filename);
2350
0
    rnew->filename = ap_make_full_path(rnew->pool, fdir, dirent->name);
2351
0
    if (r->canonical_filename == r->filename) {
2352
0
        rnew->canonical_filename = rnew->filename;
2353
0
    }
2354
2355
    /* XXX This is now less relevant; we will do a full location walk
2356
     * these days for this case.  Preserve the apr_stat results, and
2357
     * perhaps we also tag that symlinks were tested and/or found for
2358
     * r->filename.
2359
     */
2360
0
    rnew->per_dir_config = r->server->lookup_defaults;
2361
2362
0
    if ((dirent->valid & APR_FINFO_MIN) != APR_FINFO_MIN) {
2363
        /*
2364
         * apr_dir_read isn't very complete on this platform, so
2365
         * we need another apr_stat (with or without APR_FINFO_LINK
2366
         * depending on whether we allow all symlinks here.)  If this
2367
         * is an APR_LNK that resolves to an APR_DIR, then we will rerun
2368
         * everything anyways... this should be safe.
2369
         */
2370
0
        apr_status_t rv;
2371
0
        if (ap_allow_options(rnew) & OPT_SYM_LINKS) {
2372
0
            if (((rv = apr_stat(&rnew->finfo, rnew->filename,
2373
0
                                APR_FINFO_MIN, rnew->pool)) != APR_SUCCESS)
2374
0
                && (rv != APR_INCOMPLETE)) {
2375
0
                rnew->finfo.filetype = APR_NOFILE;
2376
0
            }
2377
0
        }
2378
0
        else {
2379
0
            if (((rv = apr_stat(&rnew->finfo, rnew->filename,
2380
0
                                APR_FINFO_LINK | APR_FINFO_MIN,
2381
0
                                rnew->pool)) != APR_SUCCESS)
2382
0
                && (rv != APR_INCOMPLETE)) {
2383
0
                rnew->finfo.filetype = APR_NOFILE;
2384
0
            }
2385
0
        }
2386
0
    }
2387
0
    else {
2388
0
        memcpy(&rnew->finfo, dirent, sizeof(apr_finfo_t));
2389
0
    }
2390
2391
0
    if (rnew->finfo.filetype == APR_LNK) {
2392
        /*
2393
         * Resolve this symlink.  We should tie this back to dir_walk's cache
2394
         */
2395
0
        if ((res = resolve_symlink(rnew->filename, &rnew->finfo,
2396
0
                                   ap_allow_options(rnew), rnew->pool))
2397
0
            != OK) {
2398
0
            rnew->status = res;
2399
0
            return rnew;
2400
0
        }
2401
0
    }
2402
2403
0
    if (rnew->finfo.filetype == APR_DIR) {
2404
        /* ap_make_full_path and ap_escape_uri overallocated the buffers
2405
         * by one character to help us out here.
2406
         */
2407
0
        strcat(rnew->filename, "/");
2408
0
        if (!rnew->path_info || !*rnew->path_info) {
2409
0
            strcat(rnew->uri, "/");
2410
0
        }
2411
0
    }
2412
2413
    /* fill in parsed_uri values
2414
     */
2415
0
    if (r->args && *r->args && (subtype == AP_SUBREQ_MERGE_ARGS)) {
2416
0
        ap_parse_uri(rnew, apr_pstrcat(r->pool, rnew->uri, "?",
2417
0
                                       r->args, NULL));
2418
0
    }
2419
0
    else {
2420
0
        ap_parse_uri(rnew, rnew->uri);
2421
0
    }
2422
2423
    /* We cannot return NULL without violating the API. So just turn this
2424
     * subrequest into a 500. */
2425
0
    if (ap_is_recursion_limit_exceeded(r)) {
2426
0
        rnew->status = HTTP_INTERNAL_SERVER_ERROR;
2427
0
        return rnew;
2428
0
    }
2429
2430
0
    if ((res = ap_process_request_internal(rnew))) {
2431
0
        rnew->status = res;
2432
0
    }
2433
2434
0
    return rnew;
2435
0
}
2436
2437
AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
2438
                                                 const request_rec *r,
2439
                                                 ap_filter_t *next_filter)
2440
0
{
2441
0
    request_rec *rnew;
2442
0
    int res;
2443
0
    char *fdir;
2444
0
    apr_size_t fdirlen;
2445
2446
0
    rnew = make_sub_request(r, next_filter);
2447
2448
0
    fdir = ap_make_dirstr_parent(rnew->pool, r->filename);
2449
0
    fdirlen = strlen(fdir);
2450
2451
    /* Translate r->filename, if it was canonical, it stays canonical
2452
     */
2453
0
    if (r->canonical_filename == r->filename) {
2454
0
        rnew->canonical_filename = (char*)(1);
2455
0
    }
2456
2457
0
    if (apr_filepath_merge(&rnew->filename, fdir, new_file,
2458
0
                           APR_FILEPATH_TRUENAME, rnew->pool) != APR_SUCCESS) {
2459
0
        rnew->status = HTTP_FORBIDDEN;
2460
0
        return rnew;
2461
0
    }
2462
2463
0
    if (rnew->canonical_filename) {
2464
0
        rnew->canonical_filename = rnew->filename;
2465
0
    }
2466
2467
    /*
2468
     * Check for a special case... if there are no '/' characters in new_file
2469
     * at all, and the path was the same, then we are looking at a relative
2470
     * lookup in the same directory.  Fixup the URI to match.
2471
     */
2472
2473
0
    if (strncmp(rnew->filename, fdir, fdirlen) == 0
2474
0
        && rnew->filename[fdirlen]
2475
0
        && ap_strchr_c(rnew->filename + fdirlen, '/') == NULL) {
2476
0
        apr_status_t rv;
2477
0
        if (ap_allow_options(rnew) & OPT_SYM_LINKS) {
2478
0
            if (((rv = apr_stat(&rnew->finfo, rnew->filename,
2479
0
                                APR_FINFO_MIN, rnew->pool)) != APR_SUCCESS)
2480
0
                && (rv != APR_INCOMPLETE)) {
2481
0
                rnew->finfo.filetype = APR_NOFILE;
2482
0
            }
2483
0
        }
2484
0
        else {
2485
0
            if (((rv = apr_stat(&rnew->finfo, rnew->filename,
2486
0
                                APR_FINFO_LINK | APR_FINFO_MIN,
2487
0
                                rnew->pool)) != APR_SUCCESS)
2488
0
                && (rv != APR_INCOMPLETE)) {
2489
0
                rnew->finfo.filetype = APR_NOFILE;
2490
0
            }
2491
0
        }
2492
2493
0
        if (r->uri && *r->uri) {
2494
0
            char *udir = ap_make_dirstr_parent(rnew->pool, r->uri);
2495
0
            rnew->uri = ap_make_full_path(rnew->pool, udir,
2496
0
                                          rnew->filename + fdirlen);
2497
0
            ap_parse_uri(rnew, rnew->uri);    /* fill in parsed_uri values */
2498
0
        }
2499
0
        else {
2500
0
            ap_parse_uri(rnew, new_file);        /* fill in parsed_uri values */
2501
0
            rnew->uri = apr_pstrdup(rnew->pool, "");
2502
0
        }
2503
0
    }
2504
0
    else {
2505
        /* XXX: @@@: What should be done with the parsed_uri values?
2506
         * We would be better off stripping down to the 'common' elements
2507
         * of the path, then reassembling the URI as best as we can.
2508
         */
2509
0
        ap_parse_uri(rnew, new_file);        /* fill in parsed_uri values */
2510
        /*
2511
         * XXX: this should be set properly like it is in the same-dir case
2512
         * but it's actually sometimes to impossible to do it... because the
2513
         * file may not have a uri associated with it -djg
2514
         */
2515
0
        rnew->uri = apr_pstrdup(rnew->pool, "");
2516
0
    }
2517
2518
    /* We cannot return NULL without violating the API. So just turn this
2519
     * subrequest into a 500. */
2520
0
    if (ap_is_recursion_limit_exceeded(r)) {
2521
0
        rnew->status = HTTP_INTERNAL_SERVER_ERROR;
2522
0
        return rnew;
2523
0
    }
2524
2525
0
    if ((res = ap_process_request_internal(rnew))) {
2526
0
        rnew->status = res;
2527
0
    }
2528
2529
0
    return rnew;
2530
0
}
2531
2532
AP_DECLARE(int) ap_run_sub_req(request_rec *r)
2533
0
{
2534
0
    int retval = DECLINED;
2535
    /* Run the quick handler if the subrequest is not a dirent or file
2536
     * subrequest
2537
     */
2538
0
    if (!(r->filename && r->finfo.filetype != APR_NOFILE)) {
2539
0
        retval = ap_run_quick_handler(r, 0);
2540
0
    }
2541
0
    if (retval == DECLINED) {
2542
0
        retval = ap_invoke_handler(r);
2543
0
    }
2544
0
    if (retval == DONE) {
2545
0
        retval = OK;
2546
0
    }
2547
0
    ap_finalize_sub_req_protocol(r);
2548
0
    return retval;
2549
0
}
2550
2551
AP_DECLARE(void) ap_destroy_sub_req(request_rec *r)
2552
0
{
2553
    /* Reclaim the space */
2554
0
    apr_pool_destroy(r->pool);
2555
0
}
2556
2557
/*
2558
 * Function to set the r->mtime field to the specified value if it's later
2559
 * than what's already there.
2560
 */
2561
AP_DECLARE(void) ap_update_mtime(request_rec *r, apr_time_t dependency_mtime)
2562
0
{
2563
0
    if (r->mtime < dependency_mtime) {
2564
0
        r->mtime = dependency_mtime;
2565
0
    }
2566
0
}
2567
2568
/*
2569
 * Is it the initial main request, which we only get *once* per HTTP request?
2570
 */
2571
AP_DECLARE(int) ap_is_initial_req(request_rec *r)
2572
0
{
2573
0
    return (r->main == NULL)       /* otherwise, this is a sub-request */
2574
0
           && (r->prev == NULL);   /* otherwise, this is an internal redirect */
2575
0
}
2576