19    Audit Record Generation

This chapter provides information on the mechanics of writing and reading audit records. The following topics are covered:

19.1    Introduction

Trusted programs can use the audgen() system call, the audgenl() library routine, or the audgen command to generate audit records; audgenl() is a front-end to audgen(). For arguments, the program supplies an audit event followed by audit data consisting of audit tokens and values.

The following code fragment shows how a program that checks boot authentication can call audgenl() to audit authentication failure:

if(audgenl(AUTH_EVENT,  [1]
           AUD_T_LOGIN, pr->ufld.fd_name,  [2]
           AUD_T_UID, pr->ufld.fd_uid,
           AUD_T_CHARP, "boot authentication failed"),0)== -1)
perror("audgenl");
 
 

Notes:

  1. AUTH_EVENT is the record event name. [Return to example]

  2. AUD_T_LOGIN, AUD_T_UID, and AUD_T_CHARP are tokens, each with a corresponding value. [Return to example]

These identifiers are defined in <sys/audit.h>. See Section 19.2 and Section 19.3 for descriptions of events and tokens.

19.2    Audit Events

Each audit record has an audit event associated with it. The system automatically adds the event when generating system call audit records. Self-auditing application programs pass the event as an argument to audgen() or audgenl() when generating audit records. There are two types of audit events available to application programs:

19.3    Audit Records and Tokens

The audit subsystem has no fixed record type. Instead, an audit record is a series of tuples (data objects containing two or more components). Each tuple consists of an audit token and its corresponding value; depending on the token type, the tuple might contain a length field.

The following sections describe the two types of tokens: public tokens and private tokens. Application programs use public tokens.

19.3.1    Public Tokens

Public tokens are available to application programs that generate audit records using audgen() and audgenl(). Public tokens are defined in <sys/audit.h> and begin with AUD_T_; for example, AUD_T_CHARP.

There are three basic types of public tokens:

pointer

Used to represent data strings or structures as pointers. AUD_T_CHARP (character string) and AUD_T_HOMEDIR (home directory) are two pointer-type tokens.

iovec

Used to represent data as iovec-formatted data. AUD_T_OPAQUE, and AUD_T_INTARRAY are two iovec-type tokens. (Look for the iovec comments in <sys/audit.h>. The iovec structure is defined in <sys/uio.h>. For information about iovec see the readv(2) and writev(2) reference pages.)

fixed length

Used to represent data as a 32- or 64-bit quantity. (AUD_T_RESULT and AUD_TP_LONG are 64-bit; others are 32-bit.) Most tokens use fixed-length data. AUD_T_AUID (audit ID), AUD_T_UID (user ID), and AUD_T_PID (process ID) are examples of fixed-length tokens.

The following example generates an audit record using iovec-formatted data:

#define AUD_COMPAT
#include <sys/audit.h>
#include <sys/uio.h>
 
main()
{
     char buf[100];
     int i;
     struct iovec iov;
 
     for (i = 0; i < sizeof(buf); i++)
          buf[i] = i;
 
     iov.iov_len = sizeof(buf);
     iov.iov_base = buf;
 
     if (audgenl (AUDGEN8,
                  AUD_T_CHARP, "opaque data test",
                  AUD_T_OPAQUE, &iov,
                  0 ) ==  -1 )
         perror ("audgenl");
}

19.3.2    Private Tokens

Private tokens are used by the kernel; they are not available to application programs. The audgen() system call rejects any attempts by application programs to write records that contain private tokens. Private tokens are defined in <sys/audit.h> and begin with AUD_TP_; for example AUD_TP_AUID.

The kernel uses the private tokens when creating audit records. For example, the kernel encapsulates each audit record with AUD_TP_LENGTH tuples whose value is the length of the audit record. Another example is the audgen() or audgenl() event argument, from which the kernel creates a AUD_TP_EVENT tuple.

19.4    Audit Flag and Masks

Whether an audit event actually results in the generation of an audit record depends on the following flag and mask settings:

The process audit control flag has four exclusive states:

AUDIT_OR

An audit record is generated if either the system audit mask or the process audit mask indicates such an event should be audited.

