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" "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)

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