diff --git a/SOURCES/tomcat-7.0.69-CVE-2017-5647.patch b/SOURCES/tomcat-7.0.69-CVE-2017-5647.patch new file mode 100644 index 0000000..e41590d --- /dev/null +++ b/SOURCES/tomcat-7.0.69-CVE-2017-5647.patch @@ -0,0 +1,242 @@ +--- java/org/apache/coyote/AbstractProtocol.java.orig 2017-06-09 13:12:52.784841973 -0400 ++++ java/org/apache/coyote/AbstractProtocol.java 2017-06-09 13:12:52.801842031 -0400 +@@ -681,10 +681,9 @@ + release(wrapper, processor, false, true); + } else if (state == SocketState.SENDFILE) { + // Sendfile in progress. If it fails, the socket will be +- // closed. If it works, the socket will be re-added to the +- // poller +- connections.remove(socket); +- release(wrapper, processor, false, false); ++ // closed. If it works, the socket either be added to the ++ // poller (or equivalent) to await more data or processed ++ // if there are any pipe-lined requests remaining. + } else if (state == SocketState.UPGRADED) { + // Need to keep the connection associated with the processor + connections.put(socket, processor); +--- java/org/apache/coyote/http11/Http11AprProcessor.java.orig 2017-06-09 13:12:52.786841980 -0400 ++++ java/org/apache/coyote/http11/Http11AprProcessor.java 2017-06-09 13:14:40.975211883 -0400 +@@ -38,6 +38,7 @@ + import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; + import org.apache.tomcat.util.net.AprEndpoint; + import org.apache.tomcat.util.net.SSLSupport; ++import org.apache.tomcat.util.net.SendfileKeepAliveState; + import org.apache.tomcat.util.net.SocketStatus; + import org.apache.tomcat.util.net.SocketWrapper; + +@@ -211,7 +212,15 @@ + // Do sendfile as needed: add socket to sendfile and end + if (sendfileData != null && !getErrorState().isError()) { + sendfileData.socket = socketWrapper.getSocket().longValue(); +- sendfileData.keepAlive = keepAlive; ++ if (keepAlive) { ++ if (getInputBuffer().available() == 0) { ++ sendfileData.keepAliveState = SendfileKeepAliveState.OPEN; ++ } else { ++ sendfileData.keepAliveState = SendfileKeepAliveState.PIPELINED; ++ } ++ } else { ++ sendfileData.keepAliveState = SendfileKeepAliveState.NONE; ++ } + if (!((AprEndpoint)endpoint).getSendfile().add(sendfileData)) { + // Didn't send all of the data to sendfile. + if (sendfileData.socket == 0) { +--- java/org/apache/coyote/http11/Http11NioProcessor.java.orig 2017-06-09 13:12:52.787841983 -0400 ++++ java/org/apache/coyote/http11/Http11NioProcessor.java 2017-06-09 13:12:52.800842027 -0400 +@@ -37,6 +37,7 @@ + import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment; + import org.apache.tomcat.util.net.SSLSupport; + import org.apache.tomcat.util.net.SecureNioChannel; ++import org.apache.tomcat.util.net.SendfileKeepAliveState; + import org.apache.tomcat.util.net.SocketStatus; + import org.apache.tomcat.util.net.SocketWrapper; + +@@ -275,7 +276,15 @@ + // Do sendfile as needed: add socket to sendfile and end + if (sendfileData != null && !getErrorState().isError()) { + ((KeyAttachment) socketWrapper).setSendfileData(sendfileData); +- sendfileData.keepAlive = keepAlive; ++ if (keepAlive) { ++ if (getInputBuffer().available() == 0) { ++ sendfileData.keepAliveState = SendfileKeepAliveState.OPEN; ++ } else { ++ sendfileData.keepAliveState = SendfileKeepAliveState.PIPELINED; ++ } ++ } else { ++ sendfileData.keepAliveState = SendfileKeepAliveState.NONE; ++ } + SelectionKey key = socketWrapper.getSocket().getIOChannel().keyFor( + socketWrapper.getSocket().getPoller().getSelector()); + //do the first write on this thread, might as well +--- java/org/apache/tomcat/util/net/AprEndpoint.java.orig 2017-06-09 13:12:52.788841986 -0400 ++++ java/org/apache/tomcat/util/net/AprEndpoint.java 2017-06-09 13:12:52.801842031 -0400 +@@ -2087,7 +2087,7 @@ + // Position + public long pos; + // KeepAlive flag +- public boolean keepAlive; ++ public SendfileKeepAliveState keepAliveState = SendfileKeepAliveState.NONE; + } + + +@@ -2330,20 +2330,33 @@ + state.pos = state.pos + nw; + if (state.pos >= state.end) { + remove(state); +- if (state.keepAlive) { ++ switch (state.keepAliveState) { ++ case NONE: { ++ // Close the socket since this is ++ // the end of the not keep-alive request. ++ closeSocket(state.socket); ++ break; ++ } ++ case PIPELINED: { ++ // Destroy file descriptor pool, which should close the file ++ Pool.destroy(state.fdpool); ++ Socket.timeoutSet(state.socket, getSoTimeout() * 1000); ++ // Process the pipelined request data ++ if (!processSocket(state.socket, SocketStatus.OPEN_READ)) { ++ closeSocket(state.socket); ++ } ++ break; ++ } ++ case OPEN: { + // Destroy file descriptor pool, which should close the file + Pool.destroy(state.fdpool); +- Socket.timeoutSet(state.socket, +- getSoTimeout() * 1000); +- // If all done put the socket back in the +- // poller for processing of further requests +- getPoller().add( +- state.socket, getKeepAliveTimeout(), ++ Socket.timeoutSet(state.socket, getSoTimeout() * 1000); ++ // Put the socket back in the poller for ++ // processing of further requests ++ getPoller().add(state.socket, getKeepAliveTimeout(), + true, false); +- } else { +- // Close the socket since this is +- // the end of not keep-alive request. +- closeSocket(state.socket); ++ break; ++ } + } + } + } +--- java/org/apache/tomcat/util/net/NioEndpoint.java.orig 2017-06-09 13:12:52.790841993 -0400 ++++ java/org/apache/tomcat/util/net/NioEndpoint.java 2017-06-09 13:16:26.637573150 -0400 +@@ -1380,17 +1380,32 @@ + // responsible for registering the socket for the + // appropriate event(s) if sendfile completes. + if (!calledByProcessor) { +- if ( sd.keepAlive ) { ++ switch (sd.keepAliveState) { ++ case NONE: { + if (log.isDebugEnabled()) { +- log.debug("Connection is keep alive, registering back for OP_READ"); +- } +- reg(sk,attachment,SelectionKey.OP_READ); +- } else { +- if (log.isDebugEnabled()) { + log.debug("Send file connection is being closed"); + } + cancelledKey(sk,SocketStatus.STOP,false); ++ break; ++ } ++ case PIPELINED: { ++ if (log.isDebugEnabled()) { ++ log.debug("Connection is keep alive, processing pipe-lined data"); ++ } ++ if (!processSocket(sc, SocketStatus.OPEN_READ, true)) { ++ cancelledKey(sk, SocketStatus.DISCONNECT, false); ++ } ++ break; ++ } ++ case OPEN: { ++ if (log.isDebugEnabled()) { ++ log.debug("Connection is keep alive, registering back for OP_READ"); ++ } ++ reg(sk, attachment, SelectionKey.OP_READ); ++ break; ++ } + } ++ + } + return SendfileState.DONE; + } else { +@@ -1833,6 +1848,6 @@ + public volatile long pos; + public volatile long length; + // KeepAlive flag +- public volatile boolean keepAlive; ++ public SendfileKeepAliveState keepAliveState = SendfileKeepAliveState.NONE; + } + } +--- webapps/docs/changelog.xml.orig 2017-06-09 13:12:52.791841997 -0400 ++++ webapps/docs/changelog.xml 2017-06-09 13:14:11.593111423 -0400 +@@ -58,7 +58,7 @@ + issues do not "pop up" wrt. others). + --> +
+- ++ + + + Ensure request and response facades are used when firing application +@@ -66,6 +66,13 @@ + + + ++ ++ ++ ++ Improve sendfile handling when requests are pipelined. (markt) ++ ++ ++ +
+
+ +--- java/org/apache/tomcat/util/net/SendfileKeepAliveState.java.orig 2017-06-09 13:12:52.793842003 -0400 ++++ java/org/apache/tomcat/util/net/SendfileKeepAliveState.java 2017-06-09 13:12:52.800842027 -0400 +@@ -0,0 +1,39 @@ ++/* ++ * 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.net; ++ ++public enum SendfileKeepAliveState { ++ ++ /** ++ * Keep-alive is not in use. The socket can be closed when the response has ++ * been written. ++ */ ++ NONE, ++ ++ /** ++ * Keep-alive is in use and there is pipelined data in the input buffer to ++ * be read as soon as the current response has been written. ++ */ ++ PIPELINED, ++ ++ /** ++ * Keep-alive is in use. The socket should be added to the poller (or ++ * equivalent) to await more data as soon as the current response has been ++ * written. ++ */ ++ OPEN ++} diff --git a/SOURCES/tomcat-7.0.69-CVE-2017-5648.patch b/SOURCES/tomcat-7.0.69-CVE-2017-5648.patch new file mode 100644 index 0000000..f0e99a3 --- /dev/null +++ b/SOURCES/tomcat-7.0.69-CVE-2017-5648.patch @@ -0,0 +1,136 @@ +--- java/org/apache/catalina/authenticator/FormAuthenticator.java.orig 2017-06-09 13:00:33.854323751 -0400 ++++ java/org/apache/catalina/authenticator/FormAuthenticator.java 2017-06-09 13:00:33.861323775 -0400 +@@ -406,9 +406,9 @@ + RequestDispatcher disp = + context.getServletContext().getRequestDispatcher(loginPage); + try { +- if (context.fireRequestInitEvent(request)) { ++ if (context.fireRequestInitEvent(request.getRequest())) { + disp.forward(request.getRequest(), response); +- context.fireRequestDestroyEvent(request); ++ context.fireRequestDestroyEvent(request.getRequest()); + } + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); +@@ -450,12 +450,11 @@ + } + + RequestDispatcher disp = +- context.getServletContext().getRequestDispatcher +- (config.getErrorPage()); ++ context.getServletContext().getRequestDispatcher(config.getErrorPage()); + try { +- if (context.fireRequestInitEvent(request)) { ++ if (context.fireRequestInitEvent(request.getRequest())) { + disp.forward(request.getRequest(), response); +- context.fireRequestDestroyEvent(request); ++ context.fireRequestDestroyEvent(request.getRequest()); + } + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); +--- java/org/apache/catalina/core/AsyncContextImpl.java.orig 2017-06-09 13:00:33.855323755 -0400 ++++ java/org/apache/catalina/core/AsyncContextImpl.java 2017-06-09 13:02:46.108767706 -0400 +@@ -157,6 +157,7 @@ + } + } + } finally { ++ context.fireRequestDestroyEvent(request.getRequest()); + clearServletRequestResponse(); + if (Globals.IS_SECURITY_ENABLED) { + PrivilegedAction pa = new PrivilegedSetTccl(oldCL); +--- java/org/apache/catalina/core/StandardHostValve.java.orig 2017-06-09 13:00:33.856323758 -0400 ++++ java/org/apache/catalina/core/StandardHostValve.java 2017-06-09 13:05:06.702248405 -0400 +@@ -66,7 +66,7 @@ + + static { + STRICT_SERVLET_COMPLIANCE = Globals.STRICT_SERVLET_COMPLIANCE; +- ++ + String accessSession = System.getProperty( + "org.apache.catalina.core.StandardHostValve.ACCESS_SESSION"); + if (accessSession == null) { +@@ -146,7 +146,7 @@ + if (Globals.IS_SECURITY_ENABLED) { + PrivilegedAction pa = new PrivilegedSetTccl( + context.getLoader().getClassLoader()); +- AccessController.doPrivileged(pa); ++ AccessController.doPrivileged(pa); + } else { + Thread.currentThread().setContextClassLoader + (context.getLoader().getClassLoader()); +@@ -156,9 +156,9 @@ + request.setAsyncSupported(context.getPipeline().isAsyncSupported()); + } + +- boolean asyncAtStart = request.isAsync(); ++ boolean asyncAtStart = request.isAsync(); + boolean asyncDispatching = request.isAsyncDispatching(); +- if (asyncAtStart || context.fireRequestInitEvent(request)) { ++ if (asyncAtStart || context.fireRequestInitEvent(request.getRequest())) { + + // Ask this Context to process this request. Requests that are in + // async mode and are not being dispatched to this resource must be +@@ -197,7 +197,7 @@ + if (!context.getState().isAvailable()) { + return; + } +- ++ + // Look for (and render if found) an application level error page + if (response.isErrorReportRequired()) { + if (t != null) { +@@ -208,7 +208,7 @@ + } + + if (!request.isAsync() && (!asyncAtStart || !response.isErrorReportRequired())) { +- context.fireRequestDestroyEvent(request); ++ context.fireRequestDestroyEvent(request.getRequest()); + } + } + +@@ -222,7 +222,7 @@ + if (Globals.IS_SECURITY_ENABLED) { + PrivilegedAction pa = new PrivilegedSetTccl( + StandardHostValve.class.getClassLoader()); +- AccessController.doPrivileged(pa); ++ AccessController.doPrivileged(pa); + } else { + Thread.currentThread().setContextClassLoader + (StandardHostValve.class.getClassLoader()); +@@ -258,7 +258,7 @@ + // Ask this Context to process this request + context.getPipeline().getFirst().event(request, response, event); + +- ++ + // Error page processing + response.setSuspended(false); + +@@ -469,7 +469,7 @@ + + if (response.isCommitted()) { + // Response is committed - including the error page is the +- // best we can do ++ // best we can do + rd.include(request.getRequest(), response.getResponse()); + } else { + // Reset the response (keeping the real error code and message) +--- webapps/docs/changelog.xml.orig 2017-06-09 13:00:33.858323765 -0400 ++++ webapps/docs/changelog.xml 2017-06-09 13:02:03.530622703 -0400 +@@ -57,6 +57,16 @@ + They eventually become mixed with the numbered issues. (I.e., numbered + issues do not "pop up" wrt. others). + --> ++
++ ++ ++ ++ Ensure request and response facades are used when firing application ++ listeners. (markt/remm) ++ ++ ++ ++
+
+ + diff --git a/SOURCES/tomcat-7.0.69-CVE-2017-5664.patch b/SOURCES/tomcat-7.0.69-CVE-2017-5664.patch new file mode 100644 index 0000000..4533c64 --- /dev/null +++ b/SOURCES/tomcat-7.0.69-CVE-2017-5664.patch @@ -0,0 +1,119 @@ +--- java/org/apache/catalina/servlets/DefaultServlet.java.orig 2017-06-09 13:23:44.198085912 -0400 ++++ java/org/apache/catalina/servlets/DefaultServlet.java 2017-06-09 13:23:44.209085950 -0400 +@@ -245,7 +245,7 @@ + urlEncoder.addSafeCharacter('.'); + urlEncoder.addSafeCharacter('*'); + urlEncoder.addSafeCharacter('/'); +- ++ + if (Globals.IS_SECURITY_ENABLED) { + factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); +@@ -423,6 +423,18 @@ + } + + ++ @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. + * +@@ -860,8 +872,7 @@ + } + } + +- 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. +@@ -1326,7 +1337,7 @@ + + } + +- ++ + /** + * Return an InputStream to an HTML representation of the contents + * of this directory. +@@ -1767,15 +1778,15 @@ + + + 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)) { +@@ -2364,6 +2375,8 @@ + + /** + * Validate range. ++ * ++ * @return true if the range is valid, otherwise false + */ + public boolean validate() { + if (end >= length) +--- java/org/apache/catalina/servlets/WebdavServlet.java.orig 2017-06-09 13:23:44.199085915 -0400 ++++ java/org/apache/catalina/servlets/WebdavServlet.java 2017-06-09 13:23:44.208085946 -0400 +@@ -40,6 +40,7 @@ + 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 @@ + return; + } + ++ if (req.getDispatcherType() == DispatcherType.ERROR) { ++ doGet(req, resp); ++ return; ++ } ++ + final String method = req.getMethod(); + + if (debug > 0) { +--- webapps/docs/changelog.xml.orig 2017-06-09 13:23:44.201085922 -0400 ++++ webapps/docs/changelog.xml 2017-06-09 13:25:14.586400695 -0400 +@@ -64,6 +64,17 @@ + Ensure request and response facades are used when firing application + listeners. (markt/remm) + ++ ++ Use a more reliable mechanism for the DefaultServlet when ++ determining if the current request is for custom error page or not. ++ (markt) ++ ++ ++ Ensure that when the Default or WebDAV servlets process an error ++ dispatch that the error resource is processed via the ++ doGet() method irrespective of the method used for the ++ original request that triggered the error. (markt) ++ + + + diff --git a/SPECS/tomcat.spec b/SPECS/tomcat.spec index 5165cfe..0c407b7 100644 --- a/SPECS/tomcat.spec +++ b/SPECS/tomcat.spec @@ -54,7 +54,7 @@ Name: tomcat Epoch: 0 Version: %{major_version}.%{minor_version}.%{micro_version} -Release: 11%{?dist} +Release: 12%{?dist} Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API Group: System Environment/Daemons @@ -92,6 +92,9 @@ Patch4: %{name}-7.0.69-CVE-2016-3092.patch Patch5: %{name}-7.0.69-CVE-2016-5388.patch Patch6: %{name}-7.0.69-CVE-2016-8745.patch Patch7: %{name}-7.0.69-CVE-2016-6816.patch +Patch8: %{name}-7.0.69-CVE-2017-5648.patch +Patch9: %{name}-7.0.69-CVE-2017-5647.patch +Patch10: %{name}-7.0.69-CVE-2017-5664.patch BuildArch: noarch @@ -244,6 +247,9 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name " %patch5 -p0 %patch6 -p0 %patch7 -p0 +%patch8 -p0 +%patch9 -p0 +%patch10 -p0 %{__ln_s} $(build-classpath jakarta-taglibs-core) webapps/examples/WEB-INF/lib/jstl.jar %{__ln_s} $(build-classpath jakarta-taglibs-standard) webapps/examples/WEB-INF/lib/standard.jar @@ -683,6 +689,11 @@ fi %attr(0644,root,root) %{_unitdir}/%{name}-jsvc.service %changelog +* Fri Jun 09 2017 Coty Sutherland 0:7.0.69-12 +- Resolves: rhbz#1441487 CVE-2017-5648 tomcat: Calls to application listeners did not use the appropriate facade object +- Resolves: rhbz#1441480 CVE-2017-5647 tomcat: Incorrect handling of pipelined requests when send file was used +- Resolves: rhbz#1459746 CVE-2017-5664 tomcat: Security constrained bypass in error page mechanism + * Tue Mar 28 2017 Coty Sutherland - 0:7.0.69-11 - Resolves: rhbz#1413591 CVE-2016-8745 tomcat: information disclosure due to incorrect Processor sharing - Resolves: rhbz#1402662 CVE-2016-6816 tomcat: HTTP Request smuggling vulnerability due to permitting invalid character in HTTP requests