summaryrefslogtreecommitdiff
path: root/go/nshd/nslcd_backend/db_pam.go
diff options
context:
space:
mode:
Diffstat (limited to 'go/nshd/nslcd_backend/db_pam.go')
-rw-r--r--go/nshd/nslcd_backend/db_pam.go204
1 files changed, 204 insertions, 0 deletions
diff --git a/go/nshd/nslcd_backend/db_pam.go b/go/nshd/nslcd_backend/db_pam.go
new file mode 100644
index 0000000..b704620
--- /dev/null
+++ b/go/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
+}