Linda Bissum


Quick Index

 

Audit, A policy driven security checker
for
a heterogeneous Environment
Linda Bissum

ABSTRACT

Security audit programs can be of great help to the system administrator in the work of ensuring an adequate level of security. One common problem with existing security audit programs, however, is that they implement default policies embedded in their code. Such embedded policies are not always in agreement with those of the real world (e.g., when the program complains about /usr/spool/uucppublic being world writable).

It was therefore set as a goal to design and implement a security auditing program which did had no hard coded policies embedded in the program code. The result was the security audit program called Audit , which allows policy decisions (such as whether users should be allowed to have private .rhosts file in their home directory) to be made at the local site - without requiring any program modifications. This has been achieved by making policy- and platform-dependent decisions in a specialized audit control language which is used to write the control file(s).

1. Introduction

Audit is a a program designed to assist the system administrator in performing security audits which check local security policies. Audit is also designed for a heterogeneous environment, enabling new platforms to be audited, without requiring any modification to the audit program itself.

Audit is implemented in perl. The perl language has the advantage of making Audit easily portable, as most porting problems already have been resolved in the perl interpreter. The parsing of the audit control file(s) is implemented by recursive descent, chosen for its simplicity and fast implementation. Audit performs its security audit by checking files and their attributes (file type, permissions, size, checksum, etc). However, Audit knows about items like home directories and startup files and can audit them in a relevant fashion. In addition, Audit supports auditing of the content of a file, through an interface which enables Audit to call an external script or program to check the files and/or their contents.

The alpha version of Audit has been running on several platforms (BSD, SunOS & SVr4) for some time; a beta version is expected to be available for distribution at the time of the conference.

This paper discusses goals for design and implementation, as well as showing practical examples of control files implementing different policies. The paper also discusses how implementation and installation can be done in order to prevent tampering by intruders to the greatest possible extent.

2. Purpose

One of the driving forces behind the development of Audit was the fact that existing security auditing programs contain embedded policies regarding what, when, and how to audit. This complicates modification of the security auditing program to check a system in accordance with local policy and custom. In addition, this approach obscures what is checked, a fact which can make the security checker a double edged sword, because it can lull the system administrator into a false sense of security. While Audit cannot, and does not claim to, fix all of the problems mentioned above, it is an experiment of developing a highly configurable auditing program. Before beginning development of the current incarnation of Audit , the following goals where set forth:

  • It should be possible to make changes to the security audit (to reflect the security policy) without a need to change any code in the auditing program.
  • The audit program should have no default policies embedded in the code.
  • The audit program should be able to audit files relative to users home directories.
  • The audit program should be able to handle exceptions
  • The audit program should report errors in as secure a manner as possible.

Many security violations in UNIX are caused by critical files or directories having permissions flags set incorrectly, while others are caused by faulty content of configuration files. If these problems could be addressed effectively, it should be possible to detect other security problems, such as unauthorized modification of system binaries.

Audit is primarily oriented towards auditing file attributes for a large number of files. It can, however, perform other kinds of checks, through call of external scripts and programs, such as checking the content of the password file. It was decided not to provide this kind of audits as built-in test, partly because of the difficulties of providing portable solutions, and partly because of problems in providing a good simple audit control language definitions. Instead, a generalized interface to call specialized utilities is provided. Example of such utilities (which are part of the Audit distribution) are perl scripts for checking the password and group file. External scripts also have the advantage of extensibility.

3. Existing Practice

Several public distributable security audit program are available. A brief overview of some of these are given below:

  • Secure is a security checker written in Bourne Shell. This shell script can be found in the old UNIX System Security book by Wood and Kochan.

    The main value of this script (in accordance with its goal) is as a teaching device.

    It is very System V rel. 2 oriented, and covers no networking issues at all.

  • Setperm is also a shell script from the Wood and Kochan book.

    In addition, a similar version, written in C, can be found on Xenix systems.

    This utility can check owner, group, and permissions of files.

    It compares the actual values with a list and reports any found differences.

  • Spy is an HP-UX specific security checker.

    It is not publicly available; however, it was presented at LISA III by Bruce Spence, and can be found in the conference proceedings, with some implementation details.

  • COPS , written by Dan Farmer, is the best known of the publicly available security checkers.

    Its greatest strength is its ability to check users' configuration files (e.g., .cshrc) and, through its built-in expert system, to check to see if a user has been compromised through incorrect permissions on the dot-files.

    It greatest weakness is that many policy decisions have been embedded in the code. It is also difficult to change the audit to embed local requirements.

  • Crack (written by Alec David Muffett) is a suite of Unix programs designed to quickly and efficiently find easily guessable passwords in standard crypt() encrypted Unix password files.

    This program is not a audit program in the sense of the other programs mentioned here, however it should be part of any security sensitive system administrator toolbox.

