From 26881538e1753e613b4143b28fa339812a9a6d16 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 7 May 2017 20:58:27 +0200 Subject: [PATCH] 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. --- internal/fusefrontend/fs_dir.go | 15 ++++++++------- internal/nametransform/names.go | 18 +++++++++++------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go index 05cea75..7d1e3ef 100644 --- a/internal/fusefrontend/fs_dir.go +++ b/internal/fusefrontend/fs_dir.go @@ -291,8 +291,8 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f if isLong == nametransform.LongNameContent { cNameLong, err := nametransform.ReadLongName(filepath.Join(cDirAbsPath, cName)) if err != nil { - tlog.Warn.Printf("Skipping entry %q in dir %q: Could not read .name: %v", - cName, cDirName, err) + tlog.Warn.Printf("OpenDir %q: invalid entry %q: Could not read .name: %v", + cDirName, cName, err) errorCount++ continue } @@ -304,12 +304,13 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f name, err := fs.nameTransform.DecryptName(cName, cachedIV) if err != nil { - tlog.Warn.Printf("Skipping entry %q in dir %q: %s", - cName, cDirName, err) + tlog.Warn.Printf("OpenDir %q: invalid entry %q: %v", + cDirName, cName, err) errorCount++ continue } - + // Override the ciphertext name with the plaintext name but reuse the rest + // of the structure cipherEntries[i].Name = name 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 { // Don't let the user stare on an empty directory. Report that things went // wrong. - tlog.Warn.Printf("All %d entries in directory %q were invalid, returning EIO", - errorCount, cDirName) + tlog.Warn.Printf("OpenDir %q: all %d entries were invalid, returning EIO", + cDirName, errorCount) status = fuse.EIO } diff --git a/internal/nametransform/names.go b/internal/nametransform/names.go index 71c8cd3..c96f7ce 100644 --- a/internal/nametransform/names.go +++ b/internal/nametransform/names.go @@ -2,6 +2,7 @@ package nametransform import ( + "bytes" "crypto/aes" "encoding/base64" "syscall" @@ -34,10 +35,8 @@ func New(e *eme.EMECipher, longNames bool, raw64 bool) *NameTransform { } } -// DecryptName - decrypt base64-encoded encrypted filename "cipherName" -// -// This function is exported because it allows for a very efficient readdir -// implementation (read IV once, decrypt all names using this function). +// DecryptName decrypts a base64-encoded encrypted filename "cipherName" using the +// initialization vector "iv". func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) { bin, err := n.B64.DecodeString(cipherName) if err != nil { @@ -45,16 +44,21 @@ func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error } if len(bin)%aes.BlockSize != 0 { 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, err = unPad16(bin) 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 // incorrect bytes. Kill the padding oracle by lumping everything into // 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) return plain, err