fusefrontend: allow_other: close race between mkdir and chown

Fixes the same problem as described in 72b975867a3b9bdf53fc2da62e2ba4a328d7e4ab,
except for directories instead of device nodes.
This commit is contained in:
Sebastian Lackner 2017-11-29 13:21:28 +01:00
parent 67bcbe81e8
commit 614745ee57
6 changed files with 48 additions and 29 deletions

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

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