diff options
Diffstat (limited to 'src/parabola_hackers')
-rw-r--r-- | src/parabola_hackers/.gitignore | 1 | ||||
-rw-r--r-- | src/parabola_hackers/load_all_users.go.in | 141 | ||||
-rw-r--r-- | src/parabola_hackers/nslcd_backend/db_group.go | 7 | ||||
-rw-r--r-- | src/parabola_hackers/nslcd_backend/db_pam.go | 21 | ||||
-rw-r--r-- | src/parabola_hackers/nslcd_backend/db_passwd.go | 6 | ||||
-rw-r--r-- | src/parabola_hackers/nslcd_backend/db_shadow.go | 8 | ||||
-rw-r--r-- | src/parabola_hackers/nslcd_backend/hackers.go | 30 | ||||
-rw-r--r-- | src/parabola_hackers/nslcd_backend/hackers_parse.go | 135 | ||||
-rw-r--r-- | src/parabola_hackers/password.go | 63 | ||||
-rw-r--r-- | src/parabola_hackers/util.go (renamed from src/parabola_hackers/nslcd_backend/set.go) | 26 |
10 files changed, 261 insertions, 177 deletions
diff --git a/src/parabola_hackers/.gitignore b/src/parabola_hackers/.gitignore new file mode 100644 index 0000000..c32f8fa --- /dev/null +++ b/src/parabola_hackers/.gitignore @@ -0,0 +1 @@ +/load_all_users.go diff --git a/src/parabola_hackers/load_all_users.go.in b/src/parabola_hackers/load_all_users.go.in new file mode 100644 index 0000000..aeda069 --- /dev/null +++ b/src/parabola_hackers/load_all_users.go.in @@ -0,0 +1,141 @@ +// Copyright 2015-2016 Luke Shumaker <lukeshu@sbcglobal.net>. +// +// 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 parabola_hackers + +import ( + "fmt" + "os/exec" + + yaml "gopkg.in/yaml.v2" + nslcd_proto "lukeshu.com/git/go/libnslcd.git/proto" + "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" +) + +/* Note that the password hash value should be one of: + <empty> - 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 nslcd_proto.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) + logger.Debug("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 = Set2list(groups) + } + } + } + if len(errs) > 0 { + err = &yaml.TypeError{Errors: errs} + } + + ret.Passwd.PwHash = "x" // look in shadow for the password hash + ret.Passwd.GID = -1 + + return +} diff --git a/src/parabola_hackers/nslcd_backend/db_group.go b/src/parabola_hackers/nslcd_backend/db_group.go index 8990fad..b6b0704 100644 --- a/src/parabola_hackers/nslcd_backend/db_group.go +++ b/src/parabola_hackers/nslcd_backend/db_group.go @@ -18,6 +18,7 @@ package hackers_nslcd_backend import ( p "lukeshu.com/git/go/libnslcd.git/proto" + "parabola_hackers" s "syscall" ) @@ -32,7 +33,7 @@ func (o *Hackers) groupByName(name string, users bool) p.Group { } var members_list []string if users { - members_list = set2list(members_set) + members_list = parabola_hackers.Set2list(members_set) } else { members_list = make([]string, 0) } @@ -55,7 +56,7 @@ func (o *Hackers) groupByGid(gid int32, users bool) p.Group { } var members_list []string if users { - members_list = set2list(members_set) + members_list = parabola_hackers.Set2list(members_set) } else { members_list = make([]string, 0) } @@ -111,7 +112,7 @@ func (o *Hackers) Group_ByMember(cred s.Ucred, req p.Request_Group_ByMember) <-c if uid < 0 { return } - for _, name := range o.users[uid].groups { + for _, name := range o.users[uid].Groups { group := o.groupByName(name, false) if group.ID >= 0 { ret <- group diff --git a/src/parabola_hackers/nslcd_backend/db_pam.go b/src/parabola_hackers/nslcd_backend/db_pam.go index 6b2a7c7..19d4c79 100644 --- a/src/parabola_hackers/nslcd_backend/db_pam.go +++ b/src/parabola_hackers/nslcd_backend/db_pam.go @@ -17,9 +17,8 @@ package hackers_nslcd_backend import ( - "crypto/rand" p "lukeshu.com/git/go/libnslcd.git/proto" - "math/big" + "parabola_hackers" s "syscall" ) @@ -42,10 +41,10 @@ func (o *Hackers) PAM_Authentication(cred s.Ucred, req p.Request_PAM_Authenticat AuthorizationResult: p.NSLCD_PAM_AUTH_ERR, AuthorizationError: "", } - if check_password(req.Password, user.passwd.PwHash) { + if check_password(req.Password, user.Passwd.PwHash) { obj.AuthenticationResult = p.NSLCD_PAM_SUCCESS obj.AuthorizationResult = obj.AuthenticationResult - obj.UserName = user.passwd.Name + obj.UserName = user.Passwd.Name } ret <- obj }() @@ -73,22 +72,16 @@ func (o *Hackers) PAM_Authorization(cred s.Ucred, req p.Request_PAM_Authorizatio const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" -var alphabet_len = big.NewInt(int64(len(alphabet))) - 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) - var sessionid [24]byte - for i := 0; i < len(sessionid); i++ { - bigint, err := rand.Int(rand.Reader, alphabet_len) - if err != nil { - return - } - sessionid[i] = alphabet[bigint.Int64()] + sessionid, err := parabola_hackers.RandomString(alphabet, 24) + if err != nil { + return } - ret <- p.PAM_SessionOpen{SessionID: string(sessionid[:])} + ret <- p.PAM_SessionOpen{SessionID: sessionid} }() return ret } diff --git a/src/parabola_hackers/nslcd_backend/db_passwd.go b/src/parabola_hackers/nslcd_backend/db_passwd.go index 514a8b3..b11f6af 100644 --- a/src/parabola_hackers/nslcd_backend/db_passwd.go +++ b/src/parabola_hackers/nslcd_backend/db_passwd.go @@ -39,7 +39,7 @@ func (o *Hackers) Passwd_ByName(cred s.Ucred, req p.Request_Passwd_ByName) <-cha if uid < 0 { return } - passwd := o.users[uid].passwd + passwd := o.users[uid].Passwd passwd.PwHash = "x" // only put actual hashes in the Shadow DB ret <- passwd }() @@ -57,7 +57,7 @@ func (o *Hackers) Passwd_ByUID(cred s.Ucred, req p.Request_Passwd_ByUID) <-chan if !found { return } - passwd := user.passwd + passwd := user.Passwd passwd.PwHash = "x" // only put actual hashes in the Shadow DB ret <- passwd }() @@ -72,7 +72,7 @@ func (o *Hackers) Passwd_All(cred s.Ucred, req p.Request_Passwd_All) <-chan p.Pa defer close(ret) for _, user := range o.users { - passwd := user.passwd + passwd := user.Passwd passwd.PwHash = "x" // only put actual hashes in the Shadow DB ret <- passwd } diff --git a/src/parabola_hackers/nslcd_backend/db_shadow.go b/src/parabola_hackers/nslcd_backend/db_shadow.go index 26b1b05..6166cd9 100644 --- a/src/parabola_hackers/nslcd_backend/db_shadow.go +++ b/src/parabola_hackers/nslcd_backend/db_shadow.go @@ -34,8 +34,8 @@ func (o *Hackers) Shadow_ByName(cred s.Ucred, req p.Request_Shadow_ByName) <-cha uid := o.name2uid(req.Name) user := o.users[uid] ret <- p.Shadow{ - Name: user.passwd.Name, - PwHash: user.passwd.PwHash, + Name: user.Passwd.Name, + PwHash: user.Passwd.PwHash, LastChangeDate: -1, MinDays: -1, MaxDays: -1, @@ -61,8 +61,8 @@ func (o *Hackers) Shadow_All(cred s.Ucred, req p.Request_Shadow_All) <-chan p.Sh for _, user := range o.users { ret <- p.Shadow{ - Name: user.passwd.Name, - PwHash: user.passwd.PwHash, + Name: user.Passwd.Name, + PwHash: user.Passwd.PwHash, LastChangeDate: -1, MinDays: -1, MaxDays: -1, diff --git a/src/parabola_hackers/nslcd_backend/hackers.go b/src/parabola_hackers/nslcd_backend/hackers.go index 66312c6..50d392b 100644 --- a/src/parabola_hackers/nslcd_backend/hackers.go +++ b/src/parabola_hackers/nslcd_backend/hackers.go @@ -19,18 +19,13 @@ package hackers_nslcd_backend import ( - "lukeshu.com/git/go/libnslcd.git/proto" "lukeshu.com/git/go/libnslcd.git/proto/server" "lukeshu.com/git/go/libnslcd.git/systemd" "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" + "parabola_hackers" "sync" ) -type user struct { - passwd nslcd_proto.Passwd - groups []string -} - type config struct { Pam_password_prohibit_message string } @@ -40,10 +35,9 @@ type Hackers struct { lock sync.RWMutex CfgFilename string - YamlCat string cfg config - users map[int32]user + users map[int32]parabola_hackers.User groups map[string]map[string]bool } @@ -52,7 +46,6 @@ var _ nslcd_server.Backend = &Hackers{} func (o *Hackers) Init() error { logger.Debug("hackers.git: CfgFilename = %v", o.CfgFilename) - logger.Debug("hackers.git: YamlCat = %v", o.YamlCat) err := o.Reload() if err != nil { logger.Err("hackers.git: Could not initialize: %v", err) @@ -66,7 +59,7 @@ func (o *Hackers) Close() { o.lock.Lock() defer o.lock.Unlock() - o.users = make(map[int32]user, 0) + o.users = make(map[int32]parabola_hackers.User, 0) o.groups = make(map[string]map[string]bool) } @@ -83,15 +76,22 @@ func (o *Hackers) Reload() error { logger.Info("hackers.git: pam_password_prohibit_message: %#v", o.cfg.Pam_password_prohibit_message) logger.Debug("hackers.git: Parsing user data") - o.users, err = parse_users(o.YamlCat) + o.users, err = parabola_hackers.LoadAllUsers() if err != nil { return err } o.groups = make(map[string]map[string]bool) - for _, user := range o.users { - for _, groupname := range user.groups { - o.add_user_to_group(user.passwd.Name, groupname) + 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) + } + o.users[uid] = user + for _, groupname := range user.Groups { + o.add_user_to_group(user.Passwd.Name, groupname) } } return nil @@ -99,7 +99,7 @@ func (o *Hackers) Reload() error { func (o *Hackers) name2uid(name string) int32 { for uid, data := range o.users { - if data.passwd.Name == name { + if data.Passwd.Name == name { return uid } } diff --git a/src/parabola_hackers/nslcd_backend/hackers_parse.go b/src/parabola_hackers/nslcd_backend/hackers_parse.go index b18fdf8..e6d31c8 100644 --- a/src/parabola_hackers/nslcd_backend/hackers_parse.go +++ b/src/parabola_hackers/nslcd_backend/hackers_parse.go @@ -17,13 +17,9 @@ package hackers_nslcd_backend import ( - "fmt" yaml "gopkg.in/yaml.v2" "io/ioutil" - "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" "os" - "os/exec" - "strings" ) var usersGid = name2gid("users") @@ -40,134 +36,3 @@ func parse_config(filename string) (cfg config, err error) { err = yaml.Unmarshal(contents, &cfg) return } - -func parse_users(yaml_cat string) (users map[int32]user, err error) { - contents, err := exec.Command(yaml_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 := parse_user(_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) - logger.Debug("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 parse_user(_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 = set2list(groups) - } - } - } - if len(errs) > 0 { - err = &yaml.TypeError{Errors: errs} - } - - ret.passwd.PwHash = parse_user_password(ret.passwd.HomeDir + "/.password") - ret.passwd.GID = usersGid - - return -} - -func parse_user_password(filename string) (hash string) { - hash = "!" - file, err := os.Open(filename) - if err != nil { - logger.Debug("hackers.git: %v", err) - return - } - contents, err := ioutil.ReadAll(file) - if err != nil { - logger.Debug("hackers.git: Error while reading: %q: %v", filename, err) - return - } - lines := strings.Split(string(contents), "\n") - switch len(lines) { - case 1: - hash = lines[0] - case 2: - if lines[1] == "" { - hash = lines[0] - } else { - logger.Debug("hackers.git: Invalid password format in file: %q", filename) - } - default: - logger.Debug("hackers.git: Invalid password format in file: %q", filename) - } - return -} diff --git a/src/parabola_hackers/password.go b/src/parabola_hackers/password.go new file mode 100644 index 0000000..4ea345f --- /dev/null +++ b/src/parabola_hackers/password.go @@ -0,0 +1,63 @@ +// Copyright 2015-2016 Luke Shumaker <lukeshu@sbcglobal.net>. +// +// 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 parabola_hackers + +import ( + "io/ioutil" + "lukeshu.com/git/go/libgnulinux.git/crypt" + "fmt" + "os" + "strings" +) + +/* Note that the password hash value should be one of: + <empty> - 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 */ + +func LoadUserPassword(filename string) (hash string, err error) { + file, err := os.Open(filename) + if err != nil { + return + } + contents, err := ioutil.ReadAll(file) + if err != nil { + return + } + 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) + } + default: + err = fmt.Errorf("Invalid password format in file (number of lines): %q", filename) + return + } + if hash != "!" && !crypt.SaltOk(hash) { + hash = "!" + err = fmt.Errorf("Invalid password format in file (invalid salt): %q", filename) + return + } + return +} diff --git a/src/parabola_hackers/nslcd_backend/set.go b/src/parabola_hackers/util.go index 7a01c01..9a241db 100644 --- a/src/parabola_hackers/nslcd_backend/set.go +++ b/src/parabola_hackers/util.go @@ -1,4 +1,4 @@ -// Copyright 2015 Luke Shumaker <lukeshu@sbcglobal.net>. +// Copyright 2015-2016 Luke Shumaker <lukeshu@sbcglobal.net>. // // This is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -14,9 +14,29 @@ // License along with this manual; if not, see // <http://www.gnu.org/licenses/>. -package hackers_nslcd_backend +package parabola_hackers -func set2list(set map[string]bool) []string { +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 { |