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