mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #9023 from cyphar/libcontainer-update-to-new-interface
Update vendor'd `libcontainer` and update to use new `user` interface.
This commit is contained in:
commit
b18efeeb7a
11 changed files with 833 additions and 71 deletions
|
@ -1370,7 +1370,11 @@ func ServeFd(addr string, handle http.Handler) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupGidByName(nameOrGid string) (int, error) {
|
func lookupGidByName(nameOrGid string) (int, error) {
|
||||||
groups, err := user.ParseGroupFilter(func(g *user.Group) bool {
|
groupFile, err := user.GetGroupFile()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
groups, err := user.ParseGroupFileFilter(groupFile, func(g user.Group) bool {
|
||||||
return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
|
return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -66,7 +66,7 @@ if [ "$1" = '--go' ]; then
|
||||||
mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
|
mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
|
||||||
fi
|
fi
|
||||||
|
|
||||||
clone git github.com/docker/libcontainer fd6df76562137aa3b18e44b790cb484fe2b6fa0b
|
clone git github.com/docker/libcontainer 4ae31b6ceb2c2557c9f05f42da61b0b808faa5a4
|
||||||
# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
|
# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
|
||||||
rm -rf src/github.com/docker/libcontainer/vendor
|
rm -rf src/github.com/docker/libcontainer/vendor
|
||||||
eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"
|
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 {
|
||||||
|
|
||||||
// SetupUser changes the groups, gid, and uid for the user inside the container
|
// SetupUser changes the groups, gid, and uid for the user inside the container
|
||||||
func SetupUser(u string) error {
|
func SetupUser(u string) error {
|
||||||
uid, gid, suppGids, home, err := user.GetUserGroupSupplementaryHome(u, syscall.Getuid(), syscall.Getgid(), "/")
|
// Set up defaults.
|
||||||
|
defaultExecUser := user.ExecUser{
|
||||||
|
Uid: syscall.Getuid(),
|
||||||
|
Gid: syscall.Getgid(),
|
||||||
|
Home: "/",
|
||||||
|
}
|
||||||
|
|
||||||
|
passwdFile, err := user.GetPasswdFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
groupFile, err := user.GetGroupFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
execUser, err := user.GetExecUserFile(u, &defaultExecUser, passwdFile, groupFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get supplementary groups %s", err)
|
return fmt.Errorf("get supplementary groups %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := syscall.Setgroups(suppGids); err != nil {
|
if err := syscall.Setgroups(execUser.Sgids); err != nil {
|
||||||
return fmt.Errorf("setgroups %s", err)
|
return fmt.Errorf("setgroups %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := system.Setgid(gid); err != nil {
|
if err := system.Setgid(execUser.Gid); err != nil {
|
||||||
return fmt.Errorf("setgid %s", err)
|
return fmt.Errorf("setgid %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := system.Setuid(uid); err != nil {
|
if err := system.Setuid(execUser.Uid); err != nil {
|
||||||
return fmt.Errorf("setuid %s", err)
|
return fmt.Errorf("setuid %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we didn't get HOME already, set it based on the user's HOME
|
// if we didn't get HOME already, set it based on the user's HOME
|
||||||
if envHome := os.Getenv("HOME"); envHome == "" {
|
if envHome := os.Getenv("HOME"); envHome == "" {
|
||||||
if err := os.Setenv("HOME", home); err != nil {
|
if err := os.Setenv("HOME", execUser.Home); err != nil {
|
||||||
return fmt.Errorf("set HOME %s", err)
|
return fmt.Errorf("set HOME %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1003,28 +1003,23 @@ func AddRoute(destination, source, gateway, device string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if source != "" {
|
if source != "" {
|
||||||
srcIP, srcNet, err := net.ParseCIDR(source)
|
srcIP := net.ParseIP(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("source CIDR %s couldn't be parsed", source)
|
return fmt.Errorf("source IP %s couldn't be parsed", source)
|
||||||
}
|
}
|
||||||
srcFamily := getIpFamily(srcIP)
|
srcFamily := getIpFamily(srcIP)
|
||||||
if currentFamily != -1 && currentFamily != srcFamily {
|
if currentFamily != -1 && currentFamily != srcFamily {
|
||||||
return fmt.Errorf("source and destination ip were not the same IP family")
|
return fmt.Errorf("source and destination ip were not the same IP family")
|
||||||
}
|
}
|
||||||
currentFamily = srcFamily
|
currentFamily = srcFamily
|
||||||
srcLen, bits := srcNet.Mask.Size()
|
|
||||||
if srcLen == 0 && bits == 0 {
|
|
||||||
return fmt.Errorf("source CIDR %s generated a non-canonical Mask", source)
|
|
||||||
}
|
|
||||||
msg.Family = uint8(srcFamily)
|
msg.Family = uint8(srcFamily)
|
||||||
msg.Src_len = uint8(srcLen)
|
|
||||||
var srcData []byte
|
var srcData []byte
|
||||||
if srcFamily == syscall.AF_INET {
|
if srcFamily == syscall.AF_INET {
|
||||||
srcData = srcIP.To4()
|
srcData = srcIP.To4()
|
||||||
} else {
|
} else {
|
||||||
srcData = srcIP.To16()
|
srcData = srcIP.To16()
|
||||||
}
|
}
|
||||||
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_SRC, srcData))
|
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_PREFSRC, srcData))
|
||||||
}
|
}
|
||||||
|
|
||||||
if gateway != "" {
|
if gateway != "" {
|
||||||
|
|
|
@ -280,6 +280,34 @@ func TestAddDelNetworkIp(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddRouteSourceSelection(t *testing.T) {
|
||||||
|
tstIp := "127.1.1.1"
|
||||||
|
tl := testLink{name: "tstEth", linkType: "dummy"}
|
||||||
|
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
|
||||||
|
ip := net.ParseIP(tstIp)
|
||||||
|
mask := net.IPv4Mask(255, 255, 255, 255)
|
||||||
|
ipNet := &net.IPNet{IP: ip, Mask: mask}
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(tl.name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Lost created link %#v", tl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
||||||
|
t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
upLink(t, tl.name)
|
||||||
|
defer downLink(t, tl.name)
|
||||||
|
|
||||||
|
if err := AddRoute("127.0.0.0/8", tstIp, "", tl.name); err != nil {
|
||||||
|
t.Fatalf("Failed to add route with source address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateVethPair(t *testing.T) {
|
func TestCreateVethPair(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
return
|
return
|
||||||
|
|
209
vendor/src/github.com/docker/libcontainer/sample_configs/route_source_address_selection.json
vendored
Normal file
209
vendor/src/github.com/docker/libcontainer/sample_configs/route_source_address_selection.json
vendored
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
{
|
||||||
|
"capabilities": [
|
||||||
|
"CHOWN",
|
||||||
|
"DAC_OVERRIDE",
|
||||||
|
"FOWNER",
|
||||||
|
"MKNOD",
|
||||||
|
"NET_RAW",
|
||||||
|
"SETGID",
|
||||||
|
"SETUID",
|
||||||
|
"SETFCAP",
|
||||||
|
"SETPCAP",
|
||||||
|
"NET_BIND_SERVICE",
|
||||||
|
"SYS_CHROOT",
|
||||||
|
"KILL"
|
||||||
|
],
|
||||||
|
"cgroups": {
|
||||||
|
"allowed_devices": [
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "m",
|
||||||
|
"major_number": -1,
|
||||||
|
"minor_number": -1,
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "m",
|
||||||
|
"major_number": -1,
|
||||||
|
"minor_number": -1,
|
||||||
|
"type": 98
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"major_number": 5,
|
||||||
|
"minor_number": 1,
|
||||||
|
"path": "/dev/console",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"major_number": 4,
|
||||||
|
"path": "/dev/tty0",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"major_number": 4,
|
||||||
|
"minor_number": 1,
|
||||||
|
"path": "/dev/tty1",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"major_number": 136,
|
||||||
|
"minor_number": -1,
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"major_number": 5,
|
||||||
|
"minor_number": 2,
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"major_number": 10,
|
||||||
|
"minor_number": 200,
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 3,
|
||||||
|
"path": "/dev/null",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 5,
|
||||||
|
"path": "/dev/zero",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 7,
|
||||||
|
"path": "/dev/full",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 5,
|
||||||
|
"path": "/dev/tty",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 9,
|
||||||
|
"path": "/dev/urandom",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 8,
|
||||||
|
"path": "/dev/random",
|
||||||
|
"type": 99
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "docker-koye",
|
||||||
|
"parent": "docker"
|
||||||
|
},
|
||||||
|
"restrict_sys": true,
|
||||||
|
"mount_config": {
|
||||||
|
"device_nodes": [
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 3,
|
||||||
|
"path": "/dev/null",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 5,
|
||||||
|
"path": "/dev/zero",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 7,
|
||||||
|
"path": "/dev/full",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 5,
|
||||||
|
"path": "/dev/tty",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 9,
|
||||||
|
"path": "/dev/urandom",
|
||||||
|
"type": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cgroup_permissions": "rwm",
|
||||||
|
"file_mode": 438,
|
||||||
|
"major_number": 1,
|
||||||
|
"minor_number": 8,
|
||||||
|
"path": "/dev/random",
|
||||||
|
"type": 99
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"environment": [
|
||||||
|
"HOME=/",
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"HOSTNAME=koye",
|
||||||
|
"TERM=xterm"
|
||||||
|
],
|
||||||
|
"hostname": "koye",
|
||||||
|
"namespaces": {
|
||||||
|
"NEWIPC": true,
|
||||||
|
"NEWNET": true,
|
||||||
|
"NEWNS": true,
|
||||||
|
"NEWPID": true,
|
||||||
|
"NEWUTS": true
|
||||||
|
},
|
||||||
|
"networks": [
|
||||||
|
{
|
||||||
|
"address": "127.0.0.1/0",
|
||||||
|
"gateway": "localhost",
|
||||||
|
"mtu": 1500,
|
||||||
|
"type": "loopback"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "172.17.0.101/16",
|
||||||
|
"bridge": "docker0",
|
||||||
|
"veth_prefix": "veth",
|
||||||
|
"mtu": 1500,
|
||||||
|
"type": "veth"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"destination": "0.0.0.0/0",
|
||||||
|
"source": "172.17.0.101",
|
||||||
|
"gateway": "172.17.42.1",
|
||||||
|
"interface_name": "eth0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tty": true
|
||||||
|
}
|
108
vendor/src/github.com/docker/libcontainer/user/lookup.go
vendored
Normal file
108
vendor/src/github.com/docker/libcontainer/user/lookup.go
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// The current operating system does not provide the required data for user lookups.
|
||||||
|
ErrUnsupported = errors.New("user lookup: operating system does not provide passwd-formatted data")
|
||||||
|
)
|
||||||
|
|
||||||
|
func lookupUser(filter func(u User) bool) (User, error) {
|
||||||
|
// Get operating system-specific passwd reader-closer.
|
||||||
|
passwd, err := GetPasswd()
|
||||||
|
if err != nil {
|
||||||
|
return User{}, err
|
||||||
|
}
|
||||||
|
defer passwd.Close()
|
||||||
|
|
||||||
|
// Get the users.
|
||||||
|
users, err := ParsePasswdFilter(passwd, filter)
|
||||||
|
if err != nil {
|
||||||
|
return User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// No user entries found.
|
||||||
|
if len(users) == 0 {
|
||||||
|
return User{}, fmt.Errorf("no matching entries in passwd file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume the first entry is the "correct" one.
|
||||||
|
return users[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentUser looks up the current user by their user id in /etc/passwd. If the
|
||||||
|
// user cannot be found (or there is no /etc/passwd file on the filesystem),
|
||||||
|
// then CurrentUser returns an error.
|
||||||
|
func CurrentUser() (User, error) {
|
||||||
|
return LookupUid(syscall.Getuid())
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupUser looks up a user by their username in /etc/passwd. If the user
|
||||||
|
// cannot be found (or there is no /etc/passwd file on the filesystem), then
|
||||||
|
// LookupUser returns an error.
|
||||||
|
func LookupUser(username string) (User, error) {
|
||||||
|
return lookupUser(func(u User) bool {
|
||||||
|
return u.Name == username
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot
|
||||||
|
// be found (or there is no /etc/passwd file on the filesystem), then LookupId
|
||||||
|
// returns an error.
|
||||||
|
func LookupUid(uid int) (User, error) {
|
||||||
|
return lookupUser(func(u User) bool {
|
||||||
|
return u.Uid == uid
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupGroup(filter func(g Group) bool) (Group, error) {
|
||||||
|
// Get operating system-specific group reader-closer.
|
||||||
|
group, err := GetGroup()
|
||||||
|
if err != nil {
|
||||||
|
return Group{}, err
|
||||||
|
}
|
||||||
|
defer group.Close()
|
||||||
|
|
||||||
|
// Get the users.
|
||||||
|
groups, err := ParseGroupFilter(group, filter)
|
||||||
|
if err != nil {
|
||||||
|
return Group{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// No user entries found.
|
||||||
|
if len(groups) == 0 {
|
||||||
|
return Group{}, fmt.Errorf("no matching entries in group file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume the first entry is the "correct" one.
|
||||||
|
return groups[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentGroup looks up the current user's group by their primary group id's
|
||||||
|
// entry in /etc/passwd. If the group cannot be found (or there is no
|
||||||
|
// /etc/group file on the filesystem), then CurrentGroup returns an error.
|
||||||
|
func CurrentGroup() (Group, error) {
|
||||||
|
return LookupGid(syscall.Getgid())
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupGroup looks up a group by its name in /etc/group. If the group cannot
|
||||||
|
// be found (or there is no /etc/group file on the filesystem), then LookupGroup
|
||||||
|
// returns an error.
|
||||||
|
func LookupGroup(groupname string) (Group, error) {
|
||||||
|
return lookupGroup(func(g Group) bool {
|
||||||
|
return g.Name == groupname
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupGid looks up a group by its group id in /etc/group. If the group cannot
|
||||||
|
// be found (or there is no /etc/group file on the filesystem), then LookupGid
|
||||||
|
// returns an error.
|
||||||
|
func LookupGid(gid int) (Group, error) {
|
||||||
|
return lookupGroup(func(g Group) bool {
|
||||||
|
return g.Gid == gid
|
||||||
|
})
|
||||||
|
}
|
30
vendor/src/github.com/docker/libcontainer/user/lookup_unix.go
vendored
Normal file
30
vendor/src/github.com/docker/libcontainer/user/lookup_unix.go
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unix-specific path to the passwd and group formatted files.
|
||||||
|
const (
|
||||||
|
unixPasswdFile = "/etc/passwd"
|
||||||
|
unixGroupFile = "/etc/group"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPasswdFile() (string, error) {
|
||||||
|
return unixPasswdFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPasswd() (io.ReadCloser, error) {
|
||||||
|
return os.Open(unixPasswdFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupFile() (string, error) {
|
||||||
|
return unixGroupFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroup() (io.ReadCloser, error) {
|
||||||
|
return os.Open(unixGroupFile)
|
||||||
|
}
|
21
vendor/src/github.com/docker/libcontainer/user/lookup_unsupported.go
vendored
Normal file
21
vendor/src/github.com/docker/libcontainer/user/lookup_unsupported.go
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
||||||
|
|
||||||
|
package user
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func GetPasswdFile() (string, error) {
|
||||||
|
return "", ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPasswd() (io.ReadCloser, error) {
|
||||||
|
return nil, ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupFile() (string, error) {
|
||||||
|
return "", ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroup() (io.ReadCloser, error) {
|
||||||
|
return nil, ErrUnsupported
|
||||||
|
}
|
|
@ -69,23 +69,36 @@ func parseLine(line string, v ...interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParsePasswd() ([]*User, error) {
|
func ParsePasswdFile(path string) ([]User, error) {
|
||||||
return ParsePasswdFilter(nil)
|
passwd, err := os.Open(path)
|
||||||
}
|
|
||||||
|
|
||||||
func ParsePasswdFilter(filter func(*User) bool) ([]*User, error) {
|
|
||||||
f, err := os.Open("/etc/passwd")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer passwd.Close()
|
||||||
return parsePasswdFile(f, filter)
|
return ParsePasswd(passwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePasswdFile(r io.Reader, filter func(*User) bool) ([]*User, error) {
|
func ParsePasswd(passwd io.Reader) ([]User, error) {
|
||||||
|
return ParsePasswdFilter(passwd, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error) {
|
||||||
|
passwd, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer passwd.Close()
|
||||||
|
return ParsePasswdFilter(passwd, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
|
||||||
|
if r == nil {
|
||||||
|
return nil, fmt.Errorf("nil source for passwd-formatted data")
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
s = bufio.NewScanner(r)
|
s = bufio.NewScanner(r)
|
||||||
out = []*User{}
|
out = []User{}
|
||||||
)
|
)
|
||||||
|
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
|
@ -103,7 +116,7 @@ func parsePasswdFile(r io.Reader, filter func(*User) bool) ([]*User, error) {
|
||||||
// Name:Pass:Uid:Gid:Gecos:Home:Shell
|
// Name:Pass:Uid:Gid:Gecos:Home:Shell
|
||||||
// root:x:0:0:root:/root:/bin/bash
|
// root:x:0:0:root:/root:/bin/bash
|
||||||
// adm:x:3:4:adm:/var/adm:/bin/false
|
// adm:x:3:4:adm:/var/adm:/bin/false
|
||||||
p := &User{}
|
p := User{}
|
||||||
parseLine(
|
parseLine(
|
||||||
text,
|
text,
|
||||||
&p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell,
|
&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) {
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseGroup() ([]*Group, error) {
|
func ParseGroupFile(path string) ([]Group, error) {
|
||||||
return ParseGroupFilter(nil)
|
group, err := os.Open(path)
|
||||||
}
|
|
||||||
|
|
||||||
func ParseGroupFilter(filter func(*Group) bool) ([]*Group, error) {
|
|
||||||
f, err := os.Open("/etc/group")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer group.Close()
|
||||||
return parseGroupFile(f, filter)
|
return ParseGroup(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
|
func ParseGroup(group io.Reader) ([]Group, error) {
|
||||||
|
return ParseGroupFilter(group, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) {
|
||||||
|
group, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer group.Close()
|
||||||
|
return ParseGroupFilter(group, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
|
||||||
|
if r == nil {
|
||||||
|
return nil, fmt.Errorf("nil source for group-formatted data")
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
s = bufio.NewScanner(r)
|
s = bufio.NewScanner(r)
|
||||||
out = []*Group{}
|
out = []Group{}
|
||||||
)
|
)
|
||||||
|
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
|
@ -151,7 +177,7 @@ func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
|
||||||
// Name:Pass:Gid:List
|
// Name:Pass:Gid:List
|
||||||
// root:x:0:root
|
// root:x:0:root
|
||||||
// adm:x:4:root,adm,daemon
|
// adm:x:4:root,adm,daemon
|
||||||
p := &Group{}
|
p := Group{}
|
||||||
parseLine(
|
parseLine(
|
||||||
text,
|
text,
|
||||||
&p.Name, &p.Pass, &p.Gid, &p.List,
|
&p.Name, &p.Pass, &p.Gid, &p.List,
|
||||||
|
@ -165,94 +191,160 @@ func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
type ExecUser struct {
|
||||||
func GetUserGroupSupplementaryHome(userSpec string, defaultUid, defaultGid int, defaultHome string) (int, int, []int, string, error) {
|
Uid, Gid int
|
||||||
var (
|
Sgids []int
|
||||||
uid = defaultUid
|
Home string
|
||||||
gid = defaultGid
|
}
|
||||||
suppGids = []int{}
|
|
||||||
home = defaultHome
|
|
||||||
|
|
||||||
|
// GetExecUserFile is a wrapper for GetExecUser. It reads data from each of the
|
||||||
|
// given file paths and uses that data as the arguments to GetExecUser. If the
|
||||||
|
// files cannot be opened for any reason, the error is ignored and a nil
|
||||||
|
// io.Reader is passed instead.
|
||||||
|
func GetExecUserFile(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
||||||
|
passwd, err := os.Open(passwdPath)
|
||||||
|
if err != nil {
|
||||||
|
passwd = nil
|
||||||
|
} else {
|
||||||
|
defer passwd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
group, err := os.Open(groupPath)
|
||||||
|
if err != nil {
|
||||||
|
group = nil
|
||||||
|
} else {
|
||||||
|
defer group.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetExecUser(userSpec, defaults, passwd, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExecUser parses a user specification string (using the passwd and group
|
||||||
|
// readers as sources for /etc/passwd and /etc/group data, respectively). In
|
||||||
|
// the case of blank fields or missing data from the sources, the values in
|
||||||
|
// defaults is used.
|
||||||
|
//
|
||||||
|
// GetExecUser will return an error if a user or group literal could not be
|
||||||
|
// found in any entry in passwd and group respectively.
|
||||||
|
//
|
||||||
|
// Examples of valid user specifications are:
|
||||||
|
// * ""
|
||||||
|
// * "user"
|
||||||
|
// * "uid"
|
||||||
|
// * "user:group"
|
||||||
|
// * "uid:gid
|
||||||
|
// * "user:gid"
|
||||||
|
// * "uid:group"
|
||||||
|
func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (*ExecUser, error) {
|
||||||
|
var (
|
||||||
userArg, groupArg string
|
userArg, groupArg string
|
||||||
|
name string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if defaults == nil {
|
||||||
|
defaults = new(ExecUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy over defaults.
|
||||||
|
user := &ExecUser{
|
||||||
|
Uid: defaults.Uid,
|
||||||
|
Gid: defaults.Gid,
|
||||||
|
Sgids: defaults.Sgids,
|
||||||
|
Home: defaults.Home,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sgids slice *cannot* be nil.
|
||||||
|
if user.Sgids == nil {
|
||||||
|
user.Sgids = []int{}
|
||||||
|
}
|
||||||
|
|
||||||
// allow for userArg to have either "user" syntax, or optionally "user:group" syntax
|
// allow for userArg to have either "user" syntax, or optionally "user:group" syntax
|
||||||
parseLine(userSpec, &userArg, &groupArg)
|
parseLine(userSpec, &userArg, &groupArg)
|
||||||
|
|
||||||
users, err := ParsePasswdFilter(func(u *User) bool {
|
users, err := ParsePasswdFilter(passwd, func(u User) bool {
|
||||||
if userArg == "" {
|
if userArg == "" {
|
||||||
return u.Uid == uid
|
return u.Uid == user.Uid
|
||||||
}
|
}
|
||||||
return u.Name == userArg || strconv.Itoa(u.Uid) == userArg
|
return u.Name == userArg || strconv.Itoa(u.Uid) == userArg
|
||||||
})
|
})
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && passwd != nil {
|
||||||
if userArg == "" {
|
if userArg == "" {
|
||||||
userArg = strconv.Itoa(uid)
|
userArg = strconv.Itoa(user.Uid)
|
||||||
}
|
}
|
||||||
return 0, 0, nil, "", fmt.Errorf("Unable to find user %v: %v", userArg, err)
|
return nil, fmt.Errorf("Unable to find user %v: %v", userArg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
haveUser := users != nil && len(users) > 0
|
haveUser := users != nil && len(users) > 0
|
||||||
if haveUser {
|
if haveUser {
|
||||||
// if we found any user entries that matched our filter, let's take the first one as "correct"
|
// if we found any user entries that matched our filter, let's take the first one as "correct"
|
||||||
uid = users[0].Uid
|
name = users[0].Name
|
||||||
gid = users[0].Gid
|
user.Uid = users[0].Uid
|
||||||
home = users[0].Home
|
user.Gid = users[0].Gid
|
||||||
|
user.Home = users[0].Home
|
||||||
} else if userArg != "" {
|
} else if userArg != "" {
|
||||||
// we asked for a user but didn't find them... let's check to see if we wanted a numeric user
|
// we asked for a user but didn't find them... let's check to see if we wanted a numeric user
|
||||||
uid, err = strconv.Atoi(userArg)
|
user.Uid, err = strconv.Atoi(userArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// not numeric - we have to bail
|
// not numeric - we have to bail
|
||||||
return 0, 0, nil, "", fmt.Errorf("Unable to find user %v", userArg)
|
return nil, fmt.Errorf("Unable to find user %v", userArg)
|
||||||
}
|
}
|
||||||
if uid < minId || uid > maxId {
|
|
||||||
return 0, 0, nil, "", ErrRange
|
// Must be inside valid uid range.
|
||||||
|
if user.Uid < minId || user.Uid > maxId {
|
||||||
|
return nil, ErrRange
|
||||||
}
|
}
|
||||||
|
|
||||||
// if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit
|
// if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit
|
||||||
}
|
}
|
||||||
|
|
||||||
if groupArg != "" || (haveUser && users[0].Name != "") {
|
if groupArg != "" || name != "" {
|
||||||
groups, err := ParseGroupFilter(func(g *Group) bool {
|
groups, err := ParseGroupFilter(group, func(g Group) bool {
|
||||||
|
// Explicit group format takes precedence.
|
||||||
if groupArg != "" {
|
if groupArg != "" {
|
||||||
return g.Name == groupArg || strconv.Itoa(g.Gid) == groupArg
|
return g.Name == groupArg || strconv.Itoa(g.Gid) == groupArg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if user is a member.
|
||||||
for _, u := range g.List {
|
for _, u := range g.List {
|
||||||
if u == users[0].Name {
|
if u == name {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && group != nil {
|
||||||
return 0, 0, nil, "", fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
|
return nil, fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
haveGroup := groups != nil && len(groups) > 0
|
haveGroup := groups != nil && len(groups) > 0
|
||||||
if groupArg != "" {
|
if groupArg != "" {
|
||||||
if haveGroup {
|
if haveGroup {
|
||||||
// if we found any group entries that matched our filter, let's take the first one as "correct"
|
// if we found any group entries that matched our filter, let's take the first one as "correct"
|
||||||
gid = groups[0].Gid
|
user.Gid = groups[0].Gid
|
||||||
} else {
|
} else {
|
||||||
// we asked for a group but didn't find id... let's check to see if we wanted a numeric group
|
// we asked for a group but didn't find id... let's check to see if we wanted a numeric group
|
||||||
gid, err = strconv.Atoi(groupArg)
|
user.Gid, err = strconv.Atoi(groupArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// not numeric - we have to bail
|
// not numeric - we have to bail
|
||||||
return 0, 0, nil, "", fmt.Errorf("Unable to find group %v", groupArg)
|
return nil, fmt.Errorf("Unable to find group %v", groupArg)
|
||||||
}
|
}
|
||||||
if gid < minId || gid > maxId {
|
|
||||||
return 0, 0, nil, "", ErrRange
|
// Ensure gid is inside gid range.
|
||||||
|
if user.Gid < minId || user.Gid > maxId {
|
||||||
|
return nil, ErrRange
|
||||||
}
|
}
|
||||||
|
|
||||||
// if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit
|
// if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit
|
||||||
}
|
}
|
||||||
} else if haveGroup {
|
} else if haveGroup {
|
||||||
suppGids = make([]int, len(groups))
|
// If implicit group format, fill supplementary gids.
|
||||||
|
user.Sgids = make([]int, len(groups))
|
||||||
for i, group := range groups {
|
for i, group := range groups {
|
||||||
suppGids[i] = group.Gid
|
user.Sgids[i] = group.Gid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return uid, gid, suppGids, home, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -54,7 +56,7 @@ func TestUserParseLine(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUserParsePasswd(t *testing.T) {
|
func TestUserParsePasswd(t *testing.T) {
|
||||||
users, err := parsePasswdFile(strings.NewReader(`
|
users, err := ParsePasswdFilter(strings.NewReader(`
|
||||||
root:x:0:0:root:/root:/bin/bash
|
root:x:0:0:root:/root:/bin/bash
|
||||||
adm:x:3:4:adm:/var/adm:/bin/false
|
adm:x:3:4:adm:/var/adm:/bin/false
|
||||||
this is just some garbage data
|
this is just some garbage data
|
||||||
|
@ -74,7 +76,7 @@ this is just some garbage data
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUserParseGroup(t *testing.T) {
|
func TestUserParseGroup(t *testing.T) {
|
||||||
groups, err := parseGroupFile(strings.NewReader(`
|
groups, err := ParseGroupFilter(strings.NewReader(`
|
||||||
root:x:0:root
|
root:x:0:root
|
||||||
adm:x:4:root,adm,daemon
|
adm:x:4:root,adm,daemon
|
||||||
this is just some garbage data
|
this is just some garbage data
|
||||||
|
@ -92,3 +94,259 @@ this is just some garbage data
|
||||||
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))
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidGetExecUser(t *testing.T) {
|
||||||
|
const passwdContent = `
|
||||||
|
root:x:0:0:root user:/root:/bin/bash
|
||||||
|
adm:x:42:43:adm:/var/adm:/bin/false
|
||||||
|
this is just some garbage data
|
||||||
|
`
|
||||||
|
const groupContent = `
|
||||||
|
root:x:0:root
|
||||||
|
adm:x:43:
|
||||||
|
grp:x:1234:root,adm
|
||||||
|
this is just some garbage data
|
||||||
|
`
|
||||||
|
defaultExecUser := ExecUser{
|
||||||
|
Uid: 8888,
|
||||||
|
Gid: 8888,
|
||||||
|
Sgids: []int{8888},
|
||||||
|
Home: "/8888",
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ref string
|
||||||
|
expected ExecUser
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ref: "root",
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 0,
|
||||||
|
Gid: 0,
|
||||||
|
Sgids: []int{0, 1234},
|
||||||
|
Home: "/root",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "adm",
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 42,
|
||||||
|
Gid: 43,
|
||||||
|
Sgids: []int{1234},
|
||||||
|
Home: "/var/adm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "root:adm",
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 0,
|
||||||
|
Gid: 43,
|
||||||
|
Sgids: defaultExecUser.Sgids,
|
||||||
|
Home: "/root",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "adm:1234",
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 42,
|
||||||
|
Gid: 1234,
|
||||||
|
Sgids: defaultExecUser.Sgids,
|
||||||
|
Home: "/var/adm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "42:1234",
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 42,
|
||||||
|
Gid: 1234,
|
||||||
|
Sgids: defaultExecUser.Sgids,
|
||||||
|
Home: "/var/adm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "1337:1234",
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 1337,
|
||||||
|
Gid: 1234,
|
||||||
|
Sgids: defaultExecUser.Sgids,
|
||||||
|
Home: defaultExecUser.Home,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "1337",
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 1337,
|
||||||
|
Gid: defaultExecUser.Gid,
|
||||||
|
Sgids: defaultExecUser.Sgids,
|
||||||
|
Home: defaultExecUser.Home,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "",
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: defaultExecUser.Uid,
|
||||||
|
Gid: defaultExecUser.Gid,
|
||||||
|
Sgids: defaultExecUser.Sgids,
|
||||||
|
Home: defaultExecUser.Home,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
passwd := strings.NewReader(passwdContent)
|
||||||
|
group := strings.NewReader(groupContent)
|
||||||
|
|
||||||
|
execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error())
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(test.expected, *execUser) {
|
||||||
|
t.Logf("got: %#v", execUser)
|
||||||
|
t.Logf("expected: %#v", test.expected)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidGetExecUser(t *testing.T) {
|
||||||
|
const passwdContent = `
|
||||||
|
root:x:0:0:root user:/root:/bin/bash
|
||||||
|
adm:x:42:43:adm:/var/adm:/bin/false
|
||||||
|
this is just some garbage data
|
||||||
|
`
|
||||||
|
const groupContent = `
|
||||||
|
root:x:0:root
|
||||||
|
adm:x:43:
|
||||||
|
grp:x:1234:root,adm
|
||||||
|
this is just some garbage data
|
||||||
|
`
|
||||||
|
|
||||||
|
tests := []string{
|
||||||
|
// No such user/group.
|
||||||
|
"notuser",
|
||||||
|
"notuser:notgroup",
|
||||||
|
"root:notgroup",
|
||||||
|
"notuser:adm",
|
||||||
|
"8888:notgroup",
|
||||||
|
"notuser:8888",
|
||||||
|
|
||||||
|
// Invalid user/group values.
|
||||||
|
"-1:0",
|
||||||
|
"0:-3",
|
||||||
|
"-5:-2",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
passwd := strings.NewReader(passwdContent)
|
||||||
|
group := strings.NewReader(groupContent)
|
||||||
|
|
||||||
|
execUser, err := GetExecUser(test, nil, passwd, group)
|
||||||
|
if err == nil {
|
||||||
|
t.Logf("got unexpected success when parsing '%s': %#v", test, execUser)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetExecUserNilSources(t *testing.T) {
|
||||||
|
const passwdContent = `
|
||||||
|
root:x:0:0:root user:/root:/bin/bash
|
||||||
|
adm:x:42:43:adm:/var/adm:/bin/false
|
||||||
|
this is just some garbage data
|
||||||
|
`
|
||||||
|
const groupContent = `
|
||||||
|
root:x:0:root
|
||||||
|
adm:x:43:
|
||||||
|
grp:x:1234:root,adm
|
||||||
|
this is just some garbage data
|
||||||
|
`
|
||||||
|
|
||||||
|
defaultExecUser := ExecUser{
|
||||||
|
Uid: 8888,
|
||||||
|
Gid: 8888,
|
||||||
|
Sgids: []int{8888},
|
||||||
|
Home: "/8888",
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ref string
|
||||||
|
passwd, group bool
|
||||||
|
expected ExecUser
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ref: "",
|
||||||
|
passwd: false,
|
||||||
|
group: false,
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 8888,
|
||||||
|
Gid: 8888,
|
||||||
|
Sgids: []int{8888},
|
||||||
|
Home: "/8888",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "root",
|
||||||
|
passwd: true,
|
||||||
|
group: false,
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 0,
|
||||||
|
Gid: 0,
|
||||||
|
Sgids: []int{8888},
|
||||||
|
Home: "/root",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "0",
|
||||||
|
passwd: false,
|
||||||
|
group: false,
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 0,
|
||||||
|
Gid: 8888,
|
||||||
|
Sgids: []int{8888},
|
||||||
|
Home: "/8888",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ref: "0:0",
|
||||||
|
passwd: false,
|
||||||
|
group: false,
|
||||||
|
expected: ExecUser{
|
||||||
|
Uid: 0,
|
||||||
|
Gid: 0,
|
||||||
|
Sgids: []int{8888},
|
||||||
|
Home: "/8888",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
var passwd, group io.Reader
|
||||||
|
|
||||||
|
if test.passwd {
|
||||||
|
passwd = strings.NewReader(passwdContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.group {
|
||||||
|
group = strings.NewReader(groupContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error())
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(test.expected, *execUser) {
|
||||||
|
t.Logf("got: %#v", execUser)
|
||||||
|
t.Logf("expected: %#v", test.expected)
|
||||||
|
t.Fail()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue