skip to content

A Linux user types the correct password and is still refused at login, with no message about a wrong password. Explain how PAM structures the login path and which stage rejects an otherwise-valid password.

level: seniorimportance: nice to knowfreq 38%

answer

  1. four stacks, four different questions
  2. auth is only the first of them
  3. correct password, wrong stage
  4. control flag decides how it folds
  5. vagueness is deliberate, read the log

basics

~20 s

PAM splits authentication into four independent stacks: auth (are you who you say), account (are you allowed right now), password (changing the secret) and session (setting the session up). A correct password that still fails is almost always rejected by the account or session stack.

solid answer

~50 s

PAM is the pluggable layer that programs like `login`, `sshd` and `sudo` call instead of checking passwords themselves. Each program has a file in `/etc/pam.d/` naming four stacks of modules. The **auth** stack verifies the credential — that is the part the password satisfies. The **account** stack then asks a separate question: is this account permitted to log in *now*? It is where expiry from `/etc/shadow`, `pam_nologin` blocking non-root logins, `pam_access` restricting origin, `pam_time` restricting hours, and `pam_faillock` lockouts all live. The **session** stack runs afterwards to set up the environment, limits and home directory, and can fail too. Each line carries a control flag — `required`, `requisite`, `sufficient`, `optional` — that decides how its result folds into the stack's verdict. So diagnose by stage: `chage -l user` for expiry, check for `/etc/nologin`, check lockout state, and read the service's own PAM file rather than assuming the generic one applies.

code

bash · 10 lines
bash
# Which stack does this service actually use?
cat /etc/pam.d/sshd

# Account-stage suspects, in order
sudo chage -l alice          # expiry and ageing fields
ls -l /etc/nologin           # blocks every non-root login while present
sudo faillock --user alice   # lockout tally (RHEL-family)

# The modules name themselves in the log even when the user is told nothing
sudo journalctl -u sshd -n 50

go deeper

for a junior

Know that PAM is the shared layer programs call to authenticate users, and that its configuration lives in per-service files under /etc/pam.d/.

for a middle

Explain the four stacks and what each decides, and describe how the control flags — required, requisite, sufficient, optional — combine module results into one verdict.

for a senior

Diagnose by stage: separate a credential failure from account expiry, a nologin file, a faillock lockout or a failing session module, and keep a second root session open before touching the stack.

for a principal

Own authentication policy across the fleet — where identity is backed, how second factors and lockout thresholds are standardised, and what console-level recovery exists when a PAM change locks everyone out.

