From afcda8b2c7127cb4e93b4c6f0825cdc64c2e548d Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Mon, 13 Apr 2026 09:42:20 -0700 Subject: [PATCH] Add negative test case for CheckPasswordHashUnix --- apps/wolfsshd/auth.c | 9 ++- apps/wolfsshd/auth.h | 11 ++++ apps/wolfsshd/test/test_configuration.c | 73 ++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/apps/wolfsshd/auth.c b/apps/wolfsshd/auth.c index 8a7eaebee..469eab571 100644 --- a/apps/wolfsshd/auth.c +++ b/apps/wolfsshd/auth.c @@ -151,11 +151,6 @@ USER_NODE* AddNewUser(USER_NODE* list, byte type, const byte* username, } #endif -enum { - WSSHD_AUTH_FAILURE = 0, - WSSHD_AUTH_SUCCESS = 1 -}; - /* TODO: Can use wolfSSH_ReadKey_buffer? */ static int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key, word32 keySz) @@ -309,7 +304,11 @@ static int ExtractSalt(char* hash, char** salt, int saltSz) #endif #if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN) +#ifdef WOLFSSHD_UNIT_TEST +int CheckPasswordHashUnix(const char* input, char* stored) +#else static int CheckPasswordHashUnix(const char* input, char* stored) +#endif { int ret = WSSHD_AUTH_SUCCESS; char* hashedInput; diff --git a/apps/wolfsshd/auth.h b/apps/wolfsshd/auth.h index ba5b6eb44..0ba5d42be 100644 --- a/apps/wolfsshd/auth.h +++ b/apps/wolfsshd/auth.h @@ -34,6 +34,11 @@ int DefaultUserAuthTypes(WOLFSSH* ssh, void* ctx); typedef struct WOLFSSHD_AUTH WOLFSSHD_AUTH; +enum { + WSSHD_AUTH_FAILURE = 0, + WSSHD_AUTH_SUCCESS = 1 +}; + /* * Returns WSSHD_AUTH_SUCCESS if user found, WSSHD_AUTH_FAILURE if user not * found, and negative values if an error occurs during checking. @@ -73,4 +78,10 @@ WOLFSSHD_CONFIG* wolfSSHD_AuthGetUserConf(const WOLFSSHD_AUTH* auth, HANDLE wolfSSHD_GetAuthToken(const WOLFSSHD_AUTH* auth); int wolfSSHD_GetHomeDirectory(WOLFSSHD_AUTH* auth, WOLFSSH* ssh, WCHAR* out, int outSz); #endif + +#ifdef WOLFSSHD_UNIT_TEST +#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN) +int CheckPasswordHashUnix(const char* input, char* stored); +#endif +#endif #endif /* WOLFAUTH_H */ diff --git a/apps/wolfsshd/test/test_configuration.c b/apps/wolfsshd/test/test_configuration.c index cb55b0889..fa0e86dc7 100644 --- a/apps/wolfsshd/test/test_configuration.c +++ b/apps/wolfsshd/test/test_configuration.c @@ -1,7 +1,24 @@ +/* Match auth.c's feature-test macros so crypt() is declared and so the + * pre-existing CleanupWildcardTest code keeps seeing DT_DIR. Must come + * before any system header is pulled in. */ +#ifdef __linux__ + #ifndef _XOPEN_SOURCE + #define _XOPEN_SOURCE + #endif + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif +#endif + #include +#include +#ifdef HAVE_CRYPT_H + #include +#endif #include #include +#include #ifndef WOLFSSH_DEFAULT_LOG_WIDTH #define WOLFSSH_DEFAULT_LOG_WIDTH 120 @@ -233,8 +250,62 @@ static int test_ParseConfigLine(void) return ret; } +#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN) +/* Negative-path coverage for CheckPasswordHashUnix so mutation of the + * ConstantCompare clause (the only substantive check once crypt() has + * produced its fixed-length output) does not survive the test suite. */ +static int test_CheckPasswordHashUnix(void) +{ + int ret = WS_SUCCESS; + const char* correct = "wolfssh-test-pass"; + const char* wrong = "wolfssh-test-wrong"; + /* SHA-512 crypt salt; portable across glibc-based crypt() impls. */ + const char* salt = "$6$wolfsshtestsalt$"; + char stored[128]; + char* hash; + int rc; + + hash = crypt(correct, salt); + if (hash == NULL || hash[0] == '*' || WSTRLEN(hash) == 0) { + Log(" crypt() unavailable or refused salt, skipping.\n"); + return WS_SUCCESS; + } + if (WSTRLEN(hash) >= sizeof(stored)) { + return WS_FATAL_ERROR; + } + WMEMCPY(stored, hash, WSTRLEN(hash) + 1); + + Log(" Testing scenario: correct password authenticates."); + rc = CheckPasswordHashUnix(correct, stored); + if (rc == WSSHD_AUTH_SUCCESS) { + Log(" PASSED.\n"); + } + else { + Log(" FAILED.\n"); + ret = WS_FATAL_ERROR; + } + + if (ret == WS_SUCCESS) { + Log(" Testing scenario: wrong password is rejected."); + rc = CheckPasswordHashUnix(wrong, stored); + if (rc == WSSHD_AUTH_FAILURE) { + Log(" PASSED.\n"); + } + else { + Log(" FAILED.\n"); + ret = WS_FATAL_ERROR; + } + } + + return ret; +} +#endif /* WOLFSSH_HAVE_LIBCRYPT || WOLFSSH_HAVE_LIBLOGIN */ + const TEST_CASE testCases[] = { - TEST_DECL(test_ParseConfigLine) + TEST_DECL(test_ParseConfigLine), +#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN) + TEST_DECL(test_CheckPasswordHashUnix), +#endif }; int main(int argc, char** argv)