Skip to contentSkip to conversation

ausearch improvements

PATCH In review Miloslav Trmac 2 participants 2 messages Nov 18, 2007 – Nov 19, 2007

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ausearch improvements

Hello,
the attached patch adds some functionality to ausearch. It consists of two main parts:

ausearch_add_interpreted_item() behaves like ausearch_add_item(), but the conditions are evaluated by comparing the interpreted field value, not the raw value (e.g. ("uid", "=", "mitr") instead of ("uid", "=", "500"). In principle, the application using ausearch could contain it's own code to "un-interpret" field values, but I think it is cleaner when the only place that maps raw and interpreted value is libauparse. The current implementation simply interprets the value of each field before performing the comparison; in the future, the implementation could be changed to "un-interpret" the supplied value when creating the rule if the current implementation turns out to be too slow.

ausearch_add_timestamp_item() allows placing conditions on event timestamp.

In addition, the patch fixes checking whether the operator is unknown in ausearch_add_item().
Mirek

auparse/auparse.c +140 -22
diff -urN audit/auparse/auparse.c audit-1.6.2/auparse/auparse.c
@@ -316,12 +316,13 @@
 }
 
 
-int ausearch_add_item(auparse_state_t *au, const char *field, const char *op,
-	const char *value, ausearch_rule_t how)
+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)
 {
 	aurulenode rn;
 	int t_op = lookup_op(op);
-	if (op < 0)
+	if (t_op < 0)
 		goto err_out;
 
 	// Make sure there's a field
@@ -344,15 +345,85 @@
 	}
 
 	// All pre-checks are done, build a rule
-	rn.search_op = t_op;
+	rn.search_op = t_op | op_flag;
 	if (t_op == AUSEARCH_EXISTS)
-		rn.val = NULL;
+		rn.v.field.val = NULL;
 	else {
 		if (value == NULL)
 			goto err_out;
-		rn.val = strdup(value);
+		rn.v.field.val = strdup(value);
+	}
+	rn.v.field.field = strdup(field);
+	aurule_append(&au->rules, &rn);
+	au->search_how = how;
+
+	return 0;
+err_out:
+	errno = EINVAL;
+	return -1;
+}
+
+int ausearch_add_item(auparse_state_t *au, const char *field, const char *op,
+	const char *value, ausearch_rule_t how)
+{
+	return ausearch_add_item_internal(au, field, op, value, how, 0);
+}
+
+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);
+}
+
+static int lookup_ts_op(const char *op)
+{
+	static const struct nv_pair ts_tab[] = {
+		{AUSEARCH_TIME_LT, "<"},
+		{AUSEARCH_TIME_LE, "<="},
+		{AUSEARCH_TIME_GE, ">="},
+		{AUSEARCH_TIME_GT, ">"},
+		{AUSEARCH_TIME_EQ, "="},
+	};
+
+        size_t 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;
+}
+
+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)
+		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;
 	}
-	rn.field = strdup(field);
+
+	// 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;
 
@@ -381,9 +452,9 @@
 	}
 
 	// Store it away just in case.
-	rn.field = strdup(expr);
 	rn.search_op = AUSEARCH_UNSET;
-	rn.val = NULL;
+	rn.v.field.field = strdup(expr);
+	rn.v.field.val = NULL;
 	aurule_append(&au->rules, &rn);
 	au->search_how = AUSEARCH_RULE_REGEX;
 
@@ -780,14 +851,71 @@
 	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;
