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