From 867542aef456dc5a2ffd42971c79b52abdf7e774 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 01 2017 03:21:10 +0000 Subject: import tomcat-7.0.76-2.el7 --- diff --git a/.gitignore b/.gitignore index daa0815..6bb3184 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -SOURCES/apache-tomcat-7.0.69-src.tar.gz +SOURCES/apache-tomcat-7.0.76-src.tar.gz SOURCES/tomcat-juli-adapters.jar SOURCES/tomcat-juli.jar diff --git a/.tomcat.metadata b/.tomcat.metadata index ef6c8c3..6a949e1 100644 --- a/.tomcat.metadata +++ b/.tomcat.metadata @@ -1,3 +1,3 @@ -0be9ee73295f0125b391db17ec58053cead73f09 SOURCES/apache-tomcat-7.0.69-src.tar.gz -b5aad5a5c8e358e014b3865aaa899e0deb3fa31a SOURCES/tomcat-juli-adapters.jar -da220d83c3aceea91e55b26bf4ca07ad6a8b5b29 SOURCES/tomcat-juli.jar +0a432e0853b399d7e9aec6df0679e11c6622ed51 SOURCES/apache-tomcat-7.0.76-src.tar.gz +aa3df4f86defedf62a5136d1137a1241837b0be2 SOURCES/tomcat-juli-adapters.jar +6b883e7be4738775c823e436b8b35753d82d4525 SOURCES/tomcat-juli.jar diff --git a/SOURCES/tomcat-7.0.69-CVE-2016-3092.patch b/SOURCES/tomcat-7.0.69-CVE-2016-3092.patch deleted file mode 100644 index 056bcde..0000000 --- a/SOURCES/tomcat-7.0.69-CVE-2016-3092.patch +++ /dev/null @@ -1,49 +0,0 @@ ---- java/org/apache/tomcat/util/http/fileupload/MultipartStream.java.orig 2016-06-27 11:26:04.561937786 -0400 -+++ java/org/apache/tomcat/util/http/fileupload/MultipartStream.java 2016-06-27 11:24:26.556751185 -0400 -@@ -282,11 +282,10 @@ public class MultipartStream { - 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,12 @@ public class MultipartStream { - 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; - ---- webapps/docs/changelog.xml.orig 2016-06-27 11:26:15.578958762 -0400 -+++ webapps/docs/changelog.xml 2016-06-27 11:25:26.024864412 -0400 -@@ -57,6 +57,16 @@ - They eventually become mixed with the numbered issues. (I.e., numbered - issues do not "pop up" wrt. others). - --> -+
-+ -+ -+ -+ Update the internal fork of Commons File Upload to r1743698 (1.3.1 plus -+ additional fixes). (markt) -+ -+ -+ -+
-
- - diff --git a/SOURCES/tomcat-7.0.69-CVE-2016-5388.patch b/SOURCES/tomcat-7.0.69-CVE-2016-5388.patch deleted file mode 100644 index e7620cc..0000000 --- a/SOURCES/tomcat-7.0.69-CVE-2016-5388.patch +++ /dev/null @@ -1,117 +0,0 @@ ---- conf/web.xml.orig 2016-08-23 14:41:14.488986580 -0400 -+++ conf/web.xml 2016-08-23 14:41:14.497986572 -0400 -@@ -346,6 +346,15 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - -@@ -369,7 +378,7 @@ - cgiPathPrefix - WEB-INF/cgi - -- 5 -+ 5 - - --> - ---- java/org/apache/catalina/servlets/CGIServlet.java.orig 2016-08-23 14:41:14.489986579 -0400 -+++ java/org/apache/catalina/servlets/CGIServlet.java 2016-08-23 14:42:41.287905267 -0400 -@@ -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; -@@ -276,6 +277,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 with HTTP_ 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(); - -@@ -339,6 +350,10 @@ - "stderrTimeout")); - } - -+ if (getServletConfig().getInitParameter("envHttpHeaders") != null) { -+ envHttpHeadersPattern = -+ Pattern.compile(getServletConfig().getInitParameter("envHttpHeaders")); -+ } - } - - -@@ -1106,12 +1121,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)); - } - } - ---- webapps/docs/cgi-howto.xml.orig 2016-08-23 14:41:14.490986578 -0400 -+++ webapps/docs/cgi-howto.xml 2016-08-23 14:41:14.494986575 -0400 -@@ -111,6 +111,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 - System.getProperty("file.encoding","UTF-8"). That is the system ---- webapps/docs/changelog.xml.orig 2016-08-23 14:41:14.491986578 -0400 -+++ webapps/docs/changelog.xml 2016-08-23 14:42:04.119940086 -0400 -@@ -57,6 +57,19 @@ - They eventually become mixed with the numbered issues. (I.e., numbered - issues do not "pop up" wrt. others). - --> -+
    -+ -+ -+ -+ Add a new initialisation parameter, envHttpHeaders, to -+ the CGI Servlet to mitigate httpoxy -+ (CVE-2016-5388) by default and to provide a mechanism that can be -+ used to mitigate any future, similar issues. (markt) -+ -+ -+ -+
    -
    - - diff --git a/SOURCES/tomcat-7.0.69-CVE-2016-6816.patch b/SOURCES/tomcat-7.0.69-CVE-2016-6816.patch deleted file mode 100644 index 654c804..0000000 --- a/SOURCES/tomcat-7.0.69-CVE-2016-6816.patch +++ /dev/null @@ -1,1194 +0,0 @@ ---- conf/catalina.properties.orig 2017-03-28 15:06:40.627389816 -0400 -+++ conf/catalina.properties 2017-03-28 15:06:40.657389967 -0400 -@@ -131,3 +131,7 @@ - #tomcat.util.buf.StringCache.char.enabled=true - #tomcat.util.buf.StringCache.trainThreshold=500000 - #tomcat.util.buf.StringCache.cacheSize=5000 -+ -+# Allow for changes to HTTP request validation -+# WARNING: Using this option will expose the server to CVE-2016-6816 -+#tomcat.util.http.parser.HttpParser.requestTargetAllow=| ---- java/org/apache/coyote/http11/AbstractInputBuffer.java.orig 2017-03-28 15:06:40.628389821 -0400 -+++ java/org/apache/coyote/http11/AbstractInputBuffer.java 2017-03-28 15:06:40.649389926 -0400 -@@ -28,62 +28,10 @@ - - public abstract class AbstractInputBuffer implements InputBuffer{ - -- protected static final boolean[] HTTP_TOKEN_CHAR = new boolean[128]; -- - /** - * The string manager for this package. - */ -- protected static final StringManager sm = -- StringManager.getManager(Constants.Package); -- -- -- static { -- for (int i = 0; i < 128; i++) { -- if (i < 32) { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == 127) { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '(') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == ')') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '<') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '>') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '@') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == ',') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == ';') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == ':') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '\\') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '\"') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '/') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '[') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == ']') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '?') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '=') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '{') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == '}') { -- HTTP_TOKEN_CHAR[i] = false; -- } else if (i == ' ') { -- HTTP_TOKEN_CHAR[i] = false; -- } else { -- HTTP_TOKEN_CHAR[i] = true; -- } -- } -- } -+ protected static final StringManager sm = StringManager.getManager(Constants.Package); - - - /** ---- java/org/apache/coyote/http11/InternalAprInputBuffer.java.orig 2017-03-28 15:06:40.629389826 -0400 -+++ java/org/apache/coyote/http11/InternalAprInputBuffer.java 2017-03-28 15:06:40.650389932 -0400 -@@ -30,6 +30,7 @@ - import org.apache.tomcat.jni.Status; - import org.apache.tomcat.util.buf.ByteChunk; - import org.apache.tomcat.util.buf.MessageBytes; -+import org.apache.tomcat.util.http.parser.HttpParser; - import org.apache.tomcat.util.net.AbstractEndpoint; - import org.apache.tomcat.util.net.SocketWrapper; - -@@ -70,7 +71,7 @@ - - parsingHeader = true; - swallowInput = true; -- -+ - } - - -@@ -93,7 +94,7 @@ - // --------------------------------------------------------- Public Methods - - /** -- * Recycle the input buffer. This should be called when closing the -+ * Recycle the input buffer. This should be called when closing the - * connection. - */ - @Override -@@ -105,14 +106,14 @@ - - - /** -- * Read the request line. This function is meant to be used during the -- * HTTP request header parsing. Do NOT attempt to read the request body -+ * Read the request line. This function is meant to be used during the -+ * HTTP request header parsing. Do NOT attempt to read the request body - * using it. - * - * @throws IOException If an exception occurs during the underlying socket - * read operations, or if the given buffer is not big enough to accommodate - * the whole line. -- * @return true if data is properly fed; false if no data is available -+ * @return true if data is properly fed; false if no data is available - * immediately and thread should be freed - */ - @Override -@@ -159,7 +160,7 @@ - - // - // Reading the method name -- // Method name is always US-ASCII -+ // Method name is a token - // - - boolean space = false; -@@ -172,22 +173,20 @@ - throw new EOFException(sm.getString("iib.eof.error")); - } - -- // Spec says no CR or LF in method name -- if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) { -- throw new IllegalArgumentException( -- sm.getString("iib.invalidmethod")); -- } -- // Spec says single SP but it also says be tolerant of HT -+ // Spec says method name is a token followed by a single SP but -+ // also be tolerant of multiple SP and/or HT. - if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { - space = true; - request.method().setBytes(buf, start, pos - start); -+ } else if (!HttpParser.isToken(buf[pos])) { -+ throw new IllegalArgumentException(sm.getString("iib.invalidmethod")); - } - - pos++; - - } - -- // Spec says single SP but also says be tolerant of multiple and/or HT -+ // Spec says single SP but also says be tolerant of multiple SP and/or HT - while (space) { - // Read new bytes if needed - if (pos >= lastValid) { -@@ -224,15 +223,16 @@ - if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { - space = true; - end = pos; -- } else if ((buf[pos] == Constants.CR) -+ } else if ((buf[pos] == Constants.CR) - || (buf[pos] == Constants.LF)) { - // HTTP/0.9 style request - eol = true; - space = true; - end = pos; -- } else if ((buf[pos] == Constants.QUESTION) -- && (questionPos == -1)) { -+ } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) { - questionPos = pos; -+ } else if (HttpParser.isNotRequestTarget(buf[pos])) { -+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); - } - - pos++; -@@ -241,7 +241,7 @@ - - request.unparsedURI().setBytes(buf, start, end - start); - if (questionPos >= 0) { -- request.queryString().setBytes(buf, questionPos + 1, -+ request.queryString().setBytes(buf, questionPos + 1, - end - questionPos - 1); - request.requestURI().setBytes(buf, start, questionPos - start); - } else { -@@ -269,7 +269,7 @@ - - // - // Reading the protocol -- // Protocol is always US-ASCII -+ // Protocol is always "HTTP/" DIGIT "." DIGIT - // - - while (!eol) { -@@ -286,6 +286,8 @@ - if (end == 0) - end = pos; - eol = true; -+ } else if (!HttpParser.isHttpProtocol(buf[pos])) { -+ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol")); - } - - pos++; -@@ -297,7 +299,7 @@ - } else { - request.protocol().setString(""); - } -- -+ - return true; - - } -@@ -326,7 +328,7 @@ - - /** - * Parse an HTTP header. -- * -+ * - * @return false after reading a blank line (which indicates that the - * HTTP header parsing is done - */ -@@ -384,7 +386,7 @@ - if (buf[pos] == Constants.COLON) { - colon = true; - headerValue = headers.addValue(buf, start, pos - start); -- } else if (!HTTP_TOKEN_CHAR[buf[pos]]) { -+ } else if (!HttpParser.isToken(buf[pos])) { - // If a non-token header is detected, skip the line and - // ignore the header - skipLine(start); -@@ -490,14 +492,14 @@ - - } - -- -+ - private void skipLine(int start) throws IOException { - boolean eol = false; - int lastRealByte = start; - if (pos - 1 > start) { - lastRealByte = pos - 1; - } -- -+ - while (!eol) { - - // Read new bytes if needed -@@ -521,8 +523,8 @@ - lastRealByte - start + 1, Charset.forName("ISO-8859-1")))); - } - } -- -- -+ -+ - // ---------------------------------------------------- InputBuffer Methods - - -@@ -530,7 +532,7 @@ - * Read some bytes. - */ - @Override -- public int doRead(ByteChunk chunk, Request req) -+ public int doRead(ByteChunk chunk, Request req) - throws IOException { - - if (lastActiveFilter == -1) -@@ -558,11 +560,11 @@ - // Ignore the block parameter and just call fill - return fill(); - } -- -- -+ -+ - /** - * Fill the internal buffer using data from the underlying input stream. -- * -+ * - * @return false if at end of stream - */ - protected boolean fill() -@@ -594,7 +596,7 @@ - } else { - - if (buf.length - end < 4500) { -- // In this case, the request header was really large, so we allocate a -+ // In this case, the request header was really large, so we allocate a - // brand new one; the old one will get GCed when subsequent requests - // clear all references - buf = new byte[buf.length]; -@@ -640,7 +642,7 @@ - * This class is an input buffer which will read its data from an input - * stream. - */ -- protected class SocketInputBuffer -+ protected class SocketInputBuffer - implements InputBuffer { - - -@@ -648,7 +650,7 @@ - * Read bytes into the specified chunk. - */ - @Override -- public int doRead(ByteChunk chunk, Request req ) -+ public int doRead(ByteChunk chunk, Request req ) - throws IOException { - - if (pos >= lastValid) { ---- java/org/apache/coyote/http11/InternalInputBuffer.java.orig 2017-03-28 15:06:40.630389831 -0400 -+++ java/org/apache/coyote/http11/InternalInputBuffer.java 2017-03-28 15:06:40.650389932 -0400 -@@ -28,6 +28,7 @@ - import org.apache.juli.logging.LogFactory; - import org.apache.tomcat.util.buf.ByteChunk; - import org.apache.tomcat.util.buf.MessageBytes; -+import org.apache.tomcat.util.http.parser.HttpParser; - import org.apache.tomcat.util.net.AbstractEndpoint; - import org.apache.tomcat.util.net.SocketWrapper; - -@@ -69,10 +70,10 @@ - - } - -- -+ - /** -- * Read the request line. This function is meant to be used during the -- * HTTP request header parsing. Do NOT attempt to read the request body -+ * Read the request line. This function is meant to be used during the -+ * HTTP request header parsing. Do NOT attempt to read the request body - * using it. - * - * @throws IOException If an exception occurs during the underlying socket -@@ -81,7 +82,7 @@ - */ - @Override - public boolean parseRequestLine(boolean useAvailableDataOnly) -- -+ - throws IOException { - - int start = 0; -@@ -113,7 +114,7 @@ - - // - // Reading the method name -- // Method name is always US-ASCII -+ // Method name is a token - // - - boolean space = false; -@@ -126,23 +127,20 @@ - throw new EOFException(sm.getString("iib.eof.error")); - } - -- // Spec says no CR or LF in method name -- if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) { -- throw new IllegalArgumentException( -- sm.getString("iib.invalidmethod")); -- } -- // Spec says single SP but it also says be tolerant of HT -+ // Spec says method name is a token followed by a single SP but -+ // also be tolerant of multiple SP and/or HT. - if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { - space = true; - request.method().setBytes(buf, start, pos - start); -+ } else if (!HttpParser.isToken(buf[pos])) { -+ throw new IllegalArgumentException(sm.getString("iib.invalidmethod")); - } - - pos++; - - } - -- -- // Spec says single SP but also says be tolerant of multiple and/or HT -+ // Spec says single SP but also be tolerant of multiple SP and/or HT - while (space) { - // Read new bytes if needed - if (pos >= lastValid) { -@@ -179,15 +177,16 @@ - if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { - space = true; - end = pos; -- } else if ((buf[pos] == Constants.CR) -+ } else if ((buf[pos] == Constants.CR) - || (buf[pos] == Constants.LF)) { - // HTTP/0.9 style request - eol = true; - space = true; - end = pos; -- } else if ((buf[pos] == Constants.QUESTION) -- && (questionPos == -1)) { -+ } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) { - questionPos = pos; -+ } else if (HttpParser.isNotRequestTarget(buf[pos])) { -+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); - } - - pos++; -@@ -196,14 +195,14 @@ - - request.unparsedURI().setBytes(buf, start, end - start); - if (questionPos >= 0) { -- request.queryString().setBytes(buf, questionPos + 1, -+ request.queryString().setBytes(buf, questionPos + 1, - end - questionPos - 1); - request.requestURI().setBytes(buf, start, questionPos - start); - } else { - request.requestURI().setBytes(buf, start, end - start); - } - -- // Spec says single SP but also says be tolerant of multiple and/or HT -+ // Spec says single SP but also says be tolerant of multiple SP and/or HT - while (space) { - // Read new bytes if needed - if (pos >= lastValid) { -@@ -223,9 +222,8 @@ - - // - // Reading the protocol -- // Protocol is always US-ASCII -+ // Protocol is always "HTTP/" DIGIT "." DIGIT - // -- - while (!eol) { - - // Read new bytes if needed -@@ -240,6 +238,8 @@ - if (end == 0) - end = pos; - eol = true; -+ } else if (!HttpParser.isHttpProtocol(buf[pos])) { -+ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol")); - } - - pos++; -@@ -251,7 +251,7 @@ - } else { - request.protocol().setString(""); - } -- -+ - return true; - - } -@@ -280,7 +280,7 @@ - - /** - * Parse an HTTP header. -- * -+ * - * @return false after reading a blank line (which indicates that the - * HTTP header parsing is done - */ -@@ -338,7 +338,7 @@ - if (buf[pos] == Constants.COLON) { - colon = true; - headerValue = headers.addValue(buf, start, pos - start); -- } else if (!HTTP_TOKEN_CHAR[buf[pos]]) { -+ } else if (!HttpParser.isToken(buf[pos])) { - // If a non-token header is detected, skip the line and - // ignore the header - skipLine(start); -@@ -470,7 +470,7 @@ - if (pos - 1 > start) { - lastRealByte = pos - 1; - } -- -+ - while (!eol) { - - // Read new bytes if needed -@@ -497,7 +497,7 @@ - - /** - * Fill the internal buffer using data from the underlying input stream. -- * -+ * - * @return false if at end of stream - */ - protected boolean fill() throws IOException { -@@ -524,7 +524,7 @@ - } else { - - if (buf.length - end < 4500) { -- // In this case, the request header was really large, so we allocate a -+ // In this case, the request header was really large, so we allocate a - // brand new one; the old one will get GCed when subsequent requests - // clear all references - buf = new byte[buf.length]; -@@ -551,7 +551,7 @@ - * This class is an input buffer which will read its data from an input - * stream. - */ -- protected class InputStreamInputBuffer -+ protected class InputStreamInputBuffer - implements InputBuffer { - - -@@ -559,7 +559,7 @@ - * Read bytes into the specified chunk. - */ - @Override -- public int doRead(ByteChunk chunk, Request req ) -+ public int doRead(ByteChunk chunk, Request req ) - throws IOException { - - if (pos >= lastValid) { ---- java/org/apache/coyote/http11/InternalNioInputBuffer.java.orig 2017-03-28 15:06:40.631389836 -0400 -+++ java/org/apache/coyote/http11/InternalNioInputBuffer.java 2017-03-28 15:06:40.650389932 -0400 -@@ -25,6 +25,7 @@ - import org.apache.coyote.Request; - import org.apache.tomcat.util.buf.ByteChunk; - import org.apache.tomcat.util.buf.MessageBytes; -+import org.apache.tomcat.util.http.parser.HttpParser; - import org.apache.tomcat.util.net.AbstractEndpoint; - import org.apache.tomcat.util.net.NioChannel; - import org.apache.tomcat.util.net.NioEndpoint; -@@ -92,7 +93,7 @@ - } - - // ----------------------------------------------------------- Constructors -- -+ - - /** - * Alternate constructor. -@@ -137,7 +138,7 @@ - * Underlying socket. - */ - private NioChannel socket; -- -+ - /** - * Selector pool, for blocking reads and blocking writes - */ -@@ -159,7 +160,7 @@ - // --------------------------------------------------------- Public Methods - - /** -- * Recycle the input buffer. This should be called when closing the -+ * Recycle the input buffer. This should be called when closing the - * connection. - */ - @Override -@@ -178,7 +179,7 @@ - - /** - * End processing of current HTTP request. -- * Note: All bytes of the current request should have been already -+ * Note: All bytes of the current request should have been already - * consumed. This method only resets all the pointers so that we are ready - * to parse the next HTTP request. - */ -@@ -195,14 +196,14 @@ - } - - /** -- * Read the request line. This function is meant to be used during the -- * HTTP request header parsing. Do NOT attempt to read the request body -+ * Read the request line. This function is meant to be used during the -+ * HTTP request header parsing. Do NOT attempt to read the request body - * using it. - * - * @throws IOException If an exception occurs during the underlying socket - * read operations, or if the given buffer is not big enough to accommodate - * the whole line. -- * @return true if data is properly fed; false if no data is available -+ * @return true if data is properly fed; false if no data is available - * immediately and thread should be freed - */ - @Override -@@ -217,7 +218,7 @@ - if ( parsingRequestLinePhase == 0 ) { - byte chr = 0; - do { -- -+ - // Read new bytes if needed - if (pos >= lastValid) { - if (useAvailableDataOnly) { -@@ -248,7 +249,7 @@ - if ( parsingRequestLinePhase == 2 ) { - // - // Reading the method name -- // Method name is always US-ASCII -+ // Method name is a token - // - boolean space = false; - while (!space) { -@@ -257,21 +258,20 @@ - if (!fill(true, false)) //request line parsing - return false; - } -- // Spec says no CR or LF in method name -- if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) { -- throw new IllegalArgumentException( -- sm.getString("iib.invalidmethod")); -- } -+ // Spec says method name is a token followed by a single SP but -+ // also be tolerant of multiple SP and/or HT. - if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { - space = true; - request.method().setBytes(buf, parsingRequestLineStart, pos - parsingRequestLineStart); -+ } else if (!HttpParser.isToken(buf[pos])) { -+ throw new IllegalArgumentException(sm.getString("iib.invalidmethod")); - } - pos++; - } - parsingRequestLinePhase = 3; - } - if ( parsingRequestLinePhase == 3 ) { -- // Spec says single SP but also be tolerant of multiple and/or HT -+ // Spec says single SP but also be tolerant of multiple SP and/or HT - boolean space = true; - while (space) { - // Read new bytes if needed -@@ -290,7 +290,7 @@ - } - if (parsingRequestLinePhase == 4) { - // Mark the current buffer position -- -+ - int end = 0; - // - // Reading the URI -@@ -305,21 +305,22 @@ - if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { - space = true; - end = pos; -- } else if ((buf[pos] == Constants.CR) -+ } else if ((buf[pos] == Constants.CR) - || (buf[pos] == Constants.LF)) { - // HTTP/0.9 style request - parsingRequestLineEol = true; - space = true; - end = pos; -- } else if ((buf[pos] == Constants.QUESTION) -- && (parsingRequestLineQPos == -1)) { -+ } else if ((buf[pos] == Constants.QUESTION) && (parsingRequestLineQPos == -1)) { - parsingRequestLineQPos = pos; -+ } else if (HttpParser.isNotRequestTarget(buf[pos])) { -+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); - } - pos++; - } - request.unparsedURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart); - if (parsingRequestLineQPos >= 0) { -- request.queryString().setBytes(buf, parsingRequestLineQPos + 1, -+ request.queryString().setBytes(buf, parsingRequestLineQPos + 1, - end - parsingRequestLineQPos - 1); - request.requestURI().setBytes(buf, parsingRequestLineStart, parsingRequestLineQPos - parsingRequestLineStart); - } else { -@@ -348,10 +349,10 @@ - // Mark the current buffer position - end = 0; - } -- if (parsingRequestLinePhase == 6) { -+ if (parsingRequestLinePhase == 6) { - // - // Reading the protocol -- // Protocol is always US-ASCII -+ // Protocol is always "HTTP/" DIGIT "." DIGIT - // - while (!parsingRequestLineEol) { - // Read new bytes if needed -@@ -359,17 +360,19 @@ - if (!fill(true, false)) //request line parsing - return false; - } -- -+ - if (buf[pos] == Constants.CR) { - end = pos; - } else if (buf[pos] == Constants.LF) { - if (end == 0) - end = pos; - parsingRequestLineEol = true; -+ } else if (!HttpParser.isHttpProtocol(buf[pos])) { -+ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol")); - } - pos++; - } -- -+ - if ( (end - parsingRequestLineStart) > 0) { - request.protocol().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart); - } else { -@@ -383,7 +386,7 @@ - } - throw new IllegalStateException("Invalid request line parse phase:"+parsingRequestLinePhase); - } -- -+ - private void expand(int newsize) { - if ( newsize > buf.length ) { - if (parsingHeader) { -@@ -398,7 +401,7 @@ - buf = tmp; - } - } -- -+ - /** - * Perform blocking read with a timeout if desired - * @param timeout boolean - if we want to use the timeout data -@@ -407,7 +410,7 @@ - * @throws IOException if a socket exception occurs - * @throws EOFException if end of stream is reached - */ -- -+ - private int readSocket(boolean timeout, boolean block) throws IOException { - int nRead = 0; - socket.getBufHandler().getReadBuffer().clear(); -@@ -429,7 +432,7 @@ - socket.getIOChannel().socket().getSoTimeout()); - } catch ( EOFException eof ) { - nRead = -1; -- } finally { -+ } finally { - if ( selector != null ) pool.put(selector); - } - } else { -@@ -462,7 +465,7 @@ - } - - HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS; -- -+ - do { - status = parseHeader(); - // Checking that -@@ -491,7 +494,7 @@ - - /** - * Parse an HTTP header. -- * -+ * - * @return false after reading a blank line (which indicates that the - * HTTP header parsing is done - */ -@@ -507,7 +510,7 @@ - - // Read new bytes if needed - if (pos >= lastValid) { -- if (!fill(true,false)) {//parse header -+ if (!fill(true,false)) {//parse header - headerParsePos = HeaderParsePosition.HEADER_START; - return HeaderParseStatus.NEED_MORE_DATA; - } -@@ -543,7 +546,7 @@ - - // Read new bytes if needed - if (pos >= lastValid) { -- if (!fill(true,false)) { //parse header -+ if (!fill(true,false)) { //parse header - return HeaderParseStatus.NEED_MORE_DATA; - } - } -@@ -558,7 +561,7 @@ - headerData.realPos = pos; - headerData.lastSignificantChar = pos; - break; -- } else if (!HTTP_TOKEN_CHAR[chr]) { -+ } else if (!HttpParser.isToken(chr)) { - // If a non-token header is detected, skip the line and - // ignore the header - headerData.lastSignificantChar = pos; -@@ -590,7 +593,7 @@ - while (true) { - // Read new bytes if needed - if (pos >= lastValid) { -- if (!fill(true,false)) {//parse header -+ if (!fill(true,false)) {//parse header - //HEADER_VALUE_START - return HeaderParseStatus.NEED_MORE_DATA; - } -@@ -613,7 +616,7 @@ - - // Read new bytes if needed - if (pos >= lastValid) { -- if (!fill(true,false)) {//parse header -+ if (!fill(true,false)) {//parse header - //HEADER_VALUE - return HeaderParseStatus.NEED_MORE_DATA; - } -@@ -646,7 +649,7 @@ - // Read new bytes if needed - if (pos >= lastValid) { - if (!fill(true,false)) {//parse header -- -+ - //HEADER_MULTI_LINE - return HeaderParseStatus.NEED_MORE_DATA; - } -@@ -672,7 +675,7 @@ - headerData.recycle(); - return HeaderParseStatus.HAVE_MORE_HEADERS; - } -- -+ - public int getParsingRequestLinePhase() { - return parsingRequestLinePhase; - } -@@ -771,7 +774,7 @@ - - /** - * Fill the internal buffer using data from the underlying input stream. -- * -+ * - * @return false if at end of stream - */ - @Override -@@ -780,7 +783,7 @@ - } - - protected boolean fill(boolean timeout, boolean block) throws IOException, EOFException { -- -+ - - boolean read = false; - -@@ -809,7 +812,7 @@ - * This class is an input buffer which will read its data from an input - * stream. - */ -- protected class SocketInputBuffer -+ protected class SocketInputBuffer - implements InputBuffer { - - -@@ -817,7 +820,7 @@ - * Read bytes into the specified chunk. - */ - @Override -- public int doRead(ByteChunk chunk, Request req ) -+ public int doRead(ByteChunk chunk, Request req ) - throws IOException { - - if (pos >= lastValid) { ---- java/org/apache/coyote/http11/LocalStrings.properties.orig 2017-03-28 15:06:40.632389841 -0400 -+++ java/org/apache/coyote/http11/LocalStrings.properties 2017-03-28 15:06:40.649389926 -0400 -@@ -42,8 +42,10 @@ - iib.apr.sslGeneralError=An APR general error was returned by the SSL read operation on APR/native socket [{0}] with wrapper [{1}]. It will be treated as EAGAIN and the socket returned to the poller. - - iib.eof.error=Unexpected EOF read on the socket --iib.invalidheader=The HTTP header line [{0}] does not conform to RFC 2616 and has been ignored. --iib.invalidmethod=Invalid character (CR or LF) found in method name -+iib.invalidheader=The HTTP header line [{0}] does not conform to RFC 7230 and has been ignored. -+iib.invalidmethod=Invalid character found in method name. HTTP method names must be tokens -+iib.invalidRequestTarget=Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986 -+iib.invalidHttpProtocol=Invalid character found in the HTTP protocol - iib.parseheaders.ise.error=Unexpected state: headers already parsed. Buffer not recycled? - iib.requestheadertoolarge.error=Request header is too large - ---- java/org/apache/tomcat/util/http/parser/HttpParser.java.orig 2017-03-28 15:06:40.633389846 -0400 -+++ java/org/apache/tomcat/util/http/parser/HttpParser.java 2017-03-28 15:06:40.657389967 -0400 -@@ -23,6 +23,11 @@ - import java.util.Locale; - import java.util.Map; - -+import org.apache.juli.logging.Log; -+import org.apache.juli.logging.LogFactory; -+ -+import org.apache.tomcat.util.res.StringManager; -+ - /** - * HTTP header value parser implementation. Parsing HTTP headers as per RFC2616 - * is not always as simple as it first appears. For headers that only use tokens -@@ -53,9 +58,19 @@ - private static final Map fieldTypes = - new HashMap(); - -- // Arrays used by isToken(), isHex() -- private static final boolean isToken[] = new boolean[128]; -- private static final boolean isHex[] = new boolean[128]; -+ private static final StringManager sm = StringManager.getManager(HttpParser.class); -+ -+ private static final Log log = LogFactory.getLog(HttpParser.class); -+ -+ private static final int ARRAY_SIZE = 128; -+ -+ private static final boolean[] IS_CONTROL = new boolean[ARRAY_SIZE]; -+ private static final boolean[] IS_SEPARATOR = new boolean[ARRAY_SIZE]; -+ private static final boolean[] IS_TOKEN = new boolean[ARRAY_SIZE]; -+ private static final boolean[] IS_HEX = new boolean[ARRAY_SIZE]; -+ private static final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE]; -+ private static final boolean[] IS_HTTP_PROTOCOL = new boolean[ARRAY_SIZE]; -+ private static final boolean[] REQUEST_TARGET_ALLOW = new boolean[ARRAY_SIZE]; - - static { - // Digest field types. -@@ -77,24 +92,57 @@ - // RFC2617 says nc is 8LHEX. <">8LHEX<"> will also be accepted - fieldTypes.put("nc", FIELD_TYPE_LHEX); - -- // Setup the flag arrays -- for (int i = 0; i < 128; i++) { -- if (i <= 32) { // includes '\t' and ' ' -- isToken[i] = false; -- } else if (i == '(' || i == ')' || i == '<' || i == '>' || i == '@' || -- i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' || -- i == '/' || i == '[' || i == ']' || i == '?' || i == '=' || -- i == '{' || i == '}') { -- isToken[i] = false; -- } else { -- isToken[i] = true; -+ String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow"); -+ if (prop != null) { -+ for (int i = 0; i < prop.length(); i++) { -+ char c = prop.charAt(i); -+ if (c == '{' || c == '}' || c == '|') { -+ REQUEST_TARGET_ALLOW[c] = true; -+ } else { -+ log.warn(sm.getString("httpparser.invalidRequestTargetCharacter", c)); -+ } - } -+ } - -- if (i >= '0' && i <= '9' || i >= 'A' && i <= 'F' || -- i >= 'a' && i <= 'f') { -- isHex[i] = true; -- } else { -- isHex[i] = false; -+ for (int i = 0; i < ARRAY_SIZE; i++) { -+ // Control> 0-31, 127 -+ if (i < 32 || i == 127) { -+ IS_CONTROL[i] = true; -+ } -+ -+ // Separator -+ if ( i == '(' || i == ')' || i == '<' || i == '>' || i == '@' || -+ i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' || -+ i == '/' || i == '[' || i == ']' || i == '?' || i == '=' || -+ i == '{' || i == '}' || i == ' ' || i == '\t') { -+ IS_SEPARATOR[i] = true; -+ } -+ -+ // Token: Anything 0-127 that is not a control and not a separator -+ if (!IS_CONTROL[i] && !IS_SEPARATOR[i] && i < 128) { -+ IS_TOKEN[i] = true; -+ } -+ -+ // Hex: 0-9, a-f, A-F -+ if ((i >= '0' && i <='9') || (i >= 'a' && i <= 'f') || (i >= 'A' && i <= 'F')) { -+ IS_HEX[i] = true; -+ } -+ -+ // Not valid for request target. -+ // Combination of multiple rules from RFC7230 and RFC 3986. Must be -+ // ASCII, no controls plus a few additional characters excluded -+ if (IS_CONTROL[i] || i > 127 || -+ i == ' ' || i == '\"' || i == '#' || i == '<' || i == '>' || i == '\\' || -+ i == '^' || i == '`' || i == '{' || i == '|' || i == '}') { -+ if (!REQUEST_TARGET_ALLOW[i]) { -+ IS_NOT_REQUEST_TARGET[i] = true; -+ } -+ } -+ -+ // Not valid for HTTP protocol -+ // "HTTP/" DIGIT "." DIGIT -+ if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) { -+ IS_HTTP_PROTOCOL[i] = true; - } - } - } -@@ -228,6 +276,7 @@ - return new MediaType(type, subtype, parameters); - } - -+ - public static String unquote(String input) { - if (input == null || input.length() < 2) { - return input; -@@ -258,24 +307,49 @@ - return result.toString(); - } - -- private static boolean isToken(int c) { -+ -+ public static boolean isToken(int c) { - // Fast for correct values, slower for incorrect ones - try { -- return isToken[c]; -+ return IS_TOKEN[c]; - } catch (ArrayIndexOutOfBoundsException ex) { - return false; - } - } - -- private static boolean isHex(int c) { -- // Fast for correct values, slower for incorrect ones -+ -+ public static boolean isHex(int c) { -+ // Fast for correct values, slower for some incorrect ones -+ try { -+ return IS_HEX[c]; -+ } catch (ArrayIndexOutOfBoundsException ex) { -+ return false; -+ } -+ } -+ -+ -+ public static boolean isNotRequestTarget(int c) { -+ // Fast for valid request target characters, slower for some incorrect -+ // ones - try { -- return isHex[c]; -+ return IS_NOT_REQUEST_TARGET[c]; -+ } catch (ArrayIndexOutOfBoundsException ex) { -+ return true; -+ } -+ } -+ -+ -+ public static boolean isHttpProtocol(int c) { -+ // Fast for valid HTTP protocol characters, slower for some incorrect -+ // ones -+ try { -+ return IS_HTTP_PROTOCOL[c]; - } catch (ArrayIndexOutOfBoundsException ex) { - return false; - } - } - -+ - // Skip any LWS and return the next char - private static int skipLws(StringReader input, boolean withReset) - throws IOException { ---- webapps/docs/changelog.xml.orig 2017-03-28 15:06:40.634389851 -0400 -+++ webapps/docs/changelog.xml 2017-03-28 15:11:46.543926068 -0400 -@@ -64,6 +64,24 @@ - 60409: When unable to complete sendfile request, ensure the - Processor will be added to the cache only once. (markt/violetagg) - -+ -+ Ensure that requests with HTTP method names that are not tokens (as -+ required by RFC 7231) are rejected with a 400 response. (markt) -+ -+ -+ Correct the HTTP header parser so that DEL is not treated as a valid -+ token character. (markt) -+ -+ -+ Add additional checks for valid characters to the HTTP request line -+ parsing so invalid request lines are rejected sooner. (markt) -+ -+ -+ 60594: Allow some invalid characters that were recently -+ restricted to be processed in requests by using the system property -+ tomcat.util.http.parser.HttpParser.requestTargetAllow. -+ (csutherl) -+ - - -
    ---- webapps/docs/config/systemprops.xml.orig 2017-03-28 15:06:40.635389856 -0400 -+++ webapps/docs/config/systemprops.xml 2017-03-28 15:06:40.657389967 -0400 -@@ -708,6 +708,16 @@ -

    If not specified, the default value of 3 will be used.

    - - -+ -+

    A string comprised of characters the server should allow even when they are not encoded. -+ These characters would normally result in a 400 status.

    -+

    The acceptable characters for this property are: |, { -+ , and }

    -+

    WARNING: Use of this option will expose the server to CVE-2016-6816. -+

    -+

    If not specified, the default value of null will be used.

    -+
    -+ - - -
  • ---- java/org/apache/tomcat/util/http/parser/LocalStrings.properties.orig 2017-03-28 15:06:40.637389866 -0400 -+++ java/org/apache/tomcat/util/http/parser/LocalStrings.properties 2017-03-28 15:06:40.657389967 -0400 -@@ -0,0 +1,16 @@ -+# 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. -+ -+httpparser.invalidRequestTargetCharacter=Character [{0}] is not allowed and will continue to be rejected. ---- test/org/apache/tomcat/util/http/parser/TestHttpParser.java.orig 2017-03-28 15:06:40.638389871 -0400 -+++ test/org/apache/tomcat/util/http/parser/TestHttpParser.java 2017-03-28 15:06:40.646389911 -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.http.parser; -+ -+import org.junit.Assert; -+import org.junit.Test; -+ -+public class TestHttpParser { -+ -+ @Test -+ public void testTokenDel() { -+ Assert.assertFalse("DEL is not a token", HttpParser.isToken(127)); -+ } -+} ---- test/org/apache/coyote/http11/TestInternalInputBuffer.java.orig 2017-03-28 15:06:40.639389876 -0400 -+++ test/org/apache/coyote/http11/TestInternalInputBuffer.java 2017-03-28 15:06:40.642389891 -0400 -@@ -478,4 +478,61 @@ - } - - } -+ -+ -+ @Test -+ public void testInvalidMethod() { -+ -+ InvalidMethodClient client = new InvalidMethodClient(); -+ -+ client.doRequest(); -+ assertTrue(client.getResponseLine(), client.isResponse400()); -+ assertTrue(client.isResponseBodyOK()); -+ } -+ -+ -+ /** -+ * Bug 48839 test client. -+ */ -+ private class InvalidMethodClient extends SimpleHttpClient { -+ -+ private Exception doRequest() { -+ -+ Tomcat tomcat = getTomcatInstance(); -+ -+ tomcat.addContext("", TEMP_DIR); -+ -+ try { -+ tomcat.start(); -+ setPort(tomcat.getConnector().getLocalPort()); -+ -+ // Open connection -+ connect(); -+ -+ String[] request = new String[1]; -+ request[0] = -+ "GET" + (char) 0 + " /test HTTP/1.1" + CRLF + -+ "Host: localhost:8080" + CRLF + -+ "Connection: close" + CRLF + -+ CRLF; -+ -+ setRequest(request); -+ processRequest(); // blocks until response has been read -+ -+ // Close the connection -+ disconnect(); -+ } catch (Exception e) { -+ return e; -+ } -+ return null; -+ } -+ -+ @Override -+ public boolean isResponseBodyOK() { -+ if (getResponseBody() == null) { -+ return false; -+ } -+ return true; -+ } -+ } - } diff --git a/SOURCES/tomcat-7.0.69-CVE-2016-8745.patch b/SOURCES/tomcat-7.0.69-CVE-2016-8745.patch deleted file mode 100644 index d0becbb..0000000 --- a/SOURCES/tomcat-7.0.69-CVE-2016-8745.patch +++ /dev/null @@ -1,163 +0,0 @@ ---- java/org/apache/tomcat/util/net/NioEndpoint.java.orig 2017-03-28 14:39:31.522852483 -0400 -+++ java/org/apache/tomcat/util/net/NioEndpoint.java 2017-03-28 14:39:31.528852515 -0400 -@@ -1406,11 +1406,15 @@ - } - }catch ( IOException x ) { - if ( log.isDebugEnabled() ) log.debug("Unable to complete sendfile request:", x); -- cancelledKey(sk,SocketStatus.ERROR,false); -+ if (!calledByProcessor) { -+ cancelledKey(sk,SocketStatus.ERROR,false); -+ } - return SendfileState.ERROR; - }catch ( Throwable t ) { - log.error("",t); -- cancelledKey(sk, SocketStatus.ERROR, false); -+ if (!calledByProcessor) { -+ cancelledKey(sk, SocketStatus.ERROR, false); -+ } - return SendfileState.ERROR; - } - } ---- webapps/docs/changelog.xml.orig 2017-03-28 14:39:31.523852488 -0400 -+++ webapps/docs/changelog.xml 2017-03-28 14:41:38.105546243 -0400 -@@ -57,6 +57,16 @@ - They eventually become mixed with the numbered issues. (I.e., numbered - issues do not "pop up" wrt. others). - --> -+
    -+ -+ -+ -+ 60409: When unable to complete sendfile request, ensure the -+ Processor will be added to the cache only once. (markt/violetagg) -+ -+ -+ -+
    -
    - - ---- test/org/apache/catalina/connector/TestSendFile.java.orig 2017-03-28 14:39:31.524852493 -0400 -+++ test/org/apache/catalina/connector/TestSendFile.java 2017-03-28 14:39:31.527852510 -0400 -@@ -22,10 +22,14 @@ - import java.io.FileInputStream; - import java.io.FileWriter; - import java.io.IOException; -+import java.util.ArrayList; - import java.util.Arrays; - import java.util.HashMap; - import java.util.List; - import java.util.Map; -+import java.util.Random; -+import java.util.concurrent.CountDownLatch; -+import java.util.concurrent.TimeUnit; - - import javax.servlet.ServletException; - import javax.servlet.http.HttpServlet; -@@ -33,6 +37,8 @@ - import javax.servlet.http.HttpServletResponse; - - import static org.junit.Assert.assertEquals; -+ -+import org.junit.Assert; - import org.junit.Test; - - import org.apache.catalina.Context; -@@ -163,4 +169,97 @@ - } - } - -+ -+ @Test -+ public void testBug60409() throws Exception { -+ Tomcat tomcat = getTomcatInstance(); -+ -+ Context ctx = tomcat.addContext("", TEMP_DIR); -+ File file = generateFile(TEMP_DIR, "", EXPECTED_CONTENT_LENGTH); -+ Tomcat.addServlet(ctx, "test", new Bug60409Servlet(file)); -+ ctx.addServletMapping("/", "test"); -+ -+ tomcat.start(); -+ -+ ByteChunk bc = new ByteChunk(); -+ getUrl("http://localhost:" + getPort() + "/test/?" + Globals.SENDFILE_SUPPORTED_ATTR -+ + "=true", bc, null); -+ -+ CountDownLatch latch = new CountDownLatch(2); -+ List exceptions = new ArrayList(); -+ new Thread( -+ new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, exceptions)) -+ .start(); -+ new Thread( -+ new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, exceptions)) -+ .start(); -+ -+ latch.await(3000, TimeUnit.MILLISECONDS); -+ -+ if (exceptions.size() > 0) { -+ Assert.fail(); -+ } -+ } -+ -+ private static final class Bug60409Servlet extends HttpServlet { -+ private static final long serialVersionUID = 1L; -+ private final File file; -+ -+ Bug60409Servlet(File file) { -+ this.file = file; -+ } -+ -+ @Override -+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) -+ throws ServletException, IOException { -+ if (Boolean.valueOf(req.getParameter(Globals.SENDFILE_SUPPORTED_ATTR)).booleanValue()) { -+ resp.setContentType("'application/octet-stream"); -+ resp.setCharacterEncoding("ISO-8859-1"); -+ resp.setContentLength((int) file.length()); -+ req.setAttribute(Globals.SENDFILE_FILENAME_ATTR, file.getAbsolutePath()); -+ req.setAttribute(Globals.SENDFILE_FILE_START_ATTR, new Long(0)); -+ req.setAttribute(Globals.SENDFILE_FILE_END_ATTR, new Long(file.length())); -+ file.delete(); -+ } else { -+ byte[] c = new byte[1024]; -+ Random rd = new Random(); -+ rd.nextBytes(c); -+ try { -+ Thread.sleep(1000); -+ } catch (InterruptedException e) { -+ e.printStackTrace(); -+ } -+ resp.getOutputStream().write(c); -+ } -+ } -+ -+ } -+ -+ private static final class RequestExecutor implements Runnable { -+ private final String url; -+ private final CountDownLatch latch; -+ private final List exceptions; -+ -+ RequestExecutor(String url, CountDownLatch latch, List exceptions) { -+ this.url = url; -+ this.latch = latch; -+ this.exceptions = exceptions; -+ } -+ -+ @Override -+ public void run() { -+ try { -+ ByteChunk result = new ByteChunk(); -+ int rc = getUrl(url, result, null); -+ Assert.assertEquals(HttpServletResponse.SC_OK, rc); -+ Assert.assertEquals(1024, result.getLength()); -+ } catch (Throwable e) { -+ e.printStackTrace(); -+ exceptions.add(e); -+ } finally { -+ latch.countDown(); -+ } -+ } -+ -+ } - } diff --git a/SOURCES/tomcat-7.0.69-CVE-2017-5647.patch b/SOURCES/tomcat-7.0.69-CVE-2017-5647.patch deleted file mode 100644 index e41590d..0000000 --- a/SOURCES/tomcat-7.0.69-CVE-2017-5647.patch +++ /dev/null @@ -1,242 +0,0 @@ ---- 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 deleted file mode 100644 index f0e99a3..0000000 --- a/SOURCES/tomcat-7.0.69-CVE-2017-5648.patch +++ /dev/null @@ -1,136 +0,0 @@ ---- 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 deleted file mode 100644 index 4533c64..0000000 --- a/SOURCES/tomcat-7.0.69-CVE-2017-5664.patch +++ /dev/null @@ -1,119 +0,0 @@ ---- 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/SOURCES/tomcat-7.0.76-CVE-2017-5647.patch b/SOURCES/tomcat-7.0.76-CVE-2017-5647.patch new file mode 100644 index 0000000..6789452 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2017-5647.patch @@ -0,0 +1,231 @@ +--- java/org/apache/coyote/AbstractProtocol.java.orig 2017-06-08 16:23:31.981000734 -0400 ++++ java/org/apache/coyote/AbstractProtocol.java 2017-06-08 16:23:32.002000817 -0400 +@@ -693,10 +693,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-08 16:23:31.983000742 -0400 ++++ java/org/apache/coyote/http11/Http11AprProcessor.java 2017-06-08 16:23:31.999000805 -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; ++ } + switch (((AprEndpoint)endpoint).getSendfile().add(sendfileData)) { + case DONE: + return false; +--- java/org/apache/coyote/http11/Http11NioProcessor.java.orig 2017-06-08 16:23:31.984000746 -0400 ++++ java/org/apache/coyote/http11/Http11NioProcessor.java 2017-06-08 16:23:32.000000809 -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-08 16:23:31.985000750 -0400 ++++ java/org/apache/tomcat/util/net/AprEndpoint.java 2017-06-08 16:23:32.001000813 -0400 +@@ -2106,7 +2106,7 @@ + // Position + public long pos; + // KeepAlive flag +- public boolean keepAlive; ++ public SendfileKeepAliveState keepAliveState = SendfileKeepAliveState.NONE; + } + + +@@ -2349,20 +2349,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-08 16:23:31.987000757 -0400 ++++ java/org/apache/tomcat/util/net/NioEndpoint.java 2017-06-08 16:23:32.002000817 -0400 +@@ -1383,16 +1383,30 @@ + // responsible for registering the socket for the + // appropriate event(s) if sendfile completes. + if (!calledByProcessor) { +- if ( sd.keepAlive ) { +- if (log.isDebugEnabled()) { +- log.debug("Connection is keep alive, registering back for OP_READ"); +- } +- reg(sk,attachment,SelectionKey.OP_READ); +- } else { ++ switch (sd.keepAliveState) { ++ case NONE: { + 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; +@@ -1836,6 +1850,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-08 16:23:31.989000765 -0400 ++++ webapps/docs/changelog.xml 2017-06-08 16:25:23.618440723 -0400 +@@ -73,6 +73,13 @@ + + + ++ ++ ++ ++ Improve sendfile handling when requests are pipelined. (markt) ++ ++ ++ +
    +
    + +--- java/org/apache/tomcat/util/net/SendfileKeepAliveState.java.orig 2017-06-08 16:23:31.992000777 -0400 ++++ java/org/apache/tomcat/util/net/SendfileKeepAliveState.java 2017-06-08 16:23:32.000000809 -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.76-CVE-2017-5664.patch b/SOURCES/tomcat-7.0.76-CVE-2017-5664.patch new file mode 100644 index 0000000..f1cb009 --- /dev/null +++ b/SOURCES/tomcat-7.0.76-CVE-2017-5664.patch @@ -0,0 +1,125 @@ +--- java/org/apache/catalina/servlets/DefaultServlet.java.orig 2017-06-08 16:12:18.426412964 -0400 ++++ java/org/apache/catalina/servlets/DefaultServlet.java 2017-06-08 16:12:18.436413002 -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-08 16:12:18.427412968 -0400 ++++ java/org/apache/catalina/servlets/WebdavServlet.java 2017-06-08 16:12:18.436413002 -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; +@@ -354,6 +355,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-08 16:12:18.429412975 -0400 ++++ webapps/docs/changelog.xml 2017-06-08 16:13:17.452638065 -0400 +@@ -57,6 +57,23 @@ + They eventually become mixed with the numbered issues. (I.e., numbered + issues do not "pop up" wrt. others). + --> ++
    ++ ++ ++ ++ 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 0c407b7..4619b86 100644 --- a/SPECS/tomcat.spec +++ b/SPECS/tomcat.spec @@ -31,7 +31,7 @@ %global jspspec 2.2 %global major_version 7 %global minor_version 0 -%global micro_version 69 +%global micro_version 76 %global packdname apache-tomcat-%{version}-src %global servletspec 3.0 %global elspec 2.2 @@ -54,7 +54,7 @@ Name: tomcat Epoch: 0 Version: %{major_version}.%{minor_version}.%{micro_version} -Release: 12%{?dist} +Release: 2%{?dist} Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API Group: System Environment/Daemons @@ -88,13 +88,8 @@ Patch0: %{name}-%{major_version}.%{minor_version}-bootstrap-MANIFEST.MF.patch Patch1: %{name}-%{major_version}.%{minor_version}-tomcat-users-webapp.patch Patch2: %{name}-7.0.54-rebase.patch Patch3: %{name}-7.0-catalina-policy.patch -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 +Patch4: %{name}-7.0.76-CVE-2017-5664.patch +Patch5: %{name}-7.0.76-CVE-2017-5647.patch BuildArch: noarch @@ -245,11 +240,6 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name " %patch3 -p0 %patch4 -p0 %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 @@ -689,14 +679,12 @@ 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 +* Thu Jun 08 2017 Coty Sutherland 0:7.0.76-2 +- Resolves: rhbz#1459747 CVE-2017-5664 tomcat: Security constrained bypass in error page mechanism +- Resolves: rhbz#1441481 CVE-2017-5647 tomcat: Incorrect handling of pipelined requests when send file was used + +* Wed Mar 29 2017 Coty Sutherland - 0:7.0.76-1 +- Resolves: rhbz#1414895 Rebase tomcat to the current release * Thu Aug 25 2016 Coty Sutherland - 0:7.0.69-10 - Related: rhbz#1368122