Subject: SFTP version negotiation

From: Henrick Hellström <[email-masked]>
Date: Sun, 03 Oct 2004 12:58:54 +0200

Just a thought...

What happens if a SFTP server supports v3 and v4, and a client that 
supports v3 and v6 only connects?

According to the latest draft, they are supposed to start v4, since the 
client will send v6 and the server supports at most v4, and v4 is the 
least of these two numbers.

Quoted from draft 05:

4. Protocol Initialization

    When the file transfer protocol starts, the client first sends a
    SSH_FXP_INIT (including its version number) packet to the server. The
    server responds with a SSH_FXP_VERSION packet, supplying the lowest
    of its own and the client's version number.  Both parties should from
    then on adhere to particular version of the protocol.

---

From: Damien Miller <[email-masked]>
Date: Sun, 03 Oct 2004 20:35:47 +1000

Joseph Galbraith wrote:
> Is anyone implementing v5 right now?
> 
> I'm getting ready to ship an refresh, but if people have
> implemented v5, I'll need to change the version.

OpenSSH isn't. I definitely won't until a new version is out, hopefully
resolving the open/symlink issue I mentioned in:

Message-ID: <[email-masked]>

-d

---

From: Joseph Galbraith <[email-masked]>
Date: Tue, 05 Oct 2004 09:20:12 -0600

>    then on adhere to particular version of the protocol.

This has been a concern of mine for a while.  The best solution I've 
been able to come up with (better than closing channel, I think) is this:

 > If the client supports non-contigous version, for example,
 > versions 2,3, and 6, then the INIT/VERSION negotiation
 > is insufficient.  In order to gaurentee a successful
 > connection, the MUST send the highest version for which
 > it supports all previos version (because the server
 > might pick the version the client sends or any
 > previous version.)
 >
 > However, in order to work around this limit in the
 > deployed protocol implementations, after the
 > INIT/VERSION exchange is complete, if the version
 > negotiated is at least 3, the version where extensions was
 > introduced, the client MAY send the following extension
 > to attempt to negotiate a higher version:
 >
 >   byte SSH_FXP_EXTENDED
 >   uint32 request-id
 >   string "version-negotiate"
 >   uint32 highest version-supported
 >   uint32 2nd highest version-supported
 >   ...
 >   uint32 lowest version-supported
 >
 > The client must not send any other packets after sending
 > this extension until it recieves the response.  The server
 > responds with a similar response:
 >
 > The server then responds with:
 >
 >   byte SSH_FXP_EXTENDED_REPLY
 >   uint32 request-id
 >   uint32 highest version-supported
 >   uint32 2nd highest version-supported
 >   ...
 >   uint32 lowest version-supported
 >
 > The new negotiated version to use is the
 > first value that appears on both list, or,
 > in other words, the highest version supported
 > by both the client and the server.  This
 > version MUST be at least as high as the
 > previous negotiated version.
 >
 > The next packet sent after the "version-negotiate"
 > request by the client, and the next packet sent
 > by the server after the reply will conform to
 > the new version.
 >
 > The server includes it's entire list of supported
 > versions rather than just the negotiated version
 > in order to assist in trouble shooting on the
 > client side.

I believe this mechanism maintains backwards compatibility
and gives us the version exchange we now wish we'd had
originally :-)

Thanks,

Joseph

---

From: Henrick Hellström <[email-masked]>
Date: Tue, 05 Oct 2004 23:19:30 +0200

>  > to attempt to negotiate a higher version:
[snip]
> and gives us the version exchange we now wish we'd had

I think it is good. Mostly because it serves as a statement that an 
implementation does not have to support all versions.

I am not sure it will be of any practical benefit in our case, though. 
While there might be a performance penalty opening and closing and 
reopening a SFTP sub system if the SFTP server is implemented as a 
separate executable, this is typically not a concern if the SSH server 
and the SFTP server run in the same process.

Our server implementation does not launch the SFTP server as a separate 
process, but either compiles it into the SSH server or uses dynamic 
loading (dll or so depending on platform). This means that there is only 
a marginal difference between the two approaches (open-close-reopen vs. 
open-extended renegotiation) w.r.t. both performance and code size on 
both the client side and the server side.

My only concern is that server owners who run SSH server software that 
launches SFTP as a separate executable might not be thrilled about 
getting a lot of clients that execute the SFTP application twice instead 
of once in order to get the right protocol version. As I said, it is not 
a big thing, but bad design choices tend to sum up in the end...

---

