unix_chkpwd "password check failed for user (root)": What It Means
Published
PAM's password helper checked the password offered for the named account and it did not pass — a wrong password, or a name it could not resolve — and the line itself carries no address, so the sshd lines beside it are what tell you where it came from.
What it looks like
Oct 18 03:51:11 web1 unix_chkpwd[48213]: password check failed for user (root)
- 1Time — When it was logged; this format has no year or time zone
- 2Host — The server’s host name
- 3Program — unix_chkpwd, the helper pam_unix runs to check a password, and its process ID
- 4User — The account name
Example lines as /var/log/auth.log shows them (/var/log/secure on RHEL and Fedora):
Oct 18 03:51:11 web1 unix_chkpwd[48213]: password check failed for user (root) Oct 18 03:51:14 web1 unix_chkpwd[48219]: password check failed for user (ubuntu) Oct 18 04:06:58 web1 unix_chkpwd[48507]: password check failed for user (deploy)
On our own server, the 30 days to 25 October 2026
password check failed for user (…)
24,907
lines · about 830 a day
Last seen 6 October 2026
None since the server went key-only on 6 October 2026 — turning off password authentication removed this line entirely.
LogTriage's production API server (Hetzner, Germany): port 22 open to the internet, key-only since 6 October 2026 (password and keyboard-interactive authentication off); nginx in front of the API on ports 80 and 443. Counted weekly from the sshd journal; no address ever leaves the server. More in the monthly attack report; which free blocklists already list these attackers is measured in the IP blocklist report, and you can check the addresses in your own log.
Where the line comes from
Format strings as written in the source of Linux-PAM 1.7.0 — the versions this server runs. Each links to the exact line.
-
password check failed for user (%s)message · modules/pam_unix/unix_chkpwd.c:167 (Linux-PAM)
What it means
This line is not written by sshd. It comes from unix_chkpwd, the helper binary that Linux-PAM’s pam_unix module uses to verify a password, which is why it appears in /var/log/auth.log with its own program tag and PID instead of sshd-session’s.
The sequence in the helper’s source is short. It calls helper_verify_password(), wipes the submitted password from memory, and if the result is anything other than PAM_SUCCESS it logs password check failed for user (%s) at LOG_NOTICE. The %s is the account name that was submitted, and it is the only variable in the message — no IP address, no source port, no PAM service name, no reason code.
Two details decide how much the line is worth:
- It does not prove the account exists. The message is written before the helper decides what to return. Only afterwards does the code separate the unknown-user case (
PAM_USER_UNKNOWN, which it propagates asPAM_AUTHINFO_UNAVAIL) from a general failure. A name invented by a scanner produces the same text as a staff account with a mistyped password. The lines that do distinguish them are sshd’sInvalid userand pam_unix’scheck pass; user unknown. - A blank password under
nullokis not logged. The code skips the message when the module permits empty passwords and the one supplied was blank — its comment reads “no need to log blank pass test”.
Where libaudit is compiled in, the helper also emits an AUDIT_USER_AUTH audit record, but only when its real uid is not 0.
Is it an attack?
On its own the line only says a password was offered for an account and did not check out. Read it together with the sshd lines carrying the same timestamp.
| Pattern | Reading |
|---|---|
| One line for a real user, then that user logs in from a known address | A typo |
| Several lines naming the same account, seconds apart | Guessing against one account |
Many different names in a short window — root, admin, ubuntu, user, test, deploy, postgres | A dictionary run against an exposed port |
| A name that should never authenticate by password — a service or key-only account | Worth chasing whatever the volume |
Any of the above followed by an Accepted password line | Stop triaging and treat the account as compromised |
Volume by itself is not a verdict: a host that accepts passwords from the internet produces these continuously. What changes the verdict is which names appear and what happened next.
How to investigate
# Helper failures in the journal, selected by syslog identifier
sudo journalctl -t unix_chkpwd --since "24 hours ago"
# Which account names were rejected (auth.log; on RHEL/Fedora use /var/log/secure)
sudo grep -oP 'password check failed for user \(\K[^)]+' /var/log/auth.log \
| sort | uniq -c | sort -rn | head -20
# The sshd lines around one helper line — these carry the address and port
sudo grep -E 'password check failed for user|Failed (password|keyboard-interactive)|Invalid user' \
/var/log/auth.log | grep 'Oct 18 03:51'
# Busiest source addresses, from sshd's own failure lines
sudo grep -oP 'Failed password for (?:invalid user )?\S+ from \K\S+' /var/log/auth.log \
| sort | uniq -c | sort -rn | head -20
# Did anything succeed? Check every accepted login
sudo grep -E 'Accepted (password|publickey)' /var/log/auth.log | tail -50
Check the names from the second command against /etc/passwd: a real local account is a different problem from a scanner’s wordlist. For an exported auth.log, the browser log analyzer groups the surrounding sshd lines by source, and the SSH brute-force rule encodes the one-source-many-names pattern as a detection.
How to stop it
The helper only runs when something asks PAM to verify a password, so remove the password paths and the lines stop.
- Turn off password authentication.
PasswordAuthentication no(the default isyes), and where PAM is enabled alsoKbdInteractiveAuthentication no(alsoyesby default) — sshd_config(5) notes that “PAM keyboard-interactive authentication usually serves an equivalent role to password authentication”. - Limit root. The default
PermitRootLogin prohibit-passwordmeans “password and keyboard-interactive authentication are disabled for root”;noremoves root logins entirely. - Limit who may log in at all with
AllowUsers; by default login is allowed for all users, and patterns may take theUSER@HOSTform with CIDR host criteria. - Keep the connection limits tight.
MaxAuthTriesdefaults to 6 attempts per connection andLoginGraceTimeto 120 seconds. - Leave
PerSourcePenaltiesenabled — penalties are on by default, and a client that disconnects after one or more unsuccessful authentication attempts is refused for 5 seconds by default, accumulating to a maximum of 10 minutes once 15 seconds of penalty have accrued. - Do not expose port 22 to the whole internet if a firewall rule, VPN or bastion can carry the traffic instead.
sudo sshd -t && sudo systemctl reload ssh # RHEL/Fedora: systemctl reload sshd
Validate the configuration first and keep a working session open until a new one succeeds.
Lines you will see next to it
- … for … from … port … ssh2… sshd rejected a password authentication attempt — the line names the account that was tried, the client address and the client's source port, and marks names that are not usable accounts on this host with "invalid user".
- authentication failure; logname=… uid=… euid=… tty=… ruser=… rhost=… PAM's pam_unix module rejected the credentials sshd asked it to check — the line names the client address in rhost= and, when it has one, the account in user=.
- check pass; user unknown pam_unix was asked to check a password for a name it has no account record for, so the attempt failed before any password was compared — and the line deliberately leaves out both the name and the source address.
- Invalid user … from … port … sshd rejected the login because the name the client asked for is not a valid account on this host, so no password or key was ever checked against a real user.
Detect it
Frequently Asked Questions
- Does this line mean the account actually exists?
- No. The helper writes the message as soon as the password check returns anything other than success, and only afterwards does it separate the unknown-user result (PAM_USER_UNKNOWN, propagated as PAM_AUTHINFO_UNAVAIL) from a general failure. To tell the two apart, read sshd's own "Invalid user" line or pam_unix's "check pass; user unknown" line for the same connection.
- Why is there no IP address in the message?
- The format string has a single substitution and it is the account name — the helper is a short-lived process that is handed a user and a password, not a network connection. Pivot on the timestamp and on the sshd-session lines logged in the same second, which do record the source address and port.
- Why do these lines not line up one-for-one with "Failed password" lines?
- The helper runs once for every password that pam_unix is asked to verify, and a single connection may submit several before it is cut off — MaxAuthTries defaults to 6. Key-based authentication never reaches the helper at all, and which sshd line accompanies it depends on whether the password arrived through password or keyboard-interactive authentication.
- Should I alert on this message?
- Not on single lines if the host accepts passwords from the internet, where they are constant background noise. Alert on shape instead — many different account names from the same minutes, repeated attempts against one real account, any line naming an account that should be key-only, and above all an Accepted line that follows.
Run your own auth.log through it
Paste or drop /var/log/auth.log (or journalctl -u ssh output) into the free browser analyzer: brute force, password spraying, a successful login after failures and invalid-user sweeps are flagged with the evidence lines. Nothing is uploaded.