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