Trusted Solaris Developer's Guide

Chapter 8 Application Auditing

Auditing enables administrators to monitor user actions to detect suspicious or abnormal patterns of system usage. Auditing concepts, terminology, and administration procedures are fully covered in Trusted Solaris Audit Administration. This chapter describes how to use the auditwrite(3TSOL) routine in a third-party application to create and log third-party user audit events.

Third-Party User Activities

Third-party applications audit user activities by creating third-party audit events and audit classes specific to the application and generating audit records with those events using the auditwrite(3TSOL) routine.

The application programmer defines the third-party audit events and classes used in third-party applications, and the system administrator at the site using the application sets up the above-referenced files to recognize the new events and classes.

Within the application, audit events are generated and logged to the audit trail in records. Audit records contain tokens that provide the audit event and other relevant information such as the process ID of the process that generated the event, the machine on which the event occurred, and the date and time. The audit trail is the place where audit records generated by the kernel, system applications, and third-party applications are stored in files. The following figure presents these elements and their relationships.

Figure 8-1 Audit Trail, Files, Records, and Tokens

Graphic

It is up to you to decide exactly what information is logged to the audit record by deciding which tokens are passed to the auditwrite(3TSOL) routine. Audit records should be generated in third-party applications in the highest possible interface layer where the most precise information is available, and there is more opportunity to limit the generation of less useful audit records.

Privileged Operations

The process needs the proc_audit_appl privilege in its effective set to call the auditwrite(3TSOL) routine and log third-party audit records to the audit trail. It is also required for any operations that get auditing information such as the audit state.

Header Files and Libraries

To use the programming interface described in this chapter, you need the following header file:


#include <bsm/auditwrite.h>

The examples in this chapter compile with the following libraries:


-DTSOL -lbsm -lsocket -lnsl -lintl -ltsol

Declaration and Argument Types

The auditwrite(3TSOL) routine generates and logs third-party audit events.

int auditwrite(..., AW_END);

This library routine takes a variable number of arguments of the following three kinds. Refer to the auditwrite(3TSOL) man page for a complete listing of argument commands and their meaning. The code examples in this chapter use many of the possible argument commands.

Preliminary Setup for Code Examples

A certain amount of administrative setup needs to occur to create third-party events and classes, and view audit records logged to the audit trail. The following is a summary of the administrative setup required for the code examples in this chapter to work. Trusted Solaris Audit Administration explains these and other administrative procedures in detail.

First, check that auditing is enabled and turned on. It is enabled by default, but you can check with the auditconfig(1M) command and the getcond option. Run this command from the profile shell with the sys_audit or proc_audit_appl privilege. The setcond option turns auditing on and off.


phoenix% auditconfig -getcond

Audit File Setup

This section shows you how to set up the audit_class, audit_event, and audit_control files. The best way to edit these files is as follows:

  1. Assume the Security administrator role.

  2. Launch the Application Manager.

  3. Double click the System_Admin icon.

  4. Double click the Audit Classes, Audit Events, or Audit Control action.

  5. Edit each file as described in the following sections.

Audit Classes and Audit Events

Create the third-party audit class ec and two audit events, AUE_second_signature and AUE_second_signature_verify. See the audit_class(4) and audit_event(4) man pages for more information on these files.

Audit Control (Process Preselection Mask)

The process preselection mask specifies the audit classes to be audited by the process. To set up the preselection mask to audit for third-party events, edit the /etc/security/audit_control flag parameter as follows to audit events in the example (ec) class for success and failure.

flags:ec

Settings in audit_control(4) are global to all users in the system. To make a setting specific to a user, edit the /etc/security/audit_user file (the Audit Users action) as follows:

zelda:ec

See the audit_control(4) and audit_user(4) man pages for more information on these files and settings. Log out and log back in for the newly defined process preselection mask to take effect. You could also use auditconfig(1M) with the -setpmask option to set the process preselection mask on any existing processes, but it is probably easier to set one of these files and log out and log back in once.

Viewing the Audit Trail Setup

All audit records including audit records generated by the auditwrite(3TSOL) routine are logged to the audit trail in a series of binary files at ADMIN_HIGH. The location of the audit files is set in the /etc/security/audit_control file, and by default is /var/audit. The praudit(1M) command reads the audit trail files and interprets the binary data as human-readable audit records.

