Skip to content

Commit ffd773e

Browse files
committed
v3/control: replace unchecked type asserts in DecodeControl with comma-ok
DecodeControl trusts that every controlType is a string and every criticality is a bool, and falls back to value.Value.(string) for any unknown control type. When a malformed or hostile server returns a non-string (or nil) in any of these positions, the .(string) / .(bool) cast panics inside the caller's goroutine. The user repro in #561 hit the default case where value.Value was nil: panic: interface conversion: interface {} is nil, not string go-ldap/ldap/v3.DecodeControl(...) go-ldap/ldap/v3.(*Conn).SimpleBind(...) Use comma-ok asserts at every control-frame field, return clear errors when the cast fails, and in the default ControlString case fall back to value.Data.String() when value.Value isn't a string so the application gets the bytes instead of a crash. The existing TestDecodeControl table covers the normal-path cases and still passes; the new behaviour only kicks in on malformed input where the previous code panicked. Closes #561 Signed-off-by: Charlie Tonneslan <cst0520@gmail.com>
1 parent bf231eb commit ffd773e

1 file changed

Lines changed: 32 additions & 7 deletions

File tree

v3/control.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -565,12 +565,20 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
565565
case 1:
566566
// just type, no criticality or value
567567
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
568-
ControlType = packet.Children[0].Value.(string)
568+
ct, ok := packet.Children[0].Value.(string)
569+
if !ok {
570+
return nil, fmt.Errorf("control type is not a string: %T", packet.Children[0].Value)
571+
}
572+
ControlType = ct
569573

570574
case 2:
571575
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
572576
if packet.Children[0].Value != nil {
573-
ControlType = packet.Children[0].Value.(string)
577+
ct, ok := packet.Children[0].Value.(string)
578+
if !ok {
579+
return nil, fmt.Errorf("control type is not a string: %T", packet.Children[0].Value)
580+
}
581+
ControlType = ct
574582
} else if packet.Children[0].Data != nil {
575583
ControlType = packet.Children[0].Data.String()
576584
} else {
@@ -579,20 +587,28 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
579587

580588
// Children[1] could be criticality or value (both are optional)
581589
// duck-type on whether this is a boolean
582-
if _, ok := packet.Children[1].Value.(bool); ok {
590+
if crit, ok := packet.Children[1].Value.(bool); ok {
583591
packet.Children[1].Description = "Criticality"
584-
Criticality = packet.Children[1].Value.(bool)
592+
Criticality = crit
585593
} else {
586594
packet.Children[1].Description = "Control Value"
587595
value = packet.Children[1]
588596
}
589597

590598
case 3:
591599
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
592-
ControlType = packet.Children[0].Value.(string)
600+
ct, ok := packet.Children[0].Value.(string)
601+
if !ok {
602+
return nil, fmt.Errorf("control type is not a string: %T", packet.Children[0].Value)
603+
}
604+
ControlType = ct
593605

594606
packet.Children[1].Description = "Criticality"
595-
Criticality = packet.Children[1].Value.(bool)
607+
crit, ok := packet.Children[1].Value.(bool)
608+
if !ok {
609+
return nil, fmt.Errorf("criticality is not a bool: %T", packet.Children[1].Value)
610+
}
611+
Criticality = crit
596612

597613
packet.Children[2].Description = "Control Value"
598614
value = packet.Children[2]
@@ -729,7 +745,16 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
729745
c.ControlType = ControlType
730746
c.Criticality = Criticality
731747
if value != nil {
732-
c.ControlValue = value.Value.(string)
748+
// A non-conforming or malicious server can send a non-string
749+
// (or nil) value here; the previous unchecked cast panicked
750+
// the calling goroutine, see #561. Fall back to the raw bytes
751+
// when the value isn't a string so we surface an error
752+
// instead of crashing.
753+
if s, ok := value.Value.(string); ok {
754+
c.ControlValue = s
755+
} else if value.Data != nil {
756+
c.ControlValue = value.Data.String()
757+
}
733758
}
734759
return c, nil
735760
}

0 commit comments

Comments
 (0)