Author: markt Date: Fri Jan 5 10:40:57 2018 New Revision: 1820276 URL: http://svn.apache.org/viewvc?rev=1820276&view=rev Log: Return a simple, plain text error message if a client attempts to make a plain text HTTP connection to a TLS enabled NIO or NIO2 Connector.
Modified: tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties?rev=1820276&r1=1820275&r2=1820276&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties Fri Jan 5 10:40:57 2018 @@ -121,6 +121,7 @@ channel.nio.ssl.expandNetInBuffer=Expand channel.nio.ssl.expandNetOutBuffer=Expanding network output buffer to [{0}] bytes channel.nio.ssl.sniDefault=Unable to buffer enough data to determine requested SNI host name. Using default channel.nio.ssl.sniHostName=The SNI host name extracted for this connection was [{0}] +channel.nio.ssl.foundHttp=Found an plain text HTTP request on what should be an encrypted TLS connection jsse.invalid_truststore_password=The provided trust store password could not be used to unlock and/or validate the trust store. Retrying to access the trust store with a null password which will skip validation. jsse.keystore_load_failed=Failed to load keystore type [{0}] with path [{1}] due to [{2}] Modified: tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java?rev=1820276&r1=1820275&r2=1820276&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java Fri Jan 5 10:40:57 2018 @@ -388,6 +388,12 @@ public class SecureNio2Channel extends N hostName = endpoint.getDefaultSSLHostConfigName(); clientRequestedCiphers = Collections.emptyList(); break; + case NON_SECURE: + netOutBuffer.clear(); + netOutBuffer.put(TLSClientHelloExtractor.USE_TLS_RESPONSE); + netOutBuffer.flip(); + flush(); + throw new IOException(sm.getString("channel.nio.ssl.foundHttp")); } if (log.isDebugEnabled()) { Modified: tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java?rev=1820276&r1=1820275&r2=1820276&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java Fri Jan 5 10:40:57 2018 @@ -316,6 +316,12 @@ public class SecureNioChannel extends Ni hostName = endpoint.getDefaultSSLHostConfigName(); clientRequestedCiphers = Collections.emptyList(); break; + case NON_SECURE: + netOutBuffer.clear(); + netOutBuffer.put(TLSClientHelloExtractor.USE_TLS_RESPONSE); + netOutBuffer.flip(); + flushOutbound(); + throw new IOException(sm.getString("channel.nio.ssl.foundHttp")); } if (log.isDebugEnabled()) { Modified: tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java?rev=1820276&r1=1820275&r2=1820276&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java Fri Jan 5 10:40:57 2018 @@ -25,6 +25,7 @@ import java.util.List; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.http.parser.HttpParser; import org.apache.tomcat.util.net.openssl.ciphers.Cipher; import org.apache.tomcat.util.res.StringManager; @@ -47,6 +48,14 @@ public class TLSClientHelloExtractor { private static final int TLS_EXTENSION_SERVER_NAME = 0; private static final int TLS_EXTENSION_ALPN = 16; + public static byte[] USE_TLS_RESPONSE = ("HTTP/1.1 400 \r\n" + + "Content-Type: text/plain;charset=ISO-8859-1\r\n" + + "Connection: close\r\n" + + "\r\n" + + "Bad Request\r\n" + + "This combination of host and port requires TLS.\r\n").getBytes(StandardCharsets.ISO_8859_1); + + /** * Creates the instance of the parser and processes the provided buffer. The * buffer position and limit will be modified during the execution of this @@ -57,9 +66,6 @@ public class TLSClientHelloExtractor { * @throws IOException If the client hello message is malformed */ public TLSClientHelloExtractor(ByteBuffer netInBuffer) throws IOException { - // TODO: Detect use of http on a secure connection and provide a simple - // error page. - // Buffer is in write mode at this point. Record the current position so // the buffer state can be restored at the end of this method. int pos = netInBuffer.position(); @@ -80,6 +86,10 @@ public class TLSClientHelloExtractor { } if (!isTLSHandshake(netInBuffer)) { + // Is the client trying to use clear text HTTP? + if (isHttp(netInBuffer)) { + result = ExtractorResult.NON_SECURE; + } return; } @@ -191,6 +201,7 @@ public class TLSClientHelloExtractor { } } + private static ExtractorResult handleIncompleteRead(ByteBuffer bb) { if (bb.limit() == bb.capacity()) { // Buffer not big enough @@ -226,6 +237,67 @@ public class TLSClientHelloExtractor { } + private static boolean isHttp(ByteBuffer bb) { + // Based on code in Http11InputBuffer + // Note: The actual request is not important. This code only checks that + // the buffer contains a correctly formatted HTTP request line. + // The method, target and protocol are not validated. + byte chr = 0; + bb.position(0); + + // Skip blank lines + do { + if (!bb.hasRemaining()) { + return false; + } + chr = bb.get(); + } while (chr == '\r' || chr == '\n'); + + // Read the method + do { + if (!HttpParser.isToken(chr) || !bb.hasRemaining()) { + return false; + } + chr = bb.get(); + } while (chr != ' ' && chr != '\t'); + + // Whitespace between method and target + while (chr == ' ' || chr == '\t') { + if (!bb.hasRemaining()) { + return false; + } + chr = bb.get(); + } + + // Read the target + while (chr != ' ' && chr != '\t') { + if (HttpParser.isNotRequestTarget(chr) || !bb.hasRemaining()) { + return false; + } + chr = bb.get(); + } + + // Whitespace between target and protocol + while (chr == ' ' || chr == '\t') { + if (!bb.hasRemaining()) { + return false; + } + chr = bb.get(); + } + + // Read protocol + do { + if (!HttpParser.isHttpProtocol(chr) || !bb.hasRemaining()) { + return false; + } + chr = bb.get(); + + } while (chr != '\r' && chr != '\n'); + + return true; + } + + private static boolean isAllRecordAvailable(ByteBuffer bb) { // Next two bytes (unsigned) are the size of the record. We need all of // it. @@ -288,6 +360,7 @@ public class TLSClientHelloExtractor { COMPLETE, NOT_PRESENT, UNDERFLOW, - NEED_READ + NEED_READ, + NON_SECURE } } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1820276&r1=1820275&r2=1820276&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Fri Jan 5 10:40:57 2018 @@ -107,6 +107,11 @@ <fix> Fix NIO2 handshaking with a full input buffer. (remm) </fix> + <add> + Return a simple, plain text error message if a client attempts to make a + plain text HTTP connection to a TLS enabled NIO or NIO2 Connector. + (markt) + </add> </changelog> </subsection> <subsection name="Jasper"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org