github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/lookup/lookup.go (about)

     1  package lookup
     2  
     3  import (
     4  	"os"
     5  	"strconv"
     6  
     7  	securejoin "github.com/cyphar/filepath-securejoin"
     8  	"github.com/opencontainers/runc/libcontainer/user"
     9  	"github.com/sirupsen/logrus"
    10  )
    11  
    12  const (
    13  	etcpasswd = "/etc/passwd"
    14  	etcgroup  = "/etc/group"
    15  )
    16  
    17  // Overrides allows you to override defaults in GetUserGroupInfo
    18  type Overrides struct {
    19  	DefaultUser            *user.ExecUser
    20  	ContainerEtcPasswdPath string
    21  	ContainerEtcGroupPath  string
    22  }
    23  
    24  // GetUserGroupInfo takes string forms of the the container's mount path and the container user and
    25  // returns a ExecUser with uid, gid, sgids, and home.  And override can be provided for defaults.
    26  func GetUserGroupInfo(containerMount, containerUser string, override *Overrides) (*user.ExecUser, error) {
    27  	var (
    28  		passwdDest, groupDest string
    29  		defaultExecUser       *user.ExecUser
    30  		err                   error
    31  	)
    32  
    33  	if override != nil {
    34  		// Check for an override /etc/passwd path
    35  		if override.ContainerEtcPasswdPath != "" {
    36  			passwdDest = override.ContainerEtcPasswdPath
    37  		}
    38  		// Check for an override for /etc/group path
    39  		if override.ContainerEtcGroupPath != "" {
    40  			groupDest = override.ContainerEtcGroupPath
    41  		}
    42  	}
    43  
    44  	if passwdDest == "" {
    45  		// Make sure the /etc/passwd destination is not a symlink to something naughty
    46  		if passwdDest, err = securejoin.SecureJoin(containerMount, etcpasswd); err != nil {
    47  			logrus.Debug(err)
    48  			return nil, err
    49  		}
    50  	}
    51  	if groupDest == "" {
    52  		// Make sure the /etc/group destination is not a symlink to something naughty
    53  		if groupDest, err = securejoin.SecureJoin(containerMount, etcgroup); err != nil {
    54  			logrus.Debug(err)
    55  			return nil, err
    56  		}
    57  	}
    58  
    59  	// Check for an override default user
    60  	if override != nil && override.DefaultUser != nil {
    61  		defaultExecUser = override.DefaultUser
    62  	} else {
    63  		// Define a default container user
    64  		//defaultExecUser = &user.ExecUser{
    65  		//	Uid:  0,
    66  		//	Gid:  0,
    67  		//	Home: "/",
    68  		defaultExecUser = nil
    69  
    70  	}
    71  
    72  	return user.GetExecUserPath(containerUser, defaultExecUser, passwdDest, groupDest)
    73  }
    74  
    75  // GetContainerGroups uses securejoin to get a list of numerical groupids from a container. Per the runc
    76  // function it calls: If a group name cannot be found, an error will be returned. If a group id cannot be found,
    77  // or the given group data is nil, the id will be returned as-is  provided it is in the legal range.
    78  func GetContainerGroups(groups []string, containerMount string, override *Overrides) ([]uint32, error) {
    79  	var (
    80  		groupDest string
    81  		err       error
    82  	)
    83  
    84  	groupPath := etcgroup
    85  	if override != nil && override.ContainerEtcGroupPath != "" {
    86  		groupPath = override.ContainerEtcGroupPath
    87  	}
    88  
    89  	if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil {
    90  		logrus.Debug(err)
    91  		return nil, err
    92  	}
    93  
    94  	gids, err := user.GetAdditionalGroupsPath(groups, groupDest)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	uintgids := make([]uint32, 0, len(gids))
    99  	// For libpod, we want []uint32s
   100  	for _, gid := range gids {
   101  		uintgids = append(uintgids, uint32(gid))
   102  	}
   103  	return uintgids, nil
   104  }
   105  
   106  // GetUser takes a containermount path and user name or ID and returns
   107  // a matching User structure from /etc/passwd.  If it cannot locate a user
   108  // with the provided information, an ErrNoPasswdEntries is returned.
   109  // When the provided user name was an ID, a User structure with Uid
   110  // set is returned along with ErrNoPasswdEntries.
   111  func GetUser(containerMount, userIDorName string) (*user.User, error) {
   112  	var inputIsName bool
   113  	uid, err := strconv.Atoi(userIDorName)
   114  	if err != nil {
   115  		inputIsName = true
   116  	}
   117  	passwdDest, err := securejoin.SecureJoin(containerMount, etcpasswd)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	users, err := user.ParsePasswdFileFilter(passwdDest, func(u user.User) bool {
   122  		if inputIsName {
   123  			return u.Name == userIDorName
   124  		}
   125  		return u.Uid == uid
   126  	})
   127  	if err != nil && !os.IsNotExist(err) {
   128  		return nil, err
   129  	}
   130  	if len(users) > 0 {
   131  		return &users[0], nil
   132  	}
   133  	if !inputIsName {
   134  		return &user.User{Uid: uid}, user.ErrNoPasswdEntries
   135  	}
   136  	return nil, user.ErrNoPasswdEntries
   137  }
   138  
   139  // GetGroup takes a containermount path and a group name or ID and returns
   140  // a match Group struct from /etc/group.  If it cannot locate a group,
   141  // an ErrNoGroupEntries error is returned.  When the provided group name
   142  // was an ID, a Group structure with Gid set is returned along with
   143  // ErrNoGroupEntries.
   144  func GetGroup(containerMount, groupIDorName string) (*user.Group, error) {
   145  	var inputIsName bool
   146  	gid, err := strconv.Atoi(groupIDorName)
   147  	if err != nil {
   148  		inputIsName = true
   149  	}
   150  
   151  	groupDest, err := securejoin.SecureJoin(containerMount, etcgroup)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	groups, err := user.ParseGroupFileFilter(groupDest, func(g user.Group) bool {
   157  		if inputIsName {
   158  			return g.Name == groupIDorName
   159  		}
   160  		return g.Gid == gid
   161  	})
   162  	if err != nil && !os.IsNotExist(err) {
   163  		return nil, err
   164  	}
   165  	if len(groups) > 0 {
   166  		return &groups[0], nil
   167  	}
   168  	if !inputIsName {
   169  		return &user.Group{Gid: gid}, user.ErrNoGroupEntries
   170  	}
   171  	return nil, user.ErrNoGroupEntries
   172  }