Cover V03, I05
Article
Listing 1
Listing 2
Listing 3
Listing 4

sep94.tar


Listing 4: chkaging script

#!/bin/sh
#
#       chkaging
#
#       Report current password aging settings from the
#       /etc/passwd file.  If -a is used, translates an
#       aging value into a set of weeks numbers.  If -d
#       is used, translates a set of weeks number into an
#       aging value.
#
#       Each aging value is formed from a set of base-64
#       numbers representing:
#
#       1.      the number of weeks maximum before a password
#               change is forced by the system,
#       2.      the number of weeks minimum before a new
#               password may be changed by the user, and
#       3.      the number of weeks elapsed since week 0, 1970,
#               when the password was last changed.
#
#       The base-64 numbers are formed from the set of
#       alphanumeric characters and two punctuation marks.
#       The aging value follows the password, separated by a
#       comma.  If the comma isn't there, aging is
#       deactiviated for that account.  The weeks elapsed
#       string may be missing, which corresponds to 0 weeks
#       elapsed.  That should force the password to change
#       with the next login.  According to passwd(4), the ..
#       (00) should always force a new password on the next
#       login.
#
#       Copyright 1994, Lawrence S Reznick

#       HISTORY
#
# 94Apr19       LSR
#       The base-64 digits are output in reverse place-value
#       order.  That is, if the digits should have been Hm
#       (H in the 64s place and m in the 1s place), the
#       password aging system will output it as mH.
#       Corrected the weeknum() & code_val() functions to
#       handle it that way.
#
#       Also, the NOW_WEEKS value is off because a 365-day
#       year is really 52 weeks plus 1 day.  expr doesn't
#       work with floating point, so the simplest solution

#       is to add 1 day for every 7 years since the epoch.

PW_FILE=/etc/passwd                     # Point to passwd file

#
# NOW_WEEKS holds the number of weeks elapsed since
# 1970: aging epoch
#

#NOW_WEEKS=`expr \( \`date '+%Y'\` - 1970 \) \* 52 + \`date '+%U'\``
NOW_WEEKS=`expr \`date '+%Y'\` - 1970`
NOW_WEEKS=`expr $NOW_WEEKS \* 52 + \`date '+%U'\` + \( $NOW_WEEKS / 7 \)`

#
# Given an encoded aging weeks value, return the
# numeric equivalent
#

DIGITS="./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&q
ot;

weeknum ()
{
echo $1 |
awk '{
#               for (i = 1; i <= length; i++) {
i = length;
do {
sum *= 64;
sum += index(digits, substr($0, i, 1)) - 1;
} while (--i);
}
END {
print sum;
}' digits=$DIGITS
}

#
# Given a comma-separated number list (max,min,elapsed),
# output aging code
#

code_val ()
{
echo $1 |
awk -F, '
$1 < 0 || $1 >= length(digits) {
print "Max out of range! 0 <= Max < ", length(digits);
exit;
}

$2 < 0 || $2 >= length(digits) {
print "Min out of range! 0 <= Min < ", length(digits);
exit;
}

{
code = substr(digits, $1 + 1, 1);
code = code substr(digits, $2 + 1, 1);

elapsed = $3;
do {
quot = int(elapsed / 64);
remn = elapsed % 64;
#                               base64 = substr(digits, remn + 1, 1) base64;
base64 = base64 substr(digits, remn + 1, 1);
elapsed = quot;
} while (elapsed >= 64);
#                       base64 = substr(digits, quot + 1, 1) base64;
base64 = base64 substr(digits, quot + 1, 1);

code = code base64;
print code;
}

' digits=$DIGITS
}

#
# Given a full aging value (no comma), display its
# numeric values
#

aging_val ()
{
code = $1
if [ `expr length $code` -eq 2 ]
then
code=${code}..
else if [ `expr length $code` -lt 3 ]
then
echo Invalid aging code \"$code\"
return
fi
fi

MAX=`expr $code : '\(.\)'`              # Grab max weeks
MIN=`expr $code : '.\(.\)'`             # Grab min weeks
ELAPSED=`expr $code : '..\(.*\)'`       # Grab weeks since epoch

echo "aging set to `weeknum $MAX` weeks max, \c"
echo "`weeknum $MIN` weeks min, \c"
echo "last changed `expr $NOW_WEEKS - \`weeknum $ELAPSED\`` weeks ago."
}

#
# Collect the 1st 2 fields of the password file & parse
# the aging info
#

show_aging ()
{
INFO=`
cut -f1-2 -d: $PW_FILE |        # Extract name & passwd fields
egrep -v '\*'`                  # Ignore disabled accounts

for n in $INFO
do
#               echo "`echo $n | cut -d: -f1`\t\c"

oIFS="$IFS"
IFS=","                 # Use field sep to parse aging
set $n                  # $2 has the aging field value
IFS="$oIFS"

if [ -z "$2" ]
then
#                       echo aging inactive!
NOAGING="$NOAGING `echo $n | cut -d: -f1`"
else
echo "`echo $n | cut -d: -f1`\t\c"
aging_val $2
fi
done

if [ -n "$NOAGING" ]
then
echo "\rAging inactive ($PWFILE order):"
echo $NOAGING | tr ' ' '\012' | pr -t -6
fi
}

#
# Main processing
#

if [ $# -eq 0 ]                         # Default execution
then
show_aging                      # Show passwd file's aging settings
exit 0
fi

case $1 in
-a)                             # Turn aging value into weeks number
shift                   # Skip to the aging value
for v
do
echo $v = `aging_val $v`
done
;;

-d)                             # Turn weeks numbers into aging value
shift                   # Skip to the week number sets
for v
do
echo $v = `code_val $v`
done
;;

*)                              # Tell how program works
echo "Usage:\t`basename $0` [-a aging_codes] [-d week_nums]\n"
echo "No arg:\tTells aging settings for "$PW_FILE" file."
echo "-a arg:\tTurns aging codes into week numbers."
echo "-d arg:\tTurns max,min,elapsed num sets into aging codes."
;;
esac
exit 0