diff options
| author | Jakob Unterwurzacher | 2019-01-02 21:45:40 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2019-01-02 21:45:40 +0100 | 
| commit | 2b12bba274ba75f76ac8c2af3790e4190b32396f (patch) | |
| tree | ba8a637f0677cbe387220de9f4742dd0e33c639c /internal | |
| parent | 59f1300591d44b1e5d8187b54951c03d6120289f (diff) | |
fusefronted: make EncryptPath symlink-safe
Finally allows us to delete EncryptPathDirIV.
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/fusefrontend/ctlsock_interface.go | 28 | ||||
| -rw-r--r-- | internal/fusefrontend/names.go | 21 | ||||
| -rw-r--r-- | internal/nametransform/diriv.go | 67 | ||||
| -rw-r--r-- | internal/nametransform/names.go | 5 | 
4 files changed, 28 insertions, 93 deletions
| diff --git a/internal/fusefrontend/ctlsock_interface.go b/internal/fusefrontend/ctlsock_interface.go index b29d150..15345f3 100644 --- a/internal/fusefrontend/ctlsock_interface.go +++ b/internal/fusefrontend/ctlsock_interface.go @@ -3,21 +3,45 @@ package fusefrontend  import (  	"fmt"  	"path" +	"path/filepath"  	"strings"  	"syscall"  	"github.com/rfjakob/gocryptfs/internal/ctlsock"  	"github.com/rfjakob/gocryptfs/internal/nametransform"  	"github.com/rfjakob/gocryptfs/internal/syscallcompat" +	"github.com/rfjakob/gocryptfs/internal/tlog"  )  var _ ctlsock.Interface = &FS{} // Verify that interface is implemented.  // EncryptPath implements ctlsock.Backend  // -// TODO: this function is NOT symlink-safe. +// Symlink-safe through openBackingDir().  func (fs *FS) EncryptPath(plainPath string) (string, error) { -	return fs.encryptPath(plainPath) +	if plainPath == "" { +		// Empty string gets encrypted as empty string +		return plainPath, nil +	} +	if fs.args.PlaintextNames { +		return plainPath, nil +	} +	// Encrypt path level by level using openBackingDir. Pretty inefficient, +	// but does not matter here. +	parts := strings.Split(plainPath, "/") +	wd := "" +	cPath := "" +	for _, part := range parts { +		wd = filepath.Join(wd, part) +		dirfd, cName, err := fs.openBackingDir(wd) +		if err != nil { +			return "", err +		} +		syscall.Close(dirfd) +		cPath = filepath.Join(cPath, cName) +	} +	tlog.Debug.Printf("encryptPath '%s' -> '%s'", plainPath, cPath) +	return cPath, nil  }  // DecryptPath implements ctlsock.Backend diff --git a/internal/fusefrontend/names.go b/internal/fusefrontend/names.go index 36185e2..63f2e84 100644 --- a/internal/fusefrontend/names.go +++ b/internal/fusefrontend/names.go @@ -83,24 +83,3 @@ func (fs *FS) openBackingDir(relPath string) (dirfd int, cName string, err error  	}  	return dirfd, cName, nil  } - -// encryptPath - encrypt relative plaintext path -// -// TODO: this function is NOT symlink-safe because EncryptPathDirIV is not -// symlink-safe. -func (fs *FS) encryptPath(plainPath string) (string, error) { -	if plainPath != "" { // Empty path gets encrypted all the time without actual file accesses. -		fs.AccessedSinceLastCheck = 1 -	} else { // Empty string gets encrypted as empty string -		return plainPath, nil -	} -	if fs.args.PlaintextNames { -		return plainPath, nil -	} - -	fs.dirIVLock.RLock() -	cPath, err := fs.nameTransform.EncryptPathDirIV(plainPath, fs.args.Cipherdir) -	tlog.Debug.Printf("encryptPath '%s' -> '%s' (err: %v)", plainPath, cPath, err) -	fs.dirIVLock.RUnlock() -	return cPath, err -} diff --git a/internal/nametransform/diriv.go b/internal/nametransform/diriv.go index b98de0c..93c4c68 100644 --- a/internal/nametransform/diriv.go +++ b/internal/nametransform/diriv.go @@ -25,26 +25,6 @@ const (  	DirIVFilename = "gocryptfs.diriv"  ) -// ReadDirIV - read the "gocryptfs.diriv" file from "dir" (absolute ciphertext path) -// This function is exported because it allows for an efficient readdir implementation. -// If the directory itself cannot be opened, a syscall error will be returned. -// Otherwise, a fmt.Errorf() error value is returned with the details. -// -// TODO: this function is not symlink-safe and should be deleted once the only -// remaining user, EncryptPathDirIV(), is gone. -func ReadDirIV(dir string) (iv []byte, err error) { -	fd, err := os.Open(filepath.Join(dir, DirIVFilename)) -	if err != nil { -		// Note: getting errors here is normal because of concurrent deletes. -		// Strip the useless annotation that os.Open has added and return -		// the plain syscall error. The caller will log a nice message. -		err2 := err.(*os.PathError) -		return nil, err2.Err -	} -	defer fd.Close() -	return fdReadDirIV(fd) -} -  // ReadDirIVAt reads "gocryptfs.diriv" from the directory that is opened as "dirfd".  // Using the dirfd makes it immune to concurrent renames of the directory.  func ReadDirIVAt(dirfd int) (iv []byte, err error) { @@ -132,53 +112,6 @@ func (be *NameTransform) EncryptAndHashName(name string, iv []byte) string {  	return cName  } -// EncryptPathDirIV - encrypt relative plaintext path "plainPath" using EME with -// DirIV. "rootDir" is the backing storage root directory. -// Components that are longer than 255 bytes are hashed if be.longnames == true. -// -// TODO: EncryptPathDirIV is NOT SAFE against symlink races. This function -// should eventually be deleted. -func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (string, error) { -	var err error -	// Empty string means root directory -	if plainPath == "" { -		return plainPath, nil -	} -	// Reject names longer than 255 bytes. -	baseName := filepath.Base(plainPath) -	if len(baseName) > unix.NAME_MAX { -		return "", syscall.ENAMETOOLONG -	} -	// If we have the iv and the encrypted directory name in the cache, we -	// can skip the directory walk. This optimization yields a 10% improvement -	// in the tar extract benchmark. -	parentDir := Dir(plainPath) -	if iv, cParentDir := be.DirIVCache.Lookup(parentDir); iv != nil { -		cBaseName := be.EncryptAndHashName(baseName, iv) -		return filepath.Join(cParentDir, cBaseName), nil -	} -	// We have to walk the directory tree, starting at the root directory. -	// ciphertext working directory (relative path) -	cipherWD := "" -	// plaintext working directory (relative path) -	plainWD := "" -	plainNames := strings.Split(plainPath, "/") -	for _, plainName := range plainNames { -		iv, _ := be.DirIVCache.Lookup(plainWD) -		if iv == nil { -			iv, err = ReadDirIV(filepath.Join(rootDir, cipherWD)) -			if err != nil { -				return "", err -			} -			be.DirIVCache.Store(plainWD, iv, cipherWD) -		} -		cipherName := be.EncryptAndHashName(plainName, iv) -		cipherWD = filepath.Join(cipherWD, cipherName) -		plainWD = filepath.Join(plainWD, plainName) -	} -	return cipherWD, nil -} -  // Dir is like filepath.Dir but returns "" instead of ".".  func Dir(path string) string {  	d := filepath.Dir(path) diff --git a/internal/nametransform/names.go b/internal/nametransform/names.go index 22e639a..33128b9 100644 --- a/internal/nametransform/names.go +++ b/internal/nametransform/names.go @@ -74,11 +74,10 @@ func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error  }  // EncryptName encrypts "plainName", returns a base64-encoded "cipherName64". -// Used internally by EncryptPathDirIV().  // The encryption is either CBC or EME, depending on "useEME".  // -// This function is exported because fusefrontend needs access to the full (not hashed) -// name if longname is used. Otherwise you should use EncryptPathDirIV() +// This function is exported because in some cases, fusefrontend needs access +// to the full (not hashed) name if longname is used.  func (n *NameTransform) EncryptName(plainName string, iv []byte) (cipherName64 string) {  	bin := []byte(plainName)  	bin = pad16(bin) | 
