@@ -1839,6 +1839,14 @@ static int RejectChannelOpenCb(WOLFSSH_CHANNEL* channel, void* ctx)
18391839}
18401840
18411841#ifdef WOLFSSH_FWD
1842+ static int AcceptChannelOpenCb (WOLFSSH_CHANNEL * channel , void * ctx )
1843+ {
1844+ (void )channel ;
1845+ (void )ctx ;
1846+
1847+ return WS_SUCCESS ;
1848+ }
1849+
18421850static int RejectDirectTcpipSetup (WS_FwdCbAction action , void * ctx ,
18431851 const char * host , word32 port )
18441852{
@@ -1863,6 +1871,41 @@ static int AcceptFwdCb(WS_FwdCbAction action, void* ctx,
18631871 return WS_SUCCESS ;
18641872}
18651873
1874+ /* Counts every action the library asks for. File scope rather than reached
1875+ * through the callback ctx, so a zero reading means the hook did not run and
1876+ * cannot instead mean the ctx stopped being delivered. */
1877+ static word32 fwdCbCallCount ;
1878+
1879+ static int CountingFwdCb (WS_FwdCbAction action , void * ctx ,
1880+ const char * host , word32 port )
1881+ {
1882+ (void )action ;
1883+ (void )ctx ;
1884+ (void )host ;
1885+ (void )port ;
1886+
1887+ fwdCbCallCount ++ ;
1888+
1889+ return WS_SUCCESS ;
1890+ }
1891+
1892+ /* Counts, and rejects the channel-id handoff that follows a successful
1893+ * LOCAL_SETUP -- the second of DoChannelOpen()'s two fwdCb consultations. */
1894+ static int CountingRejectChannelIdFwdCb (WS_FwdCbAction action , void * ctx ,
1895+ const char * host , word32 port )
1896+ {
1897+ (void )ctx ;
1898+ (void )host ;
1899+ (void )port ;
1900+
1901+ fwdCbCallCount ++ ;
1902+
1903+ if (action == WOLFSSH_FWD_CHANNEL_ID )
1904+ return WS_FWD_NOT_AVAILABLE ;
1905+
1906+ return WS_SUCCESS ;
1907+ }
1908+
18661909#define REGRESS_FWD_ALLOC_PORT 49152
18671910
18681911static int AllocatePortFwdCb (WS_FwdCbAction action , void * ctx ,
@@ -2674,6 +2717,112 @@ static void TestDirectTcpipNoFwdCbSendsOpenFail(void)
26742717 FreeChannelOpenHarness (& harness );
26752718}
26762719
2720+ /* Both a channelOpenCb and a fwdCb registered, open callback rejects. The
2721+ * rejection has to stand: the forwarding hook must not run, and must not
2722+ * overwrite the rejection with its own return. That clobber shipped in
2723+ * v1.5.0, where no test registered both callbacks at once. */
2724+ static void TestDirectTcpipOpenCbRejectBeatsFwdCb (void )
2725+ {
2726+ ChannelOpenHarness harness ;
2727+ byte extra [128 ];
2728+ byte in [192 ];
2729+ word32 extraSz ;
2730+ word32 inSz ;
2731+ int ret ;
2732+
2733+ fwdCbCallCount = 0 ;
2734+
2735+ extraSz = BuildDirectTcpipExtra ("127.0.0.1" , 8080 , "127.0.0.1" , 2222 ,
2736+ extra , sizeof (extra ));
2737+ inSz = BuildChannelOpenPacket ("direct-tcpip" , 9 , 0x4000 , 0x8000 ,
2738+ extra , extraSz , in , sizeof (in ));
2739+
2740+ InitChannelOpenHarness (& harness , in , inSz );
2741+ AssertIntEQ (wolfSSH_CTX_SetChannelOpenCb (harness .ctx , RejectChannelOpenCb ),
2742+ WS_SUCCESS );
2743+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx , CountingFwdCb , NULL ),
2744+ WS_SUCCESS );
2745+
2746+ ret = DoReceive (harness .ssh );
2747+ AssertChannelOpenFailResponse (& harness , ret );
2748+ AssertIntEQ (ParseChannelOpenFailRecipient (harness .io .out , harness .io .outSz ),
2749+ 9 ); /* RFC 4254 5.1: the peer's channel ID comes back */
2750+ AssertIntEQ (ParseChannelOpenFailReason (harness .io .out , harness .io .outSz ),
2751+ OPEN_ADMINISTRATIVELY_PROHIBITED );
2752+ AssertIntEQ (fwdCbCallCount , 0 );
2753+
2754+ FreeChannelOpenHarness (& harness );
2755+ }
2756+
2757+ /* The other half of the pair: the open callback accepts, so the fwdCb decides,
2758+ * and its rejection must reach the peer. */
2759+ static void TestDirectTcpipFwdCbRejectAfterOpenCbAccept (void )
2760+ {
2761+ ChannelOpenHarness harness ;
2762+ byte extra [128 ];
2763+ byte in [192 ];
2764+ word32 extraSz ;
2765+ word32 inSz ;
2766+ int ret ;
2767+
2768+ extraSz = BuildDirectTcpipExtra ("127.0.0.1" , 8080 , "127.0.0.1" , 2222 ,
2769+ extra , sizeof (extra ));
2770+ inSz = BuildChannelOpenPacket ("direct-tcpip" , 9 , 0x4000 , 0x8000 ,
2771+ extra , extraSz , in , sizeof (in ));
2772+
2773+ InitChannelOpenHarness (& harness , in , inSz );
2774+ AssertIntEQ (wolfSSH_CTX_SetChannelOpenCb (harness .ctx , AcceptChannelOpenCb ),
2775+ WS_SUCCESS );
2776+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx , RejectDirectTcpipSetup , NULL ),
2777+ WS_SUCCESS );
2778+
2779+ ret = DoReceive (harness .ssh );
2780+ AssertChannelOpenFailResponse (& harness , ret );
2781+ AssertIntEQ (ParseChannelOpenFailRecipient (harness .io .out , harness .io .outSz ),
2782+ 9 );
2783+ AssertIntEQ (ParseChannelOpenFailReason (harness .io .out , harness .io .outSz ),
2784+ OPEN_ADMINISTRATIVELY_PROHIBITED );
2785+
2786+ FreeChannelOpenHarness (& harness );
2787+ }
2788+
2789+ /* DoChannelOpen() consults the fwdCb twice. A rejection at the second
2790+ * consultation, the channel-id handoff, must fail the open the same way the
2791+ * setup rejection does. The count doubles as the positive control for the
2792+ * zero asserted above: the same counter reaches 2 here. */
2793+ static void TestDirectTcpipFwdCbRejectsChannelId (void )
2794+ {
2795+ ChannelOpenHarness harness ;
2796+ byte extra [128 ];
2797+ byte in [192 ];
2798+ word32 extraSz ;
2799+ word32 inSz ;
2800+ int ret ;
2801+
2802+ fwdCbCallCount = 0 ;
2803+
2804+ extraSz = BuildDirectTcpipExtra ("127.0.0.1" , 8080 , "127.0.0.1" , 2222 ,
2805+ extra , sizeof (extra ));
2806+ inSz = BuildChannelOpenPacket ("direct-tcpip" , 9 , 0x4000 , 0x8000 ,
2807+ extra , extraSz , in , sizeof (in ));
2808+
2809+ InitChannelOpenHarness (& harness , in , inSz );
2810+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx ,
2811+ CountingRejectChannelIdFwdCb , NULL ), WS_SUCCESS );
2812+
2813+ ret = DoReceive (harness .ssh );
2814+ AssertChannelOpenFailResponse (& harness , ret );
2815+ AssertIntEQ (ParseChannelOpenFailRecipient (harness .io .out , harness .io .outSz ),
2816+ 9 );
2817+ /* This path leaves fail_reason at OPEN_OK and leans on the default,
2818+ * unlike the rejections that set the reason themselves. */
2819+ AssertIntEQ (ParseChannelOpenFailReason (harness .io .out , harness .io .outSz ),
2820+ OPEN_ADMINISTRATIVELY_PROHIBITED );
2821+ AssertIntEQ (fwdCbCallCount , 2 );
2822+
2823+ FreeChannelOpenHarness (& harness );
2824+ }
2825+
26772826static void TestForwardedTcpipOnServerSendsOpenFail (void )
26782827{
26792828 ChannelOpenHarness harness ;
@@ -8025,6 +8174,9 @@ int main(int argc, char** argv)
80258174#ifdef WOLFSSH_FWD
80268175 TestDirectTcpipRejectSendsOpenFail ();
80278176 TestDirectTcpipNoFwdCbSendsOpenFail ();
8177+ TestDirectTcpipOpenCbRejectBeatsFwdCb ();
8178+ TestDirectTcpipFwdCbRejectAfterOpenCbAccept ();
8179+ TestDirectTcpipFwdCbRejectsChannelId ();
80288180 TestForwardedTcpipOnServerSendsOpenFail ();
80298181 TestGlobalRequestFwdNoCbSendsFailure ();
80308182 TestGlobalRequestFwdNoCbNoReplyKeepsConnection ();
0 commit comments