Skip to content

Commit 030335f

Browse files
Add auth tests to verify cases of invalid/unknowen return value
1 parent 85cf3e7 commit 030335f

1 file changed

Lines changed: 321 additions & 0 deletions

File tree

tests/auth.c

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,162 @@ static void test_pubkey_auth_wrong_key(void)
765765
}
766766
#endif /* !WOLFSSH_NO_RSA && !WOLFSSH_NO_ECC */
767767

768+
/* -----------------------------------------------------------------------
769+
* Password auth: unknown callback return value must not grant auth (issue 2486)
770+
* ----------------------------------------------------------------------- */
771+
772+
/* Tracks how many times the server password auth callback has been invoked;
773+
* must be reset to 0 before each test run. */
774+
static int invalidPwAttempts = 0;
775+
776+
/* Server userAuth callback for test_invalid_cb_password.
777+
* First call returns an out-of-enum value (-999) to exercise the default else
778+
* branch in DoUserAuthRequestPassword. Second call returns REJECTED so the
779+
* connection terminates cleanly and wolfSSH_accept() can return an error. */
780+
static int invalidPasswordServerAuth(byte authType, WS_UserAuthData* authData,
781+
void* ctx)
782+
{
783+
(void)authData;
784+
(void)ctx;
785+
if (authType != WOLFSSH_USERAUTH_PASSWORD)
786+
return WOLFSSH_USERAUTH_FAILURE;
787+
if (invalidPwAttempts++ == 0)
788+
return -999; /* unknown value: exercises default else; authFailure=1 */
789+
return WOLFSSH_USERAUTH_REJECTED; /* clean termination on retry */
790+
}
791+
792+
/* Client userAuth callback for password tests: supplies a dummy password so
793+
* the auth request reaches the server-side callback. */
794+
static int clientPasswordUserAuth(byte authType, WS_UserAuthData* authData,
795+
void* ctx)
796+
{
797+
static const byte pw[] = "dummypass";
798+
(void)ctx;
799+
if (authType != WOLFSSH_USERAUTH_PASSWORD)
800+
return WOLFSSH_USERAUTH_FAILURE;
801+
authData->sf.password.password = pw;
802+
authData->sf.password.passwordSz = (word32)(sizeof(pw) - 1);
803+
return WOLFSSH_USERAUTH_SUCCESS;
804+
}
805+
806+
/* Server thread for test_invalid_cb_password. Mirrors pubkey_server_thread
807+
* but registers invalidPasswordServerAuth and stores the return code cleanly
808+
* (no ES_ERROR abort) so the test can assert on it. */
809+
static THREAD_RETURN WOLFSSH_THREAD password_server_thread(void* args)
810+
{
811+
thread_args* serverArgs = (thread_args*)args;
812+
int ret = WS_SUCCESS;
813+
word16 port = 0;
814+
WOLFSSH_CTX* ctx = NULL;
815+
WOLFSSH* ssh = NULL;
816+
byte buf[EXAMPLE_KEYLOAD_BUFFER_SZ];
817+
word32 bufSz;
818+
WS_SOCKET_T listenFd = WOLFSSH_SOCKET_INVALID;
819+
WS_SOCKET_T clientFd = WOLFSSH_SOCKET_INVALID;
820+
SOCKADDR_IN_T clientAddr;
821+
socklen_t clientAddrSz = sizeof(clientAddr);
822+
823+
serverArgs->return_code = EXIT_SUCCESS;
824+
825+
tcp_listen(&listenFd, &port, 1);
826+
SignalTcpReady(serverArgs->signal, port);
827+
828+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
829+
if (ctx == NULL) { serverArgs->return_code = WS_MEMORY_E; goto cleanup; }
830+
831+
wolfSSH_SetUserAuth(ctx, invalidPasswordServerAuth);
832+
833+
ssh = wolfSSH_new(ctx);
834+
if (ssh == NULL) { serverArgs->return_code = WS_MEMORY_E; goto cleanup; }
835+
836+
#ifndef WOLFSSH_NO_ECDSA
837+
bufSz = (word32)load_key(1, buf, sizeof(buf));
838+
#else
839+
bufSz = (word32)load_key(0, buf, sizeof(buf));
840+
#endif
841+
if (bufSz == 0 || wolfSSH_CTX_UsePrivateKey_buffer(ctx, buf, bufSz,
842+
WOLFSSH_FORMAT_ASN1) < 0) {
843+
serverArgs->return_code = WS_BAD_FILE_E; goto cleanup;
844+
}
845+
846+
clientFd = accept(listenFd, (struct sockaddr*)&clientAddr, &clientAddrSz);
847+
if (clientFd == WOLFSSH_SOCKET_INVALID) {
848+
serverArgs->return_code = WS_SOCKET_ERROR_E; goto cleanup;
849+
}
850+
wolfSSH_set_fd(ssh, (int)clientFd);
851+
852+
ret = wolfSSH_accept(ssh);
853+
serverArgs->return_code = ret;
854+
855+
cleanup:
856+
if (ssh != NULL && clientFd != WOLFSSH_SOCKET_INVALID)
857+
wolfSSH_shutdown(ssh);
858+
if (ssh != NULL) wolfSSH_free(ssh);
859+
if (ctx != NULL) wolfSSH_CTX_free(ctx);
860+
if (clientFd != WOLFSSH_SOCKET_INVALID) WCLOSESOCKET(clientFd);
861+
if (listenFd != WOLFSSH_SOCKET_INVALID) WCLOSESOCKET(listenFd);
862+
863+
WOLFSSL_RETURN_FROM_THREAD(0);
864+
}
865+
866+
/* Test: server password-auth callback returning an unknown value must not
867+
* grant authentication. Flow:
868+
* 1. Client sends password → server callback returns -999 → authFailure=1
869+
* → SendUserAuthFailure (else branch in DoUserAuthRequestPassword hit).
870+
* 2. Client retries → server callback returns WOLFSSH_USERAUTH_REJECTED
871+
* → WS_USER_AUTH_E → server sends disconnect.
872+
* 3. wolfSSH_connect() returns WS_FATAL_ERROR; server return_code != WS_SUCCESS. */
873+
static void test_invalid_cb_password(void)
874+
{
875+
thread_args serverArgs;
876+
tcp_ready ready;
877+
THREAD_TYPE serThread;
878+
WOLFSSH_CTX* clientCtx = NULL;
879+
WOLFSSH* clientSsh = NULL;
880+
SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID;
881+
SOCKADDR_IN_T clientAddr;
882+
socklen_t clientAddrSz = sizeof(clientAddr);
883+
int ret;
884+
885+
printf("Testing password auth with unknown callback return value\n");
886+
invalidPwAttempts = 0;
887+
888+
serverArgs.signal = &ready;
889+
serverArgs.pubkeyServerCtx = NULL;
890+
InitTcpReady(serverArgs.signal);
891+
892+
ThreadStart(password_server_thread, (void*)&serverArgs, &serThread);
893+
WaitTcpReady(&ready);
894+
895+
clientCtx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
896+
AssertNotNull(clientCtx);
897+
wolfSSH_CTX_SetPublicKeyCheck(clientCtx, AcceptAnyServerHostKey);
898+
wolfSSH_SetUserAuth(clientCtx, clientPasswordUserAuth);
899+
900+
clientSsh = wolfSSH_new(clientCtx);
901+
AssertNotNull(clientSsh);
902+
wolfSSH_SetUsername(clientSsh, "jill");
903+
904+
build_addr(&clientAddr, (char*)wolfSshIp, ready.port);
905+
tcp_socket(&sockFd, ((struct sockaddr_in*)&clientAddr)->sin_family);
906+
AssertIntEQ(connect(sockFd, (const struct sockaddr*)&clientAddr,
907+
clientAddrSz), 0);
908+
wolfSSH_set_fd(clientSsh, (int)sockFd);
909+
910+
ret = wolfSSH_connect(clientSsh);
911+
AssertIntEQ(ret, WS_FATAL_ERROR);
912+
913+
wolfSSH_shutdown(clientSsh);
914+
WCLOSESOCKET(sockFd);
915+
wolfSSH_free(clientSsh);
916+
wolfSSH_CTX_free(clientCtx);
917+
918+
ThreadJoin(serThread);
919+
AssertIntNE(serverArgs.return_code, WS_SUCCESS); /* auth must NOT be granted */
920+
921+
FreeTcpReady(&ready);
922+
}
923+
768924
#endif /* pubkey test guard */
769925

