Commit 9f08b6c4 authored by Adam Langley's avatar Adam Langley

crypto/tls: don't send IP literals as SNI values.

(This relands commit a4dcc692.)

https://tools.ietf.org/html/rfc6066#section-3 states:

  “Literal IPv4 and IPv6 addresses are not permitted in "HostName".”

However, if an IP literal was set as Config.ServerName (which could
happen as easily as calling Dial with an IP address) then the code would
send the IP literal as the SNI value.

This change filters out IP literals, as recognised by net.ParseIP, from
being sent as the SNI value.

Fixes #13111.

Change-Id: I6e544a78a01388f8fe98150589d073b917087f75
Reviewed-on: https://go-review.googlesource.com/16776
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: 's avatarRuss Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 7bacfc64
......@@ -286,7 +286,8 @@ type Config struct {
// ServerName is used to verify the hostname on the returned
// certificates unless InsecureSkipVerify is given. It is also included
// in the client's handshake to support virtual hosting.
// in the client's handshake to support virtual hosting unless it is
// an IP address.
ServerName string
// ClientAuth determines the server's policy for
......
......@@ -49,13 +49,20 @@ func (c *Conn) clientHandshake() error {
return errors.New("tls: NextProtos values too large")
}
sni := c.config.ServerName
// IP address literals are not permitted as SNI values. See
// https://tools.ietf.org/html/rfc6066#section-3.
if net.ParseIP(sni) != nil {
sni = ""
}
hello := &clientHelloMsg{
vers: c.config.maxVersion(),
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: true,
scts: true,
serverName: c.config.ServerName,
serverName: sni,
supportedCurves: c.config.curvePreferences(),
supportedPoints: []uint8{pointFormatUncompressed},
nextProtoNeg: len(c.config.NextProtos) > 0,
......
......@@ -600,3 +600,30 @@ func TestHandshakClientSCTs(t *testing.T) {
}
runClientTestTLS12(t, test)
}
func TestNoIPAddressesInSNI(t *testing.T) {
for _, ipLiteral := range []string{"1.2.3.4", "::1"} {
c, s := net.Pipe()
go func() {
client := Client(c, &Config{ServerName: ipLiteral})
client.Handshake()
}()
var header [5]byte
if _, err := io.ReadFull(s, header[:]); err != nil {
t.Fatal(err)
}
recordLen := int(header[3])<<8 | int(header[4])
record := make([]byte, recordLen)
if _, err := io.ReadFull(s, record[:]); err != nil {
t.Fatal(err)
}
s.Close()
if bytes.Index(record, []byte(ipLiteral)) != -1 {
t.Errorf("IP literal %q found in ClientHello: %x", ipLiteral, record)
}
}
}
......@@ -642,14 +642,18 @@ func newTLSTransport(t *testing.T, ts *httptest.Server) *Transport {
func TestClientWithCorrectTLSServerName(t *testing.T) {
defer afterTest(t)
const serverName = "example.com"
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
if r.TLS.ServerName != "127.0.0.1" {
t.Errorf("expected client to set ServerName 127.0.0.1, got: %q", r.TLS.ServerName)
if r.TLS.ServerName != serverName {
t.Errorf("expected client to set ServerName %q, got: %q", serverName, r.TLS.ServerName)
}
}))
defer ts.Close()
c := &Client{Transport: newTLSTransport(t, ts)}
trans := newTLSTransport(t, ts)
trans.TLSClientConfig.ServerName = serverName
c := &Client{Transport: trans}
if _, err := c.Get(ts.URL); err != nil {
t.Fatalf("expected successful TLS connection, got error: %v", err)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment