VMware ESXi and vSphere Cluster Management

Understanding the Linux /etc/shadow File Format

Learn how Linux stores password hashes and password-aging policies in /etc/shadow, including all nine colon-separated fields and safe administration commands.

What /etc/shadow Does

/etc/shadow is a protected local Linux account database. It stores password-hash data and password-aging metadata, such as when a password was last changed and when it should expire.

/etc/passwd normally contains account identity information: the login name, user ID, group ID, home directory, and login shell. Its password column commonly contains an x placeholder rather than the password data. Sensitive password and aging information is normally kept in /etc/shadow.

A password is not stored as a reversibly encrypted value. Instead, Linux stores a password hash: a one-way derived representation used to compare a submitted password with the stored result. A salt is additional per-password input included during hashing, making precomputed hash tables less useful.

When the account is local, authentication components such as PAM (Pluggable Authentication Modules) commonly consult shadow data. The actual result also depends on PAM policy, NSS configuration, account state, and any centralized identity service.

Access Control and Security

Ordinary users should not be able to read /etc/shadow. Typical systems make it owned by root and assign restrictive permissions, although the exact owner, group, and mode vary by distribution. Inspect the current metadata instead of assuming a universal mode:

sudo stat -c '%A %U:%G %n' /etc/shadow

Hashes are not plaintext passwords, but they are still sensitive. An attacker who obtains them can attempt offline password guesses, especially when users choose weak or reused passwords. Do not copy, publish, or broadly share shadow entries.

Use account-management tools such as passwd, chage, and usermod for normal changes. Direct editing is error-prone and can cause authentication failures.

Record Structure

The file contains one account record per line. Each record has nine standard fields separated by colons:

login:password:last_change:min_age:max_age:warning:inactive:account_expiry:reserved

Every colon is structurally significant. A blank field is not automatically equivalent to zero. Its meaning depends on the field. Preserve the field count and the colon positions.

/etc/shadow Field Reference

Position 1 — Login name. The account name, normally matching an account entry in /etc/passwd.

Position 2 — Password field. A password hash, a lock marker, or another value defined by the platform and authentication policy.

Position 3 — Last password change. The number of days since 1970-01-01 when the password was last changed.

Position 4 — Minimum age. The number of days that must pass before the password may be changed again. Zero permits immediate changes.

Position 5 — Maximum age. The number of days a password may remain valid before expiration. A value such as 99999 has historically represented an effectively nonexpiring policy.

Position 6 — Warning period. The number of days before password expiration during which the user is warned.

Position 7 — Inactivity period. The number of days after password expiration before password-authenticated access is disabled. An empty value and a numeric value have different meanings under shadow semantics.

Position 8 — Account expiration date. An absolute number of days since 1970-01-01 after which the account is disabled, independently of password expiration.

Position 9 — Reserved field. Reserved for future or platform-specific use. Leave it unused unless the platform documentation says otherwise.

The Password Field

The second field can have several forms. A usable password normally appears as a modular crypt password string. This format uses dollar signs to identify the algorithm and parameters, followed by salt and hash data.

Common identifiers include $1$ for MD5 crypt, $5$ for SHA-256 crypt, and $6$ for SHA-512 crypt. Systems using yescrypt commonly use an identifier such as $y$, where supported. Exact identifiers, accepted formats, and default algorithms depend on the distribution and its crypt library and PAM configuration.

A conceptual modular crypt value might look like $6$settings$saltdatatogetherwithhashdata. The prefix identifies the scheme and may encode settings such as cost. The salt separates otherwise identical passwords, while the remaining data represents the derived hash.

Password Field States

Usable modular hash: The field contains a supported hash. Password authentication can proceed if the account and policy permit it.

Lock marker such as ! or *: Ordinary password authentication cannot succeed with that field. Other methods, such as SSH keys, sudo policy, service-specific authentication, or another configured mechanism, may still work.

Empty password field: This can permit or reject empty-password authentication depending on PAM policy. It is dangerous and should not be used as a normal configuration.

