Bind userauth username to the first request#1063
Conversation
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1063
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
- [Low] Same-user multi-method retry path not covered by a regression test —
tests/regress.c:1568-1614
Review generated by Skoll.
| harness->ssh->acceptState = ACCEPT_SERVER_USERAUTH_ACCEPT_SENT; | ||
| } | ||
|
|
||
| static void RepointHarnessInput(ChannelOpenHarness* harness, |
There was a problem hiding this comment.
🔵 [Low] Same-user multi-method retry path not covered by a regression test
💡 SUGGEST test
The new guard added in DoUserAuthRequest intentionally allows a second USERAUTH_REQUEST when the username is unchanged (e.g. publickey probe then password, or a failed password retry). The PR description explicitly claims "Same-username multi-method retries ... are unaffected, since the username does not change," but no test asserts this positive path: every added test either changes the username (expected disconnect) or uses a single request. A latent bug that erroneously rejected a same-user second request (for example a bad size comparison) would not be caught. Adding a test that sends two same-user requests and asserts the second is accepted (not WS_INVALID_STATE_E, no MSGID_DISCONNECT) would lock in the intended behavior.
Recommendation: Add a positive-path regression test for a same-username second request to protect the allowed retry behavior. Suggested: a TestSameUserSecondRequestAllowed() that issues two BuildUserAuthPasswordRequest("alice", ...) calls and asserts the second DoReceive does not disconnect and re-invokes the callback bound to alice.
Bind userauth username to the first request
Summary
Fixes finding f_5829. In keyboard-interactive (KBI) authentication, the
server bound the auth callback's username to whatever
ssh->userNamecurrentlyheld, not to the username that opened the exchange.
DoUserAuthRequestoverwrote
ssh->userNameon everyUSERAUTH_REQUEST, so a client couldpipeline a request for a second user while the first user's
INFO_REQUESTwasstill outstanding and have the callback run against the second username with the
pending challenge.
Root cause
DoUserAuthInfoResponsesources the username from the live session value:while
DoUserAuthRequestcalledwolfSSH_SetUsernameRaw(...)on every request,with nothing preventing a second
USERAUTH_REQUESTfrom being processed while aKBI
INFO_REQUESTwas pending. A pipelinedUSERAUTH_REQUEST(alice, kbi)thenUSERAUTH_REQUEST(bob, kbi)followed by anINFO_RESPONSEinvoked the callback asbobfor alice's exchange.This path is compiled only with
--enable-keyboard-interactive(
WOLFSSH_KEYBOARD_INTERACTIVE), which is off by default. Forpasswordandpublickeythe username and credential travel in the same packet, so only thesplit KBI flow was exploitable.
Fix
Match OpenSSH's model (
auth2.c,input_userauth_request): the username isbound by the first userauth request and stays fixed for the rest of the
authentication. A later request that changes it ends the session. With
ssh->userNameimmutable after the first request, the existingDoUserAuthInfoResponseassignment is always the originating user, so the swapis closed without KBI-specific state.
The per-request overwrite that previously ran after method dispatch is removed.
The new
byte userAuthSeenis appended to the end ofstruct WOLFSSHand iszero-initialized by the existing full-struct
WMEMSETinSshInit.Behavior
unaffected: a fresh
WOLFSSHstarts withuserAuthSeen = 0and bindsnormally.
unaffected, since the username does not change.
a disconnect, including the pipelined KBI swap.
Tests
Added regression coverage in
tests/regress.cdriving the server with theexisting in-memory transport harness (no sockets, threads, or real KEX):
TestUsernameChangeDisconnects(default build, password method): a secondrequest with a different username returns
WS_FATAL_ERROR, emitsMSGID_DISCONNECT, and never reaches the callback. Covers the guard in themost-shipped configuration.
TestKbUsernameChangeDisconnects(WOLFSSH_KEYBOARD_INTERACTIVE): thereported attack. alice's request emits an
INFO_REQUEST; bob's pipelinedrequest disconnects and never reaches the callback.
TestKbSameUserResponseSucceeds(WOLFSSH_KEYBOARD_INTERACTIVE): a normalsingle-user exchange still reaches the callback bound to the originating user.
Verification
-Werrorin both the default build and--enable-keyboard-interactive --enable-sftp.tests/regress.testandtests/api.testpass in both configurations;the existing
test_wolfSSH_KeyboardInteractivestill passes.fail (the second request is accepted), confirming they catch the bug.