optimisations
* use pyke's re cache * get an unlimited number of ldap attributes * get a perturbator for the OTP secret, in case of stolen phone * lowercase the username, to avoid strange behaviour with the OTP
This commit is contained in:
102
ldap.go
102
ldap.go
@@ -6,28 +6,27 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pyke369/golang-support/rcache"
|
||||
"gopkg.in/ldap.v2"
|
||||
)
|
||||
|
||||
type ldapConfig struct {
|
||||
servers []string
|
||||
baseDN string
|
||||
bindCn string
|
||||
bindPw string
|
||||
searchFilter string
|
||||
primaryAttribute string
|
||||
secondaryAttribute string
|
||||
validGroups []string
|
||||
mfaType string
|
||||
certAuth string
|
||||
ipMin net.IP
|
||||
ipMax net.IP
|
||||
upgradeFrom string
|
||||
routes []string
|
||||
servers []string
|
||||
baseDN string
|
||||
bindCn string
|
||||
bindPw string
|
||||
searchFilter string
|
||||
attributes []string
|
||||
validGroups []string
|
||||
mfaType string
|
||||
certAuth string
|
||||
ipMin net.IP
|
||||
ipMax net.IP
|
||||
upgradeFrom string
|
||||
routes []string
|
||||
}
|
||||
|
||||
func (l *ldapConfig) addIPRange(s string) error {
|
||||
@@ -46,24 +45,28 @@ func (l *ldapConfig) addIPRange(s string) error {
|
||||
|
||||
// auth loop. Try all auth profiles from startProfile
|
||||
// return the last possible profile and the mail if we found a mail like login
|
||||
func (s *OpenVpnMgt) AuthLoop(startProfile, user, pass string, overridePwdCheck bool) (string, string) {
|
||||
func (s *OpenVpnMgt) AuthLoop(startProfile, user, pass string, overridePwdCheck bool) (string, string, string) {
|
||||
login := []string{user}
|
||||
profile := startProfile
|
||||
mail := ""
|
||||
otpSalt := ""
|
||||
|
||||
re := regexp.MustCompile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$")
|
||||
re := rcache.Get("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$")
|
||||
|
||||
for {
|
||||
if re.MatchString(login[0]) && mail == "" {
|
||||
if mail == "" && re.MatchString(login[0]) {
|
||||
mail = login[0]
|
||||
}
|
||||
n := profile
|
||||
|
||||
for k, ldap := range s.ldap {
|
||||
if ldap.upgradeFrom != profile {
|
||||
continue
|
||||
}
|
||||
err, userOk, passOk, secondary := ldap.Auth(login, pass)
|
||||
err, userOk, passOk, attributes := ldap.Auth(login, pass)
|
||||
|
||||
if otpSalt == "" && len(attributes) > 1 && len(attributes[1]) > 0 {
|
||||
otpSalt = attributes[1][0]
|
||||
}
|
||||
|
||||
// if there is an error, try the other configurations
|
||||
if err != nil {
|
||||
@@ -74,7 +77,7 @@ func (s *OpenVpnMgt) AuthLoop(startProfile, user, pass string, overridePwdCheck
|
||||
// we did find a valid User
|
||||
if userOk {
|
||||
// the login for the new auth level is given by the current one
|
||||
login = secondary
|
||||
login = attributes[0]
|
||||
|
||||
if passOk && profile != "" {
|
||||
// it's at least the second auth level, and we have a valid
|
||||
@@ -96,7 +99,7 @@ func (s *OpenVpnMgt) AuthLoop(startProfile, user, pass string, overridePwdCheck
|
||||
}
|
||||
}
|
||||
|
||||
return profile, mail
|
||||
return profile, mail, otpSalt
|
||||
}
|
||||
|
||||
// override the real DialTLS function
|
||||
@@ -116,13 +119,11 @@ func myDialTLS(network, addr string, config *tls.Config) (*ldap.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (conf *ldapConfig) Auth(logins []string, pass string) (e error, userOk, passOk bool, attributes []string) {
|
||||
var primary, secondary []string
|
||||
|
||||
func (conf *ldapConfig) Auth(logins []string, pass string) (e error, userOk, passOk bool, attributes [][]string) {
|
||||
// special case. This configuration is a filter on the previous one
|
||||
if len(conf.servers) == 0 && len(conf.validGroups) > 0 {
|
||||
if inArray(logins, conf.validGroups) {
|
||||
return nil, true, false, logins
|
||||
return nil, true, false, [][]string{logins}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +132,7 @@ func (conf *ldapConfig) Auth(logins []string, pass string) (e error, userOk, pas
|
||||
return nil, false, false, nil
|
||||
}
|
||||
|
||||
attributes = logins
|
||||
attributes = [][]string{logins}
|
||||
for _, s := range conf.servers {
|
||||
// we force ldaps because we can
|
||||
l, err := myDialTLS("tcp", s+":636", &tls.Config{ServerName: s})
|
||||
@@ -147,10 +148,7 @@ func (conf *ldapConfig) Auth(logins []string, pass string) (e error, userOk, pas
|
||||
return err, false, false, nil
|
||||
}
|
||||
|
||||
search := []string{"dn", conf.primaryAttribute}
|
||||
if conf.secondaryAttribute != "" {
|
||||
search = append(search, conf.secondaryAttribute)
|
||||
}
|
||||
search := append(conf.attributes, "dn")
|
||||
|
||||
// search the user
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
@@ -172,39 +170,51 @@ func (conf *ldapConfig) Auth(logins []string, pass string) (e error, userOk, pas
|
||||
|
||||
// check the attributes requested in the search
|
||||
// a valid account must be part of the correct group (per instance)
|
||||
for _, attribute := range sr.Entries[0].Attributes {
|
||||
if (*attribute).Name == conf.primaryAttribute {
|
||||
primary = attribute.Values
|
||||
|
||||
ret := [][]string{}
|
||||
|
||||
for _, needed := range conf.attributes {
|
||||
ok := false
|
||||
for _, attribute := range sr.Entries[0].Attributes {
|
||||
if (*attribute).Name == needed {
|
||||
ret = append(ret, attribute.Values)
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if (*attribute).Name == conf.secondaryAttribute {
|
||||
secondary = attribute.Values
|
||||
if !ok {
|
||||
ret = append(ret, []string{})
|
||||
}
|
||||
}
|
||||
|
||||
// user must have both primary and secondary attributes
|
||||
if len(primary) == 0 {
|
||||
log.Printf("User %s has no %s attribute", logins[0], conf.primaryAttribute)
|
||||
// user must have both the first and second attributes
|
||||
if len(ret[0]) == 0 {
|
||||
log.Printf("User %s has no %s attribute", logins[0], conf.attributes[0])
|
||||
return nil, false, false, nil
|
||||
}
|
||||
|
||||
if len(secondary) == 0 {
|
||||
log.Printf("User %s has no %s attribute", logins[0], conf.secondaryAttribute)
|
||||
if len(ret[1]) == 0 {
|
||||
log.Printf("User %s has no %s attribute", logins[0], conf.attributes[1])
|
||||
return nil, false, false, nil
|
||||
}
|
||||
|
||||
// check if the primary attributes are in the validGroups list
|
||||
if len(conf.validGroups) > 0 && !inArray(conf.validGroups, primary) {
|
||||
// check if the first attribute valus are in the validGroups list
|
||||
if len(conf.validGroups) > 0 && !inArray(conf.validGroups, ret[0]) {
|
||||
return nil, false, false, nil
|
||||
}
|
||||
|
||||
// if there is no validGroups check, pass the primary attributes to the
|
||||
// if there is no validGroups check, pass the first attribute values to the
|
||||
// next level
|
||||
if len(conf.validGroups) == 0 {
|
||||
attributes = primary
|
||||
attributes = [][]string{ret[0]}
|
||||
} else {
|
||||
attributes = secondary
|
||||
attributes = [][]string{ret[1]}
|
||||
}
|
||||
|
||||
if len(ret) > 2 {
|
||||
attributes = append(attributes, ret[2:]...)
|
||||
}
|
||||
log.Println(attributes)
|
||||
|
||||
log.Printf("User %s has a valid account on %s", logins[0], s)
|
||||
|
||||
userdn := sr.Entries[0].DN
|
||||
|
||||
Reference in New Issue
Block a user