This patch adds a new tool to extract information related to virtual machines
from the audit log files.
This tool is based on the proposal sent in the RFC:
https://www.redhat.com/archives/linux-audit/2011-November/msg00014.html
---
audit.spec | 2 +
configure.ac | 2 +-
tools/Makefile.am | 2 +-
tools/auvirt/Makefile.am | 40 ++
tools/auvirt/auvirt-list.c | 105 ++++
tools/auvirt/auvirt-list.h | 31 +
tools/auvirt/auvirt.8 | 121 ++++
tools/auvirt/auvirt.c | 1308 ++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 1609 insertions(+), 2 deletions(-)
create mode 100644 tools/auvirt/Makefile.am
create mode 100644 tools/auvirt/auvirt-list.c
create mode 100644 tools/auvirt/auvirt-list.h
create mode 100644 tools/auvirt/auvirt.8
create mode 100644 tools/auvirt/auvirt.c
diff --git a/audit.spec b/audit.spec
index 383bed1..a9940b4 100644
--- a/audit.spec
+++ b/audit.spec
@@ -172,6 +172,7 @@ fi
%attr(644,root,root) %{_mandir}/man8/autrace.8.gz
%attr(644,root,root) %{_mandir}/man8/aulast.8.gz
%attr(644,root,root) %{_mandir}/man8/aulastlog.8.gz
+%attr(644,root,root) %{_mandir}/man8/auvirt.8.gz
%attr(644,root,root) %{_mandir}/man8/ausyscall.8.gz
%attr(644,root,root) %{_mandir}/man7/audit.rules.7.gz
%attr(644,root,root) %{_mandir}/man5/auditd.conf.5.gz
@@ -186,6 +187,7 @@ fi
%attr(755,root,root) %{_bindir}/aulast
%attr(755,root,root) %{_bindir}/aulastlog
%attr(755,root,root) %{_bindir}/ausyscall
+%attr(755,root,root) %{_bindir}/auvirt
%attr(755,root,root) /etc/rc.d/init.d/auditd
%attr(750,root,root) %dir %{_var}/log/audit
%attr(750,root,root) %dir /etc/audit
diff --git a/configure.ac b/configure.ac
index c1cf96f..b7f7fcd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -255,7 +255,7 @@ AC_SUBST(libev_LIBS)
AC_SUBST(LIBPRELUDE_CFLAGS)
AC_SUBST(LIBPRELUDE_LDFLAGS)
-AC_OUTPUT(Makefile lib/Makefile lib/test/Makefile auparse/Makefile auparse/test/Makefile
src/Makefile src/mt/Makefile src/libev/Makefile src/test/Makefile swig/Makefile
docs/Makefile init.d/Makefile audisp/Makefile audisp/plugins/Makefile
audisp/plugins/builtins/Makefile audisp/plugins/prelude/Makefile
audisp/plugins/remote/Makefile audisp/plugins/zos-remote/Makefile bindings/Makefile
bindings/python/Makefile tools/Makefile tools/aulast/Makefile tools/aulastlog/Makefile
tools/ausyscall/Makefile)
+AC_OUTPUT(Makefile lib/Makefile lib/test/Makefile auparse/Makefile auparse/test/Makefile
src/Makefile src/mt/Makefile src/libev/Makefile src/test/Makefile swig/Makefile
docs/Makefile init.d/Makefile audisp/Makefile audisp/plugins/Makefile
audisp/plugins/builtins/Makefile audisp/plugins/prelude/Makefile
audisp/plugins/remote/Makefile audisp/plugins/zos-remote/Makefile bindings/Makefile
bindings/python/Makefile tools/Makefile tools/aulast/Makefile tools/aulastlog/Makefile
tools/ausyscall/Makefile tools/auvirt/Makefile)
echo .
echo "
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 3b7acfe..15ba254 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -22,5 +22,5 @@
CONFIG_CLEAN_FILES = *.loT *.rej *.orig
-SUBDIRS = aulast aulastlog ausyscall
+SUBDIRS = aulast aulastlog ausyscall auvirt
diff --git a/tools/auvirt/Makefile.am b/tools/auvirt/Makefile.am
new file mode 100644
index 0000000..0e9ec5b
--- /dev/null
+++ b/tools/auvirt/Makefile.am
@@ -0,0 +1,40 @@
+#
+# Makefile.am --
+# Copyright (c) 2011 IBM Corp.
+# 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:
+# Marcelo Henrique Cerri <mhcerri(a)br.ibm.com>
+#
+
+CONFIG_CLEAN_FILES = *.loT *.rej *.orig
+AUTOMAKE_OPTIONS = no-dependencies
+EXTRA_DIST = $(man_MANS)
+INCLUDES = -I${top_srcdir} \
+ -I${top_srcdir}/lib \
+ -I${top_srcdir}/auparse \
+ -I${top_srcdir}/src
+LIBS = -L${top_builddir}/auparse \
+ -lauparse
+AM_CFLAGS = -D_GNU_SOURCE
+bin_PROGRAMS = auvirt
+noinst_HEADERS = auvirt-list.h
+man_MANS = auvirt.8
+
+auvirt_SOURCES = auvirt.c \
+ auvirt-list.c \
+ ${top_srcdir}/src/ausearch-time.c
diff --git a/tools/auvirt/auvirt-list.c b/tools/auvirt/auvirt-list.c
new file mode 100644
index 0000000..7502188
--- /dev/null
+++ b/tools/auvirt/auvirt-list.c
@@ -0,0 +1,105 @@
+#include "auvirt-list.h"
+#include <stdlib.h>
+
+list_t *list_init(list_t *list, list_free_data_fn *free_data_fn)
+{
+ if (list == NULL)
+ return NULL;
+ list->head = list->tail = NULL;
+ list->free_data_fn = free_data_fn;
+ return list;
+}
+
+list_t *list_new(list_free_data_fn *free_data_fn)
+{
+ return list_init(malloc(sizeof(list_t)), free_data_fn);
+}
+
+void list_free_node(list_node_t *node, list_free_data_fn *free_data_fn)
+{
+ if (node) {
+ if (free_data_fn)
+ free_data_fn(node->data);
+ free(node);
+ }
+}
+
+void list_free_(list_t *list, list_free_data_fn *free_data_fn)
+{
+ if (list != NULL) {
+ list_node_t *it = list->head;
+ while (it && it->next) {
+ it = it->next;
+ list_free_node(it->prev, free_data_fn);
+ }
+ list_free_node(it, free_data_fn);
+ free(list);
+ }
+}
+
+void list_free(list_t *list)
+{
+ if (list)
+ list_free_(list, list->free_data_fn);
+}
+
+list_node_t *list_insert_after(list_t *list, list_node_t *it,
+ void *data)
+{
+ list_node_t *node = NULL;
+ if (list == NULL)
+ return NULL;
+
+ /* allocate node */
+ node = malloc(sizeof(list_node_t));
+ if (node == NULL)
+ return NULL;
+ node->data = data;
+
+ /* insert the new node after it */
+ node->prev = it;
+ if (it) {
+ node->next = it->next;
+ it->next = node;
+ }
+ else
+ node->next = list->head;
+ if (node->next)
+ node->next->prev = node;
+
+ /* update list's head and tail */
+ if (it == list->tail)
+ list->tail = node;
+ if (it == NULL)
+ list->head = node;
+
+ return node;
+}
+
+list_node_t *list_append(list_t *list, void *data)
+{
+ return list_insert_after(list, list->tail, data);
+}
+
+int list_remove_(list_t *list, list_node_t *it,
+ list_free_data_fn *free_data_fn)
+{
+ if (list == NULL || it == NULL)
+ return 1;
+ if (list->head == it)
+ list->head = it->next;
+ if (list->tail == it)
+ list->tail = it->prev;
+ if (it->next)
+ it->next->prev = it->prev;
+ if (it->prev)
+ it->prev->next = it->next;
+ list_free_node(it, free_data_fn);
+ return 0;
+}
+
+int list_remove(list_t *list, list_node_t *it)
+{
+ return list_remove_(list, it, list->free_data_fn);
+}
+
diff --git a/tools/auvirt/auvirt-list.h b/tools/auvirt/auvirt-list.h
new file mode 100644
index 0000000..fb58746
--- /dev/null
+++ b/tools/auvirt/auvirt-list.h
@@ -0,0 +1,31 @@
+
+#ifndef AUVIRT_LIST_HEADER
+#define AUVIRT_LIST_HEADER
+
+typedef void (list_free_data_fn)(void *data);
+
+typedef struct list_node_t {
+ void *data;
+ struct list_node_t *prev;
+ struct list_node_t *next;
+} list_node_t;
+
+typedef struct list_t {
+ list_node_t *head;
+ list_node_t *tail;
+ list_free_data_fn *free_data_fn;
+} list_t;
+
+list_t *list_init(list_t *list, list_free_data_fn *free_data_fn);
+list_t *list_new(list_free_data_fn *free_data_fn);
+void list_free_(list_t *list, list_free_data_fn *free_data_fn);
+void list_free(list_t *list);
+list_node_t *list_insert_after(list_t *list, list_node_t *it,
+ void *data);
+list_node_t *list_append(list_t *list, void *data);
+int list_remove_(list_t *list, list_node_t *it,
+ list_free_data_fn *free_data_fn);
+int list_remove(list_t *list, list_node_t *it);
+
+#endif
+
diff --git a/tools/auvirt/auvirt.8 b/tools/auvirt/auvirt.8
new file mode 100644
index 0000000..86266f7
--- /dev/null
+++ b/tools/auvirt/auvirt.8
@@ -0,0 +1,121 @@
+.TH AUVIRT 8 "Dec 2011" "IBM Corp" "System Administration
Utilities"
+.SH NAME
+auvirt - a program that shows data related to virtual machines
+
+.SH SYNOPSIS
+.B auvirt
+[ \fIOPTIONS\fP ]
+
+.SH DESCRIPTION
+\fBauvirt\fP shows a list of guest sessions found in the audit logs. If a guest
+is specified, only the events related to that guest is considered. To specify a
+guest, both UUID or VM name can be given.
+
+For each guest session the tool prints a record with the domain name, the user
+that started the guest, the time when the guest was started and the time when
+the guest was stoped.
+
+If the option "--all-events" is given a more detailed output is shown. In this
+mode other records are shown for guest's stops, resource
+assignments, host shutdowns and AVC and anomaly events. The first field
+indicates the event type and can have the following values: start, stop,
+res, avc, anom and down (for host shutdowns).
+
+Resource assignments have the additional fields: resource type, reason and
+resource. And AVC records have the following additional fields: operation,
+result, command and target.
+
+By default, auvirt reads records from the system audit log file. But
+\fB--stdin\fP and \fB--file\fP options can be specified to change this
+behavior.
+
+.SH OPTIONS
+.TP
+\fB--all-events\fP
+Show records for all virtualization related events.
+.TP
+\fB--debug\fP
+Print debug messages to standard output.
+.TP
+\fB-f\fP, \fB--file\fP \fIfile\fP
+Read records from the given \fIfile\fP instead from the system audit log file.
+.TP
+\fB-h\fP, \fB--help\fP
+Print help message and exit.
+.TP
+\fB--proof\fP
+Add after each event a line containing all the identifiers of the audit records
+used to calculate the event. Each identifier consists of unix time,
+milliseconds and serial number.
+.TP
+\fB--show-uuid\fP
+Add the guest's UUID to each record.
+.TP
+\fB--stdin\fP
+Read records from the standard input instead from the system audit log file.
+This option cannot be specified with \fB--file\fP.
+.TP
+\fB--summary\fP
+Print a summary with information about the events found. The summary contains
+the considered range of time, the number of guest starts and stops, the number
+of resource assignments, the number of AVC and anomaly events, the number of
+host shutdowns and the number of failed operations.
+.TP
+.BR \-te ,\ \-\-end \ [\fIend-date\fP]\ [\fIend-time\fP]
+Search for events with time stamps equal to or before the given end time. The
+format of end time depends on your locale. If the date is omitted,
+.B today
+is assumed. If the time is omitted,
+.B now
+is assumed. Use 24 hour clock time rather than AM or PM to specify time.
+An example date using the en_US.utf8 locale is 09/03/2009. An example of time
+is 18:00:00. The date format accepted is influenced by the LC_TIME
+environmental variable.
+
+You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP,
+\fByesterday\fP, \fBthis\-week\fP, \fBweek\-ago\fP, \fBthis\-month\fP,
+\fBthis\-year\fP. \fBToday\fP means starting now. \fBRecent\fP is 10 minutes
+ago. \fBYesterday\fP is 1 second after midnight the previous day.
+\fBThis\-week\fP means starting 1 second after midnight on day 0 of the week
+determined by your locale (see \fBlocaltime\fP). \fBThis\-month\fP means 1
+second after midnight on day 1 of the month. \fBThis\-year\fP means the 1
+second after midnight on the first day of the first month.
+.TP
+.BR \-ts ,\ \-\-start \ [\fIstart-date\fP]\ [\fIstart-time\fP]
+Search for events with time stamps equal to or after the given end time. The
+format of end time depends on your locale. If the date is omitted,
+.B today
+is assumed. If the time is omitted,
+.B midnight
+is assumed. Use 24 hour clock time rather than AM or PM to specify time. An
+example date using the en_US.utf8 locale is 09/03/2009. An example of time is
+18:00:00. The date format accepted is influenced by the LC_TIME environmental
+variable.
+
+You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP,
+\fByesterday\fP, \fBthis\-week\fP, \fBthis\-month\fP, \fBthis\-year\fP.
+\fBToday\fP means starting at 1 second after midnight. \fBRecent\fP is 10
+minutes ago. \fBYesterday\fP is 1 second after midnight the previous day.
+\fBThis\-week\fP means starting 1 second after midnight on day 0 of the week
+determined by your locale (see \fBlocaltime\fP). \fBThis\-month\fP means 1
+second after midnight on day 1 of the month. \fBThis\-year\fP means the 1
+second after midnight on the first day of the first month.
+.TP
+\fB-u\fP, \fB--uuid\fP \ \fIUUID\fP
+Only show events related to the guest with the given UUID.
+.TP
+\fB-v\fP, \fB--vm\fP \ \fIname\fP
+Only show events related to the guest with the given name.
+
+.SH EXAMPLES
+To see all the records in this month for a guest
+
+\fBauvirt --start this-month --vm GuestVmName --all-events\fP
+
+.SH SEE ALSO
+.BR aulast (8),
+.BR ausearch (8),
+.BR aureport (8).
+
+.SH AUTHOR
+Marcelo Cerri
diff --git a/tools/auvirt/auvirt.c b/tools/auvirt/auvirt.c
new file mode 100644
index 0000000..c04780a
--- /dev/null
+++ b/tools/auvirt/auvirt.c
@@ -0,0 +1,1308 @@
+/*
+ * auvirt.c - A tool to extract data related to virtualization.
+ * Copyright (c) 2011 IBM Corp.
+ * 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:
+ * Marcelo Henrique Cerri <mhcerri(a)br.ibm.com>
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <regex.h>
+#include <time.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "auparse.h"
+#include "libaudit.h"
+#include "ausearch-time.h"
+#include "auvirt-list.h"
+
+/* Command line parameters */
+static int help_flag = 0;
+static int stdin_flag = 0;
+static int summary_flag = 0;
+static int all_events_flag = 0;
+static int uuid_flag = 0;
+static int proof_flag = 0;
+static const char *vm = NULL;
+static const char *uuid = NULL;
+static const char *file = NULL;
+static int debug = 0;
+/*
+ * The start time and end time given in the command line is stored respectively
+ * in the variables start_time and end_time that are declared/defined in the
+ * files ausearch-time.h and ausearch-time.c. These files are reused from the
+ * ausearch tool source code:
+ *
+ * time_t start_time = 0;
+ * time_t end_time = 0;
+ */
+
+/* List of events */
+enum event_type {
+ ET_NONE = 0, ET_START, ET_STOP, ET_MACHINE_ID, ET_AVC, ET_RES, ET_ANOM,
+ ET_DOWN
+};
+struct record_id {
+ time_t time;
+ unsigned int milli;
+ unsigned long serial;
+};
+struct event {
+ enum event_type type;
+ time_t start;
+ time_t end;
+ uid_t uid;
+ char *uuid;
+ char *name;
+ int success;
+ pid_t pid;
+ /* Fields specific for resource events: */
+ char *reason;
+ char *res_type;
+ char *res;
+ /* Fields specific for machine id events: */
+ char *seclevel;
+ /* Fields specific for avc events: */
+ char *target;
+ char *comm;
+ char *seresult;
+ char *seperms;
+ /* Fields to print proof information: */
+ struct record_id proof[4];
+};
+list_t *events = NULL;
+
+
+/* Auxiliary functions to allocate and to free events. */
+struct event *event_alloc()
+{
+ struct event *event = malloc(sizeof(struct event));
+ if (event) {
+ /* The new event is initialized with values that represents
+ * unset values: -1 for uid and pid and 0 (or NULL) for numbers
+ * and pointers. For example, event->end = 0 represents an
+ * unfinished event.
+ */
+ memset(event, 0, sizeof(struct event));
+ event->uid = -1;
+ event->pid = -1;
+ }
+ return event;
+}
+
+void event_free(struct event *event)
+{
+ if (event) {
+ free(event->uuid);
+ free(event->name);
+ free(event->reason);
+ free(event->res_type);
+ free(event->res);
+ free(event->seclevel);
+ free(event->target);
+ free(event->comm);
+ free(event->seresult);
+ free(event->seperms);
+ free(event);
+ }
+}
+
+inline char *copy_str(const char *str)
+{
+ return (str) ? strdup(str) : NULL;
+}
+
+void usage(FILE *output)
+{
+ fprintf(output, "usage: auvirt [--stdin] [--all-events] [--summary] "
+ "[--start start-date [start-time]] "
+ "[--end end-date [end-time]] [--file file-name] "
+ "[--show--uuid] [--proof] "
+ "[--uuid uuid] [--vm vm-name]\n");
+}
+
+/* Parse and check command line arguments */
+int parse_args(int argc, char **argv)
+{
+ /* Based on
http://www.ietf.org/rfc/rfc4122.txt */
+ const char *uuid_pattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-"
+ "[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
+ int i, rc = 0;
+ regex_t uuid_regex;
+
+ if (regcomp(&uuid_regex, uuid_pattern, REG_EXTENDED)) {
+ fprintf(stderr, "Failed to initialize program.\n");
+ return 1;
+ }
+
+ for (i = 1; i < argc; i++) {
+ const char *opt = argv[i];
+ if (opt[0] != '-') {
+ fprintf(stderr, "Argument not expected: %s\n", opt);
+ goto error;
+ } else if (strcmp("--vm", opt) == 0 ||
+ strcmp("-v", opt) == 0) {
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires "
+ "an argument.\n", opt);
+ goto error;
+ }
+ vm = argv[++i];
+ } else if (strcmp("--uuid", opt) == 0 ||
+ strcmp("-u", opt) == 0) {
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires "
+ "an argument.\n", opt);
+ goto error;
+ }
+ if (regexec(&uuid_regex, argv[i + 1], 0, NULL, 0)) {
+ fprintf(stderr, "Invalid uuid: %s\n",
+ argv[i + 1]);
+ goto error;
+ }
+ uuid = argv[++i];
+ } else if (strcmp("--all-events", opt) == 0 ||
+ strcmp("-a", opt) == 0) {
+ all_events_flag = 1;
+ } else if (strcmp("--summary", opt) == 0 ||
+ strcmp("-s", opt) == 0) {
+ summary_flag = 1;
+ } else if (strcmp("--file", opt) == 0 ||
+ strcmp("-f", opt) == 0) {
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires "
+ "an argument.\n", opt);
+ goto error;
+ }
+ file = argv[++i];
+ } else if (strcmp("--show-uuid", opt) == 0) {
+ uuid_flag = 1;
+ } else if (strcmp("--stdin", opt) == 0) {
+ stdin_flag = 1;
+ } else if (strcmp("--proof", opt) == 0) {
+ proof_flag = 1;
+ } else if (strcmp("--help", opt) == 0 ||
+ strcmp("-h", opt) == 0) {
+ help_flag = 1;
+ goto exit;
+ } else if (strcmp("--start", opt) == 0 ||
+ strcmp("-ts", opt) == 0) {
+ const char *date, *time = NULL;
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires at "
+ "least one argument.\n", opt);
+ goto error;
+ }
+ date = argv[++i];
+ if ((i + 1) < argc && argv[i + 1][0] != '-')
+ time = argv[++i];
+ /* This will set start_time */
+ if(ausearch_time_start(date, time))
+ goto error;
+ } else if (strcmp("--end", opt) == 0 ||
+ strcmp("-te", opt) == 0) {
+ const char *date, *time = NULL;
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires at "
+ "least one argument.\n", opt);
+ goto error;
+ }
+ date = argv[++i];
+ if ((i + 1) < argc && argv[i + 1][0] != '-')
+ time = argv[++i];
+ /* This will set end_time */
+ if (ausearch_time_end(date, time))
+ goto error;
+ } else if (strcmp("--debug", opt) == 0) {
+ debug = 1;
+ } else {
+ fprintf(stderr, "Unknown option \"%s\".\n", opt);
+ goto error;
+ }
+ }
+
+ /* Validate conflicting options */
+ if (stdin_flag && file) {
+ fprintf(stderr, "\"--sdtin\" and \"--file\" options "
+ "must not be specified together.\n");
+ goto error;
+ }
+
+ if (debug) {
+ fprintf(stderr, "help_flag='%i'\n", help_flag);
+ fprintf(stderr, "stdin_flag='%i'\n", stdin_flag);
+ fprintf(stderr, "all_events_flag='%i'\n", all_events_flag);
+ fprintf(stderr, "summary_flag='%i'\n", summary_flag);
+ fprintf(stderr, "uuid='%s'\n", uuid ? uuid : "(null)");
+ fprintf(stderr, "vm='%s'\n", vm ? vm : "(null)");
+ fprintf(stderr, "file='%s'\n", file ? file : "(null)");
+ fprintf(stderr, "start_time='%-.16s'\n", (start_time == 0L) ?
+ "" : ctime(&start_time));
+ fprintf(stderr, "end_time='%-.16s'\n", (end_time == 0L) ?
+ "" : ctime(&end_time));
+ }
+
+exit:
+ regfree(&uuid_regex);
+ return rc;
+error:
+ rc = 1;
+ goto exit;
+}
+
+/* Initialize an auparse_state_t with the correct log source. */
+auparse_state_t *init_auparse()
+{
+ auparse_state_t *au = NULL;
+ if (stdin_flag) {
+ au = auparse_init(AUSOURCE_FILE_POINTER, stdin);
+ } else if (file) {
+ au = auparse_init(AUSOURCE_FILE, file);
+ } else {
+ if (getuid()) {
+ fprintf(stderr, "You probably need to be root for "
+ "this to work\n");
+ }
+ au = auparse_init(AUSOURCE_LOGS, NULL);
+ }
+ if (au == NULL) {
+ fprintf(stderr, "Error: %s\n", strerror(errno));
+ }
+ return au;
+}
+
+/* Create a criteria to search for the virtualization related records */
+int create_search_criteria(auparse_state_t *au)
+{
+ char *error = NULL;
+ char expr[1024];
+ snprintf(expr, sizeof(expr),
+ "(\\record_type >= %d && \\record_type <= %d)",
+ AUDIT_FIRST_VIRT_MSG, AUDIT_LAST_VIRT_MSG);
+ if (ausearch_add_expression(au, expr, &error, AUSEARCH_RULE_CLEAR)) {
+ fprintf(stderr, "Criteria error: %s\n", error);
+ free(error);
+ return 1;
+ }
+ if (uuid) {
+ if (ausearch_add_item(au, "uuid", "=", uuid,
+ AUSEARCH_RULE_AND)) {
+ fprintf(stderr, "Criteria error: uuid\n");
+ return 1;
+ }
+ }
+ if (vm) {
+ /*
+ * If a field has its value quoted in the audit log, for
+ * example:
+ * vm="guest-name"
+ *
+ * auparse will consider the field value with quotes when
+ * matching a rule. For example, using the example above the
+ * following rule will not match:
+ * ausearch_add_item(au, "vm", "=", "guest-name",
how);
+ *
+ * But this rule will match:
+ * ausearch_add_item(au, "vm", "=",
"\"guest-name\"", how);
+ *
+ * TODO use a better approach for this problem...
+ */
+ snprintf(expr, sizeof(expr), "\"%s\"", vm);
+ if (ausearch_add_item(au, "vm", "=", expr,
+ AUSEARCH_RULE_AND)) {
+ fprintf(stderr, "Criteria error: id\n");
+ return 1;
+ }
+ }
+ if (all_events_flag || summary_flag) {
+ if (ausearch_add_item(au, "type", "=", "AVC",
+ AUSEARCH_RULE_OR)) {
+ fprintf(stderr, "Criteria error: AVC\n");
+ return 1;
+ }
+ if (ausearch_add_item(au, "type", "=",
"SYSTEM_SHUTDOWN",
+ AUSEARCH_RULE_OR)) {
+ fprintf(stderr, "Criteria error: shutdown\n");
+ return 1;
+ }
+ snprintf(expr, sizeof(expr),
+ "(\\record_type >= %d && \\record_type <= %d) ||"
+ "(\\record_type >= %d && \\record_type <= %d)",
+ AUDIT_FIRST_ANOM_MSG, AUDIT_LAST_ANOM_MSG,
+ AUDIT_FIRST_KERN_ANOM_MSG, AUDIT_LAST_KERN_ANOM_MSG);
+ if (ausearch_add_expression(au, expr, &error,
+ AUSEARCH_RULE_OR)) {
+ fprintf(stderr, "Criteria error: %s\n", error);
+ free(error);
+ return 1;
+ }
+ }
+ if (start_time) {
+ if (ausearch_add_timestamp_item(au, ">=", start_time, 0,
+ AUSEARCH_RULE_AND)) {
+ fprintf(stderr, "Criteria error: start_time\n");
+ return 1;
+ }
+ }
+ if (end_time) {
+ if (ausearch_add_timestamp_item(au, "<=", end_time, 0,
+ AUSEARCH_RULE_AND)) {
+ fprintf(stderr, "Criteria error: end_time\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Extract the most common fields from virtualization-related records. */
+int extract_virt_fields(auparse_state_t *au, const char **p_uuid,
+ uid_t *p_uid, time_t *p_time, const char **p_name,
+ int *p_suc)
+{
+ const char *field;
+ auparse_first_record(au);
+ /* Order matters */
+ if (p_uid) {
+ if (!auparse_find_field(au, field = "uid"))
+ goto error;
+ *p_uid = auparse_get_field_int(au);
+ }
+ if (p_name) {
+ if (!auparse_find_field(au, field = "vm"))
+ goto error;
+ *p_name = auparse_get_field_str(au);
+ }
+ if (p_uuid) {
+ if (!auparse_find_field(au, field = "uuid"))
+ goto error;
+ *p_uuid = auparse_get_field_str(au);
+ }
+ if (p_suc) {
+ const char *res = auparse_find_field(au, field = "res");
+ if (res == NULL)
+ goto error;
+ *p_suc = (strcmp("success", res) == 0) ? 1 : 0;
+ }
+ if (p_time) {
+ *p_time = auparse_get_time(au);
+ }
+ return 0;
+
+error:
+ if (debug) {
+ fprintf(stderr, "Failed to get field \"%s\" for record "
+ "%ld.%03u:%lu\n", field ? field : "",
+ auparse_get_time(au),
+ auparse_get_milli(au),
+ auparse_get_serial(au));
+ }
+ return 1;
+}
+
+/* Return label and categories from a security context. */
+const char *get_seclevel(const char *seclabel)
+{
+ /*
+ * system_u:system_r:svirt_t:s0:c107,c434
+ * \____ _____/
+ * '
+ * level + cat
+ */
+ int c = 0;
+ for (;seclabel && *seclabel; seclabel++) {
+ if (*seclabel == ':')
+ c += 1;
+ if (c == 3)
+ return seclabel + 1;
+ }
+ return NULL;
+}
+
+int add_proof(struct event *event, auparse_state_t *au)
+{
+ if (!proof_flag)
+ return 0;
+
+ size_t i, proof_len = sizeof(event->proof)/sizeof(event->proof[0]);
+ for (i = 0; i < proof_len; i++) {
+ if (event->proof[i].time == 0)
+ break;
+ }
+ if (i == proof_len) {
+ if (debug)
+ fprintf(stderr, "Failed to add proof.\n");
+ return 1;
+ }
+
+ event->proof[i].time = auparse_get_time(au);
+ event->proof[i].milli = auparse_get_milli(au);
+ event->proof[i].serial = auparse_get_serial(au);
+ return 0;
+}
+
+/*
+ * machine_id records are used to get the selinux context associated to a
+ * guest.
+ */
+int process_machine_id_event(auparse_state_t *au)
+{
+ uid_t uid;
+ time_t time;
+ const char *seclevel, *uuid, *name;
+ struct event *event;
+ int success;
+
+ seclevel = get_seclevel(auparse_find_field(au, "vm-ctx"));
+ if (seclevel == NULL) {
+ if (debug)
+ fprintf(stderr, "security context not found for "
+ "MACHINE_ID event.\n");
+ }
+
+ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+ return 0;
+
+ event = event_alloc();
+ if (event == NULL)
+ return 1;
+ event->type = ET_MACHINE_ID;
+ event->uuid = copy_str(uuid);
+ event->name = copy_str(name);
+ event->success = success;
+ event->seclevel = copy_str(seclevel);
+ event->uid = uid;
+ event->start = time;
+ add_proof(event, au);
+ if (list_append(events, event) == NULL) {
+ event_free(event);
+ return 1;
+ }
+ return 0;
+}
+
+int add_start_guest_event(auparse_state_t *au)
+{
+ struct event *start;
+ uid_t uid;
+ time_t time;
+ const char *uuid, *name;
+ int success;
+ list_node_t *it;
+
+ /* Just skip this record if it failed to get some of the fields */
+ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+ return 0;
+
+ /* On failure, loop backwards to update all the resources associated to
+ * the last session of this guest. When a machine_id or a stop event is
+ * found the loop can be broken because a machine_id is created at the
+ * beginning of a session and a stop event indicates a previous
+ * session.
+ */
+ if (!success) {
+ for (it = events->tail; it; it = it->prev) {
+ struct event *event = it->data;
+ if (event->success && event->uuid &&
+ strcmp(uuid, event->uuid) == 0) {
+ if (event->type == ET_STOP ||
+ event->type == ET_MACHINE_ID) {
+ /* An old session found. */
+ break;
+ } else if (event->type == ET_RES &&
+ event->end == 0) {
+ event->end = time;
+ add_proof(event, au);
+ }
+ }
+ }
+ }
+
+ start = event_alloc();
+ if (start == NULL)
+ return 1;
+ start->type = ET_START;
+ start->uuid = copy_str(uuid);
+ start->name = copy_str(name);
+ start->success = success;
+ start->uid = uid;
+ start->start = time;
+ auparse_first_record(au);
+ if (auparse_find_field(au, "vm-pid"))
+ start->pid = auparse_get_field_int(au);
+ add_proof(start, au);
+ if (list_append(events, start) == NULL) {
+ event_free(start);
+ return 1;
+ }
+ return 0;
+}
+
+int add_stop_guest_event(auparse_state_t *au)
+{
+ list_node_t *it;
+ struct event *stop, *start = NULL, *event = NULL;
+ uid_t uid;
+ time_t time;
+ const char *uuid, *name;
+ int success;
+
+ /* Just skip this record if it failed to get some of the fields */
+ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+ return 0;
+
+ /* Loop backwards to find the last start event for the uuid and
+ * update all resource records related to that guest session.
+ */
+ for (it = events->tail; it; it = it->prev) {
+ event = it->data;
+ if (event->success && event->uuid &&
+ strcmp(uuid, event->uuid) == 0) {
+ if (event->type == ET_START) {
+ /* If an old session is found it's no longer
+ * necessary to update the resource records.
+ */
+ if (event->end || start)
+ break;
+ /* This is the start event related to the
+ * current session. */
+ start = event;
+ } else if (event->type == ET_STOP ||
+ event->type == ET_MACHINE_ID) {
+ /* Old session found. */
+ break;
+ } else if (event->type == ET_RES && event->end == 0) {
+ /* Update the resource assignments. */
+ event->end = time;
+ add_proof(event, au);
+ }
+ }
+ }
+ if (start == NULL) {
+ if (debug) {
+ fprintf(stderr, "Couldn't find the correlated start i"
+ "record to the stop event.\n");
+ }
+ return 0;
+ }
+
+ /* Create a new stop event */
+ stop = event_alloc();
+ if (stop == NULL)
+ return 1;
+ stop->type = ET_STOP;
+ stop->uuid = copy_str(uuid);
+ stop->name = copy_str(name);
+ stop->success = success;
+ stop->uid = uid;
+ stop->start = time;
+ auparse_first_record(au);
+ if (auparse_find_field(au, "vm-pid"))
+ stop->pid = auparse_get_field_int(au);
+ add_proof(stop, au);
+ if (list_append(events, stop) == NULL) {
+ event_free(stop);
+ return 1;
+ }
+
+ /* Update the correlated start event. */
+ if (success) {
+ start->end = time;
+ add_proof(start, au);
+ }
+ return 0;
+}
+
+int process_control_event(auparse_state_t *au)
+{
+ const char *op;
+
+ op = auparse_find_field(au, "op");
+ if (op == NULL) {
+ if (debug)
+ fprintf(stderr, "Invalid op field.\n");
+ return 0;
+ }
+
+ if (strcmp("start", op) == 0) {
+ if (add_start_guest_event(au))
+ return 1;
+ } else if (strcmp("stop", op) == 0) {
+ if (add_stop_guest_event(au))
+ return 1;
+ } else if (debug) {
+ fprintf(stderr, "Unknown op: %s\n", op);
+ }
+ return 0;
+}
+
+inline int is_resource(const char *res)
+{
+ if (res == NULL ||
+ res[0] == '\0' ||
+ strcmp("0", res) == 0 ||
+ strcmp("?", res) == 0)
+ return 0;
+ return 1;
+}
+
+int add_resource(auparse_state_t *au, const char *uuid, uid_t uid, time_t time,
+ const char *name, int success, const char *reason,
+ const char *res_type, const char *res)
+{
+ if (!is_resource(res))
+ return 0;
+
+ struct event *event = event_alloc();
+ if (event == NULL)
+ return 1;
+ event->type = ET_RES;
+ event->uuid = copy_str(uuid);
+ event->name = copy_str(name);
+ event->success = success;
+ event->reason = copy_str(reason);
+ event->res_type = copy_str(res_type);
+ event->res = copy_str(res);
+ event->uid = uid;
+ event->start = time;
+ add_proof(event, au);
+ if (list_append(events, event) == NULL) {
+ event_free(event);
+ return 1;
+ }
+ return 0;
+}
+
+int update_resource(auparse_state_t *au, const char *uuid, uid_t uid,
+ time_t time, const char *name, int success, const char *reason,
+ const char *res_type, const char *res)
+{
+ if (!is_resource(res) || !success)
+ return 0;
+
+ list_node_t *it;
+ struct event *start = NULL;
+
+ /* Find the last start event for the uuid */
+ for (it = events->tail; it; it = it->prev) {
+ start = it->data;
+ if (start->type == ET_RES &&
+ start->success &&
+ start->uuid &&
+ strcmp(uuid, start->uuid) == 0 &&
+ strcmp(res_type, start->res_type) == 0 &&
+ strcmp(res, start->res) == 0)
+ break;
+ }
+ if (it == NULL) {
+ if (debug) {
+ fprintf(stderr, "Couldn't find the correlated resource"
+ " record to update.\n");
+ }
+ return 0;
+ }
+
+ start->end = time;
+ add_proof(start, au);
+ return 0;
+}
+
+int process_resource_event(auparse_state_t *au)
+{
+ uid_t uid;
+ time_t time;
+ const char *res_type, *uuid, *name;
+ char field[64];
+ const char *reason;
+ int success;
+
+ /* Just skip this record if it failed to get some of the fields */
+ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+ return 0;
+
+ /* Get the resource type */
+ auparse_first_record(au);
+ res_type = auparse_find_field(au, "resrc");
+ reason = auparse_find_field(au, "reason");
+ if (res_type == NULL) {
+ if (debug)
+ fprintf(stderr, "Invalid resrc field.\n");
+ return 0;
+ }
+
+ /* Resource records with these types have old and new values. New
+ * values indicate resources assignments and are added to the event
+ * list. Old values are used to update the end time of a resource
+ * assignment.
+ */
+ int rc = 0;
+ if (strcmp("disk", res_type) == 0 ||
+ strcmp("vcpu", res_type) == 0 ||
+ strcmp("mem", res_type) == 0 ||
+ strcmp("net", res_type) == 0) {
+ const char *res;
+ /* Resource removed */
+ snprintf(field, sizeof(field), "old-%s", res_type);
+ res = auparse_find_field(au, field);
+ if (res == NULL && debug) {
+ fprintf(stderr, "Failed to get %s field.\n", field);
+ } else {
+ rc += update_resource(au, uuid, uid, time, name,
+ success, reason, res_type, res);
+ }
+
+ /* Resource added */
+ snprintf(field, sizeof(field), "new-%s", res_type);
+ res = auparse_find_field(au, field);
+ if (res == NULL && debug) {
+ fprintf(stderr, "Failed to get %s field.\n", field);
+ } else {
+ rc += add_resource(au, uuid, uid, time, name, success,
+ reason, res_type, res);
+ }
+ } else if (strcmp("cgroup", res_type) == 0) {
+ auparse_first_record(au);
+ const char *cgroup = auparse_find_field(au, "cgroup");
+ rc += add_resource(au, uuid, uid, time, name, success, reason,
+ res_type, cgroup);
+ } else if (debug) {
+ fprintf(stderr, "Found an unknown resource: %s.\n",
+ res_type);
+ }
+ return rc;
+}
+
+/* Search for the last machine_id record with the given seclevel */
+struct event *get_machine_id_by_seclevel(const char *seclevel)
+{
+ struct event *machine_id = NULL;
+ list_node_t *it;
+
+ for (it = events->tail; it; it = it->prev) {
+ struct event *event = it->data;
+ if (event->type == ET_MACHINE_ID &&
+ event->seclevel != NULL &&
+ strcmp(event->seclevel, seclevel) == 0) {
+ machine_id = event;
+ break;
+ }
+ }
+
+ return machine_id;
+}
+
+/* AVC records are correlated to guest through the selinux context. */
+int process_avc(auparse_state_t *au)
+{
+ const char *target, *seclevel;
+ struct event *machine_id, *avc;
+ uid_t uid;
+ time_t time;
+
+ seclevel = get_seclevel(auparse_find_field(au, "tcontext"));
+ if (seclevel == NULL) {
+ if (debug) {
+ fprintf(stderr, "Security context not found for "
+ "AVC event.\n");
+ }
+ return 0;
+ }
+
+ if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
+ return 0;
+
+ machine_id = get_machine_id_by_seclevel(seclevel);
+ if (machine_id == NULL) {
+ if (debug) {
+ fprintf(stderr, "Couldn't get the security level from "
+ "the AVC event.\n");
+ }
+ return 0;
+ }
+
+ avc = event_alloc();
+ if (avc == NULL)
+ return 1;
+ avc->type = ET_AVC;
+
+ /* Guest info */
+ avc->uuid = copy_str(machine_id->uuid);
+ avc->name = copy_str(machine_id->name);
+ memcpy(avc->proof, machine_id->proof, sizeof(avc->proof));
+
+ /* AVC info */
+ avc->start = time;
+ avc->uid = uid;
+ avc->seclevel = copy_str(seclevel);
+ auparse_first_record(au);
+ avc->seresult = copy_str(auparse_find_field(au, "seresult"));
+ avc->seperms = copy_str(auparse_find_field(au, "seperms"));
+ avc->comm = copy_str(auparse_find_field(au, "comm"));
+ avc->target = copy_str(auparse_find_field(au, "name"));
+ add_proof(avc, au);
+ if (list_append(events, avc) == NULL) {
+ event_free(avc);
+ return 1;
+ }
+ return 0;
+}
+
+/* This function tries to correlate an anomaly record to a guest using the qemu
+ * pid or the selinux context. */
+int process_anom(auparse_state_t *au)
+{
+ uid_t uid;
+ time_t time;
+ pid_t pid = -1;
+ list_node_t *it;
+ struct event *anom, *start = NULL;
+
+ /* An anomaly record is correlated to a guest by the process id */
+ if (auparse_find_field(au, "pid")) {
+ pid = auparse_get_field_int(au);
+ } else {
+ if (debug) {
+ fprintf(stderr, "Found an anomaly record "
+ "without pid.\n");
+ }
+ }
+
+ /* Loop backwards to find a running guest with the same pid. */
+ if (pid >= 0) {
+ for (it = events->tail; it; it = it->next) {
+ struct event *event = it->data;
+ if (event->pid == pid && event->success) {
+ if (event->type == ET_STOP) {
+ break;
+ } else if (event->type == ET_START) {
+ if (event->end == 0)
+ start = event;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Try to match using selinux context */
+ if (start == NULL) {
+ const char *seclevel;
+ struct event *machine_id;
+
+ seclevel = get_seclevel(auparse_find_field(au, "subj"));
+ if (seclevel == NULL) {
+ if (debug) {
+ auparse_first_record(au);
+ const char *text = auparse_get_record_text(au);
+ fprintf(stderr, "Security context not found "
+ "for anomaly event: %s\n",
+ text ? text : "");
+ }
+ return 0;
+ }
+ machine_id = get_machine_id_by_seclevel(seclevel);
+ if (machine_id == NULL) {
+ if (debug) {
+ fprintf(stderr, "Couldn't get the security "
+ "level from the anomaly event.\n");
+ }
+ return 0;
+ }
+
+ for (it = events->tail; it; it = it->next) {
+ struct event *event = it->data;
+ if (event->success &&
+ strcmp(machine_id->uuid, event->uuid) == 0) {
+ if (event->type == ET_STOP) {
+ break;
+ } else if (event->type == ET_START) {
+ if (event->end == 0)
+ start = event;
+ break;
+ }
+ }
+ }
+ }
+
+ if (start == NULL) {
+ if (debug) {
+ const char *text = auparse_get_record_text(au);
+ fprintf(stderr, "Guest not found for "
+ "anomaly record: %s.\n",
+ text ? text : "");
+ }
+ return 0;
+ }
+
+ if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
+ return 0;
+
+ anom = event_alloc();
+ if (anom == NULL)
+ return 1;
+ anom->type = ET_ANOM;
+ anom->uuid = copy_str(start->uuid);
+ anom->name = copy_str(start->name);
+ anom->uid = uid;
+ anom->start = time;
+ anom->pid = pid;
+ memcpy(anom->proof, start->proof, sizeof(anom->proof));
+ add_proof(anom, au);
+ if (list_append(events, anom) == NULL) {
+ event_free(anom);
+ return 1;
+ }
+ return 0;
+}
+
+int process_shutdown(auparse_state_t *au)
+{
+ uid_t uid = -1;
+ time_t time = 0;
+ struct event *down;
+ list_node_t *it;
+ int success = 0;
+
+ if (extract_virt_fields(au, NULL, &uid, &time, NULL, &success))
+ return 0;
+
+ for (it = events->tail; it; it = it->prev) {
+ struct event *event = it->data;
+ if (event->success) {
+ if (event->type == ET_START || event->type == ET_RES) {
+ if (event->end == 0) {
+ event->end = time;
+ add_proof(event, au);
+ }
+ } else if (event->type == ET_DOWN) {
+ break;
+ }
+ }
+ }
+
+ down = event_alloc();
+ if (down == NULL)
+ return 1;
+ down->type = ET_DOWN;
+ down->uid = uid;
+ down->start = time;
+ down->success = success;
+ add_proof(down, au);
+ if (list_append(events, down) == NULL) {
+ event_free(down);
+ return 1;
+ }
+ return 0;
+}
+
+/* Convert record type to a string */
+const char *get_rec_type(struct event *e)
+{
+ static char buf[64];
+ if (e == NULL)
+ return "";
+
+ switch (e->type) {
+ case ET_START:
+ return "start";
+ case ET_STOP:
+ return "stop";
+ case ET_RES:
+ return "res";
+ case ET_AVC:
+ return "avc";
+ case ET_ANOM:
+ return "anom";
+ case ET_DOWN:
+ return "down";
+ }
+
+ snprintf(buf, sizeof(buf), "%d", e->type);
+ return buf;
+}
+
+/* Convert uid to a string */
+const char *get_username(struct event *e)
+{
+ static char s[256];
+ if (!e || e->uid < 0) {
+ s[0] = '?';
+ s[1] = '\0';
+ } else {
+ struct passwd *passwd = getpwuid(e->uid);
+ if (passwd == NULL || passwd->pw_name == NULL) {
+ snprintf(s, sizeof(s), "%d", e->uid);
+ } else {
+ snprintf(s, sizeof(s), "%s", passwd->pw_name);
+ }
+ }
+ return s;
+}
+
+/* Convert a time period to string */
+const char *get_time_period(struct event *event)
+{
+ size_t i = 0;
+ static char buf[128];
+
+ i += sprintf(buf + i, "%-16.16s", ctime(&event->start));
+ if (event->end) {
+ time_t secs = event->end - event->start;
+ int mins, hours, days;
+ i += sprintf(buf + i, " - %-7.5s", ctime(&event->end) + 11);
+ mins = (secs / 60) % 60;
+ hours = (secs / 3600) % 24;
+ days = secs / 86400;
+ if (days) {
+ i += sprintf(buf + i, "(%d+%02d:%02d)", days, hours,
+ mins);
+ } else {
+ i += sprintf(buf + i, "(%02d:%02d)", hours, mins);
+ }
+ } else {
+ if (!event->success &&
+ event->type != ET_AVC &&
+ event->type != ET_ANOM) {
+ i += sprintf(buf + i, " - failed");
+ }
+ }
+ return buf;
+}
+
+void print_event(struct event *event)
+{
+ /* Auxiliary macro to convert NULL to "" */
+ #define N(str) ((str) ? str : "")
+
+ /* machine id records are used just to get information about
+ * the guests. */
+ if (event->type == ET_MACHINE_ID)
+ return;
+ /* If "--all-events" is not given, only the start event is shown. */
+ if (!all_events_flag && event->type != ET_START)
+ return;
+ /* The type of event is shown only when all records are shown */
+ if (all_events_flag)
+ printf("%-5.5s ", get_rec_type(event));
+
+ /* Print common fields */
+ printf("%-25.25s", N(event->name));
+ if (uuid_flag)
+ printf("\t%-36.36s", N(event->uuid));
+ printf("\t%-11.11s\t%-35.35s", get_username(event),
+ get_time_period(event));
+
+ /* Print type specific fields */
+ if (event->type == ET_RES) {
+ printf("\t%-12.12s", N(event->res_type));
+ printf("\t%-10.10s", N(event->reason));
+ printf("\t%s", N(event->res));
+ } else if (event->type == ET_MACHINE_ID) {
+ printf("\t%s", N(event->seclevel));
+ } else if (event->type == ET_AVC) {
+ printf("\t%-12.12s", N(event->seperms));
+ printf("\t%-10.10s", N(event->seresult));
+ printf("\t%s\t%s", N(event->comm), N(event->target));
+ }
+ printf("\n");
+
+ /* Print proof */
+ if (proof_flag) {
+ int first = 1;
+ int i, len = sizeof(event->proof)/sizeof(event->proof[0]);
+ printf(" Proof:");
+ for (i = 0; i < len; i++) {
+ if (event->proof[i].time) {
+ printf("%s %ld.%03u:%lu",
+ (first) ? "" : ",",
+ event->proof[i].time,
+ event->proof[i].milli,
+ event->proof[i].serial);
+ first = 0;
+ }
+ }
+ printf("\n\n");
+ }
+}
+
+/* Print all events */
+void print_events()
+{
+ list_node_t *it;
+ for (it = events->head; it; it = it->next) {
+ struct event *event = it->data;
+ if (event)
+ print_event(event);
+ }
+}
+
+/* Count and print summary */
+void print_summary()
+{
+ /* Summary numbers */
+ time_t start_time = 0, end_time = 0;
+ long start = 0, stop = 0, res = 0, avc = 0, anom = 0,
+ shutdown = 0, failure = 0;
+
+ /* Calculate summary */
+ list_node_t *it;
+ for (it = events->head; it; it = it->next) {
+ struct event *event = it->data;
+ if (event->success == 0 &&
+ (event->type == ET_START ||
+ event->type == ET_STOP ||
+ event->type == ET_RES)) {
+ failure++;
+ } else {
+ switch (event->type) {
+ case ET_START:
+ start++;
+ break;
+ case ET_STOP:
+ stop++;
+ break;
+ case ET_RES:
+ res++;
+ break;
+ case ET_AVC:
+ avc++;
+ break;
+ case ET_ANOM:
+ anom++;
+ break;
+ case ET_DOWN:
+ shutdown++;
+ break;
+ }
+ }
+
+ /* Calculate time range */
+ if (event->start) {
+ if (start_time == 0 || event->start < start_time) {
+ start_time = event->start;
+ }
+ if (end_time == 0 || event->start > end_time) {
+ end_time = event->start;
+ }
+ }
+ if (event->end) {
+ if (start_time == 0 || event->end < start_time) {
+ start_time = event->end;
+ }
+ if (end_time == 0 || event->end > end_time) {
+ end_time = event->end;
+ }
+ }
+
+ }
+
+ /* Print summary */
+ printf("Range of time for report: %-.16s - %-.16s\n",
+ (start_time) ? ctime(&start_time) : "undef",
+ (end_time) ? ctime(&end_time) : "undef");
+ printf("Number of guest starts: %ld\n", start);
+ printf("Number of guest stops: %ld\n", stop);
+ printf("Number of resource assignments: %ld\n", res);
+ printf("Number of related AVCs: %ld\n", avc);
+ printf("Number of related anomalies: %ld\n", anom);
+ printf("Number of host shutdowns: %ld\n", shutdown);
+ printf("Number of failed operations: %ld\n", failure);
+}
+
+int main(int argc, char **argv)
+{
+ int rc = 0;
+ auparse_state_t *au = NULL;
+
+ setlocale(LC_ALL, "");
+ if (parse_args(argc, argv))
+ goto error;
+ if (help_flag) {
+ usage(stdout);
+ goto exit;
+ }
+
+ /* Initialize event list*/
+ events = list_new((list_free_data_fn*) event_free);
+ if (events == NULL)
+ goto unexpected_error;
+
+ /* Initialize auparse */
+ au = init_auparse();
+ if (au == NULL)
+ goto error;
+ if (create_search_criteria(au))
+ goto error;
+
+ while (ausearch_next_event(au) > 0) {
+ const char *op;
+ int err = 0;
+
+ switch(auparse_get_type(au)) {
+ case AUDIT_VIRT_MACHINE_ID:
+ err = process_machine_id_event(au);
+ break;
+ case AUDIT_VIRT_CONTROL:
+ err = process_control_event(au);
+ break;
+ case AUDIT_VIRT_RESOURCE:
+ err = process_resource_event(au);
+ break;
+ case AUDIT_AVC:
+ err = process_avc(au);
+ break;
+ case AUDIT_FIRST_ANOM_MSG ... AUDIT_LAST_ANOM_MSG:
+ case AUDIT_FIRST_KERN_ANOM_MSG ... AUDIT_LAST_KERN_ANOM_MSG:
+ err = process_anom(au);
+ break;
+ case AUDIT_SYSTEM_SHUTDOWN:
+ err = process_shutdown(au);
+ break;
+ }
+ if (err) {
+ goto unexpected_error;
+ }
+ auparse_next_event(au);
+ }
+
+ /* Show results */
+ if (summary_flag) {
+ print_summary();
+ } else {
+ print_events();
+ }
+
+ /* success */
+ goto exit;
+
+unexpected_error:
+ fprintf(stderr, "Unexpected error\n");
+error:
+ rc = 1;
+exit:
+ if (au)
+ auparse_destroy(au);
+ list_free(events);
+ if (debug)
+ fprintf(stdout, "Exit code: %d\n", rc);
+ return rc;
+}
+
--
1.7.1