nametranform, fusefrontend: better errors on invalid names
nametransform.DecryptName() now always returns syscall.EBADMSG if the name was invalid. fusefrontend.OpenDir error messages have been normalized.
This commit is contained in:
parent
68387b470c
commit
26881538e1
@ -291,8 +291,8 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
|
|||||||
if isLong == nametransform.LongNameContent {
|
if isLong == nametransform.LongNameContent {
|
||||||
cNameLong, err := nametransform.ReadLongName(filepath.Join(cDirAbsPath, cName))
|
cNameLong, err := nametransform.ReadLongName(filepath.Join(cDirAbsPath, cName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("Skipping entry %q in dir %q: Could not read .name: %v",
|
tlog.Warn.Printf("OpenDir %q: invalid entry %q: Could not read .name: %v",
|
||||||
cName, cDirName, err)
|
cDirName, cName, err)
|
||||||
errorCount++
|
errorCount++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -304,12 +304,13 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
|
|||||||
|
|
||||||
name, err := fs.nameTransform.DecryptName(cName, cachedIV)
|
name, err := fs.nameTransform.DecryptName(cName, cachedIV)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("Skipping entry %q in dir %q: %s",
|
tlog.Warn.Printf("OpenDir %q: invalid entry %q: %v",
|
||||||
cName, cDirName, err)
|
cDirName, cName, err)
|
||||||
errorCount++
|
errorCount++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// Override the ciphertext name with the plaintext name but reuse the rest
|
||||||
|
// of the structure
|
||||||
cipherEntries[i].Name = name
|
cipherEntries[i].Name = name
|
||||||
plain = append(plain, cipherEntries[i])
|
plain = append(plain, cipherEntries[i])
|
||||||
}
|
}
|
||||||
@ -317,8 +318,8 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
|
|||||||
if errorCount > 0 && len(plain) == 0 {
|
if errorCount > 0 && len(plain) == 0 {
|
||||||
// Don't let the user stare on an empty directory. Report that things went
|
// Don't let the user stare on an empty directory. Report that things went
|
||||||
// wrong.
|
// wrong.
|
||||||
tlog.Warn.Printf("All %d entries in directory %q were invalid, returning EIO",
|
tlog.Warn.Printf("OpenDir %q: all %d entries were invalid, returning EIO",
|
||||||
errorCount, cDirName)
|
cDirName, errorCount)
|
||||||
status = fuse.EIO
|
status = fuse.EIO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
package nametransform
|
package nametransform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"syscall"
|
"syscall"
|
||||||
@ -34,10 +35,8 @@ func New(e *eme.EMECipher, longNames bool, raw64 bool) *NameTransform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptName - decrypt base64-encoded encrypted filename "cipherName"
|
// DecryptName decrypts a base64-encoded encrypted filename "cipherName" using the
|
||||||
//
|
// initialization vector "iv".
|
||||||
// This function is exported because it allows for a very efficient readdir
|
|
||||||
// implementation (read IV once, decrypt all names using this function).
|
|
||||||
func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) {
|
func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) {
|
||||||
bin, err := n.B64.DecodeString(cipherName)
|
bin, err := n.B64.DecodeString(cipherName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -45,16 +44,21 @@ func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error
|
|||||||
}
|
}
|
||||||
if len(bin)%aes.BlockSize != 0 {
|
if len(bin)%aes.BlockSize != 0 {
|
||||||
tlog.Debug.Printf("DecryptName %q: decoded length %d is not a multiple of 16", cipherName, len(bin))
|
tlog.Debug.Printf("DecryptName %q: decoded length %d is not a multiple of 16", cipherName, len(bin))
|
||||||
return "", syscall.EINVAL
|
return "", syscall.EBADMSG
|
||||||
}
|
}
|
||||||
bin = n.emeCipher.Decrypt(iv, bin)
|
bin = n.emeCipher.Decrypt(iv, bin)
|
||||||
bin, err = unPad16(bin)
|
bin, err = unPad16(bin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Debug.Printf("pad16 error detail: %v", err)
|
tlog.Debug.Printf("DecryptName: unPad16 error: %v", err)
|
||||||
// unPad16 returns detailed errors including the position of the
|
// unPad16 returns detailed errors including the position of the
|
||||||
// incorrect bytes. Kill the padding oracle by lumping everything into
|
// incorrect bytes. Kill the padding oracle by lumping everything into
|
||||||
// a generic error.
|
// a generic error.
|
||||||
return "", syscall.EINVAL
|
return "", syscall.EBADMSG
|
||||||
|
}
|
||||||
|
// A name can never contain a null byte or "/". Make sure we never return those
|
||||||
|
// to the user, even when we read a corrupted (or fuzzed) filesystem.
|
||||||
|
if bytes.Contains(bin, []byte{0}) || bytes.Contains(bin, []byte("/")) {
|
||||||
|
return "", syscall.EBADMSG
|
||||||
}
|
}
|
||||||
plain := string(bin)
|
plain := string(bin)
|
||||||
return plain, err
|
return plain, err
|
||||||
|
Loading…
Reference in New Issue
Block a user