Skip to content

Commit 5c5e554

Browse files
Add auth tests to verify cases of invalid/unknowen return value
1 parent 90c0ee6 commit 5c5e554

1 file changed

Lines changed: 328 additions & 0 deletions

File tree

tests/auth.c

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,169 @@ static void test_pubkey_auth_wrong_key(void)
767767

768768
#endif /* pubkey test guard */
769769

770+
/* -----------------------------------------------------------------------
771+
* Password auth: unknown callback return value must not grant auth (issue 2486)
772+
* This block intentionally has no NO_SHA256 guard — password auth does not
773+
* use SHA256. The surrounding utility functions (tcp_listen, load_key, etc.)
774+
* are available because they share the base server/client/threading guard.
775+
* ----------------------------------------------------------------------- */
776+
#if !defined(NO_WOLFSSH_SERVER) && !defined(NO_WOLFSSH_CLIENT) && \
777+
!defined(SINGLE_THREADED) && !defined(WOLFSSH_TEST_BLOCK)
778+
779+
/* Tracks how many times the server password auth callback has been invoked;
780+
* must be reset to 0 before each test run. */
781+
static int invalidPwAttempts = 0;
782+
783+
/* Server userAuth callback for test_invalid_cb_password.
784+
* First call returns an out-of-enum value (-999) to exercise the default else
785+
* branch in DoUserAuthRequestPassword. Second call returns REJECTED so the
786+
* connection terminates cleanly and wolfSSH_accept() can return an error. */
787+
static int invalidPasswordServerAuth(byte authType, WS_UserAuthData* authData,
788+
void* ctx)
789+
{
790+
(void)authData;
791+
(void)ctx;
792+
if (authType != WOLFSSH_USERAUTH_PASSWORD)
793+
return WOLFSSH_USERAUTH_FAILURE;
794+
if (invalidPwAttempts++ == 0)
795+
return -999; /* unknown value: exercises default else; authFailure=1 */
796+
return WOLFSSH_USERAUTH_REJECTED; /* clean termination on retry */
797+
}
798+
799+
/* Client userAuth callback for password tests: supplies a dummy password so
800+
* the auth request reaches the server-side callback. */
801+
static int clientPasswordUserAuth(byte authType, WS_UserAuthData* authData,
802+
void* ctx)
803+
{
804+
static const byte pw[] = "dummypass";
805+
(void)ctx;
806+
if (authType != WOLFSSH_USERAUTH_PASSWORD)
807+
return WOLFSSH_USERAUTH_FAILURE;
808+
authData->sf.password.password = pw;
809+
authData->sf.password.passwordSz = (word32)(sizeof(pw) - 1);
810+
return WOLFSSH_USERAUTH_SUCCESS;
811+
}
812+
813+
/* Server thread for test_invalid_cb_password. Mirrors pubkey_server_thread
814+
* but registers invalidPasswordServerAuth and stores the return code cleanly
815+
* (no ES_ERROR abort) so the test can assert on it. */
816+
static THREAD_RETURN WOLFSSH_THREAD password_server_thread(void* args)
817+
{
818+
thread_args* serverArgs = (thread_args*)args;
819+
int ret = WS_SUCCESS;
820+
word16 port = 0;
821+
WOLFSSH_CTX* ctx = NULL;
822+
WOLFSSH* ssh = NULL;
823+
byte buf[EXAMPLE_KEYLOAD_BUFFER_SZ];
824+
word32 bufSz;
825+
WS_SOCKET_T listenFd = WOLFSSH_SOCKET_INVALID;
826+
WS_SOCKET_T clientFd = WOLFSSH_SOCKET_INVALID;
827+
SOCKADDR_IN_T clientAddr;
828+
socklen_t clientAddrSz = sizeof(clientAddr);
829+
830+
serverArgs->return_code = EXIT_SUCCESS;
831+
832+
tcp_listen(&listenFd, &port, 1);
833+
SignalTcpReady(serverArgs->signal, port);
834+
835+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
836+
if (ctx == NULL) { serverArgs->return_code = WS_MEMORY_E; goto cleanup; }
837+
838+
wolfSSH_SetUserAuth(ctx, invalidPasswordServerAuth);
839+
840+
ssh = wolfSSH_new(ctx);
841+
if (ssh == NULL) { serverArgs->return_code = WS_MEMORY_E; goto cleanup; }
842+
843+
#ifndef WOLFSSH_NO_ECDSA
844+
bufSz = (word32)load_key(1, buf, sizeof(buf));
845+
#else
846+
bufSz = (word32)load_key(0, buf, sizeof(buf));
847+
#endif
848+
if (bufSz == 0 || wolfSSH_CTX_UsePrivateKey_buffer(ctx, buf, bufSz,
849+
WOLFSSH_FORMAT_ASN1) < 0) {
850+
serverArgs->return_code = WS_BAD_FILE_E; goto cleanup;
851+
}
852+
853+
clientFd = accept(listenFd, (struct sockaddr*)&clientAddr, &clientAddrSz);
854+
if (clientFd == WOLFSSH_SOCKET_INVALID) {
855+
serverArgs->return_code = WS_SOCKET_ERROR_E; goto cleanup;
856+
}
857+
wolfSSH_set_fd(ssh, (int)clientFd);
858+
859+
ret = wolfSSH_accept(ssh);
860+
serverArgs->return_code = ret;
861+
862+
cleanup:
863+
if (ssh != NULL && clientFd != WOLFSSH_SOCKET_INVALID)
864+
wolfSSH_shutdown(ssh);
865+
if (ssh != NULL) wolfSSH_free(ssh);
866+
if (ctx != NULL) wolfSSH_CTX_free(ctx);
867+
if (clientFd != WOLFSSH_SOCKET_INVALID) WCLOSESOCKET(clientFd);
868+
if (listenFd != WOLFSSH_SOCKET_INVALID) WCLOSESOCKET(listenFd);
869+
870+
WOLFSSL_RETURN_FROM_THREAD(0);
871+
}
872+
873+
/* Test: server password-auth callback returning an unknown value must not
874+
* grant authentication. Flow:
875+
* 1. Client sends password -> server callback returns -999 -> authFailure=1
876+
* -> SendUserAuthFailure (else branch in DoUserAuthRequestPassword hit).
877+
* 2. Client retries -> server callback returns WOLFSSH_USERAUTH_REJECTED
878+
* -> WS_USER_AUTH_E -> server sends disconnect.
879+
* 3. wolfSSH_connect() returns WS_FATAL_ERROR; server return_code != WS_SUCCESS. */
880+
static void test_invalid_cb_password(void)
881+
{
882+
thread_args serverArgs;
883+
tcp_ready ready;
884+
THREAD_TYPE serThread;
885+
WOLFSSH_CTX* clientCtx = NULL;
886+
WOLFSSH* clientSsh = NULL;
887+
SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID;
888+
SOCKADDR_IN_T clientAddr;
889+
socklen_t clientAddrSz = sizeof(clientAddr);
890+
int ret;
891+
892+
printf("Testing password auth with unknown callback return value\n");
893+
invalidPwAttempts = 0;
894+
895+
serverArgs.signal = &ready;
896+
serverArgs.pubkeyServerCtx = NULL;
897+
InitTcpReady(serverArgs.signal);
898+
899+
ThreadStart(password_server_thread, (void*)&serverArgs, &serThread);
900+
WaitTcpReady(&ready);
901+
902+
clientCtx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
903+
AssertNotNull(clientCtx);
904+
wolfSSH_CTX_SetPublicKeyCheck(clientCtx, AcceptAnyServerHostKey);
905+
wolfSSH_SetUserAuth(clientCtx, clientPasswordUserAuth);
906+
907+
clientSsh = wolfSSH_new(clientCtx);
908+
AssertNotNull(clientSsh);
909+
wolfSSH_SetUsername(clientSsh, "jill");
910+
911+
build_addr(&clientAddr, (char*)wolfSshIp, ready.port);
912+
tcp_socket(&sockFd, ((struct sockaddr_in*)&clientAddr)->sin_family);
913+
AssertIntEQ(connect(sockFd, (const struct sockaddr*)&clientAddr,
914+
clientAddrSz), 0);
915+
wolfSSH_set_fd(clientSsh, (int)sockFd);
916+
917+
ret = wolfSSH_connect(clientSsh);
918+
AssertIntEQ(ret, WS_FATAL_ERROR);
919+
920+
wolfSSH_shutdown(clientSsh);
921+
WCLOSESOCKET(sockFd);
922+
wolfSSH_free(clientSsh);
923+
wolfSSH_CTX_free(clientCtx);
924+
925+
ThreadJoin(serThread);
926+
AssertIntNE(serverArgs.return_code, WS_SUCCESS); /* auth must NOT be granted */
927+
928+
FreeTcpReady(&ready);
929+
}
930+
931+
#endif /* password invalid-cb test guard */
932+
770933
#if !defined(NO_WOLFSSH_SERVER) && !defined(NO_WOLFSSH_CLIENT) && \
771934
!defined(SINGLE_THREADED) && !defined(WOLFSSH_TEST_BLOCK) && \
772935
!defined(NO_FILESYSTEM) && defined(WOLFSSH_KEYBOARD_INTERACTIVE)
@@ -1153,6 +1316,165 @@ static void test_unbalanced_client_KeyboardInteractive(void)
11531316
test_client();
11541317
unbalanced = 0;
11551318
}
1319+
1320+
/* -----------------------------------------------------------------------
1321+
* Keyboard-interactive auth: unknown callback return value must not grant
1322+
* authentication (issue 2486)
1323+
* ----------------------------------------------------------------------- */
1324+
1325+
/* Server userAuth callback for test_invalid_cb_keyboard.
1326+
* KEYBOARD_SETUP is handled normally so the exchange reaches
1327+
* DoUserAuthInfoResponse. For the KEYBOARD (response-validation) step the
1328+
* callback returns -999, an out-of-enum value, to exercise the default else
1329+
* branch in DoUserAuthInfoResponse. */
1330+
static int invalidKbServerAuth(byte authType, WS_UserAuthData* authData,
1331+
void* ctx)
1332+
{
1333+
WS_UserAuthData_Keyboard* prompts = (WS_UserAuthData_Keyboard*)ctx;
1334+
1335+
if (authType == WOLFSSH_USERAUTH_KEYBOARD_SETUP) {
1336+
WMEMCPY(&authData->sf.keyboard, prompts, sizeof(WS_UserAuthData_Keyboard));
1337+
return WS_SUCCESS;
1338+
}
1339+
if (authType == WOLFSSH_USERAUTH_KEYBOARD)
1340+
return -999; /* unknown value: exercises default else; authFailure=1 */
1341+
return WOLFSSH_USERAUTH_FAILURE;
1342+
}
1343+
1344+
/* Server thread for test_invalid_cb_keyboard. Sets up one keyboard prompt and
1345+
* registers invalidKbServerAuth. Stores the return code cleanly (no ES_ERROR
1346+
* abort) so the test can assert on it. */
1347+
static THREAD_RETURN WOLFSSH_THREAD kb_invalid_server_thread(void* args)
1348+
{
1349+
thread_args* serverArgs = (thread_args*)args;
1350+
int ret = WS_SUCCESS;
1351+
word16 port = 0;
1352+
WOLFSSH_CTX* ctx = NULL;
1353+
WOLFSSH* ssh = NULL;
1354+
byte buf[EXAMPLE_KEYLOAD_BUFFER_SZ];
1355+
word32 bufSz;
1356+
WS_SOCKET_T listenFd = WOLFSSH_SOCKET_INVALID;
1357+
WS_SOCKET_T clientFd = WOLFSSH_SOCKET_INVALID;
1358+
SOCKADDR_IN_T clientAddr;
1359+
socklen_t clientAddrSz = sizeof(clientAddr);
1360+
WS_UserAuthData_Keyboard localPrompts;
1361+
byte* kbPrompts[1];
1362+
word32 kbPromptLengths[1];
1363+
byte kbPromptEcho[1];
1364+
1365+
serverArgs->return_code = EXIT_SUCCESS;
1366+
1367+
kbPrompts[0] = (byte*)"Password: ";
1368+
kbPromptLengths[0] = 10;
1369+
kbPromptEcho[0] = 0;
1370+
WMEMSET(&localPrompts, 0, sizeof(localPrompts));
1371+
localPrompts.promptCount = 1;
1372+
localPrompts.prompts = kbPrompts;
1373+
localPrompts.promptLengths = kbPromptLengths;
1374+
localPrompts.promptEcho = kbPromptEcho;
1375+
1376+
tcp_listen(&listenFd, &port, 1);
1377+
SignalTcpReady(serverArgs->signal, port);
1378+
1379+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
1380+
if (ctx == NULL) { serverArgs->return_code = WS_MEMORY_E; goto cleanup; }
1381+
1382+
wolfSSH_SetUserAuth(ctx, invalidKbServerAuth);
1383+
1384+
ssh = wolfSSH_new(ctx);
1385+
if (ssh == NULL) { serverArgs->return_code = WS_MEMORY_E; goto cleanup; }
1386+
1387+
wolfSSH_SetUserAuthCtx(ssh, &localPrompts);
1388+
1389+
bufSz = (word32)load_key(1, buf, sizeof(buf));
1390+
if (bufSz == 0) bufSz = (word32)load_key(0, buf, sizeof(buf));
1391+
if (bufSz == 0 || wolfSSH_CTX_UsePrivateKey_buffer(ctx, buf, bufSz,
1392+
WOLFSSH_FORMAT_ASN1) < 0) {
1393+
serverArgs->return_code = WS_BAD_FILE_E; goto cleanup;
1394+
}
1395+
1396+
clientFd = accept(listenFd, (struct sockaddr*)&clientAddr, &clientAddrSz);
1397+
if (clientFd == WOLFSSH_SOCKET_INVALID) {
1398+
serverArgs->return_code = WS_SOCKET_ERROR_E; goto cleanup;
1399+
}
1400+
wolfSSH_set_fd(ssh, (int)clientFd);
1401+
1402+
ret = wolfSSH_accept(ssh);
1403+
serverArgs->return_code = ret;
1404+
1405+
cleanup:
1406+
if (ssh != NULL && clientFd != WOLFSSH_SOCKET_INVALID)
1407+
wolfSSH_shutdown(ssh);
1408+
if (ssh != NULL) wolfSSH_free(ssh);
1409+
if (ctx != NULL) wolfSSH_CTX_free(ctx);
1410+
if (clientFd != WOLFSSH_SOCKET_INVALID) WCLOSESOCKET(clientFd);
1411+
if (listenFd != WOLFSSH_SOCKET_INVALID) WCLOSESOCKET(listenFd);
1412+
1413+
WOLFSSL_RETURN_FROM_THREAD(0);
1414+
}
1415+
1416+
/* Test: server keyboard-interactive callback returning an unknown value must
1417+
* not grant authentication. Flow:
1418+
* 1. Server provides one prompt; client sends one response.
1419+
* 2. Server callback returns -999 -> authFailure=1 -> SendUserAuthFailure
1420+
* (else branch in DoUserAuthInfoResponse hit).
1421+
* 3. Client retries keyboard auth; after ssh->kbAuthAttempts reaches 3 the
1422+
* client stops trying keyboard and DoUserAuthFailure returns WS_USER_AUTH_E.
1423+
* 4. wolfSSH_connect() returns WS_FATAL_ERROR; server return_code != WS_SUCCESS. */
1424+
static void test_invalid_cb_keyboard(void)
1425+
{
1426+
thread_args serverArgs;
1427+
tcp_ready ready;
1428+
THREAD_TYPE serThread;
1429+
WOLFSSH_CTX* clientCtx = NULL;
1430+
WOLFSSH* clientSsh = NULL;
1431+
SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID;
1432+
SOCKADDR_IN_T clientAddr;
1433+
socklen_t clientAddrSz = sizeof(clientAddr);
1434+
int ret;
1435+
1436+
printf("Testing keyboard-interactive auth with unknown callback return value\n");
1437+
1438+
kbResponses[0] = (byte*)testText1;
1439+
kbResponseLengths[0] = 4;
1440+
kbResponseCount = 1;
1441+
1442+
serverArgs.signal = &ready;
1443+
serverArgs.pubkeyServerCtx = NULL;
1444+
InitTcpReady(serverArgs.signal);
1445+
1446+
ThreadStart(kb_invalid_server_thread, (void*)&serverArgs, &serThread);
1447+
WaitTcpReady(&ready);
1448+
1449+
clientCtx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
1450+
AssertNotNull(clientCtx);
1451+
wolfSSH_CTX_SetPublicKeyCheck(clientCtx, AcceptAnyServerHostKey);
1452+
wolfSSH_SetUserAuth(clientCtx, keyboardUserAuth);
1453+
1454+
clientSsh = wolfSSH_new(clientCtx);
1455+
AssertNotNull(clientSsh);
1456+
wolfSSH_SetUsername(clientSsh, "test");
1457+
1458+
build_addr(&clientAddr, (char*)wolfSshIp, ready.port);
1459+
tcp_socket(&sockFd, ((struct sockaddr_in*)&clientAddr)->sin_family);
1460+
AssertIntEQ(connect(sockFd, (const struct sockaddr*)&clientAddr,
1461+
clientAddrSz), 0);
1462+
wolfSSH_set_fd(clientSsh, (int)sockFd);
1463+
1464+
ret = wolfSSH_connect(clientSsh);
1465+
AssertIntEQ(ret, WS_FATAL_ERROR);
1466+
1467+
wolfSSH_shutdown(clientSsh);
1468+
WCLOSESOCKET(sockFd);
1469+
wolfSSH_free(clientSsh);
1470+
wolfSSH_CTX_free(clientCtx);
1471+
1472+
ThreadJoin(serThread);
1473+
AssertIntNE(serverArgs.return_code, WS_SUCCESS); /* auth must NOT be granted */
1474+
1475+
FreeTcpReady(&ready);
1476+
}
1477+
11561478
#endif /* WOLFSSH_TEST_BLOCK */
11571479

11581480
int wolfSSH_AuthTest(int argc, char** argv)
@@ -1204,6 +1526,12 @@ int wolfSSH_AuthTest(int argc, char** argv)
12041526
test_unbalanced_client_KeyboardInteractive();
12051527
#endif
12061528

1529+
/* Unknown callback return value must not grant auth (issue 2486) */
1530+
test_invalid_cb_password();
1531+
#if !defined(NO_FILESYSTEM) && defined(WOLFSSH_KEYBOARD_INTERACTIVE)
1532+
test_invalid_cb_keyboard();
1533+
#endif
1534+
12071535
AssertIntEQ(wolfSSH_Cleanup(), WS_SUCCESS);
12081536

12091537
return 0;

0 commit comments

Comments
 (0)