mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 09:00:27 +00:00
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:
parent
eb2ddc4798
commit
e046b35b97
6 changed files with 111 additions and 104 deletions
|
@ -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:
|
||||||
|
|
108
sftpd/handler.go
108
sftpd/handler.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
28
sftpd/scp.go
28
sftpd/scp.go
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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...)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue