Skip to content

Commit

Permalink
crypto/tls: set default minimum client version to TLS 1.2
Browse files Browse the repository at this point in the history
Updates #45428

Change-Id: I5d70066d4091196ec6f8bfc2edf3d78fdc0520c1
Reviewed-on: https://go-review.googlesource.com/c/go/+/359779
Run-TryBot: Filippo Valsorda <[email protected]>
TryBot-Result: Go Bot <[email protected]>
Trust: Filippo Valsorda <[email protected]>
Reviewed-by: Roland Shoemaker <[email protected]>
  • Loading branch information
FiloSottile committed Nov 5, 2021
1 parent f00b43f commit 035963c
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 17 deletions.
38 changes: 30 additions & 8 deletions src/crypto/tls/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"crypto/x509"
"errors"
"fmt"
"internal/godebug"
"io"
"net"
"strings"
Expand Down Expand Up @@ -682,11 +683,20 @@ type Config struct {
ClientSessionCache ClientSessionCache

// MinVersion contains the minimum TLS version that is acceptable.
// If zero, TLS 1.0 is currently taken as the minimum.
//
// By default, TLS 1.2 is currently used as the minimum when acting as a
// client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum
// supported by this package, both as a client and as a server.
//
// The client-side default can temporarily be reverted to TLS 1.0 by
// including the value "x509sha1=1" in the GODEBUG environment variable.
// Note that this option will be removed in Go 1.19 (but it will still be
// possible to set this field to VersionTLS10 explicitly).
MinVersion uint16

// MaxVersion contains the maximum TLS version that is acceptable.
// If zero, the maximum version supported by this package is used,
//
// By default, the maximum version supported by this package is used,
// which is currently TLS 1.3.
MaxVersion uint16

Expand Down Expand Up @@ -964,9 +974,21 @@ var supportedVersions = []uint16{
VersionTLS10,
}

func (c *Config) supportedVersions() []uint16 {
// debugEnableTLS10 enables TLS 1.0. See issue 45428.
var debugEnableTLS10 = godebug.Get("tls10default") == "1"

// roleClient and roleServer are meant to call supportedVersions and parents
// with more readability at the callsite.
const roleClient = true
const roleServer = false

func (c *Config) supportedVersions(isClient bool) []uint16 {
versions := make([]uint16, 0, len(supportedVersions))
for _, v := range supportedVersions {
if (c == nil || c.MinVersion == 0) && !debugEnableTLS10 &&
isClient && v < VersionTLS12 {
continue
}
if c != nil && c.MinVersion != 0 && v < c.MinVersion {
continue
}
Expand All @@ -978,8 +1000,8 @@ func (c *Config) supportedVersions() []uint16 {
return versions
}

func (c *Config) maxSupportedVersion() uint16 {
supportedVersions := c.supportedVersions()
func (c *Config) maxSupportedVersion(isClient bool) uint16 {
supportedVersions := c.supportedVersions(isClient)
if len(supportedVersions) == 0 {
return 0
}
Expand Down Expand Up @@ -1020,8 +1042,8 @@ func (c *Config) supportsCurve(curve CurveID) bool {

// mutualVersion returns the protocol version to use given the advertised
// versions of the peer. Priority is given to the peer preference order.
func (c *Config) mutualVersion(peerVersions []uint16) (uint16, bool) {
supportedVersions := c.supportedVersions()
func (c *Config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) {
supportedVersions := c.supportedVersions(isClient)
for _, peerVersion := range peerVersions {
for _, v := range supportedVersions {
if v == peerVersion {
Expand Down Expand Up @@ -1100,7 +1122,7 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error {
if config == nil {
config = &Config{}
}
vers, ok := config.mutualVersion(chi.SupportedVersions)
vers, ok := config.mutualVersion(roleServer, chi.SupportedVersions)
if !ok {
return errors.New("no mutually supported protocol versions")
}
Expand Down
8 changes: 4 additions & 4 deletions src/crypto/tls/handshake_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) {
return nil, nil, errors.New("tls: NextProtos values too large")
}

supportedVersions := config.supportedVersions()
supportedVersions := config.supportedVersions(roleClient)
if len(supportedVersions) == 0 {
return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
}

clientHelloVersion := config.maxSupportedVersion()
clientHelloVersion := config.maxSupportedVersion(roleClient)
// The version at the beginning of the ClientHello was capped at TLS 1.2
// for compatibility reasons. The supported_versions extension is used
// to negotiate versions now. See RFC 8446, Section 4.2.1.
Expand Down Expand Up @@ -194,7 +194,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
// If we are negotiating a protocol version that's lower than what we
// support, check for the server downgrade canaries.
// See RFC 8446, Section 4.1.3.
maxVers := c.config.maxSupportedVersion()
maxVers := c.config.maxSupportedVersion(roleClient)
tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12
tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11
if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||
Expand Down Expand Up @@ -362,7 +362,7 @@ func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error {
peerVersion = serverHello.supportedVersion
}

vers, ok := c.config.mutualVersion([]uint16{peerVersion})
vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion})
if !ok {
c.sendAlert(alertProtocolVersion)
return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion)
Expand Down
6 changes: 3 additions & 3 deletions src/crypto/tls/handshake_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
if len(clientHello.supportedVersions) == 0 {
clientVersions = supportedVersionsFromMax(clientHello.vers)
}
c.vers, ok = c.config.mutualVersion(clientVersions)
c.vers, ok = c.config.mutualVersion(roleServer, clientVersions)
if !ok {
c.sendAlert(alertProtocolVersion)
return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
Expand Down Expand Up @@ -191,7 +191,7 @@ func (hs *serverHandshakeState) processClientHello() error {
hs.hello.random = make([]byte, 32)
serverRandom := hs.hello.random
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
maxVers := c.config.maxSupportedVersion()
maxVers := c.config.maxSupportedVersion(roleServer)
if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
if c.vers == VersionTLS12 {
copy(serverRandom[24:], downgradeCanaryTLS12)
Expand Down Expand Up @@ -354,7 +354,7 @@ func (hs *serverHandshakeState) pickCipherSuite() error {
for _, id := range hs.clientHello.cipherSuites {
if id == TLS_FALLBACK_SCSV {
// The client is doing a fallback connection. See RFC 7507.
if hs.clientHello.vers < c.config.maxSupportedVersion() {
if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) {
c.sendAlert(alertInappropriateFallback)
return errors.New("tls: client using inappropriate protocol fallback")
}
Expand Down
20 changes: 19 additions & 1 deletion src/crypto/tls/handshake_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,13 +385,30 @@ func TestVersion(t *testing.T) {
}
clientConfig := &Config{
InsecureSkipVerify: true,
MinVersion: VersionTLS10,
}
state, _, err := testHandshake(t, clientConfig, serverConfig)
if err != nil {
t.Fatalf("handshake failed: %s", err)
}
if state.Version != VersionTLS11 {
t.Fatalf("Incorrect version %x, should be %x", state.Version, VersionTLS11)
t.Fatalf("incorrect version %x, should be %x", state.Version, VersionTLS11)
}

clientConfig.MinVersion = 0
_, _, err = testHandshake(t, clientConfig, serverConfig)
if err == nil {
t.Fatalf("expected failure to connect with TLS 1.0/1.1")
}

defer func(old bool) { debugEnableTLS10 = old }(debugEnableTLS10)
debugEnableTLS10 = true
_, _, err = testHandshake(t, clientConfig, serverConfig)
if err != nil {
t.Fatalf("handshake failed: %s", err)
}
if state.Version != VersionTLS11 {
t.Fatalf("incorrect version %x, should be %x", state.Version, VersionTLS11)
}
}

Expand Down Expand Up @@ -472,6 +489,7 @@ func testCrossVersionResume(t *testing.T, version uint16) {
InsecureSkipVerify: true,
ClientSessionCache: NewLRUClientSessionCache(1),
ServerName: "servername",
MinVersion: VersionTLS10,
}

// Establish a session at TLS 1.1.
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/tls/handshake_server_tls13.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error {
if id == TLS_FALLBACK_SCSV {
// Use c.vers instead of max(supported_versions) because an attacker
// could defeat this by adding an arbitrary high version otherwise.
if c.vers < c.config.maxSupportedVersion() {
if c.vers < c.config.maxSupportedVersion(roleServer) {
c.sendAlert(alertInappropriateFallback)
return errors.New("tls: client using inappropriate protocol fallback")
}
Expand Down
2 changes: 2 additions & 0 deletions src/crypto/tls/handshake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ func runMain(m *testing.M) int {
Certificates: make([]Certificate, 2),
InsecureSkipVerify: true,
CipherSuites: allCipherSuites(),
MinVersion: VersionTLS10,
MaxVersion: VersionTLS13,
}
testConfig.Certificates[0].Certificate = [][]byte{testRSACertificate}
testConfig.Certificates[0].PrivateKey = testRSAPrivateKey
Expand Down

0 comments on commit 035963c

Please sign in to comment.