From 2ca0e8f9a0f9863555a26e984cde15efff9ef8f8 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Fri, 23 Sep 2016 10:17:51 -0400 Subject: Updating golang dependancies (#4075) --- vendor/github.com/go-ldap/ldap/.githooks/pre-push | 6 + vendor/github.com/go-ldap/ldap/.travis.yml | 11 +- vendor/github.com/go-ldap/ldap/Makefile | 42 +++ vendor/github.com/go-ldap/ldap/README.md | 28 +- vendor/github.com/go-ldap/ldap/add.go | 49 ++-- vendor/github.com/go-ldap/ldap/bind.go | 42 ++- vendor/github.com/go-ldap/ldap/compare.go | 18 +- vendor/github.com/go-ldap/ldap/conn.go | 147 +++++++---- vendor/github.com/go-ldap/ldap/conn_test.go | 295 +++++++++++++++++++++- vendor/github.com/go-ldap/ldap/control.go | 132 ++++++++-- vendor/github.com/go-ldap/ldap/control_test.go | 58 +++++ vendor/github.com/go-ldap/ldap/del.go | 27 +- vendor/github.com/go-ldap/ldap/dn.go | 21 +- vendor/github.com/go-ldap/ldap/error.go | 8 +- vendor/github.com/go-ldap/ldap/error_test.go | 77 +++++- vendor/github.com/go-ldap/ldap/filter.go | 72 +++--- vendor/github.com/go-ldap/ldap/ldap.go | 3 + vendor/github.com/go-ldap/ldap/modify.go | 66 +++-- vendor/github.com/go-ldap/ldap/passwdmodify.go | 34 +-- vendor/github.com/go-ldap/ldap/search.go | 58 +++-- 20 files changed, 929 insertions(+), 265 deletions(-) create mode 100755 vendor/github.com/go-ldap/ldap/.githooks/pre-push create mode 100644 vendor/github.com/go-ldap/ldap/Makefile create mode 100644 vendor/github.com/go-ldap/ldap/control_test.go (limited to 'vendor/github.com/go-ldap') diff --git a/vendor/github.com/go-ldap/ldap/.githooks/pre-push b/vendor/github.com/go-ldap/ldap/.githooks/pre-push new file mode 100755 index 000000000..4325ee31c --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/.githooks/pre-push @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# install from the root of the repo with: +# ln -s ../../.githooks/pre-push .git/hooks/pre-push + +make vet fmt lint \ No newline at end of file diff --git a/vendor/github.com/go-ldap/ldap/.travis.yml b/vendor/github.com/go-ldap/ldap/.travis.yml index a7a38951b..7e2f641e7 100644 --- a/vendor/github.com/go-ldap/ldap/.travis.yml +++ b/vendor/github.com/go-ldap/ldap/.travis.yml @@ -1,15 +1,24 @@ language: go +env: + global: + - VET_VERSIONS="1.5 1.6 tip" + - LINT_VERSIONS="1.5 1.6 tip" go: - 1.2 - 1.3 - 1.4 - 1.5 + - 1.6 - tip go_import_path: gopkg.in/ldap.v2 install: - go get gopkg.in/asn1-ber.v1 - go get gopkg.in/ldap.v2 - go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover + - go get github.com/golang/lint/golint || true - go build -v ./... script: - - go test -v -cover ./... + - make test + - make fmt + - if [[ "$VET_VERSIONS" == *"$TRAVIS_GO_VERSION"* ]]; then make vet; fi + - if [[ "$LINT_VERSIONS" == *"$TRAVIS_GO_VERSION"* ]]; then make lint; fi diff --git a/vendor/github.com/go-ldap/ldap/Makefile b/vendor/github.com/go-ldap/ldap/Makefile new file mode 100644 index 000000000..c1fc96657 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/Makefile @@ -0,0 +1,42 @@ +.PHONY: default install build test quicktest fmt vet lint + +default: fmt vet lint build quicktest + +install: + go get -t -v ./... + +build: + go build -v ./... + +test: + go test -v -cover ./... + +quicktest: + go test ./... + +# Capture output and force failure when there is non-empty output +fmt: + @echo gofmt -l . + @OUTPUT=`gofmt -l . 2>&1`; \ + if [ "$$OUTPUT" ]; then \ + echo "gofmt must be run on the following files:"; \ + echo "$$OUTPUT"; \ + exit 1; \ + fi + +# Only run on go1.5+ +vet: + go tool vet -atomic -bool -copylocks -nilfunc -printf -shadow -rangeloops -unreachable -unsafeptr -unusedresult . + +# https://github.com/golang/lint +# go get github.com/golang/lint/golint +# Capture output and force failure when there is non-empty output +# Only run on go1.5+ +lint: + @echo golint ./... + @OUTPUT=`golint ./... 2>&1`; \ + if [ "$$OUTPUT" ]; then \ + echo "golint errors:"; \ + echo "$$OUTPUT"; \ + exit 1; \ + fi diff --git a/vendor/github.com/go-ldap/ldap/README.md b/vendor/github.com/go-ldap/ldap/README.md index f49b4d6a1..a26ed2d82 100644 --- a/vendor/github.com/go-ldap/ldap/README.md +++ b/vendor/github.com/go-ldap/ldap/README.md @@ -13,41 +13,39 @@ Import the latest version with: import "gopkg.in/ldap.v2" - ## Required Libraries: - gopkg.in/asn1-ber.v1 -## Working: +## Features: - - Connecting to LDAP server + - Connecting to LDAP server (non-TLS, TLS, STARTTLS) - Binding to LDAP server - Searching for entries - - Compiling string filters to LDAP filters + - Filter Compile / Decompile - Paging Search Results - Modify Requests / Responses - Add Requests / Responses - Delete Requests / Responses - - Better Unicode support ## Examples: - search - modify -## Tests Implemented: - - - Filter Compile / Decompile - -## TODO: +## Contributing: - - [x] Add Requests / Responses - - [x] Delete Requests / Responses - - [x] Modify DN Requests / Responses - - [ ] Compare Requests / Responses - - [ ] Implement Tests / Benchmarks +Bug reports and pull requests are welcome! +Before submitting a pull request, please make sure tests and verification scripts pass: +``` +make all +``` +To set up a pre-push hook to run the tests and verify scripts before pushing: +``` +ln -s ../../.githooks/pre-push .git/hooks/pre-push +``` --- The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/) diff --git a/vendor/github.com/go-ldap/ldap/add.go b/vendor/github.com/go-ldap/ldap/add.go index 61b795e0e..0e5f6cdba 100644 --- a/vendor/github.com/go-ldap/ldap/add.go +++ b/vendor/github.com/go-ldap/ldap/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/github.com/go-ldap/ldap/bind.go b/vendor/github.com/go-ldap/ldap/bind.go index ae68eb481..26b3cc727 100644 --- a/vendor/github.com/go-ldap/ldap/bind.go +++ b/vendor/github.com/go-ldap/ldap/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/github.com/go-ldap/ldap/compare.go b/vendor/github.com/go-ldap/ldap/compare.go index dfe728bad..cc6d2af5e 100644 --- a/vendor/github.com/go-ldap/ldap/compare.go +++ b/vendor/github.com/go-ldap/ldap/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/github.com/go-ldap/ldap/conn.go b/vendor/github.com/go-ldap/ldap/conn.go index 6aad628be..b5bd99adb 100644 --- a/vendor/github.com/go-ldap/ldap/conn.go +++ b/vendor/github.com/go-ldap/ldap/conn.go @@ -17,18 +17,27 @@ import ( ) 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 +45,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 @@ -54,10 +83,11 @@ type Conn struct { conn net.Conn isTLS bool isClosing bool + closeErr error isStartingTLS bool Debug debugging chanConfirm chan bool - chanResults map[int64]chan *PacketResponse + messageContexts map[int64]*messageContext chanMessage chan *messagePacket chanMessageID chan int64 wgSender sync.WaitGroup @@ -111,16 +141,17 @@ 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 bool), + 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() @@ -148,7 +179,7 @@ func (l *Conn) Close() { 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 @@ -167,35 +198,31 @@ func (l *Conn) nextMessageID() int64 { // 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,11 +253,11 @@ 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) { +func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (*messageContext, error) { if l.isClosing { return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed")) } @@ -238,32 +265,38 @@ func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) 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) { +func (l *Conn) finishMessage(msgCtx *messageContext) { + close(msgCtx.done) + if l.isClosing { return } @@ -277,7 +310,7 @@ func (l *Conn) finishMessage(messageID int64) { message := &messagePacket{ Op: MessageFinish, - MessageID: messageID, + MessageID: msgCtx.id, } l.sendProcessMessage(message) } @@ -297,10 +330,15 @@ 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 != nil { + msgCtx.sendResponse(&PacketResponse{Error: l.closeErr}) + } 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 @@ -324,15 +362,20 @@ 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 { go func() { @@ -351,8 +394,8 @@ 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) ber.PrintPacket(message.Packet) @@ -360,17 +403,17 @@ func (l *Conn) processMessages() { 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) } } } @@ -397,6 +440,7 @@ func (l *Conn) reader() { if err != nil { // A read error is expected here if we are closing the connection... if !l.isClosing { + l.closeErr = fmt.Errorf("unable to read LDAP response packet: %s", err) l.Debug.Printf("reader error: %s", err.Error()) } return @@ -419,6 +463,5 @@ func (l *Conn) reader() { if !l.sendProcessMessage(message) { return } - } } diff --git a/vendor/github.com/go-ldap/ldap/conn_test.go b/vendor/github.com/go-ldap/ldap/conn_test.go index 8394e5339..10766bbd4 100644 --- a/vendor/github.com/go-ldap/ldap/conn_test.go +++ b/vendor/github.com/go-ldap/ldap/conn_test.go @@ -1,9 +1,14 @@ package ldap import ( + "bytes" + "errors" + "io" "net" "net/http" "net/http/httptest" + "runtime" + "sync" "testing" "time" @@ -27,19 +32,20 @@ func TestUnresponsiveConnection(t *testing.T) { defer conn.Close() // Mock a packet - messageID := conn.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, conn.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")) packet.AppendChild(bindRequest) // Send packet and test response - channel, err := conn.sendMessage(packet) + msgCtx, err := conn.sendMessage(packet) if err != nil { t.Fatalf("error sending message: %v", err) } - packetResponse, ok := <-channel + defer conn.finishMessage(msgCtx) + + packetResponse, ok := <-msgCtx.responses if !ok { t.Fatalf("no PacketResponse in response channel") } @@ -51,3 +57,284 @@ func TestUnresponsiveConnection(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } + +// TestFinishMessage tests that we do not enter deadlock when a goroutine makes +// a request but does not handle all responses from the server. +func TestConn(t *testing.T) { + ptc := newPacketTranslatorConn() + defer ptc.Close() + + conn := NewConn(ptc, false) + conn.Start() + + // Test sending 5 different requests in series. Ensure that we can + // get a response packet from the underlying connection and also + // ensure that we can gracefully ignore unhandled responses. + for i := 0; i < 5; i++ { + t.Logf("serial request %d", i) + // Create a message and make sure we can receive responses. + msgCtx := testSendRequest(t, ptc, conn) + testReceiveResponse(t, ptc, msgCtx) + + // Send a few unhandled responses and finish the message. + testSendUnhandledResponsesAndFinish(t, ptc, conn, msgCtx, 5) + t.Logf("serial request %d done", i) + } + + // Test sending 5 different requests in parallel. + var wg sync.WaitGroup + for i := 0; i < 5; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + t.Logf("parallel request %d", i) + // Create a message and make sure we can receive responses. + msgCtx := testSendRequest(t, ptc, conn) + testReceiveResponse(t, ptc, msgCtx) + + // Send a few unhandled responses and finish the message. + testSendUnhandledResponsesAndFinish(t, ptc, conn, msgCtx, 5) + t.Logf("parallel request %d done", i) + }(i) + } + wg.Wait() + + // We cannot run Close() in a defer because t.FailNow() will run it and + // it will block if the processMessage Loop is in a deadlock. + conn.Close() +} + +func testSendRequest(t *testing.T, ptc *packetTranslatorConn, conn *Conn) (msgCtx *messageContext) { + var msgID int64 + runWithTimeout(t, time.Second, func() { + msgID = conn.nextMessageID() + }) + + requestPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + requestPacket.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, msgID, "MessageID")) + + var err error + + runWithTimeout(t, time.Second, func() { + msgCtx, err = conn.sendMessage(requestPacket) + if err != nil { + t.Fatalf("unable to send request message: %s", err) + } + }) + + // We should now be able to get this request packet out from the other + // side. + runWithTimeout(t, time.Second, func() { + if _, err = ptc.ReceiveRequest(); err != nil { + t.Fatalf("unable to receive request packet: %s", err) + } + }) + + return msgCtx +} + +func testReceiveResponse(t *testing.T, ptc *packetTranslatorConn, msgCtx *messageContext) { + // Send a mock response packet. + responsePacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Response") + responsePacket.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, msgCtx.id, "MessageID")) + + runWithTimeout(t, time.Second, func() { + if err := ptc.SendResponse(responsePacket); err != nil { + t.Fatalf("unable to send response packet: %s", err) + } + }) + + // We should be able to receive the packet from the connection. + runWithTimeout(t, time.Second, func() { + if _, ok := <-msgCtx.responses; !ok { + t.Fatal("response channel closed") + } + }) +} + +func testSendUnhandledResponsesAndFinish(t *testing.T, ptc *packetTranslatorConn, conn *Conn, msgCtx *messageContext, numResponses int) { + // Send a mock response packet. + responsePacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Response") + responsePacket.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, msgCtx.id, "MessageID")) + + // Send extra responses but do not attempt to receive them on the + // client side. + for i := 0; i < numResponses; i++ { + runWithTimeout(t, time.Second, func() { + if err := ptc.SendResponse(responsePacket); err != nil { + t.Fatalf("unable to send response packet: %s", err) + } + }) + } + + // Finally, attempt to finish this message. + runWithTimeout(t, time.Second, func() { + conn.finishMessage(msgCtx) + }) +} + +func runWithTimeout(t *testing.T, timeout time.Duration, f func()) { + runtime.Gosched() + + done := make(chan struct{}) + go func() { + f() + close(done) + }() + + runtime.Gosched() + + select { + case <-done: // Success! + case <-time.After(timeout): + _, file, line, _ := runtime.Caller(1) + t.Fatalf("%s:%d timed out", file, line) + } +} + +// packetTranslatorConn is a helful type which can be used with various tests +// in this package. It implements the net.Conn interface to be used as an +// underlying connection for a *ldap.Conn. Most methods are no-ops but the +// Read() and Write() methods are able to translate ber-encoded packets for +// testing LDAP requests and responses. +// +// Test cases can simulate an LDAP server sending a response by calling the +// SendResponse() method with a ber-encoded LDAP response packet. Test cases +// can simulate an LDAP server receiving a request from a client by calling the +// ReceiveRequest() method which returns a ber-encoded LDAP request packet. +type packetTranslatorConn struct { + lock sync.Mutex + isClosed bool + + responseCond sync.Cond + requestCond sync.Cond + + responseBuf bytes.Buffer + requestBuf bytes.Buffer +} + +var errPacketTranslatorConnClosed = errors.New("connection closed") + +func newPacketTranslatorConn() *packetTranslatorConn { + conn := &packetTranslatorConn{} + conn.responseCond = sync.Cond{L: &conn.lock} + conn.requestCond = sync.Cond{L: &conn.lock} + + return conn +} + +// Read is called by the reader() loop to receive response packets. It will +// block until there are more packet bytes available or this connection is +// closed. +func (c *packetTranslatorConn) Read(b []byte) (n int, err error) { + c.lock.Lock() + defer c.lock.Unlock() + + for !c.isClosed { + // Attempt to read data from the response buffer. If it fails + // with an EOF, wait and try again. + n, err = c.responseBuf.Read(b) + if err != io.EOF { + return n, err + } + + c.responseCond.Wait() + } + + return 0, errPacketTranslatorConnClosed +} + +// SendResponse writes the given response packet to the response buffer for +// this conection, signalling any goroutine waiting to read a response. +func (c *packetTranslatorConn) SendResponse(packet *ber.Packet) error { + c.lock.Lock() + defer c.lock.Unlock() + + if c.isClosed { + return errPacketTranslatorConnClosed + } + + // Signal any goroutine waiting to read a response. + defer c.responseCond.Broadcast() + + // Writes to the buffer should always succeed. + c.responseBuf.Write(packet.Bytes()) + + return nil +} + +// Write is called by the processMessages() loop to send request packets. +func (c *packetTranslatorConn) Write(b []byte) (n int, err error) { + c.lock.Lock() + defer c.lock.Unlock() + + if c.isClosed { + return 0, errPacketTranslatorConnClosed + } + + // Signal any goroutine waiting to read a request. + defer c.requestCond.Broadcast() + + // Writes to the buffer should always succeed. + return c.requestBuf.Write(b) +} + +// ReceiveRequest attempts to read a request packet from this connection. It +// will block until it is able to read a full request packet or until this +// connection is closed. +func (c *packetTranslatorConn) ReceiveRequest() (*ber.Packet, error) { + c.lock.Lock() + defer c.lock.Unlock() + + for !c.isClosed { + // Attempt to parse a request packet from the request buffer. + // If it fails with an unexpected EOF, wait and try again. + requestReader := bytes.NewReader(c.requestBuf.Bytes()) + packet, err := ber.ReadPacket(requestReader) + switch err { + case io.EOF, io.ErrUnexpectedEOF: + c.requestCond.Wait() + case nil: + // Advance the request buffer by the number of bytes + // read to decode the request packet. + c.requestBuf.Next(c.requestBuf.Len() - requestReader.Len()) + return packet, nil + default: + return nil, err + } + } + + return nil, errPacketTranslatorConnClosed +} + +// Close closes this connection causing Read() and Write() calls to fail. +func (c *packetTranslatorConn) Close() error { + c.lock.Lock() + defer c.lock.Unlock() + + c.isClosed = true + c.responseCond.Broadcast() + c.requestCond.Broadcast() + + return nil +} + +func (c *packetTranslatorConn) LocalAddr() net.Addr { + return (*net.TCPAddr)(nil) +} + +func (c *packetTranslatorConn) RemoteAddr() net.Addr { + return (*net.TCPAddr)(nil) +} + +func (c *packetTranslatorConn) SetDeadline(t time.Time) error { + return nil +} + +func (c *packetTranslatorConn) SetReadDeadline(t time.Time) error { + return nil +} + +func (c *packetTranslatorConn) SetWriteDeadline(t time.Time) error { + return nil +} diff --git a/vendor/github.com/go-ldap/ldap/control.go b/vendor/github.com/go-ldap/ldap/control.go index 4d8298093..5c62118d4 100644 --- a/vendor/github.com/go-ldap/ldap/control.go +++ b/vendor/github.com/go-ldap/ldap/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) @@ -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/github.com/go-ldap/ldap/control_test.go b/vendor/github.com/go-ldap/ldap/control_test.go new file mode 100644 index 000000000..3fcdab0d7 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/control_test.go @@ -0,0 +1,58 @@ +package ldap + +import ( + "bytes" + "fmt" + "reflect" + "runtime" + "testing" + + "gopkg.in/asn1-ber.v1" +) + +func TestControlPaging(t *testing.T) { + runControlTest(t, NewControlPaging(0)) + runControlTest(t, NewControlPaging(100)) +} + +func TestControlManageDsaIT(t *testing.T) { + runControlTest(t, NewControlManageDsaIT(true)) + runControlTest(t, NewControlManageDsaIT(false)) +} + +func TestControlString(t *testing.T) { + runControlTest(t, NewControlString("x", true, "y")) + runControlTest(t, NewControlString("x", true, "")) + runControlTest(t, NewControlString("x", false, "y")) + runControlTest(t, NewControlString("x", false, "")) +} + +func runControlTest(t *testing.T, originalControl Control) { + header := "" + if callerpc, _, line, ok := runtime.Caller(1); ok { + if caller := runtime.FuncForPC(callerpc); caller != nil { + header = fmt.Sprintf("%s:%d: ", caller.Name(), line) + } + } + + encodedPacket := originalControl.Encode() + encodedBytes := encodedPacket.Bytes() + + // Decode directly from the encoded packet (ensures Value is correct) + fromPacket := DecodeControl(encodedPacket) + if !bytes.Equal(encodedBytes, fromPacket.Encode().Bytes()) { + t.Errorf("%sround-trip from encoded packet failed", header) + } + if reflect.TypeOf(originalControl) != reflect.TypeOf(fromPacket) { + t.Errorf("%sgot different type decoding from encoded packet: %T vs %T", header, fromPacket, originalControl) + } + + // Decode from the wire bytes (ensures ber-encoding is correct) + fromBytes := DecodeControl(ber.DecodePacket(encodedBytes)) + if !bytes.Equal(encodedBytes, fromBytes.Encode().Bytes()) { + t.Errorf("%sround-trip from encoded bytes failed", header) + } + if reflect.TypeOf(originalControl) != reflect.TypeOf(fromPacket) { + t.Errorf("%sgot different type decoding from encoded bytes: %T vs %T", header, fromBytes, originalControl) + } +} diff --git a/vendor/github.com/go-ldap/ldap/del.go b/vendor/github.com/go-ldap/ldap/del.go index 5bb5a25d7..4fd63dc3f 100644 --- a/vendor/github.com/go-ldap/ldap/del.go +++ b/vendor/github.com/go-ldap/ldap/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/github.com/go-ldap/ldap/dn.go b/vendor/github.com/go-ldap/ldap/dn.go index 5d83c5e9a..cc70c894c 100644 --- a/vendor/github.com/go-ldap/ldap/dn.go +++ b/vendor/github.com/go-ldap/ldap/dn.go @@ -55,19 +55,25 @@ import ( ber "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) @@ -94,11 +100,9 @@ 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++ @@ -119,12 +123,11 @@ 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 } diff --git a/vendor/github.com/go-ldap/ldap/error.go b/vendor/github.com/go-ldap/ldap/error.go index 97404eb65..ff697873d 100644 --- a/vendor/github.com/go-ldap/ldap/error.go +++ b/vendor/github.com/go-ldap/ldap/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", @@ -115,8 +116,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 +128,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/github.com/go-ldap/ldap/error_test.go b/vendor/github.com/go-ldap/ldap/error_test.go index 4ec720d9f..c010ebe3e 100644 --- a/vendor/github.com/go-ldap/ldap/error_test.go +++ b/vendor/github.com/go-ldap/ldap/error_test.go @@ -1,7 +1,11 @@ package ldap import ( + "errors" + "net" + "strings" "testing" + "time" "gopkg.in/asn1-ber.v1" ) @@ -16,8 +20,8 @@ func TestNilPacket(t *testing.T) { // Test for nil result kids := []*ber.Packet{ - &ber.Packet{}, // Unused - nil, // Can't be nil + {}, // Unused + nil, // Can't be nil } pack := &ber.Packet{Children: kids} code, _ = getLDAPResultCode(pack) @@ -25,5 +29,74 @@ func TestNilPacket(t *testing.T) { if code != ErrorUnexpectedResponse { t.Errorf("Should have an 'ErrorUnexpectedResponse' error in nil packets, got: %v", code) } +} + +// TestConnReadErr tests that an unexpected error reading from underlying +// connection bubbles up to the goroutine which makes a request. +func TestConnReadErr(t *testing.T) { + conn := &signalErrConn{ + signals: make(chan error), + } + + ldapConn := NewConn(conn, false) + ldapConn.Start() + + // Make a dummy search request. + searchReq := NewSearchRequest("dc=example,dc=com", ScopeWholeSubtree, DerefAlways, 0, 0, false, "(objectClass=*)", nil, nil) + + expectedError := errors.New("this is the error you are looking for") + + // Send the signal after a short amount of time. + time.AfterFunc(10*time.Millisecond, func() { conn.signals <- expectedError }) + + // This should block until the underlyiny conn gets the error signal + // which should bubble up through the reader() goroutine, close the + // connection, and + _, err := ldapConn.Search(searchReq) + if err == nil || !strings.Contains(err.Error(), expectedError.Error()) { + t.Errorf("not the expected error: %s", err) + } +} + +// signalErrConn is a helful type used with TestConnReadErr. It implements the +// net.Conn interface to be used as a connection for the test. Most methods are +// no-ops but the Read() method blocks until it receives a signal which it +// returns as an error. +type signalErrConn struct { + signals chan error +} + +// Read blocks until an error is sent on the internal signals channel. That +// error is returned. +func (c *signalErrConn) Read(b []byte) (n int, err error) { + return 0, <-c.signals +} + +func (c *signalErrConn) Write(b []byte) (n int, err error) { + return len(b), nil +} + +func (c *signalErrConn) Close() error { + close(c.signals) + return nil +} + +func (c *signalErrConn) LocalAddr() net.Addr { + return (*net.TCPAddr)(nil) +} + +func (c *signalErrConn) RemoteAddr() net.Addr { + return (*net.TCPAddr)(nil) +} + +func (c *signalErrConn) SetDeadline(t time.Time) error { + return nil +} + +func (c *signalErrConn) SetReadDeadline(t time.Time) error { + return nil +} +func (c *signalErrConn) SetWriteDeadline(t time.Time) error { + return nil } diff --git a/vendor/github.com/go-ldap/ldap/filter.go b/vendor/github.com/go-ldap/ldap/filter.go index 63bcec1e3..7eae310f1 100644 --- a/vendor/github.com/go-ldap/ldap/filter.go +++ b/vendor/github.com/go-ldap/ldap/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 '('")) @@ -81,6 +88,7 @@ func CompileFilter(filter string) (*ber.Packet, error) { 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 +247,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 +271,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 +329,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 +343,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 +379,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 +411,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 +450,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/github.com/go-ldap/ldap/ldap.go b/vendor/github.com/go-ldap/ldap/ldap.go index 1620aaea6..90018be83 100644 --- a/vendor/github.com/go-ldap/ldap/ldap.go +++ b/vendor/github.com/go-ldap/ldap/ldap.go @@ -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", @@ -237,6 +239,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/github.com/go-ldap/ldap/modify.go b/vendor/github.com/go-ldap/ldap/modify.go index 5c042af79..e4ab6cefc 100644 --- a/vendor/github.com/go-ldap/ldap/modify.go +++ b/vendor/github.com/go-ldap/ldap/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/github.com/go-ldap/ldap/passwdmodify.go b/vendor/github.com/go-ldap/ldap/passwdmodify.go index 6d5ca975a..26110ccf4 100644 --- a/vendor/github.com/go-ldap/ldap/passwdmodify.go +++ b/vendor/github.com/go-ldap/ldap/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 } diff --git a/vendor/github.com/go-ldap/ldap/search.go b/vendor/github.com/go-ldap/ldap/search.go index 7e9495bdc..2a99894c9 100644 --- a/vendor/github.com/go-ldap/ldap/search.go +++ b/vendor/github.com/go-ldap/ldap/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 } -- cgit v1.2.3-1-g7c22