diff options
author | Sebastian Lackner | 2017-11-29 13:21:28 +0100 |
---|---|---|
committer | Sebastian Lackner | 2017-11-29 13:28:04 +0100 |
commit | 614745ee576760023961fbf815985b90f90ad1d7 (patch) | |
tree | d2577781a64df82303ed569c95cfb9a658fae5f6 /internal | |
parent | 67bcbe81e80da29fb340c5a4712831f70442d8c9 (diff) |
fusefrontend: allow_other: close race between mkdir and chown
Fixes the same problem as described in 72b975867a3b9bdf53fc2da62e2ba4a328d7e4ab,
except for directories instead of device nodes.
Diffstat (limited to 'internal')
-rw-r--r-- | internal/fusefrontend/fs_dir.go | 44 | ||||
-rw-r--r-- | internal/nametransform/diriv.go | 7 | ||||
-rw-r--r-- | internal/syscallcompat/sys_darwin.go | 17 | ||||
-rw-r--r-- | internal/syscallcompat/sys_linux.go | 5 |
4 files changed, 46 insertions, 27 deletions
diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go index ae150b5..4ffaaff 100644 --- a/internal/fusefrontend/fs_dir.go +++ b/internal/fusefrontend/fs_dir.go @@ -24,7 +24,7 @@ import ( const dsStoreName = ".DS_Store" -func (fs *FS) mkdirWithIv(cPath string, mode uint32) error { +func (fs *FS) mkdirWithIv(dirfd *os.File, cName string, mode uint32) error { // Between the creation of the directory and the creation of gocryptfs.diriv // the directory is inconsistent. Take the lock to prevent other readers // from seeing it. @@ -32,14 +32,14 @@ func (fs *FS) mkdirWithIv(cPath string, mode uint32) error { // The new directory may take the place of an older one that is still in the cache fs.nameTransform.DirIVCache.Clear() defer fs.dirIVLock.Unlock() - err := os.Mkdir(cPath, os.FileMode(mode)) + err := syscallcompat.Mkdirat(int(dirfd.Fd()), cName, mode) if err != nil { return err } // Create gocryptfs.diriv - err = nametransform.WriteDirIV(cPath) + err = nametransform.WriteDirIV(dirfd, cName) if err != nil { - err2 := syscall.Rmdir(cPath) + err2 := syscallcompat.Unlinkat(int(dirfd.Fd()), cName, unix.AT_REMOVEDIR) if err2 != nil { tlog.Warn.Printf("mkdirWithIv: rollback failed: %v", err2) } @@ -52,17 +52,19 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu if fs.isFiltered(newPath) { return fuse.EPERM } - cPath, err := fs.getBackingPath(newPath) + dirfd, cName, err := fs.openBackingPath(newPath) if err != nil { return fuse.ToStatus(err) } + defer dirfd.Close() if fs.args.PlaintextNames { - err = os.Mkdir(cPath, os.FileMode(mode)) + err = syscallcompat.Mkdirat(int(dirfd.Fd()), cName, mode) // Set owner if fs.args.PreserveOwner { - err = os.Lchown(cPath, int(context.Owner.Uid), int(context.Owner.Gid)) + err = syscallcompat.Fchownat(int(dirfd.Fd()), cName, int(context.Owner.Uid), + int(context.Owner.Gid), unix.AT_SYMLINK_NOFOLLOW) if err != nil { - tlog.Warn.Printf("Mkdir: Lchown failed: %v", err) + tlog.Warn.Printf("Mkdir: Fchownat failed: %v", err) } } return fuse.ToStatus(err) @@ -73,15 +75,7 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu mode = mode | 0300 // Handle long file name - cName := filepath.Base(cPath) if nametransform.IsLongContent(cName) { - var dirfd *os.File - dirfd, err = os.Open(filepath.Dir(cPath)) - if err != nil { - return fuse.ToStatus(err) - } - defer dirfd.Close() - // Create ".name" err = fs.nameTransform.WriteLongName(dirfd, cName, newPath) if err != nil { @@ -89,33 +83,35 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu } // Create directory - err = fs.mkdirWithIv(cPath, mode) + err = fs.mkdirWithIv(dirfd, cName, mode) if err != nil { nametransform.DeleteLongName(dirfd, cName) return fuse.ToStatus(err) } } else { - err = fs.mkdirWithIv(cPath, mode) + err = fs.mkdirWithIv(dirfd, cName, mode) if err != nil { return fuse.ToStatus(err) } } // Set permissions back to what the user wanted if origMode != mode { - err = os.Chmod(cPath, os.FileMode(origMode)) + err = syscallcompat.Fchmodat(int(dirfd.Fd()), cName, origMode, unix.AT_SYMLINK_NOFOLLOW) if err != nil { - tlog.Warn.Printf("Mkdir: Chmod failed: %v", err) + tlog.Warn.Printf("Mkdir: Fchmodat failed: %v", err) } } // Set owner if fs.args.PreserveOwner { - err = os.Lchown(cPath, int(context.Owner.Uid), int(context.Owner.Gid)) + err = syscallcompat.Fchownat(int(dirfd.Fd()), cName, int(context.Owner.Uid), + int(context.Owner.Gid), unix.AT_SYMLINK_NOFOLLOW) if err != nil { - tlog.Warn.Printf("Mkdir: Lchown 1 failed: %v", err) + tlog.Warn.Printf("Mkdir: Fchownat 1 failed: %v", err) } - err = os.Lchown(filepath.Join(cPath, nametransform.DirIVFilename), int(context.Owner.Uid), int(context.Owner.Gid)) + err = syscallcompat.Fchownat(int(dirfd.Fd()), filepath.Join(cName, nametransform.DirIVFilename), + int(context.Owner.Uid), int(context.Owner.Gid), unix.AT_SYMLINK_NOFOLLOW) if err != nil { - tlog.Warn.Printf("Mkdir: Lchown 2 failed: %v", err) + tlog.Warn.Printf("Mkdir: Fchownat 2 failed: %v", err) } } return fuse.OK diff --git a/internal/nametransform/diriv.go b/internal/nametransform/diriv.go index ffaf785..fe289c6 100644 --- a/internal/nametransform/diriv.go +++ b/internal/nametransform/diriv.go @@ -75,16 +75,17 @@ func fdReadDirIV(fd *os.File) (iv []byte, err error) { // WriteDirIV - create diriv file inside "dir" (absolute ciphertext path) // This function is exported because it is used from pathfs_frontend, main, // and also the automated tests. -func WriteDirIV(dir string) error { +func WriteDirIV(dirfd *os.File, dir string) error { iv := cryptocore.RandBytes(DirIVLen) file := filepath.Join(dir, DirIVFilename) // 0400 permissions: gocryptfs.diriv should never be modified after creation. // Don't use "ioutil.WriteFile", it causes trouble on NFS: https://github.com/rfjakob/gocryptfs/issues/105 - fd, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400) + fdRaw, err := syscallcompat.Openat(int(dirfd.Fd()), file, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400) if err != nil { - tlog.Warn.Printf("WriteDirIV: OpenFile: %v", err) + tlog.Warn.Printf("WriteDirIV: Openat: %v", err) return err } + fd := os.NewFile(uintptr(fdRaw), file) _, err = fd.Write(iv) if err != nil { tlog.Warn.Printf("WriteDirIV: Write: %v", err) diff --git a/internal/syscallcompat/sys_darwin.go b/internal/syscallcompat/sys_darwin.go index 367f67d..39a225f 100644 --- a/internal/syscallcompat/sys_darwin.go +++ b/internal/syscallcompat/sys_darwin.go @@ -187,3 +187,20 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) { defer syscall.Fchdir(cwd) return syscall.Symlink(oldpath, newpath) } + +// Poor man's Mkdirat. +func Mkdirat(dirfd int, path string, mode uint32) (err error) { + chdirMutex.Lock() + defer chdirMutex.Unlock() + cwd, err := syscall.Open(".", syscall.O_RDONLY, 0) + if err != nil { + return err + } + defer syscall.Close(cwd) + err = syscall.Fchdir(dirfd) + if err != nil { + return err + } + defer syscall.Fchdir(cwd) + return syscall.Mkdir(path, mode) +} diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go index 4fe44fd..1ea56b5 100644 --- a/internal/syscallcompat/sys_linux.go +++ b/internal/syscallcompat/sys_linux.go @@ -109,3 +109,8 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) { } return } + +// Mkdirat syscall. +func Mkdirat(dirfd int, path string, mode uint32) (err error) { + return syscall.Mkdirat(dirfd, path, mode) +} |