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
This commit is contained in:
Nicola Murino 2020-01-05 11:41:25 +01:00
parent eb2ddc4798
commit e046b35b97
6 changed files with 111 additions and 104 deletions

View file

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

View file

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

View file

@ -310,6 +310,26 @@ func TestSSHCommandPath(t *testing.T) {
if path != "/" { if path != "/" {
t.Errorf("unexpected path: %v", 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) { 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) { func TestSCPParseUploadMessage(t *testing.T) {
buf := make([]byte, 65535) buf := make([]byte, 65535)
stdErrBuf := make([]byte, 65535) stdErrBuf := make([]byte, 65535)

View file

@ -3,6 +3,7 @@ package sftpd
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"math" "math"
"os" "os"
"path" "path"
@ -83,7 +84,7 @@ func (c *scpCommand) handleRecursiveUpload() error {
} }
} else { } else {
// the destination dir is now the parent directory // the destination dir is now the parent directory
destPath = filepath.Join(destPath, "..") destPath = path.Join(destPath, "..")
} }
} else { } else {
sizeToRead, name, err := c.parseUploadMessage(command) sizeToRead, name, err := c.parseUploadMessage(command)
@ -120,7 +121,7 @@ func (c *scpCommand) handleCreateDir(dirPath string) error {
c.sendErrorMessage(err.Error()) c.sendErrorMessage(err.Error())
return err 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") err := fmt.Errorf("Permission denied")
c.connection.Log(logger.LevelWarn, logSenderSCP, "error creating dir: %#v, permission denied", dirPath) c.connection.Log(logger.LevelWarn, logSenderSCP, "error creating dir: %#v, permission denied", dirPath)
c.sendErrorMessage(err.Error()) c.sendErrorMessage(err.Error())
@ -234,7 +235,7 @@ func (c *scpCommand) handleUpload(uploadFilePath string, sizeToRead int64) error
} }
stat, statErr := os.Stat(p) stat, statErr := os.Stat(p)
if os.IsNotExist(statErr) { 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") err := fmt.Errorf("Permission denied")
c.connection.Log(logger.LevelWarn, logSenderSCP, "cannot upload file: %#v, permission denied", uploadFilePath) c.connection.Log(logger.LevelWarn, logSenderSCP, "cannot upload file: %#v, permission denied", uploadFilePath)
c.sendErrorMessage(err.Error()) c.sendErrorMessage(err.Error())
@ -256,7 +257,7 @@ func (c *scpCommand) handleUpload(uploadFilePath string, sizeToRead int64) error
return err return err
} }
if !c.connection.User.HasPerm(dataprovider.PermOverwrite, filePath) { if !c.connection.User.HasPerm(dataprovider.PermOverwrite, uploadFilePath) {
err := fmt.Errorf("Permission denied") err := fmt.Errorf("Permission denied")
c.connection.Log(logger.LevelWarn, logSenderSCP, "cannot overwrite file: %#v, permission denied", uploadFilePath) c.connection.Log(logger.LevelWarn, logSenderSCP, "cannot overwrite file: %#v, permission denied", uploadFilePath)
c.sendErrorMessage(err.Error()) c.sendErrorMessage(err.Error())
@ -302,7 +303,7 @@ func (c *scpCommand) sendDownloadProtocolMessages(dirPath string, stat os.FileIn
return err 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 // for each directory we recursively call this method again
func (c *scpCommand) handleRecursiveDownload(dirPath string, stat os.FileInfo) error { func (c *scpCommand) handleRecursiveDownload(dirPath string, stat os.FileInfo) error {
var err error var err error
@ -312,7 +313,7 @@ func (c *scpCommand) handleRecursiveDownload(dirPath string, stat os.FileInfo) e
if err != nil { if err != nil {
return err return err
} }
files, err := getDirContents(dirPath) files, err := ioutil.ReadDir(dirPath)
if err != nil { if err != nil {
c.sendErrorMessage(err.Error()) c.sendErrorMessage(err.Error())
return err return err
@ -431,7 +432,7 @@ func (c *scpCommand) handleDownload(filePath string) error {
} }
if stat.IsDir() { if stat.IsDir() {
if !c.connection.User.HasPerm(dataprovider.PermDownload, p) { if !c.connection.User.HasPerm(dataprovider.PermDownload, filePath) {
err := fmt.Errorf("Permission denied") err := fmt.Errorf("Permission denied")
c.connection.Log(logger.LevelWarn, logSenderSCP, "error downloading dir: %#v, permission denied", filePath) c.connection.Log(logger.LevelWarn, logSenderSCP, "error downloading dir: %#v, permission denied", filePath)
c.sendErrorMessage(err.Error()) c.sendErrorMessage(err.Error())
@ -441,7 +442,7 @@ func (c *scpCommand) handleDownload(filePath string) error {
return err 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") err := fmt.Errorf("Permission denied")
c.connection.Log(logger.LevelWarn, logSenderSCP, "error downloading dir: %#v, permission denied", filePath) c.connection.Log(logger.LevelWarn, logSenderSCP, "error downloading dir: %#v, permission denied", filePath)
c.sendErrorMessage(err.Error()) 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) 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
}

View file

@ -2589,48 +2589,48 @@ func TestUserPerms(t *testing.T) {
user.Permissions["/p/3"] = []string{dataprovider.PermChmod} user.Permissions["/p/3"] = []string{dataprovider.PermChmod}
user.Permissions["/p/3/4"] = []string{dataprovider.PermChtimes} user.Permissions["/p/3/4"] = []string{dataprovider.PermChtimes}
user.Permissions["/tmp"] = []string{dataprovider.PermRename} 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") 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") 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") 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") t.Error("expected permission not found")
} }
// path p and /p are the same // 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") 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") 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") 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") 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") 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") t.Error("expected permission not found")
} }
// undefined paths have permissions of the nearest path // 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") 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") 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") 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") t.Error("expected permission not found")
} }
} }

View file

@ -129,7 +129,7 @@ func (c *sshCommand) handleHashCommands() error {
if err != nil { if err != nil {
return c.sendErrorResponse(err) return c.sendErrorResponse(err)
} }
if !c.connection.User.HasPerm(dataprovider.PermListItems, path) { if !c.connection.User.HasPerm(dataprovider.PermListItems, sshPath) {
return c.sendErrorResponse(errPermissionDenied) return c.sendErrorResponse(errPermissionDenied)
} }
hash, err := computeHashForFile(h, path) 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, perms := []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermCreateDirs, dataprovider.PermListItems,
dataprovider.PermOverwrite, dataprovider.PermDelete, dataprovider.PermRename} 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) return c.sendErrorResponse(errPermissionDenied)
} }
@ -299,7 +299,7 @@ func (c *sshCommand) getSystemCommand() (systemCommand, error) {
// the home dir. // the home dir.
// If the user cannot create symlinks we add the option --munge-links, if it is not // 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) // 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) { if !utils.IsStringInSlice("--safe-links", args) {
args = append([]string{"--safe-links"}, args...) args = append([]string{"--safe-links"}, args...)
} }