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
process_options(int argc,char ** argv)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
proc_subject(char * optarg)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
proc_sid(char * optarg)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
proc_object(char * optarg)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 *
obj_lkup(char * obj_str)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
proc_type(char * optstr)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
a_isnum(char * str,int leading)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
proc_id(char * optstr,int opt)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
proc_group(char * optstr,gid_t * gid)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
proc_user(char * optstr,uid_t * uid)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
proc_date(char * optstr,int opt)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
proc_class(char * optstr)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
process_fileopt(int argc,char ** argv,int optindex)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
gather_dir(char * dir)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
proc_file(char * fname,int mode)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
check_file(audit_fcb_t * fcb,int do_check)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 *
get_next_pcb(char * suffix)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
proc_pcb(audit_pcb_t * pcb,char * suffix,int i)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
proc_label(char * optstr)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
proc_zonename(char * optstr)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
proc_fmri(char * optstr)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