xref: /illumos-gate/usr/src/cmd/auditreduce/proc.c (revision 856f710c9dc323b39da5935194d7928ffb99b67f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Main processor for auditreduce.
28  * Mproc() is the entry point for this module. It is the only visible
29  * function in this module.
30  */
31 
32 #include <sys/types.h>
33 #include <locale.h>
34 #include <bsm/libbsm.h>
35 #include <bsm/audit.h>
36 #include "auditr.h"
37 
38 extern int	write_header();
39 extern int	token_processing();
40 
41 static void	asort();
42 static audit_pcb_t *aget();
43 static int	get_file();
44 static int	write_recs();
45 static int	get_recs();
46 static int	check_rec();
47 static void	check_order();
48 static int	check_header();
49 static int	get_record();
50 
51 static char	empty_file_token[] = {
52 #ifdef _LP64
53 		AUT_OTHER_FILE64, /* token id */
54 		0, 0, 0, 0, 0, 0, 0, 0, /* seconds of time */
55 		0, 0, 0, 0, 0, 0, 0, 0, /* microseconds of time */
56 #else
57 		AUT_OTHER_FILE32, /* token id */
58 		0, 0, 0, 0, /* seconds of time */
59 		0, 0, 0, 0, /* microseconds of time */
60 #endif
61 		0, 0, /* length of path name */
62 };
63 
64 
65 /*
66  * .func	mproc - main processor.
67  * .desc	Mproc controls a single process's actions.
68  *	First one record is retreived from each pcb. As they are retreived
69  *	they are placed into a linked list sorted with oldest first. Then
70  *	the first one from the list is written out and another record
71  *	read in to replace it. The new record is placed into the list.
72  *	This continues until the list is empty.
73  * .call	ret = mproc(pcbr).
74  * .arg	pcbr	- ptr to pcb for this process.
75  * .ret	0	- no errors in processing.
76  * .ret	-1	- errors in processing (message already printed).
77  */
78 int
79 mproc(pcbr)
80 register audit_pcb_t *pcbr;
81 {
82 	int	i, ret, junk;
83 	int	nrecs = 0;		/* number of records read from stream */
84 	int	nprecs = 0;		/* number of records put to stream */
85 	register audit_pcb_t *pcb;
86 	audit_pcb_t *aget();
87 	void	asort();
88 
89 #if AUDIT_PROC_TRACE
90 	(void) fprintf(stderr, "mproc: count %d lo %d hi %d\n",
91 	    pcbr->pcb_count, pcbr->pcb_lo, pcbr->pcb_hi);
92 #endif
93 
94 	/*
95 	 * First load up a record from each input group.
96 	 */
97 	for (i = pcbr->pcb_lo; i <= pcbr->pcb_hi; i++) {
98 		pcb = &(pcbr->pcb_below[i]); /* get next PCB */
99 		while (pcb->pcb_time < 0) { /* while no active record ... */
100 			if ((ret = get_file(pcb)) == -1)
101 				break;		/*  no files - finished PCB */
102 			if (ret == -2)
103 				return (-1);	/* quit processing - failed */
104 			if (get_recs(pcb, &nrecs) == 0)
105 				asort(pcb);	/* got a rec - put in list */
106 		}
107 	}
108 	/*
109 	 * Now process all of the records.
110 	 */
111 	while ((pcb = aget()) != NULL) {	/* get oldest record */
112 		if (write_recs(pcbr, pcb, &nprecs))
113 			return (-1);
114 		while (pcb->pcb_time < 0) {	/* while we don't have a rec */
115 			if (pcb->pcb_fpr == NULL) {	/* no active file ... */
116 				if ((ret = get_file(pcb)) == -1)
117 					break;	/* no files - finished pcb */
118 				else if (ret == -2)
119 					return (-1);	/* quit - failed */
120 			}
121 			if (get_recs(pcb, &nrecs) == 0)
122 				asort(pcb);		/* put record in list */
123 		}
124 	}
125 	/*
126 	 * For root: write outfile header if no records were encountered.
127 	 * For non-root: write trailer to pipe and close pipe.
128 	 */
129 	if (pcbr->pcb_flags & PF_ROOT) {
130 		if (nprecs == 0) {
131 			if (write_header())	/* write header if no records */
132 				return (-1);
133 		}
134 	} else {
135 		pcb = &(pcbr->pcb_below[0]);	/* any old PCB will do */
136 		pcb->pcb_rec = empty_file_token;
137 		if (write_recs(pcbr, pcb, &junk))
138 			return (-1);
139 		if (fclose(pcbr->pcb_fpw) == EOF) {
140 			if (!f_quiet)
141 				(void) fprintf(stderr,
142 				    gettext("%s couldn't close pipe.\n"), ar);
143 		}
144 	}
145 	/*
146 	 * For root process tell how many records were written.
147 	 */
148 	if (f_verbose && (pcbr->pcb_flags & PF_ROOT)) {
149 		(void) fprintf(stderr,
150 		    gettext("%s %d record(s) total were written out.\n"),
151 			ar, nprecs);
152 	}
153 	return (0);
154 }
155 
156 
157 /*
158  * Head of linked-list of pcbs - sorted by time - oldest first.
159  */
160 static audit_pcb_t		*pcbls = NULL;
161 
162 /*
163  * .func	asort - audit sort.
164  * .desc	Place a pcb in the list sorted by time - oldest first.
165  * .call	asort(pcb);
166  * .arg	pcb	- ptr to pcb to install in list.
167  * .ret	void.
168  */
169 static void
170 asort(pcb)
171 register audit_pcb_t *pcb;
172 {
173 	register audit_pcb_t *pcbc, *pcbp;
174 	extern audit_pcb_t *pcbls;	/* ptr to start of list */
175 
176 	pcb->pcb_next = NULL;
177 	if (pcbls == NULL) {
178 		pcbls = pcb;		/* empty list */
179 		return;
180 	}
181 	pcbc = pcbls;			/* current pcb */
182 	pcbp = pcbls;			/* previous pcb */
183 	while (pcbc != NULL) {
184 		if (pcb->pcb_time < pcbc->pcb_time) {
185 			if (pcbp == pcbc) {
186 				pcb->pcb_next = pcbls;	/* new -> 1st in list */
187 				pcbls = pcb;
188 				return;
189 			}
190 			pcbp->pcb_next = pcb;
191 			pcb->pcb_next = pcbc;		/* new in the inside */
192 			return;
193 		}
194 		pcbp = pcbc;
195 		pcbc = pcbc->pcb_next;
196 	}
197 	pcbp->pcb_next = pcb;				/* new -> last */
198 }
199 
200 
201 /*
202  * .func	aget - audit get.
203  * .desc	Get the first pcb from the list. Pcb is removed from list, too.
204  * .call	pcb = aget().
205  * .arg	none.
206  * .ret	pcb	- ptr to pcb that was the first.
207  */
208 static audit_pcb_t *
209 aget()
210 {
211 	audit_pcb_t *pcbret;
212 	extern audit_pcb_t *pcbls;	/* ptr to start of list */
213 
214 	if (pcbls == NULL)
215 		return (pcbls);		/* empty list */
216 	pcbret = pcbls;
217 	pcbls = pcbls->pcb_next;	/* 2nd becomes 1st */
218 	return (pcbret);
219 }
220 
221 
222 /*
223  * .func	get_file - get a new file.
224  * .desc	Get the next file from the pcb's list. Check the header to see
225  *	if the file really is an audit file. If there are no more then
226  *	quit. If a file open (fopen) fails because the system file table
227  *	is full or the process file table is full then quit processing
228  *	altogether.
229  * .call	ret = get_file(pcb).
230  * .arg	pcb	- pcb holding the fcb's (files).
231  * .ret	0	- new file opened for processing.
232  * .ret	-1	- no more files - pcb finished.
233  * .ret	-2	- fatal error - quit processing.
234  */
235 static int
236 get_file(pcb)
237 register audit_pcb_t *pcb;
238 {
239 	FILE *fp;
240 	audit_fcb_t *fcb;
241 
242 	/*
243 	 * Process file list until a good one if found or empty.
244 	 */
245 	while (pcb->pcb_fpr == NULL) {
246 		if ((fcb = pcb->pcb_first) == NULL) {
247 			pcb->pcb_time = -1;
248 			return (-1);	/* pcb is all done */
249 		} else {
250 		/*
251 		 * If we are reading from files then open the next one.
252 		 */
253 			if (!f_stdin) {
254 				if ((fp = fopen(fcb->fcb_file, "r")) == NULL) {
255 					if (!f_quiet) {
256 						(void) sprintf(errbuf, gettext(
257 						"%s couldn't open:\n  %s"),
258 						ar, fcb->fcb_file);
259 						perror(errbuf);
260 					}
261 					/*
262 					 * See if file space is depleted.
263 					 * If it is then we quit.
264 					 */
265 					if (errno == ENFILE || errno == EMFILE)
266 					{
267 						return (-2);
268 					}
269 					pcb->pcb_first = fcb->fcb_next;
270 					continue;	/* try another file */
271 				}
272 			} else {
273 				/*
274 				 * Read from standard input.
275 				 */
276 				fp = stdin;
277 			}
278 			/*
279 			 * Check header of audit file.
280 			 */
281 			if (check_header(fp, fcb->fcb_name)) {
282 				if (!f_quiet) {
283 					(void) fprintf(stderr,
284 					    "%s %s:\n  %s.\n",
285 					    ar, error_str, fcb->fcb_file);
286 				}
287 				if (fclose(fp) == EOF) {
288 					if (!f_quiet) {
289 						(void) fprintf(stderr, gettext(
290 						"%s couldn't close %s.\n"),
291 						ar, fcb->fcb_file);
292 					}
293 				}
294 				pcb->pcb_first = fcb->fcb_next;
295 				continue;		/* try another file */
296 			}
297 			/*
298 			 * Found a good audit file.
299 			 * Initalize pcb for processing.
300 			 */
301 			pcb->pcb_first = fcb->fcb_next;
302 			pcb->pcb_cur = fcb;
303 			pcb->pcb_fpr = fp;
304 			pcb->pcb_nrecs = 0;
305 			pcb->pcb_nprecs = 0;
306 			pcb->pcb_otime = -1;
307 		}
308 	}
309 	return (0);
310 }
311 
312 
313 /*
314  * .func	write_recs - write records.
315  * .desc	Write record from a buffer to output stream. Keep an eye out
316  *	for the first and last records of the root's output stream.
317  * .call	ret = write_recs(pcbr, pcb, nprecs).
318  * .arg	pcbr	- ptr to node pcb.
319  * .arg	pcb		- ptr to pcb holding the stream.
320  * .arg	nprecs	- ptr to the number of put records. Updated here.
321  * .ret	0	- no errors detected.
322  * .ret	-1	- error in writing. Quit processing.
323  */
324 static int
325 write_recs(pcbr, pcb, nprecs)
326 register audit_pcb_t *pcbr, *pcb;
327 int	*nprecs;
328 {
329 	adr_t adr;
330 	char	id;
331 	int32_t	size;
332 
333 	adrm_start(&adr, pcb->pcb_rec);
334 	(void) adrm_char(&adr, &id, 1);
335 	(void) adrm_int32(&adr, &size, 1);
336 
337 	/*
338 	 * Scan for first record to be written to outfile.
339 	 * When we find it then write the header and
340 	 * save the time for the outfile name.
341 	 */
342 	if ((*nprecs)++ == 0) {
343 		if (pcbr->pcb_flags & PF_ROOT) {
344 			f_start = pcb->pcb_time;	/* save start time */
345 			if (write_header())
346 				return (-1);
347 		}
348 	}
349 	f_end = pcb->pcb_time;			/* find last record's time */
350 	pcb->pcb_time = -1;			/* disable just written rec */
351 
352 	if ((fwrite(pcb->pcb_rec, sizeof (char), size, pcbr->pcb_fpw)) !=
353 			size) {
354 		if (pcbr->pcb_flags & PF_ROOT) {
355 			(void) sprintf(errbuf, gettext(
356 				"%s write failed to %s"),
357 				ar, f_outfile ? f_outfile : gettext("stdout"));
358 			perror(errbuf);
359 		} else {
360 			perror(gettext("auditreduce: write failed to pipe"));
361 		}
362 		return (-1);
363 	}
364 	free(pcb->pcb_rec);
365 	return (0);
366 }
367 
368 /*
369  * .func get_recs - get records.
370  * .desc Get records from a stream until one passing the current selection
371  *	criteria is found or the stream is emptied.
372  * .call	ret = get_recs(pcb, nr).
373  * .arg	pcb	- ptr to pcb that holds this stream.
374  * .arg	nr	- ptr to number of records read. Updated by this routine.
375  * .ret	0	- got a record.
376  * .ret	-1	- stream is finished.
377  */
378 static int
379 get_recs(pcb, nr)
380 register audit_pcb_t *pcb;
381 int	*nr;
382 {
383 	adr_t adr;
384 	time_t secs;
385 	int	tmp;
386 	int	ret, ret2;
387 	int	nrecs = 0;	/* count how many records read this call */
388 	int	getrec = TRUE;
389 	int	alldone = FALSE;
390 	char	header_type;
391 	short	e;
392 	char	*str;
393 #if AUDIT_FILE
394 	static void	get_trace();
395 #endif
396 
397 	while (getrec) {
398 		ret = get_record(pcb->pcb_fpr, &pcb->pcb_rec,
399 			pcb->pcb_cur->fcb_name);
400 		if (ret > 0) {
401 			adrm_start(&adr, pcb->pcb_rec);
402 
403 			/* get token id */
404 			(void) adrm_char(&adr, (char *)&header_type, 1);
405 			/* skip over byte count */
406 			(void) adrm_int32(&adr, (int32_t *)&tmp, 1);
407 			/* skip over version # */
408 			(void) adrm_char(&adr, (char *)&tmp, 1);
409 			/* skip over event id */
410 			(void) adrm_short(&adr, (short *)&e, 1);
411 			/* skip over event id modifier */
412 			(void) adrm_short(&adr, (short *)&tmp, 1);
413 
414 			if (header_type == AUT_HEADER32) {
415 			    int32_t s, m;
416 
417 			    /* get seconds */
418 			    (void) adrm_int32(&adr, (int32_t *)&s, 1);
419 			    /* get microseconds */
420 			    (void) adrm_int32(&adr, (int32_t *)&m, 1);
421 			    secs = (time_t)s;
422 			} else if (header_type == AUT_HEADER32_EX) {
423 			    int32_t s, m;
424 			    int32_t t, junk[4];	/* at_type + at_addr[4] */
425 
426 			    /* skip type and ip address field */
427 			    (void) adrm_int32(&adr, (int32_t *)&t, 1);
428 			    (void) adrm_int32(&adr, (int32_t *)&junk[0], t/4);
429 
430 			    /* get seconds */
431 			    (void) adrm_int32(&adr, (int32_t *)&s, 1);
432 			    /* get microseconds */
433 			    (void) adrm_int32(&adr, (int32_t *)&m, 1);
434 			    secs = (time_t)s;
435 			} else if (header_type == AUT_HEADER64) {
436 			    int64_t s, m;
437 
438 			    /* get seconds */
439 			    (void) adrm_int64(&adr, (int64_t *)&s, 1);
440 			    /* get microseconds */
441 			    (void) adrm_int64(&adr, (int64_t *)&m, 1);
442 #if ((!defined(_LP64)) || defined(_SYSCALL32))
443 			    if (s < (time_t)INT32_MIN ||
444 				s > (time_t)INT32_MAX)
445 					secs = 0;
446 			    else
447 					secs = (time_t)s;
448 #else
449 			    secs = (time_t)s;
450 #endif
451 			} else if (header_type == AUT_HEADER64_EX) {
452 			    int64_t s, m;
453 			    int32_t t, junk[4];
454 
455 			    /* skip type and ip address field */
456 			    (void) adrm_int32(&adr, (int32_t *)&t, 1);
457 			    (void) adrm_int32(&adr, (int32_t *)&junk[0], t/4);
458 
459 			    /* get seconds */
460 			    (void) adrm_int64(&adr, (int64_t *)&s, 1);
461 			    /* get microseconds */
462 			    (void) adrm_int64(&adr, (int64_t *)&m, 1);
463 #if ((!defined(_LP64)) || defined(_SYSCALL32))
464 			    if (s < (time_t)INT32_MIN ||
465 				s > (time_t)INT32_MAX)
466 					secs = 0;
467 			    else
468 					secs = (time_t)s;
469 #else
470 			    secs = (time_t)s;
471 #endif
472 			}
473 		}
474 
475 #if AUDIT_REC
476 		(void) fprintf(stderr, "get_recs: %d ret %d recno %d\n",
477 			pcb->pcb_procno, ret, pcb->pcb_nrecs + 1);
478 #endif
479 		/*
480 		 * See if entire file is after the time window specified.
481 		 * Must be check here because the start time of the file name
482 		 * may be after the first record(s).
483 		 */
484 		if (pcb->pcb_nrecs == 0 && (pcb->pcb_flags & PF_USEFILE)) {
485 			/*
486 			 * If the first record read failed then use the time
487 			 * that was in the filename to judge.
488 			 */
489 			if (ret > 0)
490 				(pcb->pcb_cur)->fcb_start = secs;
491 			if (!f_all && (m_before <= (pcb->pcb_cur)->fcb_start)) {
492 				(void) fclose(pcb->pcb_fpr); /* ignore file */
493 				pcb->pcb_fpr = NULL;
494 				pcb->pcb_time = -1;
495 				return (-1);
496 			} else {
497 				/* Give belated announcement of file opening. */
498 				if (f_verbose) {
499 					(void) fprintf(stderr,
500 						gettext("%s opened:\n  %s.\n"),
501 						ar, (pcb->pcb_cur)->fcb_file);
502 				}
503 			}
504 		}
505 		/* Succesful acquisition of a record.  */
506 		if (ret > 0) {
507 			pcb->pcb_time = secs;	/* time of record */
508 			pcb->pcb_nrecs++;	/* # of read recs from stream */
509 			nrecs++;		/* # of recs read this call */
510 			/* Only check record if at bottom of process tree. */
511 			if (pcb->pcb_flags & PF_USEFILE) {
512 				check_order(pcb); /* check time sequence */
513 				if ((ret2 = check_rec(pcb)) == 0) {
514 					pcb->pcb_nprecs++;
515 					getrec = FALSE;
516 				} else if (ret2 == -2) {
517 					/* error */
518 					getrec = FALSE;	/* get no more recs */
519 					alldone = TRUE;	/* quit this file */
520 					free(pcb->pcb_rec);
521 				} else {
522 					/* -1: record not interesting */
523 					free(pcb->pcb_rec);
524 				}
525 			} else {
526 				pcb->pcb_nprecs++;
527 				getrec = FALSE;
528 			}
529 		} else {
530 			/* Error with record read or all done with stream. */
531 			getrec = FALSE;
532 			alldone = TRUE;
533 		}
534 	}
535 	if (alldone == TRUE) {
536 #if AUDIT_FILE
537 		get_trace(pcb);
538 #endif
539 		/* Error in record read. Display messages. */
540 		if (ret < 0 || ret2 == -2) {
541 			pcb->pcb_nrecs++;	/* # of read records */
542 			if (!f_quiet) {
543 				if (pcb->pcb_flags & PF_USEFILE) {
544 					/* Ignore if this is not_terminated. */
545 					if (!strstr((pcb->pcb_cur)->fcb_file,
546 							"not_terminated")) {
547 (void) fprintf(stderr, gettext("%s read error in %s at record %d.\n"), ar,
548 	(pcb->pcb_cur)->fcb_file, pcb->pcb_nrecs);
549 					}
550 				} else {
551 (void) fprintf(stderr, gettext("%s read error in pipe at record %d.\n"), ar,
552 	pcb->pcb_nrecs);
553 				}
554 			}
555 		} else {
556 			/*
557 			 * Only mark infile for deleting if we have succesfully
558 			 * processed all of it.
559 			 */
560 			if (pcb->pcb_flags & PF_USEFILE)
561 				(pcb->pcb_cur)->fcb_flags |= FF_DELETE;
562 		}
563 		if (fclose(pcb->pcb_fpr) == EOF) {
564 			if (!f_quiet) {
565 				if (pcb->pcb_flags & PF_USEFILE) {
566 					str = (pcb->pcb_cur)->fcb_file;
567 				} else {
568 					str = "pipe";
569 				}
570 				(void) fprintf(stderr,
571 					gettext("%s couldn't close %s.\n"),
572 					ar, str);
573 			}
574 		}
575 		pcb->pcb_fpr = NULL;
576 		pcb->pcb_time = -1;
577 		*nr += nrecs;
578 		return (-1);
579 	}
580 	*nr += nrecs;
581 	return (0);
582 }
583 
584 
585 #if AUDIT_FILE
586 /*
587  * .func get_trace - get trace.
588  * .desc If we are tracing file action (AUDIT_FILE is on) then print out
589  *	a message when the file is closed regarding how many records
590  *	were handled.
591  * .call	get_trace(pcb).
592  * .arg	pcb	- ptr to pcb holding file/pipe.
593  * .ret	void.
594  */
595 static void
596 get_trace(pcb)
597 audit_pcb_t *pcb;
598 {
599 	/*
600 	 * For file give filename, too.
601 	 */
602 	if (pcb->pcb_flags & PF_USEFILE) {
603 	(void) fprintf(stderr, "%s closed %s: %d records read recs: \
604 		%d record written.\n", ar, (pcb->pcb_cur)->fcb_file,
605 		pcb->pcb_nrecs, pcb->pcb_nprecs);
606 	} else {
607 		(void) fprintf(stderr, "%s closed pipe: %d records read: \
608 			%d records written .\n", ar, pcb->pcb_nrecs,
609 			pcb->pcb_nprecs);
610 	}
611 }
612 
613 #endif
614 
615 /*
616  * .func	check_rec - check a record.
617  * .desc	Check a record against the user's selection criteria.
618  * .call	ret = check_rec(pcb).
619  * .arg	pcb	- ptr to pcb holding the record.
620  * .ret	0	- record accepted.
621  * .ret	-1	- record rejected - continue processing file.
622  * .ret	-2	- record rejected - quit processing file.
623  */
624 static int
625 check_rec(pcb)
626 register audit_pcb_t *pcb;
627 {
628 	adr_t adr;
629 	struct timeval tv;
630 	uint_t	bytes;
631 	au_emod_t id_modifier;
632 	char	version;
633 	au_event_t event_type;
634 	char	tokenid;
635 	int	rc;	 /* return code */
636 
637 	adrm_start(&adr, pcb->pcb_rec);
638 	(void) adrm_char(&adr, &tokenid, 1);
639 
640 	/*
641 	 * checkflags will be my data structure for determining if
642 	 * a record has met ALL the selection criteria.  Once
643 	 * checkflags == flags, we have seen all we need to of the
644 	 * record, and can go to the next one.  If when we finish
645 	 * processing the record we still have stuff to see,
646 	 * checkflags != flags, and thus we should return a -1
647 	 * from this function meaning reject this record.
648 	 */
649 
650 	checkflags = 0;
651 
652 	/* must be header token -- sanity check */
653 	if (tokenid != AUT_HEADER32 && tokenid != AUT_HEADER64 &&
654 	    tokenid != AUT_HEADER32_EX && tokenid != AUT_HEADER64_EX) {
655 #if AUDIT_REC
656 		(void) fprintf(stderr,
657 		    "check_rec: %d recno %d no header %d found\n",
658 		    pcb->pcb_procno, pcb->pcb_nrecs, tokenid);
659 #endif
660 		return (-2);
661 	}
662 
663 	/*
664 	 * The header token is:
665 	 *	attribute id:		char
666 	 *	byte count:		int
667 	 *	version #:		char
668 	 *	event ID:		short
669 	 *	ID modifier:		short
670 	 *	seconds (date):		int
671 	 *	time (microsecs):	int
672 	 */
673 	(void) adrm_u_int32(&adr, (uint32_t *)&bytes, 1);
674 	(void) adrm_char(&adr, &version, 1);
675 	(void) adrm_u_short(&adr, &event_type, 1);
676 
677 	/*
678 	 * Used by s5_IPC_token to set the ipc_type so
679 	 * s5_IPC_perm_token can test.
680 	 */
681 	ipc_type = (char)0;
682 
683 	if (flags & M_TYPE) {
684 		checkflags |= M_TYPE;
685 		if (m_type != event_type)
686 			return (-1);
687 	}
688 	if (flags & M_CLASS) {
689 		au_event_ent_t *ev = NULL;
690 
691 		checkflags |= M_CLASS;
692 		if (cacheauevent(&ev, event_type) <= 0) {
693 		    (void) fprintf(stderr, gettext(
694 			"Warning: invalid event no %d in audit trail."),
695 			event_type);
696 		    return (-1);
697 		}
698 		global_class = ev->ae_class;
699 		if (!(flags & M_SORF) && !(mask.am_success & global_class))
700 			return (-1);
701 	}
702 
703 	(void) adrm_u_short(&adr, &id_modifier, 1);
704 
705 	/*
706 	 * Check record against time criteria.
707 	 * If the 'A' option was used then no time checking is done.
708 	 * The 'a' parameter is inclusive and the 'b' exclusive.
709 	 */
710 	if (tokenid == AUT_HEADER32) {
711 	    int32_t secs, msecs;
712 	    (void) adrm_int32(&adr, (int32_t *)&secs, 1);
713 	    (void) adrm_int32(&adr, (int32_t *)&msecs, 1);
714 	    tv.tv_sec = (time_t)secs;
715 	    tv.tv_usec = (suseconds_t)msecs;
716 	} else if (tokenid == AUT_HEADER32_EX) {
717 	    int32_t secs, msecs;
718 	    int32_t t, junk[5];	/* at_type + at_addr[4] */
719 	    /* skip type and ip address field */
720 	    (void) adrm_int32(&adr, (int32_t *)&t, 1);
721 	    (void) adrm_int32(&adr, (int32_t *)&junk[0], t/4);
722 	    /* get time */
723 	    (void) adrm_int32(&adr, (int32_t *)&secs, 1);
724 	    (void) adrm_int32(&adr, (int32_t *)&msecs, 1);
725 	    tv.tv_sec = (time_t)secs;
726 	    tv.tv_usec = (suseconds_t)msecs;
727 	} else if (tokenid == AUT_HEADER64) {
728 	    int64_t secs, msecs;
729 	    (void) adrm_int64(&adr, (int64_t *)&secs, 1);
730 	    (void) adrm_int64(&adr, (int64_t *)&msecs, 1);
731 #if ((!defined(_LP64)) || defined(_SYSCALL32))
732 	    if (secs < (time_t)INT32_MIN ||
733 		secs > (time_t)INT32_MAX)
734 			tv.tv_sec = 0;
735 	    else
736 			tv.tv_sec = (time_t)secs;
737 	    if (msecs < (suseconds_t)INT32_MIN ||
738 		msecs > (suseconds_t)INT32_MAX)
739 			tv.tv_usec = 0;
740 	    else
741 			tv.tv_usec = (suseconds_t)msecs;
742 #else
743 	    tv.tv_sec = (time_t)secs;
744 	    tv.tv_usec = (suseconds_t)msecs;
745 #endif
746 	} else if (tokenid == AUT_HEADER64_EX) {
747 	    int64_t secs, msecs;
748 	    int32_t t, junk[4];	/* at_type + at_addr[4] */
749 	    /* skip type and ip address field */
750 	    (void) adrm_int32(&adr, (int32_t *)&t, 1);
751 	    (void) adrm_int32(&adr, (int32_t *)&junk[0], t/4);
752 	    /* get time */
753 	    (void) adrm_int64(&adr, (int64_t *)&secs, 1);
754 	    (void) adrm_int64(&adr, (int64_t *)&msecs, 1);
755 #if ((!defined(_LP64)) || defined(_SYSCALL32))
756 	    if (secs < (time_t)INT32_MIN ||
757 		secs > (time_t)INT32_MAX)
758 			tv.tv_sec = 0;
759 	    else
760 			tv.tv_sec = (time_t)secs;
761 	    if (msecs < (suseconds_t)INT32_MIN ||
762 		msecs > (suseconds_t)INT32_MAX)
763 			tv.tv_usec = 0;
764 	    else
765 			tv.tv_usec = (suseconds_t)msecs;
766 #else
767 	    tv.tv_sec = (time_t)secs;
768 	    tv.tv_usec = (suseconds_t)msecs;
769 #endif
770 	}
771 	pcb->pcb_otime = pcb->pcb_time;
772 	if (!f_all) {
773 		if (m_after > tv.tv_sec)
774 			return (-1);
775 		if (m_before <= tv.tv_sec)
776 			return (-1);
777 	}
778 
779 	/* if no selection flags were passed, select everything */
780 	if (!flags)
781 		return (0);
782 
783 	/*
784 	 * If all information can be found in header,
785 	 * there is no need to continue processing the tokens.
786 	 */
787 	if (flags == checkflags)
788 		return (0);
789 
790 	/*
791 	 * Process tokens until we hit the end of the record
792 	 */
793 	while ((uint_t)(adr.adr_now - adr.adr_stream) < bytes) {
794 		adrm_char(&adr, &tokenid, 1);
795 		rc = token_processing(&adr, tokenid);
796 
797 		/* Any Problems? */
798 		if (rc == -2) {
799 			(void) fprintf(stderr,
800 			    gettext("auditreduce: bad token %u, terminating "
801 			    "file %s\n"), tokenid, (pcb->pcb_cur)->fcb_file);
802 			return (-2);
803 		}
804 
805 		/* Are we finished? */
806 		if (flags == checkflags)
807 			return (0);
808 	}
809 
810 	/*
811 	 * So, we haven't seen all that we need to see.  Reject record.
812 	 */
813 
814 	return (-1);
815 }
816 
817 
818 /*
819  * .func check_order - Check temporal sequence.
820  * .call check_order(pcb).
821  * .arg	 pcb - ptr to audit_pcb_t.
822  * .desc	Check to see if the records are out of temporal sequence, ie,
823  *	a record has a time stamp older than its predecessor.
824  *	Also check to see if the current record is within the bounds of
825  *	the file itself.
826  *	This routine prints a diagnostic message, unless the QUIET
827  *	option was selected.
828  * .call	check_order(pcb).
829  * .arg	pcb	- ptr to pcb holding the records.
830  * .ret	void.
831  */
832 static void
833 check_order(pcb)
834 register audit_pcb_t *pcb;
835 {
836 	char	cptr1[28], cptr2[28];	/* for error reporting */
837 
838 	/*
839 	 * If the record-past is not the oldest then say so.
840 	 */
841 	if (pcb->pcb_otime > pcb->pcb_time) {
842 		if (!f_quiet) {
843 			(void) memcpy((void *)cptr1,
844 				(void *)ctime(&pcb->pcb_otime), 26);
845 			cptr1[24] = ' ';
846 			(void) memcpy((void *)cptr2,
847 				(void *)ctime(&pcb->pcb_time), 26);
848 			cptr2[24] = ' ';
849 			(void) fprintf(stderr,
850 	gettext("%s %s had records out of order: %s was followed by %s.\n"),
851 				ar, (pcb->pcb_cur)->fcb_file, cptr1, cptr2);
852 		}
853 	}
854 }
855 
856 
857 /*
858  * .func	check_header.
859  * .desc	Read in and check the header for an audit file.
860  *	The header must read-in properly and have the magic #.
861  * .call	err = check_header(fp).
862  * .arg	fp	- file stream.
863  * .ret	0	no problems.
864  * .ret	-1	problems.
865  */
866 static int
867 check_header(fp, fn)
868 FILE *fp;
869 char	*fn;
870 {
871 	char	id;
872 	char	*fname;
873 	short	pathlength;
874 	adr_t	adr;
875 	adrf_t	adrf;
876 
877 	adrf_start(&adrf, &adr, fp);
878 
879 	if (adrf_char(&adrf, &id, 1)) {
880 		(void) sprintf(errbuf, gettext("%s is empty"), fn);
881 		error_str = errbuf;
882 		return (-1);
883 	}
884 	if (!(id == AUT_OTHER_FILE32 || id == AUT_OTHER_FILE64)) {
885 		(void) sprintf(errbuf, gettext("%s not an audit file "), fn);
886 		error_str = errbuf;
887 		return (-1);
888 	}
889 
890 	if (id == AUT_OTHER_FILE32) {
891 	    int32_t secs, msecs;
892 	    (void) adrf_int32(&adrf, (int32_t *)&secs, 1);
893 	    (void) adrf_int32(&adrf, (int32_t *)&msecs, 1);
894 	} else {
895 	    int64_t secs, msecs;
896 	    (void) adrf_int64(&adrf, (int64_t *)&secs, 1);
897 	    (void) adrf_int64(&adrf, (int64_t *)&msecs, 1);
898 #if ((!defined(_LP64)) || defined(_SYSCALL32))
899 	    if (secs < (time_t)INT32_MIN ||
900 		secs > (time_t)INT32_MAX) {
901 		    error_str = gettext("bad time stamp in file header");
902 		    return (-1);
903 	    }
904 	    if (msecs < (suseconds_t)INT32_MIN ||
905 		msecs > (suseconds_t)INT32_MAX) {
906 		    error_str = gettext("bad time stamp in file header");
907 		    return (-1);
908 	    }
909 #endif
910 	}
911 
912 	if (adrf_short(&adrf, &pathlength, 1)) {
913 		error_str = gettext("incomplete file header");
914 		return (-1);
915 	}
916 
917 	if (pathlength != 0) {
918 		fname = (char *)a_calloc(1, (size_t)pathlength);
919 		if ((fread(fname, sizeof (char), pathlength, fp)) !=
920 				pathlength) {
921 			(void) sprintf(errbuf,
922 				gettext("error in header/filename read in %s"),
923 				fn);
924 			error_str = errbuf;
925 			return (-1);
926 		}
927 		free(fname);
928 	}
929 	return (0);
930 }
931 
932 
933 /*
934  * .func	get_record - get a single record.
935  * .desc	Read a single record from stream fp. If the record to be read
936  *	is larger than the buffer given to hold it (as determined by
937  *	cur_size) then free that buffer and allocate a new and bigger
938  *	one, making sure to store its size.
939  * .call	ret = get_record(fp, buf, cur_size, flags).
940  * .arg	fp	- stream to read from.
941  * .arg	buf	- ptr to ptr to buffer to place record in.
942  * .arg	cur_size- ptr to the size of the buffer that *buf points to.
943  * .arg	flags	- flags from fcb (to get FF_NOTTERM).
944  * .ret	+number	- number of chars in the record.
945  * .ret	0	- trailer seen - file done.
946  * .ret	-1	- read error (error_str know what type).
947  */
948 static int
949 get_record(fp, buf, fn)
950 FILE *fp;
951 char	**buf;
952 char	*fn;
953 {
954 	adr_t	adr;
955 	adrf_t	adrf;
956 	int	leadin;
957 	char	id;
958 	int	lsize;
959 	short	ssize;
960 
961 	/*
962 	 * Get the token type. It will be either a header or a file
963 	 * token.
964 	 */
965 	(void) adrf_start(&adrf, &adr, fp);
966 	if (adrf_char(&adrf, &id, 1)) {
967 		(void) sprintf(errbuf, gettext(
968 			"record expected but not found in %s"),
969 			fn);
970 		error_str = errbuf;
971 		return (-1);
972 	}
973 	switch (id) {
974 	case AUT_HEADER32:
975 	case AUT_HEADER32_EX:
976 	case AUT_HEADER64:
977 	case AUT_HEADER64_EX:
978 		/*
979 		 * The header token is:
980 		 *	attribute id:		char
981 		 *	byte count:		int
982 		 *	version #:		char
983 		 *	event ID:		short
984 		 *	ID modifier:		short
985 		 *	IP address type		int	(_EX only)
986 		 *	IP address		1/4*int (_EX only)
987 		 *	seconds (date):		long
988 		 *	time (microsecs):	long
989 		 */
990 		leadin = sizeof (int32_t) + sizeof (char);
991 		(void) adrf_int32(&adrf, &lsize, 1);
992 		*buf = (char *)a_calloc(1, (size_t)(lsize + leadin));
993 		adr_start(&adr, *buf);
994 		adr_char(&adr, &id, 1);
995 		adr_int32(&adr, (int32_t *)&lsize, 1);
996 		if (fread(*buf + leadin, sizeof (char), lsize - leadin, fp) !=
997 			lsize - leadin) {
998 			(void) sprintf(errbuf,
999 				gettext("header token read failure in %s"), fn);
1000 			error_str = errbuf;
1001 			return (-1);
1002 		}
1003 		return (lsize + leadin);
1004 	case AUT_OTHER_FILE32: {
1005 		int32_t secs, msecs;
1006 		leadin =  2 * sizeof (int32_t) +
1007 				sizeof (short) + sizeof (char);
1008 		(void) adrf_int32(&adrf, (int32_t *)&secs, 1);
1009 		(void) adrf_int32(&adrf, (int32_t *)&msecs, 1);
1010 		(void) adrf_short(&adrf, &ssize, 1);
1011 		*buf = (char *)a_calloc(1, (size_t)(ssize + leadin));
1012 		adr_start(&adr, *buf);
1013 		adr_char(&adr, &id, 1);
1014 		adr_int32(&adr, (int32_t *)&secs, 1);
1015 		adr_int32(&adr, (int32_t *)&msecs, 1);
1016 		adr_short(&adr, &ssize, 1);
1017 		if (fread(*buf + leadin, sizeof (char), ssize, fp) != ssize) {
1018 			error_str = gettext("file token read failure");
1019 			return (-1);
1020 		}
1021 		return (0);		/* done! */
1022 	}
1023 	case AUT_OTHER_FILE64: {
1024 		int64_t secs, msecs;
1025 		leadin =  2 * sizeof (int64_t) +
1026 				sizeof (short) + sizeof (char);
1027 		(void) adrf_int64(&adrf, (int64_t *)&secs, 1);
1028 		(void) adrf_int64(&adrf, (int64_t *)&msecs, 1);
1029 		(void) adrf_short(&adrf, &ssize, 1);
1030 		*buf = (char *)a_calloc(1, (size_t)(ssize + leadin));
1031 		adr_start(&adr, *buf);
1032 		adr_char(&adr, &id, 1);
1033 		adr_int64(&adr, (int64_t *)&secs, 1);
1034 		adr_int64(&adr, (int64_t *)&msecs, 1);
1035 		adr_short(&adr, &ssize, 1);
1036 		if (fread(*buf + leadin, sizeof (char), ssize, fp) != ssize) {
1037 			error_str = gettext("file token read failure");
1038 			return (-1);
1039 		}
1040 		return (0);		/* done! */
1041 	}
1042 	default:
1043 		break;
1044 	}
1045 	error_str = gettext("record begins without proper token");
1046 	return (-1);
1047 }
1048