Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow deterministic diriv creation via cli flag #592

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Documentation/MANPAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 "-".
Expand Down
3 changes: 2 additions & 1 deletion cli_args.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion init_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions internal/fusefrontend/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
3 changes: 2 additions & 1 deletion internal/fusefrontend/node_dir_ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
16 changes: 7 additions & 9 deletions internal/nametransform/diriv.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package nametransform

import (
"bytes"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -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
Expand All @@ -50,18 +46,20 @@ 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
}

// WriteDirIVAt - create a new gocryptfs.diriv file in the directory opened at
// "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
Expand Down
1 change: 1 addition & 0 deletions mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion tests/test_helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
85 changes: 85 additions & 0 deletions tests/zerodiriv/zerodiriv_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}