This patch updates the vendor'd libcontainer version, so that Docker can
take advantage of the updates to the `user` API.
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com> (github: cyphar)
| ... | ... |
@@ -66,7 +66,7 @@ if [ "$1" = '--go' ]; then |
| 66 | 66 |
mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar |
| 67 | 67 |
fi |
| 68 | 68 |
|
| 69 |
-clone git github.com/docker/libcontainer fd6df76562137aa3b18e44b790cb484fe2b6fa0b |
|
| 69 |
+clone git github.com/docker/libcontainer 4ae31b6ceb2c2557c9f05f42da61b0b808faa5a4 |
|
| 70 | 70 |
# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file) |
| 71 | 71 |
rm -rf src/github.com/docker/libcontainer/vendor |
| 72 | 72 |
eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')" |
| ... | ... |
@@ -167,26 +167,43 @@ func RestoreParentDeathSignal(old int) error {
|
| 167 | 167 |
|
| 168 | 168 |
// SetupUser changes the groups, gid, and uid for the user inside the container |
| 169 | 169 |
func SetupUser(u string) error {
|
| 170 |
- uid, gid, suppGids, home, err := user.GetUserGroupSupplementaryHome(u, syscall.Getuid(), syscall.Getgid(), "/") |
|
| 170 |
+ // Set up defaults. |
|
| 171 |
+ defaultExecUser := user.ExecUser{
|
|
| 172 |
+ Uid: syscall.Getuid(), |
|
| 173 |
+ Gid: syscall.Getgid(), |
|
| 174 |
+ Home: "/", |
|
| 175 |
+ } |
|
| 176 |
+ |
|
| 177 |
+ passwdFile, err := user.GetPasswdFile() |
|
| 178 |
+ if err != nil {
|
|
| 179 |
+ return err |
|
| 180 |
+ } |
|
| 181 |
+ |
|
| 182 |
+ groupFile, err := user.GetGroupFile() |
|
| 183 |
+ if err != nil {
|
|
| 184 |
+ return err |
|
| 185 |
+ } |
|
| 186 |
+ |
|
| 187 |
+ execUser, err := user.GetExecUserFile(u, &defaultExecUser, passwdFile, groupFile) |
|
| 171 | 188 |
if err != nil {
|
| 172 | 189 |
return fmt.Errorf("get supplementary groups %s", err)
|
| 173 | 190 |
} |
| 174 | 191 |
|
| 175 |
- if err := syscall.Setgroups(suppGids); err != nil {
|
|
| 192 |
+ if err := syscall.Setgroups(execUser.Sgids); err != nil {
|
|
| 176 | 193 |
return fmt.Errorf("setgroups %s", err)
|
| 177 | 194 |
} |
| 178 | 195 |
|
| 179 |
- if err := system.Setgid(gid); err != nil {
|
|
| 196 |
+ if err := system.Setgid(execUser.Gid); err != nil {
|
|
| 180 | 197 |
return fmt.Errorf("setgid %s", err)
|
| 181 | 198 |
} |
| 182 | 199 |
|
| 183 |
- if err := system.Setuid(uid); err != nil {
|
|
| 200 |
+ if err := system.Setuid(execUser.Uid); err != nil {
|
|
| 184 | 201 |
return fmt.Errorf("setuid %s", err)
|
| 185 | 202 |
} |
| 186 | 203 |
|
| 187 | 204 |
// if we didn't get HOME already, set it based on the user's HOME |
| 188 | 205 |
if envHome := os.Getenv("HOME"); envHome == "" {
|
| 189 |
- if err := os.Setenv("HOME", home); err != nil {
|
|
| 206 |
+ if err := os.Setenv("HOME", execUser.Home); err != nil {
|
|
| 190 | 207 |
return fmt.Errorf("set HOME %s", err)
|
| 191 | 208 |
} |
| 192 | 209 |
} |
| ... | ... |
@@ -1003,28 +1003,23 @@ func AddRoute(destination, source, gateway, device string) error {
|
| 1003 | 1003 |
} |
| 1004 | 1004 |
|
| 1005 | 1005 |
if source != "" {
|
| 1006 |
- srcIP, srcNet, err := net.ParseCIDR(source) |
|
| 1006 |
+ srcIP := net.ParseIP(source) |
|
| 1007 | 1007 |
if err != nil {
|
| 1008 |
- return fmt.Errorf("source CIDR %s couldn't be parsed", source)
|
|
| 1008 |
+ return fmt.Errorf("source IP %s couldn't be parsed", source)
|
|
| 1009 | 1009 |
} |
| 1010 | 1010 |
srcFamily := getIpFamily(srcIP) |
| 1011 | 1011 |
if currentFamily != -1 && currentFamily != srcFamily {
|
| 1012 | 1012 |
return fmt.Errorf("source and destination ip were not the same IP family")
|
| 1013 | 1013 |
} |
| 1014 | 1014 |
currentFamily = srcFamily |
| 1015 |
- srcLen, bits := srcNet.Mask.Size() |
|
| 1016 |
- if srcLen == 0 && bits == 0 {
|
|
| 1017 |
- return fmt.Errorf("source CIDR %s generated a non-canonical Mask", source)
|
|
| 1018 |
- } |
|
| 1019 | 1015 |
msg.Family = uint8(srcFamily) |
| 1020 |
- msg.Src_len = uint8(srcLen) |
|
| 1021 | 1016 |
var srcData []byte |
| 1022 | 1017 |
if srcFamily == syscall.AF_INET {
|
| 1023 | 1018 |
srcData = srcIP.To4() |
| 1024 | 1019 |
} else {
|
| 1025 | 1020 |
srcData = srcIP.To16() |
| 1026 | 1021 |
} |
| 1027 |
- rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_SRC, srcData)) |
|
| 1022 |
+ rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_PREFSRC, srcData)) |
|
| 1028 | 1023 |
} |
| 1029 | 1024 |
|
| 1030 | 1025 |
if gateway != "" {
|
| ... | ... |
@@ -280,6 +280,34 @@ func TestAddDelNetworkIp(t *testing.T) {
|
| 280 | 280 |
} |
| 281 | 281 |
} |
| 282 | 282 |
|
| 283 |
+func TestAddRouteSourceSelection(t *testing.T) {
|
|
| 284 |
+ tstIp := "127.1.1.1" |
|
| 285 |
+ tl := testLink{name: "tstEth", linkType: "dummy"}
|
|
| 286 |
+ |
|
| 287 |
+ addLink(t, tl.name, tl.linkType) |
|
| 288 |
+ defer deleteLink(t, tl.name) |
|
| 289 |
+ |
|
| 290 |
+ ip := net.ParseIP(tstIp) |
|
| 291 |
+ mask := net.IPv4Mask(255, 255, 255, 255) |
|
| 292 |
+ ipNet := &net.IPNet{IP: ip, Mask: mask}
|
|
| 293 |
+ |
|
| 294 |
+ iface, err := net.InterfaceByName(tl.name) |
|
| 295 |
+ if err != nil {
|
|
| 296 |
+ t.Fatalf("Lost created link %#v", tl)
|
|
| 297 |
+ } |
|
| 298 |
+ |
|
| 299 |
+ if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
|
| 300 |
+ t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err)
|
|
| 301 |
+ } |
|
| 302 |
+ |
|
| 303 |
+ upLink(t, tl.name) |
|
| 304 |
+ defer downLink(t, tl.name) |
|
| 305 |
+ |
|
| 306 |
+ if err := AddRoute("127.0.0.0/8", tstIp, "", tl.name); err != nil {
|
|
| 307 |
+ t.Fatalf("Failed to add route with source address")
|
|
| 308 |
+ } |
|
| 309 |
+} |
|
| 310 |
+ |
|
| 283 | 311 |
func TestCreateVethPair(t *testing.T) {
|
| 284 | 312 |
if testing.Short() {
|
| 285 | 313 |
return |
| 286 | 314 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,209 @@ |
| 0 |
+{
|
|
| 1 |
+ "capabilities": [ |
|
| 2 |
+ "CHOWN", |
|
| 3 |
+ "DAC_OVERRIDE", |
|
| 4 |
+ "FOWNER", |
|
| 5 |
+ "MKNOD", |
|
| 6 |
+ "NET_RAW", |
|
| 7 |
+ "SETGID", |
|
| 8 |
+ "SETUID", |
|
| 9 |
+ "SETFCAP", |
|
| 10 |
+ "SETPCAP", |
|
| 11 |
+ "NET_BIND_SERVICE", |
|
| 12 |
+ "SYS_CHROOT", |
|
| 13 |
+ "KILL" |
|
| 14 |
+ ], |
|
| 15 |
+ "cgroups": {
|
|
| 16 |
+ "allowed_devices": [ |
|
| 17 |
+ {
|
|
| 18 |
+ "cgroup_permissions": "m", |
|
| 19 |
+ "major_number": -1, |
|
| 20 |
+ "minor_number": -1, |
|
| 21 |
+ "type": 99 |
|
| 22 |
+ }, |
|
| 23 |
+ {
|
|
| 24 |
+ "cgroup_permissions": "m", |
|
| 25 |
+ "major_number": -1, |
|
| 26 |
+ "minor_number": -1, |
|
| 27 |
+ "type": 98 |
|
| 28 |
+ }, |
|
| 29 |
+ {
|
|
| 30 |
+ "cgroup_permissions": "rwm", |
|
| 31 |
+ "major_number": 5, |
|
| 32 |
+ "minor_number": 1, |
|
| 33 |
+ "path": "/dev/console", |
|
| 34 |
+ "type": 99 |
|
| 35 |
+ }, |
|
| 36 |
+ {
|
|
| 37 |
+ "cgroup_permissions": "rwm", |
|
| 38 |
+ "major_number": 4, |
|
| 39 |
+ "path": "/dev/tty0", |
|
| 40 |
+ "type": 99 |
|
| 41 |
+ }, |
|
| 42 |
+ {
|
|
| 43 |
+ "cgroup_permissions": "rwm", |
|
| 44 |
+ "major_number": 4, |
|
| 45 |
+ "minor_number": 1, |
|
| 46 |
+ "path": "/dev/tty1", |
|
| 47 |
+ "type": 99 |
|
| 48 |
+ }, |
|
| 49 |
+ {
|
|
| 50 |
+ "cgroup_permissions": "rwm", |
|
| 51 |
+ "major_number": 136, |
|
| 52 |
+ "minor_number": -1, |
|
| 53 |
+ "type": 99 |
|
| 54 |
+ }, |
|
| 55 |
+ {
|
|
| 56 |
+ "cgroup_permissions": "rwm", |
|
| 57 |
+ "major_number": 5, |
|
| 58 |
+ "minor_number": 2, |
|
| 59 |
+ "type": 99 |
|
| 60 |
+ }, |
|
| 61 |
+ {
|
|
| 62 |
+ "cgroup_permissions": "rwm", |
|
| 63 |
+ "major_number": 10, |
|
| 64 |
+ "minor_number": 200, |
|
| 65 |
+ "type": 99 |
|
| 66 |
+ }, |
|
| 67 |
+ {
|
|
| 68 |
+ "cgroup_permissions": "rwm", |
|
| 69 |
+ "file_mode": 438, |
|
| 70 |
+ "major_number": 1, |
|
| 71 |
+ "minor_number": 3, |
|
| 72 |
+ "path": "/dev/null", |
|
| 73 |
+ "type": 99 |
|
| 74 |
+ }, |
|
| 75 |
+ {
|
|
| 76 |
+ "cgroup_permissions": "rwm", |
|
| 77 |
+ "file_mode": 438, |
|
| 78 |
+ "major_number": 1, |
|
| 79 |
+ "minor_number": 5, |
|
| 80 |
+ "path": "/dev/zero", |
|
| 81 |
+ "type": 99 |
|
| 82 |
+ }, |
|
| 83 |
+ {
|
|
| 84 |
+ "cgroup_permissions": "rwm", |
|
| 85 |
+ "file_mode": 438, |
|
| 86 |
+ "major_number": 1, |
|
| 87 |
+ "minor_number": 7, |
|
| 88 |
+ "path": "/dev/full", |
|
| 89 |
+ "type": 99 |
|
| 90 |
+ }, |
|
| 91 |
+ {
|
|
| 92 |
+ "cgroup_permissions": "rwm", |
|
| 93 |
+ "file_mode": 438, |
|
| 94 |
+ "major_number": 5, |
|
| 95 |
+ "path": "/dev/tty", |
|
| 96 |
+ "type": 99 |
|
| 97 |
+ }, |
|
| 98 |
+ {
|
|
| 99 |
+ "cgroup_permissions": "rwm", |
|
| 100 |
+ "file_mode": 438, |
|
| 101 |
+ "major_number": 1, |
|
| 102 |
+ "minor_number": 9, |
|
| 103 |
+ "path": "/dev/urandom", |
|
| 104 |
+ "type": 99 |
|
| 105 |
+ }, |
|
| 106 |
+ {
|
|
| 107 |
+ "cgroup_permissions": "rwm", |
|
| 108 |
+ "file_mode": 438, |
|
| 109 |
+ "major_number": 1, |
|
| 110 |
+ "minor_number": 8, |
|
| 111 |
+ "path": "/dev/random", |
|
| 112 |
+ "type": 99 |
|
| 113 |
+ } |
|
| 114 |
+ ], |
|
| 115 |
+ "name": "docker-koye", |
|
| 116 |
+ "parent": "docker" |
|
| 117 |
+ }, |
|
| 118 |
+ "restrict_sys": true, |
|
| 119 |
+ "mount_config": {
|
|
| 120 |
+ "device_nodes": [ |
|
| 121 |
+ {
|
|
| 122 |
+ "cgroup_permissions": "rwm", |
|
| 123 |
+ "file_mode": 438, |
|
| 124 |
+ "major_number": 1, |
|
| 125 |
+ "minor_number": 3, |
|
| 126 |
+ "path": "/dev/null", |
|
| 127 |
+ "type": 99 |
|
| 128 |
+ }, |
|
| 129 |
+ {
|
|
| 130 |
+ "cgroup_permissions": "rwm", |
|
| 131 |
+ "file_mode": 438, |
|
| 132 |
+ "major_number": 1, |
|
| 133 |
+ "minor_number": 5, |
|
| 134 |
+ "path": "/dev/zero", |
|
| 135 |
+ "type": 99 |
|
| 136 |
+ }, |
|
| 137 |
+ {
|
|
| 138 |
+ "cgroup_permissions": "rwm", |
|
| 139 |
+ "file_mode": 438, |
|
| 140 |
+ "major_number": 1, |
|
| 141 |
+ "minor_number": 7, |
|
| 142 |
+ "path": "/dev/full", |
|
| 143 |
+ "type": 99 |
|
| 144 |
+ }, |
|
| 145 |
+ {
|
|
| 146 |
+ "cgroup_permissions": "rwm", |
|
| 147 |
+ "file_mode": 438, |
|
| 148 |
+ "major_number": 5, |
|
| 149 |
+ "path": "/dev/tty", |
|
| 150 |
+ "type": 99 |
|
| 151 |
+ }, |
|
| 152 |
+ {
|
|
| 153 |
+ "cgroup_permissions": "rwm", |
|
| 154 |
+ "file_mode": 438, |
|
| 155 |
+ "major_number": 1, |
|
| 156 |
+ "minor_number": 9, |
|
| 157 |
+ "path": "/dev/urandom", |
|
| 158 |
+ "type": 99 |
|
| 159 |
+ }, |
|
| 160 |
+ {
|
|
| 161 |
+ "cgroup_permissions": "rwm", |
|
| 162 |
+ "file_mode": 438, |
|
| 163 |
+ "major_number": 1, |
|
| 164 |
+ "minor_number": 8, |
|
| 165 |
+ "path": "/dev/random", |
|
| 166 |
+ "type": 99 |
|
| 167 |
+ } |
|
| 168 |
+ ] |
|
| 169 |
+ }, |
|
| 170 |
+ "environment": [ |
|
| 171 |
+ "HOME=/", |
|
| 172 |
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", |
|
| 173 |
+ "HOSTNAME=koye", |
|
| 174 |
+ "TERM=xterm" |
|
| 175 |
+ ], |
|
| 176 |
+ "hostname": "koye", |
|
| 177 |
+ "namespaces": {
|
|
| 178 |
+ "NEWIPC": true, |
|
| 179 |
+ "NEWNET": true, |
|
| 180 |
+ "NEWNS": true, |
|
| 181 |
+ "NEWPID": true, |
|
| 182 |
+ "NEWUTS": true |
|
| 183 |
+ }, |
|
| 184 |
+ "networks": [ |
|
| 185 |
+ {
|
|
| 186 |
+ "address": "127.0.0.1/0", |
|
| 187 |
+ "gateway": "localhost", |
|
| 188 |
+ "mtu": 1500, |
|
| 189 |
+ "type": "loopback" |
|
| 190 |
+ }, |
|
| 191 |
+ {
|
|
| 192 |
+ "address": "172.17.0.101/16", |
|
| 193 |
+ "bridge": "docker0", |
|
| 194 |
+ "veth_prefix": "veth", |
|
| 195 |
+ "mtu": 1500, |
|
| 196 |
+ "type": "veth" |
|
| 197 |
+ } |
|
| 198 |
+ ], |
|
| 199 |
+ "routes": [ |
|
| 200 |
+ {
|
|
| 201 |
+ "destination": "0.0.0.0/0", |
|
| 202 |
+ "source": "172.17.0.101", |
|
| 203 |
+ "gateway": "172.17.42.1", |
|
| 204 |
+ "interface_name": "eth0" |
|
| 205 |
+ } |
|
| 206 |
+ ], |
|
| 207 |
+ "tty": true |
|
| 208 |
+} |
| 0 | 209 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,108 @@ |
| 0 |
+package user |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "syscall" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+var ( |
|
| 9 |
+ // The current operating system does not provide the required data for user lookups. |
|
| 10 |
+ ErrUnsupported = errors.New("user lookup: operating system does not provide passwd-formatted data")
|
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+func lookupUser(filter func(u User) bool) (User, error) {
|
|
| 14 |
+ // Get operating system-specific passwd reader-closer. |
|
| 15 |
+ passwd, err := GetPasswd() |
|
| 16 |
+ if err != nil {
|
|
| 17 |
+ return User{}, err
|
|
| 18 |
+ } |
|
| 19 |
+ defer passwd.Close() |
|
| 20 |
+ |
|
| 21 |
+ // Get the users. |
|
| 22 |
+ users, err := ParsePasswdFilter(passwd, filter) |
|
| 23 |
+ if err != nil {
|
|
| 24 |
+ return User{}, err
|
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ // No user entries found. |
|
| 28 |
+ if len(users) == 0 {
|
|
| 29 |
+ return User{}, fmt.Errorf("no matching entries in passwd file")
|
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ // Assume the first entry is the "correct" one. |
|
| 33 |
+ return users[0], nil |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+// CurrentUser looks up the current user by their user id in /etc/passwd. If the |
|
| 37 |
+// user cannot be found (or there is no /etc/passwd file on the filesystem), |
|
| 38 |
+// then CurrentUser returns an error. |
|
| 39 |
+func CurrentUser() (User, error) {
|
|
| 40 |
+ return LookupUid(syscall.Getuid()) |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+// LookupUser looks up a user by their username in /etc/passwd. If the user |
|
| 44 |
+// cannot be found (or there is no /etc/passwd file on the filesystem), then |
|
| 45 |
+// LookupUser returns an error. |
|
| 46 |
+func LookupUser(username string) (User, error) {
|
|
| 47 |
+ return lookupUser(func(u User) bool {
|
|
| 48 |
+ return u.Name == username |
|
| 49 |
+ }) |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot |
|
| 53 |
+// be found (or there is no /etc/passwd file on the filesystem), then LookupId |
|
| 54 |
+// returns an error. |
|
| 55 |
+func LookupUid(uid int) (User, error) {
|
|
| 56 |
+ return lookupUser(func(u User) bool {
|
|
| 57 |
+ return u.Uid == uid |
|
| 58 |
+ }) |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func lookupGroup(filter func(g Group) bool) (Group, error) {
|
|
| 62 |
+ // Get operating system-specific group reader-closer. |
|
| 63 |
+ group, err := GetGroup() |
|
| 64 |
+ if err != nil {
|
|
| 65 |
+ return Group{}, err
|
|
| 66 |
+ } |
|
| 67 |
+ defer group.Close() |
|
| 68 |
+ |
|
| 69 |
+ // Get the users. |
|
| 70 |
+ groups, err := ParseGroupFilter(group, filter) |
|
| 71 |
+ if err != nil {
|
|
| 72 |
+ return Group{}, err
|
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ // No user entries found. |
|
| 76 |
+ if len(groups) == 0 {
|
|
| 77 |
+ return Group{}, fmt.Errorf("no matching entries in group file")
|
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 80 |
+ // Assume the first entry is the "correct" one. |
|
| 81 |
+ return groups[0], nil |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// CurrentGroup looks up the current user's group by their primary group id's |
|
| 85 |
+// entry in /etc/passwd. If the group cannot be found (or there is no |
|
| 86 |
+// /etc/group file on the filesystem), then CurrentGroup returns an error. |
|
| 87 |
+func CurrentGroup() (Group, error) {
|
|
| 88 |
+ return LookupGid(syscall.Getgid()) |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+// LookupGroup looks up a group by its name in /etc/group. If the group cannot |
|
| 92 |
+// be found (or there is no /etc/group file on the filesystem), then LookupGroup |
|
| 93 |
+// returns an error. |
|
| 94 |
+func LookupGroup(groupname string) (Group, error) {
|
|
| 95 |
+ return lookupGroup(func(g Group) bool {
|
|
| 96 |
+ return g.Name == groupname |
|
| 97 |
+ }) |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+// LookupGid looks up a group by its group id in /etc/group. If the group cannot |
|
| 101 |
+// be found (or there is no /etc/group file on the filesystem), then LookupGid |
|
| 102 |
+// returns an error. |
|
| 103 |
+func LookupGid(gid int) (Group, error) {
|
|
| 104 |
+ return lookupGroup(func(g Group) bool {
|
|
| 105 |
+ return g.Gid == gid |
|
| 106 |
+ }) |
|
| 107 |
+} |
| 0 | 108 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,30 @@ |
| 0 |
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris |
|
| 1 |
+ |
|
| 2 |
+package user |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "io" |
|
| 6 |
+ "os" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// Unix-specific path to the passwd and group formatted files. |
|
| 10 |
+const ( |
|
| 11 |
+ unixPasswdFile = "/etc/passwd" |
|
| 12 |
+ unixGroupFile = "/etc/group" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+func GetPasswdFile() (string, error) {
|
|
| 16 |
+ return unixPasswdFile, nil |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+func GetPasswd() (io.ReadCloser, error) {
|
|
| 20 |
+ return os.Open(unixPasswdFile) |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func GetGroupFile() (string, error) {
|
|
| 24 |
+ return unixGroupFile, nil |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+func GetGroup() (io.ReadCloser, error) {
|
|
| 28 |
+ return os.Open(unixGroupFile) |
|
| 29 |
+} |
| 0 | 30 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,21 @@ |
| 0 |
+// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris |
|
| 1 |
+ |
|
| 2 |
+package user |
|
| 3 |
+ |
|
| 4 |
+import "io" |
|
| 5 |
+ |
|
| 6 |
+func GetPasswdFile() (string, error) {
|
|
| 7 |
+ return "", ErrUnsupported |
|
| 8 |
+} |
|
| 9 |
+ |
|
| 10 |
+func GetPasswd() (io.ReadCloser, error) {
|
|
| 11 |
+ return nil, ErrUnsupported |
|
| 12 |
+} |
|
| 13 |
+ |
|
| 14 |
+func GetGroupFile() (string, error) {
|
|
| 15 |
+ return "", ErrUnsupported |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func GetGroup() (io.ReadCloser, error) {
|
|
| 19 |
+ return nil, ErrUnsupported |
|
| 20 |
+} |
| ... | ... |
@@ -69,23 +69,36 @@ func parseLine(line string, v ...interface{}) {
|
| 69 | 69 |
} |
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 |
-func ParsePasswd() ([]*User, error) {
|
|
| 73 |
- return ParsePasswdFilter(nil) |
|
| 72 |
+func ParsePasswdFile(path string) ([]User, error) {
|
|
| 73 |
+ passwd, err := os.Open(path) |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ return nil, err |
|
| 76 |
+ } |
|
| 77 |
+ defer passwd.Close() |
|
| 78 |
+ return ParsePasswd(passwd) |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+func ParsePasswd(passwd io.Reader) ([]User, error) {
|
|
| 82 |
+ return ParsePasswdFilter(passwd, nil) |
|
| 74 | 83 |
} |
| 75 | 84 |
|
| 76 |
-func ParsePasswdFilter(filter func(*User) bool) ([]*User, error) {
|
|
| 77 |
- f, err := os.Open("/etc/passwd")
|
|
| 85 |
+func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error) {
|
|
| 86 |
+ passwd, err := os.Open(path) |
|
| 78 | 87 |
if err != nil {
|
| 79 | 88 |
return nil, err |
| 80 | 89 |
} |
| 81 |
- defer f.Close() |
|
| 82 |
- return parsePasswdFile(f, filter) |
|
| 90 |
+ defer passwd.Close() |
|
| 91 |
+ return ParsePasswdFilter(passwd, filter) |
|
| 83 | 92 |
} |
| 84 | 93 |
|
| 85 |
-func parsePasswdFile(r io.Reader, filter func(*User) bool) ([]*User, error) {
|
|
| 94 |
+func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
|
|
| 95 |
+ if r == nil {
|
|
| 96 |
+ return nil, fmt.Errorf("nil source for passwd-formatted data")
|
|
| 97 |
+ } |
|
| 98 |
+ |
|
| 86 | 99 |
var ( |
| 87 | 100 |
s = bufio.NewScanner(r) |
| 88 |
- out = []*User{}
|
|
| 101 |
+ out = []User{}
|
|
| 89 | 102 |
) |
| 90 | 103 |
|
| 91 | 104 |
for s.Scan() {
|
| ... | ... |
@@ -103,7 +116,7 @@ func parsePasswdFile(r io.Reader, filter func(*User) bool) ([]*User, error) {
|
| 103 | 103 |
// Name:Pass:Uid:Gid:Gecos:Home:Shell |
| 104 | 104 |
// root:x:0:0:root:/root:/bin/bash |
| 105 | 105 |
// adm:x:3:4:adm:/var/adm:/bin/false |
| 106 |
- p := &User{}
|
|
| 106 |
+ p := User{}
|
|
| 107 | 107 |
parseLine( |
| 108 | 108 |
text, |
| 109 | 109 |
&p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell, |
| ... | ... |
@@ -117,23 +130,36 @@ func parsePasswdFile(r io.Reader, filter func(*User) bool) ([]*User, error) {
|
| 117 | 117 |
return out, nil |
| 118 | 118 |
} |
| 119 | 119 |
|
| 120 |
-func ParseGroup() ([]*Group, error) {
|
|
| 121 |
- return ParseGroupFilter(nil) |
|
| 120 |
+func ParseGroupFile(path string) ([]Group, error) {
|
|
| 121 |
+ group, err := os.Open(path) |
|
| 122 |
+ if err != nil {
|
|
| 123 |
+ return nil, err |
|
| 124 |
+ } |
|
| 125 |
+ defer group.Close() |
|
| 126 |
+ return ParseGroup(group) |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+func ParseGroup(group io.Reader) ([]Group, error) {
|
|
| 130 |
+ return ParseGroupFilter(group, nil) |
|
| 122 | 131 |
} |
| 123 | 132 |
|
| 124 |
-func ParseGroupFilter(filter func(*Group) bool) ([]*Group, error) {
|
|
| 125 |
- f, err := os.Open("/etc/group")
|
|
| 133 |
+func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) {
|
|
| 134 |
+ group, err := os.Open(path) |
|
| 126 | 135 |
if err != nil {
|
| 127 | 136 |
return nil, err |
| 128 | 137 |
} |
| 129 |
- defer f.Close() |
|
| 130 |
- return parseGroupFile(f, filter) |
|
| 138 |
+ defer group.Close() |
|
| 139 |
+ return ParseGroupFilter(group, filter) |
|
| 131 | 140 |
} |
| 132 | 141 |
|
| 133 |
-func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
|
|
| 142 |
+func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
|
|
| 143 |
+ if r == nil {
|
|
| 144 |
+ return nil, fmt.Errorf("nil source for group-formatted data")
|
|
| 145 |
+ } |
|
| 146 |
+ |
|
| 134 | 147 |
var ( |
| 135 | 148 |
s = bufio.NewScanner(r) |
| 136 |
- out = []*Group{}
|
|
| 149 |
+ out = []Group{}
|
|
| 137 | 150 |
) |
| 138 | 151 |
|
| 139 | 152 |
for s.Scan() {
|
| ... | ... |
@@ -151,7 +177,7 @@ func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
|
| 151 | 151 |
// Name:Pass:Gid:List |
| 152 | 152 |
// root:x:0:root |
| 153 | 153 |
// adm:x:4:root,adm,daemon |
| 154 |
- p := &Group{}
|
|
| 154 |
+ p := Group{}
|
|
| 155 | 155 |
parseLine( |
| 156 | 156 |
text, |
| 157 | 157 |
&p.Name, &p.Pass, &p.Gid, &p.List, |
| ... | ... |
@@ -165,94 +191,160 @@ func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
|
| 165 | 165 |
return out, nil |
| 166 | 166 |
} |
| 167 | 167 |
|
| 168 |
-// Given a string like "user", "1000", "user:group", "1000:1000", returns the uid, gid, list of supplementary group IDs, and home directory, if available and/or applicable. |
|
| 169 |
-func GetUserGroupSupplementaryHome(userSpec string, defaultUid, defaultGid int, defaultHome string) (int, int, []int, string, error) {
|
|
| 170 |
- var ( |
|
| 171 |
- uid = defaultUid |
|
| 172 |
- gid = defaultGid |
|
| 173 |
- suppGids = []int{}
|
|
| 174 |
- home = defaultHome |
|
| 168 |
+type ExecUser struct {
|
|
| 169 |
+ Uid, Gid int |
|
| 170 |
+ Sgids []int |
|
| 171 |
+ Home string |
|
| 172 |
+} |
|
| 175 | 173 |
|
| 174 |
+// GetExecUserFile is a wrapper for GetExecUser. It reads data from each of the |
|
| 175 |
+// given file paths and uses that data as the arguments to GetExecUser. If the |
|
| 176 |
+// files cannot be opened for any reason, the error is ignored and a nil |
|
| 177 |
+// io.Reader is passed instead. |
|
| 178 |
+func GetExecUserFile(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
|
| 179 |
+ passwd, err := os.Open(passwdPath) |
|
| 180 |
+ if err != nil {
|
|
| 181 |
+ passwd = nil |
|
| 182 |
+ } else {
|
|
| 183 |
+ defer passwd.Close() |
|
| 184 |
+ } |
|
| 185 |
+ |
|
| 186 |
+ group, err := os.Open(groupPath) |
|
| 187 |
+ if err != nil {
|
|
| 188 |
+ group = nil |
|
| 189 |
+ } else {
|
|
| 190 |
+ defer group.Close() |
|
| 191 |
+ } |
|
| 192 |
+ |
|
| 193 |
+ return GetExecUser(userSpec, defaults, passwd, group) |
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+// GetExecUser parses a user specification string (using the passwd and group |
|
| 197 |
+// readers as sources for /etc/passwd and /etc/group data, respectively). In |
|
| 198 |
+// the case of blank fields or missing data from the sources, the values in |
|
| 199 |
+// defaults is used. |
|
| 200 |
+// |
|
| 201 |
+// GetExecUser will return an error if a user or group literal could not be |
|
| 202 |
+// found in any entry in passwd and group respectively. |
|
| 203 |
+// |
|
| 204 |
+// Examples of valid user specifications are: |
|
| 205 |
+// * "" |
|
| 206 |
+// * "user" |
|
| 207 |
+// * "uid" |
|
| 208 |
+// * "user:group" |
|
| 209 |
+// * "uid:gid |
|
| 210 |
+// * "user:gid" |
|
| 211 |
+// * "uid:group" |
|
| 212 |
+func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (*ExecUser, error) {
|
|
| 213 |
+ var ( |
|
| 176 | 214 |
userArg, groupArg string |
| 215 |
+ name string |
|
| 177 | 216 |
) |
| 178 | 217 |
|
| 218 |
+ if defaults == nil {
|
|
| 219 |
+ defaults = new(ExecUser) |
|
| 220 |
+ } |
|
| 221 |
+ |
|
| 222 |
+ // Copy over defaults. |
|
| 223 |
+ user := &ExecUser{
|
|
| 224 |
+ Uid: defaults.Uid, |
|
| 225 |
+ Gid: defaults.Gid, |
|
| 226 |
+ Sgids: defaults.Sgids, |
|
| 227 |
+ Home: defaults.Home, |
|
| 228 |
+ } |
|
| 229 |
+ |
|
| 230 |
+ // Sgids slice *cannot* be nil. |
|
| 231 |
+ if user.Sgids == nil {
|
|
| 232 |
+ user.Sgids = []int{}
|
|
| 233 |
+ } |
|
| 234 |
+ |
|
| 179 | 235 |
// allow for userArg to have either "user" syntax, or optionally "user:group" syntax |
| 180 | 236 |
parseLine(userSpec, &userArg, &groupArg) |
| 181 | 237 |
|
| 182 |
- users, err := ParsePasswdFilter(func(u *User) bool {
|
|
| 238 |
+ users, err := ParsePasswdFilter(passwd, func(u User) bool {
|
|
| 183 | 239 |
if userArg == "" {
|
| 184 |
- return u.Uid == uid |
|
| 240 |
+ return u.Uid == user.Uid |
|
| 185 | 241 |
} |
| 186 | 242 |
return u.Name == userArg || strconv.Itoa(u.Uid) == userArg |
| 187 | 243 |
}) |
| 188 |
- if err != nil && !os.IsNotExist(err) {
|
|
| 244 |
+ if err != nil && passwd != nil {
|
|
| 189 | 245 |
if userArg == "" {
|
| 190 |
- userArg = strconv.Itoa(uid) |
|
| 246 |
+ userArg = strconv.Itoa(user.Uid) |
|
| 191 | 247 |
} |
| 192 |
- return 0, 0, nil, "", fmt.Errorf("Unable to find user %v: %v", userArg, err)
|
|
| 248 |
+ return nil, fmt.Errorf("Unable to find user %v: %v", userArg, err)
|
|
| 193 | 249 |
} |
| 194 | 250 |
|
| 195 | 251 |
haveUser := users != nil && len(users) > 0 |
| 196 | 252 |
if haveUser {
|
| 197 | 253 |
// if we found any user entries that matched our filter, let's take the first one as "correct" |
| 198 |
- uid = users[0].Uid |
|
| 199 |
- gid = users[0].Gid |
|
| 200 |
- home = users[0].Home |
|
| 254 |
+ name = users[0].Name |
|
| 255 |
+ user.Uid = users[0].Uid |
|
| 256 |
+ user.Gid = users[0].Gid |
|
| 257 |
+ user.Home = users[0].Home |
|
| 201 | 258 |
} else if userArg != "" {
|
| 202 | 259 |
// we asked for a user but didn't find them... let's check to see if we wanted a numeric user |
| 203 |
- uid, err = strconv.Atoi(userArg) |
|
| 260 |
+ user.Uid, err = strconv.Atoi(userArg) |
|
| 204 | 261 |
if err != nil {
|
| 205 | 262 |
// not numeric - we have to bail |
| 206 |
- return 0, 0, nil, "", fmt.Errorf("Unable to find user %v", userArg)
|
|
| 263 |
+ return nil, fmt.Errorf("Unable to find user %v", userArg)
|
|
| 207 | 264 |
} |
| 208 |
- if uid < minId || uid > maxId {
|
|
| 209 |
- return 0, 0, nil, "", ErrRange |
|
| 265 |
+ |
|
| 266 |
+ // Must be inside valid uid range. |
|
| 267 |
+ if user.Uid < minId || user.Uid > maxId {
|
|
| 268 |
+ return nil, ErrRange |
|
| 210 | 269 |
} |
| 211 | 270 |
|
| 212 | 271 |
// if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit |
| 213 | 272 |
} |
| 214 | 273 |
|
| 215 |
- if groupArg != "" || (haveUser && users[0].Name != "") {
|
|
| 216 |
- groups, err := ParseGroupFilter(func(g *Group) bool {
|
|
| 274 |
+ if groupArg != "" || name != "" {
|
|
| 275 |
+ groups, err := ParseGroupFilter(group, func(g Group) bool {
|
|
| 276 |
+ // Explicit group format takes precedence. |
|
| 217 | 277 |
if groupArg != "" {
|
| 218 | 278 |
return g.Name == groupArg || strconv.Itoa(g.Gid) == groupArg |
| 219 | 279 |
} |
| 280 |
+ |
|
| 281 |
+ // Check if user is a member. |
|
| 220 | 282 |
for _, u := range g.List {
|
| 221 |
- if u == users[0].Name {
|
|
| 283 |
+ if u == name {
|
|
| 222 | 284 |
return true |
| 223 | 285 |
} |
| 224 | 286 |
} |
| 287 |
+ |
|
| 225 | 288 |
return false |
| 226 | 289 |
}) |
| 227 |
- if err != nil && !os.IsNotExist(err) {
|
|
| 228 |
- return 0, 0, nil, "", fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
|
|
| 290 |
+ if err != nil && group != nil {
|
|
| 291 |
+ return nil, fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
|
|
| 229 | 292 |
} |
| 230 | 293 |
|
| 231 | 294 |
haveGroup := groups != nil && len(groups) > 0 |
| 232 | 295 |
if groupArg != "" {
|
| 233 | 296 |
if haveGroup {
|
| 234 | 297 |
// if we found any group entries that matched our filter, let's take the first one as "correct" |
| 235 |
- gid = groups[0].Gid |
|
| 298 |
+ user.Gid = groups[0].Gid |
|
| 236 | 299 |
} else {
|
| 237 | 300 |
// we asked for a group but didn't find id... let's check to see if we wanted a numeric group |
| 238 |
- gid, err = strconv.Atoi(groupArg) |
|
| 301 |
+ user.Gid, err = strconv.Atoi(groupArg) |
|
| 239 | 302 |
if err != nil {
|
| 240 | 303 |
// not numeric - we have to bail |
| 241 |
- return 0, 0, nil, "", fmt.Errorf("Unable to find group %v", groupArg)
|
|
| 304 |
+ return nil, fmt.Errorf("Unable to find group %v", groupArg)
|
|
| 242 | 305 |
} |
| 243 |
- if gid < minId || gid > maxId {
|
|
| 244 |
- return 0, 0, nil, "", ErrRange |
|
| 306 |
+ |
|
| 307 |
+ // Ensure gid is inside gid range. |
|
| 308 |
+ if user.Gid < minId || user.Gid > maxId {
|
|
| 309 |
+ return nil, ErrRange |
|
| 245 | 310 |
} |
| 246 | 311 |
|
| 247 | 312 |
// if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit |
| 248 | 313 |
} |
| 249 | 314 |
} else if haveGroup {
|
| 250 |
- suppGids = make([]int, len(groups)) |
|
| 315 |
+ // If implicit group format, fill supplementary gids. |
|
| 316 |
+ user.Sgids = make([]int, len(groups)) |
|
| 251 | 317 |
for i, group := range groups {
|
| 252 |
- suppGids[i] = group.Gid |
|
| 318 |
+ user.Sgids[i] = group.Gid |
|
| 253 | 319 |
} |
| 254 | 320 |
} |
| 255 | 321 |
} |
| 256 | 322 |
|
| 257 |
- return uid, gid, suppGids, home, nil |
|
| 323 |
+ return user, nil |
|
| 258 | 324 |
} |
| ... | ... |
@@ -1,6 +1,8 @@ |
| 1 | 1 |
package user |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "io" |
|
| 5 |
+ "reflect" |
|
| 4 | 6 |
"strings" |
| 5 | 7 |
"testing" |
| 6 | 8 |
) |
| ... | ... |
@@ -54,7 +56,7 @@ func TestUserParseLine(t *testing.T) {
|
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 | 56 |
func TestUserParsePasswd(t *testing.T) {
|
| 57 |
- users, err := parsePasswdFile(strings.NewReader(` |
|
| 57 |
+ users, err := ParsePasswdFilter(strings.NewReader(` |
|
| 58 | 58 |
root:x:0:0:root:/root:/bin/bash |
| 59 | 59 |
adm:x:3:4:adm:/var/adm:/bin/false |
| 60 | 60 |
this is just some garbage data |
| ... | ... |
@@ -74,7 +76,7 @@ this is just some garbage data |
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 | 76 |
func TestUserParseGroup(t *testing.T) {
|
| 77 |
- groups, err := parseGroupFile(strings.NewReader(` |
|
| 77 |
+ groups, err := ParseGroupFilter(strings.NewReader(` |
|
| 78 | 78 |
root:x:0:root |
| 79 | 79 |
adm:x:4:root,adm,daemon |
| 80 | 80 |
this is just some garbage data |
| ... | ... |
@@ -92,3 +94,259 @@ this is just some garbage data |
| 92 | 92 |
t.Fatalf("Expected groups[1] to be 4 - adm - 3 members, got %v - %v - %v", groups[1].Gid, groups[1].Name, len(groups[1].List))
|
| 93 | 93 |
} |
| 94 | 94 |
} |
| 95 |
+ |
|
| 96 |
+func TestValidGetExecUser(t *testing.T) {
|
|
| 97 |
+ const passwdContent = ` |
|
| 98 |
+root:x:0:0:root user:/root:/bin/bash |
|
| 99 |
+adm:x:42:43:adm:/var/adm:/bin/false |
|
| 100 |
+this is just some garbage data |
|
| 101 |
+` |
|
| 102 |
+ const groupContent = ` |
|
| 103 |
+root:x:0:root |
|
| 104 |
+adm:x:43: |
|
| 105 |
+grp:x:1234:root,adm |
|
| 106 |
+this is just some garbage data |
|
| 107 |
+` |
|
| 108 |
+ defaultExecUser := ExecUser{
|
|
| 109 |
+ Uid: 8888, |
|
| 110 |
+ Gid: 8888, |
|
| 111 |
+ Sgids: []int{8888},
|
|
| 112 |
+ Home: "/8888", |
|
| 113 |
+ } |
|
| 114 |
+ |
|
| 115 |
+ tests := []struct {
|
|
| 116 |
+ ref string |
|
| 117 |
+ expected ExecUser |
|
| 118 |
+ }{
|
|
| 119 |
+ {
|
|
| 120 |
+ ref: "root", |
|
| 121 |
+ expected: ExecUser{
|
|
| 122 |
+ Uid: 0, |
|
| 123 |
+ Gid: 0, |
|
| 124 |
+ Sgids: []int{0, 1234},
|
|
| 125 |
+ Home: "/root", |
|
| 126 |
+ }, |
|
| 127 |
+ }, |
|
| 128 |
+ {
|
|
| 129 |
+ ref: "adm", |
|
| 130 |
+ expected: ExecUser{
|
|
| 131 |
+ Uid: 42, |
|
| 132 |
+ Gid: 43, |
|
| 133 |
+ Sgids: []int{1234},
|
|
| 134 |
+ Home: "/var/adm", |
|
| 135 |
+ }, |
|
| 136 |
+ }, |
|
| 137 |
+ {
|
|
| 138 |
+ ref: "root:adm", |
|
| 139 |
+ expected: ExecUser{
|
|
| 140 |
+ Uid: 0, |
|
| 141 |
+ Gid: 43, |
|
| 142 |
+ Sgids: defaultExecUser.Sgids, |
|
| 143 |
+ Home: "/root", |
|
| 144 |
+ }, |
|
| 145 |
+ }, |
|
| 146 |
+ {
|
|
| 147 |
+ ref: "adm:1234", |
|
| 148 |
+ expected: ExecUser{
|
|
| 149 |
+ Uid: 42, |
|
| 150 |
+ Gid: 1234, |
|
| 151 |
+ Sgids: defaultExecUser.Sgids, |
|
| 152 |
+ Home: "/var/adm", |
|
| 153 |
+ }, |
|
| 154 |
+ }, |
|
| 155 |
+ {
|
|
| 156 |
+ ref: "42:1234", |
|
| 157 |
+ expected: ExecUser{
|
|
| 158 |
+ Uid: 42, |
|
| 159 |
+ Gid: 1234, |
|
| 160 |
+ Sgids: defaultExecUser.Sgids, |
|
| 161 |
+ Home: "/var/adm", |
|
| 162 |
+ }, |
|
| 163 |
+ }, |
|
| 164 |
+ {
|
|
| 165 |
+ ref: "1337:1234", |
|
| 166 |
+ expected: ExecUser{
|
|
| 167 |
+ Uid: 1337, |
|
| 168 |
+ Gid: 1234, |
|
| 169 |
+ Sgids: defaultExecUser.Sgids, |
|
| 170 |
+ Home: defaultExecUser.Home, |
|
| 171 |
+ }, |
|
| 172 |
+ }, |
|
| 173 |
+ {
|
|
| 174 |
+ ref: "1337", |
|
| 175 |
+ expected: ExecUser{
|
|
| 176 |
+ Uid: 1337, |
|
| 177 |
+ Gid: defaultExecUser.Gid, |
|
| 178 |
+ Sgids: defaultExecUser.Sgids, |
|
| 179 |
+ Home: defaultExecUser.Home, |
|
| 180 |
+ }, |
|
| 181 |
+ }, |
|
| 182 |
+ {
|
|
| 183 |
+ ref: "", |
|
| 184 |
+ expected: ExecUser{
|
|
| 185 |
+ Uid: defaultExecUser.Uid, |
|
| 186 |
+ Gid: defaultExecUser.Gid, |
|
| 187 |
+ Sgids: defaultExecUser.Sgids, |
|
| 188 |
+ Home: defaultExecUser.Home, |
|
| 189 |
+ }, |
|
| 190 |
+ }, |
|
| 191 |
+ } |
|
| 192 |
+ |
|
| 193 |
+ for _, test := range tests {
|
|
| 194 |
+ passwd := strings.NewReader(passwdContent) |
|
| 195 |
+ group := strings.NewReader(groupContent) |
|
| 196 |
+ |
|
| 197 |
+ execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group) |
|
| 198 |
+ if err != nil {
|
|
| 199 |
+ t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error())
|
|
| 200 |
+ t.Fail() |
|
| 201 |
+ continue |
|
| 202 |
+ } |
|
| 203 |
+ |
|
| 204 |
+ if !reflect.DeepEqual(test.expected, *execUser) {
|
|
| 205 |
+ t.Logf("got: %#v", execUser)
|
|
| 206 |
+ t.Logf("expected: %#v", test.expected)
|
|
| 207 |
+ t.Fail() |
|
| 208 |
+ continue |
|
| 209 |
+ } |
|
| 210 |
+ } |
|
| 211 |
+} |
|
| 212 |
+ |
|
| 213 |
+func TestInvalidGetExecUser(t *testing.T) {
|
|
| 214 |
+ const passwdContent = ` |
|
| 215 |
+root:x:0:0:root user:/root:/bin/bash |
|
| 216 |
+adm:x:42:43:adm:/var/adm:/bin/false |
|
| 217 |
+this is just some garbage data |
|
| 218 |
+` |
|
| 219 |
+ const groupContent = ` |
|
| 220 |
+root:x:0:root |
|
| 221 |
+adm:x:43: |
|
| 222 |
+grp:x:1234:root,adm |
|
| 223 |
+this is just some garbage data |
|
| 224 |
+` |
|
| 225 |
+ |
|
| 226 |
+ tests := []string{
|
|
| 227 |
+ // No such user/group. |
|
| 228 |
+ "notuser", |
|
| 229 |
+ "notuser:notgroup", |
|
| 230 |
+ "root:notgroup", |
|
| 231 |
+ "notuser:adm", |
|
| 232 |
+ "8888:notgroup", |
|
| 233 |
+ "notuser:8888", |
|
| 234 |
+ |
|
| 235 |
+ // Invalid user/group values. |
|
| 236 |
+ "-1:0", |
|
| 237 |
+ "0:-3", |
|
| 238 |
+ "-5:-2", |
|
| 239 |
+ } |
|
| 240 |
+ |
|
| 241 |
+ for _, test := range tests {
|
|
| 242 |
+ passwd := strings.NewReader(passwdContent) |
|
| 243 |
+ group := strings.NewReader(groupContent) |
|
| 244 |
+ |
|
| 245 |
+ execUser, err := GetExecUser(test, nil, passwd, group) |
|
| 246 |
+ if err == nil {
|
|
| 247 |
+ t.Logf("got unexpected success when parsing '%s': %#v", test, execUser)
|
|
| 248 |
+ t.Fail() |
|
| 249 |
+ continue |
|
| 250 |
+ } |
|
| 251 |
+ } |
|
| 252 |
+} |
|
| 253 |
+ |
|
| 254 |
+func TestGetExecUserNilSources(t *testing.T) {
|
|
| 255 |
+ const passwdContent = ` |
|
| 256 |
+root:x:0:0:root user:/root:/bin/bash |
|
| 257 |
+adm:x:42:43:adm:/var/adm:/bin/false |
|
| 258 |
+this is just some garbage data |
|
| 259 |
+` |
|
| 260 |
+ const groupContent = ` |
|
| 261 |
+root:x:0:root |
|
| 262 |
+adm:x:43: |
|
| 263 |
+grp:x:1234:root,adm |
|
| 264 |
+this is just some garbage data |
|
| 265 |
+` |
|
| 266 |
+ |
|
| 267 |
+ defaultExecUser := ExecUser{
|
|
| 268 |
+ Uid: 8888, |
|
| 269 |
+ Gid: 8888, |
|
| 270 |
+ Sgids: []int{8888},
|
|
| 271 |
+ Home: "/8888", |
|
| 272 |
+ } |
|
| 273 |
+ |
|
| 274 |
+ tests := []struct {
|
|
| 275 |
+ ref string |
|
| 276 |
+ passwd, group bool |
|
| 277 |
+ expected ExecUser |
|
| 278 |
+ }{
|
|
| 279 |
+ {
|
|
| 280 |
+ ref: "", |
|
| 281 |
+ passwd: false, |
|
| 282 |
+ group: false, |
|
| 283 |
+ expected: ExecUser{
|
|
| 284 |
+ Uid: 8888, |
|
| 285 |
+ Gid: 8888, |
|
| 286 |
+ Sgids: []int{8888},
|
|
| 287 |
+ Home: "/8888", |
|
| 288 |
+ }, |
|
| 289 |
+ }, |
|
| 290 |
+ {
|
|
| 291 |
+ ref: "root", |
|
| 292 |
+ passwd: true, |
|
| 293 |
+ group: false, |
|
| 294 |
+ expected: ExecUser{
|
|
| 295 |
+ Uid: 0, |
|
| 296 |
+ Gid: 0, |
|
| 297 |
+ Sgids: []int{8888},
|
|
| 298 |
+ Home: "/root", |
|
| 299 |
+ }, |
|
| 300 |
+ }, |
|
| 301 |
+ {
|
|
| 302 |
+ ref: "0", |
|
| 303 |
+ passwd: false, |
|
| 304 |
+ group: false, |
|
| 305 |
+ expected: ExecUser{
|
|
| 306 |
+ Uid: 0, |
|
| 307 |
+ Gid: 8888, |
|
| 308 |
+ Sgids: []int{8888},
|
|
| 309 |
+ Home: "/8888", |
|
| 310 |
+ }, |
|
| 311 |
+ }, |
|
| 312 |
+ {
|
|
| 313 |
+ ref: "0:0", |
|
| 314 |
+ passwd: false, |
|
| 315 |
+ group: false, |
|
| 316 |
+ expected: ExecUser{
|
|
| 317 |
+ Uid: 0, |
|
| 318 |
+ Gid: 0, |
|
| 319 |
+ Sgids: []int{8888},
|
|
| 320 |
+ Home: "/8888", |
|
| 321 |
+ }, |
|
| 322 |
+ }, |
|
| 323 |
+ } |
|
| 324 |
+ |
|
| 325 |
+ for _, test := range tests {
|
|
| 326 |
+ var passwd, group io.Reader |
|
| 327 |
+ |
|
| 328 |
+ if test.passwd {
|
|
| 329 |
+ passwd = strings.NewReader(passwdContent) |
|
| 330 |
+ } |
|
| 331 |
+ |
|
| 332 |
+ if test.group {
|
|
| 333 |
+ group = strings.NewReader(groupContent) |
|
| 334 |
+ } |
|
| 335 |
+ |
|
| 336 |
+ execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group) |
|
| 337 |
+ if err != nil {
|
|
| 338 |
+ t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error())
|
|
| 339 |
+ t.Fail() |
|
| 340 |
+ continue |
|
| 341 |
+ } |
|
| 342 |
+ |
|
| 343 |
+ if !reflect.DeepEqual(test.expected, *execUser) {
|
|
| 344 |
+ t.Logf("got: %#v", execUser)
|
|
| 345 |
+ t.Logf("expected: %#v", test.expected)
|
|
| 346 |
+ t.Fail() |
|
| 347 |
+ continue |
|
| 348 |
+ } |
|
| 349 |
+ } |
|
| 350 |
+} |