Fix an issue where users can sometimes get spurious 403s on persistent connections (the description in the comments explains the logic). This would particularly hit people with reverse proxies, since these have a higher tendency of accessing things from different vhosts in the same connection. Index: httpd-2.4.2/server/config.c =================================================================== --- httpd-2.4.2.orig/server/config.c +++ httpd-2.4.2/server/config.c @@ -2131,6 +2131,32 @@ AP_CORE_DECLARE(int) ap_parse_htaccess(a else { if (!APR_STATUS_IS_ENOENT(status) && !APR_STATUS_IS_ENOTDIR(status)) { + /* + * If we are in a persistent connection, we might end up in a state + * where we can no longer read .htaccess files because we have already + * setuid(). This can either be because the previous request was for + * another vhost (basically the same problem as when setuid() fails in + * itk.c), or it can be because a .htaccess file is readable only by + * root. + * + * In any case, we don't want to give out a 403, since the request has + * a very real chance of succeeding on a fresh connection (where + * presumably uid=0). Thus, we give up serving the request on this + * TCP connection, and do a hard close of the socket. As long as we're + * in a persistent connection (and there _should_ not be a way this + * would happen on the first request in a connection, save for subrequests, + * which we special-case), this is allowed, as it is what happens on + * a timeout. The browser will simply open a new connection and try + * again (there's of course a performance hit, though, both due to + * the new connection setup and the fork() of a new server child). + */ + if (ap_has_irreversibly_setuid && r->main == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, status, r, + "Couldn't read %s, closing connection.", + filename); + ap_lingering_close(r->connection); + exit(0); + } ap_log_rerror(APLOG_MARK, APLOG_CRIT, status, r, APLOGNO(00529) "%s pcfg_openfile: unable to check htaccess file, " "ensure it is readable and that '%s' " Index: httpd-2.4.2/server/mpm/itk/itk.c =================================================================== --- httpd-2.4.2.orig/server/mpm/itk/itk.c +++ httpd-2.4.2/server/mpm/itk/itk.c @@ -50,6 +50,7 @@ #include "http_config.h" #include "http_core.h" /* for get_remote_host */ #include "http_connection.h" +#include "http_request.h" #include "scoreboard.h" #include "ap_mpm.h" #include "util_mutex.h" @@ -1546,6 +1547,23 @@ static int itk_post_perdir_config(reques return OK; } +/* See the corresponding section in server/config.c for the rationale + * behind this logic. + */ +static apr_status_t itk_dirwalk_stat(apr_finfo_t *finfo, request_rec *r, + apr_int32_t wanted) +{ + apr_status_t status = apr_stat(finfo, r->filename, wanted, r->pool); + if (ap_has_irreversibly_setuid && r->main == NULL && APR_STATUS_IS_EACCES(status)) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, status, r, + "Couldn't read %s, closing connection.", + r->filename); + ap_lingering_close(r->connection); + clean_child_exit(0); + } + return status; +} + static void itk_hooks(apr_pool_t *p) { /* Our open_logs hook function must run before the core's, or stderr @@ -1566,6 +1584,9 @@ static void itk_hooks(apr_pool_t *p) /* set the uid as fast as possible, but not before merging per-dir config */ ap_hook_header_parser(itk_post_perdir_config, NULL, NULL, APR_HOOK_REALLY_FIRST); + + /* replace core_dirwalk_stat so that we can kill the connection on stat() failure */ + ap_hook_dirwalk_stat(itk_dirwalk_stat, NULL, NULL, APR_HOOK_MIDDLE); } static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const char *arg)