Saturday, December 1, 2007

Disabling and Removing Old User Accounts

Depending upon the size of your network, keeping track of user accounts on all of your Unix and Linux boxes can range from fairly simple to unbelievably hard to keep track of. I've noticed on the internet boards that lots of people are looking for the "one" way to semi-automate determining if user accounts need to be disabled, or if they're so old they need to be deleted.

I'm not a believer in the "one" solution theory. One of the things I love about Linux and Unix is, depending on how creative you're willing to be, there are probably 50 or more ways to do anything.

In any event I started looking at this problem, and noticed that almost everyone was using the "finger," "logins" or "last" commands to figure out how long it had been since someone logged in. In some extreme cases, I've even found examples where folks "pack" and "unpack" /var/adm/wtmpx with Perl to get the information. So, I naturally thought: Is it possible to do this without using the system's login-logging facilities?

The more I thought about this, the more I came to understand that both methods had their virtues and their faults. For instance, if /var/adm/wtmpx is missing, a lot of regular system accounting programs won't work. If you corrupt that file and remove /var/adm/lastlog (I'm using Solaris as my model - substitute file locations and names as necessary), "logins," "finger" and "last" don't report correctly unless they're reporting an error. The alternative approach also has its flaws. If, for instance, a user utilizes SSH to run a single command from a remote host using key-based passworless login, that shell won't be interactive and it won't update any shell history files!

Tackling this problem will take more than one post, so we'll start out with the core; treat this as a sort of tutorial on the phases that you go through from scratch to a decent script. Our assumption here will be that we have no access to any of the regular utilities like "last," "finger" and "logins." We'll use Perl to check out all users' .sh_history or .bash_history files (just covering sh, ksh and bash for now) and use the "stat" and "time" functions to determine how long it's been since the user last logged in.

Here's the core of the code, to get started. I've written it so it can be run on its own for now:


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/perl

#
# 2007 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
#
# We don't need to check for errors on the command line since
# we won't be accepting any arguments
#

$today=time();

@user_dirs=`awk -F":" '{print \$6}' /etc/passwd`;
foreach $history_file (@user_dirs) {
chomp($history_file);
print "${history_file}: ";
if ( -f "$history_file/.sh_history" ) {
@file_dates=stat("$history_file/.sh_history");
$sh_file_date=$file_dates[8];
} else {
$sh_file_date=0;
}
if ( -f "$history_file/.bash_history" ) {
@file_dates=stat("$history_file/.sh_history");
$bash_file_date=$file_dates[8];
} else {
$bash_file_date=0;
}
if ( $sh_file_date > $bash_file_date ) {
$file_date = $sh_file_date;
} elsif ( $bash_file_date > $sh_file_date ) {
$file_date = $bash_file_date;
} else {
print "Cannot find .bash_history or .sh_history for $history_file. Moving on!\n";
next;
}
$difference=$today - $file_date;
if ( $difference >= 3888000 && $difference < 7776000 ) {
print "User needs to be disabled- ";
print "45 days old or older, but under 90\n";
# YOUR CUSTOM CODE HERE - "passwd -l" would probably suffice"
} elsif ( $difference >= 7776000 ) {
print "User needs to be deleted - ";
print "90 days old or older\n";
# YOUR CUSTOM CODE HERE -
} else {
print "OK\n";
}
}


Within the next post or two, we'll follow up on this, so that we can add functionality to check system files, using system utilities, to verify that our initial information is correct. It's possible that someone just "touch"ed a user's history file and that access time information isn't necessarily correct. Also, as we noted above, folks can be using the host remotely, in a non-interactive shell, and never alter the access time of their history file.

For the time being, this code should be a good start, and something to think about and improve upon. Until next time :)

, Mike