Auparse Normalizer
The auparse library has gained a new API starting around audit-2.7. It wasn't really ready for people to use until around 2.7.2 and still under active development. Its close enough to done that we can start using it in other programs.
In essence, all that is different in using the normalizer is that you call auparse_normalize() and then use a different field accessor function. You do not need to use auparse_find_field().
The field accessor functions come in 2 types. There are some that return a character string, and there are those that return an integer. The ones that return a string are returning metadata about the event and can be used directly. The ones that return an integer only move the auparse internal cursor to the correct field. Then you use the field just as we have in earlier programs by calling auparse_interpret_field(), or auparse_get_field_str(), or auparse_get_field_type(), etc.
The normalizer API will locate and save the field locations for the event's
- session
- subject's primary identity
- subject's secondary identity
- object's primary identity
- object's secondary identity
- second object
- results
- key
It also provides metadata about the
- kind of event
- kind of subject (privileged, daemon, user)
- action being performed by subject
- kind of object
- how the event was being performed
To illustrate how to use the API, consider the following program. Notice that we loop at the event level and do not need to iterate across records or fields.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <auparse.h>
int main(int argc, char *argv[])
{
auparse_state_t *au = auparse_init(AUSOURCE_DESCRIPTOR, 0);
if (au == NULL) {
printf("Error initializing event source\n");
return 1;
}
while (auparse_next_event(au) > 0) {
// Event level
const char *item, *evkind, *action, *str, *how, *field;
printf("---\n");
// Type:
printf("event: %s\n", auparse_get_type_name(au));
// Do normalization
if (auparse_normalize(au, NORM_OPT_NO_ATTRS) == 1) {
printf("error normalizing - skipping\n");
continue;
}
// Event kind
evkind = auparse_normalize_get_event_kind(au);
if (evkind)
printf(" event-kind: %s\n", evkind);
// Session
if (auparse_normalize_session(au) == 1) {
const char *ses = auparse_interpret_field(au);
printf(" session: %s\n", ses);
}
// Subject
if (auparse_normalize_subject_primary(au) == 1) {
const char *subj = auparse_interpret_field(au);
field = auparse_get_field_name(au);
if (strcmp(subj, "unset") == 0)
subj = "system";
printf(" subject.primary: %s=%s\n", field, subj);
}
if (auparse_normalize_subject_secondary(au) == 1) {
const char *subj = auparse_interpret_field(au);
field = auparse_get_field_name(au);
printf(" subject.secondary: %s=%s\n", field, subj);
}
// Action
action = auparse_normalize_get_action(au);
if (action) {
printf(" action: %s\n", action);
}
// Object
if (auparse_normalize_object_primary(au) == 1) {
field = auparse_get_field_name(au);
printf(" object.primary: %s=%s\n", field,
auparse_interpret_field(au));
}
if (auparse_normalize_object_secondary(au) == 1 ) {
field = auparse_get_field_name(au);
printf(" object.secondary:%s=%s\n", field,
auparse_interpret_field(au));
}
// Object kind
str = auparse_normalize_object_kind(au);
if (strcmp(str, "unknown"))
printf(" object-kind: %s\n", str);
// How action was performed
how = auparse_normalize_how(au);
if (how)
printf(" how: %s\n", how ? how : "missing-how");
if (auparse_normalize_key(au) == 1) {
const char *key = auparse_interpret_field(au);
printf(" key: %s\n", key);
}
// Results
if (auparse_normalize_get_results(au) == 1) {
int i = 0;
const char *res[] = { "fail", "success" };
item = auparse_interpret_field(au);
if (strcmp(item, "yes") == 0)
i = 1;
else if (strncmp(item, "suc", 3) == 0)
i = 1;
printf(" results: %s\n", res[i]);
}
}
printf("---\n");
auparse_destroy(au);
return 0;
}
#include <string.h>
#include <stdlib.h>
#include <auparse.h>
int main(int argc, char *argv[])
{
auparse_state_t *au = auparse_init(AUSOURCE_DESCRIPTOR, 0);
if (au == NULL) {
printf("Error initializing event source\n");
return 1;
}
while (auparse_next_event(au) > 0) {
// Event level
const char *item, *evkind, *action, *str, *how, *field;
printf("---\n");
// Type:
printf("event: %s\n", auparse_get_type_name(au));
// Do normalization
if (auparse_normalize(au, NORM_OPT_NO_ATTRS) == 1) {
printf("error normalizing - skipping\n");
continue;
}
// Event kind
evkind = auparse_normalize_get_event_kind(au);
if (evkind)
printf(" event-kind: %s\n", evkind);
// Session
if (auparse_normalize_session(au) == 1) {
const char *ses = auparse_interpret_field(au);
printf(" session: %s\n", ses);
}
// Subject
if (auparse_normalize_subject_primary(au) == 1) {
const char *subj = auparse_interpret_field(au);
field = auparse_get_field_name(au);
if (strcmp(subj, "unset") == 0)
subj = "system";
printf(" subject.primary: %s=%s\n", field, subj);
}
if (auparse_normalize_subject_secondary(au) == 1) {
const char *subj = auparse_interpret_field(au);
field = auparse_get_field_name(au);
printf(" subject.secondary: %s=%s\n", field, subj);
}
// Action
action = auparse_normalize_get_action(au);
if (action) {
printf(" action: %s\n", action);
}
// Object
if (auparse_normalize_object_primary(au) == 1) {
field = auparse_get_field_name(au);
printf(" object.primary: %s=%s\n", field,
auparse_interpret_field(au));
}
if (auparse_normalize_object_secondary(au) == 1 ) {
field = auparse_get_field_name(au);
printf(" object.secondary:%s=%s\n", field,
auparse_interpret_field(au));
}
// Object kind
str = auparse_normalize_object_kind(au);
if (strcmp(str, "unknown"))
printf(" object-kind: %s\n", str);
// How action was performed
how = auparse_normalize_how(au);
if (how)
printf(" how: %s\n", how ? how : "missing-how");
if (auparse_normalize_key(au) == 1) {
const char *key = auparse_interpret_field(au);
printf(" key: %s\n", key);
}
// Results
if (auparse_normalize_get_results(au) == 1) {
int i = 0;
const char *res[] = { "fail", "success" };
item = auparse_interpret_field(au);
if (strcmp(item, "yes") == 0)
i = 1;
else if (strncmp(item, "suc", 3) == 0)
i = 1;
printf(" results: %s\n", res[i]);
}
}
printf("---\n");
auparse_destroy(au);
return 0;
}
Compile this program using:
# gcc -o normalize normalize.c -lauparse
To run the program pass log data to it from stdout
ausearch --start today --raw | ./normalize
You should see some output similar to this:
event: DAEMON_START
event-kind: audit-daemon
session: unset
subject.primary: auid=system
subject.secondary: uid=root
action: started-audit
object-kind: service
results: success
event-kind: audit-daemon
session: unset
subject.primary: auid=system
subject.secondary: uid=root
action: started-audit
object-kind: service
results: success
You can use this program to look over normalized log data to aid in understanding where each of the parts come from.
OK, so how does this fix up the problem we saw last time? Now all we need to do is call auparse_normalize() and then auparse_normalize_subject_primary(). That's it. No conditional.
static void handle_event(auparse_state_t *au,
auparse_cb_event_t cb_event_type, void *user_data)
{
if (cb_event_type != AUPARSE_CB_EVENT_READY)
return;
auparse_first_record(au);
int type = auparse_get_type(au);
if (type == AUDIT_USER_LOGIN) {
char msg[256], *name = NULL;
const char *res = NULL;
/* create a message */
if (!auparse_normalize(au, NORM_OPT_NO_ATTRS)) {
if (auparse_normalize_subject_primary(au) == 1)
name = strdup(auparse_interpret_field(au));
if (auparse_normalize_get_results(au) == 1)
res = auparse_get_field_str(au);
}
snprintf(msg, sizeof(msg), "%s log in %s",
name ? name : "someone", res ? res : "unknown");
/* send a message */
NotifyNotification *n = notify_notification_new(note,
msg, NULL);
notify_notification_set_urgency(n, NOTIFY_URGENCY_NORMAL);
notify_notification_set_timeout(n, 3000); //3 seconds
notify_notification_show (n, NULL);
g_object_unref(G_OBJECT(n));
free(name);
}
}
auparse_cb_event_t cb_event_type, void *user_data)
{
if (cb_event_type != AUPARSE_CB_EVENT_READY)
return;
auparse_first_record(au);
int type = auparse_get_type(au);
if (type == AUDIT_USER_LOGIN) {
char msg[256], *name = NULL;
const char *res = NULL;
/* create a message */
if (!auparse_normalize(au, NORM_OPT_NO_ATTRS)) {
if (auparse_normalize_subject_primary(au) == 1)
name = strdup(auparse_interpret_field(au));
if (auparse_normalize_get_results(au) == 1)
res = auparse_get_field_str(au);
}
snprintf(msg, sizeof(msg), "%s log in %s",
name ? name : "someone", res ? res : "unknown");
/* send a message */
NotifyNotification *n = notify_notification_new(note,
msg, NULL);
notify_notification_set_urgency(n, NOTIFY_URGENCY_NORMAL);
notify_notification_set_timeout(n, 3000); //3 seconds
notify_notification_show (n, NULL);
g_object_unref(G_OBJECT(n));
free(name);
}
}
One thing to know about the code above is that you won't get the right answer until you run it with the audit-2.7.6 library which should be released soon.(I found a bug in the library and corrected it while writing this example program.)
Conclusion
We have talked about using all of the various pieces of the auparse library. It should simplify writing analytical programs. Next time we will tie everything together with a short little project to send an email when certain events occur. I was going to do it in this post but decided it was long enough already.
No comments:
Post a Comment