The SNI extension value is ASCII encoded but Netty uses UTF-8.

Motivation

RFC 6066 (https://tools.ietf.org/html/rfc6066#page-6) says that the hostname in the SNI extension is ASCII encoded but Netty decodes it using UTF-8.

Modifications

Use ASCII instead of UTF-8

Result

Fixes #6717
This commit is contained in:
Roger Kapsi 2017-05-08 14:11:16 -04:00 committed by Scott Mitchell
parent 6a8532acd1
commit 2db4f2557d
2 changed files with 60 additions and 29 deletions

View File

@ -29,7 +29,6 @@ import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.net.IDN;
import java.net.SocketAddress;
import java.util.List;
import java.util.Locale;
@ -186,11 +185,10 @@ public abstract class AbstractSniHandler<T> extends ByteToMessageDecoder impleme
}
final String hostname = in.toString(offset, serverNameLength,
CharsetUtil.UTF_8);
CharsetUtil.US_ASCII);
try {
select(ctx, IDN.toASCII(hostname,
IDN.ALLOW_UNASSIGNED).toLowerCase(Locale.US));
select(ctx, hostname.toLowerCase(Locale.US));
} catch (Throwable t) {
PlatformDependent.throwException(t);
}

View File

@ -160,38 +160,71 @@ public class SniHandlerTest {
SniHandler handler = new SniHandler(mapping);
EmbeddedChannel ch = new EmbeddedChannel(handler);
// hex dump of a client hello packet, which contains hostname "CHAT4。LEANCLOUD。CN"
String tlsHandshakeMessageHex1 = "16030100";
// part 2
String tlsHandshakeMessageHex = "bd010000b90303a74225676d1814ba57faff3b366" +
"3656ed05ee9dbb2a4dbb1bb1c32d2ea5fc39e0000000100008c0000001700150000164348" +
"415434E380824C45414E434C4F5544E38082434E000b000403000102000a00340032000e0" +
"00d0019000b000c00180009000a0016001700080006000700140015000400050012001300" +
"0100020003000f0010001100230000000d0020001e0601060206030501050205030401040" +
"20403030103020303020102020203000f00010133740000";
try {
// Push the handshake message.
// Decode should fail because SNI error
// hex dump of a client hello packet, which contains hostname "CHAT4.LEANCLOUD.CN"
String tlsHandshakeMessageHex1 = "16030100";
// part 2
String tlsHandshakeMessageHex = "c6010000c20303bb0855d66532c05a0ef784f7c384feeafa68b3" +
"b655ac7288650d5eed4aa3fb52000038c02cc030009fcca9cca8ccaac02b" +
"c02f009ec024c028006bc023c0270067c00ac0140039c009c0130033009d" +
"009c003d003c0035002f00ff010000610000001700150000124348415434" +
"2e4c45414e434c4f55442e434e000b000403000102000a000a0008001d00" +
"170019001800230000000d0020001e060106020603050105020503040104" +
"0204030301030203030201020202030016000000170000";
ch.writeInbound(Unpooled.wrappedBuffer(DatatypeConverter.parseHexBinary(tlsHandshakeMessageHex1)));
ch.writeInbound(Unpooled.wrappedBuffer(DatatypeConverter.parseHexBinary(tlsHandshakeMessageHex)));
fail();
} catch (DecoderException e) {
// expected
// This should produce an alert
assertTrue(ch.finish());
assertThat(handler.hostname(), is("chat4.leancloud.cn"));
assertThat(handler.sslContext(), is(leanContext));
} finally {
ch.finishAndReleaseAll();
}
} finally {
releaseAll(leanContext, leanContext2, nettyContext);
}
}
// This should produce an alert
assertTrue(ch.finish());
@Test(expected = DecoderException.class)
public void testNonAsciiServerNameParsing() throws Exception {
SslContext nettyContext = makeSslContext(provider, false);
SslContext leanContext = makeSslContext(provider, false);
SslContext leanContext2 = makeSslContext(provider, false);
assertThat(handler.hostname(), is("chat4.leancloud.cn"));
assertThat(handler.sslContext(), is(leanContext));
try {
DomainNameMapping<SslContext> mapping = new DomainNameMappingBuilder<SslContext>(nettyContext)
.add("*.netty.io", nettyContext)
// input with custom cases
.add("*.LEANCLOUD.CN", leanContext)
// a hostname conflict with previous one, since we are using order-sensitive config,
// the engine won't be used with the handler.
.add("chat4.leancloud.cn", leanContext2)
.build();
for (;;) {
Object msg = ch.readOutbound();
if (msg == null) {
break;
}
ReferenceCountUtil.release(msg);
SniHandler handler = new SniHandler(mapping);
EmbeddedChannel ch = new EmbeddedChannel(handler);
try {
// hex dump of a client hello packet, which contains an invalid hostname "CHAT4。LEANCLOUD。CN"
String tlsHandshakeMessageHex1 = "16030100";
// part 2
String tlsHandshakeMessageHex = "bd010000b90303a74225676d1814ba57faff3b366" +
"3656ed05ee9dbb2a4dbb1bb1c32d2ea5fc39e0000000100008c0000001700150000164348" +
"415434E380824C45414E434C4F5544E38082434E000b000403000102000a00340032000e0" +
"00d0019000b000c00180009000a0016001700080006000700140015000400050012001300" +
"0100020003000f0010001100230000000d0020001e0601060206030501050205030401040" +
"20403030103020303020102020203000f00010133740000";
// Push the handshake message.
// Decode should fail because of the badly encoded "HostName" string in the SNI extension
// that isn't ASCII as per RFC 6066 - https://tools.ietf.org/html/rfc6066#page-6
ch.writeInbound(Unpooled.wrappedBuffer(DatatypeConverter.parseHexBinary(tlsHandshakeMessageHex1)));
ch.writeInbound(Unpooled.wrappedBuffer(DatatypeConverter.parseHexBinary(tlsHandshakeMessageHex)));
} finally {
ch.finishAndReleaseAll();
}
} finally {
releaseAll(leanContext, leanContext2, nettyContext);