@@ -2297,8 +2297,9 @@ static void TestClientServiceAcceptBlockedDuringKeying(WOLFSSH* ssh)
22972297
22982298
22992299/* Drive the whole receive path with a CHANNEL_OPEN sent from a pre-auth
2300- * connectState: no channel created, no reply emitted. The connectState
2301- * gate does the rejecting; isKeying below is scene-setting only. */
2300+ * connectState: no channel created, and the only thing sent back is the
2301+ * disconnect RFC 4252 section 6 asks for. The connectState gate does the
2302+ * rejecting; isKeying below is scene-setting only. */
23022303static void TestChannelOpenRejectedBeforeKex (byte connectState )
23032304{
23042305 WOLFSSH_CTX * ctx ;
@@ -2330,7 +2331,11 @@ static void TestChannelOpenRejectedBeforeKex(byte connectState)
23302331 AssertIntEQ (ssh -> error , WS_MSGID_NOT_ALLOWED_E );
23312332 AssertNull (ssh -> channelList );
23322333 AssertIntEQ (ssh -> channelListSz , 0 );
2333- AssertIntEQ (io .outSz , 0 );
2334+ /* Not silence: the peer is told why the session ended, and the message
2335+ * is a disconnect rather than anything answering the channel open. */
2336+ AssertTrue (io .outSz > 0 );
2337+ AssertIntEQ (ParseMsgId (io .out , io .outSz ), MSGID_DISCONNECT );
2338+ AssertTrue (ssh -> disconnected );
23342339
23352340 wolfSSH_free (ssh );
23362341 wolfSSH_CTX_free (ctx );
@@ -2403,6 +2408,103 @@ static void TestServerUserauthBlockedBeforeKeyed(WOLFSSH* ssh)
24032408}
24042409
24052410
2411+ /* One packet fed to a keyed but unauthenticated server. The cases below
2412+ * differ only in the packet and the answer it draws. */
2413+ static void RunServerMsgIdAtKeyed (const byte * pkt , word32 pktSz ,
2414+ int expectRet , int expectErr , byte expectReplyMsgId ,
2415+ int expectDisconnected )
2416+ {
2417+ WOLFSSH_CTX * ctx ;
2418+ WOLFSSH * ssh ;
2419+ MemIo io ;
2420+ byte out [256 ];
2421+
2422+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_SERVER , NULL );
2423+ AssertNotNull (ctx );
2424+ wolfSSH_SetIORecv (ctx , MemRecv );
2425+ wolfSSH_SetIOSend (ctx , MemSend );
2426+
2427+ ssh = wolfSSH_new (ctx );
2428+ AssertNotNull (ssh );
2429+
2430+ MemIoInit (& io , (byte * )pkt , pktSz , out , sizeof (out ));
2431+ wolfSSH_SetIOReadCtx (ssh , & io );
2432+ wolfSSH_SetIOWriteCtx (ssh , & io );
2433+
2434+ /* Past the key exchange, short of user auth being answered. */
2435+ ssh -> acceptState = ACCEPT_KEYED ;
2436+
2437+ AssertIntEQ (wolfSSH_TestDoReceive (ssh ), expectRet );
2438+ AssertIntEQ (ssh -> error , expectErr );
2439+ /* Nothing the filter refuses may leave a channel behind. */
2440+ AssertNull (ssh -> channelList );
2441+ AssertIntEQ (ssh -> channelListSz , 0 );
2442+ AssertTrue (io .outSz > 0 );
2443+ AssertIntEQ (ParseMsgId (io .out , io .outSz ), expectReplyMsgId );
2444+ AssertIntEQ (ssh -> disconnected != 0 , expectDisconnected );
2445+
2446+ wolfSSH_free (ssh );
2447+ wolfSSH_CTX_free (ctx );
2448+ }
2449+
2450+
2451+ /* The case RFC 4252 section 6 names: an id of 80 or higher before user auth.
2452+ * Keyed, so the post-userauth limit rejects it, not the pre-keyed range. */
2453+ static void TestServerHighMsgIdBeforeAuthDisconnects (void )
2454+ {
2455+ byte pkt [256 ];
2456+ word32 pktSz ;
2457+
2458+ pktSz = BuildChannelOpenPacket ("session" , 0 , 131072 , 16384 , NULL , 0 ,
2459+ pkt , sizeof (pkt ));
2460+
2461+ RunServerMsgIdAtKeyed (pkt , pktSz , WS_FATAL_ERROR , WS_MSGID_NOT_ALLOWED_E ,
2462+ MSGID_DISCONNECT , 1 );
2463+ }
2464+
2465+
2466+ /* Id 79 is refused too, but it is unimplemented and below 80, so RFC 4253
2467+ * section 11.4 rules: UNIMPLEMENTED, and the session lives. */
2468+ static void TestServerUnknownMsgIdBeforeAuthUnimplemented (void )
2469+ {
2470+ byte pkt [256 ];
2471+ word32 pktSz ;
2472+
2473+ pktSz = WrapPacket (79 , NULL , 0 , pkt , sizeof (pkt ));
2474+
2475+ RunServerMsgIdAtKeyed (pkt , pktSz , WS_SUCCESS , WS_SUCCESS ,
2476+ MSGID_UNIMPLEMENTED , 0 );
2477+ }
2478+
2479+
2480+ /* Below 80 the dispatch decides: 53 has a case, so it disconnects where
2481+ * 79 does not. */
2482+ static void TestServerKnownAuthMsgIdBeforeAuthDisconnects (void )
2483+ {
2484+ byte pkt [256 ];
2485+ word32 pktSz ;
2486+
2487+ pktSz = WrapPacket (MSGID_USERAUTH_BANNER , NULL , 0 , pkt , sizeof (pkt ));
2488+
2489+ RunServerMsgIdAtKeyed (pkt , pktSz , WS_FATAL_ERROR , WS_MSGID_NOT_ALLOWED_E ,
2490+ MSGID_DISCONNECT , 1 );
2491+ }
2492+
2493+
2494+ /* At 80 and above the range decides instead: id 200 disconnects though
2495+ * nothing dispatches it. */
2496+ static void TestServerUnknownHighMsgIdBeforeAuthDisconnects (void )
2497+ {
2498+ byte pkt [256 ];
2499+ word32 pktSz ;
2500+
2501+ pktSz = WrapPacket (200 , NULL , 0 , pkt , sizeof (pkt ));
2502+
2503+ RunServerMsgIdAtKeyed (pkt , pktSz , WS_FATAL_ERROR , WS_MSGID_NOT_ALLOWED_E ,
2504+ MSGID_DISCONNECT , 1 );
2505+ }
2506+
2507+
24062508/* Reject the user auth messages that only the server sends, while still
24072509 * accepting the keyboard-interactive info response that it receives. */
24082510static void TestServerOnlyUserauthMsgsBlocked (WOLFSSH * ssh )
@@ -2492,8 +2594,11 @@ static void TestServerServiceRequestRejectedDuringKeying(void)
24922594 /* Not dispatched, so acceptState did not advance. */
24932595 AssertIntEQ (ssh -> clientState , CLIENT_BEGIN );
24942596 AssertIntEQ (ssh -> acceptState , ACCEPT_KEYED );
2495- /* Nothing emitted in reply. */
2496- AssertIntEQ (io .outSz , 0 );
2597+ /* Not silence: nothing answers the service request, but the peer is told
2598+ * the session ended on a protocol error. */
2599+ AssertTrue (io .outSz > 0 );
2600+ AssertIntEQ (ParseMsgId (io .out , io .outSz ), MSGID_DISCONNECT );
2601+ AssertTrue (ssh -> disconnected );
24972602
24982603 /* Allowed when only this side has started a rekey. */
24992604 ssh -> error = 0 ;
@@ -3077,6 +3182,104 @@ static void TestGlobalRequestFwdNoCbSendsFailure(void)
30773182 FreeChannelOpenHarness (& harness );
30783183}
30793184
3185+ #ifndef NO_WOLFSSH_CLIENT
3186+ /* A client is the side that asks for a remote forward, so a tcpip-forward it
3187+ * receives is answered with a failure and never registered. RFC 4254 section
3188+ * 7.1. A fwdCb is registered throughout: without the role check that callback
3189+ * is the only gate, and it would answer success. */
3190+ static void TestGlobalRequestFwdOnClientSendsFailure (void )
3191+ {
3192+ ChannelOpenHarness harness ;
3193+ byte in [256 ];
3194+ word32 inSz ;
3195+ int ret ;
3196+
3197+ inSz = BuildGlobalRequestFwdPacket ("0.0.0.0" , 2222 , 0 , 1 , in , sizeof (in ));
3198+ InitChannelOpenHarnessClient (& harness , in , inSz );
3199+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx , AcceptFwdCb , NULL ),
3200+ WS_SUCCESS );
3201+
3202+ ret = DoReceive (harness .ssh );
3203+
3204+ AssertIntEQ (ret , WS_SUCCESS );
3205+ AssertGlobalRequestReply (& harness , MSGID_REQUEST_FAILURE );
3206+
3207+ FreeChannelOpenHarness (& harness );
3208+ }
3209+
3210+ /* cancel-tcpip-forward travels the same direction, so a client refuses it on
3211+ * the same grounds. */
3212+ static void TestGlobalRequestFwdCancelOnClientSendsFailure (void )
3213+ {
3214+ ChannelOpenHarness harness ;
3215+ byte in [256 ];
3216+ word32 inSz ;
3217+ int ret ;
3218+
3219+ inSz = BuildGlobalRequestFwdPacket ("0.0.0.0" , 2222 , 1 , 1 , in , sizeof (in ));
3220+ InitChannelOpenHarnessClient (& harness , in , inSz );
3221+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx , AcceptFwdCb , NULL ),
3222+ WS_SUCCESS );
3223+
3224+ ret = DoReceive (harness .ssh );
3225+
3226+ AssertIntEQ (ret , WS_SUCCESS );
3227+ AssertGlobalRequestReply (& harness , MSGID_REQUEST_FAILURE );
3228+
3229+ FreeChannelOpenHarness (& harness );
3230+ }
3231+
3232+ /* The refusal is silent when the peer did not ask for a reply: nothing goes
3233+ * back, and the session carries on. Silence alone would also be the answer
3234+ * without the role check, since a request the callback accepts and that asks
3235+ * for no reply sends nothing either, so the callback is the discriminator
3236+ * here: the request must be turned away before it reaches one. */
3237+ static void TestGlobalRequestFwdOnClientNoReplyStaysQuiet (void )
3238+ {
3239+ ChannelOpenHarness harness ;
3240+ byte in [256 ];
3241+ word32 inSz ;
3242+ int ret ;
3243+
3244+ inSz = BuildGlobalRequestFwdPacket ("0.0.0.0" , 2222 , 0 , 0 , in , sizeof (in ));
3245+ InitChannelOpenHarnessClient (& harness , in , inSz );
3246+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx , CountingFwdCb , NULL ),
3247+ WS_SUCCESS );
3248+ fwdCbCallCount = 0 ;
3249+
3250+ ret = DoReceive (harness .ssh );
3251+
3252+ AssertIntEQ (ret , WS_SUCCESS );
3253+ AssertIntEQ (harness .io .outSz , 0 );
3254+ AssertIntEQ (fwdCbCallCount , 0 );
3255+
3256+ FreeChannelOpenHarness (& harness );
3257+ }
3258+
3259+ #endif /* !NO_WOLFSSH_CLIENT */
3260+
3261+ /* The role check must not cost the server anything: the same request that a
3262+ * client refuses is still honoured here. */
3263+ static void TestGlobalRequestFwdOnServerStillSucceeds (void )
3264+ {
3265+ ChannelOpenHarness harness ;
3266+ byte in [256 ];
3267+ word32 inSz ;
3268+ int ret ;
3269+
3270+ inSz = BuildGlobalRequestFwdPacket ("0.0.0.0" , 2222 , 0 , 1 , in , sizeof (in ));
3271+ InitChannelOpenHarness (& harness , in , inSz );
3272+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx , AcceptFwdCb , NULL ),
3273+ WS_SUCCESS );
3274+
3275+ ret = DoReceive (harness .ssh );
3276+
3277+ AssertIntEQ (ret , WS_SUCCESS );
3278+ AssertGlobalRequestReply (& harness , MSGID_REQUEST_SUCCESS );
3279+
3280+ FreeChannelOpenHarness (& harness );
3281+ }
3282+
30803283static void TestGlobalRequestFwdNoCbNoReplyKeepsConnection (void )
30813284{
30823285 ChannelOpenHarness harness ;
@@ -10816,6 +11019,10 @@ int main(int argc, char** argv)
1081611019 TestServerChannelBlockedBeforeAuth (serverSsh );
1081711020 TestServerChannelAllowedAfterAuth (serverSsh );
1081811021 TestServerUserauthBlockedBeforeKeyed (serverSsh );
11022+ TestServerHighMsgIdBeforeAuthDisconnects ();
11023+ TestServerUnknownMsgIdBeforeAuthUnimplemented ();
11024+ TestServerKnownAuthMsgIdBeforeAuthDisconnects ();
11025+ TestServerUnknownHighMsgIdBeforeAuthDisconnects ();
1081911026 TestServerOnlyUserauthMsgsBlocked (serverSsh );
1082011027 TestServerServiceRequestStateGated (serverSsh );
1082111028 TestServerServiceRequestRejectedDuringKeying ();
@@ -10836,6 +11043,12 @@ int main(int argc, char** argv)
1083611043 TestDirectTcpipFwdCbRejectsChannelId ();
1083711044 TestForwardedTcpipOnServerSendsOpenFail ();
1083811045 TestGlobalRequestFwdNoCbSendsFailure ();
11046+ #ifndef NO_WOLFSSH_CLIENT
11047+ TestGlobalRequestFwdOnClientSendsFailure ();
11048+ TestGlobalRequestFwdCancelOnClientSendsFailure ();
11049+ TestGlobalRequestFwdOnClientNoReplyStaysQuiet ();
11050+ #endif
11051+ TestGlobalRequestFwdOnServerStillSucceeds ();
1083911052 TestGlobalRequestFwdNoCbNoReplyKeepsConnection ();
1084011053 TestGlobalRequestFwdWithCbSendsSuccess ();
1084111054 TestGlobalRequestFwdPort0ReturnsAllocatedPort ();
0 commit comments