4. Heterogeneity

An important goal of Audit was the capability to support multiple platforms without modification to the audit program itself. With the usage of audit control files, this need has been eliminated. However, if the audit control files need to be distributed to the hosts being checked (using some scheme based on operating system and hardware platform), the problems in doing so would diminish the value in the heterogeneity on the program.

Unfortunately, there is currently no way to avoid the differences between operating systems or vendors.

It has therefore been necessary to design Audit so it can distinguish between information related to the system currently being audited and information related to other systems or hosts (which has no bearing in the current audit).

The problem has been minimized through use of conditional expressions in the audit control language and through support of multiple audit control files.

The Audit control files can be distributed to all hosts, independent of the underlying operation system.

This can be done with a program such as rdist(1) or by read-only NFS mounts of the necessary directories.

Of course, if the control files are made available through NFS mounts, great care must be taken that they cannot be modified on the remote host.

As a minimum, the files must be located on a file system mounted read-only on the audit master; it is insufficient to relay on the NFS read-only mounts, as they can be by-passed.

It is also possible to bypass the local read-only mounts, but this requires access to the local host.

The only secure way of providing NFS mounts is to store the control files on a disk where the read-only mode is enforced by the hardware.

If the control files are distributed by rdist(1) , it is wise to ensure they had not been modified (this too can be done with rdist ), prior to starting the audit.

In either case, it is a good idea to keep the audit master host as secure as possible, i.e., by restricted access, both across the network and physically.

In addition, it is necessary to verify both programs and control files against copies kept on off-site media.

As for any audit program, it is important to remember that if an intruder is able to get to the program and modify it, the audit it performs is no longer useful.

5. Policy Issues

As previously mentioned, the current available security auditing programs do not lend themselves very well to individual site policies.

One of the major goals of Audit was to attempt to avoid such issues. Some of the ways this has influenced the design of Audit are:

  • If a given file is not be allowed on the system, these audit programs has no way of perform the necessary audit.

    One good example of such policy decisions is to decide whether users are allowed to keep their own .rhosts file. As this file overrides the content of the system-wide configuration in /etc/hosts.equiv , the content of such files can nullify the security policies implemented in the system files.

    Similar considerations can be made for the users .forward file, which some sites disallow, to ensure no mail is automatically forwarded out from the site.

    Audit has a built-in mechanism to allow the system administrator to specify files which are not allowed on the system, either by a full path name or relative to the users' home directories.

    In a similar manner, a file can be required to exist on the system (this is in fact the default) or can specified as being optional.

  • Often it is not necessary to specify exact permission for a file. For example, it is common among system administrators to state that a file needs a permission mode of 755 or less, meaning that a permission mode of 751 or 511 is equally acceptable to a permissions of 755 (here assuming the file is a binary and not a shell script). Therefore, Audit allows a permission range to be specified wherever a single permission normally would be required.
  • Certain systems (a gateway, for example) will have different (and likely more stringent) security requirements than many other systems on the site. The design of Audit therefore allows specifications which state exceptions to the general rules.

6. Audit Methodology

In UNIX, many security problems are caused by files or directories with incorrect permissions. Audit has been primarily been designed to detect this kind of security problems. Audit is therefore able to track a large number of file attributes, such as file checksum, file size, inode number and hard links, in addition to more traditional audited file attributes, like owner, group and access permissions. It provides an effective tool to detect unauthorized system modifications, such as system binaries, modified without authorization.

Audit allows, for any given file or directory, the system administrator to check for any of the following values:

  • File checksum It is possible to specify an expected checksum for a file. The checksum is calculated by an external program such as

    sum(1) or Snefru . This allows the local site to use whatever they find is necessary to provide an adequate level of audit reliability.

  • Owner and group Is is possible to specify the expected user and group who should own the file. Both user and group can be specified as either their name, or their related numeric value, as kept in /etc/passwd and /etc/group . JTo allow for situations where multiple users and/or groups are acceptable, multiple ID's can be specified. This is useful for a specification which has to be used on multiple platforms, where group number zero on System V derived systems is named other , while it is called wheel on BSD derived systems.
  • Permissions It is possible to specify which permissions a file should have. As it is common to give a specification for permission of a file in the form of "755 or less", Audit provides a method for specifying minimum and maximum permissions.
  • File Type It is possible to specify the expected file type, such as regular file, directory or device file.
  • Inode number and device It is possible to specify the expected inode device number for the file.
  • Inode Change Time It is possible to specify the expected ctime (time of last status change) and mtime (time of last modification).
  • Number of hard links It is possible to specify the expected number of hard links to a given file or directory.
  • File Size It is possible to specify the file size in bytes and, on supported systems, in blocks (BSD derived systems only). Specifying the number of blocks on a system which does not support this causes an error to be reported.
  • File Count It is possible to specify the number of files, directories, etc. which must be in a directory.

