Skip to content

Commit

Permalink
fix: packet referral decoding
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
james-d-elliott committed Jan 25, 2023
1 parent 0e43630 commit 55184a9
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 46 deletions.
6 changes: 1 addition & 5 deletions modify.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,7 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er
switch packet.Children[1].Tag {
case ApplicationModifyResponse:
if err = GetLDAPError(packet); err != nil {
if referral, referralErr := getReferral(err, packet); referralErr != nil {
return result, referralErr
} else {
result.Referral = referral
}
result.Referral = getReferral(err, packet)

return result, err
}
Expand Down
7 changes: 1 addition & 6 deletions passwdmodify.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ func (req *PasswordModifyRequest) appendTo(envelope *ber.Packet) error {
// newPassword is the desired user's password. If empty the server can return
// an error or generate a new password that will be available in the
// PasswordModifyResult.GeneratedPassword
//
func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
return &PasswordModifyRequest{
UserIdentity: userIdentity,
Expand All @@ -96,11 +95,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, referralErr
} else {
result.Referral = referral
}
result.Referral = getReferral(err, packet)

return result, err
}
Expand Down
28 changes: 16 additions & 12 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ldap

import (
"errors"
"fmt"

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

func getReferral(err error, packet *ber.Packet) (referral string, e error) {
func getReferral(err error, packet *ber.Packet) (referral string) {
if !IsErrorWithCode(err, LDAPResultReferral) {
return "", nil
return ""
}

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)
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)
children := len(packet.Children[1].Children)

if children == 0 || (packet.Children[1].TagType != ber.TypeConstructed || packet.Children[1].ClassType != ber.ClassApplication) {
return ""
}

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, nil
}
for i := 0; i < children; i++ {
if (packet.Children[1].Children[i].Tag != ber.TagBitString && packet.Children[1].Children[i].Tag != ber.TagPrintableString) ||
packet.Children[1].Children[i].TagType != ber.TypeConstructed || packet.Children[1].Children[i].ClassType != ber.ClassContext {
continue
}

if referral, ok = packet.Children[1].Children[i].Children[0].Value.(string); ok {
return referral
}
}

return "", fmt.Errorf("ldap: returned error indicates the packet contains a referral but the referral couldn't be decoded: %w", err)
return ""
}
6 changes: 1 addition & 5 deletions v3/modify.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,7 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er
switch packet.Children[1].Tag {
case ApplicationModifyResponse:
if err = GetLDAPError(packet); err != nil {
if referral, referralErr := getReferral(err, packet); referralErr != nil {
return result, referralErr
} else {
result.Referral = referral
}
result.Referral = getReferral(err, packet)

return result, err
}
Expand Down
7 changes: 1 addition & 6 deletions v3/passwdmodify.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ func (req *PasswordModifyRequest) appendTo(envelope *ber.Packet) error {
// newPassword is the desired user's password. If empty the server can return
// an error or generate a new password that will be available in the
// PasswordModifyResult.GeneratedPassword
//
func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
return &PasswordModifyRequest{
UserIdentity: userIdentity,
Expand All @@ -96,11 +95,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, referralErr
} else {
result.Referral = referral
}
result.Referral = getReferral(err, packet)

return result, err
}
Expand Down
28 changes: 16 additions & 12 deletions v3/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ldap

import (
"errors"
"fmt"

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

func getReferral(err error, packet *ber.Packet) (referral string, e error) {
func getReferral(err error, packet *ber.Packet) (referral string) {
if !IsErrorWithCode(err, LDAPResultReferral) {
return "", nil
return ""
}

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)
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)
children := len(packet.Children[1].Children)

if children == 0 || (packet.Children[1].TagType != ber.TypeConstructed || packet.Children[1].ClassType != ber.ClassApplication) {
return ""
}

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, nil
}
for i := 0; i < children; i++ {
if (packet.Children[1].Children[i].Tag != ber.TagBitString && packet.Children[1].Children[i].Tag != ber.TagPrintableString) ||
packet.Children[1].Children[i].TagType != ber.TypeConstructed || packet.Children[1].Children[i].ClassType != ber.ClassContext {
continue
}

if referral, ok = packet.Children[1].Children[i].Children[0].Value.(string); ok {
return referral
}
}

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

0 comments on commit 55184a9

Please sign in to comment.