Assume a role with the tail(1) command and the praudit(1M) command with the proc_audit_appl and proc_audit_tcb privileges. Open a terminal at ADMIN_HIGH, change directory to where the audit records are stored, and execute the tail and praudit commands as shown to view the current audit file.


Note -

This syntax works when there is only one *not_terminated* file. If there are others, delete the older ones before executing this command.



phoenix%  cd /var/audit
phoenix% tail -0f *not_terminated* | praudit

The audit daemon logs audit records to the audit partition until they reach their maximum capacity and then starts a new file. The file currently written to is the not_terminated audit file. View the /etc/security/audit_data file to determine which file is current.

Executable Code Setup

Put the proc_audit_appl privilege in the forced and allowed privilege sets of the executable file containing the example source code by executing setfpriv(1) from the profile shell with the file_setpriv privilege. "Assigning File Privileges using a Script" explains how to do this with a script.


phoenix% setfpriv -s -f proc_audit_appl -a proc_audit_appl executable.file

Creating an Audit Record

An audit record is created by passing one control command and one or more token commands to the auditwrite(3TSOL) routine in one call (AW_WRITE) or several calls (AW_APPEND for each call with AW_WRITE in the last call). An audit record must have an AW_EVENT token and should have an AW_RETURN token to indicate which event occurred and whether the event succeeded or failed. See "Return Token" for more information.

Making Invalid and Valid Calls

These examples show the different audit records logged to the audit trail when a call to the auditwrite(3TSOL) routine is invalid and valid. The structure of audit records and tokens is described in "Token Structure".

Invalid Call

If you use more than one control command, or omit the control command, or do not include the AW_END terminator command, your code compiles and runs and a record is logged to the audit trail to record the invalid call to the auditwrite(3TSOL) routine. Note that the event is logged to the trail only if the process preselection mask audits the AUE_auditwrite event for failure.

This example shows an invalid auditwrite(3TSOL) routine call that omits the AW_END terminator command and the resulting audit record. The header files for the examples in the rest of this chapter are shown in this first program.

#include <bsm/auditwrite.h>
#include <tsol/label.h>
#include <sys/param.h>
#include <bsm/libbsm.h>
#include <tsol/priv.h>

main()
{
/* Invalid call missing AW_END. Do not do it this way. */
	auditwrite(AW_EVENT, "AUE_second_signature", AW_WRITE);
}

An invalid call is logged to syslog, and if the invalid record has enough information, it is also logged to the audit trail. In the example, the invalid call is logged to syslog only with the following information:


header, 194,2,auditwrite routine fail,,Fri Sep 06 10:11:33 1996, 
+ 179 msec text,
auditwrite routine aborted: aw_errno = 6 = Command invalid, errno = 0 
= no such device or address 
subject,zelda,zelda,staff,zelda,staff,1774,348,0 0 phoenix
slabel,C
return,failure,-1

Valid Call

This call to the auditwrite(3TSOL) routine includes the AW_END command and logs the AUE_second_signature event to the audit trail.

/* Valid call that includes AW_END */
auditwrite(AW_EVENT, "AUE_second_signature", AW_WRITE, AW_END);

The viewing terminal shows this record:


header, 4022,2,second signature requested,,Fri Sep 06 
  10:16:49 1996 + 969 msec
subject,zelda,zelda,staff,zelda,staff,1774,348,0 0 phoenix
slabel,C
return,success,0

Creating a Minimum Audit Record

An audit record consists of a sequence of tokens. Each token of the record starts with a token type followed by the token values. You can put whatever tokens and values you want into an audit record by passing the appropriate token commands to the auditwrite(3TSOL) routine.

At a minimum, every audit record has the header, subject, slabel, and return tokens. The auditwrite(3TSOL) routine call from the previous example generates the minimum audit record by specifying the AW_EVENT token command only.


Note -

Remember the proc_audit_appl privilege is needed in the effective set whenever you call auditwrite(3TSOL). The code comments indicate where privilege bracketing as described in Chapter 3, Privileges should take place. The remaining examples will not show the comments, because it is assumed you understand to do this.


