Skip to content

Match forwarded-tcpip opens to registered forwards - #1148

Open
ejohnstown wants to merge 2 commits into
wolfSSL:masterfrom
ejohnstown:fixfwd
Open

Match forwarded-tcpip opens to registered forwards#1148
ejohnstown wants to merge 2 commits into
wolfSSL:masterfrom
ejohnstown:fixfwd

Conversation

@ejohnstown

Copy link
Copy Markdown
Contributor

RFC 4254 7.2 says a forwarded-tcpip open answers a forward the client asked for, but we accepted any of them. This tracks what wolfSSH_FwdRemoteSetup() registered and refuses opens naming anything else. Enforcement starts at the first registration, so a client that frames tcpip-forward itself is unaffected. Two wolfSSH_SendPacket() bugs found while testing it are fixed here too.

  • Per-session list of registered forwards. Wildcard binds match on port alone, port 0 now requires want-reply, and repeat setups of one bind share a registration.
  • A cancel stops matching as it goes out; a want-reply cancel stays registered until the peer answers, since a refusal leaves the listener up.
  • Replies carry no request id, so a per-session queue pairs them in send order. Registration is split around the send, since the highwater callback reports a rekey's errors as the send's.
  • WS_CBIO_ERR_ISR fell through to WS_SOCKET_ERROR_E, discarding framed output the peer never refused; retry instead, as ReceiveData() does. And WS_CBIO_ERR_GENERAL left the discarded packet counted in plainSz, so SendChannelData() flushed nothing and called it a success.
  • Tests in tests/regress.c cover matching, cancel, overlapping requests, send-order pairing, port 0, registration around the send, reentrancy from a callback, and both send paths.

The three affected API contracts are documented in wolfssh/ssh.h; no signatures changed.

Issue: ZD-22195

@ejohnstown
ejohnstown requested review from wolfSSL-Fenrir-bot and a lite review from CopilotAugust 6, 2026 23:15

@wolfSSL-Fenrir-botwolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #1148

Scan targets checked:wolfssh-bugs, wolfssh-src

Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment threadtests/regress.c
Comment threadsrc/internal.c

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR tightens forwarded-tcpip channel-open handling to only accept opens that correspond to tcpip-forward registrations made via wolfSSH_FwdRemoteSetup() (per RFC 4254 §7.2), and fixes two wolfSSH_SendPacket() error-handling issues discovered during testing.

Changes:

  • Track per-session remote-forward registrations and enforce matching for inbound forwarded-tcpip opens (with wildcard + cancel/overlap semantics).
  • Introduce a send-order reply queue to pair REQUEST_SUCCESS/FAILURE with want-reply global requests/forward requests.
  • Fix wolfSSH_SendPacket() handling for WS_CBIO_ERR_ISR and WS_CBIO_ERR_GENERAL, and expand regression/API tests accordingly.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
wolfssh/ssh.hDocuments updated remote-forward enforcement semantics and global-request reply-queue behavior.
wolfssh/internal.hAdds forward tracking structs, reply queue state, and a flush counter to disambiguate post-send errors.
src/ssh.cIntegrates forward prepare/commit/discard around sends; enforces port-0 want-reply; queues want-reply global requests.
src/internal.cImplements forward tracking + reply pairing; enforces forwarded-tcpip matching; fixes wolfSSH_SendPacket() ISR/general error behavior.
tests/regress.cAdds extensive regression coverage for matching, cancellation semantics, reentrancy, send-order pairing, and send error paths.
tests/api.cAdds API bad-args coverage for port-0 without want-reply.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadwolfssh/internal.h Outdated
Comment threadsrc/internal.c Outdated
Comment threadsrc/internal.c
RFC 4254 7.2 says a forwarded-tcpip answers a forward the client asked
for, so refuse an open naming anything else.
- Register each wolfSSH_FwdRemoteSetup() per session. Enforcement starts
at the first registration, so a client that frames tcpip-forward
itself is unaffected. A wildcard bind matches on port alone, and port
0 now requires want-reply.
- Repeat setups of one bind share a registration. A cancel stops
matching as it goes out, but a want-reply cancel stays registered
until the peer answers, since a refusal leaves the listener up.
- Replies carry no request id, so a per-session queue pairs them in send
order. A want-reply wolfSSH_global_request() takes a slot as well.
- Registration is split around the send: allocate first, link only once
the output buffer flushed. The highwater callback reports a rekey's
errors as the send's, so the return code cannot decide it.
- Sending runs application callbacks, so a request resolves its
registration on commit rather than carrying it across the send.
- Tests in the client-side channel-open harness cover matching, cancel,
overlapping requests, send-order pairing, port 0, registration around
the send, and reentrancy from a callback.
Contracts for wolfSSH_FwdRemoteSetup(), wolfSSH_FwdRemoteCancel() and
wolfSSH_global_request() are in wolfssh/ssh.h.
Issue: ZD-22195
Both fixes are in wolfSSH_SendPacket(), so they cover every sender.
- WS_CBIO_ERR_ISR fell through to WS_SOCKET_ERROR_E. A signal
interrupted the send, so nothing went out and the session is
unharmed; retry it, as ReceiveData() already does.
- Callers that discard a packet on error, like the KEX and userauth
sends, were throwing away framed output the peer never refused.
- On WS_CBIO_ERR_GENERAL the output buffer is shrunk with the packet
still counted in plainSz, so SendChannelData() flushes nothing and
calls it a success. Clear it with the packet it described.
- Tests pin the retry from a forwarding sender and a plain global
request, and drive a channel send through a would-block and then a
refused flush.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@ejohnstown@wolfSSL-Fenrir-bot