keyboard-interactive: send USERAUTH_FAILURE on response-count mismatch - #1070
Conversation
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1070
Scan targets checked: wolfssh-bugs, wolfssh-src
No new issues found in the changed files. ✅
aidangarske
left a comment
There was a problem hiding this comment.
🐺 Skoll Code Review
Overall recommendation: COMMENT
Findings: 1 total — 1 posted, 0 skipped
Posted findings
- [Medium] Malformed keyboard response failure leaves challenge state active —
src/internal.c:7278-7385
Review generated by Skoll.
97c6fc8 to
e70aadf
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1070
Scan targets checked: wolfssh-bugs, wolfssh-src
Findings: 2
1 finding(s) posted as inline comments (see file-level comments below)
High (1)
Packet-index not advanced on responseCount mismatch, desyncing input buffer
File: src/internal.c:8317
Function: DoUserAuthInfoResponse
Category: Logic errors
*idx = begin at line 8317 only runs when ret == WS_SUCCESS, but in the new mismatch branch ret is WS_USER_AUTH_E at that point and only becomes success afterward via SendUserAuthFailureCount(). *idx (the caller's payloadIdx) is left at 0, so DoPacket() sets ssh->inputBuffer.idx short by the unconsumed payload, corrupting framing for the next packet on the now-surviving connection.
Recommendation: In the mismatch branch also set *idx to the full payload length (len) before returning, independent of the ret == WS_SUCCESS gate.
Referenced code: src/internal.c:8317-8322 (6 lines)
This review was generated automatically by Fenrir. Findings are non-blocking.
e70aadf to
6bc89ac
Compare
|
Hello @aidangarske, @ejohnstown , |
Problem
DoUserAuthInfoResponse()tore down the transport instead of sending anSSH_MSG_USERAUTH_FAILUREwhen a keyboard-interactiveINFO_RESPONSEcarried aresponseCountthat did not match the server's pendingpromptCount.The guard set
ret = WS_USER_AUTH_E, but the failure-response gate only sends amessage when
authFailure || partialSuccessis set, so nothing was emitted;DoReceive()convertsWS_USER_AUTH_EtoWS_FATAL_ERRORand the connectiondies. RFC 4256 requires the server to answer every
INFO_RESPONSEwithSUCCESS,FAILURE, or anotherINFO_REQUEST. Impact is limited tomalformed/malicious clients, but the response is non-conformant.
Addressed by f_6514.
Fix (
src/internal.c)Set
authFailurein the mismatch guard so the existing gate sendsSSH_MSG_USERAUTH_FAILUREand the connection survives; the client may retry.retstays non-success so the response allocation, parse, and user-auth callbackare skipped on a protocol-violating message.
Two removals came out of review:
responseCount > WOLFSSH_MAX_PROMPTSguard was dead code. Themismatch check forces
responseCount == promptCount, andpromptCountiscapped at
WOLFSSH_MAX_PROMPTSby both of its writers(
SendUserAuthKeyboardRequest()server-side,DoUserAuthInfoRequest()client-side), so the allocation below is bounded transitively.
*idx = lenline on the failure path was an unobservable no-op:DoReceive()callsShrinkBuffer(&ssh->inputBuffer, 1)on every consumedpacket, and
forcedFree = 1zeroeslength/idx, so a short payload indexnever reaches the next packet.
Net change is one functional line plus a comment.
Tests (
tests/regress.c)Both use the in-memory packet harness, feeding crafted packets into
DoReceive()and inspecting the bytes the server writes back:
TestKbInfoResponseCountMismatchSendsFailure—responseCount2 vspromptCount1; assertsWS_SUCCESSand aMSGID_USERAUTH_FAILUREreply.TestKbInfoResponseMismatchKeepsFraming— two back-to-back malformedresponses; asserts the second packet still parses and draws its own failure,
covering parser state after a rejected response.
Verification
make check: 10 passed, 1 skipped (external), 0 failed.-Werroracross 6 gcc-13 configs; lint clean.authFailure = 1aborts the first test with-1001; the framing test fails only when*idx = lenis absent andShrinkBuffer()is forced non-free, confirming it detects real desync.