Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Kerberos auth support #702

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1854c5c
add krb auth support
Nov 29, 2021
f2e846d
worked on review comments
chandanjainn Nov 30, 2021
b46e98e
Rename kerberos variables.
chandanjainn Nov 30, 2021
1f85924
Removed leading space
chandanjainn Dec 1, 2021
2fe0bca
unexport initiator states
chandanjainn Dec 6, 2021
d361509
removed the global vars in kerbauth.go
chandanjainn Dec 7, 2021
86e0074
migrated gokrb from v7 to v8.
chandanjainn Dec 8, 2021
eca4758
worked on error message
chandanjainn Dec 9, 2021
c47a35b
worked on error messages.
chandanjainn Dec 13, 2021
27f50f1
updated readme.
chandanjainn Dec 20, 2021
f792281
Merge branch 'master' into kerberos_auth
chandanjainn Jan 19, 2022
ca67c06
Wroked on review comments
chandanjainn Jan 20, 2022
6e619be
fixed the changes for readme.
chandanjainn Jan 20, 2022
d4f52ce
fix: whitespace
Jan 20, 2022
530eb45
worked on review comments
chandanjainn Jan 20, 2022
85c5bb1
fix for unit testing
chandanjainn Jan 20, 2022
04c18be
Merge branch 'master' into kerberos_auth
chandanjainn Jan 24, 2022
8059af5
renamed kerberos config variable
chandanjainn Jan 24, 2022
79d6641
Merge branch 'master' into kerberos_auth
chandanjainn Jan 24, 2022
b4e96e3
nil pointer fix
chandanjainn Jan 25, 2022
729e190
Merge branch 'kerberos_auth' of github.com:chandanjainn/go-mssqldb in…
chandanjainn Jan 25, 2022
dc1a816
removed commented code
chandanjainn Feb 8, 2022
73ae20f
removed unused field
chandanjainn Feb 8, 2022
bf01def
code formatting
chandanjainn Feb 10, 2022
dd22d87
Merge branch 'master' into kerberos_auth
chandanjainn Jun 13, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions go.mod
Expand Up @@ -4,5 +4,12 @@ go 1.11

require (
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/jcmturner/gofork v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
gopkg.in/jcmturner/gokrb5.v7 v7.5.0
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect
)
14 changes: 14 additions & 0 deletions go.sum
@@ -1,5 +1,19 @@
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM=
gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI=
gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg=
gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU=
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
173 changes: 173 additions & 0 deletions kerbauth.go
@@ -0,0 +1,173 @@
package mssql
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree 1.13 is probably a reasonable minimum requirement at this point.
That said - it may be worthwhile to decouple specific auth implementations like this one from msdsn in general and instead have each auth implementation inject itself into a discovery mechanism such as with some visitor pattern on the connection properties. I'm new enough to Go not to know the best way to have such dependency injection.
With such dependency injection you could mark the kerbauth files for 1.13+ only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How could this PR align with #718 ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shueybubbles with 718, anyone using go-mssql driver for kerberos will have to implement their own custom logic and/or maintain their own repos to handle the entire kerberos authN process.
Moreover my PR provides with flexibility to use either krb cache or keytab file which I believe is missing from 718?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My other comment re: coupling the config to the specific krb implementation is my main concern. The other PR at least avoids such coupling. I think there should be a way for the backend of the driver to manage krb more transparently than having the client use krb-specific packages to even define the connection.


import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"

"gopkg.in/jcmturner/gokrb5.v7/client"
"gopkg.in/jcmturner/gokrb5.v7/config"
"gopkg.in/jcmturner/gokrb5.v7/credentials"
"gopkg.in/jcmturner/gokrb5.v7/keytab"
"gopkg.in/jcmturner/gokrb5.v7/messages"
"gopkg.in/jcmturner/gokrb5.v7/spnego"
"gopkg.in/jcmturner/gokrb5.v7/types"
)

type krb5Auth struct {
username string
realm string
serverSPN string
password string
port uint64
krb5ConfFile string
krbFile string
initkrbwithkeytab bool
krb5Client *client.Client
state krb5ClientState
}

