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:
Norman Maurer 2018-02-16 17:25:47 +01:00 committed by Scott Mitchell
parent bc8e022601
commit 268b901844
4 changed files with 69 additions and 15 deletions

View File

@ -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 ||

View File

@ -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);
} }

View File

@ -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();

View File

@ -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);
}
}
} }