AUDIT_AND

An audit record is generated if both the system audit mask and the process audit mask indicate such an event should be audited.

AUDIT_OFF

No audit records are generated for the current process.

AUDIT_USR

An audit record is generated if the process audit mask indicates such an event should be audited.

The process audit control flag also has two nonexclusive states:

AUDIT_SYSCALL_OFF

Turns off system call record generation for the process.

AUDIT_HABITAT_USR

Turns on the habitat system calls in the user mask for the process even if system calls are turned off for the system mask. The habitat system calls are: System V - unlink() and open(); real time - memlk(), memunlk(), psx4_time_drift(), and rt_setprio(). These habitat system calls are turned on or off as a group. See Appendix B for the habitat events.

The system administrator can establish a default audit level for users, while retaining the ability to audit any individual user at whatever level the administrator deems appropriate. (See Chapter 10 for information on configuring and administering the audit subsystem.)

From a programmer's perspective, a privileged process can set its audit level (specify what gets audited), either as an absolute mask or in relation to the system audit mask. See Section 19.6 for an example showing how to set a process's audit mask. See audcntl(2) and auditmask(8) for more information.

19.5    Disabling System-Call Auditing for the Current Process

Controlling which events are audited is an important step in fine-tuning the amount of audit data collected. System calls can generate large amounts of audit data, but this data is not necessarily useful information. In general, actively auditing the modification of fields in a security-relevant database or auditing a specific security-relevant action provides more usable information than trying to derive this information from a multitude of system-call audit records. For example, the login process executes thousands of system calls, but a single informative audit record written by the login process uses less system resources and is easier to understand.

Application programs can disable system-call auditing but still allow trusted-event auditing. The following code fragment shows how to use the audcntl() system call to set AUDIT_SYSCALL_OFF:

    /* OR the AUDIT_SYSCALL_OFF bit into the audcntl flag */
    if ((cntlflag = audcntl(GET_PROC_ACNTL,
                            NULL, 0, 0, 0, 0)) == -1)
        perror("audcntl");
    else
        audcntl(SET_PROC_ACNTL, NULL, 0,
                cntlflag|AUDIT_SYSCALL_OFF, 0, 0);

19.6    Modifying System-Call Auditing for the Current Process

A process can control what is audited for itself or another process by modifying the target process's auditmask and audcntl flags. You can modify the current process's audit mask as follows:

    /* ex. set the process's auditmask to audit only LOGIN
       events and successful setgroups calls
    */
    #include <sys/audit.h>
    #include <sys/syscall.h>
    char buf[AUDIT_MASK_LEN];
    
.
.
.
bzero (buf, sizeof(buf)); A_PROCMASK_SET (buf, LOGIN, 1, 1); A_PROCMASK_SET (buf, SYS_setgroups, 1, 0); if (audcntl (SET_PROC_AMASK, buf, AUDIT_MASK_LEN, 0, 0, 0) == -1) perror ("audcntl");

The A_PROCMASK_SET macro, defined in <sys/audit.h>, takes the following arguments:

buf

The buffer containing the mask.

event name

The <sys/audit.h> header file contains trusted event names. The <sys/*syscall.h> header files contain system call names.

succeed

Indicates whether to audit success; a 1 means audit event success.

fail

Indicates whether to audit failure; a 1 means audit event failure.

See audcntl(2) for more information.

19.7    Application-Specific Audit Records

An application program provides application-specific audit data as arguments to audgen() or audgenl().

The following code fragment sends an audit record to the kernel when the specified event occurs. The event is either a trusted event from <sys/audit.h> or a site-defined event from /etc/sec/site_events. (Whether the kernel actually writes an audit record to the audit log depends on the events audited for this process.)

/* If bad_thing occurs, generate an event of type event_num,
 * with string "bad thing happened", and a result of 66.
 */
 
#include <sys/audit.h>
 
if (bad_thing) {
   if (audgenl (event_num,
                AUD_T_CHARP, "bad thing happened",
                AUD_T_RESULT, 66, 0 ) == -1)
      perror ("audgenl");
}