Distribution-specific value: Some systems and authentication stacks recognize additional markers or formats. Interpret them using the system documentation and PAM configuration.

Password-Aging Fields

Password aging is policy controlling when passwords may be changed, when they expire, and when warnings appear. The day-count fields use the Unix epoch, which begins on 1970-01-01.

Last Change

The last-change field is an integer count of days since the epoch. It is not a timestamp in seconds. For example, day 20000 corresponds to 2024-10-04.

Minimum and Maximum Age

A minimum age of 0 allows an immediate password change. A minimum age of 1 requires one day to pass before another change.

The maximum age is the number of days after the last change when the password expires. A maximum age of 90 means the password reaches its expiration point 90 days after the last-change date. Values such as 99999 are commonly treated as effectively nonexpiring, but administrators should follow the distribution's policy.

Warning and Inactivity

The warning field specifies how many days before expiration the user should receive warnings. The inactivity field specifies the grace period after password expiration. When that grace period ends, password-authenticated access can be disabled.

An empty inactivity field is not the same as a numeric value such as 0 or 30. The exact behavior should be interpreted using the system's shadow implementation and account-management tools.

Account Expiration

The account-expiration field is an absolute epoch-day count. Once that date passes, the account can be disabled even if its password was recently changed and has not reached maximum age. This is different from password expiration, which concerns the password's permitted lifetime.

Password Expiration Versus Account Expiration

Password set: The last-change field records the date. Maximum age begins counting from that date.

Warning begins: The warning period begins before the maximum-age deadline. PAM or another account policy can display warnings.

Password expires: The maximum-age deadline is reached. The user may be required to change the password, or password login may be refused according to policy.

Inactivity threshold: After password expiration, the inactivity interval can end and disable password-authenticated access.

Account expiration date: The absolute account date can disable the account regardless of the password's age.

Effective login behavior can also be affected by PAM modules, account lock state, SSH configuration, remote identity providers, SSSD, LDAP, Active Directory, containers, and application-specific authentication.

Reading a Fictional Entry Safely

Consider this deliberately fictional record. The password value is a placeholder, not a usable credential or a copied hash:

analyst:$6$REDACTED$NOT_A_REAL_HASH:20000:0:90:14:30:20150:

Interpret the nine positions as follows:

  1. Login name: analyst.
  2. Password field: A fictional SHA-512-crypt-style placeholder beginning with $6$. It must not be treated as a real hash.
  3. Last change: Day 20000, which is 2024-10-04.
  4. Minimum age: 0, so the password may be changed immediately.
  5. Maximum age: 90 days.
  6. Warning: 14 days before expiration.
  7. Inactivity: 30 days after password expiration.
  8. Account expiration: Day 20150, which is 2025-03-03.
  9. Reserved: Blank, which is appropriate for the unused standard field.

The password expires on 2025-01-02: 2024-10-04 plus 90 days. The warning period begins on 2024-12-19, 14 days before expiration. If the password remains expired, the 30-day inactivity interval reaches its threshold on 2025-02-01. The account-expiration date, 2025-03-03, is an independent later cutoff.

Comparing Two Policies

An interactive user might have minimum age 0, maximum age 90, warning 14, and inactivity 30. A service account might instead have a locked password field because it is intended to use a service credential, certificate, token, or another noninteractive mechanism. Locking the password does not automatically disable every other authentication path.

Administrative Inspection and Management

Shadow data should be inspected narrowly and privately. Avoid placing output containing hashes in shell history, shared terminals, logs, screenshots, or support tickets.

Inspecting the Configured Account Source

getent shadow uses NSS (Name Service Switch), so it can obtain shadow information from configured sources beyond the local file. Restrict the query to one account:

sudo getent shadow USERNAME

NSS may be connected to local files, LDAP, SSSD, or other providers. Therefore, a result from getent is not necessarily a line from the local /etc/shadow.

Readable Aging Information

Use chage to view policy without manually converting epoch-day values:

sudo chage -l USERNAME

For example, set an example aging policy with:

