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

Reply via email to