fusefrontend_reverse: secure GetAttr against symlink races
...by using the OpenNofollow helper & Fstatat. Also introduce a helper to convert from unix.Stat_t to syscall.Stat_t. Tracking ticket: https://github.com/rfjakob/gocryptfs/issues/165
This commit is contained in:
parent
03bf604fc0
commit
a3bdc2bf2b
|
@ -7,6 +7,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
"github.com/hanwen/go-fuse/fuse"
|
"github.com/hanwen/go-fuse/fuse"
|
||||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||||
|
@ -150,15 +152,13 @@ func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
absPath, _ := rfs.abs(pRelPath, nil)
|
// Stat the backing file/dir using Fstatat
|
||||||
// Stat the backing file
|
var st unix.Stat_t
|
||||||
var st syscall.Stat_t
|
dirFd, err := syscallcompat.OpenNofollow(rfs.args.Cipherdir, filepath.Dir(pRelPath), syscall.O_RDONLY|syscall.O_DIRECTORY, 0)
|
||||||
if relPath == "" {
|
if err != nil {
|
||||||
// Look through symlinks for the root dir
|
return nil, fuse.ToStatus(err)
|
||||||
err = syscall.Stat(absPath, &st)
|
|
||||||
} else {
|
|
||||||
err = syscall.Lstat(absPath, &st)
|
|
||||||
}
|
}
|
||||||
|
err = syscallcompat.Fstatat(dirFd, filepath.Base(pRelPath), &st, unix.AT_SYMLINK_NOFOLLOW)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,8 @@ func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
|
||||||
return nil, fuse.ToStatus(syscall.EOVERFLOW)
|
return nil, fuse.ToStatus(syscall.EOVERFLOW)
|
||||||
}
|
}
|
||||||
var a fuse.Attr
|
var a fuse.Attr
|
||||||
a.FromStat(&st)
|
st2 := syscallcompat.Unix2syscall(st)
|
||||||
|
a.FromStat(&st2)
|
||||||
// Calculate encrypted file size
|
// Calculate encrypted file size
|
||||||
if a.IsRegular() {
|
if a.IsRegular() {
|
||||||
a.Size = rfs.contentEnc.PlainSizeToCipherSize(a.Size)
|
a.Size = rfs.contentEnc.PlainSizeToCipherSize(a.Size)
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package syscallcompat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unix2syscall converts a unix.Stat_t struct to a syscall.Stat_t struct.
|
||||||
|
// A direct cast does not work because the padding is named differently in
|
||||||
|
// unix.Stat_t for some reason ("X__unused" in syscall, "_" in unix).
|
||||||
|
func Unix2syscall(u unix.Stat_t) syscall.Stat_t {
|
||||||
|
return syscall.Stat_t{
|
||||||
|
Dev: u.Dev,
|
||||||
|
Ino: u.Ino,
|
||||||
|
Nlink: u.Nlink,
|
||||||
|
Mode: u.Mode,
|
||||||
|
Uid: u.Uid,
|
||||||
|
Gid: u.Gid,
|
||||||
|
Rdev: u.Rdev,
|
||||||
|
Size: u.Size,
|
||||||
|
Blksize: u.Blksize,
|
||||||
|
Blocks: u.Blocks,
|
||||||
|
Atim: syscall.Timespec(u.Atim),
|
||||||
|
Mtim: syscall.Timespec(u.Mtim),
|
||||||
|
Ctim: syscall.Timespec(u.Ctim),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue