From 17086b1abf5cd0c87af9d154741787082f0443fb Mon Sep 17 00:00:00 2001 From: TQ Hirsch Date: Thu, 26 Oct 2023 18:35:18 +0200 Subject: [PATCH] Added username/password auth --- app/ipasso/ldap.go | 34 ++++++++++++++++++++-- app/ipasso/loginKrb.go | 64 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 87 insertions(+), 11 deletions(-) diff --git a/app/ipasso/ldap.go b/app/ipasso/ldap.go index 143723c..cbb53fb 100644 --- a/app/ipasso/ldap.go +++ b/app/ipasso/ldap.go @@ -3,6 +3,7 @@ package main import ( "errors" "flag" + "fmt" "git.thequux.com/thequux/ipasso/sso-proxy/backend" "git.thequux.com/thequux/ipasso/util" "git.thequux.com/thequux/ipasso/util/genpool" @@ -35,8 +36,10 @@ var ( ) var ( - ErrNoValidServer = errors.New("no valid server") - ldapRootLogger, ldapPoolLogger *zap.Logger + ErrNoValidServer = errors.New("no valid server") + ErrLoginFailed = errors.New("login failed") + ldapRootLogger *zap.Logger + ldapPoolLogger *zap.Logger ) func init() { @@ -67,6 +70,9 @@ func init() { ldapRootLogger.Debug("Configured LDAP rootDN", zap.String("rootDN", *ldapRootDN)) } + // configure username basedn + ldapUserBase = "cn=users,cn=accounts," + *ldapRootDN + krb5Config, err := config.Load(*krb5conf) var realmSource string = "" if err != nil { @@ -260,7 +266,31 @@ func getUserByPrincipal(principal string) (*ldap.Entry, error) { func getUserByDn(dn string) (*ldap.Entry, error) { return ldapSearchSingle(&ldap.SearchRequest{ BaseDN: dn, + Filter: "(objectClass=top)", Scope: ldap.ScopeBaseObject, Attributes: interestingUserAttr, }) } + +func getUserByUsernamePassword(username, password string) (*ldap.Entry, error) { + server := selectServer() + if server == nil { + return nil, ErrNoValidServer + } + conn, err := ldap.DialURL(server.Url) + if err != nil { + return nil, err + } + defer conn.Close() + + // try bind + userDN := fmt.Sprintf("uid=%s,%s", username, ldapUserBase) + //ldapRootLogger.Debug("Attempting bind login", zap.String("user", userDN), zap.String("password", password)) + err = conn.Bind(userDN, password) + if err != nil { + return nil, ErrLoginFailed + } + + //ldapRootLogger.Info("Successfully authorized using simple bind", zap.String("user", username), zap.String("dn", userDN)) + return getUserByDn(userDN) +} diff --git a/app/ipasso/loginKrb.go b/app/ipasso/loginKrb.go index c9c3c25..aa5d02d 100644 --- a/app/ipasso/loginKrb.go +++ b/app/ipasso/loginKrb.go @@ -1,20 +1,22 @@ package main import ( + "errors" "git.thequux.com/thequux/ipasso/sso-proxy/backend" "git.thequux.com/thequux/ipasso/util/startup" + "github.com/go-ldap/ldap/v3" "github.com/julienschmidt/httprouter" "log" "net/http" "net/http/fcgi" "strconv" - "strings" "time" ) func init() { startup.Routes.Add(func(router *httprouter.Router) { router.HandlerFunc("POST", "/login/krb5", loginKrb) + router.HandlerFunc("POST", "/login/password", loginPassword) }) } @@ -23,7 +25,6 @@ func loginKrb(w http.ResponseWriter, r *http.Request) { // The fact that we got here implies that the login succeeded env := fcgi.ProcessEnv(r) user := env["GSS_NAME"] - uid := strings.Split(user, "@")[0] expirationTxt, hasExpiration := env["GSS_SESSION_EXPIRATION"] var expiration time.Time if hasExpiration { @@ -41,21 +42,64 @@ func loginKrb(w http.ResponseWriter, r *http.Request) { return } - SessionID, err := datastore.NewSessionID() - if err != nil { - ReportError(w, err) - return - } - entry, err := getUserByPrincipal(user) if err != nil { ReportError(w, err) return } + finishLogin(w, r, entry, &expiration) +} + +func loginPassword(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + ReportError(w, err) + } + user := r.Form.Get("user") + password := r.Form.Get("password") + + if user == "" || password == "" { + ldapRootLogger.Debug("Missing value") + w.WriteHeader(http.StatusUnauthorized) + return + } + + entry, err := getUserByUsernamePassword(user, password) + if errors.Is(err, ErrLoginFailed) { + w.WriteHeader(http.StatusUnauthorized) + return + } else if err != nil { + ReportError(w, err) + return + } + + finishLogin(w, r, entry, nil) +} + +func finishLogin(w http.ResponseWriter, req *http.Request, entry *ldap.Entry, expiration *time.Time) { + SessionID, err := datastore.NewSessionID() + if err != nil { + ReportError(w, err) + return + } + + var uid string + for _, attr := range entry.Attributes { + if attr.Name == "uid" && len(attr.Values) > 0 { + uid = attr.Values[0] + } + } + + // TODO: make configurable + if expiration == nil { + defExpiration := time.Now().Add(time.Hour * 14) + expiration = &defExpiration + } + session := backend.Session{ SessionID: SessionID, - Expiration: expiration, + Expiration: *expiration, UserID: uid, LdapDN: entry.DN, } @@ -65,4 +109,6 @@ func loginKrb(w http.ResponseWriter, r *http.Request) { return } _ = datastore.PutSession(session, sessionCache) + + // TODO: set cookies, return success }