BASH: Check in /etc/shadow if user password is locked

Don't parse the shadow file manually

Parsing such files is fragile if you fail to account for all eventualities (for example, disabled passwords are often encoded as a single *; do other solutions deal with that?).

Additionally, authentication may not happen through shadow (but instead through NIS or ldap or who knows what).There are standard tools that will deal with all this for you. In this case, passwd:

-S, --status Display account status information. The status information consists of 7 fields. The first field is the user's login name. The second field indicates if the user account has a locked password (L), has no password (NP), or has a usable password (P). The third field gives the date of the last password change. The next four fields are the minimum age, maximum age, warning period, and inactivity period for the password. These ages are expressed in days.

So passwd -S | cut -d ' ' -f 2 will yield what you need. A simple if/then will translate it to your desired variable:

if [ "$(passwd -S "$USER" | cut -d ' ' -f 2)" = "P" ]
then
    disabled="False"
else
    disabled="True"
fi

The same applies to locking a user's password; this is preferably done through usermod (--lock option), not editing shadow manually.


Why not just do it all with awk?

awk -F: '/<username>/ {if(substr($2,1,1) == "!"){print "True"} else {print "False"}}' /etc/shadow

U=$user LC_ALL=C awk -F: < /etc/shadow '
  $1 "" == ENVIRON["U"] {
    user_found = 1
    if ($2 ~ /^!/) {
      print "True"
      exit 0
    } else {
      print "False"
      exit 1
    }
  }
  END {
    if (!user_found) {
      print "False"
      print "User "ENVIRON["U"]" not found" > "/dev/stderr"
      exit 2
    }
  }'

$1 "" == ENVIRON["U"] compares the first field with ENVIRON["U"] lexically. Without the "", the fields could end-up being compared numerically if they look like numbers (causing inf to match against INF or Infinity for instance).

Without LC_ALL=C, since some awk implementations use strcoll() for the == lexical comparison, it could end-up checking wrong entries for user names that sort the same.