debian/ 0000755 0000000 0000000 00000000000 13357144265 007200 5 ustar debian/javaxpoms/ 0000755 0000000 0000000 00000000000 13067502611 011177 5 ustar debian/javaxpoms/servlet-api.pom 0000644 0000000 0000000 00000003512 13067437273 014163 0 ustar
false
is used.
true
is used.
org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD
The default truststore password is changeit
.
If the org.apache.tomcat.websocket.SSL_CONTEXT
property is
- set then the org.apache.tomcat.websocket.SSL_TRUSTSTORE
and
- org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD
properties
- will be ignored.
If the org.apache.tomcat.websocket.SSL_CONTEXT
property is
+ set then the org.apache.tomcat.websocket.SSL_TRUSTSTORE
and
+ org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD
properties
+ will be ignored.
For secure server end points, host name verification is enabled by default.
+ To bypass this verification (not recommended), it is necessary to provide a
+ custom SSLContext
via the
+ org.apache.tomcat.websocket.SSL_CONTEXT
user property. The
+ custom SSLContext
must be configured with a custom
+ TrustManager
that extends
+ javax.net.ssl.X509ExtendedTrustManager
. The desired verification
+ (or lack of verification) can then be controlled by appropriate
+ implementations of the individual abstract methods.
The maximum number of request body bytes (excluding transfer encoding + overhead) that will be swallowed by Tomcat for an aborted upload. An + aborted upload is when Tomcat knows that the request body is going to be + ignored but the client still sends it. If Tomcat does not swallow the body + the client is unlikely to see the response. If not specified the default + of 2097152 (2 megabytes) will be used. A value of less than zero indicates + that no limit should be enforced.
+The maximum number of request processing threads to be created by this Connector, which therefore determines the debian/patches/CVE-2015-5351.patch 0000644 0000000 0000000 00000010042 13067502611 013231 0 ustar Description: fix CSRF protection mechanism bypass Origin: backport, https://svn.apache.org/viewvc?view=revision&revision=1720661 Origin: backport, https://svn.apache.org/viewvc?view=revision&revision=1720663 Index: tomcat7-7.0.64/webapps/host-manager/WEB-INF/jsp/401.jsp =================================================================== --- tomcat7-7.0.64.orig/webapps/host-manager/WEB-INF/jsp/401.jsp 2016-06-17 12:02:00.280256721 +0300 +++ tomcat7-7.0.64/webapps/host-manager/WEB-INF/jsp/401.jsp 2016-06-17 12:02:00.276256670 +0300 @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --%> +<%@ page session="false" trimDirectiveWhitespaces="true" %>
Index: tomcat7-7.0.64/webapps/host-manager/WEB-INF/jsp/403.jsp =================================================================== --- tomcat7-7.0.64.orig/webapps/host-manager/WEB-INF/jsp/403.jsp 2016-06-17 12:02:00.280256721 +0300 +++ tomcat7-7.0.64/webapps/host-manager/WEB-INF/jsp/403.jsp 2016-06-17 12:02:00.276256670 +0300 @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --%> +<%@ page session="false" trimDirectiveWhitespaces="true" %> Index: tomcat7-7.0.64/webapps/host-manager/WEB-INF/jsp/404.jsp =================================================================== --- tomcat7-7.0.64.orig/webapps/host-manager/WEB-INF/jsp/404.jsp 2016-06-17 12:02:00.280256721 +0300 +++ tomcat7-7.0.64/webapps/host-manager/WEB-INF/jsp/404.jsp 2016-06-17 12:02:00.276256670 +0300 @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --%> -<%@ page import="org.apache.catalina.util.RequestUtil" %> +<%@ page import="org.apache.catalina.util.RequestUtil" session="false" + trimDirectiveWhitespaces="true" %> Index: tomcat7-7.0.64/webapps/host-manager/index.jsp =================================================================== --- tomcat7-7.0.64.orig/webapps/host-manager/index.jsp 2016-06-17 12:02:00.280256721 +0300 +++ tomcat7-7.0.64/webapps/host-manager/index.jsp 2016-06-17 12:02:00.276256670 +0300 @@ -14,5 +14,5 @@ See the License for the specific language governing permissions and limitations under the License. --%> -<% response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + - "/html")); %> \ No newline at end of file +<%@ page session="false" trimDirectiveWhitespaces="true" %> +<% response.sendRedirect(request.getContextPath() + "/html"); %> \ No newline at end of file Index: tomcat7-7.0.64/webapps/manager/WEB-INF/web.xml =================================================================== --- tomcat7-7.0.64.orig/webapps/manager/WEB-INF/web.xml 2016-06-17 12:02:00.280256721 +0300 +++ tomcat7-7.0.64/webapps/manager/WEB-INF/web.xml 2016-06-17 12:02:00.276256670 +0300 @@ -116,7 +116,6 @@+ * The default value for this implementation is {@code false}. + */ + @Override + public boolean getMapperContextRootRedirectEnabled() { + return mapperContextRootRedirectEnabled; + } + + + @Override + public void setMapperDirectoryRedirectEnabled(boolean mapperDirectoryRedirectEnabled) { + this.mapperDirectoryRedirectEnabled = mapperDirectoryRedirectEnabled; + } + + + /** + * {@inheritDoc} + *
+ * The default value for this implementation is {@code false}.
+ */
+ @Override
+ public boolean getMapperDirectoryRedirectEnabled() {
+ return mapperDirectoryRedirectEnabled;
+ }
+
@Override
public void setContainerSciFilter(String containerSciFilter) {
this.containerSciFilter = containerSciFilter;
@@ -1087,7 +1124,7 @@
this.instanceManager = instanceManager;
}
-
+
@Override
public String getEncodedPath() {
return encodedPath;
Index: tomcat7-7.0.52/java/org/apache/catalina/core/mbeans-descriptors.xml
===================================================================
--- tomcat7-7.0.52.orig/java/org/apache/catalina/core/mbeans-descriptors.xml 2016-06-29 12:31:54.808361018 -0400
+++ tomcat7-7.0.52/java/org/apache/catalina/core/mbeans-descriptors.xml 2016-06-29 12:31:54.804360969 -0400
@@ -221,6 +221,14 @@
description="The object used for mapping"
type="java.lang.Object"/>
+ false
is used.
If enabled, requests for a web application context root will be
+ redirected (adding a trailing slash) if necessary by the Mapper rather
+ than the default Servlet. This is more efficient but has the side effect
+ of confirming that the context path exists. If not specified, the
+ default value of false
is used.
If enabled, requests for a web application directory will be
+ redirected (adding a trailing slash) if necessary by the Mapper rather
+ than the default Servlet. This is more efficient but has the side effect
+ of confirming that the directory is exists. If not specified, the
+ default value of false
is used.
Set to true
to ignore any settings in both the global
or Host default contexts. By default, settings
Index: tomcat7-7.0.52/test/org/apache/catalina/core/TesterContext.java
===================================================================
--- tomcat7-7.0.52.orig/test/org/apache/catalina/core/TesterContext.java 2016-06-29 12:31:54.808361018 -0400
+++ tomcat7-7.0.52/test/org/apache/catalina/core/TesterContext.java 2016-06-29 12:31:54.808361018 -0400
@@ -1219,4 +1219,20 @@
@Override
public String getContainerSciFilter() { return null; }
+
+ @Override
+ public void setMapperContextRootRedirectEnabled(boolean mapperContextRootRedirectEnabled) {
+ // NO-OP
+ }
+
+ @Override
+ public boolean getMapperContextRootRedirectEnabled() { return false; }
+
+ @Override
+ public void setMapperDirectoryRedirectEnabled(boolean mapperDirectoryRedirectEnabled) {
+ // NO-OP
+ }
+
+ @Override
+ public boolean getMapperDirectoryRedirectEnabled() { return false; }
}
debian/patches/CVE-2015-5346.patch 0000644 0000000 0000000 00000005444 13067502611 013247 0 ustar Description: fix session fixation vulnerability
Origin: backport, https://svn.apache.org/viewvc?view=revision&revision=1713187
Index: tomcat7-7.0.52/java/org/apache/catalina/connector/CoyoteAdapter.java
===================================================================
--- tomcat7-7.0.52.orig/java/org/apache/catalina/connector/CoyoteAdapter.java 2016-06-29 12:41:19.823161652 -0400
+++ tomcat7-7.0.52/java/org/apache/catalina/connector/CoyoteAdapter.java 2016-06-29 12:41:57.651617285 -0400
@@ -712,6 +712,9 @@
version = ctxt.getWebappVersion();
// Reset mapping
request.getMappingData().recycle();
+ // Recycle session info in case the correct
+ // context is configured with different settings
+ request.recycleSessionInfo();
break;
}
}
Index: tomcat7-7.0.52/java/org/apache/catalina/connector/Request.java
===================================================================
--- tomcat7-7.0.52.orig/java/org/apache/catalina/connector/Request.java 2016-06-29 12:41:19.823161652 -0400
+++ tomcat7-7.0.52/java/org/apache/catalina/connector/Request.java 2016-06-29 12:41:19.819161605 -0400
@@ -494,18 +494,7 @@
notes.clear();
cookies = null;
- if (session != null) {
- try {
- session.endAccess();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.warn(sm.getString("coyoteRequest.sessionEndAccessFail"), t);
- }
- }
- session = null;
- requestedSessionCookie = false;
- requestedSessionId = null;
- requestedSessionURL = false;
+ recycleSessionInfo();
if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
parameterMap = new ParameterMap
<Context privileged="true" antiResourceLocking="false" - docBase="${catalina.home}/webapps/manager"> + docBase="${catalina.home}/../tomcat7-admin/manager"> <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.0\.0\.1" /> </Context> --- a/conf/catalina.policy +++ b/conf/catalina.policy @@ -199,14 +199,14 @@ // - default CATALINA_HOME == CATALINA_BASE // - CATALINA_HOME != CATALINA_BASE, per instance Manager in CATALINA_BASE // - CATALINA_HOME != CATALINA_BASE, shared Manager in CATALINA_HOME -grant codeBase "file:${catalina.base}/webapps/manager/-" { +grant codeBase "file:${catalina.base}/../tomcat7-admin/manager/-" { permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.ha.session"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager.util"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.util"; }; -grant codeBase "file:${catalina.home}/webapps/manager/-" { +grant codeBase "file:${catalina.home}/../tomcat7-admin/manager/-" { permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.ha.session"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; debian/patches/CVE-2016-8745.patch 0000644 0000000 0000000 00000002516 13067502611 013253 0 ustar Description: fix information leakage between requests Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1777471 Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802312 Bug: https://bz.apache.org/bugzilla/show_bug.cgi?id=60409 Index: tomcat7-7.0.52/java/org/apache/tomcat/util/net/NioEndpoint.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/tomcat/util/net/NioEndpoint.java 2014-01-27 07:48:36.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/tomcat/util/net/NioEndpoint.java 2017-01-18 09:41:58.600528200 -0500 @@ -1379,11 +1379,15 @@ } }catch ( IOException x ) { if ( log.isDebugEnabled() ) log.debug("Unable to complete sendfile request:", x); - cancelledKey(sk,SocketStatus.ERROR,false); + if (!event) { + cancelledKey(sk,SocketStatus.ERROR,false); + } return false; }catch ( Throwable t ) { log.error("",t); - cancelledKey(sk, SocketStatus.ERROR, false); + if (!event) { + cancelledKey(sk, SocketStatus.ERROR, false); + } return false; }finally { if (sc!=null) sc.setSendFile(false); debian/patches/CVE-2018-1336.patch 0000644 0000000 0000000 00000002140 13326066420 013234 0 ustar Description: fix DoS via issue in UTF-8 decoder Origin: upstream, https://svn.apache.org/r1830376 Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802312 Index: tomcat7-7.0.52/java/org/apache/tomcat/util/buf/Utf8Decoder.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/tomcat/util/buf/Utf8Decoder.java 2013-03-05 06:28:45.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/tomcat/util/buf/Utf8Decoder.java 2018-07-25 08:24:57.057606454 -0400 @@ -277,6 +277,11 @@ public class Utf8Decoder extends Charset outRemaining--; } else { if (outRemaining < 2) { + // Encoded with 4 bytes. inIndex currently points + // to the final byte. Move it back to first byte. + inIndex -= 3; + in.position(inIndex - in.arrayOffset()); + out.position(outIndex - out.arrayOffset()); return CoderResult.OVERFLOW; } cArr[outIndex++] = (char) ((jchar >> 0xA) + 0xD7C0); debian/patches/CVE-2017-5648-pre.patch 0000644 0000000 0000000 00000065575 13102636163 014055 0 ustar Description: fix keep-alive with asynchronous servlet Bug: https://bz.apache.org/bugzilla/show_bug.cgi?id=56190 Origin: backport, https://svn.apache.org/viewvc?view=revision&revision=1574937 Index: tomcat7-7.0.52/java/org/apache/catalina/core/AsyncContextImpl.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/core/AsyncContextImpl.java 2013-08-08 14:38:34.000000000 -0400 +++ tomcat7-7.0.52/java/org/apache/catalina/core/AsyncContextImpl.java 2017-05-04 09:55:19.493279337 -0400 @@ -88,12 +88,13 @@ public class AsyncContextImpl implements logDebug("complete "); } check(); - request.getCoyoteRequest().action(ActionCode.COMMIT, null); request.getCoyoteRequest().action(ActionCode.ASYNC_COMPLETE, null); } @Override - public void fireOnComplete() { + public void fireOnComplete() throws IOException { + // Before firing the event, close the response + request.getResponse().finishResponse(); ListlistenersCopy = new ArrayList (); listenersCopy.addAll(listeners); Index: tomcat7-7.0.52/java/org/apache/coyote/AsyncContextCallback.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/coyote/AsyncContextCallback.java 2010-12-11 13:27:15.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/coyote/AsyncContextCallback.java 2017-05-04 09:55:19.493279337 -0400 @@ -16,6 +16,8 @@ */ package org.apache.coyote; +import java.io.IOException; + /** * Provides a mechanism for the Coyote connectors to signal to a * {@link javax.servlet.AsyncContext} implementation that an action, such as @@ -24,5 +26,5 @@ package org.apache.coyote; * org.apache.coyote package. */ public interface AsyncContextCallback { - public void fireOnComplete(); + public void fireOnComplete() throws IOException; } Index: tomcat7-7.0.52/java/org/apache/coyote/AsyncStateMachine.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/coyote/AsyncStateMachine.java 2013-08-01 06:08:29.000000000 -0400 +++ tomcat7-7.0.52/java/org/apache/coyote/AsyncStateMachine.java 2017-05-04 09:55:19.493279337 -0400 @@ -16,6 +16,7 @@ */ package org.apache.coyote; +import java.io.IOException; import java.security.AccessController; import java.security.PrivilegedAction; @@ -187,11 +188,21 @@ public class AsyncStateMachine { state = AsyncState.STARTED; return SocketState.LONG; } else if (state == AsyncState.MUST_COMPLETE) { - asyncCtxt.fireOnComplete(); + try { + asyncCtxt.fireOnComplete(); + } catch (IOException e) { + // Socket is in unknown state. Close it. + return SocketState.CLOSED; + } state = AsyncState.DISPATCHED; return SocketState.ASYNC_END; } else if (state == AsyncState.COMPLETING) { - asyncCtxt.fireOnComplete(); + try { + asyncCtxt.fireOnComplete(); + } catch (IOException e) { + // Socket is in unknown state. Close it. + return SocketState.CLOSED; + } state = AsyncState.DISPATCHED; return SocketState.ASYNC_END; } else if (state == AsyncState.MUST_DISPATCH) { Index: tomcat7-7.0.52/test/org/apache/catalina/core/TestAsyncContextImpl.java =================================================================== --- tomcat7-7.0.52.orig/test/org/apache/catalina/core/TestAsyncContextImpl.java 2013-08-01 06:08:29.000000000 -0400 +++ tomcat7-7.0.52/test/org/apache/catalina/core/TestAsyncContextImpl.java 2017-05-04 09:55:19.489279283 -0400 @@ -14,12 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.catalina.core; import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -46,12 +46,12 @@ import javax.servlet.http.HttpServletRes import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; + import org.junit.Assert; import org.junit.Test; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; -import org.apache.catalina.connector.Request; import org.apache.catalina.deploy.ApplicationListener; import org.apache.catalina.deploy.ErrorPage; import org.apache.catalina.startup.Tomcat; @@ -68,6 +68,20 @@ public class TestAsyncContextImpl extend // Default timeout for these tests private static final long TIMEOUT = 3000; + private static StringBuilder tracker; + + public static synchronized void resetTracker() { + tracker = new StringBuilder(); + } + + public static synchronized void track(String trace) { + tracker.append(trace); + } + + public static synchronized String getTrack() { + return tracker.toString(); + } + @Test public void testBug49528() throws Exception { // Setup Tomcat instance @@ -146,6 +160,7 @@ public class TestAsyncContextImpl extend @Test public void testAsyncStartNoComplete() throws Exception { + resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -170,14 +185,16 @@ public class TestAsyncContextImpl extend tomcat.start(); // Call the servlet the first time - ByteChunk bc1 = getUrl("http://localhost:" + getPort() + - "/?echo=run1"); - assertEquals("OK-run1", bc1.toString()); + getUrl("http://localhost:" + getPort() + "/?echo=run1"); + Assert.assertEquals("OK-run1", getTrack()); + resetTracker(); // Call the servlet the second time with a request parameter - ByteChunk bc2 = getUrl("http://localhost:" + getPort() + - "/?echo=run2"); - assertEquals("OK-run2", bc2.toString()); + getUrl("http://localhost:" + getPort() + "/?echo=run2"); + Assert.assertEquals("OK-run2", getTrack()); + + // Request may complete before listener has finished processing so wait + // up to 5 seconds for the right response // Check the access log alv.validateAccessLog(2, 500, @@ -208,8 +225,15 @@ public class TestAsyncContextImpl extend tomcat.start(); // Call the servlet once - ByteChunk bc = getUrl("http://localhost:" + getPort() + "/"); + ByteChunk bc = new ByteChunk(); + Map> headers = new HashMap >(); + getUrl("http://localhost:" + getPort() + "/", bc, headers); + assertEquals("OK", bc.toString()); + List contentLength = headers.get("Content-Length"); + Assert.assertNotNull(contentLength); + Assert.assertEquals(1, contentLength.size()); + Assert.assertEquals("2", contentLength.get(0)); // Check the access log alv.validateAccessLog(1, 200, 0, REQUEST_TIME); @@ -356,10 +380,9 @@ public class TestAsyncContextImpl extend String echo = req.getParameter("echo"); AsyncContext actxt = req.startAsync(); - resp.setContentType("text/plain"); - resp.getWriter().print("OK"); + TestAsyncContextImpl.track("OK"); if (echo != null) { - resp.getWriter().print("-" + echo); + TestAsyncContextImpl.track("-" + echo); } // Speed up the test by reducing the timeout actxt.setTimeout(ASYNC_TIMEOUT); @@ -429,6 +452,8 @@ public class TestAsyncContextImpl extend private void doTestTimeout(Boolean completeOnTimeout, Boolean asyncDispatch) throws Exception { + resetTracker(); + String dispatchUrl = null; if (asyncDispatch != null) { if (asyncDispatch.booleanValue()) { @@ -477,9 +502,8 @@ public class TestAsyncContextImpl extend tomcat.getHost().getPipeline().addValve(alvGlobal); tomcat.start(); - ByteChunk res = new ByteChunk(); try { - getUrl("http://localhost:" + getPort() + "/start", res, null); + getUrl("http://localhost:" + getPort() + "/start"); } catch (IOException ioe) { // Ignore - expected for some error conditions } @@ -503,10 +527,19 @@ public class TestAsyncContextImpl extend expected.append("onComplete-"); expected.append("requestDestroyed"); } - assertEquals(expected.toString(), res.toString()); + // Request may complete before listener has finished processing so wait + // up to 5 seconds for the right response + String expectedTrack = expected.toString(); + int count = 0; + while (!expectedTrack.equals(getTrack()) && count < 100) { + Thread.sleep(50); + count ++; + } + assertEquals(expectedTrack, getTrack()); // Check the access log - if (completeOnTimeout == null) { + if (completeOnTimeout == null || + (!completeOnTimeout.booleanValue() && asyncDispatch == null)) { alvGlobal.validateAccessLog(1, 500, TimeoutServlet.ASYNC_TIMEOUT, TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + REQUEST_TIME); @@ -544,7 +577,7 @@ public class TestAsyncContextImpl extend protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (req.isAsyncSupported()) { - resp.getWriter().print("TimeoutServletGet-"); + TestAsyncContextImpl.track("TimeoutServletGet-"); final AsyncContext ac = req.startAsync(); ac.setTimeout(ASYNC_TIMEOUT); @@ -589,6 +622,7 @@ public class TestAsyncContextImpl extend } private void doTestDispatch(int iter, boolean useThread) throws Exception { + resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -623,7 +657,7 @@ public class TestAsyncContextImpl extend if (useThread) { url.append("&useThread=y"); } - ByteChunk res = getUrl(url.toString()); + getUrl(url.toString()); StringBuilder expected = new StringBuilder("requestInitialized-"); int loop = iter; @@ -633,7 +667,15 @@ public class TestAsyncContextImpl extend } expected.append("NonAsyncServletGet-"); expected.append("requestDestroyed"); - assertEquals(expected.toString(), res.toString()); + // Request may complete before listener has finished processing so wait + // up to 5 seconds for the right response + String expectedTrack = expected.toString(); + int count = 0; + while (!expectedTrack.equals(getTrack()) && count < 100) { + Thread.sleep(50); + count ++; + } + assertEquals(expectedTrack, getTrack()); // Check the access log alv.validateAccessLog(1, 200, 0, REQUEST_TIME); @@ -659,11 +701,10 @@ public class TestAsyncContextImpl extend if ("y".equals(req.getParameter(DISPATCH_CHECK))) { if (req.getDispatcherType() != DispatcherType.ASYNC) { - resp.getWriter().write("WrongDispatcherType-"); + track("WrongDispatcherType-"); } } - resp.getWriter().write("DispatchingServletGet-"); - resp.flushBuffer(); + track("DispatchingServletGet-"); final int iter = Integer.parseInt(req.getParameter(ITER_PARAM)) - 1; final AsyncContext ctxt = req.startAsync(); if (addTrackingListener) { @@ -697,13 +738,13 @@ public class TestAsyncContextImpl extend @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - resp.getWriter().write("NonAsyncServletGet-"); - resp.flushBuffer(); + TestAsyncContextImpl.track("NonAsyncServletGet-"); } } @Test public void testListeners() throws Exception { + resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -732,12 +773,19 @@ public class TestAsyncContextImpl extend url.append(getPort()); url.append("/stage1"); - ByteChunk res = getUrl(url.toString()); + getUrl(url.toString()); - assertEquals( - "DispatchingServletGet-DispatchingServletGet-onStartAsync-" + - "TimeoutServletGet-onStartAsync-onTimeout-onComplete-", - res.toString()); + // Request may complete before listener has finished processing so wait + // up to 5 seconds for the right response + String expectedTrack = "DispatchingServletGet-DispatchingServletGet-" + + "onStartAsync-TimeoutServletGet-onStartAsync-onTimeout-" + + "onComplete-"; + int count = 0; + while (!expectedTrack.equals(getTrack()) && count < 100) { + Thread.sleep(50); + count ++; + } + Assert.assertEquals(expectedTrack, getTrack()); // Check the access log alv.validateAccessLog(1, 200, TimeoutServlet.ASYNC_TIMEOUT, @@ -753,7 +801,7 @@ public class TestAsyncContextImpl extend @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - resp.getWriter().write("DispatchingServletGet-"); + TestAsyncContextImpl.track("DispatchingServletGet-"); resp.flushBuffer(); final boolean first = TrackingServlet.first; @@ -797,16 +845,12 @@ public class TestAsyncContextImpl extend @Override public void onComplete(AsyncEvent event) throws IOException { - ServletResponse resp = event.getAsyncContext().getResponse(); - resp.getWriter().write("onComplete-"); - resp.flushBuffer(); + TestAsyncContextImpl.track("onComplete-"); } @Override public void onTimeout(AsyncEvent event) throws IOException { - ServletResponse resp = event.getAsyncContext().getResponse(); - resp.getWriter().write("onTimeout-"); - resp.flushBuffer(); + TestAsyncContextImpl.track("onTimeout-"); if (completeOnTimeout){ event.getAsyncContext().complete(); } @@ -817,9 +861,7 @@ public class TestAsyncContextImpl extend @Override public void onError(AsyncEvent event) throws IOException { - ServletResponse resp = event.getAsyncContext().getResponse(); - resp.getWriter().write("onError-"); - resp.flushBuffer(); + TestAsyncContextImpl.track("onError-"); if (completeOnError) { event.getAsyncContext().complete(); } @@ -827,9 +869,7 @@ public class TestAsyncContextImpl extend @Override public void onStartAsync(AsyncEvent event) throws IOException { - ServletResponse resp = event.getAsyncContext().getResponse(); - resp.getWriter().write("onStartAsync-"); - resp.flushBuffer(); + TestAsyncContextImpl.track("onStartAsync-"); } } @@ -838,26 +878,12 @@ public class TestAsyncContextImpl extend @Override public void requestDestroyed(ServletRequestEvent sre) { - // Need the response and it isn't available via the Servlet API - Request r = (Request) sre.getServletRequest(); - try { - r.getResponse().getWriter().print("requestDestroyed"); - } catch (IOException e) { - // Test will fail if this happens - e.printStackTrace(); - } + TestAsyncContextImpl.track("requestDestroyed"); } @Override public void requestInitialized(ServletRequestEvent sre) { - // Need the response and it isn't available via the Servlet API - Request r = (Request) sre.getServletRequest(); - try { - r.getResponse().getWriter().print("requestInitialized-"); - } catch (IOException e) { - // Test will fail if this happens - e.printStackTrace(); - } + TestAsyncContextImpl.track("requestInitialized-"); } } @@ -927,6 +953,7 @@ public class TestAsyncContextImpl extend private void doTestDispatchError(int iter, boolean useThread, boolean completeOnError) throws Exception { + resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -941,7 +968,7 @@ public class TestAsyncContextImpl extend wrapper.setAsyncSupported(true); ctx.addServletMapping("/stage1", "dispatch"); - ErrorServlet error = new ErrorServlet(true); + ErrorServlet error = new ErrorServlet(); Tomcat.addServlet(ctx, "error", error); ctx.addServletMapping("/stage2", "error"); @@ -961,7 +988,7 @@ public class TestAsyncContextImpl extend if (useThread) { url.append("&useThread=y"); } - ByteChunk res = getUrl(url.toString()); + getUrl(url.toString()); StringBuilder expected = new StringBuilder("requestInitialized-"); int loop = iter; @@ -973,29 +1000,28 @@ public class TestAsyncContextImpl extend loop--; } expected.append("ErrorServletGet-onError-onComplete-requestDestroyed"); - assertEquals(expected.toString(), res.toString()); + // Request may complete before listener has finished processing so wait + // up to 5 seconds for the right response + String expectedTrack = expected.toString(); + int count = 0; + while (!expectedTrack.equals(getTrack()) && count < 100) { + Thread.sleep(50); + count ++; + } + assertEquals(expectedTrack, getTrack()); // Check the access log - alv.validateAccessLog(1, 200, 0, REQUEST_TIME); + alv.validateAccessLog(1, 500, 0, REQUEST_TIME); } private static class ErrorServlet extends HttpServlet { private static final long serialVersionUID = 1L; - private boolean flush = false; - - public ErrorServlet(boolean flush) { - this.flush = flush; - } - @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - resp.getWriter().write("ErrorServletGet-"); - if (flush) { - resp.flushBuffer(); - } + TestAsyncContextImpl.track("ErrorServletGet-"); try { // Give the original thread a chance to exit the // ErrorReportValve before we throw this exception @@ -1009,6 +1035,7 @@ public class TestAsyncContextImpl extend @Test public void testBug50352() throws Exception { + resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -1027,9 +1054,17 @@ public class TestAsyncContextImpl extend tomcat.start(); - ByteChunk res = getUrl("http://localhost:" + getPort() + "/"); + getUrl("http://localhost:" + getPort() + "/"); - assertEquals("Runnable-onComplete-", res.toString()); + // Request may complete before listener has finished processing so wait + // up to 5 seconds for the right response + String expectedTrack = "Runnable-onComplete-"; + int count = 0; + while (!expectedTrack.equals(getTrack()) && count < 100) { + Thread.sleep(50); + count ++; + } + assertEquals(expectedTrack, getTrack()); // Check the access log alv.validateAccessLog(1, 200, AsyncStartRunnable.THREAD_SLEEP_TIME, @@ -1058,8 +1093,7 @@ public class TestAsyncContextImpl extend public void run() { try { Thread.sleep(THREAD_SLEEP_TIME); - asyncContext.getResponse().getWriter().write( - "Runnable-"); + TestAsyncContextImpl.track("Runnable-"); asyncContext.complete(); } catch (Exception e) { e.printStackTrace(); @@ -1145,7 +1179,7 @@ public class TestAsyncContextImpl extend Context ctx = tomcat.addContext("", docBase.getAbsolutePath()); - ErrorServlet error = new ErrorServlet(false); + ErrorServlet error = new ErrorServlet(); Tomcat.addServlet(ctx, "error", error); ctx.addServletMapping("/error", "error"); @@ -1529,6 +1563,7 @@ public class TestAsyncContextImpl extend private void doTestTimeoutErrorDispatch(Boolean asyncError, ErrorPageAsyncMode mode) throws Exception { + resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -1581,12 +1616,7 @@ public class TestAsyncContextImpl extend } StringBuilder expected = new StringBuilder(); - if (asyncError == null) { - // No error handler - just get the 500 response - expected.append("requestInitialized-TimeoutServletGet-"); - // Note: With an error handler the response will be reset and these - // will be lost - } + expected.append("requestInitialized-TimeoutServletGet-"); if (asyncError != null) { if (asyncError.booleanValue()) { expected.append("AsyncErrorPageGet-"); @@ -1603,7 +1633,15 @@ public class TestAsyncContextImpl extend } expected.append("requestDestroyed"); - Assert.assertEquals(expected.toString(), res.toString()); + // Request may complete before listener has finished processing so wait + // up to 5 seconds for the right response + String expectedTrack = expected.toString(); + int count = 0; + while (!expectedTrack.equals(getTrack()) && count < 100) { + Thread.sleep(50); + count ++; + } + Assert.assertEquals(expectedTrack, getTrack()); // Check the access log alvGlobal.validateAccessLog(1, 500, TimeoutServlet.ASYNC_TIMEOUT, @@ -1633,23 +1671,21 @@ public class TestAsyncContextImpl extend @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - PrintWriter writer = resp.getWriter(); - writer.write("AsyncErrorPageGet-"); - resp.flushBuffer(); + TestAsyncContextImpl.track("AsyncErrorPageGet-"); final AsyncContext ctxt = req.getAsyncContext(); switch(mode) { case COMPLETE: - writer.write("Complete-"); + TestAsyncContextImpl.track("Complete-"); ctxt.complete(); break; case DISPATCH: - writer.write("Dispatch-"); + TestAsyncContextImpl.track("Dispatch-"); ctxt.dispatch("/error/nonasync"); break; case NO_COMPLETE: - writer.write("NoOp-"); + TestAsyncContextImpl.track("NoOp-"); break; default: // Impossible @@ -1764,6 +1800,7 @@ public class TestAsyncContextImpl extend @Test public void testForbiddenDispatching() throws Exception { + resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -1787,19 +1824,24 @@ public class TestAsyncContextImpl extend tomcat.start(); - ByteChunk body = new ByteChunk(); - try { getUrl("http://localhost:" + getPort() - + "/forbiddenDispatchingServlet", body, null); + + "/forbiddenDispatchingServlet"); } catch (IOException ioe) { // This may happen if test fails. Output the exception in case it is // useful and let asserts handle the failure ioe.printStackTrace(); } - assertTrue(body.toString().contains("OK")); - assertTrue(body.toString().contains("NonAsyncServletGet")); + // Request may complete before listener has finished processing so wait + // up to 5 seconds for the right response + String expectedTrack = "OKNonAsyncServletGet-"; + int count = 0; + while (!expectedTrack.equals(getTrack()) && count < 100) { + Thread.sleep(50); + count ++; + } + Assert.assertEquals(expectedTrack, getTrack()); } private static class DispatchingGenericServlet extends GenericServlet { @@ -1827,12 +1869,12 @@ public class TestAsyncContextImpl extend } try { asyncContext.dispatch("/nonExistingServlet"); - resp.getWriter().print("FAIL"); + TestAsyncContextImpl.track("FAIL"); } catch (IllegalStateException e) { - resp.getWriter().print("OK"); + TestAsyncContextImpl.track("OK"); } } else { - resp.getWriter().print("DispatchingGenericServletGet-"); + TestAsyncContextImpl.track("DispatchingGenericServletGet-"); } } @@ -1863,7 +1905,7 @@ public class TestAsyncContextImpl extend throws ServletException, IOException { if (req instanceof ServletRequestWrapper && res instanceof ServletResponseWrapper) { - res.getWriter().print("CustomGenericServletGet-"); + TestAsyncContextImpl.track("CustomGenericServletGet-"); } } @@ -1935,8 +1977,17 @@ public class TestAsyncContextImpl extend private void requestApplicationWithGenericServlet(String path, StringBuilder expectedContent) throws Exception { - ByteChunk res = getUrl("http://localhost:" + getPort() + path); + resetTracker(); + getUrl("http://localhost:" + getPort() + path); - assertEquals(expectedContent.toString(), res.toString()); + // Request may complete before listener has finished processing so wait + // up to 5 seconds for the right response + String expectedTrack = expectedContent.toString(); + int count = 0; + while (!expectedTrack.equals(getTrack()) && count < 100) { + Thread.sleep(50); + count ++; + } + Assert.assertEquals(expectedTrack, getTrack()); } } debian/patches/CVE-2016-3092.patch 0000644 0000000 0000000 00000004167 13067502611 013245 0 ustar From: Markus Koschany Date: Sun, 26 Jun 2016 19:14:54 +0200 Subject: CVE-2016-3092 A denial of service vulnerability was identified in Commons FileUpload that occurred when the length of the multipart boundary was just below the size of the buffer (4096 bytes) used to read the uploaded file. This caused the file upload process to take several orders of magnitude longer than if the boundary was the typical tens of bytes long. Upstream advisory: http://markmail.org/message/oyxfv73jb2g7rjg3 Origin: https://svn.apache.org/r1743480 Origin: https://svn.apache.org/viewvc?view=revision&revision=1743742 --- .../apache/tomcat/util/http/fileupload/MultipartStream.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) Index: tomcat7-7.0.68/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java =================================================================== --- tomcat7-7.0.68.orig/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java 2016-06-27 14:12:36.278176085 -0400 +++ tomcat7-7.0.68/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java 2016-06-27 14:12:36.274176038 -0400 @@ -282,11 +282,10 @@ byte[] boundary, int bufSize, ProgressNotifier pNotifier) { - this.input = input; - this.bufSize = bufSize; - this.buffer = new byte[bufSize]; - this.notifier = pNotifier; + if (boundary == null) { + throw new IllegalArgumentException("boundary may not be null"); + } // We prepend CR/LF to the boundary to chop trailing CR/LF from // body-data tokens. this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length; @@ -294,6 +293,11 @@ throw new IllegalArgumentException( "The buffer size specified for the MultipartStream is too small"); } + this.input = input; + this.bufSize = Math.max(bufSize, boundaryLength*2); + this.buffer = new byte[this.bufSize]; + this.notifier = pNotifier; + this.boundary = new byte[this.boundaryLength]; this.keepRegion = this.boundary.length; debian/patches/CVE-2015-5174.patch 0000644 0000000 0000000 00000014614 13067502611 013245 0 ustar Description: fix directory traversal vulnerability in RequestUtil.java Origin: upstream, http://svn.apache.org/viewvc?view=revision&revision=1696284 Origin: upstream, http://svn.apache.org/viewvc?view=revision&revision=1700898 Index: tomcat7-7.0.52/java/org/apache/tomcat/util/http/RequestUtil.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/tomcat/util/http/RequestUtil.java 2012-02-01 05:52:00.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/tomcat/util/http/RequestUtil.java 2016-06-21 13:03:30.575220112 -0400 @@ -30,6 +30,9 @@ * try to perform security checks for malicious input. * * @param path Relative path to be normalized + * + * @return The normalized path or null
of the path cannot be + * normalized */ public static String normalize(String path) { return normalize(path, true); @@ -44,11 +47,15 @@ * * @param path Relative path to be normalized * @param replaceBackSlash Should '\\' be replaced with '/' + * + * @return The normalized path ornull
of the path cannot be + * normalized */ public static String normalize(String path, boolean replaceBackSlash) { - if (path == null) + if (path == null) { return null; + } // Create a place for the normalized path String normalized = path; @@ -56,9 +63,6 @@ if (replaceBackSlash && normalized.indexOf('\\') >= 0) normalized = normalized.replace('\\', '/'); - if (normalized.equals("/.")) - return "/"; - // Add a leading "/" if necessary if (!normalized.startsWith("/")) normalized = "/" + normalized; @@ -66,34 +70,43 @@ // Resolve occurrences of "//" in the normalized path while (true) { int index = normalized.indexOf("//"); - if (index < 0) + if (index < 0) { break; - normalized = normalized.substring(0, index) + - normalized.substring(index + 1); + } + normalized = normalized.substring(0, index) + normalized.substring(index + 1); } // Resolve occurrences of "/./" in the normalized path while (true) { int index = normalized.indexOf("/./"); - if (index < 0) + if (index < 0) { break; - normalized = normalized.substring(0, index) + - normalized.substring(index + 2); + } + normalized = normalized.substring(0, index) + normalized.substring(index + 2); } // Resolve occurrences of "/../" in the normalized path while (true) { int index = normalized.indexOf("/../"); - if (index < 0) + if (index < 0) { break; - if (index == 0) - return (null); // Trying to go outside our context + } + if (index == 0) { + return null; // Trying to go outside our context + } int index2 = normalized.lastIndexOf('/', index - 1); - normalized = normalized.substring(0, index2) + - normalized.substring(index + 3); + normalized = normalized.substring(0, index2) + normalized.substring(index + 3); + } + + if (normalized.equals("/.")) { + return "/"; + } + + if (normalized.equals("/..")) { + return null; // Trying to go outside our context } // Return the normalized path that we have completed - return (normalized); + return normalized; } } Index: tomcat7-7.0.52/test/org/apache/tomcat/util/http/TestRequestUtil.java =================================================================== --- tomcat7-7.0.52.orig/test/org/apache/tomcat/util/http/TestRequestUtil.java 2012-02-01 05:52:00.000000000 -0500 +++ tomcat7-7.0.52/test/org/apache/tomcat/util/http/TestRequestUtil.java 2016-06-21 13:03:32.787241711 -0400 @@ -23,11 +23,101 @@ public class TestRequestUtil { @Test - public void testNormalizeString() { - assertEquals("/something",RequestUtil.normalize("//something")); - assertEquals("/some/thing",RequestUtil.normalize("some//thing")); - assertEquals("/something/",RequestUtil.normalize("something//")); - assertEquals("/",RequestUtil.normalize("//")); + public void testNormalize01() { + doTestNormalize("//something", "/something"); } + @Test + public void testNormalize02() { + doTestNormalize("some//thing", "/some/thing"); + } + + @Test + public void testNormalize03() { + doTestNormalize("something//", "/something/"); + } + + @Test + public void testNormalize04() { + doTestNormalize("//", "/"); + } + + @Test + public void testNormalize05() { + doTestNormalize("//", "/"); + } + + @Test + public void testNormalize06() { + doTestNormalize("///", "/"); + } + + @Test + public void testNormalize07() { + doTestNormalize("////", "/"); + } + + @Test + public void testNormalize08() { + doTestNormalize("/.", "/"); + } + + @Test + public void testNormalize09() { + doTestNormalize("/./", "/"); + } + + @Test + public void testNormalize10() { + doTestNormalize(".", "/"); + } + + @Test + public void testNormalize11() { + doTestNormalize("/..", null); + } + + @Test + public void testNormalize12() { + doTestNormalize("/../", null); + } + + @Test + public void testNormalize13() { + doTestNormalize("..", null); + } + + @Test + public void testNormalize14() { + doTestNormalize("//..", null); + } + + @Test + public void testNormalize15() { + doTestNormalize("//../", null); + } + + @Test + public void testNormalize16() { + doTestNormalize("/./..", null); + } + + @Test + public void testNormalize17() { + doTestNormalize("/./../", null); + } + + @Test + public void testNormalize18() { + doTestNormalize("/a/../..", null); + } + + @Test + public void testNormalize19() { + doTestNormalize("/a/../../", null); + } + + private void doTestNormalize(String input, String expected) { + assertEquals(expected,RequestUtil.normalize(input)); + } } debian/patches/CVE-2016-5388.patch 0000644 0000000 0000000 00000012267 13067502611 013257 0 ustar Description: add mitigaton for httpoxy issue Origin: backport, https://svn.apache.org/viewvc?view=revision&revision=1756942 Index: tomcat7-7.0.52/conf/web.xml =================================================================== --- tomcat7-7.0.52.orig/conf/web.xml 2017-01-18 09:17:31.000000000 -0500 +++ tomcat7-7.0.52/conf/web.xml 2017-01-18 09:18:03.996626843 -0500 @@ -321,6 +321,15 @@ + + + + + + + + + @@ -344,7 +353,7 @@cgiPathPrefix WEB-INF/cgi -5 +5 --> Index: tomcat7-7.0.52/java/org/apache/catalina/servlets/CGIServlet.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/servlets/CGIServlet.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/servlets/CGIServlet.java 2017-01-18 09:18:03.996626843 -0500 @@ -36,6 +36,7 @@ import java.util.Locale; import java.util.StringTokenizer; import java.util.Vector; +import java.util.regex.Pattern; import javax.servlet.RequestDispatcher; import javax.servlet.ServletConfig; @@ -268,6 +269,16 @@ */ private long stderrTimeout = 2000; + /** + * The regular expression used to select HTTP headers to be passed to the + * CGI process as environment variables. The name of the environment + * variable will be the name of the HTTP header converter to upper case, + * prefixed withHTTP_
and with all-
characters + * converted to_
. + */ + private Pattern envHttpHeadersPattern = Pattern.compile( + "ACCEPT[-0-9A-Z]*|CACHE-CONTROL|COOKIE|HOST|IF-[-0-9A-Z]*|REFERER|USER-AGENT"); + /** object used to ensure multiple threads don't try to expand same file */ static Object expandFileLock = new Object(); @@ -331,6 +342,10 @@ "stderrTimeout")); } + if (getServletConfig().getInitParameter("envHttpHeaders") != null) { + envHttpHeadersPattern = + Pattern.compile(getServletConfig().getInitParameter("envHttpHeaders")); + } } @@ -1072,12 +1087,8 @@ //REMIND: rewrite multiple headers as if received as single //REMIND: change character set //REMIND: I forgot what the previous REMIND means - if ("AUTHORIZATION".equalsIgnoreCase(header) || - "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) { - //NOOP per CGI specification section 11.2 - } else { - envp.put("HTTP_" + header.replace('-', '_'), - req.getHeader(header)); + if (envHttpHeadersPattern.matcher(header).matches()) { + envp.put("HTTP_" + header.replace('-', '_'), req.getHeader(header)); } } Index: tomcat7-7.0.52/webapps/docs/cgi-howto.xml =================================================================== --- tomcat7-7.0.52.orig/webapps/docs/cgi-howto.xml 2014-01-26 17:13:11.000000000 -0500 +++ tomcat7-7.0.52/webapps/docs/cgi-howto.xml 2017-01-18 09:18:03.996626843 -0500 @@ -86,6 +86,12 @@executable-arg-1, executable-arg-2, and so on - additional arguments for the executable. These precede the CGI script name. By default there are no additional arguments. +envHttpHeaders - A regular expression used to select the +HTTP headers passed to the CGI process as environment variables. Note that +headers are converted to upper case before matching and that the entire header +name must match the pattern. Default is + ACCEPT[-0-9A-Z]*|CACHE-CONTROL|COOKIE|HOST|IF-[-0-9A-Z]*|REFERER|USER-AGENT
+parameterEncoding - Name of the parameter encoding to be used with the CGI servlet. Default is debian/patches/CVE-2016-6794.patch 0000644 0000000 0000000 00000013344 13067502611 013256 0 ustar Description: fix system properties read SecurityManager bypass Origin: backport, https://svn.apache.org/viewvc?view=revision&revision=1754728 Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=842664 Index: tomcat7-7.0.52/java/org/apache/catalina/loader/WebappClassLoader.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/loader/WebappClassLoader.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/loader/WebappClassLoader.java 2017-01-18 09:21:16.875497156 -0500 @@ -79,6 +79,7 @@ import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.IntrospectionUtils; import org.apache.tomcat.util.res.StringManager; +import org.apache.tomcat.util.security.PermissionCheck; /** * Specialized web application class loader. @@ -123,7 +124,7 @@ */ public class WebappClassLoader extends URLClassLoader - implements Lifecycle + implements Lifecycle, PermissionCheck { private static final org.apache.juli.logging.Log log= @@ -1751,6 +1752,27 @@ } + @Override + public boolean check(Permission permission) { + if (!Globals.IS_SECURITY_ENABLED) { + return true; + } + Policy currentPolicy = Policy.getPolicy(); + if (currentPolicy != null) { + ResourceEntry entry = findResourceInternal("/", "/"); + if (entry != null) { + CodeSource cs = new CodeSource( + entry.codeBase, (java.security.cert.Certificate[]) null); + PermissionCollection pc = currentPolicy.getPermissions(cs); + if (pc.implies(permission)) { + return true; + } + } + } + return false; + } + + /** * Returns the search path of URLs for loading classes and resources. * This includes the original list of URLs specified to the constructor, Index: tomcat7-7.0.52/java/org/apache/tomcat/util/digester/Digester.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/tomcat/util/digester/Digester.java 2014-01-27 07:42:39.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/tomcat/util/digester/Digester.java 2017-01-18 09:19:17.473729084 -0500 @@ -26,11 +26,13 @@ import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URISyntaxException; +import java.security.Permission; import java.util.EmptyStackException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.PropertyPermission; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; @@ -40,6 +42,7 @@ import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.IntrospectionUtils; +import org.apache.tomcat.util.security.PermissionCheck; import org.xml.sax.Attributes; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; @@ -81,6 +84,13 @@ implements IntrospectionUtils.PropertySource { @Override public String getProperty( String key ) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl instanceof PermissionCheck) { + Permission p = new PropertyPermission(key, "read"); + if (!((PermissionCheck) cl).check(p)) { + return null; + } + } return System.getProperty(key); } } Index: tomcat7-7.0.52/java/org/apache/tomcat/util/security/PermissionCheck.java =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.52/java/org/apache/tomcat/util/security/PermissionCheck.java 2017-01-18 09:19:17.477729143 -0500 @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.security; + +import java.security.Permission; + +/** + * This interface is implemented by components to enable privileged code to + * check whether the component has a given permission. + * This is typically used when a privileged component (e.g. the container) is + * performing an action on behalf of an untrusted component (e.g. a web + * application) without the current thread having passed through a code source + * provided by the untrusted component. Because the current thread has not + * passed through a code source provided by the untrusted component the + * SecurityManager assumes the code is trusted so the standard checking + * mechanisms can't be used. + */ +public interface PermissionCheck { + + /** + * Does this component have the given permission? + * + * @param permission The permission to test + * + * @return {@code false} if a SecurityManager is enabled and the component + * does not have the given permission, otherwise {@code false} + */ + boolean check(Permission permission); +} debian/patches/CVE-2017-5664.patch 0000644 0000000 0000000 00000010017 13163007324 013242 0 ustar Description: fix unexpected and undesirable results for static error pages Origin: backport, https://svn.apache.org/viewvc?view=revision&revision=1793471 Origin: backport, https://svn.apache.org/viewvc?view=revision&revision=1793491 Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802312 Index: tomcat7-7.0.52/java/org/apache/catalina/servlets/DefaultServlet.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/servlets/DefaultServlet.java 2017-09-27 16:48:28.783806316 -0400 +++ tomcat7-7.0.52/java/org/apache/catalina/servlets/DefaultServlet.java 2017-09-27 16:48:56.920156504 -0400 @@ -43,6 +43,7 @@ import javax.naming.NameClassPair; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.DirContext; +import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -239,7 +240,7 @@ public class DefaultServlet urlEncoder.addSafeCharacter('.'); urlEncoder.addSafeCharacter('*'); urlEncoder.addSafeCharacter('/'); - + if (Globals.IS_SECURITY_ENABLED) { factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); @@ -419,6 +420,18 @@ public class DefaultServlet } + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + if (req.getDispatcherType() == DispatcherType.ERROR) { + doGet(req, resp); + } else { + super.service(req, resp); + } + } + + /** * Process a GET request for the specified resource. * @@ -833,8 +846,7 @@ public class DefaultServlet } } - boolean isError = - response.getStatus() >= HttpServletResponse.SC_BAD_REQUEST; + boolean isError = DispatcherType.ERROR == request.getDispatcherType(); // Check if the conditions specified in the optional If headers are // satisfied. @@ -1299,7 +1311,7 @@ public class DefaultServlet } - + /** * Return an InputStream to an HTML representation of the contents * of this directory. @@ -1714,15 +1726,15 @@ public class DefaultServlet private File validateGlobalXsltFile() { - + File result = null; String base = System.getProperty(Globals.CATALINA_BASE_PROP); - + if (base != null) { File baseConf = new File(base, "conf"); result = validateGlobalXsltFile(baseConf); } - + if (result == null) { String home = System.getProperty(Globals.CATALINA_HOME_PROP); if (home != null && !home.equals(base)) { @@ -2306,6 +2318,8 @@ public class DefaultServlet /** * Validate range. + * + * @return true if the range is valid, otherwise false */ public boolean validate() { if (end >= length) Index: tomcat7-7.0.52/java/org/apache/catalina/servlets/WebdavServlet.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/servlets/WebdavServlet.java 2017-09-27 16:48:28.783806316 -0400 +++ tomcat7-7.0.52/java/org/apache/catalina/servlets/WebdavServlet.java 2017-09-27 16:48:28.779806267 -0400 @@ -40,6 +40,7 @@ import javax.naming.NameClassPair; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.DirContext; +import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -353,6 +354,11 @@ public class WebdavServlet return; } + if (req.getDispatcherType() == DispatcherType.ERROR) { + doGet(req, resp); + return; + } + final String method = req.getMethod(); if (debug > 0) { debian/patches/fix_cookie_names_in_tests.patch 0000644 0000000 0000000 00000007144 13067502611 017057 0 ustar Description: fix FTBFS by removing colons in cookie names which is illegal in newer java versions Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1715547 Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1715550 Index: tomcat7-7.0.52/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java =================================================================== --- tomcat7-7.0.52.orig/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java 2016-06-27 10:50:06.053815019 -0400 +++ tomcat7-7.0.52/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java 2016-06-27 10:50:06.049814992 -0400 @@ -412,7 +412,7 @@ new HashMapSystem.getProperty("file.encoding","UTF-8")
.>(); if (useCookie && (cookies != null)) { - reqHeaders.put(CLIENT_COOKIE_HEADER + ":", cookies); + reqHeaders.put(CLIENT_COOKIE_HEADER, cookies); } ByteChunk bc = new ByteChunk(); @@ -437,7 +437,7 @@ new HashMap >(); if (useCookie && (cookies != null)) { - reqHeaders.put(CLIENT_COOKIE_HEADER + ":", cookies); + reqHeaders.put(CLIENT_COOKIE_HEADER, cookies); } else { if (credentials != null) { @@ -625,4 +625,4 @@ return credentials; } } -} \ No newline at end of file +} Index: tomcat7-7.0.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java =================================================================== --- tomcat7-7.0.52.orig/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java 2016-06-27 10:50:06.053815019 -0400 +++ tomcat7-7.0.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java 2016-06-27 10:50:06.053815019 -0400 @@ -358,7 +358,7 @@ new HashMap >(); if (useCookie && (cookies != null)) { - reqHeaders.put(CLIENT_COOKIE_HEADER + ":", cookies); + reqHeaders.put(CLIENT_COOKIE_HEADER, cookies); } ByteChunk bc = new ByteChunk(); @@ -382,7 +382,7 @@ Map > respHeaders = new HashMap >(); if (useCookie && (cookies != null)) { - reqHeaders.put(CLIENT_COOKIE_HEADER + ":", cookies); + reqHeaders.put(CLIENT_COOKIE_HEADER, cookies); } else { if (credentials != null) { @@ -568,7 +568,7 @@ protected void addCookies(Map > reqHeaders) { if ((cookies != null) && (cookies.size() > 0)) { - reqHeaders.put(CLIENT_COOKIE_HEADER + ":", cookies); + reqHeaders.put(CLIENT_COOKIE_HEADER, cookies); } } @@ -655,4 +655,4 @@ return credentials; } } -} \ No newline at end of file +} Index: tomcat7-7.0.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java =================================================================== --- tomcat7-7.0.52.orig/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java 2016-06-27 10:50:06.053815019 -0400 +++ tomcat7-7.0.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java 2016-06-27 10:50:06.053815019 -0400 @@ -485,7 +485,7 @@ protected void addCookies(Map > reqHeaders) { if ((cookies != null) && (cookies.size() > 0)) { - reqHeaders.put(BROWSER_COOKIES + ":", cookies); + reqHeaders.put(BROWSER_COOKIES, cookies); } } -} \ No newline at end of file +} debian/patches/CVE-2018-1304.patch 0000644 0000000 0000000 00000003063 13303261435 013232 0 ustar Description: fix security constraints mapped to context root are ignored Origin: backport, https://svn.apache.org/r1823309 Bug: https://bz.apache.org/bugzilla/show_bug.cgi?id=62067 Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802312 Index: tomcat7-7.0.52/java/org/apache/catalina/realm/RealmBase.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/realm/RealmBase.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/realm/RealmBase.java 2018-05-29 10:16:16.964962972 -0400 @@ -618,9 +618,9 @@ public abstract class RealmBase extends // Check each defined security constraint String uri = request.getRequestPathMB().toString(); - // Bug47080 - in rare cases this may be null + // Bug47080 - in rare cases this may be null or "" // Mapper treats as '/' do the same to prevent NPE - if (uri == null) { + if (uri == null || uri.length() == 0) { uri = "/"; } @@ -652,7 +652,8 @@ public abstract class RealmBase extends } for(int k=0; k < patterns.length; k++) { - if(uri.equals(patterns[k])) { + // Exact match including special case for the context root. + if(uri.equals(patterns[k]) || patterns[k].length() == 0 && uri.equals("/")) { found = true; if(collection[j].findMethod(method)) { if(results == null) { debian/patches/CVE-2018-1305.patch 0000644 0000000 0000000 00000076141 13303261734 013244 0 ustar Description: fix security constraint annotations applied too late Origin: backport, https://svn.apache.org/r1823320 Origin: backport, https://svn.apache.org/r1823322 Origin: backport, https://svn.apache.org/r1824360 Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802312 Index: tomcat7-7.0.52/java/org/apache/catalina/Wrapper.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/Wrapper.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/Wrapper.java 2018-05-29 10:18:03.560946342 -0400 @@ -370,19 +370,23 @@ public interface Wrapper extends Contain public void setEnabled(boolean enabled); /** - * Set the flag that indicates - * {@link javax.servlet.annotation.ServletSecurity} annotations must be - * scanned when the Servlet is first used. + * This method is no longer used. All implementations should be NO-OPs. * - * @param b The new value of the flag + * @param b Unused. + * + * @deprecated This will be removed in Tomcat 9. */ + @Deprecated public void setServletSecurityAnnotationScanRequired(boolean b); /** - * Scan for (if necessary) and process (if found) the - * {@link javax.servlet.annotation.ServletSecurity} annotations for the - * Servlet associated with this wrapper. + * This method is no longer used. All implementations should be NO-OPs. + * + * @throws ServletException Never thrown + * + * @deprecated This will be removed in Tomcat 9. */ + @Deprecated public void servletSecurityAnnotationScan() throws ServletException; /** Index: tomcat7-7.0.52/java/org/apache/catalina/authenticator/AuthenticatorBase.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2018-05-29 10:18:03.564946343 -0400 @@ -39,7 +39,6 @@ import org.apache.catalina.Manager; import org.apache.catalina.Realm; import org.apache.catalina.Session; import org.apache.catalina.Valve; -import org.apache.catalina.Wrapper; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.deploy.LoginConfig; @@ -483,13 +482,6 @@ public abstract class AuthenticatorBase } } - // The Servlet may specify security constraints through annotations. - // Ensure that they have been processed before constraints are checked - Wrapper wrapper = (Wrapper) request.getMappingData().wrapper; - if (wrapper != null) { - wrapper.servletSecurityAnnotationScan(); - } - Realm realm = this.context.getRealm(); // Is this request URI subject to a security constraint? SecurityConstraint [] constraints Index: tomcat7-7.0.52/java/org/apache/catalina/core/ApplicationContext.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/core/ApplicationContext.java 2014-02-05 07:43:49.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/core/ApplicationContext.java 2018-05-29 10:18:03.576946344 -0400 @@ -49,8 +49,10 @@ import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import javax.servlet.ServletRequestAttributeListener; import javax.servlet.ServletRequestListener; +import javax.servlet.ServletSecurityElement; import javax.servlet.SessionCookieConfig; import javax.servlet.SessionTrackingMode; +import javax.servlet.annotation.ServletSecurity; import javax.servlet.descriptor.JspConfigDescriptor; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionListener; @@ -66,6 +68,7 @@ import org.apache.catalina.Wrapper; import org.apache.catalina.connector.Connector; import org.apache.catalina.deploy.FilterDef; import org.apache.catalina.util.ResourceSet; +import org.apache.catalina.util.Introspection; import org.apache.catalina.util.ServerInfo; import org.apache.naming.resources.DirContextURLStreamHandler; import org.apache.naming.resources.Resource; @@ -1133,14 +1136,27 @@ public class ApplicationContext } } + ServletSecurity annotation = null; if (servlet == null) { wrapper.setServletClass(servletClass); + Class> clazz = Introspection.loadClass(context, servletClass); + if (clazz != null) { + annotation = clazz.getAnnotation(ServletSecurity.class); + } } else { wrapper.setServletClass(servlet.getClass().getName()); wrapper.setServlet(servlet); + if (context.wasCreatedDynamicServlet(servlet)) { + annotation = servlet.getClass().getAnnotation(ServletSecurity.class); + } } - return context.dynamicServletAdded(wrapper); + ServletRegistration.Dynamic registration = + new ApplicationServletRegistration(wrapper, context); + if (annotation != null) { + registration.setServletSecurity(new ServletSecurityElement(annotation)); + } + return registration; } Index: tomcat7-7.0.52/java/org/apache/catalina/core/ApplicationServletRegistration.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/core/ApplicationServletRegistration.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/core/ApplicationServletRegistration.java 2018-05-29 10:18:03.560946342 -0400 @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -41,15 +41,16 @@ public class ApplicationServletRegistrat */ private static final StringManager sm = StringManager.getManager(Constants.Package); - - private Wrapper wrapper; - private Context context; - + + private final Wrapper wrapper; + private final Context context; + private ServletSecurityElement constraint; + public ApplicationServletRegistration(Wrapper wrapper, Context context) { this.wrapper = wrapper; this.context = context; - + } @Override @@ -65,9 +66,9 @@ public class ApplicationServletRegistrat @Override public Map getInitParameters() { ParameterMap result = new ParameterMap (); - + String[] parameterNames = wrapper.findInitParameters(); - + for (String parameterName : parameterNames) { result.put(parameterName, wrapper.findInitParameter(parameterName)); } @@ -91,7 +92,7 @@ public class ApplicationServletRegistrat if (getInitParameter(name) != null) { return false; } - + wrapper.addInitParameter(name, value); return true; @@ -99,9 +100,9 @@ public class ApplicationServletRegistrat @Override public Set setInitParameters(Map initParameters) { - + Set conflicts = new HashSet (); - + for (Map.Entry entry : initParameters.entrySet()) { if (entry.getKey() == null || entry.getValue() == null) { throw new IllegalArgumentException(sm.getString( @@ -151,13 +152,14 @@ public class ApplicationServletRegistrat "applicationServletRegistration.setServletSecurity.iae", getName(), context.getName())); } - + if (!context.getState().equals(LifecycleState.STARTING_PREP)) { throw new IllegalStateException(sm.getString( "applicationServletRegistration.setServletSecurity.ise", getName(), context.getName())); } + this.constraint = constraint; return context.addServletSecurity(this, constraint); } @@ -167,9 +169,9 @@ public class ApplicationServletRegistrat if (urlPatterns == null) { return Collections.emptySet(); } - + Set conflicts = new HashSet (); - + for (String urlPattern : urlPatterns) { String wrapperName = context.findServletMapping(urlPattern); if (wrapperName != null) { @@ -187,10 +189,15 @@ public class ApplicationServletRegistrat if (!conflicts.isEmpty()) { return conflicts; } - + for (String urlPattern : urlPatterns) { context.addServletMapping(urlPattern, wrapper.getName()); } + + if (constraint != null) { + context.addServletSecurity(this, constraint); + } + return Collections.emptySet(); } @@ -199,7 +206,7 @@ public class ApplicationServletRegistrat Set result = new HashSet (); String servletName = wrapper.getName(); - + String[] urlPatterns = context.findServletMappings(); for (String urlPattern : urlPatterns) { String name = context.findServletMapping(urlPattern); @@ -214,5 +221,5 @@ public class ApplicationServletRegistrat public String getRunAsRole() { return wrapper.getRunAs(); } - + } Index: tomcat7-7.0.52/java/org/apache/catalina/core/StandardContext.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/core/StandardContext.java 2018-05-29 10:09:51.000000000 -0400 +++ tomcat7-7.0.52/java/org/apache/catalina/core/StandardContext.java 2018-05-29 10:18:03.576946344 -0400 @@ -4700,27 +4700,36 @@ public class StandardContext extends Con } /** - * hook to register that we need to scan for security annotations. - * @param wrapper The wrapper for the Servlet that was added + * Create a servlet registration. + * + * @param wrapper The wrapper for which the registration should be created. + * + * @return An appropriate registration + * + * @deprecated This will be removed in Tomcat 9. The registration should be + * created directly. */ + @Deprecated public ServletRegistration.Dynamic dynamicServletAdded(Wrapper wrapper) { - Servlet s = wrapper.getServlet(); - if (s != null && createdServlets.contains(s)) { - // Mark the wrapper to indicate annotations need to be scanned - wrapper.setServletSecurityAnnotationScanRequired(true); - } return new ApplicationServletRegistration(wrapper, this); } /** - * hook to track which registrations need annotation scanning - * @param servlet + * Hook to track which Servlets were created via + * {@link ServletContext#createServlet(Class)}. + * + * @param servlet the created Servlet */ public void dynamicServletCreated(Servlet servlet) { createdServlets.add(servlet); } + public boolean wasCreatedDynamicServlet(Servlet servlet) { + return createdServlets.contains(servlet); + } + + /** * A helper class to manage the filter mappings in a Context. */ Index: tomcat7-7.0.52/java/org/apache/catalina/core/StandardWrapper.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/core/StandardWrapper.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/core/StandardWrapper.java 2018-05-29 10:18:03.564946343 -0400 @@ -5,17 +5,15 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - - package org.apache.catalina.core; import java.io.PrintStream; @@ -44,11 +42,9 @@ import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import javax.servlet.ServletSecurityElement; import javax.servlet.SingleThreadModel; import javax.servlet.UnavailableException; import javax.servlet.annotation.MultipartConfig; -import javax.servlet.annotation.ServletSecurity; import org.apache.catalina.Container; import org.apache.catalina.ContainerServlet; @@ -114,12 +110,12 @@ public class StandardWrapper extends Con * servlet is considered permanent. */ protected long available = 0L; - + /** - * The broadcaster that sends j2ee notifications. + * The broadcaster that sends j2ee notifications. */ protected NotificationBroadcasterSupport broadcaster = null; - + /** * The count of allocations that are currently active (even if they * are for the same instance, as will be true on a non-STM servlet). @@ -231,12 +227,12 @@ public class StandardWrapper extends Con */ protected Stack instancePool = null; - + /** * Wait time for servlet unload in ms. */ protected long unloadDelay = 2000; - + /** * True if this StandardWrapper is for the JspServlet @@ -259,12 +255,12 @@ public class StandardWrapper extends Con protected StandardWrapperValve swValve; protected long loadTime=0; protected int classLoadTime=0; - + /** * Multipart config */ protected MultipartConfigElement multipartConfigElement = null; - + /** * Async support */ @@ -275,26 +271,24 @@ public class StandardWrapper extends Con */ protected boolean enabled = true; - protected volatile boolean servletSecurityAnnotationScanRequired = false; - private boolean overridable = false; - + /** - * Static class array used when the SecurityManager is turned on and + * Static class array used when the SecurityManager is turned on and * Servlet.init
is invoked. */ protected static Class>[] classType = new Class[]{ServletConfig.class}; - - + + /** - * Static class array used when the SecurityManager is turned on and + * Static class array used when the SecurityManager is turned on and *Servlet.service
is invoked. - */ + */ @Deprecated protected static Class>[] classTypeUsedInService = new Class[]{ ServletRequest.class, ServletResponse.class}; - + private final ReentrantReadWriteLock parametersLock = new ReentrantReadWriteLock(); @@ -638,7 +632,7 @@ public class StandardWrapper extends Con public String[] getServletMethods() throws ServletException { instance = loadServlet(); - + Class extends Servlet> servletClazz = instance.getClass(); if (!javax.servlet.http.HttpServlet.class.isAssignableFrom( servletClazz)) { @@ -678,8 +672,8 @@ public class StandardWrapper extends Con public Servlet getServlet() { return instance; } - - + + /** * Set the associated servlet instance. */ @@ -688,13 +682,13 @@ public class StandardWrapper extends Con instance = servlet; } - + /** * {@inheritDoc} */ @Override public void setServletSecurityAnnotationScanRequired(boolean b) { - this.servletSecurityAnnotationScanRequired = b; + // NO-OP } // --------------------------------------------------------- Public Methods @@ -708,19 +702,19 @@ public class StandardWrapper extends Con @Override public void backgroundProcess() { super.backgroundProcess(); - + if (!getState().isAvailable()) return; - + if (getServlet() != null && (getServlet() instanceof PeriodicEventListener)) { ((PeriodicEventListener) getServlet()).periodicEvent(); } } - - + + /** * Extract the root cause from a servlet exception. - * + * * @param e The servlet exception */ public static Throwable getRootCause(ServletException e) { @@ -849,7 +843,7 @@ public class StandardWrapper extends Con (sm.getString("standardWrapper.unloading", getName())); boolean newInstance = false; - + // If not SingleThreadedModel, return the same instance every time if (!singleThreadModel) { @@ -1085,7 +1079,7 @@ public class StandardWrapper extends Con @Override public synchronized void load() throws ServletException { instance = loadServlet(); - + if (!instanceInitialized) { initServlet(instance); } @@ -1093,12 +1087,12 @@ public class StandardWrapper extends Con if (isJspServlet) { StringBuilder oname = new StringBuilder(MBeanUtils.getDomain(getParent())); - + oname.append(":type=JspMonitor,name="); oname.append(getName()); - + oname.append(getWebModuleKeyProperties()); - + try { jspMonitorON = new ObjectName(oname.toString()); Registry.getRegistry(null, null) @@ -1171,8 +1165,6 @@ public class StandardWrapper extends Con } } - processServletSecurityAnnotation(servlet.getClass()); - // Special handling for ContainerServlet instances if ((servlet instanceof ContainerServlet) && (isContainerProvidedServlet(servletClass) || @@ -1215,44 +1207,13 @@ public class StandardWrapper extends Con */ @Override public void servletSecurityAnnotationScan() throws ServletException { - if (getServlet() == null) { - Class> clazz = null; - try { - clazz = getParent().getLoader().getClassLoader().loadClass( - getServletClass()); - processServletSecurityAnnotation(clazz); - } catch (ClassNotFoundException e) { - // Safe to ignore. No class means no annotations to process - } - } else { - if (servletSecurityAnnotationScanRequired) { - processServletSecurityAnnotation(getServlet().getClass()); - } - } + // NO-OP } - private void processServletSecurityAnnotation(Class> clazz) { - // Calling this twice isn't harmful so no syncs - servletSecurityAnnotationScanRequired = false; - - Context ctxt = (Context) getParent(); - - if (ctxt.getIgnoreAnnotations()) { - return; - } - - ServletSecurity secAnnotation = - clazz.getAnnotation(ServletSecurity.class); - if (secAnnotation != null) { - ctxt.addServletSecurity( - new ApplicationServletRegistration(this, ctxt), - new ServletSecurityElement(secAnnotation)); - } - } private synchronized void initServlet(Servlet servlet) throws ServletException { - + if (instanceInitialized && !singleThreadModel) return; // Call the initialization method of this servlet @@ -1464,12 +1425,12 @@ public class StandardWrapper extends Con if (swallowOutput) { SystemLogHandler.startCapture(); } - + // Call the servlet destroy() method try { instanceSupport.fireInstanceEvent (InstanceEvent.BEFORE_DESTROY_EVENT, instance); - + if( Globals.IS_SECURITY_ENABLED) { try { SecurityUtil.doAsPrivilege("destroy", @@ -1480,7 +1441,7 @@ public class StandardWrapper extends Con } else { instance.destroy(); } - + instanceSupport.fireInstanceEvent (InstanceEvent.AFTER_DESTROY_EVENT, instance); @@ -1706,7 +1667,7 @@ public class StandardWrapper extends Con public boolean isAsyncSupported() { return asyncSupported; } - + @Override public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; @@ -1716,7 +1677,7 @@ public class StandardWrapper extends Con public boolean isEnabled() { return enabled; } - + @Override public void setEnabled(boolean enabled) { this.enabled = enabled; @@ -1792,24 +1753,24 @@ public class StandardWrapper extends Con */ @Override protected synchronized void startInternal() throws LifecycleException { - - // Send j2ee.state.starting notification + + // Send j2ee.state.starting notification if (this.getObjectName() != null) { - Notification notification = new Notification("j2ee.state.starting", - this.getObjectName(), + Notification notification = new Notification("j2ee.state.starting", + this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } - + // Start up this component super.startInternal(); setAvailable(0L); - // Send j2ee.state.running notification + // Send j2ee.state.running notification if (this.getObjectName() != null) { - Notification notification = - new Notification("j2ee.state.running", this.getObjectName(), + Notification notification = + new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } @@ -1828,15 +1789,15 @@ public class StandardWrapper extends Con protected synchronized void stopInternal() throws LifecycleException { setAvailable(Long.MAX_VALUE); - - // Send j2ee.state.stopping notification + + // Send j2ee.state.stopping notification if (this.getObjectName() != null) { - Notification notification = - new Notification("j2ee.state.stopping", this.getObjectName(), + Notification notification = + new Notification("j2ee.state.stopping", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } - + // Shut down our servlet instance (if it has been initialized) try { unload(); @@ -1892,7 +1853,7 @@ public class StandardWrapper extends Con } else { keyProperties.append(hostName); } - + String contextName = ((Context) getParent()).getName(); if (!contextName.startsWith("/")) { keyProperties.append('/'); @@ -1903,7 +1864,7 @@ public class StandardWrapper extends Con if (parent instanceof StandardContext) { ctx = (StandardContext) getParent(); } - + keyProperties.append(",J2EEApplication="); if (ctx == null) { keyProperties.append("none"); @@ -1916,10 +1877,10 @@ public class StandardWrapper extends Con } else { keyProperties.append(ctx.getJ2EEServer()); } - + return keyProperties.toString(); } - + /** * JSR 77. Always return false. @@ -1954,7 +1915,7 @@ public class StandardWrapper extends Con "j2ee.object.created"}, Notification.class.getName(), "servlet is created" - ), + ), new MBeanNotificationInfo(new String[] { "j2ee.state.starting"}, Notification.class.getName(), @@ -1985,37 +1946,37 @@ public class StandardWrapper extends Con return notificationInfo; } - - + + /* Add a JMX-NotificationListener * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) */ @Override - public void addNotificationListener(NotificationListener listener, + public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object object) throws IllegalArgumentException { broadcaster.addNotificationListener(listener,filter,object); } - - + + /** - * Remove a JMX-NotificationListener + * Remove a JMX-NotificationListener * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener) */ @Override - public void removeNotificationListener(NotificationListener listener) + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { broadcaster.removeNotificationListener(listener); } - - + + // ------------------------------------------------------------- Attributes - - + + @Deprecated public boolean isEventProvider() { return false; } - + @Deprecated public boolean isStatisticsProvider() { return false; Index: tomcat7-7.0.52/java/org/apache/catalina/startup/ContextConfig.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/startup/ContextConfig.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/startup/ContextConfig.java 2018-05-29 10:20:35.396991762 -0400 @@ -414,20 +414,10 @@ public class ContextConfig implements Li protected void authenticatorConfig() { LoginConfig loginConfig = context.getLoginConfig(); - - SecurityConstraint constraints[] = context.findConstraints(); - if (context.getIgnoreAnnotations() && - (constraints == null || constraints.length ==0) && - !context.getPreemptiveAuthentication()) { - return; - } else { - if (loginConfig == null) { - // Not metadata-complete or security constraints present, need - // an authenticator to support @ServletSecurity annotations - // and/or constraints - loginConfig = DUMMY_LOGIN_CONFIG; - context.setLoginConfig(loginConfig); - } + if (loginConfig == null) { + // Need an authenticator to support HttpServletRequest.login() + loginConfig = DUMMY_LOGIN_CONFIG; + context.setLoginConfig(loginConfig); } // Has an authenticator been configured already? Index: tomcat7-7.0.52/java/org/apache/catalina/startup/Tomcat.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/startup/Tomcat.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/startup/Tomcat.java 2018-05-29 10:18:03.564946343 -0400 @@ -801,13 +801,17 @@ public class Tomcat { Context context = (Context) event.getLifecycle(); if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { context.setConfigured(true); - } - // LoginConfig is required to process @ServletSecurity - // annotations - if (context.getLoginConfig() == null) { - context.setLoginConfig( - new LoginConfig("NONE", null, null, null)); - context.getPipeline().addValve(new NonLoginAuthenticator()); + + // Process annotations + WebAnnotationSet.loadApplicationAnnotations(context); + + // LoginConfig is required to process @ServletSecurity + // annotations + if (context.getLoginConfig() == null) { + context.setLoginConfig( + new LoginConfig("NONE", null, null, null)); + context.getPipeline().addValve(new NonLoginAuthenticator()); + } } } catch (ClassCastException e) { return; Index: tomcat7-7.0.52/java/org/apache/catalina/startup/WebAnnotationSet.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/startup/WebAnnotationSet.java 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/startup/WebAnnotationSet.java 2018-05-29 10:20:10.452979527 -0400 @@ -23,10 +23,13 @@ import javax.annotation.Resource; import javax.annotation.Resources; import javax.annotation.security.DeclareRoles; import javax.annotation.security.RunAs; +import javax.servlet.ServletSecurityElement; +import javax.servlet.annotation.ServletSecurity; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; +import org.apache.catalina.core.ApplicationServletRegistration; import org.apache.catalina.deploy.ContextEnvironment; import org.apache.catalina.deploy.ContextResource; import org.apache.catalina.deploy.ContextResourceEnvRef; @@ -144,9 +147,17 @@ public class WebAnnotationSet { * Ref JSR 250, equivalent to the run-as element in * the deployment descriptor */ - if (classClass.isAnnotationPresent(RunAs.class)) { - RunAs annotation = classClass.getAnnotation(RunAs.class); - wrapper.setRunAs(annotation.value()); + RunAs runAs = classClass.getAnnotation(RunAs.class); + if (runAs != null) { + wrapper.setRunAs(runAs.value()); + } + + // Process ServletSecurity annotation + ServletSecurity servletSecurity = classClass.getAnnotation(ServletSecurity.class); + if (servletSecurity != null) { + context.addServletSecurity( + new ApplicationServletRegistration(wrapper, context), + new ServletSecurityElement(servletSecurity)); } } } debian/patches/CVE-2016-8735.patch 0000644 0000000 0000000 00000003064 13067502611 013251 0 ustar Description: fix remote code execution via JmxRemoteLifecycleListener Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1767676 Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802312 Index: tomcat7-7.0.52/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java 2017-01-18 09:40:32.291409357 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java 2017-01-18 09:40:50.175641847 -0500 @@ -264,6 +264,10 @@ serverCsf = new RmiClientLocalhostSocketFactory(serverCsf); } + env.put("jmx.remote.rmi.server.credential.types", new String[] { + String[].class.getName(), + String.class.getName() }); + // Populate the env properties used to create the server if (serverCsf != null) { env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, serverCsf); @@ -328,7 +332,7 @@ cs = new RMIConnectorServer(serviceUrl, theEnv, server, ManagementFactory.getPlatformMBeanServer()); cs.start(); - registry.bind("jmxrmi", server); + registry.bind("jmxrmi", server.toStub()); log.info(sm.getString("jmxRemoteLifecycleListener.start", Integer.toString(theRmiRegistryPort), Integer.toString(theRmiServerPort), serverName)); debian/patches/CVE-2016-8735-pre.patch 0000644 0000000 0000000 00000042375 13067502611 014045 0 ustar Description: remove the restriction that prevented the use of SSL when specifying a bind address. Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1666762 Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802312 Index: tomcat7-7.0.52/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java 2017-01-18 09:38:24.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java 2017-01-18 09:40:32.291409357 -0500 @@ -25,17 +25,25 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; +import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.Locale; -import javax.management.MBeanServer; import javax.management.remote.JMXConnectorServer; -import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import javax.management.remote.rmi.RMIConnectorServer; +import javax.management.remote.rmi.RMIJRMPServerImpl; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; import javax.rmi.ssl.SslRMIClientSocketFactory; import javax.rmi.ssl.SslRMIServerSocketFactory; @@ -55,19 +63,16 @@ */ public class JmxRemoteLifecycleListener implements LifecycleListener { - private static final Log log = - LogFactory.getLog(JmxRemoteLifecycleListener.class); + private static final Log log = LogFactory.getLog(JmxRemoteLifecycleListener.class); - /** - * The string resources for this package. - */ protected static final StringManager sm = - StringManager.getManager(Constants.Package); + StringManager.getManager(Constants.Package); protected String rmiBindAddress = null; protected int rmiRegistryPortPlatform = -1; protected int rmiServerPortPlatform = -1; - protected boolean rmiSSL = true; + protected boolean rmiRegistrySSL = true; + protected boolean rmiServerSSL = true; protected String ciphers[] = null; protected String protocols[] = null; protected boolean clientAuth = true; @@ -154,9 +159,13 @@ // Get all the other parameters required from the standard system // properties. Only need to get the parameters that affect the creation // of the server port. - String rmiSSLValue = System.getProperty( + String rmiRegistrySSLValue = System.getProperty( + "com.sun.management.jmxremote.registry.ssl", "false"); + rmiRegistrySSL = Boolean.parseBoolean(rmiRegistrySSLValue); + + String rmiServerSSLValue = System.getProperty( "com.sun.management.jmxremote.ssl", "true"); - rmiSSL = Boolean.parseBoolean(rmiSSLValue); + rmiServerSSL = Boolean.parseBoolean(rmiServerSSLValue); String protocolsValue = System.getProperty( "com.sun.management.jmxremote.ssl.enabled.protocols"); @@ -171,7 +180,7 @@ } String clientAuthValue = System.getProperty( - "com.sun.management.jmxremote.ssl.need.client.auth", "true"); + "com.sun.management.jmxremote.ssl.need.client.auth", "true"); clientAuth = Boolean.parseBoolean(clientAuthValue); String authenticateValue = System.getProperty( @@ -204,47 +213,64 @@ // Create the environment HashMapenv = new HashMap (); - RMIClientSocketFactory csf = null; - RMIServerSocketFactory ssf = null; + RMIClientSocketFactory registryCsf = null; + RMIServerSocketFactory registrySsf = null; + + RMIClientSocketFactory serverCsf = null; + RMIServerSocketFactory serverSsf = null; - // Configure SSL for RMI connection if required - if (rmiSSL) { + // Configure registry socket factories + if (rmiRegistrySSL) { + registryCsf = new SslRMIClientSocketFactory(); + if (rmiBindAddress == null) { + registrySsf = new SslRMIServerSocketFactory( + ciphers, protocols, clientAuth); + } else { + registrySsf = new SslRmiServerBindSocketFactory( + ciphers, protocols, clientAuth, rmiBindAddress); + } + } else { if (rmiBindAddress != null) { - throw new IllegalStateException(sm.getString( - "jmxRemoteLifecycleListener.sslRmiBindAddress")); + registrySsf = new RmiServerBindSocketFactory(rmiBindAddress); } + } - csf = new SslRMIClientSocketFactory(); - ssf = new SslRMIServerSocketFactory(ciphers, protocols, - clientAuth); + // Configure server socket factories + if (rmiServerSSL) { + serverCsf = new SslRMIClientSocketFactory(); + if (rmiBindAddress == null) { + serverSsf = new SslRMIServerSocketFactory( + ciphers, protocols, clientAuth); + } else { + serverSsf = new SslRmiServerBindSocketFactory( + ciphers, protocols, clientAuth, rmiBindAddress); + } + } else { + if (rmiBindAddress != null) { + serverSsf = new RmiServerBindSocketFactory(rmiBindAddress); + } } - // Force server bind address if required + // By default, the registry will pick an address to listen on. + // Setting this property overrides that and ensures it listens on + // the configured address. if (rmiBindAddress != null) { - try { - ssf = new RmiServerBindSocketFactory( - InetAddress.getByName(rmiBindAddress)); - } catch (UnknownHostException e) { - log.error(sm.getString( - "jmxRemoteLifecycleListener.invalidRmiBindAddress", - rmiBindAddress), e); - } + System.setProperty("java.rmi.server.hostname", rmiBindAddress); } // Force the use of local ports if required if (useLocalPorts) { - csf = new RmiClientLocalhostSocketFactory(csf); + registryCsf = new RmiClientLocalhostSocketFactory(registryCsf); + serverCsf = new RmiClientLocalhostSocketFactory(serverCsf); } // Populate the env properties used to create the server - if (csf != null) { - env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, - csf); - env.put("com.sun.jndi.rmi.factory.socket", csf); - } - if (ssf != null) { - env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, - ssf); + if (serverCsf != null) { + env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, serverCsf); + env.put("com.sun.jndi.rmi.factory.socket", registryCsf); + } + if (serverSsf != null) { + env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverSsf); } // Configure authentication @@ -254,25 +280,27 @@ env.put("jmx.remote.x.login.config", loginModuleName); } - // Create the Platform server csPlatform = createServer("Platform", rmiBindAddress, rmiRegistryPortPlatform, - rmiServerPortPlatform, env, csf, ssf, - ManagementFactory.getPlatformMBeanServer()); + rmiServerPortPlatform, env, registryCsf, registrySsf, serverCsf, serverSsf); } else if (Lifecycle.STOP_EVENT == event.getType()) { destroyServer("Platform", csPlatform); } } + private JMXConnectorServer createServer(String serverName, String bindAddress, int theRmiRegistryPort, int theRmiServerPort, - HashMap theEnv, RMIClientSocketFactory csf, - RMIServerSocketFactory ssf, MBeanServer theMBeanServer) { + HashMap theEnv, + RMIClientSocketFactory registryCsf, RMIServerSocketFactory registrySsf, + RMIClientSocketFactory serverCsf, RMIServerSocketFactory serverSsf) { // Create the RMI registry + Registry registry; try { - LocateRegistry.createRegistry(theRmiRegistryPort, csf, ssf); + registry = LocateRegistry.createRegistry( + theRmiRegistryPort, registryCsf, registrySsf); } catch (RemoteException e) { log.error(sm.getString( "jmxRemoteLifecycleListener.createRegistryFailed", @@ -284,33 +312,23 @@ bindAddress = "localhost"; } - // Build the connection string with fixed ports - StringBuilder url = new StringBuilder(); - url.append("service:jmx:rmi://"); - url.append(bindAddress); - url.append(":"); - url.append(theRmiServerPort); - url.append("/jndi/rmi://"); - url.append(bindAddress); - url.append(":"); - url.append(theRmiRegistryPort); - url.append("/jmxrmi"); + String url = "service:jmx:rmi://" + bindAddress; JMXServiceURL serviceUrl; try { serviceUrl = new JMXServiceURL(url.toString()); } catch (MalformedURLException e) { - log.error(sm.getString( - "jmxRemoteLifecycleListener.invalidURL", - serverName, url.toString()), e); + log.error(sm.getString("jmxRemoteLifecycleListener.invalidURL", serverName, url), e); return null; } - // Start the JMX server with the connection string - JMXConnectorServer cs = null; + RMIConnectorServer cs = null; try { - cs = JMXConnectorServerFactory.newJMXConnectorServer( - serviceUrl, theEnv, theMBeanServer); + RMIJRMPServerImpl server = new RMIJRMPServerImpl( + rmiServerPortPlatform, serverCsf, serverSsf, theEnv); + cs = new RMIConnectorServer(serviceUrl, theEnv, server, + ManagementFactory.getPlatformMBeanServer()); cs.start(); + registry.bind("jmxrmi", server); log.info(sm.getString("jmxRemoteLifecycleListener.start", Integer.toString(theRmiRegistryPort), Integer.toString(theRmiServerPort), serverName)); @@ -318,10 +336,15 @@ log.error(sm.getString( "jmxRemoteLifecycleListener.createServerFailed", serverName), e); + } catch (AlreadyBoundException e) { + log.error(sm.getString( + "jmxRemoteLifecycleListener.createServerFailed", + serverName), e); } return cs; } + private void destroyServer(String serverName, JMXConnectorServer theConnectorServer) { if (theConnectorServer != null) { @@ -335,6 +358,7 @@ } } + public static class RmiClientLocalhostSocketFactory implements RMIClientSocketFactory, Serializable { @@ -358,13 +382,22 @@ } } - public static class RmiServerBindSocketFactory - implements RMIServerSocketFactory { + + public static class RmiServerBindSocketFactory implements RMIServerSocketFactory { private final InetAddress bindAddress; - public RmiServerBindSocketFactory(InetAddress address) { - bindAddress = address; + public RmiServerBindSocketFactory(String address) { + InetAddress bindAddress = null; + try { + bindAddress = InetAddress.getByName(address); + } catch (UnknownHostException e) { + log.error(sm.getString( + "jmxRemoteLifecycleListener.invalidRmiBindAddress", address), e); + // bind address will be null which means any/all local addresses + // which should be safe + } + this.bindAddress = bindAddress; } @Override @@ -372,4 +405,64 @@ return new ServerSocket(port, 0, bindAddress); } } + + + public static class SslRmiServerBindSocketFactory extends SslRMIServerSocketFactory { + + private static final SSLServerSocketFactory sslServerSocketFactory; + private static final String[] defaultProtocols; + + static { + SSLContext sslContext; + try { + sslContext = SSLContext.getDefault(); + } catch (NoSuchAlgorithmException e) { + // Can't continue. Force a failure. + throw new IllegalStateException(e); + } + sslServerSocketFactory = sslContext.getServerSocketFactory(); + String[] protocols = sslContext.getDefaultSSLParameters().getProtocols(); + List filteredProtocols = new ArrayList (protocols.length); + for (String protocol : protocols) { + if (protocol.toUpperCase(Locale.ENGLISH).contains("SSL")) { + continue; + } + filteredProtocols.add(protocol); + } + defaultProtocols = filteredProtocols.toArray(new String[filteredProtocols.size()]); + } + + private final InetAddress bindAddress; + + public SslRmiServerBindSocketFactory(String[] enabledCipherSuites, + String[] enabledProtocols, boolean needClientAuth, String address) { + super(enabledCipherSuites, enabledProtocols, needClientAuth); + InetAddress bindAddress = null; + try { + bindAddress = InetAddress.getByName(address); + } catch (UnknownHostException e) { + log.error(sm.getString( + "jmxRemoteLifecycleListener.invalidRmiBindAddress", address), e); + // bind address will be null which means any/all local addresses + // which should be safe + } + this.bindAddress = bindAddress; + } + + @Override + public ServerSocket createServerSocket(int port) throws IOException { + SSLServerSocket sslServerSocket = + (SSLServerSocket) sslServerSocketFactory.createServerSocket(port, 0, bindAddress); + if (getEnabledCipherSuites() != null) { + sslServerSocket.setEnabledCipherSuites(getEnabledCipherSuites()); + } + if (getEnabledProtocols() == null) { + sslServerSocket.setEnabledProtocols(defaultProtocols); + } else { + sslServerSocket.setEnabledProtocols(getEnabledProtocols()); + } + sslServerSocket.setNeedClientAuth(getNeedClientAuth()); + return sslServerSocket; + } + } } Index: tomcat7-7.0.52/java/org/apache/catalina/mbeans/LocalStrings.properties =================================================================== --- tomcat7-7.0.52.orig/java/org/apache/catalina/mbeans/LocalStrings.properties 2014-01-27 09:53:14.000000000 -0500 +++ tomcat7-7.0.52/java/org/apache/catalina/mbeans/LocalStrings.properties 2017-01-18 09:40:32.291409357 -0500 @@ -18,5 +18,4 @@ jmxRemoteLifecycleListener.destroyServerFailed=The JMX connector server could not be stopped for the {0} server jmxRemoteLifecycleListener.invalidURL=The JMX Service URL requested for the {0} server, "{1}", was invalid jmxRemoteLifecycleListener.start=The JMX Remote Listener has configured the registry on port {0} and the server on port {1} for the {2} server -jmxRemoteLifecycleListener.sslRmiBindAddress=rmiBindAddress is incompatible with setting the system property com.sun.management.jmxremote.ssl to true jmxRemoteLifecycleListener.invalidRmiBindAddress=Invalid RMI bind address [{0}] Index: tomcat7-7.0.52/webapps/docs/config/listeners.xml =================================================================== --- tomcat7-7.0.52.orig/webapps/docs/config/listeners.xml 2014-02-03 12:53:34.000000000 -0500 +++ tomcat7-7.0.52/webapps/docs/config/listeners.xml 2017-01-18 09:40:32.291409357 -0500 @@ -436,10 +436,7 @@
The address of the interface to be used by JMX/RMI server.
- This option is incompatible with setting the system
- property com.sun.management.jmxremote.ssl
to
- true
.
The address of the interface to be used by JMX/RMI server.
true
or false
, default
debian/patches/CVE-2014-0096.patch 0000644 0000000 0000000 00000041555 13067502611 013246 0 ustar Description: fix file disclosure via XXE issue
Origin: upstream, http://svn.apache.org/viewvc?view=revision&revision=1578637
Origin: upstream, http://svn.apache.org/viewvc?view=revision&revision=1578655
Index: tomcat7-7.0.52/conf/web.xml
===================================================================
--- tomcat7-7.0.52.orig/conf/web.xml 2014-01-25 15:13:05.000000000 -0500
+++ tomcat7-7.0.52/conf/web.xml 2014-07-24 13:22:10.407257076 -0400
@@ -88,10 +88,12 @@
-
-
-
-
+
+
+
+
+
+
contextXsltFile
. This should be a context
- relative path (e.g.: /path/to/context.xslt
). This
- overrides globalXsltFile
. If this value is present but a
- file does not exist, then globalXsltFile
will be used. If
+ configuring contextXsltFile
. This must be a context
+ relative path (e.g.: /path/to/context.xslt
) to a file with
+ a .xsl
or .xslt
extension. This overrides
+ globalXsltFile
. If this value is present but a file does
+ not exist, then globalXsltFile
will be used. If
globalXsltFile
does not exist, then the default
directory listing will be shown.
localXsltFile
. This should be a relative
- file name in the directory where the listing will take place.
- This overrides globalXsltFile
and
- contextXsltFile
. If this value is present but a file
- does not exist, then contextXsltFile
will be used. If
+ configuring localXsltFile
. This must be a file in the
+ directory where the listing will take place to with a
+ .xsl
or .xslt
extension. This overrides
+ globalXsltFile
and contextXsltFile
. If this
+ value is present but a file does not exist, then
+ contextXsltFile
will be used. If
contextXsltFile
does not exist, then
globalXsltFile
will be used. If
globalXsltFile
does not exist, then the default
debian/patches/CVE-2016-0763.patch 0000644 0000000 0000000 00000001714 13067502611 013242 0 ustar Description: fix securityManager restrictions bypass via crafted global context
Origin: backport, https://svn.apache.org/viewvc?view=revision&revision=1725931
Index: tomcat7-7.0.64/java/org/apache/naming/factory/ResourceLinkFactory.java
===================================================================
--- tomcat7-7.0.64.orig/java/org/apache/naming/factory/ResourceLinkFactory.java 2016-06-17 12:04:45.074390009 +0300
+++ tomcat7-7.0.64/java/org/apache/naming/factory/ResourceLinkFactory.java 2016-06-17 12:04:45.070389958 +0300
@@ -60,6 +60,11 @@
* @param newGlobalContext new global context value
*/
public static void setGlobalContext(Context newGlobalContext) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission(
+ ResourceLinkFactory.class.getName() + ".setGlobalContext"));
+ }
globalContext = newGlobalContext;
}
debian/patches/CVE-2017-7674.patch 0000644 0000000 0000000 00000003020 13163005541 013240 0 ustar Description: fix client and server side cache poisoning in CORS filter
Origin: upstream, https://svn.apache.org/viewvc?view=revision&revision=1795816
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802312
Bug: https://bz.apache.org/bugzilla/show_bug.cgi?id=61101
Index: tomcat7-7.0.52/java/org/apache/catalina/filters/CorsFilter.java
===================================================================
--- tomcat7-7.0.52.orig/java/org/apache/catalina/filters/CorsFilter.java 2013-06-05 05:58:22.000000000 -0400
+++ tomcat7-7.0.52/java/org/apache/catalina/filters/CorsFilter.java 2017-09-27 16:28:43.472940479 -0400
@@ -298,6 +298,10 @@ public final class CorsFilter implements
exposedHeadersString);
}
+ // Indicate the response depends on the origin
+ response.addHeader(CorsFilter.REQUEST_HEADER_VARY,
+ CorsFilter.REQUEST_HEADER_ORIGIN);
+
// Forward the request down the filter chain.
filterChain.doFilter(request, response);
}
@@ -941,6 +945,13 @@ public final class CorsFilter implements
"Access-Control-Allow-Headers";
// -------------------------------------------------- CORS Request Headers
+
+ /**
+ * The Vary header indicates allows disabling proxy caching by indicating
+ * the the response depends on the origin.
+ */
+ public static final String REQUEST_HEADER_VARY = "Vary";
+
/**
* The Origin header indicates where the cross-origin request or preflight
* request originates from.
debian/patches/CVE-2017-1261x.patch 0000644 0000000 0000000 00000072607 13303261414 013432 0 ustar Description: fix missing checks when HTTP PUTs enabled
Origin: backport, https://svn.apache.org/r1804604
Origin: backport, https://svn.apache.org/r1804643
Origin: backport, https://svn.apache.org/r1804729
Origin: backport, https://svn.apache.org/r1806940
Origin: backport, https://svn.apache.org/r1809288
Origin: backport, https://svn.apache.org/r1809293
Origin: backport, https://svn.apache.org/r1809298
Origin: backport, https://svn.apache.org/r1809358
Origin: backport, https://svn.apache.org/r1809978
Origin: backport, https://svn.apache.org/r1809992
Origin: backport, https://svn.apache.org/r1810014
Origin: backport, https://svn.apache.org/r1810026
Bug: https://bz.apache.org/bugzilla/show_bug.cgi?id=61542
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/tomcat8/+bug/1721749
Index: tomcat7-7.0.52/java/org/apache/catalina/servlets/DefaultServlet.java
===================================================================
--- tomcat7-7.0.52.orig/java/org/apache/catalina/servlets/DefaultServlet.java 2018-05-29 10:09:51.000000000 -0400
+++ tomcat7-7.0.52/java/org/apache/catalina/servlets/DefaultServlet.java 2018-05-29 10:12:50.949149044 -0400
@@ -829,23 +829,6 @@ public class DefaultServlet
return;
}
- // If the resource is not a collection, and the resource path
- // ends with "/" or "\", return NOT FOUND
- if (cacheEntry.context == null) {
- if (path.endsWith("/") || (path.endsWith("\\"))) {
- // Check if we're included so we can return the appropriate
- // missing resource name in the error
- String requestUri = (String) request.getAttribute(
- RequestDispatcher.INCLUDE_REQUEST_URI);
- if (requestUri == null) {
- requestUri = request.getRequestURI();
- }
- response.sendError(HttpServletResponse.SC_NOT_FOUND,
- requestUri);
- return;
- }
- }
-
boolean isError = DispatcherType.ERROR == request.getDispatcherType();
// Check if the conditions specified in the optional If headers are
Index: tomcat7-7.0.52/java/org/apache/naming/resources/FileDirContext.java
===================================================================
--- tomcat7-7.0.52.orig/java/org/apache/naming/resources/FileDirContext.java 2014-01-27 08:05:23.000000000 -0500
+++ tomcat7-7.0.52/java/org/apache/naming/resources/FileDirContext.java 2018-05-29 10:15:52.144973726 -0400
@@ -14,8 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.naming.resources;
import java.io.File;
@@ -75,6 +73,8 @@ public class FileDirContext extends Base
/**
* Builds a file directory context using the given environment.
+ *
+ * @param env The environment with which to build the context
*/
public FileDirContext(Hashtablenull
.
+ *
+ * @param name Normalized context-relative path (with leading '/')
+ * @param mustExist Must the specified resource exist?
+ */
+ protected File file(String name, boolean mustExist) {
+ if (name.equals("/")) {
+ name = "";
+ }
File file = new File(base, name);
- if (file.exists() && file.canRead()) {
+ return validate(file, name, mustExist, absoluteBase, canonicalBase);
+ }
- if (allowLinking)
- return file;
-
- // Check that this file belongs to our root path
- String canPath = null;
- try {
- canPath = file.getCanonicalPath();
- } catch (IOException e) {
- // Ignore
- }
- if (canPath == null)
- return null;
- // Check to see if going outside of the web application root
- if (!canPath.startsWith(absoluteBase)) {
- return null;
- }
+ protected File validate(File file, String name, boolean mustExist, String absoluteBase,
+ String canonicalBase) {
- // Case sensitivity check - this is now always done
- String fileAbsPath = file.getAbsolutePath();
- if (fileAbsPath.endsWith("."))
- fileAbsPath = fileAbsPath + "/";
- String absPath = normalize(fileAbsPath);
- canPath = normalize(canPath);
- if ((absoluteBase.length() < absPath.length())
- && (absoluteBase.length() < canPath.length())) {
- absPath = absPath.substring(absoluteBase.length() + 1);
- if (absPath == null)
- return null;
- if (absPath.equals(""))
- absPath = "/";
- canPath = canPath.substring(absoluteBase.length() + 1);
- if (canPath.equals(""))
- canPath = "/";
- if (!canPath.equals(absPath))
- return null;
- }
+ // If the requested names ends in '/', the Java File API will return a
+ // matching file if one exists. This isn't what we want as it is not
+ // consistent with the Servlet spec rules for request mapping.
+ if (name.endsWith("/") && file.isFile()) {
+ return null;
+ }
- } else {
+ // If the file/dir must exist but the identified file/dir can't be read
+ // then signal that the resource was not found
+ if (mustExist && !file.canRead()) {
+ return null;
+ }
+
+ // If allow linking is enabled, files are not limited to being located
+ // under the fileBase so all further checks are disabled.
+ if (allowLinking) {
+ return file;
+ }
+
+ // Additional Windows specific checks to handle known problems with
+ // File.getCanonicalPath()
+ if (JrePlatform.IS_WINDOWS && isInvalidWindowsFilename(name)) {
+ return null;
+ }
+
+ // Check that this file is located under the web application root
+ String canPath = null;
+ try {
+ canPath = file.getCanonicalPath();
+ } catch (IOException e) {
+ // Ignore
+ }
+ if (canPath == null || !canPath.startsWith(canonicalBase)) {
+ return null;
+ }
+
+ // Ensure that the file is not outside the fileBase. This should not be
+ // possible for standard requests (the request is normalized early in
+ // the request processing) but might be possible for some access via the
+ // Servlet API (RequestDispatcher etc.) therefore these checks are
+ // retained as an additional safety measure. absoluteBase has been
+ // normalized so absPath needs to be normalized as well.
+ String absPath = normalize(file.getAbsolutePath());
+ if ((absoluteBase.length() > absPath.length())) {
return null;
}
+
+ // Remove the fileBase location from the start of the paths since that
+ // was not part of the requested path and the remaining check only
+ // applies to the request path
+ absPath = absPath.substring(absoluteBase.length());
+ canPath = canPath.substring(canonicalBase.length());
+
+ // Case sensitivity check
+ // The normalized requested path should be an exact match the equivalent
+ // canonical path. If it is not, possible reasons include:
+ // - case differences on case insensitive file systems
+ // - Windows removing a trailing ' ' or '.' from the file name
+ //
+ // In all cases, a mis-match here results in the resource not being
+ // found
+ //
+ // absPath is normalized so canPath needs to be normalized as well
+ // Can't normalize canPath earlier as canonicalBase is not normalized
+ if (canPath.length() > 0) {
+ canPath = normalize(canPath);
+ }
+ if (!canPath.equals(absPath)) {
+ return null;
+ }
+
return file;
+ }
+
+ private boolean isInvalidWindowsFilename(String name) {
+ final int len = name.length();
+ if (len == 0) {
+ return false;
+ }
+ // This consistently ~10 times faster than the equivalent regular
+ // expression irrespective of input length.
+ for (int i = 0; i < len; i++) {
+ char c = name.charAt(i);
+ if (c == '\"' || c == '<' || c == '>') {
+ // These characters are disallowed in Windows file names and
+ // there are known problems for file names with these characters
+ // when using File#getCanonicalPath().
+ // Note: There are additional characters that are disallowed in
+ // Windows file names but these are not known to cause
+ // problems when using File#getCanonicalPath().
+ return true;
+ }
+ }
+ // Windows does not allow file names to end in ' ' unless specific low
+ // level APIs are used to create the files that bypass various checks.
+ // File names that end in ' ' are known to cause problems when using
+ // File#getCanonicalPath().
+ if (name.charAt(len -1) == ' ') {
+ return true;
+ }
+ return false;
}
@@ -1051,10 +1156,10 @@ public class FileDirContext extends Base
return super.getResourceType();
}
-
+
/**
* Get canonical path.
- *
+ *
* @return String the file's canonical path
*/
@Override
@@ -1068,10 +1173,6 @@ public class FileDirContext extends Base
}
return canonicalPath;
}
-
-
}
-
-
}
Index: tomcat7-7.0.52/java/org/apache/naming/resources/JrePlatform.java
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ tomcat7-7.0.52/java/org/apache/naming/resources/JrePlatform.java 2018-05-29 10:15:43.732978017 -0400
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.naming.resources;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+public class JrePlatform {
+
+ private static final String OS_NAME_PROPERTY = "os.name";
+ private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
+
+ static {
+ /*
+ * There are a few places where a) the behaviour of the Java API depends
+ * on the underlying platform and b) those behavioural differences have
+ * an impact on Tomcat.
+ *
+ * Tomcat therefore needs to be able to determine the platform it is
+ * running on to account for those differences.
+ *
+ * In an ideal world this code would not exist.
+ */
+
+ // This check is derived from the check in Apache Commons Lang
+ String osName;
+ if (System.getSecurityManager() == null) {
+ osName = System.getProperty(OS_NAME_PROPERTY);
+ } else {
+ osName = AccessController.doPrivileged(
+ new PrivilegedActionThe class name of the factory to use to create resources of type
javax.sql.DataSource
. If not specified the default of
- org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory
is used
+ org.apache.tomcat.jdbc.pool.DataSourceFactory
is used
which is a package renamed (to avoid conflictions) copy of
Apache Commons DBCP.
The configuration properties for Tomcat's standard data source
resource factory
- (org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory
) are
+ (org.apache.tomcat.jdbc.pool.DataSourceFactory
) are
as follows:
Digester
s available to process tld files.
*/
- private static Digester[] tldDigesters = new Digester[2];
+ private static Digester[] tldDigesters = new Digester[4];
/**
* Create (if necessary) and return a Digester configured to process the
@@ -89,20 +89,34 @@
boolean blockExternal) {
Digester digester = null;
- if (!validation) {
+ if (!validation && !blockExternal) {
if (tldDigesters[0] == null) {
tldDigesters[0] = DigesterFactory.newDigester(validation,
true, new TldRuleSet(), blockExternal);
tldDigesters[0].getParser();
}
digester = tldDigesters[0];
- } else {
+ } else if (!validation && blockExternal) {
if (tldDigesters[1] == null) {
tldDigesters[1] = DigesterFactory.newDigester(validation,
true, new TldRuleSet(), blockExternal);
tldDigesters[1].getParser();
}
digester = tldDigesters[1];
+ } else if (validation && !blockExternal) {
+ if (tldDigesters[2] == null) {
+ tldDigesters[2] = DigesterFactory.newDigester(validation,
+ true, new TldRuleSet(), blockExternal);
+ tldDigesters[2].getParser();
+ }
+ digester = tldDigesters[2];
+ } else {
+ if (tldDigesters[3] == null) {
+ tldDigesters[3] = DigesterFactory.newDigester(validation,
+ true, new TldRuleSet(), blockExternal);
+ tldDigesters[3].getParser();
+ }
+ digester = tldDigesters[3];
}
return digester;
}
Index: tomcat7-7.0.52/java/org/apache/jasper/compiler/JspDocumentParser.java
===================================================================
--- tomcat7-7.0.52.orig/java/org/apache/jasper/compiler/JspDocumentParser.java 2015-06-19 12:55:19.731993007 -0400
+++ tomcat7-7.0.52/java/org/apache/jasper/compiler/JspDocumentParser.java 2015-06-19 12:55:19.727992960 -0400
@@ -20,6 +20,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.security.AccessController;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarFile;
@@ -35,6 +36,8 @@
import org.apache.jasper.JspCompilationContext;
import org.apache.tomcat.util.descriptor.DigesterFactory;
import org.apache.tomcat.util.descriptor.LocalResolver;
+import org.apache.tomcat.util.security.PrivilegedGetTccl;
+import org.apache.tomcat.util.security.PrivilegedSetTccl;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
@@ -1464,33 +1467,58 @@
JspDocumentParser jspDocParser)
throws Exception {
- SAXParserFactory factory = SAXParserFactory.newInstance();
-
- factory.setNamespaceAware(true);
- // Preserve xmlns attributes
- factory.setFeature(
- "http://xml.org/sax/features/namespace-prefixes",
- true);
-
- factory.setValidating(validating);
- if (validating) {
- // Enable DTD validation
- factory.setFeature(
- "http://xml.org/sax/features/validation",
- true);
- // Enable schema validation
- factory.setFeature(
- "http://apache.org/xml/features/validation/schema",
- true);
+ ClassLoader original;
+ if (Constants.IS_SECURITY_ENABLED) {
+ PrivilegedGetTccl pa = new PrivilegedGetTccl();
+ original = AccessController.doPrivileged(pa);
+ } else {
+ original = Thread.currentThread().getContextClassLoader();
}
+ try {
+ if (Constants.IS_SECURITY_ENABLED) {
+ PrivilegedSetTccl pa =
+ new PrivilegedSetTccl(JspDocumentParser.class.getClassLoader());
+ AccessController.doPrivileged(pa);
+ } else {
+ Thread.currentThread().setContextClassLoader(
+ JspDocumentParser.class.getClassLoader());
+ }
+
+ SAXParserFactory factory = SAXParserFactory.newInstance();
- // Configure the parser
- SAXParser saxParser = factory.newSAXParser();
- XMLReader xmlReader = saxParser.getXMLReader();
- xmlReader.setProperty(LEXICAL_HANDLER_PROPERTY, jspDocParser);
- xmlReader.setErrorHandler(jspDocParser);
+ factory.setNamespaceAware(true);
+ // Preserve xmlns attributes
+ factory.setFeature(
+ "http://xml.org/sax/features/namespace-prefixes",
+ true);
- return saxParser;
+ factory.setValidating(validating);
+ if (validating) {
+ // Enable DTD validation
+ factory.setFeature(
+ "http://xml.org/sax/features/validation",
+ true);
+ // Enable schema validation
+ factory.setFeature(
+ "http://apache.org/xml/features/validation/schema",
+ true);
+ }
+
+ // Configure the parser
+ SAXParser saxParser = factory.newSAXParser();
+ XMLReader xmlReader = saxParser.getXMLReader();
+ xmlReader.setProperty(LEXICAL_HANDLER_PROPERTY, jspDocParser);
+ xmlReader.setErrorHandler(jspDocParser);
+
+ return saxParser;
+ } finally {
+ if (Constants.IS_SECURITY_ENABLED) {
+ PrivilegedSetTccl pa = new PrivilegedSetTccl(original);
+ AccessController.doPrivileged(pa);
+ } else {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
}
/*
Index: tomcat7-7.0.52/java/org/apache/jasper/xmlparser/ParserUtils.java
===================================================================
--- tomcat7-7.0.52.orig/java/org/apache/jasper/xmlparser/ParserUtils.java 2015-06-19 12:55:19.731993007 -0400
+++ tomcat7-7.0.52/java/org/apache/jasper/xmlparser/ParserUtils.java 2015-06-19 12:55:19.727992960 -0400
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.security.AccessController;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -29,6 +30,8 @@
import org.apache.tomcat.util.descriptor.DigesterFactory;
import org.apache.tomcat.util.descriptor.LocalResolver;
import org.apache.tomcat.util.descriptor.XmlErrorHandler;
+import org.apache.tomcat.util.security.PrivilegedGetTccl;
+import org.apache.tomcat.util.security.PrivilegedSetTccl;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
@@ -92,7 +95,23 @@
Document document = null;
// Perform an XML parse of this document, via JAXP
+ ClassLoader original;
+ if (Constants.IS_SECURITY_ENABLED) {
+ PrivilegedGetTccl pa = new PrivilegedGetTccl();
+ original = AccessController.doPrivileged(pa);
+ } else {
+ original = Thread.currentThread().getContextClassLoader();
+ }
try {
+ if (Constants.IS_SECURITY_ENABLED) {
+ PrivilegedSetTccl pa =
+ new PrivilegedSetTccl(ParserUtils.class.getClassLoader());
+ AccessController.doPrivileged(pa);
+ } else {
+ Thread.currentThread().setContextClassLoader(
+ ParserUtils.class.getClassLoader());
+ }
+
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
@@ -132,6 +151,13 @@
} catch (IOException io) {
throw new JasperException
(Localizer.getMessage("jsp.error.parse.xml", location), io);
+ } finally {
+ if (Constants.IS_SECURITY_ENABLED) {
+ PrivilegedSetTccl pa = new PrivilegedSetTccl(original);
+ AccessController.doPrivileged(pa);
+ } else {
+ Thread.currentThread().setContextClassLoader(original);
+ }
}
// Convert the resulting document to a graph of TreeNodes
Index: tomcat7-7.0.52/java/org/apache/tomcat/util/security/PrivilegedGetTccl.java
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ tomcat7-7.0.52/java/org/apache/tomcat/util/security/PrivilegedGetTccl.java 2015-06-19 12:55:41.768248901 -0400
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.security;
+
+import java.security.PrivilegedAction;
+
+public class PrivilegedGetTccl implements PrivilegedActionObject factory for resource links.
- * + * * @author Remy Maucherat */ public class ResourceLinkFactory @@ -50,6 +53,8 @@ */ private static Context globalContext = null; + private static MapIf you're seeing this page via a web browser, it means you've setup Tomcat successfully. Congratulations!
This is the default Tomcat home page. It can be found on the local filesystem at: /var/lib/tomcat7/webapps/ROOT/index.html
Tomcat7 veterans might be pleased to learn that this system instance of Tomcat is installed with CATALINA_HOME
in /usr/share/tomcat7
and CATALINA_BASE
in /var/lib/tomcat7
, following the rules from /usr/share/doc/tomcat7-common/RUNNING.txt.gz
.
You might consider installing the following packages, if you haven't already done so:
tomcat7-docs: This package installs a web application that allows to browse the Tomcat 7 documentation locally. Once installed, you can access it by clicking here.
tomcat7-examples: This package installs a web application that allows to access the Tomcat 7 Servlet and JSP examples. Once installed, you can access it by clicking here.
tomcat7-admin: This package installs two web applications that can help managing this Tomcat instance. Once installed, you can access the manager webapp and the host-manager webapp.
NOTE: For security reasons, using the manager webapp is restricted to users with role "manager-gui". The host-manager webapp is restricted to users with role "admin-gui". Users are defined in /etc/tomcat7/tomcat-users.xml
.