Skip to content

Commit caa044a

Browse files
regedajohnweldon
authored andcommitted
unified request flow && external binding to LDAP (#232)
* unified request flow && external binding to LDAP * fix debug mode * go.mod was added
1 parent 9f0d712 commit caa044a

17 files changed

+298
-325
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ go:
99
- "1.10.x"
1010
- "1.11.x"
1111
- "1.12.x"
12+
- "1.13.x"
1213
- tip
1314

1415
git:

add.go

+17-36
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
package ldap
1111

1212
import (
13-
"errors"
1413
"log"
1514

16-
"gopkg.in/asn1-ber.v1"
15+
ber "gopkg.in/asn1-ber.v1"
1716
)
1817

1918
// Attribute represents an LDAP attribute
@@ -45,20 +44,26 @@ type AddRequest struct {
4544
Controls []Control
4645
}
4746

48-
func (a AddRequest) encode() *ber.Packet {
49-
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
50-
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN"))
47+
func (req *AddRequest) appendTo(envelope *ber.Packet) error {
48+
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
49+
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN"))
5150
attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
52-
for _, attribute := range a.Attributes {
51+
for _, attribute := range req.Attributes {
5352
attributes.AppendChild(attribute.encode())
5453
}
55-
request.AppendChild(attributes)
56-
return request
54+
pkt.AppendChild(attributes)
55+
56+
envelope.AppendChild(pkt)
57+
if len(req.Controls) > 0 {
58+
envelope.AppendChild(encodeControls(req.Controls))
59+
}
60+
61+
return nil
5762
}
5863

5964
// Attribute adds an attribute with the given type and values
60-
func (a *AddRequest) Attribute(attrType string, attrVals []string) {
61-
a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals})
65+
func (req *AddRequest) Attribute(attrType string, attrVals []string) {
66+
req.Attributes = append(req.Attributes, Attribute{Type: attrType, Vals: attrVals})
6267
}
6368

6469
// NewAddRequest returns an AddRequest for the given DN, with no attributes
@@ -72,39 +77,17 @@ func NewAddRequest(dn string, controls []Control) *AddRequest {
7277

7378
// Add performs the given AddRequest
7479
func (l *Conn) Add(addRequest *AddRequest) error {
75-
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
76-
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
77-
packet.AppendChild(addRequest.encode())
78-
if len(addRequest.Controls) > 0 {
79-
packet.AppendChild(encodeControls(addRequest.Controls))
80-
}
81-
82-
l.Debug.PrintPacket(packet)
83-
84-
msgCtx, err := l.sendMessage(packet)
80+
msgCtx, err := l.doRequest(addRequest)
8581
if err != nil {
8682
return err
8783
}
8884
defer l.finishMessage(msgCtx)
8985

90-
l.Debug.Printf("%d: waiting for response", msgCtx.id)
91-
packetResponse, ok := <-msgCtx.responses
92-
if !ok {
93-
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
94-
}
95-
packet, err = packetResponse.ReadPacket()
96-
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
86+
packet, err := l.readPacket(msgCtx)
9787
if err != nil {
9888
return err
9989
}
10090

101-
if l.Debug {
102-
if err := addLDAPDescriptions(packet); err != nil {
103-
return err
104-
}
105-
ber.PrintPacket(packet)
106-
}
107-
10891
if packet.Children[1].Tag == ApplicationAddResponse {
10992
err := GetLDAPError(packet)
11093
if err != nil {
@@ -113,7 +96,5 @@ func (l *Conn) Add(addRequest *AddRequest) error {
11396
} else {
11497
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
11598
}
116-
117-
l.Debug.Printf("%d: returning", msgCtx.id)
11899
return nil
119100
}

bind.go

+50-33
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"errors"
55
"fmt"
66

7-
"gopkg.in/asn1-ber.v1"
7+
ber "gopkg.in/asn1-ber.v1"
88
)
99

1010
// SimpleBindRequest represents a username/password bind operation
@@ -35,13 +35,18 @@ func NewSimpleBindRequest(username string, password string, controls []Control)
3535
}
3636
}
3737

38-
func (bindRequest *SimpleBindRequest) encode() *ber.Packet {
39-
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
40-
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
41-
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, bindRequest.Username, "User Name"))
42-
request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, bindRequest.Password, "Password"))
38+
func (req *SimpleBindRequest) appendTo(envelope *ber.Packet) error {
39+
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
40+
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
41+
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Username, "User Name"))
42+
pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.Password, "Password"))
4343

