From 9359e83ad8fcb90f2a4ec1ba8350414d9c659532 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Sat, 30 Apr 2022 12:19:35 +1000 Subject: [PATCH 1/6] feat: return referrals for modify operation This implements returning the referral for the modify operation. Tested against a Microsoft Active Directory Read-only Domain Controller. --- modify.go | 12 +++++++++++- v3/modify.go | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/modify.go b/modify.go index f6fe3859..86b60662 100644 --- a/modify.go +++ b/modify.go @@ -135,6 +135,8 @@ func (l *Conn) Modify(modifyRequest *ModifyRequest) error { type ModifyResult struct { // Controls are the returned controls Controls []Control + // Referral is the returned referral + Referral string } // ModifyWithResult performs the ModifyRequest and returns the result @@ -159,7 +161,15 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er case ApplicationModifyResponse: err := GetLDAPError(packet) if err != nil { - return nil, err + 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 } if len(packet.Children) == 3 { for _, child := range packet.Children[2].Children { diff --git a/v3/modify.go b/v3/modify.go index f6fe3859..86b60662 100644 --- a/v3/modify.go +++ b/v3/modify.go @@ -135,6 +135,8 @@ func (l *Conn) Modify(modifyRequest *ModifyRequest) error { type ModifyResult struct { // Controls are the returned controls Controls []Control + // Referral is the returned referral + Referral string } // ModifyWithResult performs the ModifyRequest and returns the result @@ -159,7 +161,15 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er case ApplicationModifyResponse: err := GetLDAPError(packet) if err != nil { - return nil, err + 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 } if len(packet.Children) == 3 { for _, child := range packet.Children[2].Children { From a78ea342c6b0cdeb20e5616e471a9517d36feb67 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Sat, 30 Apr 2022 12:27:05 +1000 Subject: [PATCH 2/6] fix: add anti-panic guards --- modify.go | 11 ++++++++--- passwdmodify.go | 12 +++++++++--- v3/modify.go | 11 ++++++++--- v3/passwdmodify.go | 12 +++++++++--- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/modify.go b/modify.go index 86b60662..a8691cb7 100644 --- a/modify.go +++ b/modify.go @@ -161,10 +161,15 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er case ApplicationModifyResponse: err := GetLDAPError(packet) if err != nil { - if IsErrorWithCode(err, LDAPResultReferral) { + if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { for _, child := range packet.Children[1].Children { - if child.Tag == 3 { - result.Referral = child.Children[0].Value.(string) + if child.Tag == 3 && len(child.Children) >= 1 { + referral, ok := child.Children[0].Value.(string) + if !ok { + continue + } + + result.Referral = referral } } } diff --git a/passwdmodify.go b/passwdmodify.go index 62a11084..5338a0ef 100644 --- a/passwdmodify.go +++ b/passwdmodify.go @@ -97,13 +97,19 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa if packet.Children[1].Tag == ApplicationExtendedResponse { err := GetLDAPError(packet) if err != nil { - if IsErrorWithCode(err, LDAPResultReferral) { + if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { for _, child := range packet.Children[1].Children { - if child.Tag == 3 { - result.Referral = child.Children[0].Value.(string) + if child.Tag == 3 && len(child.Children) >= 1 { + referral, ok := child.Children[0].Value.(string) + if !ok { + continue + } + + result.Referral = referral } } } + return result, err } } else { diff --git a/v3/modify.go b/v3/modify.go index 86b60662..a8691cb7 100644 --- a/v3/modify.go +++ b/v3/modify.go @@ -161,10 +161,15 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er case ApplicationModifyResponse: err := GetLDAPError(packet) if err != nil { - if IsErrorWithCode(err, LDAPResultReferral) { + if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { for _, child := range packet.Children[1].Children { - if child.Tag == 3 { - result.Referral = child.Children[0].Value.(string) + if child.Tag == 3 && len(child.Children) >= 1 { + referral, ok := child.Children[0].Value.(string) + if !ok { + continue + } + + result.Referral = referral } } } diff --git a/v3/passwdmodify.go b/v3/passwdmodify.go index 62a11084..5338a0ef 100644 --- a/v3/passwdmodify.go +++ b/v3/passwdmodify.go @@ -97,13 +97,19 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa if packet.Children[1].Tag == ApplicationExtendedResponse { err := GetLDAPError(packet) if err != nil { - if IsErrorWithCode(err, LDAPResultReferral) { + if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { for _, child := range packet.Children[1].Children { - if child.Tag == 3 { - result.Referral = child.Children[0].Value.(string) + if child.Tag == 3 && len(child.Children) >= 1 { + referral, ok := child.Children[0].Value.(string) + if !ok { + continue + } + + result.Referral = referral } } } + return result, err } } else { From 6e90b1993072e70d740628b9ecb12ec365280865 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Sat, 30 Apr 2022 13:16:44 +1000 Subject: [PATCH 3/6] refactor: utilize ber consts --- modify.go | 10 +++++----- passwdmodify.go | 14 +++++++------- v3/modify.go | 10 +++++----- v3/passwdmodify.go | 14 +++++++------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/modify.go b/modify.go index a8691cb7..d036974d 100644 --- a/modify.go +++ b/modify.go @@ -163,13 +163,13 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er if err != nil { if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { for _, child := range packet.Children[1].Children { - if child.Tag == 3 && len(child.Children) >= 1 { + if child.Tag == ber.TagBitString && len(child.Children) >= 1 { referral, ok := child.Children[0].Value.(string) - if !ok { - continue - } + if ok { + result.Referral = referral - result.Referral = referral + break + } } } } diff --git a/passwdmodify.go b/passwdmodify.go index 5338a0ef..c2c9f926 100644 --- a/passwdmodify.go +++ b/passwdmodify.go @@ -99,13 +99,13 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa if err != nil { if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { for _, child := range packet.Children[1].Children { - if child.Tag == 3 && len(child.Children) >= 1 { + if child.Tag == ber.TagBitString && len(child.Children) >= 1 { referral, ok := child.Children[0].Value.(string) - if !ok { - continue - } + if ok { + result.Referral = referral - result.Referral = referral + break + } } } } @@ -118,10 +118,10 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa extendedResponse := packet.Children[1] for _, child := range extendedResponse.Children { - if child.Tag == 11 { + if child.Tag == ber.TagEmbeddedPDV { passwordModifyResponseValue := ber.DecodePacket(child.Data.Bytes()) if len(passwordModifyResponseValue.Children) == 1 { - if passwordModifyResponseValue.Children[0].Tag == 0 { + if passwordModifyResponseValue.Children[0].Tag == ber.TagEOC { result.GeneratedPassword = ber.DecodeString(passwordModifyResponseValue.Children[0].Data.Bytes()) } } diff --git a/v3/modify.go b/v3/modify.go index a8691cb7..d036974d 100644 --- a/v3/modify.go +++ b/v3/modify.go @@ -163,13 +163,13 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er if err != nil { if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { for _, child := range packet.Children[1].Children { - if child.Tag == 3 && len(child.Children) >= 1 { + if child.Tag == ber.TagBitString && len(child.Children) >= 1 { referral, ok := child.Children[0].Value.(string) - if !ok { - continue - } + if ok { + result.Referral = referral - result.Referral = referral + break + } } } } diff --git a/v3/passwdmodify.go b/v3/passwdmodify.go index 5338a0ef..c2c9f926 100644 --- a/v3/passwdmodify.go +++ b/v3/passwdmodify.go @@ -99,13 +99,13 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa if err != nil { if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { for _, child := range packet.Children[1].Children { - if child.Tag == 3 && len(child.Children) >= 1 { + if child.Tag == ber.TagBitString && len(child.Children) >= 1 { referral, ok := child.Children[0].Value.(string) - if !ok { - continue - } + if ok { + result.Referral = referral - result.Referral = referral + break + } } } } @@ -118,10 +118,10 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa extendedResponse := packet.Children[1] for _, child := range extendedResponse.Children { - if child.Tag == 11 { + if child.Tag == ber.TagEmbeddedPDV { passwordModifyResponseValue := ber.DecodePacket(child.Data.Bytes()) if len(passwordModifyResponseValue.Children) == 1 { - if passwordModifyResponseValue.Children[0].Tag == 0 { + if passwordModifyResponseValue.Children[0].Tag == ber.TagEOC { result.GeneratedPassword = ber.DecodeString(passwordModifyResponseValue.Children[0].Data.Bytes()) } } From 8da03e1a0fb5cd4d600aa7615b356bddd82fcbec Mon Sep 17 00:00:00 2001 From: James Elliott Date: Sat, 30 Apr 2022 14:40:25 +1000 Subject: [PATCH 4/6] refactor: factorize referral retrieval --- modify.go | 13 +------------ passwdmodify.go | 13 +------------ request.go | 18 ++++++++++++++++++ v3/modify.go | 13 +------------ v3/passwdmodify.go | 13 +------------ v3/request.go | 18 ++++++++++++++++++ 6 files changed, 40 insertions(+), 48 deletions(-) diff --git a/modify.go b/modify.go index d036974d..fd20e558 100644 --- a/modify.go +++ b/modify.go @@ -161,18 +161,7 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er case ApplicationModifyResponse: err := GetLDAPError(packet) if err != nil { - if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { - for _, child := range packet.Children[1].Children { - if child.Tag == ber.TagBitString && len(child.Children) >= 1 { - referral, ok := child.Children[0].Value.(string) - if ok { - result.Referral = referral - - break - } - } - } - } + result.Referral = getReferral(err, packet) return result, err } diff --git a/passwdmodify.go b/passwdmodify.go index c2c9f926..a3dc8689 100644 --- a/passwdmodify.go +++ b/passwdmodify.go @@ -97,18 +97,7 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa if packet.Children[1].Tag == ApplicationExtendedResponse { err := GetLDAPError(packet) if err != nil { - if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { - for _, child := range packet.Children[1].Children { - if child.Tag == ber.TagBitString && len(child.Children) >= 1 { - referral, ok := child.Children[0].Value.(string) - if ok { - result.Referral = referral - - break - } - } - } - } + result.Referral = getReferral(err, packet) return result, err } diff --git a/request.go b/request.go index 4ea31e90..aa6e5d92 100644 --- a/request.go +++ b/request.go @@ -69,3 +69,21 @@ func (l *Conn) readPacket(msgCtx *messageContext) (*ber.Packet, error) { } return packet, nil } + +func getReferral(err error, packet *ber.Packet) (referral string) { + var ok bool + + if !IsErrorWithCode(err, LDAPResultReferral) || len(packet.Children) < 2 { + return "" + } + + for _, child := range packet.Children[1].Children { + if child.Tag == ber.TagBitString && len(child.Children) >= 1 { + if referral, ok = child.Children[0].Value.(string); ok { + return referral + } + } + } + + return "" +} diff --git a/v3/modify.go b/v3/modify.go index d036974d..fd20e558 100644 --- a/v3/modify.go +++ b/v3/modify.go @@ -161,18 +161,7 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er case ApplicationModifyResponse: err := GetLDAPError(packet) if err != nil { - if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { - for _, child := range packet.Children[1].Children { - if child.Tag == ber.TagBitString && len(child.Children) >= 1 { - referral, ok := child.Children[0].Value.(string) - if ok { - result.Referral = referral - - break - } - } - } - } + result.Referral = getReferral(err, packet) return result, err } diff --git a/v3/passwdmodify.go b/v3/passwdmodify.go index c2c9f926..a3dc8689 100644 --- a/v3/passwdmodify.go +++ b/v3/passwdmodify.go @@ -97,18 +97,7 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa if packet.Children[1].Tag == ApplicationExtendedResponse { err := GetLDAPError(packet) if err != nil { - if IsErrorWithCode(err, LDAPResultReferral) && len(packet.Children) >= 2 { - for _, child := range packet.Children[1].Children { - if child.Tag == ber.TagBitString && len(child.Children) >= 1 { - referral, ok := child.Children[0].Value.(string) - if ok { - result.Referral = referral - - break - } - } - } - } + result.Referral = getReferral(err, packet) return result, err } diff --git a/v3/request.go b/v3/request.go index 4ea31e90..aa6e5d92 100644 --- a/v3/request.go +++ b/v3/request.go @@ -69,3 +69,21 @@ func (l *Conn) readPacket(msgCtx *messageContext) (*ber.Packet, error) { } return packet, nil } + +func getReferral(err error, packet *ber.Packet) (referral string) { + var ok bool + + if !IsErrorWithCode(err, LDAPResultReferral) || len(packet.Children) < 2 { + return "" + } + + for _, child := range packet.Children[1].Children { + if child.Tag == ber.TagBitString && len(child.Children) >= 1 { + if referral, ok = child.Children[0].Value.(string); ok { + return referral + } + } + } + + return "" +} From 1ee2d9150b472b26a440320464d5ce51b78f7509 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Thu, 26 May 2022 19:57:35 +1000 Subject: [PATCH 5/6] refactor: apply suggestions from code review --- modify.go | 9 ++++++--- passwdmodify.go | 9 ++++++--- request.go | 21 +++++++++++++++------ v3/modify.go | 9 ++++++--- v3/passwdmodify.go | 9 ++++++--- v3/request.go | 21 +++++++++++++++------ 6 files changed, 54 insertions(+), 24 deletions(-) diff --git a/modify.go b/modify.go index fd20e558..040c8e7e 100644 --- a/modify.go +++ b/modify.go @@ -159,9 +159,12 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er switch packet.Children[1].Tag { case ApplicationModifyResponse: - err := GetLDAPError(packet) - if err != nil { - result.Referral = getReferral(err, packet) + if err = GetLDAPError(packet); err != nil { + if referral, referralErr := getReferral(err, packet); referralErr != nil { + return result, err + } else { + result.Referral = referral + } return result, err } diff --git a/passwdmodify.go b/passwdmodify.go index a3dc8689..c5472723 100644 --- a/passwdmodify.go +++ b/passwdmodify.go @@ -95,9 +95,12 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa result := &PasswordModifyResult{} if packet.Children[1].Tag == ApplicationExtendedResponse { - err := GetLDAPError(packet) - if err != nil { - result.Referral = getReferral(err, packet) + if err = GetLDAPError(packet); err != nil { + if referral, referralErr := getReferral(err, packet); referralErr != nil { + return result, err + } else { + result.Referral = referral + } return result, err } diff --git a/request.go b/request.go index aa6e5d92..adc3b1c2 100644 --- a/request.go +++ b/request.go @@ -2,6 +2,7 @@ package ldap import ( "errors" + "fmt" ber "github.com/go-asn1-ber/asn1-ber" ) @@ -70,20 +71,28 @@ func (l *Conn) readPacket(msgCtx *messageContext) (*ber.Packet, error) { return packet, nil } -func getReferral(err error, packet *ber.Packet) (referral string) { - var ok bool +func getReferral(err error, packet *ber.Packet) (referral string, e error) { + if !IsErrorWithCode(err, LDAPResultReferral) { + return "", nil + } + + if len(packet.Children) < 2 { + return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but it doesn't have sufficient child nodes: %w", err) + } - if !IsErrorWithCode(err, LDAPResultReferral) || len(packet.Children) < 2 { - return "" + if packet.Children[1].Tag != ber.TagObjectDescriptor { + return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but the relevant child node isn't an object descriptor: %w", err) } + var ok bool + for _, child := range packet.Children[1].Children { if child.Tag == ber.TagBitString && len(child.Children) >= 1 { if referral, ok = child.Children[0].Value.(string); ok { - return referral + return referral, nil } } } - return "" + return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but the referral couldn't be decoded: %w", err) } diff --git a/v3/modify.go b/v3/modify.go index fd20e558..040c8e7e 100644 --- a/v3/modify.go +++ b/v3/modify.go @@ -159,9 +159,12 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er switch packet.Children[1].Tag { case ApplicationModifyResponse: - err := GetLDAPError(packet) - if err != nil { - result.Referral = getReferral(err, packet) + if err = GetLDAPError(packet); err != nil { + if referral, referralErr := getReferral(err, packet); referralErr != nil { + return result, err + } else { + result.Referral = referral + } return result, err } diff --git a/v3/passwdmodify.go b/v3/passwdmodify.go index a3dc8689..c5472723 100644 --- a/v3/passwdmodify.go +++ b/v3/passwdmodify.go @@ -95,9 +95,12 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa result := &PasswordModifyResult{} if packet.Children[1].Tag == ApplicationExtendedResponse { - err := GetLDAPError(packet) - if err != nil { - result.Referral = getReferral(err, packet) + if err = GetLDAPError(packet); err != nil { + if referral, referralErr := getReferral(err, packet); referralErr != nil { + return result, err + } else { + result.Referral = referral + } return result, err } diff --git a/v3/request.go b/v3/request.go index aa6e5d92..adc3b1c2 100644 --- a/v3/request.go +++ b/v3/request.go @@ -2,6 +2,7 @@ package ldap import ( "errors" + "fmt" ber "github.com/go-asn1-ber/asn1-ber" ) @@ -70,20 +71,28 @@ func (l *Conn) readPacket(msgCtx *messageContext) (*ber.Packet, error) { return packet, nil } -func getReferral(err error, packet *ber.Packet) (referral string) { - var ok bool +func getReferral(err error, packet *ber.Packet) (referral string, e error) { + if !IsErrorWithCode(err, LDAPResultReferral) { + return "", nil + } + + if len(packet.Children) < 2 { + return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but it doesn't have sufficient child nodes: %w", err) + } - if !IsErrorWithCode(err, LDAPResultReferral) || len(packet.Children) < 2 { - return "" + if packet.Children[1].Tag != ber.TagObjectDescriptor { + return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but the relevant child node isn't an object descriptor: %w", err) } + var ok bool + for _, child := range packet.Children[1].Children { if child.Tag == ber.TagBitString && len(child.Children) >= 1 { if referral, ok = child.Children[0].Value.(string); ok { - return referral + return referral, nil } } } - return "" + return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but the referral couldn't be decoded: %w", err) } From 260291ada12cbc54401815f456878955a05aa766 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Fri, 3 Jun 2022 07:42:15 +1000 Subject: [PATCH 6/6] fix: return referralerr --- modify.go | 2 +- passwdmodify.go | 2 +- v3/modify.go | 2 +- v3/passwdmodify.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modify.go b/modify.go index 040c8e7e..8b379558 100644 --- a/modify.go +++ b/modify.go @@ -161,7 +161,7 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er case ApplicationModifyResponse: if err = GetLDAPError(packet); err != nil { if referral, referralErr := getReferral(err, packet); referralErr != nil { - return result, err + return result, referralErr } else { result.Referral = referral } diff --git a/passwdmodify.go b/passwdmodify.go index c5472723..e776e3b3 100644 --- a/passwdmodify.go +++ b/passwdmodify.go @@ -97,7 +97,7 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa if packet.Children[1].Tag == ApplicationExtendedResponse { if err = GetLDAPError(packet); err != nil { if referral, referralErr := getReferral(err, packet); referralErr != nil { - return result, err + return result, referralErr } else { result.Referral = referral } diff --git a/v3/modify.go b/v3/modify.go index 040c8e7e..8b379558 100644 --- a/v3/modify.go +++ b/v3/modify.go @@ -161,7 +161,7 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er case ApplicationModifyResponse: if err = GetLDAPError(packet); err != nil { if referral, referralErr := getReferral(err, packet); referralErr != nil { - return result, err + return result, referralErr } else { result.Referral = referral } diff --git a/v3/passwdmodify.go b/v3/passwdmodify.go index c5472723..e776e3b3 100644 --- a/v3/passwdmodify.go +++ b/v3/passwdmodify.go @@ -97,7 +97,7 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa if packet.Children[1].Tag == ApplicationExtendedResponse { if err = GetLDAPError(packet); err != nil { if referral, referralErr := getReferral(err, packet); referralErr != nil { - return result, err + return result, referralErr } else { result.Referral = referral }