// Copyright 2015-2016 Luke Shumaker . // // 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 // . package hackers_nslcd_backend import ( "fmt" "os" "parabola_hackers" s "syscall" "lukeshu.com/git/go/libgnulinux.git/crypt" p "lukeshu.com/git/go/libnslcd.git/proto" "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" ) func checkPassword(password p.String, hash p.String) bool { return crypt.Crypt(string(password), string(hash)) == string(hash) } func hashPassword(newPassword p.String, oldHash p.String) p.String { salt := string(oldHash) if salt == "!" { str, err := parabola_hackers.RandomString(crypt.SaltAlphabet, 8) if err != nil { logger.Err("Could not generate a random string") str = "" } salt = "$6$" + str + "$" } return p.String(crypt.Crypt(string(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: p.String(""), AuthorizationResult: p.NSLCD_PAM_SUCCESS, AuthorizationError: p.String(""), } return } uid := o.name2uid(string(req.UserName)) if uid < 0 { return } user := o.users[uid] obj := p.PAM_Authentication{ AuthenticationResult: p.NSLCD_PAM_AUTH_ERR, UserName: p.String(""), AuthorizationResult: p.NSLCD_PAM_AUTH_ERR, AuthorizationError: p.String(""), } 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(string(req.UserName)) if uid < 0 { return } ret <- p.PAM_Authorization{ Result: p.NSLCD_PAM_SUCCESS, Error: p.String(""), } }() 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 := parabola_hackers.RandomString(alphabet, 24) if err != nil { return } ret <- p.PAM_SessionOpen{SessionID: p.String(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(string(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 string(user.Passwd.PwHash) == "!" && dirExists(string(user.Passwd.HomeDir)) { goto update } if !checkPassword(req.OldPassword, user.Passwd.PwHash) { ret <- p.PAM_PwMod{ Result: p.NSLCD_PAM_PERM_DENIED, Error: p.String(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: p.String("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 { logger.Err("Password hashing failed") return } // Update the PwHash on disk passwords := make(map[string]string, len(o.users)) for _, ouser := range o.users { passwords[string(ouser.Passwd.Name)] = string(ouser.Passwd.PwHash) } passwords[string(user.Passwd.Name)] = string(user.Passwd.PwHash) err := parabola_hackers.SaveAllPasswords(passwords) if err != nil { logger.Err("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: p.String(""), } }() return ret }