Skip to content

Commit 3119f6c

Browse files
Add service-name check and regress test
1 parent 7241f4e commit 3119f6c

3 files changed

Lines changed: 160 additions & 5 deletions

File tree

src/internal.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8327,6 +8327,7 @@ static int DoUserAuthRequest(WOLFSSH* ssh,
83278327
word32 begin;
83288328
int ret = WS_SUCCESS;
83298329
byte authNameId;
8330+
byte serviceValid = 1;
83308331
WS_UserAuthData authData;
83318332

83328333
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequest()");
@@ -8361,13 +8362,23 @@ static int DoUserAuthRequest(WOLFSSH* ssh,
83618362
authData.serviceName = buf + begin;
83628363
begin += authData.serviceNameSz;
83638364

8364-
ret = GetSize(&authData.authNameSz, buf, len, &begin);
8365+
if (NameToId((const char*)authData.serviceName, authData.serviceNameSz)
8366+
!= ID_SERVICE_CONNECTION) {
8367+
WLOG(WS_LOG_DEBUG, "DUAR: Invalid service name");
8368+
serviceValid = 0;
8369+
ret = SendUserAuthFailure(ssh, 0);
8370+
/* Consume all remaining data */
8371+
*idx = len;
8372+
}
8373+
else {
8374+
ret = GetSize(&authData.authNameSz, buf, len, &begin);
8375+
}
83658376
}
83668377

8367-
if (ret == WS_SUCCESS) {
8378+
if (ret == WS_SUCCESS && serviceValid) {
83688379
authData.authName = buf + begin;
83698380
begin += authData.authNameSz;
8370-
authNameId = NameToId((char*)authData.authName, authData.authNameSz);
8381+
authNameId = NameToId((const char*)authData.authName, authData.authNameSz);
83718382
ssh->authId = authNameId;
83728383

83738384
if (authNameId == ID_USERAUTH_PASSWORD)
@@ -8390,8 +8401,10 @@ static int DoUserAuthRequest(WOLFSSH* ssh,
83908401
#endif
83918402
else {
83928403
WLOG(WS_LOG_DEBUG,
8393-
"invalid userauth type: %s", IdToName(authNameId));
8404+
"DUAR: invalid userauth type: %s", IdToName(authNameId));
83948405
ret = SendUserAuthFailure(ssh, 0);
8406+
/* Consume all remaining data */
8407+
begin = len;
83958408
}
83968409

83978410
if (ret == WS_SUCCESS) {
@@ -10716,7 +10729,6 @@ int DoReceive(WOLFSSH* ssh)
1071610729
return ret;
1071710730
}
1071810731

10719-
1072010732
int DoProtoId(WOLFSSH* ssh)
1072110733
{
1072210734
int ret;
@@ -17907,6 +17919,12 @@ int wolfSSH_TestChannelPutData(WOLFSSH_CHANNEL* channel, byte* data,
1790717919
return ChannelPutData(channel, data, dataSz);
1790817920
}
1790917921

17922+
int wolfSSH_TestDoUserAuthRequest(WOLFSSH* ssh, byte* buf, word32 len,
17923+
word32* idx)
17924+
{
17925+
return DoUserAuthRequest(ssh, buf, len, idx);
17926+
}
17927+
1791017928
#ifndef WOLFSSH_NO_DH_GEX_SHA256
1791117929

1791217930
int wolfSSH_TestDoKexDhGexRequest(WOLFSSH* ssh, byte* buf, word32 len,

tests/unit.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,136 @@ 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+
* [4-byte packet_length][1-byte padding_length][1-byte msg_id]...)
940+
* 3. *idx == len (entire payload consumed; buffer stays aligned)
941+
*
942+
* For invalid-service cases the auth-method field is intentionally omitted
943+
* from the payload. DoUserAuthRequest must short-circuit at the service-name
944+
* check and still satisfy all three assertions — proving it never tries to
945+
* parse the missing auth-method field. If the short-circuit were absent,
946+
* GetSize() for authNameSz would hit end-of-buffer and return WS_BUFFER_E,
947+
* failing assertion 1.
948+
*
949+
* For the valid-service case, auth method "xyz-unknown" (always unsupported
950+
* regardless of compile-time options) is included. The function reaches
951+
* auth-method dispatch, falls to the unknown-method else-branch, and sends
952+
* USERAUTH_FAILURE via that normal path. */
953+
static int test_DoUserAuthRequest_serviceName(void)
954+
{
955+
WOLFSSH_CTX* ctx = NULL;
956+
WOLFSSH* ssh = NULL;
957+
int result = 0;
958+
struct {
959+
const char* svcName;
960+
word32 svcNameSz;
961+
const char* authMethod; /* NULL = omit field (proves short-circuit) */
962+
word32 authMethodSz;
963+
int expectRet;
964+
const char* label;
965+
} cases[] = {
966+
/* valid service: auth dispatch fires, fails on unknown method */
967+
{ "ssh-connection", 14, "xyz-unknown", 11, WS_SUCCESS,
968+
"valid svc unknown auth" },
969+
/* invalid service: short-circuit, auth-method field absent */
970+
{ "ssh-agent", 9, NULL, 0, WS_SUCCESS,
971+
"invalid ssh-agent svc" },
972+
{ "bad", 3, NULL, 0, WS_SUCCESS,
973+
"invalid bad svc" },
974+
/* zero-length service name: NameToId("",0)==ID_UNKNOWN, must reject */
975+
{ "", 0, NULL, 0, WS_SUCCESS,
976+
"zero-length svc" },
977+
/* ssh-userauth: NameToId returns ID_SERVICE_USERAUTH, not
978+
* ID_SERVICE_CONNECTION, so must also be rejected */
979+
{ "ssh-userauth", 12, NULL, 0, WS_SUCCESS,
980+
"invalid ssh-userauth svc" },
981+
};
982+
int i;
983+
984+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
985+
if (ctx == NULL) return -500;
986+
wolfSSH_SetIOSend(ctx, CaptureIoSendAuthSvc);
987+
988+
ssh = wolfSSH_new(ctx);
989+
if (ssh == NULL) { wolfSSH_CTX_free(ctx); return -501; }
990+
991+
for (i = 0; i < (int)(sizeof(cases)/sizeof(cases[0])); i++) {
992+
byte buf[128];
993+
word32 len = 0, idx = 0;
994+
word32 snsz = cases[i].svcNameSz;
995+
int ret;
996+
997+
s_authSvcCaptureSz = 0;
998+
WMEMSET(s_authSvcCapture, 0, sizeof(s_authSvcCapture));
999+
1000+
/* username: "user" */
1001+
buf[len++] = 0; buf[len++] = 0; buf[len++] = 0; buf[len++] = 4;
1002+
WMEMCPY(buf + len, "user", 4); len += 4;
1003+
1004+
/* service name */
1005+
buf[len++] = (byte)(snsz >> 24); buf[len++] = (byte)(snsz >> 16);
1006+
buf[len++] = (byte)(snsz >> 8); buf[len++] = (byte)snsz;
1007+
WMEMCPY(buf + len, cases[i].svcName, snsz); len += snsz;
1008+
1009+
/* auth method: omit for invalid-service cases to prove short-circuit */
1010+
if (cases[i].authMethod != NULL) {
1011+
word32 amsz = cases[i].authMethodSz;
1012+
buf[len++] = (byte)(amsz >> 24); buf[len++] = (byte)(amsz >> 16);
1013+
buf[len++] = (byte)(amsz >> 8); buf[len++] = (byte)amsz;
1014+
WMEMCPY(buf + len, cases[i].authMethod, amsz); len += amsz;
1015+
}
1016+
1017+
ret = wolfSSH_TestDoUserAuthRequest(ssh, buf, len, &idx);
1018+
1019+
if (ret != cases[i].expectRet) {
1020+
printf("DoUserAuthRequest_svcName[%s]: ret=%d expected=%d\n",
1021+
cases[i].label, ret, cases[i].expectRet);
1022+
result = -502 - i;
1023+
break;
1024+
}
1025+
1026+
/* MSGID_USERAUTH_FAILURE must be in the captured packet. */
1027+
if (s_authSvcCaptureSz <= 5 ||
1028+
s_authSvcCapture[5] != MSGID_USERAUTH_FAILURE) {
1029+
printf("DoUserAuthRequest_svcName[%s]: USERAUTH_FAILURE not sent "
1030+
"(capSz=%u byte5=0x%02x)\n", cases[i].label,
1031+
s_authSvcCaptureSz,
1032+
s_authSvcCaptureSz > 5 ? s_authSvcCapture[5] : 0);
1033+
result = -520 - i;
1034+
break;
1035+
}
1036+
1037+
/* All cases must consume the entire payload. */
1038+
if (idx != len) {
1039+
printf("DoUserAuthRequest_svcName[%s]: idx=%u expected len=%u\n",
1040+
cases[i].label, idx, len);
1041+
result = -510 - i;
1042+
break;
1043+
}
1044+
}
1045+
1046+
wolfSSH_free(ssh);
1047+
wolfSSH_CTX_free(ctx);
1048+
return result;
1049+
}
1050+
9211051
#if !defined(WOLFSSH_NO_RSA)
9221052

9231053
/* 2048-bit RSA private key (PKCS#1 DER).
@@ -1210,6 +1340,11 @@ int wolfSSH_UnitTest(int argc, char** argv)
12101340
unitResult = test_ChannelPutData();
12111341
printf("ChannelPutData: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED"));
12121342
testResult = testResult || unitResult;
1343+
1344+
unitResult = test_DoUserAuthRequest_serviceName();
1345+
printf("DoUserAuthRequest_serviceName: %s\n",
1346+
(unitResult == 0 ? "SUCCESS" : "FAILED"));
1347+
testResult = testResult || unitResult;
12131348
#endif
12141349

12151350
#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)