Last active
March 9, 2023 18:01
-
-
Save kingluo/7adbe1f27c3b952592d1aa89120a2f52 to your computer and use it in GitHub Desktop.
go-ldap GSSAPI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
@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
@kingluo Thank you very much.