In general, an application-generated audit record does not have to include data for the tokens listed in Table 19-1. The kernel automatically adds this information to each audit record. However, the audit subsystem does not prevent you from putting any of the public token tuples in an audit record; for example, you can add an AUD_T_AUID tuple to an audit record even though the system will later add an AUD_TP_AUID to the record. Both tuples are written to the audit log.

19.8    Site-Defined Events

A site can define its own set of audit events, called site-defined events, in the locally created and maintained file /etc/sec/site_events. The file contains one entry for each site event.

The potential range for site event numbers is MIN_SITE_EVENT (defined in <sys/audit.h>) to 1048576. The default range is 64. To change this value, set audit-site-events in /etc/sysconfigtab and reboot. For example, to allow for up to 128 site-defined events:

 
     sec:
         audit-site-events=128

Each site-event entry can contain up to INT_MAX subevents. There is no default range defined for subevents.

The maximum length for an event or subevent name is AUD_MAXEVENT_LEN, defined in <sys/audit.h>.

Application programs can generate records containing both site-defined events and the trusted events defined in <sys/audit.h> (MIN_TRUSTED_EVENT to MAX_TRUSTED_EVENT).

The auditmask utility supports preselection for site-defined events, and the audit_tool utility supposts postselection for site-defined events and subevents.

19.8.1    Sample site_events File

The syntax for a site-defined audit event entry is:

[event_name event_number [ , subevent_name subevent_number ... ] ;]

The following entries in a sample /etc/sec/site_events file demonstrate how to create site-defined events and subevents:

essence 2048,     [1]
    ess_read 0,   [2]
    ess_write 1;  [3]
rdb 2049,
    rdb_open 0,
    rdb_close 1,
    rdb_read 2,
    rdb_write 3;
decinspect 2050;

Notes:

  1. essence is the event; 2048 is the event number. Note that 2048 is MIN_SITE_EVENT, the lowest number available for site-defined events. [Return to example]

  2. ess_read is the first subevent; 0 is the first subevent number. [Return to example]

  3. ess_write is the second subevent; 1 is the second subevent number. [Return to example]

See aud_sitevent(3) for more information on site-defined events.

19.8.2    Example - Generating an Audit Record for a Site-Defined Audit Event

The following code fragment uses audgenl() to generate audit data for an rdb_close event:

int event_num, subevent_num;
 
/* translate event name(s) into event numbers */
if (aud_sitevent_num ("rdb", "rdb_close",
                      &event_num, &subevent_num ))
    printf ("aud_sitevent_num failed");
 
/* generate audit data */
else if (audgenl (event_num,
                  AUD_T_SUBEVENT, subevent_num,
                  AUD_T_CHARP, "Trusted RDB V1.0 Close",
                  0) == -1)
    perror ("audgenl");

Compaq recommends that you include an AUD_T_CHARP, event name argument pair with audgenl() when generating a record for a site-defined event. Doing so simplifies the task of analyzing audit data on a system that does not have a copy of the local site_events file.

See aud_sitevent(3) and audgenl(3) for more information.

19.9    Creating Your Own Audit Logs

You can use the audgen() system call to create your own audit log. If the size argument to audgen() is a nonzero value, audit data is copied to the userbuff specified in audgen() rather than written to the system audit logs. A trusted application can then write the data in userbuff to a unique log file. See audgen(2) for more information.

You can use the audit_tool utility to read the new audit log. More detailed information can be read from the log using the information in Section 19.10.

19.10    Parsing an Audit Log

Most people use audit_tool or dxaudit to read audit logs. The audit_tool utility is a sophisticated program that converts audit data into useful information, formats output, and handles audit records that span audit log files. When audit_tool first reads an audit log, it creates a corresponding .hdr file to maintain state information. This state information reduces the time needed for subsequent reads of the audit logs. Also, if an audit record spans audit logs, audit_tool opens both log files and creates a complete record in the header file.

The following sections describe the format and construction of audit logs; they provide:

These sections do not provide the design information needed to create a program similar to audit_tool; they do provide the basic information required to parse an audit log into records and tuples.

19.10.1    Overview of Audit Log Format and List of Common Tuples

