1package ldap
2
3import (
4	"errors"
5	"fmt"
6
7	"gopkg.in/asn1-ber.v1"
8)
9
10// SimpleBindRequest represents a username/password bind operation
11type SimpleBindRequest struct {
12	// Username is the name of the Directory object that the client wishes to bind as
13	Username string
14	// Password is the credentials to bind with
15	Password string
16	// Controls are optional controls to send with the bind request
17	Controls []Control
18	// AllowEmptyPassword sets whether the client allows binding with an empty password
19	// (normally used for unauthenticated bind).
20	AllowEmptyPassword bool
21}
22
23// SimpleBindResult contains the response from the server
24type SimpleBindResult struct {
25	Controls []Control
26}
27
28// NewSimpleBindRequest returns a bind request
29func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
30	return &SimpleBindRequest{
31		Username:           username,
32		Password:           password,
33		Controls:           controls,
34		AllowEmptyPassword: false,
35	}
36}
37
38func (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"))
43
44	return request
45}
46
47// SimpleBind performs the simple bind operation defined in the given request
48func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) {
49	if simpleBindRequest.Password == "" && !simpleBindRequest.AllowEmptyPassword {
50		return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
51	}
52
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)
66	if err != nil {
67		return nil, err
68	}
69	defer l.finishMessage(msgCtx)
70
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)
77	if err != nil {
78		return nil, err
79	}
80
81	if l.Debug {
82		if err = addLDAPDescriptions(packet); err != nil {
83			return nil, err
84		}
85		ber.PrintPacket(packet)
86	}
87
88	result := &SimpleBindResult{
89		Controls: make([]Control, 0),
90	}
91
92	if len(packet.Children) == 3 {
93		for _, child := range packet.Children[2].Children {
94			decodedChild, decodeErr := DecodeControl(child)
95			if decodeErr != nil {
96				return nil, fmt.Errorf("failed to decode child control: %s", decodeErr)
97			}
98			result.Controls = append(result.Controls, decodedChild)
99		}
100	}
101
102	err = GetLDAPError(packet)
103	return result, err
104}
105
106// Bind performs a bind with the given username and password.
107//
108// It does not allow unauthenticated bind (i.e. empty password). Use the UnauthenticatedBind method
109// for that.
110func (l *Conn) Bind(username, password string) error {
111	req := &SimpleBindRequest{
112		Username:           username,
113		Password:           password,
114		AllowEmptyPassword: false,
115	}
116	_, err := l.SimpleBind(req)
117	return err
118}
119
120// UnauthenticatedBind performs an unauthenticated bind.
121//
122// A username may be provided for trace (e.g. logging) purpose only, but it is normally not
123// authenticated or otherwise validated by the LDAP server.
124//
125// See https://tools.ietf.org/html/rfc4513#section-5.1.2 .
126// See https://tools.ietf.org/html/rfc4513#section-6.3.1 .
127func (l *Conn) UnauthenticatedBind(username string) error {
128	req := &SimpleBindRequest{
129		Username:           username,
130		Password:           "",
131		AllowEmptyPassword: true,
132	}
133	_, err := l.SimpleBind(req)
134	return err
135}
136