Skip to content

Commit 2d408c3

Browse files
authored
add tlsmin connection parameter (#45)
* add tlsmin connection parameter * fix typoe * fix param name * 1.3 is too new for old go * tls 1.3 is go 1.12+
1 parent 63479d5 commit 2d408c3

File tree

6 files changed

+71
-5
lines changed

6 files changed

+71
-5
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ Other supported formats are listed below.
5050
* 16 log statement parameters
5151
* 32 log transaction begin/end
5252
* `TrustServerCertificate`
53-
* false - Server certificate is checked. Default is false if encypt is specified.
53+
* false - Server certificate is checked. Default is false if encrypt is specified.
5454
* 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.
5555
* `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.
5656
* `hostNameInCertificate` - Specifies the Common Name (CN) in the server certificate. Default value is the server host.
57+
* `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`.
5758
* `ServerSPN` - The kerberos SPN (Service Principal Name) for the server. Default is MSSQLSvc/host:port.
5859
* `Workstation ID` - The workstation name (default is the host name)
5960
* `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`.

msdsn/conn_str.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ type Config struct {
7575
PacketSize uint16
7676
}
7777

78-
func SetupTLS(certificate string, insecureSkipVerify bool, hostInCertificate string) (*tls.Config, error) {
78+
func SetupTLS(certificate string, insecureSkipVerify bool, hostInCertificate string, minTLSVersion string) (*tls.Config, error) {
7979
config := tls.Config{
8080
ServerName: hostInCertificate,
8181
InsecureSkipVerify: insecureSkipVerify,
@@ -85,7 +85,9 @@ func SetupTLS(certificate string, insecureSkipVerify bool, hostInCertificate str
8585
// while SQL Server seems to expect one TCP segment per encrypted TDS package.
8686
// Setting DynamicRecordSizingDisabled to true disables that algorithm and uses 16384 bytes per TLS package
8787
DynamicRecordSizingDisabled: true,
88+
MinVersion: TLSVersionFromString(minTLSVersion),
8889
}
90+
8991
if len(certificate) == 0 {
9092
return &config, nil
9193
}
@@ -256,8 +258,9 @@ func Parse(dsn string) (Config, map[string]string, error) {
256258
}
257259

258260
if p.Encryption != EncryptionDisabled {
261+
tlsMin := params["tlsmin"]
259262
var err error
260-
p.TLSConfig, err = SetupTLS(certificate, trustServerCert, hostInCertificate)
263+
p.TLSConfig, err = SetupTLS(certificate, trustServerCert, hostInCertificate, tlsMin)
261264
if err != nil {
262265
return p, params, fmt.Errorf("failed to setup TLS: %w", err)
263266
}

msdsn/conn_str_go112.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//go:build go1.12
2+
// +build go1.12
3+
4+
package msdsn
5+
6+
import "crypto/tls"
7+
8+
func TLSVersionFromString(minTLSVersion string) uint16 {
9+
switch minTLSVersion {
10+
case "1.0":
11+
return tls.VersionTLS10
12+
case "1.1":
13+
return tls.VersionTLS11
14+
case "1.2":
15+
return tls.VersionTLS12
16+
case "1.3":
17+
return tls.VersionTLS13
18+
default:
19+
// use the tls package default
20+
}
21+
return 0
22+
}

msdsn/conn_str_go112pre.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//go:build !go1.12
2+
// +build !go1.12
3+
4+
package msdsn
5+
6+
import "crypto/tls"
7+
8+
func TLSVersionFromString(minTLSVersion string) uint16 {
9+
switch minTLSVersion {
10+
case "1.0":
11+
return tls.VersionTLS10
12+
case "1.1":
13+
return tls.VersionTLS11
14+
case "1.2":
15+
return tls.VersionTLS12
16+
default:
17+
// use the tls package default
18+
}
19+
return 0
20+
}

msdsn/conn_str_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package msdsn
22

33
import (
4+
"crypto/tls"
45
"reflect"
56
"testing"
67
"time"
@@ -59,7 +60,23 @@ func TestValidConnectionString(t *testing.T) {
5960
{"failoverpartner=fopartner;failoverport=2000", func(p Config) bool { return p.FailOverPartner == "fopartner" && p.FailOverPort == 2000 }},
6061
{"app name=appname;applicationintent=ReadOnly;database=testdb", func(p Config) bool { return p.AppName == "appname" && p.ReadOnlyIntent }},
6162
{"encrypt=disable", func(p Config) bool { return p.Encryption == EncryptionDisabled }},
62-
{"encrypt=true", func(p Config) bool { return p.Encryption == EncryptionRequired }},
63+
{"encrypt=disable;tlsmin=1.1", func(p Config) bool { return p.Encryption == EncryptionDisabled && p.TLSConfig == nil }},
64+
{"encrypt=true", func(p Config) bool { return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == 0 }},
65+
{"encrypt=true;tlsmin=1.0", func(p Config) bool {
66+
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS10
67+
}},
68+
{"encrypt=false;tlsmin=1.0", func(p Config) bool {
69+
return p.Encryption == EncryptionOff && p.TLSConfig.MinVersion == tls.VersionTLS10
70+
}},
71+
{"encrypt=true;tlsmin=1.1", func(p Config) bool {
72+
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS11
73+
}},
74+
{"encrypt=true;tlsmin=1.2", func(p Config) bool {
75+
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS12
76+
}},
77+
{"encrypt=true;tlsmin=1.4", func(p Config) bool {
78+
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == 0
79+
}},
6380
{"encrypt=false", func(p Config) bool { return p.Encryption == EncryptionOff }},
6481
{"connection timeout=3;dial timeout=4;keepalive=5", func(p Config) bool {
6582
return p.ConnTimeout == 3*time.Second && p.DialTimeout == 4*time.Second && p.KeepAlive == 5*time.Second
@@ -159,6 +176,9 @@ func TestValidConnectionString(t *testing.T) {
159176
{"sqlserver://someuser@somehost?connection+timeout=30&disableretry=1", func(p Config) bool {
160177
return p.Host == "somehost" && p.Port == 0 && p.Instance == "" && p.User == "someuser" && p.Password == "" && p.ConnTimeout == 30*time.Second && p.DisableRetry
161178
}},
179+
{"sqlserver://somehost?encrypt=true&tlsmin=1.1", func(p Config) bool {
180+
return p.Host == "somehost" && p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS11
181+
}},
162182
}
163183
for _, ts := range connStrings {
164184
p, _, err := Parse(ts.connStr)

tds.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ initiate_connection:
11481148
}
11491149
}
11501150
if config == nil {
1151-
config, err = msdsn.SetupTLS("", false, p.Host)
1151+
config, err = msdsn.SetupTLS("", false, p.Host, "")
11521152
if err != nil {
11531153
return nil, err
11541154
}

0 commit comments

Comments
 (0)