From fbccb160438aba6f1e16b26a982122c726afee1a Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Fri, 20 Aug 2021 17:06:18 +0200 Subject: -deterministic-names: implement for reverse mode, too --- internal/fusefrontend_reverse/ctlsock_interface.go | 3 +-- internal/fusefrontend_reverse/node_dir_ops.go | 19 ++++++-------- internal/fusefrontend_reverse/node_helpers.go | 7 +++++- internal/fusefrontend_reverse/rpath.go | 14 ++++++++++- internal/fusefrontend_reverse/virtualnode.go | 8 +++--- tests/cli/cli_test.go | 6 ----- tests/reverse/correctness_test.go | 8 +++--- tests/reverse/inomap_test.go | 6 +++-- tests/reverse/main_test.go | 23 +++++++++++++++-- tests/reverse/one_file_system_test.go | 29 ++++++++++++---------- 10 files changed, 77 insertions(+), 46 deletions(-) diff --git a/internal/fusefrontend_reverse/ctlsock_interface.go b/internal/fusefrontend_reverse/ctlsock_interface.go index 2157044..1cfdf3e 100644 --- a/internal/fusefrontend_reverse/ctlsock_interface.go +++ b/internal/fusefrontend_reverse/ctlsock_interface.go @@ -7,7 +7,6 @@ import ( "golang.org/x/sys/unix" "github.com/rfjakob/gocryptfs/internal/ctlsocksrv" - "github.com/rfjakob/gocryptfs/internal/pathiv" ) // Verify that the interface is implemented. @@ -22,7 +21,7 @@ func (rn *RootNode) EncryptPath(plainPath string) (string, error) { cipherPath := "" parts := strings.Split(plainPath, "/") for _, part := range parts { - dirIV := pathiv.Derive(cipherPath, pathiv.PurposeDirIV) + dirIV := rn.deriveDirIV(cipherPath) encryptedPart, err := rn.nameTransform.EncryptName(part, dirIV) if err != nil { return "", err diff --git a/internal/fusefrontend_reverse/node_dir_ops.go b/internal/fusefrontend_reverse/node_dir_ops.go index 21b9775..2592ebc 100644 --- a/internal/fusefrontend_reverse/node_dir_ops.go +++ b/internal/fusefrontend_reverse/node_dir_ops.go @@ -13,7 +13,6 @@ import ( "github.com/rfjakob/gocryptfs/internal/configfile" "github.com/rfjakob/gocryptfs/internal/cryptocore" "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/pathiv" "github.com/rfjakob/gocryptfs/internal/syscallcompat" "github.com/rfjakob/gocryptfs/internal/tlog" ) @@ -23,20 +22,16 @@ import ( // This function is symlink-safe through use of openBackingDir() and // ReadDirIVAt(). func (n *Node) Readdir(ctx context.Context) (stream fs.DirStream, errno syscall.Errno) { - // Virtual files: at least one gocryptfs.diriv file - virtualFiles := []fuse.DirEntry{ - {Mode: virtualFileMode, Name: nametransform.DirIVFilename}, - } rn := n.rootNode() + // Should we present a virtual gocryptfs.diriv? + var virtualFiles []fuse.DirEntry + if !rn.args.PlaintextNames && !rn.args.DeterministicNames { + virtualFiles = append(virtualFiles, fuse.DirEntry{Mode: virtualFileMode, Name: nametransform.DirIVFilename}) + } // This directory is a mountpoint. Present it as empty. if rn.args.OneFileSystem && n.isOtherFilesystem { - if rn.args.PlaintextNames { - return fs.NewListDirStream(nil), 0 - } else { - // An "empty" directory still has a gocryptfs.diriv file! - return fs.NewListDirStream(virtualFiles), 0 - } + return fs.NewListDirStream(virtualFiles), 0 } d, errno := n.prepareAtSyscall("") @@ -64,7 +59,7 @@ func (n *Node) Readdir(ctx context.Context) (stream fs.DirStream, errno syscall. return n.readdirPlaintextnames(entries) } - dirIV := pathiv.Derive(d.cPath, pathiv.PurposeDirIV) + dirIV := rn.deriveDirIV(d.cPath) // Encrypt names for i := range entries { var cName string diff --git a/internal/fusefrontend_reverse/node_helpers.go b/internal/fusefrontend_reverse/node_helpers.go index 7b286a0..b7dc086 100644 --- a/internal/fusefrontend_reverse/node_helpers.go +++ b/internal/fusefrontend_reverse/node_helpers.go @@ -2,6 +2,7 @@ package fusefrontend_reverse import ( "context" + "log" "path/filepath" "syscall" @@ -129,8 +130,8 @@ func (n *Node) lookupLongnameName(ctx context.Context, nameFile string, out *fus return } defer syscall.Close(fd) - diriv := pathiv.Derive(d.cPath, pathiv.PurposeDirIV) rn := n.rootNode() + diriv := rn.deriveDirIV(d.cPath) pName, cFullname, errno := rn.findLongnameParent(fd, diriv, nameFile) if errno != 0 { return @@ -160,6 +161,10 @@ func (n *Node) lookupLongnameName(ctx context.Context, nameFile string, out *fus // lookupDiriv returns a new Inode for a gocryptfs.diriv file inside `n`. func (n *Node) lookupDiriv(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) { + if rn := n.rootNode(); rn.args.DeterministicNames { + log.Panic("BUG: lookupDiriv called but DeterministicNames is set") + } + d, errno := n.prepareAtSyscall("") if errno != 0 { return diff --git a/internal/fusefrontend_reverse/rpath.go b/internal/fusefrontend_reverse/rpath.go index 199473b..7ebedd7 100644 --- a/internal/fusefrontend_reverse/rpath.go +++ b/internal/fusefrontend_reverse/rpath.go @@ -2,6 +2,7 @@ package fusefrontend_reverse import ( "encoding/base64" + "log" "path/filepath" "strings" "syscall" @@ -72,7 +73,7 @@ func (rn *RootNode) decryptPath(cPath string) (string, error) { // Start at the top and recurse currentCipherDir := filepath.Join(parts[:i]...) currentPlainDir := filepath.Join(transformedParts[:i]...) - dirIV := pathiv.Derive(currentCipherDir, pathiv.PurposeDirIV) + dirIV := rn.deriveDirIV(currentCipherDir) transformedPart, err := rn.rDecryptName(parts[i], dirIV, currentPlainDir) if err != nil { return "", err @@ -83,6 +84,17 @@ func (rn *RootNode) decryptPath(cPath string) (string, error) { return pRelPath, nil } +// deriveDirIV wraps pathiv.Derive but takes DeterministicNames into account. +func (rn *RootNode) deriveDirIV(cPath string) []byte { + if rn.args.PlaintextNames { + log.Panic("BUG: deriveDirIV called but PlaintextNames is set") + } + if rn.args.DeterministicNames { + return make([]byte, nametransform.DirIVLen) + } + return pathiv.Derive(cPath, pathiv.PurposeDirIV) +} + // openBackingDir receives an already decrypted relative path // "pRelPath", opens the directory that contains the target file/dir // and returns the fd to the directory and the decrypted name of the diff --git a/internal/fusefrontend_reverse/virtualnode.go b/internal/fusefrontend_reverse/virtualnode.go index 2ee9548..328f021 100644 --- a/internal/fusefrontend_reverse/virtualnode.go +++ b/internal/fusefrontend_reverse/virtualnode.go @@ -43,9 +43,11 @@ func (n *Node) lookupFileType(cName string) fileType { rn := n.rootNode() // In -plaintextname mode, neither diriv nor longname files exist. if !rn.args.PlaintextNames { - // Is it a gocryptfs.diriv file? - if cName == nametransform.DirIVFilename { - return typeDiriv + if !rn.args.DeterministicNames { + // Is it a gocryptfs.diriv file? + if cName == nametransform.DirIVFilename { + return typeDiriv + } } // Is it a gocryptfs.longname.*.name file? if t := nametransform.NameType(cName); t == nametransform.LongNameFilename { diff --git a/tests/cli/cli_test.go b/tests/cli/cli_test.go index f4162f8..85a8006 100644 --- a/tests/cli/cli_test.go +++ b/tests/cli/cli_test.go @@ -995,9 +995,3 @@ func TestMountCreat(t *testing.T) { test_helpers.UnmountPanic(mnt) } } - -// Test -init -deterministic-names -func TestInitDeterministicNames(t *testing.T) { - dir := test_helpers.InitFS(t, "-deterministic-names") - -} diff --git a/tests/reverse/correctness_test.go b/tests/reverse/correctness_test.go index 9d7a1c8..87d2f12 100644 --- a/tests/reverse/correctness_test.go +++ b/tests/reverse/correctness_test.go @@ -119,10 +119,10 @@ func TestConfigMapping(t *testing.T) { } } -// Check that the access() syscall works on virtual files -func TestAccessVirtual(t *testing.T) { - if plaintextnames { - t.Skip("test makes no sense for plaintextnames") +// Check that the access() syscall works on virtual gocryptfs.diriv files +func TestAccessVirtualDirIV(t *testing.T) { + if plaintextnames || deterministic_names { + t.Skip("test makes no sense for plaintextnames or deterministic_names") } var R_OK uint32 = 4 var W_OK uint32 = 2 diff --git a/tests/reverse/inomap_test.go b/tests/reverse/inomap_test.go index e6fc525..d5544c8 100644 --- a/tests/reverse/inomap_test.go +++ b/tests/reverse/inomap_test.go @@ -130,8 +130,10 @@ func TestVirtualFileIno(t *testing.T) { } // Lower 48 bits should come from the backing file const mask = 0xffffffffffff - if origInos.parent&mask != cipherInos.diriv&mask { - t.Errorf("diriv ino mismatch: %#x vs %#x", origInos.parent, cipherInos.diriv) + if !deterministic_names { // no diriv files with -deterministic-names + if origInos.parent&mask != cipherInos.diriv&mask { + t.Errorf("diriv ino mismatch: %#x vs %#x", origInos.parent, cipherInos.diriv) + } } if origInos.child != cipherInos.child { t.Errorf("child ino mismatch: %d vs %d", origInos.child, cipherInos.child) diff --git a/tests/reverse/main_test.go b/tests/reverse/main_test.go index 3425289..3b9e7d0 100644 --- a/tests/reverse/main_test.go +++ b/tests/reverse/main_test.go @@ -2,6 +2,7 @@ package reverse_test import ( "bytes" + "fmt" "os" "testing" @@ -9,8 +10,13 @@ import ( ) var x240 = string(bytes.Repeat([]byte("x"), 240)) + +// plaintextnames is true when the currently running test has -plaintextnames active var plaintextnames bool +// deterministic_names is true when the currently running test has -deterministic-names active +var deterministic_names bool + // dirA is a normal directory var dirA string @@ -24,10 +30,22 @@ var dirC string // to "dirC". func TestMain(m *testing.M) { var r int - for _, plaintextnames = range []bool{false, true} { + + testcases := []struct { + plaintextnames bool + deterministic_names bool + }{ + {false, false}, + {true, false}, + {false, true}, + } + for i, tc := range testcases { argsA := []string{"-reverse"} - if plaintextnames { + plaintextnames, deterministic_names = tc.plaintextnames, tc.deterministic_names + if tc.plaintextnames { argsA = append(argsA, "-plaintextnames") + } else if tc.deterministic_names { + argsA = append(argsA, "-deterministic-names") } dirA = test_helpers.InitFS(nil, argsA...) dirB = test_helpers.TmpDir + "/b" @@ -49,6 +67,7 @@ func TestMain(m *testing.M) { os.RemoveAll(dirC) if r != 0 { + fmt.Printf("testcases[%d] = %#v failed\n", i, tc) os.Exit(r) } } diff --git a/tests/reverse/one_file_system_test.go b/tests/reverse/one_file_system_test.go index 61fdb61..a3e441f 100644 --- a/tests/reverse/one_file_system_test.go +++ b/tests/reverse/one_file_system_test.go @@ -1,8 +1,9 @@ -package reverse +package reverse_test import ( "io/ioutil" "net/url" + "os" "runtime" "syscall" "testing" @@ -10,7 +11,10 @@ import ( "github.com/rfjakob/gocryptfs/tests/test_helpers" ) -func doTestOneFileSystem(t *testing.T, plaintextnames bool) { +func TestOneFileSystem(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("only works on linux") + } // Let's not explode with "TempDir: pattern contains path separator" myEscapedName := url.PathEscape(t.Name()) mnt, err := ioutil.TempDir(test_helpers.TmpDir, myEscapedName) @@ -20,6 +24,8 @@ func doTestOneFileSystem(t *testing.T, plaintextnames bool) { cliArgs := []string{"-reverse", "-zerokey", "-one-file-system"} if plaintextnames { cliArgs = append(cliArgs, "-plaintextnames") + } else if deterministic_names { + cliArgs = append(cliArgs, "-deterministic-names") } test_helpers.MountOrFatal(t, "/", mnt, cliArgs...) defer test_helpers.UnmountErr(mnt) @@ -48,25 +54,22 @@ func doTestOneFileSystem(t *testing.T, plaintextnames bool) { t.Skip("no mountpoints found, nothing to test") } for _, m := range mountpoints { - e, err := ioutil.ReadDir(mnt + "/" + m) + dir, err := os.Open(mnt + "/" + m) + if err != nil { + t.Error(err) + } + defer dir.Close() + e, err := dir.Readdirnames(-1) if err != nil { t.Error(err) } expected := 1 - if plaintextnames { + if plaintextnames || deterministic_names { expected = 0 } if len(e) != expected { - t.Errorf("mountpoint %q does not look empty: %v", m, e) + t.Errorf("mountpoint %q should have %d entries, actually has: %v", m, expected, e) } } t.Logf("tested %d mountpoints: %v", len(mountpoints), mountpoints) } - -func TestOneFileSystem(t *testing.T) { - if runtime.GOOS != "linux" { - t.Skip("only works on linux") - } - t.Run("normal", func(t *testing.T) { doTestOneFileSystem(t, false) }) - t.Run("plaintextnames", func(t *testing.T) { doTestOneFileSystem(t, true) }) -} -- cgit v1.2.3