From 22770c324dc46412241ec833e608134df8f47245 Mon Sep 17 00:00:00 2001 From: zeripath Date: Mon, 18 Feb 2019 12:34:37 +0000 Subject: [PATCH] Move to ldap.v3 to fix #5928 (#6105) Signed-off-by: Andrew Thornton --- Gopkg.lock | 10 +- Gopkg.toml | 4 +- modules/auth/ldap/ldap.go | 4 +- vendor/gopkg.in/ldap.v2/atomic_value.go | 13 - vendor/gopkg.in/ldap.v2/atomic_value_go13.go | 28 --- vendor/gopkg.in/ldap.v2/error.go | 155 ------------ vendor/gopkg.in/{ldap.v2 => ldap.v3}/LICENSE | 0 vendor/gopkg.in/{ldap.v2 => ldap.v3}/add.go | 16 +- vendor/gopkg.in/{ldap.v2 => ldap.v3}/bind.go | 104 ++++---- .../gopkg.in/{ldap.v2 => ldap.v3}/client.go | 1 + .../gopkg.in/{ldap.v2 => ldap.v3}/compare.go | 20 +- vendor/gopkg.in/{ldap.v2 => ldap.v3}/conn.go | 98 ++++++-- .../gopkg.in/{ldap.v2 => ldap.v3}/control.go | 119 +++++++-- vendor/gopkg.in/{ldap.v2 => ldap.v3}/debug.go | 0 vendor/gopkg.in/{ldap.v2 => ldap.v3}/del.go | 8 +- vendor/gopkg.in/{ldap.v2 => ldap.v3}/dn.go | 30 +-- vendor/gopkg.in/{ldap.v2 => ldap.v3}/doc.go | 0 vendor/gopkg.in/ldap.v3/error.go | 234 ++++++++++++++++++ .../gopkg.in/{ldap.v2 => ldap.v3}/filter.go | 4 - vendor/gopkg.in/{ldap.v2 => ldap.v3}/ldap.go | 86 ++++--- vendor/gopkg.in/ldap.v3/moddn.go | 104 ++++++++ .../gopkg.in/{ldap.v2 => ldap.v3}/modify.go | 77 +++--- .../{ldap.v2 => ldap.v3}/passwdmodify.go | 17 +- .../gopkg.in/{ldap.v2 => ldap.v3}/search.go | 22 +- 24 files changed, 721 insertions(+), 433 deletions(-) delete mode 100644 vendor/gopkg.in/ldap.v2/atomic_value.go delete mode 100644 vendor/gopkg.in/ldap.v2/atomic_value_go13.go delete mode 100644 vendor/gopkg.in/ldap.v2/error.go rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/LICENSE (100%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/add.go (90%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/bind.go (54%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/client.go (93%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/compare.go (80%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/conn.go (86%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/control.go (75%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/debug.go (100%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/del.go (91%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/dn.go (91%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/doc.go (100%) create mode 100644 vendor/gopkg.in/ldap.v3/error.go rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/filter.go (98%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/ldap.go (82%) create mode 100644 vendor/gopkg.in/ldap.v3/moddn.go rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/modify.go (63%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/passwdmodify.go (92%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/search.go (96%) diff --git a/Gopkg.lock b/Gopkg.lock index 0fe028a5e..fa2a58a1a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1094,12 +1094,12 @@ version = "v1.31.1" [[projects]] - digest = "1:7e1c00b9959544fa1ccca7cf0407a5b29ac6d5201059c4fac6f599cb99bfd24d" - name = "gopkg.in/ldap.v2" + digest = "1:8a502dedecf5b6d56e36f0d0e6196392baf616634af2c23108b6e8bb89ec57fc" + name = "gopkg.in/ldap.v3" packages = ["."] pruneopts = "NUT" - revision = "bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9" - version = "v2.5.1" + revision = "214f299a0ecb2a6c6f6d2b0f13977032b207dc58" + version = "v3.0.1" [[projects]] digest = "1:de2e7294c9bd0b7d07ada8e98ad02cbbaabacff90eedebe7454ebdbab50d0d19" @@ -1309,7 +1309,7 @@ "gopkg.in/editorconfig/editorconfig-core-go.v1", "gopkg.in/gomail.v2", "gopkg.in/ini.v1", - "gopkg.in/ldap.v2", + "gopkg.in/ldap.v3", "gopkg.in/macaron.v1", "gopkg.in/testfixtures.v2", "strk.kbt.io/projects/go/libravatar", diff --git a/Gopkg.toml b/Gopkg.toml index 94f15079b..3a981f529 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -95,8 +95,8 @@ ignored = ["google.golang.org/appengine*"] version = "1.31.1" [[constraint]] - name = "gopkg.in/ldap.v2" - version = "2.4.1" + name = "gopkg.in/ldap.v3" + version = "3.0.1" [[constraint]] name = "gopkg.in/macaron.v1" diff --git a/modules/auth/ldap/ldap.go b/modules/auth/ldap/ldap.go index c68af2540..f202f9408 100644 --- a/modules/auth/ldap/ldap.go +++ b/modules/auth/ldap/ldap.go @@ -11,9 +11,9 @@ import ( "fmt" "strings" - "gopkg.in/ldap.v2" - "code.gitea.io/gitea/modules/log" + + ldap "gopkg.in/ldap.v3" ) // SecurityProtocol protocol type diff --git a/vendor/gopkg.in/ldap.v2/atomic_value.go b/vendor/gopkg.in/ldap.v2/atomic_value.go deleted file mode 100644 index bccf7573e..000000000 --- a/vendor/gopkg.in/ldap.v2/atomic_value.go +++ /dev/null @@ -1,13 +0,0 @@ -// +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 deleted file mode 100644 index 04920bb26..000000000 --- a/vendor/gopkg.in/ldap.v2/atomic_value_go13.go +++ /dev/null @@ -1,28 +0,0 @@ -// +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/error.go b/vendor/gopkg.in/ldap.v2/error.go deleted file mode 100644 index 4cccb537f..000000000 --- a/vendor/gopkg.in/ldap.v2/error.go +++ /dev/null @@ -1,155 +0,0 @@ -package ldap - -import ( - "fmt" - - "gopkg.in/asn1-ber.v1" -) - -// LDAP Result Codes -const ( - LDAPResultSuccess = 0 - LDAPResultOperationsError = 1 - LDAPResultProtocolError = 2 - LDAPResultTimeLimitExceeded = 3 - LDAPResultSizeLimitExceeded = 4 - LDAPResultCompareFalse = 5 - LDAPResultCompareTrue = 6 - LDAPResultAuthMethodNotSupported = 7 - LDAPResultStrongAuthRequired = 8 - LDAPResultReferral = 10 - LDAPResultAdminLimitExceeded = 11 - LDAPResultUnavailableCriticalExtension = 12 - LDAPResultConfidentialityRequired = 13 - LDAPResultSaslBindInProgress = 14 - LDAPResultNoSuchAttribute = 16 - LDAPResultUndefinedAttributeType = 17 - LDAPResultInappropriateMatching = 18 - LDAPResultConstraintViolation = 19 - LDAPResultAttributeOrValueExists = 20 - LDAPResultInvalidAttributeSyntax = 21 - LDAPResultNoSuchObject = 32 - LDAPResultAliasProblem = 33 - LDAPResultInvalidDNSyntax = 34 - LDAPResultAliasDereferencingProblem = 36 - LDAPResultInappropriateAuthentication = 48 - LDAPResultInvalidCredentials = 49 - LDAPResultInsufficientAccessRights = 50 - LDAPResultBusy = 51 - LDAPResultUnavailable = 52 - LDAPResultUnwillingToPerform = 53 - LDAPResultLoopDetect = 54 - LDAPResultNamingViolation = 64 - LDAPResultObjectClassViolation = 65 - LDAPResultNotAllowedOnNonLeaf = 66 - LDAPResultNotAllowedOnRDN = 67 - LDAPResultEntryAlreadyExists = 68 - LDAPResultObjectClassModsProhibited = 69 - LDAPResultAffectsMultipleDSAs = 71 - LDAPResultOther = 80 - - ErrorNetwork = 200 - ErrorFilterCompile = 201 - ErrorFilterDecompile = 202 - ErrorDebugging = 203 - ErrorUnexpectedMessage = 204 - ErrorUnexpectedResponse = 205 -) - -// LDAPResultCodeMap contains string descriptions for LDAP error codes -var LDAPResultCodeMap = map[uint8]string{ - LDAPResultSuccess: "Success", - LDAPResultOperationsError: "Operations Error", - LDAPResultProtocolError: "Protocol Error", - LDAPResultTimeLimitExceeded: "Time Limit Exceeded", - LDAPResultSizeLimitExceeded: "Size Limit Exceeded", - LDAPResultCompareFalse: "Compare False", - LDAPResultCompareTrue: "Compare True", - LDAPResultAuthMethodNotSupported: "Auth Method Not Supported", - LDAPResultStrongAuthRequired: "Strong Auth Required", - LDAPResultReferral: "Referral", - LDAPResultAdminLimitExceeded: "Admin Limit Exceeded", - LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension", - LDAPResultConfidentialityRequired: "Confidentiality Required", - LDAPResultSaslBindInProgress: "Sasl Bind In Progress", - LDAPResultNoSuchAttribute: "No Such Attribute", - LDAPResultUndefinedAttributeType: "Undefined Attribute Type", - LDAPResultInappropriateMatching: "Inappropriate Matching", - LDAPResultConstraintViolation: "Constraint Violation", - LDAPResultAttributeOrValueExists: "Attribute Or Value Exists", - LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax", - LDAPResultNoSuchObject: "No Such Object", - LDAPResultAliasProblem: "Alias Problem", - LDAPResultInvalidDNSyntax: "Invalid DN Syntax", - LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem", - LDAPResultInappropriateAuthentication: "Inappropriate Authentication", - LDAPResultInvalidCredentials: "Invalid Credentials", - LDAPResultInsufficientAccessRights: "Insufficient Access Rights", - LDAPResultBusy: "Busy", - LDAPResultUnavailable: "Unavailable", - LDAPResultUnwillingToPerform: "Unwilling To Perform", - LDAPResultLoopDetect: "Loop Detect", - LDAPResultNamingViolation: "Naming Violation", - LDAPResultObjectClassViolation: "Object Class Violation", - LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf", - LDAPResultNotAllowedOnRDN: "Not Allowed On RDN", - LDAPResultEntryAlreadyExists: "Entry Already Exists", - 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) { - if packet == nil { - return ErrorUnexpectedResponse, "Empty packet" - } else if len(packet.Children) >= 2 { - response := packet.Children[1] - if response == nil { - return ErrorUnexpectedResponse, "Empty response in packet" - } - if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 { - // Children[1].Children[2] is the diagnosticMessage which is guaranteed to exist as seen here: https://tools.ietf.org/html/rfc4511#section-4.1.9 - return uint8(response.Children[0].Value.(int64)), response.Children[2].Value.(string) - } - } - - return ErrorNetwork, "Invalid packet format" -} - -// Error holds LDAP error information -type Error struct { - // Err is the underlying error - Err error - // ResultCode is the LDAP error code - ResultCode uint8 -} - -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 - } - - serverError, ok := err.(*Error) - if !ok { - return false - } - - return serverError.ResultCode == desiredResultCode -} diff --git a/vendor/gopkg.in/ldap.v2/LICENSE b/vendor/gopkg.in/ldap.v3/LICENSE similarity index 100% rename from vendor/gopkg.in/ldap.v2/LICENSE rename to vendor/gopkg.in/ldap.v3/LICENSE diff --git a/vendor/gopkg.in/ldap.v2/add.go b/vendor/gopkg.in/ldap.v3/add.go similarity index 90% rename from vendor/gopkg.in/ldap.v2/add.go rename to vendor/gopkg.in/ldap.v3/add.go index 0e5f6cdba..19bce1b75 100644 --- a/vendor/gopkg.in/ldap.v2/add.go +++ b/vendor/gopkg.in/ldap.v3/add.go @@ -41,6 +41,8 @@ type AddRequest struct { DN string // Attributes list the attributes of the new entry Attributes []Attribute + // Controls hold optional controls to send with the request + Controls []Control } func (a AddRequest) encode() *ber.Packet { @@ -60,9 +62,10 @@ func (a *AddRequest) Attribute(attrType string, attrVals []string) { } // NewAddRequest returns an AddRequest for the given DN, with no attributes -func NewAddRequest(dn string) *AddRequest { +func NewAddRequest(dn string, controls []Control) *AddRequest { return &AddRequest{ - DN: dn, + DN: dn, + Controls: controls, } } @@ -72,6 +75,9 @@ func (l *Conn) Add(addRequest *AddRequest) error { packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) packet.AppendChild(addRequest.encode()) + if len(addRequest.Controls) > 0 { + packet.AppendChild(encodeControls(addRequest.Controls)) + } l.Debug.PrintPacket(packet) @@ -100,9 +106,9 @@ func (l *Conn) Add(addRequest *AddRequest) error { } if packet.Children[1].Tag == ApplicationAddResponse { - resultCode, resultDescription := getLDAPResultCode(packet) - if resultCode != 0 { - return NewError(resultCode, errors.New(resultDescription)) + err := GetLDAPError(packet) + if err != nil { + return err } } else { log.Printf("Unexpected Response: %d", packet.Children[1].Tag) diff --git a/vendor/gopkg.in/ldap.v2/bind.go b/vendor/gopkg.in/ldap.v3/bind.go similarity index 54% rename from vendor/gopkg.in/ldap.v2/bind.go rename to vendor/gopkg.in/ldap.v3/bind.go index 26b3cc727..59c3f5ef5 100644 --- a/vendor/gopkg.in/ldap.v2/bind.go +++ b/vendor/gopkg.in/ldap.v3/bind.go @@ -1,11 +1,8 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package ldap import ( "errors" + "fmt" "gopkg.in/asn1-ber.v1" ) @@ -18,6 +15,9 @@ type SimpleBindRequest struct { Password string // Controls are optional controls to send with the bind request Controls []Control + // AllowEmptyPassword sets whether the client allows binding with an empty password + // (normally used for unauthenticated bind). + AllowEmptyPassword bool } // SimpleBindResult contains the response from the server @@ -28,9 +28,10 @@ type SimpleBindResult struct { // NewSimpleBindRequest returns a bind request func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest { return &SimpleBindRequest{ - Username: username, - Password: password, - Controls: controls, + Username: username, + Password: password, + Controls: controls, + AllowEmptyPassword: false, } } @@ -40,17 +41,22 @@ func (bindRequest *SimpleBindRequest) encode() *ber.Packet { request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, bindRequest.Username, "User Name")) request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, bindRequest.Password, "Password")) - request.AppendChild(encodeControls(bindRequest.Controls)) - return request } // SimpleBind performs the simple bind operation defined in the given request func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) { + if simpleBindRequest.Password == "" && !simpleBindRequest.AllowEmptyPassword { + return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client")) + } + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) encodedBindRequest := simpleBindRequest.encode() packet.AppendChild(encodedBindRequest) + if len(simpleBindRequest.Controls) > 0 { + packet.AppendChild(encodeControls(simpleBindRequest.Controls)) + } if l.Debug { ber.PrintPacket(packet) @@ -73,7 +79,7 @@ func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResu } if l.Debug { - if err := addLDAPDescriptions(packet); err != nil { + if err = addLDAPDescriptions(packet); err != nil { return nil, err } ber.PrintPacket(packet) @@ -85,59 +91,45 @@ func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResu if len(packet.Children) == 3 { for _, child := range packet.Children[2].Children { - result.Controls = append(result.Controls, DecodeControl(child)) + decodedChild, decodeErr := DecodeControl(child) + if decodeErr != nil { + return nil, fmt.Errorf("failed to decode child control: %s", decodeErr) + } + result.Controls = append(result.Controls, decodedChild) } } - resultCode, resultDescription := getLDAPResultCode(packet) - if resultCode != 0 { - return result, NewError(resultCode, errors.New(resultDescription)) - } - - return result, nil + err = GetLDAPError(packet) + return result, err } -// Bind performs a bind with the given username and password +// Bind performs a bind with the given username and password. +// +// It does not allow unauthenticated bind (i.e. empty password). Use the UnauthenticatedBind method +// for that. func (l *Conn) Bind(username, password string) error { - packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") - packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) - 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")) - bindRequest.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, password, "Password")) - packet.AppendChild(bindRequest) - - if l.Debug { - ber.PrintPacket(packet) - } - - msgCtx, err := l.sendMessage(packet) - if err != nil { - return err - } - defer l.finishMessage(msgCtx) - - packetResponse, ok := <-msgCtx.responses - if !ok { - return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) - } - packet, err = packetResponse.ReadPacket() - l.Debug.Printf("%d: got response %p", msgCtx.id, packet) - if err != nil { - return err - } - - if l.Debug { - if err := addLDAPDescriptions(packet); err != nil { - return err - } - ber.PrintPacket(packet) + req := &SimpleBindRequest{ + Username: username, + Password: password, + AllowEmptyPassword: false, } + _, err := l.SimpleBind(req) + return err +} - resultCode, resultDescription := getLDAPResultCode(packet) - if resultCode != 0 { - return NewError(resultCode, errors.New(resultDescription)) +// UnauthenticatedBind performs an unauthenticated bind. +// +// A username may be provided for trace (e.g. logging) purpose only, but it is normally not +// authenticated or otherwise validated by the LDAP server. +// +// See https://tools.ietf.org/html/rfc4513#section-5.1.2 . +// See https://tools.ietf.org/html/rfc4513#section-6.3.1 . +func (l *Conn) UnauthenticatedBind(username string) error { + req := &SimpleBindRequest{ + Username: username, + Password: "", + AllowEmptyPassword: true, } - - return nil + _, err := l.SimpleBind(req) + return err } diff --git a/vendor/gopkg.in/ldap.v2/client.go b/vendor/gopkg.in/ldap.v3/client.go similarity index 93% rename from vendor/gopkg.in/ldap.v2/client.go rename to vendor/gopkg.in/ldap.v3/client.go index 055b27b5f..c7f41f6f9 100644 --- a/vendor/gopkg.in/ldap.v2/client.go +++ b/vendor/gopkg.in/ldap.v3/client.go @@ -18,6 +18,7 @@ type Client interface { Add(addRequest *AddRequest) error Del(delRequest *DelRequest) error Modify(modifyRequest *ModifyRequest) error + ModifyDN(modifyDNRequest *ModifyDNRequest) error Compare(dn, attribute, value string) (bool, error) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) diff --git a/vendor/gopkg.in/ldap.v2/compare.go b/vendor/gopkg.in/ldap.v3/compare.go similarity index 80% rename from vendor/gopkg.in/ldap.v2/compare.go rename to vendor/gopkg.in/ldap.v3/compare.go index cc6d2af5e..5b5013cbe 100644 --- a/vendor/gopkg.in/ldap.v2/compare.go +++ b/vendor/gopkg.in/ldap.v3/compare.go @@ -1,7 +1,3 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// // File contains Compare functionality // // https://tools.ietf.org/html/rfc4511 @@ -41,7 +37,7 @@ func (l *Conn) Compare(dn, attribute, value string) (bool, error) { ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion") ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "AttributeDesc")) - ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagOctetString, value, "AssertionValue")) + ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "AssertionValue")) request.AppendChild(ava) packet.AppendChild(request) @@ -72,14 +68,16 @@ func (l *Conn) Compare(dn, attribute, value string) (bool, error) { } if packet.Children[1].Tag == ApplicationCompareResponse { - resultCode, resultDescription := getLDAPResultCode(packet) - if resultCode == LDAPResultCompareTrue { + err := GetLDAPError(packet) + + switch { + case IsErrorWithCode(err, LDAPResultCompareTrue): return true, nil - } else if resultCode == LDAPResultCompareFalse { + case IsErrorWithCode(err, LDAPResultCompareFalse): return false, nil - } else { - return false, NewError(resultCode, errors.New(resultDescription)) + default: + return false, err } } - return false, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag) + return false, fmt.Errorf("unexpected Response: %d", packet.Children[1].Tag) } diff --git a/vendor/gopkg.in/ldap.v2/conn.go b/vendor/gopkg.in/ldap.v3/conn.go similarity index 86% rename from vendor/gopkg.in/ldap.v2/conn.go rename to vendor/gopkg.in/ldap.v3/conn.go index eb28eb472..c20471fc2 100644 --- a/vendor/gopkg.in/ldap.v2/conn.go +++ b/vendor/gopkg.in/ldap.v3/conn.go @@ -1,7 +1,3 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package ldap import ( @@ -10,6 +6,7 @@ import ( "fmt" "log" "net" + "net/url" "sync" "sync/atomic" "time" @@ -30,6 +27,13 @@ const ( MessageTimeout = 4 ) +const ( + // DefaultLdapPort default ldap port for pure TCP connection + DefaultLdapPort = "389" + // DefaultLdapsPort default ldap port for SSL connection + DefaultLdapsPort = "636" +) + // PacketResponse contains the packet or error encountered reading a response type PacketResponse struct { // Packet is the packet read from the server @@ -81,10 +85,13 @@ const ( // Conn represents an LDAP Connection type Conn struct { + // requestTimeout is loaded atomically + // so we need to ensure 64-bit alignment on 32-bit platforms. + requestTimeout int64 conn net.Conn isTLS bool closing uint32 - closeErr atomicValue + closeErr atomic.Value isStartingTLS bool Debug debugging chanConfirm chan struct{} @@ -94,7 +101,6 @@ type Conn struct { wgClose sync.WaitGroup outstandingRequests uint messageMutex sync.Mutex - requestTimeout int64 } var _ Client = &Conn{} @@ -121,22 +127,51 @@ func Dial(network, addr string) (*Conn, error) { // DialTLS connects to the given address on the given network using tls.Dial // and then returns a new Conn for the connection. func DialTLS(network, addr string, config *tls.Config) (*Conn, error) { - dc, err := net.DialTimeout(network, addr, DefaultTimeout) + c, err := tls.DialWithDialer(&net.Dialer{Timeout: DefaultTimeout}, network, addr, config) if err != nil { return nil, NewError(ErrorNetwork, err) } - c := tls.Client(dc, config) - err = c.Handshake() - if err != nil { - // Handshake error, close the established connection before we return an error - dc.Close() - return nil, NewError(ErrorNetwork, err) - } conn := NewConn(c, true) conn.Start() return conn, nil } +// DialURL connects to the given ldap URL vie TCP using tls.Dial or net.Dial if ldaps:// +// or ldap:// specified as protocol. On success a new Conn for the connection +// is returned. +func DialURL(addr string) (*Conn, error) { + + lurl, err := url.Parse(addr) + if err != nil { + return nil, NewError(ErrorNetwork, err) + } + + host, port, err := net.SplitHostPort(lurl.Host) + if err != nil { + // we asume that error is due to missing port + host = lurl.Host + port = "" + } + + switch lurl.Scheme { + case "ldap": + if port == "" { + port = DefaultLdapPort + } + return Dial("tcp", net.JoinHostPort(host, port)) + case "ldaps": + if port == "" { + port = DefaultLdapsPort + } + tlsConf := &tls.Config{ + ServerName: host, + } + return DialTLS("tcp", net.JoinHostPort(host, port), tlsConf) + } + + return nil, NewError(ErrorNetwork, fmt.Errorf("Unknown scheme '%s'", lurl.Scheme)) +} + // NewConn returns a new Conn using conn for network I/O. func NewConn(conn net.Conn, isTLS bool) *Conn { return &Conn{ @@ -157,8 +192,8 @@ func (l *Conn) Start() { l.wgClose.Add(1) } -// isClosing returns whether or not we're currently closing. -func (l *Conn) isClosing() bool { +// IsClosing returns whether or not we're currently closing. +func (l *Conn) IsClosing() bool { return atomic.LoadUint32(&l.closing) == 1 } @@ -242,30 +277,41 @@ func (l *Conn) StartTLS(config *tls.Config) error { ber.PrintPacket(packet) } - if resultCode, message := getLDAPResultCode(packet); resultCode == LDAPResultSuccess { + if err := GetLDAPError(packet); err == nil { conn := tls.Client(l.conn, config) - if err := conn.Handshake(); err != nil { + if connErr := conn.Handshake(); connErr != nil { l.Close() - return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", err)) + return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", connErr)) } l.isTLS = true l.conn = conn } else { - return NewError(resultCode, fmt.Errorf("ldap: cannot StartTLS (%s)", message)) + return err } go l.reader() return nil } +// TLSConnectionState returns the client's TLS connection state. +// The return values are their zero values if StartTLS did +// not succeed. +func (l *Conn) TLSConnectionState() (state tls.ConnectionState, ok bool) { + tc, ok := l.conn.(*tls.Conn) + if !ok { + return + } + return tc.ConnectionState(), true +} + func (l *Conn) sendMessage(packet *ber.Packet) (*messageContext, error) { return l.sendMessageWithFlags(packet, 0) } func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (*messageContext, error) { - if l.isClosing() { + if l.IsClosing() { return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed")) } l.messageMutex.Lock() @@ -304,7 +350,7 @@ func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) func (l *Conn) finishMessage(msgCtx *messageContext) { close(msgCtx.done) - if l.isClosing() { + if l.IsClosing() { return } @@ -325,7 +371,7 @@ func (l *Conn) finishMessage(msgCtx *messageContext) { func (l *Conn) sendProcessMessage(message *messagePacket) bool { l.messageMutex.Lock() defer l.messageMutex.Unlock() - if l.isClosing() { + if l.IsClosing() { return false } l.chanMessage <- message @@ -340,7 +386,7 @@ func (l *Conn) processMessages() { 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 { + if l.IsClosing() && l.closeErr.Load() != nil { msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)}) } l.Debug.Printf("Closing channel for MessageID %d", messageID) @@ -400,7 +446,7 @@ func (l *Conn) processMessages() { 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: @@ -442,7 +488,7 @@ 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()) } diff --git a/vendor/gopkg.in/ldap.v2/control.go b/vendor/gopkg.in/ldap.v3/control.go similarity index 75% rename from vendor/gopkg.in/ldap.v2/control.go rename to vendor/gopkg.in/ldap.v3/control.go index 342f325ca..4439a865d 100644 --- a/vendor/gopkg.in/ldap.v2/control.go +++ b/vendor/gopkg.in/ldap.v3/control.go @@ -1,7 +1,3 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package ldap import ( @@ -22,13 +18,20 @@ const ( ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5" // ControlTypeManageDsaIT - https://tools.ietf.org/html/rfc3296 ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2" + + // ControlTypeMicrosoftNotification - https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx + ControlTypeMicrosoftNotification = "1.2.840.113556.1.4.528" + // ControlTypeMicrosoftShowDeleted - https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx + ControlTypeMicrosoftShowDeleted = "1.2.840.113556.1.4.417" ) // ControlTypeMap maps controls to text descriptions var ControlTypeMap = map[string]string{ - ControlTypePaging: "Paging", - ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft", - ControlTypeManageDsaIT: "Manage DSA IT", + ControlTypePaging: "Paging", + ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft", + ControlTypeManageDsaIT: "Manage DSA IT", + ControlTypeMicrosoftNotification: "Change Notification - Microsoft", + ControlTypeMicrosoftShowDeleted: "Show Deleted Objects - Microsoft", } // Control defines an interface controls provide to encode and describe themselves @@ -242,6 +245,64 @@ func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT { return &ControlManageDsaIT{Criticality: Criticality} } +// ControlMicrosoftNotification implements the control described in https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx +type ControlMicrosoftNotification struct{} + +// GetControlType returns the OID +func (c *ControlMicrosoftNotification) GetControlType() string { + return ControlTypeMicrosoftNotification +} + +// Encode returns the ber packet representation +func (c *ControlMicrosoftNotification) Encode() *ber.Packet { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftNotification, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftNotification]+")")) + + return packet +} + +// String returns a human-readable description +func (c *ControlMicrosoftNotification) String() string { + return fmt.Sprintf( + "Control Type: %s (%q)", + ControlTypeMap[ControlTypeMicrosoftNotification], + ControlTypeMicrosoftNotification) +} + +// NewControlMicrosoftNotification returns a ControlMicrosoftNotification control +func NewControlMicrosoftNotification() *ControlMicrosoftNotification { + return &ControlMicrosoftNotification{} +} + +// ControlMicrosoftShowDeleted implements the control described in https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx +type ControlMicrosoftShowDeleted struct{} + +// GetControlType returns the OID +func (c *ControlMicrosoftShowDeleted) GetControlType() string { + return ControlTypeMicrosoftShowDeleted +} + +// Encode returns the ber packet representation +func (c *ControlMicrosoftShowDeleted) Encode() *ber.Packet { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftShowDeleted, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftShowDeleted]+")")) + + return packet +} + +// String returns a human-readable description +func (c *ControlMicrosoftShowDeleted) String() string { + return fmt.Sprintf( + "Control Type: %s (%q)", + ControlTypeMap[ControlTypeMicrosoftShowDeleted], + ControlTypeMicrosoftShowDeleted) +} + +// NewControlMicrosoftShowDeleted returns a ControlMicrosoftShowDeleted control +func NewControlMicrosoftShowDeleted() *ControlMicrosoftShowDeleted { + return &ControlMicrosoftShowDeleted{} +} + // FindControl returns the first control of the given type in the list, or nil func FindControl(controls []Control, controlType string) Control { for _, c := range controls { @@ -253,7 +314,7 @@ func FindControl(controls []Control, controlType string) Control { } // DecodeControl returns a control read from the given packet, or nil if no recognized control can be made -func DecodeControl(packet *ber.Packet) Control { +func DecodeControl(packet *ber.Packet) (Control, error) { var ( ControlType = "" Criticality = false @@ -263,7 +324,7 @@ func DecodeControl(packet *ber.Packet) Control { switch len(packet.Children) { case 0: // at least one child is required for control type - return nil + return nil, fmt.Errorf("at least one child is required for control type") case 1: // just type, no criticality or value @@ -296,17 +357,20 @@ func DecodeControl(packet *ber.Packet) Control { default: // more than 3 children is invalid - return nil + return nil, fmt.Errorf("more than 3 children is invalid for controls") } switch ControlType { case ControlTypeManageDsaIT: - return NewControlManageDsaIT(Criticality) + return NewControlManageDsaIT(Criticality), nil case ControlTypePaging: value.Description += " (Paging)" c := new(ControlPaging) if value.Value != nil { - valueChildren := ber.DecodePacket(value.Data.Bytes()) + valueChildren, err := ber.DecodePacketErr(value.Data.Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to decode data bytes: %s", err) + } value.Data.Truncate(0) value.Value = nil value.AppendChild(valueChildren) @@ -318,12 +382,15 @@ func DecodeControl(packet *ber.Packet) Control { c.PagingSize = uint32(value.Children[0].Value.(int64)) c.Cookie = value.Children[1].Data.Bytes() value.Children[1].Value = c.Cookie - return c + return c, nil case ControlTypeBeheraPasswordPolicy: value.Description += " (Password Policy - Behera)" c := NewControlBeheraPasswordPolicy() if value.Value != nil { - valueChildren := ber.DecodePacket(value.Data.Bytes()) + valueChildren, err := ber.DecodePacketErr(value.Data.Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to decode data bytes: %s", err) + } value.Data.Truncate(0) value.Value = nil value.AppendChild(valueChildren) @@ -335,7 +402,10 @@ func DecodeControl(packet *ber.Packet) Control { if child.Tag == 0 { //Warning warningPacket := child.Children[0] - packet := ber.DecodePacket(warningPacket.Data.Bytes()) + packet, err := ber.DecodePacketErr(warningPacket.Data.Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to decode data bytes: %s", err) + } val, ok := packet.Value.(int64) if ok { if warningPacket.Tag == 0 { @@ -350,7 +420,10 @@ func DecodeControl(packet *ber.Packet) Control { } } else if child.Tag == 1 { // Error - packet := ber.DecodePacket(child.Data.Bytes()) + packet, err := ber.DecodePacketErr(child.Data.Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to decode data bytes: %s", err) + } val, ok := packet.Value.(int8) if !ok { // what to do? @@ -361,22 +434,26 @@ func DecodeControl(packet *ber.Packet) Control { c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error] } } - return c + return c, nil case ControlTypeVChuPasswordMustChange: c := &ControlVChuPasswordMustChange{MustChange: true} - return c + return c, nil case ControlTypeVChuPasswordWarning: c := &ControlVChuPasswordWarning{Expire: -1} expireStr := ber.DecodeString(value.Data.Bytes()) expire, err := strconv.ParseInt(expireStr, 10, 64) if err != nil { - return nil + return nil, fmt.Errorf("failed to parse value as int: %s", err) } c.Expire = expire value.Value = c.Expire - return c + return c, nil + case ControlTypeMicrosoftNotification: + return NewControlMicrosoftNotification(), nil + case ControlTypeMicrosoftShowDeleted: + return NewControlMicrosoftShowDeleted(), nil default: c := new(ControlString) c.ControlType = ControlType @@ -384,7 +461,7 @@ func DecodeControl(packet *ber.Packet) Control { if value != nil { c.ControlValue = value.Value.(string) } - return c + return c, nil } } diff --git a/vendor/gopkg.in/ldap.v2/debug.go b/vendor/gopkg.in/ldap.v3/debug.go similarity index 100% rename from vendor/gopkg.in/ldap.v2/debug.go rename to vendor/gopkg.in/ldap.v3/debug.go diff --git a/vendor/gopkg.in/ldap.v2/del.go b/vendor/gopkg.in/ldap.v3/del.go similarity index 91% rename from vendor/gopkg.in/ldap.v2/del.go rename to vendor/gopkg.in/ldap.v3/del.go index 4fd63dc3f..6f78beb18 100644 --- a/vendor/gopkg.in/ldap.v2/del.go +++ b/vendor/gopkg.in/ldap.v3/del.go @@ -40,7 +40,7 @@ func (l *Conn) Del(delRequest *DelRequest) error { packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) packet.AppendChild(delRequest.encode()) - if delRequest.Controls != nil { + if len(delRequest.Controls) > 0 { packet.AppendChild(encodeControls(delRequest.Controls)) } @@ -71,9 +71,9 @@ func (l *Conn) Del(delRequest *DelRequest) error { } if packet.Children[1].Tag == ApplicationDelResponse { - resultCode, resultDescription := getLDAPResultCode(packet) - if resultCode != 0 { - return NewError(resultCode, errors.New(resultDescription)) + err := GetLDAPError(packet) + if err != nil { + return err } } else { log.Printf("Unexpected Response: %d", packet.Children[1].Tag) diff --git a/vendor/gopkg.in/ldap.v2/dn.go b/vendor/gopkg.in/ldap.v3/dn.go similarity index 91% rename from vendor/gopkg.in/ldap.v2/dn.go rename to vendor/gopkg.in/ldap.v3/dn.go index 34e9023af..f89e73a9a 100644 --- a/vendor/gopkg.in/ldap.v2/dn.go +++ b/vendor/gopkg.in/ldap.v3/dn.go @@ -1,7 +1,3 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// // File contains DN parsing functionality // // https://tools.ietf.org/html/rfc4514 @@ -94,7 +90,8 @@ func ParseDN(str string) (*DN, error) { for i := 0; i < len(str); i++ { char := str[i] - if escaping { + switch { + case escaping: unescapedTrailingSpaces = 0 escaping = false switch char { @@ -104,22 +101,22 @@ func ParseDN(str string) (*DN, error) { } // Not a special character, assume hex encoded octet if len(str) == i+1 { - return nil, errors.New("Got corrupted escaped character") + return nil, errors.New("got corrupted escaped character") } dst := []byte{0} n, err := enchex.Decode([]byte(dst), []byte(str[i:i+2])) if err != nil { - return nil, fmt.Errorf("Failed to decode escaped character: %s", err) + return nil, fmt.Errorf("failed to decode escaped character: %s", err) } else if n != 1 { - return nil, fmt.Errorf("Expected 1 byte when un-escaping, got %d", n) + return nil, fmt.Errorf("expected 1 byte when un-escaping, got %d", n) } buffer.WriteByte(dst[0]) i++ - } else if char == '\\' { + case char == '\\': unescapedTrailingSpaces = 0 escaping = true - } else if char == '=' { + case char == '=': attribute.Type = stringFromBuffer() // Special case: If the first character in the value is # the // following data is BER encoded so we can just fast forward @@ -135,13 +132,16 @@ func ParseDN(str string) (*DN, error) { } rawBER, err := enchex.DecodeString(data) if err != nil { - return nil, fmt.Errorf("Failed to decode BER encoding: %s", err) + return nil, fmt.Errorf("failed to decode BER encoding: %s", err) + } + packet, err := ber.DecodePacketErr(rawBER) + if err != nil { + return nil, fmt.Errorf("failed to decode BER packet: %s", err) } - packet := ber.DecodePacket(rawBER) buffer.WriteString(packet.Data.String()) i += len(data) - 1 } - } else if char == ',' || char == '+' { + case char == ',' || char == '+': // We're done with this RDN or value, push it if len(attribute.Type) == 0 { return nil, errors.New("incomplete type, value pair") @@ -154,10 +154,10 @@ func ParseDN(str string) (*DN, error) { rdn = new(RelativeDN) rdn.Attributes = make([]*AttributeTypeAndValue, 0) } - } else if char == ' ' && buffer.Len() == 0 { + case char == ' ' && buffer.Len() == 0: // ignore unescaped leading spaces continue - } else { + default: if char == ' ' { // Track unescaped spaces in case they are trailing and we need to remove them unescapedTrailingSpaces++ diff --git a/vendor/gopkg.in/ldap.v2/doc.go b/vendor/gopkg.in/ldap.v3/doc.go similarity index 100% rename from vendor/gopkg.in/ldap.v2/doc.go rename to vendor/gopkg.in/ldap.v3/doc.go diff --git a/vendor/gopkg.in/ldap.v3/error.go b/vendor/gopkg.in/ldap.v3/error.go new file mode 100644 index 000000000..50ed8ab3f --- /dev/null +++ b/vendor/gopkg.in/ldap.v3/error.go @@ -0,0 +1,234 @@ +package ldap + +import ( + "fmt" + + "gopkg.in/asn1-ber.v1" +) + +// LDAP Result Codes +const ( + LDAPResultSuccess = 0 + LDAPResultOperationsError = 1 + LDAPResultProtocolError = 2 + LDAPResultTimeLimitExceeded = 3 + LDAPResultSizeLimitExceeded = 4 + LDAPResultCompareFalse = 5 + LDAPResultCompareTrue = 6 + LDAPResultAuthMethodNotSupported = 7 + LDAPResultStrongAuthRequired = 8 + LDAPResultReferral = 10 + LDAPResultAdminLimitExceeded = 11 + LDAPResultUnavailableCriticalExtension = 12 + LDAPResultConfidentialityRequired = 13 + LDAPResultSaslBindInProgress = 14 + LDAPResultNoSuchAttribute = 16 + LDAPResultUndefinedAttributeType = 17 + LDAPResultInappropriateMatching = 18 + LDAPResultConstraintViolation = 19 + LDAPResultAttributeOrValueExists = 20 + LDAPResultInvalidAttributeSyntax = 21 + LDAPResultNoSuchObject = 32 + LDAPResultAliasProblem = 33 + LDAPResultInvalidDNSyntax = 34 + LDAPResultIsLeaf = 35 + LDAPResultAliasDereferencingProblem = 36 + LDAPResultInappropriateAuthentication = 48 + LDAPResultInvalidCredentials = 49 + LDAPResultInsufficientAccessRights = 50 + LDAPResultBusy = 51 + LDAPResultUnavailable = 52 + LDAPResultUnwillingToPerform = 53 + LDAPResultLoopDetect = 54 + LDAPResultSortControlMissing = 60 + LDAPResultOffsetRangeError = 61 + LDAPResultNamingViolation = 64 + LDAPResultObjectClassViolation = 65 + LDAPResultNotAllowedOnNonLeaf = 66 + LDAPResultNotAllowedOnRDN = 67 + LDAPResultEntryAlreadyExists = 68 + LDAPResultObjectClassModsProhibited = 69 + LDAPResultResultsTooLarge = 70 + LDAPResultAffectsMultipleDSAs = 71 + LDAPResultVirtualListViewErrorOrControlError = 76 + LDAPResultOther = 80 + LDAPResultServerDown = 81 + LDAPResultLocalError = 82 + LDAPResultEncodingError = 83 + LDAPResultDecodingError = 84 + LDAPResultTimeout = 85 + LDAPResultAuthUnknown = 86 + LDAPResultFilterError = 87 + LDAPResultUserCanceled = 88 + LDAPResultParamError = 89 + LDAPResultNoMemory = 90 + LDAPResultConnectError = 91 + LDAPResultNotSupported = 92 + LDAPResultControlNotFound = 93 + LDAPResultNoResultsReturned = 94 + LDAPResultMoreResultsToReturn = 95 + LDAPResultClientLoop = 96 + LDAPResultReferralLimitExceeded = 97 + LDAPResultInvalidResponse = 100 + LDAPResultAmbiguousResponse = 101 + LDAPResultTLSNotSupported = 112 + LDAPResultIntermediateResponse = 113 + LDAPResultUnknownType = 114 + LDAPResultCanceled = 118 + LDAPResultNoSuchOperation = 119 + LDAPResultTooLate = 120 + LDAPResultCannotCancel = 121 + LDAPResultAssertionFailed = 122 + LDAPResultAuthorizationDenied = 123 + LDAPResultSyncRefreshRequired = 4096 + + ErrorNetwork = 200 + ErrorFilterCompile = 201 + ErrorFilterDecompile = 202 + ErrorDebugging = 203 + ErrorUnexpectedMessage = 204 + ErrorUnexpectedResponse = 205 + ErrorEmptyPassword = 206 +) + +// LDAPResultCodeMap contains string descriptions for LDAP error codes +var LDAPResultCodeMap = map[uint16]string{ + LDAPResultSuccess: "Success", + LDAPResultOperationsError: "Operations Error", + LDAPResultProtocolError: "Protocol Error", + LDAPResultTimeLimitExceeded: "Time Limit Exceeded", + LDAPResultSizeLimitExceeded: "Size Limit Exceeded", + LDAPResultCompareFalse: "Compare False", + LDAPResultCompareTrue: "Compare True", + LDAPResultAuthMethodNotSupported: "Auth Method Not Supported", + LDAPResultStrongAuthRequired: "Strong Auth Required", + LDAPResultReferral: "Referral", + LDAPResultAdminLimitExceeded: "Admin Limit Exceeded", + LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension", + LDAPResultConfidentialityRequired: "Confidentiality Required", + LDAPResultSaslBindInProgress: "Sasl Bind In Progress", + LDAPResultNoSuchAttribute: "No Such Attribute", + LDAPResultUndefinedAttributeType: "Undefined Attribute Type", + LDAPResultInappropriateMatching: "Inappropriate Matching", + LDAPResultConstraintViolation: "Constraint Violation", + LDAPResultAttributeOrValueExists: "Attribute Or Value Exists", + LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax", + LDAPResultNoSuchObject: "No Such Object", + LDAPResultAliasProblem: "Alias Problem", + LDAPResultInvalidDNSyntax: "Invalid DN Syntax", + LDAPResultIsLeaf: "Is Leaf", + LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem", + LDAPResultInappropriateAuthentication: "Inappropriate Authentication", + LDAPResultInvalidCredentials: "Invalid Credentials", + LDAPResultInsufficientAccessRights: "Insufficient Access Rights", + LDAPResultBusy: "Busy", + LDAPResultUnavailable: "Unavailable", + LDAPResultUnwillingToPerform: "Unwilling To Perform", + LDAPResultLoopDetect: "Loop Detect", + LDAPResultSortControlMissing: "Sort Control Missing", + LDAPResultOffsetRangeError: "Result Offset Range Error", + LDAPResultNamingViolation: "Naming Violation", + LDAPResultObjectClassViolation: "Object Class Violation", + LDAPResultResultsTooLarge: "Results Too Large", + LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf", + LDAPResultNotAllowedOnRDN: "Not Allowed On RDN", + LDAPResultEntryAlreadyExists: "Entry Already Exists", + LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited", + LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs", + LDAPResultVirtualListViewErrorOrControlError: "Failed because of a problem related to the virtual list view", + LDAPResultOther: "Other", + LDAPResultServerDown: "Cannot establish a connection", + LDAPResultLocalError: "An error occurred", + LDAPResultEncodingError: "LDAP encountered an error while encoding", + LDAPResultDecodingError: "LDAP encountered an error while decoding", + LDAPResultTimeout: "LDAP timeout while waiting for a response from the server", + LDAPResultAuthUnknown: "The auth method requested in a bind request is unknown", + LDAPResultFilterError: "An error occurred while encoding the given search filter", + LDAPResultUserCanceled: "The user canceled the operation", + LDAPResultParamError: "An invalid parameter was specified", + LDAPResultNoMemory: "Out of memory error", + LDAPResultConnectError: "A connection to the server could not be established", + LDAPResultNotSupported: "An attempt has been made to use a feature not supported LDAP", + LDAPResultControlNotFound: "The controls required to perform the requested operation were not found", + LDAPResultNoResultsReturned: "No results were returned from the server", + LDAPResultMoreResultsToReturn: "There are more results in the chain of results", + LDAPResultClientLoop: "A loop has been detected. For example when following referrals", + LDAPResultReferralLimitExceeded: "The referral hop limit has been exceeded", + LDAPResultCanceled: "Operation was canceled", + LDAPResultNoSuchOperation: "Server has no knowledge of the operation requested for cancellation", + LDAPResultTooLate: "Too late to cancel the outstanding operation", + LDAPResultCannotCancel: "The identified operation does not support cancellation or the cancel operation cannot be performed", + LDAPResultAssertionFailed: "An assertion control given in the LDAP operation evaluated to false causing the operation to not be performed", + LDAPResultSyncRefreshRequired: "Refresh Required", + LDAPResultInvalidResponse: "Invalid Response", + LDAPResultAmbiguousResponse: "Ambiguous Response", + LDAPResultTLSNotSupported: "Tls Not Supported", + LDAPResultIntermediateResponse: "Intermediate Response", + LDAPResultUnknownType: "Unknown Type", + LDAPResultAuthorizationDenied: "Authorization Denied", + + ErrorNetwork: "Network Error", + ErrorFilterCompile: "Filter Compile Error", + ErrorFilterDecompile: "Filter Decompile Error", + ErrorDebugging: "Debugging Error", + ErrorUnexpectedMessage: "Unexpected Message", + ErrorUnexpectedResponse: "Unexpected Response", + ErrorEmptyPassword: "Empty password not allowed by the client", +} + +// Error holds LDAP error information +type Error struct { + // Err is the underlying error + Err error + // ResultCode is the LDAP error code + ResultCode uint16 + // MatchedDN is the matchedDN returned if any + MatchedDN string +} + +func (e *Error) Error() string { + return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error()) +} + +// GetLDAPError creates an Error out of a BER packet representing a LDAPResult +// The return is an error object. It can be casted to a Error structure. +// This function returns nil if resultCode in the LDAPResult sequence is success(0). +func GetLDAPError(packet *ber.Packet) error { + if packet == nil { + return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty packet")} + } else if len(packet.Children) >= 2 { + response := packet.Children[1] + if response == nil { + return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty response in packet")} + } + if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 { + resultCode := uint16(response.Children[0].Value.(int64)) + if resultCode == 0 { // No error + return nil + } + return &Error{ResultCode: resultCode, MatchedDN: response.Children[1].Value.(string), + Err: fmt.Errorf(response.Children[2].Value.(string))} + } + } + + return &Error{ResultCode: ErrorNetwork, Err: fmt.Errorf("Invalid packet format")} +} + +// NewError creates an LDAP error with the given code and underlying error +func NewError(resultCode uint16, err error) error { + return &Error{ResultCode: resultCode, Err: err} +} + +// IsErrorWithCode returns true if the given error is an LDAP error with the given result code +func IsErrorWithCode(err error, desiredResultCode uint16) bool { + if err == nil { + return false + } + + serverError, ok := err.(*Error) + if !ok { + return false + } + + return serverError.ResultCode == desiredResultCode +} diff --git a/vendor/gopkg.in/ldap.v2/filter.go b/vendor/gopkg.in/ldap.v3/filter.go similarity index 98% rename from vendor/gopkg.in/ldap.v2/filter.go rename to vendor/gopkg.in/ldap.v3/filter.go index 3858a2865..4cc4207be 100644 --- a/vendor/gopkg.in/ldap.v2/filter.go +++ b/vendor/gopkg.in/ldap.v3/filter.go @@ -1,7 +1,3 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package ldap import ( diff --git a/vendor/gopkg.in/ldap.v2/ldap.go b/vendor/gopkg.in/ldap.v3/ldap.go similarity index 82% rename from vendor/gopkg.in/ldap.v2/ldap.go rename to vendor/gopkg.in/ldap.v3/ldap.go index 496924756..d7666676f 100644 --- a/vendor/gopkg.in/ldap.v2/ldap.go +++ b/vendor/gopkg.in/ldap.v3/ldap.go @@ -1,11 +1,8 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package ldap import ( "errors" + "fmt" "io/ioutil" "os" @@ -101,13 +98,13 @@ func addLDAPDescriptions(packet *ber.Packet) (err error) { switch application { case ApplicationBindRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationBindResponse: - addDefaultLDAPResponseDescriptions(packet) + err = addDefaultLDAPResponseDescriptions(packet) case ApplicationUnbindRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationSearchRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationSearchResultEntry: packet.Children[1].Children[0].Description = "Object Name" packet.Children[1].Children[1].Description = "Attributes" @@ -120,37 +117,37 @@ func addLDAPDescriptions(packet *ber.Packet) (err error) { } } if len(packet.Children) == 3 { - addControlDescriptions(packet.Children[2]) + err = addControlDescriptions(packet.Children[2]) } case ApplicationSearchResultDone: - addDefaultLDAPResponseDescriptions(packet) + err = addDefaultLDAPResponseDescriptions(packet) case ApplicationModifyRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationModifyResponse: case ApplicationAddRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationAddResponse: case ApplicationDelRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationDelResponse: case ApplicationModifyDNRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationModifyDNResponse: case ApplicationCompareRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationCompareResponse: case ApplicationAbandonRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationSearchResultReference: case ApplicationExtendedRequest: - addRequestDescriptions(packet) + err = addRequestDescriptions(packet) case ApplicationExtendedResponse: } - return nil + return err } -func addControlDescriptions(packet *ber.Packet) { +func addControlDescriptions(packet *ber.Packet) error { packet.Description = "Controls" for _, child := range packet.Children { var value *ber.Packet @@ -159,7 +156,7 @@ func addControlDescriptions(packet *ber.Packet) { switch len(child.Children) { case 0: // at least one child is required for control type - continue + return fmt.Errorf("at least one child is required for control type") case 1: // just type, no criticality or value @@ -188,8 +185,9 @@ func addControlDescriptions(packet *ber.Packet) { default: // more than 3 children is invalid - continue + return fmt.Errorf("more than 3 children for control packet found") } + if value == nil { continue } @@ -197,7 +195,10 @@ func addControlDescriptions(packet *ber.Packet) { case ControlTypePaging: value.Description += " (Paging)" if value.Value != nil { - valueChildren := ber.DecodePacket(value.Data.Bytes()) + valueChildren, err := ber.DecodePacketErr(value.Data.Bytes()) + if err != nil { + return fmt.Errorf("failed to decode data bytes: %s", err) + } value.Data.Truncate(0) value.Value = nil valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes() @@ -210,7 +211,10 @@ func addControlDescriptions(packet *ber.Packet) { case ControlTypeBeheraPasswordPolicy: value.Description += " (Password Policy - Behera Draft)" if value.Value != nil { - valueChildren := ber.DecodePacket(value.Data.Bytes()) + valueChildren, err := ber.DecodePacketErr(value.Data.Bytes()) + if err != nil { + return fmt.Errorf("failed to decode data bytes: %s", err) + } value.Data.Truncate(0) value.Value = nil value.AppendChild(valueChildren) @@ -220,7 +224,10 @@ func addControlDescriptions(packet *ber.Packet) { if child.Tag == 0 { //Warning warningPacket := child.Children[0] - packet := ber.DecodePacket(warningPacket.Data.Bytes()) + packet, err := ber.DecodePacketErr(warningPacket.Data.Bytes()) + if err != nil { + return fmt.Errorf("failed to decode data bytes: %s", err) + } val, ok := packet.Value.(int64) if ok { if warningPacket.Tag == 0 { @@ -235,7 +242,10 @@ func addControlDescriptions(packet *ber.Packet) { } } else if child.Tag == 1 { // Error - packet := ber.DecodePacket(child.Data.Bytes()) + packet, err := ber.DecodePacketErr(child.Data.Bytes()) + if err != nil { + return fmt.Errorf("failed to decode data bytes: %s", err) + } val, ok := packet.Value.(int8) if !ok { val = -1 @@ -246,28 +256,31 @@ func addControlDescriptions(packet *ber.Packet) { } } } + return nil } -func addRequestDescriptions(packet *ber.Packet) { +func addRequestDescriptions(packet *ber.Packet) error { packet.Description = "LDAP Request" packet.Children[0].Description = "Message ID" packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)] if len(packet.Children) == 3 { - addControlDescriptions(packet.Children[2]) + return addControlDescriptions(packet.Children[2]) } + return nil } -func addDefaultLDAPResponseDescriptions(packet *ber.Packet) { - resultCode, _ := getLDAPResultCode(packet) - packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")" - packet.Children[1].Children[1].Description = "Matched DN" +func addDefaultLDAPResponseDescriptions(packet *ber.Packet) error { + err := GetLDAPError(packet) + packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[err.(*Error).ResultCode] + ")" + packet.Children[1].Children[1].Description = "Matched DN (" + err.(*Error).MatchedDN + ")" packet.Children[1].Children[2].Description = "Error Message" if len(packet.Children[1].Children) > 3 { packet.Children[1].Children[3].Description = "Referral" } if len(packet.Children) == 3 { - addControlDescriptions(packet.Children[2]) + return addControlDescriptions(packet.Children[2]) } + return nil } // DebugBinaryFile reads and prints packets from the given filename @@ -277,8 +290,13 @@ func DebugBinaryFile(fileName string) error { return NewError(ErrorDebugging, err) } ber.PrintBytes(os.Stdout, file, "") - packet := ber.DecodePacket(file) - addLDAPDescriptions(packet) + packet, err := ber.DecodePacketErr(file) + if err != nil { + return fmt.Errorf("failed to decode packet: %s", err) + } + if err := addLDAPDescriptions(packet); err != nil { + return err + } ber.PrintPacket(packet) return nil diff --git a/vendor/gopkg.in/ldap.v3/moddn.go b/vendor/gopkg.in/ldap.v3/moddn.go new file mode 100644 index 000000000..803279d26 --- /dev/null +++ b/vendor/gopkg.in/ldap.v3/moddn.go @@ -0,0 +1,104 @@ +// Package ldap - moddn.go contains ModifyDN functionality +// +// https://tools.ietf.org/html/rfc4511 +// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { +// entry LDAPDN, +// newrdn RelativeLDAPDN, +// deleteoldrdn BOOLEAN, +// newSuperior [0] LDAPDN OPTIONAL } +// +// +package ldap + +import ( + "errors" + "log" + + "gopkg.in/asn1-ber.v1" +) + +// ModifyDNRequest holds the request to modify a DN +type ModifyDNRequest struct { + DN string + NewRDN string + DeleteOldRDN bool + NewSuperior string +} + +// NewModifyDNRequest creates a new request which can be passed to ModifyDN(). +// +// To move an object in the tree, set the "newSup" to the new parent entry DN. Use an +// empty string for just changing the object's RDN. +// +// For moving the object without renaming, the "rdn" must be the first +// RDN of the given DN. +// +// A call like +// mdnReq := NewModifyDNRequest("uid=someone,dc=example,dc=org", "uid=newname", true, "") +// will setup the request to just rename uid=someone,dc=example,dc=org to +// uid=newname,dc=example,dc=org. +func NewModifyDNRequest(dn string, rdn string, delOld bool, newSup string) *ModifyDNRequest { + return &ModifyDNRequest{ + DN: dn, + NewRDN: rdn, + DeleteOldRDN: delOld, + NewSuperior: newSup, + } +} + +func (m ModifyDNRequest) encode() *ber.Packet { + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyDNRequest, nil, "Modify DN Request") + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN")) + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.NewRDN, "New RDN")) + request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, m.DeleteOldRDN, "Delete old RDN")) + if m.NewSuperior != "" { + request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, m.NewSuperior, "New Superior")) + } + return request +} + +// ModifyDN renames the given DN and optionally move to another base (when the "newSup" argument +// to NewModifyDNRequest() is not ""). +func (l *Conn) ModifyDN(m *ModifyDNRequest) error { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + packet.AppendChild(m.encode()) + + l.Debug.PrintPacket(packet) + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses + if !ok { + return NewError(ErrorNetwork, errors.New("ldap: channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return err + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + return err + } + ber.PrintPacket(packet) + } + + if packet.Children[1].Tag == ApplicationModifyDNResponse { + err := GetLDAPError(packet) + if err != nil { + return err + } + } else { + log.Printf("Unexpected Response: %d", packet.Children[1].Tag) + } + + l.Debug.Printf("%d: returning", msgCtx.id) + return nil +} diff --git a/vendor/gopkg.in/ldap.v2/modify.go b/vendor/gopkg.in/ldap.v3/modify.go similarity index 63% rename from vendor/gopkg.in/ldap.v2/modify.go rename to vendor/gopkg.in/ldap.v3/modify.go index e4ab6cefc..d83e6221f 100644 --- a/vendor/gopkg.in/ldap.v2/modify.go +++ b/vendor/gopkg.in/ldap.v3/modify.go @@ -1,7 +1,3 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// // File contains Modify functionality // // https://tools.ietf.org/html/rfc4511 @@ -62,54 +58,56 @@ func (p *PartialAttribute) encode() *ber.Packet { return seq } +// Change for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 +type Change struct { + // Operation is the type of change to be made + Operation uint + // Modification is the attribute to be modified + Modification PartialAttribute +} + +func (c *Change) encode() *ber.Packet { + change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") + change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(c.Operation), "Operation")) + change.AppendChild(c.Modification.encode()) + return change +} + // ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 type ModifyRequest struct { // DN is the distinguishedName of the directory entry to modify DN string - // AddAttributes contain the attributes to add - AddAttributes []PartialAttribute - // DeleteAttributes contain the attributes to delete - DeleteAttributes []PartialAttribute - // ReplaceAttributes contain the attributes to replace - ReplaceAttributes []PartialAttribute + // Changes contain the attributes to modify + Changes []Change + // Controls hold optional controls to send with the request + Controls []Control } -// Add inserts the given attribute to the list of attributes to add +// Add appends the given attribute to the list of changes to be made func (m *ModifyRequest) Add(attrType string, attrVals []string) { - m.AddAttributes = append(m.AddAttributes, PartialAttribute{Type: attrType, Vals: attrVals}) + m.appendChange(AddAttribute, attrType, attrVals) } -// Delete inserts the given attribute to the list of attributes to delete +// Delete appends the given attribute to the list of changes to be made func (m *ModifyRequest) Delete(attrType string, attrVals []string) { - m.DeleteAttributes = append(m.DeleteAttributes, PartialAttribute{Type: attrType, Vals: attrVals}) + m.appendChange(DeleteAttribute, attrType, attrVals) } -// Replace inserts the given attribute to the list of attributes to replace +// Replace appends the given attribute to the list of changes to be made func (m *ModifyRequest) Replace(attrType string, attrVals []string) { - m.ReplaceAttributes = append(m.ReplaceAttributes, PartialAttribute{Type: attrType, Vals: attrVals}) + m.appendChange(ReplaceAttribute, attrType, attrVals) +} + +func (m *ModifyRequest) appendChange(operation uint, attrType string, attrVals []string) { + m.Changes = append(m.Changes, Change{operation, PartialAttribute{Type: attrType, Vals: attrVals}}) } func (m ModifyRequest) encode() *ber.Packet { request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request") request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN")) changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes") - for _, 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 { - 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 { - 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()) - changes.AppendChild(change) + for _, change := range m.Changes { + changes.AppendChild(change.encode()) } request.AppendChild(changes) return request @@ -118,9 +116,11 @@ func (m ModifyRequest) encode() *ber.Packet { // NewModifyRequest creates a modify request for the given DN func NewModifyRequest( dn string, + controls []Control, ) *ModifyRequest { return &ModifyRequest{ - DN: dn, + DN: dn, + Controls: controls, } } @@ -129,6 +129,9 @@ func (l *Conn) Modify(modifyRequest *ModifyRequest) error { packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) packet.AppendChild(modifyRequest.encode()) + if len(modifyRequest.Controls) > 0 { + packet.AppendChild(encodeControls(modifyRequest.Controls)) + } l.Debug.PrintPacket(packet) @@ -157,9 +160,9 @@ func (l *Conn) Modify(modifyRequest *ModifyRequest) error { } if packet.Children[1].Tag == ApplicationModifyResponse { - resultCode, resultDescription := getLDAPResultCode(packet) - if resultCode != 0 { - return NewError(resultCode, errors.New(resultDescription)) + err := GetLDAPError(packet) + if err != nil { + return err } } else { log.Printf("Unexpected Response: %d", packet.Children[1].Tag) diff --git a/vendor/gopkg.in/ldap.v2/passwdmodify.go b/vendor/gopkg.in/ldap.v3/passwdmodify.go similarity index 92% rename from vendor/gopkg.in/ldap.v2/passwdmodify.go rename to vendor/gopkg.in/ldap.v3/passwdmodify.go index 7d8246fd1..06bc21db1 100644 --- a/vendor/gopkg.in/ldap.v2/passwdmodify.go +++ b/vendor/gopkg.in/ldap.v3/passwdmodify.go @@ -32,6 +32,8 @@ type PasswordModifyRequest struct { type PasswordModifyResult struct { // GeneratedPassword holds a password generated by the server, if present GeneratedPassword string + // Referral are the returned referral + Referral string } func (r *PasswordModifyRequest) encode() (*ber.Packet, error) { @@ -124,12 +126,19 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa } if packet.Children[1].Tag == ApplicationExtendedResponse { - resultCode, resultDescription := getLDAPResultCode(packet) - if resultCode != 0 { - return nil, NewError(resultCode, errors.New(resultDescription)) + err := GetLDAPError(packet) + if err != nil { + if IsErrorWithCode(err, LDAPResultReferral) { + for _, child := range packet.Children[1].Children { + if child.Tag == 3 { + result.Referral = child.Children[0].Value.(string) + } + } + } + return result, err } } else { - return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag)) + return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("unexpected Response: %d", packet.Children[1].Tag)) } extendedResponse := packet.Children[1] diff --git a/vendor/gopkg.in/ldap.v2/search.go b/vendor/gopkg.in/ldap.v3/search.go similarity index 96% rename from vendor/gopkg.in/ldap.v2/search.go rename to vendor/gopkg.in/ldap.v3/search.go index 2a99894c9..3aa6dac0c 100644 --- a/vendor/gopkg.in/ldap.v2/search.go +++ b/vendor/gopkg.in/ldap.v3/search.go @@ -1,7 +1,3 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// // File contains Search functionality // // https://tools.ietf.org/html/rfc4511 @@ -313,10 +309,10 @@ func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) } else { castControl, ok := control.(*ControlPaging) if !ok { - return nil, fmt.Errorf("Expected paging control to be of type *ControlPaging, got %v", control) + return nil, fmt.Errorf("expected paging control to be of type *ControlPaging, got %v", control) } if castControl.PagingSize != pagingSize { - return nil, fmt.Errorf("Paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize) + return nil, fmt.Errorf("paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize) } pagingControl = castControl } @@ -379,7 +375,7 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { } packet.AppendChild(encodedSearchRequest) // encode search controls - if searchRequest.Controls != nil { + if len(searchRequest.Controls) > 0 { packet.AppendChild(encodeControls(searchRequest.Controls)) } @@ -431,13 +427,17 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { } result.Entries = append(result.Entries, entry) case 5: - resultCode, resultDescription := getLDAPResultCode(packet) - if resultCode != 0 { - return result, NewError(resultCode, errors.New(resultDescription)) + err := GetLDAPError(packet) + if err != nil { + return nil, err } if len(packet.Children) == 3 { for _, child := range packet.Children[2].Children { - result.Controls = append(result.Controls, DecodeControl(child)) + decodedChild, err := DecodeControl(child) + if err != nil { + return nil, fmt.Errorf("failed to decode child control: %s", err) + } + result.Controls = append(result.Controls, decodedChild) } } foundSearchResultDone = true