From: Marc Slemko Date: Sat, 11 Jan 1997 This patch fixes a problem in Apache whereby carefully crafted URL's could cause an error for the search for an index.html in a particular directory, thus possibly bypassing the file and returning an index of all content in the directory. This patch fixes it by distinguishing between different error codes returned by a failed stat() call. This patch will bring it up to equivalence with Apache 1.1.3. *** ../../../../work/apache_1.1.2/src/mod_dir.c Sat Jan 11 23:32:39 1997 --- mod_dir.c Sun Jan 12 06:00:27 1997 *************** *** 768,773 **** --- 768,774 ---- (dir_config_rec *)get_module_config (r->per_dir_config, &dir_module); char *names_ptr = d->index_names ? d->index_names : DEFAULT_INDEX; int allow_opts = allow_options (r); + int error_notfound = 0; if (r->uri[0] == '\0' || r->uri[strlen(r->uri)-1] != '/') { char* ifile; *************** *** 808,816 **** return OK; } ! destroy_sub_req (rr); } if (r->method_number != M_GET) return NOT_IMPLEMENTED; /* OK, nothing easy. Trot out the heavy artillery... */ --- 809,832 ---- return OK; } ! /* If the request returned something other than 404 (or 200), ! * it means the module encountered some sort of problem. To be ! * secure, we should return the error, rather than create ! * along a (possibly unsafe) directory index. ! * ! * So we store the error, and if none of the listed files ! * exist, we return the last error response we got, instead ! * of a directory listing. ! */ ! if (rr->status && rr->status != 404 && rr->status != 200) ! error_notfound = rr->status; ! ! destroy_sub_req (rr); } + if (error_notfound) + return error_notfound; + if (r->method_number != M_GET) return NOT_IMPLEMENTED; /* OK, nothing easy. Trot out the heavy artillery... */ *** ../../../../work/apache_1.1.2/src/http_request.c Sat Jan 11 23:32:38 1997 --- http_request.c Sun Jan 12 06:00:22 1997 *************** *** 1,6 **** /* ==================================================================== ! * Copyright (c) 1995 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions --- 1,6 ---- /* ==================================================================== ! * Copyright (c) 1995-1997 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions *************** *** 139,145 **** /* Dealing with the file system to get PATH_INFO */ ! void get_path_info(request_rec *r) { char *cp; char *path = r->filename; --- 139,145 ---- /* Dealing with the file system to get PATH_INFO */ ! int get_path_info(request_rec *r) { char *cp; char *path = r->filename; *************** *** 157,163 **** --- 157,166 ---- /* See if the pathname ending here exists... */ *cp = '\0'; + + errno = 0; rv = stat(path, &r->finfo); + if (cp != end) *cp = '/'; if (!rv) { *************** *** 174,182 **** r->path_info = pstrdup (r->pool, cp); *cp = '\0'; ! return; } else { last_cp = cp; while (--cp > path && *cp != '/') --- 177,192 ---- r->path_info = pstrdup (r->pool, cp); *cp = '\0'; ! return OK; } + #if defined(ENOENT) && defined(ENOTDIR) + else if (errno == ENOENT || errno == ENOTDIR) { + #else + #error Your system apparently does not define ENOENT || ENOTDIR. + #error Removal of these lines opens a security hole if protecting + #error from directory indexes with DirectoryIndex. else { + #endif last_cp = cp; while (--cp > path && *cp != '/') *************** *** 184,191 **** --- 194,209 ---- while (cp > path && cp[-1] == '/') --cp; + } + #if defined(ENOENT) && defined(ENOTDIR) + else { + log_printf(r->server, "access to %s failed for client; unable to determine if index file exists (stat() returned unexpected error[%d])", r->filename, errno); + return FORBIDDEN; } + #endif } + + return OK; } int directory_walk (request_rec *r) *************** *** 261,267 **** no2slash (test_filename); num_dirs = count_dirs(test_filename); ! get_path_info (r); if (S_ISDIR (r->finfo.st_mode)) ++num_dirs; --- 279,288 ---- no2slash (test_filename); num_dirs = count_dirs(test_filename); ! res = get_path_info (r); ! if (res != OK) { ! return res; ! } if (S_ISDIR (r->finfo.st_mode)) ++num_dirs;