Skip to content

fix: send SSH_MSG_DISCONNECT on KEX failure#1091

Open
MarkAtwood wants to merge 1 commit into
wolfSSL:masterfrom
MarkAtwood:fix/kexinit-disconnect
Open

fix: send SSH_MSG_DISCONNECT on KEX failure#1091
MarkAtwood wants to merge 1 commit into
wolfSSL:masterfrom
MarkAtwood:fix/kexinit-disconnect

Conversation

@MarkAtwood

Copy link
Copy Markdown

Bug

DoKexInit() in src/internal.c sets ret to one of the negotiation-failure codes WS_MATCH_KEX_ALGO_E / WS_MATCH_KEY_ALGO_E / WS_MATCH_ENC_ALGO_E / WS_MATCH_MAC_ALGO_E when no common KEX / host-key / cipher / MAC algorithm can be negotiated, then returns. That code propagates unchanged through DoPacket() and DoReceive(), which set ssh->error and return WS_FATAL_ERROR. No SSH_MSG_DISCONNECT is ever sent on these paths, so the peer is dropped without notification.

RFC 4253 s11.1 says an implementation SHOULD send SSH_MSG_DISCONNECT on a protocol error. The library already follows this best-effort pattern with (void)SendDisconnect(...) for service-request/accept errors (internal.c:7691, 7723); the KEX negotiation paths were inconsistent with that pattern.

Fix

At the single DoKexInit() exit point, send a best-effort SSH_MSG_DISCONNECT with reason WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED (3) when ret is any of the four match-failure codes. Uses the same (void)SendDisconnect best-effort form as existing call sites. Safe: ret is not overwritten for match errors (the SendKexInit/ssh->error propagation block is gated on ret == WS_SUCCESS).

Build verification

Compiled the flagged translation unit against a full wolfSSL build (make src/libwolfssh_la-internal.lo, exit 0) on wolfssh master 974f4fd.

Reported by static analysis (Fenrir finding 608). Severity Low (RFC uses SHOULD, not MUST).

Copilot AI review requested due to automatic review settings July 9, 2026 23:47

Copilot AI 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

Note

Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.

Ensures the library sends a best-effort SSH_MSG_DISCONNECT when KEX negotiation fails due to no mutually supported algorithms, aligning behavior with RFC 4253 guidance and existing disconnect-on-error patterns in the codebase.

Changes:

  • Adds a best-effort (void)SendDisconnect(...) call in DoKexInit() when KEX/hostkey/cipher/MAC matching fails.
  • Uses WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED as the disconnect reason for these negotiation failures.

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

Comment thread src/internal.c
Comment on lines +5335 to +5338
/* RFC 4253 11.1: best-effort SSH_MSG_DISCONNECT on negotiation failure. */
if (ret == WS_MATCH_KEX_ALGO_E || ret == WS_MATCH_KEY_ALGO_E ||
ret == WS_MATCH_ENC_ALGO_E || ret == WS_MATCH_MAC_ALGO_E) {
(void)SendDisconnect(ssh, WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED);
Sign up for free to 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.

4 participants