Browse code

CVE-2018-8014: Apache-tomcat:- Fix default CORS filter setup

Apache Tomcat default CORS filter setting lets remote users bypass security
restrictions. This fix prevents the remote users from bypassing restrictions.

Change-Id: I7230848e5190e3108c6ac0ea8fe97bcf5fba2e56
Reviewed-on: http://photon-jenkins.eng.vmware.com:8082/5296
Reviewed-by: Keerthana K <keerthanak@vmware.com>
Tested-by: gerrit-photon <photon-checkins@vmware.com>
Reviewed-by: Anish Swaminathan <anishs@vmware.com>

srinidhira0 authored on 2018/06/26 01:49:02
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,253 @@
0
+--- a/java/org/apache/catalina/filters/CorsFilter.java	2018/05/16 14:54:09	1831727
1
+@@ -267,17 +267,14 @@
2
+ 
3
+         // Section 6.1.3
4
+         // Add a single Access-Control-Allow-Origin header.
5
+-        if (anyOriginAllowed && !supportsCredentials) {
6
+-            // If resource doesn't support credentials and if any origin is
7
+-            // allowed
8
+-            // to make CORS request, return header with '*'.
9
++        if (anyOriginAllowed) {
10
++            // If any origin is allowed, return header with '*'.
11
+             response.addHeader(
12
+                     CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
13
+                     "*");
14
+         } else {
15
+-            // If the resource supports credentials add a single
16
+-            // Access-Control-Allow-Origin header, with the value of the Origin
17
+-            // header as value.
18
++            // Add a single Access-Control-Allow-Origin header, with the value
19
++            // of the Origin header as value.
20
+             response.addHeader(
21
+                     CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
22
+                     origin);
23
+@@ -781,6 +778,10 @@
24
+         // For any value other then 'true' this will be false.
25
+         this.supportsCredentials = Boolean.parseBoolean(supportsCredentials);
26
+ 
27
++        if (this.supportsCredentials && this.anyOriginAllowed) {
28
++            throw new ServletException(sm.getString("corsFilter.invalidSupportsCredentials"));
29
++        }
30
++
31
+         try {
32
+             if (!preflightMaxAge.isEmpty()) {
33
+                 this.preflightMaxAge = Long.parseLong(preflightMaxAge);
34
+@@ -1090,7 +1091,7 @@
35
+     /**
36
+      * By default, all origins are allowed to make requests.
37
+      */
38
+-    public static final String DEFAULT_ALLOWED_ORIGINS = "*";
39
++    public static final String DEFAULT_ALLOWED_ORIGINS = "";
40
+ 
41
+     /**
42
+      * By default, following methods are supported: GET, POST, HEAD and OPTIONS.
43
+@@ -1106,7 +1107,7 @@
44
+     /**
45
+      * By default, support credentials is turned on.
46
+      */
47
+-    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";
48
++    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "false";
49
+ 
50
+     /**
51
+      * By default, following headers are supported:
52
+
53
+--- a/java/org/apache/catalina/filters/LocalStrings.properties	2018/05/16 14:54:09	1831727
54
+@@ -14,6 +14,8 @@
55
+ # limitations under the License.
56
+ 
57
+ addDefaultCharset.unsupportedCharset=Specified character set [{0}] is not supported
58
++
59
++corsFilter.invalidSupportsCredentials=It is not allowed to configure supportsCredentials=[true] when allowedOrigins=[*]
60
+ corsFilter.invalidPreflightMaxAge=Unable to parse preflightMaxAge
61
+ corsFilter.nullRequest=HttpServletRequest object is null
62
+ corsFilter.nullRequestType=CORSRequestType object is null
63
+
64
+--- a/test/org/apache/catalina/filters/TestCorsFilter.java	2018/05/16 14:54:09	1831727
65
+@@ -55,8 +55,7 @@
66
+         corsFilter.doFilter(request, response, filterChain);
67
+ 
68
+         Assert.assertTrue(response.getHeader(
69
+-                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
70
+-                "https://www.apache.org"));
71
++                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
72
+         Assert.assertTrue(((Boolean) request.getAttribute(
73
+                 CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
74
+         Assert.assertTrue(request.getAttribute(
75
+@@ -88,8 +87,7 @@
76
+         corsFilter.doFilter(request, response, filterChain);
77
+ 
78
+         Assert.assertTrue(response.getHeader(
79
+-                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
80
+-                "https://www.apache.org"));
81
++                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
82
+         Assert.assertTrue(((Boolean) request.getAttribute(
83
+                 CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
84
+         Assert.assertTrue(request.getAttribute(
85
+@@ -120,8 +118,7 @@
86
+         corsFilter.doFilter(request, response, filterChain);
87
+ 
88
+         Assert.assertTrue(response.getHeader(
89
+-                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
90
+-                "https://www.apache.org"));
91
++                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
92
+         Assert.assertTrue(((Boolean) request.getAttribute(
93
+                 CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
94
+         Assert.assertTrue(request.getAttribute(
95
+@@ -166,41 +163,15 @@
96
+     }
97
+ 
98
+     /*
99
+-     * Tests the presence of the origin (and not '*') in the response, when
100
+-     * supports credentials is enabled alongwith any origin, '*'.
101
++     * Tests the that supports credentials may not be enabled with any origin,
102
++     * '*'.
103
+      *
104
+-     * @throws IOException
105
+      * @throws ServletException
106
+      */
107
+-    @Test
108
+-    public void testDoFilterSimpleAnyOriginAndSupportsCredentials()
109
+-            throws IOException, ServletException {
110
+-        TesterHttpServletRequest request = new TesterHttpServletRequest();
111
+-        request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN,
112
+-                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
113
+-        request.setMethod("GET");
114
+-        TesterHttpServletResponse response = new TesterHttpServletResponse();
115
+-
116
++    @Test(expected=ServletException.class)
117
++    public void testDoFilterSimpleAnyOriginAndSupportsCredentials() throws ServletException {
118
+         CorsFilter corsFilter = new CorsFilter();
119
+-        corsFilter.init(TesterFilterConfigs
120
+-                .getFilterConfigAnyOriginAndSupportsCredentials());
121
+-        corsFilter.doFilter(request, response, filterChain);
122
+-
123
+-        Assert.assertTrue(response.getHeader(
124
+-                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
125
+-                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
126
+-        Assert.assertTrue(response.getHeader(
127
+-                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)
128
+-                .equals(
129
+-                        "true"));
130
+-        Assert.assertTrue(((Boolean) request.getAttribute(
131
+-                CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
132
+-        Assert.assertTrue(request.getAttribute(
133
+-                CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
134
+-                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
135
+-        Assert.assertTrue(request.getAttribute(
136
+-                CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
137
+-                CorsFilter.CORSRequestType.SIMPLE.name().toLowerCase(Locale.ENGLISH)));
138
++        corsFilter.init(TesterFilterConfigs.getFilterConfigAnyOriginAndSupportsCredentials());
139
+     }
140
+ 
141
+     /*
142
+@@ -261,8 +232,7 @@
143
+         corsFilter.doFilter(request, response, filterChain);
144
+ 
145
+         Assert.assertTrue(response.getHeader(
146
+-                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
147
+-                "https://www.apache.org"));
148
++                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
149
+         Assert.assertTrue(response.getHeader(
150
+                 CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)
151
+                 .equals(TesterFilterConfigs.EXPOSED_HEADERS));
152
+@@ -727,9 +697,8 @@
153
+         });
154
+         corsFilter.doFilter(request, response, filterChain);
155
+ 
156
+-        Assert.assertTrue(response.getHeader(
157
+-                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
158
+-                "https://www.apache.org"));
159
++        Assert.assertNull(response.getHeader(
160
++                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN));
161
+         Assert.assertTrue(((Boolean) request.getAttribute(
162
+                 CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
163
+         Assert.assertTrue(request.getAttribute(
164
+@@ -1412,7 +1381,7 @@
165
+         Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
166
+         Assert.assertTrue(corsFilter.isAnyOriginAllowed());
167
+         Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
168
+-        Assert.assertTrue(corsFilter.isSupportsCredentials());
169
++        Assert.assertFalse(corsFilter.isSupportsCredentials());
170
+         Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
171
+     }
172
+ 
173
+@@ -1448,9 +1417,9 @@
174
+         Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6);
175
+         Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4);
176
+         Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
177
+-        Assert.assertTrue(corsFilter.isAnyOriginAllowed());
178
++        Assert.assertFalse(corsFilter.isAnyOriginAllowed());
179
+         Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
180
+-        Assert.assertTrue(corsFilter.isSupportsCredentials());
181
++        Assert.assertFalse(corsFilter.isSupportsCredentials());
182
+         Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
183
+     }
184
+ 
185
+@@ -1554,8 +1523,7 @@
186
+         corsFilter.doFilter(request, response, filterChain);
187
+ 
188
+         Assert.assertTrue(response.getHeader(
189
+-                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
190
+-                "https://www.apache.org"));
191
++                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
192
+         Assert.assertNull(request
193
+                 .getAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST));
194
+         Assert.assertNull(request
195
+
196
+--- a/test/org/apache/catalina/filters/TesterFilterConfigs.java	2018/05/16 14:54:09	1831727
197
+@@ -36,12 +36,13 @@
198
+     public static final TesterServletContext mockServletContext =
199
+             new TesterServletContext();
200
+ 
201
++    // Default config for the test is to allow any origin
202
+     public static FilterConfig getDefaultFilterConfig() {
203
+         final String allowedHttpHeaders =
204
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
205
+         final String allowedHttpMethods =
206
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
207
+-        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
208
++        final String allowedOrigins = ANY_ORIGIN;
209
+         final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
210
+         final String supportCredentials =
211
+                 CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
212
+@@ -59,7 +60,7 @@
213
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
214
+         final String allowedHttpMethods =
215
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
216
+-        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
217
++        final String allowedOrigins = ANY_ORIGIN;
218
+         final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
219
+         final String supportCredentials = "true";
220
+         final String preflightMaxAge =
221
+@@ -77,7 +78,7 @@
222
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
223
+         final String allowedHttpMethods =
224
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
225
+-        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
226
++        final String allowedOrigins = ANY_ORIGIN;
227
+         final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
228
+         final String supportCredentials = "false";
229
+         final String preflightMaxAge =
230
+@@ -131,7 +132,7 @@
231
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
232
+         final String allowedHttpMethods =
233
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
234
+-        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
235
++        final String allowedOrigins = ANY_ORIGIN;
236
+         final String exposedHeaders = EXPOSED_HEADERS;
237
+         final String supportCredentials =
238
+                 CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
239
+@@ -240,7 +241,7 @@
240
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
241
+         final String allowedHttpMethods =
242
+                 CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
243
+-        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
244
++        final String allowedOrigins = ANY_ORIGIN;
245
+         final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
246
+         final String supportCredentials =
247
+                 CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
248
+
... ...
@@ -1,7 +1,7 @@
1 1
 Summary:        Apache Tomcat
2 2
 Name:           apache-tomcat
3 3
 Version:        8.5.31
4
-Release:        2%{?dist}
4
+Release:        3%{?dist}
5 5
 License:        Apache
6 6
 URL:            http://tomcat.apache.org
7 7
 Group:          Applications/System
... ...
@@ -14,6 +14,7 @@ Source0:        http://mirrors.koehn.com/apache/tomcat/tomcat-8/v%{version}/src/
14 14
 Source1:        base-for-%{name}-%{version}.tar.gz
15 15
 %define sha1    base=9c954df61d7c72f6d5e7319d25351e178a84cab4
16 16
 Patch0:         apache-tomcat-use-jks-as-inmem-keystore.patch
17
+Patch1:         apache-tomcat-fix-cve-2018-8014.patch
17 18
 BuildRequires:  openjre8
18 19
 BuildRequires:  openjdk8
19 20
 BuildRequires:  apache-ant
... ...
@@ -38,6 +39,7 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name "
38 38
    -name "*.jar" -o -name "*.war" -o -name "*.zip" \) -delete
39 39
 %setup -D -b 1 -n %{name}-%{version}-src
40 40
 %patch0 -p1
41
+%patch1 -p1
41 42
 
42 43
 %build
43 44
 ant -Dbase.path="../base-for-%{name}-%{version}" deploy dist-prepare dist-source
... ...
@@ -101,6 +103,8 @@ rm -rf %{buildroot}/*
101 101
 %{_logsdir}/catalina.out
102 102
 
103 103
 %changelog
104
+*   Mon Jun 25 2018 Srinidhi Rao <srinidhir@vmware.com> 8.5.31-3
105
+-   Fix for CVE-2018-8014
104 106
 *   Thu May 17 2018 Xiaolin Li <xiaolinl@vmware.com> 8.5.31-2
105 107
 -   Mark configuration files as config(noreplace)
106 108
 *   Mon May 07 2018 Xiaolin Li <xiaolinl@vmware.com> 8.5.31-1