Skip to content

Commit

Permalink
add tlsmin connection parameter (#45)
Browse files Browse the repository at this point in the history
* add tlsmin connection parameter

* fix typoe

* fix param name

* 1.3 is too new for old go

* tls 1.3 is go 1.12+
  • Loading branch information
shueybubbles authored Aug 8, 2022
1 parent 63479d5 commit 2d408c3
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 5 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ Other supported formats are listed below.
* 16 log statement parameters
* 32 log transaction begin/end
* `TrustServerCertificate`
* false - Server certificate is checked. Default is false if encypt is specified.
* false - Server certificate is checked. Default is false if encrypt is specified.
* true - Server certificate is not checked. Default is true if encrypt is not specified. If trust server certificate is true, driver accepts any certificate presented by the server and any host name in that certificate. In this mode, TLS is susceptible to man-in-the-middle attacks. This should be used only for testing.
* `certificate` - The file that contains the public key certificate of the CA that signed the SQL Server certificate. The specified certificate overrides the go platform specific CA certificates.
* `hostNameInCertificate` - Specifies the Common Name (CN) in the server certificate. Default value is the server host.
* `tlsmin` - Specifies the minimum TLS version for negotiating encryption with the server. Recognized values are `1.0`, `1.1`, `1.2`, `1.3`. If not set to a recognized value the default value for the `tls` package will be used. The default is currently `1.2`.
* `ServerSPN` - The kerberos SPN (Service Principal Name) for the server. Default is MSSQLSvc/host:port.
* `Workstation ID` - The workstation name (default is the host name)
* `ApplicationIntent` - Can be given the value `ReadOnly` to initiate a read-only connection to an Availability Group listener. The `database` must be specified when connecting with `Application Intent` set to `ReadOnly`.
Expand Down
7 changes: 5 additions & 2 deletions msdsn/conn_str.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ type Config struct {
PacketSize uint16
}

func SetupTLS(certificate string, insecureSkipVerify bool, hostInCertificate string) (*tls.Config, error) {
func SetupTLS(certificate string, insecureSkipVerify bool, hostInCertificate string, minTLSVersion string) (*tls.Config, error) {
config := tls.Config{
ServerName: hostInCertificate,
InsecureSkipVerify: insecureSkipVerify,
Expand All @@ -85,7 +85,9 @@ func SetupTLS(certificate string, insecureSkipVerify bool, hostInCertificate str
// while SQL Server seems to expect one TCP segment per encrypted TDS package.
// Setting DynamicRecordSizingDisabled to true disables that algorithm and uses 16384 bytes per TLS package
DynamicRecordSizingDisabled: true,
MinVersion: TLSVersionFromString(minTLSVersion),
}

if len(certificate) == 0 {
return &config, nil
}
Expand Down Expand Up @@ -256,8 +258,9 @@ func Parse(dsn string) (Config, map[string]string, error) {
}

if p.Encryption != EncryptionDisabled {
tlsMin := params["tlsmin"]
var err error
p.TLSConfig, err = SetupTLS(certificate, trustServerCert, hostInCertificate)
p.TLSConfig, err = SetupTLS(certificate, trustServerCert, hostInCertificate, tlsMin)
if err != nil {
return p, params, fmt.Errorf("failed to setup TLS: %w", err)
}
Expand Down
22 changes: 22 additions & 0 deletions msdsn/conn_str_go112.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//go:build go1.12
// +build go1.12

package msdsn

import "crypto/tls"

func TLSVersionFromString(minTLSVersion string) uint16 {
switch minTLSVersion {
case "1.0":
return tls.VersionTLS10
case "1.1":
return tls.VersionTLS11
case "1.2":
return tls.VersionTLS12
case "1.3":
return tls.VersionTLS13
default:
// use the tls package default
}
return 0
}
20 changes: 20 additions & 0 deletions msdsn/conn_str_go112pre.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//go:build !go1.12
// +build !go1.12

package msdsn

import "crypto/tls"

func TLSVersionFromString(minTLSVersion string) uint16 {
switch minTLSVersion {
case "1.0":
return tls.VersionTLS10
case "1.1":
return tls.VersionTLS11
case "1.2":
return tls.VersionTLS12
default:
// use the tls package default
}
return 0
}
22 changes: 21 additions & 1 deletion msdsn/conn_str_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package msdsn

import (
"crypto/tls"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -59,7 +60,23 @@ func TestValidConnectionString(t *testing.T) {
{"failoverpartner=fopartner;failoverport=2000", func(p Config) bool { return p.FailOverPartner == "fopartner" && p.FailOverPort == 2000 }},
{"app name=appname;applicationintent=ReadOnly;database=testdb", func(p Config) bool { return p.AppName == "appname" && p.ReadOnlyIntent }},
{"encrypt=disable", func(p Config) bool { return p.Encryption == EncryptionDisabled }},
{"encrypt=true", func(p Config) bool { return p.Encryption == EncryptionRequired }},
{"encrypt=disable;tlsmin=1.1", func(p Config) bool { return p.Encryption == EncryptionDisabled && p.TLSConfig == nil }},
{"encrypt=true", func(p Config) bool { return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == 0 }},
{"encrypt=true;tlsmin=1.0", func(p Config) bool {
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS10
}},
{"encrypt=false;tlsmin=1.0", func(p Config) bool {
return p.Encryption == EncryptionOff && p.TLSConfig.MinVersion == tls.VersionTLS10
}},
{"encrypt=true;tlsmin=1.1", func(p Config) bool {
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS11
}},
{"encrypt=true;tlsmin=1.2", func(p Config) bool {
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS12
}},
{"encrypt=true;tlsmin=1.4", func(p Config) bool {
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == 0
}},
{"encrypt=false", func(p Config) bool { return p.Encryption == EncryptionOff }},
{"connection timeout=3;dial timeout=4;keepalive=5", func(p Config) bool {
return p.ConnTimeout == 3*time.Second && p.DialTimeout == 4*time.Second && p.KeepAlive == 5*time.Second
Expand Down Expand Up @@ -159,6 +176,9 @@ func TestValidConnectionString(t *testing.T) {
{"sqlserver://someuser@somehost?connection+timeout=30&disableretry=1", func(p Config) bool {
return p.Host == "somehost" && p.Port == 0 && p.Instance == "" && p.User == "someuser" && p.Password == "" && p.ConnTimeout == 30*time.Second && p.DisableRetry
}},
{"sqlserver://somehost?encrypt=true&tlsmin=1.1", func(p Config) bool {
return p.Host == "somehost" && p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS11
}},
}
for _, ts := range connStrings {
p, _, err := Parse(ts.connStr)
Expand Down
2 changes: 1 addition & 1 deletion tds.go
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,7 @@ initiate_connection:
}
}
if config == nil {
config, err = msdsn.SetupTLS("", false, p.Host)
config, err = msdsn.SetupTLS("", false, p.Host, "")
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 2d408c3

Please sign in to comment.