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