KSH93 Auditing and Accounting

Korn Shell 93 (ksh93) is the only UNIX or GNU/Linux shell that I am aware of that, with proper setup, supports a modicum of per-user accounting and auditing.  This post attempts to explain these facilities and show you how to access and manipulate the resulting accounting and auditing records.

Per-user accounting has been a feature of ksh93 since the earliest days of this shell.  It is a fairly primative facility which writes out an (undocumented) record for each user command that is executed.

An auditing facility was added in July 2008.  This is somewhat more sophisticated than the accounting facility in that it is configurable and writes out a more detailed record either locally or to a remote system for each user command that is executed.  This facility can be used to monitor, track, record, and audit the activities of one or more users on an system, including system administrators.

Both facilities only work for interactive users.  Neither facility is enabled by default.  In fact, you have to go into the ksh93 source code, modify the main Makefile to enable certain compile time options, and then recompile the sources.  This is not a trivial exercise in many cases as rebuilding ksh93 also requires that you to rebuild a number of libraries.  Source code is available at the AT&T Research AST software download site.



To build ksh93 with these facilities enabled, you must build the ksh93 executable using either the ast-base or ast-open package together with the INIT package.  Just as the shared libraries are not built if you use the ast-ksh package, neither are the accounting and auditing facilities.  I have never investigated why this is so, but I am sure that Glenn Fowler and David Korn have their reasons.

You need to modify the compile time options in ../src/cmd/ksh93/Makefile as follows to enable one or both facilities.
SHOPT_ACCT ==     1        /* accounting */
SHOPT_ACCTFILE == 1 /* per user accounting info */
SHOPT_AUDIT == 1 /* auditing */
SHOPT_AUDITFILE == "/etc/ksh_audit" /* auditing file */
After you have recompiled the sources, the new ksh executable is located in ../arch/..../bin/ subdirectory.  To see what options have actually been compiled into a particular executable, just print out the shell version string.
bash-3.2$ pwd
/home/fpm/ksh/build/arch/linux.i386-64/bin
bash-3.2$ ls -al ksh
-rwxr-xr-x 1 fpm fpm 1356931 2008-12-26 16:31 ksh
bash-3.2$ ./ksh
$ echo ${.sh.version}
Version AJLM 93t+ 2008-12-10
$ echo $KSH_VERSION
Version AJLM 93t+ 2008-12-10
The option string AJLM means that (A) auditing is enabled, (J) one SIGCHLD trap per completed job is supported, (L) per-user accounting is supported, and (M) multibyte characters are supported.

Per-user accounting is enabled using the SHACCT environmental variable.  To turn on per-user accounting, simply set SHACCT to the name of the file where you wish to store the accounting records.
export SHACCT="/tmp/ksh_acctfile"
Here is part of the resulting file. Note that the time is stored as hexadecimal seconds since the Epoch.
$ cat /tmp/ksh_acctfile
echo ${.sh.version} fpm 495990d8
pwd fpm 495990da
id fpm 495990dd
date fpm 495990e3
exit fpm 495990e5
The following shell script can be used to access the records in this file and output them in a more useful format.
#!/bin/ksh93
ACCTFILE="/tmp/ksh_acctfile"

printf "DATE TIME LOGIN COMMAMD\n\n"

# set IFS to TAB only
while IFS=" " read cmdstr name hexseconds
do
longsecs=$(printf "%ld" "0x${hexseconds}")
timestr=$(printf "%(%Y-%m-%d %H:%M:%S)T" "#${longsecs}")
print $timestr, $name, "$cmdstr"
done < $ACCTFILE
Invoking this script gives the following output for the above accounting records.
$ ./parse_acctfile
DATE TIME LOGIN COMMAMD

2008-12-29 22:09:12, fpm, echo ${.sh.version}
2008-12-29 22:09:14, fpm, pwd
2008-12-29 22:09:17, fpm, id
2008-12-29 22:09:23, fpm, date
2008-12-29 22:09:25, fpm, exit
Next we turn our attention to the auditing facility.  In addition to rebuilding ksh93 with the SHOPT_AUDIT option, you must create an audit configuration file on each system to tell ksh93 where to store the audit records and to specify which users are to be audited.  The default location for the configuration file is /etc/ksh_audit but that location be changed in the main ksh93 Makefile.  The configuration file should contain a line that defines the file to write the audit records to, followed by the UID of each user whose commands are to generate audit records.  Here is the configuration file used to generate the audit records for this part of this post.
$ cat /etc/ksh_audit
/tmp/ksh_auditfile;500
This configuration file specifies that audit records are to be written to /tmp/ksh_auditfile for the user who's UID is 500.  Note that the field delimiter is a semi-colon.