var clientWithKeytab = client.NewClientWithKeytab
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
var loadCCache = credentials.LoadCCache
var clientFromCCache = client.NewClientFromCCache
var spnegoNewNegToken = spnego.NewNegTokenInitKRB5
var spnegoToken spnego.SPNEGOToken
var spnegoUnmarshal = spnegoToken.Unmarshal
var kt = &keytab.Keytab{}
var ktUnmarshal = kt.Unmarshal

var negTokenMarshal = func(negTok spnego.NegTokenInit) ([]byte, error) {
return negTok.Marshal()
}
var getServiceTicket = func(cl *client.Client, spn string) (messages.Ticket, types.EncryptionKey, error) {
return cl.GetServiceTicket(spn)
}

func getKRB5Auth(user, serverSPN, krb5Conf, krbFile, password string, initkrbwithkeytab bool) (auth, bool) {
var port uint64
var realm string
var serviceStr string
var err error

params1 := strings.Split(serverSPN, ":")
if len(params1) != 2 {
return nil, false
}

params2 := strings.Split(params1[1], "@")
switch len(params2) {
case 1:
port, err = strconv.ParseUint(params1[1], 10, 16)
if err != nil {
return nil, false
}
case 2:
port, err = strconv.ParseUint(params2[0], 10, 16)
if err != nil {
return nil, false
}
default:
return nil, false
}

params3 := strings.Split(serverSPN, "@")
switch len(params3) {
case 1:
serviceStr = params3[0]
params3 = strings.Split(params1[0], "/")
params3 = strings.Split(params3[1], ".")
realm = params3[1] + "." + params3[2]
case 2:
realm = params3[1]
serviceStr = params3[0]
default:
return nil, false
}

return &krb5Auth{
username: user,
serverSPN: serviceStr,
port: port,
realm: realm,
krb5ConfFile: krb5Conf,
krbFile: krbFile,
password: password,
initkrbwithkeytab: initkrbwithkeytab,
}, true
}

func (auth *krb5Auth) InitialBytes() ([]byte, error) {
var err error
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
krb5CnfFile, err := os.Open(auth.krb5ConfFile)
if err != nil {
return []byte{}, err
}
c, err := config.NewConfigFromReader(krb5CnfFile)
if err != nil {
return []byte{}, err
}

// Set to lookup KDCs in DNS
c.LibDefaults.DNSLookupKDC = false

var cl *client.Client
// Init keytab from conf
if auth.initkrbwithkeytab {
keytabConf, err := ioutil.ReadFile(auth.krbFile)
if err != nil {
return []byte{}, err
}
if err = ktUnmarshal([]byte(keytabConf)); err != nil {
return []byte{}, err
}

// Init krb5 client and login
cl = clientWithKeytab(auth.username, auth.realm, kt, c, client.DisablePAFXFAST(true))

} else {
cache, err := loadCCache(auth.krbFile)
if err != nil {
return []byte{}, err
}

cl, err = clientFromCCache(cache, c)
if err != nil {
return []byte{}, err
}
}

auth.krb5Client = cl
auth.state = InitiatorStart

tkt, sessionKey, err := getServiceTicket(cl, auth.serverSPN)
if err != nil {
return []byte{}, err
}

negTok, err := spnegoNewNegToken(auth.krb5Client, tkt, sessionKey)
if err != nil {
return []byte{}, err
}

outToken, err := negTokenMarshal(negTok)
if err != nil {
return []byte{}, err
}
auth.state = InitiatorWaitForMutal
return outToken, nil
}

func (auth *krb5Auth) Free() {
auth.krb5Client.Destroy()
}

func (auth *krb5Auth) NextBytes(token []byte) ([]byte, error) {
if err := spnegoUnmarshal(token); err != nil {
err := fmt.Errorf("unmarshal APRep token failed: %w", err)
return []byte{}, err
}
auth.state = InitiatorReady
return []byte{}, nil
}