diff options
Diffstat (limited to 'framework/src/audit/auparse/expression.c')
-rw-r--r-- | framework/src/audit/auparse/expression.c | 1111 |
1 files changed, 1111 insertions, 0 deletions
diff --git a/framework/src/audit/auparse/expression.c b/framework/src/audit/auparse/expression.c new file mode 100644 index 00000000..6bed45ba --- /dev/null +++ b/framework/src/audit/auparse/expression.c @@ -0,0 +1,1111 @@ +/* +* expression.c - Expression parsing and handling +* Copyright (C) 2008,2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Miloslav Trmač <mitr@redhat.com> +* Steve Grubb <sgrubb@redhat.com> extended timestamp +*/ + +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#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; + + case EO_REGEXP_MATCHES: + regfree(expr->v.regexp); + free(expr->v.regexp); + 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 + comparison-expression: field-escape "regexp" regexp-value + field: string + field: field-escape string + value: string + regexp-value: string + regexp-value: regexp */ + +/* Token types */ +enum token_type { + /* EO_* */ + T_LEFT_PAREN = NUM_EO_VALUES, T_RIGHT_PAREN, T_STRING, T_REGEXP, + 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 '"': case '/': { + char *buf, delimiter; + size_t dest, buf_size; + + delimiter = *p->src; + buf_size = 8; + buf = parser_malloc(p, buf_size); + if (buf == NULL) + return -1; + p->src++; + dest = 0; + while (*p->src != delimiter) { + if (*p->src == '\0') { + *p->error = strdup("Terminating delimiter " + "missing"); + free(buf); + return -1; + } + if (*p->src == '\\') { + p->src++; + if (*p->src != '\\' && *p->src != delimiter) { + if (asprintf(p->error, "Unknown escape " + "sequence ``\\%c''", + *p->src) < 0) + *p->error = NULL; + 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("Delimited 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 = delimiter == '/' ? T_REGEXP : 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 if (strcmp(name, "timestamp_ex") == 0) + *dest = EF_TIMESTAMP_EX; + 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? */ + if (sscanf(p->token_value, "ts:%jd.%u:%u", &sec, + &dest->v.p.value.timestamp_ex.milli, + &dest->v.p.value.timestamp_ex.serial) != 3) { + if (sscanf(p->token_value, "ts:%jd.%u", &sec, + &dest->v.p.value.timestamp.milli) != 2) { + if (asprintf(p->error, "Invalid timestamp value `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + return -1; + } + } + /* FIXME: validate milli */ + dest->v.p.value.timestamp.sec = sec; + if (dest->v.p.value.timestamp.sec != sec) { + if (asprintf(p->error, "Timestamp overflow in `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + 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) { + if (asprintf(p->error, "Invalid record type `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + 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); + + case EF_TIMESTAMP_EX: + return parse_timestamp_value(dest, p); + + default: + abort(); + } +} + +/* Parse a \regexp comparison-expression string in *P, with \regexp parsed. + Use or free EXPR. + 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_regexp(struct parsing *p, struct expr *res) +{ + int err; + + if (lex(p) != 0) + goto err_res; + if (p->token != T_STRING && p->token != T_REGEXP) { + if (asprintf(p->error, "Regexp expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + goto err_res; + } + res->v.regexp = parser_malloc(p, sizeof(*res->v.regexp)); + if (res->v.regexp == NULL) + goto err_res; + err = regcomp(res->v.regexp, p->token_value, REG_EXTENDED | REG_NOSUB); + if (err != 0) { + size_t err_size; + char *err_msg; + + err_size = regerror(err, res->v.regexp, NULL, 0); + err_msg = parser_malloc(p, err_size); + if (err_msg == NULL) + goto err_res_regexp; + regerror(err, res->v.regexp, err_msg, err_size); + if (asprintf(p->error, "Invalid regexp: %s", err_msg) < 0) + *p->error = NULL; + free(err_msg); + goto err_res_regexp; + } + res->op = EO_REGEXP_MATCHES; + if (lex(p) != 0) { + expr_free(res); + return NULL; + } + return res; + +err_res_regexp: + free(res->v.regexp); +err_res: + free(res); + return NULL; +} + +/* 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; + } + if (strcmp(p->token_value, "regexp") == 0) + return parse_comparison_regexp(p, res); + res->virtual_field = 1; + if (parse_escaped_field_name(&res->v.p.field.id, p->token_value) + != 0) { + if (asprintf(p->error, + "Unknown escaped field name `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + 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) { + if (asprintf(p->error, "Value expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + 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) { + if (asprintf(p->error, "Value expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + goto err_field; + } + if (res->virtual_field == 0) { + if (asprintf(p->error, "Field `%s' does not support " + "value comparison", + res->v.p.field.name) < 0) + *p->error = NULL; + 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: + if (asprintf(p->error, "Operator expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + 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) { + if (asprintf(p->error, + "Right paren expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + goto err_e; + } + if (lex(p) != 0) + goto err_e; + return e; + } + + case T_FIELD_ESCAPE: case T_STRING: + return parse_comparison(p); + + default: + if (asprintf(p->error, "Unexpected token `%.*s'", p->token_len, + p->token_start) < 0) + *p->error = NULL; + return NULL; + } +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); + if (asprintf(error, "Unexpected trailing token `%.*s'", + p.token_len, p.token_start) < 0) + *error = NULL; + 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 an extended timestamp comparison-expression for with OP, SEC, + MILLI, and SERIAL. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_timestamp_comparison_ex(unsigned op, time_t sec, unsigned milli, + unsigned serial) +{ + 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_EX; + res->precomputed_value = 1; + res->v.p.value.timestamp_ex.sec = sec; + assert(milli < 1000); + res->v.p.value.timestamp_ex.milli = milli; + res->v.p.value.timestamp_ex.serial = serial; + return res; +} + +/* 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) +{ + return expr_create_timestamp_comparison_ex(op, sec, milli, 0); +} + +/* 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 \regexp expression for regexp comparison. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_regexp_expression(const char *regexp) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + goto err; + res->v.regexp = malloc(sizeof(*res->v.regexp)); + if (res->v.regexp == NULL) + goto err_res; + if (regcomp(res->v.regexp, regexp, REG_EXTENDED | REG_NOSUB) != 0) { + errno = EINVAL; + goto err_res_regexp; + } + res->op = EO_REGEXP_MATCHES; + return res; + +err_res_regexp: + free(res->v.regexp); +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) { + nvlist_first(&record->nv); + 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: case EF_TIMESTAMP_EX: + 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; + + nvlist_first(&record->nv); + 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: case EF_TIMESTAMP_EX: + 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; + + case EF_TIMESTAMP_EX: + 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 if (au->le.e.serial < expr->v.p.value.timestamp_ex.serial) + res = -1; + else if (au->le.e.serial > expr->v.p.value.timestamp_ex.serial) + 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); + nvlist_first(&record->nv); + return nvlist_find_name(&record->nv, expr->v.p.field.name) != 0; + + case EO_REGEXP_MATCHES: + return regexec(expr->v.regexp, record->record, 0, NULL, 0) == 0; + + default: + abort(); + } +} |