Here are the audit records stored in the /tmp/ksh_auditfile which match the accounting records shown previously in this post.  The field separator is a semi-colon.  The first field is the UID of the user executing the command.  The second field is the time in seconds since the Epoch.  The third field is the terminal device on which the command was executed, and the final field is the actual command executed by the user.
500;1230606552;/dev/pts/2; echo ${.sh.version}
500;1230606554;/dev/pts/2; pwd
500;1230606557;/dev/pts/2; id
500;1230606563;/dev/pts/2; date
500;1230606565;/dev/pts/2; exit
As before, here is a simple ksh93 script which parses this audit file, replaces the UID with the actual user's name and seconds since the Epoch with the actual data and time, and outputs the enhanced records in a comma separated value (CSV) format.
#!/bin/ksh93

AUDITFILE="/tmp/ksh_auditfile"

while IFS=";" read uid sec tty cmdstr
do
name=""
while IFS=":" read pwname pword pwuid rest
do
if [[ "$uid" == "$pwuid" ]]
then
name=$pwname
break
fi
done < /etc/passwd

timestr=$(printf "%(%Y-%m-%d %H:%M:%S)T" "#$sec")
print "$timestr", $name, $uid, $tty, "$cmdstr"
done < $AUDITFILE
Here is the output for the above audit records.
2008-12-29 22:09:12, fpm, 500, /dev/pts/2,  echo ${.sh.version}
2008-12-29 22:09:14, fpm, 500, /dev/pts/2, pwd
2008-12-29 22:09:17, fpm, 500, /dev/pts/2, id
2008-12-29 22:09:23, fpm, 500, /dev/pts/2, date
2008-12-29 22:09:25, fpm, 500, /dev/pts/2, exit
If the underlying operating system supports networking using the /dev/udp/host/port syntax or the /dev/tcp/host/port syntax, audit records can be sent by ksh93 across a network to another system.  This mechanism could be used to store audit records on a secured centralized system to which only specific personnel have access.  As an example, the following audit configuration file line designates that audit records for the user who's UID is 500 should be sent using UDP to the syslog network port (514) on a remote system who's IP is 192.168.0.99.
/dev/udp/192.168.0.99/514;500
Here are the same audit records stored by the syslog daemon on the remote system.
2008-12-29 22:09:12 192,169.0.115 500;1230606552;/dev/pts/2; echo ${.sh.version}
2008-12-29 22:09:14 192.169.0.115 500;1230606554;/dev/pts/2; pwd
2008-12-29 22:09:17 192.169.0.115 500;1230606557;/dev/pts/2; id
2008-12-29 22:09:23 192.169.0.115 500;1230606563;/dev/pts/2; date
2008-12-29 22:09:25 192.169.0.115 500;1230606565;/dev/pts/2; exit
Depending on the configuration of the syslog daemon on your particular system, the first part of the record may contain more or less information or be formatted differently but the final part of the record, i.e. the audit record sent by ksh93 should be in the standard audit record format.

Note that while the auditing and accounting facilities within ksh93 can provide you with much useful information regarding the actions of one or more users on a system or systems, these facilities should not be regarded as providing enhanced security akin to the Trusted Computing Base (TCB).  There are many ways of circumventing these facilities.  For example, a knowledgeable user could switch to a different shell such as bash where their actions will not be recorded.  There are a number of other ways but I will not discuss them here.

Most of the information provided in this post is not documented in a single place anywhere that I can find by searching the Internet.  The ksh93 man page does not mention either the accounting or auditing facilities.  Even the ksh93 source code is somewhat vague.  I gleened most of this information by studying the code in ../src/cmd/ksh93/edit/history.c.  If I got anything wrong, please let me know so that I can update this post.

2 comments:

Anonymous said...

I spent some time with the auditing function and i had to realize that
-tha config file must be world readable (..must be readable by the user whose activites are audited)
-the logfile must be writable by all users whose activities are audited/logged.

Thanks for the parsing scripts :)

Anonymous said...

As the person who requested the feature be added, the key is not to truly prevent malicious behavior, but to provide a record of probable points where the logging was bypassed.

The issue is that we are asked to provide a record of actions taken with administrative privilege for numerous audits. By setting it to monitor the effective UID 0, (something that isn't clear, it looks at euid, not just real uid), we can centralize the shell history of multiple system administrators. As you correctly point out, it can be bypassed, but the bypass command will still be captured, and if you are logging offhost, it will be captured and recorded.

I strongly advise use of the /dev/udp/hostname/514 destination to log the results, that sends it to a remote box's syslog.

Post a Comment