diff options
| author | Jakob Unterwurzacher | 2016-09-19 23:40:43 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2016-09-25 16:43:17 +0200 | 
| commit | be9dfe3a894bd00a2157bfc3dd19e98bcc171691 (patch) | |
| tree | e8eb9a0858af41aaeb41b11ea379a074417fca03 /internal | |
| parent | 10f38e88707f3a1f1ad69769219839a30a80c165 (diff) | |
reverse: implement dynamic diriv
Introduce a unique per-directory diriv that is generated
by hashing the encrypted directory path.
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/fusefrontend_reverse/diriv.go | 42 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rfs.go | 113 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rpath.go | 47 | 
3 files changed, 116 insertions, 86 deletions
| diff --git a/internal/fusefrontend_reverse/diriv.go b/internal/fusefrontend_reverse/diriv.go new file mode 100644 index 0000000..c4a93e4 --- /dev/null +++ b/internal/fusefrontend_reverse/diriv.go @@ -0,0 +1,42 @@ +package fusefrontend_reverse + +import ( +	"crypto/sha256" + +	"github.com/hanwen/go-fuse/fuse" +	"github.com/hanwen/go-fuse/fuse/nodefs" + +	"github.com/rfjakob/gocryptfs/internal/nametransform" +) + +// deriveDirIV derives the DirIV from the directory path by simply hashing it +func deriveDirIV(dirPath string) []byte { +	hash := sha256.Sum256([]byte(dirPath)) +	return hash[:nametransform.DirIVLen] +} + +type dirIVFile struct { +	// Embed nodefs.defaultFile for a ENOSYS implementation of all methods +	nodefs.File +	// file content +	content []byte +} + +func NewDirIVFile(dirPath string) (nodefs.File, fuse.Status) { +	return &dirIVFile{ +		File:    nodefs.NewDefaultFile(), +		content: deriveDirIV(dirPath), +	}, fuse.OK +} + +// Read - FUSE call +func (f *dirIVFile) Read(buf []byte, off int64) (resultData fuse.ReadResult, status fuse.Status) { +	if off >= int64(len(f.content)) { +		return nil, fuse.OK +	} +	end := int(off) + len(buf) +	if end > len(f.content) { +		end = len(f.content) +	} +	return fuse.ReadResultData(f.content[off:end]), fuse.OK +} diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index 3243dfd..d07971a 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -3,8 +3,7 @@ package fusefrontend_reverse  import (  	"fmt"  	"os" -	"path" -	"strings" +	"path/filepath"  	"syscall"  	"github.com/hanwen/go-fuse/fuse" @@ -50,47 +49,59 @@ func NewFS(args fusefrontend.Args) *reverseFS {  	}  } -func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) { -	// Handle gocryptfs.diriv -	if relPath == nametransform.DirIVFilename || -		strings.HasSuffix(relPath, nametransform.DirIVFilename) { - -		fmt.Printf("Handling gocryptfs.diriv\n") - -		cDir := path.Dir(relPath) -		if cDir == "." { -			cDir = "" -		} -		dir, err := rfs.decryptPath(cDir) -		if err != nil { -			fmt.Printf("decrypt err %q\n", cDir) -			return nil, fuse.ToStatus(err) -		} -		// Does the parent dir exist? -		a, status := rfs.loopbackfs.GetAttr(dir, context) -		if !status.Ok() { -			fmt.Printf("missing parent\n") -			return nil, status -		} -		// Is it a dir at all? -		if !a.IsDir() { -			fmt.Printf("not isdir\n") -			return nil, fuse.ENOTDIR -		} -		// Does the user have execute permissions? -		if a.Mode&syscall.S_IXUSR == 0 { -			fmt.Printf("not exec") -			return nil, fuse.EPERM -		} -		// All good. Let's fake the file. -		// We use the inode number of the parent dir (can this cause problems?). -		a.Mode = DirIVMode -		a.Size = nametransform.DirIVLen -		a.Nlink = 1 - -		return a, fuse.OK +// relDir is identical to filepath.Dir excepts that it returns "" when +// filepath.Dir would return ".". +// In the FUSE API, the root directory is called "", and we actually want that. +func relDir(path string) string { +	dir := filepath.Dir(path) +	if dir == "." { +		return "" +	} +	return dir +} + +// dirIVAttr handles GetAttr requests for the virtual gocryptfs.diriv files. +func (rfs *reverseFS) dirIVAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) { +	cDir := relDir(relPath) +	dir, err := rfs.decryptPath(cDir) +	if err != nil { +		fmt.Printf("decrypt err %q\n", cDir) +		return nil, fuse.ToStatus(err) +	} +	// Does the parent dir exist? +	a, status := rfs.loopbackfs.GetAttr(dir, context) +	if !status.Ok() { +		fmt.Printf("missing parent\n") +		return nil, status +	} +	// Is it a dir at all? +	if !a.IsDir() { +		fmt.Printf("not isdir\n") +		return nil, fuse.ENOTDIR +	} +	// Does the user have execute permissions? +	if a.Mode&syscall.S_IXUSR == 0 { +		fmt.Printf("not exec") +		return nil, fuse.EPERM  	} +	// All good. Let's fake the file. +	// We use the inode number of the parent dir (can this cause problems?). +	a.Mode = DirIVMode +	a.Size = nametransform.DirIVLen +	a.Nlink = 1 +	return a, fuse.OK +} + +// isDirIV determines if the path points to a gocryptfs.diriv file +func isDirIV(relPath string) bool { +	return filepath.Base(relPath) == nametransform.DirIVFilename +} + +func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) { +	if isDirIV(relPath) { +		return rfs.dirIVAttr(relPath, context) +	}  	if rfs.isFiltered(relPath) {  		return nil, fuse.EPERM  	} @@ -110,17 +121,23 @@ func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr  }  func (rfs *reverseFS) Access(relPath string, mode uint32, context *fuse.Context) fuse.Status { +	if isDirIV(relPath) { +		return fuse.OK +	}  	if rfs.isFiltered(relPath) {  		return fuse.EPERM  	} -	cPath, err := rfs.abs(rfs.encryptPath(relPath)) +	absPath, err := rfs.abs(rfs.decryptPath(relPath))  	if err != nil {  		return fuse.ToStatus(err)  	} -	return fuse.ToStatus(syscall.Access(cPath, mode)) +	return fuse.ToStatus(syscall.Access(absPath, mode))  }  func (rfs *reverseFS) Open(relPath string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) { +	if isDirIV(relPath) { +		return NewDirIVFile(relPath) +	}  	if rfs.isFiltered(relPath) {  		return nil, fuse.EPERM  	} @@ -135,8 +152,8 @@ func (rfs *reverseFS) Open(relPath string, flags uint32, context *fuse.Context)  	return NewFile(f, rfs.contentEnc)  } -func (rfs *reverseFS) OpenDir(relPath string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { -	relPath, err := rfs.decryptPath(relPath) +func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { +	relPath, err := rfs.decryptPath(cipherPath)  	if err != nil {  		return nil, fuse.ToStatus(err)  	} @@ -146,11 +163,9 @@ func (rfs *reverseFS) OpenDir(relPath string, context *fuse.Context) ([]fuse.Dir  		return nil, status  	}  	// Encrypt names +	dirIV := deriveDirIV(cipherPath)  	for i := range entries { -		entries[i].Name, err = rfs.encryptPath(entries[i].Name) -		if err != nil { -			return nil, fuse.ToStatus(err) -		} +		entries[i].Name = rfs.nameTransform.EncryptName(entries[i].Name, dirIV)  	}  	// Add virtual gocryptfs.diriv  	entries = append(entries, fuse.DirEntry{syscall.S_IFREG | 0400, nametransform.DirIVFilename}) diff --git a/internal/fusefrontend_reverse/rpath.go b/internal/fusefrontend_reverse/rpath.go index 7e11ca3..a15b31a 100644 --- a/internal/fusefrontend_reverse/rpath.go +++ b/internal/fusefrontend_reverse/rpath.go @@ -2,18 +2,11 @@ package fusefrontend_reverse  import (  	"encoding/base64" -	"fmt"  	"path/filepath"  	"strings"  	"syscall"  ) -var zeroDirIV []byte - -func init() { -	zeroDirIV = make([]byte, 16) -} -  func (rfs *reverseFS) abs(relPath string, err error) (string, error) {  	if err != nil {  		return "", err @@ -21,45 +14,25 @@ func (rfs *reverseFS) abs(relPath string, err error) (string, error) {  	return filepath.Join(rfs.args.Cipherdir, relPath), nil  } -const ( -	ENCRYPT = iota -	DECRYPT -) - -func (rfs *reverseFS) encryptPath(relPath string) (string, error) { -	return rfs.transformPath(relPath, ENCRYPT) -} -  func (rfs *reverseFS) decryptPath(relPath string) (string, error) { -	return rfs.transformPath(relPath, DECRYPT) -} - -func (rfs *reverseFS) transformPath(relPath string, direction int) (string, error) {  	if rfs.args.PlaintextNames || relPath == "" {  		return relPath, nil  	}  	var err error  	var transformedParts []string  	parts := strings.Split(relPath, "/") -	for _, part := range parts { +	for i, part := range parts {  		var transformedPart string -		switch direction { -		case ENCRYPT: -			transformedPart = rfs.nameTransform.EncryptName(part, zeroDirIV) -		case DECRYPT: -			transformedPart, err = rfs.nameTransform.DecryptName(part, zeroDirIV) -			if err != nil { -				// We get lots of decrypt requests for names like ".Trash" that -				// are invalid base64. Convert them to ENOENT so the correct -				// error gets returned to the user. -				if _, ok := err.(base64.CorruptInputError); ok { -					fmt.Printf("converting to ENOENT\n") -					return "", syscall.ENOENT -				} -				return "", err +		dirIV := deriveDirIV(filepath.Join(parts[:i]...)) +		transformedPart, err = rfs.nameTransform.DecryptName(part, dirIV) +		if err != nil { +			// We get lots of decrypt requests for names like ".Trash" that +			// are invalid base64. Convert them to ENOENT so the correct +			// error gets returned to the user. +			if _, ok := err.(base64.CorruptInputError); ok { +				return "", syscall.ENOENT  			} -		default: -			panic("bug: invalid direction value") +			return "", err  		}  		transformedParts = append(transformedParts, transformedPart)  	} | 
