Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=49284
authormarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Fri, 25 Feb 2011 19:19:13 +0000 (19:19 +0000)
committermarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Fri, 25 Feb 2011 19:19:13 +0000 (19:19 +0000)
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.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1074675 13f79535-47bb-0310-9956-ffa450edef68

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

index daef7e7..d24736a 100644 (file)
@@ -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.NioChannel;
 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 AbstractHttp11Processor {
                     .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);
index c0c27fc..2eceb31 100644 (file)
@@ -31,6 +31,7 @@ http11processor.request.process=Error processing request
 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
 
index 75de91e..1645db6 100644 (file)
@@ -175,7 +175,7 @@ public class NioChannel implements ByteChannel{
      * @return boolean
      * TODO Implement this org.apache.tomcat.util.net.SecureNioChannel method
      */
-    public boolean isInitHandshakeComplete() {
+    public boolean isHandshakeComplete() {
         return true;
     }
 
index a682b4b..04dc5ac 100644 (file)
@@ -43,8 +43,8 @@ public class SecureNioChannel extends NioChannel  {
     
     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 NioChannel  {
         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 NioChannel  {
      */
     @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 NioChannel  {
                     //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 NioChannel  {
         //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 NioChannel  {
             //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 NioChannel  {
         //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 NioChannel  {
     }
 
     @Override
-    public boolean isInitHandshakeComplete() {
-        return initHandshakeComplete;
+    public boolean isHandshakeComplete() {
+        return handshakeComplete;
     }
 
     @Override
index 2dad462..2154d6e 100644 (file)
   </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)