/* Turn proc_audit_appl on in the effective set */
auditwrite(AW_EVENT, "AUE_second_signature", AW_WRITE, AW_END);
/* Turn the proc_audit_appl privilege off */

The output lines below have one token each. The first word on each line is the token ID followed by the token components. The description text defined in /etc/security/audit_event (second signature requested) is added to the header token.

By default the subject, slabel (sensitivity label), and return tokens are placed in the audit record even though the AW_SUBJECT, AW_SLABEL, and AW_RETURN token commands were not passed to this auditwrite(3TSOL) routine call.

If you pass AW_SUBJECT, AW_SLABEL, or AW_RETURN to the auditwrite(3TSOL) routine, you must explicitly define the token values. Auditing preselection and post-selection rely on the return token value to select audit records by success or failure. Always include the return token and the appropriate success or failure value in an audit record as described in "Return Token".


header, 4022,2,second signature requested,,Fri Sep 06
 10:16:49 1996 + 969 msec
subject,zelda,zelda,staff,zelda,staff,1774,348,0 0 phoenix
slabel,C
return,success,0

Token Structure

Trusted Solaris Audit Administration presents the structure for every token and byte sizes for each component. To help you get an idea of how to read the records and determine record size if space is a concern, the subject token structure is presented here.

Token 

ID 

Audit  

ID 

User 

ID 

Group 

ID 

Real user  

ID 

Real group  

ID 

Process 

ID 

Session 

ID 

Device 

ID 

Machine 

ID 

subject 

zelda 

root 

other 

root 

other 

1774 

348 

0 0 

phoenix 

1 byte 

4 bytes 

4 bytes 

4 bytes 

4 bytes 

4 bytes 

4 bytes 

4 bytes 

4 bytes 

4 bytes 

Return Token

The Return token is AW_RETURN and takes a number (success or fail) and a return value. By default the return token indicates success and shows a return value of 0. You will want to set the return token value just before passing the token to the auditwrite(3TSOL) routine.

The return value affects whether or not the audit record is logged to the audit trail. If the process preselection mask audits the class to which the event belongs for failures only, a successful event is not logged. If the process preselection mask audits the class to which the event belongs for success only, a failed event is not logged. If the process preselection mask audits the class to which the event belongs for success and failure, successful and failed events are both logged. Also, the auditreduce(1M) post-selection program selects audit records by the success or failure value in the record's return token.

This example creates part of an audit record showing that a second signature was requested by the system. The signature_request() function attempts to obtain the signature and returns information on the success or failure of the attempt and sets the signature_request and retval parameters. The succ_or_fail parameter is set according to the value in signature_request and it and retval are passed as values for the AW_RETURN token.

char succ_or_fail;
u_int retval;

auditwrite(AW_TEXT, "Second signature needed,"
	AW_APPEND, AW_END);

if (signature_request() == -1) {
	succ_or_fail = -1;
	retval = -2;
} else {
	succ_or_fail = 0;
	retval = 1;
}

auditwrite(AW_EVENT, "AUE_second_signature",
	AW_RETURN succ_or_fail, retval,
	AW_WRITE, AW_END);

The signature was not obtained and the viewing terminal shows this record:


header,128,2,second signature requested,,Wed 
  Sep 11 10:17:37 1996, + 239 msec
text, Second signature needed
return,failure,-2
subject,zelda,zelda,staff,zelda,staff,1905,536,0 0 phoenix
slabel,C

Queueing Audit Records

To minimize system call overhead, audit records can be queued (AW_QUEUE) and written in one call to the auditwrite(3TSOL) routine. In this example, audit queueing is turned on in the first call to the auditwrite() routine and set to flush entire records when the queue contains 200 bytes of audit records. You can force the queue to flush with the AW_FLUSH token, and the queue automatically flushes whenever it is turned off with the AW_NOQUEUE token.

In this example, the queue flushes manually after the second record is added in spite of the fact that the queue does not yet have 200 data bytes. The queue flushes again at the end when queueing is turned off.

The byte limit does not cause partial records to be written to the audit trail. If the queue holds 200 bytes, all records from all calls to the auditwrite(3TSOL) routine are flushed in their entirety when the limit is reached including any data bytes over 200 that make a complete audit record.

