Archiving
Korn Shell History Files
John Spurgeon and Ed Schaefer
Shell history gives users the ability to manipulate commands previously
entered at the command line. It is not an auditing tool. Nevertheless,
systems administrators often resort to looking at shell history
files to trace user activity. When used as an audit utility, shell
history has serious drawbacks. For example:
- Once a shell history file contains the maximum number of commands,
old commands are removed as new ones are entered.
- History files may be modified by a user who is trying to cover
his tracks.
- Timestamps are not available to determine when a command was
executed.
- History files are typically stored in the user's home directories
making it difficult to process the information.
- It can be tricky to tell who executed certain commands if someone
used su to become another user, such as root.
In this column, we present a collection of shell scripts that
attempts to mitigate some of these drawbacks. You shouldn't rely
on our solution to catch users bent on mischief. Tools like Solaris's
Basic Security Module (BSM) are better suited for that type of monitoring.
However, you might find that our scripts significantly increase
the value of your shell history files. Also, the resulting information
might be easier to work with than the massive amounts of data that
can be generated by a system like BSM.
Program Design
The Korn shell provides the ability to retrieve a user's command
history, which is stored in a file identified by the HISTFILE environment
variable. The number of commands stored in this file is defined
by the value of the variable HISTSIZE. Other shells may provide
similar shell history capabilities, but our focus is limited to
the Korn shell.
When a user logs in, we set her HISTFILE and HISTSIZE variables
and make them read-only. When the user logs out, the contents of
her history file is copied to a file in an archive directory with
limited access. We also set up a cron job that periodically inserts
timestamps into open shell history files so the time a command was
executed can be estimated.
Although the design is simple, the actual implementation is somewhat
complex. The rest of the column describes the environment and the
script listings.
The Environment
Our last column, "Implementing Standard Login Scripts", discussed
customizing login scripts for interactive shells. You may want to
review this discussion if you are not familiar with the way the
Korn shell handles login scripts. On our systems, ENVFILE is set
to /etc/.kshrc. This script (Listing 1):
- Calls /etc/setenv_user (Listing 2), which sets several variables
including REAL_USER and TERM_USER, which are relevant to the way
we manage history files.
- Calls /opt/logins/bin/sh_history.initialize script (Listing
3), which sets HISTFILE and HISTSIZE.
- Sets the logout script to /etc/.logout (Listing 4).
A repository exists for both open and archived history files.
The location of the repository is defined by a read-only environmental
variable, HISTORY_DIR, which is set in all the scripts by sourcing
the sh_history.globals file (Listing 5). We've chosen the directory
/var/sandbox/.sh_history. The following directories under $HISTORY_DIR
must also exist with the following permissions:
drwxr-x--- 2 root people 17920 Dec 20 14:56 archive
drwxr-x--- 2 root people 2048 Dec 20 15:36 open
drwx------ 2 root people 1024 Dec 20 15:36 pids
sudo Setup
On our systems, every user with command-line access belongs
to a group called people. We use this group in our sudoers file
to allow users to execute certain commands as root. Within the
sudoers file, we set the following command alias and user specification:
Cmnd_Alias PEOPLE_COMMANDS = \
/opt/logins/bin/sh_history.touch, \
/opt/logins/bin/sh_history.clock_in, \
/opt/logins/bin/sh_history.wipe, \
/opt/logins/bin/sh_history.list
%people ALL = NOPASSWD: PEOPLE_COMMANDS
Explaining sudo is beyond the scope of this article. For more
information about sudo, see:
http://www.gratisoft.us/sudo/sudo.html
Sourcing the sh_history.initialize Script
When a user logs in, the program /etc/.kshrc calls sh_history.initialize.
Sourcing this script sets the values of HISTFILE and HISTSIZE.
The HISTFILE variable is a file name composed of strings representing
the date, process id, and user ids. The full pathname of a typical
history file looks like this:
/var/sandbox/.sh_history/open/051221-095155-24533.eds-as-eds
Someone who uses su to become root might generate a history file
like this:
/var/sandbox/.sh_history/open/051221-095155-24533.johns-as-root
Once the history file name is established, a call to the sh_history.touch
script (Listing 6) ensures that the history file exists and sets
the owner, group, and file permissions. Then the date, user ids,
and the output of the command /usr/bin/who -m | cut -f2
is written to the history file.
Finally, sh_history.initialize calls the sh_history.clock_in
script (Listing 7), which "clocks in" the history file. History
files that are clocked in are in use by their respective user
id. Files not in use can be archived.
Timestamping History Files
Periodically, a root cron job executes the /opt/logins/bin/sh_history.timestamp
script (Listing 8), which appends a string containing the current
date and time to the end of each open history file. For example,
the following entry in root's crontab file calls sh_history.timestamp
every 30 minutes:
0,30 * * * * /opt/cron/root/sh_history.timestamp
The timestamp script obtains a list of active shell history files
by executing the /opt/logins/bin/sh_history.list script (Listing
9). The sh_history.list script returns the list by simply executing
a find on the $HISTORY_DIR/open directory.
If any files are returned, this string:
"# Open: $(date)\n\0000\c"
is appended to each file. On our systems, the Korn shell includes
some special formatting characters when appending lines to a history
file. Figuring out what the shell was doing required a bit of
ASCII character sleuthing. We eventually determined how to mimic
the behavior of the shell by adding the string "\n0000\c" to the
end of a line. This improves the readability of the history file
when it is being used for its intended purpose.
Archiving the Shell History Files
The /opt/logins/bin/sh_history.wipe script (Listing 10) archives
history files that are no longer in use. It executes when called
by the sh_history.timestamp cron job. The script is also called
by /etc/.logout, which executes each time a user logs out.
The sh_history.wipe script generates a list of process ids
using the names of the files in the $HISTORY_DIR/pid directory.
The ps -p command determines whether the process is active;
if not, the corresponding file (a hard link) in the pid directory
is deleted. Deleting the file reduces the number of links --
presumably to one -- which triggers the sh_history.wipe script
to archive the history file.
Note that when a user is logging out, the sh_history.wipe
script sees that that the pid for that user's shell is still
active, so the user's shell history file is not archived until
the next time sh_history.wipe executes.
The script next finds all the files in the $HISTORY_DIR/open
directory. Any file with only one link is ready to be archived.
When a history file is archived, the contents of the file
are copied to a new file in the directory $HISTORY_DIR/archive.
The unreadable formatting characters are removed, and another
string is appended to the file indicating when the file was
archived. Also, the proper ownership, group, and permissions
are set on the archive file.
Although the lines are commented out, if a user has logged
in as root, we generate an alert. For more information about
this technique, see "Managing Enterprise Alerts", Sys Admin,
April 2004.
What's in the Tarball?
The scripts explained in this article are located in the /etc
and opt/logins/bin directories. The tarball is created with
relative paths to facilitate placement in your environment.
If you choose not to use /opt/logins/bin directory as your script
repository, you will need to update the scripts accordingly.
References
BSM Analyzer, Sun Help Desk, John Richardson, 10/20/2003 --
http://www.sunhelpdesk.com/jmr_BSM_Analyzer.htm
John Spurgeon is a software developer and systems administrator
for Intel's Factory Information Control Systems, IFICS, in Aloha,
Oregon. Outside of work, he enjoys turfgrass management, triathlons,
ultra-marathon cycling, and spending time with his family.
Ed Schaefer is a frequent contributor to Sys Admin. He
is a software developer and DBA for Intel's Factory Information
Control Systems, IFICS, in Aloha, Oregon. Ed also hosts the
monthly Shell Corner column on UnixReview.com. He can be reached
at: shellcorner@comcast.net. |