mirror of
https://github.com/opencontainers/runc.git
synced 2025-09-26 19:41:35 +08:00
deps: update to github.com/cyphar/filepath-securejoin@v0.4.1
This release includes a minor breaking API change that requires us to rework the types of our wrappers, but there is no practical behaviour change. Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
2
go.mod
2
go.mod
@@ -12,7 +12,7 @@ require (
|
||||
github.com/cilium/ebpf v0.17.1
|
||||
github.com/containerd/console v1.0.4
|
||||
github.com/coreos/go-systemd/v22 v22.5.0
|
||||
github.com/cyphar/filepath-securejoin v0.3.6
|
||||
github.com/cyphar/filepath-securejoin v0.4.1
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/godbus/dbus/v5 v5.1.0
|
||||
github.com/moby/sys/capability v0.4.0
|
||||
|
4
go.sum
4
go.sum
@@ -10,8 +10,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@@ -287,9 +287,6 @@ func IsLexicallyInRoot(root, path string) bool {
|
||||
// try to detect any symlink components in the path while we are doing the
|
||||
// MkdirAll.
|
||||
//
|
||||
// NOTE: Unlike os.MkdirAll, mode is not Go's os.FileMode, it is the unix mode
|
||||
// (the suid/sgid/sticky bits are not the same as for os.FileMode).
|
||||
//
|
||||
// NOTE: If unsafePath is a subpath of root, we assume that you have already
|
||||
// called SecureJoin and so we use the provided path verbatim without resolving
|
||||
// any symlinks (this is done in a way that avoids symlink-exchange races).
|
||||
@@ -300,7 +297,7 @@ func IsLexicallyInRoot(root, path string) bool {
|
||||
// handling if unsafePath has already been scoped within the rootfs (this is
|
||||
// needed for a lot of runc callers and fixing this would require reworking a
|
||||
// lot of path logic).
|
||||
func MkdirAllInRootOpen(root, unsafePath string, mode uint32) (_ *os.File, Err error) {
|
||||
func MkdirAllInRootOpen(root, unsafePath string, mode os.FileMode) (_ *os.File, Err error) {
|
||||
// If the path is already "within" the root, get the path relative to the
|
||||
// root and use that as the unsafe path. This is necessary because a lot of
|
||||
// MkdirAllInRootOpen callers have already done SecureJoin, and refactoring
|
||||
@@ -334,12 +331,12 @@ func MkdirAllInRootOpen(root, unsafePath string, mode uint32) (_ *os.File, Err e
|
||||
}
|
||||
defer rootDir.Close()
|
||||
|
||||
return securejoin.MkdirAllHandle(rootDir, unsafePath, int(mode))
|
||||
return securejoin.MkdirAllHandle(rootDir, unsafePath, mode)
|
||||
}
|
||||
|
||||
// MkdirAllInRoot is a wrapper around MkdirAllInRootOpen which closes the
|
||||
// returned handle, for callers that don't need to use it.
|
||||
func MkdirAllInRoot(root, unsafePath string, mode uint32) error {
|
||||
func MkdirAllInRoot(root, unsafePath string, mode os.FileMode) error {
|
||||
f, err := MkdirAllInRootOpen(root, unsafePath, mode)
|
||||
if err == nil {
|
||||
_ = f.Close()
|
||||
|
49
vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
generated
vendored
49
vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
generated
vendored
@@ -6,6 +6,51 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased] ##
|
||||
|
||||
## [0.4.1] - 2025-01-28 ##
|
||||
|
||||
### Fixed ###
|
||||
- The restrictions added for `root` paths passed to `SecureJoin` in 0.4.0 was
|
||||
found to be too strict and caused some regressions when folks tried to
|
||||
update, so this restriction has been relaxed to only return an error if the
|
||||
path contains a `..` component. We still recommend users use `filepath.Clean`
|
||||
(and even `filepath.EvalSymlinks`) on the `root` path they are using, but at
|
||||
least you will no longer be punished for "trivial" unclean paths.
|
||||
|
||||
## [0.4.0] - 2025-01-13 ##
|
||||
|
||||
### Breaking ####
|
||||
- `SecureJoin(VFS)` will now return an error if the provided `root` is not a
|
||||
`filepath.Clean`'d path.
|
||||
|
||||
While it is ultimately the responsibility of the caller to ensure the root is
|
||||
a safe path to use, passing a path like `/symlink/..` as a root would result
|
||||
in the `SecureJoin`'d path being placed in `/` even though `/symlink/..`
|
||||
might be a different directory, and so we should more strongly discourage
|
||||
such usage.
|
||||
|
||||
All major users of `securejoin.SecureJoin` already ensure that the paths they
|
||||
provide are safe (and this is ultimately a question of user error), but
|
||||
removing this foot-gun is probably a good idea. Of course, this is
|
||||
necessarily a breaking API change (though we expect no real users to be
|
||||
affected by it).
|
||||
|
||||
Thanks to [Erik Sjölund](https://github.com/eriksjolund), who initially
|
||||
reported this issue as a possible security issue.
|
||||
|
||||
- `MkdirAll` and `MkdirHandle` now take an `os.FileMode`-style mode argument
|
||||
instead of a raw `unix.S_*`-style mode argument, which may cause compile-time
|
||||
type errors depending on how you use `filepath-securejoin`. For most users,
|
||||
there will be no change in behaviour aside from the type change (as the
|
||||
bottom `0o777` bits are the same in both formats, and most users are probably
|
||||
only using those bits).
|
||||
|
||||
However, if you were using `unix.S_ISVTX` to set the sticky bit with
|
||||
`MkdirAll(Handle)` you will need to switch to `os.ModeSticky` otherwise you
|
||||
will get a runtime error with this update. In addition, the error message you
|
||||
will get from passing `unix.S_ISUID` and `unix.S_ISGID` will be different as
|
||||
they are treated as invalid bits now (note that previously passing said bits
|
||||
was also an error).
|
||||
|
||||
## [0.3.6] - 2024-12-17 ##
|
||||
|
||||
### Compatibility ###
|
||||
@@ -193,7 +238,9 @@ This is our first release of `github.com/cyphar/filepath-securejoin`,
|
||||
containing a full implementation with a coverage of 93.5% (the only missing
|
||||
cases are the error cases, which are hard to mocktest at the moment).
|
||||
|
||||
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...HEAD
|
||||
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.1...HEAD
|
||||
[0.4.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.0...v0.4.1
|
||||
[0.4.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...v0.4.0
|
||||
[0.3.6]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.5...v0.3.6
|
||||
[0.3.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.4...v0.3.5
|
||||
[0.3.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.3...v0.3.4
|
||||
|
2
vendor/github.com/cyphar/filepath-securejoin/VERSION
generated
vendored
2
vendor/github.com/cyphar/filepath-securejoin/VERSION
generated
vendored
@@ -1 +1 @@
|
||||
0.3.6
|
||||
0.4.1
|
||||
|
49
vendor/github.com/cyphar/filepath-securejoin/join.go
generated
vendored
49
vendor/github.com/cyphar/filepath-securejoin/join.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
|
||||
// Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
|
||||
// Copyright (C) 2017-2025 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -24,6 +24,31 @@ func IsNotExist(err error) bool {
|
||||
return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT)
|
||||
}
|
||||
|
||||
// errUnsafeRoot is returned if the user provides SecureJoinVFS with a path
|
||||
// that contains ".." components.
|
||||
var errUnsafeRoot = errors.New("root path provided to SecureJoin contains '..' components")
|
||||
|
||||
// stripVolume just gets rid of the Windows volume included in a path. Based on
|
||||
// some godbolt tests, the Go compiler is smart enough to make this a no-op on
|
||||
// Linux.
|
||||
func stripVolume(path string) string {
|
||||
return path[len(filepath.VolumeName(path)):]
|
||||
}
|
||||
|
||||
// hasDotDot checks if the path contains ".." components in a platform-agnostic
|
||||
// way.
|
||||
func hasDotDot(path string) bool {
|
||||
// If we are on Windows, strip any volume letters. It turns out that
|
||||
// C:..\foo may (or may not) be a valid pathname and we need to handle that
|
||||
// leading "..".
|
||||
path = stripVolume(path)
|
||||
// Look for "/../" in the path, but we need to handle leading and trailing
|
||||
// ".."s by adding separators. Doing this with filepath.Separator is ugly
|
||||
// so just convert to Unix-style "/" first.
|
||||
path = filepath.ToSlash(path)
|
||||
return strings.Contains("/"+path+"/", "/../")
|
||||
}
|
||||
|
||||
// SecureJoinVFS joins the two given path components (similar to [filepath.Join]) except
|
||||
// that the returned path is guaranteed to be scoped inside the provided root
|
||||
// path (when evaluated). Any symbolic links in the path are evaluated with the
|
||||
@@ -46,7 +71,22 @@ func IsNotExist(err error) bool {
|
||||
// provided via direct input or when evaluating symlinks. Therefore:
|
||||
//
|
||||
// "C:\Temp" + "D:\path\to\file.txt" results in "C:\Temp\path\to\file.txt"
|
||||
//
|
||||
// If the provided root is not [filepath.Clean] then an error will be returned,
|
||||
// as such root paths are bordering on somewhat unsafe and using such paths is
|
||||
// not best practice. We also strongly suggest that any root path is first
|
||||
// fully resolved using [filepath.EvalSymlinks] or otherwise constructed to
|
||||
// avoid containing symlink components. Of course, the root also *must not* be
|
||||
// attacker-controlled.
|
||||
func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
|
||||
// The root path must not contain ".." components, otherwise when we join
|
||||
// the subpath we will end up with a weird path. We could work around this
|
||||
// in other ways but users shouldn't be giving us non-lexical root paths in
|
||||
// the first place.
|
||||
if hasDotDot(root) {
|
||||
return "", errUnsafeRoot
|
||||
}
|
||||
|
||||
// Use the os.* VFS implementation if none was specified.
|
||||
if vfs == nil {
|
||||
vfs = osVFS{}
|
||||
@@ -59,9 +99,10 @@ func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
|
||||
linksWalked int
|
||||
)
|
||||
for remainingPath != "" {
|
||||
if v := filepath.VolumeName(remainingPath); v != "" {
|
||||
remainingPath = remainingPath[len(v):]
|
||||
}
|
||||
// On Windows, if we managed to end up at a path referencing a volume,
|
||||
// drop the volume to make sure we don't end up with broken paths or
|
||||
// escaping the root volume.
|
||||
remainingPath = stripVolume(remainingPath)
|
||||
|
||||
// Get the next path component.
|
||||
var part string
|
||||
|
49
vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go
generated
vendored
49
vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go
generated
vendored
@@ -21,6 +21,33 @@ var (
|
||||
errPossibleAttack = errors.New("possible attack detected")
|
||||
)
|
||||
|
||||
// modePermExt is like os.ModePerm except that it also includes the set[ug]id
|
||||
// and sticky bits.
|
||||
const modePermExt = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky
|
||||
|
||||
//nolint:cyclop // this function needs to handle a lot of cases
|
||||
func toUnixMode(mode os.FileMode) (uint32, error) {
|
||||
sysMode := uint32(mode.Perm())
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
sysMode |= unix.S_ISUID
|
||||
}
|
||||
if mode&os.ModeSetgid != 0 {
|
||||
sysMode |= unix.S_ISGID
|
||||
}
|
||||
if mode&os.ModeSticky != 0 {
|
||||
sysMode |= unix.S_ISVTX
|
||||
}
|
||||
// We don't allow file type bits.
|
||||
if mode&os.ModeType != 0 {
|
||||
return 0, fmt.Errorf("%w %+.3o (%s): type bits not permitted", errInvalidMode, mode, mode)
|
||||
}
|
||||
// We don't allow other unknown modes.
|
||||
if mode&^modePermExt != 0 || sysMode&unix.S_IFMT != 0 {
|
||||
return 0, fmt.Errorf("%w %+.3o (%s): unknown mode bits", errInvalidMode, mode, mode)
|
||||
}
|
||||
return sysMode, nil
|
||||
}
|
||||
|
||||
// MkdirAllHandle is equivalent to [MkdirAll], except that it is safer to use
|
||||
// in two respects:
|
||||
//
|
||||
@@ -39,17 +66,17 @@ var (
|
||||
// a brand new lookup of unsafePath (such as with [SecureJoin] or openat2) after
|
||||
// doing [MkdirAll]. If you intend to open the directory after creating it, you
|
||||
// should use MkdirAllHandle.
|
||||
func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err error) {
|
||||
// Make sure there are no os.FileMode bits set.
|
||||
if mode&^0o7777 != 0 {
|
||||
return nil, fmt.Errorf("%w for mkdir 0o%.3o", errInvalidMode, mode)
|
||||
func MkdirAllHandle(root *os.File, unsafePath string, mode os.FileMode) (_ *os.File, Err error) {
|
||||
unixMode, err := toUnixMode(mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// On Linux, mkdirat(2) (and os.Mkdir) silently ignore the suid and sgid
|
||||
// bits. We could also silently ignore them but since we have very few
|
||||
// users it seems more prudent to return an error so users notice that
|
||||
// these bits will not be set.
|
||||
if mode&^0o1777 != 0 {
|
||||
return nil, fmt.Errorf("%w for mkdir 0o%.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode)
|
||||
if unixMode&^0o1777 != 0 {
|
||||
return nil, fmt.Errorf("%w for mkdir %+.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode)
|
||||
}
|
||||
|
||||
// Try to open as much of the path as possible.
|
||||
@@ -104,9 +131,6 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
return nil, fmt.Errorf("%w: yet-to-be-created path %q contains '..' components", unix.ENOENT, remainingPath)
|
||||
}
|
||||
|
||||
// Make sure the mode doesn't have any type bits.
|
||||
mode &^= unix.S_IFMT
|
||||
|
||||
// Create the remaining components.
|
||||
for _, part := range remainingParts {
|
||||
switch part {
|
||||
@@ -123,7 +147,7 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
// directory at the same time as us. In that case, just continue on as
|
||||
// if we created it (if the created inode is not a directory, the
|
||||
// following open call will fail).
|
||||
if err := unix.Mkdirat(int(currentDir.Fd()), part, uint32(mode)); err != nil && !errors.Is(err, unix.EEXIST) {
|
||||
if err := unix.Mkdirat(int(currentDir.Fd()), part, unixMode); err != nil && !errors.Is(err, unix.EEXIST) {
|
||||
err = &os.PathError{Op: "mkdirat", Path: currentDir.Name() + "/" + part, Err: err}
|
||||
// Make the error a bit nicer if the directory is dead.
|
||||
if deadErr := isDeadInode(currentDir); deadErr != nil {
|
||||
@@ -196,10 +220,7 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
// If you plan to open the directory after you have created it or want to use
|
||||
// an open directory handle as the root, you should use [MkdirAllHandle] instead.
|
||||
// This function is a wrapper around [MkdirAllHandle].
|
||||
//
|
||||
// NOTE: The mode argument must be set the unix mode bits (unix.S_I...), not
|
||||
// the Go generic mode bits ([os.FileMode]...).
|
||||
func MkdirAll(root, unsafePath string, mode int) error {
|
||||
func MkdirAll(root, unsafePath string, mode os.FileMode) error {
|
||||
rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -27,7 +27,7 @@ github.com/coreos/go-systemd/v22/dbus
|
||||
# github.com/cpuguy83/go-md2man/v2 v2.0.5
|
||||
## explicit; go 1.11
|
||||
github.com/cpuguy83/go-md2man/v2/md2man
|
||||
# github.com/cyphar/filepath-securejoin v0.3.6
|
||||
# github.com/cyphar/filepath-securejoin v0.4.1
|
||||
## explicit; go 1.18
|
||||
github.com/cyphar/filepath-securejoin
|
||||
# github.com/docker/go-units v0.5.0
|
||||
|
Reference in New Issue
Block a user