Most of these values are obtained with the perl stat() function call. Whether the block size checks is supported is dependent on the UNIX system version. Where perl stat() function does not return a usable value, no check will be performed. If an unexpected value is found, an error message will be generated.

Most sites will have no need for auditing all these file attributes. In fact, some of them will be so cumbersome to use (such as inode numbers) that they will likely be different between otherwise identical systems. However, checking such values is useful when a in-depth security audit is required. It was therefore decided to include all file attributes (with the exception of time of last access, which makes no sense to include), in order to meet the criteria of no embedded policies in the code.

If file attributes such as inode number, time of last modification, and time of last status change are verified, in addition to the usual checksum, owner, group, and access permissions, it becomes very difficult to make any modification to such files without it being detected next time the audit program is executed. On systems with requirements of high security, the additional work required creating the audit control files can be justified by the added safety.

However, to avoid a requirement of all sites to audit all of these items, Audit uses default values for each file. The default is initially set to `no auditing' ( -no-audit ), but this default can be set to any desired value, including a value called error , which alway will generate an error messages when checked.

Values are assigned for each attribute on a file by file basis, allowing different attributes to be set for different files. If a file attribute for a given file is changed, a warning messages will be generated unless the old value is no-audit or error .

It is also very useful to be able to specify whether a file is required to be on the system. In Audit it is possible to specify a file as:

  • require If the specified file is not found on the system, Audit issuing a warning.
  • allow Presence of the file is optional; however, if found, it must adhere to all other required specifications.
  • deny The file must not be found on the system.
The deny attribute is useful for enforcing policies to not use certain files, such as .rhosts , as well as a method to discover files placed on the system by an intruder. Typical examples of names used by system crackers to hide hide files, are ``.. '' (dot-dot-space) and ``...'' (dot-dot-dot).

It is possible to reduce the member of entries in the audit control files, by using regular expressions in the file path names. If this is done, one entry can cover a large number of files with the same attributes, such as owner, group and access permissions. Using this method it is not be possible to use checksums or other attributes which differ between files.

Home Directory Auditing

A large number of security problems are represented by the dot-files in the users' home directories. Such files can, in some cases, directly harm the security of the system (.rhosts in specific), or more indirectly by being writable by others.

It is possible to use the same method as described above to audit such files, by use of regular expressions in the path names. However, this method would be cumbersome and error prone, especially as the directory character (``/'') cannot be matched by any regular expression. Instead, Audit supports auditing of files with path-names relative to the users' home directory.

This allows Audit to audit a specific file in all users' home directories, without the system administrator having to specify the path to those homes (this information will be taken directly from the password file). The same file attributes can be audited, as with the file audit described above.

File Content Auditing

Audit is not capable of performing file content auditing directly. However, such auditing can still be achieved, by calling external utilities, which currently exist for auditing the following files:
  • passwd This check provides a way to check most properties of the password file, including verification of existence of a password on all accounts. However, it does not include checking of the quality of the passwords. The quality of the passwords can be better be ensured through use of a pro-active password checker, such as passwd+ (Matthew Bishop, A Proactive Password Checker ) or through use of password cracker, such as Crack.
  • group Audit of the group file. At this time, the only verification is the existence of a non-password (a string less than 13 characters) in the password field.
  • shadow Audit of the shadow file. Currently, only the password field in the shadow file is checked and it is checked only for existence of a password on all accounts.
  • .rhosts This script checks that the file does not contain a plus sign and that all other entries consists of a host name, followed by a user name (i.e., only a host name will be considered a security breach, and reported).

Event Auditing

Audit provides no facilities for events checking. However, a separate program which checks last login time of users is provided. It is capable of flagging accounts which have not been used for a long time and accounts where a login is not expected but which have been used reasonably.

7. Audit Control Language

The audit process is determined by the audit control language. The use of a control language is inspired by setperm utility, however, the audit control language for Audit is much richer. This is necessary to achieve the required functionality.

The following is an overview of this language.

A complete syntax specification of the Audit control language can be found in appendix A.

The audit control language consists of two major parts, the definition statements and the audit control statements. Each statement can be preceded by a boolean conditional clause. The statements will only be effective if the conditional clause evaluates to true.

The following definition statements are available:

  • default Defines alternate default value for any file attribute.
  • define Defines names for string content.
  • program Definition of an external program.
  • read Reads a control file.
  • set Assigns values to variables
The audit statements specify which path names must be audited:
  • file Specify expected file attributes for a file. The path name is relative to root (``/'').
  • home Specify expected file attributes for a file. The path name is relative to the users home directory.
  • request Request to execute a program. No path name associated with the request. These statements are all described in some detail below.

The Default Statement

The default statement is used when it is necessary to define alternate default value for one or more file attribute. The initial default for all file attributes are no auditing ( no-audit ).

The default statement can also be used to substitute the string values for no-audit and error , if for example audit is used at a site where a user name is error.

The Define Statement

The define statement allows definition of named strings, very similar to the C pre-processor's #define. However, at least in the current implementation, expansion of defines is done by the lexical scanner and not by a separate pre-processor.

Audit requires the name of the string to be in all upper case. This is consistent with common practice in C and enables a significant speed-up, as only all upper case lexical symbols must be looked up in the table of defined strings. An example of a defined string:

       define WHEEL wheel,other
    
   
where the defined string then can be used as a shorthand alter in the control file.

The Program Statement

The program statement provides a way to define an external program, which later will be called by Audit . Only programs which are defined in this manner can be called.
       program sum /usr/bin/sum;
    
   
The program statement provides the system administrator with a method to specify which file attributes must be checked prior to the use of the program. \f(CR
       program arch /usr/bin/local/arch uid=bin gid=wheel,other;
    
   
It is not possible to use a program without first defining it in this manner. This helps to ensure programs which are executed from Audit have the expected file attributes.

Audit verifies a program's attributes each time it is called. While this increases the execution time, it also assures that the external program satisfies all the criteria specified in the program statement, even if called at a much later time in the auditing sequence. In most cases it is sufficiently to execute a program once, and assign it to a variable.

The checksum program is treated a bit differently, as it is called repeatedly. In addition, it would create an infinitive checking loop, if it was required to check its own checksum. Therefore, the checksum program (whose defined name is required to be sum ) is verified as its expected file attribute values are defined. Later, the ctime , mtime , and size attributes are checked at random intervals, to ensure they have not been altered since the start of the program.

The Read Statement

To avoid that all audit control information would be required to be kept in a single file, Audit provides a read command. This command will cause the specified file to be read before continuing processing the current file.
       if ($OS=bsdi) read audit.bsdi
    uid=root gid=wheel size=9673;
    
   
The read statement can be used recursively.

The Set Statement

It is possible to set variables, which later can be used in, for example, conditional clauses. A variable can either be set to a string \f(CR
       if ( $Arch=386 && $Ostype=BSDI ) set Name bsdi/386;
    
   
or, more usefully, can be set to the return value of an external program:
       set Arch exec arch;
    
   
The latter example executes the program defined in a previous program statement as arch and places the standard output in the variable named Arch . It is then possible to use this in later conditional clauses, such as:
       if ( $Arch = sun3 ) file /somefile size=32456;
    
   
or in an exec clause, such as
       file /etc/syslog.conf uid=root gid=wheel;
    file /etc/syslog.conf exec chksyslog $Arch;
    
   

The File Statement

The file statement is used for the main audit control specifications. Each path name which should be checked require sa definition using this statement. A full path name is required as there are no default directory path used.
       file /etc/passwd uid=root gid=wheel,other perm=600;
    
   
This statement causes the password file to be checked to ensure it has the expected UID, GID and permissions set. Any discrepancies will be reported by audit.

It is possible to audit all of the file attributes in this manner. In addition, an external program can be called to audit the content of the file. Assuming the file /usr/audit/bin/pwchk is a script which will check the password file for, e.g., missing passwords, the following definition would ensure this check would be performed:

       program /usr/audit/bin/pwchk uid=root gid=wheel,other;
    file /etc/passwd uid=root gid=wheel,other perm=600;
    file /etc/passwd exec pwchk;
    
   
Audit is completing the parsing of the audit control file(s), before it is starting the audit. It is therefore possible to split the audit definitions, without having the file checked multiple times.

During the parsing of a file statement, the variable $file is set to the full path name of the file. This can be used to give the path-name as a parameter to an external program.

It is possible to use regular expressions in the path name to keep the audit control file small. Regular expressions are discussed later.

The Home Statement

The home statement works as the file statement, with the difference that the path names specified are relative to the users' home directory.

A statement like \f(CR

       home \.cshrc perm=640,400;
    
   
will be expended to the equivalent of one file statement with the full path-name for ~/.cshrc for each user found in the password file.

To simplify the specification, two variables (in addition to $file ) are automatically set by Audit .

These are $uid , which will contain the name of the current user, and $gid , which will contain the name of the user's main group ID (the one used in the password files fourth field). \f(CR

       home \.cshrc uid=$uid gid=$gid perm640,400;
    
   
As the audit rules do not necessarily have to be the same for all users, there are an except and a for clause, which modifies the scope of the home statement.

The except clause enables the system administrator to provide an exception list of users and groups, for which the statement should not have any effect:

       home \.cshrc required except user=sue,paul
            uid=$uid gid=$gid perm640,400;
    
   
In a similar manner, the for clause can be used to target only specific users:
       home .profile required for user=sue,paul
            uid=$uid gid=$gid perm640,400;
    
   

The Request Statement

The request statement provides an interface to call external programs without their being associated with the audit of a specific file. An example of the use of a request statement: \f(CR
       request chkpwd;
    
   

Regular Expressions

In order to support generalizations, regular expressions are allowed in path names. This can, for example, be used to help keep the size of the control file down in size.

For example, in the /usr/lib directory, instead of listing all the library files individually, one entry in the audit control file can be used to audit them all:

       file /usr/lib/.*\\.a     uid=bin gid=bin perm=444;
    
   
Such an entry will, of course, not allow the checking of attributes which are different, such as size or checksum.

When the Audit parser encounters a regular expression in the control file, it will expand it to a list of full path names, which match the path-name and create an entry for each path name in its internal table.

Any file attributes which has been assigned where the path name has been an regular expression will be permitted to be over-written by a new value.

However, a file attribute value which has been assigned by a statement where the path-name did not contain any regular expression, will not be overwritten later.

If this is attempted by a statement with a path name which are a regular expression, that assignment will be silently ignored.

However, if the second statement also had a full path name, Audit would allow the assignment, but would issue a warning.

This strategy gives the system administrator some freedom from the sequence in which statement are written, as: \f(CR

       file /etc/.*            uid=bin gid=bin;
    file /etc/passwd        uid=root gid=wheel,other;
    
   
be identical to:
       file /etc/passwd        uid=root gid=wheel,other;
    file /etc/.*            uid=bin gid=bin;
    
   
This can also be used to ensure that no file is added to a directory. If all files in the directory are defined with explicit path names and also explicitly set to required or optional , an entry like:
       file /usr/lib/.*        denied
    
   
will flag any new file added to the directory. However, in general, using the count file attribute is probably a better way to achieve this:
           file /usr/lib           count=42;
    
   
The regular expressions used in the path names have the same syntax as regular expression in perl (as they are passed to, and evaluated by the perl interpreter). This, however, has some side effects in how path names must be specified in the audit control file.

The dot ('.') is special to regular expression and is also commonly used in file names.

Because of this, any dot in a file name must be escaped in order to ensure it is not matched against another character.

Therefore, a file like the /etc/rc.local must be specified as /etc/rc\.local , and the .rhosts file as \.rhosts

The slash ('/') is another special character, both to regular expressions and UNIX path names. This character is special to the UNIX kernel and is treated likewise by Audit . Therefore, no regular expression will be allowed to stretch across directory boundaries. For example, the path names /lib/.*\.a will match any library file in /lib , but will not match any in /usr/lib . This not only has simplified the implementation of Audit , it also resembles the traditional usage of regular expressions in the shell, which seems desirable.

The above will work because a requirement set by a regular expression cannot override a request set with an explicit path name. Audit is keeping track of each file attribute, whether the request specifying it was done by a regular expression, or with a explicit path name. The latter kind of requests will always be allowed to override the first.

Accumulative Usage of Statements

Audit uses an accumulative definition scheme. Any file attribute for a given file which has not been previously defined, can be defined at any time. Therefore, the statement:
       file /vmunix required    uid=root gid=wheel perm=755 ;
    
   
can be written as
       file /vmunix required;
    file /vmunix uid=root;
    file /vmunix gid=wheel;
    file /vmunix perm=755;
    
   
While this in itself is not very helpful, it makes a difference as soon as system specific information is added. If the kernel was to be checked for size and checksum as well as the owner, group and permissions from above, the specification could look like this:
       file /.*unix required   uid=root gid=other,wheel perm=755;
    if ($OS=bsdi)       file /vmunix   sum="29875   496";
    if ($OS=svr4)       file /vmunix   sum="05104  1435";
    if ($OS=mach)       file /unix     sum="73846   657";
    if ($OS=sunos)      file /vmunix   sum="32251   891";
    
   
Still, if a large number of such variants is required, it will make the audit control file very large and difficult to maintain. Audit therefore has a read command, which directs the audit program to read the specified file before continuing (this function is recursive).

It is therefore possible to split all audit control, specific to a certain operating system, vendor, or even host, onto a separate audit control file, which only will be read if the relevant for the machine currently being audited.

An example of the read command is shown below:

       if ($OS=mach)   read audit.mach;
    if ($OS=bsdi)   read audit.bsdi;
    if ($OS=svr4)   read audit.svr4;
    if ($OS=sunos)  read audit.sunos;
    
   

8. Error Handling

One of the concerns in the design of the Audit program, where how to report any inconsistencies found during the audit. It is necessary to choose a mechanism which is difficult to spoof (i.e., writing to a local file would be of less use than sending the reports directly to a central location). It is also necessary to ensure the amount of auditing information which is sent to the system administrator is as minimal as possible. It is a common problem with many existing audit style programs that way too much information is sent to the person who is supposed to review this information. If too much output is generated to state that the situation is essentially normal, then any possible information about an abnormal situation is bound to disappear in the general noise.

On the other hand, it is also necessary to send information that the audit has been performed and went OK. It was therefore early on decided to use syslog for error reporting. It is possible to spoof this approach, by modifying the syslog.conf file, but if this has happened, the system is already penetrated and the audit is not of much use. In addition, it is possible to use programs such as rdist or fdist (Linda Bissum and Paul M. Moriarty; Fdist: A Domain Based File Distribution System for a Heterogeneous Environment , USENIX LISA V Proceedings, 1991) to ensure the file are correct.

It is the intention to write a filtering program to intercept incoming messages from Audit on the central host and send information about such problems by e-mail to interested parties (this program will be part of the beta release).

9. Audit Control Samples

Below is a number of small audit control samples. Neither of these is in any form a complete audit control file. Instead of showing such a large file, it is rather the intention to highlight some of the possible uses of the Audit program.

First a minimum audit control file:

       file /           count=16 perm=755;
    file /           uid=root gid=wheel perm=755;
    file /tmp        perm=755 uid=root gid=wheel perm=777;
    file /usr/tmp    perm=755 uid=root gid=wheel perm=777;
    file /usr/tmp    type=dir perm=777;
    file /usr/spool/uucppublic
                         perm=755 uid=uucp gid=uucp perm=777;
    file /etc/.*     uid=root,bin perm=755,111;
    file /etc        count=53 uid=root gid=wheel perm=755;
    file /etc/passwd perm=644;
    file /etc/group  perm=644;
    file /etc/phones uid=bin gid=bin;
    file /bin        count=34 id=root perm=755;
    file /bin/.*     uid=root perm=755;
    file /usr        count=29 uid=root perm=755;
    file /usr/bin    count=216 uid=root perm=755;
    file /usr/bin/.* uid=root perm=755,111;
    
   
This file is so simple, that its practical value is doubtful. However, with the addition of an explicit list of all set user ID and set group ID files it is becoming closer to something real. Below is shown just a few such entries.
       file /bin/ps  uid=bin gid=kmem perm=4555;
    file /bin/rpc uid=root gid=bin perm=4555;
    file /bin/rsh uid=root gid=bin perm=4555;
    
   
The real definition will of cause need to contain a definition for all SUID/SGID files, as they otherwise will be flagged as a security breach.

Setting the default for some of the more interesting file attributes to something other than `no check' can be helpful to discover audit control errors. \f(CR

       default uid=error gid=error perm=error;
    
   
