diff options
| -rw-r--r-- | internal/fusefrontend/ctlsock_interface.go | 35 | ||||
| -rw-r--r-- | tests/defaults/ctlsock_test.go | 98 | ||||
| -rw-r--r-- | tests/defaults/main_test.go | 32 | ||||
| -rw-r--r-- | tests/test_helpers/helpers.go | 6 | 
4 files changed, 137 insertions, 34 deletions
| diff --git a/internal/fusefrontend/ctlsock_interface.go b/internal/fusefrontend/ctlsock_interface.go index 8d8b096..964775b 100644 --- a/internal/fusefrontend/ctlsock_interface.go +++ b/internal/fusefrontend/ctlsock_interface.go @@ -1,9 +1,12 @@  package fusefrontend  import ( -	"errors" +	"fmt" +	"path" +	"strings"  	"github.com/rfjakob/gocryptfs/internal/ctlsock" +	"github.com/rfjakob/gocryptfs/internal/nametransform"  )  var _ ctlsock.Interface = &FS{} // Verify that interface is implemented. @@ -15,5 +18,33 @@ func (fs *FS) EncryptPath(plainPath string) (string, error) {  // DecryptPath implements ctlsock.Backend  func (fs *FS) DecryptPath(cipherPath string) (string, error) { -	return "", errors.New("Forward mode does not have path decryption implemented") +	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) +		if err != nil { +			fmt.Printf("ReadDirIV: %v\n", err) +			return "", err +		} +		longPart := part +		if nametransform.IsLongContent(part) { +			longPart, err = nametransform.ReadLongName(wd + "/" + part) +			if err != nil { +				fmt.Printf("ReadLongName: %v\n", err) +				return "", err +			} +		} +		name, err := fs.nameTransform.DecryptName(longPart, dirIV) +		if err != nil { +			fmt.Printf("DecryptName: %v\n", err) +			return "", err +		} +		plainPath = path.Join(plainPath, name) +		wd = path.Join(wd, part) +	} +	return plainPath, nil  } diff --git a/tests/defaults/ctlsock_test.go b/tests/defaults/ctlsock_test.go new file mode 100644 index 0000000..13e6912 --- /dev/null +++ b/tests/defaults/ctlsock_test.go @@ -0,0 +1,98 @@ +package defaults + +import ( +	"os" +	"syscall" +	"testing" + +	"github.com/rfjakob/gocryptfs/internal/ctlsock" +	"github.com/rfjakob/gocryptfs/tests/test_helpers" +) + +func TestCtlSock(t *testing.T) { +	cDir := test_helpers.InitFS(t) +	pDir := cDir + ".mnt" +	sock := cDir + ".sock" +	test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test") +	defer test_helpers.UnmountPanic(pDir) +	req := ctlsock.RequestStruct{ +		EncryptPath: "foobar", +	} +	response := test_helpers.QueryCtlSock(t, sock, req) +	if response.Result == "" || response.ErrNo != 0 { +		t.Errorf("got an error reply: %+v", response) +	} +	req.EncryptPath = "not-existing-dir/xyz" +	response = test_helpers.QueryCtlSock(t, sock, req) +	if response.ErrNo != int32(syscall.ENOENT) || response.Result != "" { +		t.Errorf("incorrect error handling: %+v", response) +	} +	// Strange paths should not cause a crash +	crashers := []string{"/foo", "foo/", "/foo/", ".", "/////", "/../../."} +	for _, c := range crashers { +		req.EncryptPath = c +		// QueryCtlSock calls t.Fatal if it gets EOF when gocryptfs panics +		response = test_helpers.QueryCtlSock(t, sock, req) +		if response.WarnText == "" { +			t.Errorf("We should get a warning about non-canonical paths here") +		} +	} +} + +func TestCtlSockDecrypt(t *testing.T) { +	cDir := test_helpers.InitFS(t) +	pDir := cDir + ".mnt" +	sock := cDir + ".sock" +	test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test") +	defer test_helpers.UnmountPanic(pDir) + +	paths := []string{ +		"xxxxxxx123456789", +		"foo/bar/baz", +		test_helpers.X255, +		"123/" + test_helpers.X255, +		"123/" + test_helpers.X255 + "/456", +	} + +	for _, p := range paths { +		// Create path +		err := os.MkdirAll(pDir+"/"+p, 0700) +		if err != nil { +			t.Fatal(err) +		} +		// Encrypt the path through the ctlsock +		req := ctlsock.RequestStruct{ +			EncryptPath: p, +		} +		response := test_helpers.QueryCtlSock(t, sock, req) +		if response.Result == "" || response.ErrNo != 0 { +			t.Fatalf("got an error reply: %+v", response) +		} +		// Check if the encrypted path actually exists +		cPath := response.Result +		_, err = os.Stat(cDir + "/" + cPath) +		if err != nil { +			t.Fatal(err) +		} +		// Decrypt the path through the ctlsock and see if we get the original path +		req = ctlsock.RequestStruct{ +			DecryptPath: cPath, +		} +		response = test_helpers.QueryCtlSock(t, sock, req) +		if response.Result == "" || response.ErrNo != 0 { +			t.Errorf("query=%+v, response=%+v", req, response) +			continue +		} +		if response.Result != p { +			t.Errorf("want=%q got=%q", p, response.Result) +		} +	} +} + +func TestCtlSockDecryptCrash(t *testing.T) { +	cDir := test_helpers.InitFS(t) +	pDir := cDir + ".mnt" +	sock := cDir + ".sock" +	test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test") +	defer test_helpers.UnmountPanic(pDir) +} diff --git a/tests/defaults/main_test.go b/tests/defaults/main_test.go index 2ea9e12..ca11b43 100644 --- a/tests/defaults/main_test.go +++ b/tests/defaults/main_test.go @@ -8,10 +8,8 @@ import (  	"os/exec"  	"runtime"  	"sync" -	"syscall"  	"testing" -	"github.com/rfjakob/gocryptfs/internal/ctlsock"  	"github.com/rfjakob/gocryptfs/tests/test_helpers"  ) @@ -42,36 +40,6 @@ func Test1980Tar(t *testing.T) {  	}  } -func TestCtlSock(t *testing.T) { -	cDir := test_helpers.InitFS(t) -	pDir := cDir + ".mnt" -	sock := cDir + ".sock" -	test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test") -	defer test_helpers.UnmountPanic(pDir) -	req := ctlsock.RequestStruct{ -		EncryptPath: "foobar", -	} -	response := test_helpers.QueryCtlSock(t, sock, req) -	if response.Result == "" || response.ErrNo != 0 { -		t.Errorf("got an error reply: %+v", response) -	} -	req.EncryptPath = "not-existing-dir/xyz" -	response = test_helpers.QueryCtlSock(t, sock, req) -	if response.ErrNo != int32(syscall.ENOENT) || response.Result != "" { -		t.Errorf("incorrect error handling: %+v", response) -	} -	// Strange paths should not cause a crash -	crashers := []string{"/foo", "foo/", "/foo/", ".", "/////", "/../../."} -	for _, c := range crashers { -		req.EncryptPath = c -		// QueryCtlSock calls t.Fatal if it gets EOF when gocryptfs panics -		response = test_helpers.QueryCtlSock(t, sock, req) -		if response.WarnText == "" { -			t.Errorf("We should get a warning about non-canonical paths here") -		} -	} -} -  // In gocryptfs before v1.2, the file header was only read once for each  // open. But truncating a file to zero will generate a new random file ID.  // The sequence below caused an I/O error to be returned. diff --git a/tests/test_helpers/helpers.go b/tests/test_helpers/helpers.go index 299067a..f681482 100644 --- a/tests/test_helpers/helpers.go +++ b/tests/test_helpers/helpers.go @@ -1,6 +1,7 @@  package test_helpers  import ( +	"bytes"  	"crypto/md5"  	"encoding/hex"  	"encoding/json" @@ -27,6 +28,9 @@ const GocryptfsBinary = "../../gocryptfs"  // UnmountScript is the fusermount/umount compatibility wrapper script  const UnmountScript = "../fuse-unmount.bash" +// X255 contains 255 uppercase "X". This can be used as a maximum-length filename. +var X255 string +  // TmpDir is a unique temporary directory. "go test" runs package tests in parallel. We create a  // unique TmpDir in init() so the tests do not interfere.  var TmpDir string @@ -38,6 +42,8 @@ var DefaultPlainDir string  var DefaultCipherDir string  func init() { +	X255 = string(bytes.Repeat([]byte("X"), 255)) +  	os.MkdirAll(testParentDir, 0700)  	var err error  	TmpDir, err = ioutil.TempDir(testParentDir, "") | 
