pkg / sftp

SFTP support for the go.crypto/ssh package
BSD 2-Clause "Simplified" License
1.5k stars 379 forks source link

Any dir with permission 0666 cannot be written to #538

Closed SSSOCPaulCote closed 1 year ago

SSSOCPaulCote commented 1 year ago

I've noticed that as soon as a directory loses the execute bit on the owner, the sftp package will raise Permission denied errors when we try to put to that dir, move files, try to get any files within that dir, see the contents of files inside dirs within this dir or change permissions on items within the dir. To be clear, read and write access remain for the owner (going from for example 0755 to 0666). This is not consistent behaviour with other sftp tools like linux CLI sftp or windows psftp.

EDIT: my sftp server is drakkan/sftpgo

puellanivis commented 1 year ago

This is consistent with POSIX permissions though, right?

tmp$ mkdir blackout
tmp$ cd blackout
blackout$ echo "foo" > foo
blackout$ echo "bar" > bar
blackout$ mkdir baz
blackout$ ls
bar  baz/  foo
blackout$ cd ..
tmp$ chmod 666 blackout/
tmp$ cat blackout/foo
cat: blackout/foo: Permission denied
tmp$ cat blackout/bar
cat: blackout/bar: Permission denied
tmp$ ls blackout/baz
ls: cannot access 'blackout/baz': Permission denied
tmp$ ls blackout/ # we can still `readdir` the directory, though. Despite not having `x` permissions
ls: cannot access 'blackout/baz': Permission denied
ls: cannot access 'blackout/bar': Permission denied
ls: cannot access 'blackout/foo': Permission denied
bar  baz  foo
tmp$ echo "asdf" > blackout/asdf
-bash: blackout/asdf: Permission denied
tmp$ mv blackout/foo blackout/asdf
mv: failed to access 'blackout/asdf': Permission denied

In all cases, this package is not spontaneously generating any os.ErrPermission errors, so the origin of these errors would have to be on the remote end.

drakkan commented 1 year ago

Hello,

SFTPGo author here. I did a quick test and I also get a permission denied error using the sftp CLI and OpenSSH as server

Uploading ros.txt to /tmp/test_home/start/dir/ros1.txt
dest open "/tmp/test_home/start/dir/ros1.txt": Permission denied

The only difference is this one

SFTPGo:

sftp> ls
remote readdir("/start/dir"): Permission denied

OpenSSH:

sftp> cd /tmp/test_home/start/dir/
sftp> ls -la
sftp> 

so OpenSSH for some reason returns no content and no error (the directory contains some files), while SFTPGo returns a permission denied error even when listing the directory content. I think SFTPGo behaves correctly.

Just for your info, SFTPGo uses virtual permissions, you don't need chmod/chown

puellanivis commented 1 year ago

As I noted and demonstrated, (in Linux at least,) you do not need execute permission to list the filenames in a directory, just read permissions. However, you cannot get any stats on those files, because you cannot of course stat them:

tmp$ ls -l blackout
ls: cannot access 'blackout/baz': Permission denied
ls: cannot access 'blackout/bar': Permission denied
ls: cannot access 'blackout/foo': Permission denied
total 0
-????????? ? ? ? ?            ? bar
d????????? ? ? ? ?            ? baz/
-????????? ? ? ? ?            ? foo

So, OpenSSH is performing the equivalent of echo *. I’d be interested to see what the results are if there are files and a directory in the sat same /start/dir directory.

Also, since sftp does not technically have any concept of “current working directory” there’s no reason why it could not allow you to cd into a directory that you do not technically have the permissions to cd into were the process running locally (i.e. you do not have execute permissions to the directory). 🙃

SSSOCPaulCote commented 1 year ago

Hmm okay, I'll have to play around with different SFTP servers and report back. Thank you both for input.

drakkan commented 1 year ago

As I noted and demonstrated, (in Linux at least,) you do not need execute permission to list the filenames in a directory, just read permissions. However, you cannot get any stats on those files, because you cannot of course stat them:

tmp$ ls -l blackout
ls: cannot access 'blackout/baz': Permission denied
ls: cannot access 'blackout/bar': Permission denied
ls: cannot access 'blackout/foo': Permission denied
total 0
-????????? ? ? ? ?            ? bar
d????????? ? ? ? ?            ? baz/
-????????? ? ? ? ?            ? foo

So, OpenSSH is performing the equivalent of echo *. I’d be interested to see what the results are if there are files and a directory in the sat same /start/dir directory.

Also, since sftp does not technically have any concept of “current working directory” there’s no reason why it could not allow you to cd into a directory that you do not technically have the permissions to cd into were the process running locally (i.e. you do not have execute permissions to the directory). upside_down_face

I confirm. I have this at fs level

nicola@p1 /tmp/test_home/start/dir $ touch file1.txt file2.txt
nicola@p1 /tmp/test_home/start/dir $ ls -la
totale 0
drwxr-xr-x 2 nicola nicola 80 16 feb 08.09 .
drwxr-xr-x 3 nicola nicola 60 16 feb 08.09 ..
-rw-r--r-- 1 nicola nicola  0 16 feb 08.09 file1.txt
-rw-r--r-- 1 nicola nicola  0 16 feb 08.09 file2.txt
nicola@p1 /tmp/test_home/start/dir $ cd ..
nicola@p1 /tmp/test_home/start $ chmod 666 dir
nicola@p1 /tmp/test_home/start $ ls -l dir
ls: impossibile accedere a 'dir/file2.txt': Permesso negato
ls: impossibile accedere a 'dir/file1.txt': Permesso negato
totale 0
-????????? ? ? ? ?            ? file1.txt
-????????? ? ? ? ?            ? file2.txt

SFTPGo:

sftp -P 2022 b@127.0.0.1
Connected to 127.0.0.1.
sftp> cd /start/dir
sftp> ls
remote readdir("/start/dir"): Permission denied

In the logs I have this error

{"level":"debug","time":"2023-02-16T08:16:02.455","sender":"SFTP","connection_id":"SFTP_e4c2328fac6717534a6ed7806c97740f5a5928f7754b1703e4e51b1eccc8158a_1","message":"error listing directory: lstat /tmp/test_home/start/dir/file2.txt: permission denied"}

the error is generated here:

https://github.com/drakkan/sftpgo/blob/2.4.x/internal/vfs/osfs.go#L202

OpenSSH instead

sftp -P 22 nicola@127.0.0.1
nicola@127.0.0.1's password: 
Connected to 127.0.0.1.
sftp> cd /tmp/test_home/start/dir/
sftp> ls
sftp> 

so cd works for both SFTPGo and OpenSSH, but ls returns nothing and no error with OpenSSH.

nicola@p1 ~ $ pacman -Q | grep openssh
openssh 9.2p1-1
puellanivis commented 1 year ago

Poking around at things, os.ReadDir() which returns []fs.DirEntry instead of []fs.FileInfo works on a directory without execute permission. os.Readdir is breaking because after/while reading the directory entries, it tries to stat each entry, and since this fails for the first entry, it then returns an empty slice.

drakkan commented 1 year ago

Poking around at things, os.ReadDir() which returns []fs.DirEntry instead of []fs.FileInfo works on a directory without execute permission. os.Readdir is breaking because after/while reading the directory entries, it tries to stat each entry, and since this fails for the first entry, it then returns an empty slice.

Yes but ListerAt expects a FileInfo so I have to convert anyway (and also have to handle the case where the file disappeared between readdir and stat if I switch to DirEntry).

EDIT: OpenSSH does not return file lists either, it just returns an empty list (the dir contains some files) and no error