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:
parent
26286a5f8c
commit
19cb6d046a
@ -38,7 +38,11 @@ func (fs *FS) openBackingDir(relPath string) (dirfd int, cName string, err error
|
|||||||
return dirfd, ".", nil
|
return dirfd, ".", nil
|
||||||
}
|
}
|
||||||
name := filepath.Base(relPath)
|
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
|
return dirfd, cName, nil
|
||||||
}
|
}
|
||||||
// Open cipherdir (following symlinks)
|
// Open cipherdir (following symlinks)
|
||||||
@ -58,7 +62,11 @@ func (fs *FS) openBackingDir(relPath string) (dirfd int, cName string, err error
|
|||||||
syscall.Close(dirfd)
|
syscall.Close(dirfd)
|
||||||
return -1, "", err
|
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.
|
// Last part? We are done.
|
||||||
if i == len(parts)-1 {
|
if i == len(parts)-1 {
|
||||||
fs.dirCache.Store(dirRelPath, dirfd, iv)
|
fs.dirCache.Store(dirRelPath, dirfd, iv)
|
||||||
|
@ -8,8 +8,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"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
|
// encryptAndHashName encrypts "name" and hashes it to a longname if it is
|
||||||
// too long.
|
// too long.
|
||||||
func (be *NameTransform) EncryptAndHashName(name string, iv []byte) string {
|
// Returns ENAMETOOLONG if "name" is longer than 255 bytes.
|
||||||
cName := be.EncryptName(name, iv)
|
func (be *NameTransform) EncryptAndHashName(name string, iv []byte) (string, error) {
|
||||||
if be.longNames && len(cName) > unix.NAME_MAX {
|
// Prevent the user from creating files longer than 255 chars.
|
||||||
return be.HashLongName(cName)
|
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 ".".
|
// Dir is like filepath.Dir but returns "" instead of ".".
|
||||||
|
@ -12,6 +12,11 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"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.
|
// NameTransform is used to transform filenames.
|
||||||
type NameTransform struct {
|
type NameTransform struct {
|
||||||
emeCipher *eme.EMECipher
|
emeCipher *eme.EMECipher
|
||||||
|
@ -377,6 +377,57 @@ func TestRename(t *testing.T) {
|
|||||||
test_helpers.TestRename(t, test_helpers.DefaultPlainDir)
|
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) {
|
func TestLongNames(t *testing.T) {
|
||||||
fi, err := ioutil.ReadDir(test_helpers.DefaultCipherDir)
|
fi, err := ioutil.ReadDir(test_helpers.DefaultCipherDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user