diff --git a/go.mod b/go.mod index aa54ec44e..c99e2604d 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/go-enry/go-enry/v2 v2.5.2 github.com/go-git/go-billy/v5 v5.0.0 github.com/go-git/go-git/v5 v5.1.0 + github.com/go-ldap/ldap/v3 v3.2.4 github.com/go-redis/redis/v7 v7.4.0 github.com/go-sql-driver/mysql v1.5.0 github.com/go-swagger/go-swagger v0.25.0 @@ -112,10 +113,8 @@ require ( golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect golang.org/x/tools v0.0.0-20200921210052-fa0125251cc4 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect - gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.61.0 - gopkg.in/ldap.v3 v3.0.2 gopkg.in/yaml.v2 v2.3.0 mvdan.cc/xurls/v2 v2.1.0 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 diff --git a/go.sum b/go.sum index 4e21f5458..0b5609112 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGq gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= github.com/6543/go-version v1.2.3 h1:uF30BawMhoQLzqBeCwhFcWM6HVxlzMHe/zXbzJeKP+o= github.com/6543/go-version v1.2.3/go.mod h1:fcfWh4zkneEgGXe8JJptiGwp8l6JgJJgS7oTw6P83So= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -242,6 +244,8 @@ github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a h1:FQqo github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= +github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-enry/go-enry/v2 v2.5.2 h1:3f3PFAO6JitWkPi1GQ5/m6Xu4gNL1U5soJ8QaYqJ0YQ= github.com/go-enry/go-enry/v2 v2.5.2/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ= github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= @@ -256,6 +260,8 @@ github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEss github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap/v3 v3.2.4 h1:PFavAq2xTgzo/loE8qNXcQaofAaqIpI4WgaLdv+1l3E= +github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= @@ -934,6 +940,7 @@ golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= @@ -1148,8 +1155,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= -gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8KqxhFwKci30UxHy3HXPTyQ= -gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1170,8 +1175,6 @@ gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.60.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.61.0 h1:LBCdW4FmFYL4s/vDZD1RQYX7oAR6IjujCYgMdbHBR10= gopkg.in/ini.v1 v1.61.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w= -gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/modules/auth/ldap/ldap.go b/modules/auth/ldap/ldap.go index 7649639d3..6c557de01 100644 --- a/modules/auth/ldap/ldap.go +++ b/modules/auth/ldap/ldap.go @@ -14,7 +14,7 @@ import ( "code.gitea.io/gitea/modules/log" - "gopkg.in/ldap.v3" + "github.com/go-ldap/ldap/v3" ) // SecurityProtocol protocol type diff --git a/vendor/github.com/Azure/go-ntlmssp/.travis.yml b/vendor/github.com/Azure/go-ntlmssp/.travis.yml new file mode 100644 index 000000000..23c95fe95 --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/.travis.yml @@ -0,0 +1,17 @@ +sudo: false + +language: go + +before_script: + - go get -u golang.org/x/lint/golint + +go: + - 1.10.x + - master + +script: + - test -z "$(gofmt -s -l . | tee /dev/stderr)" + - test -z "$(golint ./... | tee /dev/stderr)" + - go vet ./... + - go build -v ./... + - go test -v ./... diff --git a/vendor/github.com/Azure/go-ntlmssp/LICENSE b/vendor/github.com/Azure/go-ntlmssp/LICENSE new file mode 100644 index 000000000..dc1cf39d1 --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/Azure/go-ntlmssp/README.md b/vendor/github.com/Azure/go-ntlmssp/README.md new file mode 100644 index 000000000..55cdcefab --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/README.md @@ -0,0 +1,29 @@ +# go-ntlmssp +Golang package that provides NTLM/Negotiate authentication over HTTP + +[![GoDoc](https://godoc.org/github.com/Azure/go-ntlmssp?status.svg)](https://godoc.org/github.com/Azure/go-ntlmssp) [![Build Status](https://travis-ci.org/Azure/go-ntlmssp.svg?branch=dev)](https://travis-ci.org/Azure/go-ntlmssp) + +Protocol details from https://msdn.microsoft.com/en-us/library/cc236621.aspx +Implementation hints from http://davenport.sourceforge.net/ntlm.html + +This package only implements authentication, no key exchange or encryption. It +only supports Unicode (UTF16LE) encoding of protocol strings, no OEM encoding. +This package implements NTLMv2. + +# Usage + +``` +url, user, password := "http://www.example.com/secrets", "robpike", "pw123" +client := &http.Client{ + Transport: ntlmssp.Negotiator{ + RoundTripper:&http.Transport{}, + }, +} + +req, _ := http.NewRequest("GET", url, nil) +req.SetBasicAuth(user, password) +res, _ := client.Do(req) +``` + +----- +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go b/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go new file mode 100644 index 000000000..c8930680c --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go @@ -0,0 +1,183 @@ +package ntlmssp + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "encoding/hex" + "errors" + "strings" + "time" +) + +type authenicateMessage struct { + LmChallengeResponse []byte + NtChallengeResponse []byte + + TargetName string + UserName string + + // only set if negotiateFlag_NTLMSSP_NEGOTIATE_KEY_EXCH + EncryptedRandomSessionKey []byte + + NegotiateFlags negotiateFlags + + MIC []byte +} + +type authenticateMessageFields struct { + messageHeader + LmChallengeResponse varField + NtChallengeResponse varField + TargetName varField + UserName varField + Workstation varField + _ [8]byte + NegotiateFlags negotiateFlags +} + +func (m authenicateMessage) MarshalBinary() ([]byte, error) { + if !m.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEUNICODE) { + return nil, errors.New("Only unicode is supported") + } + + target, user := toUnicode(m.TargetName), toUnicode(m.UserName) + workstation := toUnicode("go-ntlmssp") + + ptr := binary.Size(&authenticateMessageFields{}) + f := authenticateMessageFields{ + messageHeader: newMessageHeader(3), + NegotiateFlags: m.NegotiateFlags, + LmChallengeResponse: newVarField(&ptr, len(m.LmChallengeResponse)), + NtChallengeResponse: newVarField(&ptr, len(m.NtChallengeResponse)), + TargetName: newVarField(&ptr, len(target)), + UserName: newVarField(&ptr, len(user)), + Workstation: newVarField(&ptr, len(workstation)), + } + + f.NegotiateFlags.Unset(negotiateFlagNTLMSSPNEGOTIATEVERSION) + + b := bytes.Buffer{} + if err := binary.Write(&b, binary.LittleEndian, &f); err != nil { + return nil, err + } + if err := binary.Write(&b, binary.LittleEndian, &m.LmChallengeResponse); err != nil { + return nil, err + } + if err := binary.Write(&b, binary.LittleEndian, &m.NtChallengeResponse); err != nil { + return nil, err + } + if err := binary.Write(&b, binary.LittleEndian, &target); err != nil { + return nil, err + } + if err := binary.Write(&b, binary.LittleEndian, &user); err != nil { + return nil, err + } + if err := binary.Write(&b, binary.LittleEndian, &workstation); err != nil { + return nil, err + } + + return b.Bytes(), nil +} + +//ProcessChallenge crafts an AUTHENTICATE message in response to the CHALLENGE message +//that was received from the server +func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byte, error) { + if user == "" && password == "" { + return nil, errors.New("Anonymous authentication not supported") + } + + var cm challengeMessage + if err := cm.UnmarshalBinary(challengeMessageData); err != nil { + return nil, err + } + + if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) { + return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)") + } + if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) { + return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)") + } + + am := authenicateMessage{ + UserName: user, + TargetName: cm.TargetName, + NegotiateFlags: cm.NegotiateFlags, + } + + timestamp := cm.TargetInfo[avIDMsvAvTimestamp] + if timestamp == nil { // no time sent, take current time + ft := uint64(time.Now().UnixNano()) / 100 + ft += 116444736000000000 // add time between unix & windows offset + timestamp = make([]byte, 8) + binary.LittleEndian.PutUint64(timestamp, ft) + } + + clientChallenge := make([]byte, 8) + rand.Reader.Read(clientChallenge) + + ntlmV2Hash := getNtlmV2Hash(password, user, cm.TargetName) + + am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash, + cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw) + + if cm.TargetInfoRaw == nil { + am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash, + cm.ServerChallenge[:], clientChallenge) + } + return am.MarshalBinary() +} + +func ProcessChallengeWithHash(challengeMessageData []byte, user, hash string) ([]byte, error) { + if user == "" && hash == "" { + return nil, errors.New("Anonymous authentication not supported") + } + + var cm challengeMessage + if err := cm.UnmarshalBinary(challengeMessageData); err != nil { + return nil, err + } + + if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) { + return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)") + } + if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) { + return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)") + } + + am := authenicateMessage{ + UserName: user, + TargetName: cm.TargetName, + NegotiateFlags: cm.NegotiateFlags, + } + + timestamp := cm.TargetInfo[avIDMsvAvTimestamp] + if timestamp == nil { // no time sent, take current time + ft := uint64(time.Now().UnixNano()) / 100 + ft += 116444736000000000 // add time between unix & windows offset + timestamp = make([]byte, 8) + binary.LittleEndian.PutUint64(timestamp, ft) + } + + clientChallenge := make([]byte, 8) + rand.Reader.Read(clientChallenge) + + hashParts := strings.Split(hash, ":") + if len(hashParts) > 1 { + hash = hashParts[1] + } + hashBytes, err := hex.DecodeString(hash) + if err != nil { + return nil, err + } + ntlmV2Hash := hmacMd5(hashBytes, toUnicode(strings.ToUpper(user)+cm.TargetName)) + + am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash, + cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw) + + if cm.TargetInfoRaw == nil { + am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash, + cm.ServerChallenge[:], clientChallenge) + } + return am.MarshalBinary() +} diff --git a/vendor/github.com/Azure/go-ntlmssp/authheader.go b/vendor/github.com/Azure/go-ntlmssp/authheader.go new file mode 100644 index 000000000..aac3f77d1 --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/authheader.go @@ -0,0 +1,37 @@ +package ntlmssp + +import ( + "encoding/base64" + "strings" +) + +type authheader string + +func (h authheader) IsBasic() bool { + return strings.HasPrefix(string(h), "Basic ") +} + +func (h authheader) IsNegotiate() bool { + return strings.HasPrefix(string(h), "Negotiate") +} + +func (h authheader) IsNTLM() bool { + return strings.HasPrefix(string(h), "NTLM") +} + +func (h authheader) GetData() ([]byte, error) { + p := strings.Split(string(h), " ") + if len(p) < 2 { + return nil, nil + } + return base64.StdEncoding.DecodeString(string(p[1])) +} + +func (h authheader) GetBasicCreds() (username, password string, err error) { + d, err := h.GetData() + if err != nil { + return "", "", err + } + parts := strings.SplitN(string(d), ":", 2) + return parts[0], parts[1], nil +} diff --git a/vendor/github.com/Azure/go-ntlmssp/avids.go b/vendor/github.com/Azure/go-ntlmssp/avids.go new file mode 100644 index 000000000..196b5f131 --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/avids.go @@ -0,0 +1,17 @@ +package ntlmssp + +type avID uint16 + +const ( + avIDMsvAvEOL avID = iota + avIDMsvAvNbComputerName + avIDMsvAvNbDomainName + avIDMsvAvDNSComputerName + avIDMsvAvDNSDomainName + avIDMsvAvDNSTreeName + avIDMsvAvFlags + avIDMsvAvTimestamp + avIDMsvAvSingleHost + avIDMsvAvTargetName + avIDMsvChannelBindings +) diff --git a/vendor/github.com/Azure/go-ntlmssp/challenge_message.go b/vendor/github.com/Azure/go-ntlmssp/challenge_message.go new file mode 100644 index 000000000..053b55e4a --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/challenge_message.go @@ -0,0 +1,82 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +type challengeMessageFields struct { + messageHeader + TargetName varField + NegotiateFlags negotiateFlags + ServerChallenge [8]byte + _ [8]byte + TargetInfo varField +} + +func (m challengeMessageFields) IsValid() bool { + return m.messageHeader.IsValid() && m.MessageType == 2 +} + +type challengeMessage struct { + challengeMessageFields + TargetName string + TargetInfo map[avID][]byte + TargetInfoRaw []byte +} + +func (m *challengeMessage) UnmarshalBinary(data []byte) error { + r := bytes.NewReader(data) + err := binary.Read(r, binary.LittleEndian, &m.challengeMessageFields) + if err != nil { + return err + } + if !m.challengeMessageFields.IsValid() { + return fmt.Errorf("Message is not a valid challenge message: %+v", m.challengeMessageFields.messageHeader) + } + + if m.challengeMessageFields.TargetName.Len > 0 { + m.TargetName, err = m.challengeMessageFields.TargetName.ReadStringFrom(data, m.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEUNICODE)) + if err != nil { + return err + } + } + + if m.challengeMessageFields.TargetInfo.Len > 0 { + d, err := m.challengeMessageFields.TargetInfo.ReadFrom(data) + m.TargetInfoRaw = d + if err != nil { + return err + } + m.TargetInfo = make(map[avID][]byte) + r := bytes.NewReader(d) + for { + var id avID + var l uint16 + err = binary.Read(r, binary.LittleEndian, &id) + if err != nil { + return err + } + if id == avIDMsvAvEOL { + break + } + + err = binary.Read(r, binary.LittleEndian, &l) + if err != nil { + return err + } + value := make([]byte, l) + n, err := r.Read(value) + if err != nil { + return err + } + if n != int(l) { + return fmt.Errorf("Expected to read %d bytes, got only %d", l, n) + } + m.TargetInfo[id] = value + } + } + + return nil +} diff --git a/vendor/github.com/Azure/go-ntlmssp/messageheader.go b/vendor/github.com/Azure/go-ntlmssp/messageheader.go new file mode 100644 index 000000000..247e28465 --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/messageheader.go @@ -0,0 +1,21 @@ +package ntlmssp + +import ( + "bytes" +) + +var signature = [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0} + +type messageHeader struct { + Signature [8]byte + MessageType uint32 +} + +func (h messageHeader) IsValid() bool { + return bytes.Equal(h.Signature[:], signature[:]) && + h.MessageType > 0 && h.MessageType < 4 +} + +func newMessageHeader(messageType uint32) messageHeader { + return messageHeader{signature, messageType} +} diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go b/vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go new file mode 100644 index 000000000..5905c023d --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go @@ -0,0 +1,52 @@ +package ntlmssp + +type negotiateFlags uint32 + +const ( + /*A*/ negotiateFlagNTLMSSPNEGOTIATEUNICODE negotiateFlags = 1 << 0 + /*B*/ negotiateFlagNTLMNEGOTIATEOEM = 1 << 1 + /*C*/ negotiateFlagNTLMSSPREQUESTTARGET = 1 << 2 + + /*D*/ + negotiateFlagNTLMSSPNEGOTIATESIGN = 1 << 4 + /*E*/ negotiateFlagNTLMSSPNEGOTIATESEAL = 1 << 5 + /*F*/ negotiateFlagNTLMSSPNEGOTIATEDATAGRAM = 1 << 6 + /*G*/ negotiateFlagNTLMSSPNEGOTIATELMKEY = 1 << 7 + + /*H*/ + negotiateFlagNTLMSSPNEGOTIATENTLM = 1 << 9 + + /*J*/ + negotiateFlagANONYMOUS = 1 << 11 + /*K*/ negotiateFlagNTLMSSPNEGOTIATEOEMDOMAINSUPPLIED = 1 << 12 + /*L*/ negotiateFlagNTLMSSPNEGOTIATEOEMWORKSTATIONSUPPLIED = 1 << 13 + + /*M*/ + negotiateFlagNTLMSSPNEGOTIATEALWAYSSIGN = 1 << 15 + /*N*/ negotiateFlagNTLMSSPTARGETTYPEDOMAIN = 1 << 16 + /*O*/ negotiateFlagNTLMSSPTARGETTYPESERVER = 1 << 17 + + /*P*/ + negotiateFlagNTLMSSPNEGOTIATEEXTENDEDSESSIONSECURITY = 1 << 19 + /*Q*/ negotiateFlagNTLMSSPNEGOTIATEIDENTIFY = 1 << 20 + + /*R*/ + negotiateFlagNTLMSSPREQUESTNONNTSESSIONKEY = 1 << 22 + /*S*/ negotiateFlagNTLMSSPNEGOTIATETARGETINFO = 1 << 23 + + /*T*/ + negotiateFlagNTLMSSPNEGOTIATEVERSION = 1 << 25 + + /*U*/ + negotiateFlagNTLMSSPNEGOTIATE128 = 1 << 29 + /*V*/ negotiateFlagNTLMSSPNEGOTIATEKEYEXCH = 1 << 30 + /*W*/ negotiateFlagNTLMSSPNEGOTIATE56 = 1 << 31 +) + +func (field negotiateFlags) Has(flags negotiateFlags) bool { + return field&flags == flags +} + +func (field *negotiateFlags) Unset(flags negotiateFlags) { + *field = *field ^ (*field & flags) +} diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiate_message.go b/vendor/github.com/Azure/go-ntlmssp/negotiate_message.go new file mode 100644 index 000000000..e466a9861 --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/negotiate_message.go @@ -0,0 +1,64 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" + "errors" + "strings" +) + +const expMsgBodyLen = 40 + +type negotiateMessageFields struct { + messageHeader + NegotiateFlags negotiateFlags + + Domain varField + Workstation varField + + Version +} + +var defaultFlags = negotiateFlagNTLMSSPNEGOTIATETARGETINFO | + negotiateFlagNTLMSSPNEGOTIATE56 | + negotiateFlagNTLMSSPNEGOTIATE128 | + negotiateFlagNTLMSSPNEGOTIATEUNICODE | + negotiateFlagNTLMSSPNEGOTIATEEXTENDEDSESSIONSECURITY + +//NewNegotiateMessage creates a new NEGOTIATE message with the +//flags that this package supports. +func NewNegotiateMessage(domainName, workstationName string) ([]byte, error) { + payloadOffset := expMsgBodyLen + flags := defaultFlags + + if domainName != "" { + flags |= negotiateFlagNTLMSSPNEGOTIATEOEMDOMAINSUPPLIED + } + + if workstationName != "" { + flags |= negotiateFlagNTLMSSPNEGOTIATEOEMWORKSTATIONSUPPLIED + } + + msg := negotiateMessageFields{ + messageHeader: newMessageHeader(1), + NegotiateFlags: flags, + Domain: newVarField(&payloadOffset, len(domainName)), + Workstation: newVarField(&payloadOffset, len(workstationName)), + Version: DefaultVersion(), + } + + b := bytes.Buffer{} + if err := binary.Write(&b, binary.LittleEndian, &msg); err != nil { + return nil, err + } + if b.Len() != expMsgBodyLen { + return nil, errors.New("incorrect body length") + } + + payload := strings.ToUpper(domainName + workstationName) + if _, err := b.WriteString(payload); err != nil { + return nil, err + } + + return b.Bytes(), nil +} diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiator.go b/vendor/github.com/Azure/go-ntlmssp/negotiator.go new file mode 100644 index 000000000..7705eae4f --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/negotiator.go @@ -0,0 +1,144 @@ +package ntlmssp + +import ( + "bytes" + "encoding/base64" + "io" + "io/ioutil" + "net/http" + "strings" +) + +// GetDomain : parse domain name from based on slashes in the input +func GetDomain(user string) (string, string) { + domain := "" + + if strings.Contains(user, "\\") { + ucomponents := strings.SplitN(user, "\\", 2) + domain = ucomponents[0] + user = ucomponents[1] + } + return user, domain +} + +//Negotiator is a http.Roundtripper decorator that automatically +//converts basic authentication to NTLM/Negotiate authentication when appropriate. +type Negotiator struct{ http.RoundTripper } + +//RoundTrip sends the request to the server, handling any authentication +//re-sends as needed. +func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) { + // Use default round tripper if not provided + rt := l.RoundTripper + if rt == nil { + rt = http.DefaultTransport + } + // If it is not basic auth, just round trip the request as usual + reqauth := authheader(req.Header.Get("Authorization")) + if !reqauth.IsBasic() { + return rt.RoundTrip(req) + } + // Save request body + body := bytes.Buffer{} + if req.Body != nil { + _, err = body.ReadFrom(req.Body) + if err != nil { + return nil, err + } + + req.Body.Close() + req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes())) + } + // first try anonymous, in case the server still finds us + // authenticated from previous traffic + req.Header.Del("Authorization") + res, err = rt.RoundTrip(req) + if err != nil { + return nil, err + } + if res.StatusCode != http.StatusUnauthorized { + return res, err + } + + resauth := authheader(res.Header.Get("Www-Authenticate")) + if !resauth.IsNegotiate() && !resauth.IsNTLM() { + // Unauthorized, Negotiate not requested, let's try with basic auth + req.Header.Set("Authorization", string(reqauth)) + io.Copy(ioutil.Discard, res.Body) + res.Body.Close() + req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes())) + + res, err = rt.RoundTrip(req) + if err != nil { + return nil, err + } + if res.StatusCode != http.StatusUnauthorized { + return res, err + } + resauth = authheader(res.Header.Get("Www-Authenticate")) + } + + if resauth.IsNegotiate() || resauth.IsNTLM() { + // 401 with request:Basic and response:Negotiate + io.Copy(ioutil.Discard, res.Body) + res.Body.Close() + + // recycle credentials + u, p, err := reqauth.GetBasicCreds() + if err != nil { + return nil, err + } + + // get domain from username + domain := "" + u, domain = GetDomain(u) + + // send negotiate + negotiateMessage, err := NewNegotiateMessage(domain, "") + if err != nil { + return nil, err + } + if resauth.IsNTLM() { + req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(negotiateMessage)) + } else { + req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(negotiateMessage)) + } + + req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes())) + + res, err = rt.RoundTrip(req) + if err != nil { + return nil, err + } + + // receive challenge? + resauth = authheader(res.Header.Get("Www-Authenticate")) + challengeMessage, err := resauth.GetData() + if err != nil { + return nil, err + } + if !(resauth.IsNegotiate() || resauth.IsNTLM()) || len(challengeMessage) == 0 { + // Negotiation failed, let client deal with response + return res, nil + } + io.Copy(ioutil.Discard, res.Body) + res.Body.Close() + + // send authenticate + authenticateMessage, err := ProcessChallenge(challengeMessage, u, p) + if err != nil { + return nil, err + } + if resauth.IsNTLM() { + req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(authenticateMessage)) + } else { + req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(authenticateMessage)) + } + + req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes())) + + return rt.RoundTrip(req) + } + + return res, err +} diff --git a/vendor/github.com/Azure/go-ntlmssp/nlmp.go b/vendor/github.com/Azure/go-ntlmssp/nlmp.go new file mode 100644 index 000000000..1e65abe8b --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/nlmp.go @@ -0,0 +1,51 @@ +// Package ntlmssp provides NTLM/Negotiate authentication over HTTP +// +// Protocol details from https://msdn.microsoft.com/en-us/library/cc236621.aspx, +// implementation hints from http://davenport.sourceforge.net/ntlm.html . +// This package only implements authentication, no key exchange or encryption. It +// only supports Unicode (UTF16LE) encoding of protocol strings, no OEM encoding. +// This package implements NTLMv2. +package ntlmssp + +import ( + "crypto/hmac" + "crypto/md5" + "golang.org/x/crypto/md4" + "strings" +) + +func getNtlmV2Hash(password, username, target string) []byte { + return hmacMd5(getNtlmHash(password), toUnicode(strings.ToUpper(username)+target)) +} + +func getNtlmHash(password string) []byte { + hash := md4.New() + hash.Write(toUnicode(password)) + return hash.Sum(nil) +} + +func computeNtlmV2Response(ntlmV2Hash, serverChallenge, clientChallenge, + timestamp, targetInfo []byte) []byte { + + temp := []byte{1, 1, 0, 0, 0, 0, 0, 0} + temp = append(temp, timestamp...) + temp = append(temp, clientChallenge...) + temp = append(temp, 0, 0, 0, 0) + temp = append(temp, targetInfo...) + temp = append(temp, 0, 0, 0, 0) + + NTProofStr := hmacMd5(ntlmV2Hash, serverChallenge, temp) + return append(NTProofStr, temp...) +} + +func computeLmV2Response(ntlmV2Hash, serverChallenge, clientChallenge []byte) []byte { + return append(hmacMd5(ntlmV2Hash, serverChallenge, clientChallenge), clientChallenge...) +} + +func hmacMd5(key []byte, data ...[]byte) []byte { + mac := hmac.New(md5.New, key) + for _, d := range data { + mac.Write(d) + } + return mac.Sum(nil) +} diff --git a/vendor/github.com/Azure/go-ntlmssp/unicode.go b/vendor/github.com/Azure/go-ntlmssp/unicode.go new file mode 100644 index 000000000..7b4f47163 --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/unicode.go @@ -0,0 +1,29 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" + "errors" + "unicode/utf16" +) + +// helper func's for dealing with Windows Unicode (UTF16LE) + +func fromUnicode(d []byte) (string, error) { + if len(d)%2 > 0 { + return "", errors.New("Unicode (UTF 16 LE) specified, but uneven data length") + } + s := make([]uint16, len(d)/2) + err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &s) + if err != nil { + return "", err + } + return string(utf16.Decode(s)), nil +} + +func toUnicode(s string) []byte { + uints := utf16.Encode([]rune(s)) + b := bytes.Buffer{} + binary.Write(&b, binary.LittleEndian, &uints) + return b.Bytes() +} diff --git a/vendor/github.com/Azure/go-ntlmssp/varfield.go b/vendor/github.com/Azure/go-ntlmssp/varfield.go new file mode 100644 index 000000000..15f9aa113 --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/varfield.go @@ -0,0 +1,40 @@ +package ntlmssp + +import ( + "errors" +) + +type varField struct { + Len uint16 + MaxLen uint16 + BufferOffset uint32 +} + +func (f varField) ReadFrom(buffer []byte) ([]byte, error) { + if len(buffer) < int(f.BufferOffset+uint32(f.Len)) { + return nil, errors.New("Error reading data, varField extends beyond buffer") + } + return buffer[f.BufferOffset : f.BufferOffset+uint32(f.Len)], nil +} + +func (f varField) ReadStringFrom(buffer []byte, unicode bool) (string, error) { + d, err := f.ReadFrom(buffer) + if err != nil { + return "", err + } + if unicode { // UTF-16LE encoding scheme + return fromUnicode(d) + } + // OEM encoding, close enough to ASCII, since no code page is specified + return string(d), err +} + +func newVarField(ptr *int, fieldsize int) varField { + f := varField{ + Len: uint16(fieldsize), + MaxLen: uint16(fieldsize), + BufferOffset: uint32(*ptr), + } + *ptr += fieldsize + return f +} diff --git a/vendor/github.com/Azure/go-ntlmssp/version.go b/vendor/github.com/Azure/go-ntlmssp/version.go new file mode 100644 index 000000000..6d8489212 --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/version.go @@ -0,0 +1,20 @@ +package ntlmssp + +// Version is a struct representing https://msdn.microsoft.com/en-us/library/cc236654.aspx +type Version struct { + ProductMajorVersion uint8 + ProductMinorVersion uint8 + ProductBuild uint16 + _ [3]byte + NTLMRevisionCurrent uint8 +} + +// DefaultVersion returns a Version with "sensible" defaults (Windows 7) +func DefaultVersion() Version { + return Version{ + ProductMajorVersion: 6, + ProductMinorVersion: 1, + ProductBuild: 7601, + NTLMRevisionCurrent: 15, + } +} diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml b/vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml new file mode 100644 index 000000000..8bffb9017 --- /dev/null +++ b/vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml @@ -0,0 +1,39 @@ +language: go + +go: + - 1.2.x + - 1.6.x + - 1.9.x + - 1.10.x + - 1.11.x + - 1.12.x + - 1.14.x + - tip + +os: + - linux + +arch: + - amd64 + +dist: xenial + +env: + - GOARCH=amd64 + +jobs: + include: + - os: windows + go: 1.14.x + - os: osx + go: 1.14.x + - os: linux + go: 1.14.x + arch: arm64 + - os: linux + go: 1.14.x + env: + - GOARCH=386 + +script: + - go test -v -cover ./... || go test -v ./... diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/LICENSE b/vendor/github.com/go-asn1-ber/asn1-ber/LICENSE new file mode 100644 index 000000000..23f942534 --- /dev/null +++ b/vendor/github.com/go-asn1-ber/asn1-ber/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com) +Portions copyright (c) 2015-2016 go-asn1-ber Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/gopkg.in/asn1-ber.v1/README.md b/vendor/github.com/go-asn1-ber/asn1-ber/README.md similarity index 100% rename from vendor/gopkg.in/asn1-ber.v1/README.md rename to vendor/github.com/go-asn1-ber/asn1-ber/README.md diff --git a/vendor/gopkg.in/asn1-ber.v1/ber.go b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go similarity index 66% rename from vendor/gopkg.in/asn1-ber.v1/ber.go rename to vendor/github.com/go-asn1-ber/asn1-ber/ber.go index 25cc921be..4fd7a66e1 100644 --- a/vendor/gopkg.in/asn1-ber.v1/ber.go +++ b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go @@ -5,10 +5,17 @@ import ( "errors" "fmt" "io" + "math" "os" "reflect" + "time" + "unicode/utf8" ) +// MaxPacketLengthBytes specifies the maximum allowed packet size when calling ReadPacket or DecodePacket. Set to 0 for +// no limit. +var MaxPacketLengthBytes int64 = math.MaxInt32 + type Packet struct { Identifier Value interface{} @@ -138,42 +145,46 @@ var TypeMap = map[Type]string{ TypeConstructed: "Constructed", } -var Debug bool = false +var Debug = false func PrintBytes(out io.Writer, buf []byte, indent string) { - data_lines := make([]string, (len(buf)/30)+1) - num_lines := make([]string, (len(buf)/30)+1) + dataLines := make([]string, (len(buf)/30)+1) + numLines := make([]string, (len(buf)/30)+1) for i, b := range buf { - data_lines[i/30] += fmt.Sprintf("%02x ", b) - num_lines[i/30] += fmt.Sprintf("%02d ", (i+1)%100) + dataLines[i/30] += fmt.Sprintf("%02x ", b) + numLines[i/30] += fmt.Sprintf("%02d ", (i+1)%100) } - for i := 0; i < len(data_lines); i++ { - out.Write([]byte(indent + data_lines[i] + "\n")) - out.Write([]byte(indent + num_lines[i] + "\n\n")) + for i := 0; i < len(dataLines); i++ { + _, _ = out.Write([]byte(indent + dataLines[i] + "\n")) + _, _ = out.Write([]byte(indent + numLines[i] + "\n\n")) } } +func WritePacket(out io.Writer, p *Packet) { + printPacket(out, p, 0, false) +} + func PrintPacket(p *Packet) { printPacket(os.Stdout, p, 0, false) } func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) { - indent_str := "" + indentStr := "" - for len(indent_str) != indent { - indent_str += " " + for len(indentStr) != indent { + indentStr += " " } - class_str := ClassMap[p.ClassType] + classStr := ClassMap[p.ClassType] - tagtype_str := TypeMap[p.TagType] + tagTypeStr := TypeMap[p.TagType] - tag_str := fmt.Sprintf("0x%02X", p.Tag) + tagStr := fmt.Sprintf("0x%02X", p.Tag) if p.ClassType == ClassUniversal { - tag_str = tagMap[p.Tag] + tagStr = tagMap[p.Tag] } value := fmt.Sprint(p.Value) @@ -183,10 +194,10 @@ func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) { description = p.Description + ": " } - fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indent_str, description, class_str, tagtype_str, tag_str, p.Data.Len(), value) + _, _ = fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indentStr, description, classStr, tagTypeStr, tagStr, p.Data.Len(), value) if printBytes { - PrintBytes(out, p.Bytes(), indent_str) + PrintBytes(out, p.Bytes(), indentStr) } for _, child := range p.Children { @@ -194,7 +205,7 @@ func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) { } } -// ReadPacket reads a single Packet from the reader +// ReadPacket reads a single Packet from the reader. func ReadPacket(reader io.Reader) (*Packet, error) { p, _, err := readPacket(reader) if err != nil { @@ -207,7 +218,7 @@ func DecodeString(data []byte) string { return string(data) } -func parseInt64(bytes []byte) (ret int64, err error) { +func ParseInt64(bytes []byte) (ret int64, err error) { if len(bytes) > 8 { // We'll overflow an int64 in this case. err = fmt.Errorf("integer too large") @@ -230,7 +241,7 @@ func encodeInteger(i int64) []byte { var j int for ; n > 0; n-- { - out[j] = (byte(i >> uint((n-1)*8))) + out[j] = byte(i >> uint((n-1)*8)) j++ } @@ -262,7 +273,7 @@ func DecodePacket(data []byte) *Packet { } // DecodePacketErr decodes the given bytes into a single Packet -// If a decode error is encountered, nil is returned +// If a decode error is encountered, nil is returned. func DecodePacketErr(data []byte) (*Packet, error) { p, _, err := readPacket(bytes.NewBuffer(data)) if err != nil { @@ -271,7 +282,7 @@ func DecodePacketErr(data []byte) (*Packet, error) { return p, nil } -// readPacket reads a single Packet from the reader, returning the number of bytes read +// readPacket reads a single Packet from the reader, returning the number of bytes read. func readPacket(reader io.Reader) (*Packet, int, error) { identifier, length, read, err := readHeader(reader) if err != nil { @@ -330,7 +341,10 @@ func readPacket(reader io.Reader) (*Packet, int, error) { } // Read definite-length content - content := make([]byte, length, length) + if MaxPacketLengthBytes > 0 && int64(length) > MaxPacketLengthBytes { + return nil, read, fmt.Errorf("length %d greater than maximum %d", length, MaxPacketLengthBytes) + } + content := make([]byte, length) if length > 0 { _, err := io.ReadFull(reader, content) if err != nil { @@ -349,11 +363,11 @@ func readPacket(reader io.Reader) (*Packet, int, error) { switch p.Tag { case TagEOC: case TagBoolean: - val, _ := parseInt64(content) + val, _ := ParseInt64(content) p.Value = val != 0 case TagInteger: - p.Value, _ = parseInt64(content) + p.Value, _ = ParseInt64(content) case TagBitString: case TagOctetString: // the actual string encoding is not known here @@ -365,22 +379,42 @@ func readPacket(reader io.Reader) (*Packet, int, error) { case TagObjectDescriptor: case TagExternal: case TagRealFloat: + p.Value, err = ParseReal(content) case TagEnumerated: - p.Value, _ = parseInt64(content) + p.Value, _ = ParseInt64(content) case TagEmbeddedPDV: case TagUTF8String: - p.Value = DecodeString(content) + val := DecodeString(content) + if !utf8.Valid([]byte(val)) { + err = errors.New("invalid UTF-8 string") + } else { + p.Value = val + } case TagRelativeOID: case TagSequence: case TagSet: case TagNumericString: case TagPrintableString: - p.Value = DecodeString(content) + val := DecodeString(content) + if err = isPrintableString(val); err == nil { + p.Value = val + } case TagT61String: case TagVideotexString: case TagIA5String: + val := DecodeString(content) + for i, c := range val { + if c >= 0x7F { + err = fmt.Errorf("invalid character for IA5String at pos %d: %c", i, c) + break + } + } + if err == nil { + p.Value = val + } case TagUTCTime: case TagGeneralizedTime: + p.Value, err = ParseGeneralizedTime(content) case TagGraphicString: case TagVisibleString: case TagGeneralString: @@ -392,7 +426,24 @@ func readPacket(reader io.Reader) (*Packet, int, error) { p.Data.Write(content) } - return p, read, nil + return p, read, err +} + +func isPrintableString(val string) error { + for i, c := range val { + switch { + case c >= 'a' && c <= 'z': + case c >= 'A' && c <= 'Z': + case c >= '0' && c <= '9': + default: + switch c { + case '\'', '(', ')', '+', ',', '-', '.', '=', '/', ':', '?', ' ': + default: + return fmt.Errorf("invalid character in position %d", i) + } + } + } + return nil } func (p *Packet) Bytes() []byte { @@ -410,61 +461,99 @@ func (p *Packet) AppendChild(child *Packet) { p.Children = append(p.Children, child) } -func Encode(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet { +func Encode(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet { p := new(Packet) - p.ClassType = ClassType - p.TagType = TagType - p.Tag = Tag + p.ClassType = classType + p.TagType = tagType + p.Tag = tag p.Data = new(bytes.Buffer) p.Children = make([]*Packet, 0, 2) - p.Value = Value - p.Description = Description + p.Value = value + p.Description = description - if Value != nil { - v := reflect.ValueOf(Value) + if value != nil { + v := reflect.ValueOf(value) - if ClassType == ClassUniversal { - switch Tag { + if classType == ClassUniversal { + switch tag { case TagOctetString: sv, ok := v.Interface().(string) if ok { p.Data.Write([]byte(sv)) } + case TagEnumerated: + bv, ok := v.Interface().([]byte) + if ok { + p.Data.Write(bv) + } + case TagEmbeddedPDV: + bv, ok := v.Interface().([]byte) + if ok { + p.Data.Write(bv) + } + } + } else if classType == ClassContext { + switch tag { + case TagEnumerated: + bv, ok := v.Interface().([]byte) + if ok { + p.Data.Write(bv) + } + case TagEmbeddedPDV: + bv, ok := v.Interface().([]byte) + if ok { + p.Data.Write(bv) + } } } } - return p } -func NewSequence(Description string) *Packet { - return Encode(ClassUniversal, TypeConstructed, TagSequence, nil, Description) +func NewSequence(description string) *Packet { + return Encode(ClassUniversal, TypeConstructed, TagSequence, nil, description) } -func NewBoolean(ClassType Class, TagType Type, Tag Tag, Value bool, Description string) *Packet { +func NewBoolean(classType Class, tagType Type, tag Tag, value bool, description string) *Packet { intValue := int64(0) - if Value { + if value { intValue = 1 } - p := Encode(ClassType, TagType, Tag, nil, Description) + p := Encode(classType, tagType, tag, nil, description) + + p.Value = value + p.Data.Write(encodeInteger(intValue)) + + return p +} + +// NewLDAPBoolean returns a RFC 4511-compliant Boolean packet. +func NewLDAPBoolean(classType Class, tagType Type, tag Tag, value bool, description string) *Packet { + intValue := int64(0) + + if value { + intValue = 255 + } + + p := Encode(classType, tagType, tag, nil, description) - p.Value = Value + p.Value = value p.Data.Write(encodeInteger(intValue)) return p } -func NewInteger(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet { - p := Encode(ClassType, TagType, Tag, nil, Description) +func NewInteger(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet { + p := Encode(classType, tagType, tag, nil, description) - p.Value = Value - switch v := Value.(type) { + p.Value = value + switch v := value.(type) { case int: p.Data.Write(encodeInteger(int64(v))) case uint: @@ -494,11 +583,38 @@ func NewInteger(ClassType Class, TagType Type, Tag Tag, Value interface{}, Descr return p } -func NewString(ClassType Class, TagType Type, Tag Tag, Value, Description string) *Packet { - p := Encode(ClassType, TagType, Tag, nil, Description) +func NewString(classType Class, tagType Type, tag Tag, value, description string) *Packet { + p := Encode(classType, tagType, tag, nil, description) + + p.Value = value + p.Data.Write([]byte(value)) + + return p +} + +func NewGeneralizedTime(classType Class, tagType Type, tag Tag, value time.Time, description string) *Packet { + p := Encode(classType, tagType, tag, nil, description) + var s string + if value.Nanosecond() != 0 { + s = value.Format(`20060102150405.000000000Z`) + } else { + s = value.Format(`20060102150405Z`) + } + p.Value = s + p.Data.Write([]byte(s)) + return p +} - p.Value = Value - p.Data.Write([]byte(Value)) +func NewReal(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet { + p := Encode(classType, tagType, tag, nil, description) + switch v := value.(type) { + case float64: + p.Data.Write(encodeFloat(v)) + case float32: + p.Data.Write(encodeFloat(float64(v))) + default: + panic(fmt.Sprintf("Invalid type %T, expected float{64|32}", v)) + } return p } diff --git a/vendor/gopkg.in/asn1-ber.v1/content_int.go b/vendor/github.com/go-asn1-ber/asn1-ber/content_int.go similarity index 87% rename from vendor/gopkg.in/asn1-ber.v1/content_int.go rename to vendor/github.com/go-asn1-ber/asn1-ber/content_int.go index 1858b74b6..20b500f55 100644 --- a/vendor/gopkg.in/asn1-ber.v1/content_int.go +++ b/vendor/github.com/go-asn1-ber/asn1-ber/content_int.go @@ -6,7 +6,7 @@ func encodeUnsignedInteger(i uint64) []byte { var j int for ; n > 0; n-- { - out[j] = (byte(i >> uint((n-1)*8))) + out[j] = byte(i >> uint((n-1)*8)) j++ } diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/generalizedTime.go b/vendor/github.com/go-asn1-ber/asn1-ber/generalizedTime.go new file mode 100644 index 000000000..51215f061 --- /dev/null +++ b/vendor/github.com/go-asn1-ber/asn1-ber/generalizedTime.go @@ -0,0 +1,105 @@ +package ber + +import ( + "bytes" + "errors" + "fmt" + "strconv" + "time" +) + +// ErrInvalidTimeFormat is returned when the generalizedTime string was not correct. +var ErrInvalidTimeFormat = errors.New("invalid time format") + +var zeroTime = time.Time{} + +// ParseGeneralizedTime parses a string value and if it conforms to +// GeneralizedTime[^0] format, will return a time.Time for that value. +// +// [^0]: https://www.itu.int/rec/T-REC-X.690-201508-I/en Section 11.7 +func ParseGeneralizedTime(v []byte) (time.Time, error) { + var format string + var fract time.Duration + + str := []byte(DecodeString(v)) + tzIndex := bytes.IndexAny(str, "Z+-") + if tzIndex < 0 { + return zeroTime, ErrInvalidTimeFormat + } + + dot := bytes.IndexAny(str, ".,") + switch dot { + case -1: + switch tzIndex { + case 10: + format = `2006010215Z` + case 12: + format = `200601021504Z` + case 14: + format = `20060102150405Z` + default: + return zeroTime, ErrInvalidTimeFormat + } + + case 10, 12: + if tzIndex < dot { + return zeroTime, ErrInvalidTimeFormat + } + // a "," is also allowed, but would not be parsed by time.Parse(): + str[dot] = '.' + + // If is omitted, then represents a fraction of an + // hour; otherwise, if and are omitted, then + // represents a fraction of a minute; otherwise, + // represents a fraction of a second. + + // parse as float from dot to timezone + f, err := strconv.ParseFloat(string(str[dot:tzIndex]), 64) + if err != nil { + return zeroTime, fmt.Errorf("failed to parse float: %s", err) + } + // ...and strip that part + str = append(str[:dot], str[tzIndex:]...) + tzIndex = dot + + if dot == 10 { + fract = time.Duration(int64(f * float64(time.Hour))) + format = `2006010215Z` + } else { + fract = time.Duration(int64(f * float64(time.Minute))) + format = `200601021504Z` + } + + case 14: + if tzIndex < dot { + return zeroTime, ErrInvalidTimeFormat + } + str[dot] = '.' + // no need for fractional seconds, time.Parse() handles that + format = `20060102150405Z` + + default: + return zeroTime, ErrInvalidTimeFormat + } + + l := len(str) + switch l - tzIndex { + case 1: + if str[l-1] != 'Z' { + return zeroTime, ErrInvalidTimeFormat + } + case 3: + format += `0700` + str = append(str, []byte("00")...) + case 5: + format += `0700` + default: + return zeroTime, ErrInvalidTimeFormat + } + + t, err := time.Parse(format, string(str)) + if err != nil { + return zeroTime, fmt.Errorf("%s: %s", ErrInvalidTimeFormat, err) + } + return t.Add(fract), nil +} diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/go.mod b/vendor/github.com/go-asn1-ber/asn1-ber/go.mod new file mode 100644 index 000000000..ee0b4be2c --- /dev/null +++ b/vendor/github.com/go-asn1-ber/asn1-ber/go.mod @@ -0,0 +1,3 @@ +module github.com/go-asn1-ber/asn1-ber + +go 1.13 diff --git a/vendor/gopkg.in/asn1-ber.v1/header.go b/vendor/github.com/go-asn1-ber/asn1-ber/header.go similarity index 60% rename from vendor/gopkg.in/asn1-ber.v1/header.go rename to vendor/github.com/go-asn1-ber/asn1-ber/header.go index 123744e9b..7dfa6b9a7 100644 --- a/vendor/gopkg.in/asn1-ber.v1/header.go +++ b/vendor/github.com/go-asn1-ber/asn1-ber/header.go @@ -2,28 +2,37 @@ package ber import ( "errors" + "fmt" "io" ) func readHeader(reader io.Reader) (identifier Identifier, length int, read int, err error) { - if i, c, err := readIdentifier(reader); err != nil { + var ( + c, l int + i Identifier + ) + + if i, c, err = readIdentifier(reader); err != nil { return Identifier{}, 0, read, err - } else { - identifier = i - read += c } + identifier = i + read += c - if l, c, err := readLength(reader); err != nil { + if l, c, err = readLength(reader); err != nil { return Identifier{}, 0, read, err - } else { - length = l - read += c } + length = l + read += c // Validate length type with identifier (x.600, 8.1.3.2.a) if length == LengthIndefinite && identifier.TagType == TypePrimitive { return Identifier{}, 0, read, errors.New("indefinite length used with primitive type") } + if length < LengthIndefinite { + err = fmt.Errorf("length cannot be less than %d", LengthIndefinite) + return + } + return identifier, length, read, nil } diff --git a/vendor/gopkg.in/asn1-ber.v1/identifier.go b/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go similarity index 69% rename from vendor/gopkg.in/asn1-ber.v1/identifier.go rename to vendor/github.com/go-asn1-ber/asn1-ber/identifier.go index f7672a844..e8c435749 100644 --- a/vendor/gopkg.in/asn1-ber.v1/identifier.go +++ b/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "io" - "math" ) func readIdentifier(reader io.Reader) (Identifier, int, error) { @@ -80,24 +79,34 @@ func encodeIdentifier(identifier Identifier) []byte { tag := identifier.Tag - highBit := uint(63) - for { - if tag&(1<= 0; i-- { - offset := uint(i) * 7 - mask := Tag(0x7f) << offset - tagByte := (tag & mask) >> offset - if i != 0 { - tagByte |= 0x80 - } - b = append(b, byte(tagByte)) +func encodeHighTag(tag Tag) []byte { + // set cap=4 to hopefully avoid additional allocations + b := make([]byte, 0, 4) + for tag != 0 { + // t := last 7 bits of tag (HighTagValueBitmask = 0x7F) + t := tag & HighTagValueBitmask + + // right shift tag 7 to remove what was just pulled off + tag >>= 7 + + // if b already has entries this entry needs a continuation bit (0x80) + if len(b) != 0 { + t |= HighTagContinueBitmask } + + b = append(b, byte(t)) + } + // reverse + // since bits were pulled off 'tag' small to high the byte slice is in reverse order. + // example: tag = 0xFF results in {0x7F, 0x01 + 0x80 (continuation bit)} + // this needs to be reversed into 0x81 0x7F + for i, j := 0, len(b)-1; i < len(b)/2; i++ { + b[i], b[j-i] = b[j-i], b[i] } return b } diff --git a/vendor/gopkg.in/asn1-ber.v1/length.go b/vendor/github.com/go-asn1-ber/asn1-ber/length.go similarity index 71% rename from vendor/gopkg.in/asn1-ber.v1/length.go rename to vendor/github.com/go-asn1-ber/asn1-ber/length.go index 8e2ae4ddd..9cc195d0b 100644 --- a/vendor/gopkg.in/asn1-ber.v1/length.go +++ b/vendor/github.com/go-asn1-ber/asn1-ber/length.go @@ -38,6 +38,9 @@ func readLength(reader io.Reader) (length int, read int, err error) { if lengthBytes > 8 { return 0, read, errors.New("long-form length overflow") } + + // Accumulate into a 64-bit variable + var length64 int64 for i := 0; i < lengthBytes; i++ { b, err = readByte(reader) if err != nil { @@ -49,8 +52,15 @@ func readLength(reader io.Reader) (length int, read int, err error) { read++ // x.600, 8.1.3.5 - length <<= 8 - length |= int(b) + length64 <<= 8 + length64 |= int64(b) + } + + // Cast to a platform-specific integer + length = int(length64) + // Ensure we didn't overflow + if int64(length) != length64 { + return 0, read, errors.New("long-form length overflow") } default: @@ -61,11 +71,11 @@ func readLength(reader io.Reader) (length int, read int, err error) { } func encodeLength(length int) []byte { - length_bytes := encodeUnsignedInteger(uint64(length)) - if length > 127 || len(length_bytes) > 1 { - longFormBytes := []byte{(LengthLongFormBitmask | byte(len(length_bytes)))} - longFormBytes = append(longFormBytes, length_bytes...) - length_bytes = longFormBytes + lengthBytes := encodeUnsignedInteger(uint64(length)) + if length > 127 || len(lengthBytes) > 1 { + longFormBytes := []byte{LengthLongFormBitmask | byte(len(lengthBytes))} + longFormBytes = append(longFormBytes, lengthBytes...) + lengthBytes = longFormBytes } - return length_bytes + return lengthBytes } diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/real.go b/vendor/github.com/go-asn1-ber/asn1-ber/real.go new file mode 100644 index 000000000..610a003a7 --- /dev/null +++ b/vendor/github.com/go-asn1-ber/asn1-ber/real.go @@ -0,0 +1,157 @@ +package ber + +import ( + "bytes" + "errors" + "fmt" + "math" + "strconv" + "strings" +) + +func encodeFloat(v float64) []byte { + switch { + case math.IsInf(v, 1): + return []byte{0x40} + case math.IsInf(v, -1): + return []byte{0x41} + case math.IsNaN(v): + return []byte{0x42} + case v == 0.0: + if math.Signbit(v) { + return []byte{0x43} + } + return []byte{} + default: + // we take the easy part ;-) + value := []byte(strconv.FormatFloat(v, 'G', -1, 64)) + var ret []byte + if bytes.Contains(value, []byte{'E'}) { + ret = []byte{0x03} + } else { + ret = []byte{0x02} + } + ret = append(ret, value...) + return ret + } +} + +func ParseReal(v []byte) (val float64, err error) { + if len(v) == 0 { + return 0.0, nil + } + switch { + case v[0]&0x80 == 0x80: + val, err = parseBinaryFloat(v) + case v[0]&0xC0 == 0x40: + val, err = parseSpecialFloat(v) + case v[0]&0xC0 == 0x0: + val, err = parseDecimalFloat(v) + default: + return 0.0, fmt.Errorf("invalid info block") + } + if err != nil { + return 0.0, err + } + + if val == 0.0 && !math.Signbit(val) { + return 0.0, errors.New("REAL value +0 must be encoded with zero-length value block") + } + return val, nil +} + +func parseBinaryFloat(v []byte) (float64, error) { + var info byte + var buf []byte + + info, v = v[0], v[1:] + + var base int + switch info & 0x30 { + case 0x00: + base = 2 + case 0x10: + base = 8 + case 0x20: + base = 16 + case 0x30: + return 0.0, errors.New("bits 6 and 5 of information octet for REAL are equal to 11") + } + + scale := uint((info & 0x0c) >> 2) + + var expLen int + switch info & 0x03 { + case 0x00: + expLen = 1 + case 0x01: + expLen = 2 + case 0x02: + expLen = 3 + case 0x03: + expLen = int(v[0]) + if expLen > 8 { + return 0.0, errors.New("too big value of exponent") + } + v = v[1:] + } + buf, v = v[:expLen], v[expLen:] + exponent, err := ParseInt64(buf) + if err != nil { + return 0.0, err + } + + if len(v) > 8 { + return 0.0, errors.New("too big value of mantissa") + } + + mant, err := ParseInt64(v) + if err != nil { + return 0.0, err + } + mantissa := mant << scale + + if info&0x40 == 0x40 { + mantissa = -mantissa + } + + return float64(mantissa) * math.Pow(float64(base), float64(exponent)), nil +} + +func parseDecimalFloat(v []byte) (val float64, err error) { + switch v[0] & 0x3F { + case 0x01: // NR form 1 + var iVal int64 + iVal, err = strconv.ParseInt(strings.TrimLeft(string(v[1:]), " "), 10, 64) + val = float64(iVal) + case 0x02, 0x03: // NR form 2, 3 + val, err = strconv.ParseFloat(strings.Replace(strings.TrimLeft(string(v[1:]), " "), ",", ".", -1), 64) + default: + err = errors.New("incorrect NR form") + } + if err != nil { + return 0.0, err + } + + if val == 0.0 && math.Signbit(val) { + return 0.0, errors.New("REAL value -0 must be encoded as a special value") + } + return val, nil +} + +func parseSpecialFloat(v []byte) (float64, error) { + if len(v) != 1 { + return 0.0, errors.New(`encoding of "special value" must not contain exponent and mantissa`) + } + switch v[0] { + case 0x40: + return math.Inf(1), nil + case 0x41: + return math.Inf(-1), nil + case 0x42: + return math.NaN(), nil + case 0x43: + return math.Copysign(0, -1), nil + } + return 0.0, errors.New(`encoding of "special value" not from ASN.1 standard`) +} diff --git a/vendor/gopkg.in/asn1-ber.v1/util.go b/vendor/github.com/go-asn1-ber/asn1-ber/util.go similarity index 93% rename from vendor/gopkg.in/asn1-ber.v1/util.go rename to vendor/github.com/go-asn1-ber/asn1-ber/util.go index 3e56b66c8..14dc87d7c 100644 --- a/vendor/gopkg.in/asn1-ber.v1/util.go +++ b/vendor/github.com/go-asn1-ber/asn1-ber/util.go @@ -3,7 +3,7 @@ package ber import "io" func readByte(reader io.Reader) (byte, error) { - bytes := make([]byte, 1, 1) + bytes := make([]byte, 1) _, err := io.ReadFull(reader, bytes) if err != nil { if err == io.EOF { diff --git a/vendor/gopkg.in/ldap.v3/LICENSE b/vendor/github.com/go-ldap/ldap/v3/LICENSE similarity index 100% rename from vendor/gopkg.in/ldap.v3/LICENSE rename to vendor/github.com/go-ldap/ldap/v3/LICENSE diff --git a/vendor/gopkg.in/ldap.v3/add.go b/vendor/github.com/go-ldap/ldap/v3/add.go similarity index 53% rename from vendor/gopkg.in/ldap.v3/add.go rename to vendor/github.com/go-ldap/ldap/v3/add.go index 19bce1b75..baecd787d 100644 --- a/vendor/gopkg.in/ldap.v3/add.go +++ b/vendor/github.com/go-ldap/ldap/v3/add.go @@ -1,19 +1,9 @@ -// -// https://tools.ietf.org/html/rfc4511 -// -// AddRequest ::= [APPLICATION 8] SEQUENCE { -// entry LDAPDN, -// attributes AttributeList } -// -// AttributeList ::= SEQUENCE OF attribute Attribute - package ldap import ( - "errors" "log" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) // Attribute represents an LDAP attribute @@ -45,20 +35,26 @@ type AddRequest struct { Controls []Control } -func (a AddRequest) encode() *ber.Packet { - request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request") - request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN")) +func (req *AddRequest) appendTo(envelope *ber.Packet) error { + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request") + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN")) attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes") - for _, attribute := range a.Attributes { + for _, attribute := range req.Attributes { attributes.AppendChild(attribute.encode()) } - request.AppendChild(attributes) - return request + pkt.AppendChild(attributes) + + envelope.AppendChild(pkt) + if len(req.Controls) > 0 { + envelope.AppendChild(encodeControls(req.Controls)) + } + + return nil } // Attribute adds an attribute with the given type and values -func (a *AddRequest) Attribute(attrType string, attrVals []string) { - a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals}) +func (req *AddRequest) Attribute(attrType string, attrVals []string) { + req.Attributes = append(req.Attributes, Attribute{Type: attrType, Vals: attrVals}) } // NewAddRequest returns an AddRequest for the given DN, with no attributes @@ -72,39 +68,17 @@ func NewAddRequest(dn string, controls []Control) *AddRequest { // Add performs the given AddRequest func (l *Conn) Add(addRequest *AddRequest) error { - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) - packet.AppendChild(addRequest.encode()) - if len(addRequest.Controls) > 0 { - packet.AppendChild(encodeControls(addRequest.Controls)) - } - - l.Debug.PrintPacket(packet) - - msgCtx, err := l.sendMessage(packet) + msgCtx, err := l.doRequest(addRequest) if err != nil { return err } defer l.finishMessage(msgCtx) - l.Debug.Printf("%d: waiting for response", msgCtx.id) - packetResponse, ok := <-msgCtx.responses - if !ok { - return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) - } - packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + packet, err := l.readPacket(msgCtx) if err != nil { return err } - if l.Debug { - if err := addLDAPDescriptions(packet); err != nil { - return err - } - ber.PrintPacket(packet) - } - if packet.Children[1].Tag == ApplicationAddResponse { err := GetLDAPError(packet) if err != nil { @@ -113,7 +87,5 @@ func (l *Conn) Add(addRequest *AddRequest) error { } else { log.Printf("Unexpected Response: %d", packet.Children[1].Tag) } - - l.Debug.Printf("%d: returning", msgCtx.id) return nil } diff --git a/vendor/github.com/go-ldap/ldap/v3/bind.go b/vendor/github.com/go-ldap/ldap/v3/bind.go new file mode 100644 index 000000000..a7194c9c5 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/v3/bind.go @@ -0,0 +1,540 @@ +package ldap + +import ( + "bytes" + "crypto/md5" + enchex "encoding/hex" + "errors" + "fmt" + "io/ioutil" + "math/rand" + "strings" + + "github.com/Azure/go-ntlmssp" + ber "github.com/go-asn1-ber/asn1-ber" +) + +// SimpleBindRequest represents a username/password bind operation +type SimpleBindRequest struct { + // Username is the name of the Directory object that the client wishes to bind as + Username string + // Password is the credentials to bind with + Password string + // Controls are optional controls to send with the bind request + Controls []Control + // AllowEmptyPassword sets whether the client allows binding with an empty password + // (normally used for unauthenticated bind). + AllowEmptyPassword bool +} + +// SimpleBindResult contains the response from the server +type SimpleBindResult struct { + Controls []Control +} + +// NewSimpleBindRequest returns a bind request +func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest { + return &SimpleBindRequest{ + Username: username, + Password: password, + Controls: controls, + AllowEmptyPassword: false, + } +} + +func (req *SimpleBindRequest) appendTo(envelope *ber.Packet) error { + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") + pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Username, "User Name")) + pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.Password, "Password")) + + envelope.AppendChild(pkt) + if len(req.Controls) > 0 { + envelope.AppendChild(encodeControls(req.Controls)) + } + + return nil +} + +// SimpleBind performs the simple bind operation defined in the given request +func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) { + if simpleBindRequest.Password == "" && !simpleBindRequest.AllowEmptyPassword { + return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client")) + } + + msgCtx, err := l.doRequest(simpleBindRequest) + if err != nil { + return nil, err + } + defer l.finishMessage(msgCtx) + + packet, err := l.readPacket(msgCtx) + if err != nil { + return nil, err + } + + result := &SimpleBindResult{ + Controls: make([]Control, 0), + } + + if len(packet.Children) == 3 { + for _, child := range packet.Children[2].Children { + decodedChild, decodeErr := DecodeControl(child) + if decodeErr != nil { + return nil, fmt.Errorf("failed to decode child control: %s", decodeErr) + } + result.Controls = append(result.Controls, decodedChild) + } + } + + err = GetLDAPError(packet) + return result, err +} + +// Bind performs a bind with the given username and password. +// +// It does not allow unauthenticated bind (i.e. empty password). Use the UnauthenticatedBind method +// for that. +func (l *Conn) Bind(username, password string) error { + req := &SimpleBindRequest{ + Username: username, + Password: password, + AllowEmptyPassword: false, + } + _, err := l.SimpleBind(req) + return err +} + +// UnauthenticatedBind performs an unauthenticated bind. +// +// A username may be provided for trace (e.g. logging) purpose only, but it is normally not +// authenticated or otherwise validated by the LDAP server. +// +// See https://tools.ietf.org/html/rfc4513#section-5.1.2 . +// See https://tools.ietf.org/html/rfc4513#section-6.3.1 . +func (l *Conn) UnauthenticatedBind(username string) error { + req := &SimpleBindRequest{ + Username: username, + Password: "", + AllowEmptyPassword: true, + } + _, err := l.SimpleBind(req) + return err +} + +// DigestMD5BindRequest represents a digest-md5 bind operation +type DigestMD5BindRequest struct { + Host string + // Username is the name of the Directory object that the client wishes to bind as + Username string + // Password is the credentials to bind with + Password string + // Controls are optional controls to send with the bind request + Controls []Control +} + +func (req *DigestMD5BindRequest) appendTo(envelope *ber.Packet) error { + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") + request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name")) + + auth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication") + auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "DIGEST-MD5", "SASL Mech")) + request.AppendChild(auth) + envelope.AppendChild(request) + if len(req.Controls) > 0 { + envelope.AppendChild(encodeControls(req.Controls)) + } + return nil +} + +// DigestMD5BindResult contains the response from the server +type DigestMD5BindResult struct { + Controls []Control +} + +// MD5Bind performs a digest-md5 bind with the given host, username and password. +func (l *Conn) MD5Bind(host, username, password string) error { + req := &DigestMD5BindRequest{ + Host: host, + Username: username, + Password: password, + } + _, err := l.DigestMD5Bind(req) + return err +} + +// DigestMD5Bind performs the digest-md5 bind operation defined in the given request +func (l *Conn) DigestMD5Bind(digestMD5BindRequest *DigestMD5BindRequest) (*DigestMD5BindResult, error) { + if digestMD5BindRequest.Password == "" { + return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client")) + } + + msgCtx, err := l.doRequest(digestMD5BindRequest) + if err != nil { + return nil, err + } + defer l.finishMessage(msgCtx) + + packet, err := l.readPacket(msgCtx) + if err != nil { + return nil, err + } + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if l.Debug { + if err = addLDAPDescriptions(packet); err != nil { + return nil, err + } + ber.PrintPacket(packet) + } + + result := &DigestMD5BindResult{ + Controls: make([]Control, 0), + } + var params map[string]string + if len(packet.Children) == 2 { + if len(packet.Children[1].Children) == 4 { + child := packet.Children[1].Children[0] + if child.Tag != ber.TagEnumerated { + return result, GetLDAPError(packet) + } + if child.Value.(int64) != 14 { + return result, GetLDAPError(packet) + } + child = packet.Children[1].Children[3] + if child.Tag != ber.TagObjectDescriptor { + return result, GetLDAPError(packet) + } + if child.Data == nil { + return result, GetLDAPError(packet) + } + data, _ := ioutil.ReadAll(child.Data) + params, err = parseParams(string(data)) + if err != nil { + return result, fmt.Errorf("parsing digest-challenge: %s", err) + } + } + } + + if params != nil { + resp := computeResponse( + params, + "ldap/"+strings.ToLower(digestMD5BindRequest.Host), + digestMD5BindRequest.Username, + digestMD5BindRequest.Password, + ) + packet = ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") + request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name")) + + auth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication") + auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "DIGEST-MD5", "SASL Mech")) + auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, resp, "Credentials")) + request.AppendChild(auth) + packet.AppendChild(request) + msgCtx, err = l.sendMessage(packet) + if err != nil { + return nil, fmt.Errorf("send message: %s", err) + } + defer l.finishMessage(msgCtx) + packetResponse, ok := <-msgCtx.responses + if !ok { + return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return nil, fmt.Errorf("read packet: %s", err) + } + } + + err = GetLDAPError(packet) + return result, err +} + +func parseParams(str string) (map[string]string, error) { + m := make(map[string]string) + var key, value string + var state int + for i := 0; i <= len(str); i++ { + switch state { + case 0: //reading key + if i == len(str) { + return nil, fmt.Errorf("syntax error on %d", i) + } + if str[i] != '=' { + key += string(str[i]) + continue + } + state = 1 + case 1: //reading value + if i == len(str) { + m[key] = value + break + } + switch str[i] { + case ',': + m[key] = value + state = 0 + key = "" + value = "" + case '"': + if value != "" { + return nil, fmt.Errorf("syntax error on %d", i) + } + state = 2 + default: + value += string(str[i]) + } + case 2: //inside quotes + if i == len(str) { + return nil, fmt.Errorf("syntax error on %d", i) + } + if str[i] != '"' { + value += string(str[i]) + } else { + state = 1 + } + } + } + return m, nil +} + +func computeResponse(params map[string]string, uri, username, password string) string { + nc := "00000001" + qop := "auth" + cnonce := enchex.EncodeToString(randomBytes(16)) + x := username + ":" + params["realm"] + ":" + password + y := md5Hash([]byte(x)) + + a1 := bytes.NewBuffer(y) + a1.WriteString(":" + params["nonce"] + ":" + cnonce) + if len(params["authzid"]) > 0 { + a1.WriteString(":" + params["authzid"]) + } + a2 := bytes.NewBuffer([]byte("AUTHENTICATE")) + a2.WriteString(":" + uri) + ha1 := enchex.EncodeToString(md5Hash(a1.Bytes())) + ha2 := enchex.EncodeToString(md5Hash(a2.Bytes())) + + kd := ha1 + kd += ":" + params["nonce"] + kd += ":" + nc + kd += ":" + cnonce + kd += ":" + qop + kd += ":" + ha2 + resp := enchex.EncodeToString(md5Hash([]byte(kd))) + return fmt.Sprintf( + `username="%s",realm="%s",nonce="%s",cnonce="%s",nc=00000001,qop=%s,digest-uri="%s",response=%s`, + username, + params["realm"], + params["nonce"], + cnonce, + qop, + uri, + resp, + ) +} + +func md5Hash(b []byte) []byte { + hasher := md5.New() + hasher.Write(b) + return hasher.Sum(nil) +} + +func randomBytes(len int) []byte { + b := make([]byte, len) + for i := 0; i < len; i++ { + b[i] = byte(rand.Intn(256)) + } + return b +} + +var externalBindRequest = requestFunc(func(envelope *ber.Packet) error { + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") + pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name")) + + saslAuth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication") + saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "EXTERNAL", "SASL Mech")) + saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "SASL Cred")) + + pkt.AppendChild(saslAuth) + + envelope.AppendChild(pkt) + + return nil +}) + +// ExternalBind performs SASL/EXTERNAL authentication. +// +// Use ldap.DialURL("ldapi://") to connect to the Unix socket before ExternalBind. +// +// See https://tools.ietf.org/html/rfc4422#appendix-A +func (l *Conn) ExternalBind() error { + msgCtx, err := l.doRequest(externalBindRequest) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + packet, err := l.readPacket(msgCtx) + if err != nil { + return err + } + + return GetLDAPError(packet) +} + +// NTLMBind performs an NTLMSSP bind leveraging https://github.com/Azure/go-ntlmssp + +// NTLMBindRequest represents an NTLMSSP bind operation +type NTLMBindRequest struct { + // Domain is the AD Domain to authenticate too. If not specified, it will be grabbed from the NTLMSSP Challenge + Domain string + // Username is the name of the Directory object that the client wishes to bind as + Username string + // Password is the credentials to bind with + Password string + // Hash is the hex NTLM hash to bind with. Password or hash must be provided + Hash string + // Controls are optional controls to send with the bind request + Controls []Control +} + +func (req *NTLMBindRequest) appendTo(envelope *ber.Packet) error { + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") + request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name")) + + // generate an NTLMSSP Negotiation message for the specified domain (it can be blank) + negMessage, err := ntlmssp.NewNegotiateMessage(req.Domain, "") + if err != nil { + return fmt.Errorf("err creating negmessage: %s", err) + } + + // append the generated NTLMSSP message as a TagEnumerated BER value + auth := ber.Encode(ber.ClassContext, ber.TypePrimitive, ber.TagEnumerated, negMessage, "authentication") + request.AppendChild(auth) + envelope.AppendChild(request) + if len(req.Controls) > 0 { + envelope.AppendChild(encodeControls(req.Controls)) + } + return nil +} + +// NTLMBindResult contains the response from the server +type NTLMBindResult struct { + Controls []Control +} + +// NTLMBind performs an NTLMSSP Bind with the given domain, username and password +func (l *Conn) NTLMBind(domain, username, password string) error { + req := &NTLMBindRequest{ + Domain: domain, + Username: username, + Password: password, + } + _, err := l.NTLMChallengeBind(req) + return err +} + +// NTLMBindWithHash performs an NTLM Bind with an NTLM hash instead of plaintext password (pass-the-hash) +func (l *Conn) NTLMBindWithHash(domain, username, hash string) error { + req := &NTLMBindRequest{ + Domain: domain, + Username: username, + Hash: hash, + } + _, err := l.NTLMChallengeBind(req) + return err +} + +// NTLMChallengeBind performs the NTLMSSP bind operation defined in the given request +func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindResult, error) { + if ntlmBindRequest.Password == "" && ntlmBindRequest.Hash == "" { + return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client")) + } + + msgCtx, err := l.doRequest(ntlmBindRequest) + if err != nil { + return nil, err + } + defer l.finishMessage(msgCtx) + packet, err := l.readPacket(msgCtx) + if err != nil { + return nil, err + } + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if l.Debug { + if err = addLDAPDescriptions(packet); err != nil { + return nil, err + } + ber.PrintPacket(packet) + } + result := &NTLMBindResult{ + Controls: make([]Control, 0), + } + var ntlmsspChallenge []byte + + // now find the NTLM Response Message + if len(packet.Children) == 2 { + if len(packet.Children[1].Children) == 3 { + child := packet.Children[1].Children[1] + ntlmsspChallenge = child.ByteValue + // Check to make sure we got the right message. It will always start with NTLMSSP + if !bytes.Equal(ntlmsspChallenge[:7], []byte("NTLMSSP")) { + return result, GetLDAPError(packet) + } + l.Debug.Printf("%d: found ntlmssp challenge", msgCtx.id) + } + } + if ntlmsspChallenge != nil { + var err error + var responseMessage []byte + // generate a response message to the challenge with the given Username/Password if password is provided + if ntlmBindRequest.Password != "" { + responseMessage, err = ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password) + } else if ntlmBindRequest.Hash != "" { + responseMessage, err = ntlmssp.ProcessChallengeWithHash(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Hash) + } else { + err = fmt.Errorf("need a password or hash to generate reply") + } + if err != nil { + return result, fmt.Errorf("parsing ntlm-challenge: %s", err) + } + packet = ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") + request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name")) + + // append the challenge response message as a TagEmbeddedPDV BER value + auth := ber.Encode(ber.ClassContext, ber.TypePrimitive, ber.TagEmbeddedPDV, responseMessage, "authentication") + + request.AppendChild(auth) + packet.AppendChild(request) + msgCtx, err = l.sendMessage(packet) + if err != nil { + return nil, fmt.Errorf("send message: %s", err) + } + defer l.finishMessage(msgCtx) + packetResponse, ok := <-msgCtx.responses + if !ok { + return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return nil, fmt.Errorf("read packet: %s", err) + } + + } + + err = GetLDAPError(packet) + return result, err +} diff --git a/vendor/github.com/go-ldap/ldap/v3/client.go b/vendor/github.com/go-ldap/ldap/v3/client.go new file mode 100644 index 000000000..619677c77 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/v3/client.go @@ -0,0 +1,30 @@ +package ldap + +import ( + "crypto/tls" + "time" +) + +// Client knows how to interact with an LDAP server +type Client interface { + Start() + StartTLS(*tls.Config) error + Close() + SetTimeout(time.Duration) + + Bind(username, password string) error + UnauthenticatedBind(username string) error + SimpleBind(*SimpleBindRequest) (*SimpleBindResult, error) + ExternalBind() error + + Add(*AddRequest) error + Del(*DelRequest) error + Modify(*ModifyRequest) error + ModifyDN(*ModifyDNRequest) error + + Compare(dn, attribute, value string) (bool, error) + PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error) + + Search(*SearchRequest) (*SearchResult, error) + SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) +} diff --git a/vendor/github.com/go-ldap/ldap/v3/compare.go b/vendor/github.com/go-ldap/ldap/v3/compare.go new file mode 100644 index 000000000..cd43e4c53 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/v3/compare.go @@ -0,0 +1,61 @@ +package ldap + +import ( + "fmt" + + ber "github.com/go-asn1-ber/asn1-ber" +) + +// CompareRequest represents an LDAP CompareRequest operation. +type CompareRequest struct { + DN string + Attribute string + Value string +} + +func (req *CompareRequest) appendTo(envelope *ber.Packet) error { + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request") + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN")) + + ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion") + ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Attribute, "AttributeDesc")) + ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Value, "AssertionValue")) + + pkt.AppendChild(ava) + + envelope.AppendChild(pkt) + + return nil +} + +// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise +// false with any error that occurs if any. +func (l *Conn) Compare(dn, attribute, value string) (bool, error) { + msgCtx, err := l.doRequest(&CompareRequest{ + DN: dn, + Attribute: attribute, + Value: value}) + if err != nil { + return false, err + } + defer l.finishMessage(msgCtx) + + packet, err := l.readPacket(msgCtx) + if err != nil { + return false, err + } + + if packet.Children[1].Tag == ApplicationCompareResponse { + err := GetLDAPError(packet) + + switch { + case IsErrorWithCode(err, LDAPResultCompareTrue): + return true, nil + case IsErrorWithCode(err, LDAPResultCompareFalse): + return false, nil + default: + return false, err + } + } + return false, fmt.Errorf("unexpected Response: %d", packet.Children[1].Tag) +} diff --git a/vendor/gopkg.in/ldap.v3/conn.go b/vendor/github.com/go-ldap/ldap/v3/conn.go similarity index 86% rename from vendor/gopkg.in/ldap.v3/conn.go rename to vendor/github.com/go-ldap/ldap/v3/conn.go index c20471fc2..8b8c41e73 100644 --- a/vendor/gopkg.in/ldap.v3/conn.go +++ b/vendor/github.com/go-ldap/ldap/v3/conn.go @@ -11,7 +11,7 @@ import ( "sync/atomic" "time" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) const ( @@ -112,8 +112,63 @@ var _ Client = &Conn{} // multiple places will probably result in undesired behaviour. var DefaultTimeout = 60 * time.Second +// DialOpt configures DialContext. +type DialOpt func(*DialContext) + +// DialWithDialer updates net.Dialer in DialContext. +func DialWithDialer(d *net.Dialer) DialOpt { + return func(dc *DialContext) { + dc.d = d + } +} + +// DialWithTLSConfig updates tls.Config in DialContext. +func DialWithTLSConfig(tc *tls.Config) DialOpt { + return func(dc *DialContext) { + dc.tc = tc + } +} + +// DialContext contains necessary parameters to dial the given ldap URL. +type DialContext struct { + d *net.Dialer + tc *tls.Config +} + +func (dc *DialContext) dial(u *url.URL) (net.Conn, error) { + if u.Scheme == "ldapi" { + if u.Path == "" || u.Path == "/" { + u.Path = "/var/run/slapd/ldapi" + } + return dc.d.Dial("unix", u.Path) + } + + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + // we assume that error is due to missing port + host = u.Host + port = "" + } + + switch u.Scheme { + case "ldap": + if port == "" { + port = DefaultLdapPort + } + return dc.d.Dial("tcp", net.JoinHostPort(host, port)) + case "ldaps": + if port == "" { + port = DefaultLdapsPort + } + return tls.DialWithDialer(dc.d, "tcp", net.JoinHostPort(host, port), dc.tc) + } + + return nil, fmt.Errorf("Unknown scheme '%s'", u.Scheme) +} + // Dial connects to the given address on the given network using net.Dial // and then returns a new Conn for the connection. +// @deprecated Use DialURL instead. func Dial(network, addr string) (*Conn, error) { c, err := net.DialTimeout(network, addr, DefaultTimeout) if err != nil { @@ -126,6 +181,7 @@ func Dial(network, addr string) (*Conn, error) { // DialTLS connects to the given address on the given network using tls.Dial // and then returns a new Conn for the connection. +// @deprecated Use DialURL instead. func DialTLS(network, addr string, config *tls.Config) (*Conn, error) { c, err := tls.DialWithDialer(&net.Dialer{Timeout: DefaultTimeout}, network, addr, config) if err != nil { @@ -136,40 +192,31 @@ func DialTLS(network, addr string, config *tls.Config) (*Conn, error) { return conn, nil } -// DialURL connects to the given ldap URL vie TCP using tls.Dial or net.Dial if ldaps:// -// or ldap:// specified as protocol. On success a new Conn for the connection -// is returned. -func DialURL(addr string) (*Conn, error) { - - lurl, err := url.Parse(addr) +// DialURL connects to the given ldap URL. +// The following schemas are supported: ldap://, ldaps://, ldapi://. +// On success a new Conn for the connection is returned. +func DialURL(addr string, opts ...DialOpt) (*Conn, error) { + u, err := url.Parse(addr) if err != nil { return nil, NewError(ErrorNetwork, err) } - host, port, err := net.SplitHostPort(lurl.Host) - if err != nil { - // we asume that error is due to missing port - host = lurl.Host - port = "" + var dc DialContext + for _, opt := range opts { + opt(&dc) + } + if dc.d == nil { + dc.d = &net.Dialer{Timeout: DefaultTimeout} } - switch lurl.Scheme { - case "ldap": - if port == "" { - port = DefaultLdapPort - } - return Dial("tcp", net.JoinHostPort(host, port)) - case "ldaps": - if port == "" { - port = DefaultLdapsPort - } - tlsConf := &tls.Config{ - ServerName: host, - } - return DialTLS("tcp", net.JoinHostPort(host, port), tlsConf) + c, err := dc.dial(u) + if err != nil { + return nil, NewError(ErrorNetwork, err) } - return nil, NewError(ErrorNetwork, fmt.Errorf("Unknown scheme '%s'", lurl.Scheme)) + conn := NewConn(c, u.Scheme == "ldaps") + conn.Start() + return conn, nil } // NewConn returns a new Conn using conn for network I/O. @@ -187,9 +234,9 @@ func NewConn(conn net.Conn, isTLS bool) *Conn { // Start initializes goroutines to read responses and process messages func (l *Conn) Start() { + l.wgClose.Add(1) go l.reader() go l.processMessages() - l.wgClose.Add(1) } // IsClosing returns whether or not we're currently closing. @@ -274,7 +321,7 @@ func (l *Conn) StartTLS(config *tls.Config) error { l.Close() return err } - ber.PrintPacket(packet) + l.Debug.PrintPacket(packet) } if err := GetLDAPError(packet); err == nil { @@ -343,7 +390,12 @@ func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) responses: responses, }, } - l.sendProcessMessage(message) + if !l.sendProcessMessage(message) { + if l.IsClosing() { + return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed")) + } + return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message for unknown reason")) + } return message.Context, nil } @@ -447,7 +499,7 @@ func (l *Conn) processMessages() { msgCtx.sendResponse(&PacketResponse{message.Packet, nil}) } else { log.Printf("Received unexpected message %d, %v", message.MessageID, l.IsClosing()) - ber.PrintPacket(message.Packet) + l.Debug.PrintPacket(message.Packet) } case MessageTimeout: // Handle the timeout by closing the channel @@ -490,11 +542,13 @@ func (l *Conn) reader() { // A read error is expected here if we are closing the connection... if !l.IsClosing() { l.closeErr.Store(fmt.Errorf("unable to read LDAP response packet: %s", err)) - l.Debug.Printf("reader error: %s", err.Error()) + l.Debug.Printf("reader error: %s", err) } return } - addLDAPDescriptions(packet) + if err := addLDAPDescriptions(packet); err != nil { + l.Debug.Printf("descriptions error: %s", err) + } if len(packet.Children) == 0 { l.Debug.Printf("Received bad ldap packet") continue diff --git a/vendor/gopkg.in/ldap.v3/control.go b/vendor/github.com/go-ldap/ldap/v3/control.go similarity index 95% rename from vendor/gopkg.in/ldap.v3/control.go rename to vendor/github.com/go-ldap/ldap/v3/control.go index 4439a865d..7d7999cc6 100644 --- a/vendor/gopkg.in/ldap.v3/control.go +++ b/vendor/github.com/go-ldap/ldap/v3/control.go @@ -4,7 +4,7 @@ import ( "fmt" "strconv" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) const ( @@ -63,7 +63,9 @@ func (c *ControlString) Encode() *ber.Packet { if c.Criticality { packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) } - packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value")) + if c.ControlValue != "" { + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value")) + } return packet } @@ -402,33 +404,26 @@ func DecodeControl(packet *ber.Packet) (Control, error) { if child.Tag == 0 { //Warning warningPacket := child.Children[0] - packet, err := ber.DecodePacketErr(warningPacket.Data.Bytes()) + val, err := ber.ParseInt64(warningPacket.Data.Bytes()) if err != nil { return nil, fmt.Errorf("failed to decode data bytes: %s", err) } - val, ok := packet.Value.(int64) - if ok { - if warningPacket.Tag == 0 { - //timeBeforeExpiration - c.Expire = val - warningPacket.Value = c.Expire - } else if warningPacket.Tag == 1 { - //graceAuthNsRemaining - c.Grace = val - warningPacket.Value = c.Grace - } + if warningPacket.Tag == 0 { + //timeBeforeExpiration + c.Expire = val + warningPacket.Value = c.Expire + } else if warningPacket.Tag == 1 { + //graceAuthNsRemaining + c.Grace = val + warningPacket.Value = c.Grace } } else if child.Tag == 1 { // Error - packet, err := ber.DecodePacketErr(child.Data.Bytes()) - if err != nil { - return nil, fmt.Errorf("failed to decode data bytes: %s", err) - } - val, ok := packet.Value.(int8) - if !ok { - // what to do? - val = -1 + bs := child.Data.Bytes() + if len(bs) != 1 || bs[0] > 8 { + return nil, fmt.Errorf("failed to decode data bytes: %s", "invalid PasswordPolicyResponse enum value") } + val := int8(bs[0]) c.Error = val child.Value = c.Error c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error] diff --git a/vendor/gopkg.in/ldap.v3/debug.go b/vendor/github.com/go-ldap/ldap/v3/debug.go similarity index 62% rename from vendor/gopkg.in/ldap.v3/debug.go rename to vendor/github.com/go-ldap/ldap/v3/debug.go index 7279fc251..2c0b30c8d 100644 --- a/vendor/gopkg.in/ldap.v3/debug.go +++ b/vendor/github.com/go-ldap/ldap/v3/debug.go @@ -3,20 +3,26 @@ package ldap import ( "log" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) // debugging type // - has a Printf method to write the debug output type debugging bool -// write debug output +// Enable controls debugging mode. +func (debug *debugging) Enable(b bool) { + *debug = debugging(b) +} + +// Printf writes debug output. func (debug debugging) Printf(format string, args ...interface{}) { if debug { log.Printf(format, args...) } } +// PrintPacket dumps a packet. func (debug debugging) PrintPacket(packet *ber.Packet) { if debug { ber.PrintPacket(packet) diff --git a/vendor/github.com/go-ldap/ldap/v3/del.go b/vendor/github.com/go-ldap/ldap/v3/del.go new file mode 100644 index 000000000..6e9872677 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/v3/del.go @@ -0,0 +1,59 @@ +package ldap + +import ( + "log" + + ber "github.com/go-asn1-ber/asn1-ber" +) + +// DelRequest implements an LDAP deletion request +type DelRequest struct { + // DN is the name of the directory entry to delete + DN string + // Controls hold optional controls to send with the request + Controls []Control +} + +func (req *DelRequest) appendTo(envelope *ber.Packet) error { + pkt := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, req.DN, "Del Request") + pkt.Data.Write([]byte(req.DN)) + + envelope.AppendChild(pkt) + if len(req.Controls) > 0 { + envelope.AppendChild(encodeControls(req.Controls)) + } + + return nil +} + +// NewDelRequest creates a delete request for the given DN and controls +func NewDelRequest(DN string, Controls []Control) *DelRequest { + return &DelRequest{ + DN: DN, + Controls: Controls, + } +} + +// Del executes the given delete request +func (l *Conn) Del(delRequest *DelRequest) error { + msgCtx, err := l.doRequest(delRequest) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + packet, err := l.readPacket(msgCtx) + if err != nil { + return err + } + + if packet.Children[1].Tag == ApplicationDelResponse { + err := GetLDAPError(packet) + if err != nil { + return err + } + } else { + log.Printf("Unexpected Response: %d", packet.Children[1].Tag) + } + return nil +} diff --git a/vendor/gopkg.in/ldap.v3/dn.go b/vendor/github.com/go-ldap/ldap/v3/dn.go similarity index 79% rename from vendor/gopkg.in/ldap.v3/dn.go rename to vendor/github.com/go-ldap/ldap/v3/dn.go index f89e73a9a..bff137cc8 100644 --- a/vendor/gopkg.in/ldap.v3/dn.go +++ b/vendor/github.com/go-ldap/ldap/v3/dn.go @@ -1,44 +1,3 @@ -// File contains DN parsing functionality -// -// https://tools.ietf.org/html/rfc4514 -// -// distinguishedName = [ relativeDistinguishedName -// *( COMMA relativeDistinguishedName ) ] -// relativeDistinguishedName = attributeTypeAndValue -// *( PLUS attributeTypeAndValue ) -// attributeTypeAndValue = attributeType EQUALS attributeValue -// attributeType = descr / numericoid -// attributeValue = string / hexstring -// -// ; The following characters are to be escaped when they appear -// ; in the value to be encoded: ESC, one of , leading -// ; SHARP or SPACE, trailing SPACE, and NULL. -// string = [ ( leadchar / pair ) [ *( stringchar / pair ) -// ( trailchar / pair ) ] ] -// -// leadchar = LUTF1 / UTFMB -// LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A / -// %x3D / %x3F-5B / %x5D-7F -// -// trailchar = TUTF1 / UTFMB -// TUTF1 = %x01-1F / %x21 / %x23-2A / %x2D-3A / -// %x3D / %x3F-5B / %x5D-7F -// -// stringchar = SUTF1 / UTFMB -// SUTF1 = %x01-21 / %x23-2A / %x2D-3A / -// %x3D / %x3F-5B / %x5D-7F -// -// pair = ESC ( ESC / special / hexpair ) -// special = escaped / SPACE / SHARP / EQUALS -// escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE -// hexstring = SHARP 1*hexpair -// hexpair = HEX HEX -// -// where the productions , , , , -// , , , , , , , , -// , , and are defined in [RFC4512]. -// - package ldap import ( @@ -48,7 +7,7 @@ import ( "fmt" "strings" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) // AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514 @@ -69,7 +28,8 @@ type DN struct { RDNs []*RelativeDN } -// ParseDN returns a distinguishedName or an error +// ParseDN returns a distinguishedName or an error. +// The function respects https://tools.ietf.org/html/rfc4514 func ParseDN(str string) (*DN, error) { dn := new(DN) dn.RDNs = make([]*RelativeDN, 0) diff --git a/vendor/gopkg.in/ldap.v3/doc.go b/vendor/github.com/go-ldap/ldap/v3/doc.go similarity index 100% rename from vendor/gopkg.in/ldap.v3/doc.go rename to vendor/github.com/go-ldap/ldap/v3/doc.go diff --git a/vendor/gopkg.in/ldap.v3/error.go b/vendor/github.com/go-ldap/ldap/v3/error.go similarity index 94% rename from vendor/gopkg.in/ldap.v3/error.go rename to vendor/github.com/go-ldap/ldap/v3/error.go index 639ed8243..3cdb7b318 100644 --- a/vendor/gopkg.in/ldap.v3/error.go +++ b/vendor/github.com/go-ldap/ldap/v3/error.go @@ -3,7 +3,7 @@ package ldap import ( "fmt" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) // LDAP Result Codes @@ -184,6 +184,8 @@ type Error struct { ResultCode uint16 // MatchedDN is the matchedDN returned if any MatchedDN string + // Packet is the returned packet if any + Packet *ber.Packet } func (e *Error) Error() string { @@ -196,22 +198,28 @@ func (e *Error) Error() string { func GetLDAPError(packet *ber.Packet) error { if packet == nil { return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty packet")} - } else if len(packet.Children) >= 2 { + } + + if len(packet.Children) >= 2 { response := packet.Children[1] if response == nil { - return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty response in packet")} + return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty response in packet"), Packet: packet} } if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 { resultCode := uint16(response.Children[0].Value.(int64)) if resultCode == 0 { // No error return nil } - return &Error{ResultCode: resultCode, MatchedDN: response.Children[1].Value.(string), - Err: fmt.Errorf("%s", response.Children[2].Value.(string))} + return &Error{ + ResultCode: resultCode, + MatchedDN: response.Children[1].Value.(string), + Err: fmt.Errorf("%s", response.Children[2].Value.(string)), + Packet: packet, + } } } - return &Error{ResultCode: ErrorNetwork, Err: fmt.Errorf("Invalid packet format")} + return &Error{ResultCode: ErrorNetwork, Err: fmt.Errorf("Invalid packet format"), Packet: packet} } // NewError creates an LDAP error with the given code and underlying error @@ -219,8 +227,8 @@ func NewError(resultCode uint16, err error) error { return &Error{ResultCode: resultCode, Err: err} } -// IsErrorWithCode returns true if the given error is an LDAP error with the given result code -func IsErrorWithCode(err error, desiredResultCode uint16) bool { +// IsErrorAnyOf returns true if the given error is an LDAP error with any one of the given result codes +func IsErrorAnyOf(err error, codes ...uint16) bool { if err == nil { return false } @@ -230,5 +238,16 @@ func IsErrorWithCode(err error, desiredResultCode uint16) bool { return false } - return serverError.ResultCode == desiredResultCode + for _, code := range codes { + if serverError.ResultCode == code { + return true + } + } + + return false +} + +// IsErrorWithCode returns true if the given error is an LDAP error with the given result code +func IsErrorWithCode(err error, desiredResultCode uint16) bool { + return IsErrorAnyOf(err, desiredResultCode) } diff --git a/vendor/gopkg.in/ldap.v3/filter.go b/vendor/github.com/go-ldap/ldap/v3/filter.go similarity index 75% rename from vendor/gopkg.in/ldap.v3/filter.go rename to vendor/github.com/go-ldap/ldap/v3/filter.go index 4cc4207be..73505e79b 100644 --- a/vendor/gopkg.in/ldap.v3/filter.go +++ b/vendor/github.com/go-ldap/ldap/v3/filter.go @@ -5,10 +5,12 @@ import ( hexpac "encoding/hex" "errors" "fmt" + "io" "strings" + "unicode" "unicode/utf8" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) // Filter choices @@ -69,6 +71,8 @@ var MatchingRuleAssertionMap = map[uint64]string{ MatchingRuleAssertionDNAttributes: "Matching Rule Assertion DN Attributes", } +var _SymbolAny = []byte{'*'} + // CompileFilter converts a string representation of a filter into a BER-encoded packet func CompileFilter(filter string) (*ber.Packet, error) { if len(filter) == 0 || filter[0] != '(' { @@ -88,74 +92,75 @@ func CompileFilter(filter string) (*ber.Packet, error) { } // DecompileFilter converts a packet representation of a filter into a string representation -func DecompileFilter(packet *ber.Packet) (ret string, err error) { +func DecompileFilter(packet *ber.Packet) (_ string, err error) { defer func() { if r := recover(); r != nil { err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter")) } }() - ret = "(" - err = nil + + buf := bytes.NewBuffer(nil) + buf.WriteByte('(') childStr := "" switch packet.Tag { case FilterAnd: - ret += "&" + buf.WriteByte('&') for _, child := range packet.Children { childStr, err = DecompileFilter(child) if err != nil { return } - ret += childStr + buf.WriteString(childStr) } case FilterOr: - ret += "|" + buf.WriteByte('|') for _, child := range packet.Children { childStr, err = DecompileFilter(child) if err != nil { return } - ret += childStr + buf.WriteString(childStr) } case FilterNot: - ret += "!" + buf.WriteByte('!') childStr, err = DecompileFilter(packet.Children[0]) if err != nil { return } - ret += childStr + buf.WriteString(childStr) case FilterSubstrings: - ret += ber.DecodeString(packet.Children[0].Data.Bytes()) - ret += "=" + buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteByte('=') for i, child := range packet.Children[1].Children { if i == 0 && child.Tag != FilterSubstringsInitial { - ret += "*" + buf.Write(_SymbolAny) } - ret += EscapeFilter(ber.DecodeString(child.Data.Bytes())) + buf.WriteString(EscapeFilter(ber.DecodeString(child.Data.Bytes()))) if child.Tag != FilterSubstringsFinal { - ret += "*" + buf.Write(_SymbolAny) } } case FilterEqualityMatch: - ret += ber.DecodeString(packet.Children[0].Data.Bytes()) - ret += "=" - ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())) + buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteByte('=') + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))) case FilterGreaterOrEqual: - ret += ber.DecodeString(packet.Children[0].Data.Bytes()) - ret += ">=" - ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())) + buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteString(">=") + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))) case FilterLessOrEqual: - ret += ber.DecodeString(packet.Children[0].Data.Bytes()) - ret += "<=" - ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())) + buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteString("<=") + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))) case FilterPresent: - ret += ber.DecodeString(packet.Data.Bytes()) - ret += "=*" + buf.WriteString(ber.DecodeString(packet.Data.Bytes())) + buf.WriteString("=*") case FilterApproxMatch: - ret += ber.DecodeString(packet.Children[0].Data.Bytes()) - ret += "~=" - ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())) + buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteString("~=") + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))) case FilterExtensibleMatch: attr := "" dnAttributes := false @@ -176,21 +181,22 @@ func DecompileFilter(packet *ber.Packet) (ret string, err error) { } if len(attr) > 0 { - ret += attr + buf.WriteString(attr) } if dnAttributes { - ret += ":dn" + buf.WriteString(":dn") } if len(matchingRule) > 0 { - ret += ":" - ret += matchingRule + buf.WriteString(":") + buf.WriteString(matchingRule) } - ret += ":=" - ret += EscapeFilter(value) + buf.WriteString(":=") + buf.WriteString(EscapeFilter(value)) } - ret += ")" - return + buf.WriteByte(')') + + return buf.String(), nil } func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) { @@ -253,11 +259,10 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { ) state := stateReadingAttr - - attribute := "" + attribute := bytes.NewBuffer(nil) extensibleDNAttributes := false - extensibleMatchingRule := "" - condition := "" + extensibleMatchingRule := bytes.NewBuffer(nil) + condition := bytes.NewBuffer(nil) for newPos < len(filter) { remainingFilter := filter[newPos:] @@ -324,7 +329,7 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { // Still reading the attribute name default: - attribute += fmt.Sprintf("%c", currentRune) + attribute.WriteRune(currentRune) newPos += currentWidth } @@ -338,13 +343,13 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { // Still reading the matching rule oid default: - extensibleMatchingRule += fmt.Sprintf("%c", currentRune) + extensibleMatchingRule.WriteRune(currentRune) newPos += currentWidth } case stateReadingCondition: // append to the condition - condition += fmt.Sprintf("%c", currentRune) + condition.WriteRune(currentRune) newPos += currentWidth } } @@ -368,17 +373,17 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { // } // Include the matching rule oid, if specified - if len(extensibleMatchingRule) > 0 { - packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule, MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule])) + if extensibleMatchingRule.Len() > 0 { + packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule.String(), MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule])) } // Include the attribute, if specified - if len(attribute) > 0 { - packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute, MatchingRuleAssertionMap[MatchingRuleAssertionType])) + if attribute.Len() > 0 { + packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute.String(), MatchingRuleAssertionMap[MatchingRuleAssertionType])) } // Add the value (only required child) - encodedString, encodeErr := escapedStringToEncodedBytes(condition) + encodedString, encodeErr := decodeEscapedSymbols(condition.Bytes()) if encodeErr != nil { return packet, newPos, encodeErr } @@ -389,16 +394,16 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { packet.AppendChild(ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionDNAttributes, extensibleDNAttributes, MatchingRuleAssertionMap[MatchingRuleAssertionDNAttributes])) } - case packet.Tag == FilterEqualityMatch && condition == "*": - packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute, FilterMap[FilterPresent]) - case packet.Tag == FilterEqualityMatch && strings.Contains(condition, "*"): - packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) + case packet.Tag == FilterEqualityMatch && bytes.Equal(condition.Bytes(), _SymbolAny): + packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute.String(), FilterMap[FilterPresent]) + case packet.Tag == FilterEqualityMatch && bytes.Index(condition.Bytes(), _SymbolAny) > -1: + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute.String(), "Attribute")) packet.Tag = FilterSubstrings packet.Description = FilterMap[uint64(packet.Tag)] seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings") - parts := strings.Split(condition, "*") + parts := bytes.Split(condition.Bytes(), _SymbolAny) for i, part := range parts { - if part == "" { + if len(part) == 0 { continue } var tag ber.Tag @@ -410,7 +415,7 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { default: tag = FilterSubstringsAny } - encodedString, encodeErr := escapedStringToEncodedBytes(part) + encodedString, encodeErr := decodeEscapedSymbols(part) if encodeErr != nil { return packet, newPos, encodeErr } @@ -418,11 +423,11 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { } packet.AppendChild(seq) default: - encodedString, encodeErr := escapedStringToEncodedBytes(condition) + encodedString, encodeErr := decodeEscapedSymbols(condition.Bytes()) if encodeErr != nil { return packet, newPos, encodeErr } - packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute.String(), "Attribute")) packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, encodedString, "Condition")) } @@ -432,34 +437,51 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { } // Convert from "ABC\xx\xx\xx" form to literal bytes for transport -func escapedStringToEncodedBytes(escapedString string) (string, error) { - var buffer bytes.Buffer - i := 0 - for i < len(escapedString) { - currentRune, currentWidth := utf8.DecodeRuneInString(escapedString[i:]) - if currentRune == utf8.RuneError { - return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", i)) +func decodeEscapedSymbols(src []byte) (string, error) { + + var ( + buffer bytes.Buffer + offset int + reader = bytes.NewReader(src) + byteHex []byte + byteVal []byte + ) + + for { + runeVal, runeSize, err := reader.ReadRune() + if err == io.EOF { + return buffer.String(), nil + } else if err != nil { + return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: failed to read filter: %v", err)) + } else if runeVal == unicode.ReplacementChar { + return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", offset)) } - // Check for escaped hex characters and convert them to their literal value for transport. - if currentRune == '\\' { + if runeVal == '\\' { // http://tools.ietf.org/search/rfc4515 // \ (%x5C) is not a valid character unless it is followed by two HEX characters due to not // being a member of UTF1SUBSET. - if i+2 > len(escapedString) { - return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter")) + if byteHex == nil { + byteHex = make([]byte, 2) + byteVal = make([]byte, 1) + } + + if _, err := io.ReadFull(reader, byteHex); err != nil { + if err == io.ErrUnexpectedEOF { + return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter")) + } + return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: invalid characters for escape in filter: %v", err)) } - escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3]) - if decodeErr != nil { - return "", NewError(ErrorFilterCompile, errors.New("ldap: invalid characters for escape in filter")) + + if _, err := hexpac.Decode(byteVal, byteHex); err != nil { + return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: invalid characters for escape in filter: %v", err)) } - buffer.WriteByte(escByte[0]) - i += 2 // +1 from end of loop, so 3 total for \xx. + + buffer.Write(byteVal) } else { - buffer.WriteRune(currentRune) + buffer.WriteRune(runeVal) } - i += currentWidth + offset += runeSize } - return buffer.String(), nil } diff --git a/vendor/github.com/go-ldap/ldap/v3/go.mod b/vendor/github.com/go-ldap/ldap/v3/go.mod new file mode 100644 index 000000000..931e5967d --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/v3/go.mod @@ -0,0 +1,9 @@ +module github.com/go-ldap/ldap/v3 + +go 1.13 + +require ( + github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c + github.com/go-asn1-ber/asn1-ber v1.5.1 + golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect +) diff --git a/vendor/github.com/go-ldap/ldap/v3/go.sum b/vendor/github.com/go-ldap/ldap/v3/go.sum new file mode 100644 index 000000000..0d8a4f681 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/v3/go.sum @@ -0,0 +1,11 @@ +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= +github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/gopkg.in/ldap.v3/ldap.go b/vendor/github.com/go-ldap/ldap/v3/ldap.go similarity index 90% rename from vendor/gopkg.in/ldap.v3/ldap.go rename to vendor/github.com/go-ldap/ldap/v3/ldap.go index d7666676f..7ae6dfe2c 100644 --- a/vendor/gopkg.in/ldap.v3/ldap.go +++ b/vendor/github.com/go-ldap/ldap/v3/ldap.go @@ -1,12 +1,11 @@ package ldap import ( - "errors" "fmt" "io/ioutil" "os" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) // LDAP Application Codes @@ -87,7 +86,7 @@ var BeheraPasswordPolicyErrorMap = map[int8]string{ func addLDAPDescriptions(packet *ber.Packet) (err error) { defer func() { if r := recover(); r != nil { - err = NewError(ErrorDebugging, errors.New("ldap: cannot process packet to add descriptions")) + err = NewError(ErrorDebugging, fmt.Errorf("ldap: cannot process packet to add descriptions: %s", r)) } }() packet.Description = "LDAP Response" @@ -224,32 +223,26 @@ func addControlDescriptions(packet *ber.Packet) error { if child.Tag == 0 { //Warning warningPacket := child.Children[0] - packet, err := ber.DecodePacketErr(warningPacket.Data.Bytes()) + val, err := ber.ParseInt64(warningPacket.Data.Bytes()) if err != nil { return fmt.Errorf("failed to decode data bytes: %s", err) } - val, ok := packet.Value.(int64) - if ok { - if warningPacket.Tag == 0 { - //timeBeforeExpiration - value.Description += " (TimeBeforeExpiration)" - warningPacket.Value = val - } else if warningPacket.Tag == 1 { - //graceAuthNsRemaining - value.Description += " (GraceAuthNsRemaining)" - warningPacket.Value = val - } + if warningPacket.Tag == 0 { + //timeBeforeExpiration + value.Description += " (TimeBeforeExpiration)" + warningPacket.Value = val + } else if warningPacket.Tag == 1 { + //graceAuthNsRemaining + value.Description += " (GraceAuthNsRemaining)" + warningPacket.Value = val } } else if child.Tag == 1 { // Error - packet, err := ber.DecodePacketErr(child.Data.Bytes()) - if err != nil { - return fmt.Errorf("failed to decode data bytes: %s", err) - } - val, ok := packet.Value.(int8) - if !ok { - val = -1 + bs := child.Data.Bytes() + if len(bs) != 1 || bs[0] > 8 { + return fmt.Errorf("failed to decode data bytes: %s", "invalid PasswordPolicyResponse enum value") } + val := int8(bs[0]) child.Description = "Error" child.Value = val } @@ -270,10 +263,18 @@ func addRequestDescriptions(packet *ber.Packet) error { } func addDefaultLDAPResponseDescriptions(packet *ber.Packet) error { - err := GetLDAPError(packet) - packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[err.(*Error).ResultCode] + ")" - packet.Children[1].Children[1].Description = "Matched DN (" + err.(*Error).MatchedDN + ")" - packet.Children[1].Children[2].Description = "Error Message" + resultCode := uint16(LDAPResultSuccess) + matchedDN := "" + description := "Success" + if err := GetLDAPError(packet); err != nil { + resultCode = err.(*Error).ResultCode + matchedDN = err.(*Error).MatchedDN + description = "Error Message" + } + + packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")" + packet.Children[1].Children[1].Description = "Matched DN (" + matchedDN + ")" + packet.Children[1].Children[2].Description = description if len(packet.Children[1].Children) > 3 { packet.Children[1].Children[3].Description = "Referral" } diff --git a/vendor/github.com/go-ldap/ldap/v3/moddn.go b/vendor/github.com/go-ldap/ldap/v3/moddn.go new file mode 100644 index 000000000..b4865f8af --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/v3/moddn.go @@ -0,0 +1,80 @@ +package ldap + +import ( + "log" + + ber "github.com/go-asn1-ber/asn1-ber" +) + +// ModifyDNRequest holds the request to modify a DN +type ModifyDNRequest struct { + DN string + NewRDN string + DeleteOldRDN bool + NewSuperior string +} + +// NewModifyDNRequest creates a new request which can be passed to ModifyDN(). +// +// To move an object in the tree, set the "newSup" to the new parent entry DN. Use an +// empty string for just changing the object's RDN. +// +// For moving the object without renaming, the "rdn" must be the first +// RDN of the given DN. +// +// A call like +// mdnReq := NewModifyDNRequest("uid=someone,dc=example,dc=org", "uid=newname", true, "") +// will setup the request to just rename uid=someone,dc=example,dc=org to +// uid=newname,dc=example,dc=org. +func NewModifyDNRequest(dn string, rdn string, delOld bool, newSup string) *ModifyDNRequest { + return &ModifyDNRequest{ + DN: dn, + NewRDN: rdn, + DeleteOldRDN: delOld, + NewSuperior: newSup, + } +} + +func (req *ModifyDNRequest) appendTo(envelope *ber.Packet) error { + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyDNRequest, nil, "Modify DN Request") + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN")) + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.NewRDN, "New RDN")) + if req.DeleteOldRDN { + buf := []byte{0xff} + pkt.AppendChild(ber.NewString(ber.ClassUniversal,ber.TypePrimitive,ber.TagBoolean, string(buf),"Delete old RDN")) + }else{ + pkt.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, req.DeleteOldRDN, "Delete old RDN")) + } + if req.NewSuperior != "" { + pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.NewSuperior, "New Superior")) + } + + envelope.AppendChild(pkt) + + return nil +} + +// ModifyDN renames the given DN and optionally move to another base (when the "newSup" argument +// to NewModifyDNRequest() is not ""). +func (l *Conn) ModifyDN(m *ModifyDNRequest) error { + msgCtx, err := l.doRequest(m) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + packet, err := l.readPacket(msgCtx) + if err != nil { + return err + } + + if packet.Children[1].Tag == ApplicationModifyDNResponse { + err := GetLDAPError(packet) + if err != nil { + return err + } + } else { + log.Printf("Unexpected Response: %d", packet.Children[1].Tag) + } + return nil +} diff --git a/vendor/github.com/go-ldap/ldap/v3/modify.go b/vendor/github.com/go-ldap/ldap/v3/modify.go new file mode 100644 index 000000000..ee712890a --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/v3/modify.go @@ -0,0 +1,132 @@ +package ldap + +import ( + "log" + + ber "github.com/go-asn1-ber/asn1-ber" +) + +// Change operation choices +const ( + AddAttribute = 0 + DeleteAttribute = 1 + ReplaceAttribute = 2 + IncrementAttribute = 3 // (https://tools.ietf.org/html/rfc4525) +) + +// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 +type PartialAttribute struct { + // Type is the type of the partial attribute + Type string + // Vals are the values of the partial attribute + Vals []string +} + +func (p *PartialAttribute) encode() *ber.Packet { + seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute") + seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type")) + set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue") + for _, value := range p.Vals { + set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals")) + } + seq.AppendChild(set) + return seq +} + +// Change for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 +type Change struct { + // Operation is the type of change to be made + Operation uint + // Modification is the attribute to be modified + Modification PartialAttribute +} + +func (c *Change) encode() *ber.Packet { + change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") + change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(c.Operation), "Operation")) + change.AppendChild(c.Modification.encode()) + return change +} + +// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 +type ModifyRequest struct { + // DN is the distinguishedName of the directory entry to modify + DN string + // Changes contain the attributes to modify + Changes []Change + // Controls hold optional controls to send with the request + Controls []Control +} + +// Add appends the given attribute to the list of changes to be made +func (req *ModifyRequest) Add(attrType string, attrVals []string) { + req.appendChange(AddAttribute, attrType, attrVals) +} + +// Delete appends the given attribute to the list of changes to be made +func (req *ModifyRequest) Delete(attrType string, attrVals []string) { + req.appendChange(DeleteAttribute, attrType, attrVals) +} + +// Replace appends the given attribute to the list of changes to be made +func (req *ModifyRequest) Replace(attrType string, attrVals []string) { + req.appendChange(ReplaceAttribute, attrType, attrVals) +} + +// Increment appends the given attribute to the list of changes to be made +func (req *ModifyRequest) Increment(attrType string, attrVal string) { + req.appendChange(IncrementAttribute, attrType, []string{attrVal}) +} + +func (req *ModifyRequest) appendChange(operation uint, attrType string, attrVals []string) { + req.Changes = append(req.Changes, Change{operation, PartialAttribute{Type: attrType, Vals: attrVals}}) +} + +func (req *ModifyRequest) appendTo(envelope *ber.Packet) error { + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request") + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN")) + changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes") + for _, change := range req.Changes { + changes.AppendChild(change.encode()) + } + pkt.AppendChild(changes) + + envelope.AppendChild(pkt) + if len(req.Controls) > 0 { + envelope.AppendChild(encodeControls(req.Controls)) + } + + return nil +} + +// NewModifyRequest creates a modify request for the given DN +func NewModifyRequest(dn string, controls []Control) *ModifyRequest { + return &ModifyRequest{ + DN: dn, + Controls: controls, + } +} + +// Modify performs the ModifyRequest +func (l *Conn) Modify(modifyRequest *ModifyRequest) error { + msgCtx, err := l.doRequest(modifyRequest) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + packet, err := l.readPacket(msgCtx) + if err != nil { + return err + } + + if packet.Children[1].Tag == ApplicationModifyResponse { + err := GetLDAPError(packet) + if err != nil { + return err + } + } else { + log.Printf("Unexpected Response: %d", packet.Children[1].Tag) + } + return nil +} diff --git a/vendor/gopkg.in/ldap.v3/passwdmodify.go b/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go similarity index 67% rename from vendor/gopkg.in/ldap.v3/passwdmodify.go rename to vendor/github.com/go-ldap/ldap/v3/passwdmodify.go index 06bc21db1..62a110843 100644 --- a/vendor/gopkg.in/ldap.v3/passwdmodify.go +++ b/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go @@ -1,15 +1,9 @@ -// This file contains the password modify extended operation as specified in rfc 3062 -// -// https://tools.ietf.org/html/rfc3062 -// - package ldap import ( - "errors" "fmt" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) const ( @@ -36,30 +30,33 @@ type PasswordModifyResult struct { Referral string } -func (r *PasswordModifyRequest) encode() (*ber.Packet, error) { - request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation") - request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID")) +func (req *PasswordModifyRequest) appendTo(envelope *ber.Packet) error { + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation") + pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID")) + extendedRequestValue := ber.Encode(ber.ClassContext, ber.TypePrimitive, 1, nil, "Extended Request Value: Password Modify Request") passwordModifyRequestValue := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Password Modify Request") - if r.UserIdentity != "" { - passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, r.UserIdentity, "User Identity")) + if req.UserIdentity != "" { + passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.UserIdentity, "User Identity")) } - if r.OldPassword != "" { - passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, r.OldPassword, "Old Password")) + if req.OldPassword != "" { + passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, req.OldPassword, "Old Password")) } - if r.NewPassword != "" { - passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, r.NewPassword, "New Password")) + if req.NewPassword != "" { + passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, req.NewPassword, "New Password")) } - extendedRequestValue.AppendChild(passwordModifyRequestValue) - request.AppendChild(extendedRequestValue) - return request, nil + pkt.AppendChild(extendedRequestValue) + + envelope.AppendChild(pkt) + + return nil } // NewPasswordModifyRequest creates a new PasswordModifyRequest // -// According to the RFC 3602: +// According to the RFC 3602 (https://tools.ietf.org/html/rfc3062): // userIdentity is a string representing the user associated with the request. // This string may or may not be an LDAPDN (RFC 2253). // If userIdentity is empty then the operation will act on the user associated @@ -84,46 +81,18 @@ func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPasswo // PasswordModify performs the modification request func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) { - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) - - encodedPasswordModifyRequest, err := passwordModifyRequest.encode() - if err != nil { - return nil, err - } - packet.AppendChild(encodedPasswordModifyRequest) - - l.Debug.PrintPacket(packet) - - msgCtx, err := l.sendMessage(packet) + msgCtx, err := l.doRequest(passwordModifyRequest) if err != nil { return nil, err } defer l.finishMessage(msgCtx) - result := &PasswordModifyResult{} - - l.Debug.Printf("%d: waiting for response", msgCtx.id) - packetResponse, ok := <-msgCtx.responses - if !ok { - return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) - } - packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + packet, err := l.readPacket(msgCtx) if err != nil { return nil, err } - if packet == nil { - return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message")) - } - - if l.Debug { - if err := addLDAPDescriptions(packet); err != nil { - return nil, err - } - ber.PrintPacket(packet) - } + result := &PasswordModifyResult{} if packet.Children[1].Tag == ApplicationExtendedResponse { err := GetLDAPError(packet) diff --git a/vendor/github.com/go-ldap/ldap/v3/request.go b/vendor/github.com/go-ldap/ldap/v3/request.go new file mode 100644 index 000000000..8c68f34aa --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/v3/request.go @@ -0,0 +1,66 @@ +package ldap + +import ( + "errors" + + ber "github.com/go-asn1-ber/asn1-ber" +) + +var ( + errRespChanClosed = errors.New("ldap: response channel closed") + errCouldNotRetMsg = errors.New("ldap: could not retrieve message") +) + +type request interface { + appendTo(*ber.Packet) error +} + +type requestFunc func(*ber.Packet) error + +func (f requestFunc) appendTo(p *ber.Packet) error { + return f(p) +} + +func (l *Conn) doRequest(req request) (*messageContext, error) { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + if err := req.appendTo(packet); err != nil { + return nil, err + } + + if l.Debug { + l.Debug.PrintPacket(packet) + } + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return nil, err + } + l.Debug.Printf("%d: returning", msgCtx.id) + return msgCtx, nil +} + +func (l *Conn) readPacket(msgCtx *messageContext) (*ber.Packet, error) { + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses + if !ok { + return nil, NewError(ErrorNetwork, errRespChanClosed) + } + packet, err := packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return nil, err + } + + if packet == nil { + return nil, NewError(ErrorNetwork, errCouldNotRetMsg) + } + + if l.Debug { + if err = addLDAPDescriptions(packet); err != nil { + return nil, err + } + l.Debug.PrintPacket(packet) + } + return packet, nil +} diff --git a/vendor/gopkg.in/ldap.v3/search.go b/vendor/github.com/go-ldap/ldap/v3/search.go similarity index 67% rename from vendor/gopkg.in/ldap.v3/search.go rename to vendor/github.com/go-ldap/ldap/v3/search.go index 3aa6dac0c..4fcc794a5 100644 --- a/vendor/gopkg.in/ldap.v3/search.go +++ b/vendor/github.com/go-ldap/ldap/v3/search.go @@ -1,58 +1,3 @@ -// File contains Search functionality -// -// https://tools.ietf.org/html/rfc4511 -// -// SearchRequest ::= [APPLICATION 3] SEQUENCE { -// baseObject LDAPDN, -// scope ENUMERATED { -// baseObject (0), -// singleLevel (1), -// wholeSubtree (2), -// ... }, -// derefAliases ENUMERATED { -// neverDerefAliases (0), -// derefInSearching (1), -// derefFindingBaseObj (2), -// derefAlways (3) }, -// sizeLimit INTEGER (0 .. maxInt), -// timeLimit INTEGER (0 .. maxInt), -// typesOnly BOOLEAN, -// filter Filter, -// attributes AttributeSelection } -// -// AttributeSelection ::= SEQUENCE OF selector LDAPString -// -- The LDAPString is constrained to -// -- in Section 4.5.1.8 -// -// Filter ::= CHOICE { -// and [0] SET SIZE (1..MAX) OF filter Filter, -// or [1] SET SIZE (1..MAX) OF filter Filter, -// not [2] Filter, -// equalityMatch [3] AttributeValueAssertion, -// substrings [4] SubstringFilter, -// greaterOrEqual [5] AttributeValueAssertion, -// lessOrEqual [6] AttributeValueAssertion, -// present [7] AttributeDescription, -// approxMatch [8] AttributeValueAssertion, -// extensibleMatch [9] MatchingRuleAssertion, -// ... } -// -// SubstringFilter ::= SEQUENCE { -// type AttributeDescription, -// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { -// initial [0] AssertionValue, -- can occur at most once -// any [1] AssertionValue, -// final [2] AssertionValue } -- can occur at most once -// } -// -// MatchingRuleAssertion ::= SEQUENCE { -// matchingRule [1] MatchingRuleId OPTIONAL, -// type [2] AttributeDescription OPTIONAL, -// matchValue [3] AssertionValue, -// dnAttributes [4] BOOLEAN DEFAULT FALSE } -// -// - package ldap import ( @@ -61,7 +6,7 @@ import ( "sort" "strings" - "gopkg.in/asn1-ber.v1" + ber "github.com/go-asn1-ber/asn1-ber" ) // scope choices @@ -132,6 +77,17 @@ func (e *Entry) GetAttributeValues(attribute string) []string { return []string{} } +// GetEqualFoldAttributeValues returns the values for the named attribute, or an +// empty list. Attribute matching is done with strings.EqualFold. +func (e *Entry) GetEqualFoldAttributeValues(attribute string) []string { + for _, attr := range e.Attributes { + if strings.EqualFold(attribute, attr.Name) { + return attr.Values + } + } + return []string{} +} + // GetRawAttributeValues returns the byte values for the named attribute, or an empty list func (e *Entry) GetRawAttributeValues(attribute string) [][]byte { for _, attr := range e.Attributes { @@ -142,6 +98,16 @@ func (e *Entry) GetRawAttributeValues(attribute string) [][]byte { return [][]byte{} } +// GetEqualFoldRawAttributeValues returns the byte values for the named attribute, or an empty list +func (e *Entry) GetEqualFoldRawAttributeValues(attribute string) [][]byte { + for _, attr := range e.Attributes { + if strings.EqualFold(attr.Name, attribute) { + return attr.ByteValues + } + } + return [][]byte{} +} + // GetAttributeValue returns the first value for the named attribute, or "" func (e *Entry) GetAttributeValue(attribute string) string { values := e.GetAttributeValues(attribute) @@ -151,6 +117,16 @@ func (e *Entry) GetAttributeValue(attribute string) string { return values[0] } +// GetEqualFoldAttributeValue returns the first value for the named attribute, or "". +// Attribute comparison is done with strings.EqualFold. +func (e *Entry) GetEqualFoldAttributeValue(attribute string) string { + values := e.GetEqualFoldAttributeValues(attribute) + if len(values) == 0 { + return "" + } + return values[0] +} + // GetRawAttributeValue returns the first value for the named attribute, or an empty slice func (e *Entry) GetRawAttributeValue(attribute string) []byte { values := e.GetRawAttributeValues(attribute) @@ -160,6 +136,15 @@ func (e *Entry) GetRawAttributeValue(attribute string) []byte { return values[0] } +// GetEqualFoldRawAttributeValue returns the first value for the named attribute, or an empty slice +func (e *Entry) GetEqualFoldRawAttributeValue(attribute string) []byte { + values := e.GetEqualFoldRawAttributeValues(attribute) + if len(values) == 0 { + return []byte{} + } + return values[0] +} + // Print outputs a human-readable description func (e *Entry) Print() { fmt.Printf("DN: %s\n", e.DN) @@ -246,27 +231,33 @@ type SearchRequest struct { Controls []Control } -func (s *SearchRequest) encode() (*ber.Packet, error) { - request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request") - request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, s.BaseDN, "Base DN")) - request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.Scope), "Scope")) - request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.DerefAliases), "Deref Aliases")) - request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.SizeLimit), "Size Limit")) - request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.TimeLimit), "Time Limit")) - request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, s.TypesOnly, "Types Only")) +func (req *SearchRequest) appendTo(envelope *ber.Packet) error { + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request") + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.BaseDN, "Base DN")) + pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(req.Scope), "Scope")) + pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(req.DerefAliases), "Deref Aliases")) + pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(req.SizeLimit), "Size Limit")) + pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(req.TimeLimit), "Time Limit")) + pkt.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, req.TypesOnly, "Types Only")) // compile and encode filter - filterPacket, err := CompileFilter(s.Filter) + filterPacket, err := CompileFilter(req.Filter) if err != nil { - return nil, err + return err } - request.AppendChild(filterPacket) + pkt.AppendChild(filterPacket) // encode attributes attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes") - for _, attribute := range s.Attributes { + for _, attribute := range req.Attributes { attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) } - request.AppendChild(attributesPacket) - return request, nil + pkt.AppendChild(attributesPacket) + + envelope.AppendChild(pkt) + if len(req.Controls) > 0 { + envelope.AppendChild(encodeControls(req.Controls)) + } + + return nil } // NewSearchRequest creates a new search request @@ -366,22 +357,7 @@ func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) // Search performs the given search request func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) - // encode search request - encodedSearchRequest, err := searchRequest.encode() - if err != nil { - return nil, err - } - packet.AppendChild(encodedSearchRequest) - // encode search controls - if len(searchRequest.Controls) > 0 { - packet.AppendChild(encodeControls(searchRequest.Controls)) - } - - l.Debug.PrintPacket(packet) - - msgCtx, err := l.sendMessage(packet) + msgCtx, err := l.doRequest(searchRequest) if err != nil { return nil, err } @@ -392,24 +368,10 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { Referrals: make([]string, 0), Controls: make([]Control, 0)} - foundSearchResultDone := false - for !foundSearchResultDone { - l.Debug.Printf("%d: waiting for response", msgCtx.id) - packetResponse, ok := <-msgCtx.responses - if !ok { - return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) - } - packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + for { + packet, err := l.readPacket(msgCtx) if err != nil { - return nil, err - } - - if l.Debug { - if err := addLDAPDescriptions(packet); err != nil { - return nil, err - } - ber.PrintPacket(packet) + return result, err } switch packet.Children[1].Tag { @@ -429,22 +391,20 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { case 5: err := GetLDAPError(packet) if err != nil { - return nil, err + return result, err } if len(packet.Children) == 3 { for _, child := range packet.Children[2].Children { decodedChild, err := DecodeControl(child) if err != nil { - return nil, fmt.Errorf("failed to decode child control: %s", err) + return result, fmt.Errorf("failed to decode child control: %s", err) } result.Controls = append(result.Controls, decodedChild) } } - foundSearchResultDone = true + return result, nil case 19: result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string)) } } - l.Debug.Printf("%d: returning", msgCtx.id) - return result, nil } diff --git a/vendor/gopkg.in/asn1-ber.v1/.travis.yml b/vendor/gopkg.in/asn1-ber.v1/.travis.yml deleted file mode 100644 index 44aa48b87..000000000 --- a/vendor/gopkg.in/asn1-ber.v1/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go -go: - - 1.2 - - 1.3 - - 1.4 - - 1.5 - - tip -go_import_path: gopkg.in/asn-ber.v1 -install: - - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v - - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v - - go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover - - go build -v ./... -script: - - go test -v -cover ./... diff --git a/vendor/gopkg.in/asn1-ber.v1/LICENSE b/vendor/gopkg.in/asn1-ber.v1/LICENSE deleted file mode 100644 index 744875676..000000000 --- a/vendor/gopkg.in/asn1-ber.v1/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/gopkg.in/ldap.v3/.gitignore b/vendor/gopkg.in/ldap.v3/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/vendor/gopkg.in/ldap.v3/.travis.yml b/vendor/gopkg.in/ldap.v3/.travis.yml deleted file mode 100644 index d2160fd4a..000000000 --- a/vendor/gopkg.in/ldap.v3/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -sudo: false -language: go -go: - - "1.4.x" - - "1.5.x" - - "1.6.x" - - "1.7.x" - - "1.8.x" - - "1.9.x" - - "1.10.x" - - "1.11.x" - - "1.12.x" - - tip - -git: - depth: 1 - -matrix: - fast_finish: true - allow_failures: - - go: tip -go_import_path: gopkg.in/ldap.v3 -install: - - go get gopkg.in/asn1-ber.v1 - - go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover - - go get github.com/golang/lint/golint || go get golang.org/x/lint/golint || true - - go build -v ./... -script: - - make test - - make fmt - - make vet - - make lint diff --git a/vendor/gopkg.in/ldap.v3/CONTRIBUTING.md b/vendor/gopkg.in/ldap.v3/CONTRIBUTING.md deleted file mode 100644 index a7885231c..000000000 --- a/vendor/gopkg.in/ldap.v3/CONTRIBUTING.md +++ /dev/null @@ -1,12 +0,0 @@ -# Contribution Guidelines - -We welcome contribution and improvements. - -## Guiding Principles - -To begin with here is a draft from an email exchange: - - * take compatibility seriously (our semvers, compatibility with older go versions, etc) - * don't tag untested code for release - * beware of baking in implicit behavior based on other libraries/tools choices - * be as high-fidelity as possible in plumbing through LDAP data (don't mask errors or reduce power of someone using the library) diff --git a/vendor/gopkg.in/ldap.v3/Makefile b/vendor/gopkg.in/ldap.v3/Makefile deleted file mode 100644 index c49664722..000000000 --- a/vendor/gopkg.in/ldap.v3/Makefile +++ /dev/null @@ -1,82 +0,0 @@ -.PHONY: default install build test quicktest fmt vet lint - -# List of all release tags "supported" by our current Go version -# E.g. ":go1.1:go1.2:go1.3:go1.4:go1.5:go1.6:go1.7:go1.8:go1.9:go1.10:go1.11:go1.12:" -GO_RELEASE_TAGS := $(shell go list -f ':{{join (context.ReleaseTags) ":"}}:' runtime) - -# Only use the `-race` flag on newer versions of Go (version 1.3 and newer) -ifeq (,$(findstring :go1.3:,$(GO_RELEASE_TAGS))) - RACE_FLAG := -else - RACE_FLAG := -race -cpu 1,2,4 -endif - -# Run `go vet` on Go 1.12 and newer. For Go 1.5-1.11, use `go tool vet` -ifneq (,$(findstring :go1.12:,$(GO_RELEASE_TAGS))) - GO_VET := go vet \ - -atomic \ - -bool \ - -copylocks \ - -nilfunc \ - -printf \ - -rangeloops \ - -unreachable \ - -unsafeptr \ - -unusedresult \ - . -else ifneq (,$(findstring :go1.5:,$(GO_RELEASE_TAGS))) - GO_VET := go tool vet \ - -atomic \ - -bool \ - -copylocks \ - -nilfunc \ - -printf \ - -shadow \ - -rangeloops \ - -unreachable \ - -unsafeptr \ - -unusedresult \ - . -else - GO_VET := @echo "go vet skipped -- not supported on this version of Go" -endif - -default: fmt vet lint build quicktest - -install: - go get -t -v ./... - -build: - go build -v ./... - -test: - go test -v $(RACE_FLAG) -cover ./... - -quicktest: - go test ./... - -# Capture output and force failure when there is non-empty output -fmt: - @echo gofmt -l . - @OUTPUT=`gofmt -l . 2>&1`; \ - if [ "$$OUTPUT" ]; then \ - echo "gofmt must be run on the following files:"; \ - echo "$$OUTPUT"; \ - exit 1; \ - fi - -vet: - $(GO_VET) - -# https://github.com/golang/lint -# go get github.com/golang/lint/golint -# Capture output and force failure when there is non-empty output -# Only run on go1.5+ -lint: - @echo golint ./... - @OUTPUT=`command -v golint >/dev/null 2>&1 && golint ./... 2>&1`; \ - if [ "$$OUTPUT" ]; then \ - echo "golint errors:"; \ - echo "$$OUTPUT"; \ - exit 1; \ - fi diff --git a/vendor/gopkg.in/ldap.v3/README.md b/vendor/gopkg.in/ldap.v3/README.md deleted file mode 100644 index 25cf730b4..000000000 --- a/vendor/gopkg.in/ldap.v3/README.md +++ /dev/null @@ -1,54 +0,0 @@ -[![GoDoc](https://godoc.org/gopkg.in/ldap.v3?status.svg)](https://godoc.org/gopkg.in/ldap.v3) -[![Build Status](https://travis-ci.org/go-ldap/ldap.svg)](https://travis-ci.org/go-ldap/ldap) - -# Basic LDAP v3 functionality for the GO programming language. - -## Install - -For the latest version use: - - go get gopkg.in/ldap.v3 - -Import the latest version with: - - import "gopkg.in/ldap.v3" - -## Required Libraries: - - - gopkg.in/asn1-ber.v1 - -## Features: - - - Connecting to LDAP server (non-TLS, TLS, STARTTLS) - - Binding to LDAP server - - Searching for entries - - Filter Compile / Decompile - - Paging Search Results - - Modify Requests / Responses - - Add Requests / Responses - - Delete Requests / Responses - - Modify DN Requests / Responses - -## Examples: - - - search - - modify - -## Contributing: - -Bug reports and pull requests are welcome! - -Before submitting a pull request, please make sure tests and verification scripts pass: -``` -make all -``` - -To set up a pre-push hook to run the tests and verify scripts before pushing: -``` -ln -s ../../.githooks/pre-push .git/hooks/pre-push -``` - ---- -The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/) -The design is licensed under the Creative Commons 3.0 Attributions license. -Read this article for more details: http://blog.golang.org/gopher diff --git a/vendor/gopkg.in/ldap.v3/bind.go b/vendor/gopkg.in/ldap.v3/bind.go deleted file mode 100644 index 59c3f5ef5..000000000 --- a/vendor/gopkg.in/ldap.v3/bind.go +++ /dev/null @@ -1,135 +0,0 @@ -package ldap - -import ( - "errors" - "fmt" - - "gopkg.in/asn1-ber.v1" -) - -// SimpleBindRequest represents a username/password bind operation -type SimpleBindRequest struct { - // Username is the name of the Directory object that the client wishes to bind as - Username string - // Password is the credentials to bind with - Password string - // Controls are optional controls to send with the bind request - Controls []Control - // AllowEmptyPassword sets whether the client allows binding with an empty password - // (normally used for unauthenticated bind). - AllowEmptyPassword bool -} - -// SimpleBindResult contains the response from the server -type SimpleBindResult struct { - Controls []Control -} - -// NewSimpleBindRequest returns a bind request -func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest { - return &SimpleBindRequest{ - Username: username, - Password: password, - Controls: controls, - AllowEmptyPassword: false, - } -} - -func (bindRequest *SimpleBindRequest) encode() *ber.Packet { - request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") - request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) - request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, bindRequest.Username, "User Name")) - request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, bindRequest.Password, "Password")) - - return request -} - -// SimpleBind performs the simple bind operation defined in the given request -func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) { - if simpleBindRequest.Password == "" && !simpleBindRequest.AllowEmptyPassword { - return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client")) - } - - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) - encodedBindRequest := simpleBindRequest.encode() - packet.AppendChild(encodedBindRequest) - if len(simpleBindRequest.Controls) > 0 { - packet.AppendChild(encodeControls(simpleBindRequest.Controls)) - } - - if l.Debug { - ber.PrintPacket(packet) - } - - msgCtx, err := l.sendMessage(packet) - if err != nil { - return nil, err - } - defer l.finishMessage(msgCtx) - - packetResponse, ok := <-msgCtx.responses - if !ok { - return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) - } - packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", msgCtx.id, packet) - if err != nil { - return nil, err - } - - if l.Debug { - if err = addLDAPDescriptions(packet); err != nil { - return nil, err - } - ber.PrintPacket(packet) - } - - result := &SimpleBindResult{ - Controls: make([]Control, 0), - } - - if len(packet.Children) == 3 { - for _, child := range packet.Children[2].Children { - decodedChild, decodeErr := DecodeControl(child) - if decodeErr != nil { - return nil, fmt.Errorf("failed to decode child control: %s", decodeErr) - } - result.Controls = append(result.Controls, decodedChild) - } - } - - err = GetLDAPError(packet) - return result, err -} - -// Bind performs a bind with the given username and password. -// -// It does not allow unauthenticated bind (i.e. empty password). Use the UnauthenticatedBind method -// for that. -func (l *Conn) Bind(username, password string) error { - req := &SimpleBindRequest{ - Username: username, - Password: password, - AllowEmptyPassword: false, - } - _, err := l.SimpleBind(req) - return err -} - -// UnauthenticatedBind performs an unauthenticated bind. -// -// A username may be provided for trace (e.g. logging) purpose only, but it is normally not -// authenticated or otherwise validated by the LDAP server. -// -// See https://tools.ietf.org/html/rfc4513#section-5.1.2 . -// See https://tools.ietf.org/html/rfc4513#section-6.3.1 . -func (l *Conn) UnauthenticatedBind(username string) error { - req := &SimpleBindRequest{ - Username: username, - Password: "", - AllowEmptyPassword: true, - } - _, err := l.SimpleBind(req) - return err -} diff --git a/vendor/gopkg.in/ldap.v3/client.go b/vendor/gopkg.in/ldap.v3/client.go deleted file mode 100644 index c7f41f6f9..000000000 --- a/vendor/gopkg.in/ldap.v3/client.go +++ /dev/null @@ -1,28 +0,0 @@ -package ldap - -import ( - "crypto/tls" - "time" -) - -// Client knows how to interact with an LDAP server -type Client interface { - Start() - StartTLS(config *tls.Config) error - Close() - SetTimeout(time.Duration) - - Bind(username, password string) error - SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) - - Add(addRequest *AddRequest) error - Del(delRequest *DelRequest) error - Modify(modifyRequest *ModifyRequest) error - ModifyDN(modifyDNRequest *ModifyDNRequest) error - - Compare(dn, attribute, value string) (bool, error) - PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) - - Search(searchRequest *SearchRequest) (*SearchResult, error) - SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) -} diff --git a/vendor/gopkg.in/ldap.v3/compare.go b/vendor/gopkg.in/ldap.v3/compare.go deleted file mode 100644 index 5b5013cbe..000000000 --- a/vendor/gopkg.in/ldap.v3/compare.go +++ /dev/null @@ -1,83 +0,0 @@ -// File contains Compare functionality -// -// https://tools.ietf.org/html/rfc4511 -// -// CompareRequest ::= [APPLICATION 14] SEQUENCE { -// entry LDAPDN, -// ava AttributeValueAssertion } -// -// AttributeValueAssertion ::= SEQUENCE { -// attributeDesc AttributeDescription, -// assertionValue AssertionValue } -// -// AttributeDescription ::= LDAPString -// -- Constrained to -// -- [RFC4512] -// -// AttributeValue ::= OCTET STRING -// - -package ldap - -import ( - "errors" - "fmt" - - "gopkg.in/asn1-ber.v1" -) - -// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise -// false with any error that occurs if any. -func (l *Conn) Compare(dn, attribute, value string) (bool, error) { - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) - - request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request") - request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN")) - - ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion") - ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "AttributeDesc")) - ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "AssertionValue")) - request.AppendChild(ava) - packet.AppendChild(request) - - l.Debug.PrintPacket(packet) - - msgCtx, err := l.sendMessage(packet) - if err != nil { - return false, err - } - defer l.finishMessage(msgCtx) - - l.Debug.Printf("%d: waiting for response", msgCtx.id) - packetResponse, ok := <-msgCtx.responses - if !ok { - return false, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) - } - packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", msgCtx.id, packet) - if err != nil { - return false, err - } - - if l.Debug { - if err := addLDAPDescriptions(packet); err != nil { - return false, err - } - ber.PrintPacket(packet) - } - - if packet.Children[1].Tag == ApplicationCompareResponse { - err := GetLDAPError(packet) - - switch { - case IsErrorWithCode(err, LDAPResultCompareTrue): - return true, nil - case IsErrorWithCode(err, LDAPResultCompareFalse): - return false, nil - default: - return false, err - } - } - return false, fmt.Errorf("unexpected Response: %d", packet.Children[1].Tag) -} diff --git a/vendor/gopkg.in/ldap.v3/del.go b/vendor/gopkg.in/ldap.v3/del.go deleted file mode 100644 index 6f78beb18..000000000 --- a/vendor/gopkg.in/ldap.v3/del.go +++ /dev/null @@ -1,84 +0,0 @@ -// -// https://tools.ietf.org/html/rfc4511 -// -// DelRequest ::= [APPLICATION 10] LDAPDN - -package ldap - -import ( - "errors" - "log" - - "gopkg.in/asn1-ber.v1" -) - -// DelRequest implements an LDAP deletion request -type DelRequest struct { - // DN is the name of the directory entry to delete - DN string - // Controls hold optional controls to send with the request - Controls []Control -} - -func (d DelRequest) encode() *ber.Packet { - request := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, d.DN, "Del Request") - request.Data.Write([]byte(d.DN)) - return request -} - -// NewDelRequest creates a delete request for the given DN and controls -func NewDelRequest(DN string, - Controls []Control) *DelRequest { - return &DelRequest{ - DN: DN, - Controls: Controls, - } -} - -// Del executes the given delete request -func (l *Conn) Del(delRequest *DelRequest) error { - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) - packet.AppendChild(delRequest.encode()) - if len(delRequest.Controls) > 0 { - packet.AppendChild(encodeControls(delRequest.Controls)) - } - - l.Debug.PrintPacket(packet) - - msgCtx, err := l.sendMessage(packet) - if err != nil { - return err - } - defer l.finishMessage(msgCtx) - - l.Debug.Printf("%d: waiting for response", msgCtx.id) - packetResponse, ok := <-msgCtx.responses - if !ok { - return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) - } - packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", msgCtx.id, packet) - if err != nil { - return err - } - - if l.Debug { - if err := addLDAPDescriptions(packet); err != nil { - return err - } - ber.PrintPacket(packet) - } - - if packet.Children[1].Tag == ApplicationDelResponse { - err := GetLDAPError(packet) - if err != nil { - return err - } - } else { - log.Printf("Unexpected Response: %d", packet.Children[1].Tag) - } - - l.Debug.Printf("%d: returning", msgCtx.id) - return nil -} diff --git a/vendor/gopkg.in/ldap.v3/moddn.go b/vendor/gopkg.in/ldap.v3/moddn.go deleted file mode 100644 index 803279d26..000000000 --- a/vendor/gopkg.in/ldap.v3/moddn.go +++ /dev/null @@ -1,104 +0,0 @@ -// Package ldap - moddn.go contains ModifyDN functionality -// -// https://tools.ietf.org/html/rfc4511 -// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { -// entry LDAPDN, -// newrdn RelativeLDAPDN, -// deleteoldrdn BOOLEAN, -// newSuperior [0] LDAPDN OPTIONAL } -// -// -package ldap - -import ( - "errors" - "log" - - "gopkg.in/asn1-ber.v1" -) - -// ModifyDNRequest holds the request to modify a DN -type ModifyDNRequest struct { - DN string - NewRDN string - DeleteOldRDN bool - NewSuperior string -} - -// NewModifyDNRequest creates a new request which can be passed to ModifyDN(). -// -// To move an object in the tree, set the "newSup" to the new parent entry DN. Use an -// empty string for just changing the object's RDN. -// -// For moving the object without renaming, the "rdn" must be the first -// RDN of the given DN. -// -// A call like -// mdnReq := NewModifyDNRequest("uid=someone,dc=example,dc=org", "uid=newname", true, "") -// will setup the request to just rename uid=someone,dc=example,dc=org to -// uid=newname,dc=example,dc=org. -func NewModifyDNRequest(dn string, rdn string, delOld bool, newSup string) *ModifyDNRequest { - return &ModifyDNRequest{ - DN: dn, - NewRDN: rdn, - DeleteOldRDN: delOld, - NewSuperior: newSup, - } -} - -func (m ModifyDNRequest) encode() *ber.Packet { - request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyDNRequest, nil, "Modify DN Request") - request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN")) - request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.NewRDN, "New RDN")) - request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, m.DeleteOldRDN, "Delete old RDN")) - if m.NewSuperior != "" { - request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, m.NewSuperior, "New Superior")) - } - return request -} - -// ModifyDN renames the given DN and optionally move to another base (when the "newSup" argument -// to NewModifyDNRequest() is not ""). -func (l *Conn) ModifyDN(m *ModifyDNRequest) error { - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) - packet.AppendChild(m.encode()) - - l.Debug.PrintPacket(packet) - - msgCtx, err := l.sendMessage(packet) - if err != nil { - return err - } - defer l.finishMessage(msgCtx) - - l.Debug.Printf("%d: waiting for response", msgCtx.id) - packetResponse, ok := <-msgCtx.responses - if !ok { - return NewError(ErrorNetwork, errors.New("ldap: channel closed")) - } - packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", msgCtx.id, packet) - if err != nil { - return err - } - - if l.Debug { - if err := addLDAPDescriptions(packet); err != nil { - return err - } - ber.PrintPacket(packet) - } - - if packet.Children[1].Tag == ApplicationModifyDNResponse { - err := GetLDAPError(packet) - if err != nil { - return err - } - } else { - log.Printf("Unexpected Response: %d", packet.Children[1].Tag) - } - - l.Debug.Printf("%d: returning", msgCtx.id) - return nil -} diff --git a/vendor/gopkg.in/ldap.v3/modify.go b/vendor/gopkg.in/ldap.v3/modify.go deleted file mode 100644 index d83e6221f..000000000 --- a/vendor/gopkg.in/ldap.v3/modify.go +++ /dev/null @@ -1,173 +0,0 @@ -// File contains Modify functionality -// -// https://tools.ietf.org/html/rfc4511 -// -// ModifyRequest ::= [APPLICATION 6] SEQUENCE { -// object LDAPDN, -// changes SEQUENCE OF change SEQUENCE { -// operation ENUMERATED { -// add (0), -// delete (1), -// replace (2), -// ... }, -// modification PartialAttribute } } -// -// PartialAttribute ::= SEQUENCE { -// type AttributeDescription, -// vals SET OF value AttributeValue } -// -// AttributeDescription ::= LDAPString -// -- Constrained to -// -- [RFC4512] -// -// AttributeValue ::= OCTET STRING -// - -package ldap - -import ( - "errors" - "log" - - "gopkg.in/asn1-ber.v1" -) - -// Change operation choices -const ( - AddAttribute = 0 - DeleteAttribute = 1 - ReplaceAttribute = 2 -) - -// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 -type PartialAttribute struct { - // Type is the type of the partial attribute - Type string - // Vals are the values of the partial attribute - Vals []string -} - -func (p *PartialAttribute) encode() *ber.Packet { - seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute") - seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type")) - set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue") - for _, value := range p.Vals { - set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals")) - } - seq.AppendChild(set) - return seq -} - -// Change for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 -type Change struct { - // Operation is the type of change to be made - Operation uint - // Modification is the attribute to be modified - Modification PartialAttribute -} - -func (c *Change) encode() *ber.Packet { - change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") - change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(c.Operation), "Operation")) - change.AppendChild(c.Modification.encode()) - return change -} - -// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 -type ModifyRequest struct { - // DN is the distinguishedName of the directory entry to modify - DN string - // Changes contain the attributes to modify - Changes []Change - // Controls hold optional controls to send with the request - Controls []Control -} - -// Add appends the given attribute to the list of changes to be made -func (m *ModifyRequest) Add(attrType string, attrVals []string) { - m.appendChange(AddAttribute, attrType, attrVals) -} - -// Delete appends the given attribute to the list of changes to be made -func (m *ModifyRequest) Delete(attrType string, attrVals []string) { - m.appendChange(DeleteAttribute, attrType, attrVals) -} - -// Replace appends the given attribute to the list of changes to be made -func (m *ModifyRequest) Replace(attrType string, attrVals []string) { - m.appendChange(ReplaceAttribute, attrType, attrVals) -} - -func (m *ModifyRequest) appendChange(operation uint, attrType string, attrVals []string) { - m.Changes = append(m.Changes, Change{operation, PartialAttribute{Type: attrType, Vals: attrVals}}) -} - -func (m ModifyRequest) encode() *ber.Packet { - request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request") - request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN")) - changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes") - for _, change := range m.Changes { - changes.AppendChild(change.encode()) - } - request.AppendChild(changes) - return request -} - -// NewModifyRequest creates a modify request for the given DN -func NewModifyRequest( - dn string, - controls []Control, -) *ModifyRequest { - return &ModifyRequest{ - DN: dn, - Controls: controls, - } -} - -// Modify performs the ModifyRequest -func (l *Conn) Modify(modifyRequest *ModifyRequest) error { - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) - packet.AppendChild(modifyRequest.encode()) - if len(modifyRequest.Controls) > 0 { - packet.AppendChild(encodeControls(modifyRequest.Controls)) - } - - l.Debug.PrintPacket(packet) - - msgCtx, err := l.sendMessage(packet) - if err != nil { - return err - } - defer l.finishMessage(msgCtx) - - l.Debug.Printf("%d: waiting for response", msgCtx.id) - packetResponse, ok := <-msgCtx.responses - if !ok { - return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) - } - packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", msgCtx.id, packet) - if err != nil { - return err - } - - if l.Debug { - if err := addLDAPDescriptions(packet); err != nil { - return err - } - ber.PrintPacket(packet) - } - - if packet.Children[1].Tag == ApplicationModifyResponse { - err := GetLDAPError(packet) - if err != nil { - return err - } - } else { - log.Printf("Unexpected Response: %d", packet.Children[1].Tag) - } - - l.Debug.Printf("%d: returning", msgCtx.id) - return nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 44ba09003..b5b9ef3db 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -49,6 +49,8 @@ gitea.com/macaron/session/postgres # gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 ## explicit gitea.com/macaron/toolbox +# github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c +github.com/Azure/go-ntlmssp # github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml # github.com/PuerkitoBio/goquery v1.5.1 @@ -250,6 +252,8 @@ github.com/gliderlabs/ssh # github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a ## explicit github.com/glycerine/go-unsnap-stream +# github.com/go-asn1-ber/asn1-ber v1.5.1 +github.com/go-asn1-ber/asn1-ber # github.com/go-enry/go-enry/v2 v2.5.2 ## explicit github.com/go-enry/go-enry/v2 @@ -318,6 +322,9 @@ github.com/go-git/go-git/v5/utils/merkletrie/filesystem github.com/go-git/go-git/v5/utils/merkletrie/index github.com/go-git/go-git/v5/utils/merkletrie/internal/frame github.com/go-git/go-git/v5/utils/merkletrie/noder +# github.com/go-ldap/ldap/v3 v3.2.4 +## explicit +github.com/go-ldap/ldap/v3 # github.com/go-openapi/analysis v0.19.10 github.com/go-openapi/analysis github.com/go-openapi/analysis/internal @@ -935,18 +942,12 @@ google.golang.org/protobuf/runtime/protoimpl # gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc ## explicit gopkg.in/alexcesaro/quotedprintable.v3 -# gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 -## explicit -gopkg.in/asn1-ber.v1 # gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ## explicit gopkg.in/gomail.v2 # gopkg.in/ini.v1 v1.61.0 ## explicit gopkg.in/ini.v1 -# gopkg.in/ldap.v3 v3.0.2 -## explicit -gopkg.in/ldap.v3 # gopkg.in/warnings.v0 v0.1.2 gopkg.in/warnings.v0 # gopkg.in/yaml.v2 v2.3.0