Timing Out Idle Users
Larry Reznick
All users should logout when they leave their workstations
for an
extended period of time or when they leave at the end
of the day.
A workstation with a user logged in but not present
is an invitation
to a security breach. System administrators don't have
the time to
watch what everybody is doing and finger-wag when users
forget to
logout. Besides, after a certain amount of finger-wagging,
the system
administrator loses credibility with the users.
The system knows how active the user's terminal is because
the system
has to keep up with all of that activity. You could
make the system
monitor the users' activities and, if one of the terminals
is idle
for longer than a specific time, force the user to logout.
SCO UNIX
has a special program for this, named idleout. SVR4
has no
such program, but a simple shell script can do the same
work.
SCO's idleout
Run the idleout(ADM) program only once. idleout monitors
user activity on all tty lines to see when a line has
been
inactive for longer than the time you specified. Name
the idle time
in either minutes or hours:minutes. For instance, use
idleout 20
to force a logout on any tty line inactive for
more than 20 minutes, and use
idleout 1:30
to force a logout on any line inactive for more than
an hour and a half. You could also use
idleout 90
to force that same hour-and-a-half timeout. If you don't
provide an idle time threshold, idleout uses the default
time
in the textfile /etc/default/idleout. The textfile sets
an
environment variable named IDLETIME. The default setting
is
120, or two hours.
If you execute idleout manually, as root of course,
it forks
a shell, detaches from the shell you launched it from,
and sleeps
for 60 seconds. When it wakes up, it looks at the tty
lines
to see if any of them have been idle for longer than
the interval
you named. If so, it kills the offending process.
The idleout program never finishes, even if it kills
all idle
tty processes. You can't just turn it off. It keeps
watching
the tty processes, checking them once a minute and killing
any subsequent idle tty processes that come along. If
you,
as root, want to kill the idleout process, you must
check the
process status list using ps(1) and find the "sleep
60"
process. You could track that process back to its parent
process ID
(ppid) and find the shell, usually identified as "-sh"
in the ps list. Then, kill the shell and the sleep process.
But rarely would you run idleout manually. Instead,
start it
automatically every time you boot UNIX.
Bootstrap idleout
Monitor the tty idle time with every reboot. Install
idleout
in the bootstrap rc files. The /etc/rc* files are shell
scripts containing the run commands executed with every
system startup
or shutdown. There is a primary file named /etc/rc;
other files
are named for the various system run levels:
/etc/rc0 Init level 0 for system shutdown
/etc/rc1 Init level 1 for single user mode
/etc/rc2 Init level 2 for multiuser mode
/etc/rc3 Init level 3 for networked multiuser mode
These scripts refer to the contents of one of the following
directories:
/etc/rc.d Contains common run command directories
/etc/rc0.d Contains init level 0 run commands
/etc/rc1.d Contains init level 1 run commands
/etc/rc2.d Contains init level 2 run commands
/etc/rc3.d Contains init level 3 run commands.
The directories named after the init levels represent
the usual way UNIX SVR4 handles the run commands. The
file names in
those directories begin with an S or a K to identify
scripts that start or kill processes. A two-digit sequence
number
that follows the S or K identifies which file in the
S or K group must run first. The rest of the file name
generally identifies what the script does. For example,
Table 1 shows
a sample rc2.d directory's contents, and Table 2 shows
a sample
rc0.d directory's contents.
Another method for handling the run commands uses the
rc.d directory.
The rc.d directory contains subdirectories named with
single
digits from 0 to 9, representing the execution sequence
of all scripts found in those directories. Put a script
in that directory
to execute it during system startup or shutdown.
Figure out which method your system uses and change
either /etc/rc.d/8/userdef
or /etc/rc2.d/S88USRDEFINE to start idleout with the
number of minutes you want to allow.
killidle Script
If you don't have SCO UNIX, you can still kill idle
tty processes
with a shell script. The script combines who(1) and
awk(1).
The who command identifies the ttys in use and their
idle times. The awk script analyzes who's output, decides
which ttys are too old, and puts them out of our misery.
Listing
1 shows the killidle script.
The colon in the first line forces a Bourne shell to
execute the script.
The first code line,
IDLEOUT=${1:-20}
sets the IDLEOUT variable to either the value in killidle's
first command line argument or 20. Instead of using
if tests to see
whether there is a $1 argument, I use the shell's own
variable
substitution mechanism. This syntax says that if $1
has a value,
use it. If there is no $1 or it has a zero value, use
the 20.
Whichever is selected, assign the result to the variable
IDLEOUT.
The next step avoids trouble if the user gives a negative
time. I
chose to set IDLEOUT to the default 20 minutes if a
negative
time is discovered. As another approach, you might use
expr
to change the value's sign.
With IDLEOUT valid, the script uses the who command
to get the user list. The -u option limits the output
to only
those people currently logged in. It also shows the
idle time for
each of those logins. The code pipes the output to the
awk
program to process that idle time.
The awk script doesn't contain a pattern, just an action.
To
keep things easy, it extracts the column values needed
for analysis
and output: the user's name, the terminal ID, the idle
time, and the
login's process ID.
If a user has pressed a key at the tty within the last
minute,
that tty's idle column shows a period. The script's
first if test
eliminates that condition from the script's concern.
Given some idle
time amount, who shows it in the format hours:minutes.
The
hours may be set to zero. Only the minutes are needed
for this script.
An easy way to isolate the minutes is to treat the colon
as a field
separator and split the idle time into an array. Awk's
split
function does that work. split's first parameter is
the string
to be split. The second is the array to put the split
results into.
The third is the field separator. split creates the
space for
the second parameter if the array doesn't already exist.
awk
subscripts start at 1, so idletime[1] holds the hours
and idletime[2] holds the minutes. If the minutes idle
figure
is greater than that specified in the IDLEOUT variable,
it
is time to kill the process.
Assuming that a warning message for killed processes
might be helpful,
a print statement identifies which user on which terminal
is
being logged out, how long the terminal was idle, and
the pid for
debugging the script. This output not only aids in debugging,
but
also allows you to build a log file from the output
to identify users
who tend to leave the terminal idle long enough to create
potential
security breaches.
Having identified which pid has been idle too long,
the awk
script uses its system function to send a kill signal
to the pid.
The signal number 9 is the kill signal that can't be
ignored
by an application and can't be intercepted to make it
act differently.
The only process that can withstand a signal 9 is a
process
trapped in the kernel. Processes trapped in the kernel
are often known
as zombie processes, because they live on after being
killed --
they just don't want to die.
Sending the signal 9 is an extreme measure. Its main
advantage
is that it can't be intercepted and it can't be ignored.
When a process
receives this signal, no matter what it was doing, it
ceases. That
means any cleanup actions it should have done won't
get done. You
might find it nicer to send a signal 15 instead. A process
can intercept signal 15 and trigger its cleanup operations.
Another nice signal to send is 1, the hangup signal.
The terminal
will think there's no connection any more and should
terminate itself.
These are weaker signals, though, and a process can
choose to ignore
them. The signal 9 gets through no matter what, except
for
those pesky zombies.
At the end of the script is the funny-looking syntax,
IDLEOUT=$IDLEOUT
Notice that this appears on the awk command line
after the script has finished. awk doesn't use the shell's
environment variables directly. Getting a shell variable
or any other
value into an awk variable requires the use of an assignment
on awk's command line. I chose to use the same name
as the
shell variable to keep references simple.
Several worthwhile changes would include:
accommodating the keyword old in the who's
idle column for idle times older than 24 hours
handling the full hours:minutes format for a large
number of minutes requested, such as 120, or for an
hours:minutes
request format, such as 1:30
working from a table identifying certain users with
different idle times, allowing the fine-tuning of the
idle times for
certain special users.
Running killidle
You can run killidle any time manually. Just type
killidle
to use the default 20-minute idle time, or type
killidle minutes
where minutes is any positive number for the idle
minutes allowed.
The script looks through the who output and kills any
logins
idle for too long. If you're not root, though, you can
only kill your
own processes. The output messages tell you who else
has been idle
too long, but you can't do anything about it unless
you're root.
Of course, running killidle manually is a bit silly.
You'd
have to remember to run it periodically, and it wouldn't
be much of
a security monitor run if you forget to run it. If you
run it from
root's crontab, you can set it to run around the clock.
As
a root cron job, it has root's privileges, so it could
kill
other login processes without restriction. Finally,
since all cron
command output is automatically captured and mailed
to the user whose
crontab is executing, root would receive a mail message
containing
all of the timeout warnings for each killidle run.
The simplest way to put killidle in the root crontab
is to use the
line
* * * * * /usr/local/bin/killidle
assuming that /usr/local/bin is the directory
holding the killidle script. The five asterisks tell
cron
to run killidle once a minute every minute of every
day cron
is up. That could be extreme, but it would get the job
done. To reduce
the script's impact on system resources, run it only
a few times an
hour.
For example, the crontab line
0,15,30,45 * * * * /usr/local/bin/killidle
checks for idle ttys every 15 minutes. Considering
that the script's default is 20 minutes idle, the worst
case idle
time would be 29 minutes for a login started one minute
after cron's
last run. Fourteen minutes later cron runs again, but
the tty
has run for only 14 minutes, which is less than killidle's
20, so killidle leaves it alone. Fifteen minutes later,
a total
of 29 minutes, cron runs killidle again and catches
the old tty.
You could run killidle with a 15-minute option. If you
do that,
however, the same timing problem appears. A login that
is only 14
minutes old when cron runs killidle would survive this
run. killidle would eliminate the login 15 minutes later
if
the login were still idle. That login would have survived
for a total
of 29 minutes.
You could set killidle to test for an interval less
than the
interval cron runs. For example,
0,20,40 * * * * /usr/local/bin/killidle 19
kills anything 19 minutes old or older. Because cron
runs every 20 minutes, a login that started one minute
after the last
cron run is 19 minutes old, qualifying for the kill.
You're
still not eliminating the problem completely. If a tty
starts
two minutes after the cron check, it will be only 18
minutes old,
and thus will pass the 19-minute test. Another 20 minutes
passes before
the cron job starts killidle again, allowing a total
of 38 minutes of idle time.
How long you let idle logins run depends on what you're
willing to
live with. On the one hand, it doesn't take very long
for someone
to use an account left idle to break into the system.
On the other
hand, you're not going to please everybody. Some programmers,
for
instance, work at their desks near the terminal and
are not on the
terminal every moment. It isn't nice to log them out
while they've
got important facts on the screen and are busy looking
through a listing.
This is where a table of user idle times that can be
used by killidle
comes in handy. You must find a balance between security
and people
who need their temporary login environment changes and
screen displays
to remain intact when they're only away from the terminal
for a moment.
You decide how long a moment lasts.
About the Author
Larry Reznick has been programming professionally since
1978. He is currently
working on systems programming in UNIX and DOS. He teaches
C language courses
at American River College in Sacramento. He can be reached
via email at:
rezbook!reznick@csusac.ecs.csus.edu.
|