closeit: A Login Management Tool
Tom Everson
Most system administrators work hard at keeping their
systems up and
available to users as much as possible. But there are
times when you
need to do just the opposite -- keep certain users off
the system.
I help support a UNIX system where I occasionally need
to bar users
from logging in for a short time while I make changes
to their applications
or profiles. I developed a quick script to let me do
this. I found
myself using the script often and eventually expanded
it into a more
finished utility.
It's easy enough to disable all logins on a UNIX system
or lock any
individual account, but I needed more. I wanted to be
able to disable
all accounts, just one account, or any list of accounts.
And I needed
to be able to reopen them quickly and selectively. Also,
while I wanted
to keep the users out, it was essential that I still
be able to log
in as those users or su to their accounts, since certain
things
could only be tested with their identities. Finally,
the system is
a commercial one, and the users pay for their access.
They accept
the need for occasional, and sometimes unexpected, maintenance,
but
appreciate knowing when they can expect to get access
again. To preserve
good customer relations I needed a way to tell them.
I call my script closeit, and it meets all of the above
requirements.
It comes in two parts. The first and largest (see Listing
1) is comprised
of routines that let you identify the accounts you want
to disable,
compose your message to the user, and flag the accounts
as closed.
The second part (see Listing 2) is an addition to the
system's /etc/profile
file, which is sourced by the login shell for every
user login. This
part detects the flagged condition of the account, displays
the message
to the user, and aborts the login. It does not kill
an active user
session or abort current user processes; it simply prevents
future
logins until you reopen the account.
Script Functions
I wrote the script in the Korn Shell for a System V
UNIX, but it could
be implemented, with minor changes, in any UNIX environment.
The main
script begins with some variable assignments, proceeds
to function
definitions and then to the main line of its processing.
Taken in
order of appearance, the separate functions do the following.
Usage() uses a fairly standard format for informing
the user
of the purpose of the script and its proper syntax.
Getusers() looks at the /etc/passwd file and picks
out the lines that end in sh, i.e., the "real"
login
accounts. It sets the fields of each successive line
to the shell's
positional parameters, and from these selects the user
login name
and home directory. It puts these in a file ($DIRECLIST),
with each line of the form
username home_directory
I explicitly exclude root and an account called sysnews.
You may have special exclusions you want to make at
your site. If
you prefer, you can make this user list into a static
file outside
the script, but I want to know it's up to date, so I
build it each
time the script runs. Doing this also reduces the chance
that it will
become corrupted or "inadvertently" modified.
MakeMessage() lets you compose the message the debarred
user
will see. I almost always use the same format, which
tells the user
when the account can be expected to reopen. This default
message is
provided in the script. However, you can edit this into
a new message
with your favorite editor ($C_EDITOR).
WriteMessage() writes your message to a file called
.closelogin
(or a name of your choice) in the user's home directory;
the file
serves both to contain the message and to flag the account
as closed.
Before writing it it bundles the text into a here document
using the
shell's "<<" redirection and passes
this to a subshell (lines
177-178). This has the effect of causing any shell variables
that
you include in the message to be evaluated. In my default
message
I use the variables $USERNAME and $OPENTIME. You can
edit these or add others of your own. Once again, I
create my message
in the script, rather than storing it in an external
file. Then I
don't worry that it will become corrupted or be modified
by sporting
types who might get access to it (at least I don't worry
as much).
ListWho() lists all the accounts that are closed at
the time
you invoke it. This is a standalone function and is
also tacked on
to other operations as additional confirmation of what
you have done.
Finally, GoAhead() is a routine that gives the user
a chance
to back out. If the user continues, the routine checks
to see if the
$OPENTIME variable is being used in the user message
and,
if so, prompts for the reopening time.
The Main Program
The main line of programming is divided into two parts,
depending
on the name it is invoked under, which can be "closeit"
or
"openit." The closeit section validates the
syntax
of the command and uses the Usage() routine if it needs
to.
It then calls Getuser() and MakeMessage() and gives
you a chance to back out. If your command was "closeit
all,"
it then closes each account identified by GetUsers().
If you
just entered "closeit," it will take user
names one at a time
from standard input, confirming each closure or giving
you a failure
message. Press your EOF key (usually Ctrl-D) when you
are done.
You can also invoke the script as "openit."
This line of the
program lets you reopen all accounts or a list of accounts
that you
enter at standard input. It reports the results for
each one: either
the account reopened successfully, failed to do so,
or wasn't closed
in the first place. It will take input from standard
input unless
you invoke it as "openit all." In that case
the exec
< $DIRECLIST on line 285 will redirect input from
the $DIRECLIST
file and process each user account in turn.
When you give account names to this script, it will
act on the first
name in $DIRECLIST whose leading characters match what
you
give it. You don't need to enter the whole account name,
except to
avoid ambiguity; it will report back the actual account
name that
it is acting on.
Setting Up /etc/profile
The portion of this system that goes in /etc/profile
is in
Listing 2. This should be at the head of your /etc/profile
file. It looks at the home directory of the user logging
in to see
if .closelogin exists there. If it does, it displays
the message
and waits for user input. Your message should request
that the user
hit the Enter key. When input is received, the routine
looks to see
if what was entered matches a special string, which
it gets from another
file. If it does, login proceeds normally. Otherwise
a no-op program
(/bin/false) is exec'd by the shell, aborting the user's
login.
System Administration Considerations
Using a password in closeit lets you log in to the account
even when the user can't. Note, however, that each user
must be able
to read /etc/profile; otherwise, /etc/profile is not
used at login, and your efforts here will have no effect.
If /etc/profile
is readable by users, however, they will know where
you store your
secret password (the file /nowrite/.secret in my example),
and this file must also be readable by them. In my case
this is
not a problem, since users do not have access to a shell.
If your users do, they could easily find out what the
password is
and circumvent the system. You may want to make this
less likely by
writing a new password to the file just before using
closeit.
You could add this function to the script if you wish.
The file that
keeps the password is readable by all, but should be
writable only
by root.
If you want more protection you can consider using crypt
or
some other encryption mechanism to make the procedure
more like a
real password. But keep in mind that closeit is not
a security
system, and you shouldn't try to make it one. Don't
use anyone's real
password with closeit, especially not root's. It will
not
be secure. A clever user could devise other ways to
circumvent the
system, such as starting a daemon to clear the flag
file from his
home directory every few minutes. You could change the
name of that
file from time to time, but if the user is that uncooperative,
look
for other methods to control access.
The message you display to the user should also be created
in a directory
writable only by root and should itself be writable
only by root.
This will help ensure that no one except you can modify
or delete
it. closeit should only be executable by root; it tests
for
this at the head of the script.
When you use closeit, be sure you know how the account
will
be opened again if you should be unable to reopen it
or forget to
do so. Before using closeit you can pass
openit all >&-
to the UNIX at command, to ensure that everything
gets opened up in, say, an hour, even if you aren't
there to do it.
And, of course, be sure that closeit can never close
root's
account. If at some point you can't use openit for any
reason,
use the find command to find any flag files in users'
home
directories and remove them.
To use closeit, put it somewhere in root's path and
link it
to openit in the same directory. Make sure only root
can read,
write, or execute it. Make changes to the variables
at the head of
the script if you need to, and set up your "password"
file
in the same root-writable-only directory where you will
create your
message. Test closeit first on special accounts you
set up
for that purpose. Put it into production only when you
are satisfied
that it works as you expect it to.
About the Author
Tom Everson is a partner in Nova Business Systems,
Inc., a
software developer and UNIX services company in Portland,
Oregon.
He got his start in computing with IBM in 1973 (when
a word-processing
program required a cruiser class mainframe), after receiving
a PhD
in Philosophy from the Johns Hopkins University. He
has worked with
PC-based UNIX systems for the last five years.
|