diff --git a/glide.lock b/glide.lock index 74ce7428..1abf23ee 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: fe29de07f5c1580c51de0e78796bce522d933602d88a4c397b586bd88ca7ca76 -updated: 2018-10-24T14:58:32.448481302-07:00 +hash: e5972bbdf15ad612d99ce8cd34e19537b9eacb5ff53688f339e0da285eb8ec22 +updated: 2018-11-12T19:38:56.235070564+01:00 imports: - name: github.com/beevik/etree version: 4cd0dd976db869f817248477718071a28e978df0 @@ -180,7 +180,7 @@ imports: - name: gopkg.in/asn1-ber.v1 version: 4e86f4367175e39f69d9358a5f17b4dda270378d - name: gopkg.in/ldap.v2 - version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73 + version: bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9 - name: gopkg.in/square/go-jose.v2 version: 8254d6c783765f38c8675fae4427a1fe73fbd09d subpackages: diff --git a/vendor/gopkg.in/ldap.v2/LICENSE b/vendor/gopkg.in/ldap.v2/LICENSE index 74487567..6c0ed4b3 100644 --- a/vendor/gopkg.in/ldap.v2/LICENSE +++ b/vendor/gopkg.in/ldap.v2/LICENSE @@ -1,27 +1,22 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. +The MIT License (MIT) -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com) +Portions copyright (c) 2015-2016 go-ldap Authors - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. +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: -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/gopkg.in/ldap.v2/add.go b/vendor/gopkg.in/ldap.v2/add.go index 61b795e0..0e5f6cdb 100644 --- a/vendor/gopkg.in/ldap.v2/add.go +++ b/vendor/gopkg.in/ldap.v2/add.go @@ -16,73 +16,78 @@ import ( "gopkg.in/asn1-ber.v1" ) +// Attribute represents an LDAP attribute type Attribute struct { - attrType string - attrVals []string + // 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.attrType, "Type")) + 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.attrVals { + 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 string - attributes []Attribute + // DN identifies the entry being added + DN string + // Attributes list the attributes of the new entry + Attributes []Attribute } func (a AddRequest) encode() *ber.Packet { request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request") - request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.dn, "DN")) + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN")) attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes") - for _, attribute := range a.attributes { + for _, attribute := range a.Attributes { attributes.AppendChild(attribute.encode()) } request.AppendChild(attributes) return request } +// Attribute adds an attribute with the given type and values func (a *AddRequest) Attribute(attrType string, attrVals []string) { - a.attributes = append(a.attributes, Attribute{attrType: attrType, attrVals: attrVals}) + a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals}) } +// NewAddRequest returns an AddRequest for the given DN, with no attributes func NewAddRequest(dn string) *AddRequest { return &AddRequest{ - dn: dn, + DN: dn, } } +// Add performs the given AddRequest func (l *Conn) Add(addRequest *AddRequest) error { - messageID := l.nextMessageID() packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) packet.AppendChild(addRequest.encode()) l.Debug.PrintPacket(packet) - channel, err := l.sendMessage(packet) + msgCtx, err := l.sendMessage(packet) if err != nil { return err } - if channel == nil { - return NewError(ErrorNetwork, errors.New("ldap: could not send message")) - } - defer l.finishMessage(messageID) + defer l.finishMessage(msgCtx) - l.Debug.Printf("%d: waiting for response", messageID) - packetResponse, ok := <-channel + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses if !ok { - return NewError(ErrorNetwork, errors.New("ldap: channel closed")) + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) } packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", messageID, packet) + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { return err } @@ -103,6 +108,6 @@ func (l *Conn) Add(addRequest *AddRequest) error { log.Printf("Unexpected Response: %d", packet.Children[1].Tag) } - l.Debug.Printf("%d: returning", messageID) + l.Debug.Printf("%d: returning", msgCtx.id) return nil } diff --git a/vendor/gopkg.in/ldap.v2/atomic_value.go b/vendor/gopkg.in/ldap.v2/atomic_value.go new file mode 100644 index 00000000..bccf7573 --- /dev/null +++ b/vendor/gopkg.in/ldap.v2/atomic_value.go @@ -0,0 +1,13 @@ +// +build go1.4 + +package ldap + +import ( + "sync/atomic" +) + +// For compilers that support it, we just use the underlying sync/atomic.Value +// type. +type atomicValue struct { + atomic.Value +} diff --git a/vendor/gopkg.in/ldap.v2/atomic_value_go13.go b/vendor/gopkg.in/ldap.v2/atomic_value_go13.go new file mode 100644 index 00000000..04920bb2 --- /dev/null +++ b/vendor/gopkg.in/ldap.v2/atomic_value_go13.go @@ -0,0 +1,28 @@ +// +build !go1.4 + +package ldap + +import ( + "sync" +) + +// This is a helper type that emulates the use of the "sync/atomic.Value" +// struct that's available in Go 1.4 and up. +type atomicValue struct { + value interface{} + lock sync.RWMutex +} + +func (av *atomicValue) Store(val interface{}) { + av.lock.Lock() + av.value = val + av.lock.Unlock() +} + +func (av *atomicValue) Load() interface{} { + av.lock.RLock() + ret := av.value + av.lock.RUnlock() + + return ret +} diff --git a/vendor/gopkg.in/ldap.v2/bind.go b/vendor/gopkg.in/ldap.v2/bind.go index ae68eb48..26b3cc72 100644 --- a/vendor/gopkg.in/ldap.v2/bind.go +++ b/vendor/gopkg.in/ldap.v2/bind.go @@ -10,16 +10,22 @@ import ( "gopkg.in/asn1-ber.v1" ) +// SimpleBindRequest represents a username/password bind operation type SimpleBindRequest struct { + // Username is the name of the Directory object that the client wishes to bind as Username string + // Password is the credentials to bind with Password string + // Controls are optional controls to send with the bind request Controls []Control } +// 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, @@ -39,11 +45,10 @@ func (bindRequest *SimpleBindRequest) encode() *ber.Packet { return request } +// SimpleBind performs the simple bind operation defined in the given request func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) { - messageID := l.nextMessageID() - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) encodedBindRequest := simpleBindRequest.encode() packet.AppendChild(encodedBindRequest) @@ -51,21 +56,18 @@ func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResu ber.PrintPacket(packet) } - channel, err := l.sendMessage(packet) + msgCtx, err := l.sendMessage(packet) if err != nil { return nil, err } - if channel == nil { - return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message")) - } - defer l.finishMessage(messageID) + defer l.finishMessage(msgCtx) - packetResponse, ok := <-channel + packetResponse, ok := <-msgCtx.responses if !ok { - return nil, NewError(ErrorNetwork, errors.New("ldap: channel closed")) + return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) } packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", messageID, packet) + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { return nil, err } @@ -95,11 +97,10 @@ func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResu return result, nil } +// Bind performs a bind with the given username and password func (l *Conn) Bind(username, password string) error { - messageID := l.nextMessageID() - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) bindRequest.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, username, "User Name")) @@ -110,21 +111,18 @@ func (l *Conn) Bind(username, password string) error { ber.PrintPacket(packet) } - channel, err := l.sendMessage(packet) + msgCtx, err := l.sendMessage(packet) if err != nil { return err } - if channel == nil { - return NewError(ErrorNetwork, errors.New("ldap: could not send message")) - } - defer l.finishMessage(messageID) + defer l.finishMessage(msgCtx) - packetResponse, ok := <-channel + packetResponse, ok := <-msgCtx.responses if !ok { - return NewError(ErrorNetwork, errors.New("ldap: channel closed")) + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) } packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", messageID, packet) + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { return err } diff --git a/vendor/gopkg.in/ldap.v2/compare.go b/vendor/gopkg.in/ldap.v2/compare.go index dfe728ba..cc6d2af5 100644 --- a/vendor/gopkg.in/ldap.v2/compare.go +++ b/vendor/gopkg.in/ldap.v2/compare.go @@ -33,9 +33,8 @@ import ( // 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) { - messageID := l.nextMessageID() packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request") request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN")) @@ -48,22 +47,19 @@ func (l *Conn) Compare(dn, attribute, value string) (bool, error) { l.Debug.PrintPacket(packet) - channel, err := l.sendMessage(packet) + msgCtx, err := l.sendMessage(packet) if err != nil { return false, err } - if channel == nil { - return false, NewError(ErrorNetwork, errors.New("ldap: could not send message")) - } - defer l.finishMessage(messageID) + defer l.finishMessage(msgCtx) - l.Debug.Printf("%d: waiting for response", messageID) - packetResponse, ok := <-channel + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses if !ok { - return false, NewError(ErrorNetwork, errors.New("ldap: channel closed")) + return false, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) } packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", messageID, packet) + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { return false, err } diff --git a/vendor/gopkg.in/ldap.v2/conn.go b/vendor/gopkg.in/ldap.v2/conn.go index 6aad628b..eb28eb47 100644 --- a/vendor/gopkg.in/ldap.v2/conn.go +++ b/vendor/gopkg.in/ldap.v2/conn.go @@ -11,24 +11,34 @@ import ( "log" "net" "sync" + "sync/atomic" "time" "gopkg.in/asn1-ber.v1" ) const ( - MessageQuit = 0 - MessageRequest = 1 + // 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 = 3 - MessageTimeout = 4 + // 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 ) +// 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 error + // 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")) @@ -36,11 +46,31 @@ func (pr *PacketResponse) ReadPacket() (*ber.Packet, error) { 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 - Channel chan *PacketResponse + Context *messageContext } type sendMessageFlags uint @@ -53,19 +83,18 @@ const ( type Conn struct { conn net.Conn isTLS bool - isClosing bool + closing uint32 + closeErr atomicValue isStartingTLS bool Debug debugging - chanConfirm chan bool - chanResults map[int64]chan *PacketResponse + chanConfirm chan struct{} + messageContexts map[int64]*messageContext chanMessage chan *messagePacket chanMessageID chan int64 - wgSender sync.WaitGroup wgClose sync.WaitGroup - once sync.Once outstandingRequests uint messageMutex sync.Mutex - requestTimeout time.Duration + requestTimeout int64 } var _ Client = &Conn{} @@ -111,28 +140,39 @@ func DialTLS(network, addr string, config *tls.Config) (*Conn, error) { // 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 bool), - chanMessageID: make(chan int64), - chanMessage: make(chan *messagePacket, 10), - chanResults: map[int64]chan *PacketResponse{}, - requestTimeout: 0, - isTLS: isTLS, + 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() { go l.reader() go l.processMessages() l.wgClose.Add(1) } +// 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.once.Do(func() { - l.isClosing = true - l.wgSender.Wait() + 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 @@ -140,62 +180,56 @@ func (l *Conn) Close() { l.Debug.Printf("Closing network connection") if err := l.conn.Close(); err != nil { - log.Print(err) + log.Println(err) } l.wgClose.Done() - }) + } l.wgClose.Wait() } -// Sets the time after a request is sent that a MessageTimeout triggers +// SetTimeout sets the time after a request is sent that a MessageTimeout triggers func (l *Conn) SetTimeout(timeout time.Duration) { if timeout > 0 { - l.requestTimeout = timeout + atomic.StoreInt64(&l.requestTimeout, int64(timeout)) } } // Returns the next available messageID func (l *Conn) nextMessageID() int64 { - if l.chanMessageID != nil { - if messageID, ok := <-l.chanMessageID; ok { - return messageID - } + 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 { - messageID := l.nextMessageID() - 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, messageID, "MessageID")) + 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) - channel, err := l.sendMessageWithFlags(packet, startTLS) + msgCtx, err := l.sendMessageWithFlags(packet, startTLS) if err != nil { return err } - if channel == nil { - return NewError(ErrorNetwork, errors.New("ldap: could not send message")) - } + defer l.finishMessage(msgCtx) - l.Debug.Printf("%d: waiting for response", messageID) - defer l.finishMessage(messageID) - packetResponse, ok := <-channel + l.Debug.Printf("%d: waiting for response", msgCtx.id) + + packetResponse, ok := <-msgCtx.responses if !ok { - return NewError(ErrorNetwork, errors.New("ldap: channel closed")) + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) } packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", messageID, packet) + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { return err } @@ -226,45 +260,51 @@ func (l *Conn) StartTLS(config *tls.Config) error { return nil } -func (l *Conn) sendMessage(packet *ber.Packet) (chan *PacketResponse, error) { +func (l *Conn) sendMessage(packet *ber.Packet) (*messageContext, error) { return l.sendMessageWithFlags(packet, 0) } -func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (chan *PacketResponse, error) { - if l.isClosing { +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.")) + 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")) - } else { - l.isStartingTLS = true } + l.isStartingTLS = true } l.outstandingRequests++ l.messageMutex.Unlock() - out := make(chan *PacketResponse) + responses := make(chan *PacketResponse) + messageID := packet.Children[0].Value.(int64) message := &messagePacket{ Op: MessageRequest, - MessageID: packet.Children[0].Value.(int64), + MessageID: messageID, Packet: packet, - Channel: out, + Context: &messageContext{ + id: messageID, + done: make(chan struct{}), + responses: responses, + }, } l.sendProcessMessage(message) - return out, nil + return message.Context, nil } -func (l *Conn) finishMessage(messageID int64) { - if l.isClosing { +func (l *Conn) finishMessage(msgCtx *messageContext) { + close(msgCtx.done) + + if l.isClosing() { return } @@ -277,18 +317,18 @@ func (l *Conn) finishMessage(messageID int64) { message := &messagePacket{ Op: MessageFinish, - MessageID: messageID, + MessageID: msgCtx.id, } l.sendProcessMessage(message) } func (l *Conn) sendProcessMessage(message *messagePacket) bool { - if l.isClosing { + l.messageMutex.Lock() + defer l.messageMutex.Unlock() + if l.isClosing() { return false } - l.wgSender.Add(1) l.chanMessage <- message - l.wgSender.Done() return true } @@ -297,13 +337,17 @@ func (l *Conn) processMessages() { if err := recover(); err != nil { log.Printf("ldap: recovered panic in processMessages: %v", err) } - for messageID, channel := range l.chanResults { + 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(channel) - delete(l.chanResults, messageID) + close(msgCtx.responses) + delete(l.messageContexts, messageID) } close(l.chanMessageID) - l.chanConfirm <- true close(l.chanConfirm) }() @@ -312,11 +356,7 @@ func (l *Conn) processMessages() { select { case l.chanMessageID <- messageID: messageID++ - case message, ok := <-l.chanMessage: - if !ok { - l.Debug.Printf("Shutting down - message channel is closed") - return - } + case message := <-l.chanMessage: switch message.Op { case MessageQuit: l.Debug.Printf("Shutting down - quit message received") @@ -324,24 +364,30 @@ func (l *Conn) processMessages() { case MessageRequest: // Add to message list and write to network l.Debug.Printf("Sending message %d", message.MessageID) - l.chanResults[message.MessageID] = message.Channel 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 - if l.requestTimeout > 0 { + 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(l.requestTimeout) + time.Sleep(requestTimeout) timeoutMessage := &messagePacket{ Op: MessageTimeout, MessageID: message.MessageID, @@ -351,26 +397,26 @@ func (l *Conn) processMessages() { } case MessageResponse: l.Debug.Printf("Receiving message %d", message.MessageID) - if chanResult, ok := l.chanResults[message.MessageID]; ok { - chanResult <- &PacketResponse{message.Packet, nil} + 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) + log.Printf("Received unexpected message %d, %v", message.MessageID, l.isClosing()) ber.PrintPacket(message.Packet) } case MessageTimeout: // Handle the timeout by closing the channel // All reads will return immediately - if chanResult, ok := l.chanResults[message.MessageID]; ok { - chanResult <- &PacketResponse{message.Packet, errors.New("ldap: connection timed out")} + if msgCtx, ok := l.messageContexts[message.MessageID]; ok { l.Debug.Printf("Receiving message timeout for %d", message.MessageID) - delete(l.chanResults, message.MessageID) - close(chanResult) + 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 chanResult, ok := l.chanResults[message.MessageID]; ok { - close(chanResult) - delete(l.chanResults, message.MessageID) + if msgCtx, ok := l.messageContexts[message.MessageID]; ok { + delete(l.messageContexts, message.MessageID) + close(msgCtx.responses) } } } @@ -396,7 +442,8 @@ func (l *Conn) reader() { packet, err := ber.ReadPacket(l.conn) if err != nil { // A read error is expected here if we are closing the connection... - if !l.isClosing { + if !l.isClosing() { + l.closeErr.Store(fmt.Errorf("unable to read LDAP response packet: %s", err)) l.Debug.Printf("reader error: %s", err.Error()) } return @@ -419,6 +466,5 @@ func (l *Conn) reader() { if !l.sendProcessMessage(message) { return } - } } diff --git a/vendor/gopkg.in/ldap.v2/control.go b/vendor/gopkg.in/ldap.v2/control.go index 4d829809..342f325c 100644 --- a/vendor/gopkg.in/ldap.v2/control.go +++ b/vendor/gopkg.in/ldap.v2/control.go @@ -12,35 +12,48 @@ import ( ) const ( - ControlTypePaging = "1.2.840.113556.1.4.319" - ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1" + // 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 = "2.16.840.1.113730.3.4.5" - ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2" + // 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" ) +// ControlTypeMap maps controls to text descriptions var ControlTypeMap = map[string]string{ ControlTypePaging: "Paging", ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft", ControlTypeManageDsaIT: "Manage DSA IT", } +// 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]+")")) @@ -51,26 +64,32 @@ func (c *ControlString) Encode() *ber.Packet { 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 []byte + // 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, uint64(c.PagingSize), "Paging Size")) + 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) @@ -81,6 +100,7 @@ func (c *ControlPaging) Encode() *ber.Packet { 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", @@ -91,21 +111,29 @@ func (c *ControlPaging) String() string { 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 int64 - Grace int64 - Error int8 + // 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]+")")) @@ -113,6 +141,7 @@ func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet { 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", @@ -125,39 +154,49 @@ func (c *ControlBeheraPasswordPolicy) String() string { 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: %b", + "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", @@ -167,14 +206,18 @@ func (c *ControlVChuPasswordWarning) String() string { 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") @@ -185,6 +228,7 @@ func (c *ControlManageDsaIT) Encode() *ber.Packet { return packet } +// String returns a human-readable description func (c *ControlManageDsaIT) String() string { return fmt.Sprintf( "Control Type: %s (%q) Criticality: %t", @@ -193,10 +237,12 @@ func (c *ControlManageDsaIT) String() string { c.Criticality) } +// NewControlManageDsaIT returns a ControlManageDsaIT control func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT { return &ControlManageDsaIT{Criticality: Criticality} } +// 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 { @@ -206,20 +252,56 @@ func FindControl(controls []Control, controlType string) Control { 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 { - ControlType := packet.Children[0].Value.(string) - Criticality := false + var ( + ControlType = "" + Criticality = false + value *ber.Packet + ) + + switch len(packet.Children) { + case 0: + // at least one child is required for control type + return nil + + 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[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")" - value := packet.Children[1] - if len(packet.Children) == 3 { - value = packet.Children[2] 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 } - value.Description = "Control Value" switch ControlType { + case ControlTypeManageDsaIT: + return NewControlManageDsaIT(Criticality) case ControlTypePaging: value.Description += " (Paging)" c := new(ControlPaging) @@ -252,18 +334,18 @@ func DecodeControl(packet *ber.Packet) Control { for _, child := range sequence.Children { if child.Tag == 0 { //Warning - child := child.Children[0] - packet := ber.DecodePacket(child.Data.Bytes()) + warningPacket := child.Children[0] + packet := ber.DecodePacket(warningPacket.Data.Bytes()) val, ok := packet.Value.(int64) if ok { - if child.Tag == 0 { + if warningPacket.Tag == 0 { //timeBeforeExpiration c.Expire = val - child.Value = c.Expire - } else if child.Tag == 1 { + warningPacket.Value = c.Expire + } else if warningPacket.Tag == 1 { //graceAuthNsRemaining c.Grace = val - child.Value = c.Grace + warningPacket.Value = c.Grace } } } else if child.Tag == 1 { @@ -294,15 +376,19 @@ func DecodeControl(packet *ber.Packet) Control { c.Expire = expire value.Value = c.Expire + return c + default: + c := new(ControlString) + c.ControlType = ControlType + c.Criticality = Criticality + if value != nil { + c.ControlValue = value.Value.(string) + } return c } - c := new(ControlString) - c.ControlType = ControlType - c.Criticality = Criticality - c.ControlValue = value.Value.(string) - return c } +// NewControlString returns a generic control func NewControlString(controlType string, criticality bool, controlValue string) *ControlString { return &ControlString{ ControlType: controlType, @@ -311,10 +397,12 @@ func NewControlString(controlType string, criticality bool, controlValue string) } } +// 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, diff --git a/vendor/gopkg.in/ldap.v2/debug.go b/vendor/gopkg.in/ldap.v2/debug.go index b8a7ecbf..7279fc25 100644 --- a/vendor/gopkg.in/ldap.v2/debug.go +++ b/vendor/gopkg.in/ldap.v2/debug.go @@ -6,7 +6,7 @@ import ( "gopkg.in/asn1-ber.v1" ) -// debbuging type +// debugging type // - has a Printf method to write the debug output type debugging bool diff --git a/vendor/gopkg.in/ldap.v2/del.go b/vendor/gopkg.in/ldap.v2/del.go index 5bb5a25d..4fd63dc3 100644 --- a/vendor/gopkg.in/ldap.v2/del.go +++ b/vendor/gopkg.in/ldap.v2/del.go @@ -12,8 +12,11 @@ import ( "gopkg.in/asn1-ber.v1" ) +// DelRequest implements an LDAP deletion request type DelRequest struct { - DN string + // DN is the name of the directory entry to delete + DN string + // Controls hold optional controls to send with the request Controls []Control } @@ -23,6 +26,7 @@ func (d DelRequest) encode() *ber.Packet { return request } +// NewDelRequest creates a delete request for the given DN and controls func NewDelRequest(DN string, Controls []Control) *DelRequest { return &DelRequest{ @@ -31,10 +35,10 @@ func NewDelRequest(DN string, } } +// Del executes the given delete request func (l *Conn) Del(delRequest *DelRequest) error { - messageID := l.nextMessageID() packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) packet.AppendChild(delRequest.encode()) if delRequest.Controls != nil { packet.AppendChild(encodeControls(delRequest.Controls)) @@ -42,22 +46,19 @@ func (l *Conn) Del(delRequest *DelRequest) error { l.Debug.PrintPacket(packet) - channel, err := l.sendMessage(packet) + msgCtx, err := l.sendMessage(packet) if err != nil { return err } - if channel == nil { - return NewError(ErrorNetwork, errors.New("ldap: could not send message")) - } - defer l.finishMessage(messageID) + defer l.finishMessage(msgCtx) - l.Debug.Printf("%d: waiting for response", messageID) - packetResponse, ok := <-channel + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses if !ok { - return NewError(ErrorNetwork, errors.New("ldap: channel closed")) + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) } packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", messageID, packet) + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { return err } @@ -78,6 +79,6 @@ func (l *Conn) Del(delRequest *DelRequest) error { log.Printf("Unexpected Response: %d", packet.Children[1].Tag) } - l.Debug.Printf("%d: returning", messageID) + l.Debug.Printf("%d: returning", msgCtx.id) return nil } diff --git a/vendor/gopkg.in/ldap.v2/dn.go b/vendor/gopkg.in/ldap.v2/dn.go index 5d83c5e9..34e9023a 100644 --- a/vendor/gopkg.in/ldap.v2/dn.go +++ b/vendor/gopkg.in/ldap.v2/dn.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -// File contains DN parsing functionallity +// File contains DN parsing functionality // // https://tools.ietf.org/html/rfc4514 // @@ -52,22 +52,28 @@ import ( "fmt" "strings" - ber "gopkg.in/asn1-ber.v1" + "gopkg.in/asn1-ber.v1" ) +// AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514 type AttributeTypeAndValue struct { - Type string + // 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 func ParseDN(str string) (*DN, error) { dn := new(DN) dn.RDNs = make([]*RelativeDN, 0) @@ -77,9 +83,19 @@ func ParseDN(str string) (*DN, error) { 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] if escaping { + unescapedTrailingSpaces = 0 escaping = false switch char { case ' ', '"', '#', '+', ',', ';', '<', '=', '>', '\\': @@ -94,19 +110,17 @@ func ParseDN(str string) (*DN, error) { dst := []byte{0} n, err := enchex.Decode([]byte(dst), []byte(str[i:i+2])) if err != nil { - return nil, errors.New( - fmt.Sprintf("Failed to decode escaped character: %s", err)) + return nil, fmt.Errorf("Failed to decode escaped character: %s", err) } else if n != 1 { - return nil, errors.New( - fmt.Sprintf("Expected 1 byte when un-escaping, got %d", n)) + return nil, fmt.Errorf("Expected 1 byte when un-escaping, got %d", n) } buffer.WriteByte(dst[0]) i++ } else if char == '\\' { + unescapedTrailingSpaces = 0 escaping = true } else if char == '=' { - attribute.Type = buffer.String() - buffer.Reset() + 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. @@ -119,18 +133,20 @@ func ParseDN(str string) (*DN, error) { } else { data = str[i:] } - raw_ber, err := enchex.DecodeString(data) + rawBER, err := enchex.DecodeString(data) if err != nil { - return nil, errors.New( - fmt.Sprintf("Failed to decode BER encoding: %s", err)) + return nil, fmt.Errorf("Failed to decode BER encoding: %s", err) } - packet := ber.DecodePacket(raw_ber) + packet := ber.DecodePacket(rawBER) buffer.WriteString(packet.Data.String()) i += len(data) - 1 } } else if char == ',' || char == '+' { // We're done with this RDN or value, push it - attribute.Value = buffer.String() + 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 == ',' { @@ -138,8 +154,17 @@ func ParseDN(str string) (*DN, error) { rdn = new(RelativeDN) rdn.Attributes = make([]*AttributeTypeAndValue, 0) } - buffer.Reset() + } else if char == ' ' && buffer.Len() == 0 { + // ignore unescaped leading spaces + continue } else { + 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) } } @@ -147,9 +172,76 @@ func ParseDN(str string) (*DN, error) { if len(attribute.Type) == 0 { return nil, errors.New("DN ended with incomplete type, value pair") } - attribute.Value = buffer.String() + attribute.Value = stringFromBuffer() rdn.Attributes = append(rdn.Attributes, attribute) dn.RDNs = append(dn.RDNs, rdn) } return dn, nil } + +// Equal returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch). +// Returns true if they have the same number of relative distinguished names +// and corresponding relative distinguished names (by position) are the same. +func (d *DN) Equal(other *DN) bool { + if len(d.RDNs) != len(other.RDNs) { + return false + } + for i := range d.RDNs { + if !d.RDNs[i].Equal(other.RDNs[i]) { + return false + } + } + return true +} + +// AncestorOf returns true if the other DN consists of at least one RDN followed by all the RDNs of the current DN. +// "ou=widgets,o=acme.com" is an ancestor of "ou=sprockets,ou=widgets,o=acme.com" +// "ou=widgets,o=acme.com" is not an ancestor of "ou=sprockets,ou=widgets,o=foo.com" +// "ou=widgets,o=acme.com" is not an ancestor of "ou=widgets,o=acme.com" +func (d *DN) AncestorOf(other *DN) bool { + if len(d.RDNs) >= len(other.RDNs) { + return false + } + // Take the last `len(d.RDNs)` RDNs from the other DN to compare against + otherRDNs := other.RDNs[len(other.RDNs)-len(d.RDNs):] + for i := range d.RDNs { + if !d.RDNs[i].Equal(otherRDNs[i]) { + return false + } + } + return true +} + +// Equal returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch). +// Relative distinguished names are the same if and only if they have the same number of AttributeTypeAndValues +// and each attribute of the first RDN is the same as the attribute of the second RDN with the same attribute type. +// The order of attributes is not significant. +// Case of attribute types is not significant. +func (r *RelativeDN) Equal(other *RelativeDN) bool { + if len(r.Attributes) != len(other.Attributes) { + return false + } + return r.hasAllAttributes(other.Attributes) && other.hasAllAttributes(r.Attributes) +} + +func (r *RelativeDN) hasAllAttributes(attrs []*AttributeTypeAndValue) bool { + for _, attr := range attrs { + found := false + for _, myattr := range r.Attributes { + if myattr.Equal(attr) { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +// Equal returns true if the AttributeTypeAndValue is equivalent to the specified AttributeTypeAndValue +// Case of the attribute type is not significant +func (a *AttributeTypeAndValue) Equal(other *AttributeTypeAndValue) bool { + return strings.EqualFold(a.Type, other.Type) && a.Value == other.Value +} diff --git a/vendor/gopkg.in/ldap.v2/error.go b/vendor/gopkg.in/ldap.v2/error.go index 97404eb6..4cccb537 100644 --- a/vendor/gopkg.in/ldap.v2/error.go +++ b/vendor/gopkg.in/ldap.v2/error.go @@ -56,6 +56,7 @@ const ( ErrorUnexpectedResponse = 205 ) +// LDAPResultCodeMap contains string descriptions for LDAP error codes var LDAPResultCodeMap = map[uint8]string{ LDAPResultSuccess: "Success", LDAPResultOperationsError: "Operations Error", @@ -96,6 +97,13 @@ var LDAPResultCodeMap = map[uint8]string{ LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited", LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs", LDAPResultOther: "Other", + + ErrorNetwork: "Network Error", + ErrorFilterCompile: "Filter Compile Error", + ErrorFilterDecompile: "Filter Decompile Error", + ErrorDebugging: "Debugging Error", + ErrorUnexpectedMessage: "Unexpected Message", + ErrorUnexpectedResponse: "Unexpected Response", } func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) { @@ -115,8 +123,11 @@ func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) { return ErrorNetwork, "Invalid packet format" } +// Error holds LDAP error information type Error struct { - Err error + // Err is the underlying error + Err error + // ResultCode is the LDAP error code ResultCode uint8 } @@ -124,10 +135,12 @@ func (e *Error) Error() string { return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error()) } +// NewError creates an LDAP error with the given code and underlying error func NewError(resultCode uint8, err error) error { return &Error{ResultCode: resultCode, Err: err} } +// IsErrorWithCode returns true if the given error is an LDAP error with the given result code func IsErrorWithCode(err error, desiredResultCode uint8) bool { if err == nil { return false diff --git a/vendor/gopkg.in/ldap.v2/filter.go b/vendor/gopkg.in/ldap.v2/filter.go index 63bcec1e..3858a286 100644 --- a/vendor/gopkg.in/ldap.v2/filter.go +++ b/vendor/gopkg.in/ldap.v2/filter.go @@ -15,6 +15,7 @@ import ( "gopkg.in/asn1-ber.v1" ) +// Filter choices const ( FilterAnd = 0 FilterOr = 1 @@ -28,6 +29,7 @@ const ( FilterExtensibleMatch = 9 ) +// FilterMap contains human readable descriptions of Filter choices var FilterMap = map[uint64]string{ FilterAnd: "And", FilterOr: "Or", @@ -41,18 +43,21 @@ var FilterMap = map[uint64]string{ 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 @@ -60,6 +65,7 @@ const ( 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", @@ -67,6 +73,7 @@ var MatchingRuleAssertionMap = map[uint64]string{ MatchingRuleAssertionDNAttributes: "Matching Rule Assertion DN Attributes", } +// 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 '('")) @@ -75,12 +82,16 @@ func CompileFilter(filter string) (*ber.Packet, error) { if err != nil { return nil, err } - if pos != len(filter) { + 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) (ret string, err error) { defer func() { if r := recover(); r != nil { @@ -239,11 +250,13 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { packet.AppendChild(child) return packet, newPos, err default: - READING_ATTR := 0 - READING_EXTENSIBLE_MATCHING_RULE := 1 - READING_CONDITION := 2 + const ( + stateReadingAttr = 0 + stateReadingExtensibleMatchingRule = 1 + stateReadingCondition = 2 + ) - state := READING_ATTR + state := stateReadingAttr attribute := "" extensibleDNAttributes := false @@ -261,56 +274,56 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { } switch state { - case READING_ATTR: + 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 = READING_CONDITION + 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 = READING_EXTENSIBLE_MATCHING_RULE + 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 = READING_CONDITION + 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 = READING_EXTENSIBLE_MATCHING_RULE - newPos += 1 + state = stateReadingExtensibleMatchingRule + newPos++ // Equality condition case currentRune == '=': packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch]) - state = READING_CONDITION - newPos += 1 + state = stateReadingCondition + newPos++ // Greater-than or equal case currentRune == '>' && strings.HasPrefix(remainingFilter, ">="): packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual]) - state = READING_CONDITION + 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 = READING_CONDITION + state = stateReadingCondition newPos += 2 // Approx case currentRune == '~' && strings.HasPrefix(remainingFilter, "~="): packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterApproxMatch]) - state = READING_CONDITION + state = stateReadingCondition newPos += 2 // Still reading the attribute name @@ -319,12 +332,12 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { newPos += currentWidth } - case READING_EXTENSIBLE_MATCHING_RULE: + case stateReadingExtensibleMatchingRule: switch { // Matching rule OID is done case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="): - state = READING_CONDITION + state = stateReadingCondition newPos += 2 // Still reading the matching rule oid @@ -333,7 +346,7 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { newPos += currentWidth } - case READING_CONDITION: + case stateReadingCondition: // append to the condition condition += fmt.Sprintf("%c", currentRune) newPos += currentWidth @@ -369,9 +382,9 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { } // Add the value (only required child) - encodedString, err := escapedStringToEncodedBytes(condition) - if err != nil { - return packet, newPos, err + encodedString, encodeErr := escapedStringToEncodedBytes(condition) + if encodeErr != nil { + return packet, newPos, encodeErr } packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchValue, encodedString, MatchingRuleAssertionMap[MatchingRuleAssertionMatchValue])) @@ -401,17 +414,17 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) { default: tag = FilterSubstringsAny } - encodedString, err := escapedStringToEncodedBytes(part) - if err != nil { - return packet, newPos, err + encodedString, encodeErr := escapedStringToEncodedBytes(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, err := escapedStringToEncodedBytes(condition) - if err != nil { - return packet, newPos, err + encodedString, encodeErr := escapedStringToEncodedBytes(condition) + if encodeErr != nil { + return packet, newPos, encodeErr } packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, encodedString, "Condition")) @@ -440,12 +453,12 @@ func escapedStringToEncodedBytes(escapedString string) (string, error) { if i+2 > len(escapedString) { return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter")) } - if escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3]); decodeErr != nil { + escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3]) + if decodeErr != nil { return "", NewError(ErrorFilterCompile, errors.New("ldap: invalid characters for escape in filter")) - } else { - buffer.WriteByte(escByte[0]) - i += 2 // +1 from end of loop, so 3 total for \xx. } + buffer.WriteByte(escByte[0]) + i += 2 // +1 from end of loop, so 3 total for \xx. } else { buffer.WriteRune(currentRune) } diff --git a/vendor/gopkg.in/ldap.v2/ldap.go b/vendor/gopkg.in/ldap.v2/ldap.go index 1620aaea..49692475 100644 --- a/vendor/gopkg.in/ldap.v2/ldap.go +++ b/vendor/gopkg.in/ldap.v2/ldap.go @@ -9,7 +9,7 @@ import ( "io/ioutil" "os" - ber "gopkg.in/asn1-ber.v1" + "gopkg.in/asn1-ber.v1" ) // LDAP Application Codes @@ -36,6 +36,7 @@ const ( ApplicationExtendedResponse = 24 ) +// ApplicationMap contains human readable descriptions of LDAP Application Codes var ApplicationMap = map[uint8]string{ ApplicationBindRequest: "Bind Request", ApplicationBindResponse: "Bind Response", @@ -72,6 +73,7 @@ const ( BeheraPasswordInHistory = 8 ) +// BeheraPasswordPolicyErrorMap contains human readable descriptions of Behera Password Policy error codes var BeheraPasswordPolicyErrorMap = map[int8]string{ BeheraPasswordExpired: "Password expired", BeheraAccountLocked: "Account locked", @@ -151,16 +153,47 @@ func addLDAPDescriptions(packet *ber.Packet) (err error) { func addControlDescriptions(packet *ber.Packet) { packet.Description = "Controls" for _, child := range packet.Children { + var value *ber.Packet + controlType := "" child.Description = "Control" - child.Children[0].Description = "Control Type (" + ControlTypeMap[child.Children[0].Value.(string)] + ")" - value := child.Children[1] - if len(child.Children) == 3 { - child.Children[1].Description = "Criticality" - value = child.Children[2] - } - value.Description = "Control Value" + switch len(child.Children) { + case 0: + // at least one child is required for control type + continue - switch child.Children[0].Value.(string) { + 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 + continue + } + if value == nil { + continue + } + switch controlType { case ControlTypePaging: value.Description += " (Paging)" if value.Value != nil { @@ -186,18 +219,18 @@ func addControlDescriptions(packet *ber.Packet) { for _, child := range sequence.Children { if child.Tag == 0 { //Warning - child := child.Children[0] - packet := ber.DecodePacket(child.Data.Bytes()) + warningPacket := child.Children[0] + packet := ber.DecodePacket(warningPacket.Data.Bytes()) val, ok := packet.Value.(int64) if ok { - if child.Tag == 0 { + if warningPacket.Tag == 0 { //timeBeforeExpiration value.Description += " (TimeBeforeExpiration)" - child.Value = val - } else if child.Tag == 1 { + warningPacket.Value = val + } else if warningPacket.Tag == 1 { //graceAuthNsRemaining value.Description += " (GraceAuthNsRemaining)" - child.Value = val + warningPacket.Value = val } } } else if child.Tag == 1 { @@ -237,6 +270,7 @@ func addDefaultLDAPResponseDescriptions(packet *ber.Packet) { } } +// DebugBinaryFile reads and prints packets from the given filename func DebugBinaryFile(fileName string) error { file, err := ioutil.ReadFile(fileName) if err != nil { diff --git a/vendor/gopkg.in/ldap.v2/modify.go b/vendor/gopkg.in/ldap.v2/modify.go index 5c042af7..e4ab6cef 100644 --- a/vendor/gopkg.in/ldap.v2/modify.go +++ b/vendor/gopkg.in/ldap.v2/modify.go @@ -36,64 +36,76 @@ import ( "gopkg.in/asn1-ber.v1" ) +// Change operation choices const ( AddAttribute = 0 DeleteAttribute = 1 ReplaceAttribute = 2 ) +// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 type PartialAttribute struct { - attrType string - attrVals []string + // 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.attrType, "Type")) + 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.attrVals { + for _, value := range p.Vals { set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals")) } seq.AppendChild(set) return seq } +// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 type ModifyRequest struct { - dn string - addAttributes []PartialAttribute - deleteAttributes []PartialAttribute - replaceAttributes []PartialAttribute + // DN is the distinguishedName of the directory entry to modify + DN string + // AddAttributes contain the attributes to add + AddAttributes []PartialAttribute + // DeleteAttributes contain the attributes to delete + DeleteAttributes []PartialAttribute + // ReplaceAttributes contain the attributes to replace + ReplaceAttributes []PartialAttribute } +// Add inserts the given attribute to the list of attributes to add func (m *ModifyRequest) Add(attrType string, attrVals []string) { - m.addAttributes = append(m.addAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals}) + m.AddAttributes = append(m.AddAttributes, PartialAttribute{Type: attrType, Vals: attrVals}) } +// Delete inserts the given attribute to the list of attributes to delete func (m *ModifyRequest) Delete(attrType string, attrVals []string) { - m.deleteAttributes = append(m.deleteAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals}) + m.DeleteAttributes = append(m.DeleteAttributes, PartialAttribute{Type: attrType, Vals: attrVals}) } +// Replace inserts the given attribute to the list of attributes to replace func (m *ModifyRequest) Replace(attrType string, attrVals []string) { - m.replaceAttributes = append(m.replaceAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals}) + m.ReplaceAttributes = append(m.ReplaceAttributes, PartialAttribute{Type: attrType, Vals: attrVals}) } func (m ModifyRequest) encode() *ber.Packet { request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request") - request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.dn, "DN")) + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN")) changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes") - for _, attribute := range m.addAttributes { + for _, attribute := range m.AddAttributes { change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(AddAttribute), "Operation")) change.AppendChild(attribute.encode()) changes.AppendChild(change) } - for _, attribute := range m.deleteAttributes { + for _, attribute := range m.DeleteAttributes { change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(DeleteAttribute), "Operation")) change.AppendChild(attribute.encode()) changes.AppendChild(change) } - for _, attribute := range m.replaceAttributes { + for _, attribute := range m.ReplaceAttributes { change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ReplaceAttribute), "Operation")) change.AppendChild(attribute.encode()) @@ -103,38 +115,36 @@ func (m ModifyRequest) encode() *ber.Packet { return request } +// NewModifyRequest creates a modify request for the given DN func NewModifyRequest( dn string, ) *ModifyRequest { return &ModifyRequest{ - dn: dn, + DN: dn, } } +// Modify performs the ModifyRequest func (l *Conn) Modify(modifyRequest *ModifyRequest) error { - messageID := l.nextMessageID() packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) packet.AppendChild(modifyRequest.encode()) l.Debug.PrintPacket(packet) - channel, err := l.sendMessage(packet) + msgCtx, err := l.sendMessage(packet) if err != nil { return err } - if channel == nil { - return NewError(ErrorNetwork, errors.New("ldap: could not send message")) - } - defer l.finishMessage(messageID) + defer l.finishMessage(msgCtx) - l.Debug.Printf("%d: waiting for response", messageID) - packetResponse, ok := <-channel + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses if !ok { - return NewError(ErrorNetwork, errors.New("ldap: channel closed")) + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) } packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", messageID, packet) + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { return err } @@ -155,6 +165,6 @@ func (l *Conn) Modify(modifyRequest *ModifyRequest) error { log.Printf("Unexpected Response: %d", packet.Children[1].Tag) } - l.Debug.Printf("%d: returning", messageID) + l.Debug.Printf("%d: returning", msgCtx.id) return nil } diff --git a/vendor/gopkg.in/ldap.v2/passwdmodify.go b/vendor/gopkg.in/ldap.v2/passwdmodify.go index 6d5ca975..7d8246fd 100644 --- a/vendor/gopkg.in/ldap.v2/passwdmodify.go +++ b/vendor/gopkg.in/ldap.v2/passwdmodify.go @@ -16,13 +16,21 @@ 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 string - NewPassword 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 } @@ -47,7 +55,7 @@ func (r *PasswordModifyRequest) encode() (*ber.Packet, error) { return request, nil } -// Create a new PasswordModifyRequest +// NewPasswordModifyRequest creates a new PasswordModifyRequest // // According to the RFC 3602: // userIdentity is a string representing the user associated with the request. @@ -72,11 +80,10 @@ func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPasswo } } +// PasswordModify performs the modification request func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) { - messageID := l.nextMessageID() - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) encodedPasswordModifyRequest, err := passwordModifyRequest.encode() if err != nil { @@ -86,24 +93,21 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa l.Debug.PrintPacket(packet) - channel, err := l.sendMessage(packet) + msgCtx, err := l.sendMessage(packet) if err != nil { return nil, err } - if channel == nil { - return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message")) - } - defer l.finishMessage(messageID) + defer l.finishMessage(msgCtx) result := &PasswordModifyResult{} - l.Debug.Printf("%d: waiting for response", messageID) - packetResponse, ok := <-channel + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses if !ok { - return nil, NewError(ErrorNetwork, errors.New("ldap: channel closed")) + return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) } packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", messageID, packet) + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { return nil, err } @@ -131,10 +135,10 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa extendedResponse := packet.Children[1] for _, child := range extendedResponse.Children { if child.Tag == 11 { - passwordModifyReponseValue := ber.DecodePacket(child.Data.Bytes()) - if len(passwordModifyReponseValue.Children) == 1 { - if passwordModifyReponseValue.Children[0].Tag == 0 { - result.GeneratedPassword = ber.DecodeString(passwordModifyReponseValue.Children[0].Data.Bytes()) + 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()) } } } diff --git a/vendor/gopkg.in/ldap.v2/search.go b/vendor/gopkg.in/ldap.v2/search.go index 7e9495bd..2a99894c 100644 --- a/vendor/gopkg.in/ldap.v2/search.go +++ b/vendor/gopkg.in/ldap.v2/search.go @@ -68,18 +68,21 @@ import ( "gopkg.in/asn1-ber.v1" ) +// 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 @@ -87,6 +90,7 @@ const ( DerefAlways = 3 ) +// DerefMap contains human readable descriptions of derefAliases choices var DerefMap = map[int]string{ NeverDerefAliases: "NeverDerefAliases", DerefInSearching: "DerefInSearching", @@ -114,11 +118,15 @@ func NewEntry(dn string, attributes map[string][]string) *Entry { } } +// Entry represents a single search result entry type Entry struct { - DN string + // 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 { @@ -128,6 +136,7 @@ func (e *Entry) GetAttributeValues(attribute string) []string { 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 { @@ -137,6 +146,7 @@ func (e *Entry) GetRawAttributeValues(attribute string) [][]byte { 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 { @@ -145,6 +155,7 @@ func (e *Entry) GetAttributeValue(attribute string) string { 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 { @@ -153,6 +164,7 @@ func (e *Entry) GetRawAttributeValue(attribute string) []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 { @@ -160,6 +172,7 @@ func (e *Entry) 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 { @@ -180,38 +193,51 @@ func NewEntryAttribute(name string, values []string) *EntryAttribute { } } +// EntryAttribute holds a single attribute type EntryAttribute struct { - Name string - Values []string + // 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 []*Entry + // Entries are the returned entries + Entries []*Entry + // Referrals are the returned referrals Referrals []string - Controls []Control + // 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 @@ -247,6 +273,7 @@ func (s *SearchRequest) encode() (*ber.Packet, error) { return request, nil } +// NewSearchRequest creates a new search request func NewSearchRequest( BaseDN string, Scope, DerefAliases, SizeLimit, TimeLimit int, @@ -341,10 +368,10 @@ func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) return searchResult, nil } +// Search performs the given search request func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { - messageID := l.nextMessageID() packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) // encode search request encodedSearchRequest, err := searchRequest.encode() if err != nil { @@ -358,14 +385,11 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { l.Debug.PrintPacket(packet) - channel, err := l.sendMessage(packet) + msgCtx, err := l.sendMessage(packet) if err != nil { return nil, err } - if channel == nil { - return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message")) - } - defer l.finishMessage(messageID) + defer l.finishMessage(msgCtx) result := &SearchResult{ Entries: make([]*Entry, 0), @@ -374,13 +398,13 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { foundSearchResultDone := false for !foundSearchResultDone { - l.Debug.Printf("%d: waiting for response", messageID) - packetResponse, ok := <-channel + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses if !ok { - return nil, NewError(ErrorNetwork, errors.New("ldap: channel closed")) + return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) } packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", messageID, packet) + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { return nil, err } @@ -421,6 +445,6 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string)) } } - l.Debug.Printf("%d: returning", messageID) + l.Debug.Printf("%d: returning", msgCtx.id) return result, nil }