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 1 commit
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=
180 changes: 180 additions & 0 deletions kerbauth.go
@@ -0,0 +1,180 @@
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"
"log"
"os"
"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 Krb5ClientState int
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved

const (
ContextFlagREADY = 128
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
/* initiator states */
InitiatorStart Krb5ClientState = iota
InitiatorRestart
InitiatorWaitForMutal
InitiatorReady
)

type krb5Auth struct {
username string
realm string
service string
password string
port string
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
krb5ConfFile string
krbFile string
initkrbwithkeytab string
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
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, service, krb5Conf, krbFile, initkrbwithkeytab, password string) (auth, bool) {
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
if krb5Conf == "" {
krb5Conf = "/etc/krb5.conf"
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
}
var port string
var realm string
var serviceStr string

params1 := strings.Split(service, ":")
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
if len(params1) != 2 {
return nil, false
}

params2 := strings.Split(params1[1], "@")
if len(params2) == 1 {
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
port = params1[1]
} else if len(params2) == 2 {
port = params2[0]
} else if len(params2) != 2 {
return nil, false
}

params3 := strings.Split(service, "@")
if len(params3) == 1 {
serviceStr = params3[0]
params3 = strings.Split(params1[0], "/")
params3 = strings.Split(params3[1], ".")
realm = params3[1] + "." + params3[2]
} else if len(params3) == 2 {
realm = params3[1]
serviceStr = params3[0]
}

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

}

func (auth *krb5Auth) InitialBytes() ([]byte, error) {

chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
krb5CnfFile, _ := os.Open(auth.krb5ConfFile)
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
c, _ := config.NewConfigFromReader(krb5CnfFile)
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved

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

var err error
var cl *client.Client
// Init keytab from conf
if auth.initkrbwithkeytab == "true" {
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved

chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
keytabConf, err := ioutil.ReadFile(auth.krbFile)
if err != nil {
return []byte{}, err
}
if err = ktUnmarshal([]byte(keytabConf)); err != nil {
log.Printf("unmarshal keytabConf failed: %v", err)
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
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 {
log.Println(err)
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
return []byte{}, err
}

cl, err = clientFromCCache(cache, c)
if err != nil {
log.Println(err)
chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
return []byte{}, err
}
}

auth.krb5Client = cl
auth.state = InitiatorStart

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

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

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

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

func (auth *krb5Auth) NextBytes(token []byte) ([]byte, error) {

chandanjainn marked this conversation as resolved.
Show resolved Hide resolved
if err := spnegoUnmarshal(token); err != nil {
err := fmt.Errorf("unmarshal APRep token failed: %w", err)
return []byte{}, err
}

auth.state = InitiatorReady
return []byte{}, nil
}