virtual folders: allow overlapped mapped paths if quota is disabled

See #95
This commit is contained in:
Nicola Murino
2020-06-10 09:11:32 +02:00
parent 7807fa7cc2
commit 8e22dd1b13
8 changed files with 192 additions and 13 deletions

View File

@@ -304,6 +304,14 @@ func (c Connection) handleSFTPRename(sourcePath, targetPath string, request *sft
if !c.isRenamePermitted(sourcePath, request) {
return sftp.ErrSSHFxPermissionDenied
}
if c.User.IsMappedPath(sourcePath) {
c.Log(logger.LevelWarn, logSender, "renaming a directory mapped as virtual folder is not allowed: %#v", sourcePath)
return sftp.ErrSSHFxPermissionDenied
}
if c.User.IsMappedPath(targetPath) {
c.Log(logger.LevelWarn, logSender, "renaming to a directory mapped as virtual folder is not allowed: %#v", targetPath)
return sftp.ErrSSHFxPermissionDenied
}
if c.User.HasVirtualFoldersInside(request.Filepath) {
if fi, err := c.fs.Stat(sourcePath); err == nil {
if fi.IsDir() {
@@ -359,6 +367,10 @@ func (c Connection) handleSFTPRmdir(dirPath string, request *sftp.Request) error
c.Log(logger.LevelWarn, logSender, "removing a directory with a virtual folder inside is not allowed: %#v", request.Filepath)
return sftp.ErrSSHFxOpUnsupported
}
if c.User.IsMappedPath(dirPath) {
c.Log(logger.LevelWarn, logSender, "removing a directory mapped as virtual folder is not allowed: %#v", dirPath)
return sftp.ErrSSHFxPermissionDenied
}
if !c.User.HasPerm(dataprovider.PermDelete, path.Dir(request.Filepath)) {
return sftp.ErrSSHFxPermissionDenied
}
@@ -399,6 +411,14 @@ func (c Connection) handleSFTPSymlink(sourcePath string, targetPath string, requ
c.Log(logger.LevelWarn, logSender, "cross folder symlink is not supported, src: %v dst: %v", request.Filepath, request.Target)
return sftp.ErrSSHFxFailure
}
if c.User.IsMappedPath(sourcePath) {
c.Log(logger.LevelWarn, logSender, "symlinking a directory mapped as virtual folder is not allowed: %#v", sourcePath)
return sftp.ErrSSHFxPermissionDenied
}
if c.User.IsMappedPath(targetPath) {
c.Log(logger.LevelWarn, logSender, "symlinking to a directory mapped as virtual folder is not allowed: %#v", targetPath)
return sftp.ErrSSHFxPermissionDenied
}
if err := c.fs.Symlink(sourcePath, targetPath); err != nil {
c.Log(logger.LevelWarn, logSender, "failed to create symlink %#v -> %#v: %+v", sourcePath, targetPath, err)
return vfs.GetSFTPError(c.fs, err)

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@@ -475,6 +476,11 @@ func loginUser(user dataprovider.User, loginMethod, publicKey string, conn ssh.C
logger.Debug(logSender, connectionID, "cannot login user %#v, login method %#v is not allowed", user.Username, loginMethod)
return nil, fmt.Errorf("Login method %#v is not allowed for user %#v", loginMethod, user.Username)
}
if dataprovider.GetQuotaTracking() > 0 && user.HasOverlappedMappedPaths() {
logger.Debug(logSender, connectionID, "cannot login user %#v, overlapping mapped folders are allowed only with quota tracking disabled",
user.Username)
return nil, errors.New("overlapping mapped folders are allowed only with quota tracking disabled")
}
remoteAddr := conn.RemoteAddr().String()
if !user.IsLoginFromAddrAllowed(remoteAddr) {
logger.Debug(logSender, connectionID, "cannot login user %#v, remote address is not allowed: %v", user.Username, remoteAddr)

View File

@@ -3547,6 +3547,124 @@ func TestVirtualFoldersLink(t *testing.T) {
assert.NoError(t, err)
}
func TestOverlappedMappedFolders(t *testing.T) {
dataProvider := dataprovider.GetProvider()
err := dataprovider.Close(dataProvider)
assert.NoError(t, err)
err = config.LoadConfig(configDir, "")
assert.NoError(t, err)
providerConf := config.GetProviderConf()
providerConf.TrackQuota = 0
err = dataprovider.Initialize(providerConf, configDir)
assert.NoError(t, err)
httpd.SetDataProvider(dataprovider.GetProvider())
sftpd.SetDataProvider(dataprovider.GetProvider())
usePubKey := false
u := getTestUser(usePubKey)
subDir := "subdir"
mappedPath1 := filepath.Join(os.TempDir(), "vdir1")
vdirPath1 := "/vdir1"
mappedPath2 := filepath.Join(os.TempDir(), "vdir1", subDir)
vdirPath2 := "/vdir2"
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
BaseVirtualFolder: vfs.BaseVirtualFolder{
MappedPath: mappedPath1,
},
VirtualPath: vdirPath1,
})
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
BaseVirtualFolder: vfs.BaseVirtualFolder{
MappedPath: mappedPath2,
},
VirtualPath: vdirPath2,
})
err = os.MkdirAll(mappedPath1, os.ModePerm)
assert.NoError(t, err)
err = os.MkdirAll(mappedPath2, os.ModePerm)
assert.NoError(t, err)
user, _, err := httpd.AddUser(u, http.StatusOK)
assert.NoError(t, err)
client, err := getSftpClient(user, usePubKey)
if assert.NoError(t, err) {
defer client.Close()
err = checkBasicSFTP(client)
assert.NoError(t, err)
testFileName := "test_file.dat"
testFileSize := int64(131072)
testFilePath := filepath.Join(homeBasePath, testFileName)
err = createTestFile(testFilePath, testFileSize)
assert.NoError(t, err)
err = sftpUploadFile(testFilePath, testFileName, testFileSize, client)
assert.NoError(t, err)
err = sftpUploadFile(testFilePath, path.Join(vdirPath1, testFileName), testFileSize, client)
assert.NoError(t, err)
err = sftpUploadFile(testFilePath, path.Join(vdirPath2, testFileName), testFileSize, client)
assert.NoError(t, err)
fi, err := client.Stat(path.Join(vdirPath1, subDir, testFileName))
if assert.NoError(t, err) {
assert.Equal(t, testFileSize, fi.Size())
}
err = client.Rename(path.Join(vdirPath1, subDir, testFileName), path.Join(vdirPath2, testFileName+"1"))
assert.NoError(t, err)
err = client.Rename(path.Join(vdirPath2, testFileName+"1"), path.Join(vdirPath1, subDir, testFileName))
assert.NoError(t, err)
err = client.Rename(path.Join(vdirPath1, subDir), path.Join(vdirPath2, subDir))
assert.Error(t, err)
err = client.Mkdir(subDir)
assert.NoError(t, err)
err = client.Rename(subDir, path.Join(vdirPath1, subDir))
assert.Error(t, err)
err = client.RemoveDirectory(path.Join(vdirPath1, subDir))
assert.Error(t, err)
err = client.Symlink(path.Join(vdirPath1, subDir), path.Join(vdirPath1, "adir"))
assert.Error(t, err)
err = client.Mkdir(path.Join(vdirPath1, subDir+"1"))
assert.NoError(t, err)
err = client.Symlink(path.Join(vdirPath1, subDir+"1"), path.Join(vdirPath1, subDir))
assert.Error(t, err)
err = os.Remove(testFilePath)
assert.NoError(t, err)
}
dataProvider = dataprovider.GetProvider()
err = dataprovider.Close(dataProvider)
assert.NoError(t, err)
err = config.LoadConfig(configDir, "")
assert.NoError(t, err)
providerConf = config.GetProviderConf()
err = dataprovider.Initialize(providerConf, configDir)
assert.NoError(t, err)
httpd.SetDataProvider(dataprovider.GetProvider())
sftpd.SetDataProvider(dataprovider.GetProvider())
if providerConf.Driver != dataprovider.MemoryDataProviderName {
client, err = getSftpClient(user, usePubKey)
if !assert.Error(t, err) {
client.Close()
}
_, err = httpd.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
_, err = httpd.RemoveFolder(vfs.BaseVirtualFolder{MappedPath: mappedPath1}, http.StatusOK)
assert.NoError(t, err)
_, err = httpd.RemoveFolder(vfs.BaseVirtualFolder{MappedPath: mappedPath2}, http.StatusOK)
assert.NoError(t, err)
}
_, _, err = httpd.AddUser(u, http.StatusOK)
assert.Error(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
err = os.RemoveAll(mappedPath1)
assert.NoError(t, err)
err = os.RemoveAll(mappedPath2)
assert.NoError(t, err)
}
func TestVirtualFolderQuotaScan(t *testing.T) {
mappedPath := filepath.Join(os.TempDir(), "mapped_dir")
err := os.MkdirAll(mappedPath, os.ModePerm)