It's pretty simple. I would like to figure out what combination of changes in /etc/login.defs and/or /etc/pam.d/system-auth-ac. I would need to perform to allow the following behavior:

  • I want a user's password to be valid for 60 days.

  • After 60 days, the system needs to yell at the user when they log in, telling them they need to change their password ASAP.

  • The system must not impede the user's access to the system.

  • This must apply to existing users (non-system accounts, UID >=500) as well as any newly-created users.

Rationale: Limited users will not be managing the system account passwords, only system admin(s). Therefore, users should not have their access to the system impeded because the admin missed a password change. The number of accounts is rather small (maybe 9 or 10), but we're all human and we forget to do stuff from time to time.

I'm not sure if login.defs or PAM offer this. The documentation leads me to believe that you can either have the system force the user to change their password when it expires, or you can have the password not age at all. A third option is to have the password age limit set to some huge amount, like 9,999 days, and then start warning the user that their password will expire in 9,936 days, but that's not really what I need either. I've done other kinds of PAM configuration, so it's not my first trip around the block. I'm just stuck on this problem.

So, can this be done with PAM/login.defs, or do I need another utility that can take their place?

2 Answers 2


If you're not averse to using a custom script, try this as a pam_exec script:

#! /bin/bash
LAST_DATE=$(date -d "$(chage -l $PAM_USER  | awk -F: '/Last password/{print $2}')" '+%s')
TODAY=$(date '+%s')
if (( (($TODAY - $LAST_DATE) / 86400) > $MAX_AGE ))
    echo "Please change your password!"

Save it somewhere (say, /usr/local/bin/pass-warn.sh), and add a line to /etc/pam.d/sshd:

session optional pam_exec.so stdout /usr/local/bin/pass-warn.sh
  • Having some trouble with the comment formatting, so I ask to be excused in advance. If I use PAM_USER at the command line for testing, chage outputs a help section, but I do get results with USER. Regardless of which I use in the script, I get the following error after SSHing in: stdout failed: exit code 2 I'm still debugging, but any insights would be helpful. Also, could this work as-is for console (non-SSH) logins with the CLI and GUI if I drop the pam_exec.so line into /etc/pam.d/system-auth-ac? Commented Dec 30, 2014 at 16:04
  • @the_non-guru_guru that is odd. Can you look up man pam_exec on your system to see how it differs from Ubuntu's? And I think it should for CLI (but not for GUI, where would the output go to?), but I don't have a local system to check it on. Let me see if I can get a VM running.
    – muru
    Commented Dec 30, 2014 at 16:08
  • I should have mentioned that I'm working on a Red Hat Enterprise Linux 6.5 system. If you have (or want to download) a CentOS 6.5 installer, they are practically the same OS. I'll take a peek and see if I can find Ubuntu's manpage so I can do a side-by-side. Commented Dec 30, 2014 at 16:11
  • @the_non-guru_guru I have linked to Ubuntu's man page in the previous comment, and I have a CentOS 6.5 server on which I can test this out. Will update if I can fix this.
    – muru
    Commented Dec 30, 2014 at 16:12
  • @@muru I missed that, my bad. The CentOS flavor lacks the stdout argument. Setting log=/dev/stdout gives me more info. /usr/local/bin/pass-warn.sh failed: exit code 2 Commented Dec 30, 2014 at 16:19

If you set password aging at all then pam_unix.so is going to deny their authentication after the password expires. To do what you're wanting you can probably add something to their login scripts. For example, I can add the following to /etc/profile.d:

dayLastChanged=$(passwd -S $(whoami) | awk '{print $3}')

currentTimestamp=$(date "+%s")
lastChangeTimestamp=$(date -d $dayLastChanged "+%s")

timestampDifference=$(( $currentTimestamp - $lastChangeTimestamp ))
maxSeconds=$(( $maxDays * 86400 ))

if [[ $timestampDifference -gt $maxSeconds ]]; then    
   echo "Yo, you need to change your password bud. Otherwise the guys with white-on-white ties are coming for you."    
  • Files in profile.d are sourced, IIRC, so the shebang line isn't very useful.
    – muru
    Commented Dec 30, 2014 at 14:29
  • It's just something I always do for all my scripts. I just decided at some point in the past that I needed to get in the habit of always specifying my interpreter with the idea that it'll be ignored if it doesn't matter.
    – Bratchley
    Commented Dec 30, 2014 at 14:31
  • 1
    The danger with this habit is that if the file is sourced, it may be in a shell that's incompatible with your shebang. For example, here, you used a bash-specific construct (double brackets), but scripts under /etc/profile.d can be included by any Bourne-style shell, some of which will choke on [[. You might as well use the portable form [ … ] here since you aren't making any use of bash-specific functionality. Commented Dec 30, 2014 at 21:32
  • @Gilles. If that did happen then it was going to choke no matter what I did. Adding the shebang didn't hurt anything. That's more of an argument for writing sh-compatible scripts. The double square brackets were a mistake, however. The script still functions, just written in an unnecessary way.
    – Bratchley
    Commented Dec 31, 2014 at 0:54
  • 1
    @Bratchley This appears to work for the root account and fails for non-root users with "Only root can do that". A combination of your solution and muru's works. I basically use his script in the directory you specified. The only time it chokes is when you log in as an account that is not local to the system, but managed externally. Limited users appear to be able to run chage, but not passwd -S (at least on my system here). Commented Jan 2, 2015 at 20:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .