summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-06-17 16:59:45 -0400
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-06-17 16:59:45 -0400
commit13c0d03f324c61e12708ee7184cc37554edc92f7 (patch)
treee3258add66d817486a4066c052a85ea5f0b50cb2
parent24224e37b71994dc54c81cfc67dfd423578b5a59 (diff)
password tracking and changing in etc
-rw-r--r--Makefile19
-rw-r--r--src/parabola_hackers/.gitignore1
-rw-r--r--src/parabola_hackers/nslcd_backend/db_pam.go68
-rw-r--r--src/parabola_hackers/nslcd_backend/hackers.go13
-rw-r--r--src/parabola_hackers/passwords.go.in (renamed from src/parabola_hackers/password.go)70
5 files changed, 146 insertions, 25 deletions
diff --git a/Makefile b/Makefile
index 3540d90..8eaf4f3 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,7 @@ PACKAGE = parabola-hackers
sysusersdir=$(prefix)/lib/sysusers.d
systemunitdir=$(prefix)/lib/systemd/system
conf_file = $(sysconfdir)/$(PACKAGE).yml
+shadow_file = $(sysconfdir)/nshd/shadow
NET ?=
#NET ?= FORCE
user = nshd
@@ -43,7 +44,7 @@ scripts = $(filter-out common.rb common.rb.in,$(notdir $(wildcard $(srcdir)/scri
std.gen_files += LICENSE.lgpl-2.1.txt LICENSE.gpl-2.txt LICENSE.apache-2.0.txt
std.out_files += bin/cmd-nshd nshd.service nshd.sysusers scripts/common.rb test/runner
-std.sys_files += $(addprefix $(bindir)/,nshd $(scripts)) $(systemunitdir)/nshd.socket $(systemunitdir)/nshd.service $(sysusersdir)/nshd.conf $(conf_file)
+std.sys_files += $(addprefix $(bindir)/,nshd $(scripts)) $(systemunitdir)/nshd.socket $(systemunitdir)/nshd.service $(sysusersdir)/nshd.conf $(conf_file) $(shadow_file)
std.clean_files += test/*.o pkg/ .tmp* .var* $(_out)
$(srcdir)/LICENSE.lgpl-2.1.txt: $(NET)
@@ -59,6 +60,7 @@ _gen += src/lukeshu.com/git/go/libnslcd.git/proto/server/interface_backend.go
_gen += src/lukeshu.com/git/go/libnslcd.git/proto/server/func_handlerequest.go
_gen += src/lukeshu.com/git/go/libnslcd.git/proto/server/type_nilbackend.go
_out += src/parabola_hackers/users.go
+_out += src/parabola_hackers/passwords.go
_out += src/cmd-nshd/main.go
$(outdir)/bin/%-nshd: $(call golang.src,$(srcdir)) $(_gen) $(_out)
$(call golang.install,$(topsrcdir),cmd-nshd)
@@ -76,21 +78,36 @@ $(outdir)/nshd.sysusers: $(var)user
$(outdir)/scripts/common.rb: $(var)conf_file
$(outdir)/src/cmd-nshd/main.go: $(var)conf_file
$(outdir)/src/parabola_hackers/users.go: $(var)bindir
+$(outdir)/src/parabola_hackers/passwords.go: $(var)shadow_file
$(DESTDIR)$(bindir)/%: $(outdir)/bin/cmd-%
+ $(NORMAL_INSTALL)
install -TDm755 $< $@
$(DESTDIR)$(bindir)/%: $(srcdir)/scripts/%
+ $(NORMAL_INSTALL)
install -TDm755 $< $@
$(DESTDIR)$(bindir)/common.rb: $(srcdir)/scripts/common.rb
+ $(NORMAL_INSTALL)
install -TDm644 $< $@
$(DESTDIR)$(systemunitdir)/%.socket: $(outdir)/%.socket
+ $(NORMAL_INSTALL)
install -TDm644 $< $@
$(DESTDIR)$(systemunitdir)/%.service: $(outdir)/%.service
+ $(NORMAL_INSTALL)
install -TDm644 $< $@
$(DESTDIR)$(sysusersdir)/%.conf: $(outdir)/%.sysusers
+ $(NORMAL_INSTALL)
install -TDm644 $< $@
$(DESTDIR)$(conf_file): $(srcdir)/parabola-hackers.yml
+ $(NORMAL_INSTALL)
install -TDm644 $< $@
+$(DESTDIR)$(shadow_file): $(var)user $(DESTDIR)$(sysusersdir)/nshd.conf
+ $(NORMAL_INSTALL)
+ install -d $(@D)
+ touch $@
+ $(POST_INSTALL)
+ -systemd-sysusers
+ -chown $(user):$(user) $(@D) $@
.PHONY: FORCE
.SECONDARY:
diff --git a/src/parabola_hackers/.gitignore b/src/parabola_hackers/.gitignore
index e93884b..3be3f08 100644
--- a/src/parabola_hackers/.gitignore
+++ b/src/parabola_hackers/.gitignore
@@ -1 +1,2 @@
/users.go
+/passwords.go
diff --git a/src/parabola_hackers/nslcd_backend/db_pam.go b/src/parabola_hackers/nslcd_backend/db_pam.go
index 303a66c..3374170 100644
--- a/src/parabola_hackers/nslcd_backend/db_pam.go
+++ b/src/parabola_hackers/nslcd_backend/db_pam.go
@@ -17,17 +17,32 @@
package hackers_nslcd_backend
import (
+ "fmt"
"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 string, hash string) bool {
return crypt.Crypt(password, hash) == hash
}
+func hashPassword(newPassword string, oldHash string) string {
+ salt := 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 crypt.Crypt(newPassword, salt)
+}
+
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)
@@ -97,3 +112,56 @@ func (o *Hackers) PAM_SessionClose(cred s.Ucred, req p.Request_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 {
+ 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 the PwHash in memory
+ user.Passwd.PwHash = hashPassword(req.NewPassword, user.Passwd.PwHash)
+ if user.Passwd.PwHash == "" {
+ 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[ouser.Passwd.Name] = ouser.Passwd.PwHash
+ }
+ passwords[user.Passwd.Name] = 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: "",
+ }
+ }()
+ return ret
+}
diff --git a/src/parabola_hackers/nslcd_backend/hackers.go b/src/parabola_hackers/nslcd_backend/hackers.go
index f7d56e3..bb03862 100644
--- a/src/parabola_hackers/nslcd_backend/hackers.go
+++ b/src/parabola_hackers/nslcd_backend/hackers.go
@@ -82,14 +82,19 @@ func (o *Hackers) Reload() error {
return err
}
+ passwords, err := parabola_hackers.LoadAllPasswords()
+ if err != nil {
+ return err
+ }
+
o.groups = make(map[string]map[string]bool)
for uid, user := range o.users {
user.Passwd.GID = usersGid
- var _err error
- user.Passwd.PwHash, _err = parabola_hackers.LoadUserPassword(user.Passwd.HomeDir + "/.password")
- if _err != nil {
- logger.Debug("hackers.git: Ignoring password: %v", _err)
+ hash, hasHash := passwords[user.Passwd.Name]
+ if !hasHash {
+ hash = "!"
}
+ user.Passwd.PwHash = hash
o.users[uid] = user
for _, groupname := range user.Groups {
o.add_user_to_group(user.Passwd.Name, groupname)
diff --git a/src/parabola_hackers/password.go b/src/parabola_hackers/passwords.go.in
index 957de1f..0d763b9 100644
--- a/src/parabola_hackers/password.go
+++ b/src/parabola_hackers/passwords.go.in
@@ -20,9 +20,11 @@ import (
"fmt"
"io/ioutil"
"os"
+ "sort"
"strings"
"lukeshu.com/git/go/libgnulinux.git/crypt"
+ "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger"
)
/* Note that the password hash value should be one of:
@@ -32,33 +34,61 @@ import (
often used to indicate that the password is defined elsewhere
other - encrypted password, in crypt(3) format */
-func LoadUserPassword(filename string) (hash string, err error) {
- file, err := os.Open(filename)
+const shadow_file = "@shadow_file@"
+
+func LoadAllPasswords() (map[string]string, error) {
+ file, err := os.Open(shadow_file)
if err != nil {
- return
+ return nil, err
}
contents, err := ioutil.ReadAll(file)
if err != nil {
- return
+ return nil, err
}
lines := strings.Split(string(contents), "\n")
- switch len(lines) {
- case 1:
- hash = lines[0]
- case 2:
- if lines[1] == "" {
- hash = lines[0]
- } else {
- err = fmt.Errorf("Invalid password format in file: %q", filename)
+ passwords := make(map[string]string, len(lines))
+ for i, line := range lines {
+ cols := strings.SplitN(line, ":", 2)
+ if len(cols) != 2 {
+ logger.Err("hackers.git %s:%d: malformed line", shadow_file, i+1)
+ continue
+ }
+ username := cols[0]
+ hash := cols[1]
+ if hash != "!" && !crypt.SaltOk(hash) {
+ hash = "!"
+ logger.Err("%s:%d: malformed hash for user: %s", shadow_file, i+1, username)
}
- default:
- err = fmt.Errorf("Invalid password format in file (number of lines): %q", filename)
- return
+ passwords[username] = hash
+ }
+ return passwords, nil
+}
+
+func SaveAllPasswords(passwords map[string]string) error {
+ usernames := make([]string, len(passwords))
+ i := 0
+ for username, _ := range passwords {
+ usernames[i] = username
+ i++
+ }
+ sort.Strings(usernames)
+
+ file, err := os.OpenFile(shadow_file+"-", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
+ if err != nil {
+ return err
+ }
+
+ for _, username := range usernames {
+ fmt.Fprintf(file, "%s:%s\n", username, passwords[username])
}
- if hash != "!" && !crypt.SaltOk(hash) {
- hash = "!"
- err = fmt.Errorf("Invalid password format in file (invalid salt): %q", filename)
- return
+ err = file.Sync()
+ if err != nil {
+ return err
}
- return
+ err = file.Close()
+ if err != nil {
+ return err
+ }
+
+ return os.Rename(shadow_file+"-", shadow_file)
}