From df5afa4fcd9725380f94ca6476248d4cc24f889a Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Sun, 29 Nov 2015 08:22:13 -0800 Subject: v2.4.4 audit sources Change-Id: I9315a7408817db51edf084fb4d27fbb492785084 Signed-off-by: Ashlee Young --- framework/src/audit/auparse/auparse.c | 1377 +++++++++++++++++++++++++++++++++ 1 file changed, 1377 insertions(+) create mode 100644 framework/src/audit/auparse/auparse.c (limited to 'framework/src/audit/auparse/auparse.c') diff --git a/framework/src/audit/auparse/auparse.c b/framework/src/audit/auparse/auparse.c new file mode 100644 index 00000000..cd3f1180 --- /dev/null +++ b/framework/src/audit/auparse/auparse.c @@ -0,0 +1,1377 @@ +/* auparse.c -- + * Copyright 2006-08,2012-15 Red Hat Inc., Durham, North Carolina. + * 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 + */ + +#include "config.h" +#include "expression.h" +#include "internal.h" +#include "auparse.h" +#include "interpret.h" +#include "auparse-idata.h" +#include +#include +#include +#include +#include + +static int debug = 0; + +/* like strchr except string is delimited by length, not null byte */ +static char *strnchr(const char *s, int c, size_t n) +{ + char *p_char; + const char *p_end = s + n; + + for (p_char = (char *)s; p_char < p_end && *p_char != c; p_char++); + if (p_char == p_end) return NULL; + return p_char; +} + +static int setup_log_file_array(auparse_state_t *au) +{ + struct daemon_conf config; + char *filename, **tmp; + int len, num = 0, i = 0; + + /* Load config so we know where logs are */ + set_aumessage_mode(MSG_STDERR, DBG_NO); + load_config(&config, TEST_SEARCH); + + /* for each file */ + len = strlen(config.log_file) + 16; + filename = malloc(len); + if (!filename) { + fprintf(stderr, "No memory\n"); + free_config(&config); + return 1; + } + /* Find oldest log file */ + snprintf(filename, len, "%s", config.log_file); + do { + if (access(filename, R_OK) != 0) + break; + num++; + snprintf(filename, len, "%s.%d", config.log_file, num); + } while (1); + + if (num == 0) { + fprintf(stderr, "No log file\n"); + free_config(&config); + free(filename); + return 1; + } + num--; + tmp = malloc((num+2)*sizeof(char *)); + + /* Got it, now process logs from last to first */ + if (num > 0) + snprintf(filename, len, "%s.%d", config.log_file, num); + else + snprintf(filename, len, "%s", config.log_file); + do { + tmp[i++] = strdup(filename); + + /* Get next log file */ + num--; + if (num > 0) + snprintf(filename, len, "%s.%d", config.log_file, num); + else if (num == 0) + snprintf(filename, len, "%s", config.log_file); + else + break; + } while (1); + free_config(&config); + free(filename); + + // Terminate the list + tmp[i] = NULL; + au->source_list = tmp; + return 0; +} + +/* General functions that affect operation of the library */ +auparse_state_t *auparse_init(ausource_t source, const void *b) +{ + char **tmp, **bb = (char **)b, *buf = (char *)b; + int n, i; + size_t size, len; + + auparse_state_t *au = malloc(sizeof(auparse_state_t)); + if (au == NULL) { + errno = ENOMEM; + return NULL; + } + + au->in = NULL; + au->source_list = NULL; + databuf_init(&au->databuf, 0, 0); + au->callback = NULL; + au->callback_user_data = NULL; + au->callback_user_data_destroy = NULL; + switch (source) + { + case AUSOURCE_LOGS: + if (geteuid()) { + errno = EPERM; + goto bad_exit; + } + setup_log_file_array(au); + break; + case AUSOURCE_FILE: + if (access(b, R_OK)) + goto bad_exit; + tmp = malloc(2*sizeof(char *)); + tmp[0] = strdup(b); + tmp[1] = NULL; + au->source_list = tmp; + break; + case AUSOURCE_FILE_ARRAY: + n = 0; + while (bb[n]) { + if (access(bb[n], R_OK)) + goto bad_exit; + n++; + } + tmp = malloc((n+1)*sizeof(char *)); + for (i=0; isource_list = tmp; + break; + case AUSOURCE_BUFFER: + buf = buf; + len = strlen(buf); + if (databuf_init(&au->databuf, len, + DATABUF_FLAG_PRESERVE_HEAD) < 0) + goto bad_exit; + if (databuf_append(&au->databuf, buf, len) < 0) + goto bad_exit; + break; + case AUSOURCE_BUFFER_ARRAY: + size = 0; + for (n = 0; (buf = bb[n]); n++) { + len = strlen(bb[n]); + if (bb[n][len-1] != '\n') { + size += len + 1; + } else { + size += len; + } + } + if (databuf_init(&au->databuf, size, + DATABUF_FLAG_PRESERVE_HEAD) < 0) + goto bad_exit; + for (n = 0; (buf = bb[n]); n++) { + len = strlen(buf); + if (databuf_append(&au->databuf, buf, len) < 0) + goto bad_exit; + } + break; + case AUSOURCE_DESCRIPTOR: + n = (long)b; + au->in = fdopen(n, "rm"); + break; + case AUSOURCE_FILE_POINTER: + au->in = (FILE *)b; + break; + case AUSOURCE_FEED: + if (databuf_init(&au->databuf, 0, 0) < 0) goto bad_exit; + break; + default: + errno = EINVAL; + goto bad_exit; + break; + } + au->source = source; + au->list_idx = 0; + au->line_number = 0; + au->next_buf = NULL; + au->off = 0; + au->cur_buf = NULL; + au->line_pushed = 0; + aup_list_create(&au->le); + au->parse_state = EVENT_EMPTY; + au->expr = NULL; + au->find_field = NULL; + au->search_where = AUSEARCH_STOP_EVENT; + + return au; +bad_exit: + databuf_free(&au->databuf); + free(au); + return NULL; +} + + +void auparse_add_callback(auparse_state_t *au, auparse_callback_ptr callback, + void *user_data, user_destroy user_destroy_func) +{ + if (au == NULL) { + errno = EINVAL; + return; + } + + if (au->callback_user_data_destroy) { + (*au->callback_user_data_destroy)(au->callback_user_data); + au->callback_user_data = NULL; + } + + au->callback = callback; + au->callback_user_data = user_data; + au->callback_user_data_destroy = user_destroy_func; +} + +static void consume_feed(auparse_state_t *au, int flush) +{ + while (auparse_next_event(au) > 0) { + if (au->callback) { + (*au->callback)(au, AUPARSE_CB_EVENT_READY, + au->callback_user_data); + } + } + if (flush) { + // FIXME: might need a call here to force auparse_next_event() + // to consume any partial data not fully consumed. + if (au->parse_state == EVENT_ACCUMULATING) { + // Emit the event, set event cursors to initial position + aup_list_first(&au->le); + aup_list_first_field(&au->le); + au->parse_state = EVENT_EMITTED; + if (au->callback) { + (*au->callback)(au, AUPARSE_CB_EVENT_READY, + au->callback_user_data); + } + } + } +} + +int auparse_feed(auparse_state_t *au, const char *data, size_t data_len) +{ + if (databuf_append(&au->databuf, data, data_len) < 0) + return -1; + consume_feed(au, 0); + return 0; +} + +int auparse_flush_feed(auparse_state_t *au) +{ + consume_feed(au, 1); + return 0; +} + +// If there is data in the state machine, return 1 +// Otherwise return 0 to indicate its empty +int auparse_feed_has_data(const auparse_state_t *au) +{ + if (au->parse_state == EVENT_ACCUMULATING) + return 1; + return 0; +} + +void auparse_set_escape_mode(auparse_esc_t mode) +{ + set_escape_mode(mode); +} + +int auparse_reset(auparse_state_t *au) +{ + if (au == NULL) { + errno = EINVAL; + return -1; + } + + aup_list_clear(&au->le); + au->parse_state = EVENT_EMPTY; + switch (au->source) + { + case AUSOURCE_LOGS: + case AUSOURCE_FILE: + case AUSOURCE_FILE_ARRAY: + if (au->in) { + fclose(au->in); + au->in = NULL; + } + /* Fall through */ + case AUSOURCE_DESCRIPTOR: + case AUSOURCE_FILE_POINTER: + if (au->in) + rewind(au->in); + /* Fall through */ + case AUSOURCE_BUFFER: + case AUSOURCE_BUFFER_ARRAY: + au->list_idx = 0; + au->line_number = 0; + au->off = 0; + databuf_reset(&au->databuf); + break; + default: + return -1; + } + return 0; +} + + +/* Add EXPR to AU, using HOW to select the combining operator. + On success, return 0. + On error, free EXPR set errno and return -1. + NOTE: EXPR is freed on error! */ +static int add_expr(auparse_state_t *au, struct expr *expr, ausearch_rule_t how) +{ + if (au->expr == NULL) + au->expr = expr; + else if (how == AUSEARCH_RULE_CLEAR) { + expr_free(au->expr); + au->expr = expr; + } else { + struct expr *e; + + e = expr_create_binary(how == AUSEARCH_RULE_OR ? EO_OR : EO_AND, + au->expr, expr); + if (e == NULL) { + int err; + + err = errno; + expr_free(expr); + errno = err; + return -1; + } + au->expr = e; + } + return 0; +} + +static int ausearch_add_item_internal(auparse_state_t *au, const char *field, + const char *op, const char *value, ausearch_rule_t how, unsigned op_eq, + unsigned op_ne) +{ + struct expr *expr; + + // Make sure there's a field + if (field == NULL) + goto err_out; + + // Make sure how is within range + if (how < AUSEARCH_RULE_CLEAR || how > AUSEARCH_RULE_AND) + goto err_out; + + // All pre-checks are done, build a rule + if (strcmp(op, "exists") == 0) + expr = expr_create_field_exists(field); + else { + unsigned t_op; + + if (strcmp(op, "=") == 0) + t_op = op_eq; + else if (strcmp(op, "!=") == 0) + t_op = op_ne; + else + goto err_out; + if (value == NULL) + goto err_out; + expr = expr_create_comparison(field, t_op, value); + } + if (expr == NULL) + return -1; + if (add_expr(au, expr, how) != 0) + return -1; /* expr is freed by add_expr() */ + return 0; + +err_out: + errno = EINVAL; + return -1; +} + +int ausearch_add_item(auparse_state_t *au, const char *field, const char *op, + const char *value, ausearch_rule_t how) +{ + return ausearch_add_item_internal(au, field, op, value, how, EO_RAW_EQ, + EO_RAW_NE); +} + +int ausearch_add_interpreted_item(auparse_state_t *au, const char *field, + const char *op, const char *value, ausearch_rule_t how) +{ + return ausearch_add_item_internal(au, field, op, value, how, + EO_INTERPRETED_EQ, EO_INTERPRETED_NE); +} + +int ausearch_add_timestamp_item_ex(auparse_state_t *au, const char *op, + time_t sec, unsigned milli, unsigned serial, ausearch_rule_t how) +{ + static const struct { + unsigned value; + const char name[3]; + } ts_tab[] = { + {EO_VALUE_LT, "<"}, + {EO_VALUE_LE, "<="}, + {EO_VALUE_GE, ">="}, + {EO_VALUE_GT, ">"}, + {EO_VALUE_EQ, "="}, + }; + + struct expr *expr; + size_t i; + unsigned t_op; + + for (i = 0; i < sizeof(ts_tab) / sizeof(*ts_tab); i++) { + if (strcmp(ts_tab[i].name, op) == 0) + goto found_op; + } + goto err_out; +found_op: + t_op = ts_tab[i].value; + + if (milli >= 1000) + goto err_out; + + // Make sure how is within range + if (how < AUSEARCH_RULE_CLEAR || how > AUSEARCH_RULE_AND) + goto err_out; + + // All pre-checks are done, build a rule + expr = expr_create_timestamp_comparison_ex(t_op, sec, milli, serial); + if (expr == NULL) + return -1; + if (add_expr(au, expr, how) != 0) + return -1; /* expr is freed by add_expr() */ + return 0; + +err_out: + errno = EINVAL; + return -1; +} + +int ausearch_add_timestamp_item(auparse_state_t *au, const char *op, time_t sec, + unsigned milli, ausearch_rule_t how) +{ + return ausearch_add_timestamp_item_ex(au, op, sec, milli, 0, how); +} + +int ausearch_add_expression(auparse_state_t *au, const char *expression, + char **error, ausearch_rule_t how) +{ + struct expr *expr; + + if (how < AUSEARCH_RULE_CLEAR || how > AUSEARCH_RULE_AND) + goto err_einval; + + expr = expr_parse(expression, error); + if (expr == NULL) { + errno = EINVAL; + return -1; + } + + if (add_expr(au, expr, how) != 0) + goto err; /* expr is freed by add_expr() */ + return 0; + +err_einval: + errno = EINVAL; +err: + *error = NULL; + return -1; +} + +int ausearch_add_regex(auparse_state_t *au, const char *regexp) +{ + struct expr *expr; + + // Make sure there's an expression + if (regexp == NULL) + goto err_out; + + expr = expr_create_regexp_expression(regexp); + if (expr == NULL) + return -1; + if (add_expr(au, expr, AUSEARCH_RULE_AND) != 0) + return -1; /* expr is freed by add_expr() */ + return 0; + +err_out: + errno = EINVAL; + return -1; +} + +int ausearch_set_stop(auparse_state_t *au, austop_t where) +{ + if (where < AUSEARCH_STOP_EVENT || where > AUSEARCH_STOP_FIELD) { + errno = EINVAL; + return -1; + } + + au->search_where = where; + return 0; +} + +void ausearch_clear(auparse_state_t *au) +{ + if (au->expr != NULL) { + expr_free(au->expr); + au->expr = NULL; + } + au->search_where = AUSEARCH_STOP_EVENT; +} + +void auparse_destroy(auparse_state_t *au) +{ + aulookup_destroy_uid_list(); + aulookup_destroy_gid_list(); + if (au == NULL) + return; + + if (au->source_list) { + int n = 0; + while (au->source_list[n]) + free(au->source_list[n++]); + free(au->source_list); + au->source_list = NULL; + } + + au->next_buf = NULL; + free(au->cur_buf); + au->cur_buf = NULL; + aup_list_clear(&au->le); + au->parse_state = EVENT_EMPTY; + free(au->find_field); + au->find_field = NULL; + ausearch_clear(au); + databuf_free(&au->databuf); + if (au->callback_user_data_destroy) { + (*au->callback_user_data_destroy)(au->callback_user_data); + au->callback_user_data = NULL; + } + if (au->in) { + fclose(au->in); + au->in = NULL; + } + free(au); +} + +/* alloc a new buffer, cur_buf which contains a null terminated line + * without a newline (note, this implies the line may be empty (strlen == 0)) if + * successfully read a blank line (e.g. containing only a single newline). + * cur_buf will have been newly allocated with malloc. + * + * Note: cur_buf will be freed the next time this routine is called if + * cur_buf is not NULL, callers who retain a reference to the cur_buf + * pointer will need to set cur_buf to NULL to cause the previous cur_buf + * allocation to persist. + * + * Returns: + * 1 if successful (errno == 0) + * 0 if non-blocking input unavailable (errno == 0) + * -1 if error (errno contains non-zero error code) + * -2 if EOF (errno == 0) + */ + +static int readline_file(auparse_state_t *au) +{ + ssize_t rc; + char *p_last_char; + size_t n = 0; + + if (au->cur_buf != NULL) { + free(au->cur_buf); + au->cur_buf = NULL; + } + if (au->in == NULL) { + errno = EBADF; + return -1; + } + if ((rc = getline(&au->cur_buf, &n, au->in)) <= 0) { + // Note: getline always malloc's if lineptr==NULL or n==0, + // on failure malloc'ed memory is left uninitialized, + // caller must free it. + free(au->cur_buf); + au->cur_buf = NULL; + + // Note: feof() does not set errno + if (feof(au->in)) { + // return EOF condition + errno = 0; + return -2; + } + // return error condition, error code in errno + return -1; + } + p_last_char = au->cur_buf + (rc-1); + if (*p_last_char == '\n') { /* nuke newline */ + *p_last_char = 0; + } + // return success + errno = 0; + return 1; +} + + +/* malloc & copy a line into cur_buf from the internal buffer, + * next_buf. cur_buf will contain a null terminated line without a + * newline (note, this implies the line may be empty (strlen == 0)) if + * successfully read a blank line (e.g. containing only a single + * newline). + * + * Note: cur_buf will be freed the next time this routine is called if + * cur_buf is not NULL, callers who retain a reference to the cur_buf + * pointer will need to set cur_buf to NULL to cause the previous cur_buf + * allocation to persist. + * + * Returns: + * 1 if successful (errno == 0) + * 0 if non-blocking input unavailable (errno == 0) + * -1 if error (errno contains non-zero error code) + * -2 if EOF (errno == 0) + */ + +static int readline_buf(auparse_state_t *au) +{ + char *p_newline=NULL; + size_t line_len; + + if (au->cur_buf != NULL) { + free(au->cur_buf); + au->cur_buf = NULL; + } + + //if (debug) databuf_print(&au->databuf, 1, "readline_buf"); + if (au->databuf.len == 0) { + // return EOF condition + errno = 0; + return -2; + } + + if ((p_newline = strnchr(databuf_beg(&au->databuf), '\n', + au->databuf.len)) != NULL) { + line_len = p_newline - databuf_beg(&au->databuf); + + /* dup the line */ + au->cur_buf = malloc(line_len+1); // +1 for null terminator + if (au->cur_buf == NULL) + return -1; // return error condition, errno set + strncpy(au->cur_buf, databuf_beg(&au->databuf), line_len); + au->cur_buf[line_len] = 0; + + if (databuf_advance(&au->databuf, line_len+1) < 0) + return -1; + // return success + errno = 0; + return 1; + + } else { + // return no data available + errno = 0; + return 0; + } +} + +static int str2event(char *s, au_event_t *e) +{ + char *ptr; + + errno = 0; + ptr = strchr(s+10, ':'); + if (ptr) { + e->serial = strtoul(ptr+1, NULL, 10); + *ptr = 0; + if (errno) + return -1; + } else + e->serial = 0; + ptr = strchr(s, '.'); + if (ptr) { + e->milli = strtoul(ptr+1, NULL, 10); + *ptr = 0; + if (errno) + return -1; + } else + e->milli = 0; + e->sec = strtoul(s, NULL, 10); + if (errno) + return -1; + return 0; +} + +/* Returns 0 on success and 1 on error */ +static int extract_timestamp(const char *b, au_event_t *e) +{ + char *ptr, *tmp; + int rc = 1; + + e->host = NULL; + if (*b == 'n') + tmp = strndupa(b, 340); + else + tmp = strndupa(b, 80); + ptr = audit_strsplit(tmp); + if (ptr) { + // Optionally grab the node - may or may not be included + if (*ptr == 'n') { + e->host = strdup(ptr+5); + (void)audit_strsplit(NULL); // Bump along to the next one + } + // at this point we have type= + ptr = audit_strsplit(NULL); + if (ptr) { + if (*(ptr+9) == '(') + ptr+=9; + else + ptr = strchr(ptr, '('); + if (ptr) { + // now we should be pointed at the timestamp + char *eptr; + ptr++; + eptr = strchr(ptr, ')'); + if (eptr) + *eptr = 0; + + if (str2event(ptr, e) == 0) + rc = 0; +// else { +// audit_msg(LOG_ERROR, +// "Error extracting time stamp (%s)\n", +// ptr); +// } + } + // else we have a bad line + } + // else we have a bad line + } + // else we have a bad line + return rc; +} + +static int inline events_are_equal(au_event_t *e1, au_event_t *e2) +{ + // Check time & serial first since its most likely way + // to spot 2 different events + if (!(e1->serial == e2->serial && e1->milli == e2->milli && + e1->sec == e2->sec)) + return 0; + // Hmm...same so far, check if both have a host, only a string + // compare can tell if they are the same. Otherwise, if only one + // of them have a host, they are definitely not the same. Its + // a boundary on daemon config. + if (e1->host && e2->host) { + if (strcmp(e1->host, e2->host)) + return 0; + } else if (e1->host || e2->host) + return 0; + return 1; +} + +/* This function will figure out how to get the next line of input. + * storing it cur_buf. cur_buf will be NULL terminated but will not + * contain a trailing newline. This implies a successful read + * (result == 1) may result in a zero length cur_buf if a blank line + * was read. + * + * cur_buf will have been allocated with malloc. The next time this + * routine is called if cur_buf is non-NULL cur_buf will be freed, + * thus if the caller wishes to retain a reference to malloc'ed + * cur_buf data it should copy the cur_buf pointer and set cur_buf to + * NULL. + * + * Returns: + * 1 if successful (errno == 0) + * 0 if non-blocking input unavailable (errno == 0) + * -1 if error (errno contains non-zero error code) + * -2 if EOF (errno == 0) + */ + +static int retrieve_next_line(auparse_state_t *au) +{ + int rc; + + // If line was pushed back for re-reading return that + if (au->line_pushed) { + // Starting new event, clear previous event data, + // previous line is returned again for new parsing + au->line_pushed = 0; + au->line_number++; + return 1; + } + + switch (au->source) + { + case AUSOURCE_DESCRIPTOR: + case AUSOURCE_FILE_POINTER: + rc = readline_file(au); + if (rc > 0) au->line_number++; + return rc; + case AUSOURCE_LOGS: + case AUSOURCE_FILE: + case AUSOURCE_FILE_ARRAY: + // if the first time through, open file + if (au->list_idx == 0 && au->in == NULL && + au->source_list != NULL) { + if (au->source_list[au->list_idx] == NULL) { + errno = 0; + return -2; + } + au->line_number = 0; + au->in = fopen(au->source_list[au->list_idx], + "rm"); + if (au->in == NULL) + return -1; + __fsetlocking(au->in, FSETLOCKING_BYCALLER); + } + + // loop reading lines from a file + while (au->in) { + if ((rc = readline_file(au)) == -2) { + // end of file, open next file, + // try readline again + fclose(au->in); + au->in = NULL; + au->list_idx++; + au->line_number = 0; + if (au->source_list[au->list_idx]) { + au->in = fopen( + au->source_list[au->list_idx], + "rm"); + if (au->in == NULL) + return -1; + __fsetlocking(au->in, + FSETLOCKING_BYCALLER); + } + } else { + if (rc > 0) + au->line_number++; + return rc; + } + } + return -2; // return EOF + case AUSOURCE_BUFFER: + case AUSOURCE_BUFFER_ARRAY: + rc = readline_buf(au); + if (rc > 0) + au->line_number++; + return rc; + case AUSOURCE_FEED: + rc = readline_buf(au); + // No such thing as EOF for feed, translate EOF + // to data not available + if (rc == -2) + return 0; + else + if (rc > 0) + au->line_number++; + return rc; + default: + return -1; + } + return -1; /* should never reach here */ +} + +static void push_line(auparse_state_t *au) +{ + au->line_number--; + au->line_pushed = 1; +} + +/******* +* Functions that traverse events. +********/ +static int ausearch_reposition_cursors(auparse_state_t *au) +{ + int rc = 0; + + switch (au->search_where) + { + case AUSEARCH_STOP_EVENT: + aup_list_first(&au->le); + aup_list_first_field(&au->le); + break; + case AUSEARCH_STOP_RECORD: + aup_list_first_field(&au->le); + break; + case AUSEARCH_STOP_FIELD: + // do nothing - this is the normal stopping point + break; + default: + rc = -1; + break; + } + return rc; +} + +/* This is called during search once per each record. It walks the list + * of nvpairs and decides if a field matches. */ +static int ausearch_compare(auparse_state_t *au) +{ + rnode *r; + + r = aup_list_get_cur(&au->le); + if (r) + return expr_eval(au, r, au->expr); + + return 0; +} + +// Returns < 0 on error, 0 no data, > 0 success +int ausearch_next_event(auparse_state_t *au) +{ + int rc; + + if (au->expr == NULL) { + errno = EINVAL; + return -1; + } + if ((rc = auparse_first_record(au)) <= 0) + return rc; + do { + do { + if ((rc = ausearch_compare(au)) > 0) { + ausearch_reposition_cursors(au); + return 1; + } else if (rc < 0) + return rc; + } while ((rc = auparse_next_record(au)) > 0); + if (rc < 0) + return rc; + } while ((rc = auparse_next_event(au)) > 0); + if (rc < 0) + return rc; + + return 0; +} + +// Brute force go to next event. Returns < 0 on error, 0 no data, > 0 success +int auparse_next_event(auparse_state_t *au) +{ + int rc; + au_event_t event; + + if (au->parse_state == EVENT_EMITTED) { + // If the last call resulted in emitting event data then + // clear previous event data in preparation to accumulate + // new event data + aup_list_clear(&au->le); + au->parse_state = EVENT_EMPTY; + } + + // accumulate new event data + while (1) { + rc = retrieve_next_line(au); + if (debug) printf("next_line(%d) '%s'\n", rc, au->cur_buf); + if (rc == 0) return 0; // No data now + if (rc == -2) { + // We're at EOF, did we read any data previously? + // If so return data available, else return no data + // available + if (au->parse_state == EVENT_ACCUMULATING) { + if (debug) printf("EOF, EVENT_EMITTED\n"); + au->parse_state = EVENT_EMITTED; + return 1; // data is available + } + return 0; + } + if (rc > 0) { // Input available + rnode *r; + if (extract_timestamp(au->cur_buf, &event)) { + if (debug) + printf("Malformed line:%s\n", + au->cur_buf); + continue; + } + if (au->parse_state == EVENT_EMPTY) { + // First record in new event, initialize event + if (debug) + printf( + "First record in new event, initialize event\n"); + aup_list_set_event(&au->le, &event); + aup_list_append(&au->le, au->cur_buf, + au->list_idx, au->line_number); + au->parse_state = EVENT_ACCUMULATING; + au->cur_buf = NULL; + } else if (events_are_equal(&au->le.e, &event)) { + // Accumulate data into existing event + if (debug) + printf( + "Accumulate data into existing event\n"); + aup_list_append(&au->le, au->cur_buf, + au->list_idx, au->line_number); + au->parse_state = EVENT_ACCUMULATING; + au->cur_buf = NULL; + } else { + // New event, save input for next invocation + if (debug) + printf( + "New event, save current input for next invocation, EVENT_EMITTED\n"); + push_line(au); + // Emit the event, set event cursors to + // initial position + aup_list_first(&au->le); + aup_list_first_field(&au->le); + au->parse_state = EVENT_EMITTED; + free((char *)event.host); + return 1; // data is available + } + free((char *)event.host); + // Check to see if the event can be emitted due to EOE + // or something we know is a single record event. At + // this point, new record should be pointed at 'cur' + if ((r = aup_list_get_cur(&au->le)) == NULL) + continue; + if ( r->type == AUDIT_EOE || + r->type < AUDIT_FIRST_EVENT || + r->type >= AUDIT_FIRST_ANOM_MSG) { + // Emit the event, set event cursors to + // initial position + aup_list_first(&au->le); + aup_list_first_field(&au->le); + au->parse_state = EVENT_EMITTED; + return 1; // data is available + } + } else { // Read error + return -1; + } + } +} + +/* Accessors to event data */ +const au_event_t *auparse_get_timestamp(auparse_state_t *au) +{ + if (au && au->le.e.sec != 0) + return &au->le.e; + else + return NULL; +} + + +time_t auparse_get_time(auparse_state_t *au) +{ + if (au) + return au->le.e.sec; + else + return 0; +} + + +unsigned int auparse_get_milli(auparse_state_t *au) +{ + if (au) + return au->le.e.milli; + else + return 0; +} + + +unsigned long auparse_get_serial(auparse_state_t *au) +{ + if (au) + return au->le.e.serial; + else + return 0; +} + + +// Gets the machine node name +const char *auparse_get_node(auparse_state_t *au) +{ + if (au && au->le.e.host != NULL) + return strdup(au->le.e.host); + else + return NULL; +} + + +int auparse_node_compare(au_event_t *e1, au_event_t *e2) +{ + // If both have a host, only a string compare can tell if they + // are the same. Otherwise, if only one of them have a host, they + // are definitely not the same. Its a boundary on daemon config. + if (e1->host && e2->host) + return strcmp(e1->host, e2->host); + else if (e1->host) + return 1; + else if (e2->host) + return -1; + + return 0; +} + + +int auparse_timestamp_compare(au_event_t *e1, au_event_t *e2) +{ + if (e1->sec > e2->sec) + return 1; + if (e1->sec < e2->sec) + return -1; + + if (e1->milli > e2->milli) + return 1; + if (e1->milli < e2->milli) + return -1; + + if (e1->serial > e2->serial) + return 1; + if (e1->serial < e2->serial) + return -1; + + return 0; +} + +unsigned int auparse_get_num_records(auparse_state_t *au) +{ + return aup_list_get_cnt(&au->le); +} + + +/* Functions that traverse records in the same event */ +int auparse_first_record(auparse_state_t *au) +{ + int rc; + + if (aup_list_get_cnt(&au->le) == 0) { + rc = auparse_next_event(au); + if (rc <= 0) + return rc; + } + aup_list_first(&au->le); + aup_list_first_field(&au->le); + + return 1; +} + + +int auparse_next_record(auparse_state_t *au) +{ + if (aup_list_get_cnt(&au->le) == 0) { + int rc = auparse_first_record(au); + if (rc <= 0) + return rc; + } + if (aup_list_next(&au->le)) + return 1; + else + return 0; +} + + +int auparse_goto_record_num(auparse_state_t *au, unsigned int num) +{ + /* Check if a request is out of range */ + if (num >= aup_list_get_cnt(&au->le)) + return 0; + + if (aup_list_goto_rec(&au->le, num) != NULL) + return 1; + else + return 0; +} + + +/* Accessors to record data */ +int auparse_get_type(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return r->type; + else + return 0; +} + + +const char *auparse_get_type_name(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return audit_msg_type_to_name(r->type); + else + return NULL; +} + + +unsigned int auparse_get_line_number(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return r->line_number; + else + return 0; +} + + +const char *auparse_get_filename(auparse_state_t *au) +{ + switch (au->source) + { + case AUSOURCE_FILE: + case AUSOURCE_FILE_ARRAY: + break; + default: + return NULL; + } + + rnode *r = aup_list_get_cur(&au->le); + if (r) { + if (r->list_idx < 0) return NULL; + return au->source_list[r->list_idx]; + } else { + return NULL; + } +} + + +int auparse_first_field(auparse_state_t *au) +{ + return aup_list_first_field(&au->le); +} + + +int auparse_next_field(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) { + if (nvlist_next(&r->nv)) + return 1; + else + return 0; + } + return 0; +} + + +unsigned int auparse_get_num_fields(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_get_cnt(&r->nv); + else + return 0; +} + +const char *auparse_get_record_text(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return r->record; + else + return NULL; +} + + +/* scan from current location to end of event */ +const char *auparse_find_field(auparse_state_t *au, const char *name) +{ + free(au->find_field); + au->find_field = strdup(name); + + if (au->le.e.sec) { + const char *cur_name; + rnode *r; + + // look at current record before moving + r = aup_list_get_cur(&au->le); + if (r == NULL) + return NULL; + cur_name = nvlist_get_cur_name(&r->nv); + if (cur_name && strcmp(cur_name, name) == 0) + return nvlist_get_cur_val(&r->nv); + + return auparse_find_field_next(au); + } + return NULL; +} + +/* Increment 1 location and then scan for next field */ +const char *auparse_find_field_next(auparse_state_t *au) +{ + if (au->find_field == NULL) { + errno = EINVAL; + return NULL; + } + if (au->le.e.sec) { + int moved = 0; + + rnode *r = aup_list_get_cur(&au->le); + while (r) { // For each record in the event... + if (!moved) { + nvlist_next(&r->nv); + moved=1; + } + if (nvlist_find_name(&r->nv, au->find_field)) + return nvlist_get_cur_val(&r->nv); + r = aup_list_next(&au->le); + if (r) + aup_list_first_field(&au->le); + } + } + return NULL; +} + + +/* Accessors to field data */ +const char *auparse_get_field_name(auparse_state_t *au) +{ + if (au->le.e.sec) { + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_get_cur_name(&r->nv); + } + return NULL; +} + + +const char *auparse_get_field_str(auparse_state_t *au) +{ + if (au->le.e.sec) { + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_get_cur_val(&r->nv); + } + return NULL; +} + +int auparse_get_field_type(auparse_state_t *au) +{ + if (au->le.e.sec) { + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_get_cur_type(r); + } + return AUPARSE_TYPE_UNCLASSIFIED; +} + +int auparse_get_field_int(auparse_state_t *au) +{ + const char *v = auparse_get_field_str(au); + if (v) { + int val; + + errno = 0; + val = strtol(v, NULL, 10); + if (errno == 0) + return val; + } else + errno = ENODATA; + return -1; +} + +const char *auparse_interpret_field(auparse_state_t *au) +{ + if (au->le.e.sec) { + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_interp_cur_val(r); + } + return NULL; +} + -- cgit 1.2.3-korg