This chapter provides information on the mechanics of writing and reading audit records. The following topics are covered:
Disabling auditing for the current process. (See Chapter 10 for information on managing the audit subsystem.)
Parsing audit logs. (This section provides the low-level detail needed to develop additional utilities for audit data analysis.)
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:
AUTH_EVENT
is the record event name.
[Return to example]
AUD_T_LOGIN
,
AUD_T_UID
,
and
AUD_T_CHARP
are tokens, each with a corresponding value.
[Return to example]
<sys/audit.h>
.
See
Section 19.2
and
Section 19.3
for descriptions of events and tokens.
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:
Trusted events, which are defined in
<sys/audit.h>
with values between
MIN_TRUSTED_EVENT
and
(MIN_TRUSTED_EVENT + N_TRUSTED_EVENTS -1)
.
For example, the
LOGIN
event.
Site-defined events, which are defined in
/etc/sec/audit_events
with values between
MIN_SITE_EVENT
and 1048576.
The default range for site-defined events is 64.
For information on defining
site events, see
Section 19.8.
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:
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.
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.)
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"); }
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:
Process audit control flag
Process audit mask
System audit mask
The process audit control flag has four exclusive states:
An audit record is generated if either the system audit mask or the process audit mask indicates such an event should be audited.
An audit record is generated if both the system audit mask and the process audit mask indicate such an event should be audited.
No audit records are generated for the current process.
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:
Turns off system call record generation for the process.
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:
The buffer containing the mask.
The
<sys/audit.h>
header file contains trusted event names.
The
<sys/*syscall.h>
header files contain system call names.
Indicates whether
to audit success; a
1
means audit event success.
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:
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]
ess_read
is the first subevent; 0 is the
first subevent number.
[Return to example]
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:
A description of an audit log plus a list of the token types generally found in all audit records.
The binary record format with examples showing an octal dump of a record and its formatted output.
A table of token/tuple byte descriptions, which lists the data types and format for each public and private token.
Sample macros for parsing tuples.
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 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.3.
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_LSOCK | 016 | |
AUD_T_RSOCK | 017 | |
AUD_T_LHOSTNAME | 020 | |
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_LHOSTNAME | 066 | |
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 |
The algorithm for reading a stream of audit records is as follows:
Open the audit log.
Find the first audit record (starts and ends with
AUD_TP_LENGTH
tuples).
Check that the record length matches the value in the
AUD_TP_LENGTH
tuple.
(If the length does not match, discard the
record.)
Retrieve the first tuple following the
AUD_TP_LENGTH
tuple.
If the tuple length is variable, determine the size of the data.
Extract the data.
Retrieve the next tuple, check the length if necessary, and extract the data.
Repeat until no more records.
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;