diff options
| author | Jakob Unterwurzacher | 2015-12-08 16:13:29 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2015-12-08 16:17:04 +0100 | 
| commit | c6dacd6f913b4c6eb7a8917af49190dce32db108 (patch) | |
| tree | c0fd9a08f42c37bd977b95d2bb0a7c96226045c1 | |
| parent | ff8c81f95b311eb1cd9c822202519f1a90a8cdd4 (diff) | |
Add EME filename encryption & enable it by default
| -rw-r--r-- | Documentation/MANPAGE.md | 3 | ||||
| -rw-r--r-- | cryptfs/config_file.go | 20 | ||||
| -rw-r--r-- | cryptfs/config_test.go | 2 | ||||
| -rw-r--r-- | cryptfs/cryptfs.go | 2 | ||||
| -rw-r--r-- | cryptfs/filter.go | 15 | ||||
| -rw-r--r-- | cryptfs/log.go | 13 | ||||
| -rw-r--r-- | cryptfs/names_core.go (renamed from cryptfs/cryptfs_names.go) | 80 | ||||
| -rw-r--r-- | cryptfs/names_diriv.go | 31 | ||||
| -rw-r--r-- | cryptfs/names_noiv.go | 63 | ||||
| -rw-r--r-- | cryptfs/names_test.go | 11 | ||||
| -rw-r--r-- | integration_tests/example_filesystems_test.go | 4 | ||||
| -rw-r--r-- | main.go | 20 | ||||
| -rw-r--r-- | pathfs_frontend/args.go | 1 | ||||
| -rw-r--r-- | pathfs_frontend/fs.go | 28 | ||||
| -rw-r--r-- | pathfs_frontend/names.go | 35 | 
15 files changed, 204 insertions, 124 deletions
| diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md index 481e6e3..446ce37 100644 --- a/Documentation/MANPAGE.md +++ b/Documentation/MANPAGE.md @@ -42,6 +42,9 @@ Options:  **-diriv**  :	Use per-directory file name IV (default true) +**-emenames** +:	Use EME filename encryption (default true). This option implies diriv. +  **-extpass string**  :	Use an external program (like ssh-askpass) for the password prompt.  The program should return the password on stdout, a trailing newline is diff --git a/cryptfs/config_file.go b/cryptfs/config_file.go index be37c60..bf1f2a0 100644 --- a/cryptfs/config_file.go +++ b/cryptfs/config_file.go @@ -12,10 +12,6 @@ const (  	// The dot "." is not used in base64url (RFC4648), hence  	// we can never clash with an encrypted file.  	ConfDefaultName = "gocryptfs.conf" -	// Understood Feature Flags -	// Also teach isFeatureFlagKnown() about any additions -	FlagPlaintextNames = "PlaintextNames" -	FlagDirIV          = "DirIV"  )  type ConfFile struct { @@ -37,7 +33,7 @@ type ConfFile struct {  // CreateConfFile - create a new config with a random key encrypted with  // "password" and write it to "filename".  // Uses scrypt with cost parameter logN. -func CreateConfFile(filename string, password string, plaintextNames bool, logN int) error { +func CreateConfFile(filename string, password string, plaintextNames bool, logN int, EMENames bool) error {  	var cf ConfFile  	cf.filename = filename @@ -50,11 +46,13 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN  	// Set defaults  	cf.Version = HEADER_CURRENT_VERSION -	cf.FeatureFlags = []string{FlagDirIV}  	// Set values chosen by the user  	if plaintextNames {  		cf.FeatureFlags = append(cf.FeatureFlags, FlagPlaintextNames) +	} else { +		cf.FeatureFlags = append(cf.FeatureFlags, FlagDirIV) +		cf.FeatureFlags = append(cf.FeatureFlags, FlagEMENames)  	}  	// Write file to disk @@ -157,10 +155,18 @@ func (cf *ConfFile) WriteFile() error {  	return nil  } +const ( +	// Understood Feature Flags. +	// Also teach isFeatureFlagKnown() about any additions +	FlagPlaintextNames = "PlaintextNames" +	FlagDirIV          = "DirIV" +	FlagEMENames       = "EMENames" +) +  // Verify that we understand a feature flag  func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {  	switch flag { -	case FlagPlaintextNames, FlagDirIV: +	case FlagPlaintextNames, FlagDirIV, FlagEMENames:  		return true  	default:  		return false diff --git a/cryptfs/config_test.go b/cryptfs/config_test.go index 1c5a375..01c0d71 100644 --- a/cryptfs/config_test.go +++ b/cryptfs/config_test.go @@ -59,7 +59,7 @@ func TestLoadV2StrangeFeature(t *testing.T) {  }  func TestCreateConfFile(t *testing.T) { -	err := CreateConfFile("config_test/tmp.conf", "test", false, 0) +	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, true)  	if err != nil {  		t.Fatal(err)  	} diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go index df04973..5832e36 100644 --- a/cryptfs/cryptfs.go +++ b/cryptfs/cryptfs.go @@ -25,7 +25,6 @@ type CryptFS struct {  	cipherBS    uint64  	// Stores an all-zero block of size cipherBS  	allZeroBlock   []byte -	plaintextNames bool  	// DirIV cache for filename encryption  	DirIVCacheEnc DirIVCache  } @@ -59,7 +58,6 @@ func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool) *CryptFS {  		plainBS:        DEFAULT_PLAINBS,  		cipherBS:       uint64(cipherBS),  		allZeroBlock:   make([]byte, cipherBS), -		plaintextNames: plaintextNames,  	}  } diff --git a/cryptfs/filter.go b/cryptfs/filter.go deleted file mode 100644 index f80889d..0000000 --- a/cryptfs/filter.go +++ /dev/null @@ -1,15 +0,0 @@ -package cryptfs - -// IsFiltered - check if "path" should be forbidden -// -// Used to prevent name clashes with gocryptfs.conf -// when file names are not encrypted -func (be *CryptFS) IsFiltered(path string) bool { -	// gocryptfs.conf in the root directory is forbidden -	if be.plaintextNames == true && path == ConfDefaultName { -		Warn.Printf("The name /%s is reserved when -plaintextnames is used\n", -			ConfDefaultName) -		return true -	} -	return false -} diff --git a/cryptfs/log.go b/cryptfs/log.go index 64dc80e..a7fe579 100644 --- a/cryptfs/log.go +++ b/cryptfs/log.go @@ -3,6 +3,7 @@ package cryptfs  import (  	"fmt"  	"strings" +	"encoding/json"  )  type logChannel struct { @@ -26,6 +27,18 @@ func (l *logChannel) Dump(d []byte) {  	fmt.Println(strings.Replace(s, "\000", "\\0", -1))  } +func (l *logChannel) JSONDump(obj interface{}) { +	if !l.enabled { +		return +	} +	b, err := json.MarshalIndent(obj, "", "\t") +	if err != nil { +		fmt.Println(err) +	} else { +		fmt.Println(string(b)) +	} +} +  func (l *logChannel) Enable() {  	l.enabled = true  } diff --git a/cryptfs/cryptfs_names.go b/cryptfs/names_core.go index 8f8486b..0f2e5b3 100644 --- a/cryptfs/cryptfs_names.go +++ b/cryptfs/names_core.go @@ -1,6 +1,6 @@  package cryptfs -// Filename encryption / decryption function +// Filename encryption / decryption functions  import (  	"crypto/aes" @@ -8,16 +8,22 @@ import (  	"encoding/base64"  	"errors"  	"fmt" -	"strings" -) -const ( -	OpEncrypt = iota -	OpDecrypt +	"github.com/rfjakob/eme"  )  // DecryptName - decrypt base64-encoded encrypted filename "cipherName" -func (be *CryptFS) DecryptName(cipherName string, iv []byte) (string, error) { +// The used encryption is either CBC or EME, depending on the "EMENames" argument. +// +// This function is exported because it allows for a very efficient readdir +// implementation (read IV once, decrypt all names using this function). +func (be *CryptFS) DecryptName(cipherName string, iv []byte, EMENames bool) (string, error) { +	return be.decryptName(cipherName, iv, EMENames) +} + +// decryptName - decrypt base64-encoded encrypted filename "cipherName". +// The used encryption is either CBC or EME, depending on the "EMENames" argument. +func (be *CryptFS) decryptName(cipherName string, iv []byte, EMENames bool) (string, error) {  	// Make sure relative symlinks still work after encryption  	// by passing these through unchanged @@ -34,8 +40,12 @@ func (be *CryptFS) DecryptName(cipherName string, iv []byte) (string, error) {  		return "", fmt.Errorf("Decoded length %d is not a multiple of the AES block size", len(bin))  	} -	cbc := cipher.NewCBCDecrypter(be.blockCipher, iv) -	cbc.CryptBlocks(bin, bin) +	if EMENames { +		bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionDecrypt) +	} else { +		cbc := cipher.NewCBCDecrypter(be.blockCipher, iv) +		cbc.CryptBlocks(bin, bin) +	}  	bin, err = be.unPad16(bin)  	if err != nil { @@ -46,8 +56,9 @@ func (be *CryptFS) DecryptName(cipherName string, iv []byte) (string, error) {  	return plain, err  } -// EncryptName - encrypt filename -func (be *CryptFS) encryptName(plainName string, iv []byte) string { +// encryptName - encrypt "plainName", return base64-encoded "cipherName64" +// The used encryption is either CBC or EME, depending on the "EMENames" argument. +func (be *CryptFS) encryptName(plainName string, iv []byte, EMENames bool) (cipherName64 string) {  	// Make sure relative symlinks still work after encryption  	// by passing these trough unchanged @@ -58,48 +69,15 @@ func (be *CryptFS) encryptName(plainName string, iv []byte) string {  	bin := []byte(plainName)  	bin = be.pad16(bin) -	cbc := cipher.NewCBCEncrypter(be.blockCipher, iv) -	cbc.CryptBlocks(bin, bin) - -	cipherName64 := base64.URLEncoding.EncodeToString(bin) -	return cipherName64 -} - -// TranslatePathZeroIV - encrypt or decrypt path using CBC with a constant all-zero IV. -// Just splits the string on "/" and hands the parts to encryptName() / decryptName() -func (be *CryptFS) TranslatePathZeroIV(path string, op int) (string, error) { -	var err error - -	// Empty string means root directory -	if path == "" { -		return path, err +	if EMENames { +		bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionEncrypt) +	} else { +		cbc := cipher.NewCBCEncrypter(be.blockCipher, iv) +		cbc.CryptBlocks(bin, bin)  	} -	zeroIV := make([]byte, DIRIV_LEN) - -	// Run operation on each path component -	var translatedParts []string -	parts := strings.Split(path, "/") -	for _, part := range parts { -		if part == "" { -			// This happens on "/foo/bar/" on the front and on the end. -			// Don't panic. -			translatedParts = append(translatedParts, "") -			continue -		} -		var newPart string -		if op == OpEncrypt { -			newPart = be.encryptName(part, zeroIV) -		} else { -			newPart, err = be.DecryptName(part, zeroIV) -			if err != nil { -				return "", err -			} -		} -		translatedParts = append(translatedParts, newPart) -	} - -	return strings.Join(translatedParts, "/"), err +	cipherName64 = base64.URLEncoding.EncodeToString(bin) +	return cipherName64  }  // pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding diff --git a/cryptfs/names_diriv.go b/cryptfs/names_diriv.go index 035eac1..2e2429e 100644 --- a/cryptfs/names_diriv.go +++ b/cryptfs/names_diriv.go @@ -73,11 +73,8 @@ func WriteDirIV(dir string) error {  	return ioutil.WriteFile(file, iv, 0444)  } -// EncryptPathDirIV - encrypt path using CBC with DirIV -func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string) (string, error) { -	if be.plaintextNames { -		return plainPath, nil -	} +// EncryptPathDirIV - encrypt path using CBC or EME with DirIV +func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool) (cipherPath string, err error) {  	// Empty string means root directory  	if plainPath == "" {  		return plainPath, nil @@ -88,36 +85,32 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string) (string, e  	if found {  		//fmt.Print("h")  		baseName := filepath.Base(plainPath) -		cBaseName := be.encryptName(baseName, iv) -		cPath := cParentDir + "/" + cBaseName -		return cPath, nil +		cBaseName := be.encryptName(baseName, iv, eme) +		cipherPath = cParentDir + "/" + cBaseName +		return cipherPath, nil  	}  	// Walk the directory tree  	var wd = rootDir  	var encryptedNames []string -	var err error  	plainNames := strings.Split(plainPath, "/")  	for _, plainName := range plainNames {  		iv, err = be.ReadDirIV(wd)  		if err != nil {  			return "", err  		} -		encryptedName := be.encryptName(plainName, iv) +		encryptedName := be.encryptName(plainName, iv, eme)  		encryptedNames = append(encryptedNames, encryptedName)  		wd = filepath.Join(wd, encryptedName)  	}  	// Cache the final DirIV -	cPath := strings.Join(encryptedNames, "/") -	cParentDir = filepath.Dir(cPath) +	cipherPath = strings.Join(encryptedNames, "/") +	cParentDir = filepath.Dir(cipherPath)  	be.DirIVCacheEnc.store(parentDir, iv, cParentDir) -	return cPath, nil +	return cipherPath, nil  } -// DecryptPathDirIV - encrypt path using CBC with DirIV -func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string) (string, error) { -	if be.plaintextNames { -		return encryptedPath, nil -	} +// DecryptPathDirIV - encrypt path using CBC or EME with DirIV +func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string, eme bool) (string, error) {  	var wd = rootDir  	var plainNames []string  	encryptedNames := strings.Split(encryptedPath, "/") @@ -127,7 +120,7 @@ func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string) (strin  		if err != nil {  			return "", err  		} -		plainName, err := be.DecryptName(encryptedName, iv) +		plainName, err := be.decryptName(encryptedName, iv, eme)  		if err != nil {  			return "", err  		} diff --git a/cryptfs/names_noiv.go b/cryptfs/names_noiv.go new file mode 100644 index 0000000..7eed4b8 --- /dev/null +++ b/cryptfs/names_noiv.go @@ -0,0 +1,63 @@ +package cryptfs + +import ( +	"strings" +) + +const ( +	OpEncrypt = iota +	OpDecrypt +) + +// DecryptPathNoIV - decrypt path using CBC without any IV. +// This function is deprecated by the the more secure DirIV variant and only retained +// for compatability with old filesystems. +func (be *CryptFS) DecryptPathNoIV(cipherPath string) (plainPath string, err error) { +	plainPath, err = be.translatePathNoIV(cipherPath, OpDecrypt) +	return plainPath, err +} + +// EncryptPathNoIV - decrypt path using CBC without any IV. +// This function is deprecated by the the more secure DirIV variant and only retained +// for compatability with old filesystems. +func (be *CryptFS) EncryptPathNoIV(plainPath string) (cipherPath string) { +	cipherPath, _ = be.translatePathNoIV(plainPath, OpEncrypt) +	return cipherPath +} + +// translatePathZeroIV - encrypt or decrypt path using CBC with an all-zero IV. +// Just splits the string on "/" and hands the parts to encryptName() / decryptName() +func (be *CryptFS) translatePathNoIV(path string, op int) (string, error) { +	var err error + +	// Empty string means root directory +	if path == "" { +		return path, err +	} + +	zeroIV := make([]byte, DIRIV_LEN) + +	// Run operation on each path component +	var translatedParts []string +	parts := strings.Split(path, "/") +	for _, part := range parts { +		if part == "" { +			// This happens on "/foo/bar/" on the front and on the end. +			// Don't panic. +			translatedParts = append(translatedParts, "") +			continue +		} +		var newPart string +		if op == OpEncrypt { +			newPart = be.encryptName(part, zeroIV, false) +		} else { +			newPart, err = be.decryptName(part, zeroIV, false) +			if err != nil { +				return "", err +			} +		} +		translatedParts = append(translatedParts, newPart) +	} + +	return strings.Join(translatedParts, "/"), err +} diff --git a/cryptfs/names_test.go b/cryptfs/names_test.go index 1ad3391..6dffae3 100644 --- a/cryptfs/names_test.go +++ b/cryptfs/names_test.go @@ -5,7 +5,7 @@ import (  	"testing"  ) -func TestTranslatePath(t *testing.T) { +func TestEncryptPathNoIV(t *testing.T) {  	var s []string  	s = append(s, "foo")  	s = append(s, "foo12312312312312312313123123123") @@ -15,15 +15,14 @@ func TestTranslatePath(t *testing.T) {  	fs := NewCryptFS(key, true, false)  	for _, n := range s { -		c, err := fs.TranslatePathZeroIV(n, OpEncrypt) -		d, err := fs.TranslatePathZeroIV(c, OpDecrypt) +		c := fs.EncryptPathNoIV(n) +		d, err := fs.DecryptPathNoIV(c)  		if err != nil { -			t.Errorf("Got error from DecryptName: %s", err) +			t.Errorf("Got error from DecryptPathNoIV: %s", err)  		}  		if d != n { -			t.Errorf("Content mismatch, n=\"%s\" d=\"%s\"", n, d) +			t.Errorf("Content mismatch, n != d: n=%s c=%s d=%s", n, c, d)  		} -		//fmt.Printf("n=%s c=%s d=%s\n", n, c, d)  	}  } diff --git a/integration_tests/example_filesystems_test.go b/integration_tests/example_filesystems_test.go index 8081feb..a04e67e 100644 --- a/integration_tests/example_filesystems_test.go +++ b/integration_tests/example_filesystems_test.go @@ -57,7 +57,7 @@ func TestExampleFsV04(t *testing.T) {  	checkExampleContent(t, pDir)  	unmount(pDir)  	mount(cDir, pDir, "-masterkey", "74676e34-0b47c145-00dac61a-17a92316-"+ -		"bb57044c-e205b71f-65f4fdca-7cabd4b3", "-diriv=false") +		"bb57044c-e205b71f-65f4fdca-7cabd4b3", "-diriv=false", "-emenames=false")  	checkExampleContent(t, pDir)  	unmount(pDir)  	err = os.Remove(pDir) @@ -79,7 +79,7 @@ func TestExampleFsV05(t *testing.T) {  	checkExampleContent(t, pDir)  	unmount(pDir)  	mount(cDir, pDir, "-masterkey", "199eae55-36bff4af-83b9a3a2-4fa16f65-"+ -		"1549ccdb-2d08d1f0-b1b26965-1b61f896") +		"1549ccdb-2d08d1f0-b1b26965-1b61f896", "-emenames=false")  	checkExampleContent(t, pDir)  	unmount(pDir)  	err = os.Remove(pDir) @@ -35,7 +35,7 @@ const (  type argContainer struct {  	debug, init, zerokey, fusedebug, openssl, passwd, foreground, version, -	plaintextnames, quiet, diriv bool +	plaintextnames, quiet, diriv, emenames bool  	masterkey, mountpoint, cipherdir, cpuprofile, config, extpass string  	notifypid, scryptn                                            int  } @@ -55,7 +55,7 @@ func initDir(args *argContainer) {  	// Create gocryptfs.conf  	cryptfs.Info.Printf("Choose a password for protecting your files.\n")  	password := readPasswordTwice(args.extpass) -	err = cryptfs.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn) +	err = cryptfs.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, args.emenames)  	if err != nil {  		fmt.Println(err)  		os.Exit(ERREXIT_INIT) @@ -146,6 +146,7 @@ func main() {  		"file names")  	flagSet.BoolVar(&args.quiet, "q", false, "Quiet - silence informational messages")  	flagSet.BoolVar(&args.diriv, "diriv", true, "Use per-directory file name IV") +	flagSet.BoolVar(&args.emenames, "emenames", true, "Use EME filename encryption. This option implies diriv.")  	flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")  	flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")  	flagSet.StringVar(&args.config, "config", "", "Use specified config file instead of CIPHERDIR/gocryptfs.conf") @@ -262,7 +263,7 @@ func main() {  		printMasterKey(masterkey)  	}  	// Initialize FUSE server -	cryptfs.Debug.Printf("args: %v\n", args) +	cryptfs.Debug.Printf("cli args: %v\n", args)  	srv := pathfsFrontend(masterkey, args, confFile)  	cryptfs.Info.Println("Filesystem ready.")  	// We are ready - send USR1 signal to our parent @@ -289,13 +290,26 @@ func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *  		OpenSSL:        args.openssl,  		PlaintextNames: args.plaintextnames,  		DirIV:          args.diriv, +		EMENames:       args.emenames,  	}  	// confFile is nil when "-zerokey" or "-masterkey" was used  	if confFile != nil {  		// Settings from the config file override command line args  		frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(cryptfs.FlagPlaintextNames)  		frontendArgs.DirIV = confFile.IsFeatureFlagSet(cryptfs.FlagDirIV) +		frontendArgs.EMENames = confFile.IsFeatureFlagSet(cryptfs.FlagEMENames)  	} +	// EMENames implies DirIV, both on the command line and in the config file. +	if frontendArgs.EMENames { +		frontendArgs.DirIV = true +	} +	// PlainTexnames disables both EMENames and DirIV +	if frontendArgs.PlaintextNames { +		frontendArgs.DirIV = false +		frontendArgs.EMENames = false +	} +	cryptfs.Debug.Printf("frontendArgs: ") +	cryptfs.Debug.JSONDump(frontendArgs)  	finalFs := pathfs_frontend.NewFS(frontendArgs)  	pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true} diff --git a/pathfs_frontend/args.go b/pathfs_frontend/args.go index 86a907d..fb0b81f 100644 --- a/pathfs_frontend/args.go +++ b/pathfs_frontend/args.go @@ -7,4 +7,5 @@ type Args struct {  	OpenSSL        bool  	PlaintextNames bool  	DirIV          bool +	EMENames       bool  } diff --git a/pathfs_frontend/fs.go b/pathfs_frontend/fs.go index 40e0d1d..0f462b0 100644 --- a/pathfs_frontend/fs.go +++ b/pathfs_frontend/fs.go @@ -48,7 +48,7 @@ func (fs *FS) getBackingPath(relPath string) (string, error) {  func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {  	cryptfs.Debug.Printf("FS.GetAttr('%s')\n", name) -	if fs.CryptFS.IsFiltered(name) { +	if fs.isFiltered(name) {  		return nil, fuse.EPERM  	}  	cName, err := fs.encryptPath(name) @@ -103,7 +103,7 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f  			continue  		}  		var name string -		name, err = fs.CryptFS.DecryptName(cName, cachedIV) +		name, err = fs.CryptFS.DecryptName(cName, cachedIV, fs.args.EMENames)  		if err != nil {  			cryptfs.Warn.Printf("Invalid name \"%s\" in dir \"%s\": %s\n", cName, dirName, err)  			continue @@ -128,7 +128,7 @@ func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int, writeOnly bool) {  }  func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) { -	if fs.CryptFS.IsFiltered(path) { +	if fs.isFiltered(path) {  		return nil, fuse.EPERM  	}  	iflags, writeOnly := fs.mangleOpenFlags(flags) @@ -147,7 +147,7 @@ func (fs *FS) Open(path 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 fs.CryptFS.IsFiltered(path) { +	if fs.isFiltered(path) {  		return nil, fuse.EPERM  	}  	iflags, writeOnly := fs.mangleOpenFlags(flags) @@ -163,7 +163,7 @@ 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 fs.CryptFS.IsFiltered(path) { +	if fs.isFiltered(path) {  		return fuse.EPERM  	}  	cPath, err := fs.encryptPath(path) @@ -174,7 +174,7 @@ func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.  }  func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) { -	if fs.CryptFS.IsFiltered(path) { +	if fs.isFiltered(path) {  		return fuse.EPERM  	}  	cPath, err := fs.encryptPath(path) @@ -185,7 +185,7 @@ func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context)  }  func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) { -	if fs.CryptFS.IsFiltered(path) { +	if fs.isFiltered(path) {  		return fuse.EPERM  	}  	cPath, err := fs.encryptPath(path) @@ -201,7 +201,7 @@ 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 fs.CryptFS.IsFiltered(path) { +	if fs.isFiltered(path) {  		return fuse.EPERM  	}  	cPath, err := fs.encryptPath(path) @@ -244,7 +244,7 @@ func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status f  }  func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fuse.Status) { -	if fs.CryptFS.IsFiltered(relPath) { +	if fs.isFiltered(relPath) {  		return fuse.EPERM  	}  	encPath, err := fs.getBackingPath(relPath) @@ -275,7 +275,7 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu  }  func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) { -	if fs.CryptFS.IsFiltered(path) { +	if fs.isFiltered(path) {  		return fuse.EPERM  	}  	cPath, err := fs.getBackingPath(path) @@ -349,7 +349,7 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {  func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {  	cryptfs.Debug.Printf("Symlink(\"%s\", \"%s\")\n", target, linkName) -	if fs.CryptFS.IsFiltered(linkName) { +	if fs.isFiltered(linkName) {  		return fuse.EPERM  	}  	cPath, err := fs.getBackingPath(linkName) @@ -376,7 +376,7 @@ func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (co  }  func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { -	if fs.CryptFS.IsFiltered(newPath) { +	if fs.isFiltered(newPath) {  		return fuse.EPERM  	}  	cOldPath, err := fs.getBackingPath(oldPath) @@ -396,7 +396,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod  }  func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { -	if fs.CryptFS.IsFiltered(newPath) { +	if fs.isFiltered(newPath) {  		return fuse.EPERM  	}  	cOldPath, err := fs.getBackingPath(oldPath) @@ -411,7 +411,7 @@ func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code  }  func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse.Status) { -	if fs.CryptFS.IsFiltered(path) { +	if fs.isFiltered(path) {  		return fuse.EPERM  	}  	cPath, err := fs.getBackingPath(path) diff --git a/pathfs_frontend/names.go b/pathfs_frontend/names.go index bb73ff4..e1783a6 100644 --- a/pathfs_frontend/names.go +++ b/pathfs_frontend/names.go @@ -6,20 +6,47 @@ import (  	"github.com/rfjakob/gocryptfs/cryptfs"  ) +// isFiltered - check if plaintext "path" should be forbidden +// +// Prevents name clashes with internal files when file names are not encrypted +func (fs *FS) isFiltered(path string) bool { +	if !fs.args.PlaintextNames { +		return false +	} +	// gocryptfs.conf in the root directory is forbidden +	if path == cryptfs.ConfDefaultName { +		cryptfs.Warn.Printf("The name /%s is reserved when -plaintextnames is used\n", +			cryptfs.ConfDefaultName) +		return true +	} +	// Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames +	// are exclusive +	return false +} + + +// encryptPath - encrypt relative plaintext path  func (fs *FS) encryptPath(plainPath string) (string, error) { +	if fs.args.PlaintextNames { +		return plainPath, nil +	}  	if !fs.args.DirIV { -		return fs.CryptFS.TranslatePathZeroIV(plainPath, cryptfs.OpEncrypt) +		return fs.CryptFS.EncryptPathNoIV(plainPath), nil  	}  	fs.dirIVLock.RLock()  	defer fs.dirIVLock.RUnlock() -	return fs.CryptFS.EncryptPathDirIV(plainPath, fs.args.Cipherdir) +	return fs.CryptFS.EncryptPathDirIV(plainPath, fs.args.Cipherdir, fs.args.EMENames)  } +// decryptPath - decrypt relative ciphertext path  func (fs *FS) decryptPath(cipherPath string) (string, error) { +	if fs.args.PlaintextNames { +		return cipherPath, nil +	}  	if !fs.args.DirIV { -		return fs.CryptFS.TranslatePathZeroIV(cipherPath, cryptfs.OpDecrypt) +		return fs.CryptFS.DecryptPathNoIV(cipherPath)  	}  	fs.dirIVLock.RLock()  	defer fs.dirIVLock.RUnlock() -	return fs.CryptFS.DecryptPathDirIV(cipherPath, fs.args.Cipherdir) +	return fs.CryptFS.DecryptPathDirIV(cipherPath, fs.args.Cipherdir, fs.args.EMENames)  } | 
