This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new 76c5cce6f0 Add support for re-keying with TLS 1.3 76c5cce6f0 is described below commit 76c5cce6f0bcef14b0c21c38910371ca7d322d13 Author: Mark Thomas <ma...@apache.org> AuthorDate: Wed Jun 12 19:43:39 2024 +0100 Add support for re-keying with TLS 1.3 --- .../apache/tomcat/util/net/LocalStrings.properties | 2 ++ .../apache/tomcat/util/net/SecureNio2Channel.java | 26 ++++++++++++++++++++-- .../apache/tomcat/util/net/SecureNioChannel.java | 19 ++++++++++++++++ webapps/docs/changelog.xml | 3 +++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties b/java/org/apache/tomcat/util/net/LocalStrings.properties index 10b4fc1f47..9c7c601e59 100644 --- a/java/org/apache/tomcat/util/net/LocalStrings.properties +++ b/java/org/apache/tomcat/util/net/LocalStrings.properties @@ -26,6 +26,8 @@ channel.nio.ssl.expandNetInBuffer=Expanding network input buffer to [{0}] bytes channel.nio.ssl.expandNetOutBuffer=Expanding network output buffer to [{0}] bytes channel.nio.ssl.foundHttp=Found an plain text HTTP request on what should be an encrypted TLS connection channel.nio.ssl.handshakeError=Handshake error +channel.nio.ssl.handshakeWrapPending=There is already handshake data waiting to be wrapped +channel.nio.ssl.handshakeWrapQueueTooLong=The queue of handshake data to be wrapped has grown too long channel.nio.ssl.incompleteHandshake=Handshake incomplete, you must complete handshake before reading data. channel.nio.ssl.invalidCloseState=Invalid close state, will not send network data. channel.nio.ssl.invalidStatus=Unexpected status [{0}]. diff --git a/java/org/apache/tomcat/util/net/SecureNio2Channel.java b/java/org/apache/tomcat/util/net/SecureNio2Channel.java index 4ec1b3c1c7..0a3624bc26 100644 --- a/java/org/apache/tomcat/util/net/SecureNio2Channel.java +++ b/java/org/apache/tomcat/util/net/SecureNio2Channel.java @@ -54,10 +54,12 @@ public class SecureNio2Channel extends Nio2Channel { private static final Log log = LogFactory.getLog(SecureNio2Channel.class); private static final StringManager sm = StringManager.getManager(SecureNio2Channel.class); - // Value determined by observation of what the SSL Engine requested in - // various scenarios + // Value determined by observation of what the SSL Engine requested in various scenarios private static final int DEFAULT_NET_BUFFER_SIZE = 16921; + // Much longer than it should ever need to be but short enough to trigger connection closure if something goes wrong + private static final int HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT = 100; + protected final Nio2Endpoint endpoint; protected ByteBuffer netInBuffer; @@ -68,6 +70,7 @@ public class SecureNio2Channel extends Nio2Channel { protected volatile boolean sniComplete = false; private volatile boolean handshakeComplete = false; + private volatile int handshakeWrapQueueLength = 0; private volatile HandshakeStatus handshakeStatus; //gets set by handshake protected boolean closed; @@ -764,6 +767,11 @@ public class SecureNio2Channel extends Nio2Channel { //perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); + } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { + if (++handshakeWrapQueueLength > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) { + throw new ExecutionException( + new IOException(sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong"))); + } } //if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { @@ -894,6 +902,8 @@ public class SecureNio2Channel extends Nio2Channel { if (!netOutBuffer.hasRemaining()) { netOutBuffer.clear(); SSLEngineResult result = sslEngine.wrap(src, netOutBuffer); + // Call to wrap() will have included any required handshake data + handshakeWrapQueueLength = 0; written = result.bytesConsumed(); netOutBuffer.flip(); if (result.getStatus() == Status.OK) { @@ -959,6 +969,11 @@ public class SecureNio2Channel extends Nio2Channel { //perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); + } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { + if (++handshakeWrapQueueLength > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) { + throw new ExecutionException(new IOException( + sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong"))); + } } //if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { @@ -1072,6 +1087,11 @@ public class SecureNio2Channel extends Nio2Channel { //perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); + } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { + if (++handshakeWrapQueueLength > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) { + throw new ExecutionException(new IOException( + sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong"))); + } } //if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { @@ -1181,6 +1201,8 @@ public class SecureNio2Channel extends Nio2Channel { netOutBuffer.clear(); // Wrap the source data into the internal buffer SSLEngineResult result = sslEngine.wrap(src, netOutBuffer); + // Call to wrap() will have included any required handshake data + handshakeWrapQueueLength = 0; final int written = result.bytesConsumed(); netOutBuffer.flip(); if (result.getStatus() == Status.OK) { diff --git a/java/org/apache/tomcat/util/net/SecureNioChannel.java b/java/org/apache/tomcat/util/net/SecureNioChannel.java index 24762506d7..8dfba3fae1 100644 --- a/java/org/apache/tomcat/util/net/SecureNioChannel.java +++ b/java/org/apache/tomcat/util/net/SecureNioChannel.java @@ -66,6 +66,7 @@ public class SecureNioChannel extends NioChannel { protected boolean sniComplete = false; protected boolean handshakeComplete = false; + protected boolean needHandshakeWrap = false; protected HandshakeStatus handshakeStatus; //gets set by handshake protected boolean closed = false; @@ -624,6 +625,14 @@ public class SecureNioChannel extends NioChannel { //perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); + } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { + if (getOutboundRemaining() == 0) { + handshakeWrap(true); + } else if (needHandshakeWrap) { + throw new IOException(sm.getString("channel.nio.ssl.handshakeWrapPending")); + } else { + needHandshakeWrap = true; + } } //if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { @@ -713,6 +722,14 @@ public class SecureNioChannel extends NioChannel { //perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); + } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { + if (getOutboundRemaining() == 0) { + handshakeWrap(true); + } else if (needHandshakeWrap) { + throw new IOException(sm.getString("channel.nio.ssl.handshakeWrapPending")); + } else { + needHandshakeWrap = true; + } } //if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { @@ -811,6 +828,8 @@ public class SecureNioChannel extends NioChannel { netOutBuffer.clear(); SSLEngineResult result = sslEngine.wrap(src, netOutBuffer); + // Call to wrap() will have included any required handshake data + needHandshakeWrap = false; // The number of bytes written int written = result.bytesConsumed(); netOutBuffer.flip(); diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 76b0fb554b..0d012e7bf8 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -169,6 +169,9 @@ Make counting of active HTTP/2 streams per connection more robust. (markt) </fix> + <add> + Add support for TLS 1.3 client initiated re-keying. (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