From 84184975c2a60ca6613405a89cc2276079b659d6 Mon Sep 17 00:00:00 2001
From: CentOS Sources
Date: Nov 10 2020 09:48:07 +0000
Subject: import tomcat-7.0.76-16.el7_9
---
diff --git a/SOURCES/tomcat-7.0.76-CVE-2020-1935.patch b/SOURCES/tomcat-7.0.76-CVE-2020-1935.patch
new file mode 100644
index 0000000..73b1b5d
--- /dev/null
+++ b/SOURCES/tomcat-7.0.76-CVE-2020-1935.patch
@@ -0,0 +1,985 @@
+diff -up ./java/org/apache/coyote/http11/AbstractHttp11Protocol.java.orig ./java/org/apache/coyote/http11/AbstractHttp11Protocol.java
+--- ./java/org/apache/coyote/http11/AbstractHttp11Protocol.java.orig 2020-04-24 14:57:33.400591691 -0400
++++ ./java/org/apache/coyote/http11/AbstractHttp11Protocol.java 2020-04-24 14:28:55.936278683 -0400
+@@ -62,6 +62,59 @@ public abstract class AbstractHttp11Prot
+ this.relaxedQueryChars = relaxedQueryChars;
+ }
+
++ private boolean rejectIllegalHeader = false;
++ /**
++ * If an HTTP request is received that contains an illegal header name or
++ * value (e.g. the header name is not a token) will the request be rejected
++ * (with a 400 response) or will the illegal header be ignored?
++ *
++ * @return {@code true} if the request will be rejected or {@code false} if
++ * the header will be ignored
++ */
++ public boolean getRejectIllegalHeader() { return rejectIllegalHeader; }
++ /**
++ * If an HTTP request is received that contains an illegal header name or
++ * value (e.g. the header name is not a token) should the request be
++ * rejected (with a 400 response) or should the illegal header be ignored?
++ *
++ * @param rejectIllegalHeader {@code true} to reject requests with illegal
++ * header names or values, {@code false} to
++ * ignore the header
++ */
++ public void setRejectIllegalHeader(boolean rejectIllegalHeader) {
++ this.rejectIllegalHeader = rejectIllegalHeader;
++ }
++ /**
++ * If an HTTP request is received that contains an illegal header name or
++ * value (e.g. the header name is not a token) will the request be rejected
++ * (with a 400 response) or will the illegal header be ignored?
++ *
++ * @return {@code true} if the request will be rejected or {@code false} if
++ * the header will be ignored
++ *
++ * @deprecated Now an alias for {@link #getRejectIllegalHeader()}. Will be
++ * removed in Tomcat 10 onwards.
++ */
++ @Deprecated
++ public boolean getRejectIllegalHeaderName() { return rejectIllegalHeader; }
++ /**
++ * If an HTTP request is received that contains an illegal header name or
++ * value (e.g. the header name is not a token) should the request be
++ * rejected (with a 400 response) or should the illegal header be ignored?
++ *
++ * @param rejectIllegalHeaderName {@code true} to reject requests with
++ * illegal header names or values,
++ * {@code false} to ignore the header
++ *
++ * @deprecated Now an alias for {@link #setRejectIllegalHeader(boolean)}.
++ * Will be removed in Tomcat 10 onwards.
++ */
++ @Deprecated
++ public void setRejectIllegalHeaderName(boolean rejectIllegalHeaderName) {
++ this.rejectIllegalHeader = rejectIllegalHeaderName;
++ }
++
++
+ private int socketBuffer = 9000;
+ public int getSocketBuffer() { return socketBuffer; }
+ public void setSocketBuffer(int socketBuffer) {
+diff -up ./java/org/apache/coyote/http11/AbstractInputBuffer.java.orig ./java/org/apache/coyote/http11/AbstractInputBuffer.java
+--- ./java/org/apache/coyote/http11/AbstractInputBuffer.java.orig 2020-04-24 14:57:33.402591687 -0400
++++ ./java/org/apache/coyote/http11/AbstractInputBuffer.java 2020-04-24 14:28:55.936278683 -0400
+@@ -34,7 +34,6 @@ public abstract class AbstractInputBuffe
+ */
+ protected static final StringManager sm = StringManager.getManager(Constants.Package);
+
+-
+ /**
+ * Associated Coyote request.
+ */
+@@ -112,6 +111,14 @@ public abstract class AbstractInputBuffe
+ protected HttpParser httpParser;
+
+
++ /**
++ * Do HTTP headers with illegal names and/or values cause the request to be
++ * rejected? Note that the field name is not quite right but cannot be
++ * easily changed without breaking binary compatibility.
++ */
++ protected boolean rejectIllegalHeaderName;
++
++
+ // ------------------------------------------------------------- Properties
+
+
+diff -up ./java/org/apache/coyote/http11/Http11AprProcessor.java.orig ./java/org/apache/coyote/http11/Http11AprProcessor.java
+--- ./java/org/apache/coyote/http11/Http11AprProcessor.java.orig 2020-04-24 14:57:33.387591718 -0400
++++ ./java/org/apache/coyote/http11/Http11AprProcessor.java 2020-04-24 14:46:07.375029423 -0400
+@@ -61,8 +61,8 @@ public class Http11AprProcessor extends
+ // ----------------------------------------------------------- Constructors
+
+
+- public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint, int maxTrailerSize,
+- Set allowedTrailerHeaders,
++ public Http11AprProcessor(int headerBufferSize, boolean rejectIllegalHeader,
++ AprEndpoint endpoint, int maxTrailerSize, Set allowedTrailerHeaders,
+ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars,
+ String relaxedQueryChars) {
+
+@@ -70,7 +70,8 @@ public class Http11AprProcessor extends
+
+ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
+
+- inputBuffer = new InternalAprInputBuffer(request, headerBufferSize, httpParser);
++ inputBuffer = new InternalAprInputBuffer(request, headerBufferSize, rejectIllegalHeader,
++ httpParser);
+ request.setInputBuffer(inputBuffer);
+
+ outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize);
+diff -up ./java/org/apache/coyote/http11/Http11AprProtocol.java.orig ./java/org/apache/coyote/http11/Http11AprProtocol.java
+--- ./java/org/apache/coyote/http11/Http11AprProtocol.java.orig 2020-04-24 14:57:33.393591706 -0400
++++ ./java/org/apache/coyote/http11/Http11AprProtocol.java 2020-04-24 14:45:37.327092381 -0400
+@@ -299,9 +299,9 @@ public class Http11AprProtocol extends A
+ @Override
+ protected Http11AprProcessor createProcessor() {
+ Http11AprProcessor processor = new Http11AprProcessor(
+- proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint,
+- proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
+- proto.getMaxExtensionSize(),
++ proto.getMaxHttpHeaderSize(), proto.getRejectIllegalHeader(),
++ (AprEndpoint)proto.endpoint, proto.getMaxTrailerSize(),
++ proto.getAllowedTrailerHeadersAsSet(), proto.getMaxExtensionSize(),
+ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(),
+ proto.getRelaxedQueryChars());
+ processor.setAdapter(proto.adapter);
+diff -up ./java/org/apache/coyote/http11/Http11NioProcessor.java.orig ./java/org/apache/coyote/http11/Http11NioProcessor.java
+--- ./java/org/apache/coyote/http11/Http11NioProcessor.java.orig 2020-04-24 14:57:33.399591693 -0400
++++ ./java/org/apache/coyote/http11/Http11NioProcessor.java 2020-04-24 14:45:24.452119361 -0400
+@@ -66,8 +66,8 @@ public class Http11NioProcessor extends
+ // ----------------------------------------------------------- Constructors
+
+
+- public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint, int maxTrailerSize,
+- Set allowedTrailerHeaders,
++ public Http11NioProcessor(int maxHttpHeaderSize, boolean rejectIllegalHeader,
++ NioEndpoint endpoint, int maxTrailerSize, Set allowedTrailerHeaders,
+ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars,
+ String relaxedQueryChars) {
+
+@@ -75,7 +75,8 @@ public class Http11NioProcessor extends
+
+ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
+
+- inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize, httpParser);
++ inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize,
++ rejectIllegalHeader, httpParser);
+ request.setInputBuffer(inputBuffer);
+
+ outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize);
+diff -up ./java/org/apache/coyote/http11/Http11NioProtocol.java.orig ./java/org/apache/coyote/http11/Http11NioProtocol.java
+--- ./java/org/apache/coyote/http11/Http11NioProtocol.java.orig 2020-04-24 14:57:33.392591708 -0400
++++ ./java/org/apache/coyote/http11/Http11NioProtocol.java 2020-04-24 14:28:55.937278681 -0400
+@@ -264,9 +264,9 @@ public class Http11NioProtocol extends A
+ @Override
+ public Http11NioProcessor createProcessor() {
+ Http11NioProcessor processor = new Http11NioProcessor(
+- proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint,
+- proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
+- proto.getMaxExtensionSize(),
++ proto.getMaxHttpHeaderSize(), proto.getRejectIllegalHeader(),
++ (NioEndpoint)proto.endpoint, proto.getMaxTrailerSize(),
++ proto.getAllowedTrailerHeadersAsSet(), proto.getMaxExtensionSize(),
+ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(),
+ proto.getRelaxedQueryChars());
+ processor.setAdapter(proto.adapter);
+diff -up ./java/org/apache/coyote/http11/Http11Processor.java.orig ./java/org/apache/coyote/http11/Http11Processor.java
+--- ./java/org/apache/coyote/http11/Http11Processor.java.orig 2020-04-24 14:57:33.390591712 -0400
++++ ./java/org/apache/coyote/http11/Http11Processor.java 2020-04-24 15:09:20.651109515 -0400
+@@ -51,8 +51,8 @@ public class Http11Processor extends Abs
+ // ------------------------------------------------------------ Constructor
+
+
+- public Http11Processor(int headerBufferSize, JIoEndpoint endpoint, int maxTrailerSize,
+- Set allowedTrailerHeaders,
++ public Http11Processor(int headerBufferSize, boolean rejectIllegalHeader,
++ JIoEndpoint endpoint, int maxTrailerSize, Set allowedTrailerHeaders,
+ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars,
+ String relaxedQueryChars) {
+
+@@ -60,7 +60,7 @@ public class Http11Processor extends Abs
+
+ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
+
+- inputBuffer = new InternalInputBuffer(request, headerBufferSize, httpParser);
++ inputBuffer = new InternalInputBuffer(request, headerBufferSize, rejectIllegalHeader, httpParser);
+
+ request.setInputBuffer(inputBuffer);
+
+diff -up ./java/org/apache/coyote/http11/Http11Protocol.java.orig ./java/org/apache/coyote/http11/Http11Protocol.java
+--- ./java/org/apache/coyote/http11/Http11Protocol.java.orig 2020-04-24 14:57:33.395591701 -0400
++++ ./java/org/apache/coyote/http11/Http11Protocol.java 2020-04-24 14:45:08.641152489 -0400
+@@ -163,9 +163,9 @@ public class Http11Protocol extends Abst
+ @Override
+ protected Http11Processor createProcessor() {
+ Http11Processor processor = new Http11Processor(
+- proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
+- proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
+- proto.getMaxExtensionSize(),
++ proto.getMaxHttpHeaderSize(), proto.getRejectIllegalHeader(),
++ (JIoEndpoint)proto.endpoint, proto.getMaxTrailerSize(),
++ proto.getAllowedTrailerHeadersAsSet(), proto.getMaxExtensionSize(),
+ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(),
+ proto.getRelaxedQueryChars());
+ processor.setAdapter(proto.adapter);
+diff -up ./java/org/apache/coyote/http11/InternalAprInputBuffer.java.orig ./java/org/apache/coyote/http11/InternalAprInputBuffer.java
+--- ./java/org/apache/coyote/http11/InternalAprInputBuffer.java.orig 2020-04-24 14:57:33.398591695 -0400
++++ ./java/org/apache/coyote/http11/InternalAprInputBuffer.java 2020-04-24 15:08:35.338204464 -0400
+@@ -52,7 +52,7 @@ public class InternalAprInputBuffer exte
+ * Alternate constructor.
+ */
+ public InternalAprInputBuffer(Request request, int headerBufferSize,
+- HttpParser httpParser) {
++ boolean rejectIllegalHeader, HttpParser httpParser) {
+
+ this.request = request;
+ headers = request.getMimeHeaders();
+@@ -66,6 +66,8 @@ public class InternalAprInputBuffer exte
+
+ this.httpParser = httpParser;
+
++ this.rejectIllegalHeaderName = rejectIllegalHeader;
++
+ inputStreamInputBuffer = new SocketInputBuffer();
+
+ filterLibrary = new InputFilter[0];
+@@ -350,6 +352,8 @@ public class InternalAprInputBuffer exte
+ //
+
+ byte chr = 0;
++ byte prevChr = 0;
++
+ while (true) {
+
+ // Read new bytes if needed
+@@ -358,14 +362,19 @@ public class InternalAprInputBuffer exte
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+
++ prevChr = chr;
+ chr = buf[pos];
+
+- if (chr == Constants.CR) {
+- // Skip
+- } else if (chr == Constants.LF) {
+- pos++;
++ if (chr == Constants.CR && prevChr != Constants.CR) {
++ // Possible start of CRLF - process the next byte.
++ } else if (prevChr == Constants.CR && chr == Constants.LF) {
++ pos++;
+ return false;
+ } else {
++ if (prevChr == Constants.CR) {
++ // Must have read two bytes (first was CR, second was not LF)
++ pos--;
++ }
+ break;
+ }
+
+@@ -396,8 +405,9 @@ public class InternalAprInputBuffer exte
+ colon = true;
+ headerValue = headers.addValue(buf, start, pos - start);
+ } else if (!HttpParser.isToken(buf[pos])) {
+- // If a non-token header is detected, skip the line and
+- // ignore the header
++ // Non-token characters are illegal in header names
++ // Parsing continues so the error can be reported in context
++ // skipLine() will handle the error
+ skipLine(start);
+ return true;
+ }
+@@ -453,10 +463,24 @@ public class InternalAprInputBuffer exte
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+
+- if (buf[pos] == Constants.CR) {
+- // Skip
+- } else if (buf[pos] == Constants.LF) {
++ prevChr = chr;
++ chr = buf[pos];
++ if (chr == Constants.CR) {
++ // Possible start of CRLF - process the next byte.
++ } else if (prevChr == Constants.CR && chr == Constants.LF) {
+ eol = true;
++ } else if (prevChr == Constants.CR) {
++ // Invalid value
++ // Delete the header (it will be the most recent one)
++ headers.removeHeader(headers.size() - 1);
++ skipLine(start);
++ return true;
++ } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
++ // Invalid value
++ // Delete the header (it will be the most recent one)
++ headers.removeHeader(headers.size() - 1);
++ skipLine(start);
++ return true;
+ } else if (buf[pos] == Constants.SP) {
+ buf[realPos] = buf[pos];
+ realPos++;
+@@ -509,6 +533,9 @@ public class InternalAprInputBuffer exte
+ lastRealByte = pos - 1;
+ }
+
++ byte chr = 0;
++ byte prevChr = 0;
++
+ while (!eol) {
+
+ // Read new bytes if needed
+@@ -517,9 +544,12 @@ public class InternalAprInputBuffer exte
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+
+- if (buf[pos] == Constants.CR) {
++ prevChr = chr;
++ chr = buf[pos];
++
++ if (chr == Constants.CR) {
+ // Skip
+- } else if (buf[pos] == Constants.LF) {
++ } else if (prevChr == Constants.CR && chr == Constants.LF) {
+ eol = true;
+ } else {
+ lastRealByte = pos;
+@@ -527,9 +557,13 @@ public class InternalAprInputBuffer exte
+ pos++;
+ }
+
+- if (log.isDebugEnabled()) {
+- log.debug(sm.getString("iib.invalidheader", new String(buf, start,
+- lastRealByte - start + 1, Charset.forName("ISO-8859-1"))));
++ if (rejectIllegalHeaderName || log.isDebugEnabled()) {
++ String message = sm.getString("iib.invalidheader", new String(buf, start,
++ lastRealByte - start + 1, Charset.forName("ISO-8859-1")));
++ if (rejectIllegalHeaderName) {
++ throw new IllegalArgumentException(message);
++ }
++ log.debug(message);
+ }
+ }
+
+diff -up ./java/org/apache/coyote/http11/InternalInputBuffer.java.orig ./java/org/apache/coyote/http11/InternalInputBuffer.java
+--- ./java/org/apache/coyote/http11/InternalInputBuffer.java.orig 2020-04-24 14:57:33.389591714 -0400
++++ ./java/org/apache/coyote/http11/InternalInputBuffer.java 2020-04-24 15:08:54.534164236 -0400
+@@ -53,13 +53,14 @@ public class InternalInputBuffer extends
+ * Default constructor.
+ */
+ public InternalInputBuffer(Request request, int headerBufferSize,
+- HttpParser httpParser) {
++ boolean rejectIllegalHeader, HttpParser httpParser) {
+
+ this.request = request;
+ headers = request.getMimeHeaders();
+
+ buf = new byte[headerBufferSize];
+
++ this.rejectIllegalHeaderName = rejectIllegalHeader;
+ this.httpParser = httpParser;
+
+ inputStreamInputBuffer = new InputStreamInputBuffer();
+@@ -302,6 +303,8 @@ public class InternalInputBuffer extends
+ //
+
+ byte chr = 0;
++ byte prevChr = 0;
++
+ while (true) {
+
+ // Read new bytes if needed
+@@ -310,19 +313,23 @@ public class InternalInputBuffer extends
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+
++ prevChr = chr;
+ chr = buf[pos];
+
+- if (chr == Constants.CR) {
+- // Skip
+- } else if (chr == Constants.LF) {
++ if (chr == Constants.CR && prevChr != Constants.CR) {
++ // Possible start of CRLF - process the next byte.
++ } else if (prevChr == Constants.CR && chr == Constants.LF) {
+ pos++;
+ return false;
+ } else {
++ if (prevChr == Constants.CR) {
++ // Must have read two bytes (first was CR, second was not LF)
++ pos--;
++ }
+ break;
+ }
+
+ pos++;
+-
+ }
+
+ // Mark the current buffer position
+@@ -348,8 +355,9 @@ public class InternalInputBuffer extends
+ colon = true;
+ headerValue = headers.addValue(buf, start, pos - start);
+ } else if (!HttpParser.isToken(buf[pos])) {
+- // If a non-token header is detected, skip the line and
+- // ignore the header
++ // Non-token characters are illegal in header names
++ // Parsing continues so the error can be reported in context
++ // skipLine() will handle the error
+ skipLine(start);
+ return true;
+ }
+@@ -406,15 +414,29 @@ public class InternalInputBuffer extends
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+
+- if (buf[pos] == Constants.CR) {
+- // Skip
+- } else if (buf[pos] == Constants.LF) {
++ prevChr = chr;
++ chr = buf[pos];
++ if (chr == Constants.CR) {
++ // Possible start of CRLF - process the next byte.
++ } else if (prevChr == Constants.CR && chr == Constants.LF) {
+ eol = true;
+- } else if (buf[pos] == Constants.SP) {
+- buf[realPos] = buf[pos];
++ } else if (prevChr == Constants.CR) {
++ // Invalid value
++ // Delete the header (it will be the most recent one)
++ headers.removeHeader(headers.size() - 1);
++ skipLine(start);
++ return true;
++ } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
++ // Invalid value
++ // Delete the header (it will be the most recent one)
++ headers.removeHeader(headers.size() - 1);
++ skipLine(start);
++ return true;
++ } else if (chr == Constants.SP) {
++ buf[realPos] = chr;
+ realPos++;
+ } else {
+- buf[realPos] = buf[pos];
++ buf[realPos] = chr;
+ realPos++;
+ lastSignificantChar = realPos;
+ }
+@@ -480,6 +502,9 @@ public class InternalInputBuffer extends
+ lastRealByte = pos - 1;
+ }
+
++ byte chr = 0;
++ byte prevChr = 0;
++
+ while (!eol) {
+
+ // Read new bytes if needed
+@@ -488,9 +513,12 @@ public class InternalInputBuffer extends
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+
+- if (buf[pos] == Constants.CR) {
++ prevChr = chr;
++ chr = buf[pos];
++
++ if (chr == Constants.CR) {
+ // Skip
+- } else if (buf[pos] == Constants.LF) {
++ } else if (prevChr == Constants.CR && chr == Constants.LF) {
+ eol = true;
+ } else {
+ lastRealByte = pos;
+@@ -498,9 +526,13 @@ public class InternalInputBuffer extends
+ pos++;
+ }
+
+- if (log.isDebugEnabled()) {
+- log.debug(sm.getString("iib.invalidheader", new String(buf, start,
+- lastRealByte - start + 1, Charset.forName("ISO-8859-1"))));
++ if (rejectIllegalHeaderName || log.isDebugEnabled()) {
++ String message = sm.getString("iib.invalidheader", new String(buf, start,
++ lastRealByte - start + 1, Charset.forName("ISO-8859-1")));
++ if (rejectIllegalHeaderName) {
++ throw new IllegalArgumentException(message);
++ }
++ log.debug(message);
+ }
+ }
+
+diff -up ./java/org/apache/coyote/http11/InternalNioInputBuffer.java.orig ./java/org/apache/coyote/http11/InternalNioInputBuffer.java
+--- ./java/org/apache/coyote/http11/InternalNioInputBuffer.java.orig 2020-04-24 14:57:33.396591699 -0400
++++ ./java/org/apache/coyote/http11/InternalNioInputBuffer.java 2020-04-24 14:28:58.728272282 -0400
+@@ -99,12 +99,13 @@ public class InternalNioInputBuffer exte
+ * Alternate constructor.
+ */
+ public InternalNioInputBuffer(Request request, int headerBufferSize,
+- HttpParser httpParser) {
++ boolean rejectIllegalHeader, HttpParser httpParser) {
+
+ this.request = request;
+ headers = request.getMimeHeaders();
+
+ this.headerBufferSize = headerBufferSize;
++ this.rejectIllegalHeaderName = rejectIllegalHeader;
+ this.httpParser = httpParser;
+
+ inputStreamInputBuffer = new SocketInputBuffer();
+@@ -514,6 +515,8 @@ public class InternalNioInputBuffer exte
+ //
+
+ byte chr = 0;
++ byte prevChr = 0;
++
+ while (headerParsePos == HeaderParsePosition.HEADER_START) {
+
+ // Read new bytes if needed
+@@ -524,19 +527,23 @@ public class InternalNioInputBuffer exte
+ }
+ }
+
++ prevChr = chr;
+ chr = buf[pos];
+
+- if (chr == Constants.CR) {
+- // Skip
+- } else if (chr == Constants.LF) {
++ if (chr == Constants.CR && prevChr != Constants.CR) {
++ // Possible start of CRLF - process the next byte.
++ } else if (prevChr == Constants.CR && chr == Constants.LF) {
+ pos++;
+ return HeaderParseStatus.DONE;
+ } else {
++ if (prevChr == Constants.CR) {
++ // Must have read two bytes (first was CR, second was not LF)
++ pos--;
++ }
+ break;
+ }
+
+ pos++;
+-
+ }
+
+ if ( headerParsePos == HeaderParsePosition.HEADER_START ) {
+@@ -570,9 +577,10 @@ public class InternalNioInputBuffer exte
+ headerData.lastSignificantChar = pos;
+ break;
+ } else if (!HttpParser.isToken(chr)) {
+- // If a non-token header is detected, skip the line and
+- // ignore the header
++ // Non-token characters are illegal in header names
++ // Parsing continues so the error can be reported in context
+ headerData.lastSignificantChar = pos;
++ // skipLine() will handle the error
+ return skipLine();
+ }
+
+@@ -630,11 +638,22 @@ public class InternalNioInputBuffer exte
+ }
+ }
+
++ prevChr = chr;
+ chr = buf[pos];
+ if (chr == Constants.CR) {
+- // Skip
+- } else if (chr == Constants.LF) {
++ // Possible start of CRLF - process the next byte.
++ } else if (prevChr == Constants.CR && chr == Constants.LF) {
+ eol = true;
++ } else if (prevChr == Constants.CR) {
++ // Invalid value
++ // Delete the header (it will be the most recent one)
++ headers.removeHeader(headers.size() - 1);
++ return skipLine();
++ } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
++ // Invalid value
++ // Delete the header (it will be the most recent one)
++ headers.removeHeader(headers.size() - 1);
++ return skipLine();
+ } else if (chr == Constants.SP || chr == Constants.HT) {
+ buf[headerData.realPos] = chr;
+ headerData.realPos++;
+@@ -692,6 +711,9 @@ public class InternalNioInputBuffer exte
+ headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
+ boolean eol = false;
+
++ byte chr = 0;
++ byte prevChr = 0;
++
+ // Reading bytes until the end of the line
+ while (!eol) {
+
+@@ -702,9 +724,12 @@ public class InternalNioInputBuffer exte
+ }
+ }
+
+- if (buf[pos] == Constants.CR) {
++ prevChr = chr;
++ chr = buf[pos];
++
++ if (chr == Constants.CR) {
+ // Skip
+- } else if (buf[pos] == Constants.LF) {
++ } else if (prevChr == Constants.CR && chr == Constants.LF) {
+ eol = true;
+ } else {
+ headerData.lastSignificantChar = pos;
+@@ -712,11 +737,13 @@ public class InternalNioInputBuffer exte
+
+ pos++;
+ }
+- if (log.isDebugEnabled()) {
+- log.debug(sm.getString("iib.invalidheader", new String(buf,
+- headerData.start,
+- headerData.lastSignificantChar - headerData.start + 1,
+- DEFAULT_CHARSET)));
++ if (rejectIllegalHeaderName || log.isDebugEnabled()) {
++ String message = sm.getString("iib.invalidheader", new String(buf, headerData.start,
++ headerData.lastSignificantChar - headerData.start + 1, DEFAULT_CHARSET));
++ if (rejectIllegalHeaderName) {
++ throw new IllegalArgumentException(message);
++ }
++ log.debug(message);
+ }
+
+ headerParsePos = HeaderParsePosition.HEADER_START;
+diff -up ./java/org/apache/tomcat/util/http/MimeHeaders.java.orig ./java/org/apache/tomcat/util/http/MimeHeaders.java
+--- ./java/org/apache/tomcat/util/http/MimeHeaders.java.orig 2020-04-24 14:28:38.873317805 -0400
++++ ./java/org/apache/tomcat/util/http/MimeHeaders.java 2020-04-24 14:28:58.728272282 -0400
+@@ -375,7 +375,7 @@ public class MimeHeaders {
+ * reset and swap with last header
+ * @param idx the index of the header to remove.
+ */
+- private void removeHeader(int idx) {
++ public void removeHeader(int idx) {
+ MimeHeaderField mh = headers[idx];
+
+ mh.recycle();
+diff -up ./java/org/apache/tomcat/util/http/parser/HttpParser.java.orig ./java/org/apache/tomcat/util/http/parser/HttpParser.java
+--- ./java/org/apache/tomcat/util/http/parser/HttpParser.java.orig 2020-04-24 14:28:58.729272279 -0400
++++ ./java/org/apache/tomcat/util/http/parser/HttpParser.java 2020-04-24 14:37:59.945050923 -0400
+@@ -482,6 +482,17 @@ public class HttpParser {
+ }
+
+
++ public static boolean isControl(int c) {
++ // Fast for valid control characters, slower for some incorrect
++ // ones
++ try {
++ return IS_CONTROL[c];
++ } catch (ArrayIndexOutOfBoundsException ex) {
++ return false;
++ }
++ }
++
++
+ // Skip any LWS and return the next char
+ private static int skipLws(StringReader input, boolean withReset)
+ throws IOException {
+diff -up ./test/org/apache/coyote/http11/TestInternalInputBuffer.java.orig ./test/org/apache/coyote/http11/TestInternalInputBuffer.java
+--- ./test/org/apache/coyote/http11/TestInternalInputBuffer.java.orig 2020-04-24 14:57:33.385591722 -0400
++++ ./test/org/apache/coyote/http11/TestInternalInputBuffer.java 2020-04-24 14:28:58.730272277 -0400
+@@ -33,6 +33,7 @@ import static org.junit.Assert.assertTru
+ import org.junit.Test;
+
+ import org.apache.catalina.Context;
++import org.apache.catalina.connector.Connector;
+ import org.apache.catalina.startup.SimpleHttpClient;
+ import org.apache.catalina.startup.TesterServlet;
+ import org.apache.catalina.startup.Tomcat;
+@@ -130,6 +131,28 @@ public class TestInternalInputBuffer ext
+
+
+ @Test
++ public void testBug51557Valid() {
++
++ Bug51557Client client = new Bug51557Client("X-Bug51557Valid", "1234");
++
++ client.doRequest();
++ assertTrue(client.isResponse200());
++ assertEquals("1234abcd", client.getResponseBody());
++ assertTrue(client.isResponseBodyOK());
++ }
++
++
++ @Test
++ public void testBug51557Invalid() {
++
++ Bug51557Client client = new Bug51557Client("X-Bug51557=Invalid", "1234", true);
++
++ client.doRequest();
++ assertTrue(client.isResponse400());
++ }
++
++
++ @Test
+ public void testBug51557NoColon() {
+
+ Bug51557Client client = new Bug51557Client("X-Bug51557NoColon");
+@@ -142,27 +165,52 @@ public class TestInternalInputBuffer ext
+
+
+ @Test
+- public void testBug51557Separators() throws Exception {
++ public void testBug51557SeparatorsInName() throws Exception {
+ char httpSeparators[] = new char[] {
+ '\t', ' ', '\"', '(', ')', ',', '/', ':', ';', '<',
+ '=', '>', '?', '@', '[', '\\', ']', '{', '}' };
+
+ for (char s : httpSeparators) {
+- doTestBug51557Char(s);
++ doTestBug51557CharInName(s);
++ tearDown();
++ setUp();
++ }
++ }
++
++
++ @Test
++ public void testBug51557CtlInName() throws Exception {
++ for (int i = 0; i < 31; i++) {
++ doTestBug51557CharInName((char) i);
+ tearDown();
+ setUp();
+ }
++ doTestBug51557CharInName((char) 127);
+ }
+
+
+ @Test
+- public void testBug51557Ctl() throws Exception {
++ public void testBug51557CtlInValue() throws Exception {
+ for (int i = 0; i < 31; i++) {
+- doTestBug51557Char((char) i);
++ if (i == '\t') {
++ // TAB is allowed
++ continue;
++ }
++ doTestBug51557InvalidCharInValue((char) i);
++ tearDown();
++ setUp();
++ }
++ doTestBug51557InvalidCharInValue((char) 127);
++ }
++
++
++ @Test
++ public void testBug51557ObsTextInValue() throws Exception {
++ for (int i = 128; i < 255; i++) {
++ doTestBug51557ValidCharInValue((char) i);
+ tearDown();
+ setUp();
+ }
+- doTestBug51557Char((char) 127);
+ }
+
+
+@@ -205,7 +253,33 @@ public class TestInternalInputBuffer ext
+ }
+
+
+- private void doTestBug51557Char(char s) {
++ @Test
++ public void testBug51557CRStartName() {
++
++ Bug51557Client client = new Bug51557Client("\rName=",
++ "invalid");
++
++ client.doRequest();
++ Assert.assertTrue(client.isResponse200());
++ Assert.assertEquals("abcd", client.getResponseBody());
++ Assert.assertTrue(client.isResponseBodyOK());
++ }
++
++
++ @Test
++ public void testBug51557CR2StartName() {
++
++ Bug51557Client client = new Bug51557Client("\r\rName=",
++ "invalid");
++
++ client.doRequest();
++ Assert.assertTrue(client.isResponse200());
++ Assert.assertEquals("abcd", client.getResponseBody());
++ Assert.assertTrue(client.isResponseBodyOK());
++ }
++
++
++ private void doTestBug51557CharInName(char s) {
+ Bug51557Client client =
+ new Bug51557Client("X-Bug" + s + "51557", "invalid");
+
+@@ -215,22 +289,53 @@ public class TestInternalInputBuffer ext
+ assertTrue(client.isResponseBodyOK());
+ }
+
++
++ private void doTestBug51557InvalidCharInValue(char s) {
++ Bug51557Client client =
++ new Bug51557Client("X-Bug51557-Invalid", "invalid" + s + "invalid");
++
++ client.doRequest();
++ Assert.assertTrue("Testing [" + (int) s + "]", client.isResponse200());
++ Assert.assertEquals("Testing [" + (int) s + "]", "abcd", client.getResponseBody());
++ Assert.assertTrue(client.isResponseBodyOK());
++ }
++
++
++ private void doTestBug51557ValidCharInValue(char s) {
++ Bug51557Client client =
++ new Bug51557Client("X-Bug51557-Valid", "valid" + s + "valid");
++
++ client.doRequest();
++ Assert.assertTrue("Testing [" + (int) s + "]", client.isResponse200());
++ Assert.assertEquals("Testing [" + (int) s + "]", "valid" + s + "validabcd", client.getResponseBody());
++ Assert.assertTrue(client.isResponseBodyOK());
++ }
++
++
+ /**
+ * Bug 51557 test client.
+ */
+ private class Bug51557Client extends SimpleHttpClient {
+
+- private String headerName;
+- private String headerLine;
++ private final String headerName;
++ private final String headerLine;
++ private final boolean rejectIllegalHeader;
+
+ public Bug51557Client(String headerName) {
+ this.headerName = headerName;
+ this.headerLine = headerName;
++ this.rejectIllegalHeader = false;
+ }
+
+ public Bug51557Client(String headerName, String headerValue) {
++ this(headerName, headerValue, false);
++ }
++
++ public Bug51557Client(String headerName, String headerValue,
++ boolean rejectIllegalHeader) {
+ this.headerName = headerName;
+ this.headerLine = headerName + ": " + headerValue;
++ this.rejectIllegalHeader = rejectIllegalHeader;
+ }
+
+ private Exception doRequest() {
+@@ -243,8 +348,11 @@ public class TestInternalInputBuffer ext
+ root.addServletMapping("/test", "Bug51557");
+
+ try {
++ Connector connector = tomcat.getConnector();
++ Assert.assertTrue(connector.setProperty(
++ "rejectIllegalHeader", Boolean.toString(rejectIllegalHeader)));
+ tomcat.start();
+- setPort(tomcat.getConnector().getLocalPort());
++ setPort(connector.getLocalPort());
+
+ // Open connection
+ connect();
+@@ -480,6 +588,75 @@ public class TestInternalInputBuffer ext
+ }
+
+
++ /**
++ * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=59089
++ */
++ @Test
++ public void testBug59089() {
++
++ Bug59089Client client = new Bug59089Client();
++
++ client.doRequest();
++ Assert.assertTrue(client.isResponse200());
++ Assert.assertTrue(client.isResponseBodyOK());
++ }
++
++
++ /**
++ * Bug 59089 test client.
++ */
++ private class Bug59089Client extends SimpleHttpClient {
++
++ private Exception doRequest() {
++
++ // Ensure body is read correctly
++ setUseContentLength(true);
++
++ Tomcat tomcat = getTomcatInstance();
++
++ Context root = tomcat.addContext("", TEMP_DIR);
++ Tomcat.addServlet(root, "Bug59089", new TesterServlet());
++ root.addServletMapping("/test", "Bug59089");
++
++ try {
++ Connector connector = tomcat.getConnector();
++ Assert.assertTrue(connector.setProperty("rejectIllegalHeader", "false"));
++ tomcat.start();
++ setPort(connector.getLocalPort());
++
++ // Open connection
++ connect();
++
++ String[] request = new String[1];
++ request[0] = "GET http://localhost:8080/test HTTP/1.1" + CRLF +
++ "Host: localhost:8080" + CRLF +
++ "X-Header: Ignore" + CRLF +
++ "X-Header" + (char) 130 + ": Broken" + 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;
++ }
++ if (!getResponseBody().contains("OK")) {
++ return false;
++ }
++ return true;
++ }
++ }
++
++
+ @Test
+ public void testInvalidMethod() {
+
+diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
+--- webapps/docs/changelog.xml.orig 2020-04-24 14:57:33.381591731 -0400
++++ webapps/docs/changelog.xml 2020-04-24 15:44:27.588766788 -0400
+@@ -77,6 +77,19 @@
+
+
+
++
++
++
++ Add an option to reject requests that contain HTTP headers with invalid
++ (non-token) header names with a 400 response. (markt)
++
++
++ Rename the HTTP Connector attribute rejectIllegalHeaderName
++ to rejectIllegalHeader
and expand the underlying
++ implementation to include header values as well as names. (markt)
++
++
++
+
+
+
+diff -up ./webapps/docs/config/http.xml.orig ./webapps/docs/config/http.xml
+--- ./webapps/docs/config/http.xml.orig 2020-04-24 14:57:33.383591727 -0400
++++ ./webapps/docs/config/http.xml 2020-04-24 14:37:07.540160722 -0400
+@@ -542,6 +542,20 @@
+ present in the value will be ignored.
+
+
++
++ If an HTTP request is received that contains an illegal header name or
++ value (e.g. the header name is not a token) this setting determines if the
++ request will be rejected with a 400 response (true
) or if the
++ illegal header be ignored (false
). The default value is
++ false
which will cause the request to be processed but the
++ illegal header will be ignored.
++
++
++
++ This attribute is deprecated. It will be removed in Tomcat 10 onwards.
++ It is now an alias for rejectIllegalHeader.
++
++
+
+ The value is a regular expression (using java.util.regex
)
+ matching the user-agent
header of HTTP clients for which
diff --git a/SPECS/tomcat.spec b/SPECS/tomcat.spec
index 9578900..ca72409 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: 15%{?dist}
+Release: 16%{?dist}
Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API
Group: System Environment/Daemons
@@ -107,6 +107,7 @@ Patch19: %{name}-7.0.76-rhbz-1795645.patch
Patch20: %{name}-7.0.76-CVE-2019-17563.patch
Patch21: %{name}-7.0.76-CVE-2020-9484.patch
Patch22: %{name}-7.0.76-CVE-2020-13935.patch
+Patch23: %{name}-7.0.76-CVE-2020-1935.patch
BuildArch: noarch
@@ -274,6 +275,7 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name "
%patch20 -p0
%patch21 -p0
%patch22 -p0
+%patch23 -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
@@ -718,6 +720,9 @@ fi
%attr(0644,root,root) %{_unitdir}/%{name}-jsvc.service
%changelog
+* Wed Sep 23 2020 Hui Wang 0:7.0.76-16
+- Resolves: rhbz#1814315 CVE-2020-1935 tomcat: Mishandling of Transfer-Encoding header allows for HTTP request smuggling
+
* Fri Jul 17 2020 Coty Sutherland 0:7.0.76-15
- Resolves: CVE-2020-13935 tomcat: multiple requests with invalid payload length in a WebSocket frame could lead to DoS