Check for Specific Username Password Expire

hey Guys, I haven't posted in a while, But you guys were really helpful alst time.

I have had a issue with User Passwords expiring, and since I dont check /var/cron/log on the regular I never know these suers are expiring, making certain nightly jobs not run.

With this script, I want to be able to check for these particular users password expiration and mail ourselves a reminder.

I saw plenty of examples online, but I have a thing of borrowing from or editing code I cant understand as I want to understand what I am writing, and what the code is doing, helps me learn better.

Below is some code I've written to a way I can understand and work with. This code is dependent on a epoch Perl script I found online. But I dont think that is my problem presently. My problem is that right now, it does through every user in /etc/shadow and checks. I want it to only check a particular set of users our Admin usersnames all end in adm. so I want to only search for users in /etc/shadow ending in adm (for example testbedadm) and check for its expiration dates.

Any Suggestions? And sorry if this was lengthy

 
#!/bin/ksh
#Author:Emmanuel Iroanya Jr
#Edited: 
#Date:December 20th, 2012
#Purpose: The purpose of this is to check the Shadow table for the epoch value and warn the users / Email of Password Expiration  seven days in advance
#This script needs the epoch.pl I found on google to work

ID=`id | cut -d ' ' -f 1`
if [[ "${ID}" != "uid=0(root)" ]]
then
   echo "You Need To Be Root To Run This Script, Please and Thank You"
   exit 1
fi

export Shadow=/etc/shadow
#Location of the epoch.pl script I found from Google to Compare the Date
export EpochSh=/usr/local/bin/epoch.pl
export Hostname=`hostname`
#Our SSE Email Address that will get notification 
export Email="!SysEngGrp@mycompany.com"

for i in `cat $Shadow`
do
export User=`echo $i |cut -d ':' -f 1`
export MaxDay=`echo $i | cut -d ':' -f 5`
echo "$MaxDay"
export Epoch=`echo $i |cut -d ':' -f 3`
export Eval=`$EpochSh $Epoch | cut -d ':' -f 2`
echo "$Eval"
if [[ $Eval == `expr $MaxDay - 7` ]]
then
echo "Password for unix user $User on `hostname` is going to expire in a week. Please change it ASAP" | mailx -s 'Password Expiration ' $Email
elif [[ $Eval == `expr $MaxDay - 6` ]]
then
echo "Password for unix user $User on `hostname` is going to expire in 6 days. Please change it ASAP" |  mailx -s 'Password Expiration ' $Email 
elif [[ $Eval == `expr $MaxDay - 5` ]]
then
echo "Password for unix user $User on `hostname` is going to expire in 5 days. Please change it ASAP" |  mailx -s 'Password Expiration ' $Email 
elif [[ $Eval == `expr $MaxDay - 4` ]]
then
echo "Password for unix user $User on `hostname` is going to expire in 4 days. Please change it ASAP" |  mailx -s 'Password Expiration ' $Email 
elif [[ $Eval == `expr $MaxDay - 3` ]]
then
echo "Password for unix user $User on `hostname` is going to expire in 3 days. Please change it ASAP" |  mailx -s 'Password Expiration ' $Email 
elif [[ $Eval == `expr $MaxDay - 2` ]]
then
echo "Password for unix user $User on `hostname` is going to expire in 2 days. Please change it ASAP" |  mailx -s 'Password Expiration ' $Email 
elif [[ $Eval == `expr $MaxDay - 1` ]]
then
echo "Password for unix user $User on `hostname` is going to expire in 1 day. Please change it ASAP" |  mailx -s 'Password Expiration ' $Email 
elif [[ $Eval == "$MaxDay" ]]
then
echo "PASSWORD FOR USER $User HAS EXPIRED.PLEASE CHANGE IT ASAP TO AVOID PRODUCTION CRON JOBS FROM FAILING AND THE RESULTING LATE NIGHT CALLS"
fi
done

---------- Post updated at 05:14 PM ---------- Previous update was at 04:50 PM ----------

So I think I answered my question on how to look for the specific admin user with the below part,

for line in `cat $Shadow | grep adm`
do
 echo $line
done >passFile.txt
for i in `cat passFile.txt`
do

rest of my logic etc...

However, when I run it I get a bunch of varying errors like below:

I am assuming the Day Part is from the epoch.pl portion that reads the days the expr syntax error, is that from my math in my if/elseif logic?

It looks to me that your expr syntax error has to do with non-numeric data. I don't know what perl program $EpochSh does, but if it doesn't print out a number followed by a colon, you will have trouble with this line:

export Eval=`$EpochSh $Epoch | cut -d ':' -f 2`

Could that be your problem?

Also, I would like to gently point out your Useless Use Of Cat (UUOC). If you use a while loop, you can let it do your parsing for $User, $Epoch, and $Maxday:

#!/bin/ksh
while IFS=":" read User f2 Epoch f4 Maxday therest
do
echo "User: $User"
echo "Epoch: $Epoch"
echo "Maxday: $Maxday"
done < /etc/shadow

Also, if /etc/shadow's lines contained spaces, your code would fail miserably.

Another point: ksh/bash type shells let you do arithmetic, so you don't have to use expr:

