Ensure ReferenceCountedOpenSslEngine not swallow the close_notify
Motivation: We need to ensure we not swallow the close_notify that should be send back to the remote peer. See [#6167] Modifications: - Only call shutdown() in closeInbound() if there is nothing pending that should be send back to the remote peer. - Return the correct HandshakeStatus when the close_notify was received. - Only shutdown() when close_notify was received after closeOutbound() was called. Result: close_notify is correctly send back to the remote peer and handled when received.
This commit is contained in:
parent
eb5dc4bced
commit
dd055c01c7
@ -544,11 +544,14 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
final SSLEngineResult.Status rs;
|
final SSLEngineResult.Status rs;
|
||||||
|
|
||||||
// If isOutboundDone, then the data from the network BIO
|
// If isOutboundDone, then the data from the network BIO
|
||||||
// was the close_notify message and all was consumed we are not required to wait
|
// was the close_notify message, see if we also received the response yet.
|
||||||
// for the receipt the peer's close_notify message -- shutdown.
|
|
||||||
if (isOutboundDone()) {
|
if (isOutboundDone()) {
|
||||||
rs = CLOSED;
|
rs = CLOSED;
|
||||||
shutdown();
|
if (isInboundDone()) {
|
||||||
|
// If the inbound was done as well, we need to ensure we return NOT_HANDSHAKING to signal we are
|
||||||
|
// done.
|
||||||
|
hs = NOT_HANDSHAKING;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rs = OK;
|
rs = OK;
|
||||||
}
|
}
|
||||||
@ -1054,7 +1057,11 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
|
|
||||||
isInboundDone = true;
|
isInboundDone = true;
|
||||||
|
|
||||||
shutdown();
|
if (isOutboundDone()) {
|
||||||
|
// Only call shutdown if there is no outbound data pending.
|
||||||
|
// See https://github.com/netty/netty/issues/6167
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
if (handshakeState != HandshakeState.NOT_STARTED && !receivedShutdown) {
|
if (handshakeState != HandshakeState.NOT_STARTED && !receivedShutdown) {
|
||||||
throw new SSLException(
|
throw new SSLException(
|
||||||
|
@ -1214,4 +1214,113 @@ public abstract class SSLEngineTest {
|
|||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCloseNotifySequence() throws Exception {
|
||||||
|
SelfSignedCertificate cert = new SelfSignedCertificate();
|
||||||
|
|
||||||
|
clientSslCtx = SslContextBuilder
|
||||||
|
.forClient()
|
||||||
|
.trustManager(cert.cert())
|
||||||
|
.sslProvider(sslClientProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
||||||
|
|
||||||
|
serverSslCtx = SslContextBuilder
|
||||||
|
.forServer(cert.certificate(), cert.privateKey())
|
||||||
|
.sslProvider(sslServerProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteBuffer plainClientOut = ByteBuffer.allocate(client.getSession().getApplicationBufferSize());
|
||||||
|
ByteBuffer plainServerOut = ByteBuffer.allocate(server.getSession().getApplicationBufferSize());
|
||||||
|
|
||||||
|
ByteBuffer encryptedClientToServer = ByteBuffer.allocate(client.getSession().getPacketBufferSize());
|
||||||
|
ByteBuffer encryptedServerToClient = ByteBuffer.allocate(server.getSession().getPacketBufferSize());
|
||||||
|
ByteBuffer empty = ByteBuffer.allocate(0);
|
||||||
|
|
||||||
|
handshake(client, server);
|
||||||
|
|
||||||
|
// This will produce a close_notify
|
||||||
|
client.closeOutbound();
|
||||||
|
|
||||||
|
// Something still pending in the outbound buffer.
|
||||||
|
assertFalse(client.isOutboundDone());
|
||||||
|
assertFalse(client.isInboundDone());
|
||||||
|
|
||||||
|
// Now wrap and so drain the outbound buffer.
|
||||||
|
SSLEngineResult result = client.wrap(empty, encryptedClientToServer);
|
||||||
|
encryptedClientToServer.flip();
|
||||||
|
|
||||||
|
assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus());
|
||||||
|
// Need an UNWRAP to read the response of the close_notify
|
||||||
|
assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());
|
||||||
|
|
||||||
|
int produced = result.bytesProduced();
|
||||||
|
int consumed = result.bytesConsumed();
|
||||||
|
int closeNotifyLen = produced;
|
||||||
|
|
||||||
|
assertTrue(produced > 0);
|
||||||
|
assertEquals(0, consumed);
|
||||||
|
assertEquals(produced, encryptedClientToServer.remaining());
|
||||||
|
// Outbound buffer should be drained now.
|
||||||
|
assertTrue(client.isOutboundDone());
|
||||||
|
assertFalse(client.isInboundDone());
|
||||||
|
|
||||||
|
assertFalse(server.isOutboundDone());
|
||||||
|
assertFalse(server.isInboundDone());
|
||||||
|
result = server.unwrap(encryptedClientToServer, plainServerOut);
|
||||||
|
plainServerOut.flip();
|
||||||
|
|
||||||
|
assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus());
|
||||||
|
// Need a WRAP to respond to the close_notify
|
||||||
|
assertEquals(SSLEngineResult.HandshakeStatus.NEED_WRAP, result.getHandshakeStatus());
|
||||||
|
|
||||||
|
produced = result.bytesProduced();
|
||||||
|
consumed = result.bytesConsumed();
|
||||||
|
assertEquals(closeNotifyLen, consumed);
|
||||||
|
assertEquals(0, produced);
|
||||||
|
// Should have consumed the complete close_notify
|
||||||
|
assertEquals(0, encryptedClientToServer.remaining());
|
||||||
|
assertEquals(0, plainServerOut.remaining());
|
||||||
|
|
||||||
|
assertFalse(server.isOutboundDone());
|
||||||
|
assertTrue(server.isInboundDone());
|
||||||
|
|
||||||
|
result = server.wrap(empty, encryptedServerToClient);
|
||||||
|
encryptedServerToClient.flip();
|
||||||
|
assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus());
|
||||||
|
// UNWRAP/WRAP are not expected after this point
|
||||||
|
assertEquals(SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus());
|
||||||
|
|
||||||
|
produced = result.bytesProduced();
|
||||||
|
consumed = result.bytesConsumed();
|
||||||
|
assertEquals(closeNotifyLen, produced);
|
||||||
|
assertEquals(0, consumed);
|
||||||
|
|
||||||
|
assertEquals(produced, encryptedServerToClient.remaining());
|
||||||
|
assertTrue(server.isOutboundDone());
|
||||||
|
assertTrue(server.isInboundDone());
|
||||||
|
|
||||||
|
result = client.unwrap(encryptedServerToClient, plainClientOut);
|
||||||
|
plainClientOut.flip();
|
||||||
|
assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus());
|
||||||
|
// UNWRAP/WRAP are not expected after this point
|
||||||
|
assertEquals(SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus());
|
||||||
|
|
||||||
|
produced = result.bytesProduced();
|
||||||
|
consumed = result.bytesConsumed();
|
||||||
|
assertEquals(closeNotifyLen, consumed);
|
||||||
|
assertEquals(0, produced);
|
||||||
|
assertEquals(0, encryptedServerToClient.remaining());
|
||||||
|
|
||||||
|
assertTrue(client.isOutboundDone());
|
||||||
|
assertTrue(client.isInboundDone());
|
||||||
|
} finally {
|
||||||
|
cert.delete();
|
||||||
|
cleanupClientSslEngine(client);
|
||||||
|
cleanupServerSslEngine(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user