From 8f94083a2114c3aef4bc0320065e0374c420ea4a Mon Sep 17 00:00:00 2001 From: Jose M Perez Date: Thu, 12 Aug 2021 22:48:34 +0200 Subject: [PATCH] Flag -zerodiriv to create all diriv as all zero byte files --- Documentation/MANPAGE.md | 5 ++ cli_args.go | 3 +- init_dir.go | 2 +- internal/fusefrontend/args.go | 2 + internal/fusefrontend/node_dir_ops.go | 3 +- internal/nametransform/diriv.go | 16 +++-- mount.go | 1 + tests/test_helpers/helpers.go | 2 +- tests/zerodiriv/zerodiriv_test.go | 85 +++++++++++++++++++++++++++ 9 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 tests/zerodiriv/zerodiriv_test.go diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md index 1547591..07f063e 100644 --- a/Documentation/MANPAGE.md +++ b/Documentation/MANPAGE.md @@ -545,6 +545,11 @@ useful in regression testing. Applies to: all actions. +#### -zerodiriv +Create diriv as all-zero files + +Applies to: all actions without `-plaintextnames`. + #### \-\- Stop option parsing. Helpful when CIPHERDIR may start with a dash "-". diff --git a/cli_args.go b/cli_args.go index 058f932..76e8d30 100644 --- a/cli_args.go +++ b/cli_args.go @@ -30,7 +30,7 @@ type argContainer struct { plaintextnames, quiet, nosyslog, wpanic, longnames, allow_other, reverse, aessiv, nonempty, raw64, noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info, - sharedstorage, devrandom, fsck, one_file_system bool + sharedstorage, devrandom, fsck, one_file_system, zerodiriv bool // Mount options with opposites dev, nodev, suid, nosuid, exec, noexec, rw, ro, kernel_cache, acl bool masterkey, mountpoint, cipherdir, cpuprofile, @@ -179,6 +179,7 @@ func parseCliOpts(osArgs []string) (args argContainer) { flagSet.BoolVar(&args.devrandom, "devrandom", false, "Use /dev/random for generating master key") flagSet.BoolVar(&args.fsck, "fsck", false, "Run a filesystem check on CIPHERDIR") flagSet.BoolVar(&args.one_file_system, "one-file-system", false, "Don't cross filesystem boundaries") + flagSet.BoolVar(&args.zerodiriv, "zerodiriv", false, "Create diriv as all-zero files") // Mount options with opposites flagSet.BoolVar(&args.dev, "dev", false, "Allow device files") diff --git a/init_dir.go b/init_dir.go index 54a44e4..ce5f98d 100644 --- a/init_dir.go +++ b/init_dir.go @@ -103,7 +103,7 @@ func initDir(args *argContainer) { // Open cipherdir (following symlinks) dirfd, err := syscall.Open(args.cipherdir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) if err == nil { - err = nametransform.WriteDirIVAt(dirfd) + err = nametransform.WriteDirIVAt(dirfd, !args.zerodiriv) syscall.Close(dirfd) } if err != nil { diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go index d92c3ff..02ffddb 100644 --- a/internal/fusefrontend/args.go +++ b/internal/fusefrontend/args.go @@ -53,4 +53,6 @@ type Args struct { // like rsync's `--one-file-system` does. // Only applicable to reverse mode. OneFileSystem bool + // ZeroDirIV creates diriv files as all-zero files + ZeroDirIV bool } diff --git a/internal/fusefrontend/node_dir_ops.go b/internal/fusefrontend/node_dir_ops.go index 6d03544..b43a4e4 100644 --- a/internal/fusefrontend/node_dir_ops.go +++ b/internal/fusefrontend/node_dir_ops.go @@ -35,6 +35,7 @@ func haveDsstore(entries []fuse.DirEntry) bool { // should be a handle to the parent directory, cName is the name of the new // directory and mode specifies the access permissions to use. func (n *Node) mkdirWithIv(dirfd int, cName string, mode uint32, context *fuse.Context) error { + rn := n.rootNode() // Between the creation of the directory and the creation of gocryptfs.diriv // the directory is inconsistent. Take the lock to prevent other readers @@ -48,7 +49,7 @@ func (n *Node) mkdirWithIv(dirfd int, cName string, mode uint32, context *fuse.C dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_DIRECTORY|syscall.O_NOFOLLOW|syscallcompat.O_PATH, 0) if err == nil { // Create gocryptfs.diriv - err = nametransform.WriteDirIVAt(dirfd2) + err = nametransform.WriteDirIVAt(dirfd2, !rn.args.ZeroDirIV) syscall.Close(dirfd2) } if err != nil { diff --git a/internal/nametransform/diriv.go b/internal/nametransform/diriv.go index b10c899..a288aa5 100644 --- a/internal/nametransform/diriv.go +++ b/internal/nametransform/diriv.go @@ -1,7 +1,6 @@ package nametransform import ( - "bytes" "fmt" "io" "os" @@ -34,9 +33,6 @@ func ReadDirIVAt(dirfd int) (iv []byte, err error) { return fdReadDirIV(fd) } -// allZeroDirIV is preallocated to quickly check if the data read from disk is all zero -var allZeroDirIV = make([]byte, DirIVLen) - // fdReadDirIV reads and verifies the DirIV from an opened gocryptfs.diriv file. func fdReadDirIV(fd *os.File) (iv []byte, err error) { // We want to detect if the file is bigger than DirIVLen, so @@ -50,9 +46,6 @@ func fdReadDirIV(fd *os.File) (iv []byte, err error) { if len(iv) != DirIVLen { return nil, fmt.Errorf("wanted %d bytes, got %d", DirIVLen, len(iv)) } - if bytes.Equal(iv, allZeroDirIV) { - return nil, fmt.Errorf("diriv is all-zero") - } return iv, nil } @@ -60,8 +53,13 @@ func fdReadDirIV(fd *os.File) (iv []byte, err error) { // "dirfd". On error we try to delete the incomplete file. // This function is exported because it is used from fusefrontend, main, // and also the automated tests. -func WriteDirIVAt(dirfd int) error { - iv := cryptocore.RandBytes(DirIVLen) +func WriteDirIVAt(dirfd int, randomInitialization bool) error { + var iv []byte + if randomInitialization { + iv = cryptocore.RandBytes(DirIVLen) + } else { + iv = make([]byte, DirIVLen) + } // 0400 permissions: gocryptfs.diriv should never be modified after creation. // Don't use "ioutil.WriteFile", it causes trouble on NFS: // https://github.com/rfjakob/gocryptfs/commit/7d38f80a78644c8ec4900cc990bfb894387112ed diff --git a/mount.go b/mount.go index 8cc038e..64b151b 100644 --- a/mount.go +++ b/mount.go @@ -276,6 +276,7 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f KernelCache: args.kernel_cache, SharedStorage: args.sharedstorage, OneFileSystem: args.one_file_system, + ZeroDirIV: args.zerodiriv, } // confFile is nil when "-zerokey" or "-masterkey" was used if confFile != nil { diff --git a/tests/test_helpers/helpers.go b/tests/test_helpers/helpers.go index 87dba0a..f78c59c 100644 --- a/tests/test_helpers/helpers.go +++ b/tests/test_helpers/helpers.go @@ -110,7 +110,7 @@ func ResetTmpDir(createDirIV bool) { // Open cipherdir (following symlinks) dirfd, err := syscall.Open(DefaultCipherDir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) if err == nil { - err = nametransform.WriteDirIVAt(dirfd) + err = nametransform.WriteDirIVAt(dirfd, true) syscall.Close(dirfd) } if err != nil { diff --git a/tests/zerodiriv/zerodiriv_test.go b/tests/zerodiriv/zerodiriv_test.go new file mode 100644 index 0000000..3fbbf47 --- /dev/null +++ b/tests/zerodiriv/zerodiriv_test.go @@ -0,0 +1,85 @@ +package zerodiriv + +// integration tests that target zerodiriv specifically + +import ( + "bytes" + "path/filepath" + "io/ioutil" + "os" + "testing" + + "github.com/rfjakob/gocryptfs/tests/test_helpers" +) + +var cDir string +var pDir string + +var testPw = []byte("test") + +// Create and mount "-zerodiriv" fs +func TestMain(m *testing.M) { + cDir = test_helpers.InitFS(nil, "-zerodiriv") + pDir = cDir + ".mnt" + test_helpers.MountOrExit(cDir, pDir, "-zerodiriv", "-extpass", "echo test") + r := m.Run() + test_helpers.UnmountPanic(pDir) + os.Exit(r) +} + +// diriv should be all-zero on newly created dirs +func TestZeroDirIV(t *testing.T) { + // Create /dir1, move it and create it again + var dirPath = pDir+"/dir1" + var err = os.Mkdir(dirPath, 0777) + if err != nil { + t.Error(err) + } + err = os.Rename(dirPath, dirPath + ".bak") + if err != nil { + t.Error(err) + } + err = os.Mkdir(dirPath, 0777) + if err != nil { + t.Error(err) + } + + var matches []string + matches, err = filepath.Glob(cDir+"/*/gocryptfs.diriv") + if err != nil { + t.Error(err) + } + + // The contents of the both diriv files must be the same + var diriv0 []byte + diriv0, err = ioutil.ReadFile(matches[0]) + if err != nil { + t.Error(err) + } + var diriv1 []byte + diriv1, err = ioutil.ReadFile(matches[1]) + if err != nil { + t.Error(err) + } + if !bytes.Equal(diriv0, diriv1) { + t.Errorf("both dirivs should have the same value") + } + // And equal to zero + zerodiriv := make([]byte, len(diriv0)) + if !bytes.Equal(diriv0, zerodiriv) { + t.Errorf("both dirivs should be all-zero") + } +} + +// root diriv should be all-zero +func TestZeroRootDirIV(t *testing.T) { + // The contents of the diriv file must be zero + diriv, err := ioutil.ReadFile(cDir+"/gocryptfs.diriv") + if err != nil { + t.Error(err) + } + zerodiriv := make([]byte, len(diriv)) + if !bytes.Equal(diriv, zerodiriv) { + t.Errorf("root diriv should be all-zero") + } +}