if [[ $Eval == $(($x - 3)) ]]
then
echo "equal"
else
echo "NOT equal"
fi
1 Like

I'd gently propose a new "Useless Use of ELIF" award.
And I'm not sure I understand your approach to calculate the delta time until a password change is mandatory. Why don't you calculate the time difference between today (in days since epoch) and the next pw chng, which is shadow's field 3 + field 5, into e.g. DELTADAYS=fld3+fld5-today? If DELTADAYS <=0, alert, if 0 < DELTADAYS < 7, warn, otherwise OK?

1 Like

Sorry fro the late response, other work matters took my attention from this until now

Ok then, I removed the Else If Statements, and I took your way of arithmatic for my Eval, yet I still dont hae it working correctly.

 
#Author:Emmanuel Iroanya Jr
#Edited: 
#Date:December 20th, 2012
#Purpose: The purpose of this is to check the Shadow table for the epoch value and warn the users / Email of Password Expiration  seven days in advance
ID=`id | cut -d ' ' -f 1`
if [[ "${ID}" != "uid=0(root)" ]]
then
   echo "You Need To Be Root To Run This Script, Please and Thank You"
   exit 1
fi
export Shadow=/etc/shadow
EpochSh=`perl -e 'print time, "\n"'`
export Hostname=`hostname`
#Our SSE Email Address that will get notification
export Email="emmanuel@mycompany.com"
for line in `cat $Shadow | grep adm`
do
 echo $line
done >passFile.txt
for i in `cat passFile.txt`
do
User=`echo $i |cut -d ':' -f 1`
MaxDay=`echo $i | cut -d ':' -f 5`
echo "$MaxDay"
Epoch=`echo $i |cut -d ':' -f 3`
Eval=`$MaxDay+$Epoch-$EpochSh`
echo "$Eval"
                 if [[ $Eval -lt 7 ]]
                 then
                 echo "Password for unix user $User on `hostname` is going to expire in less than a week. Please change it ASAP" | mailx -s 'Password Expiration ' $Email
                 elif [[ $Eval -le 0 ]]
                 then
                 echo "PASSWORD FOR USER $User HAS EXPIRED.PLEASE CHANGE IT ASAP TO AVOID PRODUCTION CRON JOBS FROM FAILING AND THE RESULTING LATE NIGHT CALLS"
                 fi
done

What Am I not understanding?

---------- Post updated at 05:36 PM ---------- Previous update was at 05:35 PM ----------

I guess I used the Cat because it was what I was comfortable with, I will now try you while loop once I get the rest of it working.

Thank you for the suggestion! :slight_smile:

Well, I can't see what your Eval value is, but when I execute

# IFS=:; grep syslog /etc/shadow | { read user pw pwchg minpw maxpw Rest; echo "User: $user, days left: $(( pwchg + maxpw - $(date +%s)/86400 ))"; }
User: syslog, days left: 1

, I get sensible values to test against. And you can use days left for your expiration note as well.

1 Like

I changed my Eval value to this

eval Eval=`echo '( $MaxDay + $Epoch ) - ( $EpochSh / 86400 ) ' | bc`

I know get this error

syntax error on line 1, teletype

I can barely tell what you're even trying to do there, but I don't think you need the Eval builtin.

Eval=$( echo '( $MaxDay + $Epoch ) - ( $EpochSh / 86400 ) ' | bc )

echo $Eval

