SSL connection not closed properly after handshake failure
Motivation:
When SSL handshake fails, the connection should be closed. This is not true anymore after 978a46c
.
Modifications:
- Ensure we always flush and close the channel on handshake failure.
- Add testcase.
Result:
Fixes [#7724].
This commit is contained in:
parent
bc8e022601
commit
268b901844
|
@ -81,7 +81,7 @@ public abstract class AbstractSniHandler<T> extends ByteToMessageDecoder impleme
|
||||||
"not an SSL/TLS record: " + ByteBufUtil.hexDump(in));
|
"not an SSL/TLS record: " + ByteBufUtil.hexDump(in));
|
||||||
in.skipBytes(in.readableBytes());
|
in.skipBytes(in.readableBytes());
|
||||||
ctx.fireUserEventTriggered(new SniCompletionEvent(e));
|
ctx.fireUserEventTriggered(new SniCompletionEvent(e));
|
||||||
SslUtils.notifyHandshakeFailure(ctx, e, true);
|
SslUtils.handleHandshakeFailure(ctx, e, true);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
if (len == SslUtils.NOT_ENOUGH_DATA ||
|
if (len == SslUtils.NOT_ENOUGH_DATA ||
|
||||||
|
|
|
@ -865,7 +865,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method will not call
|
* This method will not call
|
||||||
* {@link #setHandshakeFailure(ChannelHandlerContext, Throwable, boolean, boolean)} or
|
* {@link #setHandshakeFailure(ChannelHandlerContext, Throwable, boolean, boolean, boolean)} or
|
||||||
* {@link #setHandshakeFailure(ChannelHandlerContext, Throwable)}.
|
* {@link #setHandshakeFailure(ChannelHandlerContext, Throwable)}.
|
||||||
* @return {@code true} if this method ends on {@link SSLEngineResult.HandshakeStatus#NOT_HANDSHAKING}.
|
* @return {@code true} if this method ends on {@link SSLEngineResult.HandshakeStatus#NOT_HANDSHAKING}.
|
||||||
*/
|
*/
|
||||||
|
@ -1002,7 +1002,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||||
// Make sure to release SSLEngine,
|
// Make sure to release SSLEngine,
|
||||||
// and notify the handshake future if the connection has been closed during handshake.
|
// and notify the handshake future if the connection has been closed during handshake.
|
||||||
setHandshakeFailure(ctx, CHANNEL_CLOSED, !outboundClosed, handshakeStarted);
|
setHandshakeFailure(ctx, CHANNEL_CLOSED, !outboundClosed, handshakeStarted, false);
|
||||||
|
|
||||||
// Ensure we always notify the sslClosePromise as well
|
// Ensure we always notify the sslClosePromise as well
|
||||||
notifyClosePromise(CHANNEL_CLOSED);
|
notifyClosePromise(CHANNEL_CLOSED);
|
||||||
|
@ -1191,7 +1191,8 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
||||||
logger.debug("SSLException during trying to call SSLEngine.wrap(...)" +
|
logger.debug("SSLException during trying to call SSLEngine.wrap(...)" +
|
||||||
" because of an previous SSLException, ignoring...", ex);
|
" because of an previous SSLException, ignoring...", ex);
|
||||||
} finally {
|
} finally {
|
||||||
setHandshakeFailure(ctx, cause, true, false);
|
// ensure we always flush and close the channel.
|
||||||
|
setHandshakeFailure(ctx, cause, true, false, true);
|
||||||
}
|
}
|
||||||
PlatformDependent.throwException(cause);
|
PlatformDependent.throwException(cause);
|
||||||
}
|
}
|
||||||
|
@ -1498,13 +1499,14 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
||||||
* Notify all the handshake futures about the failure during the handshake.
|
* Notify all the handshake futures about the failure during the handshake.
|
||||||
*/
|
*/
|
||||||
private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause) {
|
private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
setHandshakeFailure(ctx, cause, true, true);
|
setHandshakeFailure(ctx, cause, true, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify all the handshake futures about the failure during the handshake.
|
* Notify all the handshake futures about the failure during the handshake.
|
||||||
*/
|
*/
|
||||||
private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause, boolean closeInbound, boolean notify) {
|
private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause, boolean closeInbound,
|
||||||
|
boolean notify, boolean alwaysFlushAndClose) {
|
||||||
try {
|
try {
|
||||||
// Release all resources such as internal buffers that SSLEngine
|
// Release all resources such as internal buffers that SSLEngine
|
||||||
// is managing.
|
// is managing.
|
||||||
|
@ -1526,7 +1528,9 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notifyHandshakeFailure(cause, notify);
|
if (handshakePromise.tryFailure(cause) || alwaysFlushAndClose) {
|
||||||
|
SslUtils.handleHandshakeFailure(ctx, cause, notify);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// Ensure we remove and fail all pending writes in all cases and so release memory quickly.
|
// Ensure we remove and fail all pending writes in all cases and so release memory quickly.
|
||||||
releaseAndFailAll(cause);
|
releaseAndFailAll(cause);
|
||||||
|
@ -1539,12 +1543,6 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyHandshakeFailure(Throwable cause, boolean notify) {
|
|
||||||
if (handshakePromise.tryFailure(cause)) {
|
|
||||||
SslUtils.notifyHandshakeFailure(ctx, cause, notify);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyClosePromise(Throwable cause) {
|
private void notifyClosePromise(Throwable cause) {
|
||||||
if (cause == null) {
|
if (cause == null) {
|
||||||
if (sslClosePromise.trySuccess(ctx.channel())) {
|
if (sslClosePromise.trySuccess(ctx.channel())) {
|
||||||
|
@ -1725,7 +1723,9 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
notifyHandshakeFailure(HANDSHAKE_TIMED_OUT, true);
|
if (handshakePromise.tryFailure(HANDSHAKE_TIMED_OUT)) {
|
||||||
|
SslUtils.handleHandshakeFailure(ctx, HANDSHAKE_TIMED_OUT, true);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
releaseAndFailAll(HANDSHAKE_TIMED_OUT);
|
releaseAndFailAll(HANDSHAKE_TIMED_OUT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -310,7 +310,7 @@ final class SslUtils {
|
||||||
return packetLength;
|
return packetLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void notifyHandshakeFailure(ChannelHandlerContext ctx, Throwable cause, boolean notify) {
|
static void handleHandshakeFailure(ChannelHandlerContext ctx, Throwable cause, boolean notify) {
|
||||||
// We have may haven written some parts of data before an exception was thrown so ensure we always flush.
|
// We have may haven written some parts of data before an exception was thrown so ensure we always flush.
|
||||||
// See https://github.com/netty/netty/issues/3900#issuecomment-172481830
|
// See https://github.com/netty/netty/issues/3900#issuecomment-172481830
|
||||||
ctx.flush();
|
ctx.flush();
|
||||||
|
|
|
@ -599,4 +599,58 @@ public class SslHandlerTest {
|
||||||
ReferenceCountUtil.release(sslClientCtx);
|
ReferenceCountUtil.release(sslClientCtx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 10000)
|
||||||
|
public void testCloseOnHandshakeFailure() throws Exception {
|
||||||
|
final SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||||
|
|
||||||
|
final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.key(), ssc.cert()).build();
|
||||||
|
final SslContext sslClientCtx = SslContextBuilder.forClient()
|
||||||
|
.trustManager(new SelfSignedCertificate().cert())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
EventLoopGroup group = new NioEventLoopGroup(1);
|
||||||
|
Channel sc = null;
|
||||||
|
Channel cc = null;
|
||||||
|
try {
|
||||||
|
LocalAddress address = new LocalAddress(getClass().getSimpleName() + ".testCloseOnHandshakeFailure");
|
||||||
|
ServerBootstrap sb = new ServerBootstrap()
|
||||||
|
.group(group)
|
||||||
|
.channel(LocalServerChannel.class)
|
||||||
|
.childHandler(new ChannelInitializer<Channel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel ch) {
|
||||||
|
ch.pipeline().addLast(sslServerCtx.newHandler(ch.alloc()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sc = sb.bind(address).syncUninterruptibly().channel();
|
||||||
|
|
||||||
|
Bootstrap b = new Bootstrap()
|
||||||
|
.group(group)
|
||||||
|
.channel(LocalChannel.class)
|
||||||
|
.handler(new ChannelInitializer<Channel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel ch) {
|
||||||
|
ch.pipeline().addLast(sslClientCtx.newHandler(ch.alloc()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cc = b.connect(sc.localAddress()).syncUninterruptibly().channel();
|
||||||
|
SslHandler handler = cc.pipeline().get(SslHandler.class);
|
||||||
|
handler.handshakeFuture().awaitUninterruptibly();
|
||||||
|
assertFalse(handler.handshakeFuture().isSuccess());
|
||||||
|
|
||||||
|
cc.closeFuture().syncUninterruptibly();
|
||||||
|
} finally {
|
||||||
|
if (cc != null) {
|
||||||
|
cc.close().syncUninterruptibly();
|
||||||
|
}
|
||||||
|
if (sc != null) {
|
||||||
|
sc.close().syncUninterruptibly();
|
||||||
|
}
|
||||||
|
group.shutdownGracefully();
|
||||||
|
|
||||||
|
ReferenceCountUtil.release(sslServerCtx);
|
||||||
|
ReferenceCountUtil.release(sslClientCtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user