@@ -918,6 +918,128 @@ static int test_DoChannelRequest(void)
918918 return result ;
919919}
920920
921+ /* Capture buffer for the service-name unit test. Separate from the channel-
922+ * request capture so the two tests can run independently in any order. */
923+ static byte s_authSvcCapture [256 ];
924+ static word32 s_authSvcCaptureSz = 0 ;
925+
926+ static int CaptureIoSendAuthSvc (WOLFSSH * ssh , void * buf , word32 sz , void * ctx )
927+ {
928+ (void )ssh ; (void )ctx ;
929+ s_authSvcCaptureSz = (sz < (word32 )sizeof (s_authSvcCapture ))
930+ ? sz : (word32 )sizeof (s_authSvcCapture );
931+ WMEMCPY (s_authSvcCapture , buf , s_authSvcCaptureSz );
932+ return (int )sz ;
933+ }
934+
935+ /* Verify DoUserAuthRequest rejects non-"ssh-connection" service names per
936+ * RFC 4252 Section 5. For each case we assert:
937+ * 1. ret == WS_SUCCESS (connection stays open for retry)
938+ * 2. SSH_MSG_USERAUTH_FAILURE is actually sent (captured at packet byte 5)
939+ * 3. *idx == len (entire payload consumed; buffer stays aligned)
940+ *
941+ * For invalid-service cases the auth-method field is intentionally omitted
942+ * from the payload. DoUserAuthRequest must short-circuit at the service-name
943+ * check and still satisfy all three assertions — proving it never tries to
944+ * parse the missing auth-method field. If the short-circuit were absent,
945+ * GetSize() for authNameSz would hit end-of-buffer and return WS_BUFFER_E,
946+ * failing assertion 1.
947+ *
948+ * For the valid-service case, auth method "xyz-unknown" (always unsupported
949+ * regardless of compile-time options) is included. The function reaches
950+ * auth-method dispatch, falls to the unknown-method else-branch, and sends
951+ * USERAUTH_FAILURE via that normal path. */
952+ static int test_DoUserAuthRequest_serviceName (void )
953+ {
954+ WOLFSSH_CTX * ctx = NULL ;
955+ WOLFSSH * ssh = NULL ;
956+ int result = 0 ;
957+ struct {
958+ const char * svcName ;
959+ word32 svcNameSz ;
960+ const char * authMethod ; /* NULL = omit field (proves short-circuit) */
961+ word32 authMethodSz ;
962+ int expectRet ;
963+ const char * label ;
964+ } cases [] = {
965+ /* valid service: auth dispatch fires, fails on unknown method */
966+ { "ssh-connection" , 14 , "xyz-unknown" , 11 , WS_SUCCESS ,
967+ "valid svc unknown auth" },
968+ /* invalid service: short-circuit, auth-method field absent */
969+ { "ssh-agent" , 9 , NULL , 0 , WS_SUCCESS ,
970+ "invalid ssh-agent svc" },
971+ { "bad" , 3 , NULL , 0 , WS_SUCCESS ,
972+ "invalid bad svc" },
973+ };
974+ int i ;
975+
976+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_SERVER , NULL );
977+ if (ctx == NULL ) return -500 ;
978+ wolfSSH_SetIOSend (ctx , CaptureIoSendAuthSvc );
979+
980+ ssh = wolfSSH_new (ctx );
981+ if (ssh == NULL ) { wolfSSH_CTX_free (ctx ); return -501 ; }
982+
983+ for (i = 0 ; i < (int )(sizeof (cases )/sizeof (cases [0 ])); i ++ ) {
984+ byte buf [128 ];
985+ word32 len = 0 , idx = 0 ;
986+ word32 snsz = cases [i ].svcNameSz ;
987+ int ret ;
988+
989+ s_authSvcCaptureSz = 0 ;
990+ WMEMSET (s_authSvcCapture , 0 , sizeof (s_authSvcCapture ));
991+
992+ /* username: "user" */
993+ buf [len ++ ] = 0 ; buf [len ++ ] = 0 ; buf [len ++ ] = 0 ; buf [len ++ ] = 4 ;
994+ WMEMCPY (buf + len , "user" , 4 ); len += 4 ;
995+
996+ /* service name */
997+ buf [len ++ ] = (byte )(snsz >> 24 ); buf [len ++ ] = (byte )(snsz >> 16 );
998+ buf [len ++ ] = (byte )(snsz >> 8 ); buf [len ++ ] = (byte )snsz ;
999+ WMEMCPY (buf + len , cases [i ].svcName , snsz ); len += snsz ;
1000+
1001+ /* auth method: omit for invalid-service cases to prove short-circuit */
1002+ if (cases [i ].authMethod != NULL ) {
1003+ word32 amsz = cases [i ].authMethodSz ;
1004+ buf [len ++ ] = (byte )(amsz >> 24 ); buf [len ++ ] = (byte )(amsz >> 16 );
1005+ buf [len ++ ] = (byte )(amsz >> 8 ); buf [len ++ ] = (byte )amsz ;
1006+ WMEMCPY (buf + len , cases [i ].authMethod , amsz ); len += amsz ;
1007+ }
1008+
1009+ ret = wolfSSH_TestDoUserAuthRequest (ssh , buf , len , & idx );
1010+
1011+ if (ret != cases [i ].expectRet ) {
1012+ printf ("DoUserAuthRequest_svcName[%s]: ret=%d expected=%d\n" ,
1013+ cases [i ].label , ret , cases [i ].expectRet );
1014+ result = -502 - i ;
1015+ break ;
1016+ }
1017+
1018+ /* MSGID_USERAUTH_FAILURE must be in the captured packet. */
1019+ if (s_authSvcCaptureSz <= 5 ||
1020+ s_authSvcCapture [5 ] != MSGID_USERAUTH_FAILURE ) {
1021+ printf ("DoUserAuthRequest_svcName[%s]: USERAUTH_FAILURE not sent "
1022+ "(capSz=%u byte5=0x%02x)\n" , cases [i ].label ,
1023+ s_authSvcCaptureSz ,
1024+ s_authSvcCaptureSz > 5 ? s_authSvcCapture [5 ] : 0 );
1025+ result = -520 - i ;
1026+ break ;
1027+ }
1028+
1029+ /* All cases must consume the entire payload. */
1030+ if (idx != len ) {
1031+ printf ("DoUserAuthRequest_svcName[%s]: idx=%u expected len=%u\n" ,
1032+ cases [i ].label , idx , len );
1033+ result = -510 - i ;
1034+ break ;
1035+ }
1036+ }
1037+
1038+ wolfSSH_free (ssh );
1039+ wolfSSH_CTX_free (ctx );
1040+ return result ;
1041+ }
1042+
9211043#if !defined(WOLFSSH_NO_RSA )
9221044
9231045/* 2048-bit RSA private key (PKCS#1 DER).
@@ -1210,6 +1332,11 @@ int wolfSSH_UnitTest(int argc, char** argv)
12101332 unitResult = test_ChannelPutData ();
12111333 printf ("ChannelPutData: %s\n" , (unitResult == 0 ? "SUCCESS" : "FAILED" ));
12121334 testResult = testResult || unitResult ;
1335+
1336+ unitResult = test_DoUserAuthRequest_serviceName ();
1337+ printf ("DoUserAuthRequest_serviceName: %s\n" ,
1338+ (unitResult == 0 ? "SUCCESS" : "FAILED" ));
1339+ testResult = testResult || unitResult ;
12131340#endif
12141341
12151342#ifdef WOLFSSH_KEYGEN
0 commit comments