mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-10 21:53:57 +08:00
build(deps): bump github.com/moby/sys/user from 0.4.0 to 0.4.1
Bumps [github.com/moby/sys/user](https://github.com/moby/sys) from 0.4.0 to 0.4.1. - [Release notes](https://github.com/moby/sys/releases) - [Commits](https://github.com/moby/sys/compare/user/v0.4.0...user/v0.4.1) --- updated-dependencies: - dependency-name: github.com/moby/sys/user dependency-version: 0.4.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
@@ -13,7 +13,7 @@ require (
|
|||||||
github.com/moby/sys/capability v0.4.0
|
github.com/moby/sys/capability v0.4.0
|
||||||
github.com/moby/sys/devices v0.1.0
|
github.com/moby/sys/devices v0.1.0
|
||||||
github.com/moby/sys/mountinfo v0.7.2
|
github.com/moby/sys/mountinfo v0.7.2
|
||||||
github.com/moby/sys/user v0.4.0
|
github.com/moby/sys/user v0.4.1
|
||||||
github.com/moby/sys/userns v0.1.0
|
github.com/moby/sys/userns v0.1.0
|
||||||
github.com/mrunalp/fileutils v0.5.1
|
github.com/mrunalp/fileutils v0.5.1
|
||||||
github.com/opencontainers/cgroups v0.0.6
|
github.com/opencontainers/cgroups v0.0.6
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ github.com/moby/sys/devices v0.1.0 h1:uaMrDm1U3h0AwUDNWeT5lBV40v0eayt+VuukRbYn5K
|
|||||||
github.com/moby/sys/devices v0.1.0/go.mod h1:nIV6AO7t0DY2ObAm1GfL4AX9mBRqzxzHwGfvNCR9lfI=
|
github.com/moby/sys/devices v0.1.0/go.mod h1:nIV6AO7t0DY2ObAm1GfL4AX9mBRqzxzHwGfvNCR9lfI=
|
||||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||||
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
|
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
|
||||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
github.com/moby/sys/user v0.4.1 h1:RgjRlaDKi/Xmyrz4t8lyzXT6v2ooFeO/7xtchmhVWE0=
|
||||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
github.com/moby/sys/user v0.4.1/go.mod h1:E9QsW5WRe1kUAf7kW8hXKwu1uhsZEAdPLYHYSDudF4Y=
|
||||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||||
github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm/Q=
|
github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm/Q=
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
|
||||||
|
|
||||||
package user
|
package user
|
||||||
|
|
||||||
|
|||||||
+116
-113
@@ -56,11 +56,11 @@ type IDMap struct {
|
|||||||
Count int64
|
Count int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseLine(line []byte, v ...interface{}) {
|
func parseLine(line []byte, v ...any) {
|
||||||
parseParts(bytes.Split(line, []byte(":")), v...)
|
parseParts(bytes.Split(line, []byte(":")), v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseParts(parts [][]byte, v ...interface{}) {
|
func parseParts(parts [][]byte, v ...any) {
|
||||||
if len(parts) == 0 {
|
if len(parts) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -97,12 +97,7 @@ func parseParts(parts [][]byte, v ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParsePasswdFile(path string) ([]User, error) {
|
func ParsePasswdFile(path string) ([]User, error) {
|
||||||
passwd, err := os.Open(path)
|
return ParsePasswdFileFilter(path, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer passwd.Close()
|
|
||||||
return ParsePasswd(passwd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParsePasswd(passwd io.Reader) ([]User, error) {
|
func ParsePasswd(passwd io.Reader) ([]User, error) {
|
||||||
@@ -154,13 +149,7 @@ func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParseGroupFile(path string) ([]Group, error) {
|
func ParseGroupFile(path string) ([]Group, error) {
|
||||||
group, err := os.Open(path)
|
return ParseGroupFileFilter(path, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer group.Close()
|
|
||||||
return ParseGroup(group)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseGroup(group io.Reader) ([]Group, error) {
|
func ParseGroup(group io.Reader) ([]Group, error) {
|
||||||
@@ -168,7 +157,7 @@ func ParseGroup(group io.Reader) ([]Group, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) {
|
func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) {
|
||||||
group, err := os.Open(path)
|
group, err := openUserFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -180,52 +169,22 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
|
|||||||
if r == nil {
|
if r == nil {
|
||||||
return nil, errors.New("nil source for group-formatted data")
|
return nil, errors.New("nil source for group-formatted data")
|
||||||
}
|
}
|
||||||
rd := bufio.NewReader(r)
|
|
||||||
out := []Group{}
|
|
||||||
|
|
||||||
// Read the file line-by-line.
|
var (
|
||||||
for {
|
s = bufio.NewScanner(r)
|
||||||
var (
|
out = []Group{}
|
||||||
isPrefix bool
|
)
|
||||||
wholeLine []byte
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
// Read the next line. We do so in chunks (as much as reader's
|
// A group's user_list may be arbitrarily long, so allow lines that are
|
||||||
// buffer is able to keep), check if we read enough columns
|
// much larger than bufio.Scanner's default maximum token size (64 KiB).
|
||||||
// already on each step and store final result in wholeLine.
|
s.Buffer(nil, 1024*1024)
|
||||||
for {
|
|
||||||
var line []byte
|
|
||||||
line, isPrefix, err = rd.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
// We should return no error if EOF is reached
|
|
||||||
// without a match.
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple common case: line is short enough to fit in a
|
|
||||||
// single reader's buffer.
|
|
||||||
if !isPrefix && len(wholeLine) == 0 {
|
|
||||||
wholeLine = line
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
wholeLine = append(wholeLine, line...)
|
|
||||||
|
|
||||||
// Check if we read the whole line already.
|
|
||||||
if !isPrefix {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for s.Scan() {
|
||||||
// There's no spec for /etc/passwd or /etc/group, but we try to follow
|
// There's no spec for /etc/passwd or /etc/group, but we try to follow
|
||||||
// the same rules as the glibc parser, which allows comments and blank
|
// the same rules as the glibc parser, which allows comments and blank
|
||||||
// space at the beginning of a line.
|
// space at the beginning of a line.
|
||||||
wholeLine = bytes.TrimSpace(wholeLine)
|
line := bytes.TrimSpace(s.Bytes())
|
||||||
if len(wholeLine) == 0 || wholeLine[0] == '#' {
|
if len(line) == 0 || line[0] == '#' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,12 +194,17 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
|
|||||||
// root:x:0:root
|
// root:x:0:root
|
||||||
// adm:x:4:root,adm,daemon
|
// adm:x:4:root,adm,daemon
|
||||||
p := Group{}
|
p := Group{}
|
||||||
parseLine(wholeLine, &p.Name, &p.Pass, &p.Gid, &p.List)
|
parseLine(line, &p.Name, &p.Pass, &p.Gid, &p.List)
|
||||||
|
|
||||||
if filter == nil || filter(p) {
|
if filter == nil || filter(p) {
|
||||||
out = append(out, p)
|
out = append(out, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecUser struct {
|
type ExecUser struct {
|
||||||
@@ -257,12 +221,12 @@ type ExecUser struct {
|
|||||||
func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
||||||
var passwd, group io.Reader
|
var passwd, group io.Reader
|
||||||
|
|
||||||
if passwdFile, err := os.Open(passwdPath); err == nil {
|
if passwdFile, err := openUserFile(passwdPath); err == nil {
|
||||||
passwd = passwdFile
|
passwd = passwdFile
|
||||||
defer passwdFile.Close()
|
defer passwdFile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if groupFile, err := os.Open(groupPath); err == nil {
|
if groupFile, err := openUserFile(groupPath); err == nil {
|
||||||
group = groupFile
|
group = groupFile
|
||||||
defer groupFile.Close()
|
defer groupFile.Close()
|
||||||
}
|
}
|
||||||
@@ -270,6 +234,38 @@ func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath
|
|||||||
return GetExecUser(userSpec, defaults, passwd, group)
|
return GetExecUser(userSpec, defaults, passwd, group)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseNumeric parses the given UID or GID value to an integer and within
|
||||||
|
// the minID - maxID range.
|
||||||
|
//
|
||||||
|
// While the Linux kernel allows the max UID to be MaxUint32 - 2,
|
||||||
|
// and the OCI Runtime Spec has no definition about the max UID, we require
|
||||||
|
// the UID to be <= MaxInt32.
|
||||||
|
//
|
||||||
|
// See https://github.com/containerd/containerd/commit/de1341c201ffb0effebbf51d00376181968c8779
|
||||||
|
func parseNumeric(val string) (int, bool, error) {
|
||||||
|
if val == "" {
|
||||||
|
return 0, false, nil
|
||||||
|
}
|
||||||
|
id, err := strconv.Atoi(val)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, strconv.ErrSyntax) {
|
||||||
|
// Discard the error, because non-numeric values are expected
|
||||||
|
// when passing a username or group-name.
|
||||||
|
return 0, false, nil
|
||||||
|
}
|
||||||
|
if errors.Is(err, strconv.ErrRange) {
|
||||||
|
return 0, true, ErrRange
|
||||||
|
}
|
||||||
|
// Other errors ("invalid base", "invalid bit size"); should never
|
||||||
|
// happen with strconv.Atoi.
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
if id < minID || id > maxID {
|
||||||
|
return 0, true, ErrRange
|
||||||
|
}
|
||||||
|
return id, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetExecUser parses a user specification string (using the passwd and group
|
// GetExecUser parses a user specification string (using the passwd and group
|
||||||
// readers as sources for /etc/passwd and /etc/group data, respectively). In
|
// readers as sources for /etc/passwd and /etc/group data, respectively). In
|
||||||
// the case of blank fields or missing data from the sources, the values in
|
// the case of blank fields or missing data from the sources, the values in
|
||||||
@@ -315,8 +311,14 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
|
|||||||
|
|
||||||
// Convert userArg and groupArg to be numeric, so we don't have to execute
|
// Convert userArg and groupArg to be numeric, so we don't have to execute
|
||||||
// Atoi *twice* for each iteration over lines.
|
// Atoi *twice* for each iteration over lines.
|
||||||
uidArg, uidErr := strconv.Atoi(userArg)
|
uidArg, isUID, err := parseNumeric(userArg)
|
||||||
gidArg, gidErr := strconv.Atoi(groupArg)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
gidArg, isGID, err := parseNumeric(groupArg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Find the matching user.
|
// Find the matching user.
|
||||||
users, err := ParsePasswdFilter(passwd, func(u User) bool {
|
users, err := ParsePasswdFilter(passwd, func(u User) bool {
|
||||||
@@ -325,8 +327,8 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
|
|||||||
return u.Uid == user.Uid
|
return u.Uid == user.Uid
|
||||||
}
|
}
|
||||||
|
|
||||||
if uidErr == nil {
|
if isUID {
|
||||||
// If the userArg is numeric, always treat it as a UID.
|
// If the userArg is a valid numeric value, always treat it as a UID.
|
||||||
return uidArg == u.Uid
|
return uidArg == u.Uid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,18 +354,11 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
|
|||||||
// If we can't find a user with the given username, the only other valid
|
// If we can't find a user with the given username, the only other valid
|
||||||
// option is if it's a numeric username with no associated entry in passwd.
|
// option is if it's a numeric username with no associated entry in passwd.
|
||||||
|
|
||||||
if uidErr != nil {
|
if !isUID {
|
||||||
// Not numeric.
|
// Not numeric.
|
||||||
return nil, fmt.Errorf("unable to find user %s: %w", userArg, ErrNoPasswdEntries)
|
return nil, fmt.Errorf("unable to find user %s: %w", userArg, ErrNoPasswdEntries)
|
||||||
}
|
}
|
||||||
user.Uid = uidArg
|
user.Uid = uidArg
|
||||||
|
|
||||||
// Must be inside valid uid range.
|
|
||||||
if user.Uid < minID || user.Uid > maxID {
|
|
||||||
return nil, ErrRange
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okay, so it's numeric. We can just roll with this.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// On to the groups. If we matched a username, we need to do this because of
|
// On to the groups. If we matched a username, we need to do this because of
|
||||||
@@ -381,7 +376,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if gidErr == nil {
|
if isGID {
|
||||||
// If the groupArg is numeric, always treat it as a GID.
|
// If the groupArg is numeric, always treat it as a GID.
|
||||||
return gidArg == g.Gid
|
return gidArg == g.Gid
|
||||||
}
|
}
|
||||||
@@ -401,18 +396,11 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
|
|||||||
// If we can't find a group with the given name, the only other valid
|
// If we can't find a group with the given name, the only other valid
|
||||||
// option is if it's a numeric group name with no associated entry in group.
|
// option is if it's a numeric group name with no associated entry in group.
|
||||||
|
|
||||||
if gidErr != nil {
|
if !isGID {
|
||||||
// Not numeric.
|
// Not numeric.
|
||||||
return nil, fmt.Errorf("unable to find group %s: %w", groupArg, ErrNoGroupEntries)
|
return nil, fmt.Errorf("unable to find group %s: %w", groupArg, ErrNoGroupEntries)
|
||||||
}
|
}
|
||||||
user.Gid = gidArg
|
user.Gid = gidArg
|
||||||
|
|
||||||
// Must be inside valid gid range.
|
|
||||||
if user.Gid < minID || user.Gid > maxID {
|
|
||||||
return nil, ErrRange
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okay, so it's numeric. We can just roll with this.
|
|
||||||
}
|
}
|
||||||
} else if len(groups) > 0 {
|
} else if len(groups) > 0 {
|
||||||
// Supplementary group ids only make sense if in the implicit form.
|
// Supplementary group ids only make sense if in the implicit form.
|
||||||
@@ -426,55 +414,80 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// groupArg is a parsed group argument for [GetAdditionalGroups].
|
||||||
|
type groupArg struct {
|
||||||
|
name string
|
||||||
|
gid int
|
||||||
|
isNumeric bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches reports whether group g satisfies the argument. Numeric arguments
|
||||||
|
// are matched by GID only, others by name.
|
||||||
|
func (ag groupArg) matches(g Group) bool {
|
||||||
|
if ag.isNumeric {
|
||||||
|
return g.Gid == ag.gid
|
||||||
|
}
|
||||||
|
return g.Name == ag.name
|
||||||
|
}
|
||||||
|
|
||||||
// GetAdditionalGroups looks up a list of groups by name or group id
|
// GetAdditionalGroups looks up a list of groups by name or group id
|
||||||
// against the given /etc/group formatted data. If a group name cannot
|
// against the given /etc/group formatted data. If a group name cannot
|
||||||
// be found, an error will be returned. If a group id cannot be found,
|
// be found, an error will be returned. If a group id cannot be found,
|
||||||
// or the given group data is nil, the id will be returned as-is
|
// or the given group data is nil, the id will be returned as-is
|
||||||
// provided it is in the legal range.
|
// provided it is in the legal range.
|
||||||
func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, error) {
|
func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, error) {
|
||||||
|
addtlGroups := make([]groupArg, len(additionalGroups))
|
||||||
|
for i, ag := range additionalGroups {
|
||||||
|
gid, ok, err := parseNumeric(ag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
addtlGroups[i] = groupArg{
|
||||||
|
name: ag,
|
||||||
|
gid: gid,
|
||||||
|
isNumeric: ok,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
groups := []Group{}
|
groups := []Group{}
|
||||||
if group != nil {
|
if group != nil {
|
||||||
var err error
|
var err error
|
||||||
groups, err = ParseGroupFilter(group, func(g Group) bool {
|
groups, err = ParseGroupFilter(group, func(g Group) bool {
|
||||||
for _, ag := range additionalGroups {
|
for _, ag := range addtlGroups {
|
||||||
if g.Name == ag || strconv.Itoa(g.Gid) == ag {
|
if ag.matches(g) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Unable to find additional groups %v: %w", additionalGroups, err)
|
return nil, fmt.Errorf("unable to find additional groups %v: %w", additionalGroups, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gidMap := make(map[int]struct{})
|
gidMap := make(map[int]struct{})
|
||||||
for _, ag := range additionalGroups {
|
for _, ag := range addtlGroups {
|
||||||
var found bool
|
var found bool
|
||||||
for _, g := range groups {
|
for _, g := range groups {
|
||||||
// if we found a matched group either by name or gid, take the
|
if ag.matches(g) {
|
||||||
// first matched as correct
|
// take the first matched group as correct
|
||||||
if g.Name == ag || strconv.Itoa(g.Gid) == ag {
|
if g.Gid < minID || g.Gid > maxID {
|
||||||
if _, ok := gidMap[g.Gid]; !ok {
|
return nil, ErrRange
|
||||||
gidMap[g.Gid] = struct{}{}
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
gidMap[g.Gid] = struct{}{}
|
||||||
|
found = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// we asked for a group but didn't find it. let's check to see
|
// We asked for a group but didn't find it. Numeric group IDs may be
|
||||||
// if we wanted a numeric group
|
// used as-is even when they are not present in /etc/group; non-numeric
|
||||||
|
// group names must be found.
|
||||||
if !found {
|
if !found {
|
||||||
gid, err := strconv.ParseInt(ag, 10, 64)
|
if !ag.isNumeric {
|
||||||
if err != nil {
|
|
||||||
// Not a numeric ID either.
|
// Not a numeric ID either.
|
||||||
return nil, fmt.Errorf("Unable to find group %s: %w", ag, ErrNoGroupEntries)
|
return nil, fmt.Errorf("unable to find group %s: %w", ag.name, ErrNoGroupEntries)
|
||||||
}
|
}
|
||||||
// Ensure gid is inside gid range.
|
gidMap[ag.gid] = struct{}{}
|
||||||
if gid < minID || gid > maxID {
|
|
||||||
return nil, ErrRange
|
|
||||||
}
|
|
||||||
gidMap[int(gid)] = struct{}{}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gids := []int{}
|
gids := []int{}
|
||||||
@@ -498,12 +511,7 @@ func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParseSubIDFile(path string) ([]SubID, error) {
|
func ParseSubIDFile(path string) ([]SubID, error) {
|
||||||
subid, err := os.Open(path)
|
return ParseSubIDFileFilter(path, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer subid.Close()
|
|
||||||
return ParseSubID(subid)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseSubID(subid io.Reader) ([]SubID, error) {
|
func ParseSubID(subid io.Reader) ([]SubID, error) {
|
||||||
@@ -551,12 +559,7 @@ func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParseIDMapFile(path string) ([]IDMap, error) {
|
func ParseIDMapFile(path string) ([]IDMap, error) {
|
||||||
r, err := os.Open(path)
|
return ParseIDMapFileFilter(path, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer r.Close()
|
|
||||||
return ParseIDMap(r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseIDMap(r io.Reader) ([]IDMap, error) {
|
func ParseIDMap(r io.Reader) ([]IDMap, error) {
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
//go:build gofuzz
|
//go:build gofuzz
|
||||||
// +build gofuzz
|
|
||||||
|
|
||||||
package user
|
package user
|
||||||
|
|
||||||
|
|||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// maxUserFileBytes caps how much data is read from any user-database file.
|
||||||
|
// User database files are expected to be relatively small. 10 MiB provides
|
||||||
|
// generous headroom while bounding memory usage.
|
||||||
|
const maxUserFileBytes = 10 << 20
|
||||||
|
|
||||||
|
// openUserFile attempts to open a user-database file with a limitedFile
|
||||||
|
// capped at maxUserFileBytes. It produces an error if the given path is
|
||||||
|
// a non-regular file.
|
||||||
|
func openUserFile(path string) (*limitedFile, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
_ = f.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !info.Mode().IsRegular() {
|
||||||
|
_ = f.Close()
|
||||||
|
return nil, &os.PathError{
|
||||||
|
Op: "open",
|
||||||
|
Path: path,
|
||||||
|
Err: errors.New("not a regular file"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &limitedFile{
|
||||||
|
File: f,
|
||||||
|
// Allow one byte past the cap so an overflow surfaces as an
|
||||||
|
// error rather than a silent EOF that the parser would treat as
|
||||||
|
// a clean end-of-file (and miss any entries past the cap).
|
||||||
|
LimitedReader: &io.LimitedReader{R: f, N: maxUserFileBytes + 1},
|
||||||
|
name: path,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type limitedFile struct {
|
||||||
|
*os.File
|
||||||
|
*io.LimitedReader
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *limitedFile) Read(p []byte) (int, error) {
|
||||||
|
n, err := l.LimitedReader.Read(p)
|
||||||
|
if l.LimitedReader.N == 0 {
|
||||||
|
return n, &os.PathError{
|
||||||
|
Op: "read",
|
||||||
|
Path: l.name,
|
||||||
|
Err: fmt.Errorf("file exceeds %d bytes", maxUserFileBytes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
Vendored
+2
-2
@@ -62,8 +62,8 @@ github.com/moby/sys/devices
|
|||||||
# github.com/moby/sys/mountinfo v0.7.2
|
# github.com/moby/sys/mountinfo v0.7.2
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/moby/sys/mountinfo
|
github.com/moby/sys/mountinfo
|
||||||
# github.com/moby/sys/user v0.4.0
|
# github.com/moby/sys/user v0.4.1
|
||||||
## explicit; go 1.17
|
## explicit; go 1.18
|
||||||
github.com/moby/sys/user
|
github.com/moby/sys/user
|
||||||
# github.com/moby/sys/userns v0.1.0
|
# github.com/moby/sys/userns v0.1.0
|
||||||
## explicit; go 1.21
|
## explicit; go 1.21
|
||||||
|
|||||||
Reference in New Issue
Block a user