nametransform: reject names longer than 255 chars

Looks like we allowed creating longer names by accident.
Fix that, and add a test that verifies it.
This commit is contained in:
Jakob Unterwurzacher 2019-02-17 17:05:05 +01:00
parent 26286a5f8c
commit 19cb6d046a
4 changed files with 76 additions and 9 deletions

View File

@ -38,7 +38,11 @@ func (fs *FS) openBackingDir(relPath string) (dirfd int, cName string, err error
return dirfd, ".", nil
}
name := filepath.Base(relPath)
cName = fs.nameTransform.EncryptAndHashName(name, iv)
cName, err = fs.nameTransform.EncryptAndHashName(name, iv)
if err != nil {
syscall.Close(dirfd)
return -1, "", err
}
return dirfd, cName, nil
}
// Open cipherdir (following symlinks)
@ -58,7 +62,11 @@ func (fs *FS) openBackingDir(relPath string) (dirfd int, cName string, err error
syscall.Close(dirfd)
return -1, "", err
}
cName = fs.nameTransform.EncryptAndHashName(name, iv)
cName, err = fs.nameTransform.EncryptAndHashName(name, iv)
if err != nil {
syscall.Close(dirfd)
return -1, "", err
}
// Last part? We are done.
if i == len(parts)-1 {
fs.dirCache.Store(dirRelPath, dirfd, iv)

View File

@ -8,8 +8,6 @@ import (
"path/filepath"
"syscall"
"golang.org/x/sys/unix"
"github.com/rfjakob/gocryptfs/internal/cryptocore"
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
"github.com/rfjakob/gocryptfs/internal/tlog"
@ -97,12 +95,17 @@ func WriteDirIVAt(dirfd int) error {
// encryptAndHashName encrypts "name" and hashes it to a longname if it is
// too long.
func (be *NameTransform) EncryptAndHashName(name string, iv []byte) string {
cName := be.EncryptName(name, iv)
if be.longNames && len(cName) > unix.NAME_MAX {
return be.HashLongName(cName)
// Returns ENAMETOOLONG if "name" is longer than 255 bytes.
func (be *NameTransform) EncryptAndHashName(name string, iv []byte) (string, error) {
// Prevent the user from creating files longer than 255 chars.
if len(name) > NameMax {
return "", syscall.ENAMETOOLONG
}
return cName
cName := be.EncryptName(name, iv)
if be.longNames && len(cName) > NameMax {
return be.HashLongName(cName), nil
}
return cName, nil
}
// Dir is like filepath.Dir but returns "" instead of ".".

View File

@ -12,6 +12,11 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog"
)
const (
// Like ext4, we allow at most 255 bytes for a file name.
NameMax = 255
)
// NameTransform is used to transform filenames.
type NameTransform struct {
emeCipher *eme.EMECipher

View File

@ -377,6 +377,57 @@ func TestRename(t *testing.T) {
test_helpers.TestRename(t, test_helpers.DefaultPlainDir)
}
// Test that names of all lengths work
func TestNameLengths(t *testing.T) {
f, err := os.Open(test_helpers.DefaultPlainDir)
if err != nil {
t.Fatal(err)
}
entries, err := f.Readdirnames(0)
if err != nil {
t.Fatal(err)
}
f.Close()
cnt1 := len(entries)
wd := test_helpers.DefaultPlainDir + "/"
name := "x"
for len(name) < 2000 {
f, err := os.Create(wd + name + "x")
if err != nil {
break
}
name = name + "x"
f.Close()
f, err = os.Open(test_helpers.DefaultPlainDir)
if err != nil {
t.Fatal(err)
}
// In v1.7-rc2, we had a bug that allowed creation of too-long names.
// This threw errors in like this in READDIR:
//
// OpenDir ".": invalid entry "gocryptfs.longname.wrE-izsR9ciEkP7JSCFDrk_d_Nj4mQo1dGY6hjuixAU=":
// Could not read .name: ReadLongName: size=345 > limit=344
//
entries, err = f.Readdirnames(0)
if err != nil {
t.Fatal(err)
}
f.Close()
cnt2 := len(entries)
if cnt2 != cnt1+1 {
t.Fatalf("len=%d: expected %d dir entries, have %d: %v", len(name), cnt1+1, cnt2, entries)
}
err = syscall.Unlink(wd + name)
if err != nil {
t.Fatal(err)
}
}
if len(name) != 255 {
t.Errorf("maxlen=%d", len(name))
}
}
func TestLongNames(t *testing.T) {
fi, err := ioutil.ReadDir(test_helpers.DefaultCipherDir)
if err != nil {