diff options
Diffstat (limited to 'pkg/lookup')
-rw-r--r-- | pkg/lookup/lookup.go | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/pkg/lookup/lookup.go b/pkg/lookup/lookup.go new file mode 100644 index 000000000..70b97144f --- /dev/null +++ b/pkg/lookup/lookup.go @@ -0,0 +1,168 @@ +package lookup + +import ( + "os" + "strconv" + + "github.com/cyphar/filepath-securejoin" + "github.com/opencontainers/runc/libcontainer/user" + "github.com/sirupsen/logrus" +) + +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. +// When the provided user name was an ID, a User structure with Uid +// set is returned along with ErrNoPasswdEntries. +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 && !os.IsNotExist(err) { + return nil, err + } + if len(users) > 0 { + return &users[0], nil + } + if !inputIsName { + return &user.User{Uid: uid}, user.ErrNoPasswdEntries + } + return nil, user.ErrNoPasswdEntries +} + +// GetGroup takes a containermount 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. When the provided group name +// was an ID, a Group structure with Gid set is returned along with +// ErrNoGroupEntries. +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 && !os.IsNotExist(err) { + return nil, err + } + if len(groups) > 0 { + return &groups[0], nil + } + if !inputIsName { + return &user.Group{Gid: gid}, user.ErrNoGroupEntries + } + return nil, user.ErrNoGroupEntries +} |