@@ -3272,6 +3272,127 @@ static void TestChannelCloseCallbackReturnIgnored(void)
32723272 FreeChannelOpenHarness (& harness );
32733273}
32743274
3275+ /* Builds a plaintext SSH_MSG_CHANNEL_REQUEST whose type-specific tail is a
3276+ * single string, which is the shape of both "exec" and "subsystem". */
3277+ static word32 BuildChannelStringRequestPacket (word32 recipientChannelId ,
3278+ const char * type , byte wantReply , const char * arg ,
3279+ byte * out , word32 outSz )
3280+ {
3281+ byte payload [128 ];
3282+ word32 idx = 0 ;
3283+
3284+ idx = AppendUint32 (payload , sizeof (payload ), idx , recipientChannelId );
3285+ idx = AppendString (payload , sizeof (payload ), idx , type );
3286+ idx = AppendByte (payload , sizeof (payload ), idx , wantReply );
3287+ idx = AppendString (payload , sizeof (payload ), idx , arg );
3288+
3289+ return WrapPacket (MSGID_CHANNEL_REQUEST , payload , idx , out , outSz );
3290+ }
3291+
3292+ /* What a session request callback saw. */
3293+ static int sessionReqCbCalls ;
3294+ static WS_SessionType sessionReqCbType ;
3295+ static char sessionReqCbCommand [32 ];
3296+ static void * sessionReqCbCtx ;
3297+ static int sessionReqCbReturn ;
3298+
3299+ static int RecordingSessionReqCb (WOLFSSH_CHANNEL * channel , void * ctx )
3300+ {
3301+ const char * command ;
3302+
3303+ sessionReqCbCalls ++ ;
3304+ sessionReqCbCtx = ctx ;
3305+ sessionReqCbCommand [0 ] = 0 ;
3306+
3307+ if (channel != NULL ) {
3308+ sessionReqCbType = wolfSSH_ChannelGetSessionType (channel );
3309+ command = wolfSSH_ChannelGetSessionCommand (channel );
3310+ if (command != NULL ) {
3311+ WSTRNCPY (sessionReqCbCommand , command ,
3312+ sizeof (sessionReqCbCommand ) - 1 );
3313+ sessionReqCbCommand [sizeof (sessionReqCbCommand ) - 1 ] = 0 ;
3314+ }
3315+ }
3316+
3317+ return sessionReqCbReturn ;
3318+ }
3319+
3320+ /* Drives one session request through a fresh harness, with the callback
3321+ * returning cbReturn, and returns the message id the server answered with. */
3322+ static byte RunSessionRequest (const char * type , const char * arg , int cbReturn ,
3323+ WS_SessionType expectType )
3324+ {
3325+ ChannelOpenHarness harness ;
3326+ WOLFSSH_CHANNEL * channel ;
3327+ byte in [128 ];
3328+ word32 inSz ;
3329+ int cbCtx = 0 ;
3330+ byte replyId ;
3331+
3332+ sessionReqCbCalls = 0 ;
3333+ sessionReqCbType = WOLFSSH_SESSION_UNKNOWN ;
3334+ sessionReqCbCommand [0 ] = 0 ;
3335+ sessionReqCbCtx = NULL ;
3336+ sessionReqCbReturn = cbReturn ;
3337+
3338+ InitChannelOpenHarness (& harness , NULL , 0 );
3339+ if (WSTRCMP (type , "exec" ) == 0 ) {
3340+ AssertIntEQ (wolfSSH_CTX_SetChannelReqExecCb (harness .ctx ,
3341+ RecordingSessionReqCb ), WS_SUCCESS );
3342+ }
3343+ else {
3344+ AssertIntEQ (wolfSSH_CTX_SetChannelReqSubsysCb (harness .ctx ,
3345+ RecordingSessionReqCb ), WS_SUCCESS );
3346+ }
3347+ AssertIntEQ (wolfSSH_SetChannelReqCtx (harness .ssh , & cbCtx ), WS_SUCCESS );
3348+
3349+ channel = SeedUnconfirmedChannel (& harness );
3350+ AssertIntEQ (ChannelUpdatePeer (channel , 5 , 1024 , 1024 ), WS_SUCCESS );
3351+ channel -> openConfirmed = 1 ;
3352+
3353+ inSz = BuildChannelStringRequestPacket (channel -> channel , type , 1 , arg ,
3354+ in , sizeof (in ));
3355+ RepointHarnessInput (& harness , in , inSz );
3356+
3357+ AssertIntEQ (DoReceive (harness .ssh ), WS_SUCCESS );
3358+ AssertIntEQ (harness .io .inOff , harness .io .inSz );
3359+ AssertIntEQ (sessionReqCbCalls , 1 );
3360+ AssertTrue (sessionReqCbCtx == & cbCtx );
3361+ AssertIntEQ (sessionReqCbType , expectType );
3362+ AssertIntEQ (WSTRCMP (sessionReqCbCommand , arg ), 0 );
3363+
3364+ replyId = ParseMsgId (harness .io .out , harness .io .outSz );
3365+ FreeChannelOpenHarness (& harness );
3366+
3367+ return replyId ;
3368+ }
3369+
3370+ /* The exec callback is the only place an application can vet a remote
3371+ * command, and its return is what decides the reply on the wire.
3372+ * DoChannelRequest() tests only for nonzero, so a bare 1 rejects the same
3373+ * as a WS_ error. */
3374+ static void TestChannelReqExecCallbackRuns (void )
3375+ {
3376+ AssertIntEQ (RunSessionRequest ("exec" , "ls" , WS_SUCCESS ,
3377+ WOLFSSH_SESSION_EXEC ), MSGID_CHANNEL_SUCCESS );
3378+ AssertIntEQ (RunSessionRequest ("exec" , "ls" , WS_BAD_ARGUMENT ,
3379+ WOLFSSH_SESSION_EXEC ), MSGID_CHANNEL_FAILURE );
3380+ AssertIntEQ (RunSessionRequest ("exec" , "ls" , 1 ,
3381+ WOLFSSH_SESSION_EXEC ), MSGID_CHANNEL_FAILURE );
3382+ }
3383+
3384+ /* Same contract for the subsystem callback, which is how a server decides
3385+ * whether to serve SFTP on a channel. */
3386+ static void TestChannelReqSubsysCallbackRuns (void )
3387+ {
3388+ AssertIntEQ (RunSessionRequest ("subsystem" , "sftp" , WS_SUCCESS ,
3389+ WOLFSSH_SESSION_SUBSYSTEM ), MSGID_CHANNEL_SUCCESS );
3390+ AssertIntEQ (RunSessionRequest ("subsystem" , "sftp" , WS_BAD_ARGUMENT ,
3391+ WOLFSSH_SESSION_SUBSYSTEM ), MSGID_CHANNEL_FAILURE );
3392+ AssertIntEQ (RunSessionRequest ("subsystem" , "sftp" , 1 ,
3393+ WOLFSSH_SESSION_SUBSYSTEM ), MSGID_CHANNEL_FAILURE );
3394+ }
3395+
32753396/* A username change after the first userauth request must end the session. */
32763397static void TestUsernameChangeDisconnects (void )
32773398{
@@ -12472,6 +12593,8 @@ int main(int argc, char** argv)
1247212593 TestChannelOpenFailCallbackRejects ();
1247312594 TestChannelCloseCallbackRuns ();
1247412595 TestChannelCloseCallbackReturnIgnored ();
12596+ TestChannelReqExecCallbackRuns ();
12597+ TestChannelReqSubsysCallbackRuns ();
1247512598 TestSecondSessionChannelRejected ();
1247612599 TestUsernameChangeDisconnects ();
1247712600 TestSameUserRetryAllowed ();
0 commit comments