Audit logs are regular UNIX data files that contain audit records. An audit record consists of a series of tuples whose format is either token:value or token:length:value. Each record starts and ends with an AUD_TP_LENGTH tuple. (The audit_tool utility uses AUD_TP_LENGTH to determine whether an audit record is valid. If the actual length of the record does not match the AUD_TP_LENGTH value, audit_tool discards the record and provides a warning.) Table 19-1 shows the default tuples generally used for audit records.

Table 19-1:  Default Tuples Common to Most Audit Records

Tuple Comment Tuple Comment
AUD_TP_LENGTH   AUD_TP_VERSION  
AUD_TP_AUID   AUD_TP_RUID  
AUD_TP_HOSTADDR   AUD_TP_EVENTP if habitat
AUD_TP_HABITAT if habitat AUD_TP_EVENT  
AUD_TP_UID   AUD_TP_PID  
AUD_TP_PPID   AUD_TP_DEV if device is associated with a process
AUD_TP_NCPU   AUD_TP_TV_USEC  
AUD_TP_SET_UIDS if uid change AUD_TP_TID if AUDIT_USR flag is set

19.10.2    Binary Audit Log Record Format

The following example is an octal dump of one audit record taken from an audit log.

253 007 001 000 000 266 002 300     000 000 241 000 000 000 000 242 
000 000 000 000 246 020 217 202     131 247 012 002 000 000 243 000 
000 000 000 244 247 002 000 000     245 231 002 000 000 251 000 000 
000 000 257 201 076 321 061 260     260 336 004 000 013 030 000 000 
000 001 000 000 000 000 000 000     000 000 000 000 000 000 000 000 
000 000 000 000 000 000 000 000     000 014 050 000 000 000 001 000 
000 000 000 000 000 000 000 000     000 000 000 000 000 000 000 000 
000 000 000 000 000 000 000 000     000 000 000 000 000 000 000 000 
000 000 000 000 000 000 004 005     000 000 000 162 157 157 164 000 
005 002 000 000 000 057 000 006     010 000 000 000 057 142 151 156 
057 163 150 000 007 003 000 000     000 072 060 000 001 015 000 000 
000 141 162 147 166 075 144 170     154 157 147 151 156 000 001 020 
000 000 000 114 157 147 151 156     040 163 165 143 143 145 145 144 
145 144 000 032 034 000 000 000     001 000 000 000 000 000 000 000 
003 000 000 000 007 000 000 000     011 000 000 000 014 000 000 000 
026 000 000 000 051 000 000 000     000 052 000 000 000 000 000 000 
000 000 253 007 001 000 000

The values contained in the record are as follows:

In the next example, the previous audit record is disassembled into its component tuples. The left column contains the audit token, derived from the octal number in parentheses. See the <sys/audit.h> file for the #defines. If the tuple is of type token:length:value, the length is also included in the parentheses, separated from the token octal value by a slash. For example, look at the first T_CHARP tuple. The 1 indicates the token, T_CHARP; the 13 indicates that the null-terminated character string is 13 bytes in length. The remainder of each line is the tuple's value, shown either in octal format or as a character string.

AUD_TP_LENGTH      (253):   007 001 000 000 
AUD_TP_VERSION     (266):   002 300 000 000 
AUD_TP_AUID        (241):   000 000 000 000 
AUD_TP_RUID        (242):   000 000 000 000 
AUD_TP_HOSTADDR    (246):   020 217 202 131 
AUD_TP_EVENT       (247):   012 002 000 000 
AUD_TP_UID         (243):   000 000 000 000 
AUD_TP_PID         (244):   247 002 000 000 
AUD_TP_PPID        (245):   231 002 000 000 
AUD_TP_NCPU        (251):   000 000 000 000 
AUD_TP_TV_SEC      (257):   201 076 321 061 
AUD_TP_TV_USEC     (260):   260 336 004 000 
AUD_T_SLABEL       (13/24): 01 00 00 00 00 00 00 00 \
                            00 00 00 00 00 00 00 00 \
                            00 00 00 00 00 00 00 00 
