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:
Jakob Unterwurzacher 2017-12-06 00:05:28 +01:00
parent 03bf604fc0
commit a3bdc2bf2b
2 changed files with 38 additions and 9 deletions

View File

@ -7,6 +7,8 @@ import (
"path/filepath"
"syscall"
"golang.org/x/sys/unix"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"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 {
return nil, fuse.ToStatus(err)
}
absPath, _ := rfs.abs(pRelPath, nil)
// Stat the backing file
var st syscall.Stat_t
if relPath == "" {
// Look through symlinks for the root dir
err = syscall.Stat(absPath, &st)
} else {
err = syscall.Lstat(absPath, &st)
// Stat the backing file/dir using Fstatat
var st unix.Stat_t
dirFd, err := syscallcompat.OpenNofollow(rfs.args.Cipherdir, filepath.Dir(pRelPath), syscall.O_RDONLY|syscall.O_DIRECTORY, 0)
if err != nil {
return nil, fuse.ToStatus(err)
}
err = syscallcompat.Fstatat(dirFd, filepath.Base(pRelPath), &st, unix.AT_SYMLINK_NOFOLLOW)
if err != nil {
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)
}
var a fuse.Attr
a.FromStat(&st)
st2 := syscallcompat.Unix2syscall(st)
a.FromStat(&st2)
// Calculate encrypted file size
if a.IsRegular() {
a.Size = rfs.contentEnc.PlainSizeToCipherSize(a.Size)

View File

@ -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),
}
}