Have you tried the code snippet that I posted? It should run without problem, and you can use/assign the result to whatever variable you have.
Should you run into trouble with it or with any other code, please post the error msg in its context (e.g. shell's -x option), so we can help you. The forlorn syntax error on line 1, teletype does not tell me anything that could have gone wrong.

Hey, my apologies, I found the syntax error was for the built in calculator command "bc" it was what was complaining. I changed my code to below. and I also tried you method and it giving me the same numbers I get now, so thats good. However. I do not think I am doing the checking correctly because I get alot of negative numbers, EVERYTHING is less than the 7 or 0 we check for.

FUll code below:

 
#!/bin/ksh -x
#Author:Emmanuel Iroanya Jr
#Date:December 20th, 2012
#Purpose: The purpose of this is to check the Shadow table for the epoch value and warn the users / Email of Password Expiration  seven days in advance
ID=`id | cut -d ' ' -f 1`
if [[ "${ID}" != "uid=0(root)" ]]
then
   echo "You Need To Be Root To Run This Script, Please and Thank You"
   exit 1
fi
export Shadow=/etc/shadow
export EpochSh=`perl -e 'print time, "\n"'`
export Hostname=`hostname`
#Our SSE Email Address that will get notification
export Email="mygroup@mycompany.com"
for line in `cat $Shadow | grep adm`
do
 echo $line
done >passFile.txt
for i in `cat passFile.txt`
do
User=`echo $i |cut -d ':' -f 1`
MaxDay=`echo $i | cut -d ':' -f 5`
echo "$MaxDay"
Epoch=`echo $i |cut -d ':' -f 3`
echo "$Epoch"
Eval1=`expr $MaxDay + $Epoch`
Eval2=`expr $EpochSh / 86400`
Eval=`expr $Eval1 - $Eval2`
#export Eval=$(echo '( $MaxDay + $Epoch ) - ( $EpochSh / 86400 ) ' | bc)
echo "$Eval"
                 if [[ $Eval -lt 7 ]]
                 then
                 echo "Password for unix user $User on `hostname` is going to expire in less than a week. Please change it ASAP" | mailx -s 'Password Expiration ' $Email
                 elif [[ $Eval -le 0 ]]
                 then
                 echo "PASSWORD FOR USER $User HAS EXPIRED.PLEASE CHANGE IT ASAP TO AVOID PRODUCTION CRON JOBS FROM FAILING AND THE RESULTING LATE NIGHT CALLS" | mailx -s 'Password Expiration ' $Email
                 fi
done

---------- Post updated at 04:14 PM ---------- Previous update was at 04:08 PM ----------

+ + cut -d : -f 1
+ echo newadm:yj/khnry4nJiI:0:0:91::::
User=newadm
+ + cut -d : -f 5
+ echo newadm:yj/khnry4nJiI:0:0:91::::
MaxDay=91
+ echo 91
91
+ + cut -d : -f 3
+ echo newadm:yj/khnry4nJiI:0:0:91::::
Epoch=0
+ echo 0
0
+ + expr 91 + 0
Eval1=91
+ + expr 1359058335 / 86400
Eval2=15729
+ + expr 91 - 15729
Eval=-15638
+ echo -15638
-15638
+ [[ -15638 -lt 7 ]]
+ mailx -s Password Expiration  mygroup@mycompany.com
+ hostname
+ echo Password for unix user newadm on thisbox01 is going to expire in less than a week. Please change it ASAP

above is the results once I run, part of me just thinks If I switch the expressions it would be right, but I am unsure

You seem to dislike solutions handed to you on a silver plate by e.g. nails or by myself. Both offered a complete modul to you that you could easily grab and use. However, that's up to you.
The negative numbers that you get is because the passwordchange field in /etc/shadow is zero, indicating that newadm never changed it (or, cf. man shadow: The value 0 has a special meaning, which is that the user should change her pasword the next time she will log in the system. ). Simple arithmetics show that 0 + 91 - 15729 is negative, so newadm is seriously overdue to change his or her password and will deserve your mail, and rightly. Actually, the password change was due on April 3rd, 1970, you tell me why.

I don't dislike using solutions handed to me per se, just a little head strong with the way i want it to look, plus didnt know how to use the suggestions properly

Thank you for the patience however.

the newadm example was a bad one, your explanation obviously made sense. I think my real question, and promise your solution will be used, is how to deal with a username like below where a

passwd -x -1

was used to set the password to never expire. So the MaxDay variable is blank and this is what is causing them all to alarm, it gets a blank value passes it in and the number obviously will be lower that 7 or 0 and causes the email

 
+ + cut -d : -f 1
+ echo testingadm:.ezNz7T6WZLK.:15730::::::
User=newadm
+ + cut -d : -f 5
+ echo testingadm:.ezNz7T6WZLK.:15730::::::
MaxDay=
+ echo
+ + cut -d : -f 3
+ echo testingadm:.ezNz7T6WZLK.:15730::::::
Epoch=15730
+ echo 15730
15730
+ + expr + 15730
expr: syntax error
Eval1=
+ + expr 1359133813 / 86400
Eval2=15730
+ + expr - 15730
expr: syntax error
Eval=
+ echo
+ [[  -lt 7 ]]
+ mailx -s Password Expiration  mygroup@mycompany.com
+ hostname
+ echo Password for unix user newadm on cmap01 is going to expire in less than a week. Please change it ASAP

If your programming style intentionally uses eval, you may need to rethink your strategy.

When admin users don't need to change their password regularly, you might want to reconsider your site's security policy.
However, you can intercept that - depending on your shell - by using e.g. default values for parameters, or, better, sending a mail to the system admininstrator notifying him of the fact.

Disregarding that discussion, you might want to try this:

[ $(id -u) -ne 0 ] && { echo "root only"; echo exit; }
grep adm /etc/shadow | { IFS=":"; while read user pw pwchg minpw maxpw Rest; do REMAINS=$(( pwchg + ${maxpw:-99999} - $(date +%s)/86400 )); [ $REMAINS -le 0 ] && echo mail $user "alert"; [ $REMAINS -le 7 ] && echo mail $user "warning" $REMAINS "days remain" ; done; }

This will use a default value of 99999 days should maxpw be left empty. Default parameters' availibility may depend on your shell. I've put in echo es for demo purposes; remove them if you want the real stuff...
And, voila, not c[au]t s at all!

1 Like

You sir are a Genius!! that worked perfectly.

Thanks alot everyone for help. and thanks for the patience.

Not a genius, just a careful man page reader!

---------- Post updated at 07:50 PM ---------- Previous update was at 07:39 PM ----------

Rats! Found a minor error: it sends out alerts AND warnings if REMAINS is negative. Pls correct:

[ $REMAINS -le 0 ] && echo "..." || { [ $REMAINS -le 7 ] && echo "..."; } done
1 Like