AUD_T_ILABEL       (14/40): 01 00 00 00 00 00 00 00 \
                            00 00 00 00 00 00 00 00 \
                            00 00 00 00 00 00 00 00 \
                            00 00 00 00 00 00 00 00 \
                            00 00 00 00 00 00 00 00 
AUD_T_LOGIN        (4/5):   root
AUD_T_HOMEDIR      (5/2):   /
AUD_T_SHELL        (6/8):   /bin/sh
AUD_T_DEVNAME      (7/3):   :0
AUD_T_CHARP        (1/13):  argv=dxlogin
AUD_T_CHARP        (1/16):  Login succeeded
AUD_T_GIDSET       (32/28):
AUD_T_ERRNO        (51):    000 000 000 000 
AUD_T_RESULT       (52):    000 000 000 000 000 000 000 000 
AUD_TP_LENGTH      (253):   007 001 000 000

From this entry in the audit log, audit_tool produces the following record.

audit_id:    0          ruid/euid:   0/0        username: root
pid:         679        ppid: 665                  
subj il[sl]: UNCLASSIFIED [UNCLASSIFIED]
event:       login
login name:  root
home dir:    /
shell:       /bin/sh
devname:     :0
char param:  argv=dxlogin
char param:  Login succeeded
groups:      1 0 3 7 9 12 22 
directory:   /
result:      0
ip address:  16.143.130.89 (<hostname_here>)
timestamp:   Wed Jun 26 09:43:29.31 1996 EDT
cpu # = 0x0  version # = 0xc002

19.10.3    Token/Tuple Byte Descriptions

Table 19-2 lists public and private tokens with their octal values. For each tuple, the third column lists the sequence in which tuple data is written to an audit log by the kernel. Token is a 1-byte quantity; length is a 4-byte quantity. Sample Parse Macro refers to the macro that audit_tool uses to parse the tuple. These macros are provided, for reference purposes only, in Section 19.10.4.

Table 19-2:  Token/Tuple Byte Descriptions

