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