SNI values may not include a trailing dot according to https://tools.ietf.org/html/rfc6066#section-3. Although crypto/tls handled this correctly as a client, it didn't reject this as a server. This change makes sending an SNI value with a trailing dot a fatal error. Updates #18114. Change-Id: Ib7897ab40e98d4a7a4646ff8469a55233621f631 Reviewed-on: https://go-review.googlesource.com/33904 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>tls13
@@ -815,7 +815,7 @@ func hostnameInSNI(name string) string { | |||||
if net.ParseIP(host) != nil { | if net.ParseIP(host) != nil { | ||||
return "" | return "" | ||||
} | } | ||||
if len(name) > 0 && name[len(name)-1] == '.' { | |||||
for len(name) > 0 && name[len(name)-1] == '.' { | |||||
name = name[:len(name)-1] | name = name[:len(name)-1] | ||||
} | } | ||||
return name | return name | ||||
@@ -4,7 +4,10 @@ | |||||
package tls | package tls | ||||
import "bytes" | |||||
import ( | |||||
"bytes" | |||||
"strings" | |||||
) | |||||
type clientHelloMsg struct { | type clientHelloMsg struct { | ||||
raw []byte | raw []byte | ||||
@@ -393,6 +396,12 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { | |||||
} | } | ||||
if nameType == 0 { | if nameType == 0 { | ||||
m.serverName = string(d[:nameLen]) | m.serverName = string(d[:nameLen]) | ||||
// An SNI value may not include a | |||||
// trailing dot. See | |||||
// https://tools.ietf.org/html/rfc6066#section-3. | |||||
if strings.HasSuffix(m.serverName, ".") { | |||||
return false | |||||
} | |||||
break | break | ||||
} | } | ||||
d = d[nameLen:] | d = d[nameLen:] | ||||
@@ -8,6 +8,7 @@ import ( | |||||
"bytes" | "bytes" | ||||
"math/rand" | "math/rand" | ||||
"reflect" | "reflect" | ||||
"strings" | |||||
"testing" | "testing" | ||||
"testing/quick" | "testing/quick" | ||||
) | ) | ||||
@@ -123,6 +124,9 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { | |||||
} | } | ||||
if rand.Intn(10) > 5 { | if rand.Intn(10) > 5 { | ||||
m.serverName = randomString(rand.Intn(255), rand) | m.serverName = randomString(rand.Intn(255), rand) | ||||
for strings.HasSuffix(m.serverName, ".") { | |||||
m.serverName = m.serverName[:len(m.serverName)-1] | |||||
} | |||||
} | } | ||||
m.ocspStapling = rand.Intn(10) > 5 | m.ocspStapling = rand.Intn(10) > 5 | ||||
m.supportedPoints = randomBytes(rand.Intn(5)+1, rand) | m.supportedPoints = randomBytes(rand.Intn(5)+1, rand) | ||||
@@ -137,6 +137,10 @@ func TestNoRC4ByDefault(t *testing.T) { | |||||
testClientHelloFailure(t, serverConfig, clientHello, "no cipher suite supported by both client and server") | testClientHelloFailure(t, serverConfig, clientHello, "no cipher suite supported by both client and server") | ||||
} | } | ||||
func TestRejectSNIWithTrailingDot(t *testing.T) { | |||||
testClientHelloFailure(t, testConfig, &clientHelloMsg{vers: VersionTLS12, serverName: "foo.com."}, "unexpected message") | |||||
} | |||||
func TestDontSelectECDSAWithRSAKey(t *testing.T) { | func TestDontSelectECDSAWithRSAKey(t *testing.T) { | ||||
// Test that, even when both sides support an ECDSA cipher suite, it | // Test that, even when both sides support an ECDSA cipher suite, it | ||||
// won't be selected if the server's private key doesn't support it. | // won't be selected if the server's private key doesn't support it. | ||||