@@ -124,6 +124,103 @@ static byte ParseMsgId(const byte* pkt, word32 sz)
124124 return pkt [5 ];
125125}
126126
127+ static word32 AppendByte (byte * buf , word32 bufSz , word32 idx , byte value )
128+ {
129+ AssertTrue (idx < bufSz );
130+ buf [idx ++ ] = value ;
131+ return idx ;
132+ }
133+
134+ static word32 AppendUint32 (byte * buf , word32 bufSz , word32 idx , word32 value )
135+ {
136+ word32 netValue = htonl (value );
137+
138+ AssertTrue (idx + UINT32_SZ <= bufSz );
139+ WMEMCPY (buf + idx , & netValue , UINT32_SZ );
140+ idx += UINT32_SZ ;
141+ return idx ;
142+ }
143+
144+ static word32 AppendData (byte * buf , word32 bufSz , word32 idx ,
145+ const byte * data , word32 dataSz )
146+ {
147+ AssertTrue (idx + dataSz <= bufSz );
148+ if (dataSz > 0 ) {
149+ WMEMCPY (buf + idx , data , dataSz );
150+ idx += dataSz ;
151+ }
152+ return idx ;
153+ }
154+
155+ static word32 AppendString (byte * buf , word32 bufSz , word32 idx ,
156+ const char * value )
157+ {
158+ word32 valueSz = (word32 )WSTRLEN (value );
159+
160+ idx = AppendUint32 (buf , bufSz , idx , valueSz );
161+ return AppendData (buf , bufSz , idx , (const byte * )value , valueSz );
162+ }
163+
164+ static word32 WrapPacket (byte msgId , const byte * payload , word32 payloadSz ,
165+ byte * out , word32 outSz )
166+ {
167+ word32 idx = 0 ;
168+ word32 packetLen ;
169+ word32 need ;
170+ byte padLen = MIN_PAD_LENGTH ;
171+
172+ while (((UINT32_SZ + PAD_LENGTH_SZ + MSG_ID_SZ + payloadSz + padLen ) %
173+ MIN_BLOCK_SZ ) != 0 ) {
174+ padLen ++ ;
175+ }
176+
177+ packetLen = PAD_LENGTH_SZ + MSG_ID_SZ + payloadSz + padLen ;
178+ need = UINT32_SZ + packetLen ;
179+
180+ AssertTrue (outSz >= need );
181+
182+ idx = AppendUint32 (out , outSz , idx , packetLen );
183+ idx = AppendByte (out , outSz , idx , padLen );
184+ idx = AppendByte (out , outSz , idx , msgId );
185+ idx = AppendData (out , outSz , idx , payload , payloadSz );
186+ AssertTrue (idx + padLen <= outSz );
187+ WMEMSET (out + idx , 0 , padLen );
188+ idx += padLen ;
189+
190+ return idx ;
191+ }
192+
193+ static word32 BuildChannelOpenPacket (const char * type , word32 peerChannelId ,
194+ word32 peerInitialWindowSz , word32 peerMaxPacketSz ,
195+ const byte * extra , word32 extraSz , byte * out , word32 outSz )
196+ {
197+ byte payload [256 ];
198+ word32 idx = 0 ;
199+
200+ idx = AppendString (payload , sizeof (payload ), idx , type );
201+ idx = AppendUint32 (payload , sizeof (payload ), idx , peerChannelId );
202+ idx = AppendUint32 (payload , sizeof (payload ), idx , peerInitialWindowSz );
203+ idx = AppendUint32 (payload , sizeof (payload ), idx , peerMaxPacketSz );
204+ idx = AppendData (payload , sizeof (payload ), idx , extra , extraSz );
205+
206+ return WrapPacket (MSGID_CHANNEL_OPEN , payload , idx , out , outSz );
207+ }
208+
209+ #ifdef WOLFSSH_FWD
210+ static word32 BuildDirectTcpipExtra (const char * host , word32 hostPort ,
211+ const char * origin , word32 originPort , byte * out , word32 outSz )
212+ {
213+ word32 idx = 0 ;
214+
215+ idx = AppendString (out , outSz , idx , host );
216+ idx = AppendUint32 (out , outSz , idx , hostPort );
217+ idx = AppendString (out , outSz , idx , origin );
218+ idx = AppendUint32 (out , outSz , idx , originPort );
219+
220+ return idx ;
221+ }
222+ #endif
223+
127224/* Simple in-memory transport harness */
128225typedef struct {
129226 byte * in ; /* data to feed into client */
@@ -134,9 +231,6 @@ typedef struct {
134231 word32 outCap ;
135232} MemIo ;
136233
137- /* Minimal send/recv helpers for future transport-level tests; keep them static
138- * and unused for now to avoid warnings when Werror is on. */
139- #ifdef WOLFSSH_TEST_MEMIO
140234static int MemRecv (WOLFSSH * ssh , void * buf , word32 sz , void * ctx )
141235{
142236 (void )ssh ;
@@ -172,6 +266,78 @@ static void MemIoInit(MemIo* io, byte* in, word32 inSz, byte* out, word32 outCap
172266 io -> outSz = 0 ;
173267 io -> outCap = outCap ;
174268}
269+
270+ typedef struct {
271+ WOLFSSH_CTX * ctx ;
272+ WOLFSSH * ssh ;
273+ MemIo io ;
274+ byte out [256 ];
275+ } ChannelOpenHarness ;
276+
277+ static void InitChannelOpenHarness (ChannelOpenHarness * harness ,
278+ byte * in , word32 inSz )
279+ {
280+ WMEMSET (harness , 0 , sizeof (* harness ));
281+
282+ harness -> ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_SERVER , NULL );
283+ AssertNotNull (harness -> ctx );
284+
285+ wolfSSH_SetIORecv (harness -> ctx , MemRecv );
286+ wolfSSH_SetIOSend (harness -> ctx , MemSend );
287+
288+ harness -> ssh = wolfSSH_new (harness -> ctx );
289+ AssertNotNull (harness -> ssh );
290+
291+ MemIoInit (& harness -> io , in , inSz , harness -> out , sizeof (harness -> out ));
292+ wolfSSH_SetIOReadCtx (harness -> ssh , & harness -> io );
293+ wolfSSH_SetIOWriteCtx (harness -> ssh , & harness -> io );
294+ harness -> ssh -> acceptState = ACCEPT_SERVER_USERAUTH_SENT ;
295+ }
296+
297+ static void FreeChannelOpenHarness (ChannelOpenHarness * harness )
298+ {
299+ if (harness -> ssh != NULL )
300+ wolfSSH_free (harness -> ssh );
301+ if (harness -> ctx != NULL )
302+ wolfSSH_CTX_free (harness -> ctx );
303+ }
304+
305+ static void AssertChannelOpenFailResponse (const ChannelOpenHarness * harness ,
306+ int ret )
307+ {
308+ byte msgId ;
309+
310+ AssertIntEQ (ret , WS_SUCCESS );
311+ AssertIntEQ (harness -> io .inOff , harness -> io .inSz );
312+ AssertTrue (harness -> io .outSz > 0 );
313+ AssertTrue (harness -> io .outSz <= harness -> io .outCap );
314+
315+ msgId = ParseMsgId (harness -> io .out , harness -> io .outSz );
316+ AssertIntEQ (msgId , MSGID_CHANNEL_OPEN_FAIL );
317+ AssertFalse (msgId == MSGID_REQUEST_FAILURE );
318+ }
319+
320+ static int RejectChannelOpenCb (WOLFSSH_CHANNEL * channel , void * ctx )
321+ {
322+ (void )channel ;
323+ (void )ctx ;
324+
325+ return WS_BAD_ARGUMENT ;
326+ }
327+
328+ #ifdef WOLFSSH_FWD
329+ static int RejectDirectTcpipSetup (WS_FwdCbAction action , void * ctx ,
330+ const char * host , word32 port )
331+ {
332+ (void )ctx ;
333+ (void )host ;
334+ (void )port ;
335+
336+ if (action == WOLFSSH_FWD_LOCAL_SETUP )
337+ return WS_FWD_SETUP_E ;
338+
339+ return WS_SUCCESS ;
340+ }
175341#endif
176342
177343
@@ -369,6 +535,73 @@ static void TestChannelAllowedAfterAuth(WOLFSSH* ssh)
369535 AssertTrue (allowed );
370536}
371537
538+ static void TestChannelOpenCallbackRejectSendsOpenFail (void )
539+ {
540+ ChannelOpenHarness harness ;
541+ byte in [128 ];
542+ word32 inSz ;
543+ int ret ;
544+
545+ inSz = BuildChannelOpenPacket ("session" , 7 , 0x4000 , 0x8000 ,
546+ NULL , 0 , in , sizeof (in ));
547+
548+ InitChannelOpenHarness (& harness , in , inSz );
549+ AssertIntEQ (wolfSSH_CTX_SetChannelOpenCb (harness .ctx , RejectChannelOpenCb ),
550+ WS_SUCCESS );
551+
552+ ret = DoReceive (harness .ssh );
553+ AssertChannelOpenFailResponse (& harness , ret );
554+
555+ FreeChannelOpenHarness (& harness );
556+ }
557+
558+ #ifdef WOLFSSH_FWD
559+ static void TestDirectTcpipRejectSendsOpenFail (void )
560+ {
561+ ChannelOpenHarness harness ;
562+ byte extra [128 ];
563+ byte in [192 ];
564+ word32 extraSz ;
565+ word32 inSz ;
566+ int ret ;
567+
568+ extraSz = BuildDirectTcpipExtra ("127.0.0.1" , 8080 , "127.0.0.1" , 2222 ,
569+ extra , sizeof (extra ));
570+ inSz = BuildChannelOpenPacket ("direct-tcpip" , 9 , 0x4000 , 0x8000 ,
571+ extra , extraSz , in , sizeof (in ));
572+
573+ InitChannelOpenHarness (& harness , in , inSz );
574+ AssertIntEQ (wolfSSH_CTX_SetFwdCb (harness .ctx , RejectDirectTcpipSetup , NULL ),
575+ WS_SUCCESS );
576+
577+ ret = DoReceive (harness .ssh );
578+ AssertChannelOpenFailResponse (& harness , ret );
579+
580+ FreeChannelOpenHarness (& harness );
581+ }
582+ #endif
583+
584+ #ifdef WOLFSSH_AGENT
585+ static void TestAgentChannelNullAgentSendsOpenFail (void )
586+ {
587+ ChannelOpenHarness harness ;
588+ byte in [128 ];
589+ word32 inSz ;
590+ int ret ;
591+
592+ inSz = BuildChannelOpenPacket ("auth-agent@openssh.com" , 11 , 0x4000 ,
593+ 0x8000 , NULL , 0 , in , sizeof (in ));
594+
595+ InitChannelOpenHarness (& harness , in , inSz );
596+ AssertTrue (harness .ssh -> agent == NULL );
597+
598+ ret = DoReceive (harness .ssh );
599+ AssertChannelOpenFailResponse (& harness , ret );
600+
601+ FreeChannelOpenHarness (& harness );
602+ }
603+ #endif
604+
372605
373606/* Reject a peer KEXINIT once keying is in progress. */
374607static void TestKexInitRejectedWhenKeying (WOLFSSH * ssh )
@@ -583,6 +816,13 @@ int main(int argc, char** argv)
583816 TestPublicKeyFailureAborts (ssh );
584817 TestChannelBlockedBeforeAuth (ssh );
585818 TestChannelAllowedAfterAuth (ssh );
819+ TestChannelOpenCallbackRejectSendsOpenFail ();
820+ #ifdef WOLFSSH_FWD
821+ TestDirectTcpipRejectSendsOpenFail ();
822+ #endif
823+ #ifdef WOLFSSH_AGENT
824+ TestAgentChannelNullAgentSendsOpenFail ();
825+ #endif
586826 TestKexInitRejectedWhenKeying (ssh );
587827 TestClientBuffersIdempotent ();
588828 TestPasswordEofNoCrash ();
0 commit comments