xref: /freebsd/contrib/openbsm/bin/auditreduce/auditreduce.c (revision 8847579c57d6aff2b3371c707dce7a2cee8389aa)
1 /*
2  * Copyright (c) 2004 Apple Computer, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * $P4: //depot/projects/trustedbsd/openbsm/bin/auditreduce/auditreduce.c#14 $
30  */
31 
32 /*
33  * Tool used to merge and select audit records from audit trail files
34  */
35 
36 /*
37  * XXX Currently we do not support merging of records from multiple
38  * XXX audit trail files
39  * XXX We assume that records are sorted chronologically - both wrt to
40  * XXX the records present within the file and between the files themselves
41  */
42 
43 #include <bsm/libbsm.h>
44 
45 #include <err.h>
46 #include <grp.h>
47 #include <pwd.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <sysexits.h>
51 #include <string.h>
52 #include <time.h>
53 #include <unistd.h>
54 
55 #include "auditreduce.h"
56 
57 extern char		*optarg;
58 extern int		 optind, optopt, opterr,optreset;
59 
60 static au_mask_t	 maskp;		/* Class. */
61 static time_t		 p_atime;	/* Created after this time. */
62 static time_t		 p_btime;	/* Created before this time. */
63 static uint16_t		 p_evtype;	/* Event that we are searching for. */
64 static int		 p_auid;	/* Audit id. */
65 static int		 p_euid;	/* Effective user id. */
66 static int		 p_egid;	/* Effective group id. */
67 static int		 p_rgid;	/* Real group id. */
68 static int		 p_ruid;	/* Real user id. */
69 static int		 p_subid;	/* Subject id. */
70 
71 /*
72  * Following are the objects (-o option) that we can select upon.
73  */
74 static char	*p_fileobj = NULL;
75 static char	*p_msgqobj = NULL;
76 static char	*p_pidobj = NULL;
77 static char	*p_semobj = NULL;
78 static char	*p_shmobj = NULL;
79 static char	*p_sockobj = NULL;
80 
81 static uint32_t opttochk = 0;
82 
83 static void
84 usage(const char *msg)
85 {
86 	fprintf(stderr, "%s\n", msg);
87 	fprintf(stderr, "Usage: auditreduce [options] audit-trail-file [....] \n");
88 	fprintf(stderr, "\tOptions are : \n");
89 	fprintf(stderr, "\t-A : all records\n");
90 	fprintf(stderr, "\t-a YYYYMMDD[HH[[MM[SS]]] : after date\n");
91 	fprintf(stderr, "\t-b YYYYMMDD[HH[[MM[SS]]] : before date\n");
92 	fprintf(stderr, "\t-c <flags> : matching class\n");
93 	fprintf(stderr, "\t-d YYYYMMDD : on date\n");
94 	fprintf(stderr, "\t-e <uid|name>  : effective user\n");
95 	fprintf(stderr, "\t-f <gid|group> : effective group\n");
96 	fprintf(stderr, "\t-g <gid|group> : real group\n");
97 	fprintf(stderr, "\t-j <pid> : subject id \n");
98 	fprintf(stderr, "\t-m <evno|evname> : matching event\n");
99 	fprintf(stderr, "\t-o objecttype=objectvalue\n");
100 	fprintf(stderr, "\t\t file=<pathname>\n");
101 	fprintf(stderr, "\t\t msgqid=<ID>\n");
102 	fprintf(stderr, "\t\t pid=<ID>\n");
103 	fprintf(stderr, "\t\t semid=<ID>\n");
104 	fprintf(stderr, "\t\t shmid=<ID>\n");
105 	fprintf(stderr, "\t-r <uid|name> : real user\n");
106 	fprintf(stderr, "\t-u <uid|name> : audit user\n");
107 	exit(EX_USAGE);
108 }
109 
110 /*
111  * Check if the given auid matches the selection criteria.
112  */
113 static int
114 select_auid(int au)
115 {
116 
117 	/* Check if we want to select on auid. */
118 	if (ISOPTSET(opttochk, OPT_u)) {
119 		if (au != p_auid)
120 			return (0);
121 	}
122 	return (1);
123 }
124 
125 /*
126  * Check if the given euid matches the selection criteria.
127  */
128 static int
129 select_euid(int euser)
130 {
131 
132 	/* Check if we want to select on euid. */
133 	if (ISOPTSET(opttochk, OPT_e)) {
134 		if (euser != p_euid)
135 			return (0);
136 	}
137 	return (1);
138 }
139 
140 /*
141  * Check if the given egid matches the selection criteria.
142  */
143 static int
144 select_egid(int egrp)
145 {
146 
147 	/* Check if we want to select on egid. */
148 	if (ISOPTSET(opttochk, OPT_f)) {
149 		if (egrp != p_egid)
150 			return (0);
151 	}
152 	return (1);
153 }
154 
155 /*
156  * Check if the given rgid matches the selection criteria.
157  */
158 static int
159 select_rgid(int grp)
160 {
161 
162 	/* Check if we want to select on rgid. */
163 	if (ISOPTSET(opttochk, OPT_g)) {
164 		if (grp != p_rgid)
165 			return (0);
166 	}
167 	return (1);
168 }
169 
170 /*
171  * Check if the given ruid matches the selection criteria.
172  */
173 static int
174 select_ruid(int user)
175 {
176 
177 	/* Check if we want to select on rgid. */
178 	if (ISOPTSET(opttochk, OPT_r)) {
179 		if (user != p_ruid)
180 			return (0);
181 	}
182 	return (1);
183 }
184 
185 /*
186  * Check if the given subject id (pid) matches the selection criteria.
187  */
188 static int
189 select_subid(int subid)
190 {
191 
192 	/* Check if we want to select on subject uid. */
193 	if (ISOPTSET(opttochk, OPT_j)) {
194 		if (subid != p_subid)
195 			return (0);
196 	}
197 	return (1);
198 }
199 
200 
201 /*
202  * Check if object's pid maches the given pid.
203  */
204 static int
205 select_pidobj(uint32_t pid)
206 {
207 
208 	if (ISOPTSET(opttochk, OPT_op)) {
209 		if (pid != strtol(p_pidobj, (char **)NULL, 10))
210 			return (0);
211 	}
212 	return (1);
213 }
214 
215 /*
216  * Check if the given ipc object with the given type matches the selection
217  * criteria.
218  */
219 static int
220 select_ipcobj(u_char type, uint32_t id, uint32_t *optchkd)
221 {
222 
223 	if (type == AT_IPC_MSG) {
224 		SETOPT((*optchkd), OPT_om);
225 		if (ISOPTSET(opttochk, OPT_om)) {
226 			if (id != strtol(p_msgqobj, (char **)NULL, 10))
227 				return (0);
228 		}
229 		return (1);
230 	} else if (type == AT_IPC_SEM) {
231 		SETOPT((*optchkd), OPT_ose);
232 		if (ISOPTSET(opttochk, OPT_ose)) {
233 			if (id != strtol(p_semobj, (char **)NULL, 10))
234 				return (0);
235 		}
236 		return (1);
237 	} else if (type == AT_IPC_SHM) {
238 		SETOPT((*optchkd), OPT_osh);
239 		if (ISOPTSET(opttochk, OPT_osh)) {
240 			if (id != strtol(p_shmobj, (char **)NULL, 10))
241 				return (0);
242 		}
243 		return (1);
244 	}
245 
246 	/* Unknown type -- filter if *any* ipc filtering is required. */
247 	if (ISOPTSET(opttochk, OPT_om) || ISOPTSET(opttochk, OPT_ose)
248 	    || ISOPTSET(opttochk, OPT_osh))
249 		return (0);
250 
251 	return (1);
252 }
253 
254 
255 /*
256  * Check if the file name matches selection criteria.
257  */
258 static int
259 select_filepath(char *path, uint32_t *optchkd)
260 {
261 	char *loc;
262 
263 	SETOPT((*optchkd), OPT_of);
264 	if (ISOPTSET(opttochk, OPT_of)) {
265 		if (p_fileobj[0] == '~') {
266 			/* Object should not be in path. */
267 			loc = strstr(path, p_fileobj + 1);
268 			if ((loc != NULL) && (loc == path))
269 				return (0);
270 		} else {
271 			/* Object should be in path. */
272 			loc = strstr(path, p_fileobj);
273 			if ((loc == NULL) || (loc != path))
274 				return (0);
275 		}
276 	}
277 	return (1);
278 }
279 
280 /*
281  * Returns 1 if the following pass the selection rules:
282  *
283  * before-time,
284  * after time,
285  * date,
286  * class,
287  * event
288  */
289 static int
290 select_hdr32(tokenstr_t tok, uint32_t *optchkd)
291 {
292 
293 	SETOPT((*optchkd), (OPT_A | OPT_a | OPT_b | OPT_c | OPT_m));
294 
295 	/* The A option overrides a, b and d. */
296 	if (!ISOPTSET(opttochk, OPT_A)) {
297 		if (ISOPTSET(opttochk, OPT_a)) {
298 			if (difftime((time_t)tok.tt.hdr32.s, p_atime) < 0) {
299 				/* Record was created before p_atime. */
300 				return (0);
301 			}
302 		}
303 
304 		if (ISOPTSET(opttochk, OPT_b)) {
305 			if (difftime(p_btime, (time_t)tok.tt.hdr32.s) < 0) {
306 				/* Record was created after p_btime. */
307 				return (0);
308 			}
309 		}
310 	}
311 
312 	if (ISOPTSET(opttochk, OPT_c)) {
313 		/*
314 		 * Check if the classes represented by the event matches
315 		 * given class.
316 		 */
317 		if (au_preselect(tok.tt.hdr32.e_type, &maskp, AU_PRS_BOTH,
318 		    AU_PRS_USECACHE) != 1)
319 			return (0);
320 	}
321 
322 	/* Check if event matches. */
323 	if (ISOPTSET(opttochk, OPT_m)) {
324 		if (tok.tt.hdr32.e_type != p_evtype)
325 			return (0);
326 	}
327 
328 	return (1);
329 }
330 
331 /*
332  * Return 1 if checks for the the following succeed
333  * auid,
334  * euid,
335  * egid,
336  * rgid,
337  * ruid,
338  * process id
339  */
340 static int
341 select_proc32(tokenstr_t tok, uint32_t *optchkd)
342 {
343 
344 	SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_op));
345 
346 	if (!select_auid(tok.tt.proc32.auid))
347 		return (0);
348 	if (!select_euid(tok.tt.proc32.euid))
349 		return (0);
350 	if (!select_egid(tok.tt.proc32.egid))
351 		return (0);
352 	if (!select_rgid(tok.tt.proc32.rgid))
353 		return (0);
354 	if (!select_ruid(tok.tt.proc32.ruid))
355 		return (0);
356 	if (!select_pidobj(tok.tt.proc32.pid))
357 		return (0);
358 	return (1);
359 }
360 
361 /*
362  * Return 1 if checks for the the following succeed
363  * auid,
364  * euid,
365  * egid,
366  * rgid,
367  * ruid,
368  * subject id
369  */
370 static int
371 select_subj32(tokenstr_t tok, uint32_t *optchkd)
372 {
373 
374 	SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_j));
375 
376 	if (!select_auid(tok.tt.subj32.auid))
377 		return (0);
378 	if (!select_euid(tok.tt.subj32.euid))
379 		return (0);
380 	if (!select_egid(tok.tt.subj32.egid))
381 		return (0);
382 	if (!select_rgid(tok.tt.subj32.rgid))
383 		return (0);
384 	if (!select_ruid(tok.tt.subj32.ruid))
385 		return (0);
386 	if (!select_subid(tok.tt.subj32.pid))
387 		return (0);
388 	return (1);
389 }
390 
391 /*
392  * Read each record from the audit trail.  Check if it is selected after
393  * passing through each of the options
394  */
395 static int
396 select_records(FILE *fp)
397 {
398 	u_char *buf;
399 	tokenstr_t tok;
400 	int reclen;
401 	int bytesread;
402 	int selected;
403 	uint32_t optchkd;
404 
405 	int err = 0;
406 	while ((reclen = au_read_rec(fp, &buf)) != -1) {
407 		optchkd = 0;
408 		bytesread = 0;
409 		selected = 1;
410 		while ((selected == 1) && (bytesread < reclen)) {
411 			if (-1 == au_fetch_tok(&tok, buf + bytesread,
412 			    reclen - bytesread)) {
413 				/* Is this an incomplete record? */
414 				err = 1;
415 				break;
416 			}
417 
418 			/*
419 			 * For each token type we have have different
420 			 * selection criteria.
421 			 */
422 			switch(tok.id) {
423 			case AU_HEADER_32_TOKEN:
424 					selected = select_hdr32(tok,
425 					    &optchkd);
426 					break;
427 
428 			case AU_PROCESS_32_TOKEN:
429 					selected = select_proc32(tok,
430 					    &optchkd);
431 					break;
432 
433 			case AU_SUBJECT_32_TOKEN:
434 					selected = select_subj32(tok,
435 					    &optchkd);
436 					break;
437 
438 			case AU_IPC_TOKEN:
439 					selected = select_ipcobj(
440 					    tok.tt.ipc.type, tok.tt.ipc.id,
441 					    &optchkd);
442 					break;
443 
444 			case AU_FILE_TOKEN:
445 					selected = select_filepath(
446 					    tok.tt.file.name, &optchkd);
447 					break;
448 
449 			case AU_PATH_TOKEN:
450 					selected = select_filepath(
451 					    tok.tt.path.path, &optchkd);
452 					break;
453 
454 			/*
455 			 * The following tokens dont have any relevant
456 			 * attributes that we can select upon.
457 			 */
458 			case AU_TRAILER_TOKEN:
459 			case AU_ARG32_TOKEN:
460 			case AU_ATTR32_TOKEN:
461 			case AU_EXIT_TOKEN:
462 			case AU_NEWGROUPS_TOKEN:
463 			case AU_IN_ADDR_TOKEN:
464 			case AU_IP_TOKEN:
465 			case AU_IPCPERM_TOKEN:
466 			case AU_IPORT_TOKEN:
467 			case AU_OPAQUE_TOKEN:
468 			case AU_RETURN_32_TOKEN:
469 			case AU_SEQ_TOKEN:
470 			case AU_TEXT_TOKEN:
471 			case AU_ARB_TOKEN:
472 			case AU_SOCK_TOKEN:
473 			default:
474 				break;
475 			}
476 			bytesread += tok.len;
477 		}
478 		if ((selected == 1) && (!err)) {
479 			/* Check if all the options were matched. */
480 			if (!(opttochk & ~optchkd)) {
481 				/* XXX Write this record to the output file. */
482 				/* default to stdout */
483 				fwrite(buf, 1, reclen, stdout);
484 			}
485 		}
486 		free(buf);
487 	}
488 	return (0);
489 }
490 
491 /*
492  * The -o option has the form object_type=object_value.  Identify the object
493  * components.
494  */
495 void
496 parse_object_type(char *name, char *val)
497 {
498 	if (val == NULL)
499 		return;
500 
501 	if (!strcmp(name, FILEOBJ)) {
502 		p_fileobj = val;
503 		SETOPT(opttochk, OPT_of);
504 	} else if (!strcmp(name, MSGQIDOBJ)) {
505 		p_msgqobj = val;
506 		SETOPT(opttochk, OPT_om);
507 	} else if (!strcmp(name, PIDOBJ)) {
508 		p_pidobj = val;
509 		SETOPT(opttochk, OPT_op);
510 	} else if (!strcmp(name, SEMIDOBJ)) {
511 		p_semobj = val;
512 		SETOPT(opttochk, OPT_ose);
513 	} else if (!strcmp(name, SHMIDOBJ)) {
514 		p_shmobj = val;
515 		SETOPT(opttochk, OPT_osh);
516 	} else if (!strcmp(name, SOCKOBJ)) {
517 		p_sockobj = val;
518 		SETOPT(opttochk, OPT_oso);
519 	} else
520 		usage("unknown value for -o");
521 }
522 
523 int
524 main(int argc, char **argv)
525 {
526 	struct group *grp;
527 	struct passwd *pw;
528 	struct tm tm;
529 	au_event_t *n;
530 	FILE *fp;
531 	int i;
532 	char *objval, *converr;
533 	int ch;
534 	char timestr[128];
535 	char *fname;
536 
537 	converr = NULL;
538 
539 	while ((ch = getopt(argc, argv, "Aa:b:c:d:e:f:g:j:m:o:r:u:")) != -1) {
540 		switch(ch) {
541 		case 'A':
542 			SETOPT(opttochk, OPT_A);
543 			break;
544 
545 		case 'a':
546 			if (ISOPTSET(opttochk, OPT_a)) {
547 				usage("d is exclusive with a and b");
548 			}
549 			SETOPT(opttochk, OPT_a);
550 			strptime(optarg, "%Y%m%d%H%M%S", &tm);
551 			strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
552 			    &tm);
553 			/* fprintf(stderr, "Time converted = %s\n", timestr); */
554 			p_atime = mktime(&tm);
555 			break;
556 
557 		case 'b':
558 			if (ISOPTSET(opttochk, OPT_b)) {
559 				usage("d is exclusive with a and b");
560 			}
561 			SETOPT(opttochk, OPT_b);
562 			strptime(optarg, "%Y%m%d%H%M%S", &tm);
563 			strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
564 			    &tm);
565 			/* fprintf(stderr, "Time converted = %s\n", timestr); */
566 			p_btime = mktime(&tm);
567 			break;
568 
569 		case 'c':
570 			if (0 != getauditflagsbin(optarg, &maskp)) {
571 				/* Incorrect class */
572 				usage("Incorrect class");
573 			}
574 			SETOPT(opttochk, OPT_c);
575 			break;
576 
577 		case 'd':
578 			if (ISOPTSET(opttochk, OPT_b) || ISOPTSET(opttochk,
579 			    OPT_a))
580 				usage("'d' is exclusive with 'a' and 'b'");
581 			SETOPT(opttochk, OPT_d);
582 			strptime(optarg, "%Y%m%d", &tm);
583 			strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
584 			/* fprintf(stderr, "Time converted = %s\n", timestr); */
585 			p_atime = mktime(&tm);
586 			tm.tm_hour = 23;
587 			tm.tm_min = 59;
588 			tm.tm_sec = 59;
589 			strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
590 			/* fprintf(stderr, "Time converted = %s\n", timestr); */
591 			p_btime = mktime(&tm);
592 			break;
593 
594 		case 'e':
595 			p_euid = strtol(optarg, &converr, 10);
596 			if (*converr != '\0') {
597 				/* Try the actual name */
598 				if ((pw = getpwnam(optarg)) == NULL)
599 					break;
600 				p_euid = pw->pw_uid;
601 			}
602 			SETOPT(opttochk, OPT_e);
603 			break;
604 
605 		case 'f':
606 			p_egid = strtol(optarg, &converr, 10);
607 			if (*converr != '\0') {
608 				/* Try actual group name. */
609 				if ((grp = getgrnam(optarg)) == NULL)
610 					break;
611 				p_egid = grp->gr_gid;
612 			}
613 			SETOPT(opttochk, OPT_f);
614 			break;
615 
616 		case 'g':
617 			p_rgid = strtol(optarg, &converr, 10);
618 			if (*converr != '\0') {
619 				/* Try actual group name. */
620 				if ((grp = getgrnam(optarg)) == NULL)
621 					break;
622 				p_rgid = grp->gr_gid;
623 			}
624 			SETOPT(opttochk, OPT_g);
625 			break;
626 
627 		case 'j':
628 			p_subid = strtol(optarg, (char **)NULL, 10);
629 			SETOPT(opttochk, OPT_j);
630 			break;
631 
632 		case 'm':
633 			p_evtype = strtol(optarg, (char **)NULL, 10);
634 			if (p_evtype == 0) {
635 				/* Could be the string representation. */
636 				n = getauevnonam(optarg);
637 				if (n == NULL)
638 					usage("Incorrect event name");
639 				p_evtype = *n;
640 				free(n);
641 			}
642 			SETOPT(opttochk, OPT_m);
643 			break;
644 
645 		case 'o':
646 			objval = strchr(optarg, '=');
647 			if (objval != NULL) {
648 				*objval = '\0';
649 				objval += 1;
650 				parse_object_type(optarg, objval);
651 			}
652 			break;
653 
654 		case 'r':
655 			p_ruid = strtol(optarg, &converr, 10);
656 			if (*converr != '\0') {
657 				if ((pw = getpwnam(optarg)) == NULL)
658 					break;
659 				p_ruid = pw->pw_uid;
660 			}
661 			SETOPT(opttochk, OPT_r);
662 			break;
663 
664 		case 'u':
665 			p_auid = strtol(optarg, &converr, 10);
666 			if (*converr != '\0') {
667 				if ((pw = getpwnam(optarg)) == NULL)
668 					break;
669 				p_auid = pw->pw_uid;
670 			}
671 			SETOPT(opttochk, OPT_u);
672 			break;
673 
674 		case '?':
675 		default:
676 			usage("Unknown option");
677 		}
678 	}
679 	argv += optind;
680 	argc -= optind;
681 
682 	if (argc == 0)
683 		usage("Filename needed");
684 
685 	/*
686 	 * XXX: We should actually be merging records here.
687 	 */
688 	for (i = 0; i < argc; i++) {
689 		fname = argv[i];
690 		fp = fopen(fname, "r");
691 		if (fp == NULL)
692 			errx(EXIT_FAILURE, "Couldn't open %s", fname);
693 		if (select_records(fp) == -1) {
694 			errx(EXIT_FAILURE, "Couldn't select records %s",
695 			    fname);
696 		}
697 		fclose(fp);
698 	}
699 	exit(EXIT_SUCCESS);
700 }
701