return OK;
}
-/* #define REPLACE_PATH_INFO_METHOD
- */
-#ifndef REPLACE_PATH_INFO_METHOD
-
-static int check_symlinks(char *d, int opts, apr_pool_t *p)
-{
-#if defined(OS2)
- /* OS/2 doesn't have symlinks */
- return OK;
-#else
- apr_finfo_t lfi, fi;
- char *lastp;
- int res;
-
- if (opts & OPT_SYM_LINKS)
- return OK;
-
- /*
- * Strip trailing '/', if any, off what we're checking; trailing slashes
- * make some systems follow symlinks to directories even in lstat().
- * After we've done the lstat, put it back. Also, don't bother checking
- * '/' at all...
- *
- * Note that we don't have to worry about multiple slashes here because of
- * no2slash() below...
- */
-
- lastp = d + strlen(d) - 1;
- if (lastp == d)
- return OK; /* Root directory, '/' */
-
- if (*lastp == '/')
- *lastp = '\0';
- else
- lastp = NULL;
-
- res = apr_lstat(&lfi, d, APR_FINFO_TYPE | APR_FINFO_OWNER, p);
-
- if (lastp)
- *lastp = '/';
-
- /*
- * Note that we don't reject accesses to nonexistent files (multiviews or
- * the like may cons up a way to run the transaction anyway)...
- */
-
- if ((res != APR_SUCCESS && res != APR_INCOMPLETE)
- || (lfi.filetype != APR_LNK))
- return OK;
-
- /* OK, it's a symlink. May still be OK with OPT_SYM_OWNER */
-
- if (!(opts & OPT_SYM_OWNER))
- return HTTP_FORBIDDEN;
-
- /* OPT_SYM_OWNER only works if we can get the owner from the file */
-
- if (res != APR_SUCCESS)
- return HTTP_FORBIDDEN;
-
- if (apr_stat(&fi, d, APR_FINFO_OWNER, p) != APR_SUCCESS)
- return HTTP_FORBIDDEN;
-
- /* TODO: replace with an apr_compare_users() fn */
- return (fi.user == lfi.user) ? OK : HTTP_FORBIDDEN;
-
-#endif
-}
-
-
-/* Dealing with the file system to get PATH_INFO
- */
-static int get_path_info(request_rec *r)
-{
- char *cp;
- char *path = r->filename;
- char *end = &path[strlen(path)];
- char *last_cp = NULL;
- int rv;
-#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
- char bStripSlash=1;
-#endif
-
- if (r->finfo.filetype != APR_NOFILE) {
- /* assume path_info already set */
- return OK;
- }
-
-#ifdef HAVE_DRIVE_LETTERS
- /* If the directory is x:\, then we don't want to strip
- * the trailing slash since x: is not a valid directory.
- */
- if (strlen(path) == 3 && path[1] == ':' && path[2] == '/')
- bStripSlash = 0;
-#endif
-
-#ifdef HAVE_UNC_PATHS
- /* If UNC name == //machine/share/, do not
- * advance over the trailing slash. Any other
- * UNC name is OK to strip the slash.
- */
- cp = end;
- if (path[0] == '/' && path[1] == '/' &&
- path[2] != '/' && cp[-1] == '/') {
- char *p;
- int iCount=0;
- p = path;
- while ((p = strchr(p,'/')) != NULL) {
- p++;
- iCount++;
- }
-
- if (iCount == 4)
- bStripSlash = 0;
- }
-#endif
-
-#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
- if (bStripSlash)
-#endif
- /* Advance over trailing slashes ... NOT part of filename
- * if file is not a UNC name (Win32 only).
- */
- for (cp = end; cp > path && cp[-1] == '/'; --cp)
- continue;
-
- while (cp > path) {
-
- /* See if the pathname ending here exists... */
- *cp = '\0';
-
- /* ### We no longer need the test ap_os_is_filename_valid() here
- * since apr_stat isn't a posix thing - it's apr_stat's responsibility
- * to handle whatever path string arrives at its door - by platform
- * and volume restrictions as applicable...
- * TODO: This code becomes even simpler if apr_stat grows
- * an APR_PATHINCOMPLETE result to indicate that we are staring at
- * an partial virtual root. Only OS2/Win32/Netware need apply it :-)
- */
- rv = apr_stat(&r->finfo, path, APR_FINFO_MIN, r->pool);
-
- if (cp != end)
- *cp = '/';
-
- if (rv == APR_SUCCESS || rv == APR_INCOMPLETE) {
- /*
- * Aha! Found something. If it was a directory, we will search
- * contents of that directory for a multi_match, so the PATH_INFO
- * argument starts with the component after that.
- */
- if (r->finfo.filetype == APR_DIR && last_cp) {
- r->finfo.filetype = APR_NOFILE; /* No such file... */
- cp = last_cp;
- }
-
- r->path_info = apr_pstrdup(r->pool, cp);
- *cp = '\0';
- return OK;
- }
-
- if (APR_STATUS_IS_ENOENT(rv) || APR_STATUS_IS_ENOTDIR(rv)) {
- last_cp = cp;
-
- while (--cp > path && *cp != '/')
- continue;
-
- while (cp > path && cp[-1] == '/')
- --cp;
- }
- else {
- if (APR_STATUS_IS_EACCES(rv))
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- "access to %s denied", r->uri);
- else
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- "access to %s failed", r->uri);
- return HTTP_FORBIDDEN;
- }
- }
- return OK;
-}
-
-AP_DECLARE(int) ap_directory_walk(request_rec *r)
-{
- core_server_config *sconf = ap_get_module_config(r->server->module_config,
- &core_module);
- ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
- ap_conf_vector_t **sec_dir = (ap_conf_vector_t **) sconf->sec_dir->elts;
- int num_sec = sconf->sec_dir->nelts;
- char *test_filename;
- char *test_dirname;
- int res;
- unsigned i, num_dirs;
- int j, test_filename_len;
- unsigned iStart = 1;
- ap_conf_vector_t *entry_config;
- ap_conf_vector_t *this_conf;
- core_dir_config *entry_core;
-
- /* "OK" as a response to a real problem is not _OK_, but to allow broken
- * modules to proceed, we will permit the not-a-path filename to pass here.
- * We must catch it later if it's heading for the core handler. Leave an
- * INFO note here for module debugging.
- */
- if (r->filename == NULL || !ap_os_is_path_absolute(r->pool, r->filename)) {
- ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
- "Module bug? Request filename path %s is missing or "
- "or not absolute for uri %s",
- r->filename ? r->filename : "<NULL>", r->uri);
- return OK;
- }
-
- /*
- * Go down the directory hierarchy. Where we have to check for symlinks,
- * do so. Where a .htaccess file has permission to override anything,
- * try to find one. If either of these things fails, we could poke
- * around, see why, and adjust the lookup_rec accordingly --- this might
- * save us a call to get_path_info (with the attendant stat()s); however,
- * for the moment, that's not worth the trouble.
- */
- res = get_path_info(r);
- if (res != OK) {
- return res;
- }
-
- /* XXX Momentary period of extreme danger, Will Robinson.
- * Removed ap_os_canonical_filename. Anybody munging the
- * r->filename better have pre-canonicalized the name that
- * they just changed. Since the two most key functions
- * in the entire server, ap_server_root_relative() and
- * ap_make_full_path now canonicalize as they go.
- *
- * To be very safe, the server is in hyper-paranoid mode.
- * That means that non-canonical paths will be captured and
- * denied. This is very cpu/fs intensive, we need to finish
- * auditing, and remove the paranoia trigger.
- */
- if (r->filename == r->canonical_filename)
-#ifdef NO_LONGER_PARANOID
- test_filename = apr_pstrdup(r->pool, r->filename);
-#else
- {
- if (apr_filepath_merge(&test_filename, "", r->filename,
- APR_FILEPATH_NOTRELATIVE | APR_FILEPATH_TRUENAME,
- r->pool) != APR_SUCCESS
- || strcmp(test_filename, r->filename) != 0) {
- ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
- "Module bug? Filepath: %s is not the canonical %s",
- r->filename, test_filename);
- return HTTP_FORBIDDEN;
- }
- }
-#endif
- else {
- /* Apparently, somebody didn't know to update r->canonical_filename
- * which is lucky, since they didn't canonicalize r->filename either.
- */
- if (apr_filepath_merge(&test_filename, NULL, r->filename,
- APR_FILEPATH_NOTRELATIVE | APR_FILEPATH_TRUENAME,
- r->pool) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
- "Module bug? Filepath: %s is not an absolute path",
- r->filename);
- return HTTP_FORBIDDEN;
- }
- if (strcmp(r->filename, test_filename) != 0)
- r->filename = apr_pstrdup(r->pool, test_filename);
- r->canonical_filename = r->filename;
- }
-
- num_dirs = ap_count_dirs(test_filename);
-
- if ((res = check_safe_file(r))) {
- return res;
- }
-
- test_filename_len = strlen(test_filename);
- if (test_filename[test_filename_len - 1] == '/')
- --num_dirs;
-
- if (r->finfo.filetype == APR_DIR)
- ++num_dirs;
-
- /*
- * We will use test_dirname as scratch space while we build directory
- * names during the walk. Profiling shows directory_walk to be a busy
- * function so we try to avoid allocating lots of extra memory here.
- * We need 2 extra bytes, one for trailing \0 and one because
- * make_dirstr_prefix will add potentially one extra /.
- */
- test_dirname = apr_palloc(r->pool, test_filename_len + 2);
-
- /* XXX The garbage below disappears in the new directory_walk;
- */
-
-#if defined(HAVE_UNC_PATHS)
- /* If the name is a UNC name, then do not perform any true file test
- * against the machine name (start at //machine/share/)
- * This is optimized to use the normal walk (skips the redundant '/' root)
- */
- if (num_dirs > 3 && test_filename[0] == '/' && test_filename[1] == '/')
- iStart = 4;
-#endif
-
-#if defined(NETWARE)
- /* If the name is a fully qualified volume name, then do not perform any
- * true file test on the machine name (start at machine/share:/)
- * XXX: The implementation eludes me at this moment...
- * Does this make sense? Please test!
- */
- if (num_dirs > 1 && strchr(test_filename, '/') < strchr(test_filename, ':'))
- iStart = 2;
-#endif
-
- /* i keeps track of how many segments we are testing
- * j keeps track of which section we're on, see core_reorder_directories
- */
- j = 0;
- for (i = 1; i <= num_dirs; ++i) {
- int overrides_here;
- core_dir_config *core_dir = ap_get_module_config(per_dir_defaults,
- &core_module);
-
- /*
- * XXX: this could be made faster by only copying the next component
- * rather than copying the entire thing all over.
- */
- ap_make_dirstr_prefix(test_dirname, test_filename, i);
-
- /*
- * Do symlink checks first, because they are done with the
- * permissions appropriate to the *parent* directory...
- */
-
- /* Test only real names (after the root) against the real filesystem */
- if ((i > iStart) && (res = check_symlinks(test_dirname, core_dir->opts, r->pool))) {
- ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
- "Symbolic link not allowed: %s", test_dirname);
- return res;
- }
-
- /*
- * Begin *this* level by looking for matching <Directory> sections
- * from access.conf.
- */
-
- for (; j < num_sec; ++j) {
- char *entry_dir;
-
- entry_config = sec_dir[j];
- entry_core = ap_get_module_config(entry_config, &core_module);
- entry_dir = entry_core->d;
-
- if (entry_core->r || entry_core->d_components > i)
- break;
-
- this_conf = NULL;
- /* We will always add in '0' element components, e.g. plain old
- * <Directory >, and <Directory "/"> is classified as zero
- * so that Win32/Netware/OS2 etc all pick that up.
- */
- if (!entry_core->d_components) {
- this_conf = entry_config;
- }
- else if (entry_core->d_is_fnmatch) {
- if (!apr_fnmatch(entry_dir, test_dirname, FNM_PATHNAME)) {
- this_conf = entry_config;
- }
- }
- else if (!strcmp(test_dirname, entry_dir))
- this_conf = entry_config;
-
- if (this_conf) {
- per_dir_defaults = ap_merge_per_dir_configs(r->pool,
- per_dir_defaults,
- this_conf);
- core_dir = ap_get_module_config(per_dir_defaults,
- &core_module);
- }
- }
- overrides_here = core_dir->override;
-
- /* If .htaccess files are enabled, check for one. */
-
- /* Test only legal names against the real filesystem */
- if ((i >= iStart) && overrides_here) {
- ap_conf_vector_t *htaccess_conf = NULL;
-
- res = ap_parse_htaccess(&htaccess_conf, r, overrides_here,
- apr_pstrdup(r->pool, test_dirname),
- sconf->access_name);
- if (res)
- return res;
-
- if (htaccess_conf) {
- per_dir_defaults = ap_merge_per_dir_configs(r->pool,
- per_dir_defaults,
- htaccess_conf);
- r->per_dir_config = per_dir_defaults;
- }
- }
- }
-
- /*
- * Now we'll deal with the regexes.
- */
- for (; j < num_sec; ++j) {
-
- entry_config = sec_dir[j];
- entry_core = ap_get_module_config(entry_config, &core_module);
-
- if (!entry_core->r) {
- continue;
- }
- if (!ap_regexec(entry_core->r, test_dirname, 0, NULL, REG_NOTEOL)) {
- per_dir_defaults = ap_merge_per_dir_configs(r->pool,
- per_dir_defaults,
- entry_config);
- }
- }
- r->per_dir_config = per_dir_defaults;
-
- /*
- * Symlink permissions are determined by the parent. If the request is
- * for a directory then applying the symlink test here would use the
- * permissions of the directory as opposed to its parent. Consider a
- * symlink pointing to a dir with a .htaccess disallowing symlinks. If
- * you access /symlink (or /symlink/) you would get a 403 without this
- * APR_DIR test. But if you accessed /symlink/index.html, for example,
- * you would *not* get the 403.
- */
- if (r->finfo.filetype != APR_DIR
- && (res = check_symlinks(r->filename, ap_allow_options(r), r->pool))) {
- ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
- "Symbolic link not allowed: %s", r->filename);
- return res;
- }
-
- /* Save a dummy userdata element till we optimize this function.
- * If this userdata is set, directory_walk has run.
- */
- apr_pool_userdata_set((void *)1, "ap_directory_walk::cache",
- apr_pool_cleanup_null, r->pool);
-
- return OK; /* Can only "fail" if access denied by the
- * symlink goop. */
-}
-
-#else /* defined REPLACE_PATH_INFO_METHOD */
/*****************************************************************
*
return OK;
}
-#endif /* defined REPLACE_PATH_INFO_METHOD */
-
AP_DECLARE(int) ap_location_walk(request_rec *r)
{
ap_parse_uri(rnew, rnew->uri); /* fill in parsed_uri values */
-#ifdef REPLACE_PATH_INFO_METHOD
/* Preserve the apr_stat results, and perhaps we also tag that
* symlinks were tested and/or found for r->filename.
*/
else {
memcpy (&rnew->finfo, dirent, sizeof(apr_finfo_t));
}
-#endif /* defined REPLACE_PATH_INFO_METHOD */
if ((res = ap_process_request_internal(rnew))) {
rnew->status = res;
&& rnew->filename[fdirlen]
&& ap_strchr_c(rnew->filename + fdirlen, '/') == NULL)
{
-#ifdef REPLACE_PATH_INFO_METHOD
apr_status_t rv;
if (ap_allow_options(rnew) & OPT_SYM_LINKS) {
if (((rv = apr_stat(&rnew->finfo, rnew->filename,
&& (rv != APR_INCOMPLETE))
rnew->finfo.filetype = 0;
}
-#endif /* defined REPLACE_PATH_INFO_METHOD */
+
if (r->uri && *r->uri) {
char *udir = ap_make_dirstr_parent(rnew->pool, r->uri);
rnew->uri = ap_make_full_path(rnew->pool, udir, rnew->filename + fdirlen);