Korn Shell 93 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 primitive 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
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
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.


printf "DATE       TIME     LOGIN  COMMAMD\n\n"

# set IFS to TAB only
while IFS="     " read cmdstr name hexseconds
    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

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

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.



while IFS=";" read uid sec tty cmdstr
   while IFS=":" read pwname pword pwuid rest
      if [[ "$uid" == "$pwuid" ]]
   done < $PASSFILE

   timestr=$(printf "%(%Y-%m-%d %H:%M:%S)T" "#$sec")
   print "$timestr", $name, $uid, $tty, "$cmdstr"

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


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 500;1230606554;/dev/pts/2; pwd
2008-12-29 22:09:17 500;1230606557;/dev/pts/2; id
2008-12-29 22:09:23 500;1230606563;/dev/pts/2; date
2008-12-29 22:09:25 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.

1 comment to Korn Shell 93 Auditing and Accounting

  • Anonymous

    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 :)