From de56fe9e3503d98e359551072633c804794b94e1 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Tue, 3 Nov 2015 00:00:13 +0100 Subject: [PATCH] Implement PlainTextNames mode Also, forbid access to "gocryptfs.conf" in the root dir. --- cryptfs/config_file.go | 4 +-- cryptfs/cryptfs.go | 4 ++- cryptfs/cryptfs_names.go | 10 ++++-- main.go | 18 +++++++---- pathfs_frontend/fs.go | 68 ++++++++++++++++++++++++++++++++++------ 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/cryptfs/config_file.go b/cryptfs/config_file.go index de3054a..a790c51 100644 --- a/cryptfs/config_file.go +++ b/cryptfs/config_file.go @@ -76,7 +76,7 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) { // Unlock master key using password-based key // We use stock go GCM instead of OpenSSL here as speed is not important // and we get better error messages - cfs := NewCryptFS(scryptHash, false) + cfs := NewCryptFS(scryptHash, false, false) key, err := cfs.DecryptBlock(cf.EncryptedKey, 0, nil) if err != nil { Warn.Printf("failed to unlock master key: %s\n", err.Error()) @@ -95,7 +95,7 @@ func (cf *ConfFile) EncryptKey(key []byte, password string) { scryptHash := cf.ScryptObject.DeriveKey(password) // Lock master key using password-based key - cfs := NewCryptFS(scryptHash, false) + cfs := NewCryptFS(scryptHash, false, false) cf.EncryptedKey = cfs.EncryptBlock(key, 0, nil) } diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go index 9fe492d..ed19b26 100644 --- a/cryptfs/cryptfs.go +++ b/cryptfs/cryptfs.go @@ -23,9 +23,10 @@ type CryptFS struct { cipherBS uint64 // Stores an all-zero block of size cipherBS allZeroBlock []byte + plaintextNames bool } -func NewCryptFS(key []byte, useOpenssl bool) *CryptFS { +func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool) *CryptFS { if len(key) != KEY_LEN { panic(fmt.Sprintf("Unsupported key length %d", len(key))) @@ -54,6 +55,7 @@ func NewCryptFS(key []byte, useOpenssl bool) *CryptFS { plainBS: DEFAULT_PLAINBS, cipherBS: uint64(cipherBS), allZeroBlock: make([]byte, cipherBS), + plaintextNames: plaintextNames, } } diff --git a/cryptfs/cryptfs_names.go b/cryptfs/cryptfs_names.go index 5476b17..a7a9a8c 100644 --- a/cryptfs/cryptfs_names.go +++ b/cryptfs/cryptfs_names.go @@ -102,14 +102,20 @@ func (be *CryptFS) translatePath(path string, op bool) (string, error) { return strings.Join(translatedParts, "/"), err } -// EncryptPath - encrypt filename or path. Just hands it to TranslatePath(). +// EncryptPath - encrypt filename or path. Just hands it to translatePath(). func (be *CryptFS) EncryptPath(path string) string { + if be.plaintextNames { + return path + } newPath, _ := be.translatePath(path, ENCRYPT) return newPath } -// DecryptPath - decrypt filename or path. Just hands it to TranslatePath(). +// DecryptPath - decrypt filename or path. Just hands it to translatePath(). func (be *CryptFS) DecryptPath(path string) (string, error) { + if be.plaintextNames { + return path, nil + } return be.translatePath(path, DECRYPT) } diff --git a/main.go b/main.go index cf82ad2..3d9bed7 100644 --- a/main.go +++ b/main.go @@ -41,7 +41,7 @@ func initDir(dirArg string, plaintextNames bool) { err := checkDirEmpty(dir) if err != nil { - fmt.Printf("Error: \"%s\": %v\n", dirArg, err) + fmt.Printf("Invalid CIPHERDIR: %v\n", err) os.Exit(ERREXIT_INIT) } @@ -114,8 +114,8 @@ func main() { fmt.Printf("Openssl disabled\n") } if args.init { - if flag.NArg() != 1 { - fmt.Printf("Usage: %s --init CIPHERDIR\n", PROGRAM_NAME) + if flag.NArg() != 1 && args.plaintextnames == false { + fmt.Printf("Usage: %s --init [--plaintextnames] CIPHERDIR\n", PROGRAM_NAME) os.Exit(ERREXIT_USAGE) } initDir(flag.Arg(0), args.plaintextnames) // does not return @@ -154,6 +154,7 @@ func main() { key = parseMasterKey(args.masterkey) fmt.Printf("Using explicit master key.\n") } else { + // Load config file cfname := filepath.Join(args.cipherdir, cryptfs.ConfDefaultName) _, err = os.Stat(cfname) if err != nil { @@ -194,7 +195,11 @@ func main() { os.Exit(0) } - srv := pathfsFrontend(key, args.cipherdir, args.mountpoint, args.fusedebug, args.openssl) + var plaintextNames bool + if cf != nil { + plaintextNames = cf.PlaintextNames + } + srv := pathfsFrontend(key, args.cipherdir, args.mountpoint, args.fusedebug, args.openssl, plaintextNames) if args.zerokey == false && len(args.masterkey) == 0 { printMasterKey(key) @@ -215,9 +220,10 @@ func main() { // main returns with code 0 } -func pathfsFrontend(key []byte, cipherdir string, mountpoint string, debug bool, openssl bool) *fuse.Server { +func pathfsFrontend(key []byte, cipherdir string, mountpoint string, + debug bool, openssl bool, plaintextNames bool) *fuse.Server { - finalFs := pathfs_frontend.NewFS(key, cipherdir, openssl) + finalFs := pathfs_frontend.NewFS(key, cipherdir, openssl, plaintextNames) pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true} pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts) fuseOpts := &nodefs.Options{ diff --git a/pathfs_frontend/fs.go b/pathfs_frontend/fs.go index 3ec503f..1ef19d2 100644 --- a/pathfs_frontend/fs.go +++ b/pathfs_frontend/fs.go @@ -19,9 +19,9 @@ type FS struct { } // Encrypted FUSE overlay filesystem -func NewFS(key []byte, backing string, useOpenssl bool) *FS { +func NewFS(key []byte, backing string, useOpenssl bool, plaintextNames bool) *FS { return &FS{ - CryptFS: cryptfs.NewCryptFS(key, useOpenssl), + CryptFS: cryptfs.NewCryptFS(key, useOpenssl, plaintextNames), FileSystem: pathfs.NewLoopbackFileSystem(backing), backing: backing, } @@ -34,6 +34,9 @@ func (fs *FS) GetPath(relPath string) string { func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { cryptfs.Debug.Printf("FS.GetAttr('%s')\n", name) + if name == cryptfs.ConfDefaultName { + return nil, fuse.EPERM // See comment in Open() + } cName := fs.EncryptPath(name) a, status := fs.FileSystem.GetAttr(cName, context) if a == nil { @@ -56,12 +59,12 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f if cipherEntries != nil { for i := range cipherEntries { cName := cipherEntries[i].Name + if dirName == "" && cName == cryptfs.ConfDefaultName { + // ignore "gocryptfs.conf" in the top level dir + continue + } name, err := fs.DecryptPath(cName) if err != nil { - if dirName == "" && cName == cryptfs.ConfDefaultName { - // Silently ignore "gocryptfs.conf" in the top level dir - continue - } fmt.Printf("Invalid name \"%s\" in dir \"%s\": %s\n", cName, name, err) continue } @@ -85,11 +88,18 @@ func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int, writeOnly bool) { return newFlags, writeOnly } -func (fs *FS) Open(name string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) { - cryptfs.Debug.Printf("Open(%s)\n", name) +func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) { + cryptfs.Debug.Printf("Open(%s)\n", path) + + if path == cryptfs.ConfDefaultName { + // "gocryptfs.conf" in the top level dir is forbidden + // to protect the config file of this filesystem if + // "--plaintextnames" is enabled + return nil, fuse.EPERM + } iflags, writeOnly := fs.mangleOpenFlags(flags) - f, err := os.OpenFile(fs.GetPath(name), iflags, 0666) + f, err := os.OpenFile(fs.GetPath(path), iflags, 0666) if err != nil { return nil, fuse.ToStatus(err) } @@ -98,6 +108,10 @@ func (fs *FS) Open(name string, flags uint32, context *fuse.Context) (fuseFile n } func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) { + if path == cryptfs.ConfDefaultName { + return nil, fuse.EPERM // See comment in Open() + } + iflags, writeOnly := fs.mangleOpenFlags(flags) f, err := os.OpenFile(fs.GetPath(path), iflags|os.O_CREATE, os.FileMode(mode)) if err != nil { @@ -107,14 +121,26 @@ func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Conte } func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) { + if path == cryptfs.ConfDefaultName { + return fuse.EPERM // See comment in Open() + } + return fs.FileSystem.Chmod(fs.EncryptPath(path), mode, context) } func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) { + if path == cryptfs.ConfDefaultName { + return fuse.EPERM // See comment in Open() + } + return fs.FileSystem.Chown(fs.EncryptPath(path), uid, gid, context) } func (fs *FS) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) { + if name == cryptfs.ConfDefaultName { + return fuse.EPERM // See comment in Open() + } + return fs.FileSystem.Mknod(fs.EncryptPath(name), mode, dev, context) } @@ -124,6 +150,10 @@ func (fs *FS) Truncate(path string, offset uint64, context *fuse.Context) (code } func (fs *FS) Utimens(path string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) (code fuse.Status) { + if path == cryptfs.ConfDefaultName { + return fuse.EPERM // See comment in Open() + } + return fs.FileSystem.Utimens(fs.EncryptPath(path), Atime, Mtime, context) } @@ -141,10 +171,18 @@ func (fs *FS) Readlink(name string, context *fuse.Context) (out string, status f } func (fs *FS) Mkdir(path string, mode uint32, context *fuse.Context) (code fuse.Status) { + if path == cryptfs.ConfDefaultName { + return fuse.EPERM // See comment in Open() + } + return fs.FileSystem.Mkdir(fs.EncryptPath(path), mode, context) } func (fs *FS) Unlink(name string, context *fuse.Context) (code fuse.Status) { + if name == cryptfs.ConfDefaultName { + return fuse.EPERM // See comment in Open() + } + cName := fs.EncryptPath(name) code = fs.FileSystem.Unlink(cName, context) if code != fuse.OK { @@ -158,16 +196,28 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) { } func (fs *FS) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) { + if linkName == cryptfs.ConfDefaultName { + return fuse.EPERM // See comment in Open() + } + // TODO symlink encryption cryptfs.Debug.Printf("Symlink(\"%s\", \"%s\")\n", pointedTo, linkName) return fs.FileSystem.Symlink(fs.EncryptPath(pointedTo), fs.EncryptPath(linkName), context) } func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (codee fuse.Status) { + if newPath == cryptfs.ConfDefaultName { + return fuse.EPERM // See comment in Open() + } + return fs.FileSystem.Rename(fs.EncryptPath(oldPath), fs.EncryptPath(newPath), context) } func (fs *FS) Link(orig string, newName string, context *fuse.Context) (code fuse.Status) { + if newName == cryptfs.ConfDefaultName { + return fuse.EPERM // See comment in Open() + } + return fs.FileSystem.Link(fs.EncryptPath(orig), fs.EncryptPath(newName), context) }