From e8b298b20077b60d707d25a04a964e1b83756197 Mon Sep 17 00:00:00 2001 From: markt Date: Fri, 25 Feb 2011 19:19:13 +0000 Subject: [PATCH] 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. git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1074675 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/coyote/http11/Http11NioProcessor.java | 23 ++++++- .../apache/coyote/http11/LocalStrings.properties | 1 + java/org/apache/tomcat/util/net/NioChannel.java | 2 +- .../apache/tomcat/util/net/SecureNioChannel.java | 73 ++++++++++++++-------- webapps/docs/changelog.xml | 4 ++ 5 files changed, 76 insertions(+), 27 deletions(-) diff --git a/java/org/apache/coyote/http11/Http11NioProcessor.java b/java/org/apache/coyote/http11/Http11NioProcessor.java index daef7e7e2..d24736a1f 100644 --- a/java/org/apache/coyote/http11/Http11NioProcessor.java +++ b/java/org/apache/coyote/http11/Http11NioProcessor.java @@ -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); diff --git a/java/org/apache/coyote/http11/LocalStrings.properties b/java/org/apache/coyote/http11/LocalStrings.properties index c0c27fc36..2eceb31ba 100644 --- a/java/org/apache/coyote/http11/LocalStrings.properties +++ b/java/org/apache/coyote/http11/LocalStrings.properties @@ -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 diff --git a/java/org/apache/tomcat/util/net/NioChannel.java b/java/org/apache/tomcat/util/net/NioChannel.java index 75de91e17..1645db647 100644 --- a/java/org/apache/tomcat/util/net/NioChannel.java +++ b/java/org/apache/tomcat/util/net/NioChannel.java @@ -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; } diff --git a/java/org/apache/tomcat/util/net/SecureNioChannel.java b/java/org/apache/tomcat/util/net/SecureNioChannel.java index a682b4b3e..04dc5ac97 100644 --- a/java/org/apache/tomcat/util/net/SecureNioChannel.java +++ b/java/org/apache/tomcat/util/net/SecureNioChannel.java @@ -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.
* 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 diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 2dad462a6..2154d6efc 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -151,6 +151,10 @@ + + 49284: Add SSL re-negotiation support to the HTTP NIO + connector. (markt) + 50780: Fix memory leak in APR implementation of AJP connector introduced by the refactoring for 49884. (markt) -- 2.11.0