From 9f08b6c49445a30dd516104a68c7725c687c31c2 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Tue, 10 Nov 2015 11:18:50 -0800 Subject: [PATCH] crypto/tls: don't send IP literals as SNI values. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit (This relands commit a4dcc692011bf1ceca9b1a363fd83f3e59e399ee.) 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 Reviewed-by: Brad Fitzpatrick Reviewed-by: Russ Cox TryBot-Result: Gobot Gobot --- src/crypto/tls/common.go | 3 ++- src/crypto/tls/handshake_client.go | 9 ++++++++- src/crypto/tls/handshake_client_test.go | 27 +++++++++++++++++++++++++ src/net/http/client_test.go | 10 ++++++--- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index d47dc6182f..c68ebfe188 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -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 diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index 0b591d7309..462acfd1a1 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -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, diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go index 664fe8de6a..b275da15d0 100644 --- a/src/crypto/tls/handshake_client_test.go +++ b/src/crypto/tls/handshake_client_test.go @@ -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) + } + } +} diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index 86ec83add1..01f8cbaa2d 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -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) } -- 2.48.1