File apache2-mod_jk-CVE-2014-8111.patch of Package apache2-mod_jk
Index: tomcat-connectors-1.2.40-src/native/apache-2.0/mod_jk.c
===================================================================
--- tomcat-connectors-1.2.40-src.orig/native/apache-2.0/mod_jk.c
+++ tomcat-connectors-1.2.40-src/native/apache-2.0/mod_jk.c
@@ -2188,9 +2188,11 @@ static const char *jk_set_options(cmd_pa
mask = 0;
- if (action == '-' && !strncasecmp(w, "ForwardURI", strlen("ForwardURI")))
+ if (action == '-' &&
+ (!strncasecmp(w, "ForwardURI", strlen("ForwardURI")) ||
+ !strncasecmp(w, "CollapseSlashes", strlen("CollapseSlashes"))))
return apr_pstrcat(cmd->pool, "JkOptions: Illegal option '-", w,
- "': ForwardURI* options can not be disabled", NULL);
+ "': option can not be disabled", NULL);
if (!strcasecmp(w, "ForwardURICompat")) {
opt = JK_OPT_FWDURICOMPAT;
@@ -2208,6 +2210,18 @@ static const char *jk_set_options(cmd_pa
opt = JK_OPT_FWDURIPROXY;
mask = JK_OPT_FWDURIMASK;
}
+ else if (!strcasecmp(w, "CollapseSlashesAll")) {
+ opt = JK_OPT_COLLAPSEALL;
+ mask = JK_OPT_COLLAPSEMASK;
+ }
+ else if (!strcasecmp(w, "CollapseSlashesNone")) {
+ opt = JK_OPT_COLLAPSENONE;
+ mask = JK_OPT_COLLAPSEMASK;
+ }
+ else if (!strcasecmp(w, "CollapseSlashesUnmount")) {
+ opt = JK_OPT_COLLAPSEUNMOUNT;
+ mask = JK_OPT_COLLAPSEMASK;
+ }
else if (!strcasecmp(w, "ForwardDirectories")) {
opt = JK_OPT_FWDDIRS;
}
@@ -3005,6 +3019,10 @@ static void *merge_jk_config(apr_pool_t
overrides->options |= (base->options & ~base->exclude_options) & ~JK_OPT_FWDURIMASK;
else
overrides->options |= (base->options & ~base->exclude_options);
+ if (overrides->options & JK_OPT_COLLAPSEMASK)
+ overrides->options |= (base->options & ~base->exclude_options) & ~JK_OPT_COLLAPSEMASK;
+ else
+ overrides->options |= (base->options & ~base->exclude_options);
if (base->envvars) {
if (overrides->envvars && overrides->envvars_has_own) {
@@ -3482,6 +3500,8 @@ static int jk_post_config(apr_pool_t * p
uri_worker_map_switch(sconf->uw_map, sconf->log);
uri_worker_map_load(sconf->uw_map, sconf->log);
}
+ if (conf->options & JK_OPT_COLLAPSEMASK)
+ sconf->uw_map->collapse_slashes = conf->options & JK_OPT_COLLAPSEMASK;
}
else {
if (sconf->mountcopy == JK_TRUE) {
Index: tomcat-connectors-1.2.40-src/native/apache-1.3/mod_jk.c
===================================================================
--- tomcat-connectors-1.2.40-src.orig/native/apache-1.3/mod_jk.c
+++ tomcat-connectors-1.2.40-src/native/apache-1.3/mod_jk.c
@@ -2079,9 +2079,11 @@ const char *jk_set_options(cmd_parms * c
mask = 0;
- if (action == '-' && !strncasecmp(w, "ForwardURI", strlen("ForwardURI")))
+ if (action == '-' &&
+ (!strncasecmp(w, "ForwardURI", strlen("ForwardURI")) ||
+ !strncasecmp(w, "CollapseSlashes", strlen("CollapseSlashes"))))
return ap_pstrcat(cmd->pool, "JkOptions: Illegal option '-", w,
- "': ForwardURI* options can not be disabled", NULL);
+ "': option can not be disabled", NULL);
if (!strcasecmp(w, "ForwardURICompat")) {
opt = JK_OPT_FWDURICOMPAT;
@@ -2099,6 +2101,18 @@ const char *jk_set_options(cmd_parms * c
opt = JK_OPT_FWDURIPROXY;
mask = JK_OPT_FWDURIMASK;
}
+ else if (!strcasecmp(w, "CollapseSlashesAll")) {
+ opt = JK_OPT_COLLAPSEALL;
+ mask = JK_OPT_COLLAPSEMASK;
+ }
+ else if (!strcasecmp(w, "CollapseSlashesNone")) {
+ opt = JK_OPT_COLLAPSENONE;
+ mask = JK_OPT_COLLAPSEMASK;
+ }
+ else if (!strcasecmp(w, "CollapseSlashesUnmount")) {
+ opt = JK_OPT_COLLAPSEUNMOUNT;
+ mask = JK_OPT_COLLAPSEMASK;
+ }
else if (!strcasecmp(w, "ForwardDirectories")) {
opt = JK_OPT_FWDDIRS;
}
@@ -2768,6 +2782,10 @@ static void *merge_jk_config(ap_pool * p
overrides->options |= (base->options & ~base->exclude_options) & ~JK_OPT_FWDURIMASK;
else
overrides->options |= (base->options & ~base->exclude_options);
+ if (overrides->options & JK_OPT_COLLAPSEMASK)
+ overrides->options |= (base->options & ~base->exclude_options) & ~JK_OPT_COLLAPSEMASK;
+ else
+ overrides->options |= (base->options & ~base->exclude_options);
if (base->envvars) {
if (overrides->envvars && overrides->envvars_has_own) {
@@ -2988,6 +3006,8 @@ static void jk_init(server_rec * s, ap_p
uri_worker_map_switch(sconf->uw_map, sconf->log);
uri_worker_map_load(sconf->uw_map, sconf->log);
}
+ if (conf->options & JK_OPT_COLLAPSEMASK)
+ sconf->uw_map->collapse_slashes = conf->options & JK_OPT_COLLAPSEMASK;
}
else {
if (sconf->mountcopy == JK_TRUE) {
Index: tomcat-connectors-1.2.40-src/native/iis/jk_isapi_plugin.c
===================================================================
--- tomcat-connectors-1.2.40-src.orig/native/iis/jk_isapi_plugin.c
+++ tomcat-connectors-1.2.40-src/native/iis/jk_isapi_plugin.c
@@ -117,23 +117,27 @@ static char HTTP_WORKER_HEADER_INDEX[RES
#define W3SVC_REGISTRY_KEY "SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters"
#define EXTENSION_URI_TAG "extension_uri"
-#define URI_SELECT_TAG "uri_select"
-#define URI_SELECT_PARSED_VERB "parsed"
-#define URI_SELECT_UNPARSED_VERB "unparsed"
-#define URI_SELECT_ESCAPED_VERB "escaped"
-#define URI_SELECT_PROXY_VERB "proxy"
-#define URI_REWRITE_TAG "rewrite_rule_file"
-#define SHM_SIZE_TAG "shm_size"
-#define WORKER_MOUNT_RELOAD_TAG "worker_mount_reload"
-#define STRIP_SESSION_TAG "strip_session"
-#define AUTH_COMPLETE_TAG "auth_complete"
-#define REJECT_UNSAFE_TAG "reject_unsafe"
-#define WATCHDOG_INTERVAL_TAG "watchdog_interval"
-#define ENABLE_CHUNKED_ENCODING_TAG "enable_chunked_encoding"
-#define ERROR_PAGE_TAG "error_page"
+#define URI_SELECT_TAG "uri_select"
+#define URI_SELECT_PARSED_VERB "parsed"
+#define URI_SELECT_UNPARSED_VERB "unparsed"
+#define URI_SELECT_ESCAPED_VERB "escaped"
+#define URI_SELECT_PROXY_VERB "proxy"
+#define URI_REWRITE_TAG "rewrite_rule_file"
+#define SHM_SIZE_TAG "shm_size"
+#define WORKER_MOUNT_RELOAD_TAG "worker_mount_reload"
+#define STRIP_SESSION_TAG "strip_session"
+#define AUTH_COMPLETE_TAG "auth_complete"
+#define REJECT_UNSAFE_TAG "reject_unsafe"
+#define COLLAPSE_SLASHES_TAG "collapse_slashes"
+#define COLLAPSE_SLASHES_ALL_VERB "all"
+#define COLLAPSE_SLASHES_NONE_VERB "none"
+#define COLLAPSE_SLASHES_UNMOUNT_VERB "unmount"
+#define WATCHDOG_INTERVAL_TAG "watchdog_interval"
+#define ENABLE_CHUNKED_ENCODING_TAG "enable_chunked_encoding"
+#define ERROR_PAGE_TAG "error_page"
-#define LOG_ROTATION_TIME_TAG "log_rotationtime"
-#define LOG_FILESIZE_TAG "log_filesize"
+#define LOG_ROTATION_TIME_TAG "log_rotationtime"
+#define LOG_FILESIZE_TAG "log_filesize"
/* HTTP standard headers */
#define TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE "Transfer-Encoding: chunked"
@@ -500,6 +504,7 @@ static int strip_session = 0;
static int use_auth_notification_flags = 1;
static int chunked_encoding_enabled = JK_FALSE;
static int reject_unsafe = 0;
+static int collapse_slashes = JK_COLLAPSE_DEFAULT;
static volatile int watchdog_interval = 0;
static HANDLE watchdog_handle = NULL;
static char error_page_buf[INTERNET_MAX_URL_LENGTH] = {0};
@@ -2790,6 +2795,7 @@ static int init_jk(char *serverName)
uw_map->reject_unsafe = 1;
else
uw_map->reject_unsafe = 0;
+ uw_map->collapse_slashes = collapse_slashes;
uw_map->reload = worker_mount_reload;
if (worker_mount_file[0]) {
uw_map->fname = worker_mount_file;
@@ -2919,6 +2925,17 @@ int parse_uri_select(const char *uri_sel
return -1;
}
+int parse_collapse_slashes(const char *collapse_slashes)
+{
+ if (!strcasecmp(collapse_slashes, COLLAPSE_SLASHES_ALL_VERB))
+ return JK_OPT_COLLAPSEALL;
+ if (!strcasecmp(collapse_slashes, COLLAPSE_SLASHES_NONE_VERB))
+ return JK_OPT_COLLAPSENONE;
+ if (!strcasecmp(collapse_slashes, COLLAPSE_SLASHES_UNMOUNT_VERB))
+ return JK_OPT_COLLAPSEUNMOUNT;
+ return -1;
+}
+
static int read_registry_init_data(void)
{
char tmpbuf[MAX_PATH];
@@ -3016,7 +3033,18 @@ static int read_registry_init_data(void)
uri_select_option = opt;
}
else {
- goto cleanup;
+ jk_log(logger, JK_LOG_ERROR, "Invalid value '%s' for configuration item '"
+ URI_SELECT_TAG "'", tmpbuf);
+ }
+ }
+ if (get_config_parameter(src, COLLAPSE_SLASHES_TAG, tmpbuf, sizeof(tmpbuf))) {
+ int opt = parse_collapse_slashes(tmpbuf);
+ if (opt >= 0) {
+ collapse_slashes = opt;
+ }
+ else {
+ jk_log(logger, JK_LOG_ERROR, "Invalid value '%s' for configuration item '"
+ COLLAPSE_SLASHES_TAG "'", tmpbuf);
}
}
shm_config_size = get_config_int(src, SHM_SIZE_TAG, -1);
Index: tomcat-connectors-1.2.40-src/native/common/jk_uri_worker_map.c
===================================================================
--- tomcat-connectors-1.2.40-src.orig/native/common/jk_uri_worker_map.c
+++ tomcat-connectors-1.2.40-src/native/common/jk_uri_worker_map.c
@@ -176,9 +176,10 @@ static void uri_worker_map_dump(jk_uri_w
int i, off;
if (JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG, "uri map dump %s: id=%d, index=%d file='%s' reject_unsafe=%d "
- "reload=%d modified=%d checked=%d",
+ "collapse_slashes=%d reload=%d modified=%d checked=%d",
reason, uw_map->id, uw_map->index, STRNULL_FOR_NULL(uw_map->fname),
- uw_map->reject_unsafe, uw_map->reload, uw_map->modified, uw_map->checked);
+ uw_map->reject_unsafe, uw_map->collapse_slashes,
+ uw_map->reload, uw_map->modified, uw_map->checked);
}
for (i = 0; i <= 1; i++) {
jk_log(l, JK_LOG_DEBUG, "generation %d: size=%d nosize=%d capacity=%d",
@@ -244,6 +245,7 @@ int uri_worker_map_alloc(jk_uri_worker_m
uw_map->index = 0;
uw_map->fname = NULL;
uw_map->reject_unsafe = 0;
+ uw_map->collapse_slashes = JK_COLLAPSE_DEFAULT;
uw_map->reload = JK_URIMAP_DEF_RELOAD;
uw_map->modified = 0;
uw_map->checked = 0;
@@ -697,48 +699,42 @@ void parse_rule_extensions(char *rule, r
else if (!strncmp(param, JK_UWMAP_EXTENSION_ACTIVE, strlen(JK_UWMAP_EXTENSION_ACTIVE))) {
if (extensions->active)
jk_log(l, JK_LOG_WARNING,
- "rule extension '%s' only allowed once",
- JK_UWMAP_EXTENSION_ACTIVE);
+ "rule extension '" JK_UWMAP_EXTENSION_ACTIVE "' only allowed once");
else
extensions->active = param + strlen(JK_UWMAP_EXTENSION_ACTIVE);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_DISABLED, strlen(JK_UWMAP_EXTENSION_DISABLED))) {
if (extensions->disabled)
jk_log(l, JK_LOG_WARNING,
- "rule extension '%s' only allowed once",
- JK_UWMAP_EXTENSION_DISABLED);
+ "rule extension '" JK_UWMAP_EXTENSION_DISABLED "' only allowed once");
else
extensions->disabled = param + strlen(JK_UWMAP_EXTENSION_DISABLED);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_STOPPED, strlen(JK_UWMAP_EXTENSION_STOPPED))) {
if (extensions->stopped)
jk_log(l, JK_LOG_WARNING,
- "rule extension '%s' only allowed once",
- JK_UWMAP_EXTENSION_STOPPED);
+ "rule extension '" JK_UWMAP_EXTENSION_STOPPED "' only allowed once");
else
extensions->stopped = param + strlen(JK_UWMAP_EXTENSION_STOPPED);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_FAIL_ON_STATUS, strlen(JK_UWMAP_EXTENSION_FAIL_ON_STATUS))) {
if (extensions->fail_on_status_str)
jk_log(l, JK_LOG_WARNING,
- "rule extension '%s' only allowed once",
- JK_UWMAP_EXTENSION_FAIL_ON_STATUS);
+ "rule extension '" JK_UWMAP_EXTENSION_FAIL_ON_STATUS "' only allowed once");
else
extensions->fail_on_status_str = param + strlen(JK_UWMAP_EXTENSION_FAIL_ON_STATUS);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_SESSION_COOKIE, strlen(JK_UWMAP_EXTENSION_SESSION_COOKIE))) {
if (extensions->session_cookie)
jk_log(l, JK_LOG_WARNING,
- "extension '%s' in uri worker map only allowed once",
- JK_UWMAP_EXTENSION_SESSION_COOKIE);
+ "extension '" JK_UWMAP_EXTENSION_SESSION_COOKIE "' in uri worker map only allowed once");
else
extensions->session_cookie = param + strlen(JK_UWMAP_EXTENSION_SESSION_COOKIE);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_SESSION_PATH, strlen(JK_UWMAP_EXTENSION_SESSION_PATH))) {
if (extensions->session_path)
jk_log(l, JK_LOG_WARNING,
- "extension '%s' in uri worker map only allowed once",
- JK_UWMAP_EXTENSION_SESSION_PATH);
+ "extension '" JK_UWMAP_EXTENSION_SESSION_PATH "' in uri worker map only allowed once");
else {
// Check if the session identifier starts with semicolon.
if (!strcmp(param, JK_UWMAP_EXTENSION_SESSION_PATH)) {
@@ -755,8 +751,7 @@ void parse_rule_extensions(char *rule, r
else if (!strncmp(param, JK_UWMAP_EXTENSION_SET_SESSION_COOKIE, strlen(JK_UWMAP_EXTENSION_SET_SESSION_COOKIE))) {
if (extensions->set_session_cookie)
jk_log(l, JK_LOG_WARNING,
- "extension '%s' in uri worker map only allowed once",
- JK_UWMAP_EXTENSION_SET_SESSION_COOKIE);
+ "extension '" JK_UWMAP_EXTENSION_SET_SESSION_COOKIE "' in uri worker map only allowed once");
else {
int val = atoi(param + strlen(JK_UWMAP_EXTENSION_SET_SESSION_COOKIE));
if (val) {
@@ -770,8 +765,7 @@ void parse_rule_extensions(char *rule, r
else if (!strncmp(param, JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH, strlen(JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH))) {
if (extensions->session_cookie_path)
jk_log(l, JK_LOG_WARNING,
- "extension '%s' in uri worker map only allowed once",
- JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH);
+ "extension '" JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH "' in uri worker map only allowed once");
else
extensions->session_cookie_path = param + strlen(JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH);
}
@@ -1073,12 +1067,12 @@ static int is_nomatch(jk_uri_worker_map_
const char *map_uri_to_worker_ext(jk_uri_worker_map_t *uw_map,
const char *uri, const char *vhost,
rule_extension_t **extensions,
- int *index,
- jk_logger_t *l)
+ int *index, jk_logger_t *l)
{
unsigned int i;
unsigned int vhost_len;
int reject_unsafe;
+ int collapse_slashes;
int rv = -1;
char url[JK_MAX_URI_LEN+1];
@@ -1108,10 +1102,8 @@ const char *map_uri_to_worker_ext(jk_uri
return NULL;
}
}
- /* Make the copy of the provided uri and strip
- * everything after the first ';' char.
- */
reject_unsafe = uw_map->reject_unsafe;
+ collapse_slashes = uw_map->collapse_slashes;
vhost_len = 0;
/*
* In case we got a vhost, we prepend a slash
@@ -1139,6 +1131,9 @@ const char *map_uri_to_worker_ext(jk_uri
}
vhost_len += off;
}
+ /* Make the copy of the provided uri and strip
+ * everything after the first ';' char.
+ */
for (i = 0; i < strlen(uri); i++) {
if (i == JK_MAX_URI_LEN) {
jk_log(l, JK_LOG_WARNING,
@@ -1166,6 +1161,12 @@ const char *map_uri_to_worker_ext(jk_uri
jk_log(l, JK_LOG_DEBUG, "Found session identifier '%s' in url '%s'",
url_rewrite, uri);
}
+ if (collapse_slashes == JK_COLLAPSE_ALL) {
+ /* Remove multiple slashes
+ * No need to copy url, because it is local and
+ * the unchanged url is no longer needed */
+ jk_no2slash(url);
+ }
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Attempting to map URI '%s' from %d maps",
url, IND_THIS(uw_map->size));
@@ -1177,6 +1178,13 @@ const char *map_uri_to_worker_ext(jk_uri
/* In case we found a match, check for the unmounts. */
if (rv >= 0 && IND_THIS(uw_map->nosize)) {
+ if (collapse_slashes == JK_COLLAPSE_UNMOUNT) {
+ /* Remove multiple slashes when looking for
+ * unmount to prevent trivial unmount bypass attack.
+ * No need to copy url, because it is local and
+ * the unchanged url is no longer needed */
+ jk_no2slash(url);
+ }
/* Again first including vhost. */
int rc = is_nomatch(uw_map, url, rv, l);
/* If no unmount was find, try without vhost. */
Index: tomcat-connectors-1.2.40-src/native/common/jk_uri_worker_map.h
===================================================================
--- tomcat-connectors-1.2.40-src.orig/native/common/jk_uri_worker_map.h
+++ tomcat-connectors-1.2.40-src/native/common/jk_uri_worker_map.h
@@ -58,6 +58,11 @@ extern "C"
#define MATCH_TYPE_STOPPED 0x4000
*/
+#define JK_COLLAPSE_ALL 0x0001
+#define JK_COLLAPSE_NONE 0x0002
+#define JK_COLLAPSE_UNMOUNT 0x0003
+#define JK_COLLAPSE_DEFAULT JK_COLLAPSE_UNMOUNT
+
#define SOURCE_TYPE_WORKERDEF 0x0001
#define SOURCE_TYPE_JKMOUNT 0x0002
#define SOURCE_TYPE_URIMAP 0x0003
@@ -168,6 +173,8 @@ struct jk_uri_worker_map
JK_CRIT_SEC cs;
/* should we forward potentially unsafe URLs */
int reject_unsafe;
+ /* how to handle multiple adjacent slashes in URLs */
+ int collapse_slashes;
/* uriworkermap filename */
const char *fname;
/* uriworkermap reload check interval */
Index: tomcat-connectors-1.2.40-src/native/common/jk_util.c
===================================================================
--- tomcat-connectors-1.2.40-src.orig/native/common/jk_util.c
+++ tomcat-connectors-1.2.40-src/native/common/jk_util.c
@@ -2123,6 +2123,25 @@ int jk_wildchar_match(const char *str, c
return (str[x] != '\0');
}
+void jk_no2slash(char *name)
+{
+ char *d, *s;
+
+ s = d = name;
+
+ while (*s) {
+ if ((*d++ = *s) == '/') {
+ do {
+ ++s;
+ } while (*s == '/');
+ }
+ else {
+ ++s;
+ }
+ }
+ *d = '\0';
+}
+
#ifdef _MT_CODE_PTHREAD
jk_pthread_t jk_gettid()
{
Index: tomcat-connectors-1.2.40-src/native/common/jk_util.h
===================================================================
--- tomcat-connectors-1.2.40-src.orig/native/common/jk_util.h
+++ tomcat-connectors-1.2.40-src/native/common/jk_util.h
@@ -244,6 +244,8 @@ int is_http_status_fail(unsigned int htt
int jk_wildchar_match(const char *str, const char *exp, int icase);
+void jk_no2slash(char *name);
+
#define TC32_BRIDGE_TYPE 32
#define TC33_BRIDGE_TYPE 33
#define TC40_BRIDGE_TYPE 40
Index: tomcat-connectors-1.2.40-src/native/common/jk_global.h
===================================================================
--- tomcat-connectors-1.2.40-src.orig/native/common/jk_global.h
+++ tomcat-connectors-1.2.40-src/native/common/jk_global.h
@@ -272,8 +272,12 @@ extern "C"
/* Forward physical tcp peer address instead of
* client address as provided e.g. by httpd mod_remoteip. */
#define JK_OPT_FWDPHYSICAL 0x0800
+#define JK_OPT_COLLAPSEMASK 0x7000
+#define JK_OPT_COLLAPSEALL 0x1000
+#define JK_OPT_COLLAPSENONE 0x2000
+#define JK_OPT_COLLAPSEUNMOUNT 0x4000
-#define JK_OPT_DEFAULT (JK_OPT_FWDURIDEFAULT | JK_OPT_FWDKEYSIZE)
+#define JK_OPT_DEFAULT (JK_OPT_FWDURIDEFAULT | JK_OPT_FWDKEYSIZE | JK_OPT_COLLAPSEUNMOUNT)
/* Check for EBCDIC systems */