sshfs icon indicating copy to clipboard operation
sshfs copied to clipboard

SSHFS always follows symbolic links (even for eg. lchown)

Open lapsio opened this issue 8 years ago • 8 comments

https://pastebin.com/fH356wkk

when symlink is copied over SSHFS it results in error "Permission denied" (if file exists on host and sshfs user doesn't have permission to it) or "No such file or directory" (if it doesn't) while performing the same operation on local filesystem results with success. It breaks scripts basing on return codes.

Simple example with /etc/shadow:

lapsio@linux-qzuq ~> ln -s /etc/shadow ./shadlink
lapsio@linux-qzuq ~> cp --preserve=all -Rnv shadlink ./shadlink2 
‘shadlink’ -> ‘./shadlink2’
lapsio@linux-qzuq ~> file shadlink2
shadlink2: symbolic link to `/etc/shadow'
lapsio@linux-qzuq ~> cp --preserve=all -Rnv shadlink ~/SSHFS/lap/home/lapsio/shadlink3 ‘shadlink’ -> ‘/home/lapsio/SSHFS/lap/home/lapsio/shadlink3’
cp: failed to preserve ownership for /home/lapsio/SSHFS/lap/home/lapsio/shadlink3: Permission denied
lapsio@linux-qzuq ~> file ~/SSHFS/lap/home/lapsio/shadlink3 
/home/lapsio/SSHFS/lap/home/lapsio/shadlink3: symbolic link to `/etc/shadow'

lapsio avatar Nov 01 '17 16:11 lapsio

Most probably the SFTP server calls chown(2) to set the ownership, which attempts to set ownership of the target of the symlink (contratry to lchown(2)).

The SFTP command SETSTAT (https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6) does not provide a way to explicitely request a "lchown".

g-raud avatar Mar 23 '18 20:03 g-raud

I agree with g-raud. I'm not sure if there's a way to improve the behavior. At first I thought that maybe we should just not translate lchown to chown - but this translation actually happens in the kernel. sshfs just gets an inode, and can't tell if the original call was chown or lchown.

Nikratio avatar Mar 28 '18 19:03 Nikratio

See also issue #250 for the related problem when setting other inode attributes.

Nikratio avatar Aug 15 '21 18:08 Nikratio

Copying my comment from issue #250

There is an OpenSSH extension (and almost everybody uses OpenSSH so that is enough) to support this.

[email protected]. It was added in OpenSSH 8.0, in 2019.

3.7. sftp: Extension request "[email protected]"

This request is like the "setstat" command, but sets file attributes on
symlinks.  It is implemented as a SSH_FXP_EXTENDED request with the
following format:

	uint32		id
	string		"[email protected]"
	string		path
	ATTRS		attrs

See the "setstat" command for more details.

This extension is advertised in the SSH_FXP_VERSION hello with version
"1".

baryluk avatar Aug 17 '21 11:08 baryluk

I was just about to file a new issue titled

Security: fchownat(..., AT_SYMLINK_NOFOLLOW) follows symlink anyway

when I discovered this existing issue.

Posting what I wanted to post there here for googleability:


I found that sshfs silently ignores AT_SYMLINK_NOFOLLOW. This can be a security issue because an admin/script that uses e.g. chown -R --no-dereference to avoid following symlinks is led to believe that their invocation works, when it fact it does the opposite of wha they expect.

Repro:

# Create sshfs mount with a symlink pointing to a file
mkdir -p sshfs-dir
touch sshfs-dir/myfile
ln -s myfile sshfs-dir/link-to-myfile

mkdir -p sshfs-mount
sshfs localhost:sshfs-dir sshfs-mount

chown --no-dereference $(whoami):wheel sshfs-mount/link-to-myfile

ls -l sshfs-dir
# Now `myfile` is owned by group `wheel` -- it followed the symlink despite `--no-dereference`!

Another repro that shows that AT_SYMLINK_NOFOLLOW is ignored even in the case that the symlink target does NOT exist:

mkdir -p sshfs-dir
mkdir -p sshfs-mount
sshfs localhost:sshfs-dir sshfs-mount
ln -s nowhere sshfs-mount/symlink-pointing-nowhere

chown --no-dereference $USER sshfs-dir/symlink-pointing-nowhere  # works as expected

chown --no-dereference $USER sshfs-mount/symlink-pointing-nowhere

# The last command fails incorrectly with:
#     chown: changing ownership of 'sshfs-mount/symlink-pointing-nowhere': No such file or directory

# `strace -fye newfstatat,fchownat` on the last command shows that it's not `chown`'s fault,
# it simply reports what the kernel and `sshfs` return:
#
#     newfstatat(AT_FDCWD</home/niklas>, "sshfs-mount/symlink-pointing-nowhere", {st_mode=S_IFLNK|0777, st_size=7, ...}, AT_SYMLINK_NOFOLLOW) = 0
#     fchownat(AT_FDCWD</home/niklas>, "sshfs-mount/symlink-pointing-nowhere", 1000, -1, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
#
# The `newfstatat()` proves that the symlink exists, so returning `ENOENT` is wrong.
# That `fchownat` result should be the same as when run against `sshfs-dir`:
#
#     fchownat(AT_FDCWD</home/niklas>, "sshfs-dir/symlink-pointing-nowhere", 1000, -1, AT_SYMLINK_NOFOLLOW) = 0

nh2 avatar Nov 08 '23 15:11 nh2

This can be a security issue because an admin/script [..] is led to believe that their invocation works, when it fact it does the opposite

Wouldn't it be safer from a security perspective that sshfs should fail loudly when AT_SYMLINK_NOFOLLOW is used, when sshfs cannot implement it?

nh2 avatar Nov 08 '23 15:11 nh2

This can be a security issue because an admin/script [..] is led to believe that their invocation works, when it fact it does the opposite

Wouldn't it be safer from a security perspective that sshfs should fail loudly when AT_SYMLINK_NOFOLLOW is used, when sshfs cannot implement it?

Absolutely, yes.

I don't see how it could be implemented though. As far as I know, things like AT_SYMLINK_NOFOLLOW are handled by the kernel (same as chown vs lchown etc). So SSHFS has no idea what the original request was.

Nikratio avatar Nov 09 '23 11:11 Nikratio

As far as I know, things like AT_SYMLINK_NOFOLLOW are handled by the kernel (same as chown vs lchown etc). So SSHFS has no idea what the original request was.

Maybe it would make sense to create an upstream kernel bug / feature request, so this can be tracked? https://www.kernel.org/doc/html/v4.19/admin-guide/reporting-bugs.html

powellnorma avatar May 15 '24 13:05 powellnorma