/
githubmirror
/
audit-userspace
Обзор
Документация
Войти
/
githubmirror
/
audit-userspace
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v4.1.4
auparse/normalize.c
2 285 строк
56 KB
Steve Grubb
Code cleanup
22 мар 2026, 22:58
22 мар 2026, 22:58
2f1ac7a
Код
Авторство
О чём код?
/* normalize.c -- * Copyright 2016-18,2021 Red Hat Inc. * All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Authors: * Steve Grubb <sgrubb@redhat.com> */ #include <string.h> #include <stdlib.h> #include <stdint.h> #include <sys/stat.h> #include <errno.h> #include <pwd.h> #include "libaudit.h" #include "auparse.h" #include "internal.h" #include "normalize-llist.h" #include "normalize-internal.h" #include "gen_tables.h" #include "normalize_record_maps.h" #include "normalize_syscall_maps.h" #include "normalize_obj_kind_maps.h" #include "normalize_evtypetabs.h" /* * Field accessors. x is the new value, y is the variable * Layout is: 0xFFFF FFFF where first is record and second is field * Both record and field are 0 based. Simple records are always 0. Compound * records start at 0 and go up. */ #define UNSET 0xFFFFU #define get_record(y) ((y >> 16) & 0x0000FFFFU) #define set_record(y, x) (((x & 0x0000FFFFU) << 16) | (y & 0x0000FFFFU)) #define get_field(y) (y & 0x0000FFFFU) #define set_field(y, x) ((y & 0xFFFF0000U) | (x & 0x0000FFFFU)) #define is_unset(y) (get_record(y) == UNSET) #define D au->norm_data static int syscall_success; static value_t find_simple_object(auparse_state_t *au, int type); void init_normalizer(normalize_data *d) { d->evkind = NULL; d->session = set_record(0, UNSET); d->actor.primary = set_record(0, UNSET); d->actor.secondary = set_record(0, UNSET); d->actor.what = NULL; cllist_create(&d->actor.attr, NULL); d->action = NULL; d->thing.primary = set_record(0, UNSET); d->thing.secondary = set_record(0, UNSET); d->thing.two = set_record(0, UNSET); cllist_create(&d->thing.attr, NULL); d->thing.what = NORM_WHAT_UNKNOWN; d->results = set_record(0, UNSET); d->how = NULL; d->opt = NORM_OPT_ALL; d->key = set_record(0, UNSET); syscall_success = -1; } void clear_normalizer(normalize_data *d) { d->evkind = NULL; d->session = set_record(0, UNSET); d->actor.primary = set_record(0, UNSET); d->actor.secondary = set_record(0, UNSET); free((void *)d->actor.what); d->actor.what = NULL; cllist_clear(&d->actor.attr); free((void *)d->action); d->action = NULL; d->thing.primary = set_record(0, UNSET); d->thing.secondary = set_record(0, UNSET); d->thing.two = set_record(0, UNSET); cllist_clear(&d->thing.attr); d->thing.what = NORM_WHAT_UNKNOWN; d->results = set_record(0, UNSET); free((void *)d->how); d->how = NULL; d->opt = NORM_OPT_ALL; d->key = set_record(0, UNSET); syscall_success = -1; } static void set_system_subject_what(auparse_state_t *au) { D.actor.what = strdup("system"); } static void set_unknown_subject_what(auparse_state_t *au) { D.actor.what = strdup("unknown-acct"); } static unsigned int set_subject_what(auparse_state_t *au) { int uid = NORM_ACCT_UNSET - 1; int ftype = auparse_get_field_type(au); if (ftype == AUPARSE_TYPE_UID) uid = auparse_get_field_int(au); else { const char *n = auparse_get_field_name(au); if (n && strcmp(n, "acct") == 0) { const char *acct = auparse_interpret_field(au); if (acct) { uid_t lu = lookup_uid_from_name(au, acct); if (lu != (uid_t)-1) { uid = lu; goto check; } } } set_unknown_subject_what(au); return 1; } check: if (uid == NORM_ACCT_PRIV) D.actor.what = strdup("privileged-acct"); else if ((unsigned)uid == NORM_ACCT_UNSET) D.actor.what = strdup("unset-acct"); else if (uid < NORM_ACCT_MAX_SYS) D.actor.what = strdup("service-acct"); else if (uid < NORM_ACCT_MAX_USER) D.actor.what = strdup("user-acct"); else set_unknown_subject_what(au); return 0; } static unsigned int set_prime_subject(auparse_state_t *au, const char *str, unsigned int rnum) { if (auparse_find_field(au, str)) { D.actor.primary = set_record(0, rnum); D.actor.primary = set_field(D.actor.primary, auparse_get_field_num(au)); return 0; } return 1; } static unsigned int set_secondary_subject(auparse_state_t *au, const char *str, unsigned int rnum) { if (auparse_find_field(au, str)) { D.actor.secondary = set_record(0, rnum); D.actor.secondary = set_field(D.actor.secondary, auparse_get_field_num(au)); return set_subject_what(au); } return 1; } static unsigned int add_subj_attr(auparse_state_t *au, const char *str, unsigned int rnum) { value_t attr; if ((auparse_find_field(au, str))) { attr = set_record(0, rnum); attr = set_field(attr, auparse_get_field_num(au)); if (cllist_append(&D.actor.attr, attr, NULL)) return 1; return 0; } else auparse_goto_record_num(au, rnum); return 1; } static unsigned int set_prime_object(auparse_state_t *au, const char *str, unsigned int rnum) { if (auparse_find_field(au, str)) { D.thing.primary = set_record(0, rnum); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); return 0; } return 1; } static unsigned int set_prime_object2(auparse_state_t *au, const char *str, unsigned int adjust) { unsigned int rnum = 2 + adjust; auparse_goto_record_num(au, rnum); auparse_first_field(au); if (auparse_find_field(au, str)) { D.thing.two = set_record(0, rnum); D.thing.two = set_field(D.thing.two, auparse_get_field_num(au)); return 0; } return 1; } static unsigned int add_obj_attr(auparse_state_t *au, const char *str, unsigned int rnum) { value_t attr; if ((auparse_find_field(au, str))) { attr = set_record(0, rnum); attr = set_field(attr, auparse_get_field_num(au)); if (cllist_append(&D.thing.attr, attr, NULL)) return 1; return 0; } else auparse_goto_record_num(au, rnum); return 1; } static unsigned int add_session(auparse_state_t *au, unsigned int rnum) { if (auparse_find_field(au, "ses")) { D.session = set_record(0, rnum); D.session = set_field(D.session, auparse_get_field_num(au)); return 0; } else auparse_first_record(au); return 1; } static unsigned int set_results(auparse_state_t *au, unsigned int rnum) { if (auparse_find_field(au, "res")) { D.results = set_record(0, rnum); D.results = set_field(D.results, auparse_get_field_num(au)); return 0; } return 1; } static void syscall_subj_attr(auparse_state_t *au) { unsigned int rnum; auparse_first_record(au); do { rnum = auparse_get_record_num(au); if (auparse_get_type(au) == AUDIT_SYSCALL) { if (D.opt == NORM_OPT_NO_ATTRS) { add_session(au, rnum); return; } add_subj_attr(au, "ppid", rnum); add_subj_attr(au, "pid", rnum); add_subj_attr(au, "gid", rnum); add_subj_attr(au, "euid", rnum); add_subj_attr(au, "suid", rnum); add_subj_attr(au, "fsuid", rnum); add_subj_attr(au, "egid", rnum); add_subj_attr(au, "sgid", rnum); add_subj_attr(au, "fsgid", rnum); add_subj_attr(au, "tty", rnum); add_session(au, rnum); add_subj_attr(au, "subj", rnum); return; } } while (auparse_next_record(au) == 1); } static void collect_perm_obj2(auparse_state_t *au, const char *syscall) { const char *val; if (strcmp(syscall, "fchmodat") == 0) val = "a2"; else val = "a1"; auparse_first_record(au); if (auparse_find_field(au, val)) { D.thing.two = set_record(0, 0); D.thing.two = set_field(D.thing.two, auparse_get_field_num(au)); } } static void collect_own_obj2(auparse_state_t *au, const char *syscall) { const char *val; if (strcmp(syscall, "fchownat") == 0) val = "a2"; else val = "a1"; auparse_first_record(au); if (auparse_find_field(au, val)) { // if uid is -1, its not being changed, user group if (auparse_get_field_int(au) == -1 && errno == 0) auparse_next_field(au); D.thing.two = set_record(0, 0); D.thing.two = set_field(D.thing.two, auparse_get_field_num(au)); } } static void collect_id_obj2(auparse_state_t *au, const char *syscall) { unsigned int limit, cnt = 1; if (strcmp(syscall, "setuid") == 0) limit = 1; else if (strcmp(syscall, "setreuid") == 0) limit = 2; else if (strcmp(syscall, "setresuid") == 0) limit = 3; else if (strcmp(syscall, "setgid") == 0) limit = 1; else if (strcmp(syscall, "setregid") == 0) limit = 2; else if (strcmp(syscall, "setresgid") == 0) limit = 3; else return; // Shouldn't happen auparse_first_record(au); if (auparse_find_field(au, "a0")) { while (cnt <= limit) { const char *str = auparse_interpret_field(au); if ((strcmp(str, "unset") == 0) && errno == 0) { // Only move it if its safe to if (cnt < limit) { if (auparse_next_field(au) == 0) return; cnt++; } else return; } else break; } D.thing.two = set_record(0, 0); D.thing.two = set_field(D.thing.two, auparse_get_field_num(au)); } } static int collect_path_attrs(auparse_state_t *au) { value_t attr; unsigned int rnum = auparse_get_record_num(au); auparse_first_field(au); if (add_obj_attr(au, "mode", rnum)) return 1; // Failed opens don't have anything else // All the rest of the fields matter while ((auparse_next_field(au))) { attr = set_record(0, rnum); attr = set_field(attr, auparse_get_field_num(au)); if (cllist_append(&D.thing.attr, attr, NULL)) return 1; } return 0; } static void collect_cwd_attrs(auparse_state_t *au) { unsigned int rnum = auparse_get_record_num(au); add_obj_attr(au, "cwd", rnum); } static void collect_sockaddr_attrs(auparse_state_t *au) { unsigned int rnum = auparse_get_record_num(au); add_obj_attr(au, "saddr", rnum); } static void simple_file_attr(auparse_state_t *au) { int parent = 0; if (D.opt == NORM_OPT_NO_ATTRS) return; auparse_first_record(au); do { const char *f; int type = auparse_get_type(au); switch (type) { case AUDIT_PATH: f = auparse_find_field(au, "nametype"); if (f && strcmp(f, "PARENT") == 0) { if (parent == 0) parent = auparse_get_record_num(au); continue; } // First normal record is collected collect_path_attrs(au); return; break; case AUDIT_CWD: collect_cwd_attrs(au); break; case AUDIT_SOCKADDR: collect_sockaddr_attrs(au); break; } } while (auparse_next_record(au) == 1); // If we get here, path was never collected. Go back and get parent if (parent) { auparse_goto_record_num(au, parent); collect_path_attrs(au); } } static void set_file_object(auparse_state_t *au, int adjust) { const char *f; int parent = 0; unsigned int rnum; auparse_goto_record_num(au, 2 + adjust); auparse_first_field(au); // Now double check that we picked the right one. do { f = auparse_find_field(au, "nametype"); if (f) { if (strcmp(f, "PARENT")) break; if (parent == 0) parent = auparse_get_record_num(au); } } while (f && auparse_next_record(au) == 1); // Sometimes we only have the parent (failed open at dir permission) if (f == NULL) { if (parent == 0) return; auparse_goto_record_num(au, parent); auparse_first_field(au); rnum = parent; } else rnum = auparse_get_record_num(au); if (auparse_get_type(au) == AUDIT_PATH) { auparse_first_field(au); // Object set_prime_object(au, "name", rnum); f = auparse_find_field(au, "inode"); if (f) { D.thing.secondary = set_record(0, rnum); D.thing.secondary = set_field(D.thing.secondary, auparse_get_field_num(au)); } f = auparse_find_field(au, "mode"); if (f) { unsigned int mode; errno = 0; mode = strtoul(f, NULL, 8); if (errno == 0) { if (S_ISREG(mode)) D.thing.what = NORM_WHAT_FILE; else if (S_ISDIR(mode)) D.thing.what = NORM_WHAT_DIRECTORY; else if (S_ISCHR(mode)) D.thing.what = NORM_WHAT_CHAR_DEV; else if (S_ISBLK(mode)) D.thing.what = NORM_WHAT_BLOCK_DEV; else if (S_ISFIFO(mode)) D.thing.what = NORM_WHAT_FIFO; else if (S_ISLNK(mode)) D.thing.what = NORM_WHAT_LINK; else if (S_ISSOCK(mode)) D.thing.what = NORM_WHAT_SOCKET; } } } } static void set_socket_object(auparse_state_t *au) { auparse_goto_record_num(au, 1); auparse_first_field(au); set_prime_object(au, "saddr", 1); } /* This is only called processing syscall records */ static int set_program_obj(auparse_state_t *au) { auparse_first_record(au); int type = auparse_get_type(au); if (type == AUDIT_BPF) { if (auparse_find_field(au, "prog-id")) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); } } else if (type == AUDIT_EVENT_LISTENER) { if (auparse_find_field(au, "nl-mcgrp")) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); } } else if (type == AUDIT_MAC_POLICY_LOAD) { if (auparse_find_field(au, "lsm")) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); } } else if (auparse_find_field(au, "exe")) { const char *exe = auparse_interpret_field(au); if ((strncmp(exe, "/usr/bin/python", 15) == 0) || (strncmp(exe, "/usr/bin/sh", 11) == 0) || (strncmp(exe, "/usr/bin/bash", 13) == 0) || (strncmp(exe, "/usr/bin/perl", 13) == 0)) { // comm should be the previous field int fnum; if ((fnum = auparse_get_field_num(au)) > 0) auparse_goto_field_num(au, fnum - 1); else auparse_first_record(au); auparse_find_field(au, "comm"); } D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); return 0; } return 1; } /* * This function is supposed to come up with the action and object for the * syscalls. */ static int normalize_syscall(auparse_state_t *au, const char *syscall) { int rc, tmp_objkind, objtype = NORM_UNKNOWN, ttype = 0, offset = 0; const char *act = NULL, *f; // cycle through all records and see what we have tmp_objkind = objtype; rc = auparse_first_record(au); while (rc == 1) { ttype = auparse_get_type(au); if (ttype == AUDIT_AVC) { // We want to go ahead with syscall to get objects tmp_objkind = NORM_MAC; break; } else if (ttype == AUDIT_SELINUX_ERR) { objtype = NORM_MAC_ERR; break; } else if (ttype == AUDIT_NETFILTER_CFG) { objtype = NORM_IPTABLES; break; } else if (ttype == AUDIT_ANOM_PROMISCUOUS) { objtype = NORM_PROMISCUOUS; break; } else if (ttype == AUDIT_KERN_MODULE) { objtype = NORM_FILE_LDMOD; break; } else if (ttype == AUDIT_MAC_POLICY_LOAD) { objtype = NORM_MAC_LOAD; break; } else if (ttype == AUDIT_MAC_STATUS) { objtype = NORM_MAC_ENFORCE; break; } else if (ttype == AUDIT_MAC_CONFIG_CHANGE) { objtype = NORM_MAC_CONFIG; break; } else if (ttype == AUDIT_FANOTIFY) { // We want to go ahead with syscall to get objects tmp_objkind = NORM_AV; break; } else if (ttype == AUDIT_TIME_INJOFFSET || ttype == AUDIT_TIME_ADJNTPVAL) { objtype = NORM_SYSTEM_TIME; break; } else if (ttype == AUDIT_BPF) { objtype = NORM_BPF; break; } else if (ttype == AUDIT_EVENT_LISTENER) { objtype = NORM_EV_LISTEN; break; } rc = auparse_next_record(au); } // lookup system call - it can be NULL if interpret_field failed. In // that case, the s2i call will fail and leave objtype untouched if (objtype == NORM_UNKNOWN) normalize_syscall_map_s2i(syscall, &objtype); switch (objtype) { case NORM_FILE: act = "opened-file"; set_file_object(au, 0); D.thing.what = NORM_WHAT_FILE; // this gets overridden simple_file_attr(au); break; case NORM_FILE_CHATTR: act = "changed-file-attributes-of"; D.thing.what = NORM_WHAT_FILE; // this gets overridden if (strcmp(syscall, "fsetxattr") == 0) set_file_object(au, -1); else set_file_object(au, offset); simple_file_attr(au); break; case NORM_FILE_CHPERM: act = "changed-file-permissions-of"; D.thing.what = NORM_WHAT_FILE; // this gets overridden if (strcmp(syscall, "fchmod") == 0) set_file_object(au, -1); else set_file_object(au, offset); collect_perm_obj2(au, syscall); simple_file_attr(au); break; case NORM_FILE_CHOWN: act = "changed-file-ownership-of"; D.thing.what = NORM_WHAT_FILE; // this gets overridden if (strcmp(syscall, "fchown") == 0) { collect_own_obj2(au, syscall); /* fchown has no cwd or path information */ } else { collect_own_obj2(au, syscall); set_file_object(au, offset); simple_file_attr(au); } break; case NORM_FILE_LDMOD: act = "loaded-kernel-module"; D.thing.what = NORM_WHAT_FILE; auparse_goto_record_num(au, 1); set_prime_object(au, "name", 1); break; case NORM_FILE_UNLDMOD: act = "unloaded-kernel-module"; D.thing.what = NORM_WHAT_FILE; // this gets overridden // set_file_object(au, 0); // simple_file_attr(au); break; case NORM_FILE_DIR: act = "created-directory"; D.thing.what = NORM_WHAT_FILE; // this gets overridden set_file_object(au, 1); // New dir is one after simple_file_attr(au); break; case NORM_FILE_MOUNT: act = "mounted"; // this gets overridden D.thing.what = NORM_WHAT_FILESYSTEM; if (syscall_success == 1) set_prime_object2(au, "name", 0); //The device is 1 after on success 0 on fail set_file_object(au, syscall_success); // We call this directly to make sure the right // PATH record is used. (There can be 4.) collect_path_attrs(au); break; case NORM_FILE_RENAME: act = "renamed"; D.thing.what = NORM_WHAT_FILE; // this gets overridden /* A sucessfull syscall from the rename family will provide * the following items: * 0 - new dir, in which the file will be located * 1 - old dir, in which the file was located * 2 - old name, the name of the original file * if the file was already present in the new dir: * 3 - removal of the new file * 4 - creation of the new file * otherwise: * 3 - creation of the new file */ // The 3rd record will always contain the name of the new file set_prime_object2(au, "name", 3); set_file_object(au, 2); // Thing renamed is 2 after simple_file_attr(au); break; case NORM_FILE_STAT: act = "checked-metadata-of"; D.thing.what = NORM_WHAT_FILE; // this gets overridden set_file_object(au, 0); simple_file_attr(au); break; case NORM_FILE_SYS_STAT: act = "checked-filesystem-metadata-of"; D.thing.what = NORM_WHAT_FILESYSTEM; // this gets overridden set_file_object(au, 0); simple_file_attr(au); break; case NORM_FILE_LNK: act = "symlinked"; D.thing.what = NORM_WHAT_FILE; // this gets overridden set_prime_object2(au, "name", 0); set_file_object(au, 2); simple_file_attr(au); break; case NORM_FILE_UMNT: act = "unmounted"; D.thing.what = NORM_WHAT_FILESYSTEM; // this gets overridden set_file_object(au, 0); simple_file_attr(au); break; case NORM_FILE_DEL: act = "deleted"; D.thing.what = NORM_WHAT_FILE; // this gets overridden set_file_object(au, 0); simple_file_attr(au); break; case NORM_FILE_TIME: act = "changed-timestamp-of"; D.thing.what = NORM_WHAT_FILE; // this gets overridden set_file_object(au, 0); simple_file_attr(au); break; case NORM_EXEC: act = "executed"; D.thing.what = NORM_WHAT_FILE; // this gets overridden set_file_object(au, 1); simple_file_attr(au); break; case NORM_SOCKET_ACCEPT: act = "accepted-connection-from"; D.thing.what = NORM_WHAT_SOCKET; set_socket_object(au); break; case NORM_SOCKET_BIND: act = "bound-socket"; D.thing.what = NORM_WHAT_SOCKET; set_socket_object(au); break; case NORM_SOCKET_CONN: act = "connected-to"; D.thing.what = NORM_WHAT_SOCKET; set_socket_object(au); break; case NORM_SOCKET_RECV: act = "received-from"; D.thing.what = NORM_WHAT_SOCKET; set_socket_object(au); break; case NORM_SOCKET_SEND: act = "sent-to"; D.thing.what = NORM_WHAT_SOCKET; set_socket_object(au); break; case NORM_PID: { unsigned int r, cnt; value_t attr; act = "killed-pid"; D.thing.what = NORM_WHAT_PROCESS; // Loop to see how many OBJ_PID we have (normally 1) cnt = auparse_get_num_records(au); for (r = 1; r < cnt; r++) { auparse_goto_record_num(au, r); if (auparse_get_type(au) != AUDIT_OBJ_PID) continue; auparse_first_field(au); if (auparse_find_field(au, "opid")) { attr = set_record(0, auparse_get_record_num(au)); attr = set_field(attr, auparse_get_field_num(au)); cllist_append(&D.thing.attr, attr, NULL); } } // If there's more than one, it's a process group if (D.thing.attr.cnt > 1) { act = "killed-list-of-pids"; D.thing.what = NORM_WHAT_PROCESS_GROUP; } break; } case NORM_MAC_LOAD: act = normalize_record_map_i2s(ttype); // FIXME: What is the object? D.thing.what = NORM_WHAT_SECURITY_POLICY; break; case NORM_MAC_CONFIG: act = normalize_record_map_i2s(ttype); f = auparse_find_field(au, "bool"); if (f) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); } D.thing.what = NORM_WHAT_SECURITY_POLICY; break; case NORM_MAC_ENFORCE: act = normalize_record_map_i2s(ttype); f = auparse_find_field(au, "enforcing"); if (f) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); } D.thing.what = NORM_WHAT_SECURITY_POLICY; break; case NORM_MAC_ERR: // FIXME: What could the object be? act = "caused-mac-policy-error"; // For now we'll call the obj_kind the system D.thing.what = NORM_WHAT_SYSTEM; break; case NORM_IPTABLES: act = "loaded-firewall-rule-to"; auparse_first_record(au); f = auparse_find_field(au, "table"); if (f) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); } D.thing.what = NORM_WHAT_FIREWALL; break; case NORM_PROMISCUOUS: auparse_first_record(au); f = auparse_find_field(au, "dev"); if (f) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); } f = auparse_find_field(au, "prom"); if (f) { int i = auparse_get_field_int(au); if (i == 0) act = "left-promiscuous-mode-on-device"; else act = "entered-promiscuous-mode-on-device"; } D.thing.what = NORM_WHAT_SOCKET; break; case NORM_UID: case NORM_GID: act = "changed-identity-of"; D.thing.what = NORM_WHAT_PROCESS; set_program_obj(au); if (D.how) { free((void *)D.how); D.how = strdup(syscall); } collect_id_obj2(au, syscall); break; case NORM_SYSTEM_TIME: act = "changed-system-time"; // TODO: can't think of an object for this one D.thing.what = NORM_WHAT_SYSTEM; break; case NORM_MAKE_DEV: set_file_object(au, 0); simple_file_attr(au); if (D.thing.what == NORM_WHAT_CHAR_DEV) act = "made-character-device"; else if (D.thing.what == NORM_WHAT_BLOCK_DEV) act = "made-block-device"; else act = "make-device"; break; case NORM_SYSTEM_NAME: act = "changed-system-name"; // TODO: can't think of an object for this one D.thing.what = NORM_WHAT_SYSTEM; break; case NORM_SYSTEM_MEMORY: act = "allocated-memory"; if (syscall_success == 1) { // If its not a mmap avc, we can use comm act = "allocated-memory-in"; auparse_first_record(au); f = auparse_find_field(au, "comm"); if (f) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); } } D.thing.what = NORM_WHAT_MEMORY; break; case NORM_SCHEDULER: act = "adjusted-scheduling-policy-of"; D.thing.what = NORM_WHAT_PROCESS; set_program_obj(au); if (D.how) { free((void *)D.how); D.how = strdup(syscall); } break; case NORM_BPF: auparse_first_record(au); f = auparse_find_field(au, "op"); if (f) { const char *str = auparse_get_field_str(au); if (strcmp(str, "LOAD") == 0) act = "loaded-bpf-program"; else act = "unloaded-bpf-program"; } else act = "bpf-program"; D.thing.what = NORM_WHAT_PROCESS; set_program_obj(au); break; case NORM_EV_LISTEN: auparse_first_record(au); f = auparse_find_field(au, "op"); if (f) { const char *str = auparse_get_field_str(au); if (strcmp(str, "connect") == 0) act = "connected-to"; else act = "disconnected-from"; } else act = "connected"; D.thing.what = NORM_WHAT_SOCKET; set_program_obj(au); break; case NORM_SECURITY_POLICY: act = "adjusted-security-policy-of"; D.thing.what = NORM_WHAT_PROCESS; set_program_obj(au); if (D.how) { free((void *)D.how); D.how = strdup(syscall); } break; case NORM_SECURITY_ATTR: act = "retrieved-security-attr-of"; D.thing.what = NORM_WHAT_PROCESS; set_program_obj(au); break; case NORM_SECURITY_LIST: act = "listed-security-modules"; D.thing.what = NORM_WHAT_SECURITY_MODULES; break; default: { const char *k; auparse_first_record(au); k = auparse_find_field(au, "key"); if (k && strcmp(k, "(null)")) { act = "triggered-audit-rule"; D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field( D.thing.primary, auparse_get_field_num(au)); } else act = "triggered-unknown-audit-rule"; D.thing.what = NORM_WHAT_AUDIT_RULE; } break; } // We put the AVC back after gathering the object information if (tmp_objkind == NORM_MAC) act = "accessed-mac-policy-controlled-object"; else if (tmp_objkind == NORM_AV) act = "accessed-policy-controlled-file"; if (act) D.action = strdup(act); return 0; } static const char *normalize_determine_evkind(int type) { int kind; switch (type) { case AUDIT_USER_AUTH ... AUDIT_USER_ACCT: case AUDIT_CRED_ACQ ... AUDIT_USER_END: case AUDIT_USER_CHAUTHTOK ... AUDIT_CRED_REFR: case AUDIT_USER_LOGIN ... AUDIT_USER_LOGOUT: case AUDIT_LOGIN: kind = NORM_EVTYPE_USER_LOGIN; break; case AUDIT_GRP_AUTH: case AUDIT_CHGRP_ID: kind = NORM_EVTYPE_GROUP_CHANGE; break; case AUDIT_USER_MGMT: case AUDIT_ADD_USER ...AUDIT_DEL_GROUP: case AUDIT_GRP_MGMT ... AUDIT_GRP_CHAUTHTOK: case AUDIT_ACCT_LOCK ... AUDIT_ACCT_UNLOCK: kind = NORM_EVTYPE_USER_ACCT; break; case AUDIT_KERNEL: case AUDIT_SYSTEM_BOOT ... AUDIT_SERVICE_STOP: kind = NORM_EVTYPE_SYSTEM_SERVICES; break; case AUDIT_USYS_CONFIG: case AUDIT_CONFIG_CHANGE: case AUDIT_NETFILTER_CFG: case AUDIT_FEATURE_CHANGE: case AUDIT_TIME_INJOFFSET: case AUDIT_TIME_ADJNTPVAL: case AUDIT_USER_DEVICE: case AUDIT_SOFTWARE_UPDATE: kind = NORM_EVTYPE_CONFIG; break; case AUDIT_SECCOMP: kind = NORM_EVTYPE_DAC_DECISION; break; case AUDIT_TEST ... AUDIT_TRUSTED_APP: case AUDIT_USER_CMD: case AUDIT_CHUSER_ID: kind = NORM_EVTYPE_USERSPACE; break; case AUDIT_USER_TTY: case AUDIT_TTY: kind = NORM_EVTYPE_TTY; break; case AUDIT_EVENT_LISTENER: case AUDIT_FIRST_DAEMON ... AUDIT_LAST_DAEMON: kind = NORM_EVTYPE_AUDIT_DAEMON; break; case AUDIT_USER_SELINUX_ERR: case AUDIT_USER_AVC: case AUDIT_APPARMOR_ALLOWED ... AUDIT_APPARMOR_DENIED: case AUDIT_APPARMOR_ERROR: case AUDIT_AVC ... AUDIT_AVC_PATH: kind = NORM_EVTYPE_MAC_DECISION; break; case AUDIT_INTEGRITY_FIRST_MSG ... AUDIT_INTEGRITY_LAST_MSG: case AUDIT_ANOM_RBAC_INTEGRITY_FAIL: // Aide sends this kind = NORM_EVTYPE_INTEGRITY; break; case AUDIT_FIRST_KERN_ANOM_MSG ... AUDIT_LAST_KERN_ANOM_MSG: case AUDIT_FIRST_ANOM_MSG ... AUDIT_ANOM_RBAC_FAIL: case AUDIT_ANOM_CRYPTO_FAIL ... AUDIT_LAST_ANOM_MSG: kind = NORM_EVTYPE_ANOMALY; break; case AUDIT_FIRST_ANOM_RESP ... AUDIT_LAST_ANOM_RESP: kind = NORM_EVTYPE_ANOMALY_RESP; break; case AUDIT_MAC_POLICY_LOAD ... AUDIT_LAST_SELINUX: case AUDIT_AA ... AUDIT_APPARMOR_AUDIT: case AUDIT_APPARMOR_HINT ... AUDIT_APPARMOR_STATUS: case AUDIT_FIRST_USER_LSPP_MSG ... AUDIT_LAST_USER_LSPP_MSG: kind = NORM_EVTYPE_MAC; break; case AUDIT_FIRST_KERN_CRYPTO_MSG ... AUDIT_LAST_KERN_CRYPTO_MSG: case AUDIT_FIRST_CRYPTO_MSG ... AUDIT_LAST_CRYPTO_MSG: kind = NORM_EVTYPE_CRYPTO; break; case AUDIT_FIRST_VIRT_MSG ... AUDIT_LAST_VIRT_MSG: kind = NORM_EVTYPE_VIRT; break; case AUDIT_SYSCALL ... AUDIT_SOCKETCALL: case AUDIT_SOCKADDR ... AUDIT_MQ_GETSETATTR: case AUDIT_FD_PAIR ... AUDIT_OBJ_PID: case AUDIT_BPRM_FCAPS ... AUDIT_NETFILTER_PKT: case AUDIT_URINGOP: kind = NORM_EVTYPE_AUDIT_RULE; break; case AUDIT_FANOTIFY: kind = NORM_EVTYPE_AV_DECISION; break; case AUDIT_BPF: kind = NORM_EVTYPE_BPF; break; default: kind = NORM_EVTYPE_UNKNOWN; } return evtype_i2s(kind); } static const char *find_config_change_object(auparse_state_t *au) { const char *f; // Check if its an audit rule auparse_first_record(au); f = auparse_find_field(au, "key"); if (f) { const char *str = auparse_get_field_str(au); if (str && strcmp(str, "(null)")) return f; } // Next lets find the individual objects being set auparse_first_record(au); f = auparse_find_field(au, "audit_enabled"); if (f) return f; auparse_first_record(au); f = auparse_find_field(au, "audit_pid"); if (f) return f; auparse_first_record(au); f = auparse_find_field(au, "audit_backlog_limit"); if (f) return f; auparse_first_record(au); f = auparse_find_field(au, "audit_failure"); if (f) return f; auparse_first_record(au); f = auparse_find_field(au, "actions"); // seccomp-logging if (f) return f; auparse_first_record(au); f = auparse_find_field(au, "list"); // If nothing else, the list if (f) return f; return NULL; } static int normalize_compound(auparse_state_t *au) { const char *f, *syscall = NULL; int rc, recno, otype, type; otype = type = auparse_get_type(au); // All compound events have a syscall record, find it. After this // loop, type should be syscall, and otype is the original type. // Traditionally, the first record is the purpose of the event and // the syscall is added on next to support/enhance information content. if (type != AUDIT_SYSCALL) { do { // If we go off the end without finding a syscall // record, don't parse corrupt events if (auparse_next_record(au) < 0) return 1; type = auparse_get_type(au); } while (type && type != AUDIT_SYSCALL); } if (!type) return 1; // Determine the kind of event using original event type D.evkind = normalize_determine_evkind(otype); if (type == AUDIT_SYSCALL) { recno = auparse_get_record_num(au); f = auparse_find_field(au, "syscall"); if (f) { f = auparse_interpret_field(au); if (f) syscall = strdup(f); } // Results f = auparse_find_field(au, "success"); if (f) { const char *str = auparse_get_field_str(au); if (strcmp(str, "no") == 0) syscall_success = 0; else syscall_success = 1; D.results = set_record(0, recno); D.results = set_field(D.results, auparse_get_field_num(au)); } else { rc = auparse_goto_record_num(au, recno); if (rc != 1) { free((void *)syscall); return 1; } auparse_first_field(au); } // Subject - primary if (set_prime_subject(au, "auid", recno)) { rc = auparse_goto_record_num(au, recno); if (rc != 1) { free((void *)syscall); return 1; } auparse_first_field(au); } // Subject - alias, uid comes before auid if (set_secondary_subject(au, "uid", recno)) { rc = auparse_goto_record_num(au, recno); if (rc != 1) { free((void *)syscall); return 1; } auparse_first_field(au); } // Subject attributes syscall_subj_attr(au); // how auparse_first_field(au); f = auparse_find_field(au, "exe"); if (f) { const char *exe = auparse_interpret_field(au); D.how = strdup(exe); if (D.how == NULL) { fprintf(stderr, "Out of memory. Check %s file, %d line", __FILE__, __LINE__); free((void *)syscall); return 1; } if ((strncmp(D.how, "/usr/bin/python", 15) == 0) || (strncmp(D.how, "/usr/bin/sh", 11) == 0) || (strncmp(D.how, "/usr/bin/bash", 13) == 0) || (strncmp(D.how, "/usr/bin/perl", 13) == 0)) { int fnum; rc = 0; // Comm should be the previous field if ((fnum = auparse_get_field_num(au)) > 0) rc = auparse_goto_field_num(au,fnum-1); if (rc == 0) auparse_first_record(au); f = auparse_find_field(au, "comm"); if (f) { free((void *)D.how); exe = auparse_interpret_field(au); D.how = strdup(exe); } } } else { rc = auparse_goto_record_num(au, recno); if (rc != 1) { free((void *)syscall); return 1; } auparse_first_field(au); } f = auparse_find_field(au, "key"); if (f) { const char *k = auparse_get_field_str(au); if (strcmp(k, "(null)")) { // We only collect real keys D.key = set_record(0, recno); D.key = set_field(D.key, auparse_get_field_num(au)); } } // No error repositioning will be done because nothing // below uses fields. // action & object if (otype == AUDIT_ANOM_LINK) { const char *act = normalize_record_map_i2s(otype); if (act) D.action = strdup(act); set_file_object(au, 1); if (is_unset(D.thing.primary)) { int r, num = auparse_get_num_records(au); for (r = 1; r < num; r++) { if (auparse_goto_record_num(au, r) != 1) continue; if (auparse_get_type(au) == AUDIT_PATH) { auparse_first_field(au); set_prime_object(au, "name", r); D.thing.what = NORM_WHAT_LINK; break; } } if (is_unset(D.thing.primary)) { auparse_first_record(au); f = auparse_find_field(au, "path"); if (f == NULL) f = auparse_find_field(au, "cwd"); if (f) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); D.thing.what = NORM_WHAT_LINK; } } } } else if (otype == AUDIT_CONFIG_CHANGE) { auparse_first_record(au); f = auparse_find_field(au, "op"); if (f) { value_t o; // Fix the action D.action = strdup(auparse_interpret_field(au)); // Next fix the object o = find_simple_object(au, AUDIT_CONFIG_CHANGE); D.thing.primary = o; } } else { normalize_syscall(au, syscall); if (otype == AUDIT_MAC_POLICY_LOAD) set_program_obj(au); } } free((void *)syscall); return 0; } static value_t find_simple_object(auparse_state_t *au, int type) { value_t o = set_record(0, UNSET); const char *f = NULL; auparse_first_field(au); switch (type) { case AUDIT_SERVICE_START: case AUDIT_SERVICE_STOP: f = auparse_find_field(au, "unit"); D.thing.what = NORM_WHAT_SERVICE; break; case AUDIT_SYSTEM_RUNLEVEL: f = auparse_find_field(au, "new-level"); D.thing.what = NORM_WHAT_SYSTEM; break; case AUDIT_USER_ROLE_CHANGE: f = auparse_find_field(au, "selected-context"); D.thing.what = NORM_WHAT_USER_SESSION; break; case AUDIT_ROLE_ASSIGN: case AUDIT_ROLE_REMOVE: case AUDIT_USER_MGMT: case AUDIT_ACCT_LOCK: case AUDIT_ACCT_UNLOCK: case AUDIT_ADD_USER: case AUDIT_DEL_USER: case AUDIT_ADD_GROUP: case AUDIT_DEL_GROUP: case AUDIT_GRP_MGMT: f = auparse_find_field(au, "id"); if (f == NULL) { auparse_first_record(au); f = auparse_find_field(au, "acct"); } D.thing.what = NORM_WHAT_ACCT; break; case AUDIT_USER_START: case AUDIT_USER_END: case AUDIT_USER_ERR: case AUDIT_USER_LOGIN: case AUDIT_USER_LOGOUT: f = auparse_find_field(au, "terminal"); D.thing.what = NORM_WHAT_USER_SESSION; break; case AUDIT_USER_AUTH: case AUDIT_USER_ACCT: case AUDIT_CRED_ACQ: case AUDIT_CRED_REFR: case AUDIT_CRED_DISP: case AUDIT_USER_CHAUTHTOK: case AUDIT_GRP_CHAUTHTOK: case AUDIT_ANOM_LOGIN_FAILURES: case AUDIT_ANOM_LOGIN_TIME: case AUDIT_ANOM_LOGIN_SESSIONS: case AUDIT_ANOM_LOGIN_LOCATION: f = auparse_find_field(au, "acct"); D.thing.what = NORM_WHAT_USER_SESSION; break; case AUDIT_ANOM_EXEC: case AUDIT_USER_CMD: f = auparse_find_field(au, "cmd"); D.thing.what = NORM_WHAT_PROCESS; break; case AUDIT_USER_TTY: case AUDIT_TTY: auparse_first_record(au); f = auparse_find_field(au, "data"); D.thing.what = NORM_WHAT_KEYSTROKES; break; case AUDIT_USER_DEVICE: auparse_first_record(au); f = auparse_find_field(au, "device"); D.thing.what = NORM_WHAT_KEYSTROKES; break; case AUDIT_SOFTWARE_UPDATE: auparse_first_record(au); f = auparse_find_field(au, "sw"); D.thing.what = NORM_WHAT_SOFTWARE; break; case AUDIT_VIRT_MACHINE_ID: f = auparse_find_field(au, "vm"); D.thing.what = NORM_WHAT_VM; break; case AUDIT_VIRT_RESOURCE: f = auparse_find_field(au, "resrc"); D.thing.what = NORM_WHAT_VM; break; case AUDIT_VIRT_CONTROL: f = auparse_find_field(au, "op"); D.thing.what = NORM_WHAT_VM; break; case AUDIT_LABEL_LEVEL_CHANGE: f = auparse_find_field(au, "printer"); D.thing.what = NORM_WHAT_PRINTER; break; case AUDIT_CONFIG_CHANGE: f = find_config_change_object(au); D.thing.what = NORM_WHAT_AUDIT_CONFIG; break; case AUDIT_MAC_CONFIG_CHANGE: f = auparse_find_field(au, "bool"); D.thing.what = NORM_WHAT_SECURITY_POLICY; break; case AUDIT_MAC_STATUS: f = auparse_find_field(au, "enforcing"); D.thing.what = NORM_WHAT_SECURITY_POLICY; break; // These deal with policy, not sure about object yet case AUDIT_MAC_POLICY_LOAD: case AUDIT_LABEL_OVERRIDE: case AUDIT_DEV_ALLOC ... AUDIT_USER_MAC_CONFIG_CHANGE: D.thing.what = NORM_WHAT_SECURITY_POLICY; break; case AUDIT_USER: f = auparse_find_field(au, "addr"); // D.thing.what = NORM_WHAT_? break; case AUDIT_USYS_CONFIG: f = auparse_find_field(au, "op"); if (f) { free((void *)D.action); D.action = strdup(auparse_interpret_field(au)); f = NULL; } D.thing.what = NORM_WHAT_SYSTEM; break; case AUDIT_CRYPTO_KEY_USER: f = auparse_find_field(au, "fp"); D.thing.what = NORM_WHAT_USER_SESSION; break; case AUDIT_CRYPTO_SESSION: f = auparse_find_field(au, "addr"); D.thing.what = NORM_WHAT_USER_SESSION; break; case AUDIT_ANOM_RBAC_INTEGRITY_FAIL: f = auparse_find_field(au, "hostname"); D.thing.what = NORM_WHAT_FILESYSTEM; break; default: break; } if (f) { o = set_record(0, 0); o = set_field(o, auparse_get_field_num(au)); } return o; } static value_t find_simple_obj_secondary(auparse_state_t *au, int type) { value_t o = set_record(0, UNSET); const char *f = NULL; auparse_first_field(au); switch (type) { case AUDIT_CRYPTO_SESSION: f = auparse_find_field(au, "rport"); break; case AUDIT_SOFTWARE_UPDATE: f = auparse_find_field(au, "sw_type"); break; default: break; } if (f) { o = set_record(0, 0); o = set_field(o, auparse_get_field_num(au)); } return o; } static value_t find_simple_obj_primary2(auparse_state_t *au, int type) { value_t o = set_record(0, UNSET); const char *f = NULL; auparse_first_field(au); switch (type) { case AUDIT_VIRT_CONTROL: f = auparse_find_field(au, "vm"); break; case AUDIT_VIRT_RESOURCE: f = auparse_find_field(au, "vm"); break; case AUDIT_SOFTWARE_UPDATE: f = auparse_find_field(au, "root_dir"); break; default: break; } if (f) { o = set_record(0, 0); o = set_field(o, auparse_get_field_num(au)); } return o; } static void collect_simple_subj_attr(auparse_state_t *au) { if (D.opt == NORM_OPT_NO_ATTRS) return; auparse_first_record(au); add_subj_attr(au, "pid", 0); // Just pass 0 since simple is 1 record add_subj_attr(au, "subj", 0); } static void collect_userspace_subj_attr(auparse_state_t *au, int type) { if (D.opt == NORM_OPT_NO_ATTRS) return; // Just pass 0 since simple is 1 record add_subj_attr(au, "hostname", 0); add_subj_attr(au, "addr", 0); // Some events have the terminal as the object - skip for them if (type != AUDIT_USER_START && type != AUDIT_USER_END && type != AUDIT_USER_ERR) add_subj_attr(au, "terminal", 0); } static int normalize_simple(auparse_state_t *au) { const char *f, *act = NULL; int type = auparse_get_type(au); // Some older OS do not have PROCTITLE records if (type == AUDIT_SYSCALL) return normalize_compound(au); // Determine the kind of event D.evkind = normalize_determine_evkind(type); // This is for events that follow: // auid, (op), (uid), stuff if (type == AUDIT_CONFIG_CHANGE || type == AUDIT_FEATURE_CHANGE || type == AUDIT_SECCOMP || type == AUDIT_ANOM_ABEND || type == AUDIT_ANOM_PROMISCUOUS) { // Subject - primary set_prime_subject(au, "auid", 0); // Session add_session(au, 0); // Subject attrs collect_simple_subj_attr(au); // action if (type == AUDIT_CONFIG_CHANGE) { auparse_first_field(au); f = auparse_find_field(au, "op"); if (f) { const char *str = auparse_interpret_field(au); if (*str == '"') str++; if (strncmp(str, "add_rule", 8) == 0) { D.action = strdup("added-audit-rule"); D.thing.primary = find_simple_object(au, type); } else if (strncmp(str,"remove_rule",11) == 0){ D.action = strdup("deleted-audit-rule"); D.thing.primary = find_simple_object(au, type); } else goto map; } else goto map; } else { // This assigns action for feature_change, seccomp, // and anom_abend map: act = normalize_record_map_i2s(type); if (act) D.action = strdup(act); if (type == AUDIT_CONFIG_CHANGE) D.thing.primary = find_simple_object(au, type); auparse_first_record(au); } // object if (type == AUDIT_FEATURE_CHANGE) { // Subject - secondary auparse_first_field(au); if (set_secondary_subject(au, "uid", 0)) auparse_first_record(au); // how f = auparse_find_field(au, "exe"); if (f) { const char *sig = auparse_interpret_field(au); D.how = strdup(sig); } // object set_prime_object(au, "feature", 0); D.thing.what = NORM_WHAT_SYSTEM; } if (type == AUDIT_SECCOMP) { // Subject - secondary auparse_first_field(au); if (set_secondary_subject(au, "uid", 0)) auparse_first_record(au); // how f = auparse_find_field(au, "exe"); if (f) { const char *sig = auparse_interpret_field(au); D.how = strdup(sig); } // Object if (set_prime_object(au, "syscall", 0)) auparse_first_record(au); D.thing.what = NORM_WHAT_PROCESS; // Results f = auparse_find_field(au, "code"); if (f) { D.results = set_record(0, 0); D.results = set_field(D.results, auparse_get_field_num(au)); } return 0; } if (type == AUDIT_ANOM_ABEND) { // Subject - secondary auparse_first_field(au); if (set_secondary_subject(au, "uid", 0)) auparse_first_record(au); //object if (set_prime_object(au, "exe", 0)) auparse_first_record(au); D.thing.what = NORM_WHAT_PROCESS; // how f = auparse_find_field(au, "sig"); if (f) { const char *sig = auparse_interpret_field(au); D.how = strdup(sig); } } if (type == AUDIT_ANOM_PROMISCUOUS) { auparse_first_field(au); set_prime_object(au, "dev", 0); set_secondary_subject(au, "uid", 0); D.thing.what = NORM_WHAT_SOCKET; } // Results set_results(au, 0); return 0; } // This one is atypical and originates from the kernel if (type == AUDIT_LOGIN) { // Secondary if (set_secondary_subject(au, "uid", 0)) auparse_first_record(au); // Subject attrs collect_simple_subj_attr(au); // Subject if (set_prime_subject(au, "old-auid", 0)) auparse_first_record(au); // Object if (set_prime_object(au, "auid", 0)) auparse_first_record(au); D.thing.what = NORM_WHAT_USER_SESSION; // Session add_session(au, 0); // Results set_results(au, 0); // action act = normalize_record_map_i2s(type); if (act) D.action = strdup(act); // How - currently missing return 0; } // NETFILTER_CFG is atypical if (type == AUDIT_NETFILTER_CFG) { // Subject attrs collect_simple_subj_attr(au); // how f = auparse_find_field(au, "comm"); if (f) { const char *sig = auparse_interpret_field(au); D.how = strdup(sig); } D.action = strdup("loaded-firewall-rule-to"); auparse_first_record(au); f = auparse_find_field(au, "table"); if (f) { D.thing.primary = set_record(0, auparse_get_record_num(au)); D.thing.primary = set_field(D.thing.primary, auparse_get_field_num(au)); } set_system_subject_what(au); D.thing.what = NORM_WHAT_FIREWALL; return 0; } /* This one is also atypical and comes from the kernel */ if (type == AUDIT_AVC) { // how f = auparse_find_field(au, "comm"); if (f) { const char *sig = auparse_interpret_field(au); D.how = strdup(sig); } else auparse_first_record(au); // Subject set_prime_subject(au, "scontext", 0); set_unknown_subject_what(au); auparse_first_record(au); // Object if (D.opt == NORM_OPT_ALL) { // We will only collect this when everything is asked // for because it messes up text format otherwise set_prime_object(au, "tcontext", 0); auparse_first_record(au); } // Ideally we would choose tclass D.thing.what = NORM_WHAT_UNKNOWN; // action act = normalize_record_map_i2s(type); if (act) D.action = strdup(act); // find the denial auparse_first_record(au); f = auparse_find_field(au, "seresult"); if (f) { D.results = set_record(0, 0); D.results = set_field(D.results, auparse_get_field_num(au)); } // This is slim pickings without a syscall record return 0; } /* Daemon events are atypical because they never transit the kernel */ if (type >= AUDIT_FIRST_DAEMON && type < AUDIT_LAST_DAEMON) { // Subject - primary set_prime_subject(au, "auid", 0); // Secondary - optional if (set_secondary_subject(au, "uid", 0)) auparse_first_record(au); // Session - optional if (add_session(au, 0)) auparse_first_record(au); // Subject attrs collect_simple_subj_attr(au); free((void *)D.actor.what); D.actor.what = strdup("auditd"); // action act = normalize_record_map_i2s(type); if (act) D.action = strdup(act); // Object type D.thing.what = NORM_WHAT_SERVICE; // How start:init, everything else:signal if (type == AUDIT_DAEMON_START) D.how = strdup("init"); else if (type < AUDIT_DAEMON_ACCEPT && type != AUDIT_DAEMON_ABORT) D.how = strdup("signal"); // Results set_results(au, 0); return 0; } // BPF events are atypical if (type == AUDIT_BPF) { set_system_subject_what(au); auparse_first_record(au); f = auparse_find_field(au, "op"); if (f) { const char *str = auparse_get_field_str(au); if (strcmp(str, "LOAD") == 0) act = "loaded-bpf-program"; else act = "unloaded-bpf-program"; } else act = "bpf-program"; D.action = strdup(act); D.thing.what = NORM_WHAT_PROCESS; set_program_obj(au); return 0; } // LISTENER events are atypical if (type == AUDIT_EVENT_LISTENER) { // Subject - primary set_prime_subject(au, "auid", 0); // Secondary - optional auparse_first_record(au); set_secondary_subject(au, "uid", 0); // Session auparse_first_record(au); add_session(au, 0); // Subject attrs collect_simple_subj_attr(au); auparse_first_record(au); f = auparse_find_field(au, "op"); if (f) { const char *str = auparse_get_field_str(au); if (strcmp(str, "connect") == 0) act = "connected-to"; else act = "disconnected-from"; } else act = "connected"; D.action = strdup(act); set_program_obj(au); D.thing.what = NORM_WHAT_SOCKET; // How auparse_first_record(au); f = auparse_find_field(au, "exe"); if (f) { const char *exe = auparse_interpret_field(au); D.how = strdup(exe); } // Results auparse_first_record(au); set_results(au, 0); return 0; } // Labeled networking events are atypical if (type >= AUDIT_MAC_UNLBL_ALLOW && type <= AUDIT_LAST_SELINUX) { // Subject - primary set_prime_subject(au, "auid", 0); // We don't have a secondary subject, so set it to auid set_subject_what(au); // Session add_session(au, 0); // Subject attrs add_subj_attr(au, "subj", 0); // action if (type == AUDIT_MAC_UNLBL_ALLOW) { f = auparse_find_field(au, "unlbl_accept"); if (f) { if (auparse_get_field_int(au) == 1) act = "is-allowing-unlabeled-network-traffic"; else act = "is-disallowing-unlabeled-network-traffic"; } else auparse_first_record(au); } else act = normalize_record_map_i2s(type); if (act) D.action = strdup(act); if (type == AUDIT_MAC_MAP_ADD || type == AUDIT_MAC_MAP_DEL) { if (set_prime_object(au, "nlbl_domain", 0)) auparse_first_record(au); } // Object type D.thing.what = NORM_WHAT_SECURITY_POLICY; // Results set_results(au, 0); return 0; } // This is for events that follow: // uid, auid, ses, res, find_simple_object // // USER_LOGIN is different in locating the subject because if they // fail login, they are not quite in the system to have an auid. if (type == AUDIT_USER_LOGIN) { // Subject - primary if (set_prime_subject(au, "id", 0)) { auparse_first_record(au); if (set_prime_subject(au, "acct", 0) == 0) set_subject_what(au); } else // If id found, set the subjkind set_subject_what(au); auparse_first_record(au); } else { // Subject - alias, uid comes before auid if (set_secondary_subject(au, "uid", 0)) auparse_first_record(au); // Subject - primary set_prime_subject(au, "auid", 0); } // Session add_session(au, 0); // Subject attrs collect_simple_subj_attr(au); if ((type >= AUDIT_FIRST_USER_MSG && type < AUDIT_LAST_USER_MSG) || (type >= AUDIT_FIRST_USER_MSG2 && type < AUDIT_LAST_USER_MSG2)) collect_userspace_subj_attr(au, type); // Results if (type != AUDIT_USER_AVC) set_results(au, 0); else { // find the denial auparse_first_record(au); f = auparse_find_field(au, "seresult"); if (f) { D.results = set_record(0, 0); D.results = set_field(D.results, auparse_get_field_num(au)); } // Subject auparse_first_record(au); set_prime_subject(au, "scontext", 0); // Object if (D.opt == NORM_OPT_ALL) { // We will only collect this when everything is asked // for because it messes up text format otherwise auparse_first_record(au); set_prime_object(au, "tcontext", 0); } } // action if (type == AUDIT_USER_DEVICE) { auparse_first_record(au); f = auparse_find_field(au, "op"); if (f) act = f; } if (act == NULL) act = normalize_record_map_i2s(type); if (act) D.action = strdup(act); // object if (type != AUDIT_USER_AVC) { auparse_first_record(au); D.thing.primary = find_simple_object(au, type); D.thing.secondary = find_simple_obj_secondary(au, type); D.thing.two = find_simple_obj_primary2(au, type); // object attrs - rare on simple events if (D.opt == NORM_OPT_ALL) { if (type == AUDIT_USER_DEVICE) { add_obj_attr(au, "uuid", 0); } else if (type == AUDIT_SOFTWARE_UPDATE) { auparse_first_record(au); add_obj_attr(au, "key_enforce", 0); add_obj_attr(au, "gpg_res", 0); } } } // how if (type == AUDIT_SYSTEM_BOOT) { D.thing.what = NORM_WHAT_SYSTEM; f = auparse_find_field(au, "exe"); if (f) { const char *exe = auparse_interpret_field(au); D.how = strdup(exe); } return 0; } else if (type == AUDIT_SYSTEM_SHUTDOWN) { D.thing.what = NORM_WHAT_SERVICE; f = auparse_find_field(au, "exe"); if (f) { const char *exe = auparse_interpret_field(au); D.how = strdup(exe); } return 0; } auparse_first_record(au); if (type == AUDIT_ANOM_EXEC) { f = auparse_find_field(au, "terminal"); if (f) { const char *term = auparse_interpret_field(au); D.how = strdup(term); } return 0; } if (type == AUDIT_TTY) { f = auparse_find_field(au, "comm"); if (f) { const char *comm = auparse_interpret_field(au); D.how = strdup(comm); } return 0; } f = auparse_find_field(au, "exe"); if (f) { const char *exe = auparse_interpret_field(au); D.how = strdup(exe); if (D.how == NULL) { fprintf(stderr, "Out of memory. Check %s file, %d line", __FILE__, __LINE__); return 1; } if ((strncmp(D.how, "/usr/bin/python", 15) == 0) || (strncmp(D.how, "/usr/bin/sh", 11) == 0) || (strncmp(D.how, "/usr/bin/bash", 13) == 0) || (strncmp(D.how, "/usr/bin/perl", 13) == 0)) { // comm should be the previous field if its there at all int fnum; if ((fnum = auparse_get_field_num(au)) > 0) auparse_goto_field_num(au, fnum - 1); else auparse_first_record(au); f = auparse_find_field(au, "comm"); if (f) { free((void *)D.how); exe = auparse_interpret_field(au); D.how = strdup(exe); } } } return 0; } /* * This is the main entry point for the normalization. This function * will analyze the current event to pick out the important pieces. */ int auparse_normalize(auparse_state_t *au, normalize_option_t opt) { int rc; unsigned num; auparse_first_record(au); num = auparse_get_num_records(au); // Reset cursor - no idea what we are being handed auparse_first_record(au); clear_normalizer(&D); D.opt = opt; // If we have more than one record in the event its a syscall based // event. Otherwise its a simple event with all pieces in the same // record. if (num > 1) rc = normalize_compound(au); else rc = normalize_simple(au); // Reset the cursor auparse_first_record(au); return rc; } /* * This function positions the internal cursor to the record and field that * the location refers to. * Returns: < 0 error, 0 uninitialized, 1 == success */ static int seek_field(auparse_state_t *au, value_t location) { int record, field, rc; if (is_unset(location)) return 0; record = get_record(location); field = get_field(location); rc = auparse_goto_record_num(au, record); if (rc != 1) return -1; rc = auparse_goto_field_num(au, field); if (rc != 1) return -2; return 1; } const char *auparse_normalize_get_event_kind(const auparse_state_t *au) { return D.evkind; } int auparse_normalize_session(auparse_state_t *au) { return seek_field(au, D.session); } int auparse_normalize_subject_primary(auparse_state_t *au) { return seek_field(au, D.actor.primary); } int auparse_normalize_subject_secondary(auparse_state_t *au) { return seek_field(au, D.actor.secondary); } const char *auparse_normalize_subject_kind(const auparse_state_t *au) { return D.actor.what; } // Returns: -1 = error, 0 uninitialized, 1 == success int auparse_normalize_subject_first_attribute(auparse_state_t *au) { if (D.actor.attr.cnt) { data_node *n; cllist_first(&D.actor.attr); n = cllist_get_cur(&D.actor.attr); if (n) return seek_field(au, n->num); } return 0; } // Returns: -1 = error, 0 uninitialized, 1 == success int auparse_normalize_subject_next_attribute(auparse_state_t *au) { if (D.actor.attr.cnt) { data_node *n; n = cllist_next(&D.actor.attr); if (n) return seek_field(au, n->num); } return 0; } const char *auparse_normalize_get_action(const auparse_state_t *au) { return D.action; } int auparse_normalize_object_primary(auparse_state_t *au) { return seek_field(au, D.thing.primary); } int auparse_normalize_object_secondary(auparse_state_t *au) { return seek_field(au, D.thing.secondary); } int auparse_normalize_object_primary2(auparse_state_t *au) { return seek_field(au, D.thing.two); } // Returns: -1 = error, 0 uninitialized, 1 == success int auparse_normalize_object_first_attribute(auparse_state_t *au) { if (D.thing.attr.cnt) { data_node *n; cllist_first(&D.thing.attr); n = cllist_get_cur(&D.thing.attr); if (n) return seek_field(au, n->num); } return 0; } // Returns: -1 = error, 0 uninitialized, 1 == success int auparse_normalize_object_next_attribute(auparse_state_t *au) { if (D.thing.attr.cnt) { data_node *n; n = cllist_next(&D.thing.attr); if (n) return seek_field(au, n->num); } return 0; } const char *auparse_normalize_object_kind(const auparse_state_t *au) { return normalize_obj_kind_map_i2s(D.thing.what); } int auparse_normalize_object_kind_int(const auparse_state_t *au) { return D.thing.what; } int auparse_normalize_get_results(auparse_state_t *au) { return seek_field(au, D.results); } const char *auparse_normalize_how(const auparse_state_t *au) { return D.how; } int auparse_normalize_key(auparse_state_t *au) { return seek_field(au, D.key); }