diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/lookup/lookup.go | 156 | ||||
-rw-r--r-- | pkg/spec/config_linux.go | 2 | ||||
-rw-r--r-- | pkg/spec/parse.go | 14 |
3 files changed, 164 insertions, 8 deletions
diff --git a/pkg/lookup/lookup.go b/pkg/lookup/lookup.go new file mode 100644 index 000000000..b27e2a724 --- /dev/null +++ b/pkg/lookup/lookup.go @@ -0,0 +1,156 @@ +package lookup + +import ( + "github.com/cyphar/filepath-securejoin" + "github.com/opencontainers/runc/libcontainer/user" + "github.com/sirupsen/logrus" + "strconv" +) + +const ( + etcpasswd = "/etc/passwd" + etcgroup = "/etc/group" +) + +// Overrides allows you to override defaults in GetUserGroupInfo +type Overrides struct { + DefaultUser *user.ExecUser + ContainerEtcPasswdPath string + ContainerEtcGroupPath string +} + +// GetUserGroupInfo takes string forms of the the container's mount path and the container user and +// returns a ExecUser with uid, gid, sgids, and home. And override can be provided for defaults. +func GetUserGroupInfo(containerMount, containerUser string, override *Overrides) (*user.ExecUser, error) { + var ( + passwdDest, groupDest string + defaultExecUser *user.ExecUser + err error + ) + passwdPath := etcpasswd + groupPath := etcgroup + + if override != nil { + // Check for an override /etc/passwd path + if override.ContainerEtcPasswdPath != "" { + passwdPath = override.ContainerEtcPasswdPath + } + // Check for an override for /etc/group path + if override.ContainerEtcGroupPath != "" { + groupPath = override.ContainerEtcGroupPath + } + } + + // Check for an override default user + if override != nil && override.DefaultUser != nil { + defaultExecUser = override.DefaultUser + } else { + // Define a default container user + //defaultExecUser = &user.ExecUser{ + // Uid: 0, + // Gid: 0, + // Home: "/", + defaultExecUser = nil + + } + + // Make sure the /etc/group and /etc/passwd destinations are not a symlink to something naughty + if passwdDest, err = securejoin.SecureJoin(containerMount, passwdPath); err != nil { + logrus.Debug(err) + return nil, err + } + if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil { + logrus.Debug(err) + return nil, err + } + return user.GetExecUserPath(containerUser, defaultExecUser, passwdDest, groupDest) +} + +// GetContainerGroups uses securejoin to get a list of numerical groupids from a container. Per the runc +// function it calls: If a group name cannot be found, an error will be returned. If a group id cannot be found, +// or the given group data is nil, the id will be returned as-is provided it is in the legal range. +func GetContainerGroups(groups []string, containerMount string, override *Overrides) ([]uint32, error) { + var ( + groupDest string + err error + uintgids []uint32 + ) + + groupPath := etcgroup + if override != nil && override.ContainerEtcGroupPath != "" { + groupPath = override.ContainerEtcGroupPath + } + + if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil { + logrus.Debug(err) + return nil, err + } + + gids, err := user.GetAdditionalGroupsPath(groups, groupDest) + if err != nil { + return nil, err + } + // For libpod, we want []uint32s + for _, gid := range gids { + uintgids = append(uintgids, uint32(gid)) + } + return uintgids, nil +} + +// GetUser takes a containermount path and user name or id and returns +// a matching User structure from /etc/passwd. If it cannot locate a user +// with the provided information, an ErrNoPasswdEntries is returned. +func GetUser(containerMount, userIDorName string) (*user.User, error) { + var inputIsName bool + uid, err := strconv.Atoi(userIDorName) + if err != nil { + inputIsName = true + } + passwdDest, err := securejoin.SecureJoin(containerMount, etcpasswd) + if err != nil { + return nil, err + } + users, err := user.ParsePasswdFileFilter(passwdDest, func(u user.User) bool { + if inputIsName { + return u.Name == userIDorName + } + return u.Uid == uid + }) + if err != nil { + return nil, err + } + if len(users) > 0 { + return &users[0], nil + } + return nil, user.ErrNoPasswdEntries +} + +// GetGroup takes ac ontainermount path and a group name or id and returns +// a match Group struct from /etc/group. if it cannot locate a group, +// an ErrNoGroupEntries error is returned. +func GetGroup(containerMount, groupIDorName string) (*user.Group, error) { + var inputIsName bool + gid, err := strconv.Atoi(groupIDorName) + if err != nil { + inputIsName = true + } + + groupDest, err := securejoin.SecureJoin(containerMount, etcgroup) + if err != nil { + return nil, err + } + + groups, err := user.ParseGroupFileFilter(groupDest, func(g user.Group) bool { + if inputIsName { + return g.Name == groupIDorName + } + return g.Gid == gid + }) + if err != nil { + return nil, err + } + if len(groups) > 0 { + return &groups[0], nil + } + return nil, user.ErrNoGroupEntries +} diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go index 20cdcc458..5bf8eff43 100644 --- a/pkg/spec/config_linux.go +++ b/pkg/spec/config_linux.go @@ -28,7 +28,7 @@ func Device(d *configs.Device) spec.LinuxDevice { } func addDevice(g *generate.Generator, device string) error { - src, dst, permissions, err := parseDevice(device) + src, dst, permissions, err := ParseDevice(device) if err != nil { return err } diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go index 9b2dd1347..4d20e35d4 100644 --- a/pkg/spec/parse.go +++ b/pkg/spec/parse.go @@ -148,21 +148,21 @@ func getLoggingPath(opts []string) string { return "" } -// parseDevice parses device mapping string to a src, dest & permissions string -func parseDevice(device string) (string, string, string, error) { //nolint +// ParseDevice parses device mapping string to a src, dest & permissions string +func ParseDevice(device string) (string, string, string, error) { //nolint src := "" dst := "" permissions := "rwm" arr := strings.Split(device, ":") switch len(arr) { case 3: - if !validDeviceMode(arr[2]) { + if !IsValidDeviceMode(arr[2]) { return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2]) } permissions = arr[2] fallthrough case 2: - if validDeviceMode(arr[1]) { + if IsValidDeviceMode(arr[1]) { permissions = arr[1] } else { if arr[1][0] != '/' { @@ -183,9 +183,9 @@ func parseDevice(device string) (string, string, string, error) { //nolint return src, dst, permissions, nil } -// validDeviceMode checks if the mode for device is valid or not. -// Valid mode is a composition of r (read), w (write), and m (mknod). -func validDeviceMode(mode string) bool { +// IsValidDeviceMode checks if the mode for device is valid or not. +// IsValid mode is a composition of r (read), w (write), and m (mknod). +func IsValidDeviceMode(mode string) bool { var legalDeviceMode = map[rune]bool{ 'r': true, 'w': true, |