mirror of
https://github.com/containers/podman.git
synced 2025-08-24 01:50:58 +08:00
fix(deps): update module github.com/cyphar/filepath-securejoin to v0.3.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
106
vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go
generated
vendored
106
vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go
generated
vendored
@ -9,7 +9,6 @@ package securejoin
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@ -23,23 +22,23 @@ var (
|
||||
errPossibleAttack = errors.New("possible attack detected")
|
||||
)
|
||||
|
||||
// MkdirAllHandle is equivalent to MkdirAll, except that it is safer to use in
|
||||
// two respects:
|
||||
// MkdirAllHandle is equivalent to [MkdirAll], except that it is safer to use
|
||||
// in two respects:
|
||||
//
|
||||
// - The caller provides the root directory as an *os.File (preferably O_PATH)
|
||||
// - The caller provides the root directory as an *[os.File] (preferably O_PATH)
|
||||
// handle. This means that the caller can be sure which root directory is
|
||||
// being used. Note that this can be emulated by using /proc/self/fd/... as
|
||||
// the root path with MkdirAll.
|
||||
// the root path with [os.MkdirAll].
|
||||
//
|
||||
// - Once all of the directories have been created, an *os.File (O_PATH) handle
|
||||
// - Once all of the directories have been created, an *[os.File] O_PATH handle
|
||||
// to the directory at unsafePath is returned to the caller. This is done in
|
||||
// an effectively-race-free way (an attacker would only be able to swap the
|
||||
// final directory component), which is not possible to emulate with
|
||||
// MkdirAll.
|
||||
// [MkdirAll].
|
||||
//
|
||||
// In addition, the returned handle is obtained far more efficiently than doing
|
||||
// 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
|
||||
// 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.
|
||||
@ -108,35 +107,6 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
|
||||
// Make sure the mode doesn't have any type bits.
|
||||
mode &^= unix.S_IFMT
|
||||
// What properties do we expect any newly created directories to have?
|
||||
var (
|
||||
// While umask(2) is a per-thread property, and thus this value could
|
||||
// vary between threads, a functioning Go program would LockOSThread
|
||||
// threads with different umasks and so we don't need to LockOSThread
|
||||
// for this entire mkdirat loop (if we are in the locked thread with a
|
||||
// different umask, we are already locked and there's nothing for us to
|
||||
// do -- and if not then it doesn't matter which thread we run on and
|
||||
// there's nothing for us to do).
|
||||
expectedMode = uint32(unix.S_IFDIR | (mode &^ getUmask()))
|
||||
|
||||
// We would want to get the fs[ug]id here, but we can't access those
|
||||
// from userspace. In practice, nobody uses setfs[ug]id() anymore, so
|
||||
// just use the effective [ug]id (which is equivalent to the fs[ug]id
|
||||
// for programs that don't use setfs[ug]id).
|
||||
expectedUid = uint32(unix.Geteuid())
|
||||
expectedGid = uint32(unix.Getegid())
|
||||
)
|
||||
|
||||
// The setgid bit (S_ISGID = 0o2000) is inherited to child directories and
|
||||
// affects the group of any inodes created in said directory, so if the
|
||||
// starting directory has it set we need to adjust our expected mode and
|
||||
// owner to match.
|
||||
if st, err := fstatFile(currentDir); err != nil {
|
||||
return nil, fmt.Errorf("failed to stat starting path for mkdir %q: %w", currentDir.Name(), err)
|
||||
} else if st.Mode&unix.S_ISGID == unix.S_ISGID {
|
||||
expectedMode |= unix.S_ISGID
|
||||
expectedGid = st.Gid
|
||||
}
|
||||
|
||||
// Create the remaining components.
|
||||
for _, part := range remainingParts {
|
||||
@ -147,7 +117,7 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
}
|
||||
|
||||
// NOTE: mkdir(2) will not follow trailing symlinks, so we can safely
|
||||
// create the finaly component without worrying about symlink-exchange
|
||||
// create the final component without worrying about symlink-exchange
|
||||
// attacks.
|
||||
if err := unix.Mkdirat(int(currentDir.Fd()), part, uint32(mode)); err != nil {
|
||||
err = &os.PathError{Op: "mkdirat", Path: currentDir.Name() + "/" + part, Err: err}
|
||||
@ -175,40 +145,30 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
_ = currentDir.Close()
|
||||
currentDir = nextDir
|
||||
|
||||
// Make sure that the directory matches what we expect. An attacker
|
||||
// could have swapped the directory between us making it and opening
|
||||
// it. There's no way for us to be sure that the directory is
|
||||
// _precisely_ the same as the directory we created, but if we are in
|
||||
// an empty directory with the same owner and mode as the one we
|
||||
// created then there is nothing the attacker could do with this new
|
||||
// directory that they couldn't do with the old one.
|
||||
if stat, err := fstat(currentDir); err != nil {
|
||||
return nil, fmt.Errorf("check newly created directory: %w", err)
|
||||
} else {
|
||||
if stat.Mode != expectedMode {
|
||||
return nil, fmt.Errorf("%w: newly created directory %q has incorrect mode 0o%.3o (expected 0o%.3o)", errPossibleAttack, currentDir.Name(), stat.Mode, expectedMode)
|
||||
}
|
||||
if stat.Uid != expectedUid || stat.Gid != expectedGid {
|
||||
return nil, fmt.Errorf("%w: newly created directory %q has incorrect owner %d:%d (expected %d:%d)", errPossibleAttack, currentDir.Name(), stat.Uid, stat.Gid, expectedUid, expectedGid)
|
||||
}
|
||||
// Check that the directory is empty. We only need to check for
|
||||
// a single entry, and we should get EOF if the directory is
|
||||
// empty.
|
||||
_, err := currentDir.Readdirnames(1)
|
||||
if !errors.Is(err, io.EOF) {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("%w: newly created directory %q is non-empty", errPossibleAttack, currentDir.Name())
|
||||
}
|
||||
return nil, fmt.Errorf("check if newly created directory %q is empty: %w", currentDir.Name(), err)
|
||||
}
|
||||
// Reset the offset.
|
||||
_, _ = currentDir.Seek(0, unix.SEEK_SET)
|
||||
}
|
||||
// It's possible that the directory we just opened was swapped by an
|
||||
// attacker. Unfortunately there isn't much we can do to protect
|
||||
// against this, and MkdirAll's behaviour is that we will reuse
|
||||
// existing directories anyway so the need to protect against this is
|
||||
// incredibly limited (and arguably doesn't even deserve mention here).
|
||||
//
|
||||
// Ideally we might want to check that the owner and mode match what we
|
||||
// would've created -- unfortunately, it is non-trivial to verify that
|
||||
// the owner and mode of the created directory match. While plain Unix
|
||||
// DAC rules seem simple enough to emulate, there are a bunch of other
|
||||
// factors that can change the mode or owner of created directories
|
||||
// (default POSIX ACLs, mount options like uid=1,gid=2,umask=0 on
|
||||
// filesystems like vfat, etc etc). We used to try to verify this but
|
||||
// it just lead to a series of spurious errors.
|
||||
//
|
||||
// We could also check that the directory is non-empty, but
|
||||
// unfortunately some pseduofilesystems (like cgroupfs) create
|
||||
// non-empty directories, which would result in different spurious
|
||||
// errors.
|
||||
}
|
||||
return currentDir, nil
|
||||
}
|
||||
|
||||
// MkdirAll is a race-safe alternative to the Go stdlib's os.MkdirAll function,
|
||||
// MkdirAll is a race-safe alternative to the [os.MkdirAll] function,
|
||||
// where the new directory is guaranteed to be within the root directory (if an
|
||||
// attacker can move directories from inside the root to outside the root, the
|
||||
// created directory tree might be outside of the root but the key constraint
|
||||
@ -221,16 +181,16 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
// err := os.MkdirAll(path, mode)
|
||||
//
|
||||
// But is much safer. The above implementation is unsafe because if an attacker
|
||||
// can modify the filesystem tree between SecureJoin and MkdirAll, it is
|
||||
// can modify the filesystem tree between [SecureJoin] and [os.MkdirAll], it is
|
||||
// possible for MkdirAll to resolve unsafe symlink components and create
|
||||
// directories outside of the root.
|
||||
//
|
||||
// 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.
|
||||
// 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.Mode...).
|
||||
// the Go generic mode bits ([os.FileMode]...).
|
||||
func MkdirAll(root, unsafePath string, mode int) error {
|
||||
rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user