diff --git a/CHANGES b/CHANGES index 2d9f5367c3..1a589ea629 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) Introduce request taint checking framework to prevent privilege + hijacking through .htaccess. [Nick Kew] + *) mod_proxy_wstunnel: Add "upgrade" parameter to allow upgrade to other protocols. [Jean-Frederic Clere] diff --git a/include/ap_mmn.h b/include/ap_mmn.h index fa75033f3a..0967996409 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -551,6 +551,7 @@ * Added ap_scan_vchar_obstext() * 20161018.2 (2.5.0-dev) add ap_set_conn_count() * 20161018.3 (2.5.0-dev) add ap_exists_directive() + * 20161018.4 (2.5.0-dev) Add taint to request_rec and ap_request_tainted() */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -558,7 +559,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20161018 #endif -#define MODULE_MAGIC_NUMBER_MINOR 3 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 4 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/httpd.h b/include/httpd.h index ebb2a15b4c..1a33466aeb 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -1074,6 +1074,11 @@ struct request_rec { * TODO: 2 bit signed bitfield when this structure is compacted */ int double_reverse; + /** Mark the request as potentially tainted. This might become a + * bitfield if we identify different taints to be flagged. + * Always use ap_request_tainted() to check taint. + */ + int taint; }; /** @@ -2153,6 +2158,17 @@ AP_DECLARE(apr_status_t) ap_timeout_parameter_parse( */ AP_DECLARE(int) ap_request_has_body(request_rec *r); +/** Request taint flags. Only .htaccess defined. */ +#define AP_TAINT_HTACCESS 0x1 +/** + * Check whether a request is tainted by potentially-untrusted sources. + * + * @param r the request + * @param flags Taint flags to check + * @return truth value + */ +AP_DECLARE(int) ap_request_tainted(request_rec *r, int flags); + /** * Cleanup a string (mainly to be filesystem safe) * We only allow '_' and alphanumeric chars. Non-printable diff --git a/modules/generators/mod_status.c b/modules/generators/mod_status.c index 12801f94ce..db44e98d2d 100644 --- a/modules/generators/mod_status.c +++ b/modules/generators/mod_status.c @@ -213,6 +213,13 @@ static int status_handler(request_rec *r) return DECLINED; } + /* A request that has passed through .htaccess has no business + * landing up here. + */ + if (ap_request_tainted(r, AP_TAINT_HTACCESS)) { + return DECLINED; + } + #ifdef HAVE_TIMES times_per_thread = getpid() != child_pid; #endif diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index a0b08788cf..b8281e0b54 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -932,6 +932,14 @@ static int proxy_fixup(request_rec *r) if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; + /* A request that has passed through .htaccess has no business + * serving contents from so far outside its directory. + * Since we're going to decline it, don't waste time here. + */ + if (ap_request_tainted(r, AP_TAINT_HTACCESS)) { + return DECLINED; + } + /* XXX: Shouldn't we try this before we run the proxy_walk? */ url = &r->filename[6]; @@ -1025,6 +1033,13 @@ static int proxy_handler(request_rec *r) return DECLINED; } + /* A request that has passed through .htaccess has no business + * serving contents from so far outside its directory. + */ + if (ap_request_tainted(r, AP_TAINT_HTACCESS)) { + return DECLINED; + } + if (!r->proxyreq) { /* We may have forced the proxy handler via config or .htaccess */ if (r->handler && diff --git a/server/config.c b/server/config.c index 475c064e1d..b4326ff775 100644 --- a/server/config.c +++ b/server/config.c @@ -2196,6 +2196,8 @@ AP_CORE_DECLARE(int) ap_parse_htaccess(ap_conf_vector_t **result, const char *errmsg; ap_directive_t *temptree = NULL; + /* Mark the request as tainted by .htaccess */ + r->taint |= AP_TAINT_HTACCESS; dc = ap_create_per_dir_config(r->pool); parms.config_file = f; diff --git a/server/util.c b/server/util.c index ad192562c4..24062ca111 100644 --- a/server/util.c +++ b/server/util.c @@ -2580,6 +2580,21 @@ AP_DECLARE(int) ap_request_has_body(request_rec *r) return has_body; } +/** + * Check whether a request is tainted by exposure to something + * potentially untrusted. + * + */ +AP_DECLARE(int) ap_request_tainted(request_rec *r, int flags) +{ + /** Potential future: a hook or callback here could serve modules + * like mod_security and ironbee with more complex needs. + */ + return r && ((r->taint&flags) + || ap_request_tainted(r->main, flags) + || ap_request_tainted(r->prev, flags)); +} + AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_) { void **ptr = (void **)data_;