diff --git a/addy.go b/addy.go index 5866f70..fc12c8f 100644 --- a/addy.go +++ b/addy.go @@ -115,6 +115,7 @@ func addyAliasAPI(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } + defer conn.Close() err = conn.ServiceBind() if err != nil { @@ -197,6 +198,7 @@ func addyAliasAPIDelete(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } + defer conn.Close() err = conn.ServiceBind() if err != nil { diff --git a/change.go b/change.go index 4e33727..cab8b9e 100644 --- a/change.go +++ b/change.go @@ -67,31 +67,51 @@ func changePassword(w http.ResponseWriter, r *http.Request) { // Check the two new passwords are identical if r.PostFormValue("newpassword") != r.PostFormValue("new2password") { renderError(http.StatusNotAcceptable, "New passwords are not identical. Please retry.") - } else if len(r.PostFormValue("login")) == 0 { - renderError(http.StatusNotAcceptable, "Please provide a valid login") - } else if err := checkPasswdConstraint(r.PostFormValue("newpassword")); err != nil { - renderError(http.StatusNotAcceptable, "The password you chose doesn't respect all constraints: "+err.Error()) - } else { - conn, err := myLDAP.Connect() - if err != nil || conn == nil { - log.Println(err) - renderError(http.StatusInternalServerError, "Unable to process your request. Please try again later.") - } else if err := conn.ServiceBind(); err != nil { - log.Println(err) - renderError(http.StatusInternalServerError, "Unable to process your request. Please try again later.") - } else if dn, err := conn.SearchDN(r.PostFormValue("login"), true); err != nil { - log.Println(err) - // User not found: perform a dummy bind to prevent username enumeration via timing. - conn.Bind("cn=dummy,"+myLDAP.BaseDN, r.PostFormValue("password")) - renderError(http.StatusUnauthorized, "Invalid login or password.") - } else if err := conn.Bind(dn, r.PostFormValue("password")); err != nil { - log.Println(err) - renderError(http.StatusUnauthorized, "Invalid login or password.") - } else if err := conn.ChangePassword(dn, r.PostFormValue("newpassword")); err != nil { - log.Println(err) - renderError(http.StatusInternalServerError, "Unable to process your request. Please try again later.") - } else { - displayMsg(w, "Password successfully changed!", http.StatusOK) - } + return } + if len(r.PostFormValue("login")) == 0 { + renderError(http.StatusNotAcceptable, "Please provide a valid login") + return + } + if err := checkPasswdConstraint(r.PostFormValue("newpassword")); err != nil { + renderError(http.StatusNotAcceptable, "The password you chose doesn't respect all constraints: "+err.Error()) + return + } + + conn, err := myLDAP.Connect() + if err != nil || conn == nil { + log.Println(err) + renderError(http.StatusInternalServerError, "Unable to process your request. Please try again later.") + return + } + defer conn.Close() + + if err := conn.ServiceBind(); err != nil { + log.Println(err) + renderError(http.StatusInternalServerError, "Unable to process your request. Please try again later.") + return + } + + dn, err := conn.SearchDN(r.PostFormValue("login"), true) + if err != nil { + log.Println(err) + // User not found: perform a dummy bind to prevent username enumeration via timing. + conn.Bind("cn=dummy,"+myLDAP.BaseDN, r.PostFormValue("password")) + renderError(http.StatusUnauthorized, "Invalid login or password.") + return + } + + if err := conn.Bind(dn, r.PostFormValue("password")); err != nil { + log.Println(err) + renderError(http.StatusUnauthorized, "Invalid login or password.") + return + } + + if err := conn.ChangePassword(dn, r.PostFormValue("newpassword")); err != nil { + log.Println(err) + renderError(http.StatusInternalServerError, "Unable to process your request. Please try again later.") + return + } + + displayMsg(w, "Password successfully changed!", http.StatusOK) } diff --git a/ldap.go b/ldap.go index 85271fe..88318c0 100644 --- a/ldap.go +++ b/ldap.go @@ -6,6 +6,8 @@ import ( "encoding/base64" "errors" "fmt" + "strconv" + "time" "github.com/amoghe/go-crypt" "github.com/go-ldap/ldap/v3" @@ -58,6 +60,10 @@ type LDAPConn struct { connection *ldap.Conn } +func (l LDAPConn) Close() { + l.connection.Close() +} + func (l LDAPConn) ServiceBind() error { return l.connection.Bind(l.ServiceDN, l.ServicePassword) } @@ -133,6 +139,7 @@ func (l LDAPConn) ChangePassword(dn string, rawpassword string) error { modify := ldap.NewModifyRequest(dn, nil) modify.Replace("userPassword", []string{"{CRYPT}" + hashedpasswd}) + modify.Replace("shadowLastChange", []string{strconv.FormatInt(time.Now().Unix()/86400, 10)}) return l.connection.Modify(modify) } diff --git a/login.go b/login.go index 60e3f8f..41b9bc9 100644 --- a/login.go +++ b/login.go @@ -78,6 +78,7 @@ func login(login string, password string) ([]*ldap.EntryAttribute, error) { if err != nil || conn == nil { return nil, err } + defer conn.Close() if err = conn.ServiceBind(); err != nil { return nil, err diff --git a/lost.go b/lost.go index 8fbef23..7f84b57 100644 --- a/lost.go +++ b/lost.go @@ -129,6 +129,7 @@ func lostPassword(w http.ResponseWriter, r *http.Request) { displayTmplError(w, http.StatusInternalServerError, "lost.html", map[string]any{"error": "Unable to process your request. Please try again later."}) return } + defer conn.Close() // Generate the token token, dn, err := lostPasswordToken(conn, r.PostFormValue("login")) diff --git a/main.go b/main.go index 5701f36..07478b5 100644 --- a/main.go +++ b/main.go @@ -207,6 +207,7 @@ func main() { if err != nil || conn == nil { log.Fatalf("Unable to connect to LDAP: %s", err.Error()) } + defer conn.Close() token, dn, err := lostPasswordToken(conn, login) if err != nil { diff --git a/reset.go b/reset.go index 143976e..ce9efb5 100644 --- a/reset.go +++ b/reset.go @@ -79,6 +79,7 @@ func resetPassword(w http.ResponseWriter, r *http.Request) { renderError(http.StatusInternalServerError, "Unable to process your request. Please try again later.") return } + defer conn.Close() // Bind as service to perform the password change err = conn.ServiceBind()