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