aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Lackner2017-11-29 13:21:28 +0100
committerSebastian Lackner2017-11-29 13:28:04 +0100
commit614745ee576760023961fbf815985b90f90ad1d7 (patch)
treed2577781a64df82303ed569c95cfb9a658fae5f6
parent67bcbe81e80da29fb340c5a4712831f70442d8c9 (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.
-rw-r--r--init_dir.go2
-rw-r--r--internal/fusefrontend/fs_dir.go44
-rw-r--r--internal/nametransform/diriv.go7
-rw-r--r--internal/syscallcompat/sys_darwin.go17
-rw-r--r--internal/syscallcompat/sys_linux.go5
-rw-r--r--tests/test_helpers/helpers.go2
6 files changed, 48 insertions, 29 deletions
diff --git a/init_dir.go b/init_dir.go
index f057bf4..791f7d1 100644
--- a/init_dir.go
+++ b/init_dir.go
@@ -47,7 +47,7 @@ func initDir(args *argContainer) {
// Forward mode with filename encryption enabled needs a gocryptfs.diriv
// in the root dir
if !args.plaintextnames && !args.reverse {
- err = nametransform.WriteDirIV(args.cipherdir)
+ err = nametransform.WriteDirIV(nil, args.cipherdir)
if err != nil {
tlog.Fatal.Println(err)
os.Exit(exitcodes.Init)
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)
+}
diff --git a/tests/test_helpers/helpers.go b/tests/test_helpers/helpers.go
index f681482..9b81b45 100644
--- a/tests/test_helpers/helpers.go
+++ b/tests/test_helpers/helpers.go
@@ -96,7 +96,7 @@ func ResetTmpDir(createDirIV bool) {
panic(err)
}
if createDirIV {
- err = nametransform.WriteDirIV(DefaultCipherDir)
+ err = nametransform.WriteDirIV(nil, DefaultCipherDir)
if err != nil {
panic(err)
}