chldapasswd/ldap.go

138 lines
3.1 KiB
Go

package main
import (
"crypto/rand"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"github.com/amoghe/go-crypt"
"github.com/go-ldap/ldap/v3"
)
type LDAP struct {
Host string
Port int
Starttls bool
Ssl bool
BaseDN string
ServiceDN string
ServicePassword string
MailHost string
MailPort int
MailUser string
MailPassword string
}
func (l LDAP) Connect() (*LDAPConn, error) {
if l.Ssl {
if c, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", l.Host, l.Port), &tls.Config{ServerName: l.Host}); err != nil {
return nil, errors.New("unable to establish LDAPS connection to " + fmt.Sprintf("%s:%d", l.Host, l.Port) + ": " + err.Error())
} else {
return &LDAPConn{
LDAP: l,
connection: c,
}, nil
}
} else if c, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", l.Host, l.Port)); err != nil {
return nil, errors.New("unable to establish LDAP connection to " + fmt.Sprintf("%s:%d", l.Host, l.Port) + ": " + err.Error())
} else {
if l.Starttls {
if err = c.StartTLS(&tls.Config{ServerName: l.Host}); err != nil {
c.Close()
return nil, errors.New("unable to StartTLS: " + err.Error())
}
}
return &LDAPConn{
LDAP: l,
connection: c,
}, nil
}
}
type LDAPConn struct {
LDAP
connection *ldap.Conn
}
func (l LDAPConn) ServiceBind() error {
return l.connection.Bind(l.ServiceDN, l.ServicePassword)
}
func (l LDAPConn) Bind(username string, password string) error {
return l.connection.Bind(username, password)
}
func (l LDAPConn) SearchDN(username string, person bool) (string, error) {
objectClass := "organizationalPerson"
if !person {
objectClass = "simpleSecurityObject"
}
searchRequest := ldap.NewSearchRequest(
l.BaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=%s)(uid=%s))", objectClass, username),
[]string{"dn"},
nil,
)
sr, err := l.connection.Search(searchRequest)
if err != nil {
return "", err
}
if len(sr.Entries) != 1 {
return "", errors.New("User does not exist or too many entries returned")
}
return sr.Entries[0].DN, nil
}
func (l LDAPConn) GetEntry(dn string) ([]*ldap.EntryAttribute, error) {
searchRequest := ldap.NewSearchRequest(
dn,
ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,
"(objectClass=*)", []string{}, nil,
)
sr, err := l.connection.Search(searchRequest)
if err != nil {
return nil, err
}
if len(sr.Entries) != 1 {
return nil, errors.New("User does not exist or too many entries returned")
}
return sr.Entries[0].Attributes, nil
}
func genSalt() (string, error) {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(b), nil
}
func (l LDAPConn) ChangePassword(dn string, rawpassword string) error {
salt, err := genSalt()
if err != nil {
return err
}
hashedpasswd, err := crypt.Crypt(rawpassword, "$6$"+salt+"$")
if err != nil {
return err
}
modify := ldap.NewModifyRequest(dn, nil)
modify.Replace("userPassword", []string{"{CRYPT}" + hashedpasswd})
return l.connection.Modify(modify)
}