summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2016-09-19 23:40:43 +0200
committerJakob Unterwurzacher2016-09-25 16:43:17 +0200
commitbe9dfe3a894bd00a2157bfc3dd19e98bcc171691 (patch)
treee8eb9a0858af41aaeb41b11ea379a074417fca03
parent10f38e88707f3a1f1ad69769219839a30a80c165 (diff)
reverse: implement dynamic diriv
Introduce a unique per-directory diriv that is generated by hashing the encrypted directory path.
-rw-r--r--internal/fusefrontend_reverse/diriv.go42
-rw-r--r--internal/fusefrontend_reverse/rfs.go113
-rw-r--r--internal/fusefrontend_reverse/rpath.go47
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)
}