Using these defaults, any file entered into the Audit table, will be required to specify owner, group, and permissions. Any other of the file attributes could be set in a similar fashion.

In order to ensure a high quality audit, all files in the important directories, such as /, /etc, /dev, /bin , and /usr/bin all must be placed in the the audit control file. However, such entries are straight forward. Audit entries which are in regard to the users home directory is much more interesting, and the area, where big differences between sites are to be expected, as such entries are subject to local security polices.

One such policy issue is whether the users are allowed to have their private .rhosts file. A strong case can be made of technical reasons why users should not be allowed to have their own .rhosts file (with exceptions of special cases, such as root). However it has been my experience that, in the name of convenience, many sites actually allow their users to create and use private .rhosts files in spite of the security problems this can create for the responsible system administrator. Later the sites chose the a different strategy, allowing the users to create .rhosts files but where the content of those files is monitored. Audit has been designed to be able to provide audit capabilities for all of those strategies. However the third solution, checking the content of the file, is very likely to be site dependent. Audit therefore only provides the necessary mechanism for that check, by calling an external program to do the required auditing. Below is first a possible audit specification entry which reports any .rhosts file (with the exception of the users root and operator:

       home \e\.rhosts deny     except user=root,backup:
    home \e\.rhosts require  for    user=root,backup
                uid=$uid gid=$gid perm=400,600;
    
   
On a site where the users were allowed to create the .rhosts file, the audit entry could look like this:
       home \e\.rhosts allow;
    home \e\.rhosts type=file uid=$uid gid=$gid perm=400,600;
    
   
This would at least notify the system administrator of such files with inadequate permissions or ownership.

Alternatively, the .rhosts file could be allowed but would be required to be examined by an external program, to ensure its content would not violate local policy. The home statement for this would look like:

       home \.rhosts    allow;
    home \.rhosts    uid=$uid gid=$gid perm=400,600;
    home \.rhosts    exec hostchk $file;
    
   
This statement would result in any .rhosts file found in any users directory would be checked by the program hostchk .

Normally Audit does not make any changes to the the host it is auditing. This is, in most cases, a wise strategy. However, there is nothing which prevents a system administrator to create a shell script, setfile , with the following content:

       #!/bin/sh
    chown $1 $1
    chgrp $2 $3
    chmod $4 $1
    
   
and then making the following audit control statements:
       program\ setfile=/usr/audit/bin/setfile\ uid=root\ gid=wheel;
    home \e\.rhosts allow exec setfile $file $uid $gid 600;
    
   
This would set the desired UID, GID and permissions on all rhosts files on the system. How well this would be received by the user community is a question of local polices and customs.

Another use is to ensure that specific users such as uucp and ftp do not have a .forward file, as this in some case can be used by an intruder:

       home \e.forward deny for user=uucp,ftp;
    
   
This will ensure the system administrator will be notified if such files should be created. In a similar fashion Audit can look for files with names typically used by a cracker:
       home \.\.\b deny
    home \.\.\. deny
    
   
This will monitor for the famous dot-dot-space and dot-dot-dot files, however, only in the users home directories.

Finally, some other examples of possible statements for the users home directories are:

       home \.forward        allow;
    home \.forward        uid=$uid gid=$gid perm=400,600 exec;
    home \.forward        chkforward $file;
    home \.cshrc          require;
    home \.cshrc          uid=$uid gid=$gid perm=400,600;
    home \.login          require;
    home \.login          uid=$uid gid=$gid perm=400,600;
    home \.profile        require;
    home \.profile        uid=$uid gid=$gid perm=400,600;
    home \.mailrc         allow   uid=$uid gid=$gid perm=400,600;
    home \.newsrc         allow   uid=$uid gid=$gid perm=400,600;
    home \.elm            allow   uid=$uid gid=$gid perm=500,700;
    
   

10. Implementation

The most interesting aspects of Audit is its usage, however a few points should be made about its implementation.

The interpretation of the audit control file(s) is done by a recursive descent parser. While this approach is slower and is not very good at recovery after syntax errors, compared to many other parsing strategies, it is very fast to implement from the Bacchus\-Naur Form, and in addition, its lack of complex data structures is easier handle in perl.

All specifications from the audit control file(s) are placed into one internal table. Any path name which contains a regular expression (implicit path names) will been expanded prior to being entered into the table.

For each file, there are two entries for each attribute. The first is used to hold the value and the second is used for showing whether the value has bee assigned an implicit path name or one which had no regular expressions (explicit path name) Any file attribute can be assigned new values as long as implicit path name is used. When an explicit path name has been used for a file attribute, any subsequent assignment with implicit path names will be silently ignored. If the path name is explicit, Audit will generate a warning.

Any audit specification which is specified by the home statement will be expanded to one file entry for each user in the password file (unless modified by the except or for clause).

These entries are placed in the same table, as the entries specified by the file statement.

All such entries will be explicit, unless a regular expression has been used in specifying the path name relative to the users' home directories.

After all audit control files have been read and all entries been placed into the central table, the audit will start.

During this part of the process, Audit will, for each file, compare the value of the file attributes in the table with the ones kept by the actual file.

File attribute entries which has the value no-audit will not be verified.

File attribute entries which has the value error will always generate an error message.

An earlier version of Audit used find2perl to scan all the file systems instead of just checking the file entries contained in the table. Not only was this a violation of of the goal of no default polices hard-coded in the software (NFS mounted file systems was not checked), it also made Audit take an unnecessary long time to complete on a large file server. However, because of this change it is no longer possible to scan the file systems for certain files, such as device files outside of the /dev directory.

All error reporting is done through syslog . All calls to syslog are centralized though one subroutine call, making it possible to replace only this routine if another strategy for error reporting should be required.

11. Future Development

While Audit has been used for some time, there are still a number of improvements which must be implemented. Some of these are currently being worked on and will be part of the beta release. Other improvements would be nice, but are not scheduled at this time.
  • A very recent change was to use the internal file table for for file lookup.
  • Previous to that time, a find like scan of all the mounted file systems (using perl2find) was used.
  • This change gave a significant speed improvement, but a side effect of this was that it was no longer are possible to look for, e.g., devices files which are placed outside the device directory.
It is therefore necessary to provide other means for auditing this.

A new audit statement, scan , is planned for this purpose.

It syntax has not yet been finalized; however, it is the goal to combine the file attribute list from the file and home statements, with capabilities such as found in the find(1) command.

It is also a goal that only one scan of the file systems shall be done, independent of the number of scan statements found in the audit control file.

  • Up til now, the Audit control files have been created and maintained by hand. This is cumbersome and time consuming, especially for the first few files. A new utility, mkaudit is planned. This command will be able to create control files by looking at an existing system.
  • It would be nice if it is possible to alter the audit behavior from the command line.

    It is therefore the plan to make all upper case options into variables which can be used by the Audit parser in conditional clauses.

  • It is the plan to add more programs which are capable of auditing the content of files to spot problems.

12. Conclusion

When this project was started several years ago, it was based in the frustration of Secure being incomplete. The goal was to build a security audit program which was highly configurable. This has been achieved at the cost of having a configuration which can be complex. However, if the USENET distribution of audit will contain audit control files for a number of systems, this problem will have been reduced. In my experience from using audit and its various predecessors, it is worthwhile to take the time required to configure this program.

13. Availability

The final Audit will eventually be posted to USENET. However, the beta release will only be made available on a limited basis, preferable to larger, heterogeneous sites.

Acknowledgments

Thanks Larry Wall for the creation of Perl. Without Perl, this project would probably never have been completed.

Thanks to Rob Kolstad for proof reading this paper.

Appendix A

Bacchus-Naur Form for Audit Control Language

The complete syntax for the audit control language is shown below.

       audit-control ::=        <sentence> [ <audit-control> ]
    sentence ::=             [ <cond> ] <command> ";"
    
   
Condition:
       cond ::=                 "if" "(" <expr> [ && <expr> ] ")"
    expr ::=                 <variable> "=" <reg-exp>
    
   
Command:
       command ::=              <specify> | <audit>
    specify ::=              <default> | <define> | <program> | <read> | <set>
    audit ::=                <check> | <file> | <home> | <request> | <scan>
    
   
Default:
       default ::=              "default" <default-list>
    default-list ::=         <set-default> | <attr-list>
    set-default ::=          <name> "=" <string>
    name ::=                 "no-audit" | "error"
    
   
Define:
       define ::=      "define" <name> <value>
    name ::=                <upper-case-string>
    value ::=               <string> [<string> .. ]
    
   
Program:
       program ::=             "program" <path> [ <attr-list> ] [ <exec> ]
    
   
Read:
       read ::=                 "read" <file> [ <attr-list> ]
    
   
Set:
       set ::=                  "set" <varname> <value>
    value ::=                <string> | <exec>
    
   
File check:
       file ::=                 "file" <path-name> [ <attr-list> ] [ <exec> ]
    
   
Home:
       home ::=                 "home" <path-name> [<execpt>] [<file-attr>] [<exec>]
    except ::=               "execpt" <expt-list> [ <except-List> ... ]
    expt-list :=             <uid-list> | <gid-list>
    
   
Exec:
       exec ::=                  "exec" <program> <parm-list> 
    
   
Attr-list:
       attr-list ::=              <fileattr> [<attr-list>]
    
   
Fileattr:
       fileattr ::=               <count> | <sum> | <uid-list> | <gid-list> |
                               <perm> | <atime> | <mtime> | <ctime> |
                               <links> | <size> | <inode> | <dev> |
                               <rdev> | <blksize> | <blocks> | <filetype>

    count ::=                  "count" "=" <integer>
    sum ::=                    "sum" "=" ( <string> | "none" )
    uid ::=                    "uid" "=" <id-list>
    gid ::=                    "gid" "=" <id-list> 
    perm ::=                   "perm" "=" <perm-value> ["," <perm-value>]
    atime ::=                  "atime" "=" <file-date>
    mtime ::=                  "mtime" "=" <file-date>
    ctime ::=                  "ctime" "=" <file-date>
    links ::=                  "links" "=" <integer>
    dev ::=                    "dev" = <integer>
    size ::=                   "rdev" = <integer>
    size ::=                   "blksize" = <integer>
    size ::=                   "blocks" = <integer>
    size ::=                   "size" "=" <integer>
    inode ::=                  "inode" "=" <integer>
    filetype ::=               "type" "=" <type>
    
   
UID-List:
       uid-list ::=               "uid" "=" <id-list>
    
   
GID-List:
       gid-list ::=               "gid" "=" <id-list>
    
   
ID-List:
       id-list ::=                <id> [ "," <id-list> ]
    id ::=                     <string> | <integer>
    
   
File-date:
       file-date ::=              <integer> | <ascii-date>
    ascii-date ::=             <year> <month> <day> <hour> ":" <minutes> ":" <sec>
    
   
File-Type:
       type ::=                   "file" | "dir" | "dev" | "bdev" | "cdev" |
                               "symlink" | "pipe" | "special"
    
   

References

UNIX System Security by Wood, Patrick H., and Stephen G. Kochan. Hayden Books, 1986

Bruce Spence, Spy: A UNIX File System Security Monitor , USENIX LISA III Workshop Proceedings, 1989.