github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/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 return user.GetExecUserPath(containerUser, defaultExecUser, passwdDest, groupDest) 72 } 73 74 // GetContainerGroups uses securejoin to get a list of numerical groupids from a container. Per the runc 75 // function it calls: If a group name cannot be found, an error will be returned. If a group id cannot be found, 76 // or the given group data is nil, the id will be returned as-is provided it is in the legal range. 77 func GetContainerGroups(groups []string, containerMount string, override *Overrides) ([]uint32, error) { 78 var ( 79 groupDest string 80 err error 81 ) 82 83 groupPath := etcgroup 84 if override != nil && override.ContainerEtcGroupPath != "" { 85 groupPath = override.ContainerEtcGroupPath 86 } 87 88 if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil { 89 logrus.Debug(err) 90 return nil, err 91 } 92 93 gids, err := user.GetAdditionalGroupsPath(groups, groupDest) 94 if err != nil { 95 return nil, err 96 } 97 uintgids := make([]uint32, 0, len(gids)) 98 // For libpod, we want []uint32s 99 for _, gid := range gids { 100 uintgids = append(uintgids, uint32(gid)) 101 } 102 return uintgids, nil 103 } 104 105 // GetUser takes a containermount path and user name or ID and returns 106 // a matching User structure from /etc/passwd. If it cannot locate a user 107 // with the provided information, an ErrNoPasswdEntries is returned. 108 // When the provided user name was an ID, a User structure with Uid 109 // set is returned along with ErrNoPasswdEntries. 110 func GetUser(containerMount, userIDorName string) (*user.User, error) { 111 var inputIsName bool 112 uid, err := strconv.Atoi(userIDorName) 113 if err != nil { 114 inputIsName = true 115 } 116 passwdDest, err := securejoin.SecureJoin(containerMount, etcpasswd) 117 if err != nil { 118 return nil, err 119 } 120 users, err := user.ParsePasswdFileFilter(passwdDest, func(u user.User) bool { 121 if inputIsName { 122 return u.Name == userIDorName 123 } 124 return u.Uid == uid 125 }) 126 if err != nil && !os.IsNotExist(err) { 127 return nil, err 128 } 129 if len(users) > 0 { 130 return &users[0], nil 131 } 132 if !inputIsName { 133 return &user.User{Uid: uid}, user.ErrNoPasswdEntries 134 } 135 return nil, user.ErrNoPasswdEntries 136 } 137 138 // GetGroup takes a containermount path and a group name or ID and returns 139 // a match Group struct from /etc/group. If it cannot locate a group, 140 // an ErrNoGroupEntries error is returned. When the provided group name 141 // was an ID, a Group structure with Gid set is returned along with 142 // ErrNoGroupEntries. 143 func GetGroup(containerMount, groupIDorName string) (*user.Group, error) { 144 var inputIsName bool 145 gid, err := strconv.Atoi(groupIDorName) 146 if err != nil { 147 inputIsName = true 148 } 149 150 groupDest, err := securejoin.SecureJoin(containerMount, etcgroup) 151 if err != nil { 152 return nil, err 153 } 154 155 groups, err := user.ParseGroupFileFilter(groupDest, func(g user.Group) bool { 156 if inputIsName { 157 return g.Name == groupIDorName 158 } 159 return g.Gid == gid 160 }) 161 if err != nil && !os.IsNotExist(err) { 162 return nil, err 163 } 164 if len(groups) > 0 { 165 return &groups[0], nil 166 } 167 if !inputIsName { 168 return &user.Group{Gid: gid}, user.ErrNoGroupEntries 169 } 170 return nil, user.ErrNoGroupEntries 171 }