## What PAM is for Before PAM, every program that authenticated a user contained its own password-checking code. Pluggable Authentication Modules invert that: `login`, `sshd`, `su`, `sudo`, display managers and cron all hand the decision to a shared library, and the administrator composes the policy from modules. Adding two-factor authentication or a directory backend becomes an edit to a text file rather than a patch to every program. ## Four stacks, four questions Each service's file in `/etc/pam.d/` (named after the service — `/etc/pam.d/sshd`, `/etc/pam.d/sudo`) contains lines of the form: ``` <type> <control> <module> [arguments] ``` The four types are independent stacks answering different questions: - **auth** — *are you who you claim?* Verifies the credential: `pam_unix.so` against `/etc/shadow`, `pam_sss.so` against a directory, a second-factor module, and so on. - **account** — *is this account allowed to proceed, right now?* Nothing here re-checks the password. It checks validity: expiry, allowed hours, allowed origins, lockout state. - **password** — *changing the secret.* Only runs when a password is being set, and hosts quality rules such as `pam_pwquality.so`. - **session** — *set up and tear down the session.* Resource limits (`pam_limits.so`), environment (`pam_env.so`), creating a home directory (`pam_mkhomedir.so`), registering with the login manager (`pam_systemd.so`). A correct password satisfies **auth** only. A login that fails after a correct password was almost always rejected by **account** or **session** — which is exactly why the message is often vague rather than "wrong password". ## Control flags The control field decides how one module's result folds into the stack's verdict: - **required** — must succeed; failure fails the stack, but the remaining modules still run, so the user cannot tell *which* one objected. - **requisite** — must succeed, and failure returns immediately. - **sufficient** — success ends the stack successfully, provided no earlier `required` module failed. - **optional** — result ignored unless it is the only module in the stack. - **[value=action …]** — the modern, explicit form, for example `[success=1 default=ignore]`, which is what distributions actually generate when they need to skip a line. The deliberate vagueness of `required` is a security property: revealing which check failed leaks whether an account exists, is locked, or is merely out of hours. ## Where the files live Debian-family systems factor shared policy into `common-auth`, `common-account`, `common-password`, `common-session`, pulled in with `@include`. RHEL-family uses `system-auth` and `password-auth`, pulled in with `substack`, and on RHEL 8+ those files are generated by `authselect` — hand-editing them gets overwritten. Either way, always read the *service's* file, because `sshd` and `sudo` do not necessarily share a stack. ## The usual culprits for "correct password, still refused" - **Account expiry.** The eighth field of `/etc/shadow` is an absolute expiry date; `chage -l alice` prints it. Past that date, `pam_unix` fails the account stack. - **Password expired past the inactivity grace.** An expired password normally forces a change; once the inactive period lapses, the account is refused outright. - **`/etc/nologin` exists.** `pam_nologin.so` refuses every non-root login while that file is present and prints its contents. A maintenance script that failed to remove it locks everybody out. - **Lockout after failed attempts.** `pam_faillock.so` counts failures and denies for a window; `faillock --user alice` shows the tally and `faillock --user alice --reset` clears it (RHEL-family ships this by default). - **Origin or time restrictions.** `pam_access.so` (`/etc/security/access.conf`) and `pam_time.so` deny by source or by hour, with no reference to the password. - **Shell is nologin.** Not PAM at all — authentication succeeded and the session started, but `/usr/sbin/nologin` refused an interactive shell. The symptom is an immediate disconnect. - **Session stack failure.** A home directory on an unmounted network filesystem, or a `pam_limits` rule the session cannot satisfy, can end a session that authenticated perfectly. ## How to work the problem Read the service's log first — `journalctl -u sshd` or the authentication log — because PAM modules say which module denied even when the user is told nothing. Then walk the stages in order: does `chage -l` show expiry; does `/etc/nologin` exist; does `faillock` show a lockout; does the service's `/etc/pam.d/` file include a restriction the generic stack does not. Keep a second root session open while you test, since a broken PAM file can lock every account out of the machine and force console recovery.

  • What is the difference between the `required` and `requisite` control flags?
    Both must succeed for the stack to succeed. `required` keeps running the remaining modules after a failure and reports at the end, which hides *which* check objected and avoids leaking whether the account exists. `requisite` aborts immediately on failure. Distributions favour `required` for that concealment, at the cost of a less specific error for the administrator — who is expected to read the log instead.
  • Every account on a host is suddenly refused at login except root on the console. What do you check first?
    Whether `/etc/nologin` exists — `pam_nologin.so` refuses all non-root logins while it does and prints the file's contents as the message. It is typically left behind by a maintenance script or an interrupted shutdown. If the file is absent, look next for a broken edit to the shared PAM stack, which produces the same fleet-wide symptom and needs console recovery.
  • Does `sudo` go through PAM as well, and why does that matter?
    Yes — sudo has its own file in `/etc/pam.d/sudo`, so the password prompt, any second factor, and the account checks apply there too. It matters because sudo can fail for reasons that have nothing to do with sudoers: an expired account or a faillock lockout denies at the PAM layer before the policy is even consulted. When `sudo -l` shows a valid rule but the command is refused, check PAM before rereading the sudoers file.

saying these in an interview costs you the question

  • Thinking PAM only checks passwords
  • Assuming a refused login means a wrong password
  • Editing a shared PAM file with no second root session open
  • Believing every service shares one PAM configuration
  • Hand-editing authselect-generated files on RHEL 8+

context