diff options
| author | Jakob Unterwurzacher | 2018-10-01 21:28:54 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2019-01-01 16:24:09 +0100 | 
| commit | c09bf1f2284706232642431c75fa1f3d8500a9d0 (patch) | |
| tree | 80aa369bc7139cec50865a108d092e413243636d | |
| parent | ed6ed513d7f06178c0de02e8a372c33fe8f842f1 (diff) | |
fusefrontend: make DecryptPath() symlink-safe
DecryptPath is now symlink-safe through the use of *at()
functions.
| -rw-r--r-- | internal/fusefrontend/ctlsock_interface.go | 39 | ||||
| -rw-r--r-- | internal/fusefrontend/fs_dir.go | 2 | ||||
| -rw-r--r-- | internal/nametransform/longnames.go | 18 | 
3 files changed, 44 insertions, 15 deletions
| diff --git a/internal/fusefrontend/ctlsock_interface.go b/internal/fusefrontend/ctlsock_interface.go index 964775b..730ed58 100644 --- a/internal/fusefrontend/ctlsock_interface.go +++ b/internal/fusefrontend/ctlsock_interface.go @@ -4,9 +4,11 @@ import (  	"fmt"  	"path"  	"strings" +	"syscall"  	"github.com/rfjakob/gocryptfs/internal/ctlsock"  	"github.com/rfjakob/gocryptfs/internal/nametransform" +	"github.com/rfjakob/gocryptfs/internal/syscallcompat"  )  var _ ctlsock.Interface = &FS{} // Verify that interface is implemented. @@ -17,22 +19,31 @@ func (fs *FS) EncryptPath(plainPath string) (string, error) {  }  // DecryptPath implements ctlsock.Backend -func (fs *FS) DecryptPath(cipherPath string) (string, error) { +func (fs *FS) DecryptPath(cipherPath string) (plainPath string, err error) { +	dirfd, err := syscall.Open(fs.args.Cipherdir, syscall.O_RDONLY, 0) +	if err != nil { +		return "", err +	} +	defer syscall.Close(dirfd) +	return fs.decryptPathAt(dirfd, cipherPath) +} + +// decryptPathAt decrypts a ciphertext path relative to dirfd. +func (fs *FS) decryptPathAt(dirfd int, cipherPath string) (plainPath string, err error) {  	if fs.args.PlaintextNames || cipherPath == "" {  		return cipherPath, nil  	} -	plainPath := ""  	parts := strings.Split(cipherPath, "/") -	wd := fs.args.Cipherdir -	for _, part := range parts { -		dirIV, err := nametransform.ReadDirIV(wd) +	wd := dirfd +	for i, part := range parts { +		dirIV, err := nametransform.ReadDirIVAt(wd)  		if err != nil {  			fmt.Printf("ReadDirIV: %v\n", err)  			return "", err  		}  		longPart := part  		if nametransform.IsLongContent(part) { -			longPart, err = nametransform.ReadLongName(wd + "/" + part) +			longPart, err = nametransform.ReadLongNameAt(wd, part)  			if err != nil {  				fmt.Printf("ReadLongName: %v\n", err)  				return "", err @@ -44,7 +55,21 @@ func (fs *FS) DecryptPath(cipherPath string) (string, error) {  			return "", err  		}  		plainPath = path.Join(plainPath, name) -		wd = path.Join(wd, part) +		// Last path component? We are done. +		if i == len(parts)-1 { +			break +		} +		// Descend into next directory +		oldWd := wd +		wd, err = syscallcompat.Openat(wd, part, syscall.O_NOFOLLOW, 0) +		if err != nil { +			return "", err +		} +		// Unless we are in the first iteration, where dirfd is our wd, close +		// the old working directory. +		if i > 0 { +			syscall.Close(oldWd) +		}  	}  	return plainPath, nil  } diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go index 963a551..76dff8e 100644 --- a/internal/fusefrontend/fs_dir.go +++ b/internal/fusefrontend/fs_dir.go @@ -324,7 +324,7 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f  			isLong = nametransform.NameType(cName)  		}  		if isLong == nametransform.LongNameContent { -			cNameLong, err := nametransform.ReadLongName(filepath.Join(cDirAbsPath, cName)) +			cNameLong, err := nametransform.ReadLongNameAt(fd, cName)  			if err != nil {  				tlog.Warn.Printf("OpenDir %q: invalid entry %q: Could not read .name: %v",  					cDirName, cName, err) diff --git a/internal/nametransform/longnames.go b/internal/nametransform/longnames.go index da18ebb..9c8637e 100644 --- a/internal/nametransform/longnames.go +++ b/internal/nametransform/longnames.go @@ -64,18 +64,18 @@ func IsLongContent(cName string) bool {  }  // ReadLongName - read "$path.name" -func ReadLongName(path string) (string, error) { -	path += LongNameSuffix -	fd, err := os.Open(path) +func ReadLongNameAt(dirfd int, cName string) (string, error) { +	cName += LongNameSuffix +	fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_NOFOLLOW, 0)  	if err != nil {  		return "", err  	} -	defer fd.Close() +	defer syscall.Close(fd)  	// 256 (=255 padded to 16) bytes base64-encoded take 344 bytes: "AAAAAAA...AAA=="  	lim := 344  	// Allocate a bigger buffer so we see whether the file is too big  	buf := make([]byte, lim+1) -	n, err := fd.ReadAt(buf, 0) +	n, err := syscall.Pread(fd, buf, 0)  	if err != nil && err != io.EOF {  		return "", err  	} @@ -88,7 +88,9 @@ func ReadLongName(path string) (string, error) {  	return string(buf[0:n]), nil  } -// DeleteLongName deletes "hashName.name". +// DeleteLongName deletes "hashName.name" in the directory openend at "dirfd". +// +// This function is symlink-safe through the use of Unlinkat().  func DeleteLongName(dirfd int, hashName string) error {  	err := syscallcompat.Unlinkat(dirfd, hashName+LongNameSuffix, 0)  	if err != nil { @@ -99,7 +101,9 @@ func DeleteLongName(dirfd int, hashName string) error {  // WriteLongName encrypts plainName and writes it into "hashName.name".  // For the convenience of the caller, plainName may also be a path and will be -// converted internally. +// Base()named internally. +// +// This function is symlink-safe through the use of Openat().  func (n *NameTransform) WriteLongName(dirfd int, hashName string, plainName string) (err error) {  	plainName = filepath.Base(plainName) | 
