瀏覽代碼

check permissions against sftp path

instead of building filesystem paths and then checking permissions against
path relative to the home dir that is the initial sftp path
Nicola Murino 5 年之前
父節點
當前提交
e046b35b97
共有 6 個文件被更改,包括 111 次插入104 次删除
  1. 10 8
      dataprovider/user.go
  2. 55 53
      sftpd/handler.go
  3. 20 7
      sftpd/internal_test.go
  4. 9 19
      sftpd/scp.go
  5. 14 14
      sftpd/sftpd_test.go
  6. 3 3
      sftpd/ssh_cmd.go

+ 10 - 8
dataprovider/user.go

@@ -100,7 +100,8 @@ type User struct {
 	Filters UserFilters `json:"filters"`
 }
 
-// GetPermissionsForPath returns the permissions for the given path
+// GetPermissionsForPath returns the permissions for the given path.
+// The path must be an SFTP path
 func (u *User) GetPermissionsForPath(p string) []string {
 	permissions := []string{}
 	if perms, ok := u.Permissions["/"]; ok {
@@ -111,17 +112,18 @@ func (u *User) GetPermissionsForPath(p string) []string {
 		// fallback permissions
 		permissions = perms
 	}
-	relPath := u.GetRelativePath(p)
-	if len(relPath) == 0 {
-		relPath = "/"
+	sftpPath := filepath.ToSlash(p)
+	if !path.IsAbs(p) {
+		sftpPath = "/" + sftpPath
 	}
-	dirsForPath := []string{relPath}
+	sftpPath = path.Clean(sftpPath)
+	dirsForPath := []string{sftpPath}
 	for {
-		if relPath == "/" {
+		if sftpPath == "/" {
 			break
 		}
-		relPath = path.Dir(relPath)
-		dirsForPath = append(dirsForPath, relPath)
+		sftpPath = path.Dir(sftpPath)
+		dirsForPath = append(dirsForPath, sftpPath)
 	}
 	// dirsForPath contains all the dirs for a given path in reverse order
 	// for example if the path is: /1/2/3/4 it contains:

+ 55 - 53
sftpd/handler.go

@@ -6,6 +6,7 @@ import (
 	"io/ioutil"
 	"net"
 	"os"
+	"path"
 	"path/filepath"
 	"strings"
 	"sync"
@@ -51,13 +52,14 @@ func (c Connection) Log(level logger.LogLevel, sender string, format string, v .
 func (c Connection) Fileread(request *sftp.Request) (io.ReaderAt, error) {
 	updateConnectionActivity(c.ID)
 
+	if !c.User.HasPerm(dataprovider.PermDownload, path.Dir(request.Filepath)) {
+		return nil, sftp.ErrSSHFxPermissionDenied
+	}
+
 	p, err := c.buildPath(request.Filepath)
 	if err != nil {
 		return nil, getSFTPErrorFromOSError(err)
 	}
-	if !c.User.HasPerm(dataprovider.PermDownload, filepath.Dir(p)) {
-		return nil, sftp.ErrSSHFxPermissionDenied
-	}
 
 	c.lock.Lock()
 	defer c.lock.Unlock()
@@ -112,7 +114,7 @@ func (c Connection) Filewrite(request *sftp.Request) (io.WriterAt, error) {
 
 	stat, statErr := os.Stat(p)
 	if os.IsNotExist(statErr) {
-		if !c.User.HasPerm(dataprovider.PermUpload, filepath.Dir(p)) {
+		if !c.User.HasPerm(dataprovider.PermUpload, path.Dir(request.Filepath)) {
 			return nil, sftp.ErrSSHFxPermissionDenied
 		}
 		return c.handleSFTPUploadToNewFile(p, filePath)
@@ -129,7 +131,7 @@ func (c Connection) Filewrite(request *sftp.Request) (io.WriterAt, error) {
 		return nil, sftp.ErrSSHFxOpUnsupported
 	}
 
-	if !c.User.HasPerm(dataprovider.PermOverwrite, filepath.Dir(filePath)) {
+	if !c.User.HasPerm(dataprovider.PermOverwrite, path.Dir(request.Filepath)) {
 		return nil, sftp.ErrSSHFxPermissionDenied
 	}
 
@@ -157,26 +159,26 @@ func (c Connection) Filecmd(request *sftp.Request) error {
 	case "Setstat":
 		return c.handleSFTPSetstat(p, request)
 	case "Rename":
-		if err = c.handleSFTPRename(p, target); err != nil {
+		if err = c.handleSFTPRename(p, target, request); err != nil {
 			return err
 		}
 		break
 	case "Rmdir":
-		return c.handleSFTPRmdir(p)
+		return c.handleSFTPRmdir(p, request)
 
 	case "Mkdir":
-		err = c.handleSFTPMkdir(p)
+		err = c.handleSFTPMkdir(p, request)
 		if err != nil {
 			return err
 		}
 		break
 	case "Symlink":
-		if err = c.handleSFTPSymlink(p, target); err != nil {
+		if err = c.handleSFTPSymlink(p, target, request); err != nil {
 			return err
 		}
 		break
 	case "Remove":
-		return c.handleSFTPRemove(p)
+		return c.handleSFTPRemove(p, request)
 
 	default:
 		return sftp.ErrSSHFxOpUnsupported
@@ -204,7 +206,7 @@ func (c Connection) Filelist(request *sftp.Request) (sftp.ListerAt, error) {
 
 	switch request.Method {
 	case "List":
-		if !c.User.HasPerm(dataprovider.PermListItems, p) {
+		if !c.User.HasPerm(dataprovider.PermListItems, request.Filepath) {
 			return nil, sftp.ErrSSHFxPermissionDenied
 		}
 
@@ -218,7 +220,7 @@ func (c Connection) Filelist(request *sftp.Request) (sftp.ListerAt, error) {
 
 		return listerAt(files), nil
 	case "Stat":
-		if !c.User.HasPerm(dataprovider.PermListItems, filepath.Dir(p)) {
+		if !c.User.HasPerm(dataprovider.PermListItems, path.Dir(request.Filepath)) {
 			return nil, sftp.ErrSSHFxPermissionDenied
 		}
 
@@ -249,7 +251,7 @@ func (c Connection) getSFTPCmdTargetPath(requestTarget string) (string, error) {
 	return target, nil
 }
 
-func (c Connection) handleSFTPSetstat(path string, request *sftp.Request) error {
+func (c Connection) handleSFTPSetstat(filePath string, request *sftp.Request) error {
 	if setstatMode == 1 {
 		return nil
 	}
@@ -258,10 +260,10 @@ func (c Connection) handleSFTPSetstat(path string, request *sftp.Request) error
 			c.ClientVersion)
 		return sftp.ErrSSHFxBadMessage
 	}
-	pathForPerms := path
-	if fi, err := os.Lstat(path); err == nil {
+	pathForPerms := request.Filepath
+	if fi, err := os.Lstat(filePath); err == nil {
 		if fi.IsDir() {
-			pathForPerms = filepath.Dir(path)
+			pathForPerms = path.Dir(request.Filepath)
 		}
 	}
 	attrFlags := request.AttrFlags()
@@ -270,11 +272,11 @@ func (c Connection) handleSFTPSetstat(path string, request *sftp.Request) error
 			return sftp.ErrSSHFxPermissionDenied
 		}
 		fileMode := request.Attributes().FileMode()
-		if err := os.Chmod(path, fileMode); err != nil {
-			c.Log(logger.LevelWarn, logSender, "failed to chmod path %#v, mode: %v, err: %v", path, fileMode.String(), err)
+		if err := os.Chmod(filePath, fileMode); err != nil {
+			c.Log(logger.LevelWarn, logSender, "failed to chmod path %#v, mode: %v, err: %v", filePath, fileMode.String(), err)
 			return getSFTPErrorFromOSError(err)
 		}
-		logger.CommandLog(chmodLogSender, path, "", c.User.Username, fileMode.String(), c.ID, c.protocol, -1, -1, "", "", "")
+		logger.CommandLog(chmodLogSender, filePath, "", c.User.Username, fileMode.String(), c.ID, c.protocol, -1, -1, "", "", "")
 		return nil
 	} else if attrFlags.UidGid {
 		if !c.User.HasPerm(dataprovider.PermChown, pathForPerms) {
@@ -282,11 +284,11 @@ func (c Connection) handleSFTPSetstat(path string, request *sftp.Request) error
 		}
 		uid := int(request.Attributes().UID)
 		gid := int(request.Attributes().GID)
-		if err := os.Chown(path, uid, gid); err != nil {
-			c.Log(logger.LevelWarn, logSender, "failed to chown path %#v, uid: %v, gid: %v, err: %v", path, uid, gid, err)
+		if err := os.Chown(filePath, uid, gid); err != nil {
+			c.Log(logger.LevelWarn, logSender, "failed to chown path %#v, uid: %v, gid: %v, err: %v", filePath, uid, gid, err)
 			return getSFTPErrorFromOSError(err)
 		}
-		logger.CommandLog(chownLogSender, path, "", c.User.Username, "", c.ID, c.protocol, uid, gid, "", "", "")
+		logger.CommandLog(chownLogSender, filePath, "", c.User.Username, "", c.ID, c.protocol, uid, gid, "", "", "")
 		return nil
 	} else if attrFlags.Acmodtime {
 		if !c.User.HasPerm(dataprovider.PermChtimes, pathForPerms) {
@@ -297,24 +299,24 @@ func (c Connection) handleSFTPSetstat(path string, request *sftp.Request) error
 		modificationTime := time.Unix(int64(request.Attributes().Mtime), 0)
 		accessTimeString := accessTime.Format(dateFormat)
 		modificationTimeString := modificationTime.Format(dateFormat)
-		if err := os.Chtimes(path, accessTime, modificationTime); err != nil {
+		if err := os.Chtimes(filePath, accessTime, modificationTime); err != nil {
 			c.Log(logger.LevelWarn, logSender, "failed to chtimes for path %#v, access time: %v, modification time: %v, err: %v",
-				path, accessTime, modificationTime, err)
+				filePath, accessTime, modificationTime, err)
 			return getSFTPErrorFromOSError(err)
 		}
-		logger.CommandLog(chtimesLogSender, path, "", c.User.Username, "", c.ID, c.protocol, -1, -1, accessTimeString,
+		logger.CommandLog(chtimesLogSender, filePath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, accessTimeString,
 			modificationTimeString, "")
 		return nil
 	}
 	return nil
 }
 
-func (c Connection) handleSFTPRename(sourcePath string, targetPath string) error {
+func (c Connection) handleSFTPRename(sourcePath string, targetPath string, request *sftp.Request) error {
 	if c.User.GetRelativePath(sourcePath) == "/" {
 		c.Log(logger.LevelWarn, logSender, "renaming root dir is not allowed")
 		return sftp.ErrSSHFxPermissionDenied
 	}
-	if !c.User.HasPerm(dataprovider.PermRename, filepath.Dir(targetPath)) {
+	if !c.User.HasPerm(dataprovider.PermRename, path.Dir(request.Target)) {
 		return sftp.ErrSSHFxPermissionDenied
 	}
 	if err := os.Rename(sourcePath, targetPath); err != nil {
@@ -326,41 +328,41 @@ func (c Connection) handleSFTPRename(sourcePath string, targetPath string) error
 	return nil
 }
 
-func (c Connection) handleSFTPRmdir(path string) error {
-	if c.User.GetRelativePath(path) == "/" {
+func (c Connection) handleSFTPRmdir(dirPath string, request *sftp.Request) error {
+	if c.User.GetRelativePath(dirPath) == "/" {
 		c.Log(logger.LevelWarn, logSender, "removing root dir is not allowed")
 		return sftp.ErrSSHFxPermissionDenied
 	}
-	if !c.User.HasPerm(dataprovider.PermDelete, filepath.Dir(path)) {
+	if !c.User.HasPerm(dataprovider.PermDelete, path.Dir(request.Filepath)) {
 		return sftp.ErrSSHFxPermissionDenied
 	}
 
 	var fi os.FileInfo
 	var err error
-	if fi, err = os.Lstat(path); err != nil {
-		c.Log(logger.LevelWarn, logSender, "failed to remove a dir %#v: stat error: %v", path, err)
+	if fi, err = os.Lstat(dirPath); err != nil {
+		c.Log(logger.LevelWarn, logSender, "failed to remove a dir %#v: stat error: %v", dirPath, err)
 		return getSFTPErrorFromOSError(err)
 	}
 	if !fi.IsDir() || fi.Mode()&os.ModeSymlink == os.ModeSymlink {
-		c.Log(logger.LevelDebug, logSender, "cannot remove %#v is not a directory", path)
+		c.Log(logger.LevelDebug, logSender, "cannot remove %#v is not a directory", dirPath)
 		return sftp.ErrSSHFxFailure
 	}
 
-	if err = os.Remove(path); err != nil {
-		c.Log(logger.LevelWarn, logSender, "failed to remove directory %#v: %v", path, err)
+	if err = os.Remove(dirPath); err != nil {
+		c.Log(logger.LevelWarn, logSender, "failed to remove directory %#v: %v", dirPath, err)
 		return getSFTPErrorFromOSError(err)
 	}
 
-	logger.CommandLog(rmdirLogSender, path, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
+	logger.CommandLog(rmdirLogSender, dirPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
 	return sftp.ErrSSHFxOk
 }
 
-func (c Connection) handleSFTPSymlink(sourcePath string, targetPath string) error {
+func (c Connection) handleSFTPSymlink(sourcePath string, targetPath string, request *sftp.Request) error {
 	if c.User.GetRelativePath(sourcePath) == "/" {
 		c.Log(logger.LevelWarn, logSender, "symlinking root dir is not allowed")
 		return sftp.ErrSSHFxPermissionDenied
 	}
-	if !c.User.HasPerm(dataprovider.PermCreateSymlinks, filepath.Dir(targetPath)) {
+	if !c.User.HasPerm(dataprovider.PermCreateSymlinks, path.Dir(request.Target)) {
 		return sftp.ErrSSHFxPermissionDenied
 	}
 	if err := os.Symlink(sourcePath, targetPath); err != nil {
@@ -372,47 +374,47 @@ func (c Connection) handleSFTPSymlink(sourcePath string, targetPath string) erro
 	return nil
 }
 
-func (c Connection) handleSFTPMkdir(path string) error {
-	if !c.User.HasPerm(dataprovider.PermCreateDirs, filepath.Dir(path)) {
+func (c Connection) handleSFTPMkdir(dirPath string, request *sftp.Request) error {
+	if !c.User.HasPerm(dataprovider.PermCreateDirs, path.Dir(request.Filepath)) {
 		return sftp.ErrSSHFxPermissionDenied
 	}
-	if err := os.Mkdir(path, 0777); err != nil {
-		c.Log(logger.LevelWarn, logSender, "error creating missing dir: %#v error: %v", path, err)
+	if err := os.Mkdir(dirPath, 0777); err != nil {
+		c.Log(logger.LevelWarn, logSender, "error creating missing dir: %#v error: %v", dirPath, err)
 		return getSFTPErrorFromOSError(err)
 	}
-	utils.SetPathPermissions(path, c.User.GetUID(), c.User.GetGID())
+	utils.SetPathPermissions(dirPath, c.User.GetUID(), c.User.GetGID())
 
-	logger.CommandLog(mkdirLogSender, path, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
+	logger.CommandLog(mkdirLogSender, dirPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
 	return nil
 }
 
-func (c Connection) handleSFTPRemove(path string) error {
-	if !c.User.HasPerm(dataprovider.PermDelete, filepath.Dir(path)) {
+func (c Connection) handleSFTPRemove(filePath string, request *sftp.Request) error {
+	if !c.User.HasPerm(dataprovider.PermDelete, path.Dir(request.Filepath)) {
 		return sftp.ErrSSHFxPermissionDenied
 	}
 
 	var size int64
 	var fi os.FileInfo
 	var err error
-	if fi, err = os.Lstat(path); err != nil {
-		c.Log(logger.LevelWarn, logSender, "failed to remove a file %#v: stat error: %v", path, err)
+	if fi, err = os.Lstat(filePath); err != nil {
+		c.Log(logger.LevelWarn, logSender, "failed to remove a file %#v: stat error: %v", filePath, err)
 		return getSFTPErrorFromOSError(err)
 	}
 	if fi.IsDir() && fi.Mode()&os.ModeSymlink != os.ModeSymlink {
-		c.Log(logger.LevelDebug, logSender, "cannot remove %#v is not a file/symlink", path)
+		c.Log(logger.LevelDebug, logSender, "cannot remove %#v is not a file/symlink", filePath)
 		return sftp.ErrSSHFxFailure
 	}
 	size = fi.Size()
-	if err := os.Remove(path); err != nil {
-		c.Log(logger.LevelWarn, logSender, "failed to remove a file/symlink %#v: %v", path, err)
+	if err := os.Remove(filePath); err != nil {
+		c.Log(logger.LevelWarn, logSender, "failed to remove a file/symlink %#v: %v", filePath, err)
 		return getSFTPErrorFromOSError(err)
 	}
 
-	logger.CommandLog(removeLogSender, path, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
+	logger.CommandLog(removeLogSender, filePath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "")
 	if fi.Mode()&os.ModeSymlink != os.ModeSymlink {
 		dataprovider.UpdateUserQuota(dataProvider, c.User, -1, -size, false)
 	}
-	go executeAction(operationDelete, c.User.Username, path, "", "")
+	go executeAction(operationDelete, c.User.Username, filePath, "", "")
 
 	return sftp.ErrSSHFxOk
 }

+ 20 - 7
sftpd/internal_test.go

@@ -310,6 +310,26 @@ func TestSSHCommandPath(t *testing.T) {
 	if path != "/" {
 		t.Errorf("unexpected path: %v", path)
 	}
+	sshCommand.args = []string{"-t", "."}
+	path = sshCommand.getDestPath()
+	if path != "/" {
+		t.Errorf("unexpected path: %v", path)
+	}
+	sshCommand.args = []string{"-t", "//"}
+	path = sshCommand.getDestPath()
+	if path != "/" {
+		t.Errorf("unexpected path: %v", path)
+	}
+	sshCommand.args = []string{"-t", "../.."}
+	path = sshCommand.getDestPath()
+	if path != "/" {
+		t.Errorf("unexpected path: %v", path)
+	}
+	sshCommand.args = []string{"-t", "/.."}
+	path = sshCommand.getDestPath()
+	if path != "/" {
+		t.Errorf("unexpected path: %v", path)
+	}
 }
 
 func TestSSHCommandErrors(t *testing.T) {
@@ -636,13 +656,6 @@ func TestSCPFileMode(t *testing.T) {
 	}
 }
 
-func TestSCPGetNonExistingDirContent(t *testing.T) {
-	_, err := getDirContents("non_existing")
-	if err == nil {
-		t.Errorf("get non existing dir contents must fail")
-	}
-}
-
 func TestSCPParseUploadMessage(t *testing.T) {
 	buf := make([]byte, 65535)
 	stdErrBuf := make([]byte, 65535)

+ 9 - 19
sftpd/scp.go

@@ -3,6 +3,7 @@ package sftpd
 import (
 	"fmt"
 	"io"
+	"io/ioutil"
 	"math"
 	"os"
 	"path"
@@ -83,7 +84,7 @@ func (c *scpCommand) handleRecursiveUpload() error {
 				}
 			} else {
 				// the destination dir is now the parent directory
-				destPath = filepath.Join(destPath, "..")
+				destPath = path.Join(destPath, "..")
 			}
 		} else {
 			sizeToRead, name, err := c.parseUploadMessage(command)
@@ -120,7 +121,7 @@ func (c *scpCommand) handleCreateDir(dirPath string) error {
 		c.sendErrorMessage(err.Error())
 		return err
 	}
-	if !c.connection.User.HasPerm(dataprovider.PermCreateDirs, filepath.Dir(p)) {
+	if !c.connection.User.HasPerm(dataprovider.PermCreateDirs, path.Dir(dirPath)) {
 		err := fmt.Errorf("Permission denied")
 		c.connection.Log(logger.LevelWarn, logSenderSCP, "error creating dir: %#v, permission denied", dirPath)
 		c.sendErrorMessage(err.Error())
@@ -234,7 +235,7 @@ func (c *scpCommand) handleUpload(uploadFilePath string, sizeToRead int64) error
 	}
 	stat, statErr := os.Stat(p)
 	if os.IsNotExist(statErr) {
-		if !c.connection.User.HasPerm(dataprovider.PermUpload, filepath.Dir(p)) {
+		if !c.connection.User.HasPerm(dataprovider.PermUpload, path.Dir(uploadFilePath)) {
 			err := fmt.Errorf("Permission denied")
 			c.connection.Log(logger.LevelWarn, logSenderSCP, "cannot upload file: %#v, permission denied", uploadFilePath)
 			c.sendErrorMessage(err.Error())
@@ -256,7 +257,7 @@ func (c *scpCommand) handleUpload(uploadFilePath string, sizeToRead int64) error
 		return err
 	}
 
-	if !c.connection.User.HasPerm(dataprovider.PermOverwrite, filePath) {
+	if !c.connection.User.HasPerm(dataprovider.PermOverwrite, uploadFilePath) {
 		err := fmt.Errorf("Permission denied")
 		c.connection.Log(logger.LevelWarn, logSenderSCP, "cannot overwrite file: %#v, permission denied", uploadFilePath)
 		c.sendErrorMessage(err.Error())
@@ -302,7 +303,7 @@ func (c *scpCommand) sendDownloadProtocolMessages(dirPath string, stat os.FileIn
 	return err
 }
 
-// we send first all the files in the roor directory and then the directories
+// we send first all the files in the root directory and then the directories
 // for each directory we recursively call this method again
 func (c *scpCommand) handleRecursiveDownload(dirPath string, stat os.FileInfo) error {
 	var err error
@@ -312,7 +313,7 @@ func (c *scpCommand) handleRecursiveDownload(dirPath string, stat os.FileInfo) e
 		if err != nil {
 			return err
 		}
-		files, err := getDirContents(dirPath)
+		files, err := ioutil.ReadDir(dirPath)
 		if err != nil {
 			c.sendErrorMessage(err.Error())
 			return err
@@ -431,7 +432,7 @@ func (c *scpCommand) handleDownload(filePath string) error {
 	}
 
 	if stat.IsDir() {
-		if !c.connection.User.HasPerm(dataprovider.PermDownload, p) {
+		if !c.connection.User.HasPerm(dataprovider.PermDownload, filePath) {
 			err := fmt.Errorf("Permission denied")
 			c.connection.Log(logger.LevelWarn, logSenderSCP, "error downloading dir: %#v, permission denied", filePath)
 			c.sendErrorMessage(err.Error())
@@ -441,7 +442,7 @@ func (c *scpCommand) handleDownload(filePath string) error {
 		return err
 	}
 
-	if !c.connection.User.HasPerm(dataprovider.PermDownload, filepath.Dir(p)) {
+	if !c.connection.User.HasPerm(dataprovider.PermDownload, path.Dir(filePath)) {
 		err := fmt.Errorf("Permission denied")
 		c.connection.Log(logger.LevelWarn, logSenderSCP, "error downloading dir: %#v, permission denied", filePath)
 		c.sendErrorMessage(err.Error())
@@ -732,14 +733,3 @@ func getFileModeAsString(fileMode os.FileMode, isDir bool) string {
 	}
 	return fmt.Sprintf("%v%v%v%v", s, u, g, o)
 }
-
-func getDirContents(path string) ([]os.FileInfo, error) {
-	var files []os.FileInfo
-	f, err := os.Open(path)
-	if err != nil {
-		return files, err
-	}
-	files, err = f.Readdir(-1)
-	f.Close()
-	return files, err
-}

+ 14 - 14
sftpd/sftpd_test.go

@@ -2589,48 +2589,48 @@ func TestUserPerms(t *testing.T) {
 	user.Permissions["/p/3"] = []string{dataprovider.PermChmod}
 	user.Permissions["/p/3/4"] = []string{dataprovider.PermChtimes}
 	user.Permissions["/tmp"] = []string{dataprovider.PermRename}
-	if !user.HasPerm(dataprovider.PermListItems, filepath.Join(user.HomeDir, "/")) {
+	if !user.HasPerm(dataprovider.PermListItems, "/") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermListItems, filepath.Join(user.HomeDir, ".")) {
+	if !user.HasPerm(dataprovider.PermListItems, ".") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermListItems, filepath.Join(user.HomeDir, "")) {
+	if !user.HasPerm(dataprovider.PermListItems, "") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermListItems, filepath.Join(user.HomeDir, "../")) {
+	if !user.HasPerm(dataprovider.PermListItems, "../") {
 		t.Error("expected permission not found")
 	}
 	// path p and /p are the same
-	if !user.HasPerm(dataprovider.PermDelete, filepath.Join(user.HomeDir, "/p")) {
+	if !user.HasPerm(dataprovider.PermDelete, "/p") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermDownload, filepath.Join(user.HomeDir, "/p/1")) {
+	if !user.HasPerm(dataprovider.PermDownload, "/p/1") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermCreateDirs, filepath.Join(user.HomeDir, "p/2")) {
+	if !user.HasPerm(dataprovider.PermCreateDirs, "p/2") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermChmod, filepath.Join(user.HomeDir, "/p/3")) {
+	if !user.HasPerm(dataprovider.PermChmod, "/p/3") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermChtimes, filepath.Join(user.HomeDir, "p/3/4")) {
+	if !user.HasPerm(dataprovider.PermChtimes, "p/3/4/") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermChtimes, filepath.Join(user.HomeDir, "p/3/4/../4")) {
+	if !user.HasPerm(dataprovider.PermChtimes, "p/3/4/../4") {
 		t.Error("expected permission not found")
 	}
 	// undefined paths have permissions of the nearest path
-	if !user.HasPerm(dataprovider.PermListItems, filepath.Join(user.HomeDir, "/p34")) {
+	if !user.HasPerm(dataprovider.PermListItems, "/p34") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermListItems, filepath.Join(user.HomeDir, "/p34/p1/file.dat")) {
+	if !user.HasPerm(dataprovider.PermListItems, "/p34/p1/file.dat") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermChtimes, filepath.Join(user.HomeDir, "/p/3/4/5/6")) {
+	if !user.HasPerm(dataprovider.PermChtimes, "/p/3/4/5/6") {
 		t.Error("expected permission not found")
 	}
-	if !user.HasPerm(dataprovider.PermDownload, filepath.Join(user.HomeDir, "/p/1/test/file.dat")) {
+	if !user.HasPerm(dataprovider.PermDownload, "/p/1/test/file.dat") {
 		t.Error("expected permission not found")
 	}
 }

+ 3 - 3
sftpd/ssh_cmd.go

@@ -129,7 +129,7 @@ func (c *sshCommand) handleHashCommands() error {
 		if err != nil {
 			return c.sendErrorResponse(err)
 		}
-		if !c.connection.User.HasPerm(dataprovider.PermListItems, path) {
+		if !c.connection.User.HasPerm(dataprovider.PermListItems, sshPath) {
 			return c.sendErrorResponse(errPermissionDenied)
 		}
 		hash, err := computeHashForFile(h, path)
@@ -149,7 +149,7 @@ func (c *sshCommand) executeSystemCommand(command systemCommand) error {
 	}
 	perms := []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermCreateDirs, dataprovider.PermListItems,
 		dataprovider.PermOverwrite, dataprovider.PermDelete, dataprovider.PermRename}
-	if !c.connection.User.HasPerms(perms, command.realPath) {
+	if !c.connection.User.HasPerms(perms, c.getDestPath()) {
 		return c.sendErrorResponse(errPermissionDenied)
 	}
 
@@ -299,7 +299,7 @@ func (c *sshCommand) getSystemCommand() (systemCommand, error) {
 		// the home dir.
 		// If the user cannot create symlinks we add the option --munge-links, if it is not
 		// already set. This should make symlinks unusable (but manually recoverable)
-		if c.connection.User.HasPerm(dataprovider.PermCreateSymlinks, path) {
+		if c.connection.User.HasPerm(dataprovider.PermCreateSymlinks, c.getDestPath()) {
 			if !utils.IsStringInSlice("--safe-links", args) {
 				args = append([]string{"--safe-links"}, args...)
 			}