Selaa lähdekoodia

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 vuotta sitten
vanhempi
commit
e046b35b97
6 muutettua tiedostoa jossa 111 lisäystä ja 104 poistoa
  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...)
 			}