Skip to content

Commit 3197862

Browse files
committed
Add support for re-keying with TLS 1.3
1 parent 2344a4c commit 3197862

File tree

4 files changed

+48
-2
lines changed

4 files changed

+48
-2
lines changed

Diff for: 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}].

Diff for: java/org/apache/tomcat/util/net/SecureNio2Channel.java

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

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

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

6264
protected ByteBuffer netInBuffer;
@@ -67,6 +69,7 @@ public class SecureNio2Channel extends Nio2Channel {
6769
protected volatile boolean sniComplete = false;
6870

6971
private volatile boolean handshakeComplete = false;
72+
private volatile int handshakeWrapQueueLength = 0;
7073
private volatile HandshakeStatus handshakeStatus; //gets set by handshake
7174

7275
protected boolean closed;
@@ -767,6 +770,11 @@ private Integer unwrap(int nRead, long timeout, TimeUnit unit) throws ExecutionE
767770
//perform any tasks if needed
768771
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
769772
tasks();
773+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
774+
if (++handshakeWrapQueueLength > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) {
775+
throw new ExecutionException(
776+
new IOException(sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong")));
777+
}
770778
}
771779
//if we need more network data, then bail out for now.
772780
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -897,6 +905,8 @@ protected void wrap() {
897905
if (!netOutBuffer.hasRemaining()) {
898906
netOutBuffer.clear();
899907
SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
908+
// Call to wrap() will have included any required handshake data
909+
handshakeWrapQueueLength = 0;
900910
written = result.bytesConsumed();
901911
netOutBuffer.flip();
902912
if (result.getStatus() == Status.OK) {
@@ -962,6 +972,11 @@ public void completed(Integer nBytes, A attach) {
962972
//perform any tasks if needed
963973
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
964974
tasks();
975+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
976+
if (++handshakeWrapQueueLength > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) {
977+
throw new ExecutionException(new IOException(
978+
sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong")));
979+
}
965980
}
966981
//if we need more network data, then bail out for now.
967982
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -1075,6 +1090,11 @@ public void completed(Integer nBytes, A attach) {
10751090
//perform any tasks if needed
10761091
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
10771092
tasks();
1093+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
1094+
if (++handshakeWrapQueueLength > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) {
1095+
throw new ExecutionException(new IOException(
1096+
sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong")));
1097+
}
10781098
}
10791099
//if we need more network data, then bail out for now.
10801100
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -1184,6 +1204,8 @@ public <A> void write(final ByteBuffer src, final long timeout, final TimeUnit u
11841204
netOutBuffer.clear();
11851205
// Wrap the source data into the internal buffer
11861206
SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
1207+
// Call to wrap() will have included any required handshake data
1208+
handshakeWrapQueueLength = 0;
11871209
final int written = result.bytesConsumed();
11881210
netOutBuffer.flip();
11891211
if (result.getStatus() == Status.OK) {

Diff for: java/org/apache/tomcat/util/net/SecureNioChannel.java

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

6767
protected boolean handshakeComplete = false;
68+
protected boolean needHandshakeWrap = false;
6869
protected HandshakeStatus handshakeStatus; //gets set by handshake
6970

7071
protected boolean closed = false;
@@ -622,6 +623,14 @@ public int read(ByteBuffer dst) throws IOException {
622623
//perform any tasks if needed
623624
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
624625
tasks();
626+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
627+
if (getOutboundRemaining() == 0) {
628+
handshakeWrap(true);
629+
} else if (needHandshakeWrap) {
630+
throw new IOException(sm.getString("channel.nio.ssl.handshakeWrapPending"));
631+
} else {
632+
needHandshakeWrap = true;
633+
}
625634
}
626635
//if we need more network data, then bail out for now.
627636
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -711,6 +720,14 @@ public long read(ByteBuffer[] dsts, int offset, int length)
711720
//perform any tasks if needed
712721
if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
713722
tasks();
723+
} else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
724+
if (getOutboundRemaining() == 0) {
725+
handshakeWrap(true);
726+
} else if (needHandshakeWrap) {
727+
throw new IOException(sm.getString("channel.nio.ssl.handshakeWrapPending"));
728+
} else {
729+
needHandshakeWrap = true;
730+
}
714731
}
715732
//if we need more network data, then bail out for now.
716733
if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -809,6 +826,8 @@ public int write(ByteBuffer src) throws IOException {
809826
netOutBuffer.clear();
810827

811828
SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
829+
// Call to wrap() will have included any required handshake data
830+
needHandshakeWrap = false;
812831
// The number of bytes written
813832
int written = result.bytesConsumed();
814833
netOutBuffer.flip();

Diff for: webapps/docs/changelog.xml

+3
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@
165165
Make counting of active HTTP/2 streams per connection more robust.
166166
(markt)
167167
</fix>
168+
<add>
169+
Add support for TLS 1.3 client initiated re-keying. (markt)
170+
</add>
168171
</changelog>
169172
</subsection>
170173
<subsection name="Jasper">

0 commit comments

Comments
 (0)