diff --git a/src/ausearch-common.h b/src/ausearch-common.h index 179e127..2abd039 100644 --- a/src/ausearch-common.h +++ b/src/ausearch-common.h @@ -44,7 +44,8 @@ extern int event_exit, event_exit_is_set; typedef enum { F_BOTH, F_FAILED, F_SUCCESS } failed_t; typedef enum { C_NEITHER, C_ADD, C_DEL } conf_act_t; typedef enum { S_UNSET=-1, S_FAILED, S_SUCCESS } success_t; -typedef enum { RPT_RAW, RPT_DEFAULT, RPT_INTERP, RPT_PRETTY } report_t; +typedef enum { RPT_RAW, RPT_DEFAULT, RPT_INTERP, RPT_PRETTY, RPT_MACHINEINTERP +} report_t; extern failed_t event_failed; extern conf_act_t event_conf_act; diff --git a/src/ausearch-options.c b/src/ausearch-options.c index 1f64b49..9edd59f 100644 --- a/src/ausearch-options.c +++ b/src/ausearch-options.c @@ -74,7 +74,7 @@ S_HOSTNAME, S_INTERP, S_INFILE, S_MESSAGE_TYPE, S_PID, S_SYSCALL, S_OSUCCESS, S_TIME_END, S_TIME_START, S_TERMINAL, S_ALL_UID, S_EFF_UID, S_UID, S_LOGINID, S_VERSION, S_EXACT_MATCH, S_EXECUTABLE, S_CONTEXT, S_SUBJECT, S_OBJECT, S_PPID, S_KEY, S_RAW, S_NODE, S_IN_LOGS, S_JUST_ONE, S_SESSION, S_EXIT, -S_LINEBUFFERED }; +S_LINEBUFFERED, S_MACHINEINTERP }; static struct nv_pair optiontab[] = { { S_EVENT, "-a" }, @@ -97,6 +97,7 @@ static struct nv_pair optiontab[] = { { S_HOSTNAME, "--host" }, { S_INTERP, "-i" }, { S_INTERP, "--interpret" }, + { S_MACHINEINTERP, "-I" }, { S_INFILE, "-if" }, { S_INFILE, "--input" }, { S_IN_LOGS, "--input-logs" }, @@ -173,6 +174,7 @@ static void usage(void) "\t-h,--help\t\t\thelp\n" "\t-hn,--host \t\tsearch based on remote host name\n" "\t-i,--interpret\t\t\tInterpret results to be human readable\n" + "\t-I\t\t\t\tInterpret results to be machine readable\n" "\t-if,--input \tuse this file instead of current logs\n" "\t--input-logs\t\t\tUse the logs even if stdin is a pipe\n" "\t--just-one\t\t\tEmit just one event\n" @@ -474,6 +476,22 @@ int check_params(int count, char *vars[]) retval = -1; } break; + case S_MACHINEINTERP: + if (report_format == RPT_DEFAULT) + report_format = RPT_MACHINEINTERP; + else { + fprintf(stderr, + "Conflicting output format %s\n", + vars[c]); + retval = -1; + } + if (optarg) { + fprintf(stderr, + "Argument is NOT required for %s\n", + vars[c]); + retval = -1; + } + break; case S_INFILE: if (!optarg) { fprintf(stderr, diff --git a/src/ausearch-report.c b/src/ausearch-report.c index 6bb5c1a..e88b2e2 100644 --- a/src/ausearch-report.c +++ b/src/ausearch-report.c @@ -59,6 +59,8 @@ extern char *unescape(char *buf); /* Local functions */ static void output_raw(llist *l); static void output_default(llist *l); +static void output_machineinterp(llist *l); +static void output_machineinterp_node(const lnode *n); static void output_interpreted(llist *l); static void output_interpreted_node(const lnode *n); static void interpret(char *name, char *val, int comma, int rtype); @@ -84,6 +86,9 @@ void output_record(llist *l) break; case RPT_PRETTY: break; + case RPT_MACHINEINTERP: + output_machineinterp(l); + break; default: fprintf(stderr, "Report format error"); exit(1); @@ -131,6 +136,233 @@ static void output_default(llist *l) } /* + * This function will take the linked list and format it for output such that + * the output is both interpreted and yet easily machine-parsable (perl, etc). + */ +static void output_machineinterp(llist *l) +{ + const lnode *n; + + list_last(l); + n = list_get_cur(l); + if (!n) { + fprintf(stderr, "Error - no elements in record."); + return; + } + if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) + output_machineinterp_node(n); + else { + do { + output_machineinterp_node(n); + printf("----\n"); + } while ((n=list_prev(l))); + } +} + +/* + * This function will cycle through a message and lookup each type that + * it finds. + */ +static void output_machineinterp_node(const lnode *n) +{ + char *ptr, *str = n->message, *node = NULL; + + /* Check and see if we start with a node */ + if (str[0] == 'n') { + ptr=strchr(str, ' '); + if (ptr) { + *ptr = 0; + node = str; + str = ptr+1; + } + } + + // First locate time stamp. + ptr = strchr(str, '('); + if (ptr == NULL) { + fprintf(stderr, "can't find time stamp\n"); + return; + } else { + time_t t; + int milli,num = n->type; + unsigned long serial; + char *eptr; + const char *bptr; + + *ptr++ = 0; + if (num == -1) { + // see if we are older and wiser now. + bptr = strchr(str, '['); + if (bptr && bptr < ptr) { + bptr++; + eptr = strchr(bptr, ']'); + if (eptr) { + *eptr = 0; + errno = 0; + num = strtoul(bptr, NULL, 10); + *eptr = ']'; + if (errno) + num = -1; + } + } + } + + // print everything up to it. + if (num >= 0) { + bptr = audit_msg_type_to_name(num); + if (bptr) { + if (node) + printf("node=%s\n", node); + printf("type=%s\n", bptr); + goto no_print; + } + } + if (node) + printf("node=%s\n", node); + eptr = strchr(str, ' '); + if (eptr) + *eptr = 0; + printf("%s\n", str); +no_print: + + str = strchr(ptr, '.'); + if (str == NULL) + return; + *str++ = 0; + errno = 0; + t = strtoul(ptr, NULL, 10); + if (errno) + return; + ptr = strchr(str, ':'); + if (ptr == NULL) + return; + *ptr++ = 0; + milli = strtoul(str, NULL, 10); + if (errno) + return; + str = strchr(ptr, ')'); + if(str == NULL) + return; + *str++ = 0; + serial = strtoul(ptr, NULL, 10); + if (errno) + return; + printf("serial=%lu\n", serial); + printf("seconds=%u\n", t); + printf("milli=%i\n", milli); + str = strchr(str, ':'); + if (str) { + str++; + if (*str == ' ') + str++; + } + } + + if (n->type == AUDIT_SYSCALL) + a0 = n->a0; + + // for each item. + while (str && *str && (ptr = strchr(str, '='))) { + char *name, *val; + int comma = 0; + + // look back to last space - this is name + name = ptr; + while (*name != ' ' && name > str) + --name; + *ptr++ = 0; + if (*name == ' ') + name++; + + // 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) { + // msg='text... (more text)' + // kill the )' on the end + str = strrchr(ptr+1, ')'); + if (str && str[1] == '\'' && str[2] == 0) + *str = 0; + str = ptr+1; + continue; + } + + // handle avc: ... messages + if (strncmp(str, "avc: ", 5) == 0) { + char *seresult, *seperms; + + // since we know there is 'avc: ', skip it + // and all following spaces + seresult = str + 5; + while (*seresult == ' ') + seresult++; + + // find the { seperms } section + seperms = strchr(seresult, '{'); + if (!seperms) + return; + + // go backward from there killing all trailing spaces + str = seperms - 1; + while (*str == ' ') + *str-- = 0; + seperms++; + + // find the end of the seperms + str = strchr(seperms, '}'); + if (!str) + return; + *str-- = 0; + + // kill spaces on both sides of seperms + while (*str == ' ') + str--; + while (*seperms == ' ') + seperms++; + + printf("seresult=%s\n", seresult); + printf("seperms=%s\n", seperms); + + str = name; + } + + while (*str == '(' || *str == ' ') + str++; + + // print everything up to the '=' + printf("%s=", str); + + // 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)) { + *str++ = 0; + comma = 1; + } else if (str && (val == NULL)) { + *str++ = 0; + comma = 1; + } else if (val) { + str = val; + *str++ = 0; + } + } + // val points to begin & str 1 past end + val = ptr; + + // print interpreted string + interpret(name, val, comma, n->type); + printf("\n"); + } +} + +/* * 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. @@ -978,7 +1210,8 @@ static void interpret(char *name, char *val, int comma, int rtype) print_tty_data(val); break; default: - printf("%s%c", val, comma ? ',' : ' '); + printf("%s%c", val, comma && report_format != + RPT_MACHINEINTERP ? ',' : ' '); } }