Hello all.

As part of developing an audit viewing "plugin"[1] to Spacewalk[2], I wrote a small program to use libauparse to output (easily) machine-parsable audit logs. I think this functionality would be nice to have in ausearch, and as such, wrote a patch for it.

As well as reviewing this patch, I would like your feedback concerning the Spacewalk audit plugin. Any questions or constructive criticism is welcome.

Thanks,

Joshua Roys

[1]
https://www.redhat.com/archives/spacewalk-devel/2009-May/msg00121.html
http://www.stl.gtri.gatech.edu/jroys/
[2]
http://www.redhat.com/spacewalk/
diff --git a/src/ausearch-common.h b/src/ausearch-common.h
index 179e127..2abd039 100644
--- a/src/ausearch-common.h
+++ b/src/ausearch-common.h
@@ -44,7 +44,8 @@ extern int event_exit, event_exit_is_set;
 typedef enum { F_BOTH, F_FAILED, F_SUCCESS } failed_t;
 typedef enum { C_NEITHER, C_ADD, C_DEL } conf_act_t;
 typedef enum { S_UNSET=-1, S_FAILED, S_SUCCESS } success_t;
-typedef enum { RPT_RAW, RPT_DEFAULT, RPT_INTERP, RPT_PRETTY } report_t;
+typedef enum { RPT_RAW, RPT_DEFAULT, RPT_INTERP, RPT_PRETTY, RPT_MACHINEINTERP
+} report_t;
 
 extern failed_t event_failed;
 extern conf_act_t event_conf_act;
diff --git a/src/ausearch-options.c b/src/ausearch-options.c
index 1f64b49..9edd59f 100644
--- a/src/ausearch-options.c
+++ b/src/ausearch-options.c
@@ -74,7 +74,7 @@ S_HOSTNAME, S_INTERP, S_INFILE, S_MESSAGE_TYPE, S_PID, 
S_SYSCALL, S_OSUCCESS,
 S_TIME_END, S_TIME_START, S_TERMINAL, S_ALL_UID, S_EFF_UID, S_UID, S_LOGINID,
 S_VERSION, S_EXACT_MATCH, S_EXECUTABLE, S_CONTEXT, S_SUBJECT, S_OBJECT,
 S_PPID, S_KEY, S_RAW, S_NODE, S_IN_LOGS, S_JUST_ONE, S_SESSION, S_EXIT,
