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 case OBJ_SOCK:
346 if (!a_isnum(obj_val, TRUE)) {
347 obj_id = atol(obj_val);
348 socket_flag = SOCKFLG_PORT;
349 return (0);
350 }
351 if (*obj_val == '0') {
352 (void) sscanf(obj_val, "%x", (uint_t *)&obj_id);
353 socket_flag = SOCKFLG_PORT;
354 return (0);
355 }
356
357 he = getipnodebyname((const void *)obj_val, AF_INET6, 0, &err);
358 if (he == 0) {
359 he = getipnodebyname((const void *)obj_val, AF_INET,
360 0, &err);
361 if (he == 0) {
362 (void) sprintf(errbuf,
363 gettext("invalid machine name (%s)"),
364 obj_val);
365 error_str = errbuf;
366 return (-1);
367 }
368 }
369
370 if (he->h_addrtype == AF_INET6) {
371 /* LINTED */
372 if (IN6_IS_ADDR_V4MAPPED(
373 (in6_addr_t *)he->h_addr_list[0])) {
374 /* address is IPv4 (32 bits) */
375 (void) memcpy(&obj_id,
376 he->h_addr_list[0] + 12, 4);
377 ip_type = AU_IPv4;
378 } else {
379 (void) memcpy(ip_ipv6, he->h_addr_list[0], 16);
380 ip_type = AU_IPv6;
381 }
382 } else {
383 /* address is IPv4 (32 bits) */
384 (void) memcpy(&obj_id, he->h_addr_list[0], 4);
385 ip_type = AU_IPv4;
386 }
387
388 freehostent(he);
389 socket_flag = SOCKFLG_MACHINE;
390 return (0);
391 case OBJ_MSG:
392 case OBJ_SEM:
393 case OBJ_SHM:
394 case OBJ_PROC:
395 obj_id = atol(obj_val);
396 return (0);
397 case OBJ_FGROUP:
398 case OBJ_MSGGROUP:
399 case OBJ_SEMGROUP:
400 case OBJ_SHMGROUP:
401 case OBJ_PGROUP:
402 return (proc_group(obj_val, &obj_group));
403 case OBJ_FOWNER:
404 case OBJ_MSGOWNER:
405 case OBJ_SEMOWNER:
406 case OBJ_SHMOWNER:
407 case OBJ_POWNER:
408 return (proc_user(obj_val, &obj_owner));
409 case OBJ_FMRI:
410 return (proc_fmri(obj_val));
411 case OBJ_USER:
412 return (proc_user(obj_val, &obj_user));
413 case OBJ_LP: /* lp objects have not yet been defined */
414 default: /* impossible */
415 (void) sprintf(errbuf, gettext("invalid object type (%s)"),
416 obj_str);
417 error_str = errbuf;
418 return (-1);
419 } /* switch */
420 /*NOTREACHED*/
421 }
422
423
424 obj_ent_t *
obj_lkup(char * obj_str)425 obj_lkup(char *obj_str)
426 {
427 int i;
428
429 for (i = 0; i < sizeof (obj_tbl) / sizeof (obj_ent_t); i++)
430 if (strcmp(obj_str, obj_tbl[i].obj_str) == 0)
431 return (&obj_tbl[i]);
432
433 /* not in table */
434 return (NULL);
435 }
436
437
438 /*
439 * .func proc_type - process record type.
440 * .desc Process a record type. It is either as a number or a mnemonic.
441 * .call ret = proc_type(optstr).
442 * .arg optstr - ptr to name or number.
443 * .ret 0 - no errors detected.
444 * .ret -1 - error detected (error_str contains description).
445 */
446 int
proc_type(char * optstr)447 proc_type(char *optstr)
448 {
449 struct au_event_ent *aep;
450
451 /*
452 * Either a number or a name.
453 */
454
455 if (flags & M_TYPE) {
456 error_str = gettext("'m' option specified multiple times");
457 return (-1);
458 }
459 flags |= M_TYPE;
460 m_type = 0;
461 if (a_isnum(optstr, TRUE)) {
462 if ((aep = getauevnam(optstr)) != NULL)
463 m_type = aep->ae_number;
464 } else {
465 if ((aep = getauevnum((au_event_t)atoi(optstr))) !=
466 (struct au_event_ent *)NULL)
467 m_type = aep->ae_number;
468 }
469 if ((m_type == 0)) {
470 (void) sprintf(errbuf, gettext("invalid event (%s)"), optstr);
471 error_str = errbuf;
472 return (-1);
473 }
474 return (0);
475 }
476
477
478 /*
479 * .func a_isnum - is it a number?
480 * .desc Determine if a string is a number or a name.
481 * A number may have a leading '+' or '-', but then must be
482 * all digits.
483 * .call ret = a_isnum(str).
484 * .arg str - ptr to the string.
485 * .arg leading - TRUE if leading '+-' allowed.
486 * .ret 0 - is a number.
487 * .ret 1 - is not a number.
488 */
489 int
a_isnum(char * str,int leading)490 a_isnum(char *str, int leading)
491 {
492 char *strs;
493
494 if ((leading == TRUE) && (*str == '-' || *str == '+'))
495 strs = str + 1;
496 else
497 strs = str;
498
499 if (strlen(strs) == strspn(strs, "0123456789"))
500 return (0);
501 else
502 return (1);
503 }
504
505
506 /*
507 * .func proc_id - process user/group id's/
508 * .desc Process either a user number/name or group number/name.
509 * For names check to see if the name is active in the system
510 * to derive the number. If it is not active then fail. For a number
511 * also check to see if it is active, but only print a warning if it
512 * is not. An administrator may be looking at activity of a 'phantom'
513 * user.
514 * .call ret = proc_id(optstr, opt).
515 * .arg optstr - ptr to name or number.
516 * .arg opt - 'u' - audit user, 'e' - effective user, 'r' - real user,
517 * 'g' - group, 'f' - effective group.
518 * .ret 0 - no errors detected.
519 * .ret -1 - error detected (error_str contains description).
520 */
521 int
proc_id(char * optstr,int opt)522 proc_id(char *optstr, int opt)
523 {
524 switch (opt) {
525 case 'e': /* effective user id */
526 if (flags & M_USERE) {
527 error_str = gettext(
528 "'e' option specified multiple times");
529 return (-1);
530 }
531 flags |= M_USERE;
532 return (proc_user(optstr, &m_usere));
533 case 'f': /* effective group id */
534 if (flags & M_GROUPE) {
535 error_str = gettext(
536 "'f' option specified multiple times");
537 return (-1);
538 }
539 flags |= M_GROUPE;
540 return (proc_group(optstr, &m_groupe));
541 case 'r': /* real user id */
542 if (flags & M_USERR) {
543 error_str = gettext(
544 "'r' option specified multiple times");
545 return (-1);
546 }
547 flags |= M_USERR;
548 return (proc_user(optstr, &m_userr));
549 case 'u': /* audit user id */
550 if (flags & M_USERA) {
551 error_str = gettext(
552 "'u' option specified multiple times");
553 return (-1);
554 }
555 flags |= M_USERA;
556 return (proc_user(optstr, &m_usera));
557 case 'g': /* real group id */
558 if (flags & M_GROUPR) {
559 error_str = gettext(
560 "'g' option specified multiple times");
561 return (-1);
562 }
563 flags |= M_GROUPR;
564 return (proc_group(optstr, &m_groupr));
565 default: /* impossible */
566 (void) sprintf(errbuf, gettext("'%c' unknown option"), opt);
567 error_str = errbuf;
568 return (-1);
569 }
570 /*NOTREACHED*/
571 }
572
573
574 int
proc_group(char * optstr,gid_t * gid)575 proc_group(char *optstr, gid_t *gid)
576 {
577 struct group *grp;
578
579 if ((grp = getgrnam(optstr)) == NULL) {
580 if (!a_isnum(optstr, TRUE)) {
581 *gid = (gid_t)atoi(optstr);
582 return (0);
583 }
584 (void) sprintf(errbuf, gettext("group name invalid (%s)"),
585 optstr);
586 error_str = errbuf;
587 return (-1);
588 }
589 *gid = grp->gr_gid;
590 return (0);
591 }
592
593
594 int
proc_user(char * optstr,uid_t * uid)595 proc_user(char *optstr, uid_t *uid)
596 {
597 struct passwd *usr;
598
599 if ((usr = getpwnam(optstr)) == NULL) {
600 if (!a_isnum(optstr, TRUE)) {
601 *uid = (uid_t)atoi(optstr);
602 return (0);
603 }
604 (void) sprintf(errbuf, gettext("user name invalid (%s)"),
605 optstr);
606 error_str = errbuf;
607 return (-1);
608 }
609 *uid = usr->pw_uid;
610 return (0);
611 }
612
613
614 /*
615 * .func proc_date - process date argument.
616 * .desc Handle a date/time argument. See if the user has erred in combining
617 * the types of date arguments. Then parse the string and check for
618 * validity of each part.
619 * .call ret = proc_date(optstr, opt).
620 * .arg optstr - ptr to date/time string.
621 * .arg opt - 'd' for day, 'a' for after, or 'b' for before.
622 * .ret 0 - no errors detected.
623 * .ret -1 - errors detected (error_str knows what it is).
624 */
625 int
proc_date(char * optstr,int opt)626 proc_date(char *optstr, int opt)
627 {
628 static int m_day = FALSE;
629
630 if (opt == 'd') {
631 if (m_day == TRUE) {
632 error_str = gettext(
633 "'d' option may not be used with 'a' or 'b'");
634 return (-1);
635 }
636 m_day = TRUE;
637 }
638 if ((opt == 'd') && (m_before || m_after)) {
639 error_str = gettext(
640 "'d' option may not be used with 'a' or 'b'");
641 return (-1);
642 }
643 if ((opt == 'a' || opt == 'b') && m_day) {
644 error_str = gettext(
645 "'a' or 'b' option may not be used with 'd'");
646 return (-1);
647 }
648 if ((opt == 'a') && (m_after != 0)) {
649 error_str = gettext("'a' option specified multiple times");
650 return (-1);
651 }
652 if ((opt == 'b') && (m_before != 0)) {
653 error_str = gettext("'b' option specified multiple times");
654 return (-1);
655 }
656 if (parse_time(optstr, opt))
657 return (-1);
658 return (0);
659 }
660
661
662 /*
663 * .func proc_class - process message class argument.
664 * .desc Process class type and see if it is for real.
665 * .call ret = proc_class(optstr).
666 * .arg optstr - ptr to class.
667 * .ret 0 - class has class.
668 * .ret -1 - class in no good.
669 */
670 int
proc_class(char * optstr)671 proc_class(char *optstr)
672 {
673 if (flags & M_CLASS) {
674 error_str = gettext("'c' option specified multiple times");
675 return (-1);
676 }
677 flags |= M_CLASS;
678
679 if (getauditflagsbin(optstr, &mask) != 0) {
680 (void) sprintf(errbuf, gettext("unknown class (%s)"), optstr);
681 error_str = errbuf;
682 return (-1);
683 }
684
685 if (mask.am_success != mask.am_failure) {
686 flags |= M_SORF;
687 }
688
689 return (0);
690 }
691
692
693 /*
694 * .func process_fileopt - process command line file options.
695 * .desc Process the command line file options and gather the specified files
696 * together in file groups based upon file name suffix. The user can
697 * specify files explicitly on the command line or via a directory.
698 * This is called after the command line flags are processed (as
699 * denoted by '-').
700 * .call ret = process_fileopt(argc, argv, optindex).
701 * .arg argc - current value of argc.
702 * .arg argv - current value of argv.
703 * .arg optindex- current index into argv (as setup by getopt()).
704 * .ret 0 - no errors detected.
705 * .ret -1 - error detected (message already printed).
706 */
707 int
process_fileopt(int argc,char ** argv,int optindex)708 process_fileopt(int argc, char **argv, int optindex)
709 {
710 int f_mode = FM_ALLDIR;
711 char f_dr[MAXNAMLEN+1];
712 char *f_dir = f_dr;
713 char *fname;
714 static char *std = "standard input";
715 audit_fcb_t *fcb;
716 DIR * dirp;
717 struct dirent *dp;
718 audit_pcb_t *pcb;
719
720 /*
721 * Take input from stdin, not any files.
722 * Use a single fcb to do this.
723 */
724 if (f_stdin) {
725 fcb = (audit_fcb_t *)a_calloc(1, sizeof (*fcb) + strlen(std));
726 (void) strcpy(fcb->fcb_file, std);
727 fcb->fcb_suffix = fcb->fcb_name = fcb->fcb_file;
728 fcb->fcb_next = NULL;
729 fcb->fcb_start = 0;
730 fcb->fcb_end = MAXLONG; /* forever */
731 if ((pcb = get_next_pcb((char *)NULL)) == (audit_pcb_t *)NULL)
732 return (-1);
733 pcb->pcb_suffix = fcb->fcb_file;
734 pcb->pcb_dfirst = pcb->pcb_first = fcb; /* one-item list */
735 pcb->pcb_dlast = pcb->pcb_last = fcb;
736 pcb->pcb_cur = fcb;
737 }
738 /*
739 * No files specified on the command line.
740 * Process a directory of files or subdirectories.
741 */
742 else if (argc == optindex) {
743 /*
744 * A specific server directory was requested.
745 */
746 if (f_server) {
747 if (strchr(f_server, '/')) { /* given full path */
748 f_dir = f_server;
749 f_mode = FM_ALLFILE; /* all files here */
750 } else { /* directory off audit root */
751 f_dir[0] = '\0';
752 (void) strcat(f_dir, f_root);
753 (void) strcat(f_dir, "/");
754 (void) strcat(f_dir, f_server);
755 f_mode = FM_ALLFILE;
756 }
757 }
758 /*
759 * Gather all of the files in the directory 'f_dir'.
760 */
761 if (f_mode == FM_ALLFILE) {
762 if (gather_dir(f_dir)) { /* get those files together */
763 return (-1);
764 }
765 } else {
766 /*
767 * Gather all of the files in all of the
768 * directories in 'f_root'.
769 */
770 if ((dirp = opendir(f_root)) == NULL) {
771 (void) sprintf(errbuf, gettext(
772 "%s can't open directory %s"), ar, f_root);
773 perror(errbuf);
774 return (-1);
775 }
776 /* read the directory and process all of the subs */
777 for (dp = readdir(dirp);
778 dp != NULL; dp = readdir(dirp)) {
779 if (dp->d_name[0] == '.')
780 continue;
781 f_dir[0] = '\0';
782 (void) strcat(f_dir, f_root);
783 (void) strcat(f_dir, "/");
784 (void) strcat(f_dir, dp->d_name);
785 if (gather_dir(f_dir)) /* process a sub */
786 return (-1);
787 }
788 (void) closedir(dirp);
789 }
790 } else {
791 /*
792 * User specified filenames on the comm and line.
793 */
794 f_cmdline = TRUE;
795 for (; optindex < argc; optindex++) {
796 fname = argv[optindex]; /* get a filename */
797 if (proc_file(fname, FALSE))
798 return (-1);
799 }
800 }
801 return (0);
802 }
803
804
805 /*
806 * .func gather_dir - gather a directory's files together.
807 * .desc Process all of the files in a specific directory. The files may
808 * be checked for adherence to the file name form at.
809 * If the directory can't be opened that is ok - just print
810 * a message and continue.
811 * .call ret = gather_dir(dir).
812 * .arg dir - ptr to full pathname of directory.
813 * .ret 0 - no errors detected.
814 * .ret -1 - error detected (message already printed).
815 */
816 int
gather_dir(char * dir)817 gather_dir(char *dir)
818 {
819 char dname[MAXNAMLEN+1];
820 char fname[MAXNAMLEN+1];
821 DIR * dirp;
822 struct dirent *dp;
823
824 (void) snprintf(dname, sizeof (dname), "%s/files", dir);
825
826 if ((dirp = opendir(dname)) == NULL) {
827 if (errno != ENOTDIR) {
828 (void) sprintf(errbuf,
829 gettext("%s can't open directory - %s"), ar, dname);
830 perror(errbuf);
831 }
832 return (0);
833 }
834 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
835 if (dp->d_name[0] == '.') /* can't see hidden files */
836 continue;
837 fname[0] = '\0';
838 (void) strcat(fname, dname); /* create pathname of file */
839 (void) strcat(fname, "/");
840 (void) strcat(fname, dp->d_name);
841 if (proc_file(fname, TRUE))
842 return (-1);
843 }
844 (void) closedir(dirp);
845 return (0);
846 }
847
848
849 /*
850 * .func proc_file - process a single candidate file.
851 * .desc Check out a file to see if it should be used in the merge.
852 * This includes checking the name (mode is TRUE) against the
853 * file format, checking access rights to the file, and thence
854 * getting and fcb and installing the fcb into the correct pcb.
855 * If the file fails then the fcb is not installed into a pcb
856 * and the file dissapears from view.
857 * .call proc_file(fname, mode).
858 * .arg fname - ptr to full pathna me of file.
859 * .arg mode - TRUE if checking adherence to file name format.
860 * .ret 0 - no fatal errors detected.
861 * .ret -1 - fatal error detected - quit altogether
862 * (message already printed).
863 */
864 int
proc_file(char * fname,int mode)865 proc_file(char *fname, int mode)
866 {
867 int reject = FALSE;
868 size_t len;
869 struct stat stat_buf;
870 audit_fcb_t *fcb, *fcbp, *fcbprev;
871 audit_pcb_t *pcb;
872
873 /*
874 * See if it is a weird file like a directory or
875 * character special (around here?).
876 */
877 if (stat(fname, &stat_buf)) {
878 return (0);
879 }
880 if (!S_ISREG(stat_buf.st_mode))
881 return (0);
882 /*
883 * Allocate a new fcb to hold fcb and full filename.
884 */
885 len = sizeof (audit_fcb_t) + strlen(fname);
886 fcb = (audit_fcb_t *)a_calloc(1, len);
887 (void) strcpy(fcb->fcb_file, fname);
888 if (check_file(fcb, mode)) { /* check file name */
889 if (!f_quiet) {
890 (void) fprintf(stderr, "%s %s:\n %s.\n", ar,
891 error_str, fname);
892 }
893 reject = TRUE;
894 } else {
895 /*
896 * Check against file criteria.
897 * Check finish-time here, and start-time later on
898 * while processing.
899 * This is because the start time on a file can be after
900 * the first record(s).
901 */
902 if (f_complete && (fcb->fcb_flags & FF_NOTTERM) && !f_cmdline)
903 reject = TRUE;
904 if (!f_all && (fcb->fcb_end < m_after))
905 reject = TRUE;
906 if (f_machine) {
907 if (strlen(fcb->fcb_suffix) != strlen(f_machine) ||
908 (strcmp(fcb->fcb_suffix, f_machine) != 0)) {
909 reject = TRUE;
910 }
911 }
912 }
913 if (reject == FALSE) {
914 filenum++; /* count of total files to be processed */
915 fcb->fcb_next = NULL;
916 if ((pcb = get_next_pcb(fcb->fcb_suffix)) == NULL) {
917 return (-1);
918 }
919 /* Place FCB into the PCB in order - oldest first. */
920 fcbp = pcb->pcb_first;
921 fcbprev = NULL;
922 while (fcbp != NULL) {
923 if (fcb->fcb_start < fcbp->fcb_start) {
924 if (fcbprev)
925 fcbprev->fcb_next = fcb;
926 else
927 pcb->pcb_dfirst = pcb->pcb_first = fcb;
928 fcb->fcb_next = fcbp;
929 break;
930 }
931 fcbprev = fcbp;
932 fcbp = fcbp->fcb_next;
933 }
934 /* younger than all || empty list */
935 if (!fcb->fcb_next) {
936 if (pcb->pcb_first == NULL)
937 pcb->pcb_dfirst = pcb->pcb_first = fcb;
938 pcb->pcb_dlast = pcb->pcb_last = fcb;
939 if (fcbprev)
940 fcbprev->fcb_next = fcb;
941 }
942 } else {
943 free((char *)fcb); /* rejected */
944 }
945 return (0);
946 }
947
948
949 /*
950 * .func check_file - check filename and setup fcb.
951 * .desc Check adherence to the file format (do_check is TRUE) and setup
952 * the fcb with useful information.
953 * filename format: yyyymmddhhmmss.yyyymmddhhmmss.suffix
954 * yyyymmddhhmmss.not_terminated.suffix
955 * If do_check is FALSE then still see if the filename does confirm
956 * to the format. If it does then extract useful information from
957 * it (start time and end time). But if it doesn't then don't print
958 * any error messages.
959 * .call ret = check_file(fcb, do_check).
960 * .arg fcb - ptr to fcb that holds the file.
961 * .arg do_check - if TRUE do check adherence to file format.
962 * .ret 0 - no errors detected.
963 * .ret -1 - file failed somehow (error_str tells why).
964 */
965 int
check_file(audit_fcb_t * fcb,int do_check)966 check_file(audit_fcb_t *fcb, int do_check)
967 {
968 int ret;
969 char *namep, *slp;
970 char errb[256]; /* build error message */
971 struct tm tme;
972
973 errb[0] = '\0';
974 /* get just the filename */
975 for (slp = namep = fcb->fcb_file; *namep; namep++) {
976 if (*namep == '/')
977 slp = namep + 1; /* slp -> the filename itself */
978 }
979 if (do_check == FALSE) {
980 fcb->fcb_end = MAXLONG; /* forever */
981 fcb->fcb_suffix = NULL;
982 fcb->fcb_name = slp;
983 ret = 0;
984 } else {
985 ret = -1;
986 }
987 if ((int)strlen(slp) < 31) {
988 (void) sprintf(errbuf, gettext("filename too short (%d)"),
989 strlen(slp));
990 error_str = errbuf;
991 return (ret);
992 }
993 /*
994 * Get working copy of filename.
995 */
996 namep = (char *)a_calloc(1, strlen(slp) + 1);
997 (void) strcpy(namep, slp);
998 if (namep[14] != '.' || namep[29] != '.') {
999 (void) sprintf(errbuf,
1000 gettext("invalid filename format (%c or %c)"), namep[14],
1001 namep[29]);
1002 error_str = errbuf;
1003 free(namep);
1004 return (ret);
1005 }
1006 namep[14] = '\0'; /* mark off start time */
1007 namep[29] = '\0'; /* mark off finish time */
1008 if (derive_date(namep, &tme)) {
1009 (void) strcat(errb, gettext("starting time-stamp invalid - "));
1010 (void) strcat(errb, error_str);
1011 (void) strcpy(errbuf, errb);
1012 error_str = errbuf;
1013 free(namep);
1014 return (ret);
1015 }
1016 /*
1017 * Keep start time from filename. Use it to order files in
1018 * the file list. Later we will update this when we read
1019 * the first record from the file.
1020 */
1021 fcb->fcb_start = tm_to_secs(&tme);
1022
1023 if (strcmp(&namep[15], "not_terminated") == 0) {
1024 fcb->fcb_end = MAXLONG; /* forever */
1025 /*
1026 * Only treat a 'not_terminated' file as such if
1027 * it is not on the command line.
1028 */
1029 if (do_check == TRUE)
1030 fcb->fcb_flags |= FF_NOTTERM;
1031 } else if (derive_date(&namep[15], &tme)) {
1032 (void) strcat(errb, gettext("ending time-stamp invalid - "));
1033 (void) strcat(errb, error_str);
1034 (void) strcpy(errbuf, errb);
1035 error_str = errbuf;
1036 free(namep);
1037 return (ret);
1038 } else {
1039 fcb->fcb_end = tm_to_secs(&tme);
1040 }
1041 fcb->fcb_name = slp;
1042 fcb->fcb_suffix = &slp[30];
1043 free(namep);
1044 return (0);
1045 }
1046
1047
1048 /*
1049 * .func get_next_pcb - get a pcb to use.
1050 * .desc The pcb's in the array audit_pcbs are used to hold single file
1051 * groups in the form of a linked list. Each pcb holds files that
1052 * are tied together by a common suffix in the file name. Here we
1053 * get either 1. the existing pcb holding a specified sufix or
1054 * 2. a new pcb if we can't find an existing one.
1055 * .call pcb = get_next_pcb(suffix).
1056 * .arg suffix - ptr to suffix we are seeking.
1057 * .ret pcb - ptr to pcb that hold s the sought suffix.
1058 * .ret NULL- serious failure in memory allocation. Quit processing.
1059 */
1060 audit_pcb_t *
get_next_pcb(char * suffix)1061 get_next_pcb(char *suffix)
1062 {
1063 int i = 0;
1064 int zerosize;
1065 unsigned int size;
1066 audit_pcb_t *pcb;
1067
1068 /* Search through (maybe) entire array. */
1069 while (i < pcbsize) {
1070 pcb = &audit_pcbs[i++];
1071 if (pcb->pcb_first == NULL) {
1072 proc_pcb(pcb, suffix, i);
1073 return (pcb); /* came to an unused one */
1074 }
1075 if (suffix) {
1076 if (strcmp(pcb->pcb_suffix, suffix) == 0)
1077 return (pcb); /* matched one with suffix */
1078 }
1079 }
1080 /*
1081 * Uh-oh, the entire array is used and we haven't gotten one yet.
1082 * Allocate a bigger array.
1083 */
1084 pcbsize += PCB_INC;
1085 size = pcbsize * sizeof (audit_pcb_t);
1086 zerosize = size - ((pcbsize - PCB_INC) * sizeof (audit_pcb_t));
1087 if ((audit_pcbs = (audit_pcb_t *)realloc((char *)audit_pcbs, size)) ==
1088 NULL) {
1089 (void) sprintf(errbuf,
1090 gettext("%s memory reallocation failed (%d bytes)"), ar,
1091 size);
1092 perror(errbuf);
1093 audit_stats(); /* give user statistics on usage */
1094 return (NULL); /* really bad thing to have happen */
1095 }
1096 /*
1097 * Don't know if realloc clears the new memory like calloc would.
1098 */
1099 (void) memset((void *) & audit_pcbs[pcbsize-PCB_INC], 0,
1100 (size_t)zerosize);
1101 pcb = &audit_pcbs[pcbsize-PCB_INC]; /* allocate the first new one */
1102 proc_pcb(pcb, suffix, pcbsize - PCB_INC);
1103 return (pcb);
1104 }
1105
1106
1107 /*
1108 * .func proc_pcb - process pcb.
1109 * .desc Common pcb processing for above routine.
1110 * .call proc_pcb(pcb, suffix, i).
1111 * .arg pcb - ptr to pcb.
1112 * .arg suffix - prt to suffix tha t ties this group together.
1113 * .arg i - index into audit_pcbs[ ].
1114 * .ret void.
1115 */
1116 void
proc_pcb(audit_pcb_t * pcb,char * suffix,int i)1117 proc_pcb(audit_pcb_t *pcb, char *suffix, int i)
1118 {
1119 if (suffix)
1120 pcb->pcb_suffix = suffix;
1121 pcbnum++; /* one more pcb in use */
1122 pcb->pcb_size = AUDITBUFSIZE;
1123 pcb->pcb_rec = (char *)a_calloc(1, AUDITBUFSIZE);
1124 pcb->pcb_time = -1;
1125 pcb->pcb_flags |= PF_USEFILE; /* note this one controls files */
1126 pcb->pcb_procno = i; /* save index into audit_pcbs [] for id */
1127 }
1128
1129
1130 /*
1131 * .func proc_label - process label range argument.
1132 * .desc Parse label range lower-bound[;upper-bound]
1133 * .call ret = proc_label(optstr).
1134 * .arg opstr - ptr to label range string
1135 * .ret 0 - no errors detected.
1136 * .ret -1 - errors detected (error_str set).
1137 */
1138
1139 int
proc_label(char * optstr)1140 proc_label(char *optstr)
1141 {
1142 char *p;
1143 int error;
1144
1145 if (flags & M_LABEL) {
1146 error_str = gettext("'l' option specified multiple times");
1147 return (-1);
1148 }
1149 flags |= M_LABEL;
1150
1151 if ((m_label = malloc(sizeof (m_range_t))) == NULL) {
1152 return (-1);
1153 }
1154 m_label->lower_bound = NULL;
1155 m_label->upper_bound = NULL;
1156
1157 p = strchr(optstr, ';');
1158 if (p == NULL) {
1159 /* exact label match, lower and upper range bounds the same */
1160 if (str_to_label(optstr, &m_label->lower_bound, MAC_LABEL,
1161 L_NO_CORRECTION, &error) == -1) {
1162 (void) sprintf(errbuf,
1163 gettext("invalid sensitivity label (%s) err %d"),
1164 optstr, error);
1165 error_str = errbuf;
1166 goto errout;
1167 }
1168 m_label->upper_bound = m_label->lower_bound;
1169 return (0);
1170 }
1171 if (p == optstr) {
1172 /* lower bound is not specified .. default is admin_low */
1173 if (str_to_label(ADMIN_LOW, &m_label->lower_bound, MAC_LABEL,
1174 L_NO_CORRECTION, &error) == -1) {
1175 goto errout;
1176 }
1177
1178 p++;
1179 if (*p == '\0') {
1180 /* upper bound not specified .. default is admin_high */
1181 if (str_to_label(ADMIN_HIGH, &m_label->upper_bound,
1182 MAC_LABEL, L_NO_CORRECTION, &error) == -1) {
1183 goto errout;
1184 }
1185 } else {
1186 if (str_to_label(p, &m_label->upper_bound, MAC_LABEL,
1187 L_NO_CORRECTION, &error) == -1) {
1188 (void) sprintf(errbuf, gettext(
1189 "invalid sensitivity label (%s) err %d"),
1190 p, error);
1191 error_str = errbuf;
1192 goto errout;
1193 }
1194 }
1195 return (0);
1196 }
1197 *p++ = '\0';
1198 if (str_to_label(optstr, &m_label->lower_bound, MAC_LABEL,
1199 L_NO_CORRECTION, &error) == -1) {
1200 (void) sprintf(errbuf,
1201 gettext("invalid sensitivity label (%s) err %d"), optstr,
1202 error);
1203 error_str = errbuf;
1204 goto errout;
1205 }
1206 if (*p == '\0') {
1207 /* upper bound is not specified .. default is admin_high */
1208 if (str_to_label(ADMIN_HIGH, &m_label->upper_bound,
1209 MAC_LABEL, L_NO_CORRECTION, &error) == -1) {
1210 goto errout;
1211 }
1212 } else {
1213 if (str_to_label(p, &m_label->upper_bound, MAC_LABEL,
1214 L_NO_CORRECTION, &error) == -1) {
1215 (void) sprintf(errbuf,
1216 gettext("invalid sensitivity label (%s) err %d"),
1217 p, error);
1218 error_str = errbuf;
1219 goto errout;
1220 }
1221 }
1222 /* make sure that upper bound dominates the lower bound */
1223 if (!bldominates(m_label->upper_bound, m_label->lower_bound)) {
1224 *--p = ';';
1225 (void) sprintf(errbuf,
1226 gettext("invalid sensitivity label range (%s)"), optstr);
1227 error_str = errbuf;
1228 goto errout;
1229 }
1230 return (0);
1231
1232 errout:
1233 m_label_free(m_label->upper_bound);
1234 m_label_free(m_label->lower_bound);
1235 free(m_label);
1236
1237 return (-1);
1238 }
1239
1240 /*
1241 * proc_zonename - pick up zone name.
1242 *
1243 * all non-empty and not-too-long strings are valid since any name
1244 * may be valid.
1245 *
1246 * ret 0: non-empty string
1247 * ret -1: empty string or string is too long.
1248 */
1249 static int
proc_zonename(char * optstr)1250 proc_zonename(char *optstr)
1251 {
1252 size_t length = strlen(optstr);
1253 if ((length < 1) || (length > ZONENAME_MAX)) {
1254 (void) sprintf(errbuf,
1255 gettext("invalid zone name: %s"), optstr);
1256 error_str = errbuf;
1257 return (-1);
1258 }
1259 zonename = strdup(optstr);
1260 flags |= M_ZONENAME;
1261 return (0);
1262 }
1263
1264 /*
1265 * proc_frmi - set up frmi for pattern matching.
1266 * Logic ripped off of scf_walk_fmri()
1267 * Thanks to the smf team.
1268 *
1269 * ret 0: OK
1270 * ret -1: error
1271 */
1272 static int
proc_fmri(char * optstr)1273 proc_fmri(char *optstr)
1274 {
1275 if (strpbrk(optstr, "*?[") != NULL) {
1276 /* have a pattern to glob for */
1277
1278 fmri.sp_type = PATTERN_GLOB;
1279 if (optstr[0] == '*' ||
1280 (strlen(optstr) >= 4 && optstr[3] == ':')) {
1281 fmri.sp_arg = strdup(optstr);
1282 } else if ((fmri.sp_arg = malloc(strlen(optstr) + 6)) != NULL) {
1283 (void) snprintf(fmri.sp_arg, strlen(optstr) + 6,
1284 "svc:/%s", optstr);
1285 }
1286 } else {
1287 fmri.sp_type = PATTERN_PARTIAL;
1288 fmri.sp_arg = strdup(optstr);
1289 }
1290 if (fmri.sp_arg == NULL)
1291 return (-1);
1292
1293 return (0);
1294 }
1295