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