This looks like a CPU spinning handshake to me.
The operation handshake(true, true); returns an IO interest to be registered with a selector. If the client is slow here or misbehaving, you could end up in a end less loop, and hence we can have introduced a very simple DoS vulnerability here.

The simplest solution is, would be to use an individual selector. Register the socket and issue a select() on the thread you are running on. If you want to use a shared selector (like NIO does for reads and writes) it requires a bit more logic.

I understand where you are going with this solution, and I can probably fix this today as I sit on the airplane on my way home.

best
Filip



On 02/25/2011 12:19 PM, ma...@apache.org wrote:
Author: markt
Date: Fri Feb 25 19:19:13 2011
New Revision: 1074675

URL: http://svn.apache.org/viewvc?rev=1074675&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=49284
Support SSL re-negotiation in the HTTP NIO connector
There is a fair amount of renaming in this patch. The real work is in the new 
rehandshake() method in the SecureNioChannel.

Modified:
     tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java
     tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties
     tomcat/trunk/java/org/apache/tomcat/util/net/NioChannel.java
     tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java
     tomcat/trunk/webapps/docs/changelog.xml

Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java?rev=1074675&r1=1074674&r2=1074675&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java 
(original)
+++ tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java Fri Feb 
25 19:19:13 2011
@@ -23,6 +23,8 @@ import java.nio.channels.SelectionKey;
  import java.util.Locale;
  import java.util.concurrent.Executor;

+import javax.net.ssl.SSLEngine;
+
  import org.apache.coyote.ActionCode;
  import org.apache.coyote.Request;
  import org.apache.coyote.RequestInfo;
@@ -42,7 +44,9 @@ import org.apache.tomcat.util.net.NioCha
  import org.apache.tomcat.util.net.NioEndpoint;
  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.SocketStatus;
+import org.apache.tomcat.util.net.jsse.JSSEFactory;


  /**
@@ -625,8 +629,25 @@ public class Http11NioProcessor extends
                      .setLimit(maxSavePostSize);
                  inputBuffer.addActiveFilter
                      (inputFilters[Constants.BUFFERED_FILTER]);
+
+                SecureNioChannel sslChannel = (SecureNioChannel) socket;
+                SSLEngine engine = sslChannel.getSslEngine();
+                if (!engine.getNeedClientAuth()&&  
!engine.getWantClientAuth()) {
+                    // Need to re-negotiate SSL connection
+                    engine.setNeedClientAuth(true);
+                    try {
+                        sslChannel.rehandshake();
+                        sslSupport = (new JSSEFactory()).getSSLSupport(
+                                engine.getSession());
+                    } catch (IOException ioe) {
+                        
log.warn(sm.getString("http11processor.socket.sslreneg",
+                                ioe));
+                    }
+                }
                  try {
-                    Object sslO = sslSupport.getPeerCertificateChain(true);
+                    // use force=false since re-negotiation is handled above
+                    // (and it is a NO-OP for NIO anyway)
+                    Object sslO = sslSupport.getPeerCertificateChain(false);
                      if( sslO != null) {
                          request.setAttribute
                              (SSLSupport.CERTIFICATE_KEY, sslO);

Modified: tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties?rev=1074675&r1=1074674&r2=1074675&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/coyote/http11/LocalStrings.properties Fri Feb 
25 19:19:13 2011
@@ -31,6 +31,7 @@ http11processor.request.process=Error pr
  http11processor.request.finish=Error finishing request
  http11processor.response.finish=Error finishing response
  http11processor.socket.info=Exception getting socket information
+http11processor.socket.sslreneg=Exception re-negotiating SSL connection
  http11processor.socket.ssl=Exception getting SSL attributes
  http11processor.socket.timeout=Error setting socket timeout


Modified: tomcat/trunk/java/org/apache/tomcat/util/net/NioChannel.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/NioChannel.java?rev=1074675&r1=1074674&r2=1074675&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/NioChannel.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/NioChannel.java Fri Feb 25 
19:19:13 2011
@@ -175,7 +175,7 @@ public class NioChannel implements ByteC
       * @return boolean
       * TODO Implement this org.apache.tomcat.util.net.SecureNioChannel method
       */