+
+		if ((rule->search_op & AUSEARCH_INTERPRETED) != 0)
+			val = nvlist_interp_cur_val(r);
+		else
+			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;
-	int rc;
 
 	if (au->search_how == AUSEARCH_RULE_REGEX) {
+		int rc;
+
 		if (au->rules.cnt == 0) {
 			errno = EINVAL;
 			return -1;
@@ -805,22 +933,12 @@
 		int results = (au->search_how == AUSEARCH_RULE_AND);
 		aurule_first(&au->rules);
 		do {
-			int tmp = 0;
+			int tmp;
 
 			aurulenode *rule = aurule_get_cur(&au->rules);
 			if (rule == NULL) 
 				continue;
-			rc = nvlist_find_name(&r->nv, rule->field);
-			if (rc) {
-				if (rule->search_op == AUSEARCH_EXISTS)
-					return 1;
-				rc = strcmp(rule->val,
-					    nvlist_get_cur_val(&r->nv));
-				if (rule->search_op == AUSEARCH_EQUAL)
-					tmp = (rc == 0);
-				else
-					tmp = (rc != 0);
-			}
+			tmp = ausearch_interpret_rule(au, r, rule);
 			if (au->search_how == AUSEARCH_RULE_AND) {
 				if (tmp == 0)
 					return 0; /* all must match */
auparse/auparse-defs.h +13 -2
diff -urN audit/auparse/auparse-defs.h audit-1.6.2/auparse/auparse-defs.h
@@ -38,8 +38,19 @@
 	AUSOURCE_DESCRIPTOR, AUSOURCE_FILE_POINTER, AUSOURCE_FEED } ausource_t;
 
 /* This defines the types of searches that can be done */
-typedef enum { AUSEARCH_UNSET, AUSEARCH_EXISTS, AUSEARCH_EQUAL,
-        AUSEARCH_NOT_EQUAL } ausearch_op_t;
+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,
auparse/auparse.h +4 -0
diff -urN audit/auparse/auparse.h audit-1.6.2/auparse/auparse.h
@@ -53,6 +53,10 @@
 /* Functions that are part of the search interface */
 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,
+			const char *op, const char *value, ausearch_rule_t how);
+int ausearch_add_timestamp_item(auparse_state_t *au, const char *op, time_t sec,
+				unsigned milli, ausearch_rule_t how);
 int ausearch_add_regex(auparse_state_t *au, const char *expr);
 int ausearch_set_stop(auparse_state_t *au, austop_t where);
 void ausearch_clear(auparse_state_t *au);
auparse/rlist.c +5 -5
diff -urN audit/auparse/rlist.c audit-1.6.2/auparse/rlist.c
@@ -52,9 +52,7 @@
 {
 	aurulenode* newnode = malloc(sizeof(aurulenode));
 
-	newnode->field = node->field;
-	newnode->search_op = node->search_op;
-	newnode->val = node->val;
+	*newnode = *node;
 	newnode->item = l->cnt; 
 	newnode->next = NULL;
 
@@ -85,8 +83,10 @@
 	current = l->head;
 	while (current) {
 		nextnode=current->next;
-		free(current->field);
-		free(current->val);
+		if (!AUSEARCH_OP_IS_TIME__(current->search_op)) {
+			free(current->v.field.field);
+			free(current->v.field.val);
+		}
 		free(current);
 		current=nextnode;
 	}
auparse/rlist.h +11 -3
diff -urN audit/auparse/rlist.h audit-1.6.2/auparse/rlist.h
@@ -31,10 +31,18 @@
 /* This is the node of the linked list. Any data elements that are
  * per item goes here. */
 typedef struct _aurulenode{
-  char *field;			// The field name string
   ausearch_op_t search_op;	// What search op is being done
-  char *val;			// The value field
-  unsigned int item;		// Which item of the same event
+  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;
 
bindings/python/auparse_python.c +134 -0

152 lines not on this page · raw message

docs/ausearch_add_interpreted_item.3 +56 -0

56 lines not on this page · raw message

docs/ausearch_add_item.3 +5 -1
diff -urN audit/docs/ausearch_add_item.3 audit-1.6.2/docs/ausearch_add_item.3
@@ -1,4 +1,4 @@
-.TH "AUSEARCH_ADD_ITEM" "3" "Feb 2007" "Red Hat" "Linux Audit API"
+.TH "AUSEARCH_ADD_ITEM" "3" "Nov 2007" "Red Hat" "Linux Audit API"
 .SH NAME
 ausearch_add_item \- build up search rule
 .SH "SYNOPSIS"
@@ -22,6 +22,8 @@
  locate the field name and check that the value associated with it is NOT equal to the value given in this rule.
 .RE
 
+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:
 .RS
 .TP
@@ -43,6 +45,8 @@
 
 .SH "SEE ALSO"
 
+.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), 
docs/ausearch_add_timestamp_item.3 +50 -0

50 lines not on this page · raw message

docs/Makefile.am +3 -2
diff -urN audit/docs/Makefile.am audit-1.6.2/docs/Makefile.am
@@ -43,8 +43,9 @@
 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 \
-aureport.8 ausearch.8 ausearch_add_item.3 ausearch_add_regex.3 \
-ausearch_clear.3 ausearch_next_event.3 ausearch_set_stop.3 \
+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_next_event.3 ausearch_set_stop.3 \
 autrace.8 get_auditfail_action.3 set_aumessage_mode.3 \
 audispd.8 audispd.conf.5
 
#1 lore raw
Quoted 2 lines · Miloslav Trmac

the attached patch adds some functionality to ausearch.  It consists of two main parts:

Merged. Thanks for the patch!

-Steve