/* Set up queue to flush every 140 bytes */
	auditwrite(AW_QUEUE, 200, AW_END);

/* Write records to the queue */
	auditwrite(AW_EVENT, "AUE_second_signature",
		AW_TEXT, "First record in queue",
		AW_WRITE, AW_END);

	auditwrite(AW_EVENT, "AUE_second_signature_verify",
		AW_TEXT, "Second record in queue",
		AW_WRITE, AW_END);

/* Flush the queue manually */
	auditwrite(AW_FLUSH, AW_END);

/* Add another record */
	auditwrite(AW_EVENT, "AUE_second_signature",
		AW_TEXT, "Third record in queue",
		AW_WRITE, AW_END);

/* End queueing and flush */
	auditwrite(AW_NOQUEUE, AW_END);

The viewing terminal shows the following audit records:


header,204,3,second signature requested,,Mon Sep 09 10:26:28 1996,
 + 150 msec
text,First record in queue
subject,zelda,zelda,staff,zelda,staff,6098,5879,0 0 phoenix
slabel,C
return,success,0

header,204,4,second signature added,,Mon Sep 09 10:26:28 1996,
 + 152 msec
text,Second record in queue
subject,zelda,zelda,staff,zelda,staff,6098,5879,0 0 phoenix
slabel,C
return,success,0

header,204,5,second signature requested,,Mon Sep 09 10:26:28 1996,
 + 155 msec
text,Third record in queue
subject,zelda,zelda,staff,zelda,staff,6098,5879,0 0 phoenix
slabel,C
return,success,0

Specifying a Preselection Mask

Audit records are selected according to a process preselection mask set for the execution environment as explained in "Audit Control (Process Preselection Mask)". In addition, the auditwrite(3TSOL) routine has an AW_PRESELECT token that takes an audit mask structure for its value. This token causes subsequent calls to auditwrite() to audit classes based on the settings in the audit mask value until the AW_NOPRESELECT token is passed to auditwrite() telling it to use the environment preselection mask.

This example creates a process preselection mask to audit the example class (ec) for failures and passes it to the auditwrite(3TSOL) routine with AW_PRESELECT token to put it into effect. Now, only failed events belonging to the example class are logged to the audit trail. When preselection is turned off, the environment process preselection mask is restored, which for the purposes of these examples, audits events in the example class for success and failure.

char succ_or_fail;
u_int retval;
au_mask_t mask;

/* Create mask to audit failed events only in the ec class */
	getauditflagsbin("-ec", &mask);

/* Use new audit preselection mask */
	auditwrite(AW_PRESELECT, &mask, AW_END);

/* Code to generate audit records */
	auditwrite(AW_TEXT, "Second signature needed," AW_APPEND, AW_END);
	if (signature_request() == -1) {
		succ_or_fail = -1;
		retval = -2;
	} else {
		succ_or_fail = 0;
		retval = 1;
	}
	auditwrite(AW_EVENT, "AUE_second_signature",
		AW_RETURN succ_or_fail, retval, AW_WRITE, AW_END);

/* Restore environment preselection mask */
/* Events in the ec class are again audited for success and failure */
	auditwrite(AW_NOPRESELECT, AW_END);

Creating Audit Records in Parallel

Audit records are created with the default record descriptor unless record descriptors (AW_GETRD) are used (similar to file descriptors). This example uses record descriptors ad1 and ad2 to create two records in parallel, writes ad2 to the audit trail, and discards (AW_DISCARDRD) ad1. AW_DEFAULTRD (shown in the next example) switches record creation to the default record descriptor.

int ad1, ad2;
/* Get and use a record descriptor */
	auditwrite(AW_GETRD, &ad1, AW_END);
	auditwrite(AW_USERD, ad1, AW_END);

/* Append record information to the memory location at ad1 */
	auditwrite(AW_EVENT, "AUE_second_signature", AW_TEXT, "ad1 one", 
	AW_APPEND, AW_END);

/* Get second record descriptor */
	auditwrite(AW_GETRD, &ad2, AW_END);

/* Append record information to ad1 */
	auditwrite(AW_PATH, "/export/home/zelda/document_4_sig_req", 
	AW_APPEND, AW_END);

