diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/auparse/auparse.c audit-1.6.7/auparse/auparse.c --- audit/auparse/auparse.c 2008-02-11 20:59:49.000000000 +0100 +++ audit-1.6.7/auparse/auparse.c 2008-02-11 22:59:09.000000000 +0100 @@ -21,6 +21,7 @@ */ #include "config.h" +#include "expression.h" #include "internal.h" #include "auparse.h" #include "interpret.h" @@ -195,10 +196,10 @@ au->line_pushed = 0; aup_list_create(&au->le); au->parse_state = EVENT_EMPTY; - aurule_create(&au->rules); + au->expr = NULL; au->find_field = NULL; au->search_where = AUSEARCH_STOP_EVENT; - au->search_how = AUSEARCH_RULE_CLEAR; + au->regex_valid = 0; return au; bad_exit: @@ -297,67 +298,75 @@ } -static struct nv_pair optab[] = { - {AUSEARCH_EXISTS, "exists"}, - {AUSEARCH_EQUAL, "="}, - {AUSEARCH_NOT_EQUAL, "!="}, -}; -#define OP_NAMES (sizeof(optab)/sizeof(optab[0])) - -static int lookup_op(const char *op) -{ - int i; - - for (i = 0; i < OP_NAMES; i++) - if (strcmp(optab[i].name, op) == 0) - return optab[i].value; +/* 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; - return -1; + 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, - ausearch_op_t op_flag) + const char *op, const char *value, ausearch_rule_t how, unsigned op_eq, + unsigned op_ne) { - aurulenode rn; - int t_op = lookup_op(op); - if (t_op < 0) - goto err_out; + struct expr *expr; // Make sure there's a field if (field == NULL) goto err_out; // Do not allow regex to get replaced this way - if (au->search_how == AUSEARCH_RULE_REGEX) + if (au->regex_valid != 0) goto err_out; // Make sure how is within range if (how < AUSEARCH_RULE_CLEAR || how > AUSEARCH_RULE_AND) goto err_out; - if (how == AUSEARCH_RULE_CLEAR) { - aurule_clear(&au->rules); - } else if (how != au->search_how && - au->search_how != AUSEARCH_RULE_CLEAR) { - errno = EEXIST; - return -1; - } // All pre-checks are done, build a rule - rn.search_op = t_op | op_flag; - if (t_op == AUSEARCH_EXISTS) - rn.v.field.val = NULL; + 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; - rn.v.field.val = strdup(value); + expr = expr_create_comparison(field, t_op, value); } - rn.v.field.field = strdup(field); - aurule_append(&au->rules, &rn); - au->search_how = how; - + 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; @@ -366,83 +375,104 @@ 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, 0); + 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, - AUSEARCH_INTERPRETED); + EO_INTERPRETED_EQ, EO_INTERPRETED_NE); } -static int lookup_ts_op(const char *op) +int ausearch_add_timestamp_item(auparse_state_t *au, const char *op, time_t sec, + unsigned milli, ausearch_rule_t how) { - static const struct nv_pair ts_tab[] = { - {AUSEARCH_TIME_LT, "<"}, - {AUSEARCH_TIME_LE, "<="}, - {AUSEARCH_TIME_GE, ">="}, - {AUSEARCH_TIME_GT, ">"}, - {AUSEARCH_TIME_EQ, "="}, + static const struct { + unsigned value; + 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++) + for (i = 0; i < sizeof(ts_tab) / sizeof(*ts_tab); i++) { if (strcmp(ts_tab[i].name, op) == 0) - return ts_tab[i].value; - - return -1; -} + goto found_op; + } + goto err_out; +found_op: + t_op = ts_tab[i].value; -int ausearch_add_timestamp_item(auparse_state_t *au, const char *op, time_t sec, - unsigned milli, ausearch_rule_t how) -{ - aurulenode rn; - int t_op = lookup_ts_op(op); - if (op < 0) - goto err_out; if (milli >= 1000) goto err_out; // Do not allow regex to get replaced this way - if (au->search_how == AUSEARCH_RULE_REGEX) + if (au->regex_valid != 0) goto err_out; // Make sure how is within range if (how < AUSEARCH_RULE_CLEAR || how > AUSEARCH_RULE_AND) goto err_out; - if (how == AUSEARCH_RULE_CLEAR) { - aurule_clear(&au->rules); - } else if (how != au->search_how - && au->search_how != AUSEARCH_RULE_CLEAR) { - errno = EEXIST; - return -1; - } // All pre-checks are done, build a rule - rn.search_op = t_op; - rn.v.timestamp.sec = sec; - rn.v.timestamp.milli = milli; - aurule_append(&au->rules, &rn); - au->search_how = how; - + expr = expr_create_timestamp_comparison(t_op, sec, milli); + 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_expression(auparse_state_t *au, const char *expression, + char **error, ausearch_rule_t how) +{ + struct expr *expr; + + // Do not allow regex to get replaced this way + if (au->regex_valid != 0) + goto err_einval; + 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 *expr) { - aurulenode rn; int rc; // Make sure there's an expression if (expr == NULL) goto err_out; - if (au->search_how != AUSEARCH_RULE_CLEAR) + if (au->regex_valid != 0 || au->expr != NULL) goto err_out; // Compile expression now to make sure the expression is correct @@ -450,13 +480,7 @@ if (rc) { goto err_out; } - - // Store it away just in case. - rn.search_op = AUSEARCH_UNSET; - rn.v.field.field = strdup(expr); - rn.v.field.val = NULL; - aurule_append(&au->rules, &rn); - au->search_how = AUSEARCH_RULE_REGEX; + au->regex_valid = 1; return 0; @@ -478,11 +502,15 @@ void ausearch_clear(auparse_state_t *au) { - if (au->search_how == AUSEARCH_RULE_REGEX && au->rules.cnt > 0) + if (au->regex_valid != 0) { regfree(&au->regex); - aurule_clear(&au->rules); + au->regex_valid = 0; + } + if (au->expr != NULL) { + expr_free(au->expr); + au->expr = NULL; + } au->search_where = AUSEARCH_STOP_EVENT; - au->search_how = AUSEARCH_RULE_CLEAR; } hidden_def(ausearch_clear) @@ -856,76 +884,15 @@ return rc; } -/* Return the result of interpreting rule on the event record r. */ -static int ausearch_interpret_rule(auparse_state_t *au, rnode *r, - aurulenode *rule) -{ - int search_op; - - search_op = (rule->search_op & ~AUSEARCH_INTERPRETED); - if (AUSEARCH_OP_IS_TIME__(search_op)) { - int val; - - if (au->le.e.sec < rule->v.timestamp.sec) - val = -1; - else if (au->le.e.sec > rule->v.timestamp.sec) - val = 1; - else if (au->le.e.milli < rule->v.timestamp.milli) - val = -1; - else if (au->le.e.milli > rule->v.timestamp.milli) - val = 1; - else - val = 0; - switch (search_op) { - case AUSEARCH_TIME_LT: - return val < 0; - case AUSEARCH_TIME_LE: - return val <= 0; - case AUSEARCH_TIME_GE: - return val >= 0; - case AUSEARCH_TIME_GT: - return val > 0; - case AUSEARCH_TIME_EQ: - return val == 0; - default: - abort(); - } - } else { - int found, rc; - const char *val; - - found = nvlist_find_name(&r->nv, rule->v.field.field); - if (!found) - return 0; - if (search_op == AUSEARCH_EXISTS) - return 1; - - val = NULL; - if ((rule->search_op & AUSEARCH_INTERPRETED) != 0) - val = nvlist_interp_cur_val(r); - if (val == NULL) - val = nvlist_get_cur_val(&r->nv); - rc = strcmp(rule->v.field.val, val); - if (search_op == AUSEARCH_EQUAL) - return rc == 0; - else - return rc != 0; - } -} - /* 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->search_how == AUSEARCH_RULE_REGEX) { + if (au->regex_valid != 0) { int rc; - if (au->rules.cnt == 0) { - errno = EINVAL; - return -1; - } r = aup_list_get_cur(&au->le); rc = regexec(&au->regex, r->record, 0, NULL, 0); if (rc == 0) @@ -934,27 +901,9 @@ return 0; } r = aup_list_get_cur(&au->le); - if (r) { - // for each rule item - int results = (au->search_how == AUSEARCH_RULE_AND); - aurule_first(&au->rules); - do { - int tmp; + if (r) + return expr_eval(au, r, au->expr); - aurulenode *rule = aurule_get_cur(&au->rules); - if (rule == NULL) - continue; - tmp = ausearch_interpret_rule(au, r, rule); - if (au->search_how == AUSEARCH_RULE_AND) { - if (tmp == 0) - return 0; /* all must match */ - } else if (au->search_how <= AUSEARCH_RULE_OR && tmp) - return 1; /* Anytime tmp == 1 short circuit */ - } while (aurule_next(&au->rules)); - if (results) - return 1; - } - return 0; } @@ -963,7 +912,7 @@ { int rc; - if (au->rules.cnt == 0) { + if (au->expr == NULL && au->regex_valid == 0) { errno = EINVAL; return -1; } diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/auparse/auparse-defs.h audit-1.6.7/auparse/auparse-defs.h --- audit/auparse/auparse-defs.h 2007-11-19 19:44:04.000000000 +0100 +++ audit-1.6.7/auparse/auparse-defs.h 2008-02-11 23:16:54.000000000 +0100 @@ -37,21 +37,17 @@ AUSOURCE_BUFFER, AUSOURCE_BUFFER_ARRAY, AUSOURCE_DESCRIPTOR, AUSOURCE_FILE_POINTER, AUSOURCE_FEED } ausource_t; -/* This defines the types of searches that can be done */ +/* This used to define the types of searches that can be done. It is not used + any more. */ typedef enum { AUSEARCH_UNSET, AUSEARCH_EXISTS, AUSEARCH_EQUAL, AUSEARCH_NOT_EQUAL, AUSEARCH_TIME_LT, AUSEARCH_TIME_LE, AUSEARCH_TIME_GE, AUSEARCH_TIME_GT, AUSEARCH_TIME_EQ, - /* When comparing, use the value that would be returned by - auparse_interpret_field() instead of the raw value. */ AUSEARCH_INTERPRETED = 0x40000000 } ausearch_op_t; -#define AUSEARCH_OP_IS_TIME__(OP) \ - ((OP) >= AUSEARCH_TIME_LT && (OP) <= AUSEARCH_TIME_EQ) - /* This determines where to position the cursor when a search completes */ typedef enum { AUSEARCH_STOP_EVENT, AUSEARCH_STOP_RECORD, AUSEARCH_STOP_FIELD } austop_t; diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/auparse/auparse.h audit-1.6.7/auparse/auparse.h --- audit/auparse/auparse.h 2008-02-11 20:59:49.000000000 +0100 +++ audit-1.6.7/auparse/auparse.h 2008-02-13 17:45:06.000000000 +0100 @@ -49,6 +49,8 @@ void auparse_destroy(auparse_state_t *au); /* Functions that are part of the search interface */ +int ausearch_add_expression(auparse_state_t *au, const char *expression, + char **error, ausearch_rule_t how); int ausearch_add_item(auparse_state_t *au, const char *field, const char *op, const char *value, ausearch_rule_t how); int ausearch_add_interpreted_item(auparse_state_t *au, const char *field, diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/auparse/expression.c audit-1.6.7/auparse/expression.c --- audit/auparse/expression.c 1970-01-01 01:00:00.000000000 +0100 +++ audit-1.6.7/auparse/expression.c 2008-02-11 23:02:48.000000000 +0100 @@ -0,0 +1,975 @@ +/* +* expression.c - Expression parsing and handling +* Copyright (C) 2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Miloslav Trmač +*/ + +#include +#include +#include +#include + +#include "expression.h" + + /* Utilities */ + +/* Free EXPR and all its subexpressions. */ +void +expr_free(struct expr *expr) +{ + switch (expr->op) { + case EO_NOT: + expr_free(expr->v.sub[0]); + break; + + case EO_AND: case EO_OR: + expr_free(expr->v.sub[0]); + expr_free(expr->v.sub[1]); + break; + + case EO_RAW_EQ: case EO_RAW_NE: case EO_INTERPRETED_EQ: + case EO_INTERPRETED_NE: case EO_VALUE_EQ: case EO_VALUE_NE: + case EO_VALUE_LT: case EO_VALUE_LE: case EO_VALUE_GT: case EO_VALUE_GE: + if (expr->virtual_field == 0) + free(expr->v.p.field.name); + if (expr->precomputed_value == 0) + free(expr->v.p.value.string); + break; + + case EO_FIELD_EXISTS: + assert(expr->virtual_field == 0); + free(expr->v.p.field.name); + break; + + default: + abort(); + } + free(expr); +} + + /* Expression parsing. */ + +/* The formal grammar: + + start: or-expression + + or-expression: and-expression + or-expression: or-expression || and-expression + + and-expression: primary-expression + and-expression: and-expression && primary-expression + + primary-expression: ! primary-expression + primary-expression: ( or-expression ) + primary-expression: comparison-expression + + comparison-expression: field op value + field: string + field: field-escape string + value: string */ + +/* Token types */ +enum token_type { + /* EO_* */ + T_LEFT_PAREN = NUM_EO_VALUES, T_RIGHT_PAREN, T_STRING, T_FIELD_ESCAPE, + T_UNKNOWN, T_EOF +}; + +/* Expression parsing status */ +struct parsing { + char **error; /* Error message destination. */ + enum token_type token; + const char *token_start; /* Original "src" value */ + int token_len; /* int because it must be usable in %.*s */ + char *token_value; /* Non-NULL only for T_STRING, until used */ + const char *src; /* Expression source, after the current token */ +}; + +static struct expr *parse_or(struct parsing *p); + +/* Allocate SIZE bytes. + On error, return NULL and try to set *P->ERROR. */ +static void * +parser_malloc(struct parsing *p, size_t size) +{ + void *res; + + res = malloc(size); + if (res != NULL || size == 0) + return res; + *p->error = strdup("Out of memory"); + return NULL; +} + +/* Reallocate PTR to SIZE bytes. + On error, free(PTR), return NULL and try to set *P->ERROR. + NOTE: realloc() does not free(PTR), this function does. */ +static void * +parser_realloc(struct parsing *p, void *ptr, size_t size) +{ + void *res; + + res = realloc(ptr, size); + if (res != NULL || size == 0) + return res; + free(ptr); + *p->error = strdup("Out of memory"); + return NULL; +} + +/* Discard P->token_value, if any, and parse the next token in P->src. + On success, return 0. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + -1. */ +static int +lex(struct parsing *p) +{ + free(p->token_value); + p->token_value = NULL; + while (*p->src == ' ' || *p->src == '\t' || *p->src == '\n') + p->src++; + p->token_start = p->src; + switch (*p->src) { + case '\0': + p->token = T_EOF; + break; + + case '!': + p->src++; + if (*p->src == '=' && p->src[1] == '=') { + p->src += 2; + p->token = EO_VALUE_NE; + break; + } + p->token = EO_NOT; + break; + + case '"': { + char *buf; + size_t dest, buf_size; + + buf_size = 8; + buf = parser_malloc(p, buf_size); + if (buf == NULL) + return -1; + p->src++; + dest = 0; + while (*p->src != '"') { + if (*p->src == '\0') { + *p->error = strdup("Terminating quote missing"); + free(buf); + return -1; + } + if (*p->src == '\\') { + p->src++; + if (*p->src != '\\' && *p->src != '"') { + *p->error = NULL; + asprintf(p->error, "Unknown escape " + "sequence ``\\%c''", *p->src); + free(buf); + return -1; + } + } + /* +1: make sure there is space for the terminating + NUL. */ + if (dest + 1 >= buf_size) { + if (buf_size > SIZE_MAX / 2) { + *p->error = strdup("Quoted string too " + "long"); + free(buf); + return -1; + } + buf_size *= 2; + buf = parser_realloc(p, buf, buf_size); + if (buf == NULL) { + *p->error = strdup("Out of memory"); + return -1; + } + } + buf[dest] = *p->src; + dest++; + p->src++; + } + p->src++; + buf[dest] = '\0'; + p->token_value = parser_realloc(p, buf, dest + 1); + if (p->token_value == NULL) + return -1; + p->token = T_STRING; + break; + } + + case '&': + p->src++; + if (*p->src == '&') { + p->src++; + p->token = EO_AND; + break; + } + p->token = T_UNKNOWN; + break; + + case '(': + p->src++; + p->token = T_LEFT_PAREN; + break; + + case ')': + p->src++; + p->token = T_RIGHT_PAREN; + break; + + case '<': + p->src++; + if (*p->src == '=') { + p->src++; + p->token = EO_VALUE_LE; + break; + } + p->token = EO_VALUE_LT; + break; + + case '=': + p->src++; + if (*p->src == '=') { + p->src++; + p->token = EO_VALUE_EQ; + break; + } + p->token = T_UNKNOWN; + break; + + case '>': + p->src++; + if (*p->src == '=') { + p->src++; + p->token = EO_VALUE_GE; + break; + } + p->token = EO_VALUE_GT; + break; + + case '\\': + p->src++; + p->token = T_FIELD_ESCAPE; + break; + + case '|': + p->src++; + if (*p->src == '|') { + p->src++; + p->token = EO_OR; + break; + } + p->token = T_UNKNOWN; + break; + + case 'i': + if (p->src[1] == '=') { + p->src += 2; + p->token = EO_INTERPRETED_EQ; + break; + } else if (p->src[1] == '!' && p->src[2] == '=') { + p->src += 3; + p->token = EO_INTERPRETED_NE; + break; + } + goto unquoted_string; + + case 'r': + if (p->src[1] == '=') { + p->src += 2; + p->token = EO_RAW_EQ; + break; + } else if (p->src[1] == '!' && p->src[2] == '=') { + p->src += 3; + p->token = EO_RAW_NE; + break; + } + goto unquoted_string; + + default: + /* This assumes ASCII */ + assert ('Z' == 'A' + 25 && 'z' == 'a' + 25); +#define IS_UNQUOTED_STRING_CHAR(C) \ + (((C) >= 'a' && (C) <= 'z') \ + || ((C) >= 'A' && (C) <= 'Z') \ + || ((C) >= '0' && (C) <= '9') \ + || (C) == '_') + if (IS_UNQUOTED_STRING_CHAR(*p->src)) { + size_t len; + + unquoted_string: + do + p->src++; + while (IS_UNQUOTED_STRING_CHAR(*p->src)); + len = p->src - p->token_start; + p->token_value = parser_malloc(p, len + 1); + if (p->token_value == NULL) + return -1; + memcpy(p->token_value, p->token_start, len); + p->token_value[len] = '\0'; + p->token = T_STRING; + break; + } + p->src++; + p->token = T_UNKNOWN; + break; + } + if (p->src - p->token_start > INT_MAX) { + *p->error = strdup("Token too long"); + return -1; + } + p->token_len = p->src - p->token_start; + return 0; +} + +/* Parse an escaped field NAME to DEST. + Return 0 on success, -1 if NAME is unknown. */ +static int +parse_escaped_field_name(enum field_id *dest, const char *name) +{ + if (strcmp(name, "timestamp") == 0) + *dest = EF_TIMESTAMP; + else if (strcmp(name, "record_type") == 0) + *dest = EF_RECORD_TYPE; + else + return -1; + return 0; +} + +/* Parse a \timestamp field value in P->token_value to DEST. + On success, return 0. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + -1. */ +static int +parse_timestamp_value(struct expr *dest, struct parsing *p) +{ + intmax_t sec; + + assert(p->token == T_STRING); + /* FIXME: other formats? */ + /* FIXME: parse it as a float instead of hard-coding milliseconds? */ + if (sscanf(p->token_value, "ts:%jd.%u", &sec, + &dest->v.p.value.timestamp.milli) + != 2) { + *p->error = NULL; + asprintf(p->error, "Invalid timestamp value `%.*s'", + p->token_len, p->token_start); + return -1; + } + /* FIXME: validate milli */ + dest->v.p.value.timestamp.sec = sec; + if (dest->v.p.value.timestamp.sec != sec) { + *p->error = NULL; + asprintf(p->error, "Timestamp overflow in `%.*s'", p->token_len, + p->token_start); + return -1; + } + dest->precomputed_value = 1; + return 0; +} + +/* Parse a \record_type field value in P->token_value to DEST. + On success, return 0. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + -1. */ +static int +parse_record_type_value(struct expr *dest, struct parsing *p) +{ + int type; + + assert(p->token == T_STRING); + type = audit_name_to_msg_type(p->token_value); + if (type < 0) { + *p->error = NULL; + asprintf(p->error, "Invalid record type `%.*s'", p->token_len, + p->token_start); + return -1; + } + dest->v.p.value.int_value = type; + dest->precomputed_value = 1; + return 0; +} + +/* Parse a virtual field value in P->token_value to DEST. + On success, return 0. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static int +parse_virtual_field_value(struct expr *dest, struct parsing *p) +{ + switch (dest->v.p.field.id) { + case EF_TIMESTAMP: + return parse_timestamp_value(dest, p); + + case EF_RECORD_TYPE: + return parse_record_type_value(dest, p); + + default: + abort(); + } +} + +/* Parse a comparison-expression string in *P. + On success, return the parsed comparison-expression. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static struct expr * +parse_comparison(struct parsing *p) +{ + struct expr *res; + + res = parser_malloc(p, sizeof(*res)); + if (res == NULL) + return NULL; + if (p->token == T_FIELD_ESCAPE) { + if (lex(p) != 0) + goto err_res; + if (p->token != T_STRING) { + *p->error = strdup("Field name expected after field " + "escape"); + goto err_res; + } + res->virtual_field = 1; + if (parse_escaped_field_name(&res->v.p.field.id, p->token_value) + != 0) { + *p->error = NULL; + asprintf(p->error, "Unknown escaped field name `%.*s'", + p->token_len, p->token_start); + goto err_res; + } + } else { + assert(p->token == T_STRING); + res->virtual_field = 0; + res->v.p.field.name = p->token_value; + p->token_value = NULL; + } + if (lex(p) != 0) + goto err_field; + switch (p->token) { + case EO_RAW_EQ: case EO_RAW_NE: case EO_INTERPRETED_EQ: + case EO_INTERPRETED_NE: + res->op = p->token; + if (lex(p) != 0) + goto err_field; + if (p->token != T_STRING) { + *p->error = NULL; + asprintf(p->error, "Value expected, got `%.*s'", + p->token_len, p->token_start); + goto err_field; + } + res->precomputed_value = 0; + res->v.p.value.string = p->token_value; + p->token_value = NULL; + if (lex(p) != 0) { + expr_free(res); + return NULL; + } + break; + + case EO_VALUE_EQ: case EO_VALUE_NE: case EO_VALUE_LT: case EO_VALUE_LE: + case EO_VALUE_GT: case EO_VALUE_GE: + res->op = p->token; + if (lex(p) != 0) + goto err_field; + if (p->token != T_STRING) { + *p->error = NULL; + asprintf(p->error, "Value expected, got `%.*s'", + p->token_len, p->token_start); + goto err_field; + } + if (res->virtual_field == 0) { + *p->error = NULL; + asprintf (p->error, "Field `%s' does not support " + "value comparison", + res->v.p.field.name); + goto err_field; + } else { + if (parse_virtual_field_value(res, p) != 0) + goto err_field; + } + if (lex(p) != 0) { + expr_free(res); + return NULL; + } + break; + + default: + *p->error = NULL; + asprintf(p->error, "Operator expected, got `%.*s'", + p->token_len, p->token_start); + goto err_field; + } + return res; + +err_field: + if (res->virtual_field == 0) + free(res->v.p.field.name); +err_res: + free(res); + return NULL; +} + +/* Parse a primary-expression string in *P. + On success, return the parsed primary-expression. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static struct expr * +parse_primary(struct parsing *p) +{ + struct expr *e; + + switch (p->token) { + case EO_NOT: { + struct expr *res; + + if (lex(p) != 0) + return NULL; + e = parse_primary(p); + if (e == NULL) + return NULL; + res = parser_malloc(p, sizeof(*res)); + if (res == NULL) + goto err_e; + res->op = EO_NOT; + res->v.sub[0] = e; + return res; + } + + case T_LEFT_PAREN: { + if (lex(p) != 0) + return NULL; + e = parse_or(p); + if (e == NULL) + return NULL; + if (p->token != T_RIGHT_PAREN) { + *p->error = NULL; + asprintf(p->error, "Right paren expected, got `%.*s'", + p->token_len, p->token_start); + goto err_e; + } + if (lex(p) != 0) + goto err_e; + return e; + } + + case T_FIELD_ESCAPE: case T_STRING: + return parse_comparison(p); + + default: + *p->error = NULL; + asprintf(p->error, "Unexpected token `%.*s'", p->token_len, + p->token_start); + return NULL; + } + abort(); /* Should never get here */ + +err_e: + expr_free(e); + return NULL; +} + +/* Parse an and-expression string in *P. + On success, return the parsed and-expression. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static struct expr * +parse_and(struct parsing *p) +{ + struct expr *res; + + res = parse_primary(p); + if (res == NULL) + return NULL; + while (p->token == EO_AND) { + struct expr *e2, *e; + + if (lex(p) != 0) + goto err_res; + e2 = parse_primary(p); + if (e2 == NULL) + goto err_res; + e = parser_malloc(p, sizeof(*e)); + if (e == NULL) { + expr_free(e2); + goto err_res; + } + e->op = EO_AND; + e->v.sub[0] = res; + e->v.sub[1] = e2; + res = e; + } + return res; + +err_res: + expr_free(res); + return NULL; +} + +/* Parse an or-expression string in *P. + On success, return the parsed or-expression. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static struct expr * +parse_or(struct parsing *p) +{ + struct expr *res; + + res = parse_and(p); + if (res == NULL) + return NULL; + while (p->token == EO_OR) { + struct expr *e2, *e; + + if (lex(p) != 0) + goto err_res; + e2 = parse_and(p); + if (e2 == NULL) + goto err_res; + e = parser_malloc(p, sizeof(*e)); + if (e == NULL) { + expr_free(e2); + goto err_res; + } + e->op = EO_OR; + e->v.sub[0] = res; + e->v.sub[1] = e2; + res = e; + } + return res; + +err_res: + expr_free(res); + return NULL; +} + +/* Parse STRING. + On success, return the parsed expression tree. + On error, set *ERROR to an error string (for free()) or NULL, and return + NULL. (*ERROR == NULL is allowed to handle out-of-memory errors) */ +struct expr * +expr_parse(const char *string, char **error) +{ + struct parsing p; + struct expr *res; + + p.error = error; + p.token_value = NULL; + p.src = string; + if (lex(&p) != 0) + goto err; + if (p.token == T_EOF) { + *error = strdup("Empty expression"); + goto err; + } + res = parse_or(&p); + if (res != NULL && p.token != T_EOF) { + expr_free(res); + *error = NULL; + asprintf(error, "Unexpected trailing token `%.*s'", + p.token_len, p.token_start); + goto err; + } + free(p.token_value); + return res; + +err: + free(p.token_value); + return NULL; +} + + /* Manual expression creation */ + +/* Create a comparison-expression for FIELD, OP and VALUE. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_comparison(const char *field, unsigned op, const char *value) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + goto err; + assert(op == EO_RAW_EQ || op == EO_RAW_NE || op == EO_INTERPRETED_EQ + || op == EO_INTERPRETED_NE); + res->op = op; + res->virtual_field = 0; + res->precomputed_value = 0; + res->v.p.field.name = strdup(field); + if (res->v.p.field.name == NULL) + goto err_res; + res->v.p.value.string = strdup(value); + if (res->v.p.value.string == NULL) + goto err_field; + return res; + +err_field: + free(res->v.p.field.name); +err_res: + free(res); +err: + return NULL; +} + +/* Create a \timestamp comparison-expression for with OP, SEC, MILLI. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_timestamp_comparison(unsigned op, time_t sec, unsigned milli) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + return NULL; + assert(op == EO_VALUE_EQ || op == EO_VALUE_NE || op == EO_VALUE_LT + || op == EO_VALUE_LE || op == EO_VALUE_GT || op == EO_VALUE_GE); + res->op = op; + res->virtual_field = 1; + res->v.p.field.id = EF_TIMESTAMP; + res->precomputed_value = 1; + res->v.p.value.timestamp.sec = sec; + assert(milli < 1000); + res->v.p.value.timestamp.milli = milli; + return res; +} + +/* Create an EO_FIELD_EXISTS-expression for FIELD. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_field_exists(const char *field) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + goto err; + res->op = EO_FIELD_EXISTS; + res->virtual_field = 0; + res->v.p.field.name = strdup(field); + if (res->v.p.field.name == NULL) + goto err_res; + return res; + +err_res: + free(res); +err: + return NULL; +} + +/* Create a binary expresion for OP and subexpressions E1 and E2. + On success, return the created expresion. + On error, set errno and return NULL. */ +struct expr * +expr_create_binary(unsigned op, struct expr *e1, struct expr *e2) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + return NULL; + assert(op == EO_AND || op ==EO_OR); + res->op = op; + res->v.sub[0] = e1; + res->v.sub[1] = e2; + return res; +} + + /* Expression evaluation */ + +/* Return the "raw" value of the field in EXPR for RECORD in AU->le. Set + *FREE_IT to 1 if the return value should free()'d. + Return NULL on error. */ +static char * +eval_raw_value(auparse_state_t *au, rnode *record, const struct expr *expr, + int *free_it) +{ + if (expr->virtual_field == 0) { + if (nvlist_find_name(&record->nv, expr->v.p.field.name) == 0) + return NULL; + *free_it = 0; + return (char *)nvlist_get_cur_val(&record->nv); + } + switch (expr->v.p.field.id) { + case EF_TIMESTAMP: case EF_RECORD_TYPE: + return NULL; + + default: + abort(); + } +} + +/* Return the "interpreted" value of the field in EXPR for RECORD in AU->le. + Set *FREE_IT to 1 if the return value should free()'d. + Return NULL on *error. */ +static char * +eval_interpreted_value(auparse_state_t *au, rnode *record, + const struct expr *expr, int *free_it) +{ + if (expr->virtual_field == 0) { + const char *res; + + if (nvlist_find_name(&record->nv, expr->v.p.field.name) == 0) + return NULL; + *free_it = 0; + res = nvlist_interp_cur_val(record); + if (res == NULL) + res = nvlist_get_cur_val(&record->nv); + return (char *)res; + } + switch (expr->v.p.field.id) { + case EF_TIMESTAMP: case EF_RECORD_TYPE: + return NULL; + + default: + abort(); + } +} + +/* Return -1, 0, 1 depending on comparing the field in EXPR with RECORD in AU. + Set *ERROR to 0 if OK, non-zero otherwise. */ +static int +compare_values(auparse_state_t *au, rnode *record, const struct expr *expr, + int *error) +{ + int res; + if (expr->virtual_field == 0) { + *error = 1; + return 0; + } + switch (expr->v.p.field.id) { + case EF_TIMESTAMP: + if (au->le.e.sec < expr->v.p.value.timestamp.sec) + res = -1; + else if (au->le.e.sec > expr->v.p.value.timestamp.sec) + res = 1; + else if (au->le.e.milli < expr->v.p.value.timestamp.milli) + res = -1; + else if (au->le.e.milli > expr->v.p.value.timestamp.milli) + res = 1; + else + res = 0; + break; + + case EF_RECORD_TYPE: + if (record->type < expr->v.p.value.int_value) + res = -1; + else if (record->type > expr->v.p.value.int_value) + res = 1; + else + res = 0; + break; + + default: + abort(); + } + *error = 0; + return res; +} + +/* Evaluate EXPR on RECORD in AU->le. + Return 1 if EXPR is true, 0 if it false or if it fails. + (No error reporting facility is provided; an invalid term is considered to + be false; e.g. !invalid is true.) */ +int +expr_eval(auparse_state_t *au, rnode *record, const struct expr *expr) +{ + switch (expr->op) { + case EO_NOT: + return !expr_eval(au, record, expr->v.sub[0]); + + case EO_AND: + return (expr_eval(au, record, expr->v.sub[0]) + && expr_eval(au, record, expr->v.sub[1])); + + case EO_OR: + return (expr_eval(au, record, expr->v.sub[0]) + || expr_eval(au, record, expr->v.sub[1])); + + case EO_RAW_EQ: case EO_RAW_NE: { + int free_it, ne; + char *value; + + value = eval_raw_value(au, record, expr, &free_it); + if (value == NULL) + return 0; + assert(expr->precomputed_value == 0); + ne = strcmp(expr->v.p.value.string, value); + if (free_it != 0) + free(value); + return expr->op == EO_RAW_EQ ? ne == 0 : ne != 0; + } + + case EO_INTERPRETED_EQ: case EO_INTERPRETED_NE: { + int free_it, ne; + char *value; + + value = eval_interpreted_value(au, record, expr, &free_it); + if (value == NULL) + return 0; + assert(expr->precomputed_value == 0); + ne = strcmp(expr->v.p.value.string, value); + if (free_it != 0) + free(value); + return expr->op == EO_INTERPRETED_EQ ? ne == 0 : ne != 0; + } + + case EO_VALUE_EQ: case EO_VALUE_NE: case EO_VALUE_LT: case EO_VALUE_LE: + case EO_VALUE_GT: case EO_VALUE_GE: { + int err, cmp; + + cmp = compare_values(au, record, expr, &err); + if (err != 0) + return 0; + switch (expr->op) { + case EO_VALUE_EQ: + return cmp == 0; + + case EO_VALUE_NE: + return cmp != 0; + + case EO_VALUE_LT: + return cmp < 0; + + case EO_VALUE_LE: + return cmp <= 0; + + case EO_VALUE_GT: + return cmp > 0; + + case EO_VALUE_GE: + return cmp >= 0; + + default: + abort(); + } + } + + case EO_FIELD_EXISTS: + assert(expr->virtual_field == 0); + return nvlist_find_name(&record->nv, expr->v.p.field.name) != 0; + + default: + abort(); + } +} diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/auparse/expression.h audit-1.6.7/auparse/expression.h --- audit/auparse/expression.h 1970-01-01 01:00:00.000000000 +0100 +++ audit-1.6.7/auparse/expression.h 2008-02-11 22:15:48.000000000 +0100 @@ -0,0 +1,112 @@ +/* +* expression.h - Expression parsing and handling +* Copyright (C) 2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Miloslav Trmač +*/ + +#ifndef EXPRESSION_H__ +#define EXPRESSION_H__ + +#include + +#include "internal.h" + +enum { + EO_NOT, /* Uses v.sub[0] */ + EO_AND, EO_OR, /* Uses v.sub[0] and v.sub[1] */ + /* All of the following use v.p */ + EO_RAW_EQ, EO_RAW_NE, EO_INTERPRETED_EQ, EO_INTERPRETED_NE, + EO_VALUE_EQ, EO_VALUE_NE, EO_VALUE_LT, EO_VALUE_LE, EO_VALUE_GT, + EO_VALUE_GE, + /* Uses v.p.field. Cannot be specified by an expression. */ + EO_FIELD_EXISTS, + NUM_EO_VALUES, +}; + +enum field_id { + EF_TIMESTAMP, EF_RECORD_TYPE, +}; + +struct expr { + unsigned op : 8; /* EO_* */ + unsigned virtual_field : 1; + /* Can be non-zero only if virtual_field != 0 */ + unsigned precomputed_value : 1; + union { + struct expr *sub[2]; + struct { + union { + char *name; + enum field_id id; /* If virtual_field != 0 */ + } field; + union { + char *string; + /* A member from the following is selected + implicitly by field.id. */ + struct { + time_t sec; + unsigned int milli; + } timestamp; /* EF_TIMESTAMP */ + int int_value; /* EF_RECORD_TYPE */ + } value; + } p; + } v; +}; + +/* Free EXPR and all its subexpressions. */ +void expr_free(struct expr *expr) hidden; + +/* Parse STRING. + On success, return the parsed expression tree. + On error, set *ERROR to an error string (for free()) or NULL, and return + NULL. (*ERROR == NULL is allowed to handle out-of-memory errors) */ +struct expr *expr_parse(const char *string, char **error) hidden; + +/* Create a comparison-expression for FIELD, OP and VALUE. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr *expr_create_comparison(const char *field, unsigned op, + const char *value) hidden; + +/* Create a \timestamp comparison-expression for with OP, SEC, MILLI. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr *expr_create_timestamp_comparison(unsigned op, time_t sec, + unsigned milli) hidden; + +/* Create an EO_FIELD_EXISTS-expression for FIELD. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr *expr_create_field_exists(const char *field) hidden; + +/* Create a binary expresion for OP and subexpressions E1 and E2. + On success, return the created expresion. + On error, set errno and return NULL. */ +struct expr *expr_create_binary(unsigned op, struct expr *e1, struct expr *e2) + hidden; + +/* Evaluate EXPR on RECORD in AU->le. + Return 1 if EXPR is true, 0 if it false or if it fails. + (No error reporting facility is provided; an invalid term is considered to + be false; e.g. !invalid is true.) */ +int expr_eval(auparse_state_t *au, rnode *record, const struct expr *expr) + hidden; + +#endif diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/auparse/internal.h audit-1.6.7/auparse/internal.h --- audit/auparse/internal.h 2008-02-11 20:59:49.000000000 +0100 +++ audit-1.6.7/auparse/internal.h 2008-02-11 22:47:22.000000000 +0100 @@ -19,12 +19,11 @@ * Authors: * Steve Grubb */ -#ifndef AUPARSE_INTERNAL_HEADER_ +#ifndef AUPARSE_INTERNAL_HEADER #define AUPARSE_INTERNAL_HEADER #include "auparse-defs.h" #include "ellist.h" -#include "rlist.h" #include "auditd-config.h" #include "data_buf.h" #include @@ -78,13 +77,13 @@ int line_pushed; // True if retrieve_next_line() // returns same input event_list_t le; // Linked list of record in same event - aurule_list_t rules; // Search rules + struct expr *expr; // Search expression or NULL char *find_field; // Used to store field name when // searching austop_t search_where; // Where to put the cursors on a match - ausearch_rule_t search_how; // How the rules are to be applied auparser_state_t parse_state; // parsing state - regex_t regex; // compiled regex + regex_t regex; // compiled regex if regex_valid != 0 + int regex_valid; DataBuf databuf; // input data // function to call to notify user of parsing changes diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/auparse/Makefile.am audit-1.6.7/auparse/Makefile.am --- audit/auparse/Makefile.am 2008-01-17 21:41:27.000000000 +0100 +++ audit-1.6.7/auparse/Makefile.am 2008-02-11 22:22:37.000000000 +0100 @@ -27,12 +27,12 @@ LIBS = lib_LTLIBRARIES = libauparse.la include_HEADERS = auparse.h auparse-defs.h -libauparse_la_SOURCES = rlist.c nvpair.c interpret.c nvlist.c ellist.c \ - auparse.c auditd-config.c message.c data_buf.c auparse-defs.h \ - data_buf.h flagtab.h nvlist.h rlist.h auparse.h ellist.h \ - internal.h nvpair.h rnode.h captab.h famtab.h interpret.h \ - open-flagtab.h socktab.h clone-flagtab.h fcntl-cmdtab.h \ - ipctab.h private.h typetab.h +libauparse_la_SOURCES = nvpair.c interpret.c nvlist.c ellist.c \ + auparse.c auditd-config.c message.c data_buf.c auparse-defs.h \ + data_buf.h flagtab.h nvlist.h auparse.h ellist.h \ + internal.h nvpair.h rnode.h captab.h famtab.h interpret.h \ + open-flagtab.h socktab.h clone-flagtab.h fcntl-cmdtab.h \ + ipctab.h private.h typetab.h expression.c expression.h libauparse_la_LIBADD = ${top_srcdir}/lib/libaudit.la libauparse_la_DEPENDENCIES = $(libauparse_la_SOURCES) ../config.h diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/auparse/rlist.c audit-1.6.7/auparse/rlist.c --- audit/auparse/rlist.c 2008-02-11 20:59:49.000000000 +0100 +++ audit-1.6.7/auparse/rlist.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,102 +0,0 @@ -/* -* rlist.c - Minimal linked list library for search rules -* Copyright (c) 2007 Red Hat Inc., Durham, North Carolina. -* All Rights Reserved. -* -* This software may be freely redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free -* Software Foundation; either version 2, or (at your option) any -* later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; see the file COPYING. If not, write to the -* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -* Authors: -* Steve Grubb -*/ - -#include "config.h" -#include -#include "rlist.h" -#include "internal.h" - - -void aurule_create(aurule_list_t *l) -{ - l->head = NULL; - l->cur = NULL; - l->cnt = 0; -} - -static void aurule_last(aurule_list_t *l) -{ - register aurulenode* window; - - if (l->head == NULL) - return; - - window = l->head; - while (window->next) - window = window->next; - l->cur = window; -} - -void aurule_append(aurule_list_t *l, aurulenode *node) -{ - aurulenode* newnode = malloc(sizeof(aurulenode)); - - *newnode = *node; - newnode->item = l->cnt; - newnode->next = NULL; - - // if we are at top, fix this up - if (l->head == NULL) - l->head = newnode; - else { // Otherwise add pointer to newnode - if (l->cnt == (l->cur->item+1)) { - l->cur->next = newnode; - } - else { - aurule_last(l); - l->cur->next = newnode; - } - } - - // make newnode current - l->cur = newnode; - l->cnt++; -} - -void aurule_clear(aurule_list_t *l) -{ - aurulenode* nextnode; - register aurulenode* current; - - current = l->head; - while (current) { - nextnode=current->next; - if (!AUSEARCH_OP_IS_TIME__(current->search_op)) { - free(current->v.field.field); - free(current->v.field.val); - } - free(current); - current=nextnode; - } - l->head = NULL; - l->cur = NULL; - l->cnt = 0; -} - -aurulenode *aurule_next(aurule_list_t *l) -{ - if (l->cur) - l->cur = l->cur->next; - return l->cur; -} - diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/auparse/rlist.h audit-1.6.7/auparse/rlist.h --- audit/auparse/rlist.h 2008-02-11 20:59:49.000000000 +0100 +++ audit-1.6.7/auparse/rlist.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,65 +0,0 @@ -/* -* rlist.h - Header file for rlist.c -* Copyright (c) 2007 Red Hat Inc., Durham, North Carolina. -* All Rights Reserved. -* -* This software may be freely redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free -* Software Foundation; either version 2, or (at your option) any -* later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; see the file COPYING. If not, write to the -* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -* Authors: -* Steve Grubb -*/ - -#ifndef RLIST_HEADER -#define RLIST_HEADER - -#include "config.h" -#include "private.h" -#include - -/* This is the node of the linked list. Any data elements that are - * per item goes here. */ -typedef struct _aurulenode{ - ausearch_op_t search_op; // What search op is being done - union { - struct { - char *field; // The field name string - char *val; // The value field - } field; - struct { // For search_op == AUSEARCH_TIME_* - time_t sec; - unsigned int milli; - } timestamp; - } v; - unsigned int item; // Which item in the rule list - struct _aurulenode* next; // Next aurule node pointer -} aurulenode; - -/* This is the linked list head. Only data elements that are 1 per - * event goes here. */ -typedef struct { - aurulenode *head; // List head - aurulenode *cur; // Pointer to current node - unsigned int cnt; // How many items in this list -} aurule_list_t; - -void aurule_create(aurule_list_t *l) hidden; -static inline void aurule_first(aurule_list_t *l) { l->cur = l->head; } -static inline aurulenode *aurule_get_cur(aurule_list_t *l) { return l->cur; } -void aurule_append(aurule_list_t *l, aurulenode *node) hidden; -void aurule_clear(aurule_list_t *l) hidden; -aurulenode *aurule_next(aurule_list_t *l) hidden; - -#endif - diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/bindings/python/auparse_python.c audit-1.6.7/bindings/python/auparse_python.c --- audit/bindings/python/auparse_python.c 2007-11-19 19:44:04.000000000 +0100 +++ audit-1.6.7/bindings/python/auparse_python.c 2008-02-13 18:00:59.000000000 +0100 @@ -576,14 +576,67 @@ } /******************************** + * ausearch_add_expression + ********************************/ +PyDoc_STRVAR(search_add_expression_doc, +"search_add_expression(expression, how) Build up search expression\n\ +\n\ +\n\ +ausearch_add_item adds an expression to the current audit search\n\ +expression. The search conditions can then be used to scan logs,\n\ +files, or buffers for something of interest. The expression parameter\n\ +contains an expression, as specified in ausearch-expression(5).\n\ +\n\ +The how parameter determines how this search expression will affect the\n\ +existing search expression, if one is already defined. The possible\n\ +values are:\n\ +\n\ +AUSEARCH_RULE_CLEAR:\n\ +Clear the current search expression, if any, and use only this search\n\ +expression.\n\ +\n\ +AUSEARCH_RULE_OR:\n\ +\n\ +If a search expression E is already configured, replace it by\n\ +(E || this_search_expression).\n\ +\n\ +AUSEARCH_RULE_AND:\n\ +If a search expression E is already configured, replace it by\n\ +(E && this_search_expression).\n\ +\n\ +No Return value, raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_search_add_expression(AuParser *self, PyObject *args) +{ + const char *expression; + char *error; + int how; + int result; + + if (!PyArg_ParseTuple(args, "si", &expression, &how)) return NULL; + PARSER_CHECK; + + result = ausearch_add_expression(self->au, expression, &error, how); + if (result == 0) Py_RETURN_NONE; + if (error == NULL) + PyErr_SetFromErrno(PyExc_EnvironmentError); + else { + PyErr_SetString(PyExc_EnvironmentError, error); + free(error); + } + return NULL; +} + +/******************************** * ausearch_add_item ********************************/ PyDoc_STRVAR(search_add_item_doc, "search_add_item(field, op, value, how) Build up search rule\n\ \n\ \n\ -search_add_item() adds one search condition to the audit search\n\ -API. The search conditions can then be used to scan logs, files, or\n\ +search_add_item() adds one search condition to the current audit search\n\ +expression. The search conditions can then be used to scan logs, files, or\n\ buffers for something of interest. The field value is the field name\n\ that the value will be checked for. The op variable describes what\n\ kind of check is to be done. Legal op values are:\n\ @@ -601,23 +654,22 @@ \n\ The value parameter is compared to the uninterpreted field value.\n\ \n\ -The how parameter determines how this search condition will be stored\n\ -internally. The possible values are:\n\ +The how parameter determines how this search expression will affect the\n\ +existing search expression, if one is already defined. The possible\n\ +values are:\n\ \n\ AUSEARCH_RULE_CLEAR:\n\ -When this is used, it clears any previous search condition and inserts\n\ -it as the first one.\n\ +Clear the current search expression, if any, and use only this search\n\ +expression.\n\ \n\ AUSEARCH_RULE_OR:\n\ \n\ -When this is used, it means that the results of its evaluation will be\n\ -or'ed with other search conditions.\n\ +If a search expression E is already configured, replace it by\n\ +(E || this_search_expression).\n\ \n\ AUSEARCH_RULE_AND:\n\ -When this is used, it means that the results of its evaluation will be\n\ -and'ed with other search conditions.\n\ -\n\ -All search conditions must be the same type, you cannot mix 'and' and 'or'.\n\ +If a search expression E is already configured, replace it by\n\ +(E && this_search_expression).\n\ \n\ No Return value, raises exception (EnvironmentError) on error.\n\ "); @@ -647,10 +699,10 @@ "search_add_interpreted_item(field, op, value, how) Build up search rule\n\ \n\ \n\ -search_add_interpreted_item() adds one search condition to the audit search\n\ -API. The search conditions can then be used to scan logs, files, or\n\ -buffers for something of interest. The field value is the field name\n\ -that the value will be checked for. The op variable describes what\n\ +search_add_interpreted_item() adds one search condition to the current audit\n\ +search expression. The search conditions can then be used to scan logs,\n\ +files, or buffers for something of interest. The field value is the field\n\ +name that the value will be checked for. The op variable describes what\n\ kind of check is to be done. Legal op values are:\n\ \n\ 'exists':\n\ @@ -667,23 +719,22 @@ The value parameter is compared to the interpreted field value (the value\n\ that would be returned by AuParser.interpret_field).\n\ \n\ -The how parameter determines how this search condition will be stored\n\ -internally. The possible values are:\n\ +The how parameter determines how this search expression will affect the\n\ +existing search expression, if one is already defined. The possible\n\ +values are:\n\ \n\ AUSEARCH_RULE_CLEAR:\n\ -When this is used, it clears any previous search condition and inserts\n\ -it as the first one.\n\ +Clear the current search expression, if any, and use only this search\n\ +expression.\n\ \n\ AUSEARCH_RULE_OR:\n\ \n\ -When this is used, it means that the results of its evaluation will be\n\ -or'ed with other search conditions.\n\ +If a search expression E is already configured, replace it by\n\ +(E || this_search_expression).\n\ \n\ AUSEARCH_RULE_AND:\n\ -When this is used, it means that the results of its evaluation will be\n\ -and'ed with other search conditions.\n\ -\n\ -All search conditions must be the same type, you cannot mix 'and' and 'or'.\n\ +If a search expression E is already configured, replace it by\n\ +(E && this_search_expression).\n\ \n\ No Return value, raises exception (EnvironmentError) on error.\n\ "); @@ -713,31 +764,30 @@ "search_add_timestamp_item(op, sec, milli, how) Build up search rule\n\ \n\ \n\ -search_add_timestamp_item adds an event time condition to the audit\n\ -search state. The search conditions can then be used to scan logs, files,\n\ -or buffers for something of interest. The op parameter specifies the\n\ +search_add_timestamp_item adds an event time condition to the current audit\n\ +search expression. The search conditions can then be used to scan logs,\n\ +files, or buffers for something of interest. The op parameter specifies the\n\ desired comparison. Legal op values are \"<\", \"<=\", \">=\", \">\" and\n\ \"=\". The left operand of the comparison operator is the timestamp of the\n\ examined event, the right operand is specified by the sec and milli\n\ parameters.\n\ \n\ -The how parameter determines how this search condition will be stored\n\ -internally. The possible values are:\n\ +The how parameter determines how this search expression will affect the\n\ +existing search expression, if one is already defined. The possible\n\ +values are:\n\ \n\ AUSEARCH_RULE_CLEAR:\n\ -When this is used, it clears any previous search condition and inserts\n\ -it as the first one.\n\ +Clear the current search expression, if any, and use only this search\n\ +expression.\n\ \n\ AUSEARCH_RULE_OR:\n\ \n\ -When this is used, it means that the results of its evaluation will be\n\ -or'ed with other search conditions.\n\ +If a search expression E is already configured, replace it by\n\ +(E || this_search_expression).\n\ \n\ AUSEARCH_RULE_AND:\n\ -When this is used, it means that the results of its evaluation will be\n\ -and'ed with other search conditions.\n\ -\n\ -All search conditions must be the same type, you cannot mix 'and' and 'or'.\n\ +If a search expression E is already configured, replace it by\n\ +(E && this_search_expression).\n\ \n\ No Return value, raises exception (EnvironmentError) on error.\n\ "); @@ -1371,6 +1421,7 @@ {"flush_feed", (PyCFunction)AuParser_flush_feed, METH_NOARGS, flush_feed_doc}, {"add_callback", (PyCFunction)AuParser_add_callback, METH_VARARGS, add_callback_doc}, {"reset", (PyCFunction)AuParser_reset, METH_NOARGS, reset_doc}, + {"search_add_expression", (PyCFunction)AuParser_search_add_expression, METH_VARARGS, search_add_expression_doc}, {"search_add_item", (PyCFunction)AuParser_search_add_item, METH_VARARGS, search_add_item_doc}, {"search_add_interpreted_item", (PyCFunction)AuParser_search_add_interpreted_item, METH_VARARGS, search_add_interpreted_item_doc}, {"search_add_timestamp_item", (PyCFunction)AuParser_search_add_timestamp_item, METH_VARARGS, search_add_timestamp_item_doc}, diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/docs/ausearch_add_expression.3 audit-1.6.7/docs/ausearch_add_expression.3 --- audit/docs/ausearch_add_expression.3 1970-01-01 01:00:00.000000000 +0100 +++ audit-1.6.7/docs/ausearch_add_expression.3 2008-02-12 16:48:31.000000000 +0100 @@ -0,0 +1,71 @@ +.TH "AUSEARCH_ADD_expression" "3" "Feb 2008" "Red Hat" "Linux Audit API" +.SH NAME +ausearch_add_expression \- build up search expression +.SH "SYNOPSIS" +.B #include + +\fBint ausearch_add_expression(auparse_state_t *\fIau\fB, +const char *\fIexpression\fB, char **\fIerror\fB, ausearch_rule_t \fIhow\fB);\fR + +.SH "DESCRIPTION" + +.B ausearch_add_item +adds an expression to the current audit search expression. +The search conditions can then be used to scan logs, files, or buffers +for something of interest. +The +.I expression +parameter contains an expression, as specified in +.BR ausearch-expression (5). + +The +.I how +parameter determines +how this search expression will affect the existing search expression, +if one is already defined. +The possible values are: +.RS +.TP +.I AUSEARCH_RULE_CLEAR +Clear the current search expression, if any, +and use only this search expression. +.TP +.I AUSEARCH_RULE_OR +If a search expression +.I E +is already configured, +replace it by \fB(\fIE\fB || \fIthis_search_expression\fB)\fR. +.TP +.I AUSEARCH_RULE_AND +If a search expression +.I E +is already configured, +replace it by \fB(\fIE\fB && \fIthis_search_expression\fB)\fR. +.RE + +.SH "RETURN VALUE" + +If successful, +.B ausearch_add_expression +returns 0. +Otherwise, it returns -1, sets +.B errno +and it may set \fB*\fIerror\fR to an error message; +the caller must free the error message using +.BR free (3). +If an error message is not available or can not be allocated, \fB*\fIerror\fR +is set to \fBNULL\fR. + +.SH "SEE ALSO" + +.BR ausearch_add_item (3), +.BR ausearch_add_interpreted_item (3), +.BR ausearch_add_timestamp_item (3), +.BR ausearch_add_regex (3), +.BR ausearch_set_stop (3), +.BR ausearch_clear (3), +.BR ausearch_next_event (3), +.BR ausearch-expression (5). + +.SH AUTHOR +Miloslav Trmac diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/docs/ausearch_add_interpreted_item.3 audit-1.6.7/docs/ausearch_add_interpreted_item.3 --- audit/docs/ausearch_add_interpreted_item.3 2007-11-19 19:44:04.000000000 +0100 +++ audit-1.6.7/docs/ausearch_add_interpreted_item.3 2008-02-12 16:50:59.000000000 +0100 @@ -8,7 +8,7 @@ .SH "DESCRIPTION" -ausearch_add_interpreted_item adds one search condition to the audit search API. The search conditions can then be used to scan logs, files, or buffers for something of interest. The field value is the field name that the value will be checked for. The op variable describes what kind of check is to be done. Legal op values are: +ausearch_add_interpreted_item adds one search condition to the current audit search expression. The search conditions can then be used to scan logs, files, or buffers for something of interest. The field value is the field name that the value will be checked for. The op variable describes what kind of check is to be done. Legal op values are: .RS .TP @@ -24,33 +24,37 @@ The value parameter is compared to the interpreted field value (the value that would be returned by \fBauparse_interpret_field\fR(3)). -The how value determines how this search condition will be stored internally. The possible values are: +The how value determines how this search condition will affect the existing search expression if one is already defined. The possible values are: .RS .TP .I AUSEARCH_RULE_CLEAR -When this is used, it clears any previous search condition and inserts it as the first one. +Clear the current search expression, if any, and use only this search condition. .TP .I AUSEARCH_RULE_OR -When this is used, it means that the results of its evaluation will be "or"ed with other search conditions. +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB || \fIthis_search_condition\fB)\fR. .TP .I AUSEARCH_RULE_AND -When this is used, it means that the results of its evaluation will be "and"ed with other search conditions. +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB && \fIthis_search_condition\fB)\fR. .RE -All search conditions must be the same type, you cannot mix "and" and "or". - .SH "RETURN VALUE" Returns -1 if an error occurs; otherwise, 0 for success. .SH "SEE ALSO" +.BR ausearch_add_expression (3), .BR ausearch_add_item (3), .BR ausearch_add_timestamp_item (3), .BR ausearch_add_regex (3), .BR ausearch_set_stop (3), .BR ausearch_clear (3), -.BR ausearch_next_event (3). +.BR ausearch_next_event (3), +.BR ausearch-expression (5). .SH AUTHOR Steve Grubb diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/docs/ausearch_add_item.3 audit-1.6.7/docs/ausearch_add_item.3 --- audit/docs/ausearch_add_item.3 2007-11-19 19:44:04.000000000 +0100 +++ audit-1.6.7/docs/ausearch_add_item.3 2008-02-12 16:50:42.000000000 +0100 @@ -8,7 +8,7 @@ .SH "DESCRIPTION" -ausearch_add_item adds one search condition to the audit search API. The search conditions can then be used to scan logs, files, or buffers for something of interest. The field value is the field name that the value will be checked for. The op variable describes what kind of check is to be done. Legal op values are: +ausearch_add_item adds one search condition to the current audit search expression. The search conditions can then be used to scan logs, files, or buffers for something of interest. The field value is the field name that the value will be checked for. The op variable describes what kind of check is to be done. Legal op values are: .RS .TP @@ -24,33 +24,37 @@ The value parameter is compared to the uninterpreted field value. -The how value determines how this search condition will be stored internally. The possible values are: +The how value determines how this search condition will affect the existing search expression if one is already defined. The possible values are: .RS .TP .I AUSEARCH_RULE_CLEAR -When this is used, it clears any previous search condition and inserts it as the first one. +Clear the current search expression, if any, and use only this search condition. .TP .I AUSEARCH_RULE_OR -When this is used, it means that the results of its evaluation will be "or"ed with other search conditions. +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB || \fIthis_search_condition\fB)\fR. .TP .I AUSEARCH_RULE_AND -When this is used, it means that the results of its evaluation will be "and"ed with other search conditions. +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB && \fIthis_search_condition\fB)\fR. .RE -All search conditions must be the same type, you cannot mix "and" and "or". - .SH "RETURN VALUE" Returns -1 if an error occurs; otherwise, 0 for success. .SH "SEE ALSO" +.BR ausearch_add_expression (3), .BR ausearch_add_interpreted_item (3), .BR ausearch_add_timestamp_item (3), .BR ausearch_add_regex (3), .BR ausearch_set_stop (3), .BR ausearch_clear (3), -.BR ausearch_next_event (3). +.BR ausearch_next_event (3), +.BR ausearch-expression (5). .SH AUTHOR Steve Grubb diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/docs/ausearch_add_regex.3 audit-1.6.7/docs/ausearch_add_regex.3 --- audit/docs/ausearch_add_regex.3 2007-09-06 16:04:43.000000000 +0200 +++ audit-1.6.7/docs/ausearch_add_regex.3 2008-02-12 16:35:34.000000000 +0100 @@ -8,7 +8,7 @@ .SH "DESCRIPTION" -ausearch_add_regex adds one search condition based on regular expressions to the audit search API. The search conditions can then be used to scan logs, files, or buffers for something of interest. You may not use this in combination with any other rule. The regular expression follows the posix regular expression conventions. The search results are at the record level and not the field. +ausearch_add_regex adds one search condition based on regular expressions to the audit search API. The search conditions can then be used to scan logs, files, or buffers for something of interest. You may not use this in combination with any other search expression. The regular expression follows the posix regular expression conventions. The search results are at the record level and not the field. .SH "RETURN VALUE" @@ -16,8 +16,9 @@ .SH "SEE ALSO" -.BR ausearch_add_item (3), -.BR ausearch_clear (3), +.BR ausearch_add_expression (3), +.BR ausearch_add_item (3), +.BR ausearch_clear (3), .BR ausearch_next_event (3), .BR regcomp (3). diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/docs/ausearch_add_timestamp_item.3 audit-1.6.7/docs/ausearch_add_timestamp_item.3 --- audit/docs/ausearch_add_timestamp_item.3 2007-11-19 19:44:04.000000000 +0100 +++ audit-1.6.7/docs/ausearch_add_timestamp_item.3 2008-02-12 16:39:41.000000000 +0100 @@ -8,23 +8,25 @@ .SH "DESCRIPTION" -ausearch_add_timestamp_item adds an event time condition to an audit search state. The search conditions can then be used to scan logs, files, or buffers for something of interest. The op parameter specifies the desired comparison. Legal op values are \fI<\fR, \fI<=\fR, \fI>=\fR, \fI>\fR and \fI=\fR. The left operand of the comparison operator is the timestamp of the examined event, the right operand is specified by the sec and milli parameters. +ausearch_add_timestamp_item adds an event time condition to the current audit search expression. The search conditions can then be used to scan logs, files, or buffers for something of interest. The op parameter specifies the desired comparison. Legal op values are \fI<\fR, \fI<=\fR, \fI>=\fR, \fI>\fR and \fI=\fR. The left operand of the comparison operator is the timestamp of the examined event, the right operand is specified by the sec and milli parameters. -The how value determines how this search condition will be stored internally. The possible values are: +The how value determines how this search condition will affect the existing search expression if one is already defined. The possible values are: .RS .TP .I AUSEARCH_RULE_CLEAR -When this is used, it clears any previous search condition and inserts it as the first one. +Clear the current search expression, if any, and use only this search condition. .TP .I AUSEARCH_RULE_OR -When this is used, it means that the results of its evaluation will be "or"ed with other search conditions. +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB || \fIthis_search_condition\fB)\fR. .TP .I AUSEARCH_RULE_AND -When this is used, it means that the results of its evaluation will be "and"ed with other search conditions. +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB && \fIthis_search_condition\fB)\fR. .RE -All search conditions must be the same type, you cannot mix "and" and "or". - .SH "RETURN VALUE" Returns -1 if an error occurs; otherwise, 0 for success. @@ -36,15 +38,20 @@ and .BR ausearch_add_interpreted_item (3) to add conditions that check audit record fields. +Use +.BR ausearch_add_expression (3) +to add complex search expressions using a single function call. .SH "SEE ALSO" +.BR ausearch_add_expression (3), .BR ausearch_add_item (3), .BR ausearch_add_interpreted_item (3), .BR ausearch_add_regex (3), .BR ausearch_set_stop (3), .BR ausearch_clear (3), -.BR ausearch_next_event (3). +.BR ausearch_next_event (3), +.BR ausearch-expression (5). .SH AUTHOR Miloslav Trmac diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/docs/ausearch-expression.5 audit-1.6.7/docs/ausearch-expression.5 --- audit/docs/ausearch-expression.5 1970-01-01 01:00:00.000000000 +0100 +++ audit-1.6.7/docs/ausearch-expression.5 2008-02-12 16:24:47.000000000 +0100 @@ -0,0 +1,212 @@ +.TH "AUSEARCH-EXPRESSION" "5" "Feb 2008" "Red Hat" "Linux Audit" +.SH NAME +ausearch-expression \- audit search expression format + +.SH OVERVIEW +This man page describes the format of "ausearch expressions". +Parsing and evaluation of these expressions is provided by libauparse +and is common to applications that use this library. + +.SH LEXICAL STRUCTURE + +White space (ASCII space, tab and new-line characters) between tokens is +ignored. +The following tokens are recognized: + +.TP +Punctuation +.B ( ) \e + +.TP +Logical operators +.B ! && || + +.TP +Comparison operators +.B < <= == > >= !== i= i!= r= r!= + +.TP +Unquoted strings +Any non-empty sequence of ASCII letters, digits, and the +.B _ +symbol. + +.TP +Quoted strings +A sequence of characters surrounded by the +.B \(dq +quotes. +The +.B \e +character starts an escape sequence. +The only defined escape sequences are +.B \e\e +and \fB\e\(dq\fR. +The semantics of other escape sequences is undefined. + +.PP +Anywhere an unquoted string is valid, a quoted string is valid as well, +and vice versa. +In particular, field names may be specified using quoted strings, +and field values may be specified using unquoted strings. + +.SH EXPRESSION SYNTAX + +The primary expression has the following form: +.IP +.I field comparison-operator value +.PP +.I field +is either a string, +which specifies the first field with that name within the current audit record, +or the +.B \e +escape character followed by a string, +which specifies a virtual field with the specified name +(virtual fields are defined in a later section). + +.I field +is a string. +.I operator +specifies the comparison to perform + +.TP +.B r= r!= +Get the "raw" string of \fIfield\fR, +and compare it to \fIvalue\fR. +For fields in audit records, +the "raw" string is the exact string stored in the audit record +(with all escaping and unprintable character encoding left alone); +applications can read the "raw" string using +.BR auparse_get_field_str (3). +Each virtual field may define a "raw" string. +If +.I field +is not present or does not define a "raw" string, +the result of the comparison is +.B false +(regardless of the operator). + +.TP +.B i= i!= +Get the "interpreted" string of \fIfield\fR, +and compare it to \fIvalue\fR. +For fields in audit records, +the "interpreted" string is an "user-readable" interpretation of the field +value; +applications can read the "interpreted" string using +.BR auparse_interpret_field (3). +Each virtual field may define an "interpreted" string. +If +.I field +is not present or does not define an "interpreted" string, +the result of the comparison is +.B false +(regardless of the operator). + +.TP +.B < <= == > >= !== +Evaluate the "value" of \fIfield\fR, and compare it to \fIvalue\fR. +A "value" may be defined for any field or virtual field, +but no "value" is currently defined for any audit record field. +The rules of parsing \fIvalue\fR for comparing it with the "value" of +.I field +are specific for each \fIfield\fR. +If +.I field +is not present, +the result of the comparison is +.B false +(regardless of the operator). +If +.I field +does not define a "value", an error is reported when parsing the expression. + +.PP +If +.I E1 +and +.I E2 +are valid expressions, +then +.B ! +\fIE1\fR, +.I E1 +.B && +\fIE2\fR, and +.I E1 +.B || +.I E2 +are valid expressions as well, with the usual C semantics and evaluation +priorities. +Note that +.B ! +.I field op value +is interpreted as \fB!(\fIfield op value\fB)\fR, not as +\fB(!\fIfield\fB)\fI op value\fR. + +.SH VIRTUAL FIELDS + +The following virtual fields are defined: + +.TP +.B \etimestamp +The value is the timestamp of the current event. +.I value +must have the \fBts:\fIseconds\fR.\fImilli\fR format, where +.I seconds +and +.I milli +are decimal numbers specifying the seconds and milliseconds part of the +timestamp, respectively. + +.TP +.B \erecord_type +The value is the type of the current record. +.I value +is either the record type name, or a decimal number specifying the type. + +.SH SEMANTICS +The expression as a whole applies to a single record. +The expression is +.B true +for a specified event if it is +.B true +for any record associated with the event. + +.SH EXAMPLES + +As a demonstration of the semantics of handling missing fields, the following +expression is +.B true +if +.I field +is present: +.IP +.B (\fIfield\fB r= \(dq\(dq) || (\fIfield\fB r!= \(dq\(dq) +.PP +and the same expression surrounded by +.B !( +and +.B ) +is +.B true +if +.I field +is not present. + +.SH FUTURE DIRECTIONS +New escape sequences for quoted strings may be defined. + +For currently defined virtual fields that do not define a "raw" or +"interpreted" string, the definition may be added. +Therefore, don't rely on the fact +that comparing the "raw" or "interpreted" string of the field with any value +is \fBfalse\fR. + +New formats of value constants for the +.B \etimestamp +virtual field may be added. + +.SH AUTHOR +Miloslav Trmac diff --exclude .deps --exclude '*.o' --exclude '*~' -urN audit/docs/Makefile.am audit-1.6.7/docs/Makefile.am --- audit/docs/Makefile.am 2008-01-13 20:46:39.000000000 +0100 +++ audit-1.6.7/docs/Makefile.am 2008-02-12 16:37:16.000000000 +0100 @@ -43,8 +43,10 @@ auparse_get_type.3 auparse_init.3 auparse_interpret_field.3 \ auparse_next_event.3 auparse_next_field.3 auparse_next_record.3 \ auparse_node_compare.3 auparse_reset.3 auparse_timestamp_compare.3 \ +ausearch-expression.5 \ aureport.8 ausearch.8 ausearch_add_item.3 ausearch_add_interpreted_item.3 \ -ausearch_add_timestamp_item.3 ausearch_add_regex.3 ausearch_clear.3 \ +ausearch_add_expression.3 ausearch_add_timestamp_item.3 ausearch_add_regex.3 \ +ausearch_clear.3 \ ausearch_next_event.3 ausearch_set_stop.3 \ autrace.8 get_auditfail_action.3 set_aumessage_mode.3 \ audispd.8 audispd.conf.5 audispd-zos-remote.8 zos-remote.conf.5 \