2016-02-06 19:27:59 +01:00
|
|
|
package fusefrontend
|
2015-11-27 00:03:10 +01:00
|
|
|
|
2015-11-27 23:34:55 +01:00
|
|
|
// This file forwards file encryption operations to cryptfs
|
|
|
|
|
|
|
|
import (
|
2016-02-06 22:54:14 +01:00
|
|
|
"path/filepath"
|
2018-10-14 20:11:49 +02:00
|
|
|
"strings"
|
|
|
|
"syscall"
|
2016-02-06 22:54:14 +01:00
|
|
|
|
2016-02-06 19:20:54 +01:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
2018-10-14 20:11:49 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
2016-06-15 23:30:44 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
2015-11-27 23:34:55 +01:00
|
|
|
)
|
2015-11-27 00:03:10 +01:00
|
|
|
|
2015-12-08 16:13:29 +01:00
|
|
|
// 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
|
2016-02-06 19:20:54 +01:00
|
|
|
if path == configfile.ConfDefaultName {
|
2016-06-15 23:30:44 +02:00
|
|
|
tlog.Info.Printf("The name /%s is reserved when -plaintextnames is used\n",
|
2016-02-06 19:20:54 +01:00
|
|
|
configfile.ConfDefaultName)
|
2015-12-08 16:13:29 +01:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
// Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames
|
|
|
|
// are exclusive
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-02-06 22:54:14 +01:00
|
|
|
// GetBackingPath - get the absolute encrypted path of the backing file
|
|
|
|
// from the relative plaintext path "relPath"
|
2018-11-04 21:29:17 +01:00
|
|
|
//
|
|
|
|
// TODO: this function is NOT symlink-safe.
|
2016-02-06 22:54:14 +01:00
|
|
|
func (fs *FS) getBackingPath(relPath string) (string, error) {
|
|
|
|
cPath, err := fs.encryptPath(relPath)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
cAbsPath := filepath.Join(fs.args.Cipherdir, cPath)
|
2016-06-15 23:30:44 +02:00
|
|
|
tlog.Debug.Printf("getBackingPath: %s + %s -> %s", fs.args.Cipherdir, relPath, cAbsPath)
|
2016-02-06 22:54:14 +01:00
|
|
|
return cAbsPath, nil
|
|
|
|
}
|
|
|
|
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
// openBackingDir opens the parent ciphertext directory of plaintext path
|
|
|
|
// "relPath" and returns the dirfd and the encrypted basename.
|
2018-10-14 20:11:49 +02:00
|
|
|
//
|
fusefrontend: use OpenDirNofollow in openBackingDir
Rename openBackingPath to openBackingDir and use OpenDirNofollow
to be safe against symlink races. Note that openBackingDir is
not used in several important code paths like Create().
But it is used in Unlink, and the performance impact in the RM benchmark
to be acceptable:
Before
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.bYO: gocryptfs v1.6-12-g930c37e-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.07979 s, 243 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.882413 s, 297 MB/s
UNTAR: 16.703
MD5: 7.606
LS: 1.349
RM: 3.237
After
$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.jK3: gocryptfs v1.6-13-g84d6faf-dirty; go-fuse v20170619-49-gb11e293; 2018-09-08 go1.10.3
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.06261 s, 247 MB/s
READ: 262144000 bytes (262 MB, 250 MiB) copied, 0.947228 s, 277 MB/s
UNTAR: 17.197
MD5: 7.540
LS: 1.364
RM: 3.410
2018-09-08 19:27:33 +02:00
|
|
|
// The caller should then use Openat(dirfd, cName, ...) and friends.
|
2018-10-14 20:11:49 +02:00
|
|
|
// For convenience, if relPath is "", cName is going to be ".".
|
|
|
|
//
|
|
|
|
// openBackingDir is secure against symlink races by using Openat and
|
|
|
|
// ReadDirIVAt.
|
2018-09-22 20:25:47 +02:00
|
|
|
func (fs *FS) openBackingDir(relPath string) (dirfd int, cName string, err error) {
|
2018-10-14 20:11:49 +02:00
|
|
|
// With PlaintextNames, we don't need to read DirIVs. Easy.
|
|
|
|
if fs.args.PlaintextNames {
|
|
|
|
dir := nametransform.Dir(relPath)
|
|
|
|
dirfd, err = syscallcompat.OpenDirNofollow(fs.args.Cipherdir, dir)
|
|
|
|
if err != nil {
|
|
|
|
return -1, "", err
|
|
|
|
}
|
|
|
|
// If relPath is empty, cName is ".".
|
|
|
|
cName = filepath.Base(relPath)
|
|
|
|
return dirfd, cName, nil
|
2017-11-28 01:11:19 +01:00
|
|
|
}
|
2018-10-14 20:11:49 +02:00
|
|
|
// Open cipherdir (following symlinks)
|
|
|
|
dirfd, err = syscall.Open(fs.args.Cipherdir, syscall.O_RDONLY|syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
|
2017-11-28 01:11:19 +01:00
|
|
|
if err != nil {
|
2018-09-22 20:10:34 +02:00
|
|
|
return -1, "", err
|
2017-11-28 01:11:19 +01:00
|
|
|
}
|
2018-10-14 20:11:49 +02:00
|
|
|
// If relPath is empty, cName is ".".
|
|
|
|
if relPath == "" {
|
|
|
|
return dirfd, ".", nil
|
|
|
|
}
|
|
|
|
// Walk the directory tree
|
|
|
|
parts := strings.Split(relPath, "/")
|
|
|
|
for i, name := range parts {
|
|
|
|
iv, err := nametransform.ReadDirIVAt(dirfd)
|
|
|
|
if err != nil {
|
|
|
|
return -1, "", err
|
|
|
|
}
|
|
|
|
cName = fs.nameTransform.EncryptAndHashName(name, iv)
|
|
|
|
// Last part? We are done.
|
|
|
|
if i == len(parts)-1 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Not the last part? Descend into next directory.
|
|
|
|
dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_NOFOLLOW|syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
|
|
|
|
syscall.Close(dirfd)
|
|
|
|
if err != nil {
|
|
|
|
return -1, "", err
|
|
|
|
}
|
|
|
|
dirfd = dirfd2
|
|
|
|
}
|
2018-09-22 20:25:47 +02:00
|
|
|
return dirfd, cName, nil
|
2017-11-28 01:11:19 +01:00
|
|
|
}
|
|
|
|
|
2015-12-08 16:13:29 +01:00
|
|
|
// encryptPath - encrypt relative plaintext path
|
2018-11-04 21:29:17 +01:00
|
|
|
//
|
|
|
|
// TODO: this function is NOT symlink-safe because EncryptPathDirIV is not
|
|
|
|
// symlink-safe.
|
2015-11-27 00:03:10 +01:00
|
|
|
func (fs *FS) encryptPath(plainPath string) (string, error) {
|
2018-10-06 21:49:33 +02:00
|
|
|
if plainPath != "" { // Empty path gets encrypted all the time without actual file accesses.
|
|
|
|
fs.AccessedSinceLastCheck = 1
|
|
|
|
} else { // Empty string gets encrypted as empty string
|
|
|
|
return plainPath, nil
|
|
|
|
}
|
2015-12-08 16:13:29 +01:00
|
|
|
if fs.args.PlaintextNames {
|
|
|
|
return plainPath, nil
|
|
|
|
}
|
2018-10-06 21:49:33 +02:00
|
|
|
|
2015-11-27 21:48:58 +01:00
|
|
|
fs.dirIVLock.RLock()
|
2016-02-06 22:54:14 +01:00
|
|
|
cPath, err := fs.nameTransform.EncryptPathDirIV(plainPath, fs.args.Cipherdir)
|
2016-06-15 23:30:44 +02:00
|
|
|
tlog.Debug.Printf("encryptPath '%s' -> '%s' (err: %v)", plainPath, cPath, err)
|
2016-02-06 22:54:14 +01:00
|
|
|
fs.dirIVLock.RUnlock()
|
|
|
|
return cPath, err
|
2015-11-27 00:03:10 +01:00
|
|
|
}
|