Token Octal Value Tuple Format and Sample Parse Macro
AUD_T_CHARP 001 token:length:null-terminated ASCII string. PARSE_DEF3
AUD_T_SOCK 003 token:length:struct sockaddr (4.3 style (u_short); if family is > UCHAR_MAX, assume 4.4 style sockaddr of length (byte) then family (byte)). PARSE_DEF3
AUD_T_LOGIN 004 token:length:null-terminated ASCII string. PARSE_DEF3
AUD_T_HOMEDIR 005 token:length:null-terminated ASCII string. PARSE_DEF3
AUD_T_SHELL 006 token:length:null-terminated ASCII string. PARSE_DEF3
AUD_T_DEVNAME 007 token:length:null-terminated ASCII string. PARSE_DEF3
AUD_T_SERVICE 010 token:length:null-terminated ASCII string. (reserved for future use)
AUD_T_HOSTNAME 011 token:length:null-terminated ASCII string. PARSE_DEF3
AUD_T_INTP 012 token:length:int (First element is number of elements in array; note that AUD_T_INTARRAY is the preferred tuple when generating an audit record.) PARSE_DEF3
AUD_T_OPAQUE 030 token:length:value. (proplist or truly opaque; check for proplist name/value pairs else dump as hex._ PARSE_DEF6
AUD_T_INTARRAY 031 token:length:int. PARSE_DEF3
AUD_T_GIDSET 032 token:length:int1, int2, ... (unaligned). PARSE_DEF3
AUD_T_XDATA 033 token:struct aud_xdata (See <sys/audit.h>.) PARSE_DEF8
AUD_T_AUID 040 token:int. PARSE_DEF2
AUD_T_RUID 041 token:int. PARSE_DEF2
AUD_T_UID 042 token:int. PARSE_DEF2
AUD_T_PID 043 token:int. PARSE_DEF2
AUD_T_PPID 044 token:int. PARSE_DEF2
AUD_T_GID 045 token:unsigned int. PARSE_DEF2
AUD_T_EVENT 046 token:int. PARSE_DEF2
AUD_T_SUBEVENT 047 token:int. PARSE_DEF2
AUD_T_DEV 050 token:int (Parse using the major()/minor() macros from <sys/types.h>.) PARSE_DEF2
AUD_T_ERRNO 051 token:int. PARSE_DEF1
AUD_T_RESULT 052 token:long. PARSE_DEF4
AUD_T_MODE 053 token:unsigned int. PARSE_DEF2
AUD_T_HOSTADDR 054 token:unsigned int. PARSE_DEF2
AUD_T_INT 055 token:int. PARSE_DEF2
AUD_T_DESCRIP 056 token:int (file descriptor). PARSE_DEF2
AUD_T_HOSTID 057 token:int. PARSE_DEF1
AUD_T_X_ATOM 060 token:unsigned int. PARSE_DEF2
AUD_T_X_CLIENT 061 token:int. PARSE_DEF2
AUD_T_X_PROPERTY 062 token:int. PARSE_DEF2
AUD_T_X_RES_CLASS 063 token:unsigned int. PARSE_DEF2
AUD_T_X_RES_TYPE 064 token:unsigned int. PARSE_DEF2
AUD_T_X_RES_ID 065 token:unsigned int. PARSE_DEF2
AUD_T_SECEVENT 177 token:int. PARSE_DEF2
AUD_TP_ACCRGHT 201 token:length:cmsg_data (fd1, fd2, ... - See <sys/socket.h>.) PARSE_DEF3
AUD_TP_MSGHDR 202 token:length:msghdr->msg_name (See <sys/socket.h>.) PARSE_DEF3
AUD_TP_EVENTP 203 token:length: string. PARSE_DEF3
AUD_TP_HABITAT 204 token:length:string. PARSE_DEF3
AUD_TP_ADDRVEC 205 token:length:struct sockaddr (See socket.h.) PARSE_DEF3
AUD_TP_INTP 206 token:length:int. PARSE_DEF3
AUD_TP_AUID 241 token:int. PARSE_DEF1
AUD_TP_RUID 0242 token:int. PARSE_DEF1
AUD_TP_UID 0243 token:int. PARSE_DEF1
AUD_TP_PID 0244 token:int. PARSE_DEF1
AUD_TP_PPID 0245 token:int. PARSE_DEF1
AUD_TP_HOSTADDR 246 token:unsigned int. PARSE_DEF1
AUD_TP_EVENT 247 token:int. PARSE_DEF1
AUD_TP_SUBEVENT 250 token:int (Reserved for future use.) PARSE_DEF1
AUD_TP_NCPU 251 token:int. PARSE_DEF1
AUD_TP_DEV 252 token:int (Parse using the major()/minor() macros from sys/types.h.) PARSE_DEF1
AUD_TP_LENGTH 253 token:int. PARSE_DEF1
AUD_TP_IPC_GID 254 token:unsigned int (ipc|msg|shm_perm.gid). PARSE_DEF2
AUD_TP_IPC_MODE 255 token:unsigned int (ipc|msg|shm_perm.mode). PARSE_DEF2
AUD_TP_IPC_UID 256 token:int (ipc|msg|shm_perm.uid). PARSE_DEF2
AUD_TP_TV_SEC 257 token:timeval.tv_sec (See <sys/time.h>.) PARSE_DEF1
AUD_TP_TV_USEC 260 token:timeval.tv_usec (See <sys/time.h>.) PARSE_DEF1
AUD_TP_SHORT 261 token:short. PARSE_DEF2
AUD_TP_LONG 262 token:long. PARSE_DEF5
AUD_TP_VNODE_DEV 263 token:int. PARSE_DEF2
AUD_TP_VNODE_ID 264 token:unsigned int. PARSE_DEF2
AUD_TP_VNODE_MODE 265 token:unsigned int. PARSE_DEF2
AUD_TP_VERSION 266 token:unsigned int (see <sys/audit.h>) (AUD_VERSION | AUD_VERS_LONG). PARSE_DEF1
AUD_TP_SET_UIDS 267 token:int. PARSE_DEF2
AUD_TP_CONT 270 token:unsigned int (A unique int for each component of a record.) PARSE_DEF1
AUD_TP_TID 271 token:long. PARSE_DEF4
AUD_TP_PRIV 272 token:unsigned short. PARSE_DEF1

19.10.4    Parsing Tuples

The algorithm for reading a stream of audit records is as follows:

  1. Open the audit log.

  2. Find the first audit record (starts and ends with AUD_TP_LENGTH tuples).

  3. Check that the record length matches the value in the AUD_TP_LENGTH tuple. (If the length does not match, discard the record.)

  4. Retrieve the first tuple following the AUD_TP_LENGTH tuple.

  5. If the tuple length is variable, determine the size of the data.

  6. Extract the data.

  7. Retrieve the next tuple, check the length if necessary, and extract the data.

  8. Repeat until no more records.

  9. Close the audit log.

The following macros illustrate how audit_tool parses tuples. The macros are provided for reference purposes only; they illustrate one approach. Note that indx values are maintained and used by audit_tool; they are not part of the audit record tuple.

/* fixed length scalar value */
#define PARSE_DEF1(tokentype,field) \
  bcopy (&rec_ptr[i+sizeof token], &field, sizeof(field)); \
  i += (sizeof token + sizeof(field)); \
  break;
 
/* fixed length field in array */
#define PARSE_DEF2(tokentype,field,indx) \
  if (indx < AUD_NPARAM) \
      bcopy (&rec_ptr[i+sizeof token], &field[indx++], sizeof(field[0])); \
  i += (sizeof token + sizeof(field[0])); \
  break;
 
/* array of strings */
#define PARSE_DEF3(tokentype,len,field,indx) \
  bcopy (&rec_ptr[i+sizeof token], &j, sizeof(int)); \
  if (j >= rec_len) j = 0; \
  if (indx < AUD_NPARAM) { \
      len[indx] = j; \
      field[indx++] = (char *)&rec_ptr[i+(sizeof token)+(sizeof *intp)]; \
  } \
  i += (sizeof token + sizeof *intp + j); \
  break;
 
/* fixed length scalar value whose size is h/w dependent (32 or 64-bit) */
#define PARSE_DEF4(tokentype,field) \
  bzero (field.val, sizeof(field.val)); \
  j = af->version & AUD_VERS_LONG ? sizeof(int)*2 : sizeof(int); \
  bcopy (&rec_ptr[i+sizeof token], field.val, j); \
  i += (sizeof token + j); \
  break;
 
/* fixed length field in array whose size is h/w dependent (32 or 64-bit) */
#define PARSE_DEF5(tokentype,field,indx) \
  bzero (field[indx].val, sizeof(field[indx].val)); \
  j = af->version & AUD_VERS_LONG ? sizeof(int)*2 : sizeof(int); \
  if (indx < AUD_NPARAM) \
      bcopy (&rec_ptr[i+sizeof token], field[indx++].val, j); \
  i += (sizeof token + j); \
  break;
 
/* array of opaque data streams */
#define PARSE_DEF6 PARSE_DEF3
 
/* iovec element in array */
#define PARSE_DEF7(tokentype,field,indx) \
  j = sizeof(field[0]); \
  if (indx < AUD_NPARAM) { \
      bcopy (&rec_ptr[i+sizeof token], &j, sizeof(int)); \
      if (j > rec_len ) j = 0; \
      bcopy (&rec_ptr[i+sizeof token+sizeof(int)], &field[indx++], j); \
  } \
  i += (sizeof token + sizeof(int) + j); \
  break;
 
/* array of iovec elements with variable length components */
#define PARSE_DEF8(tokentype,field,ptr,indx) \
  j = sizeof(field[0]); \
  if (indx < AUD_NPARAM) { \
      bcopy (&rec_ptr[i+sizeof token], &j, sizeof(int)); \
      if (j > rec_len) j = 0; \
      bcopy (&rec_ptr[i+sizeof token+sizeof(int)], &field[indx], j); \
      ptr[indx++] = ((struct aud_xdata *) \
                    &rec_ptr[i+sizeof token+sizeof(int)])->xdata; \
  } \
  i += (sizeof token + sizeof(int) + j); \
  break;