Skip to content

Commit 49701f3

Browse files
fix: packet referral decoding
This adjusts the packet referral decoding behavior to more appropriately handle edge cases and allows to more easily obtain the underlying packet in the event of a decoding error. Instead of matching ASN.1 BER packets on the tag for referrals we match on the class and type. This is particularly important in regards to OpenLDAP which returns a ASN.1 BER Generalized Time tag for referrals.
1 parent 0e43630 commit 49701f3

6 files changed

+36
-38
lines changed

modify.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,7 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er
160160
switch packet.Children[1].Tag {
161161
case ApplicationModifyResponse:
162162
if err = GetLDAPError(packet); err != nil {
163-
if referral, referralErr := getReferral(err, packet); referralErr != nil {
164-
return result, referralErr
165-
} else {
163+
if referral := getReferral(err, packet); referral != "" {
166164
result.Referral = referral
167165
}
168166

passwdmodify.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ func (req *PasswordModifyRequest) appendTo(envelope *ber.Packet) error {
7070
// newPassword is the desired user's password. If empty the server can return
7171
// an error or generate a new password that will be available in the
7272
// PasswordModifyResult.GeneratedPassword
73-
//
7473
func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
7574
return &PasswordModifyRequest{
7675
UserIdentity: userIdentity,
@@ -96,9 +95,7 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa
9695

9796
if packet.Children[1].Tag == ApplicationExtendedResponse {
9897
if err = GetLDAPError(packet); err != nil {
99-
if referral, referralErr := getReferral(err, packet); referralErr != nil {
100-
return result, referralErr
101-
} else {
98+
if referral := getReferral(err, packet); referral != "" {
10299
result.Referral = referral
103100
}
104101

request.go

+16-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package ldap
22

33
import (
44
"errors"
5-
"fmt"
65

76
ber "github.com/go-asn1-ber/asn1-ber"
87
)
@@ -71,28 +70,33 @@ func (l *Conn) readPacket(msgCtx *messageContext) (*ber.Packet, error) {
7170
return packet, nil
7271
}
7372

74-
func getReferral(err error, packet *ber.Packet) (referral string, e error) {
73+
func getReferral(err error, packet *ber.Packet) (referral string) {
7574
if !IsErrorWithCode(err, LDAPResultReferral) {
76-
return "", nil
75+
return ""
7776
}
7877

7978
if len(packet.Children) < 2 {
80-
return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but it doesn't have sufficient child nodes: %w", err)
79+
return ""
8180
}
8281

83-
if packet.Children[1].Tag != ber.TagObjectDescriptor {
84-
return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but the relevant child node isn't an object descriptor: %w", err)
82+
children := len(packet.Children[1].Children)
83+
84+
if children == 0 || (packet.Children[1].TagType != ber.TypeConstructed || packet.Children[1].ClassType != ber.ClassApplication) {
85+
return ""
8586
}
8687

8788
var ok bool
8889

89-
for _, child := range packet.Children[1].Children {
90-
if child.Tag == ber.TagBitString && len(child.Children) >= 1 {
91-
if referral, ok = child.Children[0].Value.(string); ok {
92-
return referral, nil
93-
}
90+
for i := 0; i < children; i++ {
91+
if (packet.Children[1].Children[i].Tag != ber.TagBitString && packet.Children[1].Children[i].Tag != ber.TagPrintableString) ||
92+
packet.Children[1].Children[i].TagType != ber.TypeConstructed || packet.Children[1].Children[i].ClassType != ber.ClassContext {
93+
continue
94+
}
95+
96+
if referral, ok = packet.Children[1].Children[i].Children[0].Value.(string); ok {
97+
return referral
9498
}
9599
}
96100

97-
return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but the referral couldn't be decoded: %w", err)
101+
return ""
98102
}

v3/modify.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,7 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er
160160
switch packet.Children[1].Tag {
161161
case ApplicationModifyResponse:
162162
if err = GetLDAPError(packet); err != nil {
163-
if referral, referralErr := getReferral(err, packet); referralErr != nil {
164-
return result, referralErr
165-
} else {
163+
if referral := getReferral(err, packet); referral != "" {
166164
result.Referral = referral
167165
}
168166

v3/passwdmodify.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ func (req *PasswordModifyRequest) appendTo(envelope *ber.Packet) error {
7070
// newPassword is the desired user's password. If empty the server can return
7171
// an error or generate a new password that will be available in the
7272
// PasswordModifyResult.GeneratedPassword
73-
//
7473
func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
7574
return &PasswordModifyRequest{
7675
UserIdentity: userIdentity,
@@ -96,9 +95,7 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa
9695

9796
if packet.Children[1].Tag == ApplicationExtendedResponse {
9897
if err = GetLDAPError(packet); err != nil {
99-
if referral, referralErr := getReferral(err, packet); referralErr != nil {
100-
return result, referralErr
101-
} else {
98+
if referral := getReferral(err, packet); referral != "" {
10299
result.Referral = referral
103100
}
104101

v3/request.go

+16-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package ldap
22

33
import (
44
"errors"
5-
"fmt"
65

76
ber "github.com/go-asn1-ber/asn1-ber"
87
)
@@ -71,28 +70,33 @@ func (l *Conn) readPacket(msgCtx *messageContext) (*ber.Packet, error) {
7170
return packet, nil
7271
}
7372

74-
func getReferral(err error, packet *ber.Packet) (referral string, e error) {
73+
func getReferral(err error, packet *ber.Packet) (referral string) {
7574
if !IsErrorWithCode(err, LDAPResultReferral) {
76-
return "", nil
75+
return ""
7776
}
7877

7978
if len(packet.Children) < 2 {
80-
return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but it doesn't have sufficient child nodes: %w", err)
79+
return ""
8180
}
8281

83-
if packet.Children[1].Tag != ber.TagObjectDescriptor {
84-
return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but the relevant child node isn't an object descriptor: %w", err)
82+
children := len(packet.Children[1].Children)
83+
84+
if children == 0 || (packet.Children[1].TagType != ber.TypeConstructed || packet.Children[1].ClassType != ber.ClassApplication) {
85+
return ""
8586
}
8687

8788
var ok bool
8889

89-
for _, child := range packet.Children[1].Children {
90-
if child.Tag == ber.TagBitString && len(child.Children) >= 1 {
91-
if referral, ok = child.Children[0].Value.(string); ok {
92-
return referral, nil
93-
}
90+
for i := 0; i < children; i++ {
91+
if (packet.Children[1].Children[i].Tag != ber.TagBitString && packet.Children[1].Children[i].Tag != ber.TagPrintableString) ||
92+
packet.Children[1].Children[i].TagType != ber.TypeConstructed || packet.Children[1].Children[i].ClassType != ber.ClassContext {
93+
continue
94+
}
95+
96+
if referral, ok = packet.Children[1].Children[i].Children[0].Value.(string); ok {
97+
return referral
9498
}
9599
}
96100

97-
return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but the referral couldn't be decoded: %w", err)
101+
return ""
98102
}

0 commit comments

Comments
 (0)