/* Use ad2 */
	auditwrite(AW_USERD, ad2, AW_END);

/* Append and write record at ad2 */
	auditwrite(AW_PATH, "/export/home/zelda/document_4_sig_ver", 
	AW_APPEND, AW_END);
	auditwrite(AW_EVENT, "AUE_second_signature_verify", 
	AW_WRITE, AW_END);

/* Discard ad1 */
	auditwrite(AW_DISCARDRD, ad1, AW_END);

The viewing terminal shows the following record:


header,141,2,second signature requested,,Wed Sep 11 11:16:29 1996,
 + 150 msec
path,/export/home/zelda/document_4_sig_ver
subject,zelda,zelda,staff,zelda,staff,1983,536,0 0 phoenix
slabel,C
return,success,0

Using the Save Area

You can turn on a save area (AW_SAVERD) and store tokens there to be prepended to records before they are written to the audit trail. Getting and using a save area is similar to getting and using a record descriptor. The save areas is turned off with AW_NOSAVE.

	int ad1, ad2, ad3;
/* Turn on and use save area ad1 */
	auditwrite(AW_SAVERD, &ad1, AW_END);
	auditwrite(AW_USERD, ad1, AW_END);

/* Put text at ad1 to be prepended to other records */
	auditwrite(AW_TEXT, "Prepended Text", AW_APPEND, AW_END);

/* Use the default record descriptor and write an event there */
	auditwrite(AW_DEFAULTRD, AW_END);
	auditwrite(AW_EVENT, "AUE_second_signature", 
		AW_TEXT, "Default record",
		AW_WRITE, AW_END);

/* Get and use record descriptor ad2 */
	auditwrite(AW_GETRD, &ad2, AW_END);
	auditwrite(AW_USERD, ad2, AW_END);

/* Write an event to ad2 */
	auditwrite(AW_EVENT, "AUE_second_signature", 
		AW_TEXT, "ad2 record", AW_WRITE, AW_END);

/* Discard the save area */
	auditwrite(AW_NOSAVE, AW_END);

/* Get and use record descriptor ad3 */
	auditwrite(AW_GETRD, &ad3, AW_END);
	auditwrite(AW_USERD, ad3, AW_END);

/* Write an event to ad3 */
	auditwrite(AW_EVENT, "AUE_second_signature_verify", 
   AW_TEXT, "ad3 with no prepend", AW_WRITE, AW_END);

The viewing terminal shows these records:


header,132,2,second signature requested,,Wed Sep 11 11:16:29 1996,
 + 133 msec
text,Prepended Text
text,Default record
subject,zelda,zelda,staff,zelda,staff,1983,536,0 0 phoenix
slabel,C
return,success,0

header,128,2,second signature requested,,Wed Sep 11 11:16:29 1996,
 + 140 msec
text,Prepended Text
text,ad2 record
subject,zelda,zelda,staff,zelda,staff,1983,536,0 0 phoenix
slabel,C

header,125,2,second signature added,,Wed Sep 11 11:16:29 1996,
 + 143 msec
text,ad3 with no prepend
subject,zelda,zelda,staff,zelda,staff,1983,536,0 0 phoenix
slabel,C

Using the Server Area and Adding a Sensitivity Label

The AW_SERVER token turns on the trusted server option, which indicates the calling process is a server. When the trusted server is enabled, the auditwrite(3TSOL) routine automatically generates header and return tokens, but not the subject and slabel tokens automatically generated when the trusted server is not enabled (see "Creating a Minimum Audit Record"). When the trusted server is enabled, you must explicitly pass the AW_SUBJECT and AW_SLABEL tokens to include this information in the record.

This example turns on the trusted server option, writes a record, writes another record including the sensitivity label, then turns off the trusted server option and writes a final record so you can see the difference. The sys_trans_label privilege is needed to translate the Secret sensitivity label because the process running at Confidential does not dominate Secret.

bslabel_t senslabel;

