Understanding the /etc/shadow File Format
Learn the nine /etc/shadow fields, password hashes, lock markers, aging policies, permissions, PAM behavior, and safe Linux administration commands.
/etc/shadow is the protected local Linux account database for password-verifier data and password-aging policy. It normally contains one record for each local account. Understanding its format helps administrators diagnose login problems and configure account policies safely.
This file contains sensitive authentication data. Read it only when necessary, avoid copying its contents into tickets or chat, and prefer account-management commands over manual editing.
What /etc/shadow Is Used For
A password hash is a one-way verifier derived from a password. During password authentication, the system processes the supplied password and compares the result with the stored verifier. The original password is not stored in /etc/shadow.
The file also stores password aging values: when the password was last changed, how long it must remain unchanged, when it expires, how long to warn the user, and when an inactive account should be disabled.
| Aspect | /etc/passwd | /etc/shadow |
|---|---|---|
| Primary purpose | Account identity and profile information | Password verifiers, account markers, and password-aging data |
| Typical fields | Username, UID, GID, comment, home directory, login shell | Username, password field, aging and expiration fields |
| Typical access | Usually readable by all users so programs can map names and IDs | Restricted to root and, on some systems, an authorized shadow group |
| Password storage | Usually contains x as a reference to the shadow database | Contains the verifier or a password-account marker |
Separating password verifiers from the generally readable account database reduces exposure. Programs can discover usernames and numeric IDs without receiving the data needed to test passwords offline. File permissions do not replace strong passwords, secure hashing algorithms, patching, monitoring, and other authentication controls.
Permissions and Safe Access
Typical systems make /etc/shadow root-owned and restrict reading and writing to root, sometimes granting read access to a dedicated shadow group. Exact ownership and mode vary by distribution and installation. Inspect permissions without displaying file contents:
ls -l /etc/passwd /etc/shadowDirect editing is risky because a single missing colon, malformed number, mismatched username, or truncated hash can break account administration or authentication. For normal work, use passwd, chage, or usermod. If direct maintenance is absolutely unavoidable, vipw -s provides locking and safer editing behavior:
sudo vipw -sBefore low-level changes, establish a protected backup and a recovery plan. Validate syntax and test with a nonproduction account. Treat a backup of this file as highly sensitive: anyone who obtains password hashes may attempt offline guessing.
Record Layout
Each non-comment record uses nine logical fields separated by colons. The following is a syntax diagram, not a literal system record:
login:password-field:last-change:min-age:max-age:warning:inactive:account-expiry:reservedA fictional annotated example is shown below. The password-verifier content is intentionally represented by a neutral placeholder:
demo_user:[redacted-verifier]:19800:1:90:14:30:20000:demo_user— login name.[redacted-verifier]— a password hash or account marker.19800— day of the last password change.1— minimum days before another password change.90— maximum password age.14— warning days before expiration.30— inactive days after password expiration.20000— absolute account-expiration day.- Empty final field — reserved field.
The Nine /etc/shadow Fields
| Position | Field name | Typical contents | Meaning | Common blank or marker behavior |
|---|---|---|---|---|
| 1 | Login name | Username | Identifies the local account and corresponds to an entry in /etc/passwd. | Should not be blank for a normal account. Records should align with local account entries. |
| 2 | Password field | Hash or marker | Stores a one-way verifier or indicates a password state. | Empty can mean no password is set; * commonly makes password authentication unusable; leading ! commonly locks a hash. |
| 3 | Last password change | Day count | Days since the Unix epoch when the password was last changed. | Blank handling varies. Some tools interpret blank or special values as no recorded change. |
| 4 | Minimum password age | Day count | Minimum number of days before the user may change the password again. | Blank or zero commonly means no minimum, subject to the tool and policy. |
| 5 | Maximum password age | Day count | Maximum number of days the password remains valid. | Blank or a large value may mean no local expiration, depending on implementation. |
| 6 | Warning period | Day count | Number of days before expiration during which warnings are given. | Blank or zero may disable a warning period. |
| 7 | Inactivity period | Day count | Days after password expiration before the account is disabled for inactivity. | Blank commonly means no inactivity limit; verify with policy tools. |
| 8 | Account expiration | Day count | Absolute date after which the account cannot authenticate under the applicable policy. | Blank commonly means no local expiration date. |
| 9 | Reserved | Usually empty | Reserved for future use. | Ordinarily left empty. |
Field 1: Login Name
The first field is the username used to identify the account. It should correspond to the account name in /etc/passwd. A mismatch can cause tools to report incomplete account data or cause authentication behavior to differ from expectations. Local accounts should have consistent entries in the local account databases.
Field 2: Password Hashes and Account Markers
A normal password field contains a modular crypt format value. Modular crypt format is a structured representation that commonly includes an algorithm identifier, parameters or cost settings, a salt, and the resulting hash:
$algorithm$parameters$salts-and-hashA salt is a per-password value included in hashing. It prevents identical passwords from normally producing identical stored outputs and makes precomputed lookup attacks less useful. The value is still a verifier, not an encrypted password that administrators can decode.
| Prefix or marker | Typical interpretation | Security and compatibility notes |
|---|---|---|
$1$ | MD5 crypt | Obsolete and unsuitable for new passwords. |
$5$ | SHA-256 crypt | Older crypt-family format; use the distribution's current policy rather than selecting it manually. |
$6$ | SHA-512 crypt | Common legacy crypt-family format, but not necessarily the default on modern systems. |
$y$ | yescrypt, where supported | A modern memory-hard scheme used by some contemporary distributions. |
Leading ! | Password entry commonly locked | Often placed before an existing hash. Exact behavior depends on PAM and distribution policy. |
* | Password authentication commonly unusable | Often used for service or system accounts. Other authentication methods may still be possible. |
| Empty field | No password verifier | May permit passwordless authentication if the service and PAM configuration allow it; this is generally unsafe and must not be confused with a locked hash. |
Locking a password usually preserves the existing hash while adding a lock marker, such as a leading exclamation mark. Deleting the hash, often by making the field empty, is different: it removes the verifier rather than merely disabling the existing password. Neither state alone describes every possible login path. SSH keys, service rules, PAM modules, MFA, and centrally managed identities can continue to affect access.
Password Aging Fields
Fields 3 through 8 generally use whole days since the Unix epoch, which begins on 1970-01-01 UTC. These are date-only policy values, not ordinary timestamps.
| Field | Unit | Policy effect | Example interpretation |
|---|---|---|---|
| Last password change | Days since epoch | Records the reference point for password age. | A value of 19800 identifies the day on which the password was last changed. |
| Minimum age | Days | Prevents another password change until this many days have passed. | 1 means the user normally waits one day. |
| Maximum age | Days | Sets when the password becomes expired. | 90 means expiration is evaluated 90 days after the last change. |
| Warning period | Days | Controls how early expiration warnings begin. | 14 means warnings can begin 14 days before expiration. |
| Inactive period | Days after expiration | Disables the account after an expired password remains unused for this interval. | 30 means disablement can follow 30 inactive days after expiration. |
| Account expiration | Days since epoch | Sets an absolute account expiration date independent of password age. | A value such as 20000 identifies a fixed calendar day. |
The minimum and maximum ages govern password age. The inactive interval starts after password expiration. Account expiration is a separate absolute boundary. Blank values, zero values, and very large values can have tool-specific meanings, so inspect the interpreted result with account-management commands.
Converting Shadow Day Counts
On systems with GNU date, convert an illustrative day count to a UTC date as follows:
DAY_COUNT=19800
date -u -d "1970-01-01 + $DAY_COUNT days" +%FThe result is a UTC calendar date. Other Unix-like systems use different date syntax. Because shadow values represent whole days and policy dates are not timestamps, avoid treating a local-time conversion as more precise than the stored data.
For a human-readable interpretation, use:
sudo chage -l USERNAMEThis can show the last password change, password expiration, minimum and maximum ages, warning period, inactivity period, and account expiration without requiring you to expose the raw hash.
Account-Management Tools
| Task | Preferred command or tool | Why it is preferred | Verification method |
|---|---|---|---|
| Set or change a password | sudo passwd USERNAME | Updates the verifier through supported mechanisms and policy. | sudo passwd -S USERNAME and sudo chage -l USERNAME |
| Display aging policy | sudo chage -l USERNAME | Shows interpreted dates and intervals without manual arithmetic. | Review the displayed policy. |
| Set aging values | sudo chage ... USERNAME | Validates and updates the relevant fields. | Run chage -l again. |
| Lock password authentication | sudo passwd -l USERNAME or an appropriate usermod lock operation | Applies a standard lock state without hand-editing the hash. | sudo passwd -S USERNAME |
| Unlock password authentication | sudo passwd -u USERNAME | Removes a supported lock marker when a valid hash and policy permit it. | Check status and test the intended service. |
| Direct shadow maintenance | sudo vipw -s | Provides file locking and a controlled editing workflow. | Run account tools and perform controlled validation. |
Example: Configure a Demonstration Policy
Use this only for a test or nonproduction account and adapt it to organizational policy:
sudo chage -M 90 -m 1 -W 14 -I 30 USERNAME
sudo chage -l USERNAMETo set an absolute account expiration date:
sudo chage -E YYYY-MM-DD USERNAMEpasswd -S provides compact status information that can help distinguish a usable password, a locked password, and a passwordless or otherwise unusable state. Its exact output format varies by implementation, so use chage -l and service logs for details.
/etc/shadow, PAM, and Authentication Paths
Local Unix authentication commonly reads /etc/shadow through PAM, or Pluggable Authentication Modules. PAM is a framework that lets services apply authentication, account, password, and session rules through configured modules.
A shadow entry is only one input to an authentication decision. PAM rules, SSH configuration, login-shell restrictions, MFA, access-control policy, directory services, and service-specific settings can change the result. A local password is also different from:
- SSH public-key authentication: the client proves possession of a private key; it may work even when password authentication is locked, if SSH policy permits it.
- Centrally managed identities: LDAP, SSSD, or another identity provider may supply account and password data instead of the local files.
- Other authentication methods: hardware tokens, certificates, or MFA can be evaluated by PAM or the service.
Locking Versus Expiring
sudo passwd -l USERNAME normally disables password-based authentication by marking the password field while retaining the verifier. This is useful for temporarily suspending password login or disabling a known password path without deleting its data.
sudo chage -E YYYY-MM-DD USERNAME sets an absolute account expiration date. This applies an account-policy boundary and is not the same as locking only the password. Use a password lock when the password method should be disabled; use account expiration when the account itself should stop authenticating after a defined date. Always consider keys, PAM rules, and external identity sources.
Safe Inspection and Administration Workflow
- Confirm whether the account is local and identify the service involved.
- Use
sudo chage -l USERNAMEandsudo passwd -S USERNAMEbefore reading raw shadow data. - Use
passwd,chage, orusermodfor supported changes. - Record the authorized change, intended effect, and recovery procedure.
- For exceptional direct maintenance, make a protected backup and use
sudo vipw -s, not an unrestricted text editor. - Verify field structure, ownership, permissions, account-tool output, and the intended login method.
- Check relevant authentication logs without disclosing password hashes.
Privileged access is normally required to read the shadow database or change another user's password and policy. A regular user can usually change their own password, subject to local policy, but cannot normally inspect other users' verifiers.
Troubleshooting Common Problems
A user cannot log in after the account was locked
- Check status with
sudo passwd -S USERNAME. - Check expiration and inactivity with
sudo chage -l USERNAME. - Review the relevant service logs and PAM policy.
- If password login should be restored, use
sudo passwd -u USERNAMErather than removing a marker by hand. Confirm that a valid hash remains.
The user is told that the password has expired
- Review the maximum age and last-change date with
chage -l. - Check whether a PAM or organizational policy imposes additional expiration.
- Reset the password when authorized, then change aging values with
chage honly if policy requires it.
Manual editing broke account tools or login
- Possible causes include an incorrect colon-field count, malformed numeric values, a username mismatch, or a truncated hash.
- Restore a known-good protected backup if available.
- Use supported account tools or a controlled recovery procedure; do not guess at hash contents.
- For future exceptional edits, use
vipw -s.
Password login fails despite a valid-looking hash
- The account may be expired or inactive.
- The login shell or service may deny access.
- SSH may disallow password authentication while allowing keys.
- PAM, MFA, or an external identity provider may override local expectations.
- Check
chage -l,passwd -S, service configuration, logs, and the account's identity source before changing the hash.
Key Exam and Administration Notes
/etc/passwdis primarily the readable identity database;/etc/shadowprotects password verifiers and aging data.- There are nine colon-separated logical fields.
- Fields 3 through 8 generally count days from the Unix epoch.
- A leading
!commonly locks an existing password hash; an empty password field is a different state. $1$identifies obsolete MD5 crypt.$5$and$6$identify SHA-256 crypt and SHA-512 crypt;$y$identifies yescrypt where supported.- Use
passwd,chage, andusermodinstead of editing the file directly. - A shadow entry does not by itself determine whether SSH keys, PAM modules, MFA, or centralized identities can authenticate.
For related local account administration, see creating a Linux user, administering groups, and modifying file permissions.