mirror of https://github.com/wolfSSL/wolfssh.git
wolfsshd: enforce shadow password and account aging
- IsShadowExpired() in auth.c returns 1 when a shadow entry's sp_expire date has arrived, its sp_lstchg is 0, or the day is at or past sp_lstchg + sp_max. Negative fields leave the matching check off; a negative day count, standing for an unavailable clock, denies the entries that carry aging. WSSHD_SECS_PER_DAY converts WTIME() into the unit those fields use. The helper is compiled under HAVE_SHADOW and !WOLFSSH_USE_PAM, as its caller is. - CheckPasswordUnix() runs the shadow entry it looked up through the helper and, after an otherwise successful hash compare, logs the denial and returns WSSHD_AUTH_FAILURE. - auth.h declares IsShadowExpired() for the unit test build. - test_configuration.c adds test_IsShadowExpired() over a table of aging fields and day counts, and test_CheckPasswordUnix_expired() for the denial of a correct password. - The CheckPasswordUnix() tests share one driver, wsshd_test_CheckPasswordUnixCase(), with the crypt() setup in wsshd_test_LoadShadowHash() and the three fail-closed shadow lookups gathered into test_CheckPasswordUnix_failClosed(). Issue: F-10577pull/1171/head
parent
3aefadc102
commit
ad28e21221
|
|
@ -508,6 +508,43 @@ static void ScanShadowFile(WFILE* f)
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef WOLFSSH_USE_PAM
|
||||
/* Shadow aging fields count days since the epoch. */
|
||||
#define WSSHD_SECS_PER_DAY (24L * 60L * 60L)
|
||||
|
||||
/* Return 1 when the shadow aging fields make the account unusable for login.
|
||||
* A negative today means the system clock is unavailable, which denies only
|
||||
* the accounts that actually have aging configured. */
|
||||
#ifdef WOLFSSHD_UNIT_TEST
|
||||
int IsShadowExpired(const struct spwd* sp, long today)
|
||||
#else
|
||||
static int IsShadowExpired(const struct spwd* sp, long today)
|
||||
#endif
|
||||
{
|
||||
int expired = 0;
|
||||
|
||||
if (sp != NULL) {
|
||||
/* Account expiration date, effective on the date itself. */
|
||||
if (sp->sp_expire >= 0 &&
|
||||
(today < 0 || today >= (long)sp->sp_expire)) {
|
||||
expired = 1;
|
||||
}
|
||||
/* Password change forced at next login. */
|
||||
if (expired == 0 && sp->sp_lstchg == 0) {
|
||||
expired = 1;
|
||||
}
|
||||
/* Password aged out. Subtraction avoids overflowing the sum. */
|
||||
if (expired == 0 && sp->sp_lstchg > 0 && sp->sp_max >= 0 &&
|
||||
(today < 0 || today - (long)sp->sp_lstchg >=
|
||||
(long)sp->sp_max)) {
|
||||
expired = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return expired;
|
||||
}
|
||||
#endif /* !WOLFSSH_USE_PAM */
|
||||
|
||||
#ifdef WOLFSSHD_UNIT_TEST
|
||||
/* Test-only hook to seed cachedFakeHash without a real shadow file entry. */
|
||||
void wolfSSHD_SetCachedFakeHashForTest(const char* tmpl)
|
||||
|
|
@ -767,6 +804,8 @@ static int CheckPasswordUnix(const char* usr, const byte* pw, word32 pwSz, WOLFS
|
|||
struct passwd* pwInfo;
|
||||
#ifdef HAVE_SHADOW
|
||||
struct spwd* shadowInfo;
|
||||
time_t now;
|
||||
int expired = 0;
|
||||
/* getspnam() returns a static buffer; copy immediately before it can
|
||||
* be overwritten by any subsequent call. */
|
||||
char hashBuf[WSSHD_FAKE_HASH_SZ];
|
||||
|
|
@ -838,6 +877,10 @@ static int CheckPasswordUnix(const char* usr, const byte* pw, word32 pwSz, WOLFS
|
|||
XSTRNCPY(hashBuf, shadowInfo->sp_pwdp, sizeof(hashBuf));
|
||||
hashBuf[sizeof(hashBuf) - 1] = '\0';
|
||||
storedHash = hashBuf;
|
||||
now = WTIME(NULL);
|
||||
expired = IsShadowExpired(shadowInfo,
|
||||
(now == (time_t)-1) ? -1 :
|
||||
(long)(now / WSSHD_SECS_PER_DAY));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -867,6 +910,16 @@ static int CheckPasswordUnix(const char* usr, const byte* pw, word32 pwSz, WOLFS
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_SHADOW
|
||||
/* Deny after the hash compare so an expired account costs the same as a
|
||||
* live one. */
|
||||
if (expired && ret == WSSHD_AUTH_SUCCESS) {
|
||||
wolfSSH_Log(WS_LOG_INFO,
|
||||
"[SSHD] Password or account expired for user %s", usr);
|
||||
ret = WSSHD_AUTH_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pwStr != NULL) {
|
||||
WS_FORCEZERO(pwStr, pwSz + 1);
|
||||
WFREE(pwStr, NULL, DYNTYPE_STRING);
|
||||
|
|
|
|||
|
|
@ -150,6 +150,10 @@ int wolfSSHD_GetCachedFakeHashCountForTest(void);
|
|||
void AddShadowLineToFakeHashCache(char* line);
|
||||
/* Reads a shadow file stream line by line into the fake-hash cache. */
|
||||
void ScanShadowFile(WFILE* f);
|
||||
#ifndef WOLFSSH_USE_PAM
|
||||
/* Returns 1 when the shadow aging fields deny the account a login. */
|
||||
int IsShadowExpired(const struct spwd* sp, long today);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
/* Not shadow-specific in auth.c, so not excluded on OSX/APPLE. */
|
||||
|
|
|
|||
|
|
@ -2075,25 +2075,28 @@ static int test_CheckPasswordUnix_unknownUser(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* getspnam() failing (e.g. SSHD not run as root) must fail closed rather
|
||||
* than silently falling through to compare against the "*" default hash. */
|
||||
static int test_CheckPasswordUnix_shadowLookupFails(void)
|
||||
/* Runs CheckPasswordUnix() against a synthetic shadow entry and checks the
|
||||
* result code it returns. */
|
||||
static int wsshd_test_CheckPasswordUnixCase(struct spwd* (*stub)(const char*),
|
||||
const byte* pw, word32 pwSz, int expected, const char* scenario)
|
||||
{
|
||||
int ret = WS_SUCCESS;
|
||||
int rc;
|
||||
struct passwd* (*savedGetpwnam)(const char*);
|
||||
struct spwd* (*savedGetspnam)(const char*);
|
||||
static const byte pw[] = "guessme";
|
||||
|
||||
savedGetpwnam = wsshd_getpwnam_cb;
|
||||
savedGetspnam = wsshd_getspnam_cb;
|
||||
wsshd_getpwnam_cb = stub_getpwnam_shadowUser;
|
||||
wsshd_getspnam_cb = stub_getspnam_null;
|
||||
wsshd_getspnam_cb = stub;
|
||||
|
||||
rc = CheckPasswordUnix("shadow_branch_test_user", pw,
|
||||
(word32)(sizeof(pw) - 1), NULL);
|
||||
if (rc != WS_FATAL_ERROR) {
|
||||
Log(" FAILED: expected WS_FATAL_ERROR when getspnam() fails.\n");
|
||||
Log(" Testing scenario: %s.", scenario);
|
||||
rc = CheckPasswordUnix("shadow_branch_test_user", pw, pwSz, NULL);
|
||||
if (rc == expected) {
|
||||
Log(" PASSED.\n");
|
||||
}
|
||||
else {
|
||||
Log(" FAILED, expected %d got %d.\n", expected, rc);
|
||||
ret = WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -2102,59 +2105,74 @@ static int test_CheckPasswordUnix_shadowLookupFails(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* A shadow hash too long for CheckPasswordUnix's fixed hashBuf must fail
|
||||
* closed instead of being silently truncated. */
|
||||
static int test_CheckPasswordUnix_shadowHashTooLong(void)
|
||||
#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
|
||||
/* Fills stub_shadow_test_hash with a real crypt() hash of pw. Returns 0 when
|
||||
* the platform's crypt() ignores the modular $6$ salt, as macOS and the BSDs
|
||||
* do; see test_CheckPasswordHashUnix. */
|
||||
static int wsshd_test_LoadShadowHash(const byte* pw)
|
||||
{
|
||||
const char* salt = "$6$wolfsshtestsalt$";
|
||||
char* hash;
|
||||
|
||||
hash = crypt((const char*)pw, salt);
|
||||
if (hash == NULL || hash[0] == '*' || WSTRLEN(hash) == 0 ||
|
||||
WSTRNCMP(hash, "$6$", 3) != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (WSTRLEN(hash) >= sizeof(stub_shadow_test_hash)) {
|
||||
return 0;
|
||||
}
|
||||
WMEMCPY(stub_shadow_test_hash, hash, WSTRLEN(hash) + 1);
|
||||
return 1;
|
||||
}
|
||||
#endif /* WOLFSSH_HAVE_LIBCRYPT || WOLFSSH_HAVE_LIBLOGIN */
|
||||
|
||||
/* Shadow lookups that cannot yield a usable hash. Each must fail closed
|
||||
* rather than fall through to the "*" default or a truncated hash. */
|
||||
static const struct {
|
||||
const char* scenario;
|
||||
struct spwd* (*stub)(const char*);
|
||||
} shadowFailClosedCases[] = {
|
||||
{ "getspnam() fails, e.g. SSHD not run as root", stub_getspnam_null },
|
||||
{ "shadow hash too long for the copy buffer",
|
||||
stub_getspnam_oversizedHash },
|
||||
{ "shadow entry with no password field", stub_getspnam_nullPassword }
|
||||
};
|
||||
|
||||
static int test_CheckPasswordUnix_failClosed(void)
|
||||
{
|
||||
int ret = WS_SUCCESS;
|
||||
int rc;
|
||||
struct passwd* (*savedGetpwnam)(const char*);
|
||||
struct spwd* (*savedGetspnam)(const char*);
|
||||
word32 i;
|
||||
static const byte pw[] = "guessme";
|
||||
|
||||
savedGetpwnam = wsshd_getpwnam_cb;
|
||||
savedGetspnam = wsshd_getspnam_cb;
|
||||
wsshd_getpwnam_cb = stub_getpwnam_shadowUser;
|
||||
wsshd_getspnam_cb = stub_getspnam_oversizedHash;
|
||||
|
||||
rc = CheckPasswordUnix("shadow_branch_test_user", pw,
|
||||
(word32)(sizeof(pw) - 1), NULL);
|
||||
if (rc != WS_FATAL_ERROR) {
|
||||
Log(" FAILED: expected WS_FATAL_ERROR for oversized shadow hash.\n");
|
||||
ret = WS_FATAL_ERROR;
|
||||
for (i = 0;
|
||||
i < sizeof(shadowFailClosedCases)/sizeof(shadowFailClosedCases[0]);
|
||||
i++) {
|
||||
if (wsshd_test_CheckPasswordUnixCase(shadowFailClosedCases[i].stub,
|
||||
pw, (word32)(sizeof(pw) - 1), WS_FATAL_ERROR,
|
||||
shadowFailClosedCases[i].scenario) != WS_SUCCESS) {
|
||||
ret = WS_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
wsshd_getpwnam_cb = savedGetpwnam;
|
||||
wsshd_getspnam_cb = savedGetspnam;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* A shadow entry with a NULL sp_pwdp (e.g. an NIS/LDAP-backed account) must
|
||||
* fail closed instead of crashing on a NULL dereference in WSTRLEN(). */
|
||||
static int test_CheckPasswordUnix_shadowNullPassword(void)
|
||||
/* Days since the epoch, the unit the shadow aging fields use. */
|
||||
static long wsshd_test_TodayDays(void)
|
||||
{
|
||||
int ret = WS_SUCCESS;
|
||||
int rc;
|
||||
struct passwd* (*savedGetpwnam)(const char*);
|
||||
struct spwd* (*savedGetspnam)(const char*);
|
||||
static const byte pw[] = "guessme";
|
||||
return (long)((word64)WTIME(NULL) / (24L * 60L * 60L));
|
||||
}
|
||||
|
||||
savedGetpwnam = wsshd_getpwnam_cb;
|
||||
savedGetspnam = wsshd_getspnam_cb;
|
||||
wsshd_getpwnam_cb = stub_getpwnam_shadowUser;
|
||||
wsshd_getspnam_cb = stub_getspnam_nullPassword;
|
||||
|
||||
rc = CheckPasswordUnix("shadow_branch_test_user", pw,
|
||||
(word32)(sizeof(pw) - 1), NULL);
|
||||
if (rc != WS_FATAL_ERROR) {
|
||||
Log(" FAILED: expected WS_FATAL_ERROR for a shadow entry with a "
|
||||
"NULL password field, got %d.\n", rc);
|
||||
ret = WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
wsshd_getpwnam_cb = savedGetpwnam;
|
||||
wsshd_getspnam_cb = savedGetspnam;
|
||||
return ret;
|
||||
/* Aging fields of an account that never expires. */
|
||||
static void wsshd_test_SetLiveAging(struct spwd* sp)
|
||||
{
|
||||
sp->sp_lstchg = wsshd_test_TodayDays();
|
||||
sp->sp_min = -1;
|
||||
sp->sp_max = -1;
|
||||
sp->sp_warn = -1;
|
||||
sp->sp_inact = -1;
|
||||
sp->sp_expire = -1;
|
||||
}
|
||||
|
||||
static struct spwd* stub_getspnam_validHash(const char* name)
|
||||
|
|
@ -2163,58 +2181,139 @@ static struct spwd* stub_getspnam_validHash(const char* name)
|
|||
WMEMSET(&stub_shadow_test_sp, 0, sizeof(stub_shadow_test_sp));
|
||||
stub_shadow_test_sp.sp_namp = (char*)"shadow_branch_test_user";
|
||||
stub_shadow_test_sp.sp_pwdp = stub_shadow_test_hash;
|
||||
/* A zeroed spwd would read as "password change forced". */
|
||||
wsshd_test_SetLiveAging(&stub_shadow_test_sp);
|
||||
return &stub_shadow_test_sp;
|
||||
}
|
||||
|
||||
/* Copy-then-succeed path: a normal-length shadow hash copied into
|
||||
/* Copy-then-compare path: a normal-length shadow hash copied into
|
||||
* CheckPasswordUnix's hashBuf, then compared for real. */
|
||||
static int test_CheckPasswordUnix_shadowLookupSucceeds(void)
|
||||
{
|
||||
int ret = WS_SUCCESS;
|
||||
#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
|
||||
int rc;
|
||||
struct passwd* (*savedGetpwnam)(const char*);
|
||||
struct spwd* (*savedGetspnam)(const char*);
|
||||
static const byte correctPw[] = "guessme";
|
||||
static const byte wrongPw[] = "wrongpw";
|
||||
/* SHA-512 crypt salt; portable across glibc-based crypt() impls. */
|
||||
const char* salt = "$6$wolfsshtestsalt$";
|
||||
char* hash;
|
||||
|
||||
hash = crypt((const char*)correctPw, salt);
|
||||
/* See test_CheckPasswordHashUnix: some libc (macOS/BSD) ignore the
|
||||
* modular salt and fall back to legacy DES, so skip there. */
|
||||
if (hash == NULL || hash[0] == '*' || WSTRLEN(hash) == 0 ||
|
||||
WSTRNCMP(hash, "$6$", 3) != 0) {
|
||||
if (wsshd_test_LoadShadowHash(correctPw) == 0) {
|
||||
Log(" crypt() did not honor $6$ SHA-512, skipping.\n");
|
||||
return WS_SUCCESS;
|
||||
}
|
||||
if (WSTRLEN(hash) >= sizeof(stub_shadow_test_hash)) {
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
WMEMCPY(stub_shadow_test_hash, hash, WSTRLEN(hash) + 1);
|
||||
|
||||
savedGetpwnam = wsshd_getpwnam_cb;
|
||||
savedGetspnam = wsshd_getspnam_cb;
|
||||
wsshd_getpwnam_cb = stub_getpwnam_shadowUser;
|
||||
wsshd_getspnam_cb = stub_getspnam_validHash;
|
||||
|
||||
Log(" Testing scenario: correct password against copied shadow hash.");
|
||||
rc = CheckPasswordUnix("shadow_branch_test_user", correctPw,
|
||||
(word32)(sizeof(correctPw) - 1), NULL);
|
||||
if (rc == WSSHD_AUTH_SUCCESS) {
|
||||
Log(" PASSED.\n");
|
||||
ret = wsshd_test_CheckPasswordUnixCase(stub_getspnam_validHash, correctPw,
|
||||
(word32)(sizeof(correctPw) - 1), WSSHD_AUTH_SUCCESS,
|
||||
"correct password against copied shadow hash");
|
||||
if (ret == WS_SUCCESS) {
|
||||
ret = wsshd_test_CheckPasswordUnixCase(stub_getspnam_validHash,
|
||||
wrongPw, (word32)(sizeof(wrongPw) - 1), WSSHD_AUTH_FAILURE,
|
||||
"wrong password against copied shadow hash");
|
||||
}
|
||||
else {
|
||||
Log(" FAILED.\n");
|
||||
ret = WS_FATAL_ERROR;
|
||||
#else
|
||||
(void)stub_getspnam_validHash;
|
||||
Log(" Skipping test: password hash checking not compiled in.\n");
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Account whose expiration date passed yesterday; nothing else about the
|
||||
* entry can deny the login. */
|
||||
static struct spwd* stub_getspnam_expiredAccount(const char* name)
|
||||
{
|
||||
(void)name;
|
||||
WMEMSET(&stub_shadow_test_sp, 0, sizeof(stub_shadow_test_sp));
|
||||
stub_shadow_test_sp.sp_namp = (char*)"shadow_branch_test_user";
|
||||
stub_shadow_test_sp.sp_pwdp = stub_shadow_test_hash;
|
||||
wsshd_test_SetLiveAging(&stub_shadow_test_sp);
|
||||
stub_shadow_test_sp.sp_expire = wsshd_test_TodayDays() - 1;
|
||||
return &stub_shadow_test_sp;
|
||||
}
|
||||
|
||||
/* A correct password must still be denied once the aging fields expire the
|
||||
* account. test_IsShadowExpired covers the individual aging rules. */
|
||||
static int test_CheckPasswordUnix_expired(void)
|
||||
{
|
||||
int ret = WS_SUCCESS;
|
||||
#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
|
||||
static const byte correctPw[] = "guessme";
|
||||
|
||||
if (wsshd_test_LoadShadowHash(correctPw) == 0) {
|
||||
Log(" crypt() did not honor $6$ SHA-512, skipping.\n");
|
||||
return WS_SUCCESS;
|
||||
}
|
||||
|
||||
ret = wsshd_test_CheckPasswordUnixCase(stub_getspnam_expiredAccount,
|
||||
correctPw, (word32)(sizeof(correctPw) - 1), WSSHD_AUTH_FAILURE,
|
||||
"correct password on an expired account");
|
||||
#else
|
||||
(void)stub_getspnam_expiredAccount;
|
||||
Log(" Skipping test: password hash checking not compiled in.\n");
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Aging rules, evaluated against a fixed day so the cases are stable.
|
||||
* A negative "today" stands in for an unavailable system clock. */
|
||||
static const struct {
|
||||
const char* scenario;
|
||||
long lstchg;
|
||||
long max;
|
||||
long expire;
|
||||
long today;
|
||||
int expected;
|
||||
} shadowExpiryCases[] = {
|
||||
{ "no aging configured", 19000, -1, -1, 20000, 0 },
|
||||
{ "account expired yesterday", 19000, -1, 19999, 20000, 1 },
|
||||
{ "account expires today", 19000, -1, 20000, 20000, 1 },
|
||||
{ "account expires tomorrow", 19000, -1, 20001, 20000, 0 },
|
||||
{ "account expires next year", 19000, -1, 20365, 20000, 0 },
|
||||
{ "account expiry disabled", 19000, -1, -1, 20000, 0 },
|
||||
{ "change forced at next login", 0, -1, -1, 20000, 1 },
|
||||
{ "password aged out", 19900, 30, -1, 20000, 1 },
|
||||
{ "password ages out today", 19970, 30, -1, 20000, 1 },
|
||||
{ "password ages out tomorrow", 19971, 30, -1, 20000, 0 },
|
||||
{ "password aging disabled", 1, -1, -1, 20000, 0 },
|
||||
{ "last change unset", -1, 30, -1, 20000, 0 },
|
||||
{ "no clock, no aging configured", 19000, -1, -1, -1, 0 },
|
||||
{ "no clock, account expiry set", 19000, -1, 20365, -1, 1 },
|
||||
{ "no clock, password aging set", 19000, 30, -1, -1, 1 }
|
||||
};
|
||||
|
||||
/* Direct coverage of the shadow aging rules, independent of crypt(). */
|
||||
static int test_IsShadowExpired(void)
|
||||
{
|
||||
int ret = WS_SUCCESS;
|
||||
word32 i;
|
||||
int rc;
|
||||
struct spwd sp;
|
||||
|
||||
for (i = 0; i < sizeof(shadowExpiryCases)/sizeof(shadowExpiryCases[0]);
|
||||
i++) {
|
||||
WMEMSET(&sp, 0, sizeof(sp));
|
||||
sp.sp_namp = (char*)"shadow_branch_test_user";
|
||||
sp.sp_pwdp = (char*)"$6$wolfsshtestsalt$hash";
|
||||
sp.sp_lstchg = shadowExpiryCases[i].lstchg;
|
||||
sp.sp_min = -1;
|
||||
sp.sp_max = shadowExpiryCases[i].max;
|
||||
sp.sp_warn = -1;
|
||||
sp.sp_inact = -1;
|
||||
sp.sp_expire = shadowExpiryCases[i].expire;
|
||||
|
||||
Log(" Testing IsShadowExpired: %s.",
|
||||
shadowExpiryCases[i].scenario);
|
||||
rc = IsShadowExpired(&sp, shadowExpiryCases[i].today);
|
||||
if (rc == shadowExpiryCases[i].expected) {
|
||||
Log(" PASSED.\n");
|
||||
}
|
||||
else {
|
||||
Log(" FAILED, expected %d got %d.\n",
|
||||
shadowExpiryCases[i].expected, rc);
|
||||
ret = WS_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
Log(" Testing scenario: wrong password against copied shadow hash.");
|
||||
rc = CheckPasswordUnix("shadow_branch_test_user", wrongPw,
|
||||
(word32)(sizeof(wrongPw) - 1), NULL);
|
||||
if (rc == WSSHD_AUTH_FAILURE) {
|
||||
Log(" Testing IsShadowExpired: NULL entry.");
|
||||
if (IsShadowExpired(NULL, 20000) == 0) {
|
||||
Log(" PASSED.\n");
|
||||
}
|
||||
else {
|
||||
|
|
@ -2223,11 +2322,6 @@ static int test_CheckPasswordUnix_shadowLookupSucceeds(void)
|
|||
}
|
||||
}
|
||||
|
||||
wsshd_getpwnam_cb = savedGetpwnam;
|
||||
wsshd_getspnam_cb = savedGetspnam;
|
||||
#else
|
||||
Log(" Skipping test: password hash checking not compiled in.\n");
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -6219,11 +6313,11 @@ const TEST_CASE testCases[] = {
|
|||
#ifdef WOLFSSHD_HAVE_SHADOW
|
||||
TEST_DECL(test_GetFakeHashFromTemplate),
|
||||
TEST_DECL(test_CachedFakeHashConsumption),
|
||||
TEST_DECL(test_CheckPasswordUnix_shadowLookupFails),
|
||||
TEST_DECL(test_CheckPasswordUnix_shadowHashTooLong),
|
||||
TEST_DECL(test_CheckPasswordUnix_shadowNullPassword),
|
||||
TEST_DECL(test_CheckPasswordUnix_failClosed),
|
||||
TEST_DECL(test_CheckPasswordUnix_shadowLookupSucceeds),
|
||||
TEST_DECL(test_CheckPasswordUnix_unknownUser),
|
||||
TEST_DECL(test_IsShadowExpired),
|
||||
TEST_DECL(test_CheckPasswordUnix_expired),
|
||||
TEST_DECL(test_DoFakePasswordCheck_pubkeyUnionSafety),
|
||||
TEST_DECL(test_AuthInit),
|
||||
TEST_DECL(test_AuthInit_degradedMode),
|
||||
|
|
|
|||
Loading…
Reference in New Issue