/* Create a sensitivity label of Secret */`
	stobsl("Secret", &senslabel, NEW_LABEL, &error);

/* Turn on the trusted server option */
	auditwrite(AW_SERVER, AW_END);

/* Write a record to the audit trail */
	auditwrite(AW_EVENT, "AUE_second_signature",
		AW_TEXT, "Some text",
		AW_WRITE, AW_END);

/* Write a record to the audit trail with the sensitivity label */
	auditwrite(AW_EVENT, "AUE_second_signature",
		AW_TEXT, "Sensitivity label added",
		AW_SLABEL, &senslabel,
		AW_WRITE, AW_END);

/* Turn off the trusted server option */
	auditwrite(AW_NOSERVER, AW_END);

/* Write a final record to the audit trail */
	auditwrite(AW_EVENT, "AUE_second_signature",
		AW_TEXT, "Some more text",
		AW_WRITE, AW_END);

The viewing terminal shows these records:


header,38,2,second signature requested,,Wed Sep 11 12:46:41 1996
 + 710 msec
text,Some text
return,success,0

header,38,2,second signature requested,,Wed Sep 11 12:46:41 1996
 + 780 msec
text,Sensitivity label added 
slabel,S
return,success,0

header,112,2,second signature requested,,Wed Sep 11 12:46:41 1996
 + 799 msec
text,Some more text
return,success,0

subject,zelda,zelda,staff,zelda,staff,420,286,0 0 phoenix
slabel,C
return,success,0

Argument Information

The AW_ARG token lets you write argument information to an audit record. This example writes the return value for the signature_request() function, which is really the first and only parameter to the return() call inside the function. The argument number follows the AW_ARG token, which is followed by descriptive text and the argument value.

retval = signature_request();
auditwrite(AW_EVENT,
	"AUE_second_signature",
	AW_ARG, 1,
	"Signature request return value",
	retval);

The viewing terminal shows this record where the return value is written as 0xffffffff:


header,137,3,second signature requested,,Fri Mar 21 08:51:19 1997,+ 329 msec
argument,1,0xffffffff,Signature request return value
subject,zelda,zelda,staff,zelda,staff,420,286,0 0 phoenix
slabel,C
return,success,0

Command Line Arguments

The AW_EXEC_ARGS token lets you place the command line arguments stored in argv in the audit record.

main(argc, argv)
int argc;
char **argv;
{
/* Application code */
/* ... */
	auditwrite(AW_EVENT,
		"AUE_second_signature",
		AW_EXEC_ARGS, argv
		AW_WRITE, AW_END);
}

The viewing terminal shows this record when the program is executed as follows: program Hello World!:


header,120,3,second signature requested,,Fri Mar 21 09:31:01 1997,
 +989 msec
exec_args,3,
program,Hello World!
subject,zelda,zelda,staff,zelda,staff,420,286,0 0 phoenix
slabel,C
return,success,0

Privilege Sets

The AW_PRIVILEGE token places a privilege set into the audit record. This example logs the allowed privilege set for the specified executable file to the audit record.

priv_set_t allowed_set;

PRIV_EMPTY(&allowed_set);

retval = getfpriv("/export/home/zelda/program",
	PRIV_ALLOWED,
	allowed_set);

auditwrite(AW_EVENT,
	"AUE_second_signature",
	AW_PRIVILEGE, AU_PRIV_ALLOWED, &allowed_set,
	AW_WRITE, AW_END);

The viewing terminal shows this record:


header,116,3,second signature requested,,Fri Mar 21
 10:12:21 1997, + 809 msec
privilege,allowed,proc_audit_appl
subject,zelda,zelda,staff,zelda,staff,420,286,0 0 phoenix
slabel,C
return,success,0

Interprocess Communications Identifier

The AW_IPC token places the specified interprocess communications (IPC) identifier into the audit record. This example creates a semaphore set and puts the semaphore identifier into the audit record.

int semid;

semid = semget(IPC_PRIVATE, 3, IPC_CREAT);

auditwrite(AW_EVENT,
	"AUE_second_signature",
	AW_IPC, AT_IPC_SEM, semid,
	AW_WRITE, AW_END);

The viewing terminal shows this record where 4 is the semaphore ID:


header,104,3,second signature requested,,Fri Mar 21
 12:45:21 1997, + 339 msec
IPC,sem,65539
subject,zelda,zelda,staff,zelda,staff,420,286,0 0 phoenix
slabel,C
return,success,0