@@ -439,18 +439,47 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
439439 return user , nil
440440}
441441
442+ // groupArg is a parsed group argument for [GetAdditionalGroups].
443+ type groupArg struct {
444+ name string
445+ gid int
446+ isNumeric bool
447+ }
448+
449+ // matches reports whether group g satisfies the argument. Numeric arguments
450+ // are matched by GID only, others by name.
451+ func (ag groupArg ) matches (g Group ) bool {
452+ if ag .isNumeric {
453+ return g .Gid == ag .gid
454+ }
455+ return g .Name == ag .name
456+ }
457+
442458// GetAdditionalGroups looks up a list of groups by name or group id
443459// against the given /etc/group formatted data. If a group name cannot
444460// be found, an error will be returned. If a group id cannot be found,
445461// or the given group data is nil, the id will be returned as-is
446462// provided it is in the legal range.
447463func GetAdditionalGroups (additionalGroups []string , group io.Reader ) ([]int , error ) {
464+ addtlGroups := make ([]groupArg , len (additionalGroups ))
465+ for i , ag := range additionalGroups {
466+ gid , ok , err := parseNumeric (ag )
467+ if err != nil {
468+ return nil , err
469+ }
470+ addtlGroups [i ] = groupArg {
471+ name : ag ,
472+ gid : gid ,
473+ isNumeric : ok ,
474+ }
475+ }
476+
448477 groups := []Group {}
449478 if group != nil {
450479 var err error
451480 groups , err = ParseGroupFilter (group , func (g Group ) bool {
452- for _ , ag := range additionalGroups {
453- if g . Name == ag || strconv . Itoa ( g . Gid ) == ag {
481+ for _ , ag := range addtlGroups {
482+ if ag . matches ( g ) {
454483 return true
455484 }
456485 }
@@ -462,32 +491,28 @@ func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, err
462491 }
463492
464493 gidMap := make (map [int ]struct {})
465- for _ , ag := range additionalGroups {
494+ for _ , ag := range addtlGroups {
466495 var found bool
467496 for _ , g := range groups {
468- // if we found a matched group either by name or gid, take the
469- // first matched as correct
470- if g .Name == ag || strconv .Itoa (g .Gid ) == ag {
471- if _ , ok := gidMap [g .Gid ]; ! ok {
472- gidMap [g .Gid ] = struct {}{}
473- found = true
474- break
497+ if ag .matches (g ) {
498+ // take the first matched group as correct
499+ if g .Gid < minID || g .Gid > maxID {
500+ return nil , ErrRange
475501 }
502+ gidMap [g .Gid ] = struct {}{}
503+ found = true
504+ break
476505 }
477506 }
478- // we asked for a group but didn't find it. let's check to see
479- // if we wanted a numeric group
507+ // We asked for a group but didn't find it. Numeric group IDs may be
508+ // used as-is even when they are not present in /etc/group; non-numeric
509+ // group names must be found.
480510 if ! found {
481- gid , err := strconv .ParseInt (ag , 10 , 64 )
482- if err != nil {
511+ if ! ag .isNumeric {
483512 // Not a numeric ID either.
484- return nil , fmt .Errorf ("unable to find group %s: %w" , ag , ErrNoGroupEntries )
485- }
486- // Ensure gid is inside gid range.
487- if gid < minID || gid > maxID {
488- return nil , ErrRange
513+ return nil , fmt .Errorf ("unable to find group %s: %w" , ag .name , ErrNoGroupEntries )
489514 }
490- gidMap [int ( gid ) ] = struct {}{}
515+ gidMap [ag . gid ] = struct {}{}
491516 }
492517 }
493518 gids := []int {}
0 commit comments