/* 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; }