2 * Copyright (c) 2004 Apple Computer, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
29 * $P4: //depot/projects/trustedbsd/openbsm/bin/auditreduce/auditreduce.c#20 $
33 * Tool used to merge and select audit records from audit trail files
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
43 #include <config/config.h>
44 #ifdef HAVE_FULL_QUEUE_H
45 #include <sys/queue.h>
47 #include <compat/queue.h>
50 #include <bsm/libbsm.h>
64 #include "auditreduce.h"
66 static TAILQ_HEAD(tailhead, re_entry) re_head =
67 TAILQ_HEAD_INITIALIZER(re_head);
70 extern int optind, optopt, opterr,optreset;
72 static au_mask_t maskp; /* Class. */
73 static time_t p_atime; /* Created after this time. */
74 static time_t p_btime; /* Created before this time. */
75 static uint16_t p_evtype; /* Event that we are searching for. */
76 static int p_auid; /* Audit id. */
77 static int p_euid; /* Effective user id. */
78 static int p_egid; /* Effective group id. */
79 static int p_rgid; /* Real group id. */
80 static int p_ruid; /* Real user id. */
81 static int p_subid; /* Subject id. */
84 * Following are the objects (-o option) that we can select upon.
86 static char *p_fileobj = NULL;
87 static char *p_msgqobj = NULL;
88 static char *p_pidobj = NULL;
89 static char *p_semobj = NULL;
90 static char *p_shmobj = NULL;
91 static char *p_sockobj = NULL;
93 static uint32_t opttochk = 0;
96 parse_regexp(char *re_string)
98 char *orig, *copy, re_error[64];
100 int error, nstrs, i, len;
102 copy = strdup(re_string);
105 for (nstrs = 0, i = 0; i < len; i++) {
106 if (copy[i] == ',' && i > 0) {
107 if (copy[i - 1] == '\\')
108 strcpy(©[i - 1], ©[i]);
115 TAILQ_INIT(&re_head);
116 for (i = 0; i < nstrs + 1; i++) {
117 rep = calloc(1, sizeof(*rep));
119 (void) fprintf(stderr, "calloc: %s\n",
127 rep->re_pattern = strdup(copy);
128 error = regcomp(&rep->re_regexp, rep->re_pattern,
129 REG_EXTENDED | REG_NOSUB);
131 regerror(error, &rep->re_regexp, re_error, 64);
132 (void) fprintf(stderr, "regcomp: %s\n", re_error);
135 TAILQ_INSERT_TAIL(&re_head, rep, re_glue);
143 usage(const char *msg)
145 fprintf(stderr, "%s\n", msg);
146 fprintf(stderr, "Usage: auditreduce [options] [file ...]\n");
147 fprintf(stderr, "\tOptions are : \n");
148 fprintf(stderr, "\t-A : all records\n");
149 fprintf(stderr, "\t-a YYYYMMDD[HH[[MM[SS]]] : after date\n");
150 fprintf(stderr, "\t-b YYYYMMDD[HH[[MM[SS]]] : before date\n");
151 fprintf(stderr, "\t-c <flags> : matching class\n");
152 fprintf(stderr, "\t-d YYYYMMDD : on date\n");
153 fprintf(stderr, "\t-e <uid|name> : effective user\n");
154 fprintf(stderr, "\t-f <gid|group> : effective group\n");
155 fprintf(stderr, "\t-g <gid|group> : real group\n");
156 fprintf(stderr, "\t-j <pid> : subject id \n");
157 fprintf(stderr, "\t-m <evno|evname> : matching event\n");
158 fprintf(stderr, "\t-o objecttype=objectvalue\n");
159 fprintf(stderr, "\t\t file=<pathname>\n");
160 fprintf(stderr, "\t\t msgqid=<ID>\n");
161 fprintf(stderr, "\t\t pid=<ID>\n");
162 fprintf(stderr, "\t\t semid=<ID>\n");
163 fprintf(stderr, "\t\t shmid=<ID>\n");
164 fprintf(stderr, "\t-r <uid|name> : real user\n");
165 fprintf(stderr, "\t-u <uid|name> : audit user\n");
170 * Check if the given auid matches the selection criteria.
176 /* Check if we want to select on auid. */
177 if (ISOPTSET(opttochk, OPT_u)) {
185 * Check if the given euid matches the selection criteria.
188 select_euid(int euser)
191 /* Check if we want to select on euid. */
192 if (ISOPTSET(opttochk, OPT_e)) {
200 * Check if the given egid matches the selection criteria.
203 select_egid(int egrp)
206 /* Check if we want to select on egid. */
207 if (ISOPTSET(opttochk, OPT_f)) {
215 * Check if the given rgid matches the selection criteria.
221 /* Check if we want to select on rgid. */
222 if (ISOPTSET(opttochk, OPT_g)) {
230 * Check if the given ruid matches the selection criteria.
233 select_ruid(int user)
236 /* Check if we want to select on rgid. */
237 if (ISOPTSET(opttochk, OPT_r)) {
245 * Check if the given subject id (pid) matches the selection criteria.
248 select_subid(int subid)
251 /* Check if we want to select on subject uid. */
252 if (ISOPTSET(opttochk, OPT_j)) {
253 if (subid != p_subid)
261 * Check if object's pid maches the given pid.
264 select_pidobj(uint32_t pid)
267 if (ISOPTSET(opttochk, OPT_op)) {
268 if (pid != strtol(p_pidobj, (char **)NULL, 10))
275 * Check if the given ipc object with the given type matches the selection
279 select_ipcobj(u_char type, uint32_t id, uint32_t *optchkd)
282 if (type == AT_IPC_MSG) {
283 SETOPT((*optchkd), OPT_om);
284 if (ISOPTSET(opttochk, OPT_om)) {
285 if (id != strtol(p_msgqobj, (char **)NULL, 10))
289 } else if (type == AT_IPC_SEM) {
290 SETOPT((*optchkd), OPT_ose);
291 if (ISOPTSET(opttochk, OPT_ose)) {
292 if (id != strtol(p_semobj, (char **)NULL, 10))
296 } else if (type == AT_IPC_SHM) {
297 SETOPT((*optchkd), OPT_osh);
298 if (ISOPTSET(opttochk, OPT_osh)) {
299 if (id != strtol(p_shmobj, (char **)NULL, 10))
305 /* Unknown type -- filter if *any* ipc filtering is required. */
306 if (ISOPTSET(opttochk, OPT_om) || ISOPTSET(opttochk, OPT_ose)
307 || ISOPTSET(opttochk, OPT_osh))
315 * Check if the file name matches selection criteria.
318 select_filepath(char *path, uint32_t *optchkd)
320 struct re_entry *rep;
323 SETOPT((*optchkd), OPT_of);
325 if (ISOPTSET(opttochk, OPT_of)) {
327 TAILQ_FOREACH(rep, &re_head, re_glue) {
328 if (regexec(&rep->re_regexp, path, 0, NULL,
330 return (!rep->re_negate);
337 * Returns 1 if the following pass the selection rules:
346 select_hdr32(tokenstr_t tok, uint32_t *optchkd)
349 SETOPT((*optchkd), (OPT_A | OPT_a | OPT_b | OPT_c | OPT_m));
351 /* The A option overrides a, b and d. */
352 if (!ISOPTSET(opttochk, OPT_A)) {
353 if (ISOPTSET(opttochk, OPT_a)) {
354 if (difftime((time_t)tok.tt.hdr32.s, p_atime) < 0) {
355 /* Record was created before p_atime. */
360 if (ISOPTSET(opttochk, OPT_b)) {
361 if (difftime(p_btime, (time_t)tok.tt.hdr32.s) < 0) {
362 /* Record was created after p_btime. */
368 if (ISOPTSET(opttochk, OPT_c)) {
370 * Check if the classes represented by the event matches
373 if (au_preselect(tok.tt.hdr32.e_type, &maskp, AU_PRS_BOTH,
374 AU_PRS_USECACHE) != 1)
378 /* Check if event matches. */
379 if (ISOPTSET(opttochk, OPT_m)) {
380 if (tok.tt.hdr32.e_type != p_evtype)
388 select_return32(tokenstr_t tok_ret32, tokenstr_t tok_hdr32, uint32_t *optchkd)
392 SETOPT((*optchkd), (OPT_c));
393 if (tok_ret32.tt.ret32.status == 0)
394 sorf = AU_PRS_SUCCESS;
396 sorf = AU_PRS_FAILURE;
397 if (ISOPTSET(opttochk, OPT_c)) {
398 if (au_preselect(tok_hdr32.tt.hdr32.e_type, &maskp, sorf,
399 AU_PRS_USECACHE) != 1)
406 * Return 1 if checks for the the following succeed
415 select_proc32(tokenstr_t tok, uint32_t *optchkd)
418 SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_op));
420 if (!select_auid(tok.tt.proc32.auid))
422 if (!select_euid(tok.tt.proc32.euid))
424 if (!select_egid(tok.tt.proc32.egid))
426 if (!select_rgid(tok.tt.proc32.rgid))
428 if (!select_ruid(tok.tt.proc32.ruid))
430 if (!select_pidobj(tok.tt.proc32.pid))
436 * Return 1 if checks for the the following succeed
445 select_subj32(tokenstr_t tok, uint32_t *optchkd)
448 SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_j));
450 if (!select_auid(tok.tt.subj32.auid))
452 if (!select_euid(tok.tt.subj32.euid))
454 if (!select_egid(tok.tt.subj32.egid))
456 if (!select_rgid(tok.tt.subj32.rgid))
458 if (!select_ruid(tok.tt.subj32.ruid))
460 if (!select_subid(tok.tt.subj32.pid))
466 * Read each record from the audit trail. Check if it is selected after
467 * passing through each of the options
470 select_records(FILE *fp)
472 tokenstr_t tok_hdr32_copy;
481 while ((reclen = au_read_rec(fp, &buf)) != -1) {
485 while ((selected == 1) && (bytesread < reclen)) {
486 if (-1 == au_fetch_tok(&tok, buf + bytesread,
487 reclen - bytesread)) {
488 /* Is this an incomplete record? */
494 * For each token type we have have different
495 * selection criteria.
498 case AU_HEADER_32_TOKEN:
499 selected = select_hdr32(tok,
501 bcopy(&tok, &tok_hdr32_copy,
505 case AU_PROCESS_32_TOKEN:
506 selected = select_proc32(tok,
510 case AU_SUBJECT_32_TOKEN:
511 selected = select_subj32(tok,
516 selected = select_ipcobj(
517 tok.tt.ipc.type, tok.tt.ipc.id,
522 selected = select_filepath(
523 tok.tt.file.name, &optchkd);
527 selected = select_filepath(
528 tok.tt.path.path, &optchkd);
531 case AU_RETURN_32_TOKEN:
532 selected = select_return32(tok,
533 tok_hdr32_copy, &optchkd);
537 * The following tokens dont have any relevant
538 * attributes that we can select upon.
540 case AU_TRAILER_TOKEN:
542 case AU_ATTR32_TOKEN:
544 case AU_NEWGROUPS_TOKEN:
545 case AU_IN_ADDR_TOKEN:
547 case AU_IPCPERM_TOKEN:
549 case AU_OPAQUE_TOKEN:
557 bytesread += tok.len;
559 if ((selected == 1) && (!err)) {
560 /* Check if all the options were matched. */
561 if (!(opttochk & ~optchkd)) {
562 /* XXX Write this record to the output file. */
563 /* default to stdout */
564 fwrite(buf, 1, reclen, stdout);
573 * The -o option has the form object_type=object_value. Identify the object
577 parse_object_type(char *name, char *val)
582 if (!strcmp(name, FILEOBJ)) {
585 SETOPT(opttochk, OPT_of);
586 } else if (!strcmp(name, MSGQIDOBJ)) {
588 SETOPT(opttochk, OPT_om);
589 } else if (!strcmp(name, PIDOBJ)) {
591 SETOPT(opttochk, OPT_op);
592 } else if (!strcmp(name, SEMIDOBJ)) {
594 SETOPT(opttochk, OPT_ose);
595 } else if (!strcmp(name, SHMIDOBJ)) {
597 SETOPT(opttochk, OPT_osh);
598 } else if (!strcmp(name, SOCKOBJ)) {
600 SETOPT(opttochk, OPT_oso);
602 usage("unknown value for -o");
606 main(int argc, char **argv)
614 char *objval, *converr;
621 while ((ch = getopt(argc, argv, "Aa:b:c:d:e:f:g:j:m:o:r:u:")) != -1) {
624 SETOPT(opttochk, OPT_A);
628 if (ISOPTSET(opttochk, OPT_a)) {
629 usage("d is exclusive with a and b");
631 SETOPT(opttochk, OPT_a);
632 bzero(&tm, sizeof(tm));
633 strptime(optarg, "%Y%m%d%H%M%S", &tm);
634 strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
636 /* fprintf(stderr, "Time converted = %s\n", timestr); */
637 p_atime = mktime(&tm);
641 if (ISOPTSET(opttochk, OPT_b)) {
642 usage("d is exclusive with a and b");
644 SETOPT(opttochk, OPT_b);
645 bzero(&tm, sizeof(tm));
646 strptime(optarg, "%Y%m%d%H%M%S", &tm);
647 strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
649 /* fprintf(stderr, "Time converted = %s\n", timestr); */
650 p_btime = mktime(&tm);
654 if (0 != getauditflagsbin(optarg, &maskp)) {
655 /* Incorrect class */
656 usage("Incorrect class");
658 SETOPT(opttochk, OPT_c);
662 if (ISOPTSET(opttochk, OPT_b) || ISOPTSET(opttochk,
664 usage("'d' is exclusive with 'a' and 'b'");
665 SETOPT(opttochk, OPT_d);
666 bzero(&tm, sizeof(tm));
667 strptime(optarg, "%Y%m%d", &tm);
668 strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
669 /* fprintf(stderr, "Time converted = %s\n", timestr); */
670 p_atime = mktime(&tm);
674 strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
675 /* fprintf(stderr, "Time converted = %s\n", timestr); */
676 p_btime = mktime(&tm);
680 p_euid = strtol(optarg, &converr, 10);
681 if (*converr != '\0') {
682 /* Try the actual name */
683 if ((pw = getpwnam(optarg)) == NULL)
687 SETOPT(opttochk, OPT_e);
691 p_egid = strtol(optarg, &converr, 10);
692 if (*converr != '\0') {
693 /* Try actual group name. */
694 if ((grp = getgrnam(optarg)) == NULL)
696 p_egid = grp->gr_gid;
698 SETOPT(opttochk, OPT_f);
702 p_rgid = strtol(optarg, &converr, 10);
703 if (*converr != '\0') {
704 /* Try actual group name. */
705 if ((grp = getgrnam(optarg)) == NULL)
707 p_rgid = grp->gr_gid;
709 SETOPT(opttochk, OPT_g);
713 p_subid = strtol(optarg, (char **)NULL, 10);
714 SETOPT(opttochk, OPT_j);
718 p_evtype = strtol(optarg, (char **)NULL, 10);
720 /* Could be the string representation. */
721 n = getauevnonam(optarg);
723 usage("Incorrect event name");
726 SETOPT(opttochk, OPT_m);
730 objval = strchr(optarg, '=');
731 if (objval != NULL) {
734 parse_object_type(optarg, objval);
739 p_ruid = strtol(optarg, &converr, 10);
740 if (*converr != '\0') {
741 if ((pw = getpwnam(optarg)) == NULL)
745 SETOPT(opttochk, OPT_r);
749 p_auid = strtol(optarg, &converr, 10);
750 if (*converr != '\0') {
751 if ((pw = getpwnam(optarg)) == NULL)
755 SETOPT(opttochk, OPT_u);
760 usage("Unknown option");
767 if (select_records(stdin) == -1)
769 "Couldn't select records from stdin");
774 * XXX: We should actually be merging records here.
776 for (i = 0; i < argc; i++) {
778 fp = fopen(fname, "r");
780 errx(EXIT_FAILURE, "Couldn't open %s", fname);
781 if (select_records(fp) == -1) {
782 errx(EXIT_FAILURE, "Couldn't select records %s",