fs: stricter enforcement to only call readdir on a directory

Use O_DIRECTORY to prevent opening any other than a directory in
readdirnames.
This commit is contained in:
Michael Eischer 2024-11-02 17:44:55 +01:00
parent f8031561f2
commit b402e8a6fc
4 changed files with 33 additions and 2 deletions

View file

@ -7,3 +7,6 @@ import "syscall"
// O_NOFOLLOW instructs the kernel to not follow symlinks when opening a file.
const O_NOFOLLOW int = syscall.O_NOFOLLOW
// O_DIRECTORY instructs the kernel to only open directories.
const O_DIRECTORY int = syscall.O_DIRECTORY

View file

@ -3,5 +3,10 @@
package fs
// TODO honor flags when opening files
// O_NOFOLLOW is a noop on Windows.
const O_NOFOLLOW int = 0
// O_DIRECTORY is a noop on Windows.
const O_DIRECTORY int = 0

View file

@ -64,9 +64,10 @@ func ResetPermissions(path string) error {
return nil
}
// Readdirnames returns a list of file in a directory. Flags are passed to fs.OpenFile. O_RDONLY is implied.
// Readdirnames returns a list of file in a directory. Flags are passed to fs.OpenFile.
// O_RDONLY and O_DIRECTORY are implied.
func Readdirnames(filesystem FS, dir string, flags int) ([]string, error) {
f, err := filesystem.OpenFile(dir, O_RDONLY|flags, 0)
f, err := filesystem.OpenFile(dir, O_RDONLY|O_DIRECTORY|flags, 0)
if err != nil {
return nil, fmt.Errorf("openfile for readdirnames failed: %w", err)
}

View file

@ -0,0 +1,22 @@
//go:build unix
package fs
import (
"path/filepath"
"syscall"
"testing"
"github.com/restic/restic/internal/errors"
rtest "github.com/restic/restic/internal/test"
)
func TestReaddirnamesFifo(t *testing.T) {
// should not block when reading from a fifo instead of a directory
tempdir := t.TempDir()
fifoFn := filepath.Join(tempdir, "fifo")
rtest.OK(t, mkfifo(fifoFn, 0o600))
_, err := Readdirnames(&Local{}, fifoFn, 0)
rtest.Assert(t, errors.Is(err, syscall.ENOTDIR), "unexpected error %v", err)
}