44-
return request
44+
envelope.AppendChild(pkt)
45+
if len(req.Controls) > 0 {
46+
envelope.AppendChild(encodeControls(req.Controls))
47+
}
48+
49+
return nil
4550
}
4651

4752
// SimpleBind performs the simple bind operation defined in the given request
@@ -50,41 +55,17 @@ func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResu
5055
return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
5156
}
5257

53-
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
54-
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
55-
encodedBindRequest := simpleBindRequest.encode()
56-
packet.AppendChild(encodedBindRequest)
57-
if len(simpleBindRequest.Controls) > 0 {
58-
packet.AppendChild(encodeControls(simpleBindRequest.Controls))
59-
}
60-
61-
if l.Debug {
62-
ber.PrintPacket(packet)
63-
}
64-
65-
msgCtx, err := l.sendMessage(packet)
58+
msgCtx, err := l.doRequest(simpleBindRequest)
6659
if err != nil {
6760
return nil, err
6861
}
6962
defer l.finishMessage(msgCtx)
7063

71-
packetResponse, ok := <-msgCtx.responses
72-
if !ok {
73-
return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
74-
}
75-
packet, err = packetResponse.ReadPacket()
76-
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
64+
packet, err := l.readPacket(msgCtx)
7765
if err != nil {
7866
return nil, err
7967
}
8068

81-
if l.Debug {
82-
if err = addLDAPDescriptions(packet); err != nil {
83-
return nil, err
84-
}
85-
ber.PrintPacket(packet)
86-
}
87-
8869
result := &SimpleBindResult{
8970
Controls: make([]Control, 0),
9071
}
@@ -133,3 +114,39 @@ func (l *Conn) UnauthenticatedBind(username string) error {
133114
_, err := l.SimpleBind(req)
134115
return err
135116
}
117+
118+
var externalBindRequest = requestFunc(func(envelope *ber.Packet) error {
119+
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
120+
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
121+
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
122+
123+
saslAuth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
124+
saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "EXTERNAL", "SASL Mech"))
125+
saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "SASL Cred"))
126+
127+
pkt.AppendChild(saslAuth)
128+
129+
envelope.AppendChild(pkt)
130+
131+
return nil
132+
})
133+
134+
// ExternalBind performs SASL/EXTERNAL authentication.
135+
//
136+
// Use ldap.DialURL("ldapi://") to connect to the Unix socket before ExternalBind.
137+
//
138+
// See https://tools.ietf.org/html/rfc4422#appendix-A
139+
func (l *Conn) ExternalBind() error {
140+
msgCtx, err := l.doRequest(externalBindRequest)
141+
if err != nil {
142+
return err
143+
}
144+
defer l.finishMessage(msgCtx)
145+
146+
packet, err := l.readPacket(msgCtx)
147+
if err != nil {
148+
return err
149+
}
150+
151+
return GetLDAPError(packet)
152+
}

client.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,23 @@ import (
88
// Client knows how to interact with an LDAP server
99
type Client interface {
1010
Start()
11-
StartTLS(config *tls.Config) error
11+
StartTLS(*tls.Config) error
1212
Close()
1313
SetTimeout(time.Duration)
1414

1515
Bind(username, password string) error
16-
SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error)
16+
UnauthenticatedBind(username string) error
17+
SimpleBind(*SimpleBindRequest) (*SimpleBindResult, error)
18+
ExternalBind() error
1719

18-
Add(addRequest *AddRequest) error
19-
Del(delRequest *DelRequest) error
20-
Modify(modifyRequest *ModifyRequest) error
21-
ModifyDN(modifyDNRequest *ModifyDNRequest) error
20+
Add(*AddRequest) error
21+
Del(*DelRequest) error
22+
Modify(*ModifyRequest) error
23+
ModifyDN(*ModifyDNRequest) error
2224

2325
Compare(dn, attribute, value string) (bool, error)
24-
PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error)
26+
PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error)
2527

26-
Search(searchRequest *SearchRequest) (*SearchResult, error)
28+
Search(*SearchRequest) (*SearchResult, error)
2729
SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
2830
}

