/
githubmirror
/
audit-userspace
Обзор
Документация
Войти
/
githubmirror
/
audit-userspace
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/ausearch-report.c
877 строк
20 KB
Steve Grubb
improve text generation for process groups
15 авг 2025, 22:34
15 авг 2025, 22:34
691fe2a
Код
Авторство
О чём код?
/* * ausearch-report.c - Format and output events * Copyright (c) 2005-09,2011-13,2016-17,2021-23 Red Hat * All Rights Reserved. * * This software may be freely redistributed and/or modified under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 2, or (at your option) any * later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor * Boston, MA 02110-1335, USA. * * Authors: * Steve Grubb <sgrubb@redhat.com> */ #include "config.h" #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include "libaudit.h" #include "ausearch-options.h" #include "ausearch-parse.h" #include "ausearch-lookup.h" #include "auparse.h" #include "auparse-idata.h" #include "auditd-config.h" #include "normalize-internal.h" // pid list static int interp_init = 0; static auparse_state_t *au = NULL; /* Local functions */ static void output_raw(llist *l); static void output_default(llist *l); static void output_interpreted(llist *l); static void output_interpreted_record(const lnode *n, const event *e); static void feed_auparse(llist *l, auparse_callback_ptr callback); static void report_interpret(char *name, char *val, int comma, int rtype); static void csv_event(auparse_state_t *au, auparse_cb_event_t cb_event_type, void *user_data); static void text_event(auparse_state_t *au, auparse_cb_event_t cb_event_type, void *user_data); extern time_t lol_get_eoe_timeout(void); static auparse_state_t *feed_au = NULL; /* The machine based on elf type */ static unsigned long machine = -1; static int cur_syscall = -1; /* The first syscall argument */ static unsigned long long a0, a1; /* tracks state of interpretations */ static int loaded = 0; void ausearch_load_interpretations(const lnode *n) { if (loaded == 0) { if (!interp_init) { au = auparse_init(AUSOURCE_BUFFER, ""); if (au == NULL) return; interp_init = 1; } _auparse_load_interpretations(au, n->interp); loaded = 1; } } void ausearch_free_interpretations(void) { if (loaded && au) { _auparse_free_interpretations(au); loaded = 0; } } /* This function branches to the correct output format */ void output_event(llist *l) { switch (report_format) { case RPT_RAW: output_raw(l); break; case RPT_DEFAULT: output_default(l); break; case RPT_INTERP: output_interpreted(l); break; case RPT_PRETTY: break; case RPT_CSV: feed_auparse(l, csv_event); break; case RPT_TEXT: feed_auparse(l, text_event); break; default: fprintf(stderr, "Report format error"); exit(1); } } /* This function will output the record as is */ static void output_raw(llist *l) { const lnode *n; list_first(l); n = list_get_cur(l); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } do { // Only add the separator for enriched events. if (l->fmt == LF_ENRICHED) n->message[n->mlen] = AUDIT_INTERP_SEPARATOR; puts(n->message); } while ((n=list_next(l))); } /* * This function will take the linked list and format it for output. No * interpretation is performed. The output order is lifo for everything. */ static void output_default(llist *l) { const lnode *n; list_last(l); n = list_get_cur(l); printf("----\ntime->%s", ctime(&l->e.sec)); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) puts(n->message); // No injection possible else { do { safe_print_string_n(n->message, n->mlen, 1); } while ((n=list_prev(l))); } } /* * This function will take the linked list and format it for output. * Interpretation is performed to aid understanding of records. The output * order is lifo for everything. */ static void output_interpreted(llist *l) { const lnode *n; list_last(l); n = list_get_cur(l); printf("----\n"); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) output_interpreted_record(n, &(l->e)); else { do { output_interpreted_record(n, &(l->e)); } while ((n=list_prev(l))); } } /* * This function will cycle through a single record and lookup each field's * value that it finds. */ static void output_interpreted_record(const lnode *n, const event *e) { char *ptr, *str = n->message; int found, comma = 0; int num = n->type; struct tm *btm; char tmp[32]; // Reset these because each record could be different machine = -1; cur_syscall = -1; /* Check and see if we start with a node * If we do, and there is a space in the line * move the pointer to the first character past * the space */ if (e->node) { if ((ptr=strchr(str, ' ')) != NULL) { str = ptr+1; } } // First locate time stamp. ptr = strchr(str, '('); if (ptr == NULL) { fprintf(stderr, "can't find time stamp\n"); return; } *ptr++ = 0; /* move to the start of the timestamp */ // print everything up to it. if (num >= 0) { const char * bptr; bptr = audit_msg_type_to_name(num); if (bptr) { if (e->node) printf("node=%s ", e->node); printf("type=%s msg=audit(", bptr); goto no_print; } } if (e->node) printf("node=%s ", e->node); printf("%s(", str); no_print: str = strchr(ptr, ')'); if(str == NULL) return; *str++ = 0; btm = localtime(&e->sec); if (btm) strftime(tmp, sizeof(tmp), "%x %T", btm); else strcpy(tmp, "?"); printf("%s", tmp); printf(".%03u:%lu) ", e->milli, e->serial); if (n->type == AUDIT_SYSCALL) { a0 = n->a0; a1 = n->a1; } // for each item. ausearch_load_interpretations(n); found = 0; while (str && *str && (ptr = strchr(str, '='))) { char *name, *val; comma = 0; found = 1; // look back to last space - this is name name = ptr; while (name > str && name[-1] != ' ') --name; *ptr++ = 0; // print everything up to the '=' printf("%s=", str); // Some user messages have msg='uid=500 in this case // skip the msg= piece since the real stuff is the uid= if (strcmp(name, "msg") == 0) { str = ptr; continue; } // In the above case, after msg= we need to trim the ' from uid if (*name == '\'') name++; // get string after = to the next space or end - this is value if (*ptr == '\'' || *ptr == '"') { str = strchr(ptr+1, *ptr); if (str) { str++; if (*str) *str++ = 0; } } else { str = strchr(ptr, ','); val = strchr(ptr, ' '); if (str && val && (str < val)) { // Value side has commas and another field exists // Known: LABEL_LEVEL_CHANGE banners=none,none // Known: ROLL_ASSIGN new-role=r,r // Known: any MAC LABEL can potentially have commas int ftype = auparse_interp_adjust_type(n->type, name, val); if (ftype == AUPARSE_TYPE_MAC_LABEL) { str = val; *str++ = 0; } else { *str++ = 0; comma = 1; } } else if (str && (val == NULL)) { // Goes all the way to the end. Done parsing // Known: MCS context in PATH rec obj=u:r:t:s0:c2,c7 int ftype = auparse_interp_adjust_type(n->type, name, ptr); if (ftype == AUPARSE_TYPE_MAC_LABEL) str = NULL; else { *str++ = 0; comma = 1; } } else if (val) { // There is another field, point to next (normal path) str = val; *str++ = 0; } } // val points to begin & str 1 past end val = ptr; // print interpreted string report_interpret(name, val, comma, n->type); } ausearch_free_interpretations(); // If nothing found, just print out as is if (!found && ptr == NULL && str) safe_print_string(str, 1); // If last field had comma, output the rest else if (comma) safe_print_string(str, 1); printf("\n"); } static void report_interpret(char *name, char *val, int comma, int rtype) { int type; idata id; while (*name == ' '||*name == '(') name++; if (*name == 'a' && strcmp(name, "acct") == 0) { // Remove trailing punctuation int len = strlen(val); if (val[len-1] == ':') val[len-1] = 0; } type = auparse_interp_adjust_type(rtype, name, val); if (rtype == AUDIT_SYSCALL || rtype == AUDIT_SECCOMP || rtype == AUDIT_URINGOP) { if (rtype == AUDIT_URINGOP) machine = MACH_IO_URING; else if (machine == (unsigned long)-1) machine = audit_detect_machine(); if (*name == 'a' && strcmp(name, "arch") == 0) { unsigned long ival; errno = 0; ival = strtoul(val, NULL, 16); if (errno) { printf("arch conversion error(%s) ", val); return; } machine = audit_elf_to_machine(ival); } if (cur_syscall < 0 && ((*name == 's' && strcmp(name, "syscall") == 0) || (*name == 'u' && strcmp(name, "uring_op") == 0))) { unsigned long ival; errno = 0; ival = strtoul(val, NULL, 10); if (errno) { printf("syscall conversion error(%s) ", val); return; } cur_syscall = ival; } id.syscall = cur_syscall; } else id.syscall = 0; id.machine = machine; id.a0 = a0; id.a1 = a1; id.name = name; id.val = val; id.cwd = NULL; char *out = auparse_do_interpretation(au, type, &id, escape_mode); if (type == AUPARSE_TYPE_UNCLASSIFIED) printf("%s%c", val, comma ? ',' : ' '); else if (name[0] == 'k' && strcmp(name, "key") == 0) { char *str, *ptr = out; int count = 0; while ((str = strchr(ptr, AUDIT_KEY_SEPARATOR))) { *str = 0; if (count == 0) { printf("%s", ptr); count++; } else printf(" key=%s", ptr); ptr = str+1; } if (count == 0) printf("%s ", out); else printf(" key=%s ", ptr); } else if (type == AUPARSE_TYPE_TTY_DATA) printf("%s", out); else printf("%s ", out); free(out); } /* This function will output a normalized line of audit * fields one line per event in csv format */ static int csv_header_done = 0; extern int extra_keys, extra_labels, extra_obj2, extra_time; static void csv_event(auparse_state_t *au, auparse_cb_event_t cb_event_type, void *user_data) { if (cb_event_type != AUPARSE_CB_EVENT_READY) return; if (csv_header_done == 0) { csv_header_done = 1; printf( "NODE,EVENT,DATE,TIME,%sSERIAL_NUM,EVENT_KIND," "SESSION,SUBJ_PRIME,SUBJ_SEC,SUBJ_KIND,%sACTION," "RESULT,OBJ_PRIME,OBJ_SEC,%s%sOBJ_KIND,HOW%s\n", extra_time ? "YEAR,MONTH,DAY,WEEKDAY,HOUR,MILLI,GMT_OFFSET," : "", extra_labels ? "SUBJ_LABEL," : "", extra_obj2 ? "OBJ2," : "", extra_labels ? "OBJ_LABEL," : "", extra_keys ? ",KEY" : ""); } char tmp[20]; const char *item, *type, *evkind, *subj_kind, *action, *str, *how; int rc; time_t t = auparse_get_time(au); struct tm *tv = localtime(&t); // NODE item = auparse_get_node(au); if (item) { printf("%s", auparse_interpret_field(au)); free((void *)item); } putchar(','); // Event type = auparse_get_type_name(au); if (type) printf("%s", type); putchar(','); // Normalize rc = auparse_normalize(au, extra_labels ? NORM_OPT_ALL : NORM_OPT_NO_ATTRS); // DATE if (tv) { strftime(tmp, sizeof(tmp), "%x", tv); printf("%s", tmp); } putchar(','); // TIME if (tv) { strftime(tmp, sizeof(tmp), "%T", tv); printf("%s", tmp); } putchar(','); if (extra_time) { // YEAR if (tv) { strftime(tmp, sizeof(tmp), "%Y", tv); printf("%s", tmp); } putchar(','); // MONTH if (tv) { strftime(tmp, sizeof(tmp), "%m", tv); printf("%s", tmp); } putchar(','); // DAY if (tv) { strftime(tmp, sizeof(tmp), "%d", tv); printf("%s", tmp); } putchar(','); // WEEKDAY if (tv) { strftime(tmp, sizeof(tmp), "%u", tv); printf("%s", tmp); } putchar(','); // HOUR if (tv) { strftime(tmp, sizeof(tmp), "%k", tv); printf("%s", tmp); } putchar(','); // MILLISECOND printf("%u", auparse_get_milli(au)); putchar(','); if (tv) { char sign = tv->tm_gmtoff >= 0 ? '+' : '-'; unsigned long total = labs(tv->tm_gmtoff); unsigned long hour = total/3600; unsigned long min = (total - (hour * 3600))%60; printf("%c%02lu:%02lu", sign, hour, min); } putchar(','); } // SERIAL_NUMBER printf("%lu,", auparse_get_serial(au)); if (rc) { fprintf(stderr, "error normalizing %s\n", type); // Just dump an empty frame printf(",,,,,,,,,%s%s\n", extra_labels ? ",," : "", extra_keys ? "," : ""); return; } // EVENT_KIND evkind = auparse_normalize_get_event_kind(au); printf("%s", evkind ? evkind : "unknown"); putchar(','); // SESSION rc = auparse_normalize_session(au); if (rc == 1) printf("%s", auparse_interpret_field(au)); putchar(','); // SUBJ_PRIME rc = auparse_normalize_subject_primary(au); if (rc == 1) { const char *subj = auparse_interpret_field(au); if (strcmp(subj, "unset") == 0) subj = "system"; printf("%s", subj); } putchar(','); // SUBJ_SEC rc = auparse_normalize_subject_secondary(au); if (rc == 1) printf("%s", auparse_interpret_field(au)); putchar(','); // SUBJ_KIND subj_kind = auparse_normalize_subject_kind(au); if (subj_kind) printf("%s", subj_kind); putchar(','); // SUBJ_LABEL if (extra_labels) { rc = auparse_normalize_subject_first_attribute(au); do { if (rc == 1) { const char *name = auparse_get_field_name(au); if (strcmp(name, "subj") == 0) { printf("%s", auparse_interpret_field(au)); break; } } } while (auparse_normalize_subject_next_attribute(au) == 1); putchar(','); } // ACTION action = auparse_normalize_get_action(au); printf("%s", action ? action : "did-unknown"); putchar(','); // RESULT rc = auparse_normalize_get_results(au); if (rc == 1) { int i = 0; const char *res[] = { "failed", "success" }; item = auparse_interpret_field(au); if (strcmp(item, "yes") == 0) i = 1; else if (strncmp(item, "suc", 3) == 0) i = 1; else if (auparse_get_field_type(au) == AUPARSE_TYPE_SECCOMP && strcmp(item, "allow") == 0) i = 1; printf("%s", res[i]); } putchar(','); // OBJ_PRIME rc = auparse_normalize_object_primary(au); if (rc == 1) { const char *val; if (auparse_get_field_type(au) == AUPARSE_TYPE_ESCAPED_FILE) val = auparse_interpret_realpath(au); else if (auparse_get_type(au) == AUDIT_CONFIG_CHANGE) { if (action && ((strcmp(action, "set") == 0) || (strcmp(action, "seccomp-logging") == 0))) val = auparse_get_field_name(au); else val = auparse_interpret_field(au); } else val = auparse_interpret_field(au); printf("%s", val); } putchar(','); // OBJ_SEC rc = auparse_normalize_object_secondary(au); if (rc == 1) printf("%s", auparse_interpret_field(au)); putchar(','); // OBJECT 2 if (extra_obj2) { rc = auparse_normalize_object_primary2(au); if (rc == 1) { const char *val; if (auparse_get_field_type(au) == AUPARSE_TYPE_ESCAPED_FILE) val = auparse_interpret_realpath(au); else val = auparse_interpret_field(au); printf("%s", val); } putchar(','); } // OBJ_LABEL if (extra_labels) { rc = auparse_normalize_object_first_attribute(au); do { if (rc == 1) { const char *name = auparse_get_field_name(au); if (strcmp(name, "obj") == 0) { printf("%s", auparse_interpret_field(au)); break; } } } while (auparse_normalize_object_next_attribute(au) == 1); putchar(','); } // OBJ_KIND str = auparse_normalize_object_kind(au); printf("%s,", str); // HOW how = auparse_normalize_how(au); if (how) printf("%s", how); // KEY if (extra_keys) { putchar(','); // This is to close out HOW rc = auparse_normalize_key(au); if (rc == 1) printf("%s", auparse_interpret_field(au)); } printf("\n"); } /* This function will output a normalized line of audit * fields one line per event as an english sentence */ extern int auparse_normalize_object_kind_int(const auparse_state_t *au); static void text_event(auparse_state_t *au, auparse_cb_event_t cb_event_type, void *user_data) { if (cb_event_type != AUPARSE_CB_EVENT_READY) return; char tmp[20]; const char *item, *action, *how; int rc, type, id = -2; time_t t = auparse_get_time(au); struct tm *tv = localtime(&t); if (tv) strftime(tmp, sizeof(tmp), "%T %x", tv); else strcpy(tmp, "?"); type = auparse_get_type(au); auparse_normalize(au, NORM_OPT_NO_ATTRS); item = auparse_get_node(au); if (item) { printf("On %s at %s ", auparse_interpret_field(au), tmp); free((void *)item); } else printf("At %s ", tmp); rc = auparse_normalize_subject_primary(au); if (rc == 1) { const char *subj = auparse_interpret_field(au); id = auparse_get_field_int(au); if (strcmp(subj, "unset") == 0) subj = "system"; printf("%s", subj); } // Need to compare auid and uid before doing this rc = auparse_normalize_subject_secondary(au); if (rc == 1) { int uid = auparse_get_field_int(au); // if they are different, id exists, and uid is not unset if (uid != id && id != -2 && uid != -1) printf(", acting as %s,", auparse_interpret_field(au)); } rc = auparse_normalize_get_results(au); if (rc == 1) { int i = 0; const char *res[] = { "unsuccessfully", "successfully" }; item = auparse_interpret_field(au); if (strcmp(item, "yes") == 0) i = 1; else if (strncmp(item, "suc", 3) == 0) i = 1; else if (auparse_get_field_type(au) == AUPARSE_TYPE_SECCOMP && strcmp(item, "allow") == 0) i = 1; printf(" %s ", res[i]); } else putchar(' '); action = auparse_normalize_get_action(au); if (event_debug) { if (action == NULL) printf("error on type:%d\n", type); } printf("%s ", action ? action : "did-unknown"); rc = auparse_normalize_object_primary(au); if (rc == 1) { const char *val = NULL; int ftype; // If we have an object and this is an AVC, add some words if (action && strstr(action, "violated")) val = "accessing "; ftype = auparse_get_field_type(au); if (ftype == AUPARSE_TYPE_ESCAPED_FILE) val = auparse_interpret_realpath(au); else if (ftype == AUPARSE_TYPE_SOCKADDR) { val = auparse_interpret_sock_address(au); if (val == NULL) val = auparse_interpret_sock_family(au); } else if (type == AUDIT_CONFIG_CHANGE) { if (action && ((strcmp(action, "set") == 0) || strcmp(action, "seccomp-logging") == 0)) val = auparse_get_field_name(au); else val = auparse_interpret_field(au); } if (val == NULL) val = auparse_interpret_field(au); printf("%s ", val); } rc = auparse_normalize_object_primary2(au); if (rc == 1) { const char *val; if (auparse_get_field_type(au) == AUPARSE_TYPE_ESCAPED_FILE) val = auparse_interpret_realpath(au); else val = auparse_interpret_field(au); printf("to %s ", val); } // Print pid list if process group int kind = auparse_normalize_object_kind_int(au); if (kind == NORM_WHAT_PROCESS_GROUP || kind == NORM_WHAT_PROCESS) { rc = auparse_normalize_object_first_attribute(au); if (rc == 1) { int sep = 0, cnt = 1; putchar('('); do { const char *name = auparse_get_field_name(au); if (strcmp(name, "opid") == 0) { if (sep) putchar(','); printf("%s", auparse_interpret_field(au)); sep = 1; } rc = auparse_normalize_object_next_attribute(au); cnt++; } while (rc == 1 && cnt < 4); if (cnt >= 4) printf(",...)"); else putchar(')'); putchar(' '); } } how = auparse_normalize_how(au); if (how && action && *action != 'e') // Don't print for ended-session printf("using %s", how); printf("\n"); } /* This function will push an event into auparse. The callback arg will * perform all formatting for the intended report option. */ static void feed_auparse(llist *l, auparse_callback_ptr callback) { const lnode *n; list_first(l); n = list_get_cur(l); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } if (feed_au == NULL) { feed_au = auparse_init(AUSOURCE_FEED, 0); auparse_set_escape_mode(feed_au, escape_mode); auparse_set_eoe_timeout(lol_get_eoe_timeout()); auparse_add_callback(feed_au, callback, NULL, NULL); } do { // Records need to be terminated by a newline // Temporarily replace it. if (l->fmt == LF_ENRICHED) n->message[n->mlen] = AUDIT_INTERP_SEPARATOR; n->message[n->tlen] = 0x0a; auparse_feed(feed_au, n->message, n->tlen+1); if (l->fmt == LF_ENRICHED) n->message[n->mlen] = 0; n->message[n->tlen] = 0; } while ((n=list_next(l))); auparse_flush_feed(feed_au); } void output_auparse_finish(void) { if (feed_au) auparse_destroy(feed_au); feed_au = NULL; }