@@ -3350,6 +3350,127 @@ static void TestChannelCloseCallbackReturnIgnored(void)
33503350 FreeChannelOpenHarness (& harness );
33513351}
33523352
3353+ /* Builds a plaintext SSH_MSG_CHANNEL_REQUEST whose type-specific tail is a
3354+ * single string, which is the shape of both "exec" and "subsystem". */
3355+ static word32 BuildChannelStringRequestPacket (word32 recipientChannelId ,
3356+ const char * type , byte wantReply , const char * arg ,
3357+ byte * out , word32 outSz )
3358+ {
3359+ byte payload [128 ];
3360+ word32 idx = 0 ;
3361+
3362+ idx = AppendUint32 (payload , sizeof (payload ), idx , recipientChannelId );
3363+ idx = AppendString (payload , sizeof (payload ), idx , type );
3364+ idx = AppendByte (payload , sizeof (payload ), idx , wantReply );
3365+ idx = AppendString (payload , sizeof (payload ), idx , arg );
3366+
3367+ return WrapPacket (MSGID_CHANNEL_REQUEST , payload , idx , out , outSz );
3368+ }
3369+
3370+ /* What a session request callback saw. */
3371+ static int sessionReqCbCalls ;
3372+ static WS_SessionType sessionReqCbType ;
3373+ static char sessionReqCbCommand [32 ];
3374+ static void * sessionReqCbCtx ;
3375+ static int sessionReqCbReturn ;
3376+
3377+ static int RecordingSessionReqCb (WOLFSSH_CHANNEL * channel , void * ctx )
3378+ {
3379+ const char * command ;
3380+
3381+ sessionReqCbCalls ++ ;
3382+ sessionReqCbCtx = ctx ;
3383+ sessionReqCbCommand [0 ] = 0 ;
3384+
3385+ if (channel != NULL ) {
3386+ sessionReqCbType = wolfSSH_ChannelGetSessionType (channel );
3387+ command = wolfSSH_ChannelGetSessionCommand (channel );
3388+ if (command != NULL ) {
3389+ WSTRNCPY (sessionReqCbCommand , command ,
3390+ sizeof (sessionReqCbCommand ) - 1 );
3391+ sessionReqCbCommand [sizeof (sessionReqCbCommand ) - 1 ] = 0 ;
3392+ }
3393+ }
3394+
3395+ return sessionReqCbReturn ;
3396+ }
3397+
3398+ /* Drives one session request through a fresh harness, with the callback
3399+ * returning cbReturn, and returns the message id the server answered with. */
3400+ static byte RunSessionRequest (const char * type , const char * arg , int cbReturn ,
3401+ WS_SessionType expectType )
3402+ {
3403+ ChannelOpenHarness harness ;
3404+ WOLFSSH_CHANNEL * channel ;
3405+ byte in [128 ];
3406+ word32 inSz ;
3407+ int cbCtx = 0 ;
3408+ byte replyId ;
3409+
3410+ sessionReqCbCalls = 0 ;
3411+ sessionReqCbType = WOLFSSH_SESSION_UNKNOWN ;
3412+ sessionReqCbCommand [0 ] = 0 ;
3413+ sessionReqCbCtx = NULL ;
3414+ sessionReqCbReturn = cbReturn ;
3415+
3416+ InitChannelOpenHarness (& harness , NULL , 0 );
3417+ if (WSTRCMP (type , "exec" ) == 0 ) {
3418+ AssertIntEQ (wolfSSH_CTX_SetChannelReqExecCb (harness .ctx ,
3419+ RecordingSessionReqCb ), WS_SUCCESS );
3420+ }
3421+ else {
3422+ AssertIntEQ (wolfSSH_CTX_SetChannelReqSubsysCb (harness .ctx ,
3423+ RecordingSessionReqCb ), WS_SUCCESS );
3424+ }
3425+ AssertIntEQ (wolfSSH_SetChannelReqCtx (harness .ssh , & cbCtx ), WS_SUCCESS );
3426+
3427+ channel = SeedUnconfirmedChannel (& harness );
3428+ AssertIntEQ (ChannelUpdatePeer (channel , 5 , 1024 , 1024 ), WS_SUCCESS );
3429+ channel -> openConfirmed = 1 ;
3430+
3431+ inSz = BuildChannelStringRequestPacket (channel -> channel , type , 1 , arg ,
3432+ in , sizeof (in ));
3433+ RepointHarnessInput (& harness , in , inSz );
3434+
3435+ AssertIntEQ (DoReceive (harness .ssh ), WS_SUCCESS );
3436+ AssertIntEQ (harness .io .inOff , harness .io .inSz );
3437+ AssertIntEQ (sessionReqCbCalls , 1 );
3438+ AssertTrue (sessionReqCbCtx == & cbCtx );
3439+ AssertIntEQ (sessionReqCbType , expectType );
3440+ AssertIntEQ (WSTRCMP (sessionReqCbCommand , arg ), 0 );
3441+
3442+ replyId = ParseMsgId (harness .io .out , harness .io .outSz );
3443+ FreeChannelOpenHarness (& harness );
3444+
3445+ return replyId ;
3446+ }
3447+
3448+ /* The exec callback is the only place an application can vet a remote
3449+ * command, and its return is what decides the reply on the wire.
3450+ * DoChannelRequest() tests only for nonzero, so a bare 1 rejects the same
3451+ * as a WS_ error. */
3452+ static void TestChannelReqExecCallbackRuns (void )
3453+ {
3454+ AssertIntEQ (RunSessionRequest ("exec" , "ls" , WS_SUCCESS ,
3455+ WOLFSSH_SESSION_EXEC ), MSGID_CHANNEL_SUCCESS );
3456+ AssertIntEQ (RunSessionRequest ("exec" , "ls" , WS_BAD_ARGUMENT ,
3457+ WOLFSSH_SESSION_EXEC ), MSGID_CHANNEL_FAILURE );
3458+ AssertIntEQ (RunSessionRequest ("exec" , "ls" , 1 ,
3459+ WOLFSSH_SESSION_EXEC ), MSGID_CHANNEL_FAILURE );
3460+ }
3461+
3462+ /* Same contract for the subsystem callback, which is how a server decides
3463+ * whether to serve SFTP on a channel. */
3464+ static void TestChannelReqSubsysCallbackRuns (void )
3465+ {
3466+ AssertIntEQ (RunSessionRequest ("subsystem" , "sftp" , WS_SUCCESS ,
3467+ WOLFSSH_SESSION_SUBSYSTEM ), MSGID_CHANNEL_SUCCESS );
3468+ AssertIntEQ (RunSessionRequest ("subsystem" , "sftp" , WS_BAD_ARGUMENT ,
3469+ WOLFSSH_SESSION_SUBSYSTEM ), MSGID_CHANNEL_FAILURE );
3470+ AssertIntEQ (RunSessionRequest ("subsystem" , "sftp" , 1 ,
3471+ WOLFSSH_SESSION_SUBSYSTEM ), MSGID_CHANNEL_FAILURE );
3472+ }
3473+
33533474/* A username change after the first userauth request must end the session. */
33543475static void TestUsernameChangeDisconnects (void )
33553476{
@@ -12554,6 +12675,8 @@ int main(int argc, char** argv)
1255412675 TestChannelOpenFailCallbackRejects ();
1255512676 TestChannelCloseCallbackRuns ();
1255612677 TestChannelCloseCallbackReturnIgnored ();
12678+ TestChannelReqExecCallbackRuns ();
12679+ TestChannelReqSubsysCallbackRuns ();
1255712680 TestSecondSessionChannelRejected ();
1255812681 TestUsernameChangeDisconnects ();
1255912682 TestSameUserRetryAllowed ();
0 commit comments