File: CVE-2014-8111.patch

package info (click to toggle)
libapache-mod-jk 1%3A1.2.37-1%2Bdeb7u1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 9,160 kB
  • sloc: ansic: 54,017; xml: 13,906; sh: 9,224; java: 1,921; perl: 1,004; makefile: 203; awk: 59
file content (474 lines) | stat: -rw-r--r-- 21,238 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
From: Markus Koschany <apo@gambaru.de>
Date: Sat, 23 May 2015 00:05:21 +0200
Subject: CVE-2014-8111

It was discovered that a JkUnmount rule for a subtree of a previous JkMount rule
could be ignored. This could allow a remote attacker to potentially access a
private artifact in a tree that would otherwise not be accessible to them.

Forwarded: https://svn.apache.org/viewvc?view=revision&revision=r1647017
---
 native/apache-1.3/mod_jk.c        | 24 +++++++++++++--
 native/apache-2.0/mod_jk.c        | 24 +++++++++++++--
 native/common/jk_global.h         |  7 ++++-
 native/common/jk_uri_worker_map.c | 48 +++++++++++++++++------------
 native/common/jk_uri_worker_map.h |  7 +++++
 native/common/jk_util.c           | 19 ++++++++++++
 native/common/jk_util.h           |  2 ++
 native/iis/jk_isapi_plugin.c      | 64 ++++++++++++++++++++++++++++-----------
 8 files changed, 153 insertions(+), 42 deletions(-)

diff --git a/native/apache-1.3/mod_jk.c b/native/apache-1.3/mod_jk.c
index 81c3a58..9d6840f 100644
--- a/native/apache-1.3/mod_jk.c
+++ b/native/apache-1.3/mod_jk.c
@@ -2074,9 +2074,11 @@ const char *jk_set_options(cmd_parms * cmd, void *dummy, const char *line)
 
         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;
@@ -2094,6 +2096,18 @@ const char *jk_set_options(cmd_parms * cmd, void *dummy, const char *line)
             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;
         }
@@ -2763,6 +2777,10 @@ static void *merge_jk_config(ap_pool * p, void *basev, void *overridesv)
         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) {
@@ -2983,6 +3001,8 @@ static void jk_init(server_rec * s, ap_pool * 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) {
diff --git a/native/apache-2.0/mod_jk.c b/native/apache-2.0/mod_jk.c
index 7c04440..26345ea 100644
--- a/native/apache-2.0/mod_jk.c
+++ b/native/apache-2.0/mod_jk.c
@@ -2175,9 +2175,11 @@ static const char *jk_set_options(cmd_parms * cmd, void *dummy,
 
         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;
@@ -2195,6 +2197,18 @@ static const char *jk_set_options(cmd_parms * cmd, void *dummy,
             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;
         }
@@ -2987,6 +3001,10 @@ static void *merge_jk_config(apr_pool_t * p, void *basev, void *overridesv)
         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) {
@@ -3464,6 +3482,8 @@ static int jk_post_config(apr_pool_t * pconf,
                             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) {
diff --git a/native/common/jk_global.h b/native/common/jk_global.h
index aefe87e..942ee32 100644
--- a/native/common/jk_global.h
+++ b/native/common/jk_global.h
@@ -252,6 +252,11 @@ extern "C"
 
 #define JK_OPT_FWDURIMASK           0x0007
 
+#define JK_OPT_COLLAPSEMASK         0x7000
+#define JK_OPT_COLLAPSEALL          0x1000
+#define JK_OPT_COLLAPSENONE         0x2000
+#define JK_OPT_COLLAPSEUNMOUNT      0x4000
+
 #define JK_OPT_FWDURICOMPAT         0x0001
 #define JK_OPT_FWDURICOMPATUNPARSED 0x0002
 #define JK_OPT_FWDURIESCAPED        0x0003
@@ -269,7 +274,7 @@ extern "C"
 #define JK_OPT_FWDKEYSIZE           0x0200
 #define JK_OPT_REJECTUNSAFE         0x0400
 
-#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 */
 
diff --git a/native/common/jk_uri_worker_map.c b/native/common/jk_uri_worker_map.c
index 250cdb5..8c3d44e 100644
--- a/native/common/jk_uri_worker_map.c
+++ b/native/common/jk_uri_worker_map.c
@@ -174,9 +174,10 @@ static void uri_worker_map_dump(jk_uri_worker_map_t *uw_map,
         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",
@@ -242,6 +243,7 @@ int uri_worker_map_alloc(jk_uri_worker_map_t **uw_map_p,
         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;
@@ -681,48 +683,42 @@ void parse_rule_extensions(char *rule, rule_extension_t *extensions,
             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)) {
@@ -1034,12 +1030,12 @@ static int is_nomatch(jk_uri_worker_map_t *uw_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];
 
@@ -1069,10 +1065,8 @@ const char *map_uri_to_worker_ext(jk_uri_worker_map_t *uw_map,
             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
@@ -1100,6 +1094,9 @@ const char *map_uri_to_worker_ext(jk_uri_worker_map_t *uw_map,
         }
         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,
@@ -1127,6 +1124,12 @@ const char *map_uri_to_worker_ext(jk_uri_worker_map_t *uw_map,
             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));
@@ -1138,6 +1141,13 @@ const char *map_uri_to_worker_ext(jk_uri_worker_map_t *uw_map,
 
     /* 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. */
diff --git a/native/common/jk_uri_worker_map.h b/native/common/jk_uri_worker_map.h
index 1598937..16c14ff 100644
--- a/native/common/jk_uri_worker_map.h
+++ b/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
@@ -166,6 +171,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 */
diff --git a/native/common/jk_util.c b/native/common/jk_util.c
index 8c5d803..4455f86 100644
--- a/native/common/jk_util.c
+++ b/native/common/jk_util.c
@@ -2089,6 +2089,25 @@ int jk_wildchar_match(const char *str, const char *exp, int icase)
     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()
 {
diff --git a/native/common/jk_util.h b/native/common/jk_util.h
index 2313c2c..930943c 100644
--- a/native/common/jk_util.h
+++ b/native/common/jk_util.h
@@ -238,6 +238,8 @@ int is_http_status_fail(unsigned int http_status_fail_num,
 
 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
diff --git a/native/iis/jk_isapi_plugin.c b/native/iis/jk_isapi_plugin.c
index e949734..736ac05 100644
--- a/native/iis/jk_isapi_plugin.c
+++ b/native/iis/jk_isapi_plugin.c
@@ -117,23 +117,27 @@ static char HTTP_WORKER_HEADER_INDEX[RES_BUFFER_SIZE];
 #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 LOG_ROTATION_TIME_TAG       "log_rotationtime"
-#define LOG_FILESIZE_TAG            "log_filesize"
+#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"
 
 /* HTTP standard headers */
 #define TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE     "Transfer-Encoding: chunked"
@@ -501,6 +505,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};
@@ -2791,6 +2796,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;
@@ -2920,6 +2926,17 @@ int parse_uri_select(const char *uri_select)
     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];
@@ -3017,7 +3034,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);