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