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 }