summaryrefslogtreecommitdiff
path: root/go/src/nshd/nshd_files/users.go.in
blob: 51703fdf20abcbad45ffec718ef8bd8aac6646ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// 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 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:
   <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 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
}