-    public boolean isInitHandshakeComplete() {
+    public boolean isHandshakeComplete() {
          return true;
      }


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=1074675&r1=1074674&r2=1074675&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java Fri Feb 
25 19:19:13 2011
@@ -43,8 +43,8 @@ public class SecureNioChannel extends Ni

      protected SSLEngine sslEngine;

-    protected boolean initHandshakeComplete = false;
-    protected HandshakeStatus initHandshakeStatus; //gets set by begin 
handshake
+    protected boolean handshakeComplete = false;
+    protected HandshakeStatus handshakeStatus; //gets set by begin handshake

      protected boolean closed = false;
      protected boolean closing = false;
@@ -82,12 +82,12 @@ public class SecureNioChannel extends Ni
          netOutBuffer.limit(0);
          netInBuffer.position(0);
          netInBuffer.limit(0);
-        initHandshakeComplete = false;
+        handshakeComplete = false;
          closed = false;
          closing = false;
          //initiate handshake
          sslEngine.beginHandshake();
-        initHandshakeStatus = sslEngine.getHandshakeStatus();
+        handshakeStatus = sslEngine.getHandshakeStatus();
      }

      @Override
@@ -146,35 +146,35 @@ public class SecureNioChannel extends Ni
       */
      @Override
      public int handshake(boolean read, boolean write) throws IOException {
-        if ( initHandshakeComplete ) return 0; //we have done our initial 
handshake
+        if ( handshakeComplete ) return 0; //we have done our initial handshake

          if (!flush(netOutBuffer)) return SelectionKey.OP_WRITE; //we still 
have data to write

          SSLEngineResult handshake = null;

-        while (!initHandshakeComplete) {
-            switch ( initHandshakeStatus ) {
+        while (!handshakeComplete) {
+            switch ( handshakeStatus ) {
                  case NOT_HANDSHAKING: {
                      //should never happen
                      throw new IOException("NOT_HANDSHAKING during handshake");
                  }
                  case FINISHED: {
                      //we are complete if we have delivered the last package
-                    initHandshakeComplete = !netOutBuffer.hasRemaining();
+                    handshakeComplete = !netOutBuffer.hasRemaining();
                      //return 0 if we are complete, otherwise we still have 
data to write
-                    return initHandshakeComplete?0:SelectionKey.OP_WRITE;
+                    return handshakeComplete?0:SelectionKey.OP_WRITE;
                  }
                  case NEED_WRAP: {
                      //perform the wrap function
                      handshake = handshakeWrap(write);
                      if ( handshake.getStatus() == Status.OK ){
-                        if (initHandshakeStatus == HandshakeStatus.NEED_TASK)
-                            initHandshakeStatus = tasks();
+                        if (handshakeStatus == HandshakeStatus.NEED_TASK)
+                            handshakeStatus = tasks();
                      } else {
                          //wrap should always work with our buffers
                          throw new IOException("Unexpected status:" + 
handshake.getStatus() + " during handshake WRAP.");
                      }
-                    if ( initHandshakeStatus != HandshakeStatus.NEED_UNWRAP || 
(!flush(netOutBuffer)) ) {
+                    if ( handshakeStatus != HandshakeStatus.NEED_UNWRAP || 
(!flush(netOutBuffer)) ) {
                          //should actually return OP_READ if we have 
NEED_UNWRAP
                          return SelectionKey.OP_WRITE;
                      }
@@ -185,26 +185,26 @@ public class SecureNioChannel extends Ni
                      //perform the unwrap function
                      handshake = handshakeUnwrap(read);
                      if ( handshake.getStatus() == Status.OK ) {
-                        if (initHandshakeStatus == HandshakeStatus.NEED_TASK)
-                            initHandshakeStatus = tasks();
+                        if (handshakeStatus == HandshakeStatus.NEED_TASK)
+                            handshakeStatus = tasks();
                      } else if ( handshake.getStatus() == 
Status.BUFFER_UNDERFLOW ){
                          //read more data, reregister for OP_READ
                          return SelectionKey.OP_READ;
                      } else {
-                        throw new IOException("Invalid handshake 
status:"+initHandshakeStatus+" during handshake UNWRAP.");
+                        throw new IOException("Invalid handshake 
status:"+handshakeStatus+" during handshake UNWRAP.");
                      }//switch
                      break;
                  }
                  case NEED_TASK: {
-                    initHandshakeStatus = tasks();
+                    handshakeStatus = tasks();
                      break;
                  }
-                default: throw new IllegalStateException("Invalid handshake 
status:"+initHandshakeStatus);
+                default: throw new IllegalStateException("Invalid handshake 
status:"+handshakeStatus);
              }//switch
          }//while
          //return 0 if we are complete, otherwise reregister for any activity 
that
          //would cause this method to be called again.
-        return 
initHandshakeComplete?0:(SelectionKey.OP_WRITE|SelectionKey.OP_READ);
+        return 
handshakeComplete?0:(SelectionKey.OP_WRITE|SelectionKey.OP_READ);
      }

      /**
@@ -234,7 +234,7 @@ public class SecureNioChannel extends Ni
          //prepare the results to be written
          netOutBuffer.flip();
          //set the status
-        initHandshakeStatus = result.getHandshakeStatus();
+        handshakeStatus = result.getHandshakeStatus();
          //optimization, if we do have a writable channel, write it now
          if ( doWrite ) flush(netOutBuffer);
          return result;
@@ -268,19 +268,42 @@ public class SecureNioChannel extends Ni
              //compact the buffer, this is an optional method, wonder what 
would happen if we didn't
              netInBuffer.compact();
              //read in the status
-            initHandshakeStatus = result.getHandshakeStatus();
+            handshakeStatus = result.getHandshakeStatus();
              if ( result.getStatus() == SSLEngineResult.Status.OK&&
                   result.getHandshakeStatus() == HandshakeStatus.NEED_TASK ) {
                  //execute tasks if we need to
-                initHandshakeStatus = tasks();
+                handshakeStatus = tasks();
              }
              //perform another unwrap?
              cont = result.getStatus() == SSLEngineResult.Status.OK&&
-                   initHandshakeStatus == HandshakeStatus.NEED_UNWRAP;
+                   handshakeStatus == HandshakeStatus.NEED_UNWRAP;
          }while ( cont );
          return result;
      }

+    public void rehandshake() throws IOException {
+        int readBufLimit = getBufHandler().getReadBuffer().limit();
+        try {
+            // Expand read buffer to maximum to allow handshaking to take place
+            getBufHandler().getReadBuffer().limit(
+                    getBufHandler().getReadBuffer().capacity());
+            sslEngine.getSession().invalidate();
+            sslEngine.beginHandshake();
+            handshakeComplete = false;
+            handshakeStatus = sslEngine.getHandshakeStatus();
+            while (!handshakeComplete) {
+                handshake(true, true);
+                if (handshakeStatus == HandshakeStatus.NEED_UNWRAP)  {
+                    handshakeUnwrap(true);
+                }
+            }
+        } finally {
+            // Restore the pre-handshak value
+            getBufHandler().getReadBuffer().limit(readBufLimit);
+        }
+    }
+
+
      /**
       * Sends a SSL close message, will not physically close the connection 
here.<br>
       * To close the connection, you could do something like
@@ -353,7 +376,7 @@ public class SecureNioChannel extends Ni
          //are we in the middle of closing or closed?
          if ( closing || closed) return -1;
          //did we finish our handshake?
-        if (!initHandshakeComplete) throw new IllegalStateException("Handshake 
incomplete, you must complete handshake before reading data.");
+        if (!handshakeComplete) throw new IllegalStateException("Handshake 
incomplete, you must complete handshake before reading data.");

          //read from the network
          int netread = sc.read(netInBuffer);
@@ -474,8 +497,8 @@ public class SecureNioChannel extends Ni
      }

      @Override
-    public boolean isInitHandshakeComplete() {
-        return initHandshakeComplete;
+    public boolean isHandshakeComplete() {
+        return handshakeComplete;
      }

      @Override

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1074675&r1=1074674&r2=1074675&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Fri Feb 25 19:19:13 2011
@@ -151,6 +151,10 @@
    </subsection>
    <subsection name="Coyote">
      <changelog>
+<add>
+<bug>49284</bug>: Add SSL re-negotiation support to the HTTP NIO
+        connector. (markt)
+</add>
        <fix>
          <bug>50780</bug>: Fix memory leak in APR implementation of AJP
          connector introduced by the refactoring for<bug>49884</bug>. (markt)



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org




---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to