mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-23 11:12:44 +01:00
153 lines
4.7 KiB
Go
153 lines
4.7 KiB
Go
|
package ldap
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
ber "github.com/go-asn1-ber/asn1-ber"
|
||
|
)
|
||
|
|
||
|
// SimpleBindRequest represents a username/password bind operation
|
||
|
type SimpleBindRequest struct {
|
||
|
// Username is the name of the Directory object that the client wishes to bind as
|
||
|
Username string
|
||
|
// Password is the credentials to bind with
|
||
|
Password string
|
||
|
// Controls are optional controls to send with the bind request
|
||
|
Controls []Control
|
||
|
// AllowEmptyPassword sets whether the client allows binding with an empty password
|
||
|
// (normally used for unauthenticated bind).
|
||
|
AllowEmptyPassword bool
|
||
|
}
|
||
|
|
||
|
// SimpleBindResult contains the response from the server
|
||
|
type SimpleBindResult struct {
|
||
|
Controls []Control
|
||
|
}
|
||
|
|
||
|
// NewSimpleBindRequest returns a bind request
|
||
|
func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
|
||
|
return &SimpleBindRequest{
|
||
|
Username: username,
|
||
|
Password: password,
|
||
|
Controls: controls,
|
||
|
AllowEmptyPassword: false,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (req *SimpleBindRequest) appendTo(envelope *ber.Packet) error {
|
||
|
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
|
||
|
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
|
||
|
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Username, "User Name"))
|
||
|
pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.Password, "Password"))
|
||
|
|
||
|
envelope.AppendChild(pkt)
|
||
|
if len(req.Controls) > 0 {
|
||
|
envelope.AppendChild(encodeControls(req.Controls))
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SimpleBind performs the simple bind operation defined in the given request
|
||
|
func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) {
|
||
|
if simpleBindRequest.Password == "" && !simpleBindRequest.AllowEmptyPassword {
|
||
|
return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
|
||
|
}
|
||
|
|
||
|
msgCtx, err := l.doRequest(simpleBindRequest)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer l.finishMessage(msgCtx)
|
||
|
|
||
|
packet, err := l.readPacket(msgCtx)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
result := &SimpleBindResult{
|
||
|
Controls: make([]Control, 0),
|
||
|
}
|
||
|
|
||
|
if len(packet.Children) == 3 {
|
||
|
for _, child := range packet.Children[2].Children {
|
||
|
decodedChild, decodeErr := DecodeControl(child)
|
||
|
if decodeErr != nil {
|
||
|
return nil, fmt.Errorf("failed to decode child control: %s", decodeErr)
|
||
|
}
|
||
|
result.Controls = append(result.Controls, decodedChild)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
err = GetLDAPError(packet)
|
||
|
return result, err
|
||
|
}
|
||
|
|
||
|
// Bind performs a bind with the given username and password.
|
||
|
//
|
||
|
// It does not allow unauthenticated bind (i.e. empty password). Use the UnauthenticatedBind method
|
||
|
// for that.
|
||
|
func (l *Conn) Bind(username, password string) error {
|
||
|
req := &SimpleBindRequest{
|
||
|
Username: username,
|
||
|
Password: password,
|
||
|
AllowEmptyPassword: false,
|
||
|
}
|
||
|
_, err := l.SimpleBind(req)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// UnauthenticatedBind performs an unauthenticated bind.
|
||
|
//
|
||
|
// A username may be provided for trace (e.g. logging) purpose only, but it is normally not
|
||
|
// authenticated or otherwise validated by the LDAP server.
|
||
|
//
|
||
|
// See https://tools.ietf.org/html/rfc4513#section-5.1.2 .
|
||
|
// See https://tools.ietf.org/html/rfc4513#section-6.3.1 .
|
||
|
func (l *Conn) UnauthenticatedBind(username string) error {
|
||
|
req := &SimpleBindRequest{
|
||
|
Username: username,
|
||
|
Password: "",
|
||
|
AllowEmptyPassword: true,
|
||
|
}
|
||
|
_, err := l.SimpleBind(req)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
var externalBindRequest = requestFunc(func(envelope *ber.Packet) error {
|
||
|
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
|
||
|
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
|
||
|
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
|
||
|
|
||
|
saslAuth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
|
||
|
saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "EXTERNAL", "SASL Mech"))
|
||
|
saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "SASL Cred"))
|
||
|
|
||
|
pkt.AppendChild(saslAuth)
|
||
|
|
||
|
envelope.AppendChild(pkt)
|
||
|
|
||
|
return nil
|
||
|
})
|
||
|
|
||
|
// ExternalBind performs SASL/EXTERNAL authentication.
|
||
|
//
|
||
|
// Use ldap.DialURL("ldapi://") to connect to the Unix socket before ExternalBind.
|
||
|
//
|
||
|
// See https://tools.ietf.org/html/rfc4422#appendix-A
|
||
|
func (l *Conn) ExternalBind() error {
|
||
|
msgCtx, err := l.doRequest(externalBindRequest)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer l.finishMessage(msgCtx)
|
||
|
|
||
|
packet, err := l.readPacket(msgCtx)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return GetLDAPError(packet)
|
||
|
}
|