xref: /illumos-gate/usr/src/cmd/auditreduce/option.c (revision 7c478bd9)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Command line option processing for auditreduce.
31  * The entry point is process_options(), which is called by main().
32  * Process_options() is the only function visible outside this module.
33  */
34 
35 #include <locale.h>
36 #include <sys/zone.h>	/* for max zonename length */
37 
38 #ifdef	TSOL
39 #include <tsol/label.h>
40 #endif	/* TSOL */
41 
42 #include "auditr.h"
43 
44 /*
45  * Object entry.
46  * Maps object strings specified on the command line to a flag
47  * used when searching by object type.
48  */
49 
50 struct obj_ent {
51 	char	*obj_str; /* string specified on the command line */
52 	int	obj_flag; /* flag used when searching */
53 };
54 
55 typedef struct obj_ent obj_ent_t;
56 
57 /*
58  * Supports searches by object type.
59  */
60 static obj_ent_t obj_tbl[] = {
61 			{ "file", OBJ_PATH },
62 			{ "filegroup", OBJ_FGROUP },
63 			{ "fileowner", OBJ_FOWNER },
64 			{ "lp", OBJ_LP   },
65 			{ "msgqid", OBJ_MSG  },
66 			{ "msgqgroup", OBJ_MSGGROUP },
67 			{ "msgqowner", OBJ_MSGOWNER },
68 			{ "path", OBJ_PATH },
69 			{ "pid", OBJ_PROC },
70 			{ "procgroup", OBJ_PGROUP },
71 			{ "procowner", OBJ_POWNER },
72 			{ "semid", OBJ_SEM  },
73 			{ "semgroup", OBJ_SEMGROUP  },
74 			{ "semowner", OBJ_SEMOWNER  },
75 			{ "shmid", OBJ_SHM  },
76 			{ "shmgroup", OBJ_SHMGROUP  },
77 			{ "shmowner", OBJ_SHMOWNER  },
78 			{ "sock", OBJ_SOCK } };
79 
80 extern int	derive_date(char *, struct tm *);
81 extern int	parse_time(char *, int);
82 extern char	*re_comp2(char *);
83 extern time_t	tm_to_secs(struct tm *);
84 
85 static int	a_isnum(char *, int);
86 static int	check_file(audit_fcb_t *, int);
87 static int	gather_dir(char *);
88 static audit_pcb_t *get_next_pcb(char *);
89 static obj_ent_t *obj_lkup(char *);
90 static int	proc_class(char *);
91 static int	proc_date(char *, int);
92 static int	proc_file(char *, int);
93 static int	process_fileopt(int, char *argv[], int);
94 static int	proc_group(char *, gid_t *);
95 static int	proc_id(char *, int);
96 static int	proc_object(char *);
97 static void	proc_pcb(audit_pcb_t *, char *, int);
98 #ifdef	TSOL
99 static int	proc_slabel(char *);
100 #endif	/* TSOL */
101 static int	proc_subject(char *);
102 static int	proc_type(char *);
103 static int	proc_user(char *, uid_t *);
104 static int	proc_zonename(char *);
105 
106 /*
107  * .func	process_options - process command line options.
108  * .desc	Process the user's command line options. These are of two types:
109  *	single letter flags that are denoted by '-', and filenames. Some
110  *	of the flags have arguments. Getopt() is used to get the flags.
111  *	When this is done it calls process_fileopt() to handle any filenames
112  *	that were there.
113  * .call	ret = process_options(argc, argv).
114  * .arg	argc	- the original value.
115  * .arg	argv	- the original value.
116  * .ret	0	- no errors detected.
117  * .ret	-1	- command line error detected (message already printed).
118  */
119 int
120 process_options(int argc, char **argv)
121 {
122 	int	opt;
123 	int	error = FALSE;
124 	int	error_combo = FALSE;
125 	extern int	optind;		/* in getopt() */
126 	extern char	*optarg;	/* in getopt() - holds arg to flag */
127 
128 	static char	*options = "ACD:M:NQR:S:VO:a:b:c:d:e:g:j:m:o:r:s:u:z:";
129 
130 	error_str = gettext("general error");
131 
132 	zonename = NULL;
133 	/*
134 	 * Big switch to process the flags.
135 	 * Start_over: is for handling the '-' for standard input. Getopt()
136 	 * doesn't recognize it.
137 	 */
138 start_over:
139 	while ((opt = getopt(argc, argv, options)) != EOF) {
140 		switch (opt) {
141 		case 'A':		/* all records from the files */
142 			f_all = TRUE;
143 			break;
144 		case 'C':		/* process only completed files */
145 			f_complete = TRUE;
146 			break;
147 		case 'D':		/* delete the files when done */
148 			/* force 'A' 'C' 'O' to be active */
149 			f_all = f_complete = TRUE;
150 			f_outfile = optarg;
151 			f_delete = TRUE;
152 			break;
153 		case 'M':		/* only files from a certain machine */
154 			f_machine = optarg;
155 			break;
156 		case 'N':		/* new object selection mode */
157 			new_mode = TRUE;
158 			break;
159 		case 'Q':		/* no file error reporting */
160 			f_quiet = TRUE;
161 			break;
162 		case 'R':		/* from specified root */
163 			f_root = optarg;
164 			break;
165 		case 'S':		/* from specified server */
166 			f_server = optarg;
167 			break;
168 		case 'V':		/* list all files as they are opened */
169 			f_verbose = TRUE;
170 			break;
171 		case 'O':		/* write to outfile */
172 			f_outfile = optarg;
173 			break;
174 		case 'a':		/* after 'date' */
175 		case 'b':		/* before 'date' */
176 		case 'd':		/* from 'day' */
177 			if (proc_date(optarg, opt))
178 				error = TRUE;
179 			break;
180 		case 'j':		/* subject */
181 			if (proc_subject(optarg))
182 				error = TRUE;
183 			break;
184 		case 'm':		/* message 'type' */
185 			if (proc_type(optarg))
186 				error = TRUE;
187 			break;
188 		case 'o':		/* object type */
189 			if (proc_object(optarg))
190 				error = TRUE;
191 			break;
192 		case 'c':		/* message class */
193 			if (proc_class(optarg))
194 				error = TRUE;
195 			break;
196 		case 'u':		/* form audit user */
197 		case 'e':		/* form effective user */
198 		case 'r':		/* form real user */
199 		case 'f':		/* form effective group */
200 		case 'g':		/* form real group */
201 			if (proc_id(optarg, opt))
202 				error = TRUE;
203 			break;
204 #ifdef	TSOL
205 		case 's':		/* sensitivity label range */
206 			if (proc_slabel(optarg))
207 				error = TRUE;
208 			break;
209 #endif	/* TSOL */
210 		case 'z':		/* zone name */
211 			if (proc_zonename(optarg))
212 				error = TRUE;
213 			break;
214 		default:
215 			return (-1);
216 		}
217 		if (error) {
218 			(void) fprintf(stderr,
219 				gettext("%s command line error - %s.\n"),
220 				ar, error_str);
221 			return (-1);
222 		}
223 	}
224 	/* catch '-' option for stdin processing - getopt() won't see it */
225 	if (optind < argc) {
226 		if (argv[optind][0] == '-' && argv[optind][1] == '\0') {
227 			optind++;
228 			f_stdin = TRUE;
229 			goto start_over;
230 		}
231 	}
232 	/*
233 	 * Give a default value for 'b' option if not specified.
234 	 */
235 	if (m_before == 0)
236 		m_before = MAXLONG;	/* forever */
237 	/*
238 	 * Validate combinations of options.
239 	 * The following are done:
240 	 *	1. Can't have 'M' or 'S' or 'R' with filenames.
241 	 *	2. Can't have an after ('a') time after a before ('b') time.
242 	 *	3. Delete ('D') must have 'C' and 'A' and 'O' with it.
243 	 *	4. Input from stdin ('-') can't have filenames too.
244 	 */
245 	if ((f_machine || f_server || f_root) && (argc != optind)) {
246 		error_str = gettext(
247 		    "no filenames allowed with 'M' or 'S' or 'R' options");
248 		error_combo = TRUE;
249 	}
250 	if (m_after >= m_before) {
251 		error_str =
252 			gettext("'a' parameter must be before 'b' parameter");
253 		error_combo = TRUE;
254 	}
255 	if (f_delete &&
256 	    (!f_complete || !f_all || !f_outfile)) {
257 		error_str = gettext(
258 		    "'C', 'A', and 'O' must be specified with 'D'");
259 		error_combo = TRUE;
260 	}
261 	if (f_stdin && (argc != optind)) {
262 		error_str = gettext("no filenames allowed with '-' option");
263 		error_combo = TRUE;
264 	}
265 	/*
266 	 * If error with option combos then print message and exit.
267 	 * If there was an error with just an option then exit.
268 	 */
269 	if (error_combo) {
270 		(void) fprintf(stderr,
271 		    gettext("%s command line error - %s.\n"), ar, error_str);
272 		return (-1);
273 	}
274 	if (f_root == NULL)
275 		f_root = "/etc/security/audit";
276 	/*
277 	 * Now handle any filenames included in the command line.
278 	 */
279 	return (process_fileopt(argc, argv, optind));
280 }
281 
282 int
283 proc_subject(char *optarg)
284 {
285 	if (flags & M_SUBJECT) {
286 		error_str = gettext("'j' option specified multiple times");
287 		return (-1);
288 	}
289 	flags |= M_SUBJECT;
290 	subj_id = atol(optarg);
291 	return (0);
292 }
293 
294 
295 int
296 proc_object(char *optarg)
297 {
298 	char	*obj_str;
299 	char	*obj_val;
300 	char	*obj_arg;
301 	int	err;
302 
303 	obj_ent_t *oep;
304 	struct hostent *he;
305 
306 	if (flags & M_OBJECT) {
307 		error_str = gettext("'o' option specified multiple times");
308 		return (-1);
309 	}
310 	flags |= M_OBJECT;
311 	if ((obj_arg = strdup(optarg)) == (char *)0)
312 		return (-1);
313 	if ((obj_str = strtok(optarg, "=")) == (char *)0 ||
314 	    (oep = obj_lkup(obj_str)) == (obj_ent_t *)0 ||
315 	    (obj_val = strtok((char *)0, "=")) == (char *)0) {
316 		(void) sprintf(errbuf, gettext("invalid object arg (%s)"),
317 		    obj_arg);
318 		error_str = errbuf;
319 		return (-1);
320 	}
321 
322 	obj_flag = oep->obj_flag;
323 
324 	switch (obj_flag) {
325 	case OBJ_PATH:
326 		if ((error_str = re_comp2(obj_val)) != (char *)NULL) {
327 			return (-1);
328 		}
329 		return (0);
330 		/* NOTREACHED */
331 	case OBJ_SOCK:
332 		if (!a_isnum(obj_val, TRUE)) {
333 			obj_id = atol(obj_val);
334 			socket_flag = SOCKFLG_PORT;
335 			return (0);
336 		}
337 		if (*obj_val == '0') {
338 			(void) sscanf(obj_val, "%x", (uint_t *)&obj_id);
339 			socket_flag = SOCKFLG_PORT;
340 			return (0);
341 		}
342 
343 		he = getipnodebyname((const void *)obj_val, AF_INET6, 0, &err);
344 		if (he == 0) {
345 			he = getipnodebyname((const void *)obj_val, AF_INET,
346 			    0, &err);
347 			if (he == 0) {
348 				(void) sprintf(errbuf,
349 				    gettext("invalid machine name (%s)"),
350 				    obj_val);
351 				error_str = errbuf;
352 				return (-1);
353 			}
354 		}
355 
356 		if (he->h_addrtype == AF_INET6) {
357 			/* LINTED */
358 			if (IN6_IS_ADDR_V4MAPPED((in6_addr_t *)
359 				he->h_addr_list[0])) {
360 				/* address is IPv4 (32 bits) */
361 				(void) memcpy(&obj_id, he->h_addr_list[0], 4);
362 				ip_type = AU_IPv4;
363 			} else {
364 				(void) memcpy(ip_ipv6, he->h_addr_list[0], 16);
365 				ip_type = AU_IPv6;
366 			}
367 		} else {
368 			/* address is IPv4 (32 bits) */
369 			(void) memcpy(&obj_id, he->h_addr_list[0], 4);
370 			ip_type = AU_IPv4;
371 		}
372 
373 		freehostent(he);
374 		socket_flag = SOCKFLG_MACHINE;
375 		return (0);
376 		break;
377 	case OBJ_MSG:
378 	case OBJ_SEM:
379 	case OBJ_SHM:
380 	case OBJ_PROC:
381 		obj_id = atol(obj_val);
382 		return (0);
383 		/* NOTREACHED */
384 	case OBJ_FGROUP:
385 	case OBJ_MSGGROUP:
386 	case OBJ_SEMGROUP:
387 	case OBJ_SHMGROUP:
388 	case OBJ_PGROUP:
389 		return (proc_group(obj_val, &obj_group));
390 		/* NOTREACHED */
391 	case OBJ_FOWNER:
392 	case OBJ_MSGOWNER:
393 	case OBJ_SEMOWNER:
394 	case OBJ_SHMOWNER:
395 	case OBJ_POWNER:
396 		return (proc_user(obj_val, &obj_owner));
397 		/* NOTREACHED */
398 	case OBJ_LP: /* lp objects have not yet been defined */
399 	default: /* impossible */
400 		(void) sprintf(errbuf, gettext("invalid object type (%s)"),
401 		    obj_str);
402 		error_str = errbuf;
403 		return (-1);
404 		/* NOTREACHED */
405 	} /* switch */
406 	/*NOTREACHED*/
407 }
408 
409 
410 obj_ent_t *
411 obj_lkup(char *obj_str)
412 {
413 	int	i;
414 
415 	for (i = 0; i < sizeof (obj_tbl) / sizeof (obj_ent_t); i++)
416 		if (strcmp(obj_str, obj_tbl[i].obj_str) == 0)
417 			return (&obj_tbl[i]);
418 
419 	/* not in table */
420 	return ((obj_ent_t *)0);
421 }
422 
423 
424 /*
425  * .func	proc_type - process record type.
426  * .desc	Process a record type. It is either as a number or a mnemonic.
427  * .call	ret = proc_type(optstr).
428  * .arg	optstr	- ptr to name or number.
429  * .ret	0	- no errors detected.
430  * .ret	-1	- error detected (error_str contains description).
431  */
432 int
433 proc_type(char *optstr)
434 {
435 	struct au_event_ent *aep;
436 
437 	/*
438 	 * Either a number or a name.
439 	 */
440 
441 	if (flags & M_TYPE) {
442 		error_str = gettext("'m' option specified multiple times");
443 		return (-1);
444 	}
445 	flags |= M_TYPE;
446 	m_type = 0;
447 	if (a_isnum(optstr, TRUE)) {
448 		if ((aep = getauevnam(optstr)) != (struct au_event_ent *)NULL)
449 			m_type = aep->ae_number;
450 	} else {
451 		if ((aep = getauevnum((au_event_t)atoi(optstr))) !=
452 		    (struct au_event_ent *)NULL)
453 			m_type = aep->ae_number;
454 	}
455 	if ((m_type == 0)) {
456 		(void) sprintf(errbuf, gettext("invalid event (%s)"), optstr);
457 		error_str = errbuf;
458 		return (-1);
459 	}
460 	return (0);
461 }
462 
463 
464 /*
465  * .func	a_isnum - is it a number?
466  * .desc	Determine if a string is a number or a name.
467  *	A number may have a leading '+' or '-', but then must be
468  *	all digits.
469  * .call	ret = a_isnum(str).
470  * .arg	str - ptr to the string.
471  * .arg	leading	- TRUE if leading '+-' allowed.
472  * .ret	0	- is a number.
473  * .ret	1	- is not a number.
474  */
475 int
476 a_isnum(char *str, int leading)
477 {
478 	char	*strs;
479 
480 	if ((leading == TRUE) && (*str == '-' || *str == '+'))
481 		strs = str + 1;
482 	else
483 		strs = str;
484 
485 	if (strlen(strs) == strspn(strs, "0123456789"))
486 		return (0);
487 	else
488 		return (1);
489 }
490 
491 
492 /*
493  * .func	proc_id	- process user/group id's/
494  * .desc	Process either a user number/name or group number/name.
495  *	For names check to see if the name is active in the system
496  *	to derive the number. If it is not active then fail. For a number
497  *	also check to see if it is active, but only print a warning if it
498  *	is not. An administrator may be looking at activity of a 'phantom'
499  *	user.
500  * .call	ret = proc_id(optstr, opt).
501  * .arg	optstr	- ptr to name or number.
502  * .arg	opt	- 'u' - audit user, 'e' - effective user, 'r' - real user,
503  *		  'g' - group, 'f' - effective group.
504  * .ret	0	- no errors detected.
505  * .ret	-1	- error detected (error_str contains description).
506  */
507 int
508 proc_id(char *optstr, int opt)
509 {
510 	switch (opt) {
511 	case 'e': 		/* effective user id */
512 		if (flags & M_USERE) {
513 			error_str = gettext(
514 			    "'e' option specified multiple times");
515 			return (-1);
516 		}
517 		flags |= M_USERE;
518 		return (proc_user(optstr, &m_usere));
519 		/* NOTREACHED */
520 	case 'f': 		/* effective group id */
521 		if (flags & M_GROUPE) {
522 			error_str = gettext(
523 			    "'f' option specified multiple times");
524 			return (-1);
525 		}
526 		flags |= M_GROUPE;
527 		return (proc_group(optstr, &m_groupe));
528 		/* NOTREACHED */
529 	case 'r': 		/* real user id */
530 		if (flags & M_USERR) {
531 			error_str = gettext(
532 			    "'r' option specified multiple times");
533 			return (-1);
534 		}
535 		flags |= M_USERR;
536 		return (proc_user(optstr, &m_userr));
537 		/* NOTREACHED */
538 	case 'u': 		/* audit user id */
539 		if (flags & M_USERA) {
540 			error_str = gettext(
541 			    "'u' option specified multiple times");
542 			return (-1);
543 		}
544 		flags |= M_USERA;
545 		return (proc_user(optstr, &m_usera));
546 		/* NOTREACHED */
547 	case 'g': 		/* real group id */
548 		if (flags & M_GROUPR) {
549 			error_str = gettext(
550 			    "'g' option specified multiple times");
551 			return (-1);
552 		}
553 		flags |= M_GROUPR;
554 		return (proc_group(optstr, &m_groupr));
555 		/* NOTREACHED */
556 	default: 		/* impossible */
557 		(void) sprintf(errbuf, gettext("'%c' unknown option"), opt);
558 		error_str = errbuf;
559 		return (-1);
560 		/* NOTREACHED */
561 	}
562 	/*NOTREACHED*/
563 }
564 
565 
566 int
567 proc_group(char *optstr, gid_t *gid)
568 {
569 	struct group *grp;
570 
571 	if ((grp = getgrnam(optstr)) == NULL) {
572 		if (!a_isnum(optstr, TRUE)) {
573 			*gid = (gid_t)atoi(optstr);
574 			return (0);
575 		}
576 		(void) sprintf(errbuf, gettext("group name invalid (%s)"),
577 		    optstr);
578 		error_str = errbuf;
579 		return (-1);
580 	}
581 	*gid = grp->gr_gid;
582 	return (0);
583 }
584 
585 
586 int
587 proc_user(char *optstr, uid_t *uid)
588 {
589 	struct passwd *usr;
590 
591 	if ((usr = getpwnam(optstr)) == NULL) {
592 		if (!a_isnum(optstr, TRUE)) {
593 			*uid = (uid_t)atoi(optstr);
594 			return (0);
595 		}
596 		(void) sprintf(errbuf, gettext("user name invalid (%s)"),
597 		    optstr);
598 		error_str = errbuf;
599 		return (-1);
600 	}
601 	*uid = usr->pw_uid;
602 	return (0);
603 }
604 
605 
606 /*
607  * .func proc_date - process date argument.
608  * .desc Handle a date/time argument. See if the user has erred in combining
609  *	the types of date arguments. Then parse the string and check for
610  *	validity of each part.
611  * .call	ret = proc_date(optstr, opt).
612  * .arg	optstr	- ptr to date/time string.
613  * .arg	opt	- 'd' for day, 'a' for after, or 'b' for before.
614  * .ret	0	- no errors detected.
615  * .ret	-1	- errors detected (error_str knows what it is).
616  */
617 int
618 proc_date(char *optstr, int opt)
619 {
620 	static int	m_day = FALSE;
621 
622 	if (opt == 'd') {
623 		if (m_day == TRUE) {
624 			error_str = gettext(
625 			    "'d' option may not be used with 'a' or 'b'");
626 			return (-1);
627 		}
628 		m_day = TRUE;
629 	}
630 	if ((opt == 'd') && (m_before || m_after)) {
631 		error_str = gettext(
632 		    "'d' option may not be used with 'a' or 'b'");
633 		return (-1);
634 	}
635 	if ((opt == 'a' || opt == 'b') && m_day) {
636 		error_str = gettext(
637 		    "'a' or 'b' option may not be used with 'd'");
638 		return (-1);
639 	}
640 	if ((opt == 'a') && (m_after != 0)) {
641 		error_str = gettext("'a' option specified multiple times");
642 		return (-1);
643 	}
644 	if ((opt == 'b') && (m_before != 0)) {
645 		error_str = gettext("'b' option specified multiple times");
646 		return (-1);
647 	}
648 	if (parse_time(optstr, opt))
649 		return (-1);
650 	return (0);
651 }
652 
653 
654 /*
655  * .func	proc_class - process message class argument.
656  * .desc	Process class type and see if it is for real.
657  * .call	ret = proc_class(optstr).
658  * .arg	optstr	- ptr to class.
659  * .ret	0	- class has class.
660  * .ret	-1	- class in no good.
661  */
662 int
663 proc_class(char *optstr)
664 {
665 	if (flags & M_CLASS) {
666 		error_str = gettext("'c' option specified multiple times");
667 		return (-1);
668 	}
669 	flags |= M_CLASS;
670 
671 	if (getauditflagsbin(optstr, &mask) != 0) {
672 		(void) sprintf(errbuf, gettext("unknown class (%s)"), optstr);
673 		error_str = errbuf;
674 		return (-1);
675 	}
676 
677 	if (mask.am_success != mask.am_failure) {
678 		flags |= M_SORF;
679 	}
680 
681 	return (0);
682 }
683 
684 
685 /*
686  * .func process_fileopt - process command line file options.
687  * .desc Process the command line file options and gather the specified files
688  *	together in file groups based upon file name suffix. The user can
689  *	specify files explicitly on the command line or via a directory.
690  *	This is called after the command line flags are processed (as
691  *	denoted by '-').
692  * .call	ret = process_fileopt(argc, argv, optindex).
693  * .arg	argc	- current value of argc.
694  * .arg	argv	- current value of argv.
695  * .arg	optindex- current index into argv (as setup by getopt()).
696  * .ret	0	- no errors detected.
697  * .ret	-1	- error detected (message already printed).
698  */
699 int
700 process_fileopt(int argc, char **argv, int optindex)
701 {
702 	int	f_mode = FM_ALLDIR;
703 	char	f_dr[MAXNAMLEN+1];
704 	char	*f_dir = f_dr;
705 	char	*fname;
706 	static char	*std = "standard input";
707 	audit_fcb_t *fcb;
708 	DIR * dirp;
709 	struct dirent *dp;
710 	audit_pcb_t *pcb;
711 
712 	/*
713 	 * Take input from stdin, not any files.
714 	 * Use a single fcb to do this.
715 	 */
716 	if (f_stdin) {
717 		fcb = (audit_fcb_t *)a_calloc(1, sizeof (*fcb) + strlen(std));
718 		(void) strcpy(fcb->fcb_file, std);
719 		fcb->fcb_suffix = fcb->fcb_name = fcb->fcb_file;
720 		fcb->fcb_next = NULL;
721 		fcb->fcb_start = 0;
722 		fcb->fcb_end = MAXLONG;		/* forever */
723 		if ((pcb = get_next_pcb((char *)NULL)) == (audit_pcb_t *)NULL)
724 			return (-1);
725 		pcb->pcb_suffix = fcb->fcb_file;
726 		pcb->pcb_dfirst = pcb->pcb_first = fcb;	/* one-item list */
727 		pcb->pcb_dlast = pcb->pcb_last = fcb;
728 		pcb->pcb_cur = fcb;
729 	}
730 	/*
731 	 * No files specified on the command line.
732 	 * Process a directory of files or subdirectories.
733 	 */
734 	else if (argc == optindex) {
735 		/*
736 		 * A specific server directory was requested.
737 		 */
738 		if (f_server) {
739 			if (strchr(f_server, '/')) {	/* given full path */
740 				f_dir = f_server;
741 				f_mode = FM_ALLFILE;	/* all files here */
742 			} else {		/* directory off audit root */
743 				f_dir[0] = '\0';
744 				(void) strcat(f_dir, f_root);
745 				(void) strcat(f_dir, "/");
746 				(void) strcat(f_dir, f_server);
747 				f_mode = FM_ALLFILE;
748 			}
749 		}
750 		/*
751 		 * Gather all of the files in the directory 'f_dir'.
752 		 */
753 		if (f_mode == FM_ALLFILE) {
754 			if (gather_dir(f_dir)) { /* get those files together */
755 				return (-1);
756 			}
757 		} else {
758 			/*
759 			 * Gather all of the files in all of the
760 			 * directories in 'f_root'.
761 			 */
762 			if ((dirp = opendir(f_root)) == NULL) {
763 				(void) sprintf(errbuf, gettext(
764 				    "%s can't open directory %s"), ar, f_root);
765 				perror(errbuf);
766 				return (-1);
767 			}
768 			/* read the directory and process all of the subs */
769 			for (dp = readdir(dirp);
770 			    dp != NULL; dp = readdir(dirp)) {
771 				if (dp->d_name[0] == '.')
772 					continue;
773 				f_dir[0] = '\0';
774 				(void) strcat(f_dir, f_root);
775 				(void) strcat(f_dir, "/");
776 				(void) strcat(f_dir, dp->d_name);
777 				if (gather_dir(f_dir))	/* process a sub */
778 					return (-1);
779 			}
780 			(void) closedir(dirp);
781 		}
782 	} else {
783 		/*
784 		 * User specified filenames on the comm and line.
785 		 */
786 		f_cmdline = TRUE;
787 		for (; optindex < argc; optindex++) {
788 			fname = argv[optindex];		/* get a filename */
789 			if (proc_file(fname, FALSE))
790 				return (-1);
791 		}
792 	}
793 	return (0);
794 }
795 
796 
797 /*
798  * .func	gather_dir - gather a directory's files together.
799  * .desc	Process all of the files in a specific directory. The files may
800  *	be checked for adherence to the file name form at.
801  *	If the directory can't be opened that is ok - just print
802  *	a message and continue.
803  * .call	ret = gather_dir(dir).
804  * .arg	dir	- ptr to full pathname of directory.
805  * .ret	0	- no errors detected.
806  * .ret	-1	- error detected (message already printed).
807  */
808 int
809 gather_dir(char *dir)
810 {
811 	char	dname[MAXNAMLEN+1];
812 	char	fname[MAXNAMLEN+1];
813 	DIR * dirp;
814 	struct dirent *dp;
815 
816 	(void) snprintf(dname, sizeof (dname), "%s/files", dir);
817 
818 	if ((dirp = opendir(dname)) == NULL) {
819 		if (errno != ENOTDIR) {
820 			(void) sprintf(errbuf,
821 			    gettext("%s can't open directory - %s"), ar, dname);
822 			perror(errbuf);
823 		}
824 		return (0);
825 	}
826 	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
827 		if (dp->d_name[0] == '.')	/* can't see hidden files */
828 			continue;
829 		fname[0] = '\0';
830 		(void) strcat(fname, dname);	/* create pathname of file */
831 		(void) strcat(fname, "/");
832 		(void) strcat(fname, dp->d_name);
833 		if (proc_file(fname, TRUE))
834 			return (-1);
835 	}
836 	(void) closedir(dirp);
837 	return (0);
838 }
839 
840 
841 /*
842  * .func	proc_file - process a single candidate file.
843  * .desc	Check out a file to see if it should be used in the merge.
844  *	This includes checking the name (mode is TRUE) against the
845  *	file format, checking access rights to the file, and thence
846  *	getting and fcb and installing the fcb into the correct pcb.
847  *	If the file fails then the fcb is not installed into a pcb
848  *	and the file dissapears from view.
849  * .call	proc_file(fname, mode).
850  * .arg	fname	- ptr to full pathna me of file.
851  * .arg	mode	- TRUE if checking adherence to file name format.
852  * .ret	0	- no fatal errors detected.
853  * .ret	-1	- fatal error detected - quit altogether
854  *		  (message already printed).
855  */
856 int
857 proc_file(char *fname, int mode)
858 {
859 	int reject = FALSE;
860 	size_t len;
861 	struct stat stat_buf;
862 	audit_fcb_t *fcb, *fcbp, *fcbprev;
863 	audit_pcb_t *pcb;
864 
865 	/*
866 	 * See if it is a weird file like a directory or
867 	 * character special (around here?).
868 	 */
869 	if (stat(fname, &stat_buf)) {
870 		return (0);
871 	}
872 	if ((stat_buf.st_mode & S_IFREG) == 0)
873 		return (0);
874 	/*
875 	 * Allocate a new fcb to hold fcb and full filename.
876 	 */
877 	len = sizeof (audit_fcb_t) + strlen(fname);
878 	fcb = (audit_fcb_t *)a_calloc(1, len);
879 	(void) strcpy(fcb->fcb_file, fname);
880 	if (check_file(fcb, mode)) { /* check file name */
881 		if (!f_quiet) {
882 			(void) fprintf(stderr, "%s %s:\n  %s.\n", ar,
883 			    error_str, fname);
884 		}
885 		reject = TRUE;
886 	} else {
887 		/*
888 		 * Check against file criteria.
889 		 * Check finish-time here, and start-time later on
890 		 * while processing.
891 		 * This is because the start time on a file can be after
892 		 * the first record(s).
893 		 */
894 		if (f_complete && (fcb->fcb_flags & FF_NOTTERM) && !f_cmdline)
895 			reject = TRUE;
896 		if (!f_all && (fcb->fcb_end < m_after))
897 			reject = TRUE;
898 		if (f_machine) {
899 			if (strlen(fcb->fcb_suffix) != strlen(f_machine) ||
900 			    (strcmp(fcb->fcb_suffix, f_machine) != 0)) {
901 				reject = TRUE;
902 			}
903 		}
904 	}
905 	if (reject == FALSE) {
906 		filenum++;	/* count of total files to be processed */
907 		fcb->fcb_next = NULL;
908 		if ((pcb = get_next_pcb(fcb->fcb_suffix)) == NULL) {
909 			return (-1);
910 		}
911 		/* Place FCB into the PCB in order - oldest first.  */
912 		fcbp = pcb->pcb_first;
913 		fcbprev = NULL;
914 		while (fcbp != NULL) {
915 			if (fcb->fcb_start < fcbp->fcb_start) {
916 				if (fcbprev)
917 					fcbprev->fcb_next = fcb;
918 				else
919 					pcb->pcb_dfirst = pcb->pcb_first = fcb;
920 				fcb->fcb_next = fcbp;
921 				break;
922 			}
923 			fcbprev = fcbp;
924 			fcbp = fcbp->fcb_next;
925 		}
926 		/* younger than all || empty list */
927 		if (!fcb->fcb_next) {
928 			if (pcb->pcb_first == NULL)
929 				pcb->pcb_dfirst = pcb->pcb_first = fcb;
930 			pcb->pcb_dlast = pcb->pcb_last = fcb;
931 			if (fcbprev)
932 				fcbprev->fcb_next = fcb;
933 		}
934 	} else {
935 		free((char *)fcb);	/* rejected */
936 	}
937 	return (0);
938 }
939 
940 
941 /*
942  * .func	check_file - check filename and setup fcb.
943  * .desc	Check adherence to the file format (do_check is TRUE) and setup
944  *	the fcb with useful information.
945  *	filename format: yyyymmddhhmmss.yyyymmddhhmmss.suffix
946  *			 yyyymmddhhmmss.not_terminated.suffix
947  *	If do_check is FALSE then still see if the filename does confirm
948  *	to the format. If it does then extract useful information from
949  *	it (start time and end time).  But if it doesn't then don't print
950  *	any error messages.
951  * .call	ret = check_file(fcb, do_check).
952  * .arg	fcb	- ptr to fcb that holds the file.
953  * .arg	do_check - if TRUE do check adherence to file format.
954  * .ret	0	- no errors detected.
955  * .ret	-1	- file failed somehow (error_str tells why).
956  */
957 int
958 check_file(audit_fcb_t *fcb, int do_check)
959 {
960 	int	ret;
961 	char	*namep, *slp;
962 	char	errb[256];		/* build error message */
963 	struct tm tme;
964 
965 	errb[0] = '\0';
966 	/* get just the filename */
967 	for (slp = namep = fcb->fcb_file; *namep; namep++) {
968 		if (*namep == '/')
969 			slp = namep + 1; /* slp -> the filename itself */
970 	}
971 	if (do_check == FALSE) {
972 		fcb->fcb_end = MAXLONG;		/* forever */
973 		fcb->fcb_suffix = NULL;
974 		fcb->fcb_name = slp;
975 		ret = 0;
976 	} else {
977 		ret = -1;
978 	}
979 	if ((int)strlen(slp) < 31) {
980 		(void) sprintf(errbuf, gettext("filename too short (%d)"),
981 		    strlen(slp));
982 		error_str = errbuf;
983 		return (ret);
984 	}
985 	/*
986 	 * Get working copy of filename.
987 	 */
988 	namep = (char *)a_calloc(1, strlen(slp) + 1);
989 	(void) strcpy(namep, slp);
990 	if (namep[14] != '.' || namep[29] != '.') {
991 		(void) sprintf(errbuf,
992 		    gettext("invalid filename format (%c or %c)"), namep[14],
993 		    namep[29]);
994 		error_str = errbuf;
995 		free(namep);
996 		return (ret);
997 	}
998 	namep[14] = '\0';			/* mark off start time */
999 	namep[29] = '\0';			/* mark off finish time */
1000 	if (derive_date(namep, &tme)) {
1001 		(void) strcat(errb, gettext("starting time-stamp invalid - "));
1002 		(void) strcat(errb, error_str);
1003 		(void) strcpy(errbuf, errb);
1004 		error_str = errbuf;
1005 		free(namep);
1006 		return (ret);
1007 	}
1008 	/*
1009 	 * Keep start time from filename. Use it to order files in
1010 	 * the file list. Later we will update this when we read
1011 	 * the first record from the file.
1012 	 */
1013 	fcb->fcb_start = tm_to_secs(&tme);
1014 
1015 	if (strcmp(&namep[15], "not_terminated") == 0) {
1016 		fcb->fcb_end = MAXLONG;		/* forever */
1017 		/*
1018 		 * Only treat a 'not_terminated' file as such if
1019 		 * it is not on the command line.
1020 		 */
1021 		if (do_check == TRUE)
1022 			fcb->fcb_flags |= FF_NOTTERM;
1023 	} else if (derive_date(&namep[15], &tme)) {
1024 		(void) strcat(errb, gettext("ending time-stamp invalid - "));
1025 		(void) strcat(errb, error_str);
1026 		(void) strcpy(errbuf, errb);
1027 		error_str = errbuf;
1028 		free(namep);
1029 		return (ret);
1030 	} else {
1031 		fcb->fcb_end = tm_to_secs(&tme);
1032 	}
1033 	fcb->fcb_name = slp;
1034 	fcb->fcb_suffix = &slp[30];
1035 	free(namep);
1036 	return (0);
1037 }
1038 
1039 
1040 /*
1041  * .func get_next_pcb - get a pcb to use.
1042  * .desc	The pcb's in the array audit_pcbs are used to hold single file
1043  *	groups in the form of a linked list. Each pcb holds files that
1044  *	are tied together by a common suffix in the file name. Here we
1045  *	get either 1. the existing pcb holding a specified sufix or
1046  *	2. a new pcb if we can't find an existing one.
1047  * .call	pcb = get_next_pcb(suffix).
1048  * .arg	suffix	- ptr to suffix we are seeking.
1049  * .ret	pcb	- ptr to pcb that hold s the sought suffix.
1050  * .ret	NULL- serious failure in memory allocation. Quit processing.
1051  */
1052 audit_pcb_t *
1053 get_next_pcb(char *suffix)
1054 {
1055 	int	i = 0;
1056 	int	zerosize;
1057 	unsigned int	size;
1058 	audit_pcb_t *pcb;
1059 
1060 	/* Search through (maybe) entire array. */
1061 	while (i < pcbsize) {
1062 		pcb = &audit_pcbs[i++];
1063 		if (pcb->pcb_first == NULL) {
1064 			proc_pcb(pcb, suffix, i);
1065 			return (pcb);	/* came to an unused one */
1066 		}
1067 		if (suffix) {
1068 			if (strcmp(pcb->pcb_suffix, suffix) == 0)
1069 				return (pcb);	/* matched one with suffix */
1070 		}
1071 	}
1072 	/*
1073 	 * Uh-oh, the entire array is used and we haven't gotten one yet.
1074 	 * Allocate a bigger array.
1075 	 */
1076 	pcbsize += PCB_INC;
1077 	size = pcbsize * sizeof (audit_pcb_t);
1078 	zerosize = size - ((pcbsize - PCB_INC) * sizeof (audit_pcb_t));
1079 	if ((audit_pcbs = (audit_pcb_t *)realloc((char *)audit_pcbs, size)) ==
1080 	    NULL) {
1081 		(void) sprintf(errbuf,
1082 		    gettext("%s memory reallocation failed (%d bytes)"), ar,
1083 		    size);
1084 		perror(errbuf);
1085 		audit_stats();		/* give user statistics on usage */
1086 		return (NULL);		/* really bad thing to have happen */
1087 	}
1088 	/*
1089 	 * Don't know if realloc clears the new memory like calloc would.
1090 	 */
1091 	(void) memset((void *) & audit_pcbs[pcbsize-PCB_INC], 0,
1092 	    (size_t)zerosize);
1093 	pcb = &audit_pcbs[pcbsize-PCB_INC];	/* allocate the first new one */
1094 	proc_pcb(pcb, suffix, pcbsize - PCB_INC);
1095 	return (pcb);
1096 }
1097 
1098 
1099 /*
1100  * .func proc_pcb - process pcb.
1101  * .desc	Common pcb processing for above routine.
1102  * .call	proc_pcb(pcb, suffix, i).
1103  * .arg	pcb	- ptr to pcb.
1104  * .arg	suffix	- prt to suffix tha t ties this group together.
1105  * .arg	i	- index into audit_pcbs[ ].
1106  * .ret	void.
1107  */
1108 void
1109 proc_pcb(audit_pcb_t *pcb, char *suffix, int i)
1110 {
1111 	if (suffix)
1112 		pcb->pcb_suffix = suffix;
1113 	pcbnum++;	/* one more pcb in use */
1114 	pcb->pcb_size = AUDITBUFSIZE;
1115 	pcb->pcb_rec = (char *)a_calloc(1, AUDITBUFSIZE);
1116 	pcb->pcb_time = -1;
1117 	pcb->pcb_flags |= PF_FILE;	/* note this one controls files */
1118 	pcb->pcb_procno = i;	/* save index into audit_pcbs [] for id */
1119 }
1120 
1121 
1122 #ifdef	TSOL
1123 /*
1124  * .func	proc_slabel - process sensitivity label range argument.
1125  * .desc	Parse sensitivity label range sl:sl
1126  * .call	ret = proc_slabel(optstr).
1127  * .arg	opstr	- ptr to label range string
1128  * .ret 0	- no errors detected.
1129  * .ret -1	- errors detected or not Trusted Solaris (error_str set).
1130  */
1131 
1132 int
1133 proc_slabel(char *optstr)
1134 {
1135 	char	*p;
1136 	int	error;
1137 
1138 	if (flags & M_SLABEL) {
1139 		error_str = gettext("'s' option specified multiple times");
1140 		return (-1);
1141 	}
1142 
1143 	flags |= M_SLABEL;
1144 	p = strchr(optstr, ':');
1145 	if (p == NULL) {
1146 		/* exact label match, lower and upper range bounds the same */
1147 		if (stobsl(optstr, &m_slabel.lower_bound, NO_CORRECTION,
1148 		    &error) == 0) {
1149 			(void) sprintf(errbuf,
1150 			    gettext("invalid sensitivity label (%s) err %d"),
1151 			    optstr, error);
1152 			error_str = errbuf;
1153 			return (-1);
1154 		}
1155 		m_slabel.upper_bound = m_slabel.lower_bound;
1156 		return (0);
1157 	}
1158 	if (p == optstr) {
1159 		/* lower bound is not specified .. default is admin_low */
1160 		bsllow(&m_slabel.lower_bound);
1161 		if (stobsl(p + 1, &m_slabel.upper_bound, NO_CORRECTION,
1162 		    &error) == 0) {
1163 			(void) sprintf(errbuf,
1164 			    gettext("invalid sensitivity label (%s) err %d"),
1165 			    p + 1, error);
1166 			error_str = errbuf;
1167 			return (-1);
1168 		}
1169 		return (0);
1170 	}
1171 	*p++ = '\0';
1172 	if (stobsl(optstr, &m_slabel.lower_bound, NO_CORRECTION, &error) == 0) {
1173 		(void) sprintf(errbuf,
1174 		    gettext("invalid sensitivity label (%s) err %d"), optstr,
1175 		    error);
1176 		error_str = errbuf;
1177 		return (-1);
1178 	}
1179 	if (*p == '\0')
1180 		/* upper bound is not specified .. default is admin_high */
1181 		bslhigh(&m_slabel.upper_bound);
1182 	else {
1183 		if (stobsl(p, &m_slabel.upper_bound, NO_CORRECTION, &error) ==
1184 		    0) {
1185 			(void) sprintf(errbuf,
1186 			    gettext("invalid sensitivity label (%s) err %d"),
1187 			    p, error);
1188 			error_str = errbuf;
1189 			return (-1);
1190 		}
1191 	}
1192 	/* make sure that upper bound dominates the lower bound */
1193 	if (!bldominates(&m_slabel.upper_bound, &m_slabel.lower_bound)) {
1194 		*--p = ':';
1195 		(void) sprintf(errbuf,
1196 		    gettext("invalid sensitivity label range (%s)"), optstr);
1197 		error_str = errbuf;
1198 		return (-1);
1199 	}
1200 	return (0);
1201 }
1202 #endif	/* !TSOL */
1203 
1204 /*
1205  * proc_zonename - pick up zone name.
1206  *
1207  * all non-empty and not-too-long strings are valid since any name
1208  * may be valid.
1209  *
1210  * ret 0:	non-empty string
1211  * ret -1:	empty string or string is too long.
1212  */
1213 static int
1214 proc_zonename(char *optstr)
1215 {
1216 	size_t	length = strlen(optstr);
1217 	if ((length < 1) || (length > ZONENAME_MAX)) {
1218 		(void) sprintf(errbuf,
1219 		    gettext("invalid zone name: %s"), optstr);
1220 		error_str = errbuf;
1221 		return (-1);
1222 	}
1223 	zonename = strdup(optstr);
1224 	flags |= M_ZONENAME;
1225 	return (0);
1226 }
1227