770926
#if !defined(NO_WOLFSSH_SERVER) && !defined(NO_WOLFSSH_CLIENT) && \
@@ -1153,6 +1309,165 @@ static void test_unbalanced_client_KeyboardInteractive(void)
11531309
test_client();
11541310
unbalanced = 0;
11551311
}
1312+
1313+
/* -----------------------------------------------------------------------
1314+
* Keyboard-interactive auth: unknown callback return value must not grant
1315+
* authentication (issue 2486)
1316+
* ----------------------------------------------------------------------- */
1317+
1318+
/* Server userAuth callback for test_invalid_cb_keyboard.
1319+
* KEYBOARD_SETUP is handled normally so the exchange reaches
1320+
* DoUserAuthInfoResponse. For the KEYBOARD (response-validation) step the
1321+
* callback returns -999, an out-of-enum value, to exercise the default else
1322+
* branch in DoUserAuthInfoResponse. */
1323+
static int invalidKbServerAuth(byte authType, WS_UserAuthData* authData,
1324+
void* ctx)
1325+
{
1326+
WS_UserAuthData_Keyboard* prompts = (WS_UserAuthData_Keyboard*)ctx;
1327+
1328+
if (authType == WOLFSSH_USERAUTH_KEYBOARD_SETUP) {
1329+
WMEMCPY(&authData->sf.keyboard, prompts, sizeof(WS_UserAuthData_Keyboard));
1330+
return WS_SUCCESS;
1331+
}
1332+
if (authType == WOLFSSH_USERAUTH_KEYBOARD)
1333+
return -999; /* unknown value: exercises default else; authFailure=1 */
1334+
return WOLFSSH_USERAUTH_FAILURE;
1335+
}
1336+
1337+
/* Server thread for test_invalid_cb_keyboard. Sets up one keyboard prompt and
1338+
* registers invalidKbServerAuth. Stores the return code cleanly (no ES_ERROR
1339+
* abort) so the test can assert on it. */
1340+
static THREAD_RETURN WOLFSSH_THREAD kb_invalid_server_thread(void* args)
1341+
{
1342+
thread_args* serverArgs = (thread_args*)args;
1343+
int ret = WS_SUCCESS;
1344+
word16 port = 0;
1345+
WOLFSSH_CTX* ctx = NULL;
1346+
WOLFSSH* ssh = NULL;
1347+
byte buf[EXAMPLE_KEYLOAD_BUFFER_SZ];
1348+
word32 bufSz;
1349+
WS_SOCKET_T listenFd = WOLFSSH_SOCKET_INVALID;
1350+
WS_SOCKET_T clientFd = WOLFSSH_SOCKET_INVALID;
1351+
SOCKADDR_IN_T clientAddr;
1352+
socklen_t clientAddrSz = sizeof(clientAddr);
1353+
WS_UserAuthData_Keyboard localPrompts;
1354+
byte* kbPrompts[1];
1355+
word32 kbPromptLengths[1];
1356+
byte kbPromptEcho[1];
1357+
1358+
serverArgs->return_code = EXIT_SUCCESS;
1359+
1360+
kbPrompts[0] = (byte*)"Password: ";
1361+
kbPromptLengths[0] = 10;
1362+
kbPromptEcho[0] = 0;
1363+
WMEMSET(&localPrompts, 0, sizeof(localPrompts));
1364+
localPrompts.promptCount = 1;
1365+
localPrompts.prompts = kbPrompts;
1366+
localPrompts.promptLengths = kbPromptLengths;
1367+
localPrompts.promptEcho = kbPromptEcho;
1368+
1369+
tcp_listen(&listenFd, &port, 1);
1370+
SignalTcpReady(serverArgs->signal, port);
1371+
1372+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
1373+
if (ctx == NULL) { serverArgs->return_code = WS_MEMORY_E; goto cleanup; }
1374+
1375+
wolfSSH_SetUserAuth(ctx, invalidKbServerAuth);
1376+
1377+
ssh = wolfSSH_new(ctx);
1378+
if (ssh == NULL) { serverArgs->return_code = WS_MEMORY_E; goto cleanup; }
1379+
1380+
wolfSSH_SetUserAuthCtx(ssh, &localPrompts);
1381+
1382+
bufSz = (word32)load_key(1, buf, sizeof(buf));
1383+
if (bufSz == 0) bufSz = (word32)load_key(0, buf, sizeof(buf));
1384+
if (bufSz == 0 || wolfSSH_CTX_UsePrivateKey_buffer(ctx, buf, bufSz,
1385+
WOLFSSH_FORMAT_ASN1) < 0) {
1386+
serverArgs->return_code = WS_BAD_FILE_E; goto cleanup;
1387+
}
1388+
1389+
clientFd = accept(listenFd, (struct sockaddr*)&clientAddr, &clientAddrSz);
1390+
if (clientFd == WOLFSSH_SOCKET_INVALID) {
1391+
serverArgs->return_code = WS_SOCKET_ERROR_E; goto cleanup;
1392+
}
1393+
wolfSSH_set_fd(ssh, (int)clientFd);
1394+
1395+
ret = wolfSSH_accept(ssh);
1396+
serverArgs->return_code = ret;
1397+
1398+
cleanup:
1399+
if (ssh != NULL && clientFd != WOLFSSH_SOCKET_INVALID)
1400+
wolfSSH_shutdown(ssh);
1401+
if (ssh != NULL) wolfSSH_free(ssh);
1402+
if (ctx != NULL) wolfSSH_CTX_free(ctx);
1403+
if (clientFd != WOLFSSH_SOCKET_INVALID) WCLOSESOCKET(clientFd);
1404+
if (listenFd != WOLFSSH_SOCKET_INVALID) WCLOSESOCKET(listenFd);
1405+
1406+
WOLFSSL_RETURN_FROM_THREAD(0);
1407+
}
1408+
1409+
/* Test: server keyboard-interactive callback returning an unknown value must
1410+
* not grant authentication. Flow:
1411+
* 1. Server provides one prompt; client sends one response.
1412+
* 2. Server callback returns -999 → authFailure=1 → SendUserAuthFailure
1413+
* (else branch in DoUserAuthInfoResponse hit).
1414+
* 3. Client retries keyboard auth; after ssh->kbAuthAttempts reaches 3 the
1415+
* client stops trying keyboard and DoUserAuthFailure returns WS_USER_AUTH_E.
1416+
* 4. wolfSSH_connect() returns WS_FATAL_ERROR; server return_code != WS_SUCCESS. */
1417+
static void test_invalid_cb_keyboard(void)
1418+
{
1419+
thread_args serverArgs;
1420+
tcp_ready ready;
1421+
THREAD_TYPE serThread;
1422+
WOLFSSH_CTX* clientCtx = NULL;
1423+
WOLFSSH* clientSsh = NULL;
1424+
SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID;
1425+
SOCKADDR_IN_T clientAddr;
1426+
socklen_t clientAddrSz = sizeof(clientAddr);
1427+
int ret;
1428+
1429+
printf("Testing keyboard-interactive auth with unknown callback return value\n");
1430+
1431+
kbResponses[0] = (byte*)testText1;
1432+
kbResponseLengths[0] = 4;
1433+
kbResponseCount = 1;
1434+
1435+
serverArgs.signal = &ready;
1436+
serverArgs.pubkeyServerCtx = NULL;
1437+
InitTcpReady(serverArgs.signal);
1438+
1439+
ThreadStart(kb_invalid_server_thread, (void*)&serverArgs, &serThread);
1440+
WaitTcpReady(&ready);
1441+
1442+
clientCtx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
1443+
AssertNotNull(clientCtx);
1444+
wolfSSH_CTX_SetPublicKeyCheck(clientCtx, AcceptAnyServerHostKey);
1445+
wolfSSH_SetUserAuth(clientCtx, keyboardUserAuth);
1446+
1447+
clientSsh = wolfSSH_new(clientCtx);
1448+
AssertNotNull(clientSsh);
1449+
wolfSSH_SetUsername(clientSsh, "test");
1450+
1451+
build_addr(&clientAddr, (char*)wolfSshIp, ready.port);
1452+
tcp_socket(&sockFd, ((struct sockaddr_in*)&clientAddr)->sin_family);
1453+
AssertIntEQ(connect(sockFd, (const struct sockaddr*)&clientAddr,
1454+
clientAddrSz), 0);
1455+
wolfSSH_set_fd(clientSsh, (int)sockFd);
1456+
1457+
ret = wolfSSH_connect(clientSsh);
1458+
AssertIntEQ(ret, WS_FATAL_ERROR);
1459+
1460+
wolfSSH_shutdown(clientSsh);
1461+
WCLOSESOCKET(sockFd);
1462+
wolfSSH_free(clientSsh);
1463+
wolfSSH_CTX_free(clientCtx);
1464+
1465+
ThreadJoin(serThread);
1466+
AssertIntNE(serverArgs.return_code, WS_SUCCESS); /* auth must NOT be granted */
1467+
1468+
FreeTcpReady(&ready);
1469+
}
1470+
11561471
#endif /* WOLFSSH_TEST_BLOCK */
11571472

11581473
int wolfSSH_AuthTest(int argc, char** argv)
@@ -1204,6 +1519,12 @@ int wolfSSH_AuthTest(int argc, char** argv)
12041519
test_unbalanced_client_KeyboardInteractive();
12051520
#endif
12061521

1522+
/* Unknown callback return value must not grant auth (issue 2486) */
1523+
test_invalid_cb_password();
1524+
#if !defined(NO_FILESYSTEM) && defined(WOLFSSH_KEYBOARD_INTERACTIVE)
1525+
test_invalid_cb_keyboard();
1526+
#endif
1527+
12071528
AssertIntEQ(wolfSSH_Cleanup(), WS_SUCCESS);
12081529

12091530
return 0;

0 commit comments

Comments
 (0)