2 * Copyright (c) 2004-2008 Apple Inc.
3 * Copyright (c) 2016 Robert N. M. Watson
6 * Portions of this software were developed by BAE Systems, the University of
7 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
8 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
9 * Computing (TC) research program.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
20 * its contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
27 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
37 * Tool used to merge and select audit records from audit trail files
41 * XXX Currently we do not support merging of records from multiple
42 * XXX audit trail files
43 * XXX We assume that records are sorted chronologically - both wrt to
44 * XXX the records present within the file and between the files themselves
47 #include <config/config.h>
49 #define _GNU_SOURCE /* Required for strptime() on glibc2. */
51 #ifdef HAVE_FULL_QUEUE_H
52 #include <sys/queue.h>
54 #include <compat/queue.h>
58 #include <sys/capsicum.h>
62 #include <bsm/libbsm.h>
77 #include <compat/strlcpy.h>
80 #include "auditreduce.h"
82 static TAILQ_HEAD(tailhead, re_entry) re_head =
83 TAILQ_HEAD_INITIALIZER(re_head);
86 extern int optind, optopt, opterr,optreset;
88 static au_mask_t maskp; /* Class. */
89 static time_t p_atime; /* Created after this time. */
90 static time_t p_btime; /* Created before this time. */
91 static int p_auid; /* Audit id. */
92 static int p_euid; /* Effective user id. */
93 static int p_egid; /* Effective group id. */
94 static int p_rgid; /* Real group id. */
95 static int p_ruid; /* Real user id. */
96 static int p_subid; /* Subject id. */
99 * Maintain a dynamically sized array of events for -m
101 static uint16_t *p_evec; /* Event type list */
102 static int p_evec_used; /* Number of events used */
103 static int p_evec_alloc; /* Number of events allocated */
106 * Following are the objects (-o option) that we can select upon.
108 static char *p_fileobj = NULL;
109 static char *p_msgqobj = NULL;
110 static char *p_pidobj = NULL;
111 static char *p_semobj = NULL;
112 static char *p_shmobj = NULL;
113 static char *p_sockobj = NULL;
115 static uint32_t opttochk = 0;
118 parse_regexp(char *re_string)
120 char *orig, *copy, re_error[64];
121 struct re_entry *rep;
122 int error, nstrs, i, len;
124 copy = strdup(re_string);
127 for (nstrs = 0, i = 0; i < len; i++) {
128 if (copy[i] == ',' && i > 0) {
129 if (copy[i - 1] == '\\')
130 strlcpy(©[i - 1], ©[i], len);
137 TAILQ_INIT(&re_head);
138 for (i = 0; i < nstrs + 1; i++) {
139 rep = calloc(1, sizeof(*rep));
141 (void) fprintf(stderr, "calloc: %s\n",
149 rep->re_pattern = strdup(copy);
150 error = regcomp(&rep->re_regexp, rep->re_pattern,
151 REG_EXTENDED | REG_NOSUB);
153 regerror(error, &rep->re_regexp, re_error, 64);
154 (void) fprintf(stderr, "regcomp: %s\n", re_error);
157 TAILQ_INSERT_TAIL(&re_head, rep, re_glue);
165 usage(const char *msg)
167 fprintf(stderr, "%s\n", msg);
168 fprintf(stderr, "Usage: auditreduce [options] [file ...]\n");
169 fprintf(stderr, "\tOptions are : \n");
170 fprintf(stderr, "\t-A : all records\n");
171 fprintf(stderr, "\t-a YYYYMMDD[HH[[MM[SS]]] : after date\n");
172 fprintf(stderr, "\t-b YYYYMMDD[HH[[MM[SS]]] : before date\n");
173 fprintf(stderr, "\t-c <flags> : matching class\n");
174 fprintf(stderr, "\t-d YYYYMMDD : on date\n");
175 fprintf(stderr, "\t-e <uid|name> : effective user\n");
176 fprintf(stderr, "\t-f <gid|group> : effective group\n");
177 fprintf(stderr, "\t-g <gid|group> : real group\n");
178 fprintf(stderr, "\t-j <pid> : subject id \n");
179 fprintf(stderr, "\t-m <evno|evname> : matching event\n");
180 fprintf(stderr, "\t-o objecttype=objectvalue\n");
181 fprintf(stderr, "\t\t file=<pathname>\n");
182 fprintf(stderr, "\t\t msgqid=<ID>\n");
183 fprintf(stderr, "\t\t pid=<ID>\n");
184 fprintf(stderr, "\t\t semid=<ID>\n");
185 fprintf(stderr, "\t\t shmid=<ID>\n");
186 fprintf(stderr, "\t-r <uid|name> : real user\n");
187 fprintf(stderr, "\t-u <uid|name> : audit user\n");
188 fprintf(stderr, "\t-v : select non-matching records\n");
193 * Check if the given auid matches the selection criteria.
199 /* Check if we want to select on auid. */
200 if (ISOPTSET(opttochk, OPT_u)) {
208 * Check if the given euid matches the selection criteria.
211 select_euid(int euser)
214 /* Check if we want to select on euid. */
215 if (ISOPTSET(opttochk, OPT_e)) {
223 * Check if the given egid matches the selection criteria.
226 select_egid(int egrp)
229 /* Check if we want to select on egid. */
230 if (ISOPTSET(opttochk, OPT_f)) {
238 * Check if the given rgid matches the selection criteria.
244 /* Check if we want to select on rgid. */
245 if (ISOPTSET(opttochk, OPT_g)) {
253 * Check if the given ruid matches the selection criteria.
256 select_ruid(int user)
259 /* Check if we want to select on rgid. */
260 if (ISOPTSET(opttochk, OPT_r)) {
268 * Check if the given subject id (pid) matches the selection criteria.
271 select_subid(int subid)
274 /* Check if we want to select on subject uid. */
275 if (ISOPTSET(opttochk, OPT_j)) {
276 if (subid != p_subid)
284 * Check if object's pid maches the given pid.
287 select_pidobj(uint32_t pid)
290 if (ISOPTSET(opttochk, OPT_op)) {
291 if (pid != (uint32_t)strtol(p_pidobj, (char **)NULL, 10))
298 * Check if the given ipc object with the given type matches the selection
302 select_ipcobj(u_char type, uint32_t id, uint32_t *optchkd)
305 if (type == AT_IPC_MSG) {
306 SETOPT((*optchkd), OPT_om);
307 if (ISOPTSET(opttochk, OPT_om)) {
308 if (id != (uint32_t)strtol(p_msgqobj, (char **)NULL,
313 } else if (type == AT_IPC_SEM) {
314 SETOPT((*optchkd), OPT_ose);
315 if (ISOPTSET(opttochk, OPT_ose)) {
316 if (id != (uint32_t)strtol(p_semobj, (char **)NULL, 10))
320 } else if (type == AT_IPC_SHM) {
321 SETOPT((*optchkd), OPT_osh);
322 if (ISOPTSET(opttochk, OPT_osh)) {
323 if (id != (uint32_t)strtol(p_shmobj, (char **)NULL, 10))
329 /* Unknown type -- filter if *any* ipc filtering is required. */
330 if (ISOPTSET(opttochk, OPT_om) || ISOPTSET(opttochk, OPT_ose)
331 || ISOPTSET(opttochk, OPT_osh))
339 * Check if the file name matches selection criteria.
342 select_filepath(char *path, uint32_t *optchkd)
344 struct re_entry *rep;
347 SETOPT((*optchkd), OPT_of);
349 if (ISOPTSET(opttochk, OPT_of)) {
351 TAILQ_FOREACH(rep, &re_head, re_glue) {
352 if (regexec(&rep->re_regexp, path, 0, NULL,
354 return (!rep->re_negate);
361 * Returns 1 if the following pass the selection rules:
370 select_hdr32(tokenstr_t tok, uint32_t *optchkd)
375 SETOPT((*optchkd), (OPT_A | OPT_a | OPT_b | OPT_c | OPT_m | OPT_v));
377 /* The A option overrides a, b and d. */
378 if (!ISOPTSET(opttochk, OPT_A)) {
379 if (ISOPTSET(opttochk, OPT_a)) {
380 if (difftime((time_t)tok.tt.hdr32.s, p_atime) < 0) {
381 /* Record was created before p_atime. */
386 if (ISOPTSET(opttochk, OPT_b)) {
387 if (difftime(p_btime, (time_t)tok.tt.hdr32.s) < 0) {
388 /* Record was created after p_btime. */
394 if (ISOPTSET(opttochk, OPT_c)) {
396 * Check if the classes represented by the event matches
399 if (au_preselect(tok.tt.hdr32.e_type, &maskp, AU_PRS_BOTH,
400 AU_PRS_USECACHE) != 1)
404 /* Check if event matches. */
405 if (ISOPTSET(opttochk, OPT_m)) {
407 for (ev = p_evec; ev < &p_evec[p_evec_used]; ev++)
408 if (tok.tt.hdr32.e_type == *ev)
418 select_return32(tokenstr_t tok_ret32, tokenstr_t tok_hdr32, uint32_t *optchkd)
422 SETOPT((*optchkd), (OPT_c));
423 if (tok_ret32.tt.ret32.status == 0)
424 sorf = AU_PRS_SUCCESS;
426 sorf = AU_PRS_FAILURE;
427 if (ISOPTSET(opttochk, OPT_c)) {
428 if (au_preselect(tok_hdr32.tt.hdr32.e_type, &maskp, sorf,
429 AU_PRS_USECACHE) != 1)
436 * Return 1 if checks for the the following succeed
445 select_proc32(tokenstr_t tok, uint32_t *optchkd)
448 SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_op));
450 if (!select_auid(tok.tt.proc32.auid))
452 if (!select_euid(tok.tt.proc32.euid))
454 if (!select_egid(tok.tt.proc32.egid))
456 if (!select_rgid(tok.tt.proc32.rgid))
458 if (!select_ruid(tok.tt.proc32.ruid))
460 if (!select_pidobj(tok.tt.proc32.pid))
466 * Return 1 if checks for the the following succeed
475 select_subj32(tokenstr_t tok, uint32_t *optchkd)
478 SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_j));
480 if (!select_auid(tok.tt.subj32.auid))
482 if (!select_euid(tok.tt.subj32.euid))
484 if (!select_egid(tok.tt.subj32.egid))
486 if (!select_rgid(tok.tt.subj32.rgid))
488 if (!select_ruid(tok.tt.subj32.ruid))
490 if (!select_subid(tok.tt.subj32.pid))
496 * Read each record from the audit trail. Check if it is selected after
497 * passing through each of the options
500 select_records(FILE *fp)
502 tokenstr_t tok_hdr32_copy;
512 while ((reclen = au_read_rec(fp, &buf)) != -1) {
516 while ((selected == 1) && (bytesread < reclen)) {
517 if (-1 == au_fetch_tok(&tok, buf + bytesread,
518 reclen - bytesread)) {
519 /* Is this an incomplete record? */
525 * For each token type we have have different
526 * selection criteria.
530 selected = select_hdr32(tok,
532 bcopy(&tok, &tok_hdr32_copy,
537 selected = select_proc32(tok,
542 selected = select_subj32(tok,
547 selected = select_ipcobj(
548 tok.tt.ipc.type, tok.tt.ipc.id,
553 selected = select_filepath(
554 tok.tt.path.path, &optchkd);
558 selected = select_return32(tok,
559 tok_hdr32_copy, &optchkd);
565 bytesread += tok.len;
567 /* Check if all the options were matched. */
568 print = ((selected == 1) && (!err) && (!(opttochk & ~optchkd)));
569 if (ISOPTSET(opttochk, OPT_v))
572 (void) fwrite(buf, 1, reclen, stdout);
579 * The -o option has the form object_type=object_value. Identify the object
583 parse_object_type(char *name, char *val)
588 if (!strcmp(name, FILEOBJ)) {
591 SETOPT(opttochk, OPT_of);
592 } else if (!strcmp(name, MSGQIDOBJ)) {
594 SETOPT(opttochk, OPT_om);
595 } else if (!strcmp(name, PIDOBJ)) {
597 SETOPT(opttochk, OPT_op);
598 } else if (!strcmp(name, SEMIDOBJ)) {
600 SETOPT(opttochk, OPT_ose);
601 } else if (!strcmp(name, SHMIDOBJ)) {
603 SETOPT(opttochk, OPT_osh);
604 } else if (!strcmp(name, SOCKOBJ)) {
606 SETOPT(opttochk, OPT_oso);
608 usage("unknown value for -o");
612 main(int argc, char **argv)
620 char *objval, *converr;
625 #ifdef HAVE_CAP_ENTER
632 while ((ch = getopt(argc, argv, "Aa:b:c:d:e:f:g:j:m:o:r:u:v")) != -1) {
635 SETOPT(opttochk, OPT_A);
639 if (ISOPTSET(opttochk, OPT_a)) {
640 usage("d is exclusive with a and b");
642 SETOPT(opttochk, OPT_a);
643 bzero(&tm, sizeof(tm));
644 strptime(optarg, "%Y%m%d%H%M%S", &tm);
645 strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
647 /* fprintf(stderr, "Time converted = %s\n", timestr); */
648 p_atime = mktime(&tm);
652 if (ISOPTSET(opttochk, OPT_b)) {
653 usage("d is exclusive with a and b");
655 SETOPT(opttochk, OPT_b);
656 bzero(&tm, sizeof(tm));
657 strptime(optarg, "%Y%m%d%H%M%S", &tm);
658 strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
660 /* fprintf(stderr, "Time converted = %s\n", timestr); */
661 p_btime = mktime(&tm);
665 if (0 != getauditflagsbin(optarg, &maskp)) {
666 /* Incorrect class */
667 usage("Incorrect class");
669 SETOPT(opttochk, OPT_c);
673 if (ISOPTSET(opttochk, OPT_b) || ISOPTSET(opttochk,
675 usage("'d' is exclusive with 'a' and 'b'");
676 SETOPT(opttochk, OPT_d);
677 bzero(&tm, sizeof(tm));
678 strptime(optarg, "%Y%m%d", &tm);
679 strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
680 /* fprintf(stderr, "Time converted = %s\n", timestr); */
681 p_atime = mktime(&tm);
685 strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
686 /* fprintf(stderr, "Time converted = %s\n", timestr); */
687 p_btime = mktime(&tm);
691 p_euid = strtol(optarg, &converr, 10);
692 if (*converr != '\0') {
693 /* Try the actual name */
694 if ((pw = getpwnam(optarg)) == NULL)
698 SETOPT(opttochk, OPT_e);
702 p_egid = strtol(optarg, &converr, 10);
703 if (*converr != '\0') {
704 /* Try actual group name. */
705 if ((grp = getgrnam(optarg)) == NULL)
707 p_egid = grp->gr_gid;
709 SETOPT(opttochk, OPT_f);
713 p_rgid = strtol(optarg, &converr, 10);
714 if (*converr != '\0') {
715 /* Try actual group name. */
716 if ((grp = getgrnam(optarg)) == NULL)
718 p_rgid = grp->gr_gid;
720 SETOPT(opttochk, OPT_g);
724 p_subid = strtol(optarg, (char **)NULL, 10);
725 SETOPT(opttochk, OPT_j);
729 if (p_evec == NULL) {
731 p_evec = malloc(sizeof(*etp) * p_evec_alloc);
734 } else if (p_evec_alloc == p_evec_used) {
736 p_evec = realloc(p_evec,
737 sizeof(*p_evec) * p_evec_alloc);
741 etp = &p_evec[p_evec_used++];
742 *etp = strtol(optarg, (char **)NULL, 10);
744 /* Could be the string representation. */
745 n = getauevnonam(optarg);
747 usage("Incorrect event name");
750 SETOPT(opttochk, OPT_m);
754 objval = strchr(optarg, '=');
755 if (objval != NULL) {
758 parse_object_type(optarg, objval);
763 p_ruid = strtol(optarg, &converr, 10);
764 if (*converr != '\0') {
765 if ((pw = getpwnam(optarg)) == NULL)
769 SETOPT(opttochk, OPT_r);
773 p_auid = strtol(optarg, &converr, 10);
774 if (*converr != '\0') {
775 if ((pw = getpwnam(optarg)) == NULL)
779 SETOPT(opttochk, OPT_u);
783 SETOPT(opttochk, OPT_v);
788 usage("Unknown option");
795 #ifdef HAVE_CAP_ENTER
796 retval = cap_enter();
797 if (retval != 0 && errno != ENOSYS)
798 err(EXIT_FAILURE, "cap_enter");
800 if (select_records(stdin) == -1)
802 "Couldn't select records from stdin");
807 * XXX: We should actually be merging records here.
809 for (i = 0; i < argc; i++) {
811 fp = fopen(fname, "r");
813 errx(EXIT_FAILURE, "Couldn't open %s", fname);
816 * If operating with sandboxing, create a sandbox process for
817 * each trail file we operate on. This avoids the need to do
818 * fancy things with file descriptors, etc, when iterating on
819 * a list of arguments.
821 * NB: Unlike praudit(1), auditreduce(1) terminates if it hits
822 * any errors. Propagate the error from the child to the
823 * parent if any problems arise.
825 #ifdef HAVE_CAP_ENTER
829 retval = cap_enter();
830 if (retval != 0 && errno != ENOSYS)
831 errx(EXIT_FAILURE, "cap_enter");
832 if (select_records(fp) == -1)
834 "Couldn't select records %s", fname);
838 /* Parent. Await child termination, check exit value. */
839 while ((pid = waitpid(childpid, &status, 0)) != childpid);
840 if (WEXITSTATUS(status) != 0)
843 if (select_records(fp) == -1)
844 errx(EXIT_FAILURE, "Couldn't select records %s",