From: Damien Miller <[email-masked]>
Date: Wed, 06 Oct 2004 10:20:40 +1000

>  >   uint32 lowest version-supported

I'm in two minds about this: we need some way to negotiate versions, but
this effectively eliminates the usefulness of the version sent at
protocol start, which is going to have to negotiate to 3 for the
foreseeable future.

As far as the protocol itself, we could use the extended data in the
SSH_FXP_INIT packet to indicate protocol support. e.g.

byte SSH_FXP_INIT
uint32 version
string "version-negotiate"
string highest-version-supported,2nd-highest-version,...

The return SSH_FXP_VERSION packet can do the same. The client and server
then pick the first common item from the lists. Every SSH implementation
is going to have code for matching this sorts of list of comma separated
strings, so I think it makes sense to reuse it. This also saves a
round-trip at protocol start.

Another alternative would be to do a new protocol start message with
proper version negotiation and rename the subsystem (e.g. "sftp2"), but
I think that this is overkill.

-d

---

From: Joseph Galbraith <[email-masked]>
Date: Wed, 06 Oct 2004 12:40:12 -0600

> string highest-version-supported,2nd-highest-version,...

Unfortunately, we were forced to remove SSH_FXP_INIT
extension data because it broke compatibility with
down-level servers.

(I.e., the client has to send the INIT packet before
it knows that the server is a mature enough version
to understand about extensions.)

> then pick the first common item from the lists.

We could have the server send it's list of supported versions
as part of the FXP_VERSION packet and then allow the client
to request that the server switch to one of the listed
versions... but that doesn't save the round trip, since the
client still has to do the switch as an extension w/ a round
trip.

> Every SSH implementation

I considered the string version, and I'm definitely not opposed
to going that route.  It was really just a flip of a coin for
me...

- Joseph
I thought about using the string version of this... and
I didn't

The server could

---

From: "denis bider" <[email-masked]>
Date: Fri, 8 Oct 2004 03:54:29 +0200

> client still has to do the switch as an extension w/ a round

It does save a round trip because if the server includes a "versions" extension
and the client elects to send a request for one of those higher versions, the
client can fire-and-forget that request and proceed with other requests
immediately. The spec could mandate that the server MUST always respond
positively to a well-formed client request for a higher version if that version
was advertised by the server in the extensions of SSH_FXP_VERSION.

It could look something like this:

 -> SSH_FXP_INIT
    version: x

 <- SSH_FXP_VERSION
    version: x
    ext name: "versions"
    ext data: "x+3,x+1"

 -> SSH_FXP_SELECTVERSION
    version: x+3
 -> SSH_FXP_REALPATH
    request id: 0
    path: "."

 <- SSH_FXP_VERSIONSELECTED
 <- SSH_FXP_NAME
    request id: 0
    count: 1
    filename
    attributes

The spec would mandate:

- The highest version number that a client should ever send is "x" (the last
version number before this 2nd mechanism was introduced - say 5 at this point
if the extension is introduced in SFTP v6).

- A server supporting versions >x MUST send the "versions" extension
enumerating the versions higher than x that it supports. Mentioning versions
<=x is pointless, we should recommend people not do it lest someone overeager
populate this with 4,3,2,1.

  The "versions" extension should be sent whenever the version sent by the
client indicates that the client supports SSH_FXP_VERSION extensions. So, this
should be sent even if the client advertises e.g. version 3. This is so that a
client supporting 3 and 6 can advertise 3 and get 3 against version 5 servers,
but select 6 when the server is 6+.

- If the client wishes to do so, it can switch protocol version to one of the
higher versions advertised by the server by a direct reply to the server's
SSH_FXP_VERSION message. This can be done with SSH_FXP_SELECTVERSION (newly
invented). There is no request ID because it is not necessary, the message can
only appear in immediate response to SSH_FXP_VERSION, cannot appear multiple
times.

- The server replies to that with SSH_FXP_VERSIONSELECTED. This is just to
maintain the request-reply dynamic of the SFTP protocol and is not really
necessary. If the client's request was faulty, the server cannot do much but
close the channel anyway. If the client's request was alright, the server must
honor it. We could just as well do without this response message in fact -
unless someone has something to say for it, I propose we might even drop this
response.

- The client need not wait for SSH_FXP_VERSIONSELECTED, and when this message
arrives it can be ignored. The client can pipeline requests immediately after
sending SSH_FXP_SELECTVERSION.

Seems like an OK solution to me - no roundtrip problem, and has immediate
benefit of solving the issue of the client supporting only versions 3 and 6.

Votes?

denis