wolfsshd: enforce shadow password and account aging - #1184
Open
yosuke-wolfssl wants to merge 1 commit into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates wolfsshd’s Unix shadow-password authentication to enforce shadow account/password aging rules (account expiry, forced password change, and maximum password age) so expired/aged accounts can’t continue authenticating via password even if the hash matches.
Changes:
- Add
IsShadowExpired()and wire it intoCheckPasswordUnix()to deny password authentication after a successful hash compare when shadow aging indicates expiration. - Refactor/expand unit tests to cover both the new aging rules and the
CheckPasswordUnix()expired-account behavior. - Consolidate several shadow failure-mode tests into a table-driven helper for maintainability.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
apps/wolfsshd/auth.c |
Adds shadow-aging evaluation (IsShadowExpired) and enforces expiry in the password auth flow post-hash-compare. |
apps/wolfsshd/auth.h |
Exposes IsShadowExpired for unit tests under shadow-enabled builds. |
apps/wolfsshd/test/test_configuration.c |
Refactors CheckPasswordUnix shadow tests, and adds direct coverage for shadow aging rules + an expired-account password test. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- 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-10577
yosuke-wolfssl
force-pushed
the
fix/f_10577
branch
from
August 20, 2026 06:52
55408f9 to
3e7b4f4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
CheckPasswordUnix()read onlyshadowInfo->sp_pwdpfrom the shadow entry itlooked up. The aging fields —
sp_expire,sp_lstchg,sp_max— were neverexamined, so an account past its expiration date, or a password past its
maximum age, authenticated normally as long as the stored hash matched. Any
account an administrator had locked with
chage -Eor aged out still had aworking password over SSH. Closes f-10577.
Fix (
apps/wolfsshd/auth.c)New
IsShadowExpired()denies a login when any of these holds:sp_expire >= 0 && today >= sp_expiresp_lstchg == 0sp_lstchg > 0 && sp_max >= 0 && today - sp_lstchg >= sp_maxNegative fields leave the matching check off, which is how an unset shadow
field arrives from
getspnam(). A clock failure denies only entries thatactually carry aging.
SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, andDoUserAuthRequestPassword()already rejects password-change requests per RFC 4252 §8.
crypt()still executes andan expired account costs the same as a live one.
HAVE_SHADOWand only without PAM, matching its caller.Two deliberate choices worth a reviewer's eye: expiry takes effect on the
stored date, matching
shadow-utils/pam_unix(OpenSSH uses the day after);and both checks live in the password path, so public-key login to an expired
account is still permitted.
Tests (
apps/wolfsshd/test/test_configuration.c)test_IsShadowExpiredwalks a table of aging fields and day counts, includingboth sides of each boundary;
test_CheckPasswordUnix_expiredputs a correctpassword through an expired account. The
CheckPasswordUnixtests now shareone driver, and the three fail-closed shadow-lookup tests collapse into one
table-driven
test_CheckPasswordUnix_failClosed.Verification
--enable-all --enable-ossh-certs,-O2 -Werror: clean. GCC preflightsweep, 6 configs: clean.
make check10 pass / 2 skip / 0 fail.or shifting either boundary a day in either direction each fails a test.
chage -E/chage -Mshadow entries.arithmetic.
Not in this PR:
--with-pambuilds fail on master already (CheckPasswordPAM()does not match
CallbackCheckPassword), so the PAM path is untouched here.