]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Revamped ap_directory_walk logic, without a path_info helper is now
authorWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 8 Oct 2001 17:38:52 +0000 (17:38 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 8 Oct 2001 17:38:52 +0000 (17:38 +0000)
  activated.  It may be bumpy for a few days, and we have more optimizations
  to put in place, but it's time to get this in the developer's test code.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@91359 13f79535-47bb-0310-9956-ffa450edef68

server/request.c

index 7ced634d8724001636ebe0f48becac6d75eb0413..ed9a8eb69278f76b58e0cfb8617f281cf4af3fc1 100644 (file)
@@ -415,455 +415,6 @@ static int resolve_symlink(char *d, apr_finfo_t *lfi, int opts, apr_pool_t *p)
     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 */
 
 /*****************************************************************
  *
@@ -1372,8 +923,6 @@ minimerge2:
     return OK;
 }
 
-#endif /* defined REPLACE_PATH_INFO_METHOD */
-
 
 AP_DECLARE(int) ap_location_walk(request_rec *r)
 {
@@ -1820,7 +1369,6 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_dirent(const apr_finfo_t *dirent,
     
     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.  
      */
@@ -1850,7 +1398,6 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_dirent(const apr_finfo_t *dirent,
     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;
@@ -1901,7 +1448,6 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
            && 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,
@@ -1915,7 +1461,7 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
                                                       && (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);