Flag -zerodiriv to create all diriv as all zero byte files

This commit is contained in:
Jose M Perez 2021-08-12 22:48:34 +02:00 committed by Jakob Unterwurzacher
parent 02c91d73ce
commit 8f94083a21
9 changed files with 106 additions and 13 deletions

View File

@ -545,6 +545,11 @@ useful in regression testing.
Applies to: all actions. 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 Stop option parsing. Helpful when CIPHERDIR may start with a
dash "-". dash "-".

View File

@ -30,7 +30,7 @@ type argContainer struct {
plaintextnames, quiet, nosyslog, wpanic, plaintextnames, quiet, nosyslog, wpanic,
longnames, allow_other, reverse, aessiv, nonempty, raw64, longnames, allow_other, reverse, aessiv, nonempty, raw64,
noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info, 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 // Mount options with opposites
dev, nodev, suid, nosuid, exec, noexec, rw, ro, kernel_cache, acl bool dev, nodev, suid, nosuid, exec, noexec, rw, ro, kernel_cache, acl bool
masterkey, mountpoint, cipherdir, cpuprofile, 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.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.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.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 // Mount options with opposites
flagSet.BoolVar(&args.dev, "dev", false, "Allow device files") flagSet.BoolVar(&args.dev, "dev", false, "Allow device files")

View File

@ -103,7 +103,7 @@ func initDir(args *argContainer) {
// Open cipherdir (following symlinks) // Open cipherdir (following symlinks)
dirfd, err := syscall.Open(args.cipherdir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) dirfd, err := syscall.Open(args.cipherdir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
if err == nil { if err == nil {
err = nametransform.WriteDirIVAt(dirfd) err = nametransform.WriteDirIVAt(dirfd, !args.zerodiriv)
syscall.Close(dirfd) syscall.Close(dirfd)
} }
if err != nil { if err != nil {

View File

@ -53,4 +53,6 @@ type Args struct {
// like rsync's `--one-file-system` does. // like rsync's `--one-file-system` does.
// Only applicable to reverse mode. // Only applicable to reverse mode.
OneFileSystem bool OneFileSystem bool
// ZeroDirIV creates diriv files as all-zero files
ZeroDirIV bool
} }

View File

@ -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 // should be a handle to the parent directory, cName is the name of the new
// directory and mode specifies the access permissions to use. // directory and mode specifies the access permissions to use.
func (n *Node) mkdirWithIv(dirfd int, cName string, mode uint32, context *fuse.Context) error { func (n *Node) mkdirWithIv(dirfd int, cName string, mode uint32, context *fuse.Context) error {
rn := n.rootNode() rn := n.rootNode()
// Between the creation of the directory and the creation of gocryptfs.diriv // Between the creation of the directory and the creation of gocryptfs.diriv
// the directory is inconsistent. Take the lock to prevent other readers // 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) dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_DIRECTORY|syscall.O_NOFOLLOW|syscallcompat.O_PATH, 0)
if err == nil { if err == nil {
// Create gocryptfs.diriv // Create gocryptfs.diriv
err = nametransform.WriteDirIVAt(dirfd2) err = nametransform.WriteDirIVAt(dirfd2, !rn.args.ZeroDirIV)
syscall.Close(dirfd2) syscall.Close(dirfd2)
} }
if err != nil { if err != nil {

View File

@ -1,7 +1,6 @@
package nametransform package nametransform
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -34,9 +33,6 @@ func ReadDirIVAt(dirfd int) (iv []byte, err error) {
return fdReadDirIV(fd) 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. // fdReadDirIV reads and verifies the DirIV from an opened gocryptfs.diriv file.
func fdReadDirIV(fd *os.File) (iv []byte, err error) { func fdReadDirIV(fd *os.File) (iv []byte, err error) {
// We want to detect if the file is bigger than DirIVLen, so // 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 { if len(iv) != DirIVLen {
return nil, fmt.Errorf("wanted %d bytes, got %d", DirIVLen, len(iv)) 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 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. // "dirfd". On error we try to delete the incomplete file.
// This function is exported because it is used from fusefrontend, main, // This function is exported because it is used from fusefrontend, main,
// and also the automated tests. // and also the automated tests.
func WriteDirIVAt(dirfd int) error { func WriteDirIVAt(dirfd int, randomInitialization bool) error {
iv := cryptocore.RandBytes(DirIVLen) var iv []byte
if randomInitialization {
iv = cryptocore.RandBytes(DirIVLen)
} else {
iv = make([]byte, DirIVLen)
}
// 0400 permissions: gocryptfs.diriv should never be modified after creation. // 0400 permissions: gocryptfs.diriv should never be modified after creation.
// Don't use "ioutil.WriteFile", it causes trouble on NFS: // Don't use "ioutil.WriteFile", it causes trouble on NFS:
// https://github.com/rfjakob/gocryptfs/commit/7d38f80a78644c8ec4900cc990bfb894387112ed // https://github.com/rfjakob/gocryptfs/commit/7d38f80a78644c8ec4900cc990bfb894387112ed

View File

@ -276,6 +276,7 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
KernelCache: args.kernel_cache, KernelCache: args.kernel_cache,
SharedStorage: args.sharedstorage, SharedStorage: args.sharedstorage,
OneFileSystem: args.one_file_system, OneFileSystem: args.one_file_system,
ZeroDirIV: args.zerodiriv,
} }
// confFile is nil when "-zerokey" or "-masterkey" was used // confFile is nil when "-zerokey" or "-masterkey" was used
if confFile != nil { if confFile != nil {

View File

@ -110,7 +110,7 @@ func ResetTmpDir(createDirIV bool) {
// Open cipherdir (following symlinks) // Open cipherdir (following symlinks)
dirfd, err := syscall.Open(DefaultCipherDir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) dirfd, err := syscall.Open(DefaultCipherDir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
if err == nil { if err == nil {
err = nametransform.WriteDirIVAt(dirfd) err = nametransform.WriteDirIVAt(dirfd, true)
syscall.Close(dirfd) syscall.Close(dirfd)
} }
if err != nil { if err != nil {

View File

@ -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")
}
}