sudo chage -m 1 -M 90 -W 14 -I 30 USERNAME

Set an absolute account expiration date with:

sudo chage -E YYYY-MM-DD USERNAME

Password and Lock Management

Use passwd to set or reset a password:

sudo passwd USERNAME

Lock or unlock password-based authentication with:

sudo passwd -l USERNAME
sudo passwd -u USERNAME

Unlock only when a valid password hash exists and restoring password authentication is authorized. The -l operation does not necessarily disable SSH keys or other authentication methods.

Using usermod

usermod provides another administrative interface for account expiration and post-password-expiration inactivity:

sudo usermod -e YYYY-MM-DD -f 30 USERNAME

Options and behavior can vary slightly by implementation, so verify the result with chage -l.

When Direct Editing Is Unavoidable

If a documented recovery procedure requires direct editing, use the purpose-built editor workflow:

sudo vipw -s

vipw -s is safer than opening the file with a general editor because it provides locking and validation appropriate to account databases. Still make a verified backup, preserve all nine fields and separators, and have a tested recovery path before changing a privileged account.

Defaults and Policy Layers

Some distributions define default aging values in /etc/login.defs. These defaults commonly influence newly created accounts; changing them does not necessarily update existing accounts.

PAM configuration under /etc/pam.d/ influences password quality, expiration handling, empty-password behavior, and authentication outcomes. Do not apply blanket PAM edits: module names, file paths, and policy semantics vary by distribution.

Operational Cautions

  • Do not manually alter a hash field unless there is a documented recovery procedure and a verified backup.
  • Never remove or add colons casually. A malformed field count can prevent logins.
  • Do not replace blank fields with zero without understanding the field-specific meaning.
  • Use a maintenance console or tested recovery path before changing access policies for a privileged account.
  • Test changes with a noncritical account before ending a maintenance session.
  • Changing local /etc/shadow may not affect users authenticated through LDAP, Active Directory, SSSD, containers, or another external identity system.

Troubleshooting Common Problems

A User Must Change a Password or Cannot Use the Current Password

Likely causes include a reached maximum age, an administrator-forced expiration, or centralized PAM or directory policy. Start with:

sudo chage -l USERNAME
getent passwd USERNAME

Determine whether the account is local, check applicable NSS or SSSD configuration, and review relevant authentication logs. Use passwd or chage to resolve the issue only after confirming the intended policy.

Password Login Fails After passwd -l

The password field may now contain a lock marker. Check the state with:

sudo passwd -S USERNAME

Also check whether SSH keys or other authentication methods remain enabled. Use sudo passwd -u USERNAME only when password authentication should be restored. For full access restriction, use the appropriate account-expiration, shell, SSH, authorization, or service-specific controls.

An Account Is Disabled Despite a Recent Password Change

Check chage -l USERNAME. An absolute account-expiration date may have passed, a post-expiration inactivity interval may have elapsed, or a directory service may be applying a separate policy. Adjust the relevant setting only if continued access is authorized.

A Manual Edit Prevents Logins

Common causes include a moved colon, invalid date field, corrupted hash marker, or changed ownership and permissions. From a privileged console, compare the structure with a known-good local record, inspect metadata, and review authentication logs:

sudo stat -c '%A %U:%G %n' /etc/shadow

Restore from a verified backup or repair through vipw -s. Validate access with a noncritical account before closing the maintenance session.

Exam-Relevant Notes

  • /etc/passwd is primarily identity data; /etc/shadow protects password hashes and aging data.
  • Shadow passwords are one-way hashes, not reversibly encrypted passwords.
  • The nine fields are colon-separated and ordered; blank fields have field-specific meanings.
  • Epoch-day values count from 1970-01-01.
  • Password expiration and account expiration are different events.
  • A lock marker blocks ordinary password authentication but does not guarantee that every authentication method is disabled.
  • NSS determines where account information comes from, while PAM commonly enforces authentication and account policy.
  • Use passwd, chage, usermod, and vipw -s rather than casual manual edits.

For related account identity information, see the Linux account-file format lesson.