diff options
Diffstat (limited to 'go/src/nshd/nslcd_backend/db_pam.go')
-rw-r--r-- | go/src/nshd/nslcd_backend/db_pam.go | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/go/src/nshd/nslcd_backend/db_pam.go b/go/src/nshd/nslcd_backend/db_pam.go new file mode 100644 index 0000000..b704620 --- /dev/null +++ b/go/src/nshd/nslcd_backend/db_pam.go @@ -0,0 +1,204 @@ +// Copyright 2015-2016 Luke Shumaker <git.lukeshu@sbcglobal>. +// +// This is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this manual; if not, see +// <http://www.gnu.org/licenses/>. + +package nslcd_backend + +import ( + "fmt" + "os" + + "nshd/util" + "nshd/nshd_files" + + s "golang.org/x/sys/unix" + p "git.lukeshu.com/go/libnslcd/nslcd_proto" + + "git.lukeshu.com/go/libgnulinux/crypt" + "git.lukeshu.com/go/libsystemd/sd_daemon" +) + +func checkPassword(password string, hash string) bool { + return crypt.Crypt(password, hash) == hash +} + +func hashPassword(newPassword string, oldHash string) string { + salt := oldHash + if salt == "!" { + str, err := util.RandomString(crypt.SaltAlphabet, 8) + if err != nil { + sd_daemon.Log.Err("Could not generate a random string") + str = "" + } + salt = "$6$" + str + "$" + } + return crypt.Crypt(newPassword, salt) +} + +func dirExists(path string) bool { + stat, err := os.Stat(path) + if err != nil { + return false + } + return stat.IsDir() +} + +func (o *Hackers) PAM_Authentication(cred s.Ucred, req p.Request_PAM_Authentication) <-chan p.PAM_Authentication { + o.lock.RLock() + ret := make(chan p.PAM_Authentication) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + if len(req.UserName) == 0 && len(req.Password) == 0 && cred.Uid == 0 { + ret <- p.PAM_Authentication{ + AuthenticationResult: p.NSLCD_PAM_SUCCESS, + UserName: "", + AuthorizationResult: p.NSLCD_PAM_SUCCESS, + AuthorizationError: "", + } + return + } + + uid := o.name2uid(req.UserName) + if uid < 0 { + return + } + + user := o.users[uid] + obj := p.PAM_Authentication{ + AuthenticationResult: p.NSLCD_PAM_AUTH_ERR, + UserName: "", + AuthorizationResult: p.NSLCD_PAM_AUTH_ERR, + AuthorizationError: "", + } + if checkPassword(req.Password, user.Passwd.PwHash) { + obj.AuthenticationResult = p.NSLCD_PAM_SUCCESS + obj.AuthorizationResult = obj.AuthenticationResult + obj.UserName = user.Passwd.Name + } + ret <- obj + }() + return ret +} + +func (o *Hackers) PAM_Authorization(cred s.Ucred, req p.Request_PAM_Authorization) <-chan p.PAM_Authorization { + o.lock.RLock() + ret := make(chan p.PAM_Authorization) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + uid := o.name2uid(req.UserName) + if uid < 0 { + return + } + ret <- p.PAM_Authorization{ + Result: p.NSLCD_PAM_SUCCESS, + Error: "", + } + }() + return ret +} + +const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + +func (o *Hackers) PAM_SessionOpen(cred s.Ucred, req p.Request_PAM_SessionOpen) <-chan p.PAM_SessionOpen { + ret := make(chan p.PAM_SessionOpen) + go func() { + defer close(ret) + + sessionid, err := util.RandomString(alphabet, 24) + if err != nil { + return + } + ret <- p.PAM_SessionOpen{SessionID: sessionid} + }() + return ret +} + +func (o *Hackers) PAM_SessionClose(cred s.Ucred, req p.Request_PAM_SessionClose) <-chan p.PAM_SessionClose { + ret := make(chan p.PAM_SessionClose) + go close(ret) + return ret +} + +func (o *Hackers) PAM_PwMod(cred s.Ucred, req p.Request_PAM_PwMod) <-chan p.PAM_PwMod { + ret := make(chan p.PAM_PwMod) + o.lock.Lock() + go func() { + defer close(ret) + defer o.lock.Unlock() + + uid := o.name2uid(req.UserName) + if uid < 0 { + return + } + user := o.users[uid] + + // Check the OldPassword + if req.AsRoot == 1 && cred.Uid == 0 { + goto update + } + // special hack: if the old password is not + // set, but the home directory exists, let the + // user set their password + if user.Passwd.PwHash == "!" && dirExists(user.Passwd.HomeDir) { + goto update + } + if !checkPassword(req.OldPassword, user.Passwd.PwHash) { + ret <- p.PAM_PwMod{ + Result: p.NSLCD_PAM_PERM_DENIED, + Error: fmt.Sprintf("password change failed: %s", "Old password did not match"), + } + return + } + update: + if len(req.NewPassword) == 0 { + ret <- p.PAM_PwMod{ + Result: p.NSLCD_PAM_PERM_DENIED, + Error: "password cannot be empty", + } + return + } + + // Update the PwHash in memory + user.Passwd.PwHash = hashPassword(req.NewPassword, user.Passwd.PwHash) + if len(user.Passwd.PwHash) == 0 { + sd_daemon.Log.Err("Password hashing failed") + return + } + + // Update the PwHash on disk + passwords := make(map[string]string, len(o.users)) + for _, ouser := range o.users { + passwords[ouser.Passwd.Name] = ouser.Passwd.PwHash + } + passwords[user.Passwd.Name] = user.Passwd.PwHash + err := nshd_files.SaveAllPasswords(passwords) + if err != nil { + sd_daemon.Log.Err(fmt.Sprintf("Writing passwords to disk: %v", err)) + return + } + + // Ok, we're done, commit the changes + o.users[uid] = user + ret <- p.PAM_PwMod{ + Result: p.NSLCD_PAM_SUCCESS, + Error: "", + } + }() + return ret +} |