Skip to content

Commit a618537

Browse files
Copilotshueybubbles
andcommitted
Implement -J flag for server certificate pinning with strict encryption
Co-authored-by: shueybubbles <[email protected]>
1 parent 9a77bbb commit a618537

3 files changed

Lines changed: 19 additions & 0 deletions

File tree

cmd/sqlcmd/sqlcmd.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type SQLCmdArguments struct {
5757
ApplicationIntent string
5858
EncryptConnection string
5959
HostNameInCertificate string
60+
ServerCertificate string
6061
DriverLoggingLevel int
6162
ExitOnError bool
6263
ErrorSeverityLevel uint8
@@ -157,6 +158,8 @@ func (a *SQLCmdArguments) Validate(c *cobra.Command) (err error) {
157158
err = rangeParameterError("-y", fmt.Sprint(*a.VariableTypeWidth), 0, 8000, true)
158159
case a.QueryTimeout < 0 || a.QueryTimeout > 65534:
159160
err = rangeParameterError("-t", fmt.Sprint(a.QueryTimeout), 0, 65534, true)
161+
case a.ServerCertificate != "" && a.EncryptConnection != "s" && a.EncryptConnection != "strict":
162+
err = localizer.Errorf("The -J parameter can only be used with strict encryption mode (-Ys or -N strict).")
160163
}
161164
}
162165
if err != nil {
@@ -429,6 +432,7 @@ func setFlags(rootCmd *cobra.Command, args *SQLCmdArguments) {
429432
rootCmd.Flags().StringVarP(&args.ApplicationIntent, applicationIntent, "K", "default", localizer.Sprintf("Declares the application workload type when connecting to a server. The only currently supported value is ReadOnly. If %s is not specified, the sqlcmd utility will not support connectivity to a secondary replica in an Always On availability group", localizer.ApplicationIntentFlagShort))
430433
rootCmd.Flags().StringVarP(&args.EncryptConnection, encryptConnection, "N", "default", localizer.Sprintf("This switch is used by the client to request an encrypted connection"))
431434
rootCmd.Flags().StringVarP(&args.HostNameInCertificate, "host-name-in-certificate", "F", "", localizer.Sprintf("Specifies the host name in the server certificate."))
435+
rootCmd.Flags().StringVarP(&args.ServerCertificate, "server-certificate", "J", "", localizer.Sprintf("Specifies the path to a server certificate file (PEM, DER, or CER) to match against the server's TLS certificate. Used with strict encryption mode (-Ys) for certificate pinning instead of standard certificate validation."))
432436
// Can't use NoOptDefVal until this fix: https://github.com/spf13/cobra/issues/866
433437
//rootCmd.Flags().Lookup(encryptConnection).NoOptDefVal = "true"
434438
rootCmd.Flags().BoolVarP(&args.Vertical, "vertical", "", false, localizer.Sprintf("Prints the output in vertical format. This option sets the sqlcmd scripting variable %s to '%s'. The default is false", sqlcmd.SQLCMDFORMAT, "vert"))
@@ -721,6 +725,7 @@ func setConnect(connect *sqlcmd.ConnectSettings, args *SQLCmdArguments, vars *sq
721725
connect.Encrypt = args.EncryptConnection
722726
}
723727
connect.HostNameInCertificate = args.HostNameInCertificate
728+
connect.ServerCertificate = args.ServerCertificate
724729
connect.PacketSize = args.PacketSize
725730
connect.WorkstationName = args.WorkstationName
726731
connect.LogLevel = args.DriverLoggingLevel

cmd/sqlcmd/sqlcmd_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ func TestValidCommandLineToArgsConversion(t *testing.T) {
111111
{[]string{"-N", "s", "-F", "myserver.domain.com"}, func(args SQLCmdArguments) bool {
112112
return args.EncryptConnection == "s" && args.HostNameInCertificate == "myserver.domain.com"
113113
}},
114+
{[]string{"-N", "s", "-J", "/path/to/cert.pem"}, func(args SQLCmdArguments) bool {
115+
return args.EncryptConnection == "s" && args.ServerCertificate == "/path/to/cert.pem"
116+
}},
117+
{[]string{"-N", "strict", "-J", "/path/to/cert.der"}, func(args SQLCmdArguments) bool {
118+
return args.EncryptConnection == "strict" && args.ServerCertificate == "/path/to/cert.der"
119+
}},
114120
}
115121

116122
for _, test := range commands {
@@ -162,6 +168,9 @@ func TestInvalidCommandLine(t *testing.T) {
162168
{[]string{"-;"}, "';': Unknown Option. Enter '-?' for help."},
163169
{[]string{"-t", "-2"}, "'-t -2': value must be greater than or equal to 0 and less than or equal to 65534."},
164170
{[]string{"-N", "invalid"}, "'-N invalid': Unexpected argument. Argument value has to be one of [m[andatory] yes 1 t[rue] disable o[ptional] no 0 f[alse] s[trict]]."},
171+
{[]string{"-J", "/path/to/cert.pem"}, "The -J parameter can only be used with strict encryption mode (-Ys or -N strict)."},
172+
{[]string{"-N", "m", "-J", "/path/to/cert.pem"}, "The -J parameter can only be used with strict encryption mode (-Ys or -N strict)."},
173+
{[]string{"-N", "optional", "-J", "/path/to/cert.pem"}, "The -J parameter can only be used with strict encryption mode (-Ys or -N strict)."},
165174
}
166175

167176
for _, test := range commands {

pkg/sqlcmd/connect.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ type ConnectSettings struct {
6060
ChangePassword string
6161
// The HostNameInCertificate is the name to use for the host in the certificate validation
6262
HostNameInCertificate string
63+
// ServerCertificate is the path to a certificate file to match against the server's TLS certificate
64+
ServerCertificate string
6365
}
6466

6567
func (c ConnectSettings) authenticationMethod() string {
@@ -150,6 +152,9 @@ func (connect ConnectSettings) ConnectionString() (connectionString string, err
150152
if connect.HostNameInCertificate != "" {
151153
query.Add(msdsn.HostNameInCertificate, connect.HostNameInCertificate)
152154
}
155+
if connect.ServerCertificate != "" {
156+
query.Add(msdsn.Certificate, connect.ServerCertificate)
157+
}
153158
if connect.LogLevel > 0 {
154159
query.Add(msdsn.LogParam, fmt.Sprint(connect.LogLevel))
155160
}

0 commit comments

Comments
 (0)