mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-08-24 09:17:53 +00:00
Callers that only care about the IDs should try to convert the
identifier to an integer before calling the Get* functions, so they
can save the cost of hitting the filesystem and maybe or maybe not
finding the other fields (User.Name, etc.). But callers that *want*
the other fields but only actually need the ID can, with this commit,
just call the Get* function and ignore ErrNo*Entries responses:
user, err := lookup.GetUser(mount, userIDorName)
if err != nil && err != ErrNoPasswdEntries {
return err
}
Previously, they'd have to perform their own integer-conversion
attempt in Get* error handling, with logic like:
user, err := lookup.GetUser(mount, userIDorName)
if err == ErrNoPasswdEntries {
uuid, err := strconv.ParseUint(userIDorName, 10, 32)
if err == nil {
user.Uid = int(uuid)
}
} else if err != nil {
return err
}
Signed-off-by: W. Trevor King <wking@tremily.us>
168 lines
4.8 KiB
Go
168 lines
4.8 KiB
Go
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
|
|
}
|