pkg / sftp

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

SSH_FXP_REALPATH and symlinks #512

Closed drakkan closed 1 year ago

drakkan commented 2 years ago

Hi,

OpenSSH resolves symlinks for SSH_FXP_REALPATH calls (without failing if the symlink is broken), see here.

We simply return a cleaned path without resolving any symbolic links. This is what the v3 specs literally say (v6 has more details):

The SSH_FXP_REALPATH request can be used to have the server canonicalize any given path name to an absolute path. This is useful for converting path names containing ".." components or relative pathnames without a leading slash into absolute paths

If we want to behave like OpenSSH I think we can use os.Lstat and os.Readlink for our server implementation.

For request-server it is more tricky, we would have to generate Lstat and Readlink calls ourself. Maybe we can just document our realpath behaviour and remove the depracation notice from RealPathFileLister. Request server users can implement RealPathFileLister if they want to resolve symlinks. What do you think about? Thanks

puellanivis commented 2 years ago

Sounds probably good.

drakkan commented 2 years ago

uhm ... the problem here is that we should also return an error so we cannot reuse the current interface.

puellanivis commented 2 years ago

šŸ˜ Yeah, looking back at it now, it should have always retuned an error.

drakkan commented 2 years ago

If we cannot change the RealPath interface, I think I have to wait for a v2 to fix this issue.

I don't want to maintain a custom branch, I think this is a minor issue, we don't violate the specs. Do you have other (non-ugly) ideas?

The current code base never returns an error because it does not resolve symbolic links, so no errors can occur

puellanivis commented 2 years ago

Well right now, the interface isnā€™t used for anything right? And weā€™re supposed to just be using it as an extension interface anyways, right?

So, switching the interface shouldnā€™t actually break anything? šŸ¤”

drakkan commented 2 years ago

Well right now, the interface isnā€™t used for anything right? And weā€™re supposed to just be using it as an extension interface anyways, right?

So, switching the interface shouldnā€™t actually break anything? thinking

If we could change the RealPathFileLister like this

type RealPathFileLister interface {
    FileLister
    RealPath(string) (string, error)
}

would be perfect. I think it is unlikely that anyone is using this interface as it was added for the start directory feature but it basically doesn't work.

Another solution is to add a new extension interface but this way we have to check for 2 interfaces here. This doesn't seem ideal

puellanivis commented 2 years ago

A type switch at that point wouldnā€™t be the worst thing ever, and I donā€™t see that it would be any particularly performance critical path either.

So, I kind of see two different options:

puellanivis commented 2 years ago

Forgot to mention: I did a search on Github for RealPathFileLister and the only results that come up are our code and vendoring of our code.

But then the worry is always that you might break closed source, right? The one you cannot account for ahead of time.

drakkan commented 2 years ago

Ok, do you have any preference? Maybe the second option will avoid a backward incompatible change in the future

puellanivis commented 2 years ago

Yeah, Iā€™m partial to the second option. Thereā€™s a higher possibility of source-code-level breakage, but that risk was always going to be fairly low, and the fix is relatively easy as well, just add an error return and return a nil.

It would make sense though to also document that the legacy form is still supported, even though it might not be exported. Just being clear about what kind of behavior exists there (since for anyone coming in new, it might seem astonishing), and why it exists.