/
githubmirror
/
audit-userspace
Обзор
Документация
Войти
/
githubmirror
/
audit-userspace
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v4.2
auparse/auparse.c
2 418 строк
54 KB
Steve Grubb
Fix inconsistent licenses
20 июл 2026, 22:59
20 июл 2026, 22:59
674a75b
Код
Авторство
О чём код?
/* auparse.c -- * Copyright 2006-08,2012-23 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 General Public License * along with this library; 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 <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <stdio_ext.h> #include <limits.h> #include "internal.h" #include "expression.h" #include "auparse.h" #include "interpret.h" #include "auparse-idata.h" #include "libaudit.h" #include "common.h" //#define LOL_EVENTS_DEBUG01 1 // add debug for list of list event // processing #ifdef LOL_EVENTS_DEBUG01 static int debug = 0; #endif static time_t eoe_timeout = EOE_TIMEOUT; static int au_auparse_next_event(auparse_state_t *au, int *malformed); /* 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 access_ok(const char *filename) { int rc = access(filename, R_OK); if (rc == 0) return rc; #ifdef HAVE_FACCESSAT // If we have faccessat, let's try effective ids. return faccessat(AT_FDCWD, filename, R_OK, AT_EACCESS); #else // If we don't, return what we have from access() return rc; #endif } /* * free_source_list - release a NULL-terminated source file list * Args: * list - array of allocated file names * Rtns: * void */ static void free_source_list(char **list) { int n = 0; if (list == NULL) return; while (list[n]) free(list[n++]); free(list); } 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 */ if (secure_getenv("AUPARSE_DEBUG")) set_aumessage_mode(au, MSG_STDERR, DBG_NO); aup_load_config(au, &config, TEST_SEARCH); /* for each file */ len = strlen(config.log_file) + 16; filename = malloc(len); if (!filename) { fprintf(stderr, "No memory\n"); aup_free_config(&config); return 1; } /* Find oldest log file */ snprintf(filename, len, "%s", config.log_file); do { if (access_ok(filename) != 0) break; num++; snprintf(filename, len, "%s.%d", config.log_file, num); } while (1); if (num == 0) { fprintf(stderr, "No log file\n"); aup_free_config(&config); free(filename); return 1; } num--; tmp = malloc((num+2)*sizeof(char *)); if (!tmp) { fprintf(stderr, "Out of memory. Check %s file, %d line", __FILE__, __LINE__); aup_free_config(&config); free(filename); return 1; } /* 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); aup_free_config(&config); free(filename); // Terminate the list tmp[i] = NULL; au->source_list = tmp; return 0; } /* * au_lol_create - Create and initialise the base List of List event structure * Args: * lol - pointer to memory holding structure (eg the static au_lo variable) * Rtns: * NULL - no memory * ptr - pointer to array of event nodes (au_lolnode) */ static au_lolnode *au_lol_create(au_lol *lol) { int sz = ARRAY_LIMIT * sizeof(au_lolnode); lol->maxi = -1; if ((lol->array = (au_lolnode *)malloc(sz)) == NULL) return NULL; lol->limit = ARRAY_LIMIT; memset(lol->array, 0x00, sz); return lol->array; } /* * au_lol_clear - Free or rest the base List of List event structure * * Args: * lol - pointer to memory holding structure (eg the static au_lo variable) * reset - flag to indicate a reset of the structure, or the complete * freeing of memory * Rtns: * void */ static void au_lol_clear(au_lol *lol, int reset) { int i; if (lol->array) { for (i = 0; i <= lol->maxi; i++) { if (lol->array[i].l) { aup_list_clear(lol->array[i].l); free(lol->array[i].l); } } } if (reset) { /* If resetting, we just zero fields */ if (lol->array) memset(lol->array, 0x00, lol->limit * sizeof(au_lolnode)); lol->maxi = -1; } else { /* If not resetting, we free everything */ if (lol->array) free(lol->array); lol->array = NULL; lol->maxi = -1; } } /* * au_lol_append - Add a new event to our base List of List structure * * Args: * lol - pointer to memory holding structure (eg the static au_lo variable) * l - event list structure (which contains an event's constituent records) * Rtns: * ptr - pointer to au_lolnode which holds the event list structure * NULL - failed to reallocate memory */ static au_lolnode *au_lol_append(au_lol *lol, event_list_t *l) { int i; size_t new_size; au_lolnode *ptr; for (i = 0; i < lol->limit; i++) { au_lolnode *cur = &lol->array[i]; if (cur->status == EBS_EMPTY) { cur->l = l; cur->status = EBS_BUILDING; if (i > lol->maxi) lol->maxi = i; return cur; } } /* Over ran the array, make it bigger */ new_size = sizeof(au_lolnode) * (lol->limit + ARRAY_LIMIT); ptr = realloc(lol->array, new_size); if (ptr) { lol->array = ptr; memset(&lol->array[lol->limit], 0x00, sizeof(au_lolnode) * ARRAY_LIMIT); lol->array[i].l = l; lol->array[i].status = EBS_BUILDING; lol->maxi = i; lol->limit += ARRAY_LIMIT; } return ptr; } /* * au_get_ready_event - Find the next COMPLETE event in our list and mark EMPTY * * Args: * lol - pointer to memory holding structure (eg the static au_lo variable) * is_test - do not mark the node EMPTY * Rtns: * ptr - pointer to complete node (possibly just marked empty) * NULL - no complete nodes exist */ static event_list_t *au_get_ready_event(auparse_state_t *au, int is_test) { int i; au_lol *lol = au->au_lo; au_lolnode *lowest = NULL; if (au->au_ready == 0) { //if (debug) printf("No events ready\n"); return NULL; } for (i=0; i<=lol->maxi; i++) { // Look for the event with the lowest timestamp au_lolnode *cur = &(lol->array[i]); if (cur->status == EBS_EMPTY) continue; // If we are just testing for a complete event, return if (is_test && cur->status == EBS_COMPLETE) return cur->l; if (lowest == NULL) lowest = cur; else if (auparse_timestamp_compare(&(lowest->l->e), &(cur->l->e)) == 1) lowest = cur; } if (lowest && lowest->status == EBS_COMPLETE) { lowest->status = EBS_EMPTY; au->au_ready--; return lowest->l; } return NULL; } /* * au_check_events - Run though all events marking those we can mark COMPLETE * * Args: * lol - pointer to memory holding structure (eg the static au_lo variable) * sec - time of current event from stream being processed. We use this to see * how old the events are we have in our list * Rtns: * void */ static void au_check_events(auparse_state_t *au, time_t sec) { rnode *r; int i; au_lol *lol = au->au_lo; for(i=0; i<=lol->maxi; i++) { au_lolnode *cur = &lol->array[i]; if (cur->status == EBS_BUILDING) { if ((r = aup_list_get_cur(cur->l)) == NULL) continue; // If eoe_timeout seconds have elapsed, we are done if (cur->l->e.sec + eoe_timeout <= sec) { cur->status = EBS_COMPLETE; au->au_ready++; } else if (audit_is_last_record(r->type)) { // If known to be 1 record event, we are done cur->status = EBS_COMPLETE; au->au_ready++; } } } } /* * au_terminate_all_events - Mark all events in 'BUILD' state to be COMPLETE * * Args: * lol - pointer to memory holding structure (eg the static au_lo variable) * Rtns: * void */ static void au_terminate_all_events(auparse_state_t *au) { int i; au_lol *lol = au->au_lo; for (i=0; i<=lol->maxi; i++) { au_lolnode *cur = &lol->array[i]; if (cur->status == EBS_BUILDING) { cur->status = EBS_COMPLETE; au->au_ready++; //if (debug) printf("%d events complete\n", au->au_ready); } } } #ifdef LOL_EVENTS_DEBUG01 /* * print_list_t - Print summary of event's records * Args: * l - event_list to print * Rtns: * void */ void print_list_t(event_list_t *l) { rnode *r; if (l == NULL) { printf("\n"); return; } printf("0x%p: %lld.%3.3u:%lu %s", l, (long long int)l->e.sec, l->e.milli, l->e.serial, l->e.host ? l->e.host : ""); printf(" cnt=%u", l->cnt); for (r = l->head; r != NULL; r = r->next) { printf(" {%d %d %u}", r->type, r->list_idx, r->line_number); } printf("\n"); } /* * lol_status - return type of event state as a character * Args: * s - event state * Rtns: * char - E, B or C for EMPTY, BUILDING or COMPLETE, or '*' for unknown */ static char lol_status(au_lol_t s) { switch(s) { case EBS_EMPTY: return 'E'; break; case EBS_BUILDING: return 'B'; break; case EBS_COMPLETE: return 'C'; break; } return '*'; } /* * print_lol - Print a list of list events and their records * Args: * label - String to act as label when printing * lol - pointer to memory holding structure (eg the static au_lo variable) * Rtns: * void */ void print_lol(char *label, au_lol *lol) { int i; printf("%s 0x%p: a: 0x%p, %d, %d\n", label, lol, lol->array, lol->maxi, lol->limit); if (debug > 1) for (i = 0; i <= lol->maxi; i++) { printf("{%2d 0x%p %c } ", i, (&lol->array[i]), lol_status(lol->array[i].status)); print_list_t(lol->array[i].l); } if (lol->maxi >= 0) printf("\n"); } #endif /* LOL_EVENTS_DEBUG01 */ /* General functions that affect operation of the library */ /* * au_setup_userspace_configitems - load userspace configuration items from auditd.conf * * Args: * au - pointer to auparseing state structure * * Rtns: * void */ static void au_setup_userspace_configitems(auparse_state_t *au) { struct daemon_conf config; /* Load config so we know where logs are */ if (secure_getenv("AUPARSE_DEBUG")) set_aumessage_mode(au, MSG_STDERR, DBG_NO); aup_load_config(au, &config, TEST_SEARCH); eoe_timeout = (time_t)config.end_of_event_timeout; aup_free_config(&config); } 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->le = NULL; /* * Set up the List of List events base structure */ au->au_lo = calloc(sizeof(au_lol), 1); if (au->au_lo == NULL) { free(au); errno = ENOMEM; return NULL; } au_lol_clear(au->au_lo, 0); // python doesn't call auparse_destroy if (au_lol_create(au->au_lo) == NULL) { free(au->au_lo); free(au); errno = ENOMEM; return NULL; } au->au_ready = 0; au->escape_mode = AUPARSE_ESC_TTY; au->message_mode = MSG_QUIET; au->debug_message = DBG_NO; 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; au_setup_userspace_configitems(au); switch (source) { case AUSOURCE_LOGS: if (setup_log_file_array(au)) goto bad_exit; break; case AUSOURCE_FILE: if (b == NULL) goto bad_exit; if (access_ok(b)) goto bad_exit; tmp = calloc(2, sizeof(char *)); if (tmp == NULL) goto bad_exit; tmp[0] = strdup(b); if (tmp[0] == NULL) { free_source_list(tmp); goto bad_exit; } au->source_list = tmp; break; case AUSOURCE_FILE_ARRAY: if (bb == NULL) goto bad_exit; n = 0; while (bb[n]) { if (access_ok(bb[n])) goto bad_exit; n++; } tmp = calloc(n+1, sizeof(char *)); if (tmp == NULL) goto bad_exit; for (i=0; i<n; i++) { tmp[i] = strdup(bb[i]); if (tmp[i] == NULL) { free_source_list(tmp); goto bad_exit; } } au->source_list = tmp; break; case AUSOURCE_BUFFER: if (buf == NULL) goto bad_exit; 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: if (bb == NULL) goto bad_exit; size = 0; for (n = 0; bb[n]; n++) { buf = bb[n]; len = strlen(buf); if (len == 0 || buf[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; /* Array entries need only be NUL terminated. Keep boundaries * when concatenating them. */ if ((len == 0 || buf[len - 1] != '\n') && databuf_append(&au->databuf, "\n", 1) < 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; au->parse_state = EVENT_EMPTY; au->expr = NULL; au->find_field = NULL; au->search_where = AUSEARCH_STOP_EVENT; au->tmp_translation = NULL; au->uid_cache = NULL; au->gid_cache = NULL; init_interpretation_list(au); init_normalizer(&au->norm_data); return au; bad_exit: free_source_list(au->source_list); databuf_free(&au->databuf); /* Free list of events list (au_lo) structure */ au_lol_clear(au->au_lo, 0); free(au->au_lo); 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; } /* validate_feed - check whether @au is ready to consume feed data * @au: parser state machine to validate * * Return: 0 on success, -1 on failure with errno set. */ static int validate_feed(const auparse_state_t *au) { if (au == NULL || au->source != AUSOURCE_FEED || au->callback == NULL) { errno = EINVAL; return -1; } return 0; } /* dispatch_feed_callback - call the current feed callback if registered * @au: parser state machine * * auparse_add_callback() may be called from inside a callback. Look up the * callback for each delivery so clearing it during feed processing only skips * later events instead of dereferencing a NULL function pointer. * * Return: none. */ static void dispatch_feed_callback(auparse_state_t *au) { auparse_callback_ptr callback = au->callback; void *user_data = au->callback_user_data; if (callback == NULL) return; (*callback)(au, AUPARSE_CB_EVENT_READY, user_data); } /* consume_feed - process buffered feed data and optionally flush events * @au: parser state machine * @flush: non-zero to terminate and deliver outstanding events * * Return: 0 on success, -1 on failure with errno set. */ static int consume_feed(auparse_state_t *au, int flush) { int rc, malformed, saved_errno = 0; //if (debug) printf("consume feed, flush %d\n", flush); do { /* * This calls the lower-level parser so malformed feed * records can be reported while preserving auparse_next_event() * normalizer state handling. */ malformed = 0; clear_normalizer(&au->norm_data); rc = au_auparse_next_event(au, &malformed); if (malformed && saved_errno == 0) saved_errno = EBADMSG; if (rc > 0) dispatch_feed_callback(au); } while (rc > 0); if (rc < 0) { if (errno == 0) errno = EIO; return -1; } if (flush) { /* Terminate all outstanding events, as we are at end of input * (ie mark BUILDING events as COMPLETE events) then execute * the callback on each event */ event_list_t *l; //if (debug) printf("terminate all events in flush\n"); au_terminate_all_events(au); while ((l = au_get_ready_event(au, 0)) != NULL) { rnode *r; au->le = l; // make this current the event of interest aup_list_first(l); r = aup_list_get_cur(l); free_interpretation_list(au); load_interpretation_list(au, r->interp); aup_list_first_field(l); dispatch_feed_callback(au); } } if (saved_errno) { errno = saved_errno; return -1; } return 0; } int auparse_new_buffer(auparse_state_t *au, const char *data, size_t data_len) { if (au->source != AUSOURCE_BUFFER) return 1; auparse_reset(au); if (databuf_replace(&au->databuf, data, data_len) < 0) return 1; return 0; } /* auparse_feed - append data to the feed parser and consume what is ready * @au: parser state machine * @data: feed data to append * @data_len: length of @data * * Return: 0 on success, -1 on failure with errno set. */ int auparse_feed(auparse_state_t *au, const char *data, size_t data_len) { if (data == NULL && data_len != 0) { errno = EINVAL; return -1; } if (validate_feed(au) < 0) return -1; if (databuf_append(&au->databuf, data, data_len) < 0) { if (errno == 0) errno = ENOMEM; return -1; } return consume_feed(au, 0); } /* auparse_flush_feed - flush pending feed data through the parser * @au: parser state machine * * Return: 0 on success, -1 on failure with errno set. */ int auparse_flush_feed(auparse_state_t *au) { if (validate_feed(au) < 0) return -1; return consume_feed(au, 1); } // If there is any 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) return 0; int i; au_lol *lol = au->au_lo; // An improvement would be to track how many events we have stored // to avoid a costly loop for (i=0; i <= lol->maxi; i++) { au_lolnode *cur = &(lol->array[i]); if (cur->status > EBS_EMPTY) return 1; } return 0; } // If there is a ready event in the state machine, return 1. // Otherwise return 0 to indicate its empty int auparse_feed_has_ready_event(auparse_state_t *au) { if (au_get_ready_event(au, 1) != NULL) return 1; return 0; } /* auparse_feed_age_events - complete feed events aged out by the clock * @au: parser state machine * * Return: none. */ void auparse_feed_age_events(auparse_state_t *au) { time_t t; if (validate_feed(au) < 0) return; t = time(NULL); au_check_events(au, t); (void)consume_feed(au, 0); } void auparse_set_escape_mode(auparse_state_t *au, auparse_esc_t mode) { if (au == NULL) return; au->escape_mode = mode; } /* * Non-public function. Subject to change. * buf is a string of name value pairs to be used for interpreting. * Calling this function automatically releases the previous list. */ void _auparse_load_interpretations(auparse_state_t *au, const char *buf) { free_interpretation_list(au); if (buf == NULL) return; load_interpretation_list(au, buf); } /* * Non-public function. Subject to change. */ void _auparse_free_interpretations(auparse_state_t *au) { free_interpretation_list(au); } int auparse_reset(auparse_state_t *au) { if (au == NULL) { errno = EINVAL; return -1; } /* Create or Free list of events list (au_lo) structure */ if (au->au_lo->array == NULL) au_lol_create(au->au_lo); else au_lol_clear(au->au_lo, 1); au->parse_state = EVENT_EMPTY; au->au_ready = 0; au->le = NULL; 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; } free_interpretation_list((auparse_state_t *)au); return 0; } char *auparse_metrics(const auparse_state_t *au) { char *metrics; unsigned int uid, gid; aulookup_metrics(au, &uid, &gid); if (asprintf(&metrics, "max lol available: %lu\n" "max lol used: %d\n" "pending lol: %d\n" "uid cache size: %u\n" "gid cache size: %u", au->au_lo->limit, au->au_lo->maxi, au->au_ready, uid, gid) < 0) metrics = NULL; return metrics; } /* 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; } au->expr->started = 0; 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; } static void auparse_destroy_common(auparse_state_t *au) { if (au == NULL) return; free_source_list(au->source_list); au->source_list = NULL; au->next_buf = NULL; free(au->cur_buf); au->cur_buf = NULL; au->le = NULL; 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_interpretation_list(au); clear_normalizer(&au->norm_data); au_lol_clear(au->au_lo, 0); free((void *)au->tmp_translation); free(au->au_lo); free(au); } void auparse_destroy(auparse_state_t *au) { if (au == NULL) return; _aulookup_destroy_uid_list(au); aulookup_destroy_gid_list(au); auparse_destroy_common(au); } void auparse_destroy_ext(auparse_state_t *au, auparse_destroy_what_t what) { if (what == AUPARSE_DESTROY_COMMON) auparse_destroy_common(au); else if (what == AUPARSE_DESTROY_ALL) auparse_destroy(au); return; } /* 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; unsigned long sec; errno = 0; sec = strtoul(s, NULL, 10); // Check the unsigned value before converting it to signed time_t. if (errno || eoe_timeout < 0 || eoe_timeout >= LONG_MAX || sec > (unsigned long)LONG_MAX - (unsigned long)eoe_timeout - 1) return -1; e->sec = (time_t)sec; ptr = strchr(s, '.'); if (ptr) { ptr++; e->milli = strtoul(ptr, NULL, 10); if (errno || e->milli > 999) return -1; s = ptr; } else e->milli = 0; ptr = strchr(s, ':'); if (ptr) { ptr++; e->serial = strtoul(ptr, NULL, 10); if (errno) return -1; } else e->serial = 0; 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' && strnlen(ptr, 8) > 5) { e->host = strdup(ptr+5); (void)audit_strsplit(NULL);// Bump along to next one } // at this point we have type= ptr = audit_strsplit(NULL); // strlen is for fuzzers that make invalid lines if (ptr && strnlen(ptr, 20) > 18) { 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 we have a bad line } // else we have a bad line } if (rc) free((void *)e->host); // else we have a bad line return rc; } static int events_are_equal(const au_event_t *e1, const 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 */ } /******* * Functions that traverse events. ********/ static int ausearch_reposition_cursors(const 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; if (au->le == NULL) return 0; r = aup_list_get_cur(au->le); if (r) { int res = expr_eval(au, r, au->expr); return res; } return 0; } // Returns < 0 on error, 0 no data, > 0 success int ausearch_cur_event(auparse_state_t* au) { int rc, records; if (au->expr == NULL) { errno = EINVAL; return -1; } records = auparse_get_num_records(au); for (int i = 0; i < records; i++) { if (auparse_goto_record_num(au, i) != 1) return -1; if ((rc = ausearch_compare(au)) > 0) { ausearch_reposition_cursors(au); return 1; } else if (rc < 0) return rc; } 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 (au->expr->started == 0) { if ((rc = auparse_first_record(au)) <= 0) return rc; au->expr->started = 1; } else { if ((rc = auparse_next_event(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; } /* au_auparse_next_event - get the next complete event * @au: parser state machine * @malformed: optional flag set when a malformed record is skipped * * Return: less than 0 on error, 0 if no data is available, or greater * than 0 if an event is available and set as the current event. */ static int au_auparse_next_event(auparse_state_t *au, int *malformed) { int rc, i, built; event_list_t *l; au_event_t e; if (au == NULL || au->au_lo == NULL) { errno = EINVAL; return -1; } /* * Deal with Python memory management issues where it issues a * auparse_destroy() call after an auparse_init() call but then wants * to still work with auparse data. Basically, we assume if the user * wants to parse for events (calling auparse_next_event()) we accept * that they expect the memory structures to exist. This is a bit * 'disconcerting' but the au_lol capability is a patch trying to * redress a singleton approach to event processing. */ if (au->au_lo->array == NULL && au->au_lo->maxi == -1) { #ifdef LOL_EVENTS_DEBUG01 if (debug) printf("Creating lol array\n"); #endif /* LOL_EVENTS_DEBUG01 */ if (au_lol_create(au->au_lo) == NULL) return -1; } if (au->au_lo->array == NULL) return -1; /* * First see if we have any empty events but with an allocated event * list. These would have just been processed, so we can free them */ for (i = 0; i <= au->au_lo->maxi; i++) { au_lolnode *cur = &au->au_lo->array[i]; if (cur->status == EBS_EMPTY && cur->l) { #ifdef LOL_EVENTS_DEBUG01 if (debug) { printf("Freeing at start "); print_list_t(cur->l); } #endif /* LOL_EVENTS_DEBUG01 */ aup_list_clear(cur->l); free(cur->l); au->le = NULL; // this should crash any usage // of au->le until reset cur->l = NULL; } } /* * Now see if we have completed events queued, and if so grab the * first one and set it to be the 'current' event of interest */ if ((l = au_get_ready_event(au, 0)) != NULL) { rnode *r; aup_list_first(l); r = aup_list_get_cur(l); free_interpretation_list(au); load_interpretation_list(au, r->interp); aup_list_first_field(l); au->le = l; #ifdef LOL_EVENTS_DEBUG01 if (debug) print_lol("upfront", au->au_lo); #endif /* LOL_EVENTS_DEBUG01 */ return 1; } /* * If no complete events are available, lets ingest */ while (1) { for (i = 0; i <= au->au_lo->maxi; i++) { au_lolnode *cur = &au->au_lo->array[i]; if (cur->status == EBS_EMPTY && cur->l) { #ifdef LOL_EVENTS_DEBUG01 if (debug) { printf("Freeing at loop"); print_list_t(cur->l); } #endif /* LOL_EVENTS_DEBUG01 */ aup_list_clear(cur->l); free(cur->l); au->le = NULL; /* this should crash any usage of au->le until reset */ cur->l = NULL; } } rc = retrieve_next_line(au); #ifdef LOL_EVENTS_DEBUG01 if (debug) printf("next_line(%d) '%s'\n", rc, au->cur_buf); #endif /* LOL_EVENTS_DEBUG01 */ if (rc == 0) { #ifdef LOL_EVENTS_DEBUG01 if (debug) printf("Empty line\n"); #endif /* LOL_EVENTS_DEBUG01 */ return 0; /* NO data now */ } if (rc == -2) { /* * We are at EOF, so see if we have any accumulated * events. */ #ifdef LOL_EVENTS_DEBUG01 if (debug) printf("EOF\n"); #endif /* LOL_EVENTS_DEBUG01 */ au_terminate_all_events(au); if ((l = au_get_ready_event(au, 0)) != NULL) { rnode *r; aup_list_first(l); r = aup_list_get_cur(l); free_interpretation_list(au); load_interpretation_list(au, r->interp); aup_list_first_field(l); au->le = l; #ifdef LOL_EVENTS_DEBUG01 if (debug) print_lol("eof termination", au->au_lo); #endif /* LOL_EVENTS_DEBUG01 */ return 1; } return 0; } else if (rc < 0) { #ifdef LOL_EVENTS_DEBUG01 /* Straight error */ if (debug) printf("Error %d\n", rc); #endif /* LOL_EVENTS_DEBUG01 */ return -1; } /* So we got a successful read ie rc > 0 */ if (extract_timestamp(au->cur_buf, &e)) { #ifdef LOL_EVENTS_DEBUG01 if (debug) printf("Malformed line:%s\n", au->cur_buf); #endif /* LOL_EVENTS_DEBUG01 */ if (malformed) *malformed = 1; continue; } /* * Is this an event we have already been building? */ built = 0; for (i = 0; i <= au->au_lo->maxi; i++) { au_lolnode *cur = &au->au_lo->array[i]; if (cur->status == EBS_BUILDING) { if (events_are_equal(&cur->l->e, &e)) { #ifdef LOL_EVENTS_DEBUG01 if (debug) printf("Adding event to building event\n"); #endif /* LOL_EVENTS_DEBUG01 */ if (aup_list_append(cur->l, au->cur_buf, au->list_idx, au->line_number) < 0) { au->cur_buf = NULL; continue; } au->cur_buf = NULL; free((char *)e.host); au_check_events(au, e.sec); #ifdef LOL_EVENTS_DEBUG01 if (debug) print_lol("building",au->au_lo); #endif /* LOL_EVENTS_DEBUG01 */ /* we built something, so break out */ built++; break; } } } if (built) continue; /* So create one */ #ifdef LOL_EVENTS_DEBUG01 if (debug) printf("First record in new event, initialize event\n"); #endif /* LOL_EVENTS_DEBUG01 */ if ((l=(event_list_t *)malloc(sizeof(event_list_t))) == NULL) { free((char *)e.host); return -1; } aup_list_create(l); aup_list_set_event(l, &e); if (aup_list_append(l, au->cur_buf, au->list_idx, au->line_number) < 0) { au->cur_buf = NULL; aup_list_clear(l); free(l); continue; } // Eat standalone EOE - main event was already marked complete if (l->head->type == AUDIT_EOE) { au->cur_buf = NULL; aup_list_clear(l); free(l); continue; } if (au_lol_append(au->au_lo, l) == NULL) { free((char *)e.host); au->cur_buf = NULL; aup_list_clear(l); free(l); #ifdef LOL_EVENTS_DEBUG01 if (debug) printf("error appending to lol\n"); #endif /* LOL_EVENTS_DEBUG01 */ return -1; } au->cur_buf = NULL; free((char *)e.host); au_check_events(au, e.sec); if ((l = au_get_ready_event(au, 0)) != NULL) { rnode *r; aup_list_first(l); r = aup_list_get_cur(l); free_interpretation_list(au); load_interpretation_list(au, r->interp); aup_list_first_field(l); au->le = l; #ifdef LOL_EVENTS_DEBUG01 if (debug) print_lol("basic", au->au_lo); #endif /* LOL_EVENTS_DEBUG01 */ return 1; } } } // Brute force go to next event. Returns < 0 on error, 0 no data, > 0 success int auparse_next_event(auparse_state_t *au) { clear_normalizer(&au->norm_data); return au_auparse_next_event(au, NULL); } /* Accessors to event data */ const au_event_t *auparse_get_timestamp(const auparse_state_t *au) { if (au && au->le && au->le->e.sec != 0) return &au->le->e; else return NULL; } time_t auparse_get_time(const auparse_state_t *au) { if (au && au->le) return au->le->e.sec; else return 0; } unsigned int auparse_get_milli(const auparse_state_t *au) { if (au && au->le) return au->le->e.milli; else return 0; } unsigned long auparse_get_serial(const auparse_state_t *au) { if (au && au->le) return au->le->e.serial; else return 0; } // Gets the machine node name const char *auparse_get_node(const auparse_state_t *au) { if (au && au->le && au->le->e.host != NULL) return strdup(au->le->e.host); else return NULL; } int auparse_node_compare(const au_event_t *e1, const 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(const au_event_t *e1, const 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(const auparse_state_t *au) { // Its OK if au->le == NULL because get_cnt handles it return aup_list_get_cnt(au->le); } unsigned int auparse_get_record_num(const auparse_state_t *au) { if (au->le == NULL) return 0; rnode *r = aup_list_get_cur(au->le); if (r) return r->item; return 0; } /* Functions that traverse records in the same event */ int auparse_first_record(auparse_state_t *au) { int rc; rnode *r; // Its OK if au->le == NULL because get_cnt handles it if (aup_list_get_cnt(au->le) == 0) { // This function loads interpretations rc = auparse_next_event(au); if (rc <= 0) return rc; } r = aup_list_get_cur(au->le); if (r && r->item == 0 && interpretation_list_cnt(au)) { // If we are on the first record and the list has previously // been loaded, just pull cursor back and avoid loading the // interpretation list. aup_list_first_field(au->le); return 1; } aup_list_first(au->le); r = aup_list_get_cur(au->le); free_interpretation_list(au); load_interpretation_list(au, r->interp); aup_list_first_field(au->le); return 1; } /* * Returns: -1 if an error occurs, * 0 if no more records in current event, * 1 for success. */ int auparse_next_record(auparse_state_t *au) { rnode *r; free_interpretation_list(au); // Its OK if au->le == NULL because get_cnt handles it if (aup_list_get_cnt(au->le) == 0) { int rc = auparse_first_record(au); if (rc <= 0) return rc; } r = aup_list_next(au->le); if (r) { load_interpretation_list(au, r->interp); return 1; } else return 0; } int auparse_goto_record_num(auparse_state_t *au, unsigned int num) { rnode *r; r = aup_list_get_cur(au->le); if (r && r->item == num && interpretation_list_cnt(au)) { // If we are on the first record and the list has previously // been loaded, just pull cursor back and avoid loading the // interpretation list. aup_list_first_field(au->le); return 1; } /* Check if a request is out of range */ free_interpretation_list(au); // Its OK if au->le == NULL because get_cnt handles it if (num >= aup_list_get_cnt(au->le)) return 0; r = aup_list_goto_rec(au->le, num); if (r != NULL) { load_interpretation_list(au, r->interp); aup_list_first_field(au->le); return 1; } else return 0; } /* Accessors to record data */ int auparse_get_type(const auparse_state_t *au) { if (au->le == NULL) return 0; rnode *r = aup_list_get_cur(au->le); if (r) return r->type; else return 0; } const char *auparse_get_type_name(const auparse_state_t *au) { if (au->le == NULL) return NULL; 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(const auparse_state_t *au) { if (au->le == NULL) return 0; rnode *r = aup_list_get_cur(au->le); if (r) return r->line_number; else return 0; } const char *auparse_get_filename(const auparse_state_t *au) { switch (au->source) { case AUSOURCE_FILE: case AUSOURCE_FILE_ARRAY: break; default: return NULL; } if (au->le == NULL) 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(const auparse_state_t *au) { if (au->le == NULL) return 0; return aup_list_first_field(au->le); } int auparse_next_field(const auparse_state_t *au) { if (au->le == NULL) return 0; 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(const auparse_state_t *au) { if (au->le == NULL) return 0; 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(const auparse_state_t *au) { if (au->le == NULL) return NULL; rnode *r = aup_list_get_cur(au->le); if (r) return r->record; else return NULL; } const char *auparse_get_record_interpretations(const auparse_state_t *au) { if (au->le == NULL) return NULL; rnode *r = aup_list_get_cur(au->le); if (r) return r->interp; else return NULL; } /* scan from current location to end of event */ const char *auparse_find_field(auparse_state_t *au, const char *name) { if (au->le == NULL) return NULL; 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->le == NULL) return NULL; 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) { if (nvlist_next(&r->nv) == NULL) return NULL; 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); free_interpretation_list(au); load_interpretation_list(au, r->interp); } } } return NULL; } /* Accessors to field data */ unsigned int auparse_get_field_num(const auparse_state_t *au) { if (au->le == NULL) return 0; rnode *r = aup_list_get_cur(au->le); if (r) { nvnode *n = nvlist_get_cur(&r->nv); if (n) return n->item; } return 0; } int auparse_goto_field_num(const auparse_state_t *au, unsigned int num) { if (au->le == NULL) return 0; rnode *r = aup_list_get_cur(au->le); if (r) { if (num >= r->nv.cnt) return 0; if ((nvlist_goto_rec(&r->nv, num))) return 1; } return 0; } const char *auparse_get_field_name(const auparse_state_t *au) { if (au->le == NULL) return NULL; 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(const auparse_state_t *au) { if (au->le == NULL) return NULL; 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(const auparse_state_t *au) { if (au->le == NULL) return AUPARSE_TYPE_UNCLASSIFIED; 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(const 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 == NULL) return NULL; if (au->le->e.sec) { rnode *r = aup_list_get_cur(au->le); if (r) { r->cwd = NULL; return nvlist_interp_cur_val(au, r); } } return NULL; } const char *auparse_interpret_realpath(const auparse_state_t *au) { if (au->le == NULL) return NULL; if (au->le->e.sec) { rnode *r = aup_list_get_cur(au->le); if (r) { if (nvlist_get_cur_type(r) != AUPARSE_TYPE_ESCAPED_FILE) return NULL; // Tell it to make a realpath r->cwd = au->le->cwd; return nvlist_interp_cur_val((auparse_state_t *)au, r); } } return NULL; } static const char *auparse_interpret_sock_parts(auparse_state_t *au, const char *field) { if (au->le == NULL) return NULL; if (au->le->e.sec) { rnode *r = aup_list_get_cur(au->le); if (r == NULL) return NULL; // This is limited to socket address fields if (nvlist_get_cur_type(r) != AUPARSE_TYPE_SOCKADDR) return NULL; // Get interpretation const char *val=nvlist_interp_cur_val((auparse_state_t *)au,r); if (val == NULL) return NULL; // make a copy since we modify it char *tmp = strdup(val); if (tmp == NULL) return NULL; // Locate the address part char *aval = strstr(tmp, field); if (aval) { // Get past the = aval += strlen(field); // find other side char *ptr = strchr(aval, ' '); if (ptr) { // terminate, copy, and return it *ptr = 0; const char *final = strdup(aval); free(tmp); free((void *)au->tmp_translation); au->tmp_translation = final; return final; } } free(tmp); } return NULL; } const char *auparse_interpret_sock_family(auparse_state_t *au) { return auparse_interpret_sock_parts(au, "fam="); } const char *auparse_interpret_sock_port(auparse_state_t *au) { return auparse_interpret_sock_parts(au, "lport="); } const char *auparse_interpret_sock_address(auparse_state_t *au) { return auparse_interpret_sock_parts(au, "laddr="); } /* * auparse_set_eoe_timeout - set the end of event timeout value * * Args * new_tmo - new timeout value * Rtns * 0 - correctly set * 1 - failed to set */ int auparse_set_eoe_timeout (time_t new_tmo) { if (new_tmo == 0) return 1; eoe_timeout = new_tmo; return 0; }