From d4afb027e59356442be7e702d64097c0da78bbe2 Mon Sep 17 00:00:00 2001 From: Matt Ouille Date: Sun, 9 Feb 2020 00:17:10 -0800 Subject: [PATCH] Add LDAP support --- irc/accounts.go | 76 ++++++++++++++++++++++++++++++++++++++++++++++++- irc/config.go | 23 +++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/irc/accounts.go b/irc/accounts.go index 67e3d0c5..821d77a9 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -4,6 +4,7 @@ package irc import ( + "crypto/tls" "encoding/json" "fmt" "net/smtp" @@ -14,6 +15,7 @@ import ( "time" "unicode" + "github.com/go-ldap/ldap" "github.com/oragono/oragono/irc/caps" "github.com/oragono/oragono/irc/passwd" "github.com/oragono/oragono/irc/utils" @@ -828,8 +830,80 @@ func (am *AccountManager) checkPassphrase(accountName, passphrase string) (accou return } +func (am *AccountManager) checkLDAPPassphrase(accountName, passphrase string) (account ClientAccount, err error) { + var ( + host, url string + port int + ) + + host = am.server.AccountConfig().LDAP.Servers.Host + port = am.server.AccountConfig().LDAP.Servers.Port + + account, err = am.LoadAccount(accountName) + if err != nil { + return + } + + if !account.Verified { + err = errAccountUnverified + return + } + + if am.server.AccountConfig().LDAP.Servers.UseSSL { + url = fmt.Sprintf("ldaps://%s:%d", host, port) + } else { + url = fmt.Sprintf("ldap://%s:%d", host, port) + } + + l, err := ldap.DialURL(url) + if err != nil { + return + } + defer l.Close() + + if am.server.AccountConfig().LDAP.Servers.StartTLS { + err = l.StartTLS(&tls.Config{InsecureSkipVerify: am.server.AccountConfig().LDAP.Servers.SkipTLSVerify}) + if err != nil { + return + } + } + + err = l.Bind(am.server.AccountConfig().LDAP.BindDN, am.server.AccountConfig().LDAP.BindPwd) + if err != nil { + return + } + + for _, baseDN := range am.server.AccountConfig().LDAP.SearchBaseDNs { + req := ldap.NewSearchRequest(baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, am.server.AccountConfig().LDAP.Timeout, false, fmt.Sprintf("(&(objectClass=organizationalPerson)(uid=%s))", accountName), []string{"dn"}, nil) + sr, err := l.Search(req) + if err != nil { + return + } + + userdn := sr.Entries[0].DN + + if len(sr.Entries) > 0 { + // verify the user passphrase + err = l.Bind(userdn, passphrase) + if err != nil { + continue + } + break + } + } + + return +} + func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName string, passphrase string) error { - account, err := am.checkPassphrase(accountName, passphrase) + var account ClientAccount + var err error + + if am.server.AccountConfig().LDAP.Enabled { + account, err = am.checkLDAPPassphrase(accountName, passphrase) + } + + account, err = am.checkPassphrase(accountName, passphrase) if err != nil { return err } diff --git a/irc/config.go b/irc/config.go index 361cd15f..6a315835 100644 --- a/irc/config.go +++ b/irc/config.go @@ -68,6 +68,7 @@ type AccountConfig struct { Exempted []string exemptedNets []net.IPNet } `yaml:"require-sasl"` + LDAP LDAPConfig LoginThrottling struct { Enabled bool Duration time.Duration @@ -82,6 +83,28 @@ type AccountConfig struct { VHosts VHostConfig } +type LDAPConfig struct { + Timeout int + Enabled bool + AllowSignup bool `yaml:"allow-signup"` + BindDN string `yaml:"bind-dn"` + BindPwd string `yaml:"bind-password"` + SearchFilter string `yaml:"search-filter"` + SearchBaseDNs []string `yaml:"search-base-dns"` + Attributes map[string]string + Servers LDAPServerConfig +} + +type LDAPServerConfig struct { + Host string + Port int + UseSSL bool `yaml:"use-ssl"` + StartTLS bool `yaml:"start-tls"` + SkipTLSVerify bool `yaml:"skip-tls-verify"` + ClientCert string `yaml:"client-cert"` + ClientKey string `yaml:"client-key"` +} + // AccountRegistrationConfig controls account registration. type AccountRegistrationConfig struct { Enabled bool