fusefrontend_reverse: use OpenNofollow in findLongnameParent

Protects findLongnameParent against symlink races.

Also add comments to several functions along the way.

Reported at https://github.com/rfjakob/gocryptfs/issues/165
This commit is contained in:
Jakob Unterwurzacher 2018-01-17 20:52:52 +01:00
parent 8951eb2472
commit 959e1fc1e2
3 changed files with 25 additions and 9 deletions

View File

@ -2,7 +2,6 @@ package fusefrontend_reverse
import ( import (
"log" "log"
"os"
"path/filepath" "path/filepath"
"sync" "sync"
"syscall" "syscall"
@ -13,6 +12,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/nametransform" "github.com/rfjakob/gocryptfs/internal/nametransform"
"github.com/rfjakob/gocryptfs/internal/pathiv" "github.com/rfjakob/gocryptfs/internal/pathiv"
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
@ -27,6 +27,9 @@ const (
shortNameMax = 175 shortNameMax = 175
) )
// longnameParentCache maps dir+"/"+longname to plaintextname.
// Yes, the combination of relative plaintext dir path and encrypted
// longname is strange, but works fine as a map index.
var longnameParentCache map[string]string var longnameParentCache map[string]string
var longnameCacheLock sync.Mutex var longnameCacheLock sync.Mutex
@ -48,7 +51,9 @@ func initLongnameCache() {
go longnameCacheCleaner() go longnameCacheCleaner()
} }
// findLongnameParent converts "gocryptfs.longname.XYZ" to the plaintext name // findLongnameParent converts "longname" = "gocryptfs.longname.XYZ" to the
// plaintext name. "dir" = relative plaintext path to the directory the
// longname file is in, "dirIV" = directory IV of the directory.
func (rfs *ReverseFS) findLongnameParent(dir string, dirIV []byte, longname string) (plaintextName string, err error) { func (rfs *ReverseFS) findLongnameParent(dir string, dirIV []byte, longname string) (plaintextName string, err error) {
longnameCacheLock.Lock() longnameCacheLock.Lock()
hit := longnameParentCache[dir+"/"+longname] hit := longnameParentCache[dir+"/"+longname]
@ -56,26 +61,27 @@ func (rfs *ReverseFS) findLongnameParent(dir string, dirIV []byte, longname stri
if hit != "" { if hit != "" {
return hit, nil return hit, nil
} }
absDir := filepath.Join(rfs.args.Cipherdir, dir) fd, err := syscallcompat.OpenNofollow(rfs.args.Cipherdir, dir, syscall.O_RDONLY|syscall.O_DIRECTORY, 0)
dirfd, err := os.Open(absDir)
if err != nil { if err != nil {
tlog.Warn.Printf("findLongnameParent: opendir failed: %v\n", err) tlog.Warn.Printf("findLongnameParent: opendir failed: %v\n", err)
return "", err return "", err
} }
dirEntries, err := dirfd.Readdirnames(-1) dirEntries, err := syscallcompat.Getdents(fd)
dirfd.Close() syscall.Close(fd)
if err != nil { if err != nil {
tlog.Warn.Printf("findLongnameParent: Readdirnames failed: %v\n", err) tlog.Warn.Printf("findLongnameParent: Getdents failed: %v\n", err)
return "", err return "", err
} }
longnameCacheLock.Lock() longnameCacheLock.Lock()
defer longnameCacheLock.Unlock() defer longnameCacheLock.Unlock()
for _, plaintextName = range dirEntries { for _, entry := range dirEntries {
plaintextName := entry.Name
if len(plaintextName) <= shortNameMax { if len(plaintextName) <= shortNameMax {
continue continue
} }
cName := rfs.nameTransform.EncryptName(plaintextName, dirIV) cName := rfs.nameTransform.EncryptName(plaintextName, dirIV)
if len(cName) <= syscall.NAME_MAX { if len(cName) <= syscall.NAME_MAX {
// Entry should have been skipped by the "continue" above
log.Panic("logic error or wrong shortNameMax constant?") log.Panic("logic error or wrong shortNameMax constant?")
} }
hName := rfs.nameTransform.HashLongName(cName) hName := rfs.nameTransform.HashLongName(cName)

View File

@ -24,6 +24,9 @@ func (rfs *ReverseFS) abs(relPath string, err error) (string, error) {
return filepath.Join(rfs.args.Cipherdir, relPath), nil return filepath.Join(rfs.args.Cipherdir, relPath), nil
} }
// rDecryptName decrypts the ciphertext name "cName", given the dirIV of the
// directory "cName" lies in. The relative plaintext path to the directory
// "pDir" is used if a "gocryptfs.longname.XYZ.name" must be resolved.
func (rfs *ReverseFS) rDecryptName(cName string, dirIV []byte, pDir string) (pName string, err error) { func (rfs *ReverseFS) rDecryptName(cName string, dirIV []byte, pDir string) (pName string, err error) {
nameType := nametransform.NameType(cName) nameType := nametransform.NameType(cName)
if nameType == nametransform.LongNameNone { if nameType == nametransform.LongNameNone {
@ -58,6 +61,8 @@ func (rfs *ReverseFS) rDecryptName(cName string, dirIV []byte, pDir string) (pNa
return pName, nil return pName, nil
} }
// decryptPath decrypts a relative ciphertext path to a relative plaintext
// path.
func (rfs *ReverseFS) decryptPath(relPath string) (string, error) { func (rfs *ReverseFS) decryptPath(relPath string) (string, error) {
if rfs.args.PlaintextNames || relPath == "" { if rfs.args.PlaintextNames || relPath == "" {
return relPath, nil return relPath, nil

View File

@ -17,6 +17,8 @@ type rPathCacheContainer struct {
dirIV []byte dirIV []byte
} }
// lookup relative ciphertext path "cPath". Returns dirIV, relative
// plaintext path.
func (c *rPathCacheContainer) lookup(cPath string) ([]byte, string) { func (c *rPathCacheContainer) lookup(cPath string) ([]byte, string) {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
@ -28,7 +30,9 @@ func (c *rPathCacheContainer) lookup(cPath string) ([]byte, string) {
return nil, "" return nil, ""
} }
// store - write entry for "cPath" into the cache // store - write entry for the directory at relative ciphertext path "cPath"
// into the cache.
// "dirIV" = directory IV of the directory, "pPath" = relative plaintext path
func (c *rPathCacheContainer) store(cPath string, dirIV []byte, pPath string) { func (c *rPathCacheContainer) store(cPath string, dirIV []byte, pPath string) {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
@ -37,4 +41,5 @@ func (c *rPathCacheContainer) store(cPath string, dirIV []byte, pPath string) {
c.pPath = pPath c.pPath = pPath
} }
// rPathCache: see rPathCacheContainer above for a detailed description
var rPathCache rPathCacheContainer var rPathCache rPathCacheContainer