mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	[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>
This commit is contained in:
		
							
								
								
									
										22
									
								
								vendor/github.com/go-ldap/ldap/v3/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/go-ldap/ldap/v3/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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. | ||||
							
								
								
									
										91
									
								
								vendor/github.com/go-ldap/ldap/v3/add.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								vendor/github.com/go-ldap/ldap/v3/add.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										540
									
								
								vendor/github.com/go-ldap/ldap/v3/bind.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										540
									
								
								vendor/github.com/go-ldap/ldap/v3/bind.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										30
									
								
								vendor/github.com/go-ldap/ldap/v3/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/go-ldap/ldap/v3/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										61
									
								
								vendor/github.com/go-ldap/ldap/v3/compare.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/go-ldap/ldap/v3/compare.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										570
									
								
								vendor/github.com/go-ldap/ldap/v3/conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										570
									
								
								vendor/github.com/go-ldap/ldap/v3/conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										492
									
								
								vendor/github.com/go-ldap/ldap/v3/control.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										492
									
								
								vendor/github.com/go-ldap/ldap/v3/control.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										30
									
								
								vendor/github.com/go-ldap/ldap/v3/debug.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/go-ldap/ldap/v3/debug.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										59
									
								
								vendor/github.com/go-ldap/ldap/v3/del.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/go-ldap/ldap/v3/del.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										207
									
								
								vendor/github.com/go-ldap/ldap/v3/dn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								vendor/github.com/go-ldap/ldap/v3/dn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										4
									
								
								vendor/github.com/go-ldap/ldap/v3/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/go-ldap/ldap/v3/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| /* | ||||
| Package ldap provides basic LDAP v3 functionality. | ||||
| */ | ||||
| package ldap | ||||
							
								
								
									
										253
									
								
								vendor/github.com/go-ldap/ldap/v3/error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								vendor/github.com/go-ldap/ldap/v3/error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										487
									
								
								vendor/github.com/go-ldap/ldap/v3/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										487
									
								
								vendor/github.com/go-ldap/ldap/v3/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/go-ldap/ldap/v3/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/go-ldap/ldap/v3/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| ) | ||||
							
								
								
									
										11
									
								
								vendor/github.com/go-ldap/ldap/v3/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/go-ldap/ldap/v3/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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= | ||||
							
								
								
									
										339
									
								
								vendor/github.com/go-ldap/ldap/v3/ldap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								vendor/github.com/go-ldap/ldap/v3/ldap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										80
									
								
								vendor/github.com/go-ldap/ldap/v3/moddn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/go-ldap/ldap/v3/moddn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										132
									
								
								vendor/github.com/go-ldap/ldap/v3/modify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								vendor/github.com/go-ldap/ldap/v3/modify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										126
									
								
								vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										66
									
								
								vendor/github.com/go-ldap/ldap/v3/request.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/go-ldap/ldap/v3/request.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										410
									
								
								vendor/github.com/go-ldap/ldap/v3/search.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										410
									
								
								vendor/github.com/go-ldap/ldap/v3/search.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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)) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 6543
					6543