compare.go

+26-29
Original file line numberDiff line numberDiff line change
@@ -20,53 +20,50 @@
2020
package ldap
2121

2222
import (
23-
"errors"
2423
"fmt"
2524

26-
"gopkg.in/asn1-ber.v1"
25+
ber "gopkg.in/asn1-ber.v1"
2726
)
2827

29-
// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise
30-
// false with any error that occurs if any.
31-
func (l *Conn) Compare(dn, attribute, value string) (bool, error) {
32-
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
33-
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
28+
// CompareRequest represents an LDAP CompareRequest operation.
29+
type CompareRequest struct {
30+
DN string
31+
Attribute string
32+
Value string
33+
}
3434

35-
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request")
36-
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN"))
35+
func (req *CompareRequest) appendTo(envelope *ber.Packet) error {
36+
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request")
37+
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN"))
3738

3839
ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion")
39-
ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "AttributeDesc"))
40-
ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "AssertionValue"))
41-
request.AppendChild(ava)
42-
packet.AppendChild(request)
40+
ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Attribute, "AttributeDesc"))
41+
ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Value, "AssertionValue"))
4342

44-
l.Debug.PrintPacket(packet)
43+
pkt.AppendChild(ava)
4544

46-
msgCtx, err := l.sendMessage(packet)
45+
envelope.AppendChild(pkt)
46+
47+
return nil
48+
}
49+
50+
// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise
51+
// false with any error that occurs if any.
52+
func (l *Conn) Compare(dn, attribute, value string) (bool, error) {
53+
msgCtx, err := l.doRequest(&CompareRequest{
54+
DN: dn,
55+
Attribute: attribute,
56+
Value: value})
4757
if err != nil {
4858
return false, err
4959
}
5060
defer l.finishMessage(msgCtx)
5161

52-
l.Debug.Printf("%d: waiting for response", msgCtx.id)
53-
packetResponse, ok := <-msgCtx.responses
54-
if !ok {
55-
return false, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
56-
}
57-
packet, err = packetResponse.ReadPacket()
58-
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
62+
packet, err := l.readPacket(msgCtx)
5963
if err != nil {
6064
return false, err
6165
}
6266

63-
if l.Debug {
64-
if err := addLDAPDescriptions(packet); err != nil {
65-
return false, err
66-
}
67-
ber.PrintPacket(packet)
68-
}
69-
7067
if packet.Children[1].Tag == ApplicationCompareResponse {
7168
err := GetLDAPError(packet)
7269

conn.go

+10-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"sync/atomic"
1212
"time"
1313

14-
"gopkg.in/asn1-ber.v1"
14+
ber "gopkg.in/asn1-ber.v1"
1515
)
1616

1717
const (
@@ -140,7 +140,6 @@ func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
140140
// or ldap:// specified as protocol. On success a new Conn for the connection
141141
// is returned.
142142
func DialURL(addr string) (*Conn, error) {
143-
144143
lurl, err := url.Parse(addr)
145144
if err != nil {
146145
return nil, NewError(ErrorNetwork, err)
@@ -154,6 +153,11 @@ func DialURL(addr string) (*Conn, error) {
154153
}
155154

156155
switch lurl.Scheme {
156+
case "ldapi":
157+
if lurl.Path == "" || lurl.Path == "/" {
158+
lurl.Path = "/var/run/slapd/ldapi"
159+
}
160+
return Dial("unix", lurl.Path)
157161
case "ldap":
158162
if port == "" {
159163
port = DefaultLdapPort
@@ -490,11 +494,13 @@ func (l *Conn) reader() {
490494
// A read error is expected here if we are closing the connection...
491495
if !l.IsClosing() {
492496
l.closeErr.Store(fmt.Errorf("unable to read LDAP response packet: %s", err))
493-
l.Debug.Printf("reader error: %s", err.Error())
497+
l.Debug.Printf("reader error: %s", err)
494498
}
495499
return
496500
}
497-
addLDAPDescriptions(packet)
501+
if err := addLDAPDescriptions(packet); err != nil {
502+
l.Debug.Printf("descriptions error: %s", err)
503+
}
498504
if len(packet.Children) == 0 {
499505
l.Debug.Printf("Received bad ldap packet")
500506
continue

0 commit comments

Comments
 (0)