package chrootuser import ( "os/user" "strconv" "strings" "github.com/pkg/errors" ) var ( // ErrNoSuchUser indicates that the user provided by the caller does not // exist in /etc/passws ErrNoSuchUser = errors.New("user does not exist in /etc/passwd") ) // GetUser will return the uid, gid of the user specified in the userspec // it will use the /etc/passwd and /etc/group files inside of the rootdir // to return this information. // userspec format [user | user:group | uid | uid:gid | user:gid | uid:group ] func GetUser(rootdir, userspec string) (uint32, uint32, error) { var gid64 uint64 var gerr error = user.UnknownGroupError("error looking up group") spec := strings.SplitN(userspec, ":", 2) userspec = spec[0] groupspec := "" if userspec == "" { return 0, 0, nil } if len(spec) > 1 { groupspec = spec[1] } uid64, uerr := strconv.ParseUint(userspec, 10, 32) if uerr == nil && groupspec == "" { // We parsed the user name as a number, and there's no group // component, so try to look up the primary GID of the user who // has this UID. var name string name, gid64, gerr = lookupGroupForUIDInContainer(rootdir, uid64) if gerr == nil { userspec = name } else { // Leave userspec alone, but swallow the error and just // use GID 0. gid64 = 0 gerr = nil } } if uerr != nil { // The user ID couldn't be parsed as a number, so try to look // up the user's UID and primary GID. uid64, gid64, uerr = lookupUserInContainer(rootdir, userspec) gerr = uerr } if groupspec != "" { // We have a group name or number, so parse it. gid64, gerr = strconv.ParseUint(groupspec, 10, 32) if gerr != nil { // The group couldn't be parsed as a number, so look up // the group's GID. gid64, gerr = lookupGroupInContainer(rootdir, groupspec) } } if uerr == nil && gerr == nil { return uint32(uid64), uint32(gid64), nil } err := errors.Wrapf(uerr, "error determining run uid") if uerr == nil { err = errors.Wrapf(gerr, "error determining run gid") } return 0, 0, err } // GetGroup returns the gid by looking it up in the /etc/group file // groupspec format [ group | gid ] func GetGroup(rootdir, groupspec string) (uint32, error) { gid64, gerr := strconv.ParseUint(groupspec, 10, 32) if gerr != nil { // The group couldn't be parsed as a number, so look up // the group's GID. gid64, gerr = lookupGroupInContainer(rootdir, groupspec) } if gerr != nil { return 0, errors.Wrapf(gerr, "error looking up group for gid %q", groupspec) } return uint32(gid64), nil } // GetAdditionalGroupsForUser returns a list of gids that userid is associated with func GetAdditionalGroupsForUser(rootdir string, userid uint64) ([]uint32, error) { gids, err := lookupAdditionalGroupsForUIDInContainer(rootdir, userid) if err != nil { return nil, errors.Wrapf(err, "error looking up supplemental groups for uid %d", userid) } return gids, nil } // LookupUIDInContainer returns username and gid associated with a UID in a container // it will use the /etc/passwd files inside of the rootdir // to return this information. func LookupUIDInContainer(rootdir string, uid uint64) (user string, gid uint64, err error) { return lookupUIDInContainer(rootdir, uid) }