@@ -235,10 +235,14 @@ func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath
235235// * "uid:gid
236236// * "user:gid"
237237// * "uid:group"
238+ //
239+ // It should be noted that if you specify a numeric user or group id, they will
240+ // not be evaluated as usernames (only the metadata will be filled). So attempting
241+ // to parse a user with user.Name = "1337" will produce the user with a UID of
242+ // 1337.
238243func GetExecUser (userSpec string , defaults * ExecUser , passwd , group io.Reader ) (* ExecUser , error ) {
239244 var (
240245 userArg , groupArg string
241- name string
242246 )
243247
244248 if defaults == nil {
@@ -261,11 +265,22 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
261265 // allow for userArg to have either "user" syntax, or optionally "user:group" syntax
262266 parseLine (userSpec , & userArg , & groupArg )
263267
268+ // Convert userArg and groupArg to be numeric, so we don't have to execute
269+ // Atoi *twice* for each iteration over lines.
270+ uidArg , uidErr := strconv .Atoi (userArg )
271+ gidArg , gidErr := strconv .Atoi (groupArg )
272+
264273 users , err := ParsePasswdFilter (passwd , func (u User ) bool {
265274 if userArg == "" {
266275 return u .Uid == user .Uid
267276 }
268- return u .Name == userArg || strconv .Itoa (u .Uid ) == userArg
277+
278+ if uidErr == nil {
279+ // If the userArg is numeric, always treat it as a UID.
280+ return uidArg == u .Uid
281+ }
282+
283+ return u .Name == userArg
269284 })
270285 if err != nil && passwd != nil {
271286 if userArg == "" {
@@ -274,71 +289,81 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
274289 return nil , fmt .Errorf ("Unable to find user %v: %v" , userArg , err )
275290 }
276291
277- haveUser := users != nil && len ( users ) > 0
278- if haveUser {
279- // if we found any user entries that matched our filter, let 's take the first one as "correct"
280- name = users [0 ].Name
292+ var matchedUserName string
293+ if len ( users ) > 0 {
294+ // First match wins, even if there 's more than one matching entry.
295+ matchedUserName = users [0 ].Name
281296 user .Uid = users [0 ].Uid
282297 user .Gid = users [0 ].Gid
283298 user .Home = users [0 ].Home
284299 } else if userArg != "" {
285- // we asked for a user but didn't find them... let's check to see if we wanted a numeric user
286- user .Uid , err = strconv .Atoi (userArg )
287- if err != nil {
288- // not numeric - we have to bail
289- return nil , fmt .Errorf ("Unable to find user %v" , userArg )
300+ // If we can't find a user with the given username, the only other valid
301+ // option is if it's a numeric username with no associated entry in passwd.
302+
303+ if uidErr != nil {
304+ // Not numeric.
305+ return nil , fmt .Errorf ("unable to find user %s: %v" , userArg , ErrNoPasswdEntries )
290306 }
307+ user .Uid = uidArg
291308
292309 // Must be inside valid uid range.
293310 if user .Uid < minId || user .Uid > maxId {
294311 return nil , ErrRange
295312 }
296313
297- // if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit
314+ // Okay, so it's numeric. We can just roll with this.
298315 }
299316
300- if groupArg != "" || name != "" {
317+ // On to the groups. If we matched a username, we need to do this because of
318+ // the supplementary group IDs.
319+ if groupArg != "" || matchedUserName != "" {
301320 groups , err := ParseGroupFilter (group , func (g Group ) bool {
302- // Explicit group format takes precedence.
303- if groupArg != "" {
304- return g .Name == groupArg || strconv .Itoa (g .Gid ) == groupArg
321+ // If the group argument isn't explicit, we'll just search for it.
322+ if groupArg == "" {
323+ // Check if user is a member of this group.
324+ for _ , u := range g .List {
325+ if u == matchedUserName {
326+ return true
327+ }
328+ }
329+ return false
305330 }
306331
307- // Check if user is a member.
308- for _ , u := range g .List {
309- if u == name {
310- return true
311- }
332+ if gidErr == nil {
333+ // If the groupArg is numeric, always treat it as a GID.
334+ return gidArg == g .Gid
312335 }
313336
314- return false
337+ return g . Name == groupArg
315338 })
316339 if err != nil && group != nil {
317- return nil , fmt .Errorf ("Unable to find groups for user %v: %v" , users [ 0 ]. Name , err )
340+ return nil , fmt .Errorf ("unable to find groups for spec %v: %v" , matchedUserName , err )
318341 }
319342
320343 haveGroup := groups != nil && len (groups ) > 0
321344 if groupArg != "" {
322345 if haveGroup {
323346 // if we found any group entries that matched our filter, let's take the first one as "correct"
324347 user .Gid = groups [0 ].Gid
325- } else {
326- // we asked for a group but didn't find id... let's check to see if we wanted a numeric group
327- user .Gid , err = strconv .Atoi (groupArg )
328- if err != nil {
329- // not numeric - we have to bail
330- return nil , fmt .Errorf ("Unable to find group %v" , groupArg )
348+ } else if groupArg != "" {
349+ // If we can't find a group with the given name, the only other valid
350+ // option is if it's a numeric group name with no associated entry in group.
351+
352+ if gidErr != nil {
353+ // Not numeric.
354+ return nil , fmt .Errorf ("unable to find group %s: %v" , groupArg , ErrNoGroupEntries )
331355 }
356+ user .Gid = gidArg
332357
333358 // Ensure gid is inside gid range.
334359 if user .Gid < minId || user .Gid > maxId {
335360 return nil , ErrRange
336361 }
337362
338- // if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit
363+ // Okay, so it's numeric. We can just roll with this.
339364 }
340- } else if haveGroup {
341- // If implicit group format, fill supplementary gids .
365+ } else if len ( groups ) > 0 {
366+ // Supplementary group ids only make sense if in the implicit form .
342367 user .Sgids = make ([]int , len (groups ))
343368 for i , group := range groups {
344369 user .Sgids [i ] = group .Gid
0 commit comments