fix: unknown request types should return failure - #112
Conversation
81948a9 to
5089da4
Compare
|
Sorry for the delay in reviewing this one @cquintana92 - thanks for this! I think this is a good addition (and helps with spec compliance) - however, in terms of public facing API surface, I think it'd be worth extending the We have a construct of I've put together #114, which builds on this PR and:
Let me know if this works for you! |
|
@jcspencer made the changes and also adapted the tests so they pass on Windows, let me know if this is enough. Also there is a failing job regarding a yanked dep, I'll leave that one to the project maintainers 👍 |
|
Hi @cquintana92, thanks for the PR, it looks really nice 👌
Sadly I've updated the deps in #115 if you could rebase this on top of |
Signed-off-by: Carlos Quintana <carlos@cquintana.dev>
Signed-off-by: Carlos Quintana <carlos@cquintana.dev>
57ac7cb to
27c4a2d
Compare
|
@wiktor-k rebased 👍 |
|
@cquintana92 thanks! @jcspencer is that okay with you if we merge this one and then continue on with #114 ? |
|
@wiktor-k sure thing! I’ll rebase once this is merged 🎉 |
Fix: Reply
SSH_AGENT_FAILUREto unknown request types instead of closing the connectionIssue
When a client sends a request whose message type is not recognised, the agent closes the connection without replying. Per draft-miller-ssh-agent-14 § 3.1:
OpenSSH's
ssh-agentbehaves that way, but agents built on this crate currently do not, because the connection is dropped at the codec layer before any session handling runs.Impact
Some clients probe the agent with a legacy request type that is not implemented by modern agents. For example Ruby's
net-sshopens negotiation withSSH2_AGENT_REQUEST_VERSION(message type1). When the agent closes the socket instead of replying,net-sshraises aFrozenErrorand deploy tools built on it cannot connect at all, even thoughssh/ssh-addwork fine against the same agent.Root cause
The framing
Codecdecodes every incoming frame into aRequestbefore theSessionis invoked. For an unknown message type,Request::decodereturnedErr(UnsupportedCommand). That decoder error is fatal to the stream:tokio_util'sFramedImplenters an errored state after a decode error and yieldsEOFon the next poll, so the socket is torn down beforeSession::handleis ever called. NoSessionoverride can prevent this with the current code.Proposed solution
Request::Unknown(u8)variant that captures the raw message type byte. Unknown message types now decode successfully (the payload following the type byte is skipped, bounded by themessage length prefix), encode back to the original type byte, and report the correctmessage_id.Session::handleimplementation, respond toRequest::UnknownwithResponse::Failure(i.e.SSH_AGENT_FAILURE, message type 5) and keep the connection open, matching OpenSSH's behaviour.The default implementation suits all existing agents; because
handleis the documented override point, users who need custom handling for a particular unknown type can still intercept it themselves.Testing
New integration test
tests/unknown_request.rsstarts a real agent over a Unix socket, replays the exactnet-sshprobe (SSH2_AGENT_REQUEST_VERSION, type 1, body"2.0"), and asserts that:SSH_AGENT_FAILURE(message type 5) instead of closing the connection.SSH_AGENTC_REQUEST_IDENTITIES).The test fails against the previous behaviour (connection closed without a reply) and passes with this change.
Validation
cargo testreports all suites pass (unit, roundtrip, doc, new integration test)cargo clippy --workspace --no-deps --all-targets -- -D warningscomes cleancargo fmt --checkcomes cleanCompatibility
This is a backward-compatible additive change:
Requestgains one new variant (the compiler will flag exhaustive matches, which is desirable since callers must now decide how to handle unknown messages). No existing behaviour changes.