-S_LINEBUFFERED };
+S_LINEBUFFERED, S_MACHINEINTERP };
 
 static struct nv_pair optiontab[] = {
        { S_EVENT, "-a" },
@@ -97,6 +97,7 @@ static struct nv_pair optiontab[] = {
        { S_HOSTNAME, "--host" },
        { S_INTERP, "-i" },
        { S_INTERP, "--interpret" },
+       { S_MACHINEINTERP, "-I" },
        { S_INFILE, "-if" },
        { S_INFILE, "--input" },
        { S_IN_LOGS, "--input-logs" },
@@ -173,6 +174,7 @@ static void usage(void)
        "\t-h,--help\t\t\thelp\n"
        "\t-hn,--host <Host Name>\t\tsearch based on remote host name\n"
        "\t-i,--interpret\t\t\tInterpret results to be human readable\n"
+       "\t-I\t\t\t\tInterpret results to be machine readable\n"
        "\t-if,--input <Input File name>\tuse this file instead of current 
logs\n"
        "\t--input-logs\t\t\tUse the logs even if stdin is a pipe\n"
        "\t--just-one\t\t\tEmit just one event\n"
@@ -474,6 +476,22 @@ int check_params(int count, char *vars[])
                                retval = -1;
                        }
                        break;
+               case S_MACHINEINTERP:
+                       if (report_format == RPT_DEFAULT)
+                               report_format = RPT_MACHINEINTERP;
+                       else {
+                               fprintf(stderr, 
+                                       "Conflicting output format %s\n",
+                                       vars[c]);
+                               retval = -1;
+                       }
+                       if (optarg) {
+                               fprintf(stderr, 
+                                       "Argument is NOT required for %s\n",
+                                       vars[c]);
+                               retval = -1;
+                       }
+                       break;
                case S_INFILE:
                        if (!optarg) {
                                fprintf(stderr, 
diff --git a/src/ausearch-report.c b/src/ausearch-report.c
index 6bb5c1a..e88b2e2 100644
--- a/src/ausearch-report.c
+++ b/src/ausearch-report.c
@@ -59,6 +59,8 @@ extern char *unescape(char *buf);
 /* Local functions */
 static void output_raw(llist *l);
 static void output_default(llist *l);
+static void output_machineinterp(llist *l);
+static void output_machineinterp_node(const lnode *n);
 static void output_interpreted(llist *l);
 static void output_interpreted_node(const lnode *n);
 static void interpret(char *name, char *val, int comma, int rtype);
@@ -84,6 +86,9 @@ void output_record(llist *l)
                        break;
                case RPT_PRETTY:
                        break;
+               case RPT_MACHINEINTERP:
+                       output_machineinterp(l);
+                       break;
                default:
                        fprintf(stderr, "Report format error");
                        exit(1);
@@ -131,6 +136,233 @@ static void output_default(llist *l)
 }
 
 /*
+ * This function will take the linked list and format it for output such that
+ * the output is both interpreted and yet easily machine-parsable (perl, etc).
+ */
+static void output_machineinterp(llist *l)
+{
+       const lnode *n;
+
+       list_last(l);
+       n = list_get_cur(l);
+       if (!n) {
+               fprintf(stderr, "Error - no elements in record.");
+               return;
+       }
+       if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) 
+               output_machineinterp_node(n);
+       else {
+               do {
+                       output_machineinterp_node(n);
+                       printf("----\n");
+               } while ((n=list_prev(l)));
+       }
+}
+
+/*
+ * This function will cycle through a message and lookup each type that 
+ * it finds. 
+ */
+static void output_machineinterp_node(const lnode *n)
+{
+       char *ptr, *str = n->message, *node = NULL;
+
+       /* Check and see if we start with a node */
+       if (str[0] == 'n') {
+               ptr=strchr(str, ' ');
+               if (ptr) {
+                       *ptr = 0;
+                       node = str;
+                       str = ptr+1;
+               }
+       }
+
+       // First locate time stamp.
+       ptr = strchr(str, '(');
+       if (ptr == NULL) {
+               fprintf(stderr, "can't find time stamp\n");
+               return;
+       } else {
+               time_t t;
+               int milli,num = n->type;
+               unsigned long serial;
+               char *eptr;
+               const char *bptr;
+
+               *ptr++ = 0;
+               if (num == -1) {
+                       // see if we are older and wiser now.
+                       bptr = strchr(str, '[');
+                       if (bptr && bptr < ptr) {
+                               bptr++;
+                               eptr = strchr(bptr, ']');
+                               if (eptr) {
+                                       *eptr = 0;
+                                       errno = 0;
+                                       num = strtoul(bptr, NULL, 10);
+                                       *eptr = ']';
+                                       if (errno) 
+                                               num = -1;
+                               }
+                       }
+               }
+
+               // print everything up to it.
+               if (num >= 0) {
+                       bptr = audit_msg_type_to_name(num);
+                       if (bptr) {
+                               if (node)
+                                       printf("node=%s\n", node);
+                               printf("type=%s\n", bptr);
+                               goto no_print;
+                       }
+               } 
+               if (node)
+                       printf("node=%s\n", node);
+               eptr = strchr(str, ' ');
+               if (eptr)
+                       *eptr = 0;
+               printf("%s\n", str);
+no_print:
+
+               str = strchr(ptr, '.');
+               if (str == NULL)
+                       return;
+               *str++ = 0;
+               errno = 0;
+               t = strtoul(ptr, NULL, 10);
+               if (errno)
+                       return;
+               ptr = strchr(str, ':');
+               if (ptr == NULL)
+                       return;
+               *ptr++ = 0;
+               milli = strtoul(str, NULL, 10);
+               if (errno)
+                       return;
+               str = strchr(ptr, ')');
+               if(str == NULL)
+                       return;
+               *str++ = 0;
+               serial = strtoul(ptr, NULL, 10);
+               if (errno)
+                       return;
+               printf("serial=%lu\n", serial);
+               printf("seconds=%u\n", t);
+               printf("milli=%i\n", milli);
+               str = strchr(str, ':');
+               if (str) {
+                       str++;
+                       if (*str == ' ')
+                               str++;
+               }
+       }
+
+       if (n->type == AUDIT_SYSCALL) 
+               a0 = n->a0;
+
+       // for each item.
+       while (str && *str && (ptr = strchr(str, '='))) {
+               char *name, *val;
+               int comma = 0;
+
+               // look back to last space - this is name
+               name = ptr;
+               while (*name != ' ' && name > str)
+                       --name;
+               *ptr++ = 0;
+               if (*name == ' ')
+                       name++;
+
+               // Some user messages have msg='uid=500   in this case
+               // skip the msg= piece since the real stuff is the uid=
+               if (strcmp(name, "msg") == 0) {
+                       // msg='text... (more text)'
+                       // kill the )' on the end
+                       str = strrchr(ptr+1, ')');
+                       if (str && str[1] == '\'' && str[2] == 0)
+                               *str = 0;
+                       str = ptr+1;
+                       continue;
+               }
+
+               // handle avc: ... messages
+               if (strncmp(str, "avc: ", 5) == 0) {
+                       char *seresult, *seperms;
+
+                       // since we know there is 'avc: ', skip it
+                       // and all following spaces
+                       seresult = str + 5;
+                       while (*seresult == ' ')
+                               seresult++;
+
+                       // find the { seperms } section
+                       seperms = strchr(seresult, '{');
+                       if (!seperms)
+                               return;
+
+                       // go backward from there killing all trailing spaces
+                       str = seperms - 1;
+                       while (*str == ' ')
+                               *str-- = 0;
+                       seperms++;
+
+                       // find the end of the seperms
+                       str = strchr(seperms, '}');
+                       if (!str)
+                               return;
+                       *str-- = 0;
+
+                       // kill spaces on both sides of seperms
+                       while (*str == ' ')
+                               str--;
+                       while (*seperms == ' ')
+                               seperms++;
+
+                       printf("seresult=%s\n", seresult);
+                       printf("seperms=%s\n", seperms);
+
+                       str = name;
+               }
+
+               while (*str == '(' || *str == ' ')
+                       str++;
+
+               // print everything up to the '='
+               printf("%s=", str);
+
+               // get string after = to the next space or end - this is value
+               if (*ptr == '\'' || *ptr == '"') {
+                       str = strchr(ptr+1, *ptr);
+                       if (str) {
+                               str++;
+                               if (*str)
+                                       *str++ = 0;
+                       }
+               } else {
+                       str = strchr(ptr, ',');
+                       val = strchr(ptr, ' ');
+                       if (str && val && (str < val)) {
+                               *str++ = 0;
+                               comma = 1;
+                       } else if (str && (val == NULL)) {
+                               *str++ = 0;
+                               comma = 1;
+                       } else if (val) {
+                               str = val;
+                               *str++ = 0;
+                       }
+               }
+               // val points to begin & str 1 past end
+               val = ptr;
+
+               // print interpreted string
+               interpret(name, val, comma, n->type);
+               printf("\n");
+       }
+}
+
+/*
  * This function will take the linked list and format it for output. 
  * Interpretation is performed to aid understanding of records. The output
  * order is lifo for everything.
@@ -978,7 +1210,8 @@ static void interpret(char *name, char *val, int comma, 
int rtype)
                        print_tty_data(val);
                        break;
                default:
-                       printf("%s%c", val, comma ? ',' : ' ');
+                       printf("%s%c", val, comma && report_format !=
+                               RPT_MACHINEINTERP ? ',' : ' ');
        }
 }
 
--
Linux-audit mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/linux-audit

Reply via email to