From 374311b891c587bbeb554057e04b55f69073d1af Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 3 Feb 2017 02:44:33 -0500 Subject: Less magic in the build system; don't hide the go workspace. --- .gitignore | 1 - .gitmodules | 20 ++-- GNUmakefile | 3 +- LICENSE.bsd3.txt | 2 +- Makefile | 87 +++++++------- go/.gitignore | 2 + go/git.lukeshu.com/go/libgnulinux | 1 - go/git.lukeshu.com/go/libnslcd | 1 - go/git.lukeshu.com/go/libsystemd | 1 - go/golang.org/x/sys | 1 - go/gopkg.in/yaml.v2 | 1 - go/nshd/.gitignore | 1 - go/nshd/main.go.in | 33 ------ go/nshd/nshd_files/.gitignore | 2 - go/nshd/nshd_files/passwords.go.in | 97 ---------------- go/nshd/nshd_files/users.go.in | 143 ----------------------- go/nshd/nslcd_backend/db_config.go | 39 ------- go/nshd/nslcd_backend/db_group.go | 141 ----------------------- go/nshd/nslcd_backend/db_pam.go | 204 --------------------------------- go/nshd/nslcd_backend/db_passwd.go | 88 -------------- go/nshd/nslcd_backend/db_shadow.go | 77 ------------- go/nshd/nslcd_backend/hackers.go | 124 -------------------- go/nshd/nslcd_backend/util.go | 58 ---------- go/nshd/util/util.go | 47 -------- go/src/git.lukeshu.com/go/libgnulinux | 1 + go/src/git.lukeshu.com/go/libnslcd | 1 + go/src/git.lukeshu.com/go/libsystemd | 1 + go/src/golang.org/x/sys | 1 + go/src/gopkg.in/yaml.v2 | 1 + go/src/nshd/.gitignore | 1 + go/src/nshd/main.go.in | 33 ++++++ go/src/nshd/nshd_files/.gitignore | 2 + go/src/nshd/nshd_files/passwords.go.in | 97 ++++++++++++++++ go/src/nshd/nshd_files/users.go.in | 143 +++++++++++++++++++++++ go/src/nshd/nslcd_backend/db_config.go | 39 +++++++ go/src/nshd/nslcd_backend/db_group.go | 141 +++++++++++++++++++++++ go/src/nshd/nslcd_backend/db_pam.go | 204 +++++++++++++++++++++++++++++++++ go/src/nshd/nslcd_backend/db_passwd.go | 88 ++++++++++++++ go/src/nshd/nslcd_backend/db_shadow.go | 77 +++++++++++++ go/src/nshd/nslcd_backend/hackers.go | 124 ++++++++++++++++++++ go/src/nshd/nslcd_backend/util.go | 58 ++++++++++ go/src/nshd/util/util.go | 47 ++++++++ 42 files changed, 1112 insertions(+), 1121 deletions(-) create mode 100644 go/.gitignore delete mode 160000 go/git.lukeshu.com/go/libgnulinux delete mode 160000 go/git.lukeshu.com/go/libnslcd delete mode 160000 go/git.lukeshu.com/go/libsystemd delete mode 160000 go/golang.org/x/sys delete mode 160000 go/gopkg.in/yaml.v2 delete mode 100644 go/nshd/.gitignore delete mode 100644 go/nshd/main.go.in delete mode 100644 go/nshd/nshd_files/.gitignore delete mode 100644 go/nshd/nshd_files/passwords.go.in delete mode 100644 go/nshd/nshd_files/users.go.in delete mode 100644 go/nshd/nslcd_backend/db_config.go delete mode 100644 go/nshd/nslcd_backend/db_group.go delete mode 100644 go/nshd/nslcd_backend/db_pam.go delete mode 100644 go/nshd/nslcd_backend/db_passwd.go delete mode 100644 go/nshd/nslcd_backend/db_shadow.go delete mode 100644 go/nshd/nslcd_backend/hackers.go delete mode 100644 go/nshd/nslcd_backend/util.go delete mode 100644 go/nshd/util/util.go create mode 160000 go/src/git.lukeshu.com/go/libgnulinux create mode 160000 go/src/git.lukeshu.com/go/libnslcd create mode 160000 go/src/git.lukeshu.com/go/libsystemd create mode 160000 go/src/golang.org/x/sys create mode 160000 go/src/gopkg.in/yaml.v2 create mode 100644 go/src/nshd/.gitignore create mode 100644 go/src/nshd/main.go.in create mode 100644 go/src/nshd/nshd_files/.gitignore create mode 100644 go/src/nshd/nshd_files/passwords.go.in create mode 100644 go/src/nshd/nshd_files/users.go.in create mode 100644 go/src/nshd/nslcd_backend/db_config.go create mode 100644 go/src/nshd/nslcd_backend/db_group.go create mode 100644 go/src/nshd/nslcd_backend/db_pam.go create mode 100644 go/src/nshd/nslcd_backend/db_passwd.go create mode 100644 go/src/nshd/nslcd_backend/db_shadow.go create mode 100644 go/src/nshd/nslcd_backend/hackers.go create mode 100644 go/src/nshd/nslcd_backend/util.go create mode 100644 go/src/nshd/util/util.go diff --git a/.gitignore b/.gitignore index 5c304e4..40f76d0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ /nshd.service /nshd.sysusers -/.gopath *.o *~ parabola-keyring-* diff --git a/.gitmodules b/.gitmodules index 434287e..5b9a9ba 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,15 +1,15 @@ -[submodule "go/gopkg.in/yaml.v2"] - path = go/gopkg.in/yaml.v2 +[submodule "go/src/gopkg.in/yaml.v2"] + path = go/src/gopkg.in/yaml.v2 url = https://gopkg.in/yaml.v2/ -[submodule "go/git.lukeshu.com/go/libgnulinux"] - path = go/git.lukeshu.com/go/libgnulinux +[submodule "go/src/git.lukeshu.com/go/libgnulinux"] + path = go/src/git.lukeshu.com/go/libgnulinux url = https://git.lukeshu.com/go/libgnulinux/ -[submodule "go/git.lukeshu.com/go/libnslcd"] - path = go/git.lukeshu.com/go/libnslcd +[submodule "go/src/git.lukeshu.com/go/libnslcd"] + path = go/src/git.lukeshu.com/go/libnslcd url = https://git.lukeshu.com/go/libnslcd/ -[submodule "go/git.lukeshu.com/go/libsystemd"] - path = go/git.lukeshu.com/go/libsystemd +[submodule "go/src/git.lukeshu.com/go/libsystemd"] + path = go/src/git.lukeshu.com/go/libsystemd url = https://git.lukeshu.com/go/libsystemd/ -[submodule "go/golang.org/x/sys"] - path = go/golang.org/x/sys +[submodule "go/src/golang.org/x/sys"] + path = go/src/golang.org/x/sys url = https://go.googlesource.com/sys diff --git a/GNUmakefile b/GNUmakefile index cc5a40d..8b86fb9 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -4,10 +4,9 @@ # Most of the complexity is dancing around to avoid having any # possibly conflicting identifiers. -MAKEFLAGS += --no-print-directory rest = $(wordlist 2,$(words $1),$1) target = $(or $(firstword $(MAKECMDGOALS)),default) $(or $(call rest,$(MAKECMDGOALS)),_$(target)): $(target) @: $(target): - @+$(MAKE) -f Makefile --no-builtin-rules --no-builtin-variables $(MAKECMDGOALS) + @+$(MAKE) --no-print-directory -f Makefile --no-builtin-rules --no-builtin-variables $(MAKECMDGOALS) diff --git a/LICENSE.bsd3.txt b/LICENSE.bsd3.txt index 92d2b94..f33946f 120000 --- a/LICENSE.bsd3.txt +++ b/LICENSE.bsd3.txt @@ -1 +1 @@ -go/golang.org/x/sys/LICENSE \ No newline at end of file +go/src/golang.org/x/sys/LICENSE \ No newline at end of file diff --git a/Makefile b/Makefile index f7f51ac..775b42a 100644 --- a/Makefile +++ b/Makefile @@ -17,9 +17,12 @@ include config.mk include $(topsrcdir)/build-aux/Makefile.head.mk files.src.src := $(filter-out go/golang.org/x/sys/plan9/% go/golang.org/x/sys/windows/%,$(files.src.src)) -var = .var. -programs = nshd nshd-setuid nshd-tester common.rb -scripts = $(filter-out %.c %.o $(programs) common.rb common.rb.in,$(notdir $(wildcard $(srcdir)/bin/*))) + +bins_c = nshd-setuid nshd-tester +bins_go = nshd +bins_lib = common.rb +bins_script = $(filter-out %.c %.o %.in $(bins_c) $(bins_go) $(bins_lib),$(notdir $(wildcard $(srcdir)/bin/*))) +bins = $(bins_c) $(bins_go) $(bins_lib) $(bins_script) .PHONY: FORCE .SECONDARY: @@ -36,12 +39,6 @@ cgo_variables = CGO_ENABLED CGO_CFLAGS CGO_CPPFLAGS CGO_CXXFLAGS CGO_LDFLAGS CC $(foreach v,$(cgo_variables),$(eval $v ?=)) export $(cgo_variables) -$(outdir)/.gopath/src: - rm -rf -- $(@D) - mkdir $(@D) - ln -sr $(srcdir)/go $@ - touch $@ - # # Generate (pre-tarball) @@ -56,58 +53,52 @@ $(srcdir)/LICENSE.apache-2.0.txt: $(NET) $(srcdir)/LICENSE.wtfpl-2.txt: $(NET) curl http://www.wtfpl.net/txt/copying/ > $@ -files.generate: go-generate -go-generate: .gopath/src - +GOPATH=$(abspath $(outdir)/.gopath) go generate git.lukeshu.com/... +$(outdir)/$(files.generate): go-generate +go-generate: + +GOPATH=$(abspath $(srcdir)/go) go generate git.lukeshu.com/... .PHONY: go-generate -files.src.int += .gopath/ .gopath/* at.targets += go-generate # # Build (post-tarball) -files.out.all += $(addprefix bin/,$(programs)) nshd.service nshd.sysusers -files.out.int += bin/*.o .gopath/ .gopath/* .tmp* .var* +files.out.all += $(addprefix bin/,$(filter-out $(bins_script),$(bins))) nshd.service nshd.sysusers +files.out.int += go/pkg/ go/bin/ go/bin/* -# Dependencies -$(outdir)/bin/nshd-setuid: -ldl -$(outdir)/nshd.service: $(var)user $(var)bindir -$(outdir)/nshd.sysusers: $(var)user -$(outdir)/bin/common.rb: $(var)conf_file - -$(outdir)/.gopath/bin/nshd: \ - $(outdir)/go/nshd/main.go \ - $(outdir)/go/nshd/nshd_files/users.go \ - $(outdir)/go/nshd/nshd_files/passwords.go -$(outdir)/go/nshd/main.go: $(var)conf_file -$(outdir)/go/nshd/nshd_files/users.go: $(var)bindir -$(outdir)/go/nshd/nshd_files/passwords.go: $(var)shadow_file - -# Go: copy out of .gopath -$(outdir)/bin/nshd: \ -$(outdir)/bin/%: $(outdir)/.gopath/bin/% +# Go +$(addprefix %/bin/,$(bins_go)): %/src + GOPATH=$(abspath $*) go install $(bins_go) +$(outdir)/bin/%: $(outdir)/go/bin/% cp -T $< $@ -# Go: compile+link -$(outdir)/.gopath/bin/nshd: \ -$(outdir)/.gopath/bin/%: FORCE go-generate - GOPATH=$(abspath $(outdir)/.gopath) go install $(@F) - -# C: compile -$(outdir)/%.o: $(srcdir)/%.c $(var)CC $(var)CPPFLAGS $(var)CFLAGS - $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(filter-out $(var)%,$^) -# C: link -$(outdir)/bin/nshd-tester $(outdir)/bin/nshd-setuid: \ -$(outdir)/%: $(outdir)/%.o $(var)CC $(var)LDFLAGS - $(CC) $(LDFLAGS) -o $@ $(filter-out $(var)%,$^) - +# C +$(addprefix $(outdir)/bin/,$(bins_c)): \ +$(outdir)/%: $(srcdir)/%.c $(var.)CC $(var.)CPPFLAGS $(var.)CFLAGS + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(filter-out $(var.)%,$^) # Basic variable substitution $(outdir)/%: $(srcdir)/%.in - < $< sed $(foreach v,$(patsubst $(var)%,%,$(filter $(var)%,$^)), -e 's|@$v@|$($v)|g' ) > $@ + < $< sed $(foreach v,$(patsubst $(var.)%,%,$(filter $(var.)%,$^)), -e 's|@$v@|$($v)|g' ) > $@ + +# Dependencies +$(outdir)/bin/nshd-setuid: -ldl +$(outdir)/nshd.service: $(var.)user $(var.)bindir +$(outdir)/nshd.sysusers: $(var.)user +$(outdir)/bin/common.rb: $(var.)conf_file + +$(outdir)/go/src/nshd/main.go: $(var.)conf_file +$(outdir)/go/src/nshd/nshd_files/users.go: $(var.)bindir +$(outdir)/go/src/nshd/nshd_files/passwords.go: $(var.)shadow_file + +goconf = \ + $(outdir)/go/src/nshd/main.go \ + $(outdir)/go/src/nshd/nshd_files/users.go \ + $(outdir)/go/src/nshd/nshd_files/passwords.go +$(outdir)/go/bin/nshd: $(goconf) $(outdir)/$(files.generate) +files.out.all += $(goconf) # Install -files.sys.all += $(addprefix $(bindir)/,$(programs) $(scripts)) $(systemunitdir)/nshd.socket $(systemunitdir)/nshd.service $(sysusersdir)/nshd.conf $(conf_file) $(shadow_file) +files.sys.all += $(addprefix $(bindir)/,$(bins)) $(systemunitdir)/nshd.socket $(systemunitdir)/nshd.service $(sysusersdir)/nshd.conf $(conf_file) $(shadow_file) $(DESTDIR)$(bindir)/%: $(outdir)/bin/% $(NORMAL_INSTALL) @@ -129,7 +120,7 @@ $(DESTDIR)$(bindir)/common.rb: $(srcdir)/bin/common.rb $(DESTDIR)$(conf_file): $(srcdir)/parabola-hackers.yml $(NORMAL_INSTALL) install -TDm644 $< $@ -$(DESTDIR)$(shadow_file): $(var)user $(DESTDIR)$(sysusersdir)/nshd.conf +$(DESTDIR)$(shadow_file): $(var.)user $(DESTDIR)$(sysusersdir)/nshd.conf $(NORMAL_INSTALL) install -d $(@D) touch $@ diff --git a/go/.gitignore b/go/.gitignore new file mode 100644 index 0000000..4c48abf --- /dev/null +++ b/go/.gitignore @@ -0,0 +1,2 @@ +/pkg/ +/bin/ diff --git a/go/git.lukeshu.com/go/libgnulinux b/go/git.lukeshu.com/go/libgnulinux deleted file mode 160000 index b2bae3c..0000000 --- a/go/git.lukeshu.com/go/libgnulinux +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b2bae3c73817740b48a4698c6aa863d70234a64c diff --git a/go/git.lukeshu.com/go/libnslcd b/go/git.lukeshu.com/go/libnslcd deleted file mode 160000 index 939ef44..0000000 --- a/go/git.lukeshu.com/go/libnslcd +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 939ef442f33dadf17f60ebf9f0c1fbaaa27f0c62 diff --git a/go/git.lukeshu.com/go/libsystemd b/go/git.lukeshu.com/go/libsystemd deleted file mode 160000 index 1ac9db6..0000000 --- a/go/git.lukeshu.com/go/libsystemd +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1ac9db65fda0693d13336e6471a858914348f2fc diff --git a/go/golang.org/x/sys b/go/golang.org/x/sys deleted file mode 160000 index d75a526..0000000 --- a/go/golang.org/x/sys +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d75a52659825e75fff6158388dddc6a5b04f9ba5 diff --git a/go/gopkg.in/yaml.v2 b/go/gopkg.in/yaml.v2 deleted file mode 160000 index f7716cb..0000000 --- a/go/gopkg.in/yaml.v2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f7716cbe52baa25d2e9b0d0da546fcf909fc16b4 diff --git a/go/nshd/.gitignore b/go/nshd/.gitignore deleted file mode 100644 index 00870e2..0000000 --- a/go/nshd/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/main.go diff --git a/go/nshd/main.go.in b/go/nshd/main.go.in deleted file mode 100644 index 5564128..0000000 --- a/go/nshd/main.go.in +++ /dev/null @@ -1,33 +0,0 @@ -// 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 -// . - -// Command nshd is an implementation of nslcd that talks to -// hackers.git instead of LDAP. -package main - -import ( - "os" - "nshd/nslcd_backend" - - "git.lukeshu.com/go/libnslcd/nslcd_systemd" -) - -func main() { - backend := &nslcd_backend.Hackers{ - CfgFilename: "@conf_file@", - } - os.Exit(int(nslcd_systemd.Main(backend))) -} diff --git a/go/nshd/nshd_files/.gitignore b/go/nshd/nshd_files/.gitignore deleted file mode 100644 index 3be3f08..0000000 --- a/go/nshd/nshd_files/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/users.go -/passwords.go diff --git a/go/nshd/nshd_files/passwords.go.in b/go/nshd/nshd_files/passwords.go.in deleted file mode 100644 index 679f7c0..0000000 --- a/go/nshd/nshd_files/passwords.go.in +++ /dev/null @@ -1,97 +0,0 @@ -// 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 nshd_files - -import ( - "fmt" - "io/ioutil" - "os" - "sort" - "strings" - - "git.lukeshu.com/go/libgnulinux/crypt" - "git.lukeshu.com/go/libsystemd/sd_daemon" -) - -/* Note that the password hash value should be one of: - - no password set, allow login without password - ! - used to prevent logins - x - "valid" encrypted password that does not match any valid password - often used to indicate that the password is defined elsewhere - other - encrypted password, in crypt(3) format */ - -const shadow_file = "@shadow_file@" - -func LoadAllPasswords() (map[string]string, error) { - file, err := os.Open(shadow_file) - if err != nil { - return nil, err - } - contents, err := ioutil.ReadAll(file) - if err != nil { - return nil, err - } - lines := strings.Split(string(contents), "\n") - passwords := make(map[string]string, len(lines)) - for i, line := range lines { - if line == "" { - continue - } - cols := strings.SplitN(line, ":", 2) - if len(cols) != 2 { - sd_daemon.Log.Err(fmt.Sprintf("hackers.git %s:%d: malformed line", shadow_file, i+1)) - continue - } - username := cols[0] - hash := cols[1] - if hash != "!" && !crypt.SaltOk(hash) { - hash = "!" - sd_daemon.Log.Err(fmt.Sprintf("%s:%d: malformed hash for user: %s", shadow_file, i+1, username)) - } - 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]) - } - err = file.Sync() - if err != nil { - return err - } - err = file.Close() - if err != nil { - return err - } - - return os.Rename(shadow_file+"-", shadow_file) -} diff --git a/go/nshd/nshd_files/users.go.in b/go/nshd/nshd_files/users.go.in deleted file mode 100644 index 51703fd..0000000 --- a/go/nshd/nshd_files/users.go.in +++ /dev/null @@ -1,143 +0,0 @@ -// 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 nshd_files - -import ( - "fmt" - "os/exec" - - "nshd/util" - - yaml "gopkg.in/yaml.v2" - p "git.lukeshu.com/go/libnslcd/nslcd_proto" - "git.lukeshu.com/go/libsystemd/sd_daemon" -) - -/* Note that the password hash value should be one of: - - no password set, allow login without password - ! - used to prevent logins - x - "valid" encrypted password that does not match any valid password - often used to indicate that the password is defined elsewhere - other - encrypted password, in crypt(3) format */ - -type User struct { - Passwd p.Passwd - Groups []string -} - -func LoadAllUsers() (users map[int32]User, err error) { - contents, err := exec.Command("@bindir@/meta-cat").Output() - if err != nil { - return - } - - var _data interface{} - err = yaml.Unmarshal(contents, &_data) - if err != nil { - return - } - - data, isMap := _data.(map[interface{}]interface{}) - errs := []string{} - if !isMap { - errs = append(errs, "root node is not a map") - } else { - users = make(map[int32]User, len(data)) - for _uid, _user := range data { - uid, isInt := _uid.(int) - if !isInt { - errs = append(errs, fmt.Sprintf("UID is not an int: %T ( %#v )", _uid, _uid)) - continue - } - user, _err := parseUser(_user) - if _err != nil { - errs = append(errs, fmt.Sprintf("Could not parse data for UID %d: %v", uid, _err)) - continue - } - user.Passwd.UID = int32(uid) - sd_daemon.Log.Debug(fmt.Sprintf("hackers.git: -> User %d(%s) parsed", user.Passwd.UID, user.Passwd.Name)) - users[user.Passwd.UID] = user - } - } - if len(errs) > 0 { - users = nil - err = &yaml.TypeError{Errors: errs} - } - return -} - -func parseUser(_data interface{}) (ret User, err error) { - data, isMap := _data.(map[interface{}]interface{}) - errs := []string{} - if !isMap { - errs = append(errs, "root node is not a map") - } else { - if iface, isSet := data["username"]; !isSet { - errs = append(errs, "\"username\" is not set") - } else if str, isTyp := iface.(string); !isTyp { - errs = append(errs, "\"username\" is not a string") - } else { - ret.Passwd.Name = str - ret.Passwd.HomeDir = "/home/" + str - } - - if iface, isSet := data["fullname"]; !isSet { - errs = append(errs, "\"fullname\" is not set") - } else if str, isTyp := iface.(string); !isTyp { - errs = append(errs, "\"fullname\" is not a string") - } else { - ret.Passwd.GECOS = str - } - - if iface, isSet := data["shell"]; !isSet { - errs = append(errs, "\"shell\" is not set") - } else if str, isTyp := iface.(string); !isTyp { - errs = append(errs, "\"shell\" is not a string") - } else { - ret.Passwd.Shell = str - } - - if iface, isSet := data["groups"]; !isSet { - ret.Groups = make([]string, 0) - } else if ary, isTyp := iface.([]interface{}); !isTyp { - errs = append(errs, "\"groups\" is not an array") - } else { - groups := make(map[string]bool, len(ary)) - e := false - for _, iface := range ary { - if str, isTyp := iface.(string); !isTyp { - errs = append(errs, "\"group\" item is not an array") - e = true - break - } else { - groups[str] = true - } - } - if !e { - ret.Groups = util.Set2list(groups) - } - } - } - if len(errs) > 0 { - err = &yaml.TypeError{Errors: errs} - } - - ret.Passwd.PwHash = string("x") // look in shadow for the password hash - ret.Passwd.GID = -1 - - return -} diff --git a/go/nshd/nslcd_backend/db_config.go b/go/nshd/nslcd_backend/db_config.go deleted file mode 100644 index d00bf02..0000000 --- a/go/nshd/nslcd_backend/db_config.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 nslcd_backend - -import ( - p "git.lukeshu.com/go/libnslcd/nslcd_proto" - s "golang.org/x/sys/unix" -) - -func (o *Hackers) Config_Get(cred s.Ucred, req p.Request_Config_Get) <-chan p.Config { - o.lock.RLock() - ret := make(chan p.Config) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - switch req.Key { - case p.NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE: - if o.cfg.Pam_password_prohibit_message != "" { - ret <- p.Config{Value: o.cfg.Pam_password_prohibit_message} - } - } - }() - return ret -} diff --git a/go/nshd/nslcd_backend/db_group.go b/go/nshd/nslcd_backend/db_group.go deleted file mode 100644 index 04c7e3b..0000000 --- a/go/nshd/nslcd_backend/db_group.go +++ /dev/null @@ -1,141 +0,0 @@ -// 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 nslcd_backend - -import ( - "nshd/util" - - p "git.lukeshu.com/go/libnslcd/nslcd_proto" - s "golang.org/x/sys/unix" -) - -func (o *Hackers) groupByName(name string, users bool) p.Group { - members_set, found := o.groups[name] - if !found { - return p.Group{ID: -1} - } - gid := name2gid(name) - if gid < 0 { - return p.Group{ID: -1} - } - var members_list []string - if users { - members_list = util.Set2list(members_set) - } else { - members_list = make([]string, 0) - } - return p.Group{ - Name: name, - PwHash: "x", - ID: gid, - Members: members_list, - } -} - -func (o *Hackers) groupByGid(gid int32, users bool) p.Group { - name, found := gid2name(gid) - if !found { - return p.Group{ID: -1} - } - members_set, found := o.groups[name] - if !found { - return p.Group{ID: -1} - } - var members_list []string - if users { - members_list = util.Set2list(members_set) - } else { - members_list = make([]string, 0) - } - return p.Group{ - Name: name, - PwHash: "x", - ID: gid, - Members: members_list, - } -} - -func (o *Hackers) Group_ByName(cred s.Ucred, req p.Request_Group_ByName) <-chan p.Group { - o.lock.RLock() - ret := make(chan p.Group) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - group := o.groupByName(req.Name, true) - if group.ID < 0 { - return - } - ret <- group - }() - return ret -} - -func (o *Hackers) Group_ByGid(cred s.Ucred, req p.Request_Group_ByGid) <-chan p.Group { - o.lock.RLock() - ret := make(chan p.Group) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - group := o.groupByGid(req.Gid, true) - if group.ID < 0 { - return - } - ret <- group - }() - return ret -} - -// note that the BYMEMBER call returns an empty members list -func (o *Hackers) Group_ByMember(cred s.Ucred, req p.Request_Group_ByMember) <-chan p.Group { - o.lock.RLock() - ret := make(chan p.Group) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - uid := o.name2uid(req.Member) - if uid < 0 { - return - } - for _, name := range o.users[uid].Groups { - group := o.groupByName(name, false) - if group.ID >= 0 { - ret <- group - } - } - }() - return ret -} - -func (o *Hackers) Group_All(cred s.Ucred, req p.Request_Group_All) <-chan p.Group { - o.lock.RLock() - ret := make(chan p.Group) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - for name, _ := range o.groups { - group := o.groupByName(name, true) - if group.ID >= 0 { - ret <- group - } - } - }() - return ret -} diff --git a/go/nshd/nslcd_backend/db_pam.go b/go/nshd/nslcd_backend/db_pam.go deleted file mode 100644 index b704620..0000000 --- a/go/nshd/nslcd_backend/db_pam.go +++ /dev/null @@ -1,204 +0,0 @@ -// 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 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 -} diff --git a/go/nshd/nslcd_backend/db_passwd.go b/go/nshd/nslcd_backend/db_passwd.go deleted file mode 100644 index ace127e..0000000 --- a/go/nshd/nslcd_backend/db_passwd.go +++ /dev/null @@ -1,88 +0,0 @@ -// 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 nslcd_backend - -import ( - p "git.lukeshu.com/go/libnslcd/nslcd_proto" - s "golang.org/x/sys/unix" -) - -/* Note that the output password hash value should be one of: - - no password set, allow login without password - ! - used to prevent logins - x - "valid" encrypted password that does not match any valid - password often used to indicate that the password is - defined elsewhere (i.e., in the shadow database) - other - encrypted password, in crypt(3) format - - A "!" prefix on a password hash "locks" the account; therefore a a - hash of "!" says "no login"; while a hash of "x" says "you may log - in", but fails to authorize; passing the buck to the next database. - - */ - -func (o *Hackers) Passwd_ByName(cred s.Ucred, req p.Request_Passwd_ByName) <-chan p.Passwd { - o.lock.RLock() - ret := make(chan p.Passwd) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - uid := o.name2uid(req.Name) - if uid < 0 { - return - } - passwd := o.users[uid].Passwd - passwd.PwHash = "x" // only put actual hashes in the Shadow DB - ret <- passwd - }() - return ret -} - -func (o *Hackers) Passwd_ByUID(cred s.Ucred, req p.Request_Passwd_ByUID) <-chan p.Passwd { - o.lock.RLock() - ret := make(chan p.Passwd) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - user, found := o.users[req.UID] - if !found { - return - } - passwd := user.Passwd - passwd.PwHash = "x" // only put actual hashes in the Shadow DB - ret <- passwd - }() - return ret -} - -func (o *Hackers) Passwd_All(cred s.Ucred, req p.Request_Passwd_All) <-chan p.Passwd { - o.lock.RLock() - ret := make(chan p.Passwd) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - for _, user := range o.users { - passwd := user.Passwd - passwd.PwHash = "x" // only put actual hashes in the Shadow DB - ret <- passwd - } - }() - return ret -} diff --git a/go/nshd/nslcd_backend/db_shadow.go b/go/nshd/nslcd_backend/db_shadow.go deleted file mode 100644 index ab1d68f..0000000 --- a/go/nshd/nslcd_backend/db_shadow.go +++ /dev/null @@ -1,77 +0,0 @@ -// 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 nslcd_backend - -import ( - p "git.lukeshu.com/go/libnslcd/nslcd_proto" - s "golang.org/x/sys/unix" -) - -func (o *Hackers) Shadow_ByName(cred s.Ucred, req p.Request_Shadow_ByName) <-chan p.Shadow { - o.lock.RLock() - ret := make(chan p.Shadow) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - if cred.Uid != 0 { - return - } - uid := o.name2uid(req.Name) - user := o.users[uid] - ret <- p.Shadow{ - Name: user.Passwd.Name, - PwHash: user.Passwd.PwHash, - LastChangeDate: -1, - MinDays: -1, - MaxDays: -1, - WarnDays: -1, - InactDays: -1, - ExpireDate: -1, - Flag: -1, - } - }() - return ret -} - -func (o *Hackers) Shadow_All(cred s.Ucred, req p.Request_Shadow_All) <-chan p.Shadow { - o.lock.RLock() - ret := make(chan p.Shadow) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - if cred.Uid != 0 { - return - } - - for _, user := range o.users { - ret <- p.Shadow{ - Name: user.Passwd.Name, - PwHash: user.Passwd.PwHash, - LastChangeDate: -1, - MinDays: -1, - MaxDays: -1, - WarnDays: -1, - InactDays: -1, - ExpireDate: -1, - Flag: -1, - } - } - }() - return ret -} diff --git a/go/nshd/nslcd_backend/hackers.go b/go/nshd/nslcd_backend/hackers.go deleted file mode 100644 index 44107b2..0000000 --- a/go/nshd/nslcd_backend/hackers.go +++ /dev/null @@ -1,124 +0,0 @@ -// 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 nslcd_backend is an nslcd_server Backend that -// speaks to hackers.git. -package nslcd_backend - -import ( - "fmt" - "sync" - - "nshd/nshd_files" - - "git.lukeshu.com/go/libnslcd/nslcd_server" - "git.lukeshu.com/go/libnslcd/nslcd_systemd" - "git.lukeshu.com/go/libsystemd/sd_daemon" -) - -type config struct { - Pam_password_prohibit_message string -} - -type Hackers struct { - nslcd_server.NilBackend - lock sync.RWMutex - - CfgFilename string - - cfg config - users map[int32]nshd_files.User - groups map[string]map[string]bool -} - -var _ nslcd_systemd.Backend = &Hackers{} -var _ nslcd_server.Backend = &Hackers{} - -func (o *Hackers) Init() error { - sd_daemon.Log.Debug(fmt.Sprintf("hackers.git: CfgFilename = %v", o.CfgFilename)) - err := o.Reload() - if err != nil { - sd_daemon.Log.Err(fmt.Sprintf("hackers.git: Could not initialize: %v", err)) - return err - } - return nil -} - -func (o *Hackers) Close() { - sd_daemon.Log.Info("hackers.git: Closing session") - o.lock.Lock() - defer o.lock.Unlock() - - o.users = make(map[int32]nshd_files.User, 0) - o.groups = make(map[string]map[string]bool) -} - -func (o *Hackers) Reload() error { - sd_daemon.Log.Info("hackers.git: Loading session") - o.lock.Lock() - defer o.lock.Unlock() - - var err error - o.cfg, err = parse_config(o.CfgFilename) - if err != nil { - return err - } - sd_daemon.Log.Info(fmt.Sprintf("hackers.git: pam_password_prohibit_message: %#v", o.cfg.Pam_password_prohibit_message)) - - sd_daemon.Log.Debug("hackers.git: Parsing user data") - o.users, err = nshd_files.LoadAllUsers() - if err != nil { - return err - } - - passwords, err := nshd_files.LoadAllPasswords() - if err != nil { - return err - } - - o.groups = make(map[string]map[string]bool) - for uid, user := range o.users { - user.Passwd.GID = usersGid - 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) - } - } - return nil -} - -func (o *Hackers) name2uid(name string) int32 { - for uid, data := range o.users { - if data.Passwd.Name == name { - return uid - } - } - return -1 -} - -func (o *Hackers) add_user_to_group(username string, groupname string) { - group, found := o.groups[groupname] - if !found { - group = make(map[string]bool) - o.groups[groupname] = group - } - group[username] = true -} diff --git a/go/nshd/nslcd_backend/util.go b/go/nshd/nslcd_backend/util.go deleted file mode 100644 index aa29b03..0000000 --- a/go/nshd/nslcd_backend/util.go +++ /dev/null @@ -1,58 +0,0 @@ -// 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 nslcd_backend - -import ( - "io/ioutil" - "os" - - yaml "gopkg.in/yaml.v2" - "git.lukeshu.com/go/libgnulinux/getgr" -) - -func name2gid(name string) int32 { - gr, err := getgr.ByName(name) - if gr == nil || err != nil { - return -1 - } else { - return int32(gr.Gid) - } -} - -func gid2name(gid int32) (string, bool) { - gr, err := getgr.ByGid(gid) - if gr == nil || err != nil { - return "", false - } else { - return gr.Name, true - } -} - -var usersGid = name2gid("users") - -func parse_config(filename string) (cfg config, err error) { - file, err := os.Open(filename) - if err != nil { - return - } - contents, err := ioutil.ReadAll(file) - if err != nil { - return - } - err = yaml.Unmarshal(contents, &cfg) - return -} diff --git a/go/nshd/util/util.go b/go/nshd/util/util.go deleted file mode 100644 index 4d3fd57..0000000 --- a/go/nshd/util/util.go +++ /dev/null @@ -1,47 +0,0 @@ -// 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 util - -import ( - "crypto/rand" - "math/big" -) - -func RandomString(alphabet string, n uint) (str string, err error) { - var alphabet_len = big.NewInt(int64(len(alphabet))) - var bigint *big.Int - _str := make([]byte, n) - for i := 0; i < len(_str); i++ { - bigint, err = rand.Int(rand.Reader, alphabet_len) - if err != nil { - return - } - _str[i] = alphabet[bigint.Int64()] - } - str = string(_str[:]) - return -} - -func Set2list(set map[string]bool) []string { - list := make([]string, len(set)) - i := uint(0) - for item, _ := range set { - list[i] = item - i++ - } - return list -} diff --git a/go/src/git.lukeshu.com/go/libgnulinux b/go/src/git.lukeshu.com/go/libgnulinux new file mode 160000 index 0000000..b2bae3c --- /dev/null +++ b/go/src/git.lukeshu.com/go/libgnulinux @@ -0,0 +1 @@ +Subproject commit b2bae3c73817740b48a4698c6aa863d70234a64c diff --git a/go/src/git.lukeshu.com/go/libnslcd b/go/src/git.lukeshu.com/go/libnslcd new file mode 160000 index 0000000..939ef44 --- /dev/null +++ b/go/src/git.lukeshu.com/go/libnslcd @@ -0,0 +1 @@ +Subproject commit 939ef442f33dadf17f60ebf9f0c1fbaaa27f0c62 diff --git a/go/src/git.lukeshu.com/go/libsystemd b/go/src/git.lukeshu.com/go/libsystemd new file mode 160000 index 0000000..1ac9db6 --- /dev/null +++ b/go/src/git.lukeshu.com/go/libsystemd @@ -0,0 +1 @@ +Subproject commit 1ac9db65fda0693d13336e6471a858914348f2fc diff --git a/go/src/golang.org/x/sys b/go/src/golang.org/x/sys new file mode 160000 index 0000000..d75a526 --- /dev/null +++ b/go/src/golang.org/x/sys @@ -0,0 +1 @@ +Subproject commit d75a52659825e75fff6158388dddc6a5b04f9ba5 diff --git a/go/src/gopkg.in/yaml.v2 b/go/src/gopkg.in/yaml.v2 new file mode 160000 index 0000000..f7716cb --- /dev/null +++ b/go/src/gopkg.in/yaml.v2 @@ -0,0 +1 @@ +Subproject commit f7716cbe52baa25d2e9b0d0da546fcf909fc16b4 diff --git a/go/src/nshd/.gitignore b/go/src/nshd/.gitignore new file mode 100644 index 0000000..00870e2 --- /dev/null +++ b/go/src/nshd/.gitignore @@ -0,0 +1 @@ +/main.go diff --git a/go/src/nshd/main.go.in b/go/src/nshd/main.go.in new file mode 100644 index 0000000..5564128 --- /dev/null +++ b/go/src/nshd/main.go.in @@ -0,0 +1,33 @@ +// 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 +// . + +// Command nshd is an implementation of nslcd that talks to +// hackers.git instead of LDAP. +package main + +import ( + "os" + "nshd/nslcd_backend" + + "git.lukeshu.com/go/libnslcd/nslcd_systemd" +) + +func main() { + backend := &nslcd_backend.Hackers{ + CfgFilename: "@conf_file@", + } + os.Exit(int(nslcd_systemd.Main(backend))) +} diff --git a/go/src/nshd/nshd_files/.gitignore b/go/src/nshd/nshd_files/.gitignore new file mode 100644 index 0000000..3be3f08 --- /dev/null +++ b/go/src/nshd/nshd_files/.gitignore @@ -0,0 +1,2 @@ +/users.go +/passwords.go diff --git a/go/src/nshd/nshd_files/passwords.go.in b/go/src/nshd/nshd_files/passwords.go.in new file mode 100644 index 0000000..679f7c0 --- /dev/null +++ b/go/src/nshd/nshd_files/passwords.go.in @@ -0,0 +1,97 @@ +// 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 nshd_files + +import ( + "fmt" + "io/ioutil" + "os" + "sort" + "strings" + + "git.lukeshu.com/go/libgnulinux/crypt" + "git.lukeshu.com/go/libsystemd/sd_daemon" +) + +/* Note that the password hash value should be one of: + - no password set, allow login without password + ! - used to prevent logins + x - "valid" encrypted password that does not match any valid password + often used to indicate that the password is defined elsewhere + other - encrypted password, in crypt(3) format */ + +const shadow_file = "@shadow_file@" + +func LoadAllPasswords() (map[string]string, error) { + file, err := os.Open(shadow_file) + if err != nil { + return nil, err + } + contents, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + lines := strings.Split(string(contents), "\n") + passwords := make(map[string]string, len(lines)) + for i, line := range lines { + if line == "" { + continue + } + cols := strings.SplitN(line, ":", 2) + if len(cols) != 2 { + sd_daemon.Log.Err(fmt.Sprintf("hackers.git %s:%d: malformed line", shadow_file, i+1)) + continue + } + username := cols[0] + hash := cols[1] + if hash != "!" && !crypt.SaltOk(hash) { + hash = "!" + sd_daemon.Log.Err(fmt.Sprintf("%s:%d: malformed hash for user: %s", shadow_file, i+1, username)) + } + 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]) + } + err = file.Sync() + if err != nil { + return err + } + err = file.Close() + if err != nil { + return err + } + + return os.Rename(shadow_file+"-", shadow_file) +} diff --git a/go/src/nshd/nshd_files/users.go.in b/go/src/nshd/nshd_files/users.go.in new file mode 100644 index 0000000..51703fd --- /dev/null +++ b/go/src/nshd/nshd_files/users.go.in @@ -0,0 +1,143 @@ +// 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 nshd_files + +import ( + "fmt" + "os/exec" + + "nshd/util" + + yaml "gopkg.in/yaml.v2" + p "git.lukeshu.com/go/libnslcd/nslcd_proto" + "git.lukeshu.com/go/libsystemd/sd_daemon" +) + +/* Note that the password hash value should be one of: + - no password set, allow login without password + ! - used to prevent logins + x - "valid" encrypted password that does not match any valid password + often used to indicate that the password is defined elsewhere + other - encrypted password, in crypt(3) format */ + +type User struct { + Passwd p.Passwd + Groups []string +} + +func LoadAllUsers() (users map[int32]User, err error) { + contents, err := exec.Command("@bindir@/meta-cat").Output() + if err != nil { + return + } + + var _data interface{} + err = yaml.Unmarshal(contents, &_data) + if err != nil { + return + } + + data, isMap := _data.(map[interface{}]interface{}) + errs := []string{} + if !isMap { + errs = append(errs, "root node is not a map") + } else { + users = make(map[int32]User, len(data)) + for _uid, _user := range data { + uid, isInt := _uid.(int) + if !isInt { + errs = append(errs, fmt.Sprintf("UID is not an int: %T ( %#v )", _uid, _uid)) + continue + } + user, _err := parseUser(_user) + if _err != nil { + errs = append(errs, fmt.Sprintf("Could not parse data for UID %d: %v", uid, _err)) + continue + } + user.Passwd.UID = int32(uid) + sd_daemon.Log.Debug(fmt.Sprintf("hackers.git: -> User %d(%s) parsed", user.Passwd.UID, user.Passwd.Name)) + users[user.Passwd.UID] = user + } + } + if len(errs) > 0 { + users = nil + err = &yaml.TypeError{Errors: errs} + } + return +} + +func parseUser(_data interface{}) (ret User, err error) { + data, isMap := _data.(map[interface{}]interface{}) + errs := []string{} + if !isMap { + errs = append(errs, "root node is not a map") + } else { + if iface, isSet := data["username"]; !isSet { + errs = append(errs, "\"username\" is not set") + } else if str, isTyp := iface.(string); !isTyp { + errs = append(errs, "\"username\" is not a string") + } else { + ret.Passwd.Name = str + ret.Passwd.HomeDir = "/home/" + str + } + + if iface, isSet := data["fullname"]; !isSet { + errs = append(errs, "\"fullname\" is not set") + } else if str, isTyp := iface.(string); !isTyp { + errs = append(errs, "\"fullname\" is not a string") + } else { + ret.Passwd.GECOS = str + } + + if iface, isSet := data["shell"]; !isSet { + errs = append(errs, "\"shell\" is not set") + } else if str, isTyp := iface.(string); !isTyp { + errs = append(errs, "\"shell\" is not a string") + } else { + ret.Passwd.Shell = str + } + + if iface, isSet := data["groups"]; !isSet { + ret.Groups = make([]string, 0) + } else if ary, isTyp := iface.([]interface{}); !isTyp { + errs = append(errs, "\"groups\" is not an array") + } else { + groups := make(map[string]bool, len(ary)) + e := false + for _, iface := range ary { + if str, isTyp := iface.(string); !isTyp { + errs = append(errs, "\"group\" item is not an array") + e = true + break + } else { + groups[str] = true + } + } + if !e { + ret.Groups = util.Set2list(groups) + } + } + } + if len(errs) > 0 { + err = &yaml.TypeError{Errors: errs} + } + + ret.Passwd.PwHash = string("x") // look in shadow for the password hash + ret.Passwd.GID = -1 + + return +} diff --git a/go/src/nshd/nslcd_backend/db_config.go b/go/src/nshd/nslcd_backend/db_config.go new file mode 100644 index 0000000..d00bf02 --- /dev/null +++ b/go/src/nshd/nslcd_backend/db_config.go @@ -0,0 +1,39 @@ +// 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 nslcd_backend + +import ( + p "git.lukeshu.com/go/libnslcd/nslcd_proto" + s "golang.org/x/sys/unix" +) + +func (o *Hackers) Config_Get(cred s.Ucred, req p.Request_Config_Get) <-chan p.Config { + o.lock.RLock() + ret := make(chan p.Config) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + switch req.Key { + case p.NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE: + if o.cfg.Pam_password_prohibit_message != "" { + ret <- p.Config{Value: o.cfg.Pam_password_prohibit_message} + } + } + }() + return ret +} diff --git a/go/src/nshd/nslcd_backend/db_group.go b/go/src/nshd/nslcd_backend/db_group.go new file mode 100644 index 0000000..04c7e3b --- /dev/null +++ b/go/src/nshd/nslcd_backend/db_group.go @@ -0,0 +1,141 @@ +// 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 nslcd_backend + +import ( + "nshd/util" + + p "git.lukeshu.com/go/libnslcd/nslcd_proto" + s "golang.org/x/sys/unix" +) + +func (o *Hackers) groupByName(name string, users bool) p.Group { + members_set, found := o.groups[name] + if !found { + return p.Group{ID: -1} + } + gid := name2gid(name) + if gid < 0 { + return p.Group{ID: -1} + } + var members_list []string + if users { + members_list = util.Set2list(members_set) + } else { + members_list = make([]string, 0) + } + return p.Group{ + Name: name, + PwHash: "x", + ID: gid, + Members: members_list, + } +} + +func (o *Hackers) groupByGid(gid int32, users bool) p.Group { + name, found := gid2name(gid) + if !found { + return p.Group{ID: -1} + } + members_set, found := o.groups[name] + if !found { + return p.Group{ID: -1} + } + var members_list []string + if users { + members_list = util.Set2list(members_set) + } else { + members_list = make([]string, 0) + } + return p.Group{ + Name: name, + PwHash: "x", + ID: gid, + Members: members_list, + } +} + +func (o *Hackers) Group_ByName(cred s.Ucred, req p.Request_Group_ByName) <-chan p.Group { + o.lock.RLock() + ret := make(chan p.Group) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + group := o.groupByName(req.Name, true) + if group.ID < 0 { + return + } + ret <- group + }() + return ret +} + +func (o *Hackers) Group_ByGid(cred s.Ucred, req p.Request_Group_ByGid) <-chan p.Group { + o.lock.RLock() + ret := make(chan p.Group) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + group := o.groupByGid(req.Gid, true) + if group.ID < 0 { + return + } + ret <- group + }() + return ret +} + +// note that the BYMEMBER call returns an empty members list +func (o *Hackers) Group_ByMember(cred s.Ucred, req p.Request_Group_ByMember) <-chan p.Group { + o.lock.RLock() + ret := make(chan p.Group) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + uid := o.name2uid(req.Member) + if uid < 0 { + return + } + for _, name := range o.users[uid].Groups { + group := o.groupByName(name, false) + if group.ID >= 0 { + ret <- group + } + } + }() + return ret +} + +func (o *Hackers) Group_All(cred s.Ucred, req p.Request_Group_All) <-chan p.Group { + o.lock.RLock() + ret := make(chan p.Group) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + for name, _ := range o.groups { + group := o.groupByName(name, true) + if group.ID >= 0 { + ret <- group + } + } + }() + return ret +} 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 . +// +// 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 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 +} diff --git a/go/src/nshd/nslcd_backend/db_passwd.go b/go/src/nshd/nslcd_backend/db_passwd.go new file mode 100644 index 0000000..ace127e --- /dev/null +++ b/go/src/nshd/nslcd_backend/db_passwd.go @@ -0,0 +1,88 @@ +// 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 nslcd_backend + +import ( + p "git.lukeshu.com/go/libnslcd/nslcd_proto" + s "golang.org/x/sys/unix" +) + +/* Note that the output password hash value should be one of: + - no password set, allow login without password + ! - used to prevent logins + x - "valid" encrypted password that does not match any valid + password often used to indicate that the password is + defined elsewhere (i.e., in the shadow database) + other - encrypted password, in crypt(3) format + + A "!" prefix on a password hash "locks" the account; therefore a a + hash of "!" says "no login"; while a hash of "x" says "you may log + in", but fails to authorize; passing the buck to the next database. + + */ + +func (o *Hackers) Passwd_ByName(cred s.Ucred, req p.Request_Passwd_ByName) <-chan p.Passwd { + o.lock.RLock() + ret := make(chan p.Passwd) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + uid := o.name2uid(req.Name) + if uid < 0 { + return + } + passwd := o.users[uid].Passwd + passwd.PwHash = "x" // only put actual hashes in the Shadow DB + ret <- passwd + }() + return ret +} + +func (o *Hackers) Passwd_ByUID(cred s.Ucred, req p.Request_Passwd_ByUID) <-chan p.Passwd { + o.lock.RLock() + ret := make(chan p.Passwd) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + user, found := o.users[req.UID] + if !found { + return + } + passwd := user.Passwd + passwd.PwHash = "x" // only put actual hashes in the Shadow DB + ret <- passwd + }() + return ret +} + +func (o *Hackers) Passwd_All(cred s.Ucred, req p.Request_Passwd_All) <-chan p.Passwd { + o.lock.RLock() + ret := make(chan p.Passwd) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + for _, user := range o.users { + passwd := user.Passwd + passwd.PwHash = "x" // only put actual hashes in the Shadow DB + ret <- passwd + } + }() + return ret +} diff --git a/go/src/nshd/nslcd_backend/db_shadow.go b/go/src/nshd/nslcd_backend/db_shadow.go new file mode 100644 index 0000000..ab1d68f --- /dev/null +++ b/go/src/nshd/nslcd_backend/db_shadow.go @@ -0,0 +1,77 @@ +// 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 nslcd_backend + +import ( + p "git.lukeshu.com/go/libnslcd/nslcd_proto" + s "golang.org/x/sys/unix" +) + +func (o *Hackers) Shadow_ByName(cred s.Ucred, req p.Request_Shadow_ByName) <-chan p.Shadow { + o.lock.RLock() + ret := make(chan p.Shadow) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + if cred.Uid != 0 { + return + } + uid := o.name2uid(req.Name) + user := o.users[uid] + ret <- p.Shadow{ + Name: user.Passwd.Name, + PwHash: user.Passwd.PwHash, + LastChangeDate: -1, + MinDays: -1, + MaxDays: -1, + WarnDays: -1, + InactDays: -1, + ExpireDate: -1, + Flag: -1, + } + }() + return ret +} + +func (o *Hackers) Shadow_All(cred s.Ucred, req p.Request_Shadow_All) <-chan p.Shadow { + o.lock.RLock() + ret := make(chan p.Shadow) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + if cred.Uid != 0 { + return + } + + for _, user := range o.users { + ret <- p.Shadow{ + Name: user.Passwd.Name, + PwHash: user.Passwd.PwHash, + LastChangeDate: -1, + MinDays: -1, + MaxDays: -1, + WarnDays: -1, + InactDays: -1, + ExpireDate: -1, + Flag: -1, + } + } + }() + return ret +} diff --git a/go/src/nshd/nslcd_backend/hackers.go b/go/src/nshd/nslcd_backend/hackers.go new file mode 100644 index 0000000..44107b2 --- /dev/null +++ b/go/src/nshd/nslcd_backend/hackers.go @@ -0,0 +1,124 @@ +// 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 nslcd_backend is an nslcd_server Backend that +// speaks to hackers.git. +package nslcd_backend + +import ( + "fmt" + "sync" + + "nshd/nshd_files" + + "git.lukeshu.com/go/libnslcd/nslcd_server" + "git.lukeshu.com/go/libnslcd/nslcd_systemd" + "git.lukeshu.com/go/libsystemd/sd_daemon" +) + +type config struct { + Pam_password_prohibit_message string +} + +type Hackers struct { + nslcd_server.NilBackend + lock sync.RWMutex + + CfgFilename string + + cfg config + users map[int32]nshd_files.User + groups map[string]map[string]bool +} + +var _ nslcd_systemd.Backend = &Hackers{} +var _ nslcd_server.Backend = &Hackers{} + +func (o *Hackers) Init() error { + sd_daemon.Log.Debug(fmt.Sprintf("hackers.git: CfgFilename = %v", o.CfgFilename)) + err := o.Reload() + if err != nil { + sd_daemon.Log.Err(fmt.Sprintf("hackers.git: Could not initialize: %v", err)) + return err + } + return nil +} + +func (o *Hackers) Close() { + sd_daemon.Log.Info("hackers.git: Closing session") + o.lock.Lock() + defer o.lock.Unlock() + + o.users = make(map[int32]nshd_files.User, 0) + o.groups = make(map[string]map[string]bool) +} + +func (o *Hackers) Reload() error { + sd_daemon.Log.Info("hackers.git: Loading session") + o.lock.Lock() + defer o.lock.Unlock() + + var err error + o.cfg, err = parse_config(o.CfgFilename) + if err != nil { + return err + } + sd_daemon.Log.Info(fmt.Sprintf("hackers.git: pam_password_prohibit_message: %#v", o.cfg.Pam_password_prohibit_message)) + + sd_daemon.Log.Debug("hackers.git: Parsing user data") + o.users, err = nshd_files.LoadAllUsers() + if err != nil { + return err + } + + passwords, err := nshd_files.LoadAllPasswords() + if err != nil { + return err + } + + o.groups = make(map[string]map[string]bool) + for uid, user := range o.users { + user.Passwd.GID = usersGid + 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) + } + } + return nil +} + +func (o *Hackers) name2uid(name string) int32 { + for uid, data := range o.users { + if data.Passwd.Name == name { + return uid + } + } + return -1 +} + +func (o *Hackers) add_user_to_group(username string, groupname string) { + group, found := o.groups[groupname] + if !found { + group = make(map[string]bool) + o.groups[groupname] = group + } + group[username] = true +} diff --git a/go/src/nshd/nslcd_backend/util.go b/go/src/nshd/nslcd_backend/util.go new file mode 100644 index 0000000..aa29b03 --- /dev/null +++ b/go/src/nshd/nslcd_backend/util.go @@ -0,0 +1,58 @@ +// 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 nslcd_backend + +import ( + "io/ioutil" + "os" + + yaml "gopkg.in/yaml.v2" + "git.lukeshu.com/go/libgnulinux/getgr" +) + +func name2gid(name string) int32 { + gr, err := getgr.ByName(name) + if gr == nil || err != nil { + return -1 + } else { + return int32(gr.Gid) + } +} + +func gid2name(gid int32) (string, bool) { + gr, err := getgr.ByGid(gid) + if gr == nil || err != nil { + return "", false + } else { + return gr.Name, true + } +} + +var usersGid = name2gid("users") + +func parse_config(filename string) (cfg config, err error) { + file, err := os.Open(filename) + if err != nil { + return + } + contents, err := ioutil.ReadAll(file) + if err != nil { + return + } + err = yaml.Unmarshal(contents, &cfg) + return +} diff --git a/go/src/nshd/util/util.go b/go/src/nshd/util/util.go new file mode 100644 index 0000000..4d3fd57 --- /dev/null +++ b/go/src/nshd/util/util.go @@ -0,0 +1,47 @@ +// 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 util + +import ( + "crypto/rand" + "math/big" +) + +func RandomString(alphabet string, n uint) (str string, err error) { + var alphabet_len = big.NewInt(int64(len(alphabet))) + var bigint *big.Int + _str := make([]byte, n) + for i := 0; i < len(_str); i++ { + bigint, err = rand.Int(rand.Reader, alphabet_len) + if err != nil { + return + } + _str[i] = alphabet[bigint.Int64()] + } + str = string(_str[:]) + return +} + +func Set2list(set map[string]bool) []string { + list := make([]string, len(set)) + i := uint(0) + for item, _ := range set { + list[i] = item + i++ + } + return list +} -- cgit v1.2.2