Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ darwin-arm64/sqlcmd
linux-amd64/sqlcmd
linux-arm64/sqlcmd
linux-s390x/sqlcmd

# Build artifacts in root
/sqlcmd
/sqlcmd_binary
5 changes: 5 additions & 0 deletions cmd/sqlcmd/sqlcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type SQLCmdArguments struct {
ApplicationIntent string
EncryptConnection string
HostNameInCertificate string
ServerCertificate string
DriverLoggingLevel int
ExitOnError bool
ErrorSeverityLevel uint8
Expand Down Expand Up @@ -157,6 +158,8 @@ func (a *SQLCmdArguments) Validate(c *cobra.Command) (err error) {
err = rangeParameterError("-y", fmt.Sprint(*a.VariableTypeWidth), 0, 8000, true)
case a.QueryTimeout < 0 || a.QueryTimeout > 65534:
err = rangeParameterError("-t", fmt.Sprint(a.QueryTimeout), 0, 65534, true)
case a.ServerCertificate != "" && a.EncryptConnection != "s" && a.EncryptConnection != "strict":
err = localizer.Errorf("The -J parameter can only be used with strict encryption mode (-N s or -N strict).")
}
}
if err != nil {
Expand Down Expand Up @@ -429,6 +432,7 @@ func setFlags(rootCmd *cobra.Command, args *SQLCmdArguments) {
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))
rootCmd.Flags().StringVarP(&args.EncryptConnection, encryptConnection, "N", "default", localizer.Sprintf("This switch is used by the client to request an encrypted connection"))
rootCmd.Flags().StringVarP(&args.HostNameInCertificate, "host-name-in-certificate", "F", "", localizer.Sprintf("Specifies the host name in the server certificate."))
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 (-N s or -N strict) for certificate pinning instead of standard certificate validation."))
// Can't use NoOptDefVal until this fix: https://github.com/spf13/cobra/issues/866
//rootCmd.Flags().Lookup(encryptConnection).NoOptDefVal = "true"
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"))
Expand Down Expand Up @@ -721,6 +725,7 @@ func setConnect(connect *sqlcmd.ConnectSettings, args *SQLCmdArguments, vars *sq
connect.Encrypt = args.EncryptConnection
}
connect.HostNameInCertificate = args.HostNameInCertificate
connect.ServerCertificate = args.ServerCertificate
connect.PacketSize = args.PacketSize
connect.WorkstationName = args.WorkstationName
connect.LogLevel = args.DriverLoggingLevel
Expand Down
9 changes: 9 additions & 0 deletions cmd/sqlcmd/sqlcmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ func TestValidCommandLineToArgsConversion(t *testing.T) {
{[]string{"-N", "s", "-F", "myserver.domain.com"}, func(args SQLCmdArguments) bool {
return args.EncryptConnection == "s" && args.HostNameInCertificate == "myserver.domain.com"
}},
{[]string{"-N", "s", "-J", "/path/to/cert.pem"}, func(args SQLCmdArguments) bool {
return args.EncryptConnection == "s" && args.ServerCertificate == "/path/to/cert.pem"
}},
{[]string{"-N", "strict", "-J", "/path/to/cert.der"}, func(args SQLCmdArguments) bool {
return args.EncryptConnection == "strict" && args.ServerCertificate == "/path/to/cert.der"
}},
}

for _, test := range commands {
Expand Down Expand Up @@ -162,6 +168,9 @@ func TestInvalidCommandLine(t *testing.T) {
{[]string{"-;"}, "';': Unknown Option. Enter '-?' for help."},
{[]string{"-t", "-2"}, "'-t -2': value must be greater than or equal to 0 and less than or equal to 65534."},
{[]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]]."},
{[]string{"-J", "/path/to/cert.pem"}, "The -J parameter can only be used with strict encryption mode (-N s or -N strict)."},
{[]string{"-N", "m", "-J", "/path/to/cert.pem"}, "The -J parameter can only be used with strict encryption mode (-N s or -N strict)."},
{[]string{"-N", "optional", "-J", "/path/to/cert.pem"}, "The -J parameter can only be used with strict encryption mode (-N s or -N strict)."},
}

for _, test := range commands {
Expand Down
5 changes: 5 additions & 0 deletions pkg/sqlcmd/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type ConnectSettings struct {
ChangePassword string
// The HostNameInCertificate is the name to use for the host in the certificate validation
HostNameInCertificate string
// ServerCertificate is the path to a certificate file to match against the server's TLS certificate
ServerCertificate string
}

func (c ConnectSettings) authenticationMethod() string {
Expand Down Expand Up @@ -150,6 +152,9 @@ func (connect ConnectSettings) ConnectionString() (connectionString string, err
if connect.HostNameInCertificate != "" {
query.Add(msdsn.HostNameInCertificate, connect.HostNameInCertificate)
}
if connect.ServerCertificate != "" {
query.Add(msdsn.Certificate, connect.ServerCertificate)
}
if connect.LogLevel > 0 {
query.Add(msdsn.LogParam, fmt.Sprint(connect.LogLevel))
}
Expand Down