summaryrefslogtreecommitdiffstats
path: root/vendor/github.com
diff options
context:
space:
mode:
author6543 <6543@obermui.de>2020-10-15 21:27:33 +0200
committerGitHub <noreply@github.com>2020-10-15 15:27:33 -0400
commite374bb7e2dede03eeacaec376c8fbb3c05d07a25 (patch)
treed946047c3c62ee57be2ed13f0e0bebf5f19bc021 /vendor/github.com
parentbcf45bb162e50554a09b9735cf966f8679595b09 (diff)
downloadgitea-e374bb7e2dede03eeacaec376c8fbb3c05d07a25.tar.gz
gitea-e374bb7e2dede03eeacaec376c8fbb3c05d07a25.zip
[Vendor] Update go-ldap to v3.2.4 (#13163)
* [Vendor] update go-ldap to v3.0.3 * update go-ldap to v3.2.4 Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/.travis.yml17
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/LICENSE21
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/README.md29
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/authenticate_message.go183
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/authheader.go37
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/avids.go17
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/challenge_message.go82
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/messageheader.go21
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go52
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/negotiate_message.go64
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/negotiator.go144
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/nlmp.go51
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/unicode.go29
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/varfield.go40
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/version.go20
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml39
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/LICENSE22
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/README.md24
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/ber.go620
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/content_int.go25
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/generalizedTime.go105
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/go.mod3
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/header.go38
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/identifier.go112
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/length.go81
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/real.go157
-rw-r--r--vendor/github.com/go-asn1-ber/asn1-ber/util.go24
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/LICENSE22
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/add.go91
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/bind.go540
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/client.go30
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/compare.go61
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/conn.go570
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/control.go492
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/debug.go30
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/del.go59
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/dn.go207
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/doc.go4
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/error.go253
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/filter.go487
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/go.mod9
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/go.sum11
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/ldap.go339
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/moddn.go80
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/modify.go132
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/passwdmodify.go126
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/request.go66
-rw-r--r--vendor/github.com/go-ldap/ldap/v3/search.go410
48 files changed, 6076 insertions, 0 deletions
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 0000000000..23c95fe951
--- /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 0000000000..dc1cf39d13
--- /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 0000000000..55cdcefab7
--- /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 0000000000..c8930680c5
--- /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 0000000000..aac3f77d10
--- /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 0000000000..196b5f1316
--- /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 0000000000..053b55e4ad
--- /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 0000000000..247e284652
--- /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 0000000000..5905c023d6
--- /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 0000000000..e466a9861d
--- /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 0000000000..7705eae4f8
--- /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 0000000000..1e65abe8b5
--- /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 0000000000..7b4f47163d
--- /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 0000000000..15f9aa113d
--- /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 0000000000..6d84892124
--- /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 0000000000..8bffb90170
--- /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 0000000000..23f9425345
--- /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/github.com/go-asn1-ber/asn1-ber/README.md b/vendor/github.com/go-asn1-ber/asn1-ber/README.md
new file mode 100644
index 0000000000..e3a9560d68
--- /dev/null
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/README.md
@@ -0,0 +1,24 @@
+[![GoDoc](https://godoc.org/gopkg.in/asn1-ber.v1?status.svg)](https://godoc.org/gopkg.in/asn1-ber.v1) [![Build Status](https://travis-ci.org/go-asn1-ber/asn1-ber.svg)](https://travis-ci.org/go-asn1-ber/asn1-ber)
+
+
+ASN1 BER Encoding / Decoding Library for the GO programming language.
+---------------------------------------------------------------------
+
+Required libraries:
+ None
+
+Working:
+ Very basic encoding / decoding needed for LDAP protocol
+
+Tests Implemented:
+ A few
+
+TODO:
+ Fix all encoding / decoding to conform to ASN1 BER spec
+ Implement Tests / Benchmarks
+
+---
+
+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/github.com/go-asn1-ber/asn1-ber/ber.go b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go
new file mode 100644
index 0000000000..4fd7a66e18
--- /dev/null
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go
@@ -0,0 +1,620 @@
+package ber
+
+import (
+ "bytes"
+ "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{}
+ ByteValue []byte
+ Data *bytes.Buffer
+ Children []*Packet
+ Description string
+}
+
+type Identifier struct {
+ ClassType Class
+ TagType Type
+ Tag Tag
+}
+
+type Tag uint64
+
+const (
+ TagEOC Tag = 0x00
+ TagBoolean Tag = 0x01
+ TagInteger Tag = 0x02
+ TagBitString Tag = 0x03
+ TagOctetString Tag = 0x04
+ TagNULL Tag = 0x05
+ TagObjectIdentifier Tag = 0x06
+ TagObjectDescriptor Tag = 0x07
+ TagExternal Tag = 0x08
+ TagRealFloat Tag = 0x09
+ TagEnumerated Tag = 0x0a
+ TagEmbeddedPDV Tag = 0x0b
+ TagUTF8String Tag = 0x0c
+ TagRelativeOID Tag = 0x0d
+ TagSequence Tag = 0x10
+ TagSet Tag = 0x11
+ TagNumericString Tag = 0x12
+ TagPrintableString Tag = 0x13
+ TagT61String Tag = 0x14
+ TagVideotexString Tag = 0x15
+ TagIA5String Tag = 0x16
+ TagUTCTime Tag = 0x17
+ TagGeneralizedTime Tag = 0x18
+ TagGraphicString Tag = 0x19
+ TagVisibleString Tag = 0x1a
+ TagGeneralString Tag = 0x1b
+ TagUniversalString Tag = 0x1c
+ TagCharacterString Tag = 0x1d
+ TagBMPString Tag = 0x1e
+ TagBitmask Tag = 0x1f // xxx11111b
+
+ // HighTag indicates the start of a high-tag byte sequence
+ HighTag Tag = 0x1f // xxx11111b
+ // HighTagContinueBitmask indicates the high-tag byte sequence should continue
+ HighTagContinueBitmask Tag = 0x80 // 10000000b
+ // HighTagValueBitmask obtains the tag value from a high-tag byte sequence byte
+ HighTagValueBitmask Tag = 0x7f // 01111111b
+)
+
+const (
+ // LengthLongFormBitmask is the mask to apply to the length byte to see if a long-form byte sequence is used
+ LengthLongFormBitmask = 0x80
+ // LengthValueBitmask is the mask to apply to the length byte to get the number of bytes in the long-form byte sequence
+ LengthValueBitmask = 0x7f
+
+ // LengthIndefinite is returned from readLength to indicate an indefinite length
+ LengthIndefinite = -1
+)
+
+var tagMap = map[Tag]string{
+ TagEOC: "EOC (End-of-Content)",
+ TagBoolean: "Boolean",
+ TagInteger: "Integer",
+ TagBitString: "Bit String",
+ TagOctetString: "Octet String",
+ TagNULL: "NULL",
+ TagObjectIdentifier: "Object Identifier",
+ TagObjectDescriptor: "Object Descriptor",
+ TagExternal: "External",
+ TagRealFloat: "Real (float)",
+ TagEnumerated: "Enumerated",
+ TagEmbeddedPDV: "Embedded PDV",
+ TagUTF8String: "UTF8 String",
+ TagRelativeOID: "Relative-OID",
+ TagSequence: "Sequence and Sequence of",
+ TagSet: "Set and Set OF",
+ TagNumericString: "Numeric String",
+ TagPrintableString: "Printable String",
+ TagT61String: "T61 String",
+ TagVideotexString: "Videotex String",
+ TagIA5String: "IA5 String",
+ TagUTCTime: "UTC Time",
+ TagGeneralizedTime: "Generalized Time",
+ TagGraphicString: "Graphic String",
+ TagVisibleString: "Visible String",
+ TagGeneralString: "General String",
+ TagUniversalString: "Universal String",
+ TagCharacterString: "Character String",
+ TagBMPString: "BMP String",
+}
+
+type Class uint8
+
+const (
+ ClassUniversal Class = 0 // 00xxxxxxb
+ ClassApplication Class = 64 // 01xxxxxxb
+ ClassContext Class = 128 // 10xxxxxxb
+ ClassPrivate Class = 192 // 11xxxxxxb
+ ClassBitmask Class = 192 // 11xxxxxxb
+)
+
+var ClassMap = map[Class]string{
+ ClassUniversal: "Universal",
+ ClassApplication: "Application",
+ ClassContext: "Context",
+ ClassPrivate: "Private",
+}
+
+type Type uint8
+
+const (
+ TypePrimitive Type = 0 // xx0xxxxxb
+ TypeConstructed Type = 32 // xx1xxxxxb
+ TypeBitmask Type = 32 // xx1xxxxxb
+)
+
+var TypeMap = map[Type]string{
+ TypePrimitive: "Primitive",
+ TypeConstructed: "Constructed",
+}
+
+var Debug = false
+
+func PrintBytes(out io.Writer, buf []byte, indent string) {
+ dataLines := make([]string, (len(buf)/30)+1)
+ numLines := make([]string, (len(buf)/30)+1)
+
+ for i, b := range buf {
+ dataLines[i/30] += fmt.Sprintf("%02x ", b)
+ numLines[i/30] += fmt.Sprintf("%02d ", (i+1)%100)
+ }
+
+ 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) {
+ indentStr := ""
+
+ for len(indentStr) != indent {
+ indentStr += " "
+ }
+
+ classStr := ClassMap[p.ClassType]
+
+ tagTypeStr := TypeMap[p.TagType]
+
+ tagStr := fmt.Sprintf("0x%02X", p.Tag)
+
+ if p.ClassType == ClassUniversal {
+ tagStr = tagMap[p.Tag]
+ }
+
+ value := fmt.Sprint(p.Value)
+ description := ""
+
+ if p.Description != "" {
+ description = p.Description + ": "
+ }
+
+ _, _ = 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(), indentStr)
+ }
+
+ for _, child := range p.Children {
+ printPacket(out, child, indent+1, printBytes)
+ }
+}
+
+// ReadPacket reads a single Packet from the reader.
+func ReadPacket(reader io.Reader) (*Packet, error) {
+ p, _, err := readPacket(reader)
+ if err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+func DecodeString(data []byte) string {
+ return string(data)
+}
+
+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")
+ return
+ }
+ for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
+ ret <<= 8
+ ret |= int64(bytes[bytesRead])
+ }
+
+ // Shift up and down in order to sign extend the result.
+ ret <<= 64 - uint8(len(bytes))*8
+ ret >>= 64 - uint8(len(bytes))*8
+ return
+}
+
+func encodeInteger(i int64) []byte {
+ n := int64Length(i)
+ out := make([]byte, n)
+
+ var j int
+ for ; n > 0; n-- {
+ out[j] = byte(i >> uint((n-1)*8))
+ j++
+ }
+
+ return out
+}
+
+func int64Length(i int64) (numBytes int) {
+ numBytes = 1
+
+ for i > 127 {
+ numBytes++
+ i >>= 8
+ }
+
+ for i < -128 {
+ numBytes++
+ i >>= 8
+ }
+
+ return
+}
+
+// DecodePacket decodes the given bytes into a single Packet
+// If a decode error is encountered, nil is returned.
+func DecodePacket(data []byte) *Packet {
+ p, _, _ := readPacket(bytes.NewBuffer(data))
+
+ return p
+}
+
+// DecodePacketErr decodes the given bytes into a single Packet
+// If a decode error is encountered, nil is returned.
+func DecodePacketErr(data []byte) (*Packet, error) {
+ p, _, err := readPacket(bytes.NewBuffer(data))
+ if err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+// 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 {
+ return nil, read, err
+ }
+
+ p := &Packet{
+ Identifier: identifier,
+ }
+
+ p.Data = new(bytes.Buffer)
+ p.Children = make([]*Packet, 0, 2)
+ p.Value = nil
+
+ if p.TagType == TypeConstructed {
+ // TODO: if universal, ensure tag type is allowed to be constructed
+
+ // Track how much content we've read
+ contentRead := 0
+ for {
+ if length != LengthIndefinite {
+ // End if we've read what we've been told to
+ if contentRead == length {
+ break
+ }
+ // Detect if a packet boundary didn't fall on the expected length
+ if contentRead > length {
+ return nil, read, fmt.Errorf("expected to read %d bytes, read %d", length, contentRead)
+ }
+ }
+
+ // Read the next packet
+ child, r, err := readPacket(reader)
+ if err != nil {
+ return nil, read, err
+ }
+ contentRead += r
+ read += r
+
+ // Test is this is the EOC marker for our packet
+ if isEOCPacket(child) {
+ if length == LengthIndefinite {
+ break
+ }
+ return nil, read, errors.New("eoc child not allowed with definite length")
+ }
+
+ // Append and continue
+ p.AppendChild(child)
+ }
+ return p, read, nil
+ }
+
+ if length == LengthIndefinite {
+ return nil, read, errors.New("indefinite length used with primitive type")
+ }
+
+ // Read definite-length content
+ 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 {
+ if err == io.EOF {
+ return nil, read, io.ErrUnexpectedEOF
+ }
+ return nil, read, err
+ }
+ read += length
+ }
+
+ if p.ClassType == ClassUniversal {
+ p.Data.Write(content)
+ p.ByteValue = content
+
+ switch p.Tag {
+ case TagEOC:
+ case TagBoolean:
+ val, _ := ParseInt64(content)
+
+ p.Value = val != 0
+ case TagInteger:
+ p.Value, _ = ParseInt64(content)
+ case TagBitString:
+ case TagOctetString:
+ // the actual string encoding is not known here
+ // (e.g. for LDAP content is already an UTF8-encoded
+ // string). Return the data without further processing
+ p.Value = DecodeString(content)
+ case TagNULL:
+ case TagObjectIdentifier:
+ case TagObjectDescriptor:
+ case TagExternal:
+ case TagRealFloat:
+ p.Value, err = ParseReal(content)
+ case TagEnumerated:
+ p.Value, _ = ParseInt64(content)
+ case TagEmbeddedPDV:
+ case TagUTF8String:
+ 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:
+ 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:
+ case TagUniversalString:
+ case TagCharacterString:
+ case TagBMPString:
+ }
+ } else {
+ p.Data.Write(content)
+ }
+
+ 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 {
+ var out bytes.Buffer
+
+ out.Write(encodeIdentifier(p.Identifier))
+ out.Write(encodeLength(p.Data.Len()))
+ out.Write(p.Data.Bytes())
+
+ return out.Bytes()
+}
+
+func (p *Packet) AppendChild(child *Packet) {
+ p.Data.Write(child.Bytes())
+ p.Children = append(p.Children, child)
+}
+
+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.Data = new(bytes.Buffer)
+
+ p.Children = make([]*Packet, 0, 2)
+
+ p.Value = value
+ p.Description = description
+
+ if value != nil {
+ v := reflect.ValueOf(value)
+
+ 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 NewBoolean(classType Class, tagType Type, tag Tag, value bool, description string) *Packet {
+ intValue := int64(0)
+
+ if value {
+ intValue = 1
+ }
+
+ 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.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)
+
+ p.Value = value
+ switch v := value.(type) {
+ case int:
+ p.Data.Write(encodeInteger(int64(v)))
+ case uint:
+ p.Data.Write(encodeInteger(int64(v)))
+ case int64:
+ p.Data.Write(encodeInteger(v))
+ case uint64:
+ // TODO : check range or add encodeUInt...
+ p.Data.Write(encodeInteger(int64(v)))
+ case int32:
+ p.Data.Write(encodeInteger(int64(v)))
+ case uint32:
+ p.Data.Write(encodeInteger(int64(v)))
+ case int16:
+ p.Data.Write(encodeInteger(int64(v)))
+ case uint16:
+ p.Data.Write(encodeInteger(int64(v)))
+ case int8:
+ p.Data.Write(encodeInteger(int64(v)))
+ case uint8:
+ p.Data.Write(encodeInteger(int64(v)))
+ default:
+ // TODO : add support for big.Int ?
+ panic(fmt.Sprintf("Invalid type %T, expected {u|}int{64|32|16|8}", v))
+ }
+
+ return p
+}
+
+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
+}
+
+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/github.com/go-asn1-ber/asn1-ber/content_int.go b/vendor/github.com/go-asn1-ber/asn1-ber/content_int.go
new file mode 100644
index 0000000000..20b500f553
--- /dev/null
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/content_int.go
@@ -0,0 +1,25 @@
+package ber
+
+func encodeUnsignedInteger(i uint64) []byte {
+ n := uint64Length(i)
+ out := make([]byte, n)
+
+ var j int
+ for ; n > 0; n-- {
+ out[j] = byte(i >> uint((n-1)*8))
+ j++
+ }
+
+ return out
+}
+
+func uint64Length(i uint64) (numBytes int) {
+ numBytes = 1
+
+ for i > 255 {
+ numBytes++
+ i >>= 8
+ }
+
+ return
+}
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 0000000000..51215f0619
--- /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 <minute> is omitted, then <fraction> represents a fraction of an
+ // hour; otherwise, if <second> and <leap-second> are omitted, then
+ // <fraction> represents a fraction of a minute; otherwise, <fraction>
+ // 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 0000000000..ee0b4be2c2
--- /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/github.com/go-asn1-ber/asn1-ber/header.go b/vendor/github.com/go-asn1-ber/asn1-ber/header.go
new file mode 100644
index 0000000000..7dfa6b9a7d
--- /dev/null
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/header.go
@@ -0,0 +1,38 @@
+package ber
+
+import (
+ "errors"
+ "fmt"
+ "io"
+)
+
+func readHeader(reader io.Reader) (identifier Identifier, length int, read int, err error) {
+ var (
+ c, l int
+ i Identifier
+ )
+
+ if i, c, err = readIdentifier(reader); err != nil {
+ return Identifier{}, 0, read, err
+ }
+ identifier = i
+ read += c
+
+ if l, c, err = readLength(reader); err != nil {
+ return Identifier{}, 0, read, err
+ }
+ 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/github.com/go-asn1-ber/asn1-ber/identifier.go b/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
new file mode 100644
index 0000000000..e8c435749a
--- /dev/null
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
@@ -0,0 +1,112 @@
+package ber
+
+import (
+ "errors"
+ "fmt"
+ "io"
+)
+
+func readIdentifier(reader io.Reader) (Identifier, int, error) {
+ identifier := Identifier{}
+ read := 0
+
+ // identifier byte
+ b, err := readByte(reader)
+ if err != nil {
+ if Debug {
+ fmt.Printf("error reading identifier byte: %v\n", err)
+ }
+ return Identifier{}, read, err
+ }
+ read++
+
+ identifier.ClassType = Class(b) & ClassBitmask
+ identifier.TagType = Type(b) & TypeBitmask
+
+ if tag := Tag(b) & TagBitmask; tag != HighTag {
+ // short-form tag
+ identifier.Tag = tag
+ return identifier, read, nil
+ }
+
+ // high-tag-number tag
+ tagBytes := 0
+ for {
+ b, err := readByte(reader)
+ if err != nil {
+ if Debug {
+ fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err)
+ }
+ return Identifier{}, read, err
+ }
+ tagBytes++
+ read++
+
+ // Lowest 7 bits get appended to the tag value (x.690, 8.1.2.4.2.b)
+ identifier.Tag <<= 7
+ identifier.Tag |= Tag(b) & HighTagValueBitmask
+
+ // First byte may not be all zeros (x.690, 8.1.2.4.2.c)
+ if tagBytes == 1 && identifier.Tag == 0 {
+ return Identifier{}, read, errors.New("invalid first high-tag-number tag byte")
+ }
+ // Overflow of int64
+ // TODO: support big int tags?
+ if tagBytes > 9 {
+ return Identifier{}, read, errors.New("high-tag-number tag overflow")
+ }
+
+ // Top bit of 0 means this is the last byte in the high-tag-number tag (x.690, 8.1.2.4.2.a)
+ if Tag(b)&HighTagContinueBitmask == 0 {
+ break
+ }
+ }
+
+ return identifier, read, nil
+}
+
+func encodeIdentifier(identifier Identifier) []byte {
+ b := []byte{0x0}
+ b[0] |= byte(identifier.ClassType)
+ b[0] |= byte(identifier.TagType)
+
+ if identifier.Tag < HighTag {
+ // Short-form
+ b[0] |= byte(identifier.Tag)
+ } else {
+ // high-tag-number
+ b[0] |= byte(HighTag)
+
+ tag := identifier.Tag
+
+ b = append(b, encodeHighTag(tag)...)
+ }
+ return b
+}
+
+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/github.com/go-asn1-ber/asn1-ber/length.go b/vendor/github.com/go-asn1-ber/asn1-ber/length.go
new file mode 100644
index 0000000000..9cc195d0bd
--- /dev/null
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/length.go
@@ -0,0 +1,81 @@
+package ber
+
+import (
+ "errors"
+ "fmt"
+ "io"
+)
+
+func readLength(reader io.Reader) (length int, read int, err error) {
+ // length byte
+ b, err := readByte(reader)
+ if err != nil {
+ if Debug {
+ fmt.Printf("error reading length byte: %v\n", err)
+ }
+ return 0, 0, err
+ }
+ read++
+
+ switch {
+ case b == 0xFF:
+ // Invalid 0xFF (x.600, 8.1.3.5.c)
+ return 0, read, errors.New("invalid length byte 0xff")
+
+ case b == LengthLongFormBitmask:
+ // Indefinite form, we have to decode packets until we encounter an EOC packet (x.600, 8.1.3.6)
+ length = LengthIndefinite
+
+ case b&LengthLongFormBitmask == 0:
+ // Short definite form, extract the length from the bottom 7 bits (x.600, 8.1.3.4)
+ length = int(b) & LengthValueBitmask
+
+ case b&LengthLongFormBitmask != 0:
+ // Long definite form, extract the number of length bytes to follow from the bottom 7 bits (x.600, 8.1.3.5.b)
+ lengthBytes := int(b) & LengthValueBitmask
+ // Protect against overflow
+ // TODO: support big int length?
+ 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 {
+ if Debug {
+ fmt.Printf("error reading long-form length byte %d: %v\n", i, err)
+ }
+ return 0, read, err
+ }
+ read++
+
+ // x.600, 8.1.3.5
+ 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:
+ return 0, read, errors.New("invalid length byte")
+ }
+
+ return length, read, nil
+}
+
+func encodeLength(length int) []byte {
+ lengthBytes := encodeUnsignedInteger(uint64(length))
+ if length > 127 || len(lengthBytes) > 1 {
+ longFormBytes := []byte{LengthLongFormBitmask | byte(len(lengthBytes))}
+ longFormBytes = append(longFormBytes, lengthBytes...)
+ lengthBytes = longFormBytes
+ }
+ 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 0000000000..610a003a73
--- /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/github.com/go-asn1-ber/asn1-ber/util.go b/vendor/github.com/go-asn1-ber/asn1-ber/util.go
new file mode 100644
index 0000000000..14dc87d7c9
--- /dev/null
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/util.go
@@ -0,0 +1,24 @@
+package ber
+
+import "io"
+
+func readByte(reader io.Reader) (byte, error) {
+ bytes := make([]byte, 1)
+ _, err := io.ReadFull(reader, bytes)
+ if err != nil {
+ if err == io.EOF {
+ return 0, io.ErrUnexpectedEOF
+ }
+ return 0, err
+ }
+ return bytes[0], nil
+}
+
+func isEOCPacket(p *Packet) bool {
+ return p != nil &&
+ p.Tag == TagEOC &&
+ p.ClassType == ClassUniversal &&
+ p.TagType == TypePrimitive &&
+ len(p.ByteValue) == 0 &&
+ len(p.Children) == 0
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/LICENSE b/vendor/github.com/go-ldap/ldap/v3/LICENSE
new file mode 100644
index 0000000000..6c0ed4b387
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
+Portions copyright (c) 2015-2016 go-ldap 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/github.com/go-ldap/ldap/v3/add.go b/vendor/github.com/go-ldap/ldap/v3/add.go
new file mode 100644
index 0000000000..baecd787d2
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/add.go
@@ -0,0 +1,91 @@
+package ldap
+
+import (
+ "log"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// Attribute represents an LDAP attribute
+type Attribute struct {
+ // Type is the name of the LDAP attribute
+ Type string
+ // Vals are the LDAP attribute values
+ Vals []string
+}
+
+func (a *Attribute) encode() *ber.Packet {
+ seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
+ seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type"))
+ set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
+ for _, value := range a.Vals {
+ set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
+ }
+ seq.AppendChild(set)
+ return seq
+}
+
+// AddRequest represents an LDAP AddRequest operation
+type AddRequest struct {
+ // DN identifies the entry being added
+ DN string
+ // Attributes list the attributes of the new entry
+ Attributes []Attribute
+ // Controls hold optional controls to send with the request
+ Controls []Control
+}
+
+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 req.Attributes {
+ attributes.AppendChild(attribute.encode())
+ }
+ 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 (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
+func NewAddRequest(dn string, controls []Control) *AddRequest {
+ return &AddRequest{
+ DN: dn,
+ Controls: controls,
+ }
+
+}
+
+// Add performs the given AddRequest
+func (l *Conn) Add(addRequest *AddRequest) error {
+ msgCtx, err := l.doRequest(addRequest)
+ if err != nil {
+ return err
+ }
+ defer l.finishMessage(msgCtx)
+
+ packet, err := l.readPacket(msgCtx)
+ if err != nil {
+ return err
+ }
+
+ if packet.Children[1].Tag == ApplicationAddResponse {
+ 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/bind.go b/vendor/github.com/go-ldap/ldap/v3/bind.go
new file mode 100644
index 0000000000..a7194c9c52
--- /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 0000000000..619677c779
--- /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 0000000000..cd43e4c53d
--- /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/github.com/go-ldap/ldap/v3/conn.go b/vendor/github.com/go-ldap/ldap/v3/conn.go
new file mode 100644
index 0000000000..8b8c41e73f
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/conn.go
@@ -0,0 +1,570 @@
+package ldap
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "log"
+ "net"
+ "net/url"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+const (
+ // MessageQuit causes the processMessages loop to exit
+ MessageQuit = 0
+ // MessageRequest sends a request to the server
+ MessageRequest = 1
+ // MessageResponse receives a response from the server
+ MessageResponse = 2
+ // MessageFinish indicates the client considers a particular message ID to be finished
+ MessageFinish = 3
+ // MessageTimeout indicates the client-specified timeout for a particular message ID has been reached
+ MessageTimeout = 4
+)
+
+const (
+ // DefaultLdapPort default ldap port for pure TCP connection
+ DefaultLdapPort = "389"
+ // DefaultLdapsPort default ldap port for SSL connection
+ DefaultLdapsPort = "636"
+)
+
+// PacketResponse contains the packet or error encountered reading a response
+type PacketResponse struct {
+ // Packet is the packet read from the server
+ Packet *ber.Packet
+ // Error is an error encountered while reading
+ Error error
+}
+
+// ReadPacket returns the packet or an error
+func (pr *PacketResponse) ReadPacket() (*ber.Packet, error) {
+ if (pr == nil) || (pr.Packet == nil && pr.Error == nil) {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve response"))
+ }
+ return pr.Packet, pr.Error
+}
+
+type messageContext struct {
+ id int64
+ // close(done) should only be called from finishMessage()
+ done chan struct{}
+ // close(responses) should only be called from processMessages(), and only sent to from sendResponse()
+ responses chan *PacketResponse
+}
+
+// sendResponse should only be called within the processMessages() loop which
+// is also responsible for closing the responses channel.
+func (msgCtx *messageContext) sendResponse(packet *PacketResponse) {
+ select {
+ case msgCtx.responses <- packet:
+ // Successfully sent packet to message handler.
+ case <-msgCtx.done:
+ // The request handler is done and will not receive more
+ // packets.
+ }
+}
+
+type messagePacket struct {
+ Op int
+ MessageID int64
+ Packet *ber.Packet
+ Context *messageContext
+}
+
+type sendMessageFlags uint
+
+const (
+ startTLS sendMessageFlags = 1 << iota
+)
+
+// Conn represents an LDAP Connection
+type Conn struct {
+ // requestTimeout is loaded atomically
+ // so we need to ensure 64-bit alignment on 32-bit platforms.
+ requestTimeout int64
+ conn net.Conn
+ isTLS bool
+ closing uint32
+ closeErr atomic.Value
+ isStartingTLS bool
+ Debug debugging
+ chanConfirm chan struct{}
+ messageContexts map[int64]*messageContext
+ chanMessage chan *messagePacket
+ chanMessageID chan int64
+ wgClose sync.WaitGroup
+ outstandingRequests uint
+ messageMutex sync.Mutex
+}
+
+var _ Client = &Conn{}
+
+// DefaultTimeout is a package-level variable that sets the timeout value
+// used for the Dial and DialTLS methods.
+//
+// WARNING: since this is a package-level variable, setting this value from
+// 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 {
+ return nil, NewError(ErrorNetwork, err)
+ }
+ conn := NewConn(c, false)
+ conn.Start()
+ return conn, nil
+}
+
+// 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 {
+ return nil, NewError(ErrorNetwork, err)
+ }
+ conn := NewConn(c, true)
+ conn.Start()
+ return conn, nil
+}
+
+// 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)
+ }
+
+ var dc DialContext
+ for _, opt := range opts {
+ opt(&dc)
+ }
+ if dc.d == nil {
+ dc.d = &net.Dialer{Timeout: DefaultTimeout}
+ }
+
+ c, err := dc.dial(u)
+ if err != nil {
+ return nil, NewError(ErrorNetwork, err)
+ }
+
+ conn := NewConn(c, u.Scheme == "ldaps")
+ conn.Start()
+ return conn, nil
+}
+
+// NewConn returns a new Conn using conn for network I/O.
+func NewConn(conn net.Conn, isTLS bool) *Conn {
+ return &Conn{
+ conn: conn,
+ chanConfirm: make(chan struct{}),
+ chanMessageID: make(chan int64),
+ chanMessage: make(chan *messagePacket, 10),
+ messageContexts: map[int64]*messageContext{},
+ requestTimeout: 0,
+ isTLS: isTLS,
+ }
+}
+
+// Start initializes goroutines to read responses and process messages
+func (l *Conn) Start() {
+ l.wgClose.Add(1)
+ go l.reader()
+ go l.processMessages()
+}
+
+// IsClosing returns whether or not we're currently closing.
+func (l *Conn) IsClosing() bool {
+ return atomic.LoadUint32(&l.closing) == 1
+}
+
+// setClosing sets the closing value to true
+func (l *Conn) setClosing() bool {
+ return atomic.CompareAndSwapUint32(&l.closing, 0, 1)
+}
+
+// Close closes the connection.
+func (l *Conn) Close() {
+ l.messageMutex.Lock()
+ defer l.messageMutex.Unlock()
+
+ if l.setClosing() {
+ l.Debug.Printf("Sending quit message and waiting for confirmation")
+ l.chanMessage <- &messagePacket{Op: MessageQuit}
+ <-l.chanConfirm
+ close(l.chanMessage)
+
+ l.Debug.Printf("Closing network connection")
+ if err := l.conn.Close(); err != nil {
+ log.Println(err)
+ }
+
+ l.wgClose.Done()
+ }
+ l.wgClose.Wait()
+}
+
+// SetTimeout sets the time after a request is sent that a MessageTimeout triggers
+func (l *Conn) SetTimeout(timeout time.Duration) {
+ if timeout > 0 {
+ atomic.StoreInt64(&l.requestTimeout, int64(timeout))
+ }
+}
+
+// Returns the next available messageID
+func (l *Conn) nextMessageID() int64 {
+ if messageID, ok := <-l.chanMessageID; ok {
+ return messageID
+ }
+ return 0
+}
+
+// StartTLS sends the command to start a TLS session and then creates a new TLS Client
+func (l *Conn) StartTLS(config *tls.Config) error {
+ if l.isTLS {
+ return NewError(ErrorNetwork, errors.New("ldap: already encrypted"))
+ }
+
+ 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, ApplicationExtendedRequest, nil, "Start TLS")
+ request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
+ packet.AppendChild(request)
+ l.Debug.PrintPacket(packet)
+
+ msgCtx, err := l.sendMessageWithFlags(packet, startTLS)
+ 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 {
+ l.Close()
+ return err
+ }
+ l.Debug.PrintPacket(packet)
+ }
+
+ if err := GetLDAPError(packet); err == nil {
+ conn := tls.Client(l.conn, config)
+
+ if connErr := conn.Handshake(); connErr != nil {
+ l.Close()
+ return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", connErr))
+ }
+
+ l.isTLS = true
+ l.conn = conn
+ } else {
+ return err
+ }
+ go l.reader()
+
+ return nil
+}
+
+// TLSConnectionState returns the client's TLS connection state.
+// The return values are their zero values if StartTLS did
+// not succeed.
+func (l *Conn) TLSConnectionState() (state tls.ConnectionState, ok bool) {
+ tc, ok := l.conn.(*tls.Conn)
+ if !ok {
+ return
+ }
+ return tc.ConnectionState(), true
+}
+
+func (l *Conn) sendMessage(packet *ber.Packet) (*messageContext, error) {
+ return l.sendMessageWithFlags(packet, 0)
+}
+
+func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (*messageContext, error) {
+ if l.IsClosing() {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
+ }
+ l.messageMutex.Lock()
+ l.Debug.Printf("flags&startTLS = %d", flags&startTLS)
+ if l.isStartingTLS {
+ l.messageMutex.Unlock()
+ return nil, NewError(ErrorNetwork, errors.New("ldap: connection is in startls phase"))
+ }
+ if flags&startTLS != 0 {
+ if l.outstandingRequests != 0 {
+ l.messageMutex.Unlock()
+ return nil, NewError(ErrorNetwork, errors.New("ldap: cannot StartTLS with outstanding requests"))
+ }
+ l.isStartingTLS = true
+ }
+ l.outstandingRequests++
+
+ l.messageMutex.Unlock()
+
+ responses := make(chan *PacketResponse)
+ messageID := packet.Children[0].Value.(int64)
+ message := &messagePacket{
+ Op: MessageRequest,
+ MessageID: messageID,
+ Packet: packet,
+ Context: &messageContext{
+ id: messageID,
+ done: make(chan struct{}),
+ responses: responses,
+ },
+ }
+ 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
+}
+
+func (l *Conn) finishMessage(msgCtx *messageContext) {
+ close(msgCtx.done)
+
+ if l.IsClosing() {
+ return
+ }
+
+ l.messageMutex.Lock()
+ l.outstandingRequests--
+ if l.isStartingTLS {
+ l.isStartingTLS = false
+ }
+ l.messageMutex.Unlock()
+
+ message := &messagePacket{
+ Op: MessageFinish,
+ MessageID: msgCtx.id,
+ }
+ l.sendProcessMessage(message)
+}
+
+func (l *Conn) sendProcessMessage(message *messagePacket) bool {
+ l.messageMutex.Lock()
+ defer l.messageMutex.Unlock()
+ if l.IsClosing() {
+ return false
+ }
+ l.chanMessage <- message
+ return true
+}
+
+func (l *Conn) processMessages() {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("ldap: recovered panic in processMessages: %v", err)
+ }
+ for messageID, msgCtx := range l.messageContexts {
+ // If we are closing due to an error, inform anyone who
+ // is waiting about the error.
+ if l.IsClosing() && l.closeErr.Load() != nil {
+ msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)})
+ }
+ l.Debug.Printf("Closing channel for MessageID %d", messageID)
+ close(msgCtx.responses)
+ delete(l.messageContexts, messageID)
+ }
+ close(l.chanMessageID)
+ close(l.chanConfirm)
+ }()
+
+ var messageID int64 = 1
+ for {
+ select {
+ case l.chanMessageID <- messageID:
+ messageID++
+ case message := <-l.chanMessage:
+ switch message.Op {
+ case MessageQuit:
+ l.Debug.Printf("Shutting down - quit message received")
+ return
+ case MessageRequest:
+ // Add to message list and write to network
+ l.Debug.Printf("Sending message %d", message.MessageID)
+
+ buf := message.Packet.Bytes()
+ _, err := l.conn.Write(buf)
+ if err != nil {
+ l.Debug.Printf("Error Sending Message: %s", err.Error())
+ message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)})
+ close(message.Context.responses)
+ break
+ }
+
+ // Only add to messageContexts if we were able to
+ // successfully write the message.
+ l.messageContexts[message.MessageID] = message.Context
+
+ // Add timeout if defined
+ requestTimeout := time.Duration(atomic.LoadInt64(&l.requestTimeout))
+ if requestTimeout > 0 {
+ go func() {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("ldap: recovered panic in RequestTimeout: %v", err)
+ }
+ }()
+ time.Sleep(requestTimeout)
+ timeoutMessage := &messagePacket{
+ Op: MessageTimeout,
+ MessageID: message.MessageID,
+ }
+ l.sendProcessMessage(timeoutMessage)
+ }()
+ }
+ case MessageResponse:
+ l.Debug.Printf("Receiving message %d", message.MessageID)
+ if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
+ msgCtx.sendResponse(&PacketResponse{message.Packet, nil})
+ } else {
+ log.Printf("Received unexpected message %d, %v", message.MessageID, l.IsClosing())
+ l.Debug.PrintPacket(message.Packet)
+ }
+ case MessageTimeout:
+ // Handle the timeout by closing the channel
+ // All reads will return immediately
+ if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
+ l.Debug.Printf("Receiving message timeout for %d", message.MessageID)
+ msgCtx.sendResponse(&PacketResponse{message.Packet, errors.New("ldap: connection timed out")})
+ delete(l.messageContexts, message.MessageID)
+ close(msgCtx.responses)
+ }
+ case MessageFinish:
+ l.Debug.Printf("Finished message %d", message.MessageID)
+ if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
+ delete(l.messageContexts, message.MessageID)
+ close(msgCtx.responses)
+ }
+ }
+ }
+ }
+}
+
+func (l *Conn) reader() {
+ cleanstop := false
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("ldap: recovered panic in reader: %v", err)
+ }
+ if !cleanstop {
+ l.Close()
+ }
+ }()
+
+ for {
+ if cleanstop {
+ l.Debug.Printf("reader clean stopping (without closing the connection)")
+ return
+ }
+ packet, err := ber.ReadPacket(l.conn)
+ if err != nil {
+ // 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)
+ }
+ return
+ }
+ 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
+ }
+ l.messageMutex.Lock()
+ if l.isStartingTLS {
+ cleanstop = true
+ }
+ l.messageMutex.Unlock()
+ message := &messagePacket{
+ Op: MessageResponse,
+ MessageID: packet.Children[0].Value.(int64),
+ Packet: packet,
+ }
+ if !l.sendProcessMessage(message) {
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/control.go b/vendor/github.com/go-ldap/ldap/v3/control.go
new file mode 100644
index 0000000000..7d7999cc6b
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/control.go
@@ -0,0 +1,492 @@
+package ldap
+
+import (
+ "fmt"
+ "strconv"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+const (
+ // ControlTypePaging - https://www.ietf.org/rfc/rfc2696.txt
+ ControlTypePaging = "1.2.840.113556.1.4.319"
+ // ControlTypeBeheraPasswordPolicy - https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
+ ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
+ // ControlTypeVChuPasswordMustChange - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
+ ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
+ // ControlTypeVChuPasswordWarning - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
+ ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
+ // ControlTypeManageDsaIT - https://tools.ietf.org/html/rfc3296
+ ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
+
+ // ControlTypeMicrosoftNotification - https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
+ ControlTypeMicrosoftNotification = "1.2.840.113556.1.4.528"
+ // ControlTypeMicrosoftShowDeleted - https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
+ ControlTypeMicrosoftShowDeleted = "1.2.840.113556.1.4.417"
+)
+
+// ControlTypeMap maps controls to text descriptions
+var ControlTypeMap = map[string]string{
+ ControlTypePaging: "Paging",
+ ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
+ ControlTypeManageDsaIT: "Manage DSA IT",
+ ControlTypeMicrosoftNotification: "Change Notification - Microsoft",
+ ControlTypeMicrosoftShowDeleted: "Show Deleted Objects - Microsoft",
+}
+
+// Control defines an interface controls provide to encode and describe themselves
+type Control interface {
+ // GetControlType returns the OID
+ GetControlType() string
+ // Encode returns the ber packet representation
+ Encode() *ber.Packet
+ // String returns a human-readable description
+ String() string
+}
+
+// ControlString implements the Control interface for simple controls
+type ControlString struct {
+ ControlType string
+ Criticality bool
+ ControlValue string
+}
+
+// GetControlType returns the OID
+func (c *ControlString) GetControlType() string {
+ return c.ControlType
+}
+
+// Encode returns the ber packet representation
+func (c *ControlString) Encode() *ber.Packet {
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")"))
+ if c.Criticality {
+ packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
+ }
+ if c.ControlValue != "" {
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value"))
+ }
+ return packet
+}
+
+// String returns a human-readable description
+func (c *ControlString) String() string {
+ return fmt.Sprintf("Control Type: %s (%q) Criticality: %t Control Value: %s", ControlTypeMap[c.ControlType], c.ControlType, c.Criticality, c.ControlValue)
+}
+
+// ControlPaging implements the paging control described in https://www.ietf.org/rfc/rfc2696.txt
+type ControlPaging struct {
+ // PagingSize indicates the page size
+ PagingSize uint32
+ // Cookie is an opaque value returned by the server to track a paging cursor
+ Cookie []byte
+}
+
+// GetControlType returns the OID
+func (c *ControlPaging) GetControlType() string {
+ return ControlTypePaging
+}
+
+// Encode returns the ber packet representation
+func (c *ControlPaging) Encode() *ber.Packet {
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypePaging, "Control Type ("+ControlTypeMap[ControlTypePaging]+")"))
+
+ p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Paging)")
+ seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Search Control Value")
+ seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.PagingSize), "Paging Size"))
+ cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
+ cookie.Value = c.Cookie
+ cookie.Data.Write(c.Cookie)
+ seq.AppendChild(cookie)
+ p2.AppendChild(seq)
+
+ packet.AppendChild(p2)
+ return packet
+}
+
+// String returns a human-readable description
+func (c *ControlPaging) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t PagingSize: %d Cookie: %q",
+ ControlTypeMap[ControlTypePaging],
+ ControlTypePaging,
+ false,
+ c.PagingSize,
+ c.Cookie)
+}
+
+// SetCookie stores the given cookie in the paging control
+func (c *ControlPaging) SetCookie(cookie []byte) {
+ c.Cookie = cookie
+}
+
+// ControlBeheraPasswordPolicy implements the control described in https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
+type ControlBeheraPasswordPolicy struct {
+ // Expire contains the number of seconds before a password will expire
+ Expire int64
+ // Grace indicates the remaining number of times a user will be allowed to authenticate with an expired password
+ Grace int64
+ // Error indicates the error code
+ Error int8
+ // ErrorString is a human readable error
+ ErrorString string
+}
+
+// GetControlType returns the OID
+func (c *ControlBeheraPasswordPolicy) GetControlType() string {
+ return ControlTypeBeheraPasswordPolicy
+}
+
+// Encode returns the ber packet representation
+func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet {
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeBeheraPasswordPolicy, "Control Type ("+ControlTypeMap[ControlTypeBeheraPasswordPolicy]+")"))
+
+ return packet
+}
+
+// String returns a human-readable description
+func (c *ControlBeheraPasswordPolicy) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t Expire: %d Grace: %d Error: %d, ErrorString: %s",
+ ControlTypeMap[ControlTypeBeheraPasswordPolicy],
+ ControlTypeBeheraPasswordPolicy,
+ false,
+ c.Expire,
+ c.Grace,
+ c.Error,
+ c.ErrorString)
+}
+
+// ControlVChuPasswordMustChange implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
+type ControlVChuPasswordMustChange struct {
+ // MustChange indicates if the password is required to be changed
+ MustChange bool
+}
+
+// GetControlType returns the OID
+func (c *ControlVChuPasswordMustChange) GetControlType() string {
+ return ControlTypeVChuPasswordMustChange
+}
+
+// Encode returns the ber packet representation
+func (c *ControlVChuPasswordMustChange) Encode() *ber.Packet {
+ return nil
+}
+
+// String returns a human-readable description
+func (c *ControlVChuPasswordMustChange) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t MustChange: %v",
+ ControlTypeMap[ControlTypeVChuPasswordMustChange],
+ ControlTypeVChuPasswordMustChange,
+ false,
+ c.MustChange)
+}
+
+// ControlVChuPasswordWarning implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
+type ControlVChuPasswordWarning struct {
+ // Expire indicates the time in seconds until the password expires
+ Expire int64
+}
+
+// GetControlType returns the OID
+func (c *ControlVChuPasswordWarning) GetControlType() string {
+ return ControlTypeVChuPasswordWarning
+}
+
+// Encode returns the ber packet representation
+func (c *ControlVChuPasswordWarning) Encode() *ber.Packet {
+ return nil
+}
+
+// String returns a human-readable description
+func (c *ControlVChuPasswordWarning) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t Expire: %b",
+ ControlTypeMap[ControlTypeVChuPasswordWarning],
+ ControlTypeVChuPasswordWarning,
+ false,
+ c.Expire)
+}
+
+// ControlManageDsaIT implements the control described in https://tools.ietf.org/html/rfc3296
+type ControlManageDsaIT struct {
+ // Criticality indicates if this control is required
+ Criticality bool
+}
+
+// GetControlType returns the OID
+func (c *ControlManageDsaIT) GetControlType() string {
+ return ControlTypeManageDsaIT
+}
+
+// Encode returns the ber packet representation
+func (c *ControlManageDsaIT) Encode() *ber.Packet {
+ //FIXME
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeManageDsaIT, "Control Type ("+ControlTypeMap[ControlTypeManageDsaIT]+")"))
+ if c.Criticality {
+ packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
+ }
+ return packet
+}
+
+// String returns a human-readable description
+func (c *ControlManageDsaIT) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t",
+ ControlTypeMap[ControlTypeManageDsaIT],
+ ControlTypeManageDsaIT,
+ c.Criticality)
+}
+
+// NewControlManageDsaIT returns a ControlManageDsaIT control
+func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {
+ return &ControlManageDsaIT{Criticality: Criticality}
+}
+
+// ControlMicrosoftNotification implements the control described in https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
+type ControlMicrosoftNotification struct{}
+
+// GetControlType returns the OID
+func (c *ControlMicrosoftNotification) GetControlType() string {
+ return ControlTypeMicrosoftNotification
+}
+
+// Encode returns the ber packet representation
+func (c *ControlMicrosoftNotification) Encode() *ber.Packet {
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftNotification, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftNotification]+")"))
+
+ return packet
+}
+
+// String returns a human-readable description
+func (c *ControlMicrosoftNotification) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q)",
+ ControlTypeMap[ControlTypeMicrosoftNotification],
+ ControlTypeMicrosoftNotification)
+}
+
+// NewControlMicrosoftNotification returns a ControlMicrosoftNotification control
+func NewControlMicrosoftNotification() *ControlMicrosoftNotification {
+ return &ControlMicrosoftNotification{}
+}
+
+// ControlMicrosoftShowDeleted implements the control described in https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
+type ControlMicrosoftShowDeleted struct{}
+
+// GetControlType returns the OID
+func (c *ControlMicrosoftShowDeleted) GetControlType() string {
+ return ControlTypeMicrosoftShowDeleted
+}
+
+// Encode returns the ber packet representation
+func (c *ControlMicrosoftShowDeleted) Encode() *ber.Packet {
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftShowDeleted, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftShowDeleted]+")"))
+
+ return packet
+}
+
+// String returns a human-readable description
+func (c *ControlMicrosoftShowDeleted) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q)",
+ ControlTypeMap[ControlTypeMicrosoftShowDeleted],
+ ControlTypeMicrosoftShowDeleted)
+}
+
+// NewControlMicrosoftShowDeleted returns a ControlMicrosoftShowDeleted control
+func NewControlMicrosoftShowDeleted() *ControlMicrosoftShowDeleted {
+ return &ControlMicrosoftShowDeleted{}
+}
+
+// FindControl returns the first control of the given type in the list, or nil
+func FindControl(controls []Control, controlType string) Control {
+ for _, c := range controls {
+ if c.GetControlType() == controlType {
+ return c
+ }
+ }
+ return nil
+}
+
+// DecodeControl returns a control read from the given packet, or nil if no recognized control can be made
+func DecodeControl(packet *ber.Packet) (Control, error) {
+ var (
+ ControlType = ""
+ Criticality = false
+ value *ber.Packet
+ )
+
+ switch len(packet.Children) {
+ case 0:
+ // at least one child is required for control type
+ return nil, fmt.Errorf("at least one child is required for control type")
+
+ case 1:
+ // just type, no criticality or value
+ packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
+ ControlType = packet.Children[0].Value.(string)
+
+ case 2:
+ packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
+ ControlType = packet.Children[0].Value.(string)
+
+ // Children[1] could be criticality or value (both are optional)
+ // duck-type on whether this is a boolean
+ if _, ok := packet.Children[1].Value.(bool); ok {
+ packet.Children[1].Description = "Criticality"
+ Criticality = packet.Children[1].Value.(bool)
+ } else {
+ packet.Children[1].Description = "Control Value"
+ value = packet.Children[1]
+ }
+
+ case 3:
+ packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
+ ControlType = packet.Children[0].Value.(string)
+
+ packet.Children[1].Description = "Criticality"
+ Criticality = packet.Children[1].Value.(bool)
+
+ packet.Children[2].Description = "Control Value"
+ value = packet.Children[2]
+
+ default:
+ // more than 3 children is invalid
+ return nil, fmt.Errorf("more than 3 children is invalid for controls")
+ }
+
+ switch ControlType {
+ case ControlTypeManageDsaIT:
+ return NewControlManageDsaIT(Criticality), nil
+ case ControlTypePaging:
+ value.Description += " (Paging)"
+ c := new(ControlPaging)
+ if value.Value != nil {
+ valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ value.Data.Truncate(0)
+ value.Value = nil
+ value.AppendChild(valueChildren)
+ }
+ value = value.Children[0]
+ value.Description = "Search Control Value"
+ value.Children[0].Description = "Paging Size"
+ value.Children[1].Description = "Cookie"
+ c.PagingSize = uint32(value.Children[0].Value.(int64))
+ c.Cookie = value.Children[1].Data.Bytes()
+ value.Children[1].Value = c.Cookie
+ return c, nil
+ case ControlTypeBeheraPasswordPolicy:
+ value.Description += " (Password Policy - Behera)"
+ c := NewControlBeheraPasswordPolicy()
+ if value.Value != nil {
+ valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ value.Data.Truncate(0)
+ value.Value = nil
+ value.AppendChild(valueChildren)
+ }
+
+ sequence := value.Children[0]
+
+ for _, child := range sequence.Children {
+ if child.Tag == 0 {
+ //Warning
+ warningPacket := child.Children[0]
+ val, err := ber.ParseInt64(warningPacket.Data.Bytes())
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ 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
+ 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]
+ }
+ }
+ return c, nil
+ case ControlTypeVChuPasswordMustChange:
+ c := &ControlVChuPasswordMustChange{MustChange: true}
+ return c, nil
+ case ControlTypeVChuPasswordWarning:
+ c := &ControlVChuPasswordWarning{Expire: -1}
+ expireStr := ber.DecodeString(value.Data.Bytes())
+
+ expire, err := strconv.ParseInt(expireStr, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse value as int: %s", err)
+ }
+ c.Expire = expire
+ value.Value = c.Expire
+
+ return c, nil
+ case ControlTypeMicrosoftNotification:
+ return NewControlMicrosoftNotification(), nil
+ case ControlTypeMicrosoftShowDeleted:
+ return NewControlMicrosoftShowDeleted(), nil
+ default:
+ c := new(ControlString)
+ c.ControlType = ControlType
+ c.Criticality = Criticality
+ if value != nil {
+ c.ControlValue = value.Value.(string)
+ }
+ return c, nil
+ }
+}
+
+// NewControlString returns a generic control
+func NewControlString(controlType string, criticality bool, controlValue string) *ControlString {
+ return &ControlString{
+ ControlType: controlType,
+ Criticality: criticality,
+ ControlValue: controlValue,
+ }
+}
+
+// NewControlPaging returns a paging control
+func NewControlPaging(pagingSize uint32) *ControlPaging {
+ return &ControlPaging{PagingSize: pagingSize}
+}
+
+// NewControlBeheraPasswordPolicy returns a ControlBeheraPasswordPolicy
+func NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy {
+ return &ControlBeheraPasswordPolicy{
+ Expire: -1,
+ Grace: -1,
+ Error: -1,
+ }
+}
+
+func encodeControls(controls []Control) *ber.Packet {
+ packet := ber.Encode(ber.ClassContext, ber.TypeConstructed, 0, nil, "Controls")
+ for _, control := range controls {
+ packet.AppendChild(control.Encode())
+ }
+ return packet
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/debug.go b/vendor/github.com/go-ldap/ldap/v3/debug.go
new file mode 100644
index 0000000000..2c0b30c8d0
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/debug.go
@@ -0,0 +1,30 @@
+package ldap
+
+import (
+ "log"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// debugging type
+// - has a Printf method to write the debug output
+type debugging bool
+
+// 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 0000000000..6e98726775
--- /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/github.com/go-ldap/ldap/v3/dn.go b/vendor/github.com/go-ldap/ldap/v3/dn.go
new file mode 100644
index 0000000000..bff137cc85
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/dn.go
@@ -0,0 +1,207 @@
+package ldap
+
+import (
+ "bytes"
+ enchex "encoding/hex"
+ "errors"
+ "fmt"
+ "strings"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514
+type AttributeTypeAndValue struct {
+ // Type is the attribute type
+ Type string
+ // Value is the attribute value
+ Value string
+}
+
+// RelativeDN represents a relativeDistinguishedName from https://tools.ietf.org/html/rfc4514
+type RelativeDN struct {
+ Attributes []*AttributeTypeAndValue
+}
+
+// DN represents a distinguishedName from https://tools.ietf.org/html/rfc4514
+type DN struct {
+ RDNs []*RelativeDN
+}
+
+// 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)
+ rdn := new(RelativeDN)
+ rdn.Attributes = make([]*AttributeTypeAndValue, 0)
+ buffer := bytes.Buffer{}
+ attribute := new(AttributeTypeAndValue)
+ escaping := false
+
+ unescapedTrailingSpaces := 0
+ stringFromBuffer := func() string {
+ s := buffer.String()
+ s = s[0 : len(s)-unescapedTrailingSpaces]
+ buffer.Reset()
+ unescapedTrailingSpaces = 0
+ return s
+ }
+
+ for i := 0; i < len(str); i++ {
+ char := str[i]
+ switch {
+ case escaping:
+ unescapedTrailingSpaces = 0
+ escaping = false
+ switch char {
+ case ' ', '"', '#', '+', ',', ';', '<', '=', '>', '\\':
+ buffer.WriteByte(char)
+ continue
+ }
+ // Not a special character, assume hex encoded octet
+ if len(str) == i+1 {
+ return nil, errors.New("got corrupted escaped character")
+ }
+
+ dst := []byte{0}
+ n, err := enchex.Decode([]byte(dst), []byte(str[i:i+2]))
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode escaped character: %s", err)
+ } else if n != 1 {
+ return nil, fmt.Errorf("expected 1 byte when un-escaping, got %d", n)
+ }
+ buffer.WriteByte(dst[0])
+ i++
+ case char == '\\':
+ unescapedTrailingSpaces = 0
+ escaping = true
+ case char == '=':
+ attribute.Type = stringFromBuffer()
+ // Special case: If the first character in the value is # the
+ // following data is BER encoded so we can just fast forward
+ // and decode.
+ if len(str) > i+1 && str[i+1] == '#' {
+ i += 2
+ index := strings.IndexAny(str[i:], ",+")
+ data := str
+ if index > 0 {
+ data = str[i : i+index]
+ } else {
+ data = str[i:]
+ }
+ rawBER, err := enchex.DecodeString(data)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode BER encoding: %s", err)
+ }
+ packet, err := ber.DecodePacketErr(rawBER)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode BER packet: %s", err)
+ }
+ buffer.WriteString(packet.Data.String())
+ i += len(data) - 1
+ }
+ case char == ',' || char == '+':
+ // We're done with this RDN or value, push it
+ if len(attribute.Type) == 0 {
+ return nil, errors.New("incomplete type, value pair")
+ }
+ attribute.Value = stringFromBuffer()
+ rdn.Attributes = append(rdn.Attributes, attribute)
+ attribute = new(AttributeTypeAndValue)
+ if char == ',' {
+ dn.RDNs = append(dn.RDNs, rdn)
+ rdn = new(RelativeDN)
+ rdn.Attributes = make([]*AttributeTypeAndValue, 0)
+ }
+ case char == ' ' && buffer.Len() == 0:
+ // ignore unescaped leading spaces
+ continue
+ default:
+ if char == ' ' {
+ // Track unescaped spaces in case they are trailing and we need to remove them
+ unescapedTrailingSpaces++
+ } else {
+ // Reset if we see a non-space char
+ unescapedTrailingSpaces = 0
+ }
+ buffer.WriteByte(char)
+ }
+ }
+ if buffer.Len() > 0 {
+ if len(attribute.Type) == 0 {
+ return nil, errors.New("DN ended with incomplete type, value pair")
+ }
+ attribute.Value = stringFromBuffer()
+ rdn.Attributes = append(rdn.Attributes, attribute)
+ dn.RDNs = append(dn.RDNs, rdn)
+ }
+ return dn, nil
+}
+
+// Equal returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
+// Returns true if they have the same number of relative distinguished names
+// and corresponding relative distinguished names (by position) are the same.
+func (d *DN) Equal(other *DN) bool {
+ if len(d.RDNs) != len(other.RDNs) {
+ return false
+ }
+ for i := range d.RDNs {
+ if !d.RDNs[i].Equal(other.RDNs[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// AncestorOf returns true if the other DN consists of at least one RDN followed by all the RDNs of the current DN.
+// "ou=widgets,o=acme.com" is an ancestor of "ou=sprockets,ou=widgets,o=acme.com"
+// "ou=widgets,o=acme.com" is not an ancestor of "ou=sprockets,ou=widgets,o=foo.com"
+// "ou=widgets,o=acme.com" is not an ancestor of "ou=widgets,o=acme.com"
+func (d *DN) AncestorOf(other *DN) bool {
+ if len(d.RDNs) >= len(other.RDNs) {
+ return false
+ }
+ // Take the last `len(d.RDNs)` RDNs from the other DN to compare against
+ otherRDNs := other.RDNs[len(other.RDNs)-len(d.RDNs):]
+ for i := range d.RDNs {
+ if !d.RDNs[i].Equal(otherRDNs[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// Equal returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
+// Relative distinguished names are the same if and only if they have the same number of AttributeTypeAndValues
+// and each attribute of the first RDN is the same as the attribute of the second RDN with the same attribute type.
+// The order of attributes is not significant.
+// Case of attribute types is not significant.
+func (r *RelativeDN) Equal(other *RelativeDN) bool {
+ if len(r.Attributes) != len(other.Attributes) {
+ return false
+ }
+ return r.hasAllAttributes(other.Attributes) && other.hasAllAttributes(r.Attributes)
+}
+
+func (r *RelativeDN) hasAllAttributes(attrs []*AttributeTypeAndValue) bool {
+ for _, attr := range attrs {
+ found := false
+ for _, myattr := range r.Attributes {
+ if myattr.Equal(attr) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}
+
+// Equal returns true if the AttributeTypeAndValue is equivalent to the specified AttributeTypeAndValue
+// Case of the attribute type is not significant
+func (a *AttributeTypeAndValue) Equal(other *AttributeTypeAndValue) bool {
+ return strings.EqualFold(a.Type, other.Type) && a.Value == other.Value
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/doc.go b/vendor/github.com/go-ldap/ldap/v3/doc.go
new file mode 100644
index 0000000000..f20d39bc99
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/doc.go
@@ -0,0 +1,4 @@
+/*
+Package ldap provides basic LDAP v3 functionality.
+*/
+package ldap
diff --git a/vendor/github.com/go-ldap/ldap/v3/error.go b/vendor/github.com/go-ldap/ldap/v3/error.go
new file mode 100644
index 0000000000..3cdb7b318c
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/error.go
@@ -0,0 +1,253 @@
+package ldap
+
+import (
+ "fmt"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// LDAP Result Codes
+const (
+ LDAPResultSuccess = 0
+ LDAPResultOperationsError = 1
+ LDAPResultProtocolError = 2
+ LDAPResultTimeLimitExceeded = 3
+ LDAPResultSizeLimitExceeded = 4
+ LDAPResultCompareFalse = 5
+ LDAPResultCompareTrue = 6
+ LDAPResultAuthMethodNotSupported = 7
+ LDAPResultStrongAuthRequired = 8
+ LDAPResultReferral = 10
+ LDAPResultAdminLimitExceeded = 11
+ LDAPResultUnavailableCriticalExtension = 12
+ LDAPResultConfidentialityRequired = 13
+ LDAPResultSaslBindInProgress = 14
+ LDAPResultNoSuchAttribute = 16
+ LDAPResultUndefinedAttributeType = 17
+ LDAPResultInappropriateMatching = 18
+ LDAPResultConstraintViolation = 19
+ LDAPResultAttributeOrValueExists = 20
+ LDAPResultInvalidAttributeSyntax = 21
+ LDAPResultNoSuchObject = 32
+ LDAPResultAliasProblem = 33
+ LDAPResultInvalidDNSyntax = 34
+ LDAPResultIsLeaf = 35
+ LDAPResultAliasDereferencingProblem = 36
+ LDAPResultInappropriateAuthentication = 48
+ LDAPResultInvalidCredentials = 49
+ LDAPResultInsufficientAccessRights = 50
+ LDAPResultBusy = 51
+ LDAPResultUnavailable = 52
+ LDAPResultUnwillingToPerform = 53
+ LDAPResultLoopDetect = 54
+ LDAPResultSortControlMissing = 60
+ LDAPResultOffsetRangeError = 61
+ LDAPResultNamingViolation = 64
+ LDAPResultObjectClassViolation = 65
+ LDAPResultNotAllowedOnNonLeaf = 66
+ LDAPResultNotAllowedOnRDN = 67
+ LDAPResultEntryAlreadyExists = 68
+ LDAPResultObjectClassModsProhibited = 69
+ LDAPResultResultsTooLarge = 70
+ LDAPResultAffectsMultipleDSAs = 71
+ LDAPResultVirtualListViewErrorOrControlError = 76
+ LDAPResultOther = 80
+ LDAPResultServerDown = 81
+ LDAPResultLocalError = 82
+ LDAPResultEncodingError = 83
+ LDAPResultDecodingError = 84
+ LDAPResultTimeout = 85
+ LDAPResultAuthUnknown = 86
+ LDAPResultFilterError = 87
+ LDAPResultUserCanceled = 88
+ LDAPResultParamError = 89
+ LDAPResultNoMemory = 90
+ LDAPResultConnectError = 91
+ LDAPResultNotSupported = 92
+ LDAPResultControlNotFound = 93
+ LDAPResultNoResultsReturned = 94
+ LDAPResultMoreResultsToReturn = 95
+ LDAPResultClientLoop = 96
+ LDAPResultReferralLimitExceeded = 97
+ LDAPResultInvalidResponse = 100
+ LDAPResultAmbiguousResponse = 101
+ LDAPResultTLSNotSupported = 112
+ LDAPResultIntermediateResponse = 113
+ LDAPResultUnknownType = 114
+ LDAPResultCanceled = 118
+ LDAPResultNoSuchOperation = 119
+ LDAPResultTooLate = 120
+ LDAPResultCannotCancel = 121
+ LDAPResultAssertionFailed = 122
+ LDAPResultAuthorizationDenied = 123
+ LDAPResultSyncRefreshRequired = 4096
+
+ ErrorNetwork = 200
+ ErrorFilterCompile = 201
+ ErrorFilterDecompile = 202
+ ErrorDebugging = 203
+ ErrorUnexpectedMessage = 204
+ ErrorUnexpectedResponse = 205
+ ErrorEmptyPassword = 206
+)
+
+// LDAPResultCodeMap contains string descriptions for LDAP error codes
+var LDAPResultCodeMap = map[uint16]string{
+ LDAPResultSuccess: "Success",
+ LDAPResultOperationsError: "Operations Error",
+ LDAPResultProtocolError: "Protocol Error",
+ LDAPResultTimeLimitExceeded: "Time Limit Exceeded",
+ LDAPResultSizeLimitExceeded: "Size Limit Exceeded",
+ LDAPResultCompareFalse: "Compare False",
+ LDAPResultCompareTrue: "Compare True",
+ LDAPResultAuthMethodNotSupported: "Auth Method Not Supported",
+ LDAPResultStrongAuthRequired: "Strong Auth Required",
+ LDAPResultReferral: "Referral",
+ LDAPResultAdminLimitExceeded: "Admin Limit Exceeded",
+ LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension",
+ LDAPResultConfidentialityRequired: "Confidentiality Required",
+ LDAPResultSaslBindInProgress: "Sasl Bind In Progress",
+ LDAPResultNoSuchAttribute: "No Such Attribute",
+ LDAPResultUndefinedAttributeType: "Undefined Attribute Type",
+ LDAPResultInappropriateMatching: "Inappropriate Matching",
+ LDAPResultConstraintViolation: "Constraint Violation",
+ LDAPResultAttributeOrValueExists: "Attribute Or Value Exists",
+ LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax",
+ LDAPResultNoSuchObject: "No Such Object",
+ LDAPResultAliasProblem: "Alias Problem",
+ LDAPResultInvalidDNSyntax: "Invalid DN Syntax",
+ LDAPResultIsLeaf: "Is Leaf",
+ LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem",
+ LDAPResultInappropriateAuthentication: "Inappropriate Authentication",
+ LDAPResultInvalidCredentials: "Invalid Credentials",
+ LDAPResultInsufficientAccessRights: "Insufficient Access Rights",
+ LDAPResultBusy: "Busy",
+ LDAPResultUnavailable: "Unavailable",
+ LDAPResultUnwillingToPerform: "Unwilling To Perform",
+ LDAPResultLoopDetect: "Loop Detect",
+ LDAPResultSortControlMissing: "Sort Control Missing",
+ LDAPResultOffsetRangeError: "Result Offset Range Error",
+ LDAPResultNamingViolation: "Naming Violation",
+ LDAPResultObjectClassViolation: "Object Class Violation",
+ LDAPResultResultsTooLarge: "Results Too Large",
+ LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf",
+ LDAPResultNotAllowedOnRDN: "Not Allowed On RDN",
+ LDAPResultEntryAlreadyExists: "Entry Already Exists",
+ LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited",
+ LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs",
+ LDAPResultVirtualListViewErrorOrControlError: "Failed because of a problem related to the virtual list view",
+ LDAPResultOther: "Other",
+ LDAPResultServerDown: "Cannot establish a connection",
+ LDAPResultLocalError: "An error occurred",
+ LDAPResultEncodingError: "LDAP encountered an error while encoding",
+ LDAPResultDecodingError: "LDAP encountered an error while decoding",
+ LDAPResultTimeout: "LDAP timeout while waiting for a response from the server",
+ LDAPResultAuthUnknown: "The auth method requested in a bind request is unknown",
+ LDAPResultFilterError: "An error occurred while encoding the given search filter",
+ LDAPResultUserCanceled: "The user canceled the operation",
+ LDAPResultParamError: "An invalid parameter was specified",
+ LDAPResultNoMemory: "Out of memory error",
+ LDAPResultConnectError: "A connection to the server could not be established",
+ LDAPResultNotSupported: "An attempt has been made to use a feature not supported LDAP",
+ LDAPResultControlNotFound: "The controls required to perform the requested operation were not found",
+ LDAPResultNoResultsReturned: "No results were returned from the server",
+ LDAPResultMoreResultsToReturn: "There are more results in the chain of results",
+ LDAPResultClientLoop: "A loop has been detected. For example when following referrals",
+ LDAPResultReferralLimitExceeded: "The referral hop limit has been exceeded",
+ LDAPResultCanceled: "Operation was canceled",
+ LDAPResultNoSuchOperation: "Server has no knowledge of the operation requested for cancellation",
+ LDAPResultTooLate: "Too late to cancel the outstanding operation",
+ LDAPResultCannotCancel: "The identified operation does not support cancellation or the cancel operation cannot be performed",
+ LDAPResultAssertionFailed: "An assertion control given in the LDAP operation evaluated to false causing the operation to not be performed",
+ LDAPResultSyncRefreshRequired: "Refresh Required",
+ LDAPResultInvalidResponse: "Invalid Response",
+ LDAPResultAmbiguousResponse: "Ambiguous Response",
+ LDAPResultTLSNotSupported: "Tls Not Supported",
+ LDAPResultIntermediateResponse: "Intermediate Response",
+ LDAPResultUnknownType: "Unknown Type",
+ LDAPResultAuthorizationDenied: "Authorization Denied",
+
+ ErrorNetwork: "Network Error",
+ ErrorFilterCompile: "Filter Compile Error",
+ ErrorFilterDecompile: "Filter Decompile Error",
+ ErrorDebugging: "Debugging Error",
+ ErrorUnexpectedMessage: "Unexpected Message",
+ ErrorUnexpectedResponse: "Unexpected Response",
+ ErrorEmptyPassword: "Empty password not allowed by the client",
+}
+
+// Error holds LDAP error information
+type Error struct {
+ // Err is the underlying error
+ Err error
+ // ResultCode is the LDAP error code
+ 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 {
+ return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
+}
+
+// GetLDAPError creates an Error out of a BER packet representing a LDAPResult
+// The return is an error object. It can be casted to a Error structure.
+// This function returns nil if resultCode in the LDAPResult sequence is success(0).
+func GetLDAPError(packet *ber.Packet) error {
+ if packet == nil {
+ return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty packet")}
+ }
+
+ if len(packet.Children) >= 2 {
+ response := packet.Children[1]
+ if response == nil {
+ 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)),
+ Packet: packet,
+ }
+ }
+ }
+
+ return &Error{ResultCode: ErrorNetwork, Err: fmt.Errorf("Invalid packet format"), Packet: packet}
+}
+
+// NewError creates an LDAP error with the given code and underlying error
+func NewError(resultCode uint16, err error) error {
+ return &Error{ResultCode: resultCode, Err: err}
+}
+
+// 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
+ }
+
+ serverError, ok := err.(*Error)
+ if !ok {
+ return false
+ }
+
+ 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/github.com/go-ldap/ldap/v3/filter.go b/vendor/github.com/go-ldap/ldap/v3/filter.go
new file mode 100644
index 0000000000..73505e79b6
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/filter.go
@@ -0,0 +1,487 @@
+package ldap
+
+import (
+ "bytes"
+ hexpac "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// Filter choices
+const (
+ FilterAnd = 0
+ FilterOr = 1
+ FilterNot = 2
+ FilterEqualityMatch = 3
+ FilterSubstrings = 4
+ FilterGreaterOrEqual = 5
+ FilterLessOrEqual = 6
+ FilterPresent = 7
+ FilterApproxMatch = 8
+ FilterExtensibleMatch = 9
+)
+
+// FilterMap contains human readable descriptions of Filter choices
+var FilterMap = map[uint64]string{
+ FilterAnd: "And",
+ FilterOr: "Or",
+ FilterNot: "Not",
+ FilterEqualityMatch: "Equality Match",
+ FilterSubstrings: "Substrings",
+ FilterGreaterOrEqual: "Greater Or Equal",
+ FilterLessOrEqual: "Less Or Equal",
+ FilterPresent: "Present",
+ FilterApproxMatch: "Approx Match",
+ FilterExtensibleMatch: "Extensible Match",
+}
+
+// SubstringFilter options
+const (
+ FilterSubstringsInitial = 0
+ FilterSubstringsAny = 1
+ FilterSubstringsFinal = 2
+)
+
+// FilterSubstringsMap contains human readable descriptions of SubstringFilter choices
+var FilterSubstringsMap = map[uint64]string{
+ FilterSubstringsInitial: "Substrings Initial",
+ FilterSubstringsAny: "Substrings Any",
+ FilterSubstringsFinal: "Substrings Final",
+}
+
+// MatchingRuleAssertion choices
+const (
+ MatchingRuleAssertionMatchingRule = 1
+ MatchingRuleAssertionType = 2
+ MatchingRuleAssertionMatchValue = 3
+ MatchingRuleAssertionDNAttributes = 4
+)
+
+// MatchingRuleAssertionMap contains human readable descriptions of MatchingRuleAssertion choices
+var MatchingRuleAssertionMap = map[uint64]string{
+ MatchingRuleAssertionMatchingRule: "Matching Rule Assertion Matching Rule",
+ MatchingRuleAssertionType: "Matching Rule Assertion Type",
+ MatchingRuleAssertionMatchValue: "Matching Rule Assertion Match Value",
+ 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] != '(' {
+ return nil, NewError(ErrorFilterCompile, errors.New("ldap: filter does not start with an '('"))
+ }
+ packet, pos, err := compileFilter(filter, 1)
+ if err != nil {
+ return nil, err
+ }
+ switch {
+ case pos > len(filter):
+ return nil, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
+ case pos < len(filter):
+ return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:])))
+ }
+ return packet, nil
+}
+
+// DecompileFilter converts a packet representation of a filter into a string representation
+func DecompileFilter(packet *ber.Packet) (_ string, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter"))
+ }
+ }()
+
+ buf := bytes.NewBuffer(nil)
+ buf.WriteByte('(')
+ childStr := ""
+
+ switch packet.Tag {
+ case FilterAnd:
+ buf.WriteByte('&')
+ for _, child := range packet.Children {
+ childStr, err = DecompileFilter(child)
+ if err != nil {
+ return
+ }
+ buf.WriteString(childStr)
+ }
+ case FilterOr:
+ buf.WriteByte('|')
+ for _, child := range packet.Children {
+ childStr, err = DecompileFilter(child)
+ if err != nil {
+ return
+ }
+ buf.WriteString(childStr)
+ }
+ case FilterNot:
+ buf.WriteByte('!')
+ childStr, err = DecompileFilter(packet.Children[0])
+ if err != nil {
+ return
+ }
+ buf.WriteString(childStr)
+
+ case FilterSubstrings:
+ 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 {
+ buf.Write(_SymbolAny)
+ }
+ buf.WriteString(EscapeFilter(ber.DecodeString(child.Data.Bytes())))
+ if child.Tag != FilterSubstringsFinal {
+ buf.Write(_SymbolAny)
+ }
+ }
+ case FilterEqualityMatch:
+ buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
+ buf.WriteByte('=')
+ buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
+ case FilterGreaterOrEqual:
+ buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
+ buf.WriteString(">=")
+ buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
+ case FilterLessOrEqual:
+ buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
+ buf.WriteString("<=")
+ buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
+ case FilterPresent:
+ buf.WriteString(ber.DecodeString(packet.Data.Bytes()))
+ buf.WriteString("=*")
+ case FilterApproxMatch:
+ 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
+ matchingRule := ""
+ value := ""
+
+ for _, child := range packet.Children {
+ switch child.Tag {
+ case MatchingRuleAssertionMatchingRule:
+ matchingRule = ber.DecodeString(child.Data.Bytes())
+ case MatchingRuleAssertionType:
+ attr = ber.DecodeString(child.Data.Bytes())
+ case MatchingRuleAssertionMatchValue:
+ value = ber.DecodeString(child.Data.Bytes())
+ case MatchingRuleAssertionDNAttributes:
+ dnAttributes = child.Value.(bool)
+ }
+ }
+
+ if len(attr) > 0 {
+ buf.WriteString(attr)
+ }
+ if dnAttributes {
+ buf.WriteString(":dn")
+ }
+ if len(matchingRule) > 0 {
+ buf.WriteString(":")
+ buf.WriteString(matchingRule)
+ }
+ buf.WriteString(":=")
+ buf.WriteString(EscapeFilter(value))
+ }
+
+ buf.WriteByte(')')
+
+ return buf.String(), nil
+}
+
+func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) {
+ for pos < len(filter) && filter[pos] == '(' {
+ child, newPos, err := compileFilter(filter, pos+1)
+ if err != nil {
+ return pos, err
+ }
+ pos = newPos
+ parent.AppendChild(child)
+ }
+ if pos == len(filter) {
+ return pos, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
+ }
+
+ return pos + 1, nil
+}
+
+func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
+ var (
+ packet *ber.Packet
+ err error
+ )
+
+ defer func() {
+ if r := recover(); r != nil {
+ err = NewError(ErrorFilterCompile, errors.New("ldap: error compiling filter"))
+ }
+ }()
+ newPos := pos
+
+ currentRune, currentWidth := utf8.DecodeRuneInString(filter[newPos:])
+
+ switch currentRune {
+ case utf8.RuneError:
+ return nil, 0, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
+ case '(':
+ packet, newPos, err = compileFilter(filter, pos+currentWidth)
+ newPos++
+ return packet, newPos, err
+ case '&':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterAnd, nil, FilterMap[FilterAnd])
+ newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
+ return packet, newPos, err
+ case '|':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterOr, nil, FilterMap[FilterOr])
+ newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
+ return packet, newPos, err
+ case '!':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterNot, nil, FilterMap[FilterNot])
+ var child *ber.Packet
+ child, newPos, err = compileFilter(filter, pos+currentWidth)
+ packet.AppendChild(child)
+ return packet, newPos, err
+ default:
+ const (
+ stateReadingAttr = 0
+ stateReadingExtensibleMatchingRule = 1
+ stateReadingCondition = 2
+ )
+
+ state := stateReadingAttr
+ attribute := bytes.NewBuffer(nil)
+ extensibleDNAttributes := false
+ extensibleMatchingRule := bytes.NewBuffer(nil)
+ condition := bytes.NewBuffer(nil)
+
+ for newPos < len(filter) {
+ remainingFilter := filter[newPos:]
+ currentRune, currentWidth = utf8.DecodeRuneInString(remainingFilter)
+ if currentRune == ')' {
+ break
+ }
+ if currentRune == utf8.RuneError {
+ return packet, newPos, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
+ }
+
+ switch state {
+ case stateReadingAttr:
+ switch {
+ // Extensible rule, with only DN-matching
+ case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
+ extensibleDNAttributes = true
+ state = stateReadingCondition
+ newPos += 5
+
+ // Extensible rule, with DN-matching and a matching OID
+ case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:"):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
+ extensibleDNAttributes = true
+ state = stateReadingExtensibleMatchingRule
+ newPos += 4
+
+ // Extensible rule, with attr only
+ case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
+ state = stateReadingCondition
+ newPos += 2
+
+ // Extensible rule, with no DN attribute matching
+ case currentRune == ':':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
+ state = stateReadingExtensibleMatchingRule
+ newPos++
+
+ // Equality condition
+ case currentRune == '=':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
+ state = stateReadingCondition
+ newPos++
+
+ // Greater-than or equal
+ case currentRune == '>' && strings.HasPrefix(remainingFilter, ">="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
+ state = stateReadingCondition
+ newPos += 2
+
+ // Less-than or equal
+ case currentRune == '<' && strings.HasPrefix(remainingFilter, "<="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
+ state = stateReadingCondition
+ newPos += 2
+
+ // Approx
+ case currentRune == '~' && strings.HasPrefix(remainingFilter, "~="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterApproxMatch])
+ state = stateReadingCondition
+ newPos += 2
+
+ // Still reading the attribute name
+ default:
+ attribute.WriteRune(currentRune)
+ newPos += currentWidth
+ }
+
+ case stateReadingExtensibleMatchingRule:
+ switch {
+
+ // Matching rule OID is done
+ case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
+ state = stateReadingCondition
+ newPos += 2
+
+ // Still reading the matching rule oid
+ default:
+ extensibleMatchingRule.WriteRune(currentRune)
+ newPos += currentWidth
+ }
+
+ case stateReadingCondition:
+ // append to the condition
+ condition.WriteRune(currentRune)
+ newPos += currentWidth
+ }
+ }
+
+ if newPos == len(filter) {
+ err = NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
+ return packet, newPos, err
+ }
+ if packet == nil {
+ err = NewError(ErrorFilterCompile, errors.New("ldap: error parsing filter"))
+ return packet, newPos, err
+ }
+
+ switch {
+ case packet.Tag == FilterExtensibleMatch:
+ // MatchingRuleAssertion ::= SEQUENCE {
+ // matchingRule [1] MatchingRuleID OPTIONAL,
+ // type [2] AttributeDescription OPTIONAL,
+ // matchValue [3] AssertionValue,
+ // dnAttributes [4] BOOLEAN DEFAULT FALSE
+ // }
+
+ // Include the matching rule oid, if specified
+ if extensibleMatchingRule.Len() > 0 {
+ packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule.String(), MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule]))
+ }
+
+ // Include the attribute, if specified
+ 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 := decodeEscapedSymbols(condition.Bytes())
+ if encodeErr != nil {
+ return packet, newPos, encodeErr
+ }
+ packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchValue, encodedString, MatchingRuleAssertionMap[MatchingRuleAssertionMatchValue]))
+
+ // Defaults to false, so only include in the sequence if true
+ if extensibleDNAttributes {
+ packet.AppendChild(ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionDNAttributes, extensibleDNAttributes, MatchingRuleAssertionMap[MatchingRuleAssertionDNAttributes]))
+ }
+
+ 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 := bytes.Split(condition.Bytes(), _SymbolAny)
+ for i, part := range parts {
+ if len(part) == 0 {
+ continue
+ }
+ var tag ber.Tag
+ switch i {
+ case 0:
+ tag = FilterSubstringsInitial
+ case len(parts) - 1:
+ tag = FilterSubstringsFinal
+ default:
+ tag = FilterSubstringsAny
+ }
+ encodedString, encodeErr := decodeEscapedSymbols(part)
+ if encodeErr != nil {
+ return packet, newPos, encodeErr
+ }
+ seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, tag, encodedString, FilterSubstringsMap[uint64(tag)]))
+ }
+ packet.AppendChild(seq)
+ default:
+ encodedString, encodeErr := decodeEscapedSymbols(condition.Bytes())
+ if encodeErr != nil {
+ return packet, newPos, encodeErr
+ }
+ 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"))
+ }
+
+ newPos += currentWidth
+ return packet, newPos, err
+ }
+}
+
+// Convert from "ABC\xx\xx\xx" form to literal bytes for transport
+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))
+ }
+
+ 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 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))
+ }
+
+ if _, err := hexpac.Decode(byteVal, byteHex); err != nil {
+ return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: invalid characters for escape in filter: %v", err))
+ }
+
+ buffer.Write(byteVal)
+ } else {
+ buffer.WriteRune(runeVal)
+ }
+
+ offset += runeSize
+ }
+}
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 0000000000..931e5967da
--- /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 0000000000..0d8a4f681f
--- /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/github.com/go-ldap/ldap/v3/ldap.go b/vendor/github.com/go-ldap/ldap/v3/ldap.go
new file mode 100644
index 0000000000..7ae6dfe2cc
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/ldap.go
@@ -0,0 +1,339 @@
+package ldap
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// LDAP Application Codes
+const (
+ ApplicationBindRequest = 0
+ ApplicationBindResponse = 1
+ ApplicationUnbindRequest = 2
+ ApplicationSearchRequest = 3
+ ApplicationSearchResultEntry = 4
+ ApplicationSearchResultDone = 5
+ ApplicationModifyRequest = 6
+ ApplicationModifyResponse = 7
+ ApplicationAddRequest = 8
+ ApplicationAddResponse = 9
+ ApplicationDelRequest = 10
+ ApplicationDelResponse = 11
+ ApplicationModifyDNRequest = 12
+ ApplicationModifyDNResponse = 13
+ ApplicationCompareRequest = 14
+ ApplicationCompareResponse = 15
+ ApplicationAbandonRequest = 16
+ ApplicationSearchResultReference = 19
+ ApplicationExtendedRequest = 23
+ ApplicationExtendedResponse = 24
+)
+
+// ApplicationMap contains human readable descriptions of LDAP Application Codes
+var ApplicationMap = map[uint8]string{
+ ApplicationBindRequest: "Bind Request",
+ ApplicationBindResponse: "Bind Response",
+ ApplicationUnbindRequest: "Unbind Request",
+ ApplicationSearchRequest: "Search Request",
+ ApplicationSearchResultEntry: "Search Result Entry",
+ ApplicationSearchResultDone: "Search Result Done",
+ ApplicationModifyRequest: "Modify Request",
+ ApplicationModifyResponse: "Modify Response",
+ ApplicationAddRequest: "Add Request",
+ ApplicationAddResponse: "Add Response",
+ ApplicationDelRequest: "Del Request",
+ ApplicationDelResponse: "Del Response",
+ ApplicationModifyDNRequest: "Modify DN Request",
+ ApplicationModifyDNResponse: "Modify DN Response",
+ ApplicationCompareRequest: "Compare Request",
+ ApplicationCompareResponse: "Compare Response",
+ ApplicationAbandonRequest: "Abandon Request",
+ ApplicationSearchResultReference: "Search Result Reference",
+ ApplicationExtendedRequest: "Extended Request",
+ ApplicationExtendedResponse: "Extended Response",
+}
+
+// Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)
+const (
+ BeheraPasswordExpired = 0
+ BeheraAccountLocked = 1
+ BeheraChangeAfterReset = 2
+ BeheraPasswordModNotAllowed = 3
+ BeheraMustSupplyOldPassword = 4
+ BeheraInsufficientPasswordQuality = 5
+ BeheraPasswordTooShort = 6
+ BeheraPasswordTooYoung = 7
+ BeheraPasswordInHistory = 8
+)
+
+// BeheraPasswordPolicyErrorMap contains human readable descriptions of Behera Password Policy error codes
+var BeheraPasswordPolicyErrorMap = map[int8]string{
+ BeheraPasswordExpired: "Password expired",
+ BeheraAccountLocked: "Account locked",
+ BeheraChangeAfterReset: "Password must be changed",
+ BeheraPasswordModNotAllowed: "Policy prevents password modification",
+ BeheraMustSupplyOldPassword: "Policy requires old password in order to change password",
+ BeheraInsufficientPasswordQuality: "Password fails quality checks",
+ BeheraPasswordTooShort: "Password is too short for policy",
+ BeheraPasswordTooYoung: "Password has been changed too recently",
+ BeheraPasswordInHistory: "New password is in list of old passwords",
+}
+
+// Adds descriptions to an LDAP Response packet for debugging
+func addLDAPDescriptions(packet *ber.Packet) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = NewError(ErrorDebugging, fmt.Errorf("ldap: cannot process packet to add descriptions: %s", r))
+ }
+ }()
+ packet.Description = "LDAP Response"
+ packet.Children[0].Description = "Message ID"
+
+ application := uint8(packet.Children[1].Tag)
+ packet.Children[1].Description = ApplicationMap[application]
+
+ switch application {
+ case ApplicationBindRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationBindResponse:
+ err = addDefaultLDAPResponseDescriptions(packet)
+ case ApplicationUnbindRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationSearchRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationSearchResultEntry:
+ packet.Children[1].Children[0].Description = "Object Name"
+ packet.Children[1].Children[1].Description = "Attributes"
+ for _, child := range packet.Children[1].Children[1].Children {
+ child.Description = "Attribute"
+ child.Children[0].Description = "Attribute Name"
+ child.Children[1].Description = "Attribute Values"
+ for _, grandchild := range child.Children[1].Children {
+ grandchild.Description = "Attribute Value"
+ }
+ }
+ if len(packet.Children) == 3 {
+ err = addControlDescriptions(packet.Children[2])
+ }
+ case ApplicationSearchResultDone:
+ err = addDefaultLDAPResponseDescriptions(packet)
+ case ApplicationModifyRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationModifyResponse:
+ case ApplicationAddRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationAddResponse:
+ case ApplicationDelRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationDelResponse:
+ case ApplicationModifyDNRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationModifyDNResponse:
+ case ApplicationCompareRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationCompareResponse:
+ case ApplicationAbandonRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationSearchResultReference:
+ case ApplicationExtendedRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationExtendedResponse:
+ }
+
+ return err
+}
+
+func addControlDescriptions(packet *ber.Packet) error {
+ packet.Description = "Controls"
+ for _, child := range packet.Children {
+ var value *ber.Packet
+ controlType := ""
+ child.Description = "Control"
+ switch len(child.Children) {
+ case 0:
+ // at least one child is required for control type
+ return fmt.Errorf("at least one child is required for control type")
+
+ case 1:
+ // just type, no criticality or value
+ controlType = child.Children[0].Value.(string)
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
+
+ case 2:
+ controlType = child.Children[0].Value.(string)
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
+ // Children[1] could be criticality or value (both are optional)
+ // duck-type on whether this is a boolean
+ if _, ok := child.Children[1].Value.(bool); ok {
+ child.Children[1].Description = "Criticality"
+ } else {
+ child.Children[1].Description = "Control Value"
+ value = child.Children[1]
+ }
+
+ case 3:
+ // criticality and value present
+ controlType = child.Children[0].Value.(string)
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
+ child.Children[1].Description = "Criticality"
+ child.Children[2].Description = "Control Value"
+ value = child.Children[2]
+
+ default:
+ // more than 3 children is invalid
+ return fmt.Errorf("more than 3 children for control packet found")
+ }
+
+ if value == nil {
+ continue
+ }
+ switch controlType {
+ case ControlTypePaging:
+ value.Description += " (Paging)"
+ if value.Value != nil {
+ valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+ if err != nil {
+ return fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ value.Data.Truncate(0)
+ value.Value = nil
+ valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes()
+ value.AppendChild(valueChildren)
+ }
+ value.Children[0].Description = "Real Search Control Value"
+ value.Children[0].Children[0].Description = "Paging Size"
+ value.Children[0].Children[1].Description = "Cookie"
+
+ case ControlTypeBeheraPasswordPolicy:
+ value.Description += " (Password Policy - Behera Draft)"
+ if value.Value != nil {
+ valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+ if err != nil {
+ return fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ value.Data.Truncate(0)
+ value.Value = nil
+ value.AppendChild(valueChildren)
+ }
+ sequence := value.Children[0]
+ for _, child := range sequence.Children {
+ if child.Tag == 0 {
+ //Warning
+ warningPacket := child.Children[0]
+ val, err := ber.ParseInt64(warningPacket.Data.Bytes())
+ if err != nil {
+ return fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ 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
+ 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
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func addRequestDescriptions(packet *ber.Packet) error {
+ packet.Description = "LDAP Request"
+ packet.Children[0].Description = "Message ID"
+ packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)]
+ if len(packet.Children) == 3 {
+ return addControlDescriptions(packet.Children[2])
+ }
+ return nil
+}
+
+func addDefaultLDAPResponseDescriptions(packet *ber.Packet) error {
+ 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"
+ }
+ if len(packet.Children) == 3 {
+ return addControlDescriptions(packet.Children[2])
+ }
+ return nil
+}
+
+// DebugBinaryFile reads and prints packets from the given filename
+func DebugBinaryFile(fileName string) error {
+ file, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ return NewError(ErrorDebugging, err)
+ }
+ ber.PrintBytes(os.Stdout, file, "")
+ packet, err := ber.DecodePacketErr(file)
+ if err != nil {
+ return fmt.Errorf("failed to decode packet: %s", err)
+ }
+ if err := addLDAPDescriptions(packet); err != nil {
+ return err
+ }
+ ber.PrintPacket(packet)
+
+ return nil
+}
+
+var hex = "0123456789abcdef"
+
+func mustEscape(c byte) bool {
+ return c > 0x7f || c == '(' || c == ')' || c == '\\' || c == '*' || c == 0
+}
+
+// EscapeFilter escapes from the provided LDAP filter string the special
+// characters in the set `()*\` and those out of the range 0 < c < 0x80,
+// as defined in RFC4515.
+func EscapeFilter(filter string) string {
+ escape := 0
+ for i := 0; i < len(filter); i++ {
+ if mustEscape(filter[i]) {
+ escape++
+ }
+ }
+ if escape == 0 {
+ return filter
+ }
+ buf := make([]byte, len(filter)+escape*2)
+ for i, j := 0, 0; i < len(filter); i++ {
+ c := filter[i]
+ if mustEscape(c) {
+ buf[j+0] = '\\'
+ buf[j+1] = hex[c>>4]
+ buf[j+2] = hex[c&0xf]
+ j += 3
+ } else {
+ buf[j] = c
+ j++
+ }
+ }
+ return string(buf)
+}
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 0000000000..b4865f8af6
--- /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 0000000000..ee712890ad
--- /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/github.com/go-ldap/ldap/v3/passwdmodify.go b/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
new file mode 100644
index 0000000000..62a110843d
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
@@ -0,0 +1,126 @@
+package ldap
+
+import (
+ "fmt"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+const (
+ passwordModifyOID = "1.3.6.1.4.1.4203.1.11.1"
+)
+
+// PasswordModifyRequest implements the Password Modify Extended Operation as defined in https://www.ietf.org/rfc/rfc3062.txt
+type PasswordModifyRequest struct {
+ // UserIdentity is an optional string representation of the user associated with the request.
+ // This string may or may not be an LDAPDN [RFC2253].
+ // If no UserIdentity field is present, the request acts up upon the password of the user currently associated with the LDAP session
+ UserIdentity string
+ // OldPassword, if present, contains the user's current password
+ OldPassword string
+ // NewPassword, if present, contains the desired password for this user
+ NewPassword string
+}
+
+// PasswordModifyResult holds the server response to a PasswordModifyRequest
+type PasswordModifyResult struct {
+ // GeneratedPassword holds a password generated by the server, if present
+ GeneratedPassword string
+ // Referral are the returned referral
+ Referral string
+}
+
+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 req.UserIdentity != "" {
+ passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.UserIdentity, "User Identity"))
+ }
+ if req.OldPassword != "" {
+ passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, req.OldPassword, "Old Password"))
+ }
+ if req.NewPassword != "" {
+ passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, req.NewPassword, "New Password"))
+ }
+ extendedRequestValue.AppendChild(passwordModifyRequestValue)
+
+ pkt.AppendChild(extendedRequestValue)
+
+ envelope.AppendChild(pkt)
+
+ return nil
+}
+
+// NewPasswordModifyRequest creates a new PasswordModifyRequest
+//
+// 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
+// with the session.
+//
+// oldPassword is the current user's password, it can be empty or it can be
+// needed depending on the session user access rights (usually an administrator
+// can change a user's password without knowing the current one) and the
+// password policy (see pwdSafeModify password policy's attribute)
+//
+// newPassword is the desired user's password. If empty the server can return
+// an error or generate a new password that will be available in the
+// PasswordModifyResult.GeneratedPassword
+//
+func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
+ return &PasswordModifyRequest{
+ UserIdentity: userIdentity,
+ OldPassword: oldPassword,
+ NewPassword: newPassword,
+ }
+}
+
+// PasswordModify performs the modification request
+func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) {
+ msgCtx, err := l.doRequest(passwordModifyRequest)
+ if err != nil {
+ return nil, err
+ }
+ defer l.finishMessage(msgCtx)
+
+ packet, err := l.readPacket(msgCtx)
+ if err != nil {
+ return nil, err
+ }
+
+ result := &PasswordModifyResult{}
+
+ if packet.Children[1].Tag == ApplicationExtendedResponse {
+ err := GetLDAPError(packet)
+ if err != nil {
+ if IsErrorWithCode(err, LDAPResultReferral) {
+ for _, child := range packet.Children[1].Children {
+ if child.Tag == 3 {
+ result.Referral = child.Children[0].Value.(string)
+ }
+ }
+ }
+ return result, err
+ }
+ } else {
+ return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("unexpected Response: %d", packet.Children[1].Tag))
+ }
+
+ extendedResponse := packet.Children[1]
+ for _, child := range extendedResponse.Children {
+ if child.Tag == 11 {
+ passwordModifyResponseValue := ber.DecodePacket(child.Data.Bytes())
+ if len(passwordModifyResponseValue.Children) == 1 {
+ if passwordModifyResponseValue.Children[0].Tag == 0 {
+ result.GeneratedPassword = ber.DecodeString(passwordModifyResponseValue.Children[0].Data.Bytes())
+ }
+ }
+ }
+ }
+
+ return result, nil
+}
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 0000000000..8c68f34aaf
--- /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/github.com/go-ldap/ldap/v3/search.go b/vendor/github.com/go-ldap/ldap/v3/search.go
new file mode 100644
index 0000000000..4fcc794a59
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/search.go
@@ -0,0 +1,410 @@
+package ldap
+
+import (
+ "errors"
+ "fmt"
+ "sort"
+ "strings"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// scope choices
+const (
+ ScopeBaseObject = 0
+ ScopeSingleLevel = 1
+ ScopeWholeSubtree = 2
+)
+
+// ScopeMap contains human readable descriptions of scope choices
+var ScopeMap = map[int]string{
+ ScopeBaseObject: "Base Object",
+ ScopeSingleLevel: "Single Level",
+ ScopeWholeSubtree: "Whole Subtree",
+}
+
+// derefAliases
+const (
+ NeverDerefAliases = 0
+ DerefInSearching = 1
+ DerefFindingBaseObj = 2
+ DerefAlways = 3
+)
+
+// DerefMap contains human readable descriptions of derefAliases choices
+var DerefMap = map[int]string{
+ NeverDerefAliases: "NeverDerefAliases",
+ DerefInSearching: "DerefInSearching",
+ DerefFindingBaseObj: "DerefFindingBaseObj",
+ DerefAlways: "DerefAlways",
+}
+
+// NewEntry returns an Entry object with the specified distinguished name and attribute key-value pairs.
+// The map of attributes is accessed in alphabetical order of the keys in order to ensure that, for the
+// same input map of attributes, the output entry will contain the same order of attributes
+func NewEntry(dn string, attributes map[string][]string) *Entry {
+ var attributeNames []string
+ for attributeName := range attributes {
+ attributeNames = append(attributeNames, attributeName)
+ }
+ sort.Strings(attributeNames)
+
+ var encodedAttributes []*EntryAttribute
+ for _, attributeName := range attributeNames {
+ encodedAttributes = append(encodedAttributes, NewEntryAttribute(attributeName, attributes[attributeName]))
+ }
+ return &Entry{
+ DN: dn,
+ Attributes: encodedAttributes,
+ }
+}
+
+// Entry represents a single search result entry
+type Entry struct {
+ // DN is the distinguished name of the entry
+ DN string
+ // Attributes are the returned attributes for the entry
+ Attributes []*EntryAttribute
+}
+
+// GetAttributeValues returns the values for the named attribute, or an empty list
+func (e *Entry) GetAttributeValues(attribute string) []string {
+ for _, attr := range e.Attributes {
+ if attr.Name == attribute {
+ return attr.Values
+ }
+ }
+ 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 {
+ if attr.Name == attribute {
+ return attr.ByteValues
+ }
+ }
+ 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)
+ if len(values) == 0 {
+ return ""
+ }
+ 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)
+ if len(values) == 0 {
+ return []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)
+ for _, attr := range e.Attributes {
+ attr.Print()
+ }
+}
+
+// PrettyPrint outputs a human-readable description indenting
+func (e *Entry) PrettyPrint(indent int) {
+ fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN)
+ for _, attr := range e.Attributes {
+ attr.PrettyPrint(indent + 2)
+ }
+}
+
+// NewEntryAttribute returns a new EntryAttribute with the desired key-value pair
+func NewEntryAttribute(name string, values []string) *EntryAttribute {
+ var bytes [][]byte
+ for _, value := range values {
+ bytes = append(bytes, []byte(value))
+ }
+ return &EntryAttribute{
+ Name: name,
+ Values: values,
+ ByteValues: bytes,
+ }
+}
+
+// EntryAttribute holds a single attribute
+type EntryAttribute struct {
+ // Name is the name of the attribute
+ Name string
+ // Values contain the string values of the attribute
+ Values []string
+ // ByteValues contain the raw values of the attribute
+ ByteValues [][]byte
+}
+
+// Print outputs a human-readable description
+func (e *EntryAttribute) Print() {
+ fmt.Printf("%s: %s\n", e.Name, e.Values)
+}
+
+// PrettyPrint outputs a human-readable description with indenting
+func (e *EntryAttribute) PrettyPrint(indent int) {
+ fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values)
+}
+
+// SearchResult holds the server's response to a search request
+type SearchResult struct {
+ // Entries are the returned entries
+ Entries []*Entry
+ // Referrals are the returned referrals
+ Referrals []string
+ // Controls are the returned controls
+ Controls []Control
+}
+
+// Print outputs a human-readable description
+func (s *SearchResult) Print() {
+ for _, entry := range s.Entries {
+ entry.Print()
+ }
+}
+
+// PrettyPrint outputs a human-readable description with indenting
+func (s *SearchResult) PrettyPrint(indent int) {
+ for _, entry := range s.Entries {
+ entry.PrettyPrint(indent)
+ }
+}
+
+// SearchRequest represents a search request to send to the server
+type SearchRequest struct {
+ BaseDN string
+ Scope int
+ DerefAliases int
+ SizeLimit int
+ TimeLimit int
+ TypesOnly bool
+ Filter string
+ Attributes []string
+ Controls []Control
+}
+
+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(req.Filter)
+ if err != nil {
+ return err
+ }
+ pkt.AppendChild(filterPacket)
+ // encode attributes
+ attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
+ for _, attribute := range req.Attributes {
+ attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
+ }
+ pkt.AppendChild(attributesPacket)
+
+ envelope.AppendChild(pkt)
+ if len(req.Controls) > 0 {
+ envelope.AppendChild(encodeControls(req.Controls))
+ }
+
+ return nil
+}
+
+// NewSearchRequest creates a new search request
+func NewSearchRequest(
+ BaseDN string,
+ Scope, DerefAliases, SizeLimit, TimeLimit int,
+ TypesOnly bool,
+ Filter string,
+ Attributes []string,
+ Controls []Control,
+) *SearchRequest {
+ return &SearchRequest{
+ BaseDN: BaseDN,
+ Scope: Scope,
+ DerefAliases: DerefAliases,
+ SizeLimit: SizeLimit,
+ TimeLimit: TimeLimit,
+ TypesOnly: TypesOnly,
+ Filter: Filter,
+ Attributes: Attributes,
+ Controls: Controls,
+ }
+}
+
+// SearchWithPaging accepts a search request and desired page size in order to execute LDAP queries to fulfill the
+// search request. All paged LDAP query responses will be buffered and the final result will be returned atomically.
+// The following four cases are possible given the arguments:
+// - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size
+// - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries
+// - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request
+// - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries
+// A requested pagingSize of 0 is interpreted as no limit by LDAP servers.
+func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) {
+ var pagingControl *ControlPaging
+
+ control := FindControl(searchRequest.Controls, ControlTypePaging)
+ if control == nil {
+ pagingControl = NewControlPaging(pagingSize)
+ searchRequest.Controls = append(searchRequest.Controls, pagingControl)
+ } else {
+ castControl, ok := control.(*ControlPaging)
+ if !ok {
+ return nil, fmt.Errorf("expected paging control to be of type *ControlPaging, got %v", control)
+ }
+ if castControl.PagingSize != pagingSize {
+ return nil, fmt.Errorf("paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize)
+ }
+ pagingControl = castControl
+ }
+
+ searchResult := new(SearchResult)
+ for {
+ result, err := l.Search(searchRequest)
+ l.Debug.Printf("Looking for Paging Control...")
+ if err != nil {
+ return searchResult, err
+ }
+ if result == nil {
+ return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
+ }
+
+ for _, entry := range result.Entries {
+ searchResult.Entries = append(searchResult.Entries, entry)
+ }
+ for _, referral := range result.Referrals {
+ searchResult.Referrals = append(searchResult.Referrals, referral)
+ }
+ for _, control := range result.Controls {
+ searchResult.Controls = append(searchResult.Controls, control)
+ }
+
+ l.Debug.Printf("Looking for Paging Control...")
+ pagingResult := FindControl(result.Controls, ControlTypePaging)
+ if pagingResult == nil {
+ pagingControl = nil
+ l.Debug.Printf("Could not find paging control. Breaking...")
+ break
+ }
+
+ cookie := pagingResult.(*ControlPaging).Cookie
+ if len(cookie) == 0 {
+ pagingControl = nil
+ l.Debug.Printf("Could not find cookie. Breaking...")
+ break
+ }
+ pagingControl.SetCookie(cookie)
+ }
+
+ if pagingControl != nil {
+ l.Debug.Printf("Abandoning Paging...")
+ pagingControl.PagingSize = 0
+ l.Search(searchRequest)
+ }
+
+ return searchResult, nil
+}
+
+// Search performs the given search request
+func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
+ msgCtx, err := l.doRequest(searchRequest)
+ if err != nil {
+ return nil, err
+ }
+ defer l.finishMessage(msgCtx)
+
+ result := &SearchResult{
+ Entries: make([]*Entry, 0),
+ Referrals: make([]string, 0),
+ Controls: make([]Control, 0)}
+
+ for {
+ packet, err := l.readPacket(msgCtx)
+ if err != nil {
+ return result, err
+ }
+
+ switch packet.Children[1].Tag {
+ case 4:
+ entry := new(Entry)
+ entry.DN = packet.Children[1].Children[0].Value.(string)
+ for _, child := range packet.Children[1].Children[1].Children {
+ attr := new(EntryAttribute)
+ attr.Name = child.Children[0].Value.(string)
+ for _, value := range child.Children[1].Children {
+ attr.Values = append(attr.Values, value.Value.(string))
+ attr.ByteValues = append(attr.ByteValues, value.ByteValue)
+ }
+ entry.Attributes = append(entry.Attributes, attr)
+ }
+ result.Entries = append(result.Entries, entry)
+ case 5:
+ err := GetLDAPError(packet)
+ if err != nil {
+ return result, err
+ }
+ if len(packet.Children) == 3 {
+ for _, child := range packet.Children[2].Children {
+ decodedChild, err := DecodeControl(child)
+ if err != nil {
+ return result, fmt.Errorf("failed to decode child control: %s", err)
+ }
+ result.Controls = append(result.Controls, decodedChild)
+ }
+ }
+ return result, nil
+ case 19:
+ result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string))
+ }
+ }
+}