Skip to content

Commit b2d19fb

Browse files
Add service-name check and regress test
1 parent fdf621c commit b2d19fb

3 files changed

Lines changed: 147 additions & 2 deletions

File tree

src/internal.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8367,6 +8367,7 @@ static int DoUserAuthRequest(WOLFSSH* ssh,
83678367
word32 begin;
83688368
int ret = WS_SUCCESS;
83698369
byte authNameId;
8370+
byte serviceValid = 1;
83708371
WS_UserAuthData authData;
83718372

83728373
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequest()");
@@ -8401,10 +8402,19 @@ static int DoUserAuthRequest(WOLFSSH* ssh,
84018402
authData.serviceName = buf + begin;
84028403
begin += authData.serviceNameSz;
84038404

8404-
ret = GetSize(&authData.authNameSz, buf, len, &begin);
8405+
if (NameToId((const char*)authData.serviceName, authData.serviceNameSz)
8406+
!= ID_SERVICE_CONNECTION) {
8407+
WLOG(WS_LOG_DEBUG, "DUAR: Invalid service name");
8408+
serviceValid = 0;
8409+
ret = SendUserAuthFailure(ssh, 0);
8410+
*idx = len;
8411+
}
8412+
else {
8413+
ret = GetSize(&authData.authNameSz, buf, len, &begin);
8414+
}
84058415
}
84068416

8407-
if (ret == WS_SUCCESS) {
8417+
if (ret == WS_SUCCESS && serviceValid) {
84088418
authData.authName = buf + begin;
84098419
begin += authData.authNameSz;
84108420
authNameId = NameToId((char*)authData.authName, authData.authNameSz);
@@ -10773,6 +10783,12 @@ int wolfSSH_TestChannelPutData(WOLFSSH_CHANNEL* channel, byte* data,
1077310783
{
1077410784
return ChannelPutData(channel, data, dataSz);
1077510785
}
10786+
10787+
int wolfSSH_TestDoUserAuthRequest(WOLFSSH* ssh, byte* buf, word32 len,
10788+
word32* idx)
10789+
{
10790+
return DoUserAuthRequest(ssh, buf, len, idx);
10791+
}
1077610792
#endif
1077710793

1077810794

tests/unit.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

wolfssh/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,8 @@ enum WS_MessageIdLimits {
13381338
WOLFSSH_API int wolfSSH_TestDoKexDhInit(WOLFSSH* ssh, byte* buf,
13391339
word32 len, word32* idx);
13401340
WOLFSSH_API int wolfSSH_TestChannelPutData(WOLFSSH_CHANNEL*, byte*, word32);
1341+
WOLFSSH_API int wolfSSH_TestDoUserAuthRequest(WOLFSSH* ssh, byte* buf,
1342+
word32 len, word32* idx);
13411343
#ifndef WOLFSSH_NO_DH_GEX_SHA256
13421344
WOLFSSH_API int wolfSSH_TestDoKexDhGexRequest(WOLFSSH* ssh, byte* buf,
13431345
word32 len, word32* idx);

0 commit comments

Comments
 (0)