Skip to content

Commit 76c5cce

Browse files
committed
Add support for re-keying with TLS 1.3
1 parent 9fec9a8 commit 76c5cce

File tree

4 files changed

+48
-2
lines changed

4 files changed

+48
-2
lines changed

java/org/apache/tomcat/util/net/LocalStrings.properties

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ channel.nio.ssl.expandNetInBuffer=Expanding network input buffer to [{0}] bytes
2626
channel.nio.ssl.expandNetOutBuffer=Expanding network output buffer to [{0}] bytes
2727
channel.nio.ssl.foundHttp=Found an plain text HTTP request on what should be an encrypted TLS connection
2828
channel.nio.ssl.handshakeError=Handshake error
29+
channel.nio.ssl.handshakeWrapPending=There is already handshake data waiting to be wrapped
30+
channel.nio.ssl.handshakeWrapQueueTooLong=The queue of handshake data to be wrapped has grown too long
2931
channel.nio.ssl.incompleteHandshake=Handshake incomplete, you must complete handshake before reading data.
3032
channel.nio.ssl.invalidCloseState=Invalid close state, will not send network data.
3133
channel.nio.ssl.invalidStatus=Unexpected status [{0}].

java/org/apache/tomcat/util/net/SecureNio2Channel.java

+24-2
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@ public class SecureNio2Channel extends Nio2Channel {
5454
private static final Log log = LogFactory.getLog(SecureNio2Channel.class);
5555
private static final StringManager sm = StringManager.getManager(SecureNio2Channel.class);
5656

57-
// Value determined by observation of what the SSL Engine requested in
58-
// various scenarios
57+
// Value determined by observation of what the SSL Engine requested in various scenarios
5958
private static final int DEFAULT_NET_BUFFER_SIZE = 16921;
6059

60+
// Much longer than it should ever need to be but short enough to trigger connection closure if something goes wrong
61+
private static final int HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT = 100;
62+
6163
protected final Nio2Endpoint endpoint;
6264

6365
protected ByteBuffer netInBuffer;
@@ -68,6 +70,7 @@ public class SecureNio2Channel extends Nio2Channel {
6870
protected volatile boolean sniComplete = false;
6971

7072
private volatile boolean handshakeComplete = false;
73+
private volatile int handshakeWrapQueueLength = 0;
7174
private volatile HandshakeStatus handshakeStatus; //gets set by handshake
7275

7376
protected boolean closed;
@@ -764,6 +767,11 @@ private Integer unwrap(int nRead, long timeout, TimeUnit unit) throws ExecutionE
764767
//perform any tasks if needed
765768
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
766769
tasks();
770+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
771+
if (++handshakeWrapQueueLength > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) {
772+
throw new ExecutionException(
773+
new IOException(sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong")));
774+
}
767775
}
768776
//if we need more network data, then bail out for now.
769777
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -894,6 +902,8 @@ protected void wrap() {
894902
if (!netOutBuffer.hasRemaining()) {
895903
netOutBuffer.clear();
896904
SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
905+
// Call to wrap() will have included any required handshake data
906+
handshakeWrapQueueLength = 0;
897907
written = result.bytesConsumed();
898908
netOutBuffer.flip();
899909
if (result.getStatus() == Status.OK) {
@@ -959,6 +969,11 @@ public void completed(Integer nBytes, A attach) {
959969
//perform any tasks if needed
960970
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
961971
tasks();
972+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
973+
if (++handshakeWrapQueueLength > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) {
974+
throw new ExecutionException(new IOException(
975+
sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong")));
976+
}
962977
}
963978
//if we need more network data, then bail out for now.
964979
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -1072,6 +1087,11 @@ public void completed(Integer nBytes, A attach) {
10721087
//perform any tasks if needed
10731088
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
10741089
tasks();
1090+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
1091+
if (++handshakeWrapQueueLength > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) {
1092+
throw new ExecutionException(new IOException(
1093+
sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong")));
1094+
}
10751095
}
10761096
//if we need more network data, then bail out for now.
10771097
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -1181,6 +1201,8 @@ public <A> void write(final ByteBuffer src, final long timeout, final TimeUnit u
11811201
netOutBuffer.clear();
11821202
// Wrap the source data into the internal buffer
11831203
SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
1204+
// Call to wrap() will have included any required handshake data
1205+
handshakeWrapQueueLength = 0;
11841206
final int written = result.bytesConsumed();
11851207
netOutBuffer.flip();
11861208
if (result.getStatus() == Status.OK) {

java/org/apache/tomcat/util/net/SecureNioChannel.java

+19
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public class SecureNioChannel extends NioChannel {
6666
protected boolean sniComplete = false;
6767

6868
protected boolean handshakeComplete = false;
69+
protected boolean needHandshakeWrap = false;
6970
protected HandshakeStatus handshakeStatus; //gets set by handshake
7071

7172
protected boolean closed = false;
@@ -624,6 +625,14 @@ public int read(ByteBuffer dst) throws IOException {
624625
//perform any tasks if needed
625626
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
626627
tasks();
628+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
629+
if (getOutboundRemaining() == 0) {
630+
handshakeWrap(true);
631+
} else if (needHandshakeWrap) {
632+
throw new IOException(sm.getString("channel.nio.ssl.handshakeWrapPending"));
633+
} else {
634+
needHandshakeWrap = true;
635+
}
627636
}
628637
//if we need more network data, then bail out for now.
629638
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -713,6 +722,14 @@ public long read(ByteBuffer[] dsts, int offset, int length)
713722
//perform any tasks if needed
714723
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
715724
tasks();
725+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
726+
if (getOutboundRemaining() == 0) {
727+
handshakeWrap(true);
728+
} else if (needHandshakeWrap) {
729+
throw new IOException(sm.getString("channel.nio.ssl.handshakeWrapPending"));
730+
} else {
731+
needHandshakeWrap = true;
732+
}
716733
}
717734
//if we need more network data, then bail out for now.
718735
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -811,6 +828,8 @@ public int write(ByteBuffer src) throws IOException {
811828
netOutBuffer.clear();
812829

813830
SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
831+
// Call to wrap() will have included any required handshake data
832+
needHandshakeWrap = false;
814833
// The number of bytes written
815834
int written = result.bytesConsumed();
816835
netOutBuffer.flip();

webapps/docs/changelog.xml

+3
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@
169169
Make counting of active HTTP/2 streams per connection more robust.
170170
(markt)
171171
</fix>
172+
<add>
173+
Add support for TLS 1.3 client initiated re-keying. (markt)
174+
</add>
172175
</changelog>
173176
</subsection>
174177
<subsection name="Jasper">

0 commit comments

Comments
 (0)