Skip to content

Instantly share code, notes, and snippets.

@kingluo
Last active March 9, 2023 18:01
Show Gist options
  • Save kingluo/7adbe1f27c3b952592d1aa89120a2f52 to your computer and use it in GitHub Desktop.
Save kingluo/7adbe1f27c3b952592d1aa89120a2f52 to your computer and use it in GitHub Desktop.
go-ldap GSSAPI
module github.com/kingluo/foobar
go 1.19
require (
github.com/go-ldap/ldap/v3 v3.4.5-0.20230226130424-b64a808c288f
gopkg.in/jcmturner/gokrb5.v7 v7.5.0
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/jcmturner/gofork v1.7.6 // indirect
github.com/stretchr/testify v1.8.1 // indirect
golang.org/x/crypto v0.6.0 // indirect
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/rpc.v1 v1.1.0 // indirect
)
package main
import (
"fmt"
"log"
"gopkg.in/jcmturner/gokrb5.v7/client"
"gopkg.in/jcmturner/gokrb5.v7/config"
"gopkg.in/jcmturner/gokrb5.v7/crypto"
"gopkg.in/jcmturner/gokrb5.v7/gssapi"
"gopkg.in/jcmturner/gokrb5.v7/iana/flags"
"gopkg.in/jcmturner/gokrb5.v7/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v7/messages"
"gopkg.in/jcmturner/gokrb5.v7/spnego"
"gopkg.in/jcmturner/gokrb5.v7/types"
ldap "github.com/go-ldap/ldap/v3"
)
type GSSAPIState struct {
token spnego.KRB5Token
ekey types.EncryptionKey
Subkey types.EncryptionKey
asrep bool
}
func (state *GSSAPIState) DeleteSecContext() error {
return nil
}
func (state *GSSAPIState) InitSecContext(target string, _ []byte) (outputToken []byte, needContinue bool, err error) {
cfg, err := config.Load("/opt/bonsai/.ci/krb5/krb5.conf")
if err != nil {
log.Panic(err)
}
cl := client.NewClientWithPassword("chuck", "BONSAI.TEST", "xxx", cfg, client.DisablePAFXFAST(true))
err = cl.Login()
if err != nil {
log.Panic(err)
}
tkt, key, err := cl.GetServiceTicket("ldap/windows.bonsai.test")
if err != nil {
log.Panic(err)
}
token, err := spnego.NewKRB5TokenAPREQ(cl, tkt, key, []int{gssapi.ContextFlagInteg, gssapi.ContextFlagConf, gssapi.ContextFlagMutual}, []int{flags.APOptionMutualRequired})
if err != nil {
log.Panic(err)
}
state.ekey = key
state.token = token
outputToken, err = token.Marshal()
if err != nil {
log.Panic(err)
}
needContinue = false
return
}
func (state *GSSAPIState) NegotiateSaslAuth(input []byte, authzid string) ([]byte, error) {
if !state.asrep {
err := state.token.Unmarshal(input)
if err != nil {
return nil, err
}
if state.token.IsAPRep() {
state.asrep = true
encpart, err := crypto.DecryptEncPart(state.token.APRep.EncPart, state.ekey, keyusage.AP_REP_ENCPART)
if err != nil {
return nil, err
}
part := &messages.EncAPRepPart{}
err = part.Unmarshal(encpart)
if err != nil {
return nil, err
}
state.Subkey = part.Subkey
}
if state.token.IsKRBError() {
return nil, state.token.KRBError
}
return make([]byte, 0), nil
}
token := &gssapi.WrapToken{}
err := token.Unmarshal(input, true)
if err != nil {
return nil, err
}
if (token.Flags & 0b1) == 0 {
return nil, fmt.Errorf("Got a Wrapped token that's not from the server")
}
key := state.ekey
if (token.Flags & 0b100) != 0 {
key = state.Subkey
}
if (token.Flags & 0b10) != 0 {
_, err = token.Verify(key, keyusage.GSSAPI_ACCEPTOR_SEAL)
if err != nil {
return nil, err
}
}
pl := token.Payload
if len(pl) != 4 {
return nil, fmt.Errorf("Server send bad final token for SASL GSSAPI Handshake")
}
// We never want a security layer
b := [4]byte{0, 0, 0, 0}
payload := append(b[:], []byte(authzid)...)
encType, err := crypto.GetEtype(key.KeyType)
if err != nil {
return nil, err
}
token = &gssapi.WrapToken{
Flags: 0b100,
EC: uint16(encType.GetHMACBitLength() / 8),
RRC: 0,
SndSeqNum: 1,
Payload: payload,
}
if err := token.SetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil {
return nil, err
}
return token.Marshal()
}
func main() {
l, err := ldap.DialURL("ldap://bonsai.test")
if err != nil {
log.Fatal(err)
}
defer l.Close()
cli := &GSSAPIState{}
err = l.GSSAPIBind(cli, "ldap/windows.bonsai.test", "")
if err != nil {
log.Fatal(err)
}
searchRequest := ldap.NewSearchRequest(
"ou=xxx,dc=bonsai,dc=test",
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
"(objectclass=*)",
[]string{},
nil,
)
sr, err := l.Search(searchRequest)
if err != nil {
log.Fatal(err)
}
sr.PrettyPrint(2)
}
@kingluo
Copy link
Author

kingluo commented Mar 9, 2023

@Denis-shl Here is my lua library, which encapsulates bonsai.
https://github.com/kingluo/lua-resty-ffi-ldap

If you're interested, please have a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment