summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/fusefrontend/node_helpers.go10
-rw-r--r--internal/fusefrontend/root_node.go6
-rw-r--r--internal/nametransform/diriv.go50
-rw-r--r--internal/nametransform/names.go13
4 files changed, 75 insertions, 4 deletions
diff --git a/internal/fusefrontend/node_helpers.go b/internal/fusefrontend/node_helpers.go
index b2f1d4a..f2d1e5e 100644
--- a/internal/fusefrontend/node_helpers.go
+++ b/internal/fusefrontend/node_helpers.go
@@ -121,7 +121,15 @@ func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno sy
var iv []byte
dirfd, iv = rn.dirCache.Lookup(n)
if dirfd > 0 {
- cName, err := rn.nameTransform.EncryptAndHashName(child, iv)
+ var cName string
+ var err error
+ if rn.nameTransform.HaveBadnamePatterns() {
+ //BadName allowed, try to determine filenames
+ cName, err = rn.nameTransform.EncryptAndHashBadName(child, iv, dirfd)
+ } else {
+ cName, err = rn.nameTransform.EncryptAndHashName(child, iv)
+ }
+
if err != nil {
return -1, "", fs.ToErrno(err)
}
diff --git a/internal/fusefrontend/root_node.go b/internal/fusefrontend/root_node.go
index a830cc4..35b7be0 100644
--- a/internal/fusefrontend/root_node.go
+++ b/internal/fusefrontend/root_node.go
@@ -245,7 +245,11 @@ func (rn *RootNode) openBackingDir(relPath string) (dirfd int, cName string, err
syscall.Close(dirfd)
return -1, "", err
}
- cName, err = rn.nameTransform.EncryptAndHashName(name, iv)
+ if rn.nameTransform.HaveBadnamePatterns() {
+ cName, err = rn.nameTransform.EncryptAndHashBadName(name, iv, dirfd)
+ } else {
+ cName, err = rn.nameTransform.EncryptAndHashName(name, iv)
+ }
if err != nil {
syscall.Close(dirfd)
return -1, "", err
diff --git a/internal/nametransform/diriv.go b/internal/nametransform/diriv.go
index 1d27aa5..d62b3fb 100644
--- a/internal/nametransform/diriv.go
+++ b/internal/nametransform/diriv.go
@@ -6,11 +6,13 @@ import (
"io"
"os"
"path/filepath"
+ "strings"
"syscall"
"github.com/rfjakob/gocryptfs/internal/cryptocore"
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
"github.com/rfjakob/gocryptfs/internal/tlog"
+ "golang.org/x/sys/unix"
)
const (
@@ -112,6 +114,54 @@ func (be *NameTransform) EncryptAndHashName(name string, iv []byte) (string, err
return cName, nil
}
+// EncryptAndHashBadName tries to find the "name" substring, which (encrypted and hashed)
+// leads to an unique existing file
+// Returns ENOENT if cipher file does not exist or is not unique
+func (be *NameTransform) EncryptAndHashBadName(name string, iv []byte, dirfd int) (cName string, err error) {
+ var st unix.Stat_t
+ var filesFound int
+ lastFoundName, err := be.EncryptAndHashName(name, iv)
+ if !strings.HasSuffix(name, BadNameFlag) || err != nil {
+ //Default mode: same behaviour on error or no BadNameFlag on "name"
+ return lastFoundName, err
+ }
+ //Default mode: Check if File extists without modifications
+ err = syscallcompat.Fstatat(dirfd, lastFoundName, &st, unix.AT_SYMLINK_NOFOLLOW)
+ if err == nil {
+ //file found, return result
+ return lastFoundName, nil
+ }
+ //BadName Mode: check if the name was tranformed without change (badname suffix and undecryptable cipher name)
+ err = syscallcompat.Fstatat(dirfd, name[:len(name)-len(BadNameFlag)], &st, unix.AT_SYMLINK_NOFOLLOW)
+ if err == nil {
+ filesFound++
+ lastFoundName = name[:len(name)-len(BadNameFlag)]
+ }
+ // search for the longest badname pattern match
+ for charpos := len(name) - len(BadNameFlag); charpos > 0; charpos-- {
+ //only use original cipher name and append assumed suffix (without badname flag)
+ cNamePart, err := be.EncryptName(name[:charpos], iv)
+ if err != nil {
+ //expand suffix on error
+ continue
+ }
+ if be.longNames && len(cName) > NameMax {
+ cNamePart = be.HashLongName(cName)
+ }
+ cNameBadReverse := cNamePart + name[charpos:len(name)-len(BadNameFlag)]
+ err = syscallcompat.Fstatat(dirfd, cNameBadReverse, &st, unix.AT_SYMLINK_NOFOLLOW)
+ if err == nil {
+ filesFound++
+ lastFoundName = cNameBadReverse
+ }
+ }
+ if filesFound == 1 {
+ return lastFoundName, nil
+ }
+ // more than 1 possible file found, ignore
+ return "", syscall.ENOENT
+}
+
// 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 ca28230..f730184 100644
--- a/internal/nametransform/names.go
+++ b/internal/nametransform/names.go
@@ -15,6 +15,8 @@ import (
const (
// Like ext4, we allow at most 255 bytes for a file name.
NameMax = 255
+ //BadNameFlag is appended to filenames in plain mode if a ciphername is inavlid but is shown
+ BadNameFlag = " GOCRYPTFS_BAD_NAME"
)
// NameTransformer is an interface used to transform filenames.
@@ -22,11 +24,13 @@ type NameTransformer interface {
DecryptName(cipherName string, iv []byte) (string, error)
EncryptName(plainName string, iv []byte) (string, error)
EncryptAndHashName(name string, iv []byte) (string, error)
+ EncryptAndHashBadName(name string, iv []byte, dirfd int) (string, error)
// HashLongName - take the hash of a long string "name" and return
// "gocryptfs.longname.[sha256]"
//
// This function does not do any I/O.
HashLongName(name string) string
+ HaveBadnamePatterns() bool
WriteLongNameAt(dirfd int, hashName string, plainName string) error
B64EncodeToString(src []byte) string
B64DecodeString(s string) ([]byte, error)
@@ -70,10 +74,10 @@ func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error
for charpos := len(cipherName) - 1; charpos >= nameMin; charpos-- {
res, err = n.decryptName(cipherName[:charpos], iv)
if err == nil {
- return res + cipherName[charpos:] + " GOCRYPTFS_BAD_NAME", nil
+ return res + cipherName[charpos:] + BadNameFlag, nil
}
}
- return cipherName + " GOCRYPTFS_BAD_NAME", nil
+ return cipherName + BadNameFlag, nil
}
}
}
@@ -135,3 +139,8 @@ func (n *NameTransform) B64EncodeToString(src []byte) string {
func (n *NameTransform) B64DecodeString(s string) ([]byte, error) {
return n.B64.DecodeString(s)
}
+
+// HaveBadnamePatterns returns true if BadName patterns were provided
+func (n *NameTransform) HaveBadnamePatterns() bool